quake2-zaero/z_ai.c

492 lines
9.8 KiB
C

#include "g_local.h"
#define Z_RADUISLISTSIZE 2000
void ai_run_melee(edict_t *self);
qboolean FindTarget (edict_t *self);
qboolean SV_StepDirection (edict_t *ent, float yaw, float dist);
void SV_NewChaseDir (edict_t *actor, vec3_t eOrigin, float dist);
#if 0
void z_aiMoveTo(edict_t *self, float dist)
{
// sanity check
if (!(self->monsterinfo.scriptState & MSS_AIMOVETO))
return;
#if 0
if (!SV_StepDirection (self, self->ideal_yaw, dist))
{
SV_NewChaseDir (self, self->monsterinfo.aiMoveTo, dist);
}
#endif
}
#endif
/*
=============
zSchoolAllVisiable
Creates a list of all entities in the raduis of Z_RADUISLISTSIZE
==============
*/
void zCreateRaduisList(edict_t *self)
{
edict_t *head, *list;
vec3_t vec;
if(self->zRaduisList)
{ // already created for this think, don't bother doing it again...
return;
}
head = NULL;
list = self;
while(1)
{
head = findradius(head, self->s.origin, Z_RADUISLISTSIZE);
if(head == NULL)
break;
if(head != self)
{
list->zRaduisList = head;
VectorSubtract(self->s.origin, head->s.origin, vec);
head->zDistance = VectorLength(vec);
list = head;
}
}
list->zRaduisList = NULL;
};
/*
=============
zSchoolAllVisiable
Create list of monsters of the same schooling type that are ahead of you.
==============
*/
int zSchoolAllVisiable(edict_t *self)
{
int max;
edict_t *head, *list;
max = 0;
zCreateRaduisList(self);
head = self->zRaduisList;
list = self;
while (head)
{
if(strcmp(head->classname, self->classname) == 0 && (self->monsterinfo.aiflags & AI_SCHOOLING) && (head->health > 0) &&
(head->zDistance <= self->monsterinfo.zSchoolSightRadius) && (visible(self, head)) && (infront(self, head)))
{
list->zSchoolChain = head;
list = head;
max++;
}
head = head->zRaduisList;
}
list->zSchoolChain = NULL;
return max;
}
/*
=============
zFindRoamYaw
Check direction moving in does not hit a wall... if it does change direction.
==============
*/
int zFindRoamYaw(edict_t *self, float distcheck)
{
vec3_t forward, end, angles;
trace_t tr;
float current = anglemod(self->s.angles[YAW]);
if(current <= self->ideal_yaw - 1 || current > self->ideal_yaw + 1)
{
if(fabs(current - self->ideal_yaw) <= 359.0)
{
return 0;
}
}
AngleVectors (self->s.angles, forward, NULL, NULL);
VectorMA (self->s.origin, distcheck, forward, end);
tr = gi.trace (self->s.origin, self->mins, self->maxs, end, self, MASK_SOLID);
if (tr.fraction < 1.0)
{
if(random() > 0.75)
{
self->ideal_yaw = vectoyaw(forward);
self->ideal_yaw = self->ideal_yaw + 180;
}
else
{
float dir = random() > 0.5 ? -45 : 45;
float maxtrys = 100;
VectorCopy(self->s.angles, angles);
while(tr.fraction < 1.0 && maxtrys)
{
// blocked, change ideal yaw...
self->ideal_yaw = vectoyaw(forward);
self->ideal_yaw = self->ideal_yaw + (random() * dir);
// self->ideal_yaw = self->ideal_yaw + (-45 + (random() * 90));
angles[YAW] = anglemod (self->ideal_yaw);
AngleVectors (angles, forward, NULL, NULL);
VectorMA (self->s.origin, distcheck, forward, end);
tr = gi.trace (self->s.origin, self->mins, self->maxs, end, self, MASK_SOLID);
maxtrys--;
}
}
return 1;
}
return 0;
};
/*
=============
zSchoolMonsters
Roaming schooling ai.
==============
*/
int zSchoolMonsters(edict_t *self, float dist, int runStyle, float *currentSpeed)
{
int maxInsight;
int newRunStyle;
maxInsight = zSchoolAllVisiable(self);
// If you're not out in front
if(maxInsight > 0)
{
float totalSpeed;
float totalBearing;
float distanceToNearest, distanceToLeader, dist;
edict_t *nearestEntity, *list;
vec3_t vec;
totalSpeed = 0;
totalBearing = 0;
distanceToNearest = 10000;
distanceToLeader = 0;
list = self->zSchoolChain;
while(list)
{
// Gather data on those you see
totalSpeed += list->speed;
totalBearing += anglemod(list->s.angles[YAW]);
VectorSubtract(self->s.origin, list->s.origin, vec);
dist = VectorLength(vec);
if(dist < distanceToNearest)
{
distanceToNearest = dist;
nearestEntity = list;
}
if(dist > distanceToLeader)
{
distanceToLeader = dist;
}
list = list->zSchoolChain;
}
// Rule 1) Match average speed of those in the list
self->speed = (totalSpeed / maxInsight) * 1.5;
// Rule 2) Move towards the perceived center of gravity of the herd
self->ideal_yaw = totalBearing / maxInsight;
// check if hitting something
if(!zFindRoamYaw(self, 10))
{
// Rule 3) Maintain a minimum distance from those around you
if(distanceToNearest <= self->monsterinfo.zSchoolMinimumDistance)
{
self->ideal_yaw = nearestEntity->s.angles[YAW];
self->speed = nearestEntity->speed;
}
}
}
else
{ //You are in front, so slow down a bit
edict_t *head;
self->speed = (self->speed * self->monsterinfo.zSchoolDecayRate);
// check direction
zFindRoamYaw(self, 100);
// change directions of the monsters following you...
zCreateRaduisList(self);
head = self->zRaduisList;
while (head)
{
if(strcmp(head->classname, self->classname) == 0 && (head->health > 0) &&
(head->zDistance <= self->monsterinfo.zSchoolSightRadius) && (visible(self, head)))
{
head->ideal_yaw = self->ideal_yaw + (-20 + (random() * 40));
}
head = head->zRaduisList;
}
}
// if(self.rm_schoolFlags & 1)
// { // check to see is I keep away from "other" entities...
// zSchoolCheckForOtherEntities(checkOtherRaduis);
// }
if(self->speed > self->monsterinfo.zSchoolMaxSpeed)
{
self->speed = self->monsterinfo.zSchoolMaxSpeed;
}
if(self->speed < self->monsterinfo.zSchoolMinSpeed)
{
self->speed = self->monsterinfo.zSchoolMinSpeed;
}
if(self->speed <= self->monsterinfo.zSpeedStandMax)
{
newRunStyle = 0;
if(newRunStyle != runStyle)
{
*currentSpeed = 1;
}
else
{
*currentSpeed = (self->speed - self->monsterinfo.zSchoolMinSpeed) + 1;
}
}
else if(self->speed <= self->monsterinfo.zSpeedWalkMax)
{
newRunStyle = 1;
if(newRunStyle != runStyle)
{
*currentSpeed = 1;
}
else
{
*currentSpeed = (self->speed - self->monsterinfo.zSpeedStandMax) + 1;
}
}
else
{
newRunStyle = 2;
if(newRunStyle != runStyle)
{
*currentSpeed = 1;
}
else
{
*currentSpeed = (self->speed - self->monsterinfo.zSpeedWalkMax) + 1;
}
}
return newRunStyle;
}
/*
=============
ai_schoolStand
Used for standing around and looking for players / schooling monsters of the same type.
Distance is for slight position adjustments needed by the animations
==============
*/
void ai_schoolStand (edict_t *self, float dist)
{
float speed;
if(!(self->monsterinfo.aiflags & AI_SCHOOLING))
{
ai_stand(self, dist);
return;
}
// init school var's for this frame
self->zRaduisList = NULL;
if(self->enemy || FindTarget(self))
{
ai_stand(self, dist);
return;
}
else
{
// run schooling routines
switch(zSchoolMonsters(self, dist, 0, &speed))
{
case 1:
self->monsterinfo.walk (self);
break;
case 2:
self->monsterinfo.run (self);
break;
}
}
// do the normal stand stuff
if (dist)
M_walkmove (self, self->ideal_yaw, dist);
// M_walkmove (self, self->ideal_yaw, dist * speed);
}
/*
=============
ai_schoolRun
The monster has an enemy it is trying to kill
=============
*/
void ai_schoolRun (edict_t *self, float dist)
{
float speed;
if(!(self->monsterinfo.aiflags & AI_SCHOOLING))
{
ai_run(self, dist);
return;
}
// init school var's for this frame
self->zRaduisList = NULL;
if(self->enemy || FindTarget(self))
{
ai_run(self, dist);
return;
}
else
{
// run schooling routines
switch(zSchoolMonsters(self, dist, 2, &speed))
{
case 0:
self->monsterinfo.stand (self);
break;
case 1:
self->monsterinfo.walk (self);
break;
}
}
// do the normal run stuff
SV_StepDirection (self, self->ideal_yaw, dist);
// SV_StepDirection (self, self->ideal_yaw, dist * speed);
}
/*
=============
ai_schoolWalk
The monster is walking it's beat
=============
*/
void ai_schoolWalk (edict_t *self, float dist)
{
float speed;
if(!(self->monsterinfo.aiflags & AI_SCHOOLING))
{
ai_walk(self, dist);
return;
}
// init school var's for this frame
self->zRaduisList = NULL;
if(self->enemy || FindTarget(self))
{
ai_walk(self, dist);
return;
}
else
{
// run schooling routines
switch(zSchoolMonsters(self, dist, 1, &speed))
{
case 0:
self->monsterinfo.stand (self);
break;
case 2:
self->monsterinfo.run (self);
break;
}
}
// do the normal walk stuff
SV_StepDirection (self, self->ideal_yaw, dist);
// SV_StepDirection (self, self->ideal_yaw, dist * speed);
}
/*
=============
ai_schoolCharge
Turns towards target and advances
Use this call with a distnace of 0 to replace ai_face
==============
*/
void ai_schoolCharge (edict_t *self, float dist)
{
/*
if(!(self->monsterinfo.aiflags & AI_SCHOOLING))
{
ai_charge(self, dist);
return;
}
*/
ai_charge(self, dist);
}