Untitled diff

Created Diff never expires
37 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
752 lines
41 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
755 lines
//========= Copyright Valve Corporation, All rights reserved. ============//
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//
// Purpose:
// Purpose:
//
//
// $NoKeywords: $
// $NoKeywords: $
//=============================================================================//
//=============================================================================//


#include "cbase.h"
#include "cbase.h"


#include "ai_senses.h"
#include "ai_senses.h"


#include "soundent.h"
#include "soundent.h"
#include "team.h"
#include "team.h"
#include "ai_basenpc.h"
#include "ai_basenpc.h"
#include "saverestore_utlvector.h"
#include "saverestore_utlvector.h"


#ifdef PORTAL
#ifdef PORTAL
#include "portal_util_shared.h"
#include "portal_util_shared.h"
#endif
#endif


// memdbgon must be the last include file in a .cpp file!!!
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#include "tier0/memdbgon.h"


// Use this to disable caching and other optimizations in senses
// Use this to disable caching and other optimizations in senses
#define DEBUG_SENSES 1
#define DEBUG_SENSES 1


#ifdef DEBUG_SENSES
#ifdef DEBUG_SENSES
#define AI_PROFILE_SENSES(tag) AI_PROFILE_SCOPE(tag)
#define AI_PROFILE_SENSES(tag) AI_PROFILE_SCOPE(tag)
#else
#else
#define AI_PROFILE_SENSES(tag) ((void)0)
#define AI_PROFILE_SENSES(tag) ((void)0)
#endif
#endif


const float AI_STANDARD_NPC_SEARCH_TIME = .25;
const float AI_STANDARD_NPC_SEARCH_TIME = .25;
const float AI_EFFICIENT_NPC_SEARCH_TIME = .35;
const float AI_EFFICIENT_NPC_SEARCH_TIME = .35;
const float AI_HIGH_PRIORITY_SEARCH_TIME = 0.15;
const float AI_HIGH_PRIORITY_SEARCH_TIME = 0.15;
const float AI_MISC_SEARCH_TIME = 0.45;
const float AI_MISC_SEARCH_TIME = 0.45;


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


CAI_SensedObjectsManager g_AI_SensedObjectsManager;
CAI_SensedObjectsManager g_AI_SensedObjectsManager;


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


#pragma pack(push)
#pragma pack(push)
#pragma pack(1)
#pragma pack(1)


struct AISightIterVal_t
struct AISightIterVal_t
{
{
char array;
char array;
short iNext;
short iNext;
char SeenArray;
char SeenArray;
};
};


#pragma pack(pop)
#pragma pack(pop)




//=============================================================================
//=============================================================================
//
//
// CAI_Senses
// CAI_Senses
//
//
//=============================================================================
//=============================================================================


BEGIN_SIMPLE_DATADESC( CAI_Senses )
BEGIN_SIMPLE_DATADESC( CAI_Senses )


DEFINE_FIELD( m_LookDist, FIELD_FLOAT ),
DEFINE_FIELD( m_LookDist, FIELD_FLOAT ),
DEFINE_FIELD( m_LastLookDist, FIELD_FLOAT ),
DEFINE_FIELD( m_LastLookDist, FIELD_FLOAT ),
DEFINE_FIELD( m_TimeLastLook, FIELD_TIME ),
DEFINE_FIELD( m_TimeLastLook, FIELD_TIME ),
DEFINE_FIELD( m_iSensingFlags, FIELD_INTEGER ),
DEFINE_FIELD( m_iSensingFlags, FIELD_INTEGER ),
// m_iAudibleList (no way to save?)
// m_iAudibleList (no way to save?)
DEFINE_UTLVECTOR(m_SeenHighPriority, FIELD_EHANDLE ),
DEFINE_UTLVECTOR(m_SeenHighPriority, FIELD_EHANDLE ),
DEFINE_UTLVECTOR(m_SeenNPCs, FIELD_EHANDLE ),
DEFINE_UTLVECTOR(m_SeenNPCs, FIELD_EHANDLE ),
DEFINE_UTLVECTOR(m_SeenMisc, FIELD_EHANDLE ),
DEFINE_UTLVECTOR(m_SeenMisc, FIELD_EHANDLE ),
// m_SeenArrays (not saved, rebuilt)
// m_SeenArrays (not saved, rebuilt)


// Could fold these three and above timer into one concept, but would invalidate savegames
// Could fold these three and above timer into one concept, but would invalidate savegames
DEFINE_FIELD( m_TimeLastLookHighPriority, FIELD_TIME ),
DEFINE_FIELD( m_TimeLastLookHighPriority, FIELD_TIME ),
DEFINE_FIELD( m_TimeLastLookNPCs, FIELD_TIME ),
DEFINE_FIELD( m_TimeLastLookNPCs, FIELD_TIME ),
DEFINE_FIELD( m_TimeLastLookMisc, FIELD_TIME ),
DEFINE_FIELD( m_TimeLastLookMisc, FIELD_TIME ),


END_DATADESC()
END_DATADESC()


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


