Team Fortress 2 Source Code as on 22/4/2020
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.

752 lines
19 KiB

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