Diff
checker
텍스트
텍스트
이미지
문서
Excel
폴더
Legal
Enterprise
데스크톱
요금제
로그인
데스크톱 앱 다운로드
텍스트 비교
두 텍스트 파일의 차이점을 찾아보세요
도구
기록
실시간 편집
공백 변경 숨기기
변경 없는 행 숨기기
줄바꿈 비활성화
레이아웃
나란히 보기
합쳐 보기
비교 단위
스마트
단어
글자
텍스트 스타일
모양 변경
구문 강조
언어 선택
제외
텍스트 변환
첫 변경으로
수정
Diffchecker Desktop
가장 안전하게 Diffchecker를 사용하는 방법. 데스크톱 앱을 사용하면 비교 데이터가 외부로 전송되지 않습니다!
데스크톱 앱 받기
Untitled diff
생성일
9년 전
비교 결과 만료 없음
초기화
내보내기
공유
설명
36 삭제
행
총
삭제
글자
총
삭제
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
752 행
복사
43 추가
행
총
추가
글자
총
추가
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
755 행
복사
//========= 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 );
}
}
//=============================================================================
//=============================================================================
복사
복사됨
복사
복사됨
저장된 비교 결과
원본
파일 열기
//========= 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 ); } //=============================================================================
수정본
파일 열기
//========= 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 ); } //=============================================================================
비교하기