269 lines
6.2 KiB
C++
269 lines
6.2 KiB
C++
|
// These utilities are meant for strictly non-player, non-team NPCs.
|
||
|
// These functions are in their own file because they are only intended
|
||
|
// for use with NPCs who's logic has been overriden from the original
|
||
|
// AI code, and who's code resides in files with the AI_ prefix.
|
||
|
|
||
|
#include "b_local.h"
|
||
|
#include "g_nav.h"
|
||
|
|
||
|
#define MAX_RADIUS_ENTS 128
|
||
|
#define DEFAULT_RADIUS 45
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
AI_GetGroupSize
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
int AI_GetGroupSize( vec3_t origin, int radius, team_t playerTeam, gentity_t *avoid )
|
||
|
{
|
||
|
gentity_t *radiusEnts[ MAX_RADIUS_ENTS ];
|
||
|
vec3_t mins, maxs;
|
||
|
int numEnts, realCount = 0;
|
||
|
|
||
|
//Setup the bbox to search in
|
||
|
for ( int i = 0; i < 3; i++ )
|
||
|
{
|
||
|
mins[i] = origin[i] - radius;
|
||
|
maxs[i] = origin[i] + radius;
|
||
|
}
|
||
|
|
||
|
//Get the number of entities in a given space
|
||
|
numEnts = gi.EntitiesInBox( mins, maxs, radiusEnts, MAX_RADIUS_ENTS );
|
||
|
|
||
|
//Cull this list
|
||
|
for ( int j = 0; j < numEnts; j++ )
|
||
|
{
|
||
|
//Validate clients
|
||
|
if ( radiusEnts[ j ]->client == NULL )
|
||
|
continue;
|
||
|
|
||
|
//Skip the requested avoid ent if present
|
||
|
if ( ( avoid != NULL ) && ( radiusEnts[ j ] == avoid ) )
|
||
|
continue;
|
||
|
|
||
|
//Must be on the same team
|
||
|
if ( radiusEnts[ j ]->client->playerTeam != playerTeam )
|
||
|
continue;
|
||
|
|
||
|
//Must be alive
|
||
|
if ( radiusEnts[ j ]->health <= 0 )
|
||
|
continue;
|
||
|
|
||
|
realCount++;
|
||
|
}
|
||
|
|
||
|
return realCount;
|
||
|
}
|
||
|
|
||
|
//Overload
|
||
|
|
||
|
int AI_GetGroupSize( gentity_t *ent, int radius )
|
||
|
{
|
||
|
if ( ( ent == NULL ) || ( ent->client == NULL ) )
|
||
|
return -1;
|
||
|
|
||
|
return AI_GetGroupSize( ent->currentOrigin, radius, ent->client->playerTeam, ent );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
AI_GetGroup
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
//NOTENOTE: I hate duplicating code, but this is so specific...
|
||
|
|
||
|
//FIXME: Use with caution, these are too special-cased at the moment
|
||
|
|
||
|
void AI_GetGroup( AIGroupInfo_t &group, vec3_t origin, vec3_t angles, int cone, int radius, team_t playerTeam, gentity_t *avoid, gentity_t *enemy )
|
||
|
{
|
||
|
gentity_t *radiusEnts[ MAX_RADIUS_ENTS ];
|
||
|
vec3_t mins, maxs;
|
||
|
int numEnts;
|
||
|
|
||
|
memset( &group, 0, sizeof( group ) );
|
||
|
|
||
|
//Setup the bbox to search in
|
||
|
for ( int i = 0; i < 3; i++ )
|
||
|
{
|
||
|
mins[i] = origin[i] - radius;
|
||
|
maxs[i] = origin[i] + radius;
|
||
|
}
|
||
|
|
||
|
//Get the number of entities in a given space
|
||
|
numEnts = gi.EntitiesInBox( mins, maxs, radiusEnts, MAX_RADIUS_ENTS );
|
||
|
|
||
|
//Cull this list
|
||
|
for ( int j = 0; j < numEnts; j++ )
|
||
|
{
|
||
|
//Validate clients
|
||
|
if ( radiusEnts[ j ]->client == NULL )
|
||
|
continue;
|
||
|
|
||
|
//Skip the requested avoid ent if present
|
||
|
if ( ( avoid != NULL ) && ( radiusEnts[ j ] == avoid ) )
|
||
|
continue;
|
||
|
|
||
|
//Must be on the same team
|
||
|
if ( radiusEnts[ j ]->client->playerTeam != playerTeam )
|
||
|
continue;
|
||
|
|
||
|
//Must be alive
|
||
|
if ( radiusEnts[ j ]->health <= 0 )
|
||
|
continue;
|
||
|
|
||
|
group.numGroup++;
|
||
|
|
||
|
//Check for in FOV
|
||
|
if ( InFOV( radiusEnts[ j ]->currentOrigin, origin, angles, cone, cone ) )
|
||
|
{
|
||
|
//Check for a supplied enemy
|
||
|
if ( enemy != NULL )
|
||
|
{
|
||
|
//If the enemy is closer to use than the teammate, then disregard them
|
||
|
if ( DistanceSquared( enemy->currentOrigin, origin ) < DistanceSquared( radiusEnts[ j ]->currentOrigin, origin ) )
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//Another one found
|
||
|
group.numFront++;
|
||
|
}
|
||
|
|
||
|
group.numState[ radiusEnts[ j ]->NPC->squadState ]++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Overload
|
||
|
|
||
|
void AI_GetGroup( AIGroupInfo_t &group, gentity_t *ent, int radius )
|
||
|
{
|
||
|
if ( ent->client == NULL )
|
||
|
return;
|
||
|
|
||
|
vec3_t temp, angles;
|
||
|
|
||
|
//FIXME: This is specialized code.. move?
|
||
|
if ( ent->enemy )
|
||
|
{
|
||
|
VectorSubtract( ent->enemy->currentOrigin, ent->currentOrigin, temp );
|
||
|
VectorNormalize( temp ); //FIXME: Needed?
|
||
|
vectoangles( temp, angles );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VectorCopy( ent->currentAngles, angles );
|
||
|
}
|
||
|
|
||
|
AI_GetGroup( group, ent->currentOrigin, ent->currentAngles, DEFAULT_RADIUS, radius, ent->client->playerTeam, ent, ent->enemy );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
AI_CheckEnemyCollision
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
qboolean AI_CheckEnemyCollision( gentity_t *ent, qboolean takeEnemy )
|
||
|
{
|
||
|
if ( ent == NULL )
|
||
|
return qfalse;
|
||
|
|
||
|
if ( ent->svFlags & SVF_LOCKEDENEMY )
|
||
|
return qfalse;
|
||
|
|
||
|
navInfo_t info;
|
||
|
|
||
|
NAV_GetLastMove( info );
|
||
|
|
||
|
//See if we've hit something
|
||
|
if ( ( info.blocker ) && ( info.blocker != ent->enemy ) )
|
||
|
{
|
||
|
if ( ( info.blocker->client ) && ( info.blocker->client->playerTeam == ent->client->enemyTeam ) )
|
||
|
{
|
||
|
if ( takeEnemy )
|
||
|
G_SetEnemy( ent, info.blocker );
|
||
|
|
||
|
return qtrue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return qfalse;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
AI_DistributeAttack
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
#define MAX_RADIUS_ENTS 128
|
||
|
|
||
|
gentity_t *AI_DistributeAttack( gentity_t *attacker, gentity_t *enemy, team_t team, int threshold )
|
||
|
{
|
||
|
//Don't take new targets
|
||
|
if ( NPC->svFlags & SVF_LOCKEDENEMY )
|
||
|
return enemy;
|
||
|
|
||
|
int numSurrounding = AI_GetGroupSize( enemy->currentOrigin, 48, team, attacker );
|
||
|
|
||
|
//First, see if we should look for the player
|
||
|
if ( enemy != &g_entities[0] )
|
||
|
{
|
||
|
int aroundPlayer = AI_GetGroupSize( g_entities[0].currentOrigin, 48, team, attacker );
|
||
|
|
||
|
//See if we're above our threshold
|
||
|
if ( aroundPlayer < threshold )
|
||
|
{
|
||
|
return &g_entities[0];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//See if our current enemy is still ok
|
||
|
if ( numSurrounding < threshold )
|
||
|
return enemy;
|
||
|
|
||
|
//Otherwise we need to take a new enemy if possible
|
||
|
vec3_t mins, maxs;
|
||
|
|
||
|
//Setup the bbox to search in
|
||
|
for ( int i = 0; i < 3; i++ )
|
||
|
{
|
||
|
mins[i] = enemy->currentOrigin[i] - 512;
|
||
|
maxs[i] = enemy->currentOrigin[i] + 512;
|
||
|
}
|
||
|
|
||
|
//Get the number of entities in a given space
|
||
|
gentity_t *radiusEnts[ MAX_RADIUS_ENTS ];
|
||
|
|
||
|
int numEnts = gi.EntitiesInBox( mins, maxs, radiusEnts, MAX_RADIUS_ENTS );
|
||
|
|
||
|
//Cull this list
|
||
|
for ( int j = 0; j < numEnts; j++ )
|
||
|
{
|
||
|
//Validate clients
|
||
|
if ( radiusEnts[ j ]->client == NULL )
|
||
|
continue;
|
||
|
|
||
|
//Skip the requested avoid ent if present
|
||
|
if ( ( radiusEnts[ j ] == enemy ) )
|
||
|
continue;
|
||
|
|
||
|
//Must be on the same team
|
||
|
if ( radiusEnts[ j ]->client->playerTeam != enemy->client->playerTeam )
|
||
|
continue;
|
||
|
|
||
|
//Must be alive
|
||
|
if ( radiusEnts[ j ]->health <= 0 )
|
||
|
continue;
|
||
|
|
||
|
//Must not be overwhelmed
|
||
|
if ( AI_GetGroupSize( radiusEnts[j]->currentOrigin, 48, team, attacker ) > threshold )
|
||
|
continue;
|
||
|
|
||
|
return radiusEnts[j];
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|