492 lines
9.8 KiB
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);
|
||
|
}
|
||
|
|
||
|
|
||
|
|