diff --git a/Makefile b/Makefile index aa4ebaf6..05585682 100644 --- a/Makefile +++ b/Makefile @@ -1480,7 +1480,7 @@ CTF_OBJS_ = \ src/common/shared/flash.o \ src/common/shared/rand.o \ src/common/shared/shared.o \ - src/ctf/g_ai.o \ + src/game/g_ai.o \ src/game/g_chase.o \ src/game/g_cmds.o \ src/ctf/g_combat.o \ diff --git a/src/ctf/g_ai.c b/src/ctf/g_ai.c deleted file mode 100644 index 1bc87ec8..00000000 --- a/src/ctf/g_ai.c +++ /dev/null @@ -1,1236 +0,0 @@ -/* - * 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 ai_checkattack(edict_t *self); - -qboolean enemy_vis; -qboolean enemy_infront; -int enemy_range; -float enemy_yaw; - -/* ============================================================================ */ - -/* - * 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; - - 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)) - { - level.sight_client = ent; - return; /* got one */ - } - - if (check == start) - { - level.sight_client = NULL; - return; /* nobody to see */ - } - } -} - -/* ============================================================================ */ - -/* - * 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); -} - -/* - * 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; - - if (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); - self->ideal_yaw = vectoyaw(v); - - 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); - } - - M_ChangeYaw(self); - ai_checkattack(self); - } - else - { - FindTarget(self); - } - - return; - } - - if (FindTarget(self)) - { - return; - } - - if (level.time > self->monsterinfo.pausetime) - { - self->monsterinfo.walk(self); - return; - } - - 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_time = level.time + 15 + random() * 15; - } - else - { - self->monsterinfo.idle_time = level.time + random() * 15; - } - } -} - -/* - * The monster is walking it's beat - */ -void -ai_walk(edict_t *self, float dist) -{ - M_MoveToGoal(self, dist); - - /* 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.idle_time = level.time + 15 + random() * 15; - } - else - { - self->monsterinfo.idle_time = level.time + random() * 15; - } - } -} - -/* - * 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; - - VectorSubtract(self->enemy->s.origin, self->s.origin, v); - self->ideal_yaw = vectoyaw(v); - M_ChangeYaw(self); - - if (dist) - { - M_walkmove(self, self->s.angles[YAW], 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); - } - - if (FindTarget(self)) - { - return; - } - - 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 - */ - -/* - * 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; - - 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; -} - -/* - * 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; - - VectorCopy(self->s.origin, spot1); - spot1[2] += self->viewheight; - VectorCopy(other->s.origin, spot2); - spot2[2] += other->viewheight; - trace = gi.trace(spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE); - - if (trace.fraction == 1.0) - { - return true; - } - - return false; -} - -/* - * 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; - } - - 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); - - if (dot > 0.3) - { - return true; - } - - return false; -} - -/* ============================================================================ */ - -void -HuntTarget(edict_t *self) -{ - vec3_t vec; - - self->goalentity = self->enemy; - - if (self->monsterinfo.aiflags & AI_STAND_GROUND) - { - self->monsterinfo.stand(self); - } - else - { - self->monsterinfo.run(self); - } - - VectorSubtract(self->enemy->s.origin, self->s.origin, vec); - self->ideal_yaw = vectoyaw(vec); - - /* wait a while before first attack */ - if (!(self->monsterinfo.aiflags & AI_STAND_GROUND)) - { - AttackFinished(self, 1); - } -} - -void -FoundTarget(edict_t *self) -{ - /* let other monsters see this monster for a while */ - if (self->enemy->client) - { - level.sight_entity = self; - level.sight_entity_framenum = level.framenum; - level.sight_entity->light_level = 128; - } - - self->show_hostile = (int)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); - 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); - return; - } - - /* 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! */ - self->movetarget->targetname = NULL; - self->monsterinfo.pausetime = 0; - - /* run for it */ - self->monsterinfo.run(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; - - if (self->monsterinfo.aiflags & AI_GOOD_GUY) - { - return false; - } - - /* 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 */ - heardit = false; - - if ((level.sight_entity_framenum >= (level.framenum - 1)) && - !(self->spawnflags & 1)) - { - client = level.sight_entity; - - if (client->enemy == self->enemy) - { - return false; - } - } - else if (level.sound_entity_framenum >= (level.framenum - 1)) - { - client = level.sound_entity; - heardit = true; - } - else if (!(self->enemy) && - (level.sound2_entity_framenum >= (level.framenum - 1)) && - !(self->spawnflags & 1)) - { - client = level.sound2_entity; - heardit = true; - } - else - { - client = level.sight_client; - - if (!client) - { - return false; /* no clients to get mad at */ - } - } - - /* if the entity went away, forget it */ - if (!client->inuse) - { - return false; - } - - if (client == self->enemy) - { - 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); - - if (r == RANGE_FAR) - { - return false; - } - - /* is client in an spot too dark to be seen? */ - if (client->light_level <= 5) - { - return false; - } - - if (!visible(self, client)) - { - return false; - } - - if (r == RANGE_NEAR) - { - if ((client->show_hostile < (int)level.time) && !infront(self, client)) - { - return false; - } - } - else if (r == RANGE_MID) - { - if (!infront(self, client)) - { - return false; - } - } - - self->enemy = client; - - if (strcmp(self->enemy->classname, "player_noise") != 0) - { - self->monsterinfo.aiflags &= ~AI_SOUND_TARGET; - - if (!self->enemy->client) - { - self->enemy = self->enemy->enemy; - - if (!self->enemy->client) - { - self->enemy = NULL; - return false; - } - } - } - } - else /* heardit */ - { - vec3_t temp; - - if (self->spawnflags & 1) - { - 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); - - 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 */ - if (client->areanum != self->areanum) - { - if (!gi.AreasConnected(self->areanum, client->areanum)) - { - return false; - } - } - - self->ideal_yaw = vectoyaw(temp); - M_ChangeYaw(self); - - /* hunt the sound for a bit; hopefully find the real player */ - self->monsterinfo.aiflags |= AI_SOUND_TARGET; - self->enemy = client; - } - - /* got one */ - FoundTarget(self); - - if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && - (self->monsterinfo.sight)) - { - self->monsterinfo.sight(self, self->enemy); - } - - return true; -} - -/* ============================================================================= */ - -qboolean -FacingIdeal(edict_t *self) -{ - float delta; - - delta = anglemod(self->s.angles[YAW] - self->ideal_yaw); - - if ((delta > 45) && (delta < 315)) - { - return false; - } - - return true; -} - -/* ============================================================================= */ - -qboolean -M_CheckAttack(edict_t *self) -{ - 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); - spot1[2] += self->viewheight; - 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); - - /* do we have a clear shot? */ - if (tr.ent != self->enemy) - { - return false; - } - } - - /* melee attack */ - if (enemy_range == RANGE_MELEE) - { - /* 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 */ - 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) - { - chance = 0.4; - } - else if (enemy_range == RANGE_MELEE) - { - chance = 0.2; - } - else if (enemy_range == RANGE_NEAR) - { - chance = 0.1; - } - else if (enemy_range == RANGE_MID) - { - chance = 0.02; - } - else - { - return false; - } - - if (skill->value == 0) - { - chance *= 0.5; - } - else if (skill->value >= 2) - { - chance *= 2; - } - - if (random() < chance) - { - self->monsterinfo.attack_state = AS_MISSILE; - 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; -} - -/* - * 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); - - if (FacingIdeal(self)) - { - if (self->monsterinfo.melee) - { - self->monsterinfo.melee(self); - self->monsterinfo.attack_state = AS_STRAIGHT; - } - } -} - -/* - * 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); - - if (FacingIdeal(self)) - { - if (self->monsterinfo.attack) - { - self->monsterinfo.attack(self); - self->monsterinfo.attack_state = AS_STRAIGHT; - } - } -} - -/* - * Strafe sideways, but stay at - * aproximately the same range - */ -void -ai_run_slide(edict_t *self, float distance) -{ - float ofs; - - self->ideal_yaw = enemy_yaw; - M_ChangeYaw(self); - - if (self->monsterinfo.lefty) - { - ofs = 90; - } - else - { - ofs = -90; - } - - 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); -} - -/* - * Decides if we're going to attack or - * do something else * used by ai_run - * and ai_stand - */ -qboolean -ai_checkattack(edict_t *self) -{ - vec3_t temp; - qboolean hesDeadJim; - - /* 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) && !visible(self, self->goalentity)) - { - if ((level.time - self->enemy->last_sound_time) > 5.0) - { - 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); - } - } - else - { - self->show_hostile = (int)level.time + 1; - return false; - } - } - } - - enemy_vis = false; - - /* see if the enemy is dead */ - hesDeadJim = false; - - if ((!self->enemy) || (!self->enemy->inuse)) - { - hesDeadJim = true; - } - else if (self->monsterinfo.aiflags & AI_MEDIC) - { - if (self->enemy->health > 0) - { - hesDeadJim = true; - self->monsterinfo.aiflags &= ~AI_MEDIC; - } - } - else - { - 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; - - if (self->oldenemy && (self->oldenemy->health > 0)) - { - self->enemy = self->oldenemy; - self->oldenemy = NULL; - HuntTarget(self); - } - else - { - if (self->movetarget) - { - self->goalentity = self->movetarget; - 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 */ - self->monsterinfo.pausetime = level.time + 100000000; - self->monsterinfo.stand(self); - } - - return true; - } - } - - self->show_hostile = (int)level.time + 1; /* wake up other monsters */ - - /* 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); - } - - enemy_infront = infront(self, self->enemy); - enemy_range = range(self, self->enemy); - VectorSubtract(self->enemy->s.origin, self->s.origin, temp); - enemy_yaw = vectoyaw(temp); - - if (self->monsterinfo.attack_state == AS_MISSILE) - { - ai_run_missile(self); - return true; - } - - if (self->monsterinfo.attack_state == AS_MELEE) - { - ai_run_melee(self); - return true; - } - - /* if enemy is not currently visible, - we will never attack */ - if (!enemy_vis) - { - return false; - } - - return self->monsterinfo.checkattack(self); -} - -/* - * 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; - - /* if we're going to a combat point, just proceed */ - if (self->monsterinfo.aiflags & AI_COMBAT_POINT) - { - M_MoveToGoal(self, dist); - return; - } - - if (self->monsterinfo.aiflags & AI_SOUND_TARGET) - { - 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); - return; - } - - M_MoveToGoal(self, dist); - - if (!FindTarget(self)) - { - return; - } - } - - if (ai_checkattack(self)) - { - return; - } - - if (self->monsterinfo.attack_state == AS_SLIDING) - { - ai_run_slide(self, dist); - return; - } - - if (enemy_vis) - { - M_MoveToGoal(self, dist); - self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; - VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); - self->monsterinfo.trail_time = level.time; - return; - } - - if ((self->monsterinfo.search_time) && - (level.time > (self->monsterinfo.search_time + 20))) - { - M_MoveToGoal(self, dist); - self->monsterinfo.search_time = 0; - return; - } - - save = self->goalentity; - tempgoal = G_Spawn(); - self->goalentity = tempgoal; - - new = false; - - if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT)) - { - /* 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; - } - - if (self->monsterinfo.aiflags & AI_PURSUE_NEXT) - { - self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT; - - /* 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); - new = true; - } - else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN) - { - self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN; - marker = PlayerTrail_PickFirst(self); - } - else - { - marker = PlayerTrail_PickNext(self); - } - - if (marker) - { - 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); - d1 = VectorLength(v); - - if (d1 <= dist) - { - self->monsterinfo.aiflags |= AI_PURSUE_NEXT; - dist = d1; - } - - 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); - - if (tr.fraction < 1) - { - VectorSubtract(self->goalentity->s.origin, self->s.origin, v); - d1 = VectorLength(v); - center = tr.fraction; - 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); - 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); - right = tr.fraction; - - 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); - } - - 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); - self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); - } - 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); - } - - 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); - self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); - } - } - } - - M_MoveToGoal(self, dist); - - G_FreeEdict(tempgoal); - - if (self) - { - self->goalentity = save; - } -} - diff --git a/src/ctf/monster/misc/player.h b/src/ctf/monster/misc/player.h deleted file mode 100644 index 7cba4283..00000000 --- a/src/ctf/monster/misc/player.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 1997-2001 Id Software, Inc. - * Copyright (c) ZeniMax Media 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. - * - * ======================================================================= - * - * Player (the arm and the weapons) animation. - * - * ======================================================================= - */ - -#define FRAME_stand01 0 -#define FRAME_stand02 1 -#define FRAME_stand03 2 -#define FRAME_stand04 3 -#define FRAME_stand05 4 -#define FRAME_stand06 5 -#define FRAME_stand07 6 -#define FRAME_stand08 7 -#define FRAME_stand09 8 -#define FRAME_stand10 9 -#define FRAME_stand11 10 -#define FRAME_stand12 11 -#define FRAME_stand13 12 -#define FRAME_stand14 13 -#define FRAME_stand15 14 -#define FRAME_stand16 15 -#define FRAME_stand17 16 -#define FRAME_stand18 17 -#define FRAME_stand19 18 -#define FRAME_stand20 19 -#define FRAME_stand21 20 -#define FRAME_stand22 21 -#define FRAME_stand23 22 -#define FRAME_stand24 23 -#define FRAME_stand25 24 -#define FRAME_stand26 25 -#define FRAME_stand27 26 -#define FRAME_stand28 27 -#define FRAME_stand29 28 -#define FRAME_stand30 29 -#define FRAME_stand31 30 -#define FRAME_stand32 31 -#define FRAME_stand33 32 -#define FRAME_stand34 33 -#define FRAME_stand35 34 -#define FRAME_stand36 35 -#define FRAME_stand37 36 -#define FRAME_stand38 37 -#define FRAME_stand39 38 -#define FRAME_stand40 39 -#define FRAME_run1 40 -#define FRAME_run2 41 -#define FRAME_run3 42 -#define FRAME_run4 43 -#define FRAME_run5 44 -#define FRAME_run6 45 -#define FRAME_attack1 46 -#define FRAME_attack2 47 -#define FRAME_attack3 48 -#define FRAME_attack4 49 -#define FRAME_attack5 50 -#define FRAME_attack6 51 -#define FRAME_attack7 52 -#define FRAME_attack8 53 -#define FRAME_pain101 54 -#define FRAME_pain102 55 -#define FRAME_pain103 56 -#define FRAME_pain104 57 -#define FRAME_pain201 58 -#define FRAME_pain202 59 -#define FRAME_pain203 60 -#define FRAME_pain204 61 -#define FRAME_pain301 62 -#define FRAME_pain302 63 -#define FRAME_pain303 64 -#define FRAME_pain304 65 -#define FRAME_jump1 66 -#define FRAME_jump2 67 -#define FRAME_jump3 68 -#define FRAME_jump4 69 -#define FRAME_jump5 70 -#define FRAME_jump6 71 -#define FRAME_flip01 72 -#define FRAME_flip02 73 -#define FRAME_flip03 74 -#define FRAME_flip04 75 -#define FRAME_flip05 76 -#define FRAME_flip06 77 -#define FRAME_flip07 78 -#define FRAME_flip08 79 -#define FRAME_flip09 80 -#define FRAME_flip10 81 -#define FRAME_flip11 82 -#define FRAME_flip12 83 -#define FRAME_salute01 84 -#define FRAME_salute02 85 -#define FRAME_salute03 86 -#define FRAME_salute04 87 -#define FRAME_salute05 88 -#define FRAME_salute06 89 -#define FRAME_salute07 90 -#define FRAME_salute08 91 -#define FRAME_salute09 92 -#define FRAME_salute10 93 -#define FRAME_salute11 94 -#define FRAME_taunt01 95 -#define FRAME_taunt02 96 -#define FRAME_taunt03 97 -#define FRAME_taunt04 98 -#define FRAME_taunt05 99 -#define FRAME_taunt06 100 -#define FRAME_taunt07 101 -#define FRAME_taunt08 102 -#define FRAME_taunt09 103 -#define FRAME_taunt10 104 -#define FRAME_taunt11 105 -#define FRAME_taunt12 106 -#define FRAME_taunt13 107 -#define FRAME_taunt14 108 -#define FRAME_taunt15 109 -#define FRAME_taunt16 110 -#define FRAME_taunt17 111 -#define FRAME_wave01 112 -#define FRAME_wave02 113 -#define FRAME_wave03 114 -#define FRAME_wave04 115 -#define FRAME_wave05 116 -#define FRAME_wave06 117 -#define FRAME_wave07 118 -#define FRAME_wave08 119 -#define FRAME_wave09 120 -#define FRAME_wave10 121 -#define FRAME_wave11 122 -#define FRAME_point01 123 -#define FRAME_point02 124 -#define FRAME_point03 125 -#define FRAME_point04 126 -#define FRAME_point05 127 -#define FRAME_point06 128 -#define FRAME_point07 129 -#define FRAME_point08 130 -#define FRAME_point09 131 -#define FRAME_point10 132 -#define FRAME_point11 133 -#define FRAME_point12 134 -#define FRAME_crstnd01 135 -#define FRAME_crstnd02 136 -#define FRAME_crstnd03 137 -#define FRAME_crstnd04 138 -#define FRAME_crstnd05 139 -#define FRAME_crstnd06 140 -#define FRAME_crstnd07 141 -#define FRAME_crstnd08 142 -#define FRAME_crstnd09 143 -#define FRAME_crstnd10 144 -#define FRAME_crstnd11 145 -#define FRAME_crstnd12 146 -#define FRAME_crstnd13 147 -#define FRAME_crstnd14 148 -#define FRAME_crstnd15 149 -#define FRAME_crstnd16 150 -#define FRAME_crstnd17 151 -#define FRAME_crstnd18 152 -#define FRAME_crstnd19 153 -#define FRAME_crwalk1 154 -#define FRAME_crwalk2 155 -#define FRAME_crwalk3 156 -#define FRAME_crwalk4 157 -#define FRAME_crwalk5 158 -#define FRAME_crwalk6 159 -#define FRAME_crattak1 160 -#define FRAME_crattak2 161 -#define FRAME_crattak3 162 -#define FRAME_crattak4 163 -#define FRAME_crattak5 164 -#define FRAME_crattak6 165 -#define FRAME_crattak7 166 -#define FRAME_crattak8 167 -#define FRAME_crattak9 168 -#define FRAME_crpain1 169 -#define FRAME_crpain2 170 -#define FRAME_crpain3 171 -#define FRAME_crpain4 172 -#define FRAME_crdeath1 173 -#define FRAME_crdeath2 174 -#define FRAME_crdeath3 175 -#define FRAME_crdeath4 176 -#define FRAME_crdeath5 177 -#define FRAME_death101 178 -#define FRAME_death102 179 -#define FRAME_death103 180 -#define FRAME_death104 181 -#define FRAME_death105 182 -#define FRAME_death106 183 -#define FRAME_death201 184 -#define FRAME_death202 185 -#define FRAME_death203 186 -#define FRAME_death204 187 -#define FRAME_death205 188 -#define FRAME_death206 189 -#define FRAME_death301 190 -#define FRAME_death302 191 -#define FRAME_death303 192 -#define FRAME_death304 193 -#define FRAME_death305 194 -#define FRAME_death306 195 -#define FRAME_death307 196 -#define FRAME_death308 197 - -#define MODEL_SCALE 1.000000 diff --git a/src/game/g_ai.c b/src/game/g_ai.c index 25b750ab..974c86a0 100644 --- a/src/game/g_ai.c +++ b/src/game/g_ai.c @@ -21,6 +21,8 @@ * ======================================================================= * * 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. * * ======================================================================= */ @@ -111,7 +113,7 @@ ai_move(edict_t *self, float dist) /* * * Used for standing around and looking - * for players Distance is for slight + * for players. Distance is for slight * position adjustments needed by the * animations */ @@ -347,26 +349,30 @@ ai_turn(edict_t *self, float dist) /* ============================================================================ */ /* + * * .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. + * 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 + * 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. + * 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. + * 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 */ @@ -645,6 +651,11 @@ FindTarget(edict_t *self) else { client = level.sight_client; + + if (!client) + { + return false; /* no clients to get mad at */ + } } /* if the entity went away, forget it */ @@ -962,6 +973,10 @@ M_CheckAttack(edict_t *self) { chance = 0.4; } + else if (enemy_range == RANGE_MELEE) + { + chance = 0.2; + } else if (enemy_range == RANGE_NEAR) { chance = 0.1;