Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

749 lines
19 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ai_senses.h"
  9. #include "soundent.h"
  10. #include "team.h"
  11. #include "ai_basenpc.h"
  12. #include "saverestore_utlvector.h"
  13. #ifdef PORTAL
  14. #include "portal_util_shared.h"
  15. #endif
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. // Use this to disable caching and other optimizations in senses
  19. #define DEBUG_SENSES 1
  20. #ifdef DEBUG_SENSES
  21. #define AI_PROFILE_SENSES(tag) AI_PROFILE_SCOPE(tag)
  22. #else
  23. #define AI_PROFILE_SENSES(tag) ((void)0)
  24. #endif
  25. const float AI_STANDARD_NPC_SEARCH_TIME = .25;
  26. const float AI_EFFICIENT_NPC_SEARCH_TIME = .35;
  27. const float AI_HIGH_PRIORITY_SEARCH_TIME = 0.15;
  28. const float AI_MISC_SEARCH_TIME = 0.45;
  29. //-----------------------------------------------------------------------------
  30. CAI_SensedObjectsManager g_AI_SensedObjectsManager;
  31. //-----------------------------------------------------------------------------
  32. #pragma pack(push)
  33. #pragma pack(1)
  34. struct AISightIterVal_t
  35. {
  36. char array;
  37. short iNext;
  38. char SeenArray;
  39. #ifdef PLATFORM_64BITS
  40. uint32 unused;
  41. #endif
  42. };
  43. #pragma pack(pop)
  44. //=============================================================================
  45. //
  46. // CAI_Senses
  47. //
  48. //=============================================================================
  49. BEGIN_SIMPLE_DATADESC( CAI_Senses )
  50. DEFINE_FIELD( m_LookDist, FIELD_FLOAT ),
  51. DEFINE_FIELD( m_LastLookDist, FIELD_FLOAT ),
  52. DEFINE_FIELD( m_TimeLastLook, FIELD_TIME ),
  53. DEFINE_FIELD( m_iSensingFlags, FIELD_INTEGER ),
  54. // m_iAudibleList (no way to save?)
  55. DEFINE_UTLVECTOR(m_SeenHighPriority, FIELD_EHANDLE ),
  56. DEFINE_UTLVECTOR(m_SeenNPCs, FIELD_EHANDLE ),
  57. DEFINE_UTLVECTOR(m_SeenMisc, FIELD_EHANDLE ),
  58. // m_SeenArrays (not saved, rebuilt)
  59. // Could fold these three and above timer into one concept, but would invalidate savegames
  60. DEFINE_FIELD( m_TimeLastLookHighPriority, FIELD_TIME ),
  61. DEFINE_FIELD( m_TimeLastLookNPCs, FIELD_TIME ),
  62. DEFINE_FIELD( m_TimeLastLookMisc, FIELD_TIME ),
  63. END_DATADESC()
  64. //-----------------------------------------------------------------------------
  65. bool CAI_Senses::CanHearSound( CSound *pSound )
  66. {
  67. if ( pSound->m_hOwner.Get() == GetOuter() )
  68. return false;
  69. if( GetOuter()->GetState() == NPC_STATE_SCRIPT && pSound->IsSoundType( SOUND_DANGER ) )
  70. {
  71. // For now, don't hear danger in scripted sequences. This probably isn't a
  72. // good long term solution, but it makes the Bank Exterior work better.
  73. return false;
  74. }
  75. if ( GetOuter()->IsInAScript() )
  76. return false;
  77. // @TODO (toml 10-18-02): what about player sounds and notarget?
  78. float flHearDistanceSq = pSound->Volume() * GetOuter()->HearingSensitivity();
  79. flHearDistanceSq *= flHearDistanceSq;
  80. if( pSound->GetSoundOrigin().DistToSqr( GetOuter()->EarPosition() ) <= flHearDistanceSq )
  81. {
  82. return GetOuter()->QueryHearSound( pSound );
  83. }
  84. return false;
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Listen - npcs dig through the active sound list for
  88. // any sounds that may interest them. (smells, too!)
  89. void CAI_Senses::Listen( void )
  90. {
  91. m_iAudibleList = SOUNDLIST_EMPTY;
  92. int iSoundMask = GetOuter()->GetSoundInterests();
  93. if ( iSoundMask != SOUND_NONE && !(GetOuter()->HasSpawnFlags(SF_NPC_WAIT_TILL_SEEN)) )
  94. {
  95. int iSound = CSoundEnt::ActiveList();
  96. while ( iSound != SOUNDLIST_EMPTY )
  97. {
  98. CSound *pCurrentSound = CSoundEnt::SoundPointerForIndex( iSound );
  99. if ( pCurrentSound && (iSoundMask & pCurrentSound->SoundType()) && CanHearSound( pCurrentSound ) )
  100. {
  101. // the npc cares about this sound, and it's close enough to hear.
  102. pCurrentSound->m_iNextAudible = m_iAudibleList;
  103. m_iAudibleList = iSound;
  104. }
  105. iSound = pCurrentSound->NextSound();
  106. }
  107. }
  108. GetOuter()->OnListened();
  109. }
  110. //-----------------------------------------------------------------------------
  111. bool CAI_Senses::ShouldSeeEntity( CBaseEntity *pSightEnt )
  112. {
  113. if ( pSightEnt == GetOuter() )
  114. return false;
  115. if ( GetOuter()->OnlySeeAliveEntities() && !pSightEnt->IsAlive() )
  116. return false;
  117. if ( pSightEnt->IsPlayer() && ( pSightEnt->GetFlags() & FL_NOTARGET ) )
  118. return false;
  119. // don't notice anyone waiting to be seen by the player
  120. if ( pSightEnt->m_spawnflags & SF_NPC_WAIT_TILL_SEEN )
  121. return false;
  122. if ( !pSightEnt->CanBeSeenBy( GetOuter() ) )
  123. return false;
  124. if ( !GetOuter()->QuerySeeEntity( pSightEnt, true ) )
  125. return false;
  126. return true;
  127. }
  128. //-----------------------------------------------------------------------------
  129. bool CAI_Senses::CanSeeEntity( CBaseEntity *pSightEnt )
  130. {
  131. return ( GetOuter()->FInViewCone( pSightEnt ) && GetOuter()->FVisible( pSightEnt ) );
  132. }
  133. #ifdef PORTAL
  134. bool CAI_Senses::CanSeeEntityThroughPortal( const CPortal_Base2D *pPortal, CBaseEntity *pSightEnt )
  135. {
  136. return GetOuter()->FVisibleThroughPortal( pPortal, pSightEnt );
  137. }
  138. #endif
  139. //-----------------------------------------------------------------------------
  140. bool CAI_Senses::DidSeeEntity( CBaseEntity *pSightEnt ) const
  141. {
  142. AISightIter_t iter = (AISightIter_t)(-1);
  143. CBaseEntity *pTestEnt;
  144. pTestEnt = GetFirstSeenEntity( &iter );
  145. while( pTestEnt )
  146. {
  147. if ( pSightEnt == pTestEnt )
  148. return true;
  149. pTestEnt = GetNextSeenEntity( &iter );
  150. }
  151. return false;
  152. }
  153. //-----------------------------------------------------------------------------
  154. void CAI_Senses::NoteSeenEntity( CBaseEntity *pSightEnt )
  155. {
  156. pSightEnt->m_pLink = GetOuter()->m_pLink;
  157. GetOuter()->m_pLink = pSightEnt;
  158. }
  159. //-----------------------------------------------------------------------------
  160. bool CAI_Senses::WaitingUntilSeen( CBaseEntity *pSightEnt )
  161. {
  162. if ( GetOuter()->m_spawnflags & SF_NPC_WAIT_TILL_SEEN )
  163. {
  164. if ( pSightEnt->IsPlayer() )
  165. {
  166. CBasePlayer *pPlayer = ToBasePlayer( pSightEnt );
  167. Vector zero = Vector(0,0,0);
  168. // don't link this client in the list if the npc is wait till seen and the player isn't facing the npc
  169. if ( pPlayer
  170. // && pPlayer->FVisible( GetOuter() )
  171. && pPlayer->FInViewCone( GetOuter() )
  172. && FBoxVisible( pSightEnt, static_cast<CBaseEntity*>(GetOuter()), zero ) )
  173. {
  174. // player sees us, become normal now.
  175. GetOuter()->m_spawnflags &= ~SF_NPC_WAIT_TILL_SEEN;
  176. return false;
  177. }
  178. }
  179. return true;
  180. }
  181. return false;
  182. }
  183. //-----------------------------------------------------------------------------
  184. bool CAI_Senses::SeeEntity( CBaseEntity *pSightEnt )
  185. {
  186. GetOuter()->OnSeeEntity( pSightEnt );
  187. // insert at the head of my sight list
  188. NoteSeenEntity( pSightEnt );
  189. return true;
  190. }
  191. //-----------------------------------------------------------------------------
  192. CBaseEntity *CAI_Senses::GetFirstSeenEntity( AISightIter_t *pIter, seentype_t iSeenType ) const
  193. {
  194. COMPILE_TIME_ASSERT( sizeof( AISightIter_t ) == sizeof( AISightIterVal_t ) );
  195. AISightIterVal_t *pIterVal = (AISightIterVal_t *)pIter;
  196. // If we're searching for a specific type, start in that array
  197. pIterVal->SeenArray = (char)iSeenType;
  198. int iFirstArray = ( iSeenType == SEEN_ALL ) ? 0 : iSeenType;
  199. for ( int i = iFirstArray; i < ARRAYSIZE( m_SeenArrays ); i++ )
  200. {
  201. if ( m_SeenArrays[i]->Count() != 0 )
  202. {
  203. pIterVal->array = i;
  204. pIterVal->iNext = 1;
  205. return (*m_SeenArrays[i])[0];
  206. }
  207. }
  208. (*pIter) = (AISightIter_t)(-1);
  209. return NULL;
  210. }
  211. //-----------------------------------------------------------------------------
  212. CBaseEntity *CAI_Senses::GetNextSeenEntity( AISightIter_t *pIter ) const
  213. {
  214. if ( ((intp)*pIter) != -1 )
  215. {
  216. AISightIterVal_t *pIterVal = (AISightIterVal_t *)pIter;
  217. for ( int i = pIterVal->array; i < ARRAYSIZE( m_SeenArrays ); i++ )
  218. {
  219. for ( int j = pIterVal->iNext; j < m_SeenArrays[i]->Count(); j++ )
  220. {
  221. if ( (*m_SeenArrays[i])[j].Get() != NULL )
  222. {
  223. pIterVal->array = i;
  224. pIterVal->iNext = j+1;
  225. return (*m_SeenArrays[i])[j];
  226. }
  227. }
  228. pIterVal->iNext = 0;
  229. // If we're searching for a specific type, don't move to the next array
  230. if ( pIterVal->SeenArray != SEEN_ALL )
  231. break;
  232. }
  233. (*pIter) = (AISightIter_t)(-1);
  234. }
  235. return NULL;
  236. }
  237. //-----------------------------------------------------------------------------
  238. void CAI_Senses::BeginGather()
  239. {
  240. // clear my sight list
  241. GetOuter()->m_pLink = NULL;
  242. }
  243. //-----------------------------------------------------------------------------
  244. void CAI_Senses::EndGather( int nSeen, CUtlVector<EHANDLE> *pResult )
  245. {
  246. pResult->SetCount( nSeen );
  247. if ( nSeen )
  248. {
  249. CBaseEntity *pCurrent = GetOuter()->m_pLink;
  250. for (int i = 0; i < nSeen; i++ )
  251. {
  252. Assert( pCurrent );
  253. (*pResult)[i].Set( pCurrent );
  254. pCurrent = pCurrent->m_pLink;
  255. }
  256. GetOuter()->m_pLink = NULL;
  257. }
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Look - Base class npc function to find enemies or
  261. // food by sight. iDistance is distance ( in units ) that the
  262. // npc can see.
  263. //
  264. // Sets the sight bits of the m_afConditions mask to indicate
  265. // which types of entities were sighted.
  266. // Function also sets the Looker's m_pLink
  267. // to the head of a link list that contains all visible ents.
  268. // (linked via each ent's m_pLink field)
  269. //
  270. void CAI_Senses::Look( int iDistance )
  271. {
  272. if ( m_TimeLastLook != gpGlobals->curtime || m_LastLookDist != iDistance )
  273. {
  274. //-----------------------------
  275. LookForHighPriorityEntities( iDistance );
  276. LookForNPCs( iDistance);
  277. LookForObjects( iDistance );
  278. //-----------------------------
  279. m_LastLookDist = iDistance;
  280. m_TimeLastLook = gpGlobals->curtime;
  281. }
  282. GetOuter()->OnLooked( iDistance );
  283. }
  284. //-----------------------------------------------------------------------------
  285. bool CAI_Senses::Look( CBaseEntity *pSightEnt )
  286. {
  287. if ( WaitingUntilSeen( pSightEnt ) )
  288. return false;
  289. if ( ShouldSeeEntity( pSightEnt ) && CanSeeEntity( pSightEnt ) )
  290. {
  291. return SeeEntity( pSightEnt );
  292. }
  293. return false;
  294. }
  295. #ifdef PORTAL
  296. bool CAI_Senses::LookThroughPortal( const CPortal_Base2D *pPortal, CBaseEntity *pSightEnt )
  297. {
  298. if ( WaitingUntilSeen( pSightEnt ) )
  299. return false;
  300. if ( ShouldSeeEntity( pSightEnt ) && CanSeeEntityThroughPortal( pPortal, pSightEnt ) )
  301. {
  302. return SeeEntity( pSightEnt );
  303. }
  304. return false;
  305. }
  306. #endif
  307. //-----------------------------------------------------------------------------
  308. int CAI_Senses::LookForHighPriorityEntities( int iDistance )
  309. {
  310. int nSeen = 0;
  311. if ( gpGlobals->curtime - m_TimeLastLookHighPriority > AI_HIGH_PRIORITY_SEARCH_TIME )
  312. {
  313. AI_PROFILE_SENSES(CAI_Senses_LookForHighPriorityEntities);
  314. m_TimeLastLookHighPriority = gpGlobals->curtime;
  315. BeginGather();
  316. const Vector &origin = GetAbsOrigin();
  317. // Players
  318. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  319. {
  320. CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
  321. if ( pPlayer )
  322. {
  323. if ( IsWithinSenseDistance( pPlayer->GetAbsOrigin(), origin, iDistance ) && Look( pPlayer ) )
  324. {
  325. nSeen++;
  326. }
  327. #ifdef PORTAL
  328. else if( !HasSensingFlags(SENSING_FLAGS_IGNORE_PORTALS) )
  329. {
  330. CPortal_Base2D *pPortal = GetOuter()->FInViewConeThroughPortal( pPlayer );
  331. float distSq = ( iDistance * iDistance );
  332. if ( pPortal && UTIL_Portal_DistanceThroughPortalSqr( pPortal, origin, pPlayer->GetAbsOrigin() ) < distSq && LookThroughPortal( pPortal, pPlayer ) )
  333. {
  334. nSeen++;
  335. }
  336. }
  337. #endif
  338. }
  339. }
  340. EndGather( nSeen, &m_SeenHighPriority );
  341. }
  342. else
  343. {
  344. for ( int i = m_SeenHighPriority.Count() - 1; i >= 0; --i )
  345. {
  346. if ( m_SeenHighPriority[i].Get() == NULL )
  347. m_SeenHighPriority.FastRemove( i );
  348. }
  349. nSeen = m_SeenHighPriority.Count();
  350. }
  351. return nSeen;
  352. }
  353. //-----------------------------------------------------------------------------
  354. int CAI_Senses::LookForNPCs( int iDistance )
  355. {
  356. bool bRemoveStaleFromCache = false;
  357. const Vector &origin = GetAbsOrigin();
  358. AI_Efficiency_t efficiency = GetOuter()->GetEfficiency();
  359. float timeNPCs = ( efficiency < AIE_VERY_EFFICIENT ) ? AI_STANDARD_NPC_SEARCH_TIME : AI_EFFICIENT_NPC_SEARCH_TIME;
  360. if ( gpGlobals->curtime - m_TimeLastLookNPCs > timeNPCs )
  361. {
  362. AI_PROFILE_SENSES(CAI_Senses_LookForNPCs);
  363. m_TimeLastLookNPCs = gpGlobals->curtime;
  364. if ( efficiency < AIE_SUPER_EFFICIENT )
  365. {
  366. int i, nSeen = 0;
  367. BeginGather();
  368. CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
  369. for ( i = 0; i < g_AI_Manager.NumAIs(); i++ )
  370. {
  371. if ( ppAIs[i] != GetOuter() && ( ppAIs[i]->ShouldNotDistanceCull() || IsWithinSenseDistance( origin, ppAIs[i]->GetAbsOrigin(), iDistance ) ) )
  372. {
  373. if ( Look( ppAIs[i] ) )
  374. {
  375. nSeen++;
  376. }
  377. }
  378. }
  379. EndGather( nSeen, &m_SeenNPCs );
  380. return nSeen;
  381. }
  382. bRemoveStaleFromCache = true;
  383. // Fall through
  384. }
  385. for ( int i = m_SeenNPCs.Count() - 1; i >= 0; --i )
  386. {
  387. if ( m_SeenNPCs[i].Get() == NULL )
  388. {
  389. m_SeenNPCs.FastRemove( i );
  390. }
  391. else if ( bRemoveStaleFromCache )
  392. {
  393. if ( ( !((CAI_BaseNPC *)m_SeenNPCs[i].Get())->ShouldNotDistanceCull() &&
  394. !IsWithinSenseDistance( origin, m_SeenNPCs[i]->GetAbsOrigin(), iDistance ) ) ||
  395. !Look( m_SeenNPCs[i] ) )
  396. {
  397. m_SeenNPCs.FastRemove( i );
  398. }
  399. }
  400. }
  401. return m_SeenNPCs.Count();
  402. }
  403. //-----------------------------------------------------------------------------
  404. int CAI_Senses::LookForObjects( int iDistance )
  405. {
  406. const int BOX_QUERY_MASK = FL_OBJECT;
  407. int nSeen = 0;
  408. if ( gpGlobals->curtime - m_TimeLastLookMisc > AI_MISC_SEARCH_TIME )
  409. {
  410. AI_PROFILE_SENSES(CAI_Senses_LookForObjects);
  411. m_TimeLastLookMisc = gpGlobals->curtime;
  412. BeginGather();
  413. const Vector &origin = GetAbsOrigin();
  414. int iter = 0;
  415. CBaseEntity *pEnt = g_AI_SensedObjectsManager.GetFirst( &iter );
  416. while ( pEnt )
  417. {
  418. if ( pEnt->GetFlags() & BOX_QUERY_MASK )
  419. {
  420. if ( IsWithinSenseDistance( origin, pEnt->GetAbsOrigin(), iDistance ) && Look( pEnt) )
  421. {
  422. nSeen++;
  423. }
  424. }
  425. pEnt = g_AI_SensedObjectsManager.GetNext( &iter );
  426. }
  427. EndGather( nSeen, &m_SeenMisc );
  428. }
  429. else
  430. {
  431. for ( int i = m_SeenMisc.Count() - 1; i >= 0; --i )
  432. {
  433. if ( m_SeenMisc[i].Get() == NULL )
  434. m_SeenMisc.FastRemove( i );
  435. }
  436. nSeen = m_SeenMisc.Count();
  437. }
  438. return nSeen;
  439. }
  440. //-----------------------------------------------------------------------------
  441. float CAI_Senses::GetTimeLastUpdate( CBaseEntity *pEntity )
  442. {
  443. if ( !pEntity )
  444. return 0;
  445. if ( pEntity->IsPlayer() )
  446. return m_TimeLastLookHighPriority;
  447. if ( pEntity->IsNPC() )
  448. return m_TimeLastLookNPCs;
  449. return m_TimeLastLookMisc;
  450. }
  451. //-----------------------------------------------------------------------------
  452. CSound* CAI_Senses::GetFirstHeardSound( AISoundIter_t *pIter )
  453. {
  454. int iFirst = GetAudibleList();
  455. if ( iFirst == SOUNDLIST_EMPTY )
  456. {
  457. *pIter = (AISoundIter_t)SOUNDLIST_EMPTY;
  458. return NULL;
  459. }
  460. *pIter = (AISoundIter_t)(intp)iFirst;
  461. return CSoundEnt::SoundPointerForIndex( iFirst );
  462. }
  463. //-----------------------------------------------------------------------------
  464. CSound* CAI_Senses::GetNextHeardSound( AISoundIter_t *pIter )
  465. {
  466. int iCurrent = size_cast<int>( (intp)*pIter );
  467. if ( iCurrent == SOUNDLIST_EMPTY )
  468. {
  469. *pIter = (AISoundIter_t)SOUNDLIST_EMPTY;
  470. return NULL;
  471. }
  472. iCurrent = CSoundEnt::SoundPointerForIndex( iCurrent )->m_iNextAudible;
  473. *pIter = (AISoundIter_t)(intp)iCurrent;
  474. if ( iCurrent == SOUNDLIST_EMPTY )
  475. return NULL;
  476. return CSoundEnt::SoundPointerForIndex( iCurrent );
  477. }
  478. //-----------------------------------------------------------------------------
  479. CSound *CAI_Senses::GetClosestSound( bool fScent, int validTypes, bool bUsePriority )
  480. {
  481. float flBestDist = MAX_COORD_RANGE*MAX_COORD_RANGE;// so first nearby sound will become best so far.
  482. float flDist;
  483. int iBestPriority = SOUND_PRIORITY_VERY_LOW;
  484. AISoundIter_t iter;
  485. CSound *pResult = NULL;
  486. CSound *pCurrent = GetFirstHeardSound( &iter );
  487. Vector earPosition = GetOuter()->EarPosition();
  488. while ( pCurrent )
  489. {
  490. if ( ( !fScent && pCurrent->FIsSound() ) ||
  491. ( fScent && pCurrent->FIsScent() ) )
  492. {
  493. if( pCurrent->IsSoundType( validTypes ) && !GetOuter()->ShouldIgnoreSound( pCurrent ) )
  494. {
  495. if( !bUsePriority || GetOuter()->GetSoundPriority(pCurrent) >= iBestPriority )
  496. {
  497. flDist = ( pCurrent->GetSoundOrigin() - earPosition ).LengthSqr();
  498. if ( flDist < flBestDist )
  499. {
  500. pResult = pCurrent;
  501. flBestDist = flDist;
  502. iBestPriority = GetOuter()->GetSoundPriority(pCurrent);
  503. }
  504. }
  505. }
  506. }
  507. pCurrent = GetNextHeardSound( &iter );
  508. }
  509. return pResult;
  510. }
  511. //-----------------------------------------------------------------------------
  512. void CAI_Senses::PerformSensing( void )
  513. {
  514. AI_PROFILE_SCOPE (CAI_BaseNPC_PerformSensing);
  515. // -----------------
  516. // Look
  517. // -----------------
  518. if( !HasSensingFlags(SENSING_FLAGS_DONT_LOOK) )
  519. Look( m_LookDist );
  520. // ------------------
  521. // Listen
  522. // ------------------
  523. if( !HasSensingFlags(SENSING_FLAGS_DONT_LISTEN) )
  524. Listen();
  525. }
  526. //-----------------------------------------------------------------------------
  527. //-----------------------------------------------------------------------------
  528. void CAI_SensedObjectsManager::Init()
  529. {
  530. CBaseEntity *pEnt = NULL;
  531. while ( ( pEnt = gEntList.NextEnt( pEnt ) ) != NULL )
  532. {
  533. OnEntitySpawned( pEnt );
  534. }
  535. gEntList.AddListenerEntity( this );
  536. }
  537. //-----------------------------------------------------------------------------
  538. void CAI_SensedObjectsManager::Term()
  539. {
  540. gEntList.RemoveListenerEntity( this );
  541. m_SensedObjects.RemoveAll();
  542. }
  543. //-----------------------------------------------------------------------------
  544. CBaseEntity *CAI_SensedObjectsManager::GetFirst( int *pIter )
  545. {
  546. if ( m_SensedObjects.Count() )
  547. {
  548. *pIter = 1;
  549. return m_SensedObjects[0];
  550. }
  551. *pIter = 0;
  552. return NULL;
  553. }
  554. //-----------------------------------------------------------------------------
  555. CBaseEntity *CAI_SensedObjectsManager::GetNext( int *pIter )
  556. {
  557. int i = *pIter;
  558. if ( i && i < m_SensedObjects.Count() )
  559. {
  560. (*pIter)++;
  561. return m_SensedObjects[i];
  562. }
  563. *pIter = 0;
  564. return NULL;
  565. }
  566. //-----------------------------------------------------------------------------
  567. void CAI_SensedObjectsManager::OnEntitySpawned( CBaseEntity *pEntity )
  568. {
  569. if ( ( pEntity->GetFlags() & FL_OBJECT ) && !pEntity->IsPlayer() && !pEntity->IsNPC() )
  570. {
  571. m_SensedObjects.AddToTail( pEntity );
  572. }
  573. }
  574. //-----------------------------------------------------------------------------
  575. void CAI_SensedObjectsManager::OnEntityDeleted( CBaseEntity *pEntity )
  576. {
  577. if ( ( pEntity->GetFlags() & FL_OBJECT ) && !pEntity->IsPlayer() && !pEntity->IsNPC() )
  578. {
  579. int i = m_SensedObjects.Find( pEntity );
  580. if ( i != m_SensedObjects.InvalidIndex() )
  581. m_SensedObjects.FastRemove( i );
  582. }
  583. }
  584. void CAI_SensedObjectsManager::AddEntity( CBaseEntity *pEntity )
  585. {
  586. if ( m_SensedObjects.Find( pEntity ) != m_SensedObjects.InvalidIndex() )
  587. return;
  588. // We shouldn't be adding players or NPCs to this list
  589. Assert( !pEntity->IsPlayer() && !pEntity->IsNPC() );
  590. // Add the object flag so it gets removed when it dies
  591. pEntity->AddFlag( FL_OBJECT );
  592. m_SensedObjects.AddToTail( pEntity );
  593. }
  594. //=============================================================================