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.

2010 lines
53 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "entitylist.h"
  9. #include "utlvector.h"
  10. #include "igamesystem.h"
  11. #include "collisionutils.h"
  12. #include "utlsortvector.h"
  13. #include "tier0/vprof.h"
  14. #include "mapentities.h"
  15. #include "client.h"
  16. #include "ai_initutils.h"
  17. #include "globalstate.h"
  18. #include "datacache/imdlcache.h"
  19. #include "tier1/utlhash.h"
  20. #ifdef PORTAL2
  21. #include "team.h"
  22. #include "portal_mp_gamerules.h"
  23. #endif // PORTAL2
  24. #ifdef HL2_DLL
  25. #include "npc_playercompanion.h"
  26. #endif // HL2_DLL
  27. // memdbgon must be the last include file in a .cpp file!!!
  28. #include "tier0/memdbgon.h"
  29. void SceneManager_ClientActive( CBasePlayer *player );
  30. static CUtlVector<IServerNetworkable*> g_DeleteList;
  31. CGlobalEntityList gEntList;
  32. CBaseEntityList *g_pEntityList = &gEntList;
  33. class CAimTargetManager : public IEntityListener
  34. {
  35. public:
  36. // Called by CEntityListSystem
  37. void LevelInitPreEntity()
  38. {
  39. gEntList.AddListenerEntity( this );
  40. Clear();
  41. }
  42. void LevelShutdownPostEntity()
  43. {
  44. gEntList.RemoveListenerEntity( this );
  45. Clear();
  46. }
  47. void Clear()
  48. {
  49. m_targetList.Purge();
  50. }
  51. void ForceRepopulateList()
  52. {
  53. Clear();
  54. CBaseEntity *pEnt = gEntList.FirstEnt();
  55. while( pEnt )
  56. {
  57. if( ShouldAddEntity(pEnt) )
  58. AddEntity(pEnt);
  59. pEnt = gEntList.NextEnt( pEnt );
  60. }
  61. }
  62. bool ShouldAddEntity( CBaseEntity *pEntity )
  63. {
  64. return ((pEntity->GetFlags() & FL_AIMTARGET) != 0);
  65. }
  66. // IEntityListener
  67. virtual void OnEntityCreated( CBaseEntity *pEntity ) {}
  68. virtual void OnEntitySpawned( CBaseEntity *pEntity )
  69. {
  70. if ( ShouldAddEntity( pEntity ) )
  71. {
  72. RemoveEntity( pEntity );
  73. AddEntity( pEntity );
  74. }
  75. }
  76. virtual void OnEntityDeleted( CBaseEntity *pEntity )
  77. {
  78. if ( !(pEntity->GetFlags() & FL_AIMTARGET) )
  79. return;
  80. RemoveEntity(pEntity);
  81. }
  82. void AddEntity( CBaseEntity *pEntity )
  83. {
  84. if ( pEntity->IsMarkedForDeletion() )
  85. return;
  86. m_targetList.AddToTail( pEntity );
  87. }
  88. void RemoveEntity( CBaseEntity *pEntity )
  89. {
  90. int index = m_targetList.Find( pEntity );
  91. if ( m_targetList.IsValidIndex(index) )
  92. {
  93. m_targetList.FastRemove( index );
  94. }
  95. }
  96. int ListCount() { return m_targetList.Count(); }
  97. int ListCopy( CBaseEntity *pList[], int listMax )
  98. {
  99. int count = MIN(listMax, ListCount() );
  100. memcpy( pList, m_targetList.Base(), sizeof(CBaseEntity *) * count );
  101. return count;
  102. }
  103. CBaseEntity *ListElement( int iIndex )
  104. {
  105. return m_targetList[iIndex];
  106. }
  107. private:
  108. CUtlVector<CBaseEntity *> m_targetList;
  109. };
  110. static CAimTargetManager g_AimManager;
  111. int AimTarget_ListCount()
  112. {
  113. return g_AimManager.ListCount();
  114. }
  115. int AimTarget_ListCopy( CBaseEntity *pList[], int listMax )
  116. {
  117. return g_AimManager.ListCopy( pList, listMax );
  118. }
  119. CBaseEntity *AimTarget_ListElement( int iIndex )
  120. {
  121. return g_AimManager.ListElement( iIndex );
  122. }
  123. void AimTarget_ForceRepopulateList()
  124. {
  125. g_AimManager.ForceRepopulateList();
  126. }
  127. // Manages a list of all entities currently doing game simulation or thinking
  128. // NOTE: This is usually a small subset of the global entity list, so it's
  129. // an optimization to maintain this list incrementally rather than polling each
  130. // frame.
  131. struct simthinkentry_t
  132. {
  133. unsigned short entEntry;
  134. unsigned short unused0;
  135. int nextThinkTick;
  136. };
  137. class CSimThinkManager : public IEntityListener
  138. {
  139. public:
  140. CSimThinkManager()
  141. {
  142. Clear();
  143. }
  144. void Clear()
  145. {
  146. m_simThinkList.Purge();
  147. for ( int i = 0; i < ARRAYSIZE(m_entinfoIndex); i++ )
  148. {
  149. m_entinfoIndex[i] = 0xFFFF;
  150. }
  151. }
  152. void LevelInitPreEntity()
  153. {
  154. gEntList.AddListenerEntity( this );
  155. }
  156. void LevelShutdownPostEntity()
  157. {
  158. gEntList.RemoveListenerEntity( this );
  159. Clear();
  160. }
  161. void OnEntityCreated( CBaseEntity *pEntity )
  162. {
  163. }
  164. void OnEntityDeleted( CBaseEntity *pEntity )
  165. {
  166. RemoveEntinfoIndex( pEntity->GetRefEHandle().GetEntryIndex() );
  167. }
  168. void RemoveEntinfoIndex( int index )
  169. {
  170. int listHandle = m_entinfoIndex[index];
  171. // If this guy is in the active list, remove him
  172. if ( listHandle != 0xFFFF )
  173. {
  174. Assert(m_simThinkList[listHandle].entEntry == index);
  175. m_simThinkList.FastRemove( listHandle );
  176. m_entinfoIndex[index] = 0xFFFF;
  177. // fast remove shifted someone, update that someone
  178. if ( listHandle < m_simThinkList.Count() )
  179. {
  180. m_entinfoIndex[m_simThinkList[listHandle].entEntry] = listHandle;
  181. }
  182. }
  183. }
  184. int ListCount()
  185. {
  186. return m_simThinkList.Count();
  187. }
  188. int ListCopy( CBaseEntity *pList[], int listMax )
  189. {
  190. int count = MIN(listMax, ListCount());
  191. int out = 0;
  192. for ( int i = 0; i < count; i++ )
  193. {
  194. // only copy out entities that will simulate or think this frame
  195. if ( m_simThinkList[i].nextThinkTick <= gpGlobals->tickcount )
  196. {
  197. Assert(m_simThinkList[i].nextThinkTick>=0);
  198. int entinfoIndex = m_simThinkList[i].entEntry;
  199. const CEntInfo *pInfo = gEntList.GetEntInfoPtrByIndex( entinfoIndex );
  200. if ( !pInfo->m_pEntity )
  201. continue;
  202. pList[out] = (CBaseEntity *)pInfo->m_pEntity;
  203. Assert(m_simThinkList[i].nextThinkTick==0 || pList[out]->GetFirstThinkTick()==m_simThinkList[i].nextThinkTick);
  204. Assert( gEntList.IsEntityPtr( pList[out] ) );
  205. out++;
  206. }
  207. }
  208. return out;
  209. }
  210. void EntityChanged( CBaseEntity *pEntity )
  211. {
  212. // might change after deletion, don't put back into the list
  213. if ( pEntity->IsMarkedForDeletion() )
  214. return;
  215. const CBaseHandle &eh = pEntity->GetRefEHandle();
  216. if ( !eh.IsValid() )
  217. return;
  218. int index = eh.GetEntryIndex();
  219. if ( pEntity->IsEFlagSet( EFL_NO_THINK_FUNCTION ) && pEntity->IsEFlagSet( EFL_NO_GAME_PHYSICS_SIMULATION ) )
  220. {
  221. RemoveEntinfoIndex( index );
  222. }
  223. else
  224. {
  225. // already in the list? (had think or sim last time, now has both - or had both last time, now just one)
  226. if ( m_entinfoIndex[index] == 0xFFFF )
  227. {
  228. MEM_ALLOC_CREDIT();
  229. m_entinfoIndex[index] = m_simThinkList.AddToTail();
  230. m_simThinkList[m_entinfoIndex[index]].entEntry = (unsigned short)index;
  231. m_simThinkList[m_entinfoIndex[index]].nextThinkTick = 0;
  232. if ( pEntity->IsEFlagSet(EFL_NO_GAME_PHYSICS_SIMULATION) )
  233. {
  234. m_simThinkList[m_entinfoIndex[index]].nextThinkTick = pEntity->GetFirstThinkTick();
  235. Assert(m_simThinkList[m_entinfoIndex[index]].nextThinkTick>=0);
  236. }
  237. }
  238. else
  239. {
  240. Assert( m_simThinkList[m_entinfoIndex[index]].entEntry == index );
  241. // updating existing entry - if no sim, reset think time
  242. if ( pEntity->IsEFlagSet(EFL_NO_GAME_PHYSICS_SIMULATION) )
  243. {
  244. m_simThinkList[m_entinfoIndex[index]].nextThinkTick = pEntity->GetFirstThinkTick();
  245. Assert(m_simThinkList[m_entinfoIndex[index]].nextThinkTick>=0);
  246. }
  247. else
  248. {
  249. m_simThinkList[m_entinfoIndex[index]].nextThinkTick = 0;
  250. }
  251. }
  252. }
  253. }
  254. private:
  255. unsigned short m_entinfoIndex[NUM_ENT_ENTRIES];
  256. CUtlVector<simthinkentry_t> m_simThinkList;
  257. };
  258. CSimThinkManager g_SimThinkManager;
  259. int SimThink_ListCount()
  260. {
  261. return g_SimThinkManager.ListCount();
  262. }
  263. int SimThink_ListCopy( CBaseEntity *pList[], int listMax )
  264. {
  265. return g_SimThinkManager.ListCopy( pList, listMax );
  266. }
  267. void SimThink_EntityChanged( CBaseEntity *pEntity )
  268. {
  269. g_SimThinkManager.EntityChanged( pEntity );
  270. }
  271. // This manages a list of entities queued up to receive PostClientMessages callbacks
  272. class CPostClientMessageManager
  273. {
  274. public:
  275. void LevelShutdownPostEntity()
  276. {
  277. m_list.Purge();
  278. }
  279. void AddEntity( CBaseEntity *pEntity )
  280. {
  281. MEM_ALLOC_CREDIT();
  282. m_list.AddToTail( (unsigned short)pEntity->GetRefEHandle().GetEntryIndex() );
  283. }
  284. void PostClientMessagesSent()
  285. {
  286. for ( int i = m_list.Count()-1; i >= 0; --i )
  287. {
  288. CBaseEntity *pEntity = (CBaseEntity *)gEntList.GetEntInfoPtrByIndex( m_list[i] )->m_pEntity;
  289. if ( pEntity )
  290. {
  291. pEntity->PostClientMessagesSent();
  292. }
  293. }
  294. m_list.RemoveAll();
  295. }
  296. private:
  297. // UNDONE: Need to use ehandles instead?
  298. CUtlVector<unsigned short> m_list;
  299. };
  300. static CPostClientMessageManager g_PostClientManager;
  301. static CBaseEntityClassList *s_pClassLists = NULL;
  302. CBaseEntityClassList::CBaseEntityClassList()
  303. {
  304. m_pNextClassList = s_pClassLists;
  305. s_pClassLists = this;
  306. }
  307. CBaseEntityClassList::~CBaseEntityClassList()
  308. {
  309. }
  310. //-----------------------------------------------------------------------------
  311. // Entity hash tables
  312. //-----------------------------------------------------------------------------
  313. struct EntsByStringList_t
  314. {
  315. string_t iszStr;
  316. CBaseEntity *pHead;
  317. };
  318. class CEntsByStringHashFuncs
  319. {
  320. public:
  321. CEntsByStringHashFuncs( int ) {}
  322. bool operator()( const EntsByStringList_t &lhs, const EntsByStringList_t &rhs ) const
  323. {
  324. return lhs.iszStr == rhs.iszStr;
  325. }
  326. unsigned int operator()( const EntsByStringList_t &item ) const
  327. {
  328. COMPILE_TIME_ASSERT( sizeof( char * ) == sizeof( intp ) );
  329. return HashIntp( ( intp )item.iszStr.ToCStr() );
  330. }
  331. };
  332. typedef CUtlHash<EntsByStringList_t , CEntsByStringHashFuncs, CEntsByStringHashFuncs > CEntsByStringTable;
  333. //-------------------------------------
  334. CEntsByStringTable g_EntsByClassname( 512 );
  335. //-----------------------------------------------------------------------------
  336. CGlobalEntityList::CGlobalEntityList()
  337. {
  338. m_iHighestEnt = m_iNumEnts = m_iNumEdicts = 0;
  339. m_bClearingEntities = false;
  340. }
  341. // removes the entity from the global list
  342. // only called from with the CBaseEntity destructor
  343. static bool g_fInCleanupDelete;
  344. void CGlobalEntityList::UpdateName( IHandleEntity *pHandleEnt, CBaseHandle hEnt )
  345. {
  346. CEntInfo *pEntInfo = const_cast<CEntInfo *>( GetEntInfoPtr( hEnt ) );
  347. IServerUnknown *pUnk = static_cast<IServerUnknown*>(LookupEntity( hEnt ));
  348. CBaseEntity *pEnt = pUnk->GetBaseEntity();
  349. pEntInfo->m_iClassName = MAKE_STRING( pEnt->GetClassname() );
  350. pEntInfo->m_iName = pEnt->GetEntityName();
  351. }
  352. void CGlobalEntityList::UpdateName( IHandleEntity *pEnt )
  353. {
  354. UpdateName( pEnt, pEnt->GetRefEHandle() );
  355. }
  356. // mark an entity as deleted
  357. void CGlobalEntityList::AddToDeleteList( IServerNetworkable *ent )
  358. {
  359. if ( ent && ent->GetEntityHandle()->GetRefEHandle() != CBaseHandle( INVALID_EHANDLE ) )
  360. {
  361. g_DeleteList.AddToTail( ent );
  362. }
  363. }
  364. extern bool g_bDisableEhandleAccess;
  365. // call this before and after each frame to delete all of the marked entities.
  366. void CGlobalEntityList::CleanupDeleteList( void )
  367. {
  368. VPROF( "CGlobalEntityList::CleanupDeleteList" );
  369. SNPROF( "CGlobalEntityList::CleanupDeleteList" );
  370. g_fInCleanupDelete = true;
  371. // clean up the vphysics delete list as well
  372. PhysOnCleanupDeleteList();
  373. g_bDisableEhandleAccess = true;
  374. for ( int i = 0; i < g_DeleteList.Count(); i++ )
  375. {
  376. g_DeleteList[i]->Release();
  377. }
  378. g_bDisableEhandleAccess = false;
  379. g_DeleteList.RemoveAll();
  380. g_fInCleanupDelete = false;
  381. }
  382. int CGlobalEntityList::ResetDeleteList( void )
  383. {
  384. int result = g_DeleteList.Count();
  385. g_DeleteList.RemoveAll();
  386. return result;
  387. }
  388. // add a class that gets notified of entity events
  389. void CGlobalEntityList::AddListenerEntity( IEntityListener *pListener )
  390. {
  391. if ( m_entityListeners.Find( pListener ) >= 0 )
  392. {
  393. AssertMsg( 0, "Can't add listeners multiple times\n" );
  394. return;
  395. }
  396. m_entityListeners.AddToTail( pListener );
  397. }
  398. void CGlobalEntityList::RemoveListenerEntity( IEntityListener *pListener )
  399. {
  400. m_entityListeners.FindAndRemove( pListener );
  401. }
  402. void CGlobalEntityList::Clear( void )
  403. {
  404. m_bClearingEntities = true;
  405. // Add all remaining entities in the game to the delete list and call appropriate UpdateOnRemove
  406. CBaseHandle hCur = FirstHandle();
  407. while ( hCur != InvalidHandle() )
  408. {
  409. IServerNetworkable *ent = GetServerNetworkable( hCur );
  410. if ( ent )
  411. {
  412. MDLCACHE_CRITICAL_SECTION();
  413. // Force UpdateOnRemove to be called
  414. UTIL_Remove( ent );
  415. }
  416. hCur = NextHandle( hCur );
  417. }
  418. CleanupDeleteList();
  419. // free the memory
  420. g_DeleteList.Purge();
  421. #ifdef _DEBUG
  422. for ( UtlHashHandle_t handle = g_EntsByClassname.GetFirstHandle(); g_EntsByClassname.IsValidHandle(handle); handle = g_EntsByClassname.GetNextHandle(handle) )
  423. {
  424. EntsByStringList_t &element = g_EntsByClassname[handle];
  425. Assert( element.pHead == NULL );
  426. }
  427. #endif
  428. g_EntsByClassname.RemoveAll();
  429. CBaseEntity::m_nDebugPlayer = -1;
  430. CBaseEntity::m_bInDebugSelect = false;
  431. m_iHighestEnt = 0;
  432. m_iNumEnts = 0;
  433. m_bClearingEntities = false;
  434. }
  435. int CGlobalEntityList::NumberOfEntities( void )
  436. {
  437. return m_iNumEnts;
  438. }
  439. int CGlobalEntityList::NumberOfEdicts( void )
  440. {
  441. return m_iNumEdicts;
  442. }
  443. CBaseEntity *CGlobalEntityList::NextEnt( CBaseEntity *pCurrentEnt )
  444. {
  445. if ( !pCurrentEnt )
  446. {
  447. const CEntInfo *pInfo = FirstEntInfo();
  448. if ( !pInfo )
  449. return NULL;
  450. return (CBaseEntity *)pInfo->m_pEntity;
  451. }
  452. // Run through the list until we get a CBaseEntity.
  453. const CEntInfo *pList = GetEntInfoPtr( pCurrentEnt->GetRefEHandle() );
  454. if ( pList )
  455. pList = NextEntInfo(pList);
  456. while ( pList )
  457. {
  458. #if 0
  459. if ( pList->m_pEntity )
  460. {
  461. IServerUnknown *pUnk = static_cast<IServerUnknown*>(const_cast<IHandleEntity*>(pList->m_pEntity));
  462. CBaseEntity *pRet = pUnk->GetBaseEntity();
  463. if ( pRet )
  464. return pRet;
  465. }
  466. #else
  467. return (CBaseEntity *)pList->m_pEntity;
  468. #endif
  469. pList = pList->m_pNext;
  470. }
  471. return NULL;
  472. }
  473. void CGlobalEntityList::ReportEntityFlagsChanged( CBaseEntity *pEntity, unsigned int flagsOld, unsigned int flagsNow )
  474. {
  475. if ( pEntity->IsMarkedForDeletion() )
  476. return;
  477. // UNDONE: Move this into IEntityListener instead?
  478. unsigned int flagsChanged = flagsOld ^ flagsNow;
  479. if ( flagsChanged & FL_AIMTARGET )
  480. {
  481. unsigned int flagsAdded = flagsNow & flagsChanged;
  482. unsigned int flagsRemoved = flagsOld & flagsChanged;
  483. if ( flagsAdded & FL_AIMTARGET )
  484. {
  485. g_AimManager.AddEntity( pEntity );
  486. }
  487. if ( flagsRemoved & FL_AIMTARGET )
  488. {
  489. g_AimManager.RemoveEntity( pEntity );
  490. }
  491. }
  492. }
  493. void CGlobalEntityList::AddPostClientMessageEntity( CBaseEntity *pEntity )
  494. {
  495. g_PostClientManager.AddEntity( pEntity );
  496. }
  497. void CGlobalEntityList::PostClientMessagesSent()
  498. {
  499. g_PostClientManager.PostClientMessagesSent();
  500. }
  501. //-----------------------------------------------------------------------------
  502. // Purpose: Used to confirm a pointer is a pointer to an entity, useful for
  503. // asserts.
  504. //-----------------------------------------------------------------------------
  505. bool CGlobalEntityList::IsEntityPtr( void *pTest )
  506. {
  507. if ( pTest )
  508. {
  509. const CEntInfo *pInfo = FirstEntInfo();
  510. for ( ;pInfo; pInfo = pInfo->m_pNext )
  511. {
  512. if ( pTest == (void *)pInfo->m_pEntity )
  513. return true;
  514. }
  515. }
  516. return false;
  517. }
  518. //-----------------------------------------------------------------------------
  519. // Purpose: Iterates the entities with a given classname.
  520. // Input : pStartEntity - Last entity found, NULL to start a new iteration.
  521. // szName - Classname to search for.
  522. //-----------------------------------------------------------------------------
  523. CBaseEntity *CGlobalEntityList::FindEntityByClassname( CBaseEntity *pStartEntity, const char *szName )
  524. {
  525. const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
  526. for ( ;pInfo; pInfo = pInfo->m_pNext )
  527. {
  528. CBaseEntity *pEntity = (CBaseEntity *)pInfo->m_pEntity;
  529. if ( !pEntity )
  530. {
  531. DevWarning( "NULL entity in global entity list!\n" );
  532. continue;
  533. }
  534. if ( EntityNamesMatch( szName, pInfo->m_iClassName ) )
  535. return pEntity;
  536. }
  537. return NULL;
  538. }
  539. CBaseEntity *CGlobalEntityList::FindEntityByClassnameFast( CBaseEntity *pStartEntity, string_t iszClassname )
  540. {
  541. if ( pStartEntity )
  542. {
  543. return pStartEntity->m_pNextByClass;
  544. }
  545. EntsByStringList_t key = { iszClassname };
  546. UtlHashHandle_t hEntry = g_EntsByClassname.Find( key );
  547. if ( hEntry != g_EntsByClassname.InvalidHandle() )
  548. {
  549. return g_EntsByClassname[hEntry].pHead;
  550. }
  551. return NULL;
  552. }
  553. //-----------------------------------------------------------------------------
  554. // Purpose: Finds an entity given a procedural name.
  555. // Input : szName - The procedural name to search for, should start with '!'.
  556. // pSearchingEntity -
  557. // pActivator - The activator entity if this was called from an input
  558. // or Use handler.
  559. //-----------------------------------------------------------------------------
  560. CBaseEntity *CGlobalEntityList::FindEntityProcedural( const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
  561. {
  562. //
  563. // Check for the name escape character.
  564. //
  565. if ( szName[0] == '!' )
  566. {
  567. const char *pName = szName + 1;
  568. //
  569. // It is a procedural name, look for the ones we understand.
  570. //
  571. if ( FStrEq( pName, "player" ) )
  572. {
  573. return (CBaseEntity *)UTIL_PlayerByIndex( 1 );
  574. }
  575. else if ( FStrEq( pName, "pvsplayer" ) )
  576. {
  577. if ( pSearchingEntity )
  578. {
  579. return CBaseEntity::Instance( UTIL_FindClientInPVS( pSearchingEntity->edict() ) );
  580. }
  581. else if ( pActivator )
  582. {
  583. // FIXME: error condition?
  584. return CBaseEntity::Instance( UTIL_FindClientInPVS( pActivator->edict() ) );
  585. }
  586. else
  587. {
  588. // FIXME: error condition?
  589. return (CBaseEntity *)UTIL_PlayerByIndex( 1 );
  590. }
  591. }
  592. else if ( FStrEq( pName, "activator" ) )
  593. {
  594. return pActivator;
  595. }
  596. else if ( FStrEq( pName, "caller" ) )
  597. {
  598. return pCaller;
  599. }
  600. else if ( FStrEq( pName, "picker" ) )
  601. {
  602. return UTIL_PlayerByIndex(1) ? UTIL_PlayerByIndex(1)->FindPickerEntity() : NULL;
  603. }
  604. else if ( FStrEq( pName, "self" ) )
  605. {
  606. return pSearchingEntity;
  607. }
  608. #ifdef PORTAL2
  609. else if ( FStrEq( pName, "player_orange" ) )
  610. {
  611. CTeam *pTeam = GetGlobalTeam( TEAM_RED );
  612. Assert( pTeam );
  613. if ( pTeam == NULL )
  614. return NULL;
  615. for ( int i = 0; i < pTeam->GetNumPlayers(); i++ )
  616. {
  617. if ( pTeam->GetPlayer( i ) != NULL )
  618. {
  619. return (CBaseEntity *) pTeam->GetPlayer( i );
  620. }
  621. }
  622. }
  623. else if ( FStrEq( pName, "player_blue" ) )
  624. {
  625. CTeam *pTeam = GetGlobalTeam( TEAM_BLUE );
  626. Assert( pTeam );
  627. if ( pTeam == NULL )
  628. return NULL;
  629. for ( int i = 0; i < pTeam->GetNumPlayers(); i++ )
  630. {
  631. if ( pTeam->GetPlayer( i ) != NULL )
  632. {
  633. return (CBaseEntity *) pTeam->GetPlayer( i );
  634. }
  635. }
  636. }
  637. #endif // PORTAL2
  638. else
  639. {
  640. Warning( "Invalid entity search name %s\n", szName );
  641. Assert(0);
  642. }
  643. }
  644. return NULL;
  645. }
  646. //-----------------------------------------------------------------------------
  647. // Purpose: Iterates the entities with a given name.
  648. // Input : pStartEntity - Last entity found, NULL to start a new iteration.
  649. // szName - Name to search for.
  650. // pActivator - Activator entity if this was called from an input
  651. // handler or Use handler.
  652. //-----------------------------------------------------------------------------
  653. CBaseEntity *CGlobalEntityList::FindEntityByName( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller, IEntityFindFilter *pFilter )
  654. {
  655. if ( !szName || szName[0] == 0 )
  656. return NULL;
  657. if ( szName[0] == '!' )
  658. {
  659. //
  660. // Avoid an infinite loop, only find one match per procedural search!
  661. //
  662. if (pStartEntity == NULL)
  663. return FindEntityProcedural( szName, pSearchingEntity, pActivator, pCaller );
  664. return NULL;
  665. }
  666. const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
  667. for ( ;pInfo; pInfo = pInfo->m_pNext )
  668. {
  669. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  670. if ( !ent )
  671. {
  672. DevWarning( "NULL entity in global entity list!\n" );
  673. continue;
  674. }
  675. if ( pInfo->m_iName == NULL_STRING )
  676. continue;
  677. if ( EntityNamesMatch( szName, pInfo->m_iName ) )
  678. {
  679. if ( pFilter && !pFilter->ShouldFindEntity(ent) )
  680. continue;
  681. return ent;
  682. }
  683. }
  684. return NULL;
  685. }
  686. CBaseEntity *CGlobalEntityList::FindEntityByNameFast( CBaseEntity *pStartEntity, string_t iszName )
  687. {
  688. if ( iszName == NULL_STRING || STRING(iszName)[0] == 0 )
  689. return NULL;
  690. const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
  691. for ( ;pInfo; pInfo = pInfo->m_pNext )
  692. {
  693. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  694. if ( !ent )
  695. {
  696. DevWarning( "NULL entity in global entity list!\n" );
  697. continue;
  698. }
  699. if ( pInfo->m_iName == NULL_STRING )
  700. continue;
  701. if ( pInfo->m_iName == iszName )
  702. {
  703. return ent;
  704. }
  705. }
  706. return NULL;
  707. }
  708. //-----------------------------------------------------------------------------
  709. // Purpose:
  710. // Input : pStartEntity -
  711. // szModelName -
  712. //-----------------------------------------------------------------------------
  713. CBaseEntity *CGlobalEntityList::FindEntityByModel( CBaseEntity *pStartEntity, const char *szModelName )
  714. {
  715. const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
  716. for ( ;pInfo; pInfo = pInfo->m_pNext )
  717. {
  718. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  719. if ( !ent )
  720. {
  721. DevWarning( "NULL entity in global entity list!\n" );
  722. continue;
  723. }
  724. if ( !ent->edict() || !ent->GetModelName() )
  725. continue;
  726. if ( FStrEq( STRING(ent->GetModelName()), szModelName ) )
  727. return ent;
  728. }
  729. return NULL;
  730. }
  731. //-----------------------------------------------------------------------------
  732. // Purpose: Iterates the entities with a given target.
  733. // Input : pStartEntity -
  734. // szName -
  735. //-----------------------------------------------------------------------------
  736. // FIXME: obsolete, remove
  737. CBaseEntity *CGlobalEntityList::FindEntityByTarget( CBaseEntity *pStartEntity, const char *szName )
  738. {
  739. const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
  740. for ( ;pInfo; pInfo = pInfo->m_pNext )
  741. {
  742. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  743. if ( !ent )
  744. {
  745. DevWarning( "NULL entity in global entity list!\n" );
  746. continue;
  747. }
  748. if ( !ent->m_target )
  749. continue;
  750. if ( FStrEq( STRING(ent->m_target), szName ) )
  751. return ent;
  752. }
  753. return NULL;
  754. }
  755. //-----------------------------------------------------------------------------
  756. // Purpose: Iterates the entities with a given target.
  757. // Input : pStartEntity -
  758. // szName -
  759. //-----------------------------------------------------------------------------
  760. // FIXME: obsolete, remove
  761. CBaseEntity *CGlobalEntityList::FindEntityByOutputTarget( CBaseEntity *pStartEntity, string_t iTarget )
  762. {
  763. const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
  764. for ( ;pInfo; pInfo = pInfo->m_pNext )
  765. {
  766. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  767. if ( !ent )
  768. {
  769. DevWarning( "NULL entity in global entity list!\n" );
  770. continue;
  771. }
  772. datamap_t *dmap = ent->GetDataDescMap();
  773. while ( dmap )
  774. {
  775. int fields = dmap->dataNumFields;
  776. for ( int i = 0; i < fields; i++ )
  777. {
  778. typedescription_t *dataDesc = &dmap->dataDesc[i];
  779. if ( ( dataDesc->fieldType == FIELD_CUSTOM ) && ( dataDesc->flags & FTYPEDESC_OUTPUT ) )
  780. {
  781. CBaseEntityOutput *pOutput = (CBaseEntityOutput *)((intp)ent + (intp)dataDesc->fieldOffset);
  782. if ( pOutput->GetActionForTarget( iTarget ) )
  783. return ent;
  784. }
  785. }
  786. dmap = dmap->baseMap;
  787. }
  788. }
  789. return NULL;
  790. }
  791. //-----------------------------------------------------------------------------
  792. // Purpose: Used to iterate all the entities within a sphere.
  793. // Input : pStartEntity -
  794. // vecCenter -
  795. // flRadius -
  796. //-----------------------------------------------------------------------------
  797. CBaseEntity *CGlobalEntityList::FindEntityInSphere( CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius )
  798. {
  799. const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
  800. for ( ;pInfo; pInfo = pInfo->m_pNext )
  801. {
  802. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  803. if ( !ent )
  804. {
  805. DevWarning( "NULL entity in global entity list!\n" );
  806. continue;
  807. }
  808. if ( !ent->edict() )
  809. continue;
  810. Vector vecRelativeCenter;
  811. ent->CollisionProp()->WorldToCollisionSpace( vecCenter, &vecRelativeCenter );
  812. if ( !IsBoxIntersectingSphere( ent->CollisionProp()->OBBMins(), ent->CollisionProp()->OBBMaxs(), vecRelativeCenter, flRadius ) )
  813. continue;
  814. return ent;
  815. }
  816. // nothing found
  817. return NULL;
  818. }
  819. //-----------------------------------------------------------------------------
  820. // Purpose: Finds the nearest entity by name within a radius
  821. // Input : szName - Entity name to search for.
  822. // vecSrc - Center of search radius.
  823. // flRadius - Search radius for classname search, 0 to search everywhere.
  824. // pSearchingEntity - The entity that is doing the search.
  825. // pActivator - The activator entity if this was called from an input
  826. // or Use handler, NULL otherwise.
  827. // Output : Returns a pointer to the found entity, NULL if none.
  828. //-----------------------------------------------------------------------------
  829. CBaseEntity *CGlobalEntityList::FindEntityByNameNearest( const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
  830. {
  831. CBaseEntity *pEntity = NULL;
  832. //
  833. // Check for matching class names within the search radius.
  834. //
  835. float flMaxDist2 = flRadius * flRadius;
  836. if (flMaxDist2 == 0)
  837. {
  838. flMaxDist2 = MAX_TRACE_LENGTH * MAX_TRACE_LENGTH;
  839. }
  840. CBaseEntity *pSearch = NULL;
  841. while ((pSearch = gEntList.FindEntityByName( pSearch, szName, pSearchingEntity, pActivator, pCaller )) != NULL)
  842. {
  843. if ( !pSearch->edict() )
  844. continue;
  845. float flDist2 = (pSearch->GetAbsOrigin() - vecSrc).LengthSqr();
  846. if (flMaxDist2 > flDist2)
  847. {
  848. pEntity = pSearch;
  849. flMaxDist2 = flDist2;
  850. }
  851. }
  852. return pEntity;
  853. }
  854. //-----------------------------------------------------------------------------
  855. // Purpose: Finds the first entity by name within a radius
  856. // Input : pStartEntity - The entity to start from when doing the search.
  857. // szName - Entity name to search for.
  858. // vecSrc - Center of search radius.
  859. // flRadius - Search radius for classname search, 0 to search everywhere.
  860. // pSearchingEntity - The entity that is doing the search.
  861. // pActivator - The activator entity if this was called from an input
  862. // or Use handler, NULL otherwise.
  863. // Output : Returns a pointer to the found entity, NULL if none.
  864. //-----------------------------------------------------------------------------
  865. CBaseEntity *CGlobalEntityList::FindEntityByNameWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
  866. {
  867. //
  868. // Check for matching class names within the search radius.
  869. //
  870. CBaseEntity *pEntity = pStartEntity;
  871. float flMaxDist2 = flRadius * flRadius;
  872. if (flMaxDist2 == 0)
  873. {
  874. return gEntList.FindEntityByName( pEntity, szName, pSearchingEntity, pActivator, pCaller );
  875. }
  876. while ((pEntity = gEntList.FindEntityByName( pEntity, szName, pSearchingEntity, pActivator, pCaller )) != NULL)
  877. {
  878. if ( !pEntity->edict() )
  879. continue;
  880. float flDist2 = (pEntity->GetAbsOrigin() - vecSrc).LengthSqr();
  881. if (flMaxDist2 > flDist2)
  882. {
  883. return pEntity;
  884. }
  885. }
  886. return NULL;
  887. }
  888. //-----------------------------------------------------------------------------
  889. // Purpose: Finds the nearest entity by class name withing given search radius.
  890. // Input : szName - Entity name to search for. Treated as a target name first,
  891. // then as an entity class name, ie "info_target".
  892. // vecSrc - Center of search radius.
  893. // flRadius - Search radius for classname search, 0 to search everywhere.
  894. // Output : Returns a pointer to the found entity, NULL if none.
  895. //-----------------------------------------------------------------------------
  896. CBaseEntity *CGlobalEntityList::FindEntityByClassnameNearest( const char *szName, const Vector &vecSrc, float flRadius )
  897. {
  898. CBaseEntity *pEntity = NULL;
  899. //
  900. // Check for matching class names within the search radius.
  901. //
  902. float flMaxDist2 = flRadius * flRadius;
  903. if (flMaxDist2 == 0)
  904. {
  905. flMaxDist2 = MAX_TRACE_LENGTH * MAX_TRACE_LENGTH;
  906. }
  907. CBaseEntity *pSearch = NULL;
  908. while ((pSearch = gEntList.FindEntityByClassname( pSearch, szName )) != NULL)
  909. {
  910. if ( !pSearch->edict() )
  911. continue;
  912. float flDist2 = (pSearch->GetAbsOrigin() - vecSrc).LengthSqr();
  913. if (flMaxDist2 > flDist2)
  914. {
  915. pEntity = pSearch;
  916. flMaxDist2 = flDist2;
  917. }
  918. }
  919. return pEntity;
  920. }
  921. CBaseEntity *CGlobalEntityList::FindEntityByClassnameNearestFast( string_t iszName, const Vector &vecSrc, float flRadius )
  922. {
  923. CBaseEntity *pEntity = NULL;
  924. //
  925. // Check for matching class names within the search radius.
  926. //
  927. float flMaxDist2 = flRadius * flRadius;
  928. if (flMaxDist2 == 0)
  929. {
  930. flMaxDist2 = MAX_TRACE_LENGTH * MAX_TRACE_LENGTH;
  931. }
  932. CBaseEntity *pSearch = NULL;
  933. while ((pSearch = gEntList.FindEntityByClassnameFast( pSearch, iszName )) != NULL)
  934. {
  935. if ( !pSearch->edict() )
  936. continue;
  937. float flDist2 = (pSearch->GetAbsOrigin() - vecSrc).LengthSqr();
  938. if (flMaxDist2 > flDist2)
  939. {
  940. pEntity = pSearch;
  941. flMaxDist2 = flDist2;
  942. }
  943. }
  944. return pEntity;
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Purpose: Finds the nearest entity by class name withing given search radius.
  948. // Input : szName - Entity name to search for. Treated as a target name first,
  949. // then as an entity class name, ie "info_target".
  950. // vecSrc - Center of search radius.
  951. // flRadius - Search radius for classname search, 0 to search everywhere.
  952. // Output : Returns a pointer to the found entity, NULL if none.
  953. //-----------------------------------------------------------------------------
  954. CBaseEntity *CGlobalEntityList::FindEntityByClassnameNearest2D( const char *szName, const Vector &vecSrc, float flRadius )
  955. {
  956. CBaseEntity *pEntity = NULL;
  957. //
  958. // Check for matching class names within the search radius.
  959. //
  960. float flMaxDist2 = flRadius * flRadius;
  961. if (flMaxDist2 == 0)
  962. {
  963. flMaxDist2 = MAX_TRACE_LENGTH * MAX_TRACE_LENGTH;
  964. }
  965. CBaseEntity *pSearch = NULL;
  966. while ((pSearch = gEntList.FindEntityByClassname( pSearch, szName )) != NULL)
  967. {
  968. if ( !pSearch->edict() )
  969. continue;
  970. float flDist2 = (pSearch->GetAbsOrigin().AsVector2D() - vecSrc.AsVector2D()).LengthSqr();
  971. if (flMaxDist2 > flDist2)
  972. {
  973. pEntity = pSearch;
  974. flMaxDist2 = flDist2;
  975. }
  976. }
  977. return pEntity;
  978. }
  979. //-----------------------------------------------------------------------------
  980. // Purpose: Finds the first entity within radius distance by class name.
  981. // Input : pStartEntity - The entity to start from when doing the search.
  982. // szName - Entity class name, ie "info_target".
  983. // vecSrc - Center of search radius.
  984. // flRadius - Search radius for classname search, 0 to search everywhere.
  985. // Output : Returns a pointer to the found entity, NULL if none.
  986. //-----------------------------------------------------------------------------
  987. CBaseEntity *CGlobalEntityList::FindEntityByClassnameWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecSrc, float flRadius )
  988. {
  989. //
  990. // Check for matching class names within the search radius.
  991. //
  992. CBaseEntity *pEntity = pStartEntity;
  993. float flMaxDist2 = flRadius * flRadius;
  994. if (flMaxDist2 == 0)
  995. {
  996. return gEntList.FindEntityByClassname( pEntity, szName );
  997. }
  998. while ((pEntity = gEntList.FindEntityByClassname( pEntity, szName )) != NULL)
  999. {
  1000. if ( !pEntity->edict() && !pEntity->IsEFlagSet( EFL_SERVER_ONLY ) )
  1001. continue;
  1002. // Instead of checking absorigin vs sphere, check if the obb intersects the sphere.
  1003. Vector vecRelativeCenter;
  1004. pEntity->CollisionProp()->WorldToCollisionSpace( vecSrc, &vecRelativeCenter );
  1005. if ( IsBoxIntersectingSphere( pEntity->CollisionProp()->OBBMins(), pEntity->CollisionProp()->OBBMaxs(), vecRelativeCenter, flRadius ) )
  1006. {
  1007. return pEntity;
  1008. }
  1009. }
  1010. return NULL;
  1011. }
  1012. //-----------------------------------------------------------------------------
  1013. // Purpose: Finds the first entity within an extent by class name.
  1014. // Input : pStartEntity - The entity to start from when doing the search.
  1015. // szName - Entity class name, ie "info_target".
  1016. // vecMins - Search mins.
  1017. // vecMaxs - Search maxs.
  1018. // Output : Returns a pointer to the found entity, NULL if none.
  1019. //-----------------------------------------------------------------------------
  1020. CBaseEntity *CGlobalEntityList::FindEntityByClassnameWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecMins, const Vector &vecMaxs )
  1021. {
  1022. //
  1023. // Check for matching class names within the search radius.
  1024. //
  1025. CBaseEntity *pEntity = pStartEntity;
  1026. while ((pEntity = gEntList.FindEntityByClassname( pEntity, szName )) != NULL)
  1027. {
  1028. if ( !pEntity->edict() && !pEntity->IsEFlagSet( EFL_SERVER_ONLY ) )
  1029. continue;
  1030. // check if the aabb intersects the search aabb.
  1031. Vector entMins, entMaxs;
  1032. pEntity->CollisionProp()->WorldSpaceAABB( &entMins, &entMaxs );
  1033. if ( IsBoxIntersectingBox( vecMins, vecMaxs, entMins, entMaxs ) )
  1034. {
  1035. return pEntity;
  1036. }
  1037. }
  1038. return NULL;
  1039. }
  1040. //-----------------------------------------------------------------------------
  1041. // Purpose: Finds an entity by target name or class name.
  1042. // Input : pStartEntity - The entity to start from when doing the search.
  1043. // szName - Entity name to search for. Treated as a target name first,
  1044. // then as an entity class name, ie "info_target".
  1045. // vecSrc - Center of search radius.
  1046. // flRadius - Search radius for classname search, 0 to search everywhere.
  1047. // pSearchingEntity - The entity that is doing the search.
  1048. // pActivator - The activator entity if this was called from an input
  1049. // or Use handler, NULL otherwise.
  1050. // Output : Returns a pointer to the found entity, NULL if none.
  1051. //-----------------------------------------------------------------------------
  1052. CBaseEntity *CGlobalEntityList::FindEntityGeneric( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
  1053. {
  1054. CBaseEntity *pEntity = NULL;
  1055. pEntity = gEntList.FindEntityByName( pStartEntity, szName, pSearchingEntity, pActivator, pCaller );
  1056. if (!pEntity)
  1057. {
  1058. pEntity = gEntList.FindEntityByClassname( pStartEntity, szName );
  1059. }
  1060. return pEntity;
  1061. }
  1062. //-----------------------------------------------------------------------------
  1063. // Purpose: Finds the first entity by target name or class name within a radius
  1064. // Input : pStartEntity - The entity to start from when doing the search.
  1065. // szName - Entity name to search for. Treated as a target name first,
  1066. // then as an entity class name, ie "info_target".
  1067. // vecSrc - Center of search radius.
  1068. // flRadius - Search radius for classname search, 0 to search everywhere.
  1069. // pSearchingEntity - The entity that is doing the search.
  1070. // pActivator - The activator entity if this was called from an input
  1071. // or Use handler, NULL otherwise.
  1072. // Output : Returns a pointer to the found entity, NULL if none.
  1073. //-----------------------------------------------------------------------------
  1074. CBaseEntity *CGlobalEntityList::FindEntityGenericWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
  1075. {
  1076. CBaseEntity *pEntity = NULL;
  1077. pEntity = gEntList.FindEntityByNameWithin( pStartEntity, szName, vecSrc, flRadius, pSearchingEntity, pActivator, pCaller );
  1078. if (!pEntity)
  1079. {
  1080. pEntity = gEntList.FindEntityByClassnameWithin( pStartEntity, szName, vecSrc, flRadius );
  1081. }
  1082. return pEntity;
  1083. }
  1084. //-----------------------------------------------------------------------------
  1085. // Purpose: Finds the nearest entity by target name or class name within a radius.
  1086. // Input : pStartEntity - The entity to start from when doing the search.
  1087. // szName - Entity name to search for. Treated as a target name first,
  1088. // then as an entity class name, ie "info_target".
  1089. // vecSrc - Center of search radius.
  1090. // flRadius - Search radius for classname search, 0 to search everywhere.
  1091. // pSearchingEntity - The entity that is doing the search.
  1092. // pActivator - The activator entity if this was called from an input
  1093. // or Use handler, NULL otherwise.
  1094. // Output : Returns a pointer to the found entity, NULL if none.
  1095. //-----------------------------------------------------------------------------
  1096. CBaseEntity *CGlobalEntityList::FindEntityGenericNearest( const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
  1097. {
  1098. CBaseEntity *pEntity = NULL;
  1099. pEntity = gEntList.FindEntityByNameNearest( szName, vecSrc, flRadius, pSearchingEntity, pActivator, pCaller );
  1100. if (!pEntity)
  1101. {
  1102. pEntity = gEntList.FindEntityByClassnameNearest( szName, vecSrc, flRadius );
  1103. }
  1104. return pEntity;
  1105. }
  1106. //-----------------------------------------------------------------------------
  1107. // Purpose: Find the nearest entity along the facing direction from the given origin
  1108. // within the angular threshold (ignores worldspawn) with the
  1109. // given classname.
  1110. // Input : origin -
  1111. // facing -
  1112. // threshold -
  1113. // classname -
  1114. //-----------------------------------------------------------------------------
  1115. CBaseEntity *CGlobalEntityList::FindEntityClassNearestFacing( const Vector &origin, const Vector &facing, float threshold, char *classname)
  1116. {
  1117. float bestDot = threshold;
  1118. CBaseEntity *best_ent = NULL;
  1119. const CEntInfo *pInfo = FirstEntInfo();
  1120. for ( ;pInfo; pInfo = pInfo->m_pNext )
  1121. {
  1122. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  1123. if ( !ent )
  1124. {
  1125. DevWarning( "NULL entity in global entity list!\n" );
  1126. continue;
  1127. }
  1128. // FIXME: why is this skipping pointsize entities?
  1129. if (ent->IsPointSized() )
  1130. continue;
  1131. // Make vector to entity
  1132. Vector to_ent = (ent->GetAbsOrigin() - origin);
  1133. VectorNormalize( to_ent );
  1134. float dot = DotProduct (facing , to_ent );
  1135. if (dot > bestDot)
  1136. {
  1137. if (FClassnameIs(ent,classname))
  1138. {
  1139. // Ignore if worldspawn
  1140. if (!FClassnameIs( ent, "worldspawn" ) && !FClassnameIs( ent, "soundent"))
  1141. {
  1142. bestDot = dot;
  1143. best_ent = ent;
  1144. }
  1145. }
  1146. }
  1147. }
  1148. return best_ent;
  1149. }
  1150. //-----------------------------------------------------------------------------
  1151. // Purpose: Find the nearest entity along the facing direction from the given origin
  1152. // within the angular threshold (ignores worldspawn)
  1153. // Input : origin -
  1154. // facing -
  1155. // threshold -
  1156. //-----------------------------------------------------------------------------
  1157. CBaseEntity *CGlobalEntityList::FindEntityNearestFacing( const Vector &origin, const Vector &facing, float threshold)
  1158. {
  1159. float bestDot = threshold;
  1160. CBaseEntity *best_ent = NULL;
  1161. const CEntInfo *pInfo = FirstEntInfo();
  1162. for ( ;pInfo; pInfo = pInfo->m_pNext )
  1163. {
  1164. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  1165. if ( !ent )
  1166. {
  1167. DevWarning( "NULL entity in global entity list!\n" );
  1168. continue;
  1169. }
  1170. // Ignore logical entities
  1171. if (!ent->edict())
  1172. continue;
  1173. // Make vector to entity
  1174. Vector to_ent = ent->WorldSpaceCenter() - origin;
  1175. VectorNormalize(to_ent);
  1176. float dot = DotProduct( facing, to_ent );
  1177. if (dot <= bestDot)
  1178. continue;
  1179. // Ignore if worldspawn
  1180. if (!FStrEq( STRING(ent->m_iClassname), "worldspawn") && !FStrEq( STRING(ent->m_iClassname), "soundent"))
  1181. {
  1182. bestDot = dot;
  1183. best_ent = ent;
  1184. }
  1185. }
  1186. return best_ent;
  1187. }
  1188. void CGlobalEntityList::OnAddEntity( IHandleEntity *pEnt, CBaseHandle handle )
  1189. {
  1190. int i = handle.GetEntryIndex();
  1191. // record current list details
  1192. m_iNumEnts++;
  1193. if ( i > m_iHighestEnt )
  1194. m_iHighestEnt = i;
  1195. // If it's a CBaseEntity, notify the listeners.
  1196. CBaseEntity *pBaseEnt = static_cast<IServerUnknown*>(pEnt)->GetBaseEntity();
  1197. if ( pBaseEnt->edict() )
  1198. m_iNumEdicts++;
  1199. // NOTE: Must be a CBaseEntity on server
  1200. Assert( pBaseEnt );
  1201. //DevMsg(2,"Created %s\n", pBaseEnt->GetClassname() );
  1202. for ( i = m_entityListeners.Count()-1; i >= 0; i-- )
  1203. {
  1204. m_entityListeners[i]->OnEntityCreated( pBaseEnt );
  1205. }
  1206. }
  1207. void CGlobalEntityList::OnRemoveEntity( IHandleEntity *pEnt, CBaseHandle handle )
  1208. {
  1209. #ifdef DEBUG
  1210. if ( !g_fInCleanupDelete )
  1211. {
  1212. int i;
  1213. for ( i = 0; i < g_DeleteList.Count(); i++ )
  1214. {
  1215. if ( g_DeleteList[i]->GetEntityHandle() == pEnt )
  1216. {
  1217. g_DeleteList.FastRemove( i );
  1218. Msg( "ERROR: Entity being destroyed but previously threaded on g_DeleteList\n" );
  1219. break;
  1220. }
  1221. }
  1222. }
  1223. #endif
  1224. CBaseEntity *pBaseEnt = static_cast<IServerUnknown*>(pEnt)->GetBaseEntity();
  1225. if ( pBaseEnt->edict() )
  1226. m_iNumEdicts--;
  1227. m_iNumEnts--;
  1228. }
  1229. void CGlobalEntityList::NotifyCreateEntity( CBaseEntity *pEnt )
  1230. {
  1231. if ( !pEnt )
  1232. return;
  1233. // Make sure no one is trying to build an entity without game strings.
  1234. Assert( MAKE_STRING( pEnt->GetClassname() ) == FindPooledString( pEnt->GetClassname() ) &&
  1235. ( pEnt->GetEntityName() == NULL_STRING || pEnt->GetEntityName() == FindPooledString( pEnt->GetEntityName().ToCStr() ) ) );
  1236. Assert( pEnt->m_pPrevByClass == NULL && pEnt->m_pNextByClass == NULL && pEnt->m_ListByClass == g_EntsByClassname.InvalidHandle() );
  1237. EntsByStringList_t dummyEntry = { MAKE_STRING( pEnt->GetClassname() ), 0 };
  1238. UtlHashHandle_t hEntry = g_EntsByClassname.Insert( dummyEntry );
  1239. EntsByStringList_t *pEntry = &g_EntsByClassname[hEntry];
  1240. pEnt->m_ListByClass = hEntry;
  1241. if ( pEntry->pHead )
  1242. {
  1243. pEntry->pHead->m_pPrevByClass = pEnt;
  1244. pEnt->m_pNextByClass = pEntry->pHead;
  1245. Assert( pEnt->m_pPrevByClass == NULL );
  1246. }
  1247. pEntry->pHead = pEnt;
  1248. //DevMsg(2,"Deleted %s\n", pBaseEnt->GetClassname() );
  1249. for ( int i = m_entityListeners.Count()-1; i >= 0; i-- )
  1250. {
  1251. m_entityListeners[i]->OnEntityCreated( pEnt );
  1252. }
  1253. }
  1254. void CGlobalEntityList::NotifySpawn( CBaseEntity *pEnt )
  1255. {
  1256. if ( !pEnt )
  1257. return;
  1258. //DevMsg(2,"Deleted %s\n", pBaseEnt->GetClassname() );
  1259. for ( int i = m_entityListeners.Count()-1; i >= 0; i-- )
  1260. {
  1261. m_entityListeners[i]->OnEntitySpawned( pEnt );
  1262. }
  1263. }
  1264. // NOTE: This doesn't happen in OnRemoveEntity() specifically because
  1265. // listeners may want to reference the object as it's being deleted
  1266. // OnRemoveEntity isn't called until the destructor and all data is invalid.
  1267. void CGlobalEntityList::NotifyRemoveEntity( CBaseEntity *pBaseEnt )
  1268. {
  1269. if ( !pBaseEnt )
  1270. return;
  1271. //DevMsg(2,"Deleted %s\n", pBaseEnt->GetClassname() );
  1272. for ( int i = m_entityListeners.Count()-1; i >= 0; i-- )
  1273. {
  1274. m_entityListeners[i]->OnEntityDeleted( pBaseEnt );
  1275. }
  1276. if ( pBaseEnt->m_ListByClass != g_EntsByClassname.InvalidHandle() )
  1277. {
  1278. EntsByStringList_t *pEntry = &g_EntsByClassname[pBaseEnt->m_ListByClass];
  1279. if ( pEntry->pHead == pBaseEnt )
  1280. {
  1281. pEntry->pHead = pBaseEnt->m_pNextByClass;
  1282. // Don't remove empty list, on the assumption that the number of classes that are not referenced again is small
  1283. // Plus during map load we get a lot of precache others that hit this [8/8/2008 tom]
  1284. }
  1285. Assert( g_EntsByClassname[pBaseEnt->m_ListByClass].pHead != pBaseEnt );
  1286. if ( pBaseEnt->m_pNextByClass )
  1287. {
  1288. pBaseEnt->m_pNextByClass->m_pPrevByClass = pBaseEnt->m_pPrevByClass;
  1289. }
  1290. if ( pBaseEnt->m_pPrevByClass )
  1291. {
  1292. pBaseEnt->m_pPrevByClass->m_pNextByClass = pBaseEnt->m_pNextByClass;
  1293. }
  1294. pBaseEnt->m_pPrevByClass = pBaseEnt->m_pNextByClass = NULL;
  1295. pBaseEnt->m_ListByClass = g_EntsByClassname.InvalidHandle();
  1296. }
  1297. }
  1298. //-----------------------------------------------------------------------------
  1299. // NOTIFY LIST
  1300. //
  1301. // Allows entities to get events fired when another entity changes
  1302. //-----------------------------------------------------------------------------
  1303. struct entitynotify_t
  1304. {
  1305. CBaseEntity *pNotify;
  1306. CBaseEntity *pWatched;
  1307. };
  1308. class CNotifyList : public INotify, public IEntityListener
  1309. {
  1310. public:
  1311. // INotify
  1312. void AddEntity( CBaseEntity *pNotify, CBaseEntity *pWatched );
  1313. void RemoveEntity( CBaseEntity *pNotify, CBaseEntity *pWatched );
  1314. void ReportNamedEvent( CBaseEntity *pEntity, const char *pEventName );
  1315. void ClearEntity( CBaseEntity *pNotify );
  1316. void ReportSystemEvent( CBaseEntity *pEntity, notify_system_event_t eventType, const notify_system_event_params_t &params );
  1317. // IEntityListener
  1318. virtual void OnEntityCreated( CBaseEntity *pEntity );
  1319. virtual void OnEntityDeleted( CBaseEntity *pEntity );
  1320. // Called from CEntityListSystem
  1321. void LevelInitPreEntity();
  1322. void LevelShutdownPreEntity();
  1323. private:
  1324. CUtlVector<entitynotify_t> m_notifyList;
  1325. };
  1326. void CNotifyList::AddEntity( CBaseEntity *pNotify, CBaseEntity *pWatched )
  1327. {
  1328. // OPTIMIZE: Also flag pNotify for faster "RemoveAllNotify" ?
  1329. pWatched->AddEFlags( EFL_NOTIFY );
  1330. int index = m_notifyList.AddToTail();
  1331. entitynotify_t &notify = m_notifyList[index];
  1332. notify.pNotify = pNotify;
  1333. notify.pWatched = pWatched;
  1334. }
  1335. // Remove noitfication for an entity
  1336. void CNotifyList::RemoveEntity( CBaseEntity *pNotify, CBaseEntity *pWatched )
  1337. {
  1338. for ( int i = m_notifyList.Count(); --i >= 0; )
  1339. {
  1340. if ( m_notifyList[i].pNotify == pNotify && m_notifyList[i].pWatched == pWatched)
  1341. {
  1342. m_notifyList.FastRemove(i);
  1343. }
  1344. }
  1345. }
  1346. void CNotifyList::ReportNamedEvent( CBaseEntity *pEntity, const char *pInputName )
  1347. {
  1348. variant_t emptyVariant;
  1349. if ( !pEntity->IsEFlagSet(EFL_NOTIFY) )
  1350. return;
  1351. for ( int i = 0; i < m_notifyList.Count(); i++ )
  1352. {
  1353. if ( m_notifyList[i].pWatched == pEntity )
  1354. {
  1355. m_notifyList[i].pNotify->AcceptInput( pInputName, pEntity, pEntity, emptyVariant, 0 );
  1356. }
  1357. }
  1358. }
  1359. void CNotifyList::LevelInitPreEntity()
  1360. {
  1361. gEntList.AddListenerEntity( this );
  1362. }
  1363. void CNotifyList::LevelShutdownPreEntity( void )
  1364. {
  1365. gEntList.RemoveListenerEntity( this );
  1366. m_notifyList.Purge();
  1367. }
  1368. void CNotifyList::OnEntityCreated( CBaseEntity *pEntity )
  1369. {
  1370. }
  1371. void CNotifyList::OnEntityDeleted( CBaseEntity *pEntity )
  1372. {
  1373. ReportDestroyEvent( pEntity );
  1374. ClearEntity( pEntity );
  1375. }
  1376. // UNDONE: Slow linear search?
  1377. void CNotifyList::ClearEntity( CBaseEntity *pNotify )
  1378. {
  1379. for ( int i = m_notifyList.Count(); --i >= 0; )
  1380. {
  1381. if ( m_notifyList[i].pNotify == pNotify || m_notifyList[i].pWatched == pNotify)
  1382. {
  1383. m_notifyList.FastRemove(i);
  1384. }
  1385. }
  1386. }
  1387. void CNotifyList::ReportSystemEvent( CBaseEntity *pEntity, notify_system_event_t eventType, const notify_system_event_params_t &params )
  1388. {
  1389. if ( !pEntity->IsEFlagSet(EFL_NOTIFY) )
  1390. return;
  1391. for ( int i = 0; i < m_notifyList.Count(); i++ )
  1392. {
  1393. if ( m_notifyList[i].pWatched == pEntity )
  1394. {
  1395. m_notifyList[i].pNotify->NotifySystemEvent( pEntity, eventType, params );
  1396. }
  1397. }
  1398. }
  1399. static CNotifyList g_NotifyList;
  1400. INotify *g_pNotify = &g_NotifyList;
  1401. class CEntityTouchManager : public IEntityListener
  1402. {
  1403. public:
  1404. // called by CEntityListSystem
  1405. void LevelInitPreEntity()
  1406. {
  1407. gEntList.AddListenerEntity( this );
  1408. Clear();
  1409. }
  1410. void LevelShutdownPostEntity()
  1411. {
  1412. gEntList.RemoveListenerEntity( this );
  1413. Clear();
  1414. }
  1415. void FrameUpdatePostEntityThink();
  1416. void Clear()
  1417. {
  1418. m_updateList.Purge();
  1419. }
  1420. // IEntityListener
  1421. virtual void OnEntityCreated( CBaseEntity *pEntity ) {}
  1422. virtual void OnEntityDeleted( CBaseEntity *pEntity )
  1423. {
  1424. if ( !pEntity->GetCheckUntouch() )
  1425. return;
  1426. int index = m_updateList.Find( pEntity );
  1427. if ( m_updateList.IsValidIndex(index) )
  1428. {
  1429. m_updateList.FastRemove( index );
  1430. }
  1431. }
  1432. void AddEntity( CBaseEntity *pEntity )
  1433. {
  1434. if ( pEntity->IsMarkedForDeletion() )
  1435. return;
  1436. Assert( !m_updateList.IsValidIndex( m_updateList.Find( pEntity ) ) );
  1437. m_updateList.AddToTail( pEntity );
  1438. }
  1439. void RemoveEntity( CBaseEntity *pEntity )
  1440. {
  1441. m_updateList.FindAndFastRemove( pEntity );
  1442. }
  1443. private:
  1444. CUtlVector<CBaseEntity *> m_updateList;
  1445. };
  1446. static CEntityTouchManager g_TouchManager;
  1447. void EntityTouch_Add( CBaseEntity *pEntity )
  1448. {
  1449. g_TouchManager.AddEntity( pEntity );
  1450. }
  1451. void EntityTouch_Remove( CBaseEntity *pEntity )
  1452. {
  1453. g_TouchManager.RemoveEntity( pEntity );
  1454. }
  1455. void CEntityTouchManager::FrameUpdatePostEntityThink()
  1456. {
  1457. VPROF( "CEntityTouchManager::FrameUpdatePostEntityThink" );
  1458. SNPROF( "CEntityTouchManager::FrameUpdatePostEntityThink" );
  1459. // Loop through all entities again, checking their untouch if flagged to do so
  1460. int count = m_updateList.Count();
  1461. if ( count )
  1462. {
  1463. // copy off the list
  1464. CBaseEntity **ents = (CBaseEntity **)stackalloc( sizeof(CBaseEntity *) * count );
  1465. memcpy( ents, m_updateList.Base(), sizeof(CBaseEntity *) * count );
  1466. // clear it
  1467. m_updateList.RemoveAll();
  1468. // now update those ents
  1469. for ( int i = 0; i < count; i++ )
  1470. {
  1471. //Assert( ents[i]->GetCheckUntouch() );
  1472. if ( ents[i]->GetCheckUntouch() )
  1473. {
  1474. ents[i]->PhysicsCheckForEntityUntouch();
  1475. }
  1476. }
  1477. stackfree( ents );
  1478. }
  1479. }
  1480. class CRespawnEntitiesFilter : public IMapEntityFilter
  1481. {
  1482. public:
  1483. virtual bool ShouldCreateEntity( const char *pClassname )
  1484. {
  1485. // Create everything but the world
  1486. return Q_stricmp( pClassname, "worldspawn" ) != 0;
  1487. }
  1488. virtual CBaseEntity* CreateNextEntity( const char *pClassname )
  1489. {
  1490. return CreateEntityByName( pClassname );
  1491. }
  1492. };
  1493. // One hook to rule them all...
  1494. // Since most of the little list managers in here only need one or two of the game
  1495. // system callbacks, this hook is a game system that passes them the appropriate callbacks
  1496. class CEntityListSystem : public CAutoGameSystemPerFrame
  1497. {
  1498. public:
  1499. CEntityListSystem( char const *name ) : CAutoGameSystemPerFrame( name )
  1500. {
  1501. m_bRespawnAllEntities = false;
  1502. }
  1503. void LevelInitPreEntity()
  1504. {
  1505. g_NotifyList.LevelInitPreEntity();
  1506. g_TouchManager.LevelInitPreEntity();
  1507. g_AimManager.LevelInitPreEntity();
  1508. g_SimThinkManager.LevelInitPreEntity();
  1509. #ifdef HL2_DLL
  1510. OverrideMoveCache_LevelInitPreEntity();
  1511. #endif // HL2_DLL
  1512. }
  1513. void LevelShutdownPreEntity()
  1514. {
  1515. g_NotifyList.LevelShutdownPreEntity();
  1516. }
  1517. void LevelShutdownPostEntity()
  1518. {
  1519. g_TouchManager.LevelShutdownPostEntity();
  1520. g_AimManager.LevelShutdownPostEntity();
  1521. g_PostClientManager.LevelShutdownPostEntity();
  1522. g_SimThinkManager.LevelShutdownPostEntity();
  1523. #ifdef HL2_DLL
  1524. OverrideMoveCache_LevelShutdownPostEntity();
  1525. #endif // HL2_DLL
  1526. CBaseEntityClassList *pClassList = s_pClassLists;
  1527. while ( pClassList )
  1528. {
  1529. pClassList->LevelShutdownPostEntity();
  1530. pClassList = pClassList->m_pNextClassList;
  1531. }
  1532. }
  1533. void FrameUpdatePostEntityThink()
  1534. {
  1535. g_TouchManager.FrameUpdatePostEntityThink();
  1536. if ( m_bRespawnAllEntities )
  1537. {
  1538. m_bRespawnAllEntities = false;
  1539. // Don't change globalstate owing to deletion here
  1540. GlobalEntity_EnableStateUpdates( false );
  1541. // Remove all entities
  1542. int nPlayerIndex = -1;
  1543. CBaseEntity *pEnt = gEntList.FirstEnt();
  1544. while ( pEnt )
  1545. {
  1546. CBaseEntity *pNextEnt = gEntList.NextEnt( pEnt );
  1547. if ( pEnt->IsPlayer() )
  1548. {
  1549. nPlayerIndex = pEnt->entindex();
  1550. }
  1551. if ( !pEnt->IsEFlagSet( EFL_KEEP_ON_RECREATE_ENTITIES ) )
  1552. {
  1553. UTIL_Remove( pEnt );
  1554. }
  1555. pEnt = pNextEnt;
  1556. }
  1557. gEntList.CleanupDeleteList();
  1558. GlobalEntity_EnableStateUpdates( true );
  1559. // Allows us to immediately re-use the edict indices we just freed to avoid edict overflow
  1560. engine->AllowImmediateEdictReuse();
  1561. // Reset node counter used during load
  1562. CNodeEnt::m_nNodeCount = 0;
  1563. CRespawnEntitiesFilter filter;
  1564. MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true );
  1565. // Allocate a CBasePlayer for pev, and call spawn
  1566. if ( nPlayerIndex >= 0 )
  1567. {
  1568. edict_t *pEdict = INDEXENT( nPlayerIndex );
  1569. ClientPutInServer( pEdict, "unnamed" );
  1570. ClientActive( pEdict, false );
  1571. CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  1572. SceneManager_ClientActive( pPlayer );
  1573. }
  1574. }
  1575. }
  1576. bool m_bRespawnAllEntities;
  1577. };
  1578. static CEntityListSystem g_EntityListSystem( "CEntityListSystem" );
  1579. //-----------------------------------------------------------------------------
  1580. // Respawns all entities in the level
  1581. //-----------------------------------------------------------------------------
  1582. void RespawnEntities()
  1583. {
  1584. g_EntityListSystem.m_bRespawnAllEntities = true;
  1585. }
  1586. static ConCommand restart_entities( "respawn_entities", RespawnEntities, "Respawn all the entities in the map.", FCVAR_CHEAT | FCVAR_SPONLY );
  1587. class CSortedEntityList
  1588. {
  1589. public:
  1590. CSortedEntityList() : m_sortedList(), m_emptyCount(0) {}
  1591. typedef CBaseEntity *ENTITYPTR;
  1592. class CEntityReportLess
  1593. {
  1594. public:
  1595. bool Less( const ENTITYPTR &src1, const ENTITYPTR &src2, void *pCtx )
  1596. {
  1597. if ( stricmp( src1->GetClassname(), src2->GetClassname() ) < 0 )
  1598. return true;
  1599. return false;
  1600. }
  1601. };
  1602. void AddEntityToList( CBaseEntity *pEntity )
  1603. {
  1604. if ( !pEntity )
  1605. {
  1606. m_emptyCount++;
  1607. }
  1608. else
  1609. {
  1610. m_sortedList.Insert( pEntity );
  1611. }
  1612. }
  1613. void ReportEntityList()
  1614. {
  1615. const char *pLastClass = "";
  1616. int count = 0;
  1617. int edicts = 0;
  1618. for ( int i = 0; i < m_sortedList.Count(); i++ )
  1619. {
  1620. CBaseEntity *pEntity = m_sortedList[i];
  1621. if ( !pEntity )
  1622. continue;
  1623. if ( pEntity->edict() )
  1624. edicts++;
  1625. const char *pClassname = pEntity->GetClassname();
  1626. if ( !FStrEq( pClassname, pLastClass ) )
  1627. {
  1628. if ( count )
  1629. {
  1630. Msg("Class: %s (%d)\n", pLastClass, count );
  1631. }
  1632. pLastClass = pClassname;
  1633. count = 1;
  1634. }
  1635. else
  1636. count++;
  1637. }
  1638. if ( pLastClass[0] != 0 && count )
  1639. {
  1640. Msg("Class: %s (%d)\n", pLastClass, count );
  1641. }
  1642. if ( m_sortedList.Count() )
  1643. {
  1644. Msg("Total %d entities (%d empty, %d edicts)\n", m_sortedList.Count(), m_emptyCount, edicts );
  1645. }
  1646. }
  1647. private:
  1648. CUtlSortVector< CBaseEntity *, CEntityReportLess > m_sortedList;
  1649. int m_emptyCount;
  1650. };
  1651. CON_COMMAND(report_entities, "Lists all entities")
  1652. {
  1653. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1654. return;
  1655. CSortedEntityList list;
  1656. CBaseEntity *pEntity = gEntList.FirstEnt();
  1657. while ( pEntity )
  1658. {
  1659. list.AddEntityToList( pEntity );
  1660. pEntity = gEntList.NextEnt( pEntity );
  1661. }
  1662. list.ReportEntityList();
  1663. }
  1664. CON_COMMAND(report_touchlinks, "Lists all touchlinks")
  1665. {
  1666. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1667. return;
  1668. CSortedEntityList list;
  1669. CBaseEntity *pEntity = gEntList.FirstEnt();
  1670. const char *pClassname = NULL;
  1671. if ( args.ArgC() > 1 )
  1672. {
  1673. pClassname = args.Arg(1);
  1674. }
  1675. while ( pEntity )
  1676. {
  1677. if ( !pClassname || FClassnameIs(pEntity, pClassname) )
  1678. {
  1679. touchlink_t *root = ( touchlink_t * )pEntity->GetDataObject( TOUCHLINK );
  1680. if ( root )
  1681. {
  1682. touchlink_t *link = root->nextLink;
  1683. while ( link != root )
  1684. {
  1685. list.AddEntityToList( link->entityTouched );
  1686. link = link->nextLink;
  1687. }
  1688. }
  1689. }
  1690. pEntity = gEntList.NextEnt( pEntity );
  1691. }
  1692. list.ReportEntityList();
  1693. }
  1694. CON_COMMAND(report_simthinklist, "Lists all simulating/thinking entities")
  1695. {
  1696. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1697. return;
  1698. CBaseEntity *pTmp[NUM_ENT_ENTRIES];
  1699. int count = SimThink_ListCopy( pTmp, ARRAYSIZE(pTmp) );
  1700. CSortedEntityList list;
  1701. for ( int i = 0; i < count; i++ )
  1702. {
  1703. if ( !pTmp[i] )
  1704. continue;
  1705. list.AddEntityToList( pTmp[i] );
  1706. }
  1707. list.ReportEntityList();
  1708. }