You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
752 lines
19 KiB
752 lines
19 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
|
|
#include "ai_senses.h" |
|
|
|
#include "soundent.h" |
|
#include "team.h" |
|
#include "ai_basenpc.h" |
|
#include "saverestore_utlvector.h" |
|
|
|
#ifdef PORTAL |
|
#include "portal_util_shared.h" |
|
#endif |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
// Use this to disable caching and other optimizations in senses |
|
#define DEBUG_SENSES 1 |
|
|
|
#ifdef DEBUG_SENSES |
|
#define AI_PROFILE_SENSES(tag) AI_PROFILE_SCOPE(tag) |
|
#else |
|
#define AI_PROFILE_SENSES(tag) ((void)0) |
|
#endif |
|
|
|
const float AI_STANDARD_NPC_SEARCH_TIME = .25; |
|
const float AI_EFFICIENT_NPC_SEARCH_TIME = .35; |
|
const float AI_HIGH_PRIORITY_SEARCH_TIME = 0.15; |
|
const float AI_MISC_SEARCH_TIME = 0.45; |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
CAI_SensedObjectsManager g_AI_SensedObjectsManager; |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
#pragma pack(push) |
|
#pragma pack(1) |
|
|
|
struct AISightIterVal_t |
|
{ |
|
char array; |
|
short iNext; |
|
char SeenArray; |
|
}; |
|
|
|
#pragma pack(pop) |
|
|
|
|
|
//============================================================================= |
|
// |
|
// CAI_Senses |
|
// |
|
//============================================================================= |
|
|
|
BEGIN_SIMPLE_DATADESC( CAI_Senses ) |
|
|
|
DEFINE_FIELD( m_LookDist, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_LastLookDist, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_TimeLastLook, FIELD_TIME ), |
|
DEFINE_FIELD( m_iSensingFlags, FIELD_INTEGER ), |
|
// m_iAudibleList (no way to save?) |
|
DEFINE_UTLVECTOR(m_SeenHighPriority, FIELD_EHANDLE ), |
|
DEFINE_UTLVECTOR(m_SeenNPCs, FIELD_EHANDLE ), |
|
DEFINE_UTLVECTOR(m_SeenMisc, FIELD_EHANDLE ), |
|
// m_SeenArrays (not saved, rebuilt) |
|
|
|
// Could fold these three and above timer into one concept, but would invalidate savegames |
|
DEFINE_FIELD( m_TimeLastLookHighPriority, FIELD_TIME ), |
|
DEFINE_FIELD( m_TimeLastLookNPCs, FIELD_TIME ), |
|
DEFINE_FIELD( m_TimeLastLookMisc, FIELD_TIME ), |
|
|
|
END_DATADESC() |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool CAI_Senses::CanHearSound( CSound *pSound ) |
|
{ |
|
if ( pSound->m_hOwner.Get() == GetOuter() ) |
|
return false; |
|
|
|
if( GetOuter()->GetState() == NPC_STATE_SCRIPT && pSound->IsSoundType( SOUND_DANGER ) ) |
|
{ |
|
// For now, don't hear danger in scripted sequences. This probably isn't a |
|
// good long term solution, but it makes the Bank Exterior work better. |
|
return false; |
|
} |
|
|
|
if ( GetOuter()->IsInAScript() ) |
|
return false; |
|
|
|
// @TODO (toml 10-18-02): what about player sounds and notarget? |
|
float flHearDistanceSq = pSound->Volume() * GetOuter()->HearingSensitivity(); |
|
flHearDistanceSq *= flHearDistanceSq; |
|
if( pSound->GetSoundOrigin().DistToSqr( GetOuter()->EarPosition() ) <= flHearDistanceSq ) |
|
{ |
|
return GetOuter()->QueryHearSound( pSound ); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Listen - npcs dig through the active sound list for |
|
// any sounds that may interest them. (smells, too!) |
|
|
|
void CAI_Senses::Listen( void ) |
|
{ |
|
m_iAudibleList = SOUNDLIST_EMPTY; |
|
|
|
int iSoundMask = GetOuter()->GetSoundInterests(); |
|
|
|
if ( iSoundMask != SOUND_NONE && !(GetOuter()->HasSpawnFlags(SF_NPC_WAIT_TILL_SEEN)) ) |
|
{ |
|
int iSound = CSoundEnt::ActiveList(); |
|
|
|
while ( iSound != SOUNDLIST_EMPTY ) |
|
{ |
|
CSound *pCurrentSound = CSoundEnt::SoundPointerForIndex( iSound ); |
|
|
|
if ( pCurrentSound && (iSoundMask & pCurrentSound->SoundType()) && CanHearSound( pCurrentSound ) ) |
|
{ |
|
// the npc cares about this sound, and it's close enough to hear. |
|
pCurrentSound->m_iNextAudible = m_iAudibleList; |
|
m_iAudibleList = iSound; |
|
} |
|
|
|
iSound = pCurrentSound->NextSound(); |
|
} |
|
} |
|
|
|
GetOuter()->OnListened(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool CAI_Senses::ShouldSeeEntity( CBaseEntity *pSightEnt ) |
|
{ |
|
if ( pSightEnt == GetOuter() || !pSightEnt->IsAlive() ) |
|
return false; |
|
|
|
if ( pSightEnt->IsPlayer() && ( pSightEnt->GetFlags() & FL_NOTARGET ) ) |
|
return false; |
|
|
|
// don't notice anyone waiting to be seen by the player |
|
if ( pSightEnt->m_spawnflags & SF_NPC_WAIT_TILL_SEEN ) |
|
return false; |
|
|
|
if ( !pSightEnt->CanBeSeenBy( GetOuter() ) ) |
|
return false; |
|
|
|
if ( !GetOuter()->QuerySeeEntity( pSightEnt, true ) ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool CAI_Senses::CanSeeEntity( CBaseEntity *pSightEnt ) |
|
{ |
|
return ( GetOuter()->FInViewCone( pSightEnt ) && GetOuter()->FVisible( pSightEnt ) ); |
|
} |
|
|
|
#ifdef PORTAL |
|
bool CAI_Senses::CanSeeEntityThroughPortal( const CProp_Portal *pPortal, CBaseEntity *pSightEnt ) |
|
{ |
|
return GetOuter()->FVisibleThroughPortal( pPortal, pSightEnt ); |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool CAI_Senses::DidSeeEntity( CBaseEntity *pSightEnt ) const |
|
{ |
|
AISightIter_t iter; |
|
CBaseEntity *pTestEnt; |
|
|
|
pTestEnt = GetFirstSeenEntity( &iter ); |
|
|
|
while( pTestEnt ) |
|
{ |
|
if ( pSightEnt == pTestEnt ) |
|
return true; |
|
pTestEnt = GetNextSeenEntity( &iter ); |
|
} |
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
void CAI_Senses::NoteSeenEntity( CBaseEntity *pSightEnt ) |
|
{ |
|
pSightEnt->m_pLink = GetOuter()->m_pLink; |
|
GetOuter()->m_pLink = pSightEnt; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool CAI_Senses::WaitingUntilSeen( CBaseEntity *pSightEnt ) |
|
{ |
|
if ( GetOuter()->m_spawnflags & SF_NPC_WAIT_TILL_SEEN ) |
|
{ |
|
if ( pSightEnt->IsPlayer() ) |
|
{ |
|
CBasePlayer *pPlayer = ToBasePlayer( 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 |
|
// && pPlayer->FVisible( GetOuter() ) |
|
&& pPlayer->FInViewCone( GetOuter() ) |
|
&& FBoxVisible( pSightEnt, static_cast<CBaseEntity*>(GetOuter()), zero ) ) |
|
{ |
|
// player sees us, become normal now. |
|
GetOuter()->m_spawnflags &= ~SF_NPC_WAIT_TILL_SEEN; |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool CAI_Senses::SeeEntity( CBaseEntity *pSightEnt ) |
|
{ |
|
GetOuter()->OnSeeEntity( pSightEnt ); |
|
|
|
// insert at the head of my sight list |
|
NoteSeenEntity( pSightEnt ); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
CBaseEntity *CAI_Senses::GetFirstSeenEntity( 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->SeenArray = (char)iSeenType; |
|
int iFirstArray = ( iSeenType == SEEN_ALL ) ? 0 : iSeenType; |
|
|
|
for ( int i = iFirstArray; i < ARRAYSIZE( m_SeenArrays ); i++ ) |
|
{ |
|
if ( m_SeenArrays[i]->Count() != 0 ) |
|
{ |
|
pIterVal->array = i; |
|
pIterVal->iNext = 1; |
|
return (*m_SeenArrays[i])[0]; |
|
} |
|
} |
|
|
|
(*pIter) = (AISightIter_t)(-1); |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
CBaseEntity *CAI_Senses::GetNextSeenEntity( AISightIter_t *pIter ) const |
|
{ |
|
if ( ((int)*pIter) != -1 ) |
|
{ |
|
AISightIterVal_t *pIterVal = (AISightIterVal_t *)pIter; |
|
|
|
for ( int i = pIterVal->array; i < ARRAYSIZE( m_SeenArrays ); i++ ) |
|
{ |
|
for ( int j = pIterVal->iNext; j < m_SeenArrays[i]->Count(); j++ ) |
|
{ |
|
if ( (*m_SeenArrays[i])[j].Get() != NULL ) |
|
{ |
|
pIterVal->array = i; |
|
pIterVal->iNext = j+1; |
|
return (*m_SeenArrays[i])[j]; |
|
} |
|
} |
|
pIterVal->iNext = 0; |
|
|
|
// If we're searching for a specific type, don't move to the next array |
|
if ( pIterVal->SeenArray != SEEN_ALL ) |
|
break; |
|
} |
|
(*pIter) = (AISightIter_t)(-1); |
|
} |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
void CAI_Senses::BeginGather() |
|
{ |
|
// clear my sight list |
|
GetOuter()->m_pLink = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
void CAI_Senses::EndGather( int nSeen, CUtlVector<EHANDLE> *pResult ) |
|
{ |
|
pResult->SetCount( nSeen ); |
|
if ( nSeen ) |
|
{ |
|
CBaseEntity *pCurrent = GetOuter()->m_pLink; |
|
for (int i = 0; i < nSeen; i++ ) |
|
{ |
|
Assert( pCurrent ); |
|
(*pResult)[i].Set( pCurrent ); |
|
pCurrent = pCurrent->m_pLink; |
|
} |
|
GetOuter()->m_pLink = NULL; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Look - Base class npc function to find enemies or |
|
// food by sight. iDistance is distance ( in units ) that the |
|
// npc can see. |
|
// |
|
// Sets the sight bits of the m_afConditions mask to indicate |
|
// which types of entities were sighted. |
|
// Function also sets the Looker's m_pLink |
|
// to the head of a link list that contains all visible ents. |
|
// (linked via each ent's m_pLink field) |
|
// |
|
|
|
void CAI_Senses::Look( int iDistance ) |
|
{ |
|
if ( m_TimeLastLook != gpGlobals->curtime || m_LastLookDist != iDistance ) |
|
{ |
|
//----------------------------- |
|
|
|
LookForHighPriorityEntities( iDistance ); |
|
LookForNPCs( iDistance); |
|
LookForObjects( iDistance ); |
|
|
|
//----------------------------- |
|
|
|
m_LastLookDist = iDistance; |
|
m_TimeLastLook = gpGlobals->curtime; |
|
} |
|
|
|
GetOuter()->OnLooked( iDistance ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool CAI_Senses::Look( CBaseEntity *pSightEnt ) |
|
{ |
|
if ( WaitingUntilSeen( pSightEnt ) ) |
|
return false; |
|
|
|
if ( ShouldSeeEntity( pSightEnt ) && CanSeeEntity( pSightEnt ) ) |
|
{ |
|
return SeeEntity( pSightEnt ); |
|
} |
|
return false; |
|
} |
|
|
|
#ifdef PORTAL |
|
bool CAI_Senses::LookThroughPortal( const CProp_Portal *pPortal, CBaseEntity *pSightEnt ) |
|
{ |
|
if ( WaitingUntilSeen( pSightEnt ) ) |
|
return false; |
|
|
|
if ( ShouldSeeEntity( pSightEnt ) && CanSeeEntityThroughPortal( pPortal, pSightEnt ) ) |
|
{ |
|
return SeeEntity( pSightEnt ); |
|
} |
|
return false; |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
int CAI_Senses::LookForHighPriorityEntities( int iDistance ) |
|
{ |
|
int nSeen = 0; |
|
if ( gpGlobals->curtime - m_TimeLastLookHighPriority > AI_HIGH_PRIORITY_SEARCH_TIME ) |
|
{ |
|
AI_PROFILE_SENSES(CAI_Senses_LookForHighPriorityEntities); |
|
m_TimeLastLookHighPriority = gpGlobals->curtime; |
|
|
|
BeginGather(); |
|
|
|
float distSq = ( iDistance * iDistance ); |
|
const Vector &origin = GetAbsOrigin(); |
|
|
|
// Players |
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ ) |
|
{ |
|
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); |
|
|
|
if ( pPlayer ) |
|
{ |
|
if ( origin.DistToSqr(pPlayer->GetAbsOrigin()) < distSq && Look( pPlayer ) ) |
|
{ |
|
nSeen++; |
|
} |
|
#ifdef PORTAL |
|
else |
|
{ |
|
CProp_Portal *pPortal = GetOuter()->FInViewConeThroughPortal( pPlayer ); |
|
if ( pPortal && UTIL_Portal_DistanceThroughPortalSqr( pPortal, origin, pPlayer->GetAbsOrigin() ) < distSq && LookThroughPortal( pPortal, pPlayer ) ) |
|
{ |
|
nSeen++; |
|
} |
|
} |
|
#endif |
|
} |
|
} |
|
|
|
EndGather( nSeen, &m_SeenHighPriority ); |
|
} |
|
else |
|
{ |
|
for ( int i = m_SeenHighPriority.Count() - 1; i >= 0; --i ) |
|
{ |
|
if ( m_SeenHighPriority[i].Get() == NULL ) |
|
m_SeenHighPriority.FastRemove( i ); |
|
} |
|
nSeen = m_SeenHighPriority.Count(); |
|
} |
|
|
|
return nSeen; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
int CAI_Senses::LookForNPCs( int iDistance ) |
|
{ |
|
bool bRemoveStaleFromCache = false; |
|
float distSq = ( iDistance * iDistance ); |
|
const Vector &origin = GetAbsOrigin(); |
|
AI_Efficiency_t efficiency = GetOuter()->GetEfficiency(); |
|
float timeNPCs = ( efficiency < AIE_VERY_EFFICIENT ) ? AI_STANDARD_NPC_SEARCH_TIME : AI_EFFICIENT_NPC_SEARCH_TIME; |
|
if ( gpGlobals->curtime - m_TimeLastLookNPCs > timeNPCs ) |
|
{ |
|
AI_PROFILE_SENSES(CAI_Senses_LookForNPCs); |
|
|
|
m_TimeLastLookNPCs = gpGlobals->curtime; |
|
|
|
if ( efficiency < AIE_SUPER_EFFICIENT ) |
|
{ |
|
int i, nSeen = 0; |
|
|
|
BeginGather(); |
|
|
|
CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); |
|
|
|
for ( i = 0; i < g_AI_Manager.NumAIs(); i++ ) |
|
{ |
|
if ( ppAIs[i] != GetOuter() && ( ppAIs[i]->ShouldNotDistanceCull() || origin.DistToSqr(ppAIs[i]->GetAbsOrigin()) < distSq ) ) |
|
{ |
|
if ( Look( ppAIs[i] ) ) |
|
{ |
|
nSeen++; |
|
} |
|
} |
|
} |
|
|
|
EndGather( nSeen, &m_SeenNPCs ); |
|
|
|
return nSeen; |
|
} |
|
|
|
bRemoveStaleFromCache = true; |
|
// Fall through |
|
} |
|
|
|
for ( int i = m_SeenNPCs.Count() - 1; i >= 0; --i ) |
|
{ |
|
if ( m_SeenNPCs[i].Get() == NULL ) |
|
{ |
|
m_SeenNPCs.FastRemove( i ); |
|
} |
|
else if ( bRemoveStaleFromCache ) |
|
{ |
|
if ( ( !((CAI_BaseNPC *)m_SeenNPCs[i].Get())->ShouldNotDistanceCull() && |
|
origin.DistToSqr(m_SeenNPCs[i]->GetAbsOrigin()) > distSq ) || |
|
!Look( m_SeenNPCs[i] ) ) |
|
{ |
|
m_SeenNPCs.FastRemove( i ); |
|
} |
|
} |
|
} |
|
|
|
return m_SeenNPCs.Count(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
int CAI_Senses::LookForObjects( int iDistance ) |
|
{ |
|
const int BOX_QUERY_MASK = FL_OBJECT; |
|
int nSeen = 0; |
|
|
|
if ( gpGlobals->curtime - m_TimeLastLookMisc > AI_MISC_SEARCH_TIME ) |
|
{ |
|
AI_PROFILE_SENSES(CAI_Senses_LookForObjects); |
|
m_TimeLastLookMisc = gpGlobals->curtime; |
|
|
|
BeginGather(); |
|
|
|
float distSq = ( iDistance * iDistance ); |
|
const Vector &origin = GetAbsOrigin(); |
|
int iter; |
|
CBaseEntity *pEnt = g_AI_SensedObjectsManager.GetFirst( &iter ); |
|
while ( pEnt ) |
|
{ |
|
if ( pEnt->GetFlags() & BOX_QUERY_MASK ) |
|
{ |
|
if ( origin.DistToSqr(pEnt->GetAbsOrigin()) < distSq && Look( pEnt) ) |
|
{ |
|
nSeen++; |
|
} |
|
} |
|
pEnt = g_AI_SensedObjectsManager.GetNext( &iter ); |
|
} |
|
|
|
EndGather( nSeen, &m_SeenMisc ); |
|
} |
|
else |
|
{ |
|
for ( int i = m_SeenMisc.Count() - 1; i >= 0; --i ) |
|
{ |
|
if ( m_SeenMisc[i].Get() == NULL ) |
|
m_SeenMisc.FastRemove( i ); |
|
} |
|
nSeen = m_SeenMisc.Count(); |
|
} |
|
|
|
return nSeen; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
float CAI_Senses::GetTimeLastUpdate( CBaseEntity *pEntity ) |
|
{ |
|
if ( !pEntity ) |
|
return 0; |
|
if ( pEntity->IsPlayer() ) |
|
return m_TimeLastLookHighPriority; |
|
if ( pEntity->IsNPC() ) |
|
return m_TimeLastLookNPCs; |
|
return m_TimeLastLookMisc; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
CSound* CAI_Senses::GetFirstHeardSound( AISoundIter_t *pIter ) |
|
{ |
|
int iFirst = GetAudibleList(); |
|
|
|
if ( iFirst == SOUNDLIST_EMPTY ) |
|
{ |
|
*pIter = NULL; |
|
return NULL; |
|
} |
|
|
|
*pIter = (AISoundIter_t)iFirst; |
|
return CSoundEnt::SoundPointerForIndex( iFirst ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
CSound* CAI_Senses::GetNextHeardSound( AISoundIter_t *pIter ) |
|
{ |
|
if ( !*pIter ) |
|
return NULL; |
|
|
|
int iCurrent = (int)*pIter; |
|
|
|
Assert( iCurrent != SOUNDLIST_EMPTY ); |
|
if ( iCurrent == SOUNDLIST_EMPTY ) |
|
{ |
|
*pIter = NULL; |
|
return NULL; |
|
} |
|
|
|
iCurrent = CSoundEnt::SoundPointerForIndex( iCurrent )->m_iNextAudible; |
|
if ( iCurrent == SOUNDLIST_EMPTY ) |
|
{ |
|
*pIter = NULL; |
|
return NULL; |
|
} |
|
|
|
*pIter = (AISoundIter_t)iCurrent; |
|
return CSoundEnt::SoundPointerForIndex( iCurrent ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
CSound *CAI_Senses::GetClosestSound( bool fScent, int validTypes, bool bUsePriority ) |
|
{ |
|
float flBestDist = MAX_COORD_RANGE*MAX_COORD_RANGE;// so first nearby sound will become best so far. |
|
float flDist; |
|
int iBestPriority = SOUND_PRIORITY_VERY_LOW; |
|
|
|
AISoundIter_t iter; |
|
|
|
CSound *pResult = NULL; |
|
CSound *pCurrent = GetFirstHeardSound( &iter ); |
|
|
|
Vector earPosition = GetOuter()->EarPosition(); |
|
|
|
while ( pCurrent ) |
|
{ |
|
if ( ( !fScent && pCurrent->FIsSound() ) || |
|
( fScent && pCurrent->FIsScent() ) ) |
|
{ |
|
if( pCurrent->IsSoundType( validTypes ) && !GetOuter()->ShouldIgnoreSound( pCurrent ) ) |
|
{ |
|
if( !bUsePriority || GetOuter()->GetSoundPriority(pCurrent) >= iBestPriority ) |
|
{ |
|
flDist = ( pCurrent->GetSoundOrigin() - earPosition ).LengthSqr(); |
|
|
|
if ( flDist < flBestDist ) |
|
{ |
|
pResult = pCurrent; |
|
flBestDist = flDist; |
|
|
|
iBestPriority = GetOuter()->GetSoundPriority(pCurrent); |
|
} |
|
} |
|
} |
|
} |
|
|
|
pCurrent = GetNextHeardSound( &iter ); |
|
} |
|
|
|
return pResult; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
void CAI_Senses::PerformSensing( void ) |
|
{ |
|
AI_PROFILE_SCOPE (CAI_BaseNPC_PerformSensing); |
|
|
|
// ----------------- |
|
// Look |
|
// ----------------- |
|
if( !HasSensingFlags(SENSING_FLAGS_DONT_LOOK) ) |
|
Look( m_LookDist ); |
|
|
|
// ------------------ |
|
// Listen |
|
// ------------------ |
|
if( !HasSensingFlags(SENSING_FLAGS_DONT_LISTEN) ) |
|
Listen(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
|
|
void CAI_SensedObjectsManager::Init() |
|
{ |
|
CBaseEntity *pEnt = NULL; |
|
while ( ( pEnt = gEntList.NextEnt( pEnt ) ) != NULL ) |
|
{ |
|
OnEntitySpawned( pEnt ); |
|
} |
|
|
|
gEntList.AddListenerEntity( this ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
void CAI_SensedObjectsManager::Term() |
|
{ |
|
gEntList.RemoveListenerEntity( this ); |
|
m_SensedObjects.RemoveAll(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
CBaseEntity *CAI_SensedObjectsManager::GetFirst( int *pIter ) |
|
{ |
|
if ( m_SensedObjects.Count() ) |
|
{ |
|
*pIter = 1; |
|
return m_SensedObjects[0]; |
|
} |
|
|
|
*pIter = 0; |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
CBaseEntity *CAI_SensedObjectsManager::GetNext( int *pIter ) |
|
{ |
|
int i = *pIter; |
|
if ( i && i < m_SensedObjects.Count() ) |
|
{ |
|
(*pIter)++; |
|
return m_SensedObjects[i]; |
|
} |
|
|
|
*pIter = 0; |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
void CAI_SensedObjectsManager::OnEntitySpawned( CBaseEntity *pEntity ) |
|
{ |
|
if ( ( pEntity->GetFlags() & FL_OBJECT ) && !pEntity->IsPlayer() && !pEntity->IsNPC() ) |
|
{ |
|
m_SensedObjects.AddToTail( pEntity ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
void CAI_SensedObjectsManager::OnEntityDeleted( CBaseEntity *pEntity ) |
|
{ |
|
if ( ( pEntity->GetFlags() & FL_OBJECT ) && !pEntity->IsPlayer() && !pEntity->IsNPC() ) |
|
{ |
|
int i = m_SensedObjects.Find( pEntity ); |
|
if ( i != m_SensedObjects.InvalidIndex() ) |
|
m_SensedObjects.FastRemove( i ); |
|
} |
|
} |
|
|
|
void CAI_SensedObjectsManager::AddEntity( CBaseEntity *pEntity ) |
|
{ |
|
if ( m_SensedObjects.Find( pEntity ) != m_SensedObjects.InvalidIndex() ) |
|
return; |
|
|
|
// We shouldn't be adding players or NPCs to this list |
|
Assert( !pEntity->IsPlayer() && !pEntity->IsNPC() ); |
|
|
|
// Add the object flag so it gets removed when it dies |
|
pEntity->AddFlag( FL_OBJECT ); |
|
m_SensedObjects.AddToTail( pEntity ); |
|
} |
|
|
|
//=============================================================================
|
|
|