📄 g_ai.c
字号:
// 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;
}
//=============================================================================
/*
============
FacingIdeal
============
*/
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;
}
/*
=============
ai_run_melee
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))
{
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)
{
self->ideal_yaw = enemy_yaw;
M_ChangeYaw (self);
if (FacingIdeal(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)
{
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);
}
/*
=============
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)
{
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)
{
if ((level.time - self->enemy->teleport_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 = 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;
// FIXME: look all around for other targets
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 = 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);
}
// look for other coop players here
// if (coop && self->monsterinfo.search_time < level.time)
// {
// if (FindTarget (self))
// return true;
// }
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);
// JDC self->ideal_yaw = enemy_yaw;
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);
}
/*
=============
ai_run
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, dist))
return;
if (self->monsterinfo.attack_state == AS_SLIDING)
{
ai_run_slide (self, dist);
return;
}
if (enemy_vis)
{
// if (self.aiflags & AI_LOST_SIGHT)
// dprint("regained sight\n");
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;
}
// 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)))
{
M_MoveToGoal (self, dist);
self->monsterinfo.search_time = 0;
// dprint("search timeout\n");
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
// dprint("lost sight of player, last seen at "); dprint(vtos(self.last_sighting)); dprint("\n");
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;
// dprint("reached current goal: "); dprint(vtos(self.origin)); dprint(" "); dprint(vtos(self.last_sighting)); dprint(" "); dprint(ftos(vlen(self.origin - self.last_sighting))); dprint("\n");
// give ourself more time since we got this far
self->monsterinfo.search_time = level.time + 5;
if (self->monsterinfo.aiflags & AI_PURSUE_TEMP)
{
// dprint("was temp goal; retrying original\n");
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];
// dprint("heading is "); dprint(ftos(self.ideal_yaw)); dprint("\n");
// debug_drawline(self.origin, self.last_sighting, 52);
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)
{
// gi.dprintf("checking for course correction\n");
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);
// gi.dprintf("incomplete path, go part way and adjust again\n");
}
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);
// gi.dprintf("adjusted left\n");
// debug_drawline(self.origin, self.last_sighting, 152);
}
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);
// gi.dprintf("incomplete path, go part way and adjust again\n");
}
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);
// gi.dprintf("adjusted right\n");
// debug_drawline(self.origin, self.last_sighting, 152);
}
}
// else gi.dprintf("course was fine\n");
}
M_MoveToGoal (self, dist);
G_FreeEdict(tempgoal);
if (self)
self->goalentity = save;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -