Diff
checker
Text
Text
Images
Documents
Excel
Folders
Legal
Enterprise
Desktop
Pricing
Sign in
Download Diffchecker Desktop
Compare text
Find the difference between two text files
Tools
History
Real-time editor
Hide whitespace changes
Hide unchanged lines
Disable line wrap
Layout
Split
Unified
Diff precision
Smart
Word
Char
Text styles
Change appearance
Syntax highlighting
Choose syntax
Ignore
Transform text
Go to first change
Edit input
Diffchecker Desktop
The most secure way to run Diffchecker. Get the Diffchecker Desktop app: your diffs never leave your computer!
Get Desktop
Untitled diff
Created
9 years ago
Diff never expires
Clear
Export
Share
Explain
36 removals
Lines
Total
Removed
Characters
Total
Removed
To continue using this feature, upgrade to
Diff
checker
Pro
View Pricing
752 lines
Copy
43 additions
Lines
Total
Added
Characters
Total
Added
To continue using this feature, upgrade to
Diff
checker
Pro
View Pricing
755 lines
Copy
//========= 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 )
{
{
Copy
Copied
Copy
Copied
if ( pSound->m_hOwner.Get() ==
GetOuter
() )
if ( pSound->m_hOwner.Get() ==
GetCharacter
() )
return false;
return false;
Copy
Copied
Copy
Copied
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;
}
}
Copy
Copied
Copy
Copied
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?
Copy
Copied
Copy
Copied
float flHearDistanceSq = pSound->Volume() *
GetOuter
()->HearingSensitivity();
float flHearDistanceSq = pSound->Volume() *
GetCharacter
()->HearingSensitivity();
flHearDistanceSq *= flHearDistanceSq;
flHearDistanceSq *= flHearDistanceSq;
Copy
Copied
Copy
Copied
if( pSound->GetSoundOrigin().DistToSqr(
GetOuter
()->EarPosition() ) <= flHearDistanceSq )
if( pSound->GetSoundOrigin().DistToSqr(
GetCharacter
()->EarPosition() ) <= flHearDistanceSq )
{
{
Copy
Copied
Copy
Copied
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;
Copy
Copied
Copy
Copied
int iSoundMask =
GetOuter
()->GetSoundInterests();
int iSoundMask =
GetCharacter
()->GetSoundInterests();
Copy
Copied
Copy
Copied
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();
}
}
}
}
Copy
Copied
Copy
Copied
GetOuter
()->OnListened();
GetCharacter
()->OnListened();
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CAI_Senses::ShouldSeeEntity( CBaseEntity *pSightEnt )
bool CAI_Senses::ShouldSeeEntity( CBaseEntity *pSightEnt )
{
{
Copy
Copied
Copy
Copied
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;
Copy
Copied
Copy
Copied
if (
!pSightEnt->CanBeSeenBy( GetOuter() ) )
if (
GetOuter() &&
!pSightEnt->CanBeSeenBy( GetOuter() ) )
return false;
return false;
Copy
Copied
Copy
Copied
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 )
{
{
Copy
Copied
Copy
Copied
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 )
{
{
Copy
Copied
Copy
Copied
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 )
{
{
Copy
Copied
Copy
Copied
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() )
Copy
Copied
Copy
Copied
&& 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.
Copy
Copied
Copy
Copied
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 )
{
{
Copy
Copied
Copy
Copied
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
Copy
Copied
Copy
Copied
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 )
{
{
Copy
Copied
Copy
Copied
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;
}
}
Copy
Copied
Copy
Copied
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;
}
}
Copy
Copied
Copy
Copied
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();
Copy
Copied
Copy
Copied
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++ )
{
{
Copy
Copied
Copy
Copied
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 );
Copy
Copied
Copy
Copied
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() ) )
{
{
Copy
Copied
Copy
Copied
if( pCurrent->IsSoundType( validTypes ) && !
GetOuter
()->ShouldIgnoreSound( pCurrent ) )
if( pCurrent->IsSoundType( validTypes ) && !
GetCharacter
()->ShouldIgnoreSound( pCurrent ) )
{
{
Copy
Copied
Copy
Copied
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;
Copy
Copied
Copy
Copied
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 );
}
}
//=============================================================================
//=============================================================================
Copy
Copied
Copy
Copied
Saved diffs
Original text
Open file
//========= 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 ); } //=============================================================================
Changed text
Open file
//========= 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() == GetCharacter() ) return false; 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 // 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() * GetCharacter()->HearingSensitivity(); flHearDistanceSq *= flHearDistanceSq; if( pSound->GetSoundOrigin().DistToSqr( GetCharacter()->EarPosition() ) <= flHearDistanceSq ) { return GetCharacter()->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 = GetCharacter()->GetSoundInterests(); if ( iSoundMask != SOUND_NONE && !(GetCharacter()->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(); } } GetCharacter()->OnListened(); } //----------------------------------------------------------------------------- bool CAI_Senses::ShouldSeeEntity( CBaseEntity *pSightEnt ) { if ( pSightEnt == GetCharacter() || !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 ( GetOuter() && !pSightEnt->CanBeSeenBy( GetOuter() ) ) return false; if ( !GetCharacter()->QuerySeeEntity( pSightEnt, true ) ) return false; return true; } //----------------------------------------------------------------------------- bool CAI_Senses::CanSeeEntity( CBaseEntity *pSightEnt ) { return GetCharacter()->IsAbleToSee( pSightEnt, CBaseCombatCharacter::USE_FOV ); //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 = GetCharacter()->m_pLink; GetCharacter()->m_pLink = pSightEnt; } //----------------------------------------------------------------------------- bool CAI_Senses::WaitingUntilSeen( CBaseEntity *pSightEnt ) { if ( GetCharacter()->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( GetCharacter() ) && FBoxVisible( pSightEnt, static_cast<CBaseEntity*>(GetCharacter()), zero ) ) { // player sees us, become normal now. GetCharacter()->m_spawnflags &= ~SF_NPC_WAIT_TILL_SEEN; return false; } } return true; } return false; } //----------------------------------------------------------------------------- bool CAI_Senses::SeeEntity( CBaseEntity *pSightEnt ) { GetCharacter()->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 GetCharacter()->m_pLink = NULL; } //----------------------------------------------------------------------------- void CAI_Senses::EndGather( int nSeen, CUtlVector<EHANDLE> *pResult ) { pResult->SetCount( nSeen ); if ( nSeen ) { CBaseEntity *pCurrent = GetCharacter()->m_pLink; for (int i = 0; i < nSeen; i++ ) { Assert( pCurrent ); (*pResult)[i].Set( pCurrent ); pCurrent = pCurrent->m_pLink; } GetCharacter()->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; } GetCharacter()->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()) ? GetOuter()->GetEfficiency() : AIE_NORMAL; 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] != GetCharacter() && ( 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 = GetCharacter()->EarPosition(); while ( pCurrent ) { if ( ( !fScent && pCurrent->FIsSound() ) || ( fScent && pCurrent->FIsScent() ) ) { if( pCurrent->IsSoundType( validTypes ) && !GetCharacter()->ShouldIgnoreSound( pCurrent ) ) { if( !bUsePriority || GetCharacter()->GetSoundPriority(pCurrent) >= iBestPriority ) { flDist = ( pCurrent->GetSoundOrigin() - earPosition ).LengthSqr(); if ( flDist < flBestDist ) { pResult = pCurrent; flBestDist = flDist; iBestPriority = GetCharacter()->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 ); } //=============================================================================
Find difference