mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-07 17:29:36 +00:00
1
This commit is contained in:
@@ -0,0 +1,752 @@
|
||||
//========= 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 );
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
Reference in New Issue
Block a user