sqwarmed/sdk_src/game/server/swarm/asw_ai_senses.cpp

280 lines
8.0 KiB
C++

#include "cbase.h"
#include "asw_ai_senses.h"
#include "iasw_spawnable_npc.h"
#include "ai_basenpc.h"
#include "saverestore_utlvector.h"
#include "asw_shareddefs.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#ifdef DEBUG_SENSES
#define AI_PROFILE_SENSES(tag) AI_PROFILE_SCOPE(tag)
#else
#define AI_PROFILE_SENSES(tag) ((void)0)
#endif
// in asw the drones don't really need to sense for NPCs other than the marines at all, so boost these search times up
const float ASW_AI_STANDARD_NPC_SEARCH_TIME = 1.0; // in HL2 is .25
const float ASW_AI_EFFICIENT_NPC_SEARCH_TIME = 1.0; // in HL2 is .35
const float ASW_AI_HIGH_PRIORITY_SEARCH_TIME = 0.15;
const float ASW_AI_MISC_SEARCH_TIME = 0.45;
// sense a little less frequently than in HL2 since we have so many aliens
const float ASW_AI_STANDARD_MARINE_SEARCH_TIME = .35;
const float ASW_AI_EFFICIENT_MARINE_SEARCH_TIME = .45;
const float ASW_AI_SUPER_EFFICIENT_MARINE_SEARCH_TIME = .55;
const float ASW_AI_MARINE_LOOK_HEIGHT = 128.0f;
#pragma pack(push)
#pragma pack(1)
struct AISightIterVal_t
{
char array;
short iNext;
char SwarmSensedArray;
};
#pragma pack(pop)
bool CASW_BaseAI_Senses::WaitingUntilSeen( CBaseEntity *pSightEnt )
{
if ( GetOuter()->GetSpawnFlags() & SF_NPC_WAIT_TILL_SEEN )
{
// asw, wake up if marines see us (not players)
if ( pSightEnt && pSightEnt->Classify() == CLASS_ASW_MARINE )
{
CBaseCombatCharacter *pBCC = dynamic_cast<CBaseCombatCharacter*>( pSightEnt );
Vector zero = Vector(0,0,0);
// don't link this client in the list if the npc is wait till seen and the player isn't facing the npc
if (// && pPlayer->FVisible( GetOuter() )
pBCC->FInViewCone( GetOuter() )
&& FBoxVisible( pSightEnt, static_cast<CBaseEntity*>(GetOuter()), zero ) )
{
// marine sees us, become normal now.
GetOuter()->RemoveSpawnFlags( SF_NPC_WAIT_TILL_SEEN );
return false;
}
}
return true;
}
return false;
}
//-----------------------------------------------------------------------------
BEGIN_SIMPLE_DATADESC( CASW_Marine_AI_Senses )
DEFINE_FIELD( m_flLookHeight, FIELD_FLOAT ),
END_DATADESC()
CASW_Marine_AI_Senses::CASW_Marine_AI_Senses()
: m_flLookHeight( ASW_AI_MARINE_LOOK_HEIGHT )
{
}
bool CASW_Marine_AI_Senses::IsWithinSenseDistance( const Vector &source, const Vector &dest, float dist )
{
return CASW_BaseAI_Senses::IsWithinSenseDistance( source, dest, dist ) &&
( dest.z - source.z < m_flLookHeight ); // can see infinitely down, but not up
}
//-----------------------------------------------------------------------------
BEGIN_SIMPLE_DATADESC( CASW_AI_Senses )
DEFINE_FIELD( m_SwarmSenseDist, FIELD_FLOAT ),
DEFINE_FIELD( m_LastSwarmSenseDist, FIELD_FLOAT ),
DEFINE_FIELD( m_TimeLastSwarmSense, FIELD_TIME ),
DEFINE_UTLVECTOR(m_SwarmSensedMarines, FIELD_EHANDLE ),
// m_SwarmSenseArrays (not saved, rebuilt)
DEFINE_EMBEDDED( m_SwarmSenseMarinesTimer ),
END_DATADESC()
CASW_AI_Senses::CASW_AI_Senses() :
m_SwarmSenseDist(576),
m_TimeLastSwarmSense(0),
m_LastSwarmSenseDist(0)
{
m_SwarmSenseArrays[0] = &m_SwarmSensedMarines;
}
CASW_AI_Senses::~CASW_AI_Senses()
{
}
void CASW_AI_Senses::PerformSensing()
{
BaseClass::PerformSensing();
// Use our Swarm senses to detect marines through walls
SwarmSense(m_SwarmSenseDist);
}
void CASW_AI_Senses::SwarmSense(int iDistance)
{
IASW_Spawnable_NPC* pAlienOuter = dynamic_cast<IASW_Spawnable_NPC*>(GetOuter());
if (!pAlienOuter)
return;
if ( m_TimeLastSwarmSense != gpGlobals->curtime || m_LastSwarmSenseDist != iDistance )
{
SwarmSenseMarines(iDistance);
m_LastSwarmSenseDist = iDistance;
m_TimeLastSwarmSense = gpGlobals->curtime;
}
pAlienOuter->OnSwarmSensed( iDistance );
}
int CASW_AI_Senses::SwarmSenseMarines(int iDistance)
{
bool bRemoveStaleFromCache = false;
float distSq = ( iDistance * iDistance );
const Vector &origin = GetAbsOrigin();
if ( m_SwarmSenseMarinesTimer.Expired() )
{
AI_PROFILE_SENSES(CASW_AI_Senses_SwarmSenseMarines);
AI_Efficiency_t efficiency = GetOuter()->GetEfficiency();
float fSenseTime = ASW_AI_STANDARD_MARINE_SEARCH_TIME;
if (efficiency == AIE_VERY_EFFICIENT)
fSenseTime = ASW_AI_EFFICIENT_MARINE_SEARCH_TIME;
else if (efficiency == AIE_SUPER_EFFICIENT)
fSenseTime = ASW_AI_SUPER_EFFICIENT_MARINE_SEARCH_TIME;
//m_SwarmSenseMarinesTimer.Reset( ( efficiency < AIE_VERY_EFFICIENT ) ? ASW_AI_STANDARD_NPC_SEARCH_TIME : ASW_AI_EFFICIENT_NPC_SEARCH_TIME );
m_SwarmSenseMarinesTimer.Reset( fSenseTime );
//if ( GetOuter()->GetEfficiency() < AIE_SUPER_EFFICIENT )
{
int nSeen = 0;
BeginGather();
CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
{
#if OTHER_IMPORTANT_ENTITIES_NOT_BAKED
if ( ppAIs[i] != GetOuter()->GetTarget() && ppAIs[i] != GetOuter()->GetEnemy() )
#endif
{
if ( ppAIs[i] != GetOuter() && ( ppAIs[i]->ShouldNotDistanceCull() || origin.DistToSqr(ppAIs[i]->GetAbsOrigin()) < distSq ) && CanSwarmSense( ppAIs[i] ) )
{
nSeen++;
}
}
}
EndGather( nSeen, &m_SwarmSensedMarines );
return nSeen;
}
bRemoveStaleFromCache = true;
// Fall through
}
for ( int i = m_SwarmSensedMarines.Count() - 1; i >= 0; --i )
{
if ( m_SwarmSensedMarines[i].Get() == NULL )
{
m_SwarmSensedMarines.FastRemove( i );
}
else if ( bRemoveStaleFromCache )
{
if ( ( !((CAI_BaseNPC *)m_SwarmSensedMarines[i].Get())->ShouldNotDistanceCull() &&
origin.DistToSqr(m_SwarmSensedMarines[i]->GetAbsOrigin()) > distSq ) ||
!CanSwarmSense( m_SwarmSensedMarines[i] ) )
{
m_SwarmSensedMarines.FastRemove( i );
}
}
}
return m_SwarmSensedMarines.Count();
}
bool CASW_AI_Senses::CanSwarmSense( CBaseEntity *pSightEnt )
{
if ( WaitingUntilSeen( pSightEnt ) )
return false;
if ( ShouldSeeEntity( pSightEnt ) ) // && CanSeeEntity( pSightEnt ) // can always swarm sense things in our search radius
{
return SwarmSenseEntity( pSightEnt );
}
return false;
}
bool CASW_AI_Senses::SwarmSenseEntity( CBaseEntity *pSightEnt )
{
IASW_Spawnable_NPC* pAlienOuter = dynamic_cast<IASW_Spawnable_NPC*>(GetOuter());
if (!pAlienOuter)
return false;
pAlienOuter->OnSwarmSenseEntity( pSightEnt );
// insert at the head of my sight list
NoteSeenEntity( pSightEnt );
return true;
}
CBaseEntity *CASW_AI_Senses::GetFirstSwarmSenseEntity( AISightIter_t *pIter, seentype_t iSeenType ) const
{
COMPILE_TIME_ASSERT( sizeof( AISightIter_t ) == sizeof( AISightIterVal_t ) );
AISightIterVal_t *pIterVal = (AISightIterVal_t *)pIter;
// If we're searching for a specific type, start in that array
pIterVal->SwarmSensedArray = (char)iSeenType;
int iFirstArray = ( iSeenType == SEEN_ALL ) ? 0 : iSeenType;
for ( int i = iFirstArray; i < ARRAYSIZE( m_SwarmSenseArrays ); i++ )
{
if ( m_SwarmSenseArrays[i]->Count() != 0 )
{
pIterVal->array = i;
pIterVal->iNext = 1;
return (*m_SwarmSenseArrays[i])[0];
}
}
(*pIter) = (AISightIter_t)(-1);
return NULL;
}
//-----------------------------------------------------------------------------
CBaseEntity *CASW_AI_Senses::GetNextSwarmSenseEntity( AISightIter_t *pIter ) const
{
if ( ((int)*pIter) != -1 )
{
AISightIterVal_t *pIterVal = (AISightIterVal_t *)pIter;
for ( int i = pIterVal->array; i < ARRAYSIZE( m_SwarmSenseArrays ); i++ )
{
for ( int j = pIterVal->iNext; j < m_SwarmSenseArrays[i]->Count(); j++ )
{
if ( (*m_SwarmSenseArrays[i])[j].Get() != NULL )
{
pIterVal->array = i;
pIterVal->iNext = j+1;
return (*m_SwarmSenseArrays[i])[j];
}
}
pIterVal->iNext = 0;
// If we're searching for a specific type, don't move to the next array
if ( pIterVal->SwarmSensedArray != SEEN_ALL )
break;
}
(*pIter) = (AISightIter_t)(-1);
}
return NULL;
}