diff --git a/src/g_ai.c b/src/g_ai.c index 32a6ce6..67db556 100644 --- a/src/g_ai.c +++ b/src/g_ai.c @@ -1,148 +1,170 @@ /* -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. - -*/ -// g_ai.c + * 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 basic AI functions like enemy detection, attacking and so on. + * While mostly unused by the CTF code the functions must be here since + * big parts of the game logic rely on them. + * + * ======================================================================= + */ #include "header/local.h" -qboolean FindTarget (edict_t *self); -extern cvar_t *maxclients; +qboolean FindTarget(edict_t *self); -qboolean ai_checkattack (edict_t *self, float dist); +extern cvar_t *maxclients; -qboolean enemy_vis; -qboolean enemy_infront; -int enemy_range; -float enemy_yaw; +qboolean ai_checkattack(edict_t *self, float dist); -//============================================================================ +qboolean enemy_vis; +qboolean enemy_infront; +int enemy_range; +float enemy_yaw; +/* ============================================================================ */ /* -================= -AI_SetSightClient - -Called once each frame to set level.sight_client to the -player to be checked for in findtarget. - -If all clients are either dead or in notarget, sight_client -will be null. - -In coop games, sight_client will cycle between the clients. -================= -*/ -void AI_SetSightClient (void) + * Called once each frame to set level.sight_client + * to theplayer to be checked for in findtarget. + * If all clients are either dead or in notarget, + * sight_client will be null. In coop games, + * sight_client will cycle between the clients. + */ +void +AI_SetSightClient(void) { - edict_t *ent; - int start, check; + edict_t *ent; + int start, check; if (level.sight_client == NULL) + { start = 1; + } else + { start = level.sight_client - g_edicts; + } check = start; + while (1) { check++; + if (check > game.maxclients) + { check = 1; + } + ent = &g_edicts[check]; - if (ent->inuse - && ent->health > 0 - && !(ent->flags & FL_NOTARGET) ) + + if (ent->inuse && + (ent->health > 0) && + !(ent->flags & FL_NOTARGET)) { level.sight_client = ent; - return; // got one + return; /* got one */ } + if (check == start) { level.sight_client = NULL; - return; // nobody to see + return; /* nobody to see */ } } } -//============================================================================ +/* ============================================================================ */ /* -============= -ai_move - -Move the specified distance at current facing. -This replaces the QC functions: ai_forward, ai_back, ai_pain, and ai_painforward -============== -*/ -void ai_move (edict_t *self, float dist) + * Move the specified distance at + * current facing. This replaces the + * QC functions: ai_forward, ai_back, + * ai_pain, and ai_painforward + */ +void +ai_move(edict_t *self, float dist) { - M_walkmove (self, self->s.angles[YAW], dist); + M_walkmove(self, self->s.angles[YAW], dist); } - /* -============= -ai_stand - -Used for standing around and looking for players -Distance is for slight position adjustments needed by the animations -============== -*/ -void ai_stand (edict_t *self, float dist) + * Used for standing around and looking + * for players. Distance is for slight + * position adjustments needed by the + * animations + */ +void +ai_stand(edict_t *self, float dist) { - vec3_t v; + vec3_t v; if (dist) - M_walkmove (self, self->s.angles[YAW], dist); + { + M_walkmove(self, self->s.angles[YAW], dist); + } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { if (self->enemy) { - VectorSubtract (self->enemy->s.origin, self->s.origin, v); + VectorSubtract(self->enemy->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); - if (self->s.angles[YAW] != self->ideal_yaw && self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) + + if ((self->s.angles[YAW] != self->ideal_yaw) && + self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) { - self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); - self->monsterinfo.run (self); + self->monsterinfo.aiflags &= + ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); + self->monsterinfo.run(self); } - M_ChangeYaw (self); - ai_checkattack (self, 0); + + M_ChangeYaw(self); + ai_checkattack(self, 0); } else - FindTarget (self); + { + FindTarget(self); + } + return; } - if (FindTarget (self)) + if (FindTarget(self)) + { return; - + } + if (level.time > self->monsterinfo.pausetime) { - self->monsterinfo.walk (self); + self->monsterinfo.walk(self); return; } - if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && (level.time > self->monsterinfo.idle_time)) + if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && + (level.time > self->monsterinfo.idle_time)) { if (self->monsterinfo.idle_time) { - self->monsterinfo.idle (self); + self->monsterinfo.idle(self); self->monsterinfo.idle_time = level.time + 15 + random() * 15; } else @@ -152,27 +174,25 @@ void ai_stand (edict_t *self, float dist) } } - /* -============= -ai_walk - -The monster is walking it's beat -============= -*/ -void ai_walk (edict_t *self, float dist) + * The monster is walking it's beat + */ +void +ai_walk(edict_t *self, float dist) { - M_MoveToGoal (self, dist); + M_MoveToGoal(self, dist); - // check for noticing a player - if (FindTarget (self)) + /* check for noticing a player */ + if (FindTarget(self)) + { return; + } if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time)) { if (self->monsterinfo.idle_time) { - self->monsterinfo.search (self); + self->monsterinfo.search(self); self->monsterinfo.idle_time = level.time + 15 + random() * 15; } else @@ -182,178 +202,201 @@ void ai_walk (edict_t *self, float dist) } } - /* -============= -ai_charge - -Turns towards target and advances -Use this call with a distnace of 0 to replace ai_face -============== -*/ -void ai_charge (edict_t *self, float dist) + * Turns towards target and advances + * Use this call with a distnace of 0 + * to replace ai_face + */ +void +ai_charge(edict_t *self, float dist) { - vec3_t v; + vec3_t v; - VectorSubtract (self->enemy->s.origin, self->s.origin, v); + VectorSubtract(self->enemy->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); - M_ChangeYaw (self); + M_ChangeYaw(self); if (dist) - M_walkmove (self, self->s.angles[YAW], dist); + { + M_walkmove(self, self->s.angles[YAW], dist); + } } - /* -============= -ai_turn - -don't move, but turn towards ideal_yaw -Distance is for slight position adjustments needed by the animations -============= -*/ -void ai_turn (edict_t *self, float dist) + * Don't move, but turn towards ideal_yaw + * Distance is for slight position adjustments + * needed by the animations + */ +void +ai_turn(edict_t *self, float dist) { if (dist) - M_walkmove (self, self->s.angles[YAW], dist); + { + M_walkmove(self, self->s.angles[YAW], dist); + } - if (FindTarget (self)) + if (FindTarget(self)) + { return; - - M_ChangeYaw (self); + } + + M_ChangeYaw(self); } +/* + * + * .enemy + * Will be world if not currently angry at anyone. + * + * .movetarget + * The next path spot to walk toward. If .enemy, + * ignore .movetarget. When an enemy is killed, + * the monster will try to return to it's path. + * + * .hunt_time + * Set to time + something when the player is in + * sight, but movement straight for him is blocked. + * This causes the monster to use wall following code for + * movement direction instead of sighting on the player. + * + * .ideal_yaw + * A yaw angle of the intended direction, which will be + * turned towards at up to 45 deg / state. If the enemy + * is in view and hunt_time is not active, this will be + * the exact line towards the enemy. + * + * .pausetime + * A monster will leave it's stand state and head towards + * it's .movetarget when time > .pausetime. + * + * walkmove(angle, speed) primitive is all or nothing + */ /* - -.enemy -Will be world if not currently angry at anyone. - -.movetarget -The next path spot to walk toward. If .enemy, ignore .movetarget. -When an enemy is killed, the monster will try to return to it's path. - -.hunt_time -Set to time + something when the player is in sight, but movement straight for -him is blocked. This causes the monster to use wall following code for -movement direction instead of sighting on the player. - -.ideal_yaw -A yaw angle of the intended direction, which will be turned towards at up -to 45 deg / state. If the enemy is in view and hunt_time is not active, -this will be the exact line towards the enemy. - -.pausetime -A monster will leave it's stand state and head towards it's .movetarget when -time > .pausetime. - -walkmove(angle, speed) primitive is all or nothing -*/ - -/* -============= -range - -returns the range catagorization of an entity reletive to self -0 melee range, will become hostile even if back is turned -1 visibility and infront, or visibility and show hostile -2 infront and show hostile -3 only triggered by damage -============= -*/ -int range (edict_t *self, edict_t *other) + * returns the range catagorization of an entity reletive to self + * 0 melee range, will become hostile even if back is turned + * 1 visibility and infront, or visibility and show hostile + * 2 infront and show hostile + * 3 only triggered by damage + */ +int +range(edict_t *self, edict_t *other) { - vec3_t v; - float len; + vec3_t v; + float len; + + VectorSubtract(self->s.origin, other->s.origin, v); + len = VectorLength(v); - VectorSubtract (self->s.origin, other->s.origin, v); - len = VectorLength (v); if (len < MELEE_DISTANCE) + { return RANGE_MELEE; + } + if (len < 500) + { return RANGE_NEAR; + } + if (len < 1000) + { return RANGE_MID; + } + return RANGE_FAR; } /* -============= -visible - -returns 1 if the entity is visible to self, even if not infront () -============= -*/ -qboolean visible (edict_t *self, edict_t *other) + * returns 1 if the entity is visible + * to self, even if not infront (). + */ +qboolean +visible(edict_t *self, edict_t *other) { - vec3_t spot1; - vec3_t spot2; - trace_t trace; + vec3_t spot1; + vec3_t spot2; + trace_t trace; - VectorCopy (self->s.origin, spot1); + VectorCopy(self->s.origin, spot1); spot1[2] += self->viewheight; - VectorCopy (other->s.origin, spot2); + VectorCopy(other->s.origin, spot2); spot2[2] += other->viewheight; - trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE); - + trace = gi.trace(spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE); + if (trace.fraction == 1.0) + { return true; + } + return false; } - /* -============= -infront - -returns 1 if the entity is in front (in sight) of self -============= -*/ -qboolean infront (edict_t *self, edict_t *other) + * returns 1 if the entity is in front (in sight) of self + */ +qboolean +infront(edict_t *self, edict_t *other) { - vec3_t vec; - float dot; - vec3_t forward; - - if ((self == NULL) || (other == NULL)) - return false; + vec3_t vec; + float dot; + vec3_t forward; - AngleVectors (self->s.angles, forward, NULL, NULL); - - if ((self == NULL) || (other == NULL)) + if ((self == NULL) || (other == NULL)) + { return false; + } + + AngleVectors(self->s.angles, forward, NULL, NULL); + + if ((self == NULL) || (other == NULL)) + { + return false; + } + + VectorSubtract(other->s.origin, self->s.origin, vec); + VectorNormalize(vec); + dot = DotProduct(vec, forward); - VectorSubtract (other->s.origin, self->s.origin, vec); - VectorNormalize (vec); - dot = DotProduct (vec, forward); - if (dot > 0.3) + { return true; + } + return false; } +/* ============================================================================ */ -//============================================================================ - -void HuntTarget (edict_t *self) +void +HuntTarget(edict_t *self) { - vec3_t vec; + vec3_t vec; self->goalentity = self->enemy; + if (self->monsterinfo.aiflags & AI_STAND_GROUND) - self->monsterinfo.stand (self); + { + self->monsterinfo.stand(self); + } else - self->monsterinfo.run (self); - VectorSubtract (self->enemy->s.origin, self->s.origin, vec); + { + self->monsterinfo.run(self); + } + + VectorSubtract(self->enemy->s.origin, self->s.origin, vec); self->ideal_yaw = vectoyaw(vec); - // wait a while before first attack + + /* wait a while before first attack */ if (!(self->monsterinfo.aiflags & AI_STAND_GROUND)) - AttackFinished (self, 1); + { + AttackFinished(self, 1); + } } -void FoundTarget (edict_t *self) +void +FoundTarget(edict_t *self) { - // let other monsters see this monster for a while + /* let other monsters see this monster for a while */ if (self->enemy->client) { level.sight_entity = self; @@ -361,83 +404,88 @@ void FoundTarget (edict_t *self) level.sight_entity->light_level = 128; } - self->show_hostile = level.time + 1; // wake up other monsters + self->show_hostile = level.time + 1; /* wake up other monsters */ VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); self->monsterinfo.trail_time = level.time; if (!self->combattarget) { - HuntTarget (self); + HuntTarget(self); return; } self->goalentity = self->movetarget = G_PickTarget(self->combattarget); + if (!self->movetarget) { self->goalentity = self->movetarget = self->enemy; - HuntTarget (self); - gi.dprintf("%s at %s, combattarget %s not found\n", self->classname, vtos(self->s.origin), self->combattarget); + HuntTarget(self); + gi.dprintf("%s at %s, combattarget %s not found\n", + self->classname, + vtos(self->s.origin), + self->combattarget); return; } - // clear out our combattarget, these are a one shot deal + /* clear out our combattarget, these are a one shot deal */ self->combattarget = NULL; self->monsterinfo.aiflags |= AI_COMBAT_POINT; - // clear the targetname, that point is ours! + /* clear the targetname, that point is ours! */ self->movetarget->targetname = NULL; self->monsterinfo.pausetime = 0; - // run for it - self->monsterinfo.run (self); + /* run for it */ + self->monsterinfo.run(self); } - /* -=========== -FindTarget - -Self is currently not attacking anything, so try to find a target - -Returns TRUE if an enemy was sighted - -When a player fires a missile, the point of impact becomes a fakeplayer so -that monsters that see the impact will respond as if they had seen the -player. - -To avoid spending too much time, only a single client (or fakeclient) is -checked each frame. This means multi player games will have slightly -slower noticing monsters. -============ -*/ -qboolean FindTarget (edict_t *self) + * Self is currently not attacking anything, + * so try to find a target + * + * Returns TRUE if an enemy was sighted + * + * When a player fires a missile, the point of + * impact becomes a fakeplayer so that monsters + * that see the impact will respond as if they + * had seen the player. + * + * To avoid spending too much time, only a single + * client (or fakeclient) is checked each frame. + * This means multi player games will have slightly + * slower noticing monsters. + */ +qboolean +FindTarget(edict_t *self) { - edict_t *client; - qboolean heardit; - int r; + edict_t *client; + qboolean heardit; + int r; if (self->monsterinfo.aiflags & AI_GOOD_GUY) { - //FIXME look for monsters? return false; } - // if we're going to a combat point, just proceed + /* if we're going to a combat point, just proceed */ if (self->monsterinfo.aiflags & AI_COMBAT_POINT) + { return false; + } - // if the first spawnflag bit is set, the monster will only wake up on - // really seeing the player, not another monster getting angry or hearing - // something - - // revised behavior so they will wake up if they "see" a player make a noise - // but not weapon impact/explosion noises - + /* if the first spawnflag bit is set, + the monster will only wake up on + really seeing the player, not another + monster getting angry or hearing + something */ heardit = false; - if ((level.sight_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) ) + + if ((level.sight_entity_framenum >= (level.framenum - 1)) && + !(self->spawnflags & 1)) { client = level.sight_entity; + if (client->enemy == self->enemy) { return false; @@ -448,7 +496,9 @@ qboolean FindTarget (edict_t *self) client = level.sound_entity; heardit = true; } - else if (!(self->enemy) && (level.sound2_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) ) + else if (!(self->enemy) && + (level.sound2_entity_framenum >= (level.framenum - 1)) && + !(self->spawnflags & 1)) { client = level.sound2_entity; heardit = true; @@ -456,65 +506,85 @@ qboolean FindTarget (edict_t *self) else { client = level.sight_client; + if (!client) - return false; // no clients to get mad at + { + return false; /* no clients to get mad at */ + } } - // if the entity went away, forget it + /* if the entity went away, forget it */ if (!client->inuse) + { return false; + } if (client == self->enemy) - return true; // JDC false; + { + return true; + } if (client->client) { if (client->flags & FL_NOTARGET) + { return false; + } } else if (client->svflags & SVF_MONSTER) { if (!client->enemy) + { return false; + } + if (client->enemy->flags & FL_NOTARGET) + { return false; + } } else if (heardit) { if (client->owner->flags & FL_NOTARGET) + { return false; + } } else + { return false; + } if (!heardit) { - r = range (self, client); + r = range(self, client); if (r == RANGE_FAR) + { return false; + } - // this is where we would check invisibility - - // is client in an spot too dark to be seen? + /* is client in an spot too dark to be seen? */ if (client->light_level <= 5) + { return false; + } - if (!visible (self, client)) + if (!visible(self, client)) { return false; } if (r == RANGE_NEAR) { - if (client->show_hostile < level.time && !infront (self, client)) + if ((client->show_hostile < level.time) && !infront(self, client)) { return false; } } else if (r == RANGE_MID) { - if (!infront (self, client)) + if (!infront(self, client)) { return false; } @@ -529,6 +599,7 @@ qboolean FindTarget (edict_t *self) if (!self->enemy->client) { self->enemy = self->enemy->enemy; + if (!self->enemy->client) { self->enemy = NULL; @@ -537,117 +608,143 @@ qboolean FindTarget (edict_t *self) } } } - else // heardit + else /* heardit */ { - vec3_t temp; + vec3_t temp; if (self->spawnflags & 1) { - if (!visible (self, client)) + if (!visible(self, client)) + { return false; + } } else { if (!gi.inPHS(self->s.origin, client->s.origin)) + { return false; + } } - VectorSubtract (client->s.origin, self->s.origin, temp); + VectorSubtract(client->s.origin, self->s.origin, temp); - if (VectorLength(temp) > 1000) // too far to hear + if (VectorLength(temp) > 1000) /* too far to hear */ { return false; } - // check area portals - if they are different and not connected then we can't hear it + /* check area portals - if they are different + and not connected then we can't hear it */ if (client->areanum != self->areanum) + { if (!gi.AreasConnected(self->areanum, client->areanum)) + { return false; + } + } self->ideal_yaw = vectoyaw(temp); - M_ChangeYaw (self); + M_ChangeYaw(self); - // hunt the sound for a bit; hopefully find the real player + /* hunt the sound for a bit; hopefully find the real player */ self->monsterinfo.aiflags |= AI_SOUND_TARGET; self->enemy = client; } - // - // got one - // - FoundTarget (self); + /* got one */ + FoundTarget(self); - if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight)) - self->monsterinfo.sight (self, self->enemy); + if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && + (self->monsterinfo.sight)) + { + self->monsterinfo.sight(self, self->enemy); + } return true; } +/* ============================================================================= */ -//============================================================================= - -/* -============ -FacingIdeal - -============ -*/ -qboolean FacingIdeal(edict_t *self) +qboolean +FacingIdeal(edict_t *self) { - float delta; + float delta; delta = anglemod(self->s.angles[YAW] - self->ideal_yaw); - if (delta > 45 && delta < 315) + + if ((delta > 45) && (delta < 315)) + { return false; + } + return true; } +/* ============================================================================= */ -//============================================================================= - -qboolean M_CheckAttack (edict_t *self) +qboolean +M_CheckAttack(edict_t *self) { - vec3_t spot1, spot2; - float chance; - trace_t tr; + vec3_t spot1, spot2; + float chance; + trace_t tr; if (self->enemy->health > 0) { - // see if any entities are in the way of the shot - VectorCopy (self->s.origin, spot1); + /* see if any entities are in the way of the shot */ + VectorCopy(self->s.origin, spot1); spot1[2] += self->viewheight; - VectorCopy (self->enemy->s.origin, spot2); + VectorCopy(self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; - tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WINDOW); + tr = gi.trace( spot1, NULL, NULL, spot2, self, + CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA | + CONTENTS_WINDOW); - // do we have a clear shot? + /* do we have a clear shot? */ if (tr.ent != self->enemy) + { return false; + } } - - // melee attack + + /* melee attack */ if (enemy_range == RANGE_MELEE) { - // don't always melee in easy mode - if (skill->value == 0 && (rand()&3) ) + /* don't always melee in easy mode */ + if ((skill->value == 0) && (rand() & 3)) + { return false; + } + if (self->monsterinfo.melee) + { self->monsterinfo.attack_state = AS_MELEE; + } else + { self->monsterinfo.attack_state = AS_MISSILE; + } + return true; } - - // missile attack + + /* missile attack */ if (!self->monsterinfo.attack) + { return false; - + } + if (level.time < self->monsterinfo.attack_finished) + { return false; - + } + if (enemy_range == RANGE_FAR) + { return false; + } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { @@ -671,114 +768,119 @@ qboolean M_CheckAttack (edict_t *self) } if (skill->value == 0) + { chance *= 0.5; + } else if (skill->value >= 2) + { chance *= 2; + } - if (random () < chance) + if (random() < chance) { self->monsterinfo.attack_state = AS_MISSILE; - self->monsterinfo.attack_finished = level.time + 2*random(); + self->monsterinfo.attack_finished = level.time + 2 * random(); return true; } if (self->flags & FL_FLY) { if (random() < 0.3) + { self->monsterinfo.attack_state = AS_SLIDING; + } else + { self->monsterinfo.attack_state = AS_STRAIGHT; + } } return false; } - /* -============= -ai_run_melee - -Turn and close until within an angle to launch a melee attack -============= -*/ -void ai_run_melee(edict_t *self) + * Turn and close until within an + * angle to launch a melee attack + */ +void +ai_run_melee(edict_t *self) { self->ideal_yaw = enemy_yaw; - M_ChangeYaw (self); + M_ChangeYaw(self); if (FacingIdeal(self)) { - self->monsterinfo.melee (self); + self->monsterinfo.melee(self); self->monsterinfo.attack_state = AS_STRAIGHT; } } - /* -============= -ai_run_missile - -Turn in place until within an angle to launch a missile attack -============= -*/ -void ai_run_missile(edict_t *self) + * Turn in place until within an + * angle to launch a missile attack + */ +void +ai_run_missile(edict_t *self) { self->ideal_yaw = enemy_yaw; - M_ChangeYaw (self); + M_ChangeYaw(self); if (FacingIdeal(self)) { - self->monsterinfo.attack (self); + self->monsterinfo.attack(self); self->monsterinfo.attack_state = AS_STRAIGHT; } } - /* -============= -ai_run_slide - -Strafe sideways, but stay at aproximately the same range -============= -*/ -void ai_run_slide(edict_t *self, float distance) + * Strafe sideways, but stay at + * aproximately the same range + */ +void +ai_run_slide(edict_t *self, float distance) { - float ofs; - + float ofs; + self->ideal_yaw = enemy_yaw; - M_ChangeYaw (self); + M_ChangeYaw(self); if (self->monsterinfo.lefty) + { ofs = 90; + } else + { ofs = -90; - - if (M_walkmove (self, self->ideal_yaw + ofs, distance)) + } + + if (M_walkmove(self, self->ideal_yaw + ofs, distance)) + { return; - + } + self->monsterinfo.lefty = 1 - self->monsterinfo.lefty; - M_walkmove (self, self->ideal_yaw - ofs, distance); + M_walkmove(self, self->ideal_yaw - ofs, distance); } - /* -============= -ai_checkattack - -Decides if we're going to attack or do something else -used by ai_run and ai_stand -============= -*/ -qboolean ai_checkattack (edict_t *self, float dist) + * Decides if we're going to attack or + * do something else * used by ai_run + * and ai_stand + */ +qboolean +ai_checkattack(edict_t *self, float dist) { - vec3_t temp; - qboolean hesDeadJim; + vec3_t temp; + qboolean hesDeadJim; - // this causes monsters to run blindly to the combat point w/o firing + /* this causes monsters to run blindly + to the combat point w/o firing */ if (self->goalentity) { if (self->monsterinfo.aiflags & AI_COMBAT_POINT) + { return false; + } if (self->monsterinfo.aiflags & AI_SOUND_TARGET) { @@ -787,13 +889,22 @@ qboolean ai_checkattack (edict_t *self, float dist) if (self->goalentity == self->enemy) { if (self->movetarget) + { self->goalentity = self->movetarget; + } else + { self->goalentity = NULL; - } + } + } + self->monsterinfo.aiflags &= ~AI_SOUND_TARGET; + if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) - self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); + { + self->monsterinfo.aiflags &= + ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); + } } else { @@ -805,8 +916,9 @@ qboolean ai_checkattack (edict_t *self, float dist) enemy_vis = false; - // see if the enemy is dead + /* see if the enemy is dead */ hesDeadJim = false; + if ((!self->enemy) || (!self->enemy->inuse)) { hesDeadJim = true; @@ -824,151 +936,156 @@ qboolean ai_checkattack (edict_t *self, float dist) if (self->monsterinfo.aiflags & AI_BRUTAL) { if (self->enemy->health <= -80) + { hesDeadJim = true; + } } else { if (self->enemy->health <= 0) + { hesDeadJim = true; + } } } if (hesDeadJim) { self->enemy = NULL; - // FIXME: look all around for other targets - if (self->oldenemy && self->oldenemy->health > 0) + + if (self->oldenemy && (self->oldenemy->health > 0)) { self->enemy = self->oldenemy; self->oldenemy = NULL; - HuntTarget (self); + HuntTarget(self); } else { if (self->movetarget) { self->goalentity = self->movetarget; - self->monsterinfo.walk (self); + self->monsterinfo.walk(self); } else { - // we need the pausetime otherwise the stand code - // will just revert to walking with no target and - // the monsters will wonder around aimlessly trying - // to hunt the world entity + /* we need the pausetime otherwise the stand code + will just revert to walking with no target and + the monsters will wonder around aimlessly trying + to hunt the world entity */ self->monsterinfo.pausetime = level.time + 100000000; - self->monsterinfo.stand (self); + self->monsterinfo.stand(self); } + return true; } } - self->show_hostile = level.time + 1; // wake up other monsters + self->show_hostile = level.time + 1; /* wake up other monsters */ - // check knowledge of enemy + /* check knowledge of enemy */ enemy_vis = visible(self, self->enemy); + if (enemy_vis) { self->monsterinfo.search_time = level.time + 5; - VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting); + VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); } enemy_infront = infront(self, self->enemy); enemy_range = range(self, self->enemy); - VectorSubtract (self->enemy->s.origin, self->s.origin, temp); + VectorSubtract(self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); - if (self->monsterinfo.attack_state == AS_MISSILE) { - ai_run_missile (self); + ai_run_missile(self); return true; } + if (self->monsterinfo.attack_state == AS_MELEE) { - ai_run_melee (self); + ai_run_melee(self); return true; } - // if enemy is not currently visible, we will never attack + /* if enemy is not currently visible, + we will never attack */ if (!enemy_vis) + { return false; + } - return self->monsterinfo.checkattack (self); + return self->monsterinfo.checkattack(self); } - /* -============= -ai_run - -The monster has an enemy it is trying to kill -============= -*/ -void ai_run (edict_t *self, float dist) + * The monster has an enemy it is trying to kill + */ +void +ai_run(edict_t *self, float dist) { - vec3_t v; - edict_t *tempgoal; - edict_t *save; - qboolean new; - edict_t *marker; - float d1, d2; - trace_t tr; - vec3_t v_forward, v_right; - float left, center, right; - vec3_t left_target, right_target; + vec3_t v; + edict_t *tempgoal; + edict_t *save; + qboolean new; + edict_t *marker; + float d1, d2; + trace_t tr; + vec3_t v_forward, v_right; + float left, center, right; + vec3_t left_target, right_target; - // if we're going to a combat point, just proceed + /* if we're going to a combat point, just proceed */ if (self->monsterinfo.aiflags & AI_COMBAT_POINT) { - M_MoveToGoal (self, dist); + M_MoveToGoal(self, dist); return; } if (self->monsterinfo.aiflags & AI_SOUND_TARGET) { - VectorSubtract (self->s.origin, self->enemy->s.origin, v); + VectorSubtract(self->s.origin, self->enemy->s.origin, v); + if (VectorLength(v) < 64) { - self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND); - self->monsterinfo.stand (self); + self->monsterinfo.aiflags |= + (AI_STAND_GROUND | AI_TEMP_STAND_GROUND); + self->monsterinfo.stand(self); return; } - M_MoveToGoal (self, dist); + M_MoveToGoal(self, dist); - if (!FindTarget (self)) + if (!FindTarget(self)) + { return; + } } - if (ai_checkattack (self, dist)) + if (ai_checkattack(self, dist)) + { return; + } if (self->monsterinfo.attack_state == AS_SLIDING) { - ai_run_slide (self, dist); + ai_run_slide(self, dist); return; } if (enemy_vis) { - M_MoveToGoal (self, dist); + M_MoveToGoal(self, dist); self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; - VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting); + VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); self->monsterinfo.trail_time = level.time; return; } - // coop will change to another enemy if visible - if (coop->value) - { // FIXME: insane guys get mad with this, which causes crashes! - if (FindTarget (self)) - return; - } - - if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20))) + if ((self->monsterinfo.search_time) && + (level.time > (self->monsterinfo.search_time + 20))) { - M_MoveToGoal (self, dist); + M_MoveToGoal(self, dist); self->monsterinfo.search_time = 0; return; } @@ -981,7 +1098,7 @@ void ai_run (edict_t *self, float dist) if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT)) { - // just lost sight of the player, decide where to go first + /* just lost sight of the player, decide where to go first */ self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN); self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP); new = true; @@ -991,104 +1108,123 @@ void ai_run (edict_t *self, float dist) { self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT; - // give ourself more time since we got this far + /* give ourself more time since we got this far */ self->monsterinfo.search_time = level.time + 5; if (self->monsterinfo.aiflags & AI_PURSUE_TEMP) { self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP; marker = NULL; - VectorCopy (self->monsterinfo.saved_goal, self->monsterinfo.last_sighting); + VectorCopy(self->monsterinfo.saved_goal, + self->monsterinfo.last_sighting); new = true; } else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN) { self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN; - marker = PlayerTrail_PickFirst (self); + marker = PlayerTrail_PickFirst(self); } else { - marker = PlayerTrail_PickNext (self); + marker = PlayerTrail_PickNext(self); } if (marker) { - VectorCopy (marker->s.origin, self->monsterinfo.last_sighting); + VectorCopy(marker->s.origin, self->monsterinfo.last_sighting); self->monsterinfo.trail_time = marker->timestamp; self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW]; new = true; } } - VectorSubtract (self->s.origin, self->monsterinfo.last_sighting, v); + VectorSubtract(self->s.origin, self->monsterinfo.last_sighting, v); d1 = VectorLength(v); + if (d1 <= dist) { self->monsterinfo.aiflags |= AI_PURSUE_NEXT; dist = d1; } - VectorCopy (self->monsterinfo.last_sighting, self->goalentity->s.origin); + VectorCopy(self->monsterinfo.last_sighting, self->goalentity->s.origin); if (new) { - tr = gi.trace(self->s.origin, self->mins, self->maxs, self->monsterinfo.last_sighting, self, MASK_PLAYERSOLID); + tr = gi.trace(self->s.origin, self->mins, self->maxs, + self->monsterinfo.last_sighting, self, + MASK_PLAYERSOLID); + if (tr.fraction < 1) { - VectorSubtract (self->goalentity->s.origin, self->s.origin, v); + VectorSubtract(self->goalentity->s.origin, self->s.origin, v); d1 = VectorLength(v); center = tr.fraction; - d2 = d1 * ((center+1)/2); + d2 = d1 * ((center + 1) / 2); self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); AngleVectors(self->s.angles, v_forward, v_right, NULL); VectorSet(v, d2, -16, 0); - G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target); - tr = gi.trace(self->s.origin, self->mins, self->maxs, left_target, self, MASK_PLAYERSOLID); + G_ProjectSource(self->s.origin, v, v_forward, v_right, left_target); + tr = gi.trace(self->s.origin, self->mins, self->maxs, left_target, + self, MASK_PLAYERSOLID); left = tr.fraction; VectorSet(v, d2, 16, 0); - G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target); - tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, self, MASK_PLAYERSOLID); + G_ProjectSource(self->s.origin, v, v_forward, v_right, right_target); + tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, + self, MASK_PLAYERSOLID); right = tr.fraction; - center = (d1*center)/d2; - if (left >= center && left > right) + center = (d1 * center) / d2; + + if ((left >= center) && (left > right)) { if (left < 1) { VectorSet(v, d2 * left * 0.5, -16, 0); - G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target); + G_ProjectSource(self->s.origin, + v, + v_forward, + v_right, + left_target); } - VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal); + + VectorCopy(self->monsterinfo.last_sighting, + self->monsterinfo.saved_goal); self->monsterinfo.aiflags |= AI_PURSUE_TEMP; - VectorCopy (left_target, self->goalentity->s.origin); - VectorCopy (left_target, self->monsterinfo.last_sighting); - VectorSubtract (self->goalentity->s.origin, self->s.origin, v); + VectorCopy(left_target, self->goalentity->s.origin); + VectorCopy(left_target, self->monsterinfo.last_sighting); + VectorSubtract(self->goalentity->s.origin, self->s.origin, v); self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); } - else if (right >= center && right > left) + else if ((right >= center) && (right > left)) { if (right < 1) { VectorSet(v, d2 * right * 0.5, 16, 0); - G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target); + G_ProjectSource(self->s.origin, v, v_forward, + v_right, right_target); } - VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal); + + VectorCopy(self->monsterinfo.last_sighting, + self->monsterinfo.saved_goal); self->monsterinfo.aiflags |= AI_PURSUE_TEMP; - VectorCopy (right_target, self->goalentity->s.origin); - VectorCopy (right_target, self->monsterinfo.last_sighting); - VectorSubtract (self->goalentity->s.origin, self->s.origin, v); + VectorCopy(right_target, self->goalentity->s.origin); + VectorCopy(right_target, self->monsterinfo.last_sighting); + VectorSubtract(self->goalentity->s.origin, self->s.origin, v); self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); } } } - M_MoveToGoal (self, dist); + M_MoveToGoal(self, dist); G_FreeEdict(tempgoal); if (self) + { self->goalentity = save; + } }