stvoy-sp-sdk/game/AI_Utils.cpp

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;
}