/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -------------------------------------------------------------- The ACE Bot is a product of Steve Yeager, and is available from the ACE Bot homepage, at http://www.axionfx.com/ace. This program is a modification of the ACE Bot, and is therefore in NO WAY supported by Steve Yeager. */ #include "../g_local.h" #include "ai_local.h" //ACE //========================================== // AI_CanMove // Checks if bot can move (really just checking the ground) // Also, this is not a real accurate check, but does a // pretty good job and looks for lava/slime. //========================================== qboolean AI_CanMove(edict_t *self, int direction) { vec3_t forward, right; vec3_t offset,start,end; vec3_t angles; trace_t tr; // Now check to see if move will move us off an edge VectorCopy(self->s.angles,angles); if(direction == BOT_MOVE_LEFT) angles[1] += 90; else if(direction == BOT_MOVE_RIGHT) angles[1] -= 90; else if(direction == BOT_MOVE_BACK) angles[1] -=180; // Set up the vectors AngleVectors (angles, forward, right, NULL); VectorSet(offset, 36, 0, 24); G_ProjectSource (self->s.origin, offset, forward, right, start); VectorSet(offset, 36, 0, -100); G_ProjectSource (self->s.origin, offset, forward, right, end); trap_Trace(&tr, start, NULL, NULL, end, self, MASK_AISOLID); if(tr.fraction == 1.0 || tr.contents & (CONTENTS_LAVA|CONTENTS_SLIME)) { //if(AIDevel.debugChased) //jal: is too spammy. Temporary disabled // G_PrintMsg (AIDevel.devguy, "%s: move blocked\n", self->bot.botStatus.netname); return qfalse; } return qtrue;// yup, can move } //=================== // AI_IsStep // Checks the floor one step below the player. Used to detect // if the player is really falling or just walking down a stair. //=================== qboolean AI_IsStep (edict_t *ent) { vec3_t point; trace_t trace; //determine a point below point[0] = ent->s.origin[0]; point[1] = ent->s.origin[1]; point[2] = ent->s.origin[2] - (1.6*AI_STEPSIZE); //trace to point trap_Trace (&trace, ent->s.origin, ent->r.mins, ent->r.maxs, point, ent, MASK_PLAYERSOLID); //trace = gi.trace( ent->s.origin, ent->r.mins, ent->r.maxs, point, ent, MASK_PLAYERSOLID); if (trace.plane.normal[2] < 0.7 && !trace.startsolid) return qfalse; //found solid. return qtrue; } //========================================== // AI_IsLadder // check if entity is touching in front of a ladder //========================================== qboolean AI_IsLadder(vec3_t origin, vec3_t v_angle, vec3_t mins, vec3_t maxs, edict_t *passent) { vec3_t spot; vec3_t flatforward, zforward; trace_t trace; AngleVectors( v_angle, zforward, NULL, NULL); // check for ladder flatforward[0] = zforward[0]; flatforward[1] = zforward[1]; flatforward[2] = 0; VectorNormalize (flatforward); VectorMA ( origin, 1, flatforward, spot); trap_Trace(&trace, origin, mins, maxs, spot, passent, MASK_AISOLID); //trace = gi.trace( origin, mins, maxs, spot, passent, MASK_AISOLID); if ((trace.fraction < 1) && (trace.surfFlags & SURF_LADDER)) //if ((trace.fraction < 1) && (trace.contents & CONTENTS_LADDER)) return qtrue; return qfalse; } //========================================== // AI_CheckEyes // Helper for ACEMV_SpecialMove. // Tries to turn when in front of obstacle //========================================== qboolean AI_CheckEyes(edict_t *self, usercmd_t *ucmd) { vec3_t forward, right; vec3_t leftstart, rightstart,focalpoint; vec3_t dir,offset; trace_t traceRight; trace_t traceLeft; // Get current angle and set up "eyes" VectorCopy(self->s.angles,dir); AngleVectors (dir, forward, right, NULL); if(!self->movetarget) VectorSet(offset,200,0,self->r.maxs[2]*0.5); // focalpoint else VectorSet(offset,64,0,self->r.maxs[2]*0.5); // wander focalpoint G_ProjectSource (self->s.origin, offset, forward, right, focalpoint); VectorSet(offset, 0, 18, self->r.maxs[2]*0.5); G_ProjectSource (self->s.origin, offset, forward, right, leftstart); offset[1] -= 36; //VectorSet(offset, 0, -18, self->r.maxs[2]*0.5); G_ProjectSource (self->s.origin, offset, forward, right, rightstart); trap_Trace(&traceRight, rightstart, NULL, NULL, focalpoint, self, MASK_AISOLID); trap_Trace(&traceLeft, leftstart, NULL, NULL, focalpoint, self, MASK_AISOLID); //traceRight = gi.trace( rightstart, NULL, NULL, focalpoint, self, MASK_AISOLID); //traceLeft = gi.trace( leftstart, NULL, NULL, focalpoint, self, MASK_AISOLID); // Find the side with more open space and turn if(traceRight.fraction != 1 || traceLeft.fraction != 1) { if(traceRight.fraction > traceLeft.fraction) self->s.angles[YAW] += (1.0 - traceLeft.fraction) * 45.0; else self->s.angles[YAW] += -(1.0 - traceRight.fraction) * 45.0; ucmd->forwardmove = 400; return qtrue; } return qfalse; } //========================================== // AI_SpecialMove // Handle special cases of crouch/jump // If the move is resolved here, this function returns qtrue. //========================================== qboolean AI_SpecialMove(edict_t *self, usercmd_t *ucmd) { vec3_t forward; trace_t tr; vec3_t boxmins, boxmaxs, boxorigin; // Get current direction AngleVectors( tv(0, self->s.angles[YAW], 0), forward, NULL, NULL ); //make sure we are bloqued VectorCopy( self->s.origin, boxorigin ); VectorMA( boxorigin, 8, forward, boxorigin ); //move box by 8 to front trap_Trace( &tr, self->s.origin, self->r.mins, self->r.maxs, boxorigin, self, MASK_AISOLID); if( !tr.startsolid && tr.fraction == 1.0 ) // not bloqued return qfalse; //ramps //jabot092(2) if( tr.plane.normal[2] >= 0.7 ) return qfalse; if( self->ai.pers.moveTypesMask & LINK_CROUCH || self->is_swim ) { //crouch box VectorCopy( self->s.origin, boxorigin ); VectorCopy( self->r.mins, boxmins ); VectorCopy( self->r.maxs, boxmaxs ); boxmaxs[2] = 14; //crouched size VectorMA( boxorigin, 8, forward, boxorigin ); //move box by 8 to front //see if bloqued trap_Trace( &tr, boxorigin, boxmins, boxmaxs, boxorigin, self, MASK_AISOLID); if( !tr.startsolid ) // can move by crouching { ucmd->forwardmove = 400; ucmd->upmove = -400; return qtrue; } } if( self->ai.pers.moveTypesMask & LINK_JUMP && self->groundentity ) { //jump box VectorCopy( self->s.origin, boxorigin ); VectorCopy( self->r.mins, boxmins ); VectorCopy( self->r.maxs, boxmaxs ); VectorMA( boxorigin, 8, forward, boxorigin ); //move box by 8 to front // boxorigin[2] += ( boxmins[2] + AI_JUMPABLE_HEIGHT ); //put at bottom + jumpable height boxmaxs[2] = boxmaxs[2] - boxmins[2]; //total player box height in boxmaxs boxmins[2] = 0; //if( boxmaxs[2] > AI_JUMPABLE_HEIGHT ) //the box is smaller than AI_JUMPABLE_HEIGHT //{ //boxmaxs[2] -= AI_JUMPABLE_HEIGHT; trap_Trace( &tr, boxorigin, boxmins, boxmaxs, boxorigin, self, MASK_AISOLID); if( !tr.startsolid ) //can move by jumping { ucmd->forwardmove = 400; ucmd->upmove = 400; return qtrue; } //} } //nothing worked, check for turning return AI_CheckEyes(self, ucmd); } //========================================== // AI_ChangeAngle // Make the change in angles a little more gradual, not so snappy // Subtle, but noticeable. // // Modified from the original id ChangeYaw code... //========================================== void AI_ChangeAngle (edict_t *ent) { float ideal_yaw; float ideal_pitch; float current_yaw; float current_pitch; float move; float speed; vec3_t ideal_angle; // Normalize the move angle first VectorNormalize(ent->ai.move_vector); current_yaw = anglemod(ent->s.angles[YAW]); current_pitch = anglemod(ent->s.angles[PITCH]); VecToAngles (ent->ai.move_vector, ideal_angle); ideal_yaw = anglemod(ideal_angle[YAW]); ideal_pitch = anglemod(ideal_angle[PITCH]); // Yaw if (current_yaw != ideal_yaw) { move = ideal_yaw - current_yaw; speed = ent->yaw_speed * FRAMETIME; if (ideal_yaw > current_yaw) { if (move >= 180) move = move - 360; } else { if (move <= -180) move = move + 360; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } ent->s.angles[YAW] = anglemod (current_yaw + move); } // Pitch if (current_pitch != ideal_pitch) { move = ideal_pitch - current_pitch; speed = ent->yaw_speed * FRAMETIME; if (ideal_pitch > current_pitch) { if (move >= 180) move = move - 360; } else { if (move <= -180) move = move + 360; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } ent->s.angles[PITCH] = anglemod (current_pitch + move); } } //========================================== // AI_MoveToGoalEntity // Set bot to move to it's movetarget. (following node path) //========================================== qboolean AI_MoveToGoalEntity(edict_t *self, usercmd_t *ucmd) { if (!self->movetarget || !self->r.client) return qfalse; // If a rocket or grenade is around deal with it // Simple, but effective (could be rewritten to be more accurate) if(!Q_stricmp(self->movetarget->classname,"rocket") || !Q_stricmp(self->movetarget->classname,"grenade") || !Q_stricmp(self->movetarget->classname,"hgrenade")) { VectorSubtract (self->movetarget->s.origin, self->s.origin, self->ai.move_vector); AI_ChangeAngle(self); if(AIDevel.debugChased && bot_showcombat->integer) G_PrintMsg (AIDevel.chaseguy, "%s: Oh crap a rocket!\n",self->ai.pers.netname); // strafe left/right if(rand()%1 && AI_CanMove(self, BOT_MOVE_LEFT)) ucmd->sidemove = -400; else if(AI_CanMove(self, BOT_MOVE_RIGHT)) ucmd->sidemove = 400; return qtrue; } // Set bot's movement direction VectorSubtract (self->movetarget->s.origin, self->s.origin, self->ai.move_vector); AI_ChangeAngle(self); if(!AI_CanMove(self, BOT_MOVE_FORWARD) ) { self->movetarget = NULL; ucmd->forwardmove = -100; return qfalse; } //Move ucmd->forwardmove = 400; return qtrue; }