bool CAI_Senses::CanHearSound( CSound *pSound )
bool CAI_Senses::CanHearSound( CSound *pSound )
{
{
if ( pSound->m_hOwner.Get() == GetOuter() )
if ( pSound->m_hOwner.Get() == GetCharacter() )
return false;
return false;


if( GetOuter()->GetState() == NPC_STATE_SCRIPT && pSound->IsSoundType( SOUND_DANGER ) )
if ( GetOuter() ) {
{
if ( GetOuter()->GetState() == NPC_STATE_SCRIPT && pSound->IsSoundType( SOUND_DANGER ) ) {
// For now, don't hear danger in scripted sequences. This probably isn't a
// 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.
// good long term solution, but it makes the Bank Exterior work better.
return false;
return false;
}
}


if ( GetOuter()->IsInAScript() )
if ( GetOuter()->IsInAScript() )
return false;
return false;
}


// @TODO (toml 10-18-02): what about player sounds and notarget?
// @TODO (toml 10-18-02): what about player sounds and notarget?
float flHearDistanceSq = pSound->Volume() * GetOuter()->HearingSensitivity();
float flHearDistanceSq = pSound->Volume() * GetCharacter()->HearingSensitivity();
flHearDistanceSq *= flHearDistanceSq;
flHearDistanceSq *= flHearDistanceSq;
if( pSound->GetSoundOrigin().DistToSqr( GetOuter()->EarPosition() ) <= flHearDistanceSq )
if( pSound->GetSoundOrigin().DistToSqr( GetCharacter()->EarPosition() ) <= flHearDistanceSq )
{
{
return GetOuter()->QueryHearSound( pSound );
return GetCharacter()->QueryHearSound( pSound );
}
}


return false;
return false;
}
}




//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Listen - npcs dig through the active sound list for
// Listen - npcs dig through the active sound list for
// any sounds that may interest them. (smells, too!)
// any sounds that may interest them. (smells, too!)


void CAI_Senses::Listen( void )
void CAI_Senses::Listen( void )
{
{
m_iAudibleList = SOUNDLIST_EMPTY;
m_iAudibleList = SOUNDLIST_EMPTY;


int iSoundMask = GetOuter()->GetSoundInterests();
int iSoundMask = GetCharacter()->GetSoundInterests();
if ( iSoundMask != SOUND_NONE && !(GetOuter()->HasSpawnFlags(SF_NPC_WAIT_TILL_SEEN)) )
if ( iSoundMask != SOUND_NONE && !(GetCharacter()->HasSpawnFlags(SF_NPC_WAIT_TILL_SEEN)) )
{
{
int iSound = CSoundEnt::ActiveList();
int iSound = CSoundEnt::ActiveList();
while ( iSound != SOUNDLIST_EMPTY )
while ( iSound != SOUNDLIST_EMPTY )
{
{
CSound *pCurrentSound = CSoundEnt::SoundPointerForIndex( iSound );
CSound *pCurrentSound = CSoundEnt::SoundPointerForIndex( iSound );


if ( pCurrentSound && (iSoundMask & pCurrentSound->SoundType()) && CanHearSound( pCurrentSound ) )
if ( pCurrentSound && (iSoundMask & pCurrentSound->SoundType()) && CanHearSound( pCurrentSound ) )
{
{
// the npc cares about this sound, and it's close enough to hear.
// the npc cares about this sound, and it's close enough to hear.
pCurrentSound->m_iNextAudible = m_iAudibleList;
pCurrentSound->m_iNextAudible = m_iAudibleList;
m_iAudibleList = iSound;
m_iAudibleList = iSound;
}
}


iSound = pCurrentSound->NextSound();
iSound = pCurrentSound->NextSound();
}
}
}
}
GetOuter()->OnListened();
GetCharacter()->OnListened();
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


bool CAI_Senses::ShouldSeeEntity( CBaseEntity *pSightEnt )
bool CAI_Senses::ShouldSeeEntity( CBaseEntity *pSightEnt )
{
{
if ( pSightEnt == GetOuter() || !pSightEnt->IsAlive() )
if ( pSightEnt == GetCharacter() || !pSightEnt->IsAlive() )
return false;
return false;


if ( pSightEnt->IsPlayer() && ( pSightEnt->GetFlags() & FL_NOTARGET ) )
if ( pSightEnt->IsPlayer() && ( pSightEnt->GetFlags() & FL_NOTARGET ) )
return false;
return false;


// don't notice anyone waiting to be seen by the player
// don't notice anyone waiting to be seen by the player
if ( pSightEnt->m_spawnflags & SF_NPC_WAIT_TILL_SEEN )
if ( pSightEnt->m_spawnflags & SF_NPC_WAIT_TILL_SEEN )
return false;
return false;


if ( !pSightEnt->CanBeSeenBy( GetOuter() ) )
if ( GetOuter() && !pSightEnt->CanBeSeenBy( GetOuter() ) )
return false;
return false;
if ( !GetOuter()->QuerySeeEntity( pSightEnt, true ) )
if ( !GetCharacter()->QuerySeeEntity( pSightEnt, true ) )
return false;
return false;


return true;
return true;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


bool CAI_Senses::CanSeeEntity( CBaseEntity *pSightEnt )
bool CAI_Senses::CanSeeEntity( CBaseEntity *pSightEnt )
{
{
return ( GetOuter()->FInViewCone( pSightEnt ) && GetOuter()->FVisible( pSightEnt ) );
return GetCharacter()->IsAbleToSee( pSightEnt, CBaseCombatCharacter::USE_FOV );
//return ( GetOuter()->FInViewCone( pSightEnt ) && GetOuter()->FVisible( pSightEnt ) );
}
}


#ifdef PORTAL
#ifdef PORTAL
bool CAI_Senses::CanSeeEntityThroughPortal( const CProp_Portal *pPortal, CBaseEntity *pSightEnt )
bool CAI_Senses::CanSeeEntityThroughPortal( const CProp_Portal *pPortal, CBaseEntity *pSightEnt )
{
{
return GetOuter()->FVisibleThroughPortal( pPortal, pSightEnt );
return GetOuter()->FVisibleThroughPortal( pPortal, pSightEnt );
}
}
#endif
#endif


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


bool CAI_Senses::DidSeeEntity( CBaseEntity *pSightEnt ) const
bool CAI_Senses::DidSeeEntity( CBaseEntity *pSightEnt ) const
{
{
AISightIter_t iter;
AISightIter_t iter;
CBaseEntity *pTestEnt;
CBaseEntity *pTestEnt;


pTestEnt = GetFirstSeenEntity( &iter );
pTestEnt = GetFirstSeenEntity( &iter );


while( pTestEnt )
while( pTestEnt )
{
{
if ( pSightEnt == pTestEnt )
if ( pSightEnt == pTestEnt )
return true;
return true;
pTestEnt = GetNextSeenEntity( &iter );
pTestEnt = GetNextSeenEntity( &iter );
}
}
return false;
return false;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


void CAI_Senses::NoteSeenEntity( CBaseEntity *pSightEnt )
void CAI_Senses::NoteSeenEntity( CBaseEntity *pSightEnt )
{
{
pSightEnt->m_pLink = GetOuter()->m_pLink;
pSightEnt->m_pLink = GetCharacter()->m_pLink;
GetOuter()->m_pLink = pSightEnt;
GetCharacter()->m_pLink = pSightEnt;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


bool CAI_Senses::WaitingUntilSeen( CBaseEntity *pSightEnt )
bool CAI_Senses::WaitingUntilSeen( CBaseEntity *pSightEnt )
{
{
if ( GetOuter()->m_spawnflags & SF_NPC_WAIT_TILL_SEEN )
if ( GetCharacter()->m_spawnflags & SF_NPC_WAIT_TILL_SEEN )
{
{
if ( pSightEnt->IsPlayer() )
if ( pSightEnt->IsPlayer() )
{
{
CBasePlayer *pPlayer = ToBasePlayer( pSightEnt );
CBasePlayer *pPlayer = ToBasePlayer( pSightEnt );
Vector zero = Vector(0,0,0);
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
// 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
if ( pPlayer
// && pPlayer->FVisible( GetOuter() )
// && pPlayer->FVisible( GetOuter() )
&& pPlayer->FInViewCone( GetOuter() )
&& pPlayer->FInViewCone( GetCharacter() )
&& FBoxVisible( pSightEnt, static_cast<CBaseEntity*>(GetOuter()), zero ) )
&& FBoxVisible( pSightEnt, static_cast<CBaseEntity*>(GetCharacter()), zero ) )
{
{
// player sees us, become normal now.
// player sees us, become normal now.
GetOuter()->m_spawnflags &= ~SF_NPC_WAIT_TILL_SEEN;
GetCharacter()->m_spawnflags &= ~SF_NPC_WAIT_TILL_SEEN;
return false;
return false;
}
}
}
}
return true;
return true;
}
}


return false;
return false;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


bool CAI_Senses::SeeEntity( CBaseEntity *pSightEnt )
bool CAI_Senses::SeeEntity( CBaseEntity *pSightEnt )
{
{
GetOuter()->OnSeeEntity( pSightEnt );
GetCharacter()->OnSeeEntity( pSightEnt );


// insert at the head of my sight list
// insert at the head of my sight list
NoteSeenEntity( pSightEnt );
NoteSeenEntity( pSightEnt );


return true;
return true;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


CBaseEntity *CAI_Senses::GetFirstSeenEntity( AISightIter_t *pIter, seentype_t iSeenType ) const
CBaseEntity *CAI_Senses::GetFirstSeenEntity( AISightIter_t *pIter, seentype_t iSeenType ) const
{
{
COMPILE_TIME_ASSERT( sizeof( AISightIter_t ) == sizeof( AISightIterVal_t ) );
COMPILE_TIME_ASSERT( sizeof( AISightIter_t ) == sizeof( AISightIterVal_t ) );
AISightIterVal_t *pIterVal = (AISightIterVal_t *)pIter;
AISightIterVal_t *pIterVal = (AISightIterVal_t *)pIter;
// If we're searching for a specific type, start in that array
// If we're searching for a specific type, start in that array
pIterVal->SeenArray = (char)iSeenType;
pIterVal->SeenArray = (char)iSeenType;
int iFirstArray = ( iSeenType == SEEN_ALL ) ? 0 : iSeenType;
int iFirstArray = ( iSeenType == SEEN_ALL ) ? 0 : iSeenType;


for ( int i = iFirstArray; i < ARRAYSIZE( m_SeenArrays ); i++ )
for ( int i = iFirstArray; i < ARRAYSIZE( m_SeenArrays ); i++ )
{
{
if ( m_SeenArrays[i]->Count() != 0 )
if ( m_SeenArrays[i]->Count() != 0 )
{
{
pIterVal->array = i;
pIterVal->array = i;
pIterVal->iNext = 1;
pIterVal->iNext = 1;
return (*m_SeenArrays[i])[0];
return (*m_SeenArrays[i])[0];
}
}
}
}
(*pIter) = (AISightIter_t)(-1);
(*pIter) = (AISightIter_t)(-1);
return NULL;
return NULL;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


CBaseEntity *CAI_Senses::GetNextSeenEntity( AISightIter_t *pIter ) const
CBaseEntity *CAI_Senses::GetNextSeenEntity( AISightIter_t *pIter ) const
{
{
if ( ((int)*pIter) != -1 )
if ( ((int)*pIter) != -1 )
{
{
AISightIterVal_t *pIterVal = (AISightIterVal_t *)pIter;
AISightIterVal_t *pIterVal = (AISightIterVal_t *)pIter;
for ( int i = pIterVal->array; i < ARRAYSIZE( m_SeenArrays ); i++ )
for ( int i = pIterVal->array; i < ARRAYSIZE( m_SeenArrays ); i++ )
{
{
for ( int j = pIterVal->iNext; j < m_SeenArrays[i]->Count(); j++ )
for ( int j = pIterVal->iNext; j < m_SeenArrays[i]->Count(); j++ )
{
{
if ( (*m_SeenArrays[i])[j].Get() != NULL )
if ( (*m_SeenArrays[i])[j].Get() != NULL )
{
{
pIterVal->array = i;
pIterVal->array = i;
pIterVal->iNext = j+1;
pIterVal->iNext = j+1;
return (*m_SeenArrays[i])[j];
return (*m_SeenArrays[i])[j];
}
}
}
}
pIterVal->iNext = 0;
pIterVal->iNext = 0;


// If we're searching for a specific type, don't move to the next array
// If we're searching for a specific type, don't move to the next array
if ( pIterVal->SeenArray != SEEN_ALL )
if ( pIterVal->SeenArray != SEEN_ALL )
break;
break;
}
}
(*pIter) = (AISightIter_t)(-1);
(*pIter) = (AISightIter_t)(-1);
}
}
return NULL;
return NULL;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


void CAI_Senses::BeginGather()
void CAI_Senses::BeginGather()
{
{
// clear my sight list
// clear my sight list
GetOuter()->m_pLink = NULL;
GetCharacter()->m_pLink = NULL;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


void CAI_Senses::EndGather( int nSeen, CUtlVector<EHANDLE> *pResult )
void CAI_Senses::EndGather( int nSeen, CUtlVector<EHANDLE> *pResult )
{
{
pResult->SetCount( nSeen );
pResult->SetCount( nSeen );
if ( nSeen )
if ( nSeen )
{
{
CBaseEntity *pCurrent = GetOuter()->m_pLink;
CBaseEntity *pCurrent = GetCharacter()->m_pLink;
for (int i = 0; i < nSeen; i++ )
for (int i = 0; i < nSeen; i++ )
{
{
Assert( pCurrent );
Assert( pCurrent );
(*pResult)[i].Set( pCurrent );
(*pResult)[i].Set( pCurrent );
pCurrent = pCurrent->m_pLink;
pCurrent = pCurrent->m_pLink;
}
}
GetOuter()->m_pLink = NULL;
GetCharacter()->m_pLink = NULL;
}
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Look - Base class npc function to find enemies or
// Look - Base class npc function to find enemies or
// food by sight. iDistance is distance ( in units ) that the
// food by sight. iDistance is distance ( in units ) that the
// npc can see.
// npc can see.
//
//
// Sets the sight bits of the m_afConditions mask to indicate
// Sets the sight bits of the m_afConditions mask to indicate
// which types of entities were sighted.
// which types of entities were sighted.
// Function also sets the Looker's m_pLink
// Function also sets the Looker's m_pLink
// to the head of a link list that contains all visible ents.
// to the head of a link list that contains all visible ents.
// (linked via each ent's m_pLink field)
// (linked via each ent's m_pLink field)
//
//


void CAI_Senses::Look( int iDistance )
void CAI_Senses::Look( int iDistance )
{
{
if ( m_TimeLastLook != gpGlobals->curtime || m_LastLookDist != iDistance )
if ( m_TimeLastLook != gpGlobals->curtime || m_LastLookDist != iDistance )
{
{
//-----------------------------
//-----------------------------
LookForHighPriorityEntities( iDistance );
LookForHighPriorityEntities( iDistance );
LookForNPCs( iDistance);
LookForNPCs( iDistance);
LookForObjects( iDistance );
LookForObjects( iDistance );
//-----------------------------
//-----------------------------
m_LastLookDist = iDistance;
m_LastLookDist = iDistance;
m_TimeLastLook = gpGlobals->curtime;
m_TimeLastLook = gpGlobals->curtime;
}
}
GetOuter()->OnLooked( iDistance );
GetCharacter()->OnLooked( iDistance );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


bool CAI_Senses::Look( CBaseEntity *pSightEnt )
bool CAI_Senses::Look( CBaseEntity *pSightEnt )
{
{
if ( WaitingUntilSeen( pSightEnt ) )
if ( WaitingUntilSeen( pSightEnt ) )
return false;
return false;
if ( ShouldSeeEntity( pSightEnt ) && CanSeeEntity( pSightEnt ) )
if ( ShouldSeeEntity( pSightEnt ) && CanSeeEntity( pSightEnt ) )
{
{
return SeeEntity( pSightEnt );
return SeeEntity( pSightEnt );
}
}
return false;
return false;
}
}


#ifdef PORTAL
#ifdef PORTAL
bool CAI_Senses::LookThroughPortal( const CProp_Portal *pPortal, CBaseEntity *pSightEnt )
bool CAI_Senses::LookThroughPortal( const CProp_Portal *pPortal, CBaseEntity *pSightEnt )
{
{
if ( WaitingUntilSeen( pSightEnt ) )
if ( WaitingUntilSeen( pSightEnt ) )
return false;
return false;


if ( ShouldSeeEntity( pSightEnt ) && CanSeeEntityThroughPortal( pPortal, pSightEnt ) )
if ( ShouldSeeEntity( pSightEnt ) && CanSeeEntityThroughPortal( pPortal, pSightEnt ) )
{
{
return SeeEntity( pSightEnt );
return SeeEntity( pSightEnt );
}
}
return false;
return false;
}
}
#endif
#endif


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


int CAI_Senses::LookForHighPriorityEntities( int iDistance )
int CAI_Senses::LookForHighPriorityEntities( int iDistance )
{
{
int nSeen = 0;
int nSeen = 0;
if ( gpGlobals->curtime - m_TimeLastLookHighPriority > AI_HIGH_PRIORITY_SEARCH_TIME )
if ( gpGlobals->curtime - m_TimeLastLookHighPriority > AI_HIGH_PRIORITY_SEARCH_TIME )
{
{
AI_PROFILE_SENSES(CAI_Senses_LookForHighPriorityEntities);
AI_PROFILE_SENSES(CAI_Senses_LookForHighPriorityEntities);
m_TimeLastLookHighPriority = gpGlobals->curtime;
m_TimeLastLookHighPriority = gpGlobals->curtime;
BeginGather();
BeginGather();
float distSq = ( iDistance * iDistance );
float distSq = ( iDistance * iDistance );
const Vector &origin = GetAbsOrigin();
const Vector &origin = GetAbsOrigin();
// Players
// Players
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
{
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );


if ( pPlayer )
if ( pPlayer )
{
{
if ( origin.DistToSqr(pPlayer->GetAbsOrigin()) < distSq && Look( pPlayer ) )
if ( origin.DistToSqr(pPlayer->GetAbsOrigin()) < distSq && Look( pPlayer ) )
{
{
nSeen++;
nSeen++;
}
}
#ifdef PORTAL
#ifdef PORTAL
else
else
{
{
CProp_Portal *pPortal = GetOuter()->FInViewConeThroughPortal( pPlayer );
CProp_Portal *pPortal = GetOuter()->FInViewConeThroughPortal( pPlayer );
if ( pPortal && UTIL_Portal_DistanceThroughPortalSqr( pPortal, origin, pPlayer->GetAbsOrigin() ) < distSq && LookThroughPortal( pPortal, pPlayer ) )
if ( pPortal && UTIL_Portal_DistanceThroughPortalSqr( pPortal, origin, pPlayer->GetAbsOrigin() ) < distSq && LookThroughPortal( pPortal, pPlayer ) )
{
{
nSeen++;
nSeen++;
}
}
}
}
#endif
#endif
}
}
}
}
EndGather( nSeen, &m_SeenHighPriority );
EndGather( nSeen, &m_SeenHighPriority );
}
}
else
else
{
{
for ( int i = m_SeenHighPriority.Count() - 1; i >= 0; --i )
for ( int i = m_SeenHighPriority.Count() - 1; i >= 0; --i )
{
{
if ( m_SeenHighPriority[i].Get() == NULL )
if ( m_SeenHighPriority[i].Get() == NULL )
m_SeenHighPriority.FastRemove( i );
m_SeenHighPriority.FastRemove( i );
}
}
nSeen = m_SeenHighPriority.Count();
nSeen = m_SeenHighPriority.Count();
}
}
return nSeen;
return nSeen;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


int CAI_Senses::LookForNPCs( int iDistance )
int CAI_Senses::LookForNPCs( int iDistance )
{
{
bool bRemoveStaleFromCache = false;
bool bRemoveStaleFromCache = false;
float distSq = ( iDistance * iDistance );
float distSq = ( iDistance * iDistance );
const Vector &origin = GetAbsOrigin();
const Vector &origin = GetAbsOrigin();
AI_Efficiency_t efficiency = GetOuter()->GetEfficiency();
AI_Efficiency_t efficiency = (GetOuter()) ? GetOuter()->GetEfficiency() : AIE_NORMAL;
float timeNPCs = ( efficiency < AIE_VERY_EFFICIENT ) ? AI_STANDARD_NPC_SEARCH_TIME : AI_EFFICIENT_NPC_SEARCH_TIME;
float timeNPCs = ( efficiency < AIE_VERY_EFFICIENT ) ? AI_STANDARD_NPC_SEARCH_TIME : AI_EFFICIENT_NPC_SEARCH_TIME;
if ( gpGlobals->curtime - m_TimeLastLookNPCs > timeNPCs )
if ( gpGlobals->curtime - m_TimeLastLookNPCs > timeNPCs )
{
{
AI_PROFILE_SENSES(CAI_Senses_LookForNPCs);
AI_PROFILE_SENSES(CAI_Senses_LookForNPCs);


m_TimeLastLookNPCs = gpGlobals->curtime;
m_TimeLastLookNPCs = gpGlobals->curtime;


if ( efficiency < AIE_SUPER_EFFICIENT )
if ( efficiency < AIE_SUPER_EFFICIENT )
{
{
int i, nSeen = 0;
int i, nSeen = 0;


BeginGather();
BeginGather();


CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
for ( i = 0; i < g_AI_Manager.NumAIs(); i++ )
for ( i = 0; i < g_AI_Manager.NumAIs(); i++ )
{
{
if ( ppAIs[i] != GetOuter() && ( ppAIs[i]->ShouldNotDistanceCull() || origin.DistToSqr(ppAIs[i]->GetAbsOrigin()) < distSq ) )
if ( ppAIs[i] != GetCharacter() && ( ppAIs[i]->ShouldNotDistanceCull() || origin.DistToSqr(ppAIs[i]->GetAbsOrigin()) < distSq ) )
{
{
if ( Look( ppAIs[i] ) )
if ( Look( ppAIs[i] ) )
{
{
nSeen++;
nSeen++;
}
}
}
}
}
}


EndGather( nSeen, &m_SeenNPCs );
EndGather( nSeen, &m_SeenNPCs );


return nSeen;
return nSeen;
}
}


bRemoveStaleFromCache = true;
bRemoveStaleFromCache = true;
// Fall through
// Fall through
}
}


for ( int i = m_SeenNPCs.Count() - 1; i >= 0; --i )
for ( int i = m_SeenNPCs.Count() - 1; i >= 0; --i )
{
{
if ( m_SeenNPCs[i].Get() == NULL )
if ( m_SeenNPCs[i].Get() == NULL )
{
{
m_SeenNPCs.FastRemove( i );
m_SeenNPCs.FastRemove( i );
}
}
else if ( bRemoveStaleFromCache )
else if ( bRemoveStaleFromCache )
{
{
if ( ( !((CAI_BaseNPC *)m_SeenNPCs[i].Get())->ShouldNotDistanceCull() &&
if ( ( !((CAI_BaseNPC *)m_SeenNPCs[i].Get())->ShouldNotDistanceCull() &&
origin.DistToSqr(m_SeenNPCs[i]->GetAbsOrigin()) > distSq ) ||
origin.DistToSqr(m_SeenNPCs[i]->GetAbsOrigin()) > distSq ) ||
!Look( m_SeenNPCs[i] ) )
!Look( m_SeenNPCs[i] ) )
{
{
m_SeenNPCs.FastRemove( i );
m_SeenNPCs.FastRemove( i );
}
}
}
}
}
}


return m_SeenNPCs.Count();
return m_SeenNPCs.Count();
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


int CAI_Senses::LookForObjects( int iDistance )
int CAI_Senses::LookForObjects( int iDistance )
{
{
const int BOX_QUERY_MASK = FL_OBJECT;
const int BOX_QUERY_MASK = FL_OBJECT;
int nSeen = 0;
int nSeen = 0;


if ( gpGlobals->curtime - m_TimeLastLookMisc > AI_MISC_SEARCH_TIME )
if ( gpGlobals->curtime - m_TimeLastLookMisc > AI_MISC_SEARCH_TIME )
{
{
AI_PROFILE_SENSES(CAI_Senses_LookForObjects);
AI_PROFILE_SENSES(CAI_Senses_LookForObjects);
m_TimeLastLookMisc = gpGlobals->curtime;
m_TimeLastLookMisc = gpGlobals->curtime;
BeginGather();
BeginGather();


float distSq = ( iDistance * iDistance );
float distSq = ( iDistance * iDistance );
const Vector &origin = GetAbsOrigin();
const Vector &origin = GetAbsOrigin();
int iter;
int iter;
CBaseEntity *pEnt = g_AI_SensedObjectsManager.GetFirst( &iter );
CBaseEntity *pEnt = g_AI_SensedObjectsManager.GetFirst( &iter );
while ( pEnt )
while ( pEnt )
{
{
if ( pEnt->GetFlags() & BOX_QUERY_MASK )
if ( pEnt->GetFlags() & BOX_QUERY_MASK )
{
{
if ( origin.DistToSqr(pEnt->GetAbsOrigin()) < distSq && Look( pEnt) )
if ( origin.DistToSqr(pEnt->GetAbsOrigin()) < distSq && Look( pEnt) )
{
{
nSeen++;
nSeen++;
}
}
}
}
pEnt = g_AI_SensedObjectsManager.GetNext( &iter );
pEnt = g_AI_SensedObjectsManager.GetNext( &iter );
}
}
EndGather( nSeen, &m_SeenMisc );
EndGather( nSeen, &m_SeenMisc );
}
}
else
else
{
{
for ( int i = m_SeenMisc.Count() - 1; i >= 0; --i )
for ( int i = m_SeenMisc.Count() - 1; i >= 0; --i )
{
{
if ( m_SeenMisc[i].Get() == NULL )
if ( m_SeenMisc[i].Get() == NULL )
m_SeenMisc.FastRemove( i );
m_SeenMisc.FastRemove( i );
}
}
nSeen = m_SeenMisc.Count();
nSeen = m_SeenMisc.Count();
}
}


return nSeen;
return nSeen;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


float CAI_Senses::GetTimeLastUpdate( CBaseEntity *pEntity )
float CAI_Senses::GetTimeLastUpdate( CBaseEntity *pEntity )
{
{
if ( !pEntity )
if ( !pEntity )
return 0;
return 0;
if ( pEntity->IsPlayer() )
if ( pEntity->IsPlayer() )
return m_TimeLastLookHighPriority;
return m_TimeLastLookHighPriority;
if ( pEntity->IsNPC() )
if ( pEntity->IsNPC() )
return m_TimeLastLookNPCs;
return m_TimeLastLookNPCs;
return m_TimeLastLookMisc;
return m_TimeLastLookMisc;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


CSound* CAI_Senses::GetFirstHeardSound( AISoundIter_t *pIter )
CSound* CAI_Senses::GetFirstHeardSound( AISoundIter_t *pIter )
{
{
int iFirst = GetAudibleList();
int iFirst = GetAudibleList();


if ( iFirst == SOUNDLIST_EMPTY )
if ( iFirst == SOUNDLIST_EMPTY )
{
{
*pIter = NULL;
*pIter = NULL;
return NULL;
return NULL;
}
}
*pIter = (AISoundIter_t)iFirst;
*pIter = (AISoundIter_t)iFirst;
return CSoundEnt::SoundPointerForIndex( iFirst );
return CSoundEnt::SoundPointerForIndex( iFirst );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


CSound* CAI_Senses::GetNextHeardSound( AISoundIter_t *pIter )
CSound* CAI_Senses::GetNextHeardSound( AISoundIter_t *pIter )
{
{
if ( !*pIter )
if ( !*pIter )
return NULL;
return NULL;


int iCurrent = (int)*pIter;
int iCurrent = (int)*pIter;
Assert( iCurrent != SOUNDLIST_EMPTY );
Assert( iCurrent != SOUNDLIST_EMPTY );
if ( iCurrent == SOUNDLIST_EMPTY )
if ( iCurrent == SOUNDLIST_EMPTY )
{
{
*pIter = NULL;
*pIter = NULL;
return NULL;
return NULL;
}
}
iCurrent = CSoundEnt::SoundPointerForIndex( iCurrent )->m_iNextAudible;
iCurrent = CSoundEnt::SoundPointerForIndex( iCurrent )->m_iNextAudible;
if ( iCurrent == SOUNDLIST_EMPTY )
if ( iCurrent == SOUNDLIST_EMPTY )
{
{
*pIter = NULL;
*pIter = NULL;
return NULL;
return NULL;
}
}
*pIter = (AISoundIter_t)iCurrent;
*pIter = (AISoundIter_t)iCurrent;
return CSoundEnt::SoundPointerForIndex( iCurrent );
return CSoundEnt::SoundPointerForIndex( iCurrent );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


CSound *CAI_Senses::GetClosestSound( bool fScent, int validTypes, bool bUsePriority )
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 flBestDist = MAX_COORD_RANGE*MAX_COORD_RANGE;// so first nearby sound will become best so far.
float flDist;
float flDist;
int iBestPriority = SOUND_PRIORITY_VERY_LOW;
int iBestPriority = SOUND_PRIORITY_VERY_LOW;
AISoundIter_t iter;
AISoundIter_t iter;
CSound *pResult = NULL;
CSound *pResult = NULL;
CSound *pCurrent = GetFirstHeardSound( &iter );
CSound *pCurrent = GetFirstHeardSound( &iter );


Vector earPosition = GetOuter()->EarPosition();
Vector earPosition = GetCharacter()->EarPosition();
while ( pCurrent )
while ( pCurrent )
{
{
if ( ( !fScent && pCurrent->FIsSound() ) ||
if ( ( !fScent && pCurrent->FIsSound() ) ||
( fScent && pCurrent->FIsScent() ) )
( fScent && pCurrent->FIsScent() ) )
{
{
if( pCurrent->IsSoundType( validTypes ) && !GetOuter()->ShouldIgnoreSound( pCurrent ) )
if( pCurrent->IsSoundType( validTypes ) && !GetCharacter()->ShouldIgnoreSound( pCurrent ) )
{
{
if( !bUsePriority || GetOuter()->GetSoundPriority(pCurrent) >= iBestPriority )
if( !bUsePriority || GetCharacter()->GetSoundPriority(pCurrent) >= iBestPriority )
{
{
flDist = ( pCurrent->GetSoundOrigin() - earPosition ).LengthSqr();
flDist = ( pCurrent->GetSoundOrigin() - earPosition ).LengthSqr();


if ( flDist < flBestDist )
if ( flDist < flBestDist )
{
{
pResult = pCurrent;
pResult = pCurrent;
flBestDist = flDist;
flBestDist = flDist;


iBestPriority = GetOuter()->GetSoundPriority(pCurrent);
iBestPriority = GetCharacter()->GetSoundPriority(pCurrent);
}
}
}
}
}
}
}
}
pCurrent = GetNextHeardSound( &iter );
pCurrent = GetNextHeardSound( &iter );
}
}
return pResult;
return pResult;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


void CAI_Senses::PerformSensing( void )
void CAI_Senses::PerformSensing( void )
{
{
AI_PROFILE_SCOPE (CAI_BaseNPC_PerformSensing);
AI_PROFILE_SCOPE (CAI_BaseNPC_PerformSensing);
// -----------------
// -----------------
// Look
// Look
// -----------------
// -----------------
if( !HasSensingFlags(SENSING_FLAGS_DONT_LOOK) )
if( !HasSensingFlags(SENSING_FLAGS_DONT_LOOK) )
Look( m_LookDist );
Look( m_LookDist );
// ------------------
// ------------------
// Listen
// Listen
// ------------------
// ------------------
if( !HasSensingFlags(SENSING_FLAGS_DONT_LISTEN) )
if( !HasSensingFlags(SENSING_FLAGS_DONT_LISTEN) )
Listen();
Listen();
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


void CAI_SensedObjectsManager::Init()
void CAI_SensedObjectsManager::Init()
{
{
CBaseEntity *pEnt = NULL;
CBaseEntity *pEnt = NULL;
while ( ( pEnt = gEntList.NextEnt( pEnt ) ) != NULL )
while ( ( pEnt = gEntList.NextEnt( pEnt ) ) != NULL )
{
{
OnEntitySpawned( pEnt );
OnEntitySpawned( pEnt );
}
}


gEntList.AddListenerEntity( this );
gEntList.AddListenerEntity( this );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


void CAI_SensedObjectsManager::Term()
void CAI_SensedObjectsManager::Term()
{
{
gEntList.RemoveListenerEntity( this );
gEntList.RemoveListenerEntity( this );
m_SensedObjects.RemoveAll();
m_SensedObjects.RemoveAll();
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


CBaseEntity *CAI_SensedObjectsManager::GetFirst( int *pIter )
CBaseEntity *CAI_SensedObjectsManager::GetFirst( int *pIter )
{
{
if ( m_SensedObjects.Count() )
if ( m_SensedObjects.Count() )
{
{
*pIter = 1;
*pIter = 1;
return m_SensedObjects[0];
return m_SensedObjects[0];
}
}
*pIter = 0;
*pIter = 0;
return NULL;
return NULL;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


CBaseEntity *CAI_SensedObjectsManager::GetNext( int *pIter )
CBaseEntity *CAI_SensedObjectsManager::GetNext( int *pIter )
{
{
int i = *pIter;
int i = *pIter;
if ( i && i < m_SensedObjects.Count() )
if ( i && i < m_SensedObjects.Count() )
{
{
(*pIter)++;
(*pIter)++;
return m_SensedObjects[i];
return m_SensedObjects[i];
}
}


*pIter = 0;
*pIter = 0;
return NULL;
return NULL;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


void CAI_SensedObjectsManager::OnEntitySpawned( CBaseEntity *pEntity )
void CAI_SensedObjectsManager::OnEntitySpawned( CBaseEntity *pEntity )
{
{
if ( ( pEntity->GetFlags() & FL_OBJECT ) && !pEntity->IsPlayer() && !pEntity->IsNPC() )
if ( ( pEntity->GetFlags() & FL_OBJECT ) && !pEntity->IsPlayer() && !pEntity->IsNPC() )
{
{
m_SensedObjects.AddToTail( pEntity );
m_SensedObjects.AddToTail( pEntity );
}
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


void CAI_SensedObjectsManager::OnEntityDeleted( CBaseEntity *pEntity )
void CAI_SensedObjectsManager::OnEntityDeleted( CBaseEntity *pEntity )
{
{
if ( ( pEntity->GetFlags() & FL_OBJECT ) && !pEntity->IsPlayer() && !pEntity->IsNPC() )
if ( ( pEntity->GetFlags() & FL_OBJECT ) && !pEntity->IsPlayer() && !pEntity->IsNPC() )
{
{
int i = m_SensedObjects.Find( pEntity );
int i = m_SensedObjects.Find( pEntity );
if ( i != m_SensedObjects.InvalidIndex() )
if ( i != m_SensedObjects.InvalidIndex() )
m_SensedObjects.FastRemove( i );
m_SensedObjects.FastRemove( i );
}
}
}
}


void CAI_SensedObjectsManager::AddEntity( CBaseEntity *pEntity )
void CAI_SensedObjectsManager::AddEntity( CBaseEntity *pEntity )
{
{
if ( m_SensedObjects.Find( pEntity ) != m_SensedObjects.InvalidIndex() )
if ( m_SensedObjects.Find( pEntity ) != m_SensedObjects.InvalidIndex() )
return;
return;


// We shouldn't be adding players or NPCs to this list
// We shouldn't be adding players or NPCs to this list
Assert( !pEntity->IsPlayer() && !pEntity->IsNPC() );
Assert( !pEntity->IsPlayer() && !pEntity->IsNPC() );


// Add the object flag so it gets removed when it dies
// Add the object flag so it gets removed when it dies
pEntity->AddFlag( FL_OBJECT );
pEntity->AddFlag( FL_OBJECT );
m_SensedObjects.AddToTail( pEntity );
m_SensedObjects.AddToTail( pEntity );
}
}


//=============================================================================
//=============================================================================