aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/server/entitylist.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/entitylist.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/game/server/entitylist.cpp')
-rw-r--r--sp/src/game/server/entitylist.cpp3272
1 files changed, 1636 insertions, 1636 deletions
diff --git a/sp/src/game/server/entitylist.cpp b/sp/src/game/server/entitylist.cpp
index 3ad3cada..6244fa7d 100644
--- a/sp/src/game/server/entitylist.cpp
+++ b/sp/src/game/server/entitylist.cpp
@@ -1,1636 +1,1636 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "entitylist.h"
-#include "utlvector.h"
-#include "igamesystem.h"
-#include "collisionutils.h"
-#include "UtlSortVector.h"
-#include "tier0/vprof.h"
-#include "mapentities.h"
-#include "client.h"
-#include "ai_initutils.h"
-#include "globalstate.h"
-#include "datacache/imdlcache.h"
-
-#ifdef HL2_DLL
-#include "npc_playercompanion.h"
-#endif // HL2_DLL
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-CBaseEntity *FindPickerEntity( CBasePlayer *pPlayer );
-void SceneManager_ClientActive( CBasePlayer *player );
-
-static CUtlVector<IServerNetworkable*> g_DeleteList;
-
-CGlobalEntityList gEntList;
-CBaseEntityList *g_pEntityList = &gEntList;
-
-class CAimTargetManager : public IEntityListener
-{
-public:
- // Called by CEntityListSystem
- void LevelInitPreEntity()
- {
- gEntList.AddListenerEntity( this );
- Clear();
- }
- void LevelShutdownPostEntity()
- {
- gEntList.RemoveListenerEntity( this );
- Clear();
- }
-
- void Clear()
- {
- m_targetList.Purge();
- }
-
- void ForceRepopulateList()
- {
- Clear();
-
- CBaseEntity *pEnt = gEntList.FirstEnt();
-
- while( pEnt )
- {
- if( ShouldAddEntity(pEnt) )
- AddEntity(pEnt);
-
- pEnt = gEntList.NextEnt( pEnt );
- }
- }
-
- bool ShouldAddEntity( CBaseEntity *pEntity )
- {
- return ((pEntity->GetFlags() & FL_AIMTARGET) != 0);
- }
-
- // IEntityListener
- virtual void OnEntityCreated( CBaseEntity *pEntity ) {}
- virtual void OnEntityDeleted( CBaseEntity *pEntity )
- {
- if ( !(pEntity->GetFlags() & FL_AIMTARGET) )
- return;
- RemoveEntity(pEntity);
- }
- void AddEntity( CBaseEntity *pEntity )
- {
- if ( pEntity->IsMarkedForDeletion() )
- return;
- m_targetList.AddToTail( pEntity );
- }
- void RemoveEntity( CBaseEntity *pEntity )
- {
- int index = m_targetList.Find( pEntity );
- if ( m_targetList.IsValidIndex(index) )
- {
- m_targetList.FastRemove( index );
- }
- }
- int ListCount() { return m_targetList.Count(); }
- int ListCopy( CBaseEntity *pList[], int listMax )
- {
- int count = MIN(listMax, ListCount() );
- memcpy( pList, m_targetList.Base(), sizeof(CBaseEntity *) * count );
- return count;
- }
-
-private:
- CUtlVector<CBaseEntity *> m_targetList;
-};
-
-static CAimTargetManager g_AimManager;
-
-int AimTarget_ListCount()
-{
- return g_AimManager.ListCount();
-}
-int AimTarget_ListCopy( CBaseEntity *pList[], int listMax )
-{
- return g_AimManager.ListCopy( pList, listMax );
-}
-void AimTarget_ForceRepopulateList()
-{
- g_AimManager.ForceRepopulateList();
-}
-
-
-// Manages a list of all entities currently doing game simulation or thinking
-// NOTE: This is usually a small subset of the global entity list, so it's
-// an optimization to maintain this list incrementally rather than polling each
-// frame.
-struct simthinkentry_t
-{
- unsigned short entEntry;
- unsigned short unused0;
- int nextThinkTick;
-};
-class CSimThinkManager : public IEntityListener
-{
-public:
- CSimThinkManager()
- {
- Clear();
- }
- void Clear()
- {
- m_simThinkList.Purge();
- for ( int i = 0; i < ARRAYSIZE(m_entinfoIndex); i++ )
- {
- m_entinfoIndex[i] = 0xFFFF;
- }
- }
- void LevelInitPreEntity()
- {
- gEntList.AddListenerEntity( this );
- }
-
- void LevelShutdownPostEntity()
- {
- gEntList.RemoveListenerEntity( this );
- Clear();
- }
-
- void OnEntityCreated( CBaseEntity *pEntity )
- {
- Assert( m_entinfoIndex[pEntity->GetRefEHandle().GetEntryIndex()] == 0xFFFF );
- }
- void OnEntityDeleted( CBaseEntity *pEntity )
- {
- RemoveEntinfoIndex( pEntity->GetRefEHandle().GetEntryIndex() );
- }
-
- void RemoveEntinfoIndex( int index )
- {
- int listHandle = m_entinfoIndex[index];
- // If this guy is in the active list, remove him
- if ( listHandle != 0xFFFF )
- {
- Assert(m_simThinkList[listHandle].entEntry == index);
- m_simThinkList.FastRemove( listHandle );
- m_entinfoIndex[index] = 0xFFFF;
-
- // fast remove shifted someone, update that someone
- if ( listHandle < m_simThinkList.Count() )
- {
- m_entinfoIndex[m_simThinkList[listHandle].entEntry] = listHandle;
- }
- }
- }
- int ListCount()
- {
- return m_simThinkList.Count();
- }
-
- int ListCopy( CBaseEntity *pList[], int listMax )
- {
- int count = MIN(listMax, ListCount());
- int out = 0;
- for ( int i = 0; i < count; i++ )
- {
- // only copy out entities that will simulate or think this frame
- if ( m_simThinkList[i].nextThinkTick <= gpGlobals->tickcount )
- {
- Assert(m_simThinkList[i].nextThinkTick>=0);
- int entinfoIndex = m_simThinkList[i].entEntry;
- const CEntInfo *pInfo = gEntList.GetEntInfoPtrByIndex( entinfoIndex );
- pList[out] = (CBaseEntity *)pInfo->m_pEntity;
- Assert(m_simThinkList[i].nextThinkTick==0 || pList[out]->GetFirstThinkTick()==m_simThinkList[i].nextThinkTick);
- Assert( gEntList.IsEntityPtr( pList[out] ) );
- out++;
- }
- }
-
- return out;
- }
-
- void EntityChanged( CBaseEntity *pEntity )
- {
- // might change after deletion, don't put back into the list
- if ( pEntity->IsMarkedForDeletion() )
- return;
-
- const CBaseHandle &eh = pEntity->GetRefEHandle();
- if ( !eh.IsValid() )
- return;
-
- int index = eh.GetEntryIndex();
- if ( pEntity->IsEFlagSet( EFL_NO_THINK_FUNCTION ) && pEntity->IsEFlagSet( EFL_NO_GAME_PHYSICS_SIMULATION ) )
- {
- RemoveEntinfoIndex( index );
- }
- else
- {
- // already in the list? (had think or sim last time, now has both - or had both last time, now just one)
- if ( m_entinfoIndex[index] == 0xFFFF )
- {
- MEM_ALLOC_CREDIT();
- m_entinfoIndex[index] = m_simThinkList.AddToTail();
- m_simThinkList[m_entinfoIndex[index]].entEntry = (unsigned short)index;
- m_simThinkList[m_entinfoIndex[index]].nextThinkTick = 0;
- if ( pEntity->IsEFlagSet(EFL_NO_GAME_PHYSICS_SIMULATION) )
- {
- m_simThinkList[m_entinfoIndex[index]].nextThinkTick = pEntity->GetFirstThinkTick();
- Assert(m_simThinkList[m_entinfoIndex[index]].nextThinkTick>=0);
- }
- }
- else
- {
- // updating existing entry - if no sim, reset think time
- if ( pEntity->IsEFlagSet(EFL_NO_GAME_PHYSICS_SIMULATION) )
- {
- m_simThinkList[m_entinfoIndex[index]].nextThinkTick = pEntity->GetFirstThinkTick();
- Assert(m_simThinkList[m_entinfoIndex[index]].nextThinkTick>=0);
- }
- else
- {
- m_simThinkList[m_entinfoIndex[index]].nextThinkTick = 0;
- }
- }
- }
- }
-
-private:
- unsigned short m_entinfoIndex[NUM_ENT_ENTRIES];
- CUtlVector<simthinkentry_t> m_simThinkList;
-};
-
-CSimThinkManager g_SimThinkManager;
-
-int SimThink_ListCount()
-{
- return g_SimThinkManager.ListCount();
-}
-
-int SimThink_ListCopy( CBaseEntity *pList[], int listMax )
-{
- return g_SimThinkManager.ListCopy( pList, listMax );
-}
-
-void SimThink_EntityChanged( CBaseEntity *pEntity )
-{
- g_SimThinkManager.EntityChanged( pEntity );
-}
-
-static CBaseEntityClassList *s_pClassLists = NULL;
-CBaseEntityClassList::CBaseEntityClassList()
-{
- m_pNextClassList = s_pClassLists;
- s_pClassLists = this;
-}
-CBaseEntityClassList::~CBaseEntityClassList()
-{
-}
-
-CGlobalEntityList::CGlobalEntityList()
-{
- m_iHighestEnt = m_iNumEnts = m_iNumEdicts = 0;
- m_bClearingEntities = false;
-}
-
-
-// removes the entity from the global list
-// only called from with the CBaseEntity destructor
-static bool g_fInCleanupDelete;
-
-
-// mark an entity as deleted
-void CGlobalEntityList::AddToDeleteList( IServerNetworkable *ent )
-{
- if ( ent && ent->GetEntityHandle()->GetRefEHandle() != INVALID_EHANDLE_INDEX )
- {
- g_DeleteList.AddToTail( ent );
- }
-}
-
-extern bool g_bDisableEhandleAccess;
-// call this before and after each frame to delete all of the marked entities.
-void CGlobalEntityList::CleanupDeleteList( void )
-{
- VPROF( "CGlobalEntityList::CleanupDeleteList" );
- g_fInCleanupDelete = true;
- // clean up the vphysics delete list as well
- PhysOnCleanupDeleteList();
-
- g_bDisableEhandleAccess = true;
- for ( int i = 0; i < g_DeleteList.Count(); i++ )
- {
- g_DeleteList[i]->Release();
- }
- g_bDisableEhandleAccess = false;
- g_DeleteList.RemoveAll();
-
- g_fInCleanupDelete = false;
-}
-
-int CGlobalEntityList::ResetDeleteList( void )
-{
- int result = g_DeleteList.Count();
- g_DeleteList.RemoveAll();
- return result;
-}
-
-
- // add a class that gets notified of entity events
-void CGlobalEntityList::AddListenerEntity( IEntityListener *pListener )
-{
- if ( m_entityListeners.Find( pListener ) >= 0 )
- {
- AssertMsg( 0, "Can't add listeners multiple times\n" );
- return;
- }
- m_entityListeners.AddToTail( pListener );
-}
-
-void CGlobalEntityList::RemoveListenerEntity( IEntityListener *pListener )
-{
- m_entityListeners.FindAndRemove( pListener );
-}
-
-void CGlobalEntityList::Clear( void )
-{
- m_bClearingEntities = true;
-
- // Add all remaining entities in the game to the delete list and call appropriate UpdateOnRemove
- CBaseHandle hCur = FirstHandle();
- while ( hCur != InvalidHandle() )
- {
- IServerNetworkable *ent = GetServerNetworkable( hCur );
- if ( ent )
- {
- MDLCACHE_CRITICAL_SECTION();
- // Force UpdateOnRemove to be called
- UTIL_Remove( ent );
- }
- hCur = NextHandle( hCur );
- }
-
- CleanupDeleteList();
- // free the memory
- g_DeleteList.Purge();
-
- CBaseEntity::m_nDebugPlayer = -1;
- CBaseEntity::m_bInDebugSelect = false;
- m_iHighestEnt = 0;
- m_iNumEnts = 0;
-
- m_bClearingEntities = false;
-}
-
-
-int CGlobalEntityList::NumberOfEntities( void )
-{
- return m_iNumEnts;
-}
-
-int CGlobalEntityList::NumberOfEdicts( void )
-{
- return m_iNumEdicts;
-}
-
-CBaseEntity *CGlobalEntityList::NextEnt( CBaseEntity *pCurrentEnt )
-{
- if ( !pCurrentEnt )
- {
- const CEntInfo *pInfo = FirstEntInfo();
- if ( !pInfo )
- return NULL;
-
- return (CBaseEntity *)pInfo->m_pEntity;
- }
-
- // Run through the list until we get a CBaseEntity.
- const CEntInfo *pList = GetEntInfoPtr( pCurrentEnt->GetRefEHandle() );
- if ( pList )
- pList = NextEntInfo(pList);
-
- while ( pList )
- {
-#if 0
- if ( pList->m_pEntity )
- {
- IServerUnknown *pUnk = static_cast<IServerUnknown*>(const_cast<IHandleEntity*>(pList->m_pEntity));
- CBaseEntity *pRet = pUnk->GetBaseEntity();
- if ( pRet )
- return pRet;
- }
-#else
- return (CBaseEntity *)pList->m_pEntity;
-#endif
- pList = pList->m_pNext;
- }
-
- return NULL;
-
-}
-
-
-void CGlobalEntityList::ReportEntityFlagsChanged( CBaseEntity *pEntity, unsigned int flagsOld, unsigned int flagsNow )
-{
- if ( pEntity->IsMarkedForDeletion() )
- return;
- // UNDONE: Move this into IEntityListener instead?
- unsigned int flagsChanged = flagsOld ^ flagsNow;
- if ( flagsChanged & FL_AIMTARGET )
- {
- unsigned int flagsAdded = flagsNow & flagsChanged;
- unsigned int flagsRemoved = flagsOld & flagsChanged;
-
- if ( flagsAdded & FL_AIMTARGET )
- {
- g_AimManager.AddEntity( pEntity );
- }
- if ( flagsRemoved & FL_AIMTARGET )
- {
- g_AimManager.RemoveEntity( pEntity );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Used to confirm a pointer is a pointer to an entity, useful for
-// asserts.
-//-----------------------------------------------------------------------------
-bool CGlobalEntityList::IsEntityPtr( void *pTest )
-{
- if ( pTest )
- {
- const CEntInfo *pInfo = FirstEntInfo();
- for ( ;pInfo; pInfo = pInfo->m_pNext )
- {
- if ( pTest == (void *)pInfo->m_pEntity )
- return true;
- }
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Iterates the entities with a given classname.
-// Input : pStartEntity - Last entity found, NULL to start a new iteration.
-// szName - Classname to search for.
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityByClassname( CBaseEntity *pStartEntity, const char *szName )
-{
- const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
-
- for ( ;pInfo; pInfo = pInfo->m_pNext )
- {
- CBaseEntity *pEntity = (CBaseEntity *)pInfo->m_pEntity;
- if ( !pEntity )
- {
- DevWarning( "NULL entity in global entity list!\n" );
- continue;
- }
-
- if ( pEntity->ClassMatches(szName) )
- return pEntity;
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds an entity given a procedural name.
-// Input : szName - The procedural name to search for, should start with '!'.
-// pSearchingEntity -
-// pActivator - The activator entity if this was called from an input
-// or Use handler.
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityProcedural( const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
-{
- //
- // Check for the name escape character.
- //
- if ( szName[0] == '!' )
- {
- const char *pName = szName + 1;
-
- //
- // It is a procedural name, look for the ones we understand.
- //
- if ( FStrEq( pName, "player" ) )
- {
- return (CBaseEntity *)UTIL_PlayerByIndex( 1 );
- }
- else if ( FStrEq( pName, "pvsplayer" ) )
- {
- if ( pSearchingEntity )
- {
- return CBaseEntity::Instance( UTIL_FindClientInPVS( pSearchingEntity->edict() ) );
- }
- else if ( pActivator )
- {
- // FIXME: error condition?
- return CBaseEntity::Instance( UTIL_FindClientInPVS( pActivator->edict() ) );
- }
- else
- {
- // FIXME: error condition?
- return (CBaseEntity *)UTIL_PlayerByIndex( 1 );
- }
-
- }
- else if ( FStrEq( pName, "activator" ) )
- {
- return pActivator;
- }
- else if ( FStrEq( pName, "caller" ) )
- {
- return pCaller;
- }
- else if ( FStrEq( pName, "picker" ) )
- {
- return FindPickerEntity( UTIL_PlayerByIndex(1) );
- }
- else if ( FStrEq( pName, "self" ) )
- {
- return pSearchingEntity;
- }
- else
- {
- Warning( "Invalid entity search name %s\n", szName );
- Assert(0);
- }
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Iterates the entities with a given name.
-// Input : pStartEntity - Last entity found, NULL to start a new iteration.
-// szName - Name to search for.
-// pActivator - Activator entity if this was called from an input
-// handler or Use handler.
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityByName( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller, IEntityFindFilter *pFilter )
-{
- if ( !szName || szName[0] == 0 )
- return NULL;
-
- if ( szName[0] == '!' )
- {
- //
- // Avoid an infinite loop, only find one match per procedural search!
- //
- if (pStartEntity == NULL)
- return FindEntityProcedural( szName, pSearchingEntity, pActivator, pCaller );
-
- return NULL;
- }
-
- const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
-
- for ( ;pInfo; pInfo = pInfo->m_pNext )
- {
- CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
- if ( !ent )
- {
- DevWarning( "NULL entity in global entity list!\n" );
- continue;
- }
-
- if ( !ent->m_iName )
- continue;
-
- if ( ent->NameMatches( szName ) )
- {
- if ( pFilter && !pFilter->ShouldFindEntity(ent) )
- continue;
-
- return ent;
- }
- }
-
- return NULL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pStartEntity -
-// szModelName -
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityByModel( CBaseEntity *pStartEntity, const char *szModelName )
-{
- const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
-
- for ( ;pInfo; pInfo = pInfo->m_pNext )
- {
- CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
- if ( !ent )
- {
- DevWarning( "NULL entity in global entity list!\n" );
- continue;
- }
-
- if ( !ent->edict() || !ent->GetModelName() )
- continue;
-
- if ( FStrEq( STRING(ent->GetModelName()), szModelName ) )
- return ent;
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Iterates the entities with a given target.
-// Input : pStartEntity -
-// szName -
-//-----------------------------------------------------------------------------
-// FIXME: obsolete, remove
-CBaseEntity *CGlobalEntityList::FindEntityByTarget( CBaseEntity *pStartEntity, const char *szName )
-{
- const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
-
- for ( ;pInfo; pInfo = pInfo->m_pNext )
- {
- CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
- if ( !ent )
- {
- DevWarning( "NULL entity in global entity list!\n" );
- continue;
- }
-
- if ( !ent->m_target )
- continue;
-
- if ( FStrEq( STRING(ent->m_target), szName ) )
- return ent;
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Used to iterate all the entities within a sphere.
-// Input : pStartEntity -
-// vecCenter -
-// flRadius -
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityInSphere( CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius )
-{
- const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
-
- for ( ;pInfo; pInfo = pInfo->m_pNext )
- {
- CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
- if ( !ent )
- {
- DevWarning( "NULL entity in global entity list!\n" );
- continue;
- }
-
- if ( !ent->edict() )
- continue;
-
- Vector vecRelativeCenter;
- ent->CollisionProp()->WorldToCollisionSpace( vecCenter, &vecRelativeCenter );
- if ( !IsBoxIntersectingSphere( ent->CollisionProp()->OBBMins(), ent->CollisionProp()->OBBMaxs(), vecRelativeCenter, flRadius ) )
- continue;
-
- return ent;
- }
-
- // nothing found
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds the nearest entity by name within a radius
-// Input : szName - Entity name to search for.
-// vecSrc - Center of search radius.
-// flRadius - Search radius for classname search, 0 to search everywhere.
-// pSearchingEntity - The entity that is doing the search.
-// pActivator - The activator entity if this was called from an input
-// or Use handler, NULL otherwise.
-// Output : Returns a pointer to the found entity, NULL if none.
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityByNameNearest( const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
-{
- CBaseEntity *pEntity = NULL;
-
- //
- // Check for matching class names within the search radius.
- //
- float flMaxDist2 = flRadius * flRadius;
- if (flMaxDist2 == 0)
- {
- flMaxDist2 = MAX_TRACE_LENGTH * MAX_TRACE_LENGTH;
- }
-
- CBaseEntity *pSearch = NULL;
- while ((pSearch = gEntList.FindEntityByName( pSearch, szName, pSearchingEntity, pActivator, pCaller )) != NULL)
- {
- if ( !pSearch->edict() )
- continue;
-
- float flDist2 = (pSearch->GetAbsOrigin() - vecSrc).LengthSqr();
-
- if (flMaxDist2 > flDist2)
- {
- pEntity = pSearch;
- flMaxDist2 = flDist2;
- }
- }
-
- return pEntity;
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds the first entity by name within a radius
-// Input : pStartEntity - The entity to start from when doing the search.
-// szName - Entity name to search for.
-// vecSrc - Center of search radius.
-// flRadius - Search radius for classname search, 0 to search everywhere.
-// pSearchingEntity - The entity that is doing the search.
-// pActivator - The activator entity if this was called from an input
-// or Use handler, NULL otherwise.
-// Output : Returns a pointer to the found entity, NULL if none.
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityByNameWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
-{
- //
- // Check for matching class names within the search radius.
- //
- CBaseEntity *pEntity = pStartEntity;
- float flMaxDist2 = flRadius * flRadius;
- if (flMaxDist2 == 0)
- {
- return gEntList.FindEntityByName( pEntity, szName, pSearchingEntity, pActivator, pCaller );
- }
-
- while ((pEntity = gEntList.FindEntityByName( pEntity, szName, pSearchingEntity, pActivator, pCaller )) != NULL)
- {
- if ( !pEntity->edict() )
- continue;
-
- float flDist2 = (pEntity->GetAbsOrigin() - vecSrc).LengthSqr();
-
- if (flMaxDist2 > flDist2)
- {
- return pEntity;
- }
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds the nearest entity by class name withing given search radius.
-// Input : szName - Entity name to search for. Treated as a target name first,
-// then as an entity class name, ie "info_target".
-// vecSrc - Center of search radius.
-// flRadius - Search radius for classname search, 0 to search everywhere.
-// Output : Returns a pointer to the found entity, NULL if none.
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityByClassnameNearest( const char *szName, const Vector &vecSrc, float flRadius )
-{
- CBaseEntity *pEntity = NULL;
-
- //
- // Check for matching class names within the search radius.
- //
- float flMaxDist2 = flRadius * flRadius;
- if (flMaxDist2 == 0)
- {
- flMaxDist2 = MAX_TRACE_LENGTH * MAX_TRACE_LENGTH;
- }
-
- CBaseEntity *pSearch = NULL;
- while ((pSearch = gEntList.FindEntityByClassname( pSearch, szName )) != NULL)
- {
- if ( !pSearch->edict() )
- continue;
-
- float flDist2 = (pSearch->GetAbsOrigin() - vecSrc).LengthSqr();
-
- if (flMaxDist2 > flDist2)
- {
- pEntity = pSearch;
- flMaxDist2 = flDist2;
- }
- }
-
- return pEntity;
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds the first entity within radius distance by class name.
-// Input : pStartEntity - The entity to start from when doing the search.
-// szName - Entity class name, ie "info_target".
-// vecSrc - Center of search radius.
-// flRadius - Search radius for classname search, 0 to search everywhere.
-// Output : Returns a pointer to the found entity, NULL if none.
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityByClassnameWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecSrc, float flRadius )
-{
- //
- // Check for matching class names within the search radius.
- //
- CBaseEntity *pEntity = pStartEntity;
- float flMaxDist2 = flRadius * flRadius;
- if (flMaxDist2 == 0)
- {
- return gEntList.FindEntityByClassname( pEntity, szName );
- }
-
- while ((pEntity = gEntList.FindEntityByClassname( pEntity, szName )) != NULL)
- {
- if ( !pEntity->edict() )
- continue;
-
- float flDist2 = (pEntity->GetAbsOrigin() - vecSrc).LengthSqr();
-
- if (flMaxDist2 > flDist2)
- {
- return pEntity;
- }
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds the first entity within an extent by class name.
-// Input : pStartEntity - The entity to start from when doing the search.
-// szName - Entity class name, ie "info_target".
-// vecMins - Search mins.
-// vecMaxs - Search maxs.
-// Output : Returns a pointer to the found entity, NULL if none.
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityByClassnameWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecMins, const Vector &vecMaxs )
-{
- //
- // Check for matching class names within the search radius.
- //
- CBaseEntity *pEntity = pStartEntity;
-
- while ((pEntity = gEntList.FindEntityByClassname( pEntity, szName )) != NULL)
- {
- if ( !pEntity->edict() && !pEntity->IsEFlagSet( EFL_SERVER_ONLY ) )
- continue;
-
- // check if the aabb intersects the search aabb.
- Vector entMins, entMaxs;
- pEntity->CollisionProp()->WorldSpaceAABB( &entMins, &entMaxs );
- if ( IsBoxIntersectingBox( vecMins, vecMaxs, entMins, entMaxs ) )
- {
- return pEntity;
- }
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds an entity by target name or class name.
-// Input : pStartEntity - The entity to start from when doing the search.
-// szName - Entity name to search for. Treated as a target name first,
-// then as an entity class name, ie "info_target".
-// vecSrc - Center of search radius.
-// flRadius - Search radius for classname search, 0 to search everywhere.
-// pSearchingEntity - The entity that is doing the search.
-// pActivator - The activator entity if this was called from an input
-// or Use handler, NULL otherwise.
-// Output : Returns a pointer to the found entity, NULL if none.
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityGeneric( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
-{
- CBaseEntity *pEntity = NULL;
-
- pEntity = gEntList.FindEntityByName( pStartEntity, szName, pSearchingEntity, pActivator, pCaller );
- if (!pEntity)
- {
- pEntity = gEntList.FindEntityByClassname( pStartEntity, szName );
- }
-
- return pEntity;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds the first entity by target name or class name within a radius
-// Input : pStartEntity - The entity to start from when doing the search.
-// szName - Entity name to search for. Treated as a target name first,
-// then as an entity class name, ie "info_target".
-// vecSrc - Center of search radius.
-// flRadius - Search radius for classname search, 0 to search everywhere.
-// pSearchingEntity - The entity that is doing the search.
-// pActivator - The activator entity if this was called from an input
-// or Use handler, NULL otherwise.
-// Output : Returns a pointer to the found entity, NULL if none.
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityGenericWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
-{
- CBaseEntity *pEntity = NULL;
-
- pEntity = gEntList.FindEntityByNameWithin( pStartEntity, szName, vecSrc, flRadius, pSearchingEntity, pActivator, pCaller );
- if (!pEntity)
- {
- pEntity = gEntList.FindEntityByClassnameWithin( pStartEntity, szName, vecSrc, flRadius );
- }
-
- return pEntity;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds the nearest entity by target name or class name within a radius.
-// Input : pStartEntity - The entity to start from when doing the search.
-// szName - Entity name to search for. Treated as a target name first,
-// then as an entity class name, ie "info_target".
-// vecSrc - Center of search radius.
-// flRadius - Search radius for classname search, 0 to search everywhere.
-// pSearchingEntity - The entity that is doing the search.
-// pActivator - The activator entity if this was called from an input
-// or Use handler, NULL otherwise.
-// Output : Returns a pointer to the found entity, NULL if none.
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityGenericNearest( const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
-{
- CBaseEntity *pEntity = NULL;
-
- pEntity = gEntList.FindEntityByNameNearest( szName, vecSrc, flRadius, pSearchingEntity, pActivator, pCaller );
- if (!pEntity)
- {
- pEntity = gEntList.FindEntityByClassnameNearest( szName, vecSrc, flRadius );
- }
-
- return pEntity;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Find the nearest entity along the facing direction from the given origin
-// within the angular threshold (ignores worldspawn) with the
-// given classname.
-// Input : origin -
-// facing -
-// threshold -
-// classname -
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityClassNearestFacing( const Vector &origin, const Vector &facing, float threshold, char *classname)
-{
- float bestDot = threshold;
- CBaseEntity *best_ent = NULL;
-
- const CEntInfo *pInfo = FirstEntInfo();
-
- for ( ;pInfo; pInfo = pInfo->m_pNext )
- {
- CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
- if ( !ent )
- {
- DevWarning( "NULL entity in global entity list!\n" );
- continue;
- }
-
- // FIXME: why is this skipping pointsize entities?
- if (ent->IsPointSized() )
- continue;
-
- // Make vector to entity
- Vector to_ent = (ent->GetAbsOrigin() - origin);
-
- VectorNormalize( to_ent );
- float dot = DotProduct (facing , to_ent );
- if (dot > bestDot)
- {
- if (FClassnameIs(ent,classname))
- {
- // Ignore if worldspawn
- if (!FClassnameIs( ent, "worldspawn" ) && !FClassnameIs( ent, "soundent"))
- {
- bestDot = dot;
- best_ent = ent;
- }
- }
- }
- }
- return best_ent;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Find the nearest entity along the facing direction from the given origin
-// within the angular threshold (ignores worldspawn)
-// Input : origin -
-// facing -
-// threshold -
-//-----------------------------------------------------------------------------
-CBaseEntity *CGlobalEntityList::FindEntityNearestFacing( const Vector &origin, const Vector &facing, float threshold)
-{
- float bestDot = threshold;
- CBaseEntity *best_ent = NULL;
-
- const CEntInfo *pInfo = FirstEntInfo();
-
- for ( ;pInfo; pInfo = pInfo->m_pNext )
- {
- CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
- if ( !ent )
- {
- DevWarning( "NULL entity in global entity list!\n" );
- continue;
- }
-
- // Ignore logical entities
- if (!ent->edict())
- continue;
-
- // Make vector to entity
- Vector to_ent = ent->WorldSpaceCenter() - origin;
- VectorNormalize(to_ent);
-
- float dot = DotProduct( facing, to_ent );
- if (dot <= bestDot)
- continue;
-
- // Ignore if worldspawn
- if (!FStrEq( STRING(ent->m_iClassname), "worldspawn") && !FStrEq( STRING(ent->m_iClassname), "soundent"))
- {
- bestDot = dot;
- best_ent = ent;
- }
- }
- return best_ent;
-}
-
-
-void CGlobalEntityList::OnAddEntity( IHandleEntity *pEnt, CBaseHandle handle )
-{
- int i = handle.GetEntryIndex();
-
- // record current list details
- m_iNumEnts++;
- if ( i > m_iHighestEnt )
- m_iHighestEnt = i;
-
- // If it's a CBaseEntity, notify the listeners.
- CBaseEntity *pBaseEnt = static_cast<IServerUnknown*>(pEnt)->GetBaseEntity();
- if ( pBaseEnt->edict() )
- m_iNumEdicts++;
-
- // NOTE: Must be a CBaseEntity on server
- Assert( pBaseEnt );
- //DevMsg(2,"Created %s\n", pBaseEnt->GetClassname() );
- for ( i = m_entityListeners.Count()-1; i >= 0; i-- )
- {
- m_entityListeners[i]->OnEntityCreated( pBaseEnt );
- }
-}
-
-
-void CGlobalEntityList::OnRemoveEntity( IHandleEntity *pEnt, CBaseHandle handle )
-{
-#ifdef DEBUG
- if ( !g_fInCleanupDelete )
- {
- int i;
- for ( i = 0; i < g_DeleteList.Count(); i++ )
- {
- if ( g_DeleteList[i]->GetEntityHandle() == pEnt )
- {
- g_DeleteList.FastRemove( i );
- Msg( "ERROR: Entity being destroyed but previously threaded on g_DeleteList\n" );
- break;
- }
- }
- }
-#endif
-
- CBaseEntity *pBaseEnt = static_cast<IServerUnknown*>(pEnt)->GetBaseEntity();
- if ( pBaseEnt->edict() )
- m_iNumEdicts--;
-
- m_iNumEnts--;
-}
-
-void CGlobalEntityList::NotifyCreateEntity( CBaseEntity *pEnt )
-{
- if ( !pEnt )
- return;
-
- //DevMsg(2,"Deleted %s\n", pBaseEnt->GetClassname() );
- for ( int i = m_entityListeners.Count()-1; i >= 0; i-- )
- {
- m_entityListeners[i]->OnEntityCreated( pEnt );
- }
-}
-
-void CGlobalEntityList::NotifySpawn( CBaseEntity *pEnt )
-{
- if ( !pEnt )
- return;
-
- //DevMsg(2,"Deleted %s\n", pBaseEnt->GetClassname() );
- for ( int i = m_entityListeners.Count()-1; i >= 0; i-- )
- {
- m_entityListeners[i]->OnEntitySpawned( pEnt );
- }
-}
-
-// NOTE: This doesn't happen in OnRemoveEntity() specifically because
-// listeners may want to reference the object as it's being deleted
-// OnRemoveEntity isn't called until the destructor and all data is invalid.
-void CGlobalEntityList::NotifyRemoveEntity( CBaseHandle hEnt )
-{
- CBaseEntity *pBaseEnt = GetBaseEntity( hEnt );
- if ( !pBaseEnt )
- return;
-
- //DevMsg(2,"Deleted %s\n", pBaseEnt->GetClassname() );
- for ( int i = m_entityListeners.Count()-1; i >= 0; i-- )
- {
- m_entityListeners[i]->OnEntityDeleted( pBaseEnt );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// NOTIFY LIST
-//
-// Allows entities to get events fired when another entity changes
-//-----------------------------------------------------------------------------
-struct entitynotify_t
-{
- CBaseEntity *pNotify;
- CBaseEntity *pWatched;
-};
-class CNotifyList : public INotify, public IEntityListener
-{
-public:
- // INotify
- void AddEntity( CBaseEntity *pNotify, CBaseEntity *pWatched );
- void RemoveEntity( CBaseEntity *pNotify, CBaseEntity *pWatched );
- void ReportNamedEvent( CBaseEntity *pEntity, const char *pEventName );
- void ClearEntity( CBaseEntity *pNotify );
- void ReportSystemEvent( CBaseEntity *pEntity, notify_system_event_t eventType, const notify_system_event_params_t &params );
-
- // IEntityListener
- virtual void OnEntityCreated( CBaseEntity *pEntity );
- virtual void OnEntityDeleted( CBaseEntity *pEntity );
-
- // Called from CEntityListSystem
- void LevelInitPreEntity();
- void LevelShutdownPreEntity();
-
-private:
- CUtlVector<entitynotify_t> m_notifyList;
-};
-
-void CNotifyList::AddEntity( CBaseEntity *pNotify, CBaseEntity *pWatched )
-{
- // OPTIMIZE: Also flag pNotify for faster "RemoveAllNotify" ?
- pWatched->AddEFlags( EFL_NOTIFY );
- int index = m_notifyList.AddToTail();
- entitynotify_t &notify = m_notifyList[index];
- notify.pNotify = pNotify;
- notify.pWatched = pWatched;
-}
-
-// Remove noitfication for an entity
-void CNotifyList::RemoveEntity( CBaseEntity *pNotify, CBaseEntity *pWatched )
-{
- for ( int i = m_notifyList.Count(); --i >= 0; )
- {
- if ( m_notifyList[i].pNotify == pNotify && m_notifyList[i].pWatched == pWatched)
- {
- m_notifyList.FastRemove(i);
- }
- }
-}
-
-
-void CNotifyList::ReportNamedEvent( CBaseEntity *pEntity, const char *pInputName )
-{
- variant_t emptyVariant;
-
- if ( !pEntity->IsEFlagSet(EFL_NOTIFY) )
- return;
-
- for ( int i = 0; i < m_notifyList.Count(); i++ )
- {
- if ( m_notifyList[i].pWatched == pEntity )
- {
- m_notifyList[i].pNotify->AcceptInput( pInputName, pEntity, pEntity, emptyVariant, 0 );
- }
- }
-}
-
-void CNotifyList::LevelInitPreEntity()
-{
- gEntList.AddListenerEntity( this );
-}
-
-void CNotifyList::LevelShutdownPreEntity( void )
-{
- gEntList.RemoveListenerEntity( this );
- m_notifyList.Purge();
-}
-
-void CNotifyList::OnEntityCreated( CBaseEntity *pEntity )
-{
-}
-
-void CNotifyList::OnEntityDeleted( CBaseEntity *pEntity )
-{
- ReportDestroyEvent( pEntity );
- ClearEntity( pEntity );
-}
-
-
-// UNDONE: Slow linear search?
-void CNotifyList::ClearEntity( CBaseEntity *pNotify )
-{
- for ( int i = m_notifyList.Count(); --i >= 0; )
- {
- if ( m_notifyList[i].pNotify == pNotify || m_notifyList[i].pWatched == pNotify)
- {
- m_notifyList.FastRemove(i);
- }
- }
-}
-
-void CNotifyList::ReportSystemEvent( CBaseEntity *pEntity, notify_system_event_t eventType, const notify_system_event_params_t &params )
-{
- if ( !pEntity->IsEFlagSet(EFL_NOTIFY) )
- return;
-
- for ( int i = 0; i < m_notifyList.Count(); i++ )
- {
- if ( m_notifyList[i].pWatched == pEntity )
- {
- m_notifyList[i].pNotify->NotifySystemEvent( pEntity, eventType, params );
- }
- }
-}
-
-static CNotifyList g_NotifyList;
-INotify *g_pNotify = &g_NotifyList;
-
-class CEntityTouchManager : public IEntityListener
-{
-public:
- // called by CEntityListSystem
- void LevelInitPreEntity()
- {
- gEntList.AddListenerEntity( this );
- Clear();
- }
- void LevelShutdownPostEntity()
- {
- gEntList.RemoveListenerEntity( this );
- Clear();
- }
- void FrameUpdatePostEntityThink();
-
- void Clear()
- {
- m_updateList.Purge();
- }
-
- // IEntityListener
- virtual void OnEntityCreated( CBaseEntity *pEntity ) {}
- virtual void OnEntityDeleted( CBaseEntity *pEntity )
- {
- if ( !pEntity->GetCheckUntouch() )
- return;
- int index = m_updateList.Find( pEntity );
- if ( m_updateList.IsValidIndex(index) )
- {
- m_updateList.FastRemove( index );
- }
- }
- void AddEntity( CBaseEntity *pEntity )
- {
- if ( pEntity->IsMarkedForDeletion() )
- return;
- m_updateList.AddToTail( pEntity );
- }
-
-private:
- CUtlVector<CBaseEntity *> m_updateList;
-};
-
-static CEntityTouchManager g_TouchManager;
-
-void EntityTouch_Add( CBaseEntity *pEntity )
-{
- g_TouchManager.AddEntity( pEntity );
-}
-
-
-void CEntityTouchManager::FrameUpdatePostEntityThink()
-{
- VPROF( "CEntityTouchManager::FrameUpdatePostEntityThink" );
- // Loop through all entities again, checking their untouch if flagged to do so
-
- int count = m_updateList.Count();
- if ( count )
- {
- // copy off the list
- CBaseEntity **ents = (CBaseEntity **)stackalloc( sizeof(CBaseEntity *) * count );
- memcpy( ents, m_updateList.Base(), sizeof(CBaseEntity *) * count );
- // clear it
- m_updateList.RemoveAll();
-
- // now update those ents
- for ( int i = 0; i < count; i++ )
- {
- //Assert( ents[i]->GetCheckUntouch() );
- if ( ents[i]->GetCheckUntouch() )
- {
- ents[i]->PhysicsCheckForEntityUntouch();
- }
- }
- stackfree( ents );
- }
-}
-
-class CRespawnEntitiesFilter : public IMapEntityFilter
-{
-public:
- virtual bool ShouldCreateEntity( const char *pClassname )
- {
- // Create everything but the world
- return Q_stricmp( pClassname, "worldspawn" ) != 0;
- }
-
- virtual CBaseEntity* CreateNextEntity( const char *pClassname )
- {
- return CreateEntityByName( pClassname );
- }
-};
-
-// One hook to rule them all...
-// Since most of the little list managers in here only need one or two of the game
-// system callbacks, this hook is a game system that passes them the appropriate callbacks
-class CEntityListSystem : public CAutoGameSystemPerFrame
-{
-public:
- CEntityListSystem( char const *name ) : CAutoGameSystemPerFrame( name )
- {
- m_bRespawnAllEntities = false;
- }
- void LevelInitPreEntity()
- {
- g_NotifyList.LevelInitPreEntity();
- g_TouchManager.LevelInitPreEntity();
- g_AimManager.LevelInitPreEntity();
- g_SimThinkManager.LevelInitPreEntity();
-#ifdef HL2_DLL
- OverrideMoveCache_LevelInitPreEntity();
-#endif // HL2_DLL
- }
- void LevelShutdownPreEntity()
- {
- g_NotifyList.LevelShutdownPreEntity();
- }
- void LevelShutdownPostEntity()
- {
- g_TouchManager.LevelShutdownPostEntity();
- g_AimManager.LevelShutdownPostEntity();
- g_SimThinkManager.LevelShutdownPostEntity();
-#ifdef HL2_DLL
- OverrideMoveCache_LevelShutdownPostEntity();
-#endif // HL2_DLL
- CBaseEntityClassList *pClassList = s_pClassLists;
- while ( pClassList )
- {
- pClassList->LevelShutdownPostEntity();
- pClassList = pClassList->m_pNextClassList;
- }
- }
-
- void FrameUpdatePostEntityThink()
- {
- g_TouchManager.FrameUpdatePostEntityThink();
-
- if ( m_bRespawnAllEntities )
- {
- m_bRespawnAllEntities = false;
-
- // Don't change globalstate owing to deletion here
- GlobalEntity_EnableStateUpdates( false );
-
- // Remove all entities
- int nPlayerIndex = -1;
- CBaseEntity *pEnt = gEntList.FirstEnt();
- while ( pEnt )
- {
- CBaseEntity *pNextEnt = gEntList.NextEnt( pEnt );
- if ( pEnt->IsPlayer() )
- {
- nPlayerIndex = pEnt->entindex();
- }
- if ( !pEnt->IsEFlagSet( EFL_KEEP_ON_RECREATE_ENTITIES ) )
- {
- UTIL_Remove( pEnt );
- }
- pEnt = pNextEnt;
- }
-
- gEntList.CleanupDeleteList();
-
- GlobalEntity_EnableStateUpdates( true );
-
- // Allows us to immediately re-use the edict indices we just freed to avoid edict overflow
- engine->AllowImmediateEdictReuse();
-
- // Reset node counter used during load
- CNodeEnt::m_nNodeCount = 0;
-
- CRespawnEntitiesFilter filter;
- MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true );
-
- // Allocate a CBasePlayer for pev, and call spawn
- if ( nPlayerIndex >= 0 )
- {
- edict_t *pEdict = engine->PEntityOfEntIndex( nPlayerIndex );
- ClientPutInServer( pEdict, "unnamed" );
- ClientActive( pEdict, false );
-
- CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
- SceneManager_ClientActive( pPlayer );
- }
- }
- }
-
- bool m_bRespawnAllEntities;
-};
-
-static CEntityListSystem g_EntityListSystem( "CEntityListSystem" );
-
-//-----------------------------------------------------------------------------
-// Respawns all entities in the level
-//-----------------------------------------------------------------------------
-void RespawnEntities()
-{
- g_EntityListSystem.m_bRespawnAllEntities = true;
-}
-
-static ConCommand restart_entities( "respawn_entities", RespawnEntities, "Respawn all the entities in the map.", FCVAR_CHEAT | FCVAR_SPONLY );
-
-class CSortedEntityList
-{
-public:
- CSortedEntityList() : m_sortedList(), m_emptyCount(0) {}
-
- typedef CBaseEntity *ENTITYPTR;
- class CEntityReportLess
- {
- public:
- bool Less( const ENTITYPTR &src1, const ENTITYPTR &src2, void *pCtx )
- {
- if ( stricmp( src1->GetClassname(), src2->GetClassname() ) < 0 )
- return true;
-
- return false;
- }
- };
-
- void AddEntityToList( CBaseEntity *pEntity )
- {
- if ( !pEntity )
- {
- m_emptyCount++;
- }
- else
- {
- m_sortedList.Insert( pEntity );
- }
- }
- void ReportEntityList()
- {
- const char *pLastClass = "";
- int count = 0;
- int edicts = 0;
- for ( int i = 0; i < m_sortedList.Count(); i++ )
- {
- CBaseEntity *pEntity = m_sortedList[i];
- if ( !pEntity )
- continue;
-
- if ( pEntity->edict() )
- edicts++;
-
- const char *pClassname = pEntity->GetClassname();
- if ( !FStrEq( pClassname, pLastClass ) )
- {
- if ( count )
- {
- Msg("Class: %s (%d)\n", pLastClass, count );
- }
-
- pLastClass = pClassname;
- count = 1;
- }
- else
- count++;
- }
- if ( pLastClass[0] != 0 && count )
- {
- Msg("Class: %s (%d)\n", pLastClass, count );
- }
- if ( m_sortedList.Count() )
- {
- Msg("Total %d entities (%d empty, %d edicts)\n", m_sortedList.Count(), m_emptyCount, edicts );
- }
- }
-private:
- CUtlSortVector< CBaseEntity *, CEntityReportLess > m_sortedList;
- int m_emptyCount;
-};
-
-
-
-CON_COMMAND(report_entities, "Lists all entities")
-{
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- CSortedEntityList list;
- CBaseEntity *pEntity = gEntList.FirstEnt();
- while ( pEntity )
- {
- list.AddEntityToList( pEntity );
- pEntity = gEntList.NextEnt( pEntity );
- }
- list.ReportEntityList();
-}
-
-
-CON_COMMAND(report_touchlinks, "Lists all touchlinks")
-{
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- CSortedEntityList list;
- CBaseEntity *pEntity = gEntList.FirstEnt();
- const char *pClassname = NULL;
- if ( args.ArgC() > 1 )
- {
- pClassname = args.Arg(1);
- }
- while ( pEntity )
- {
- if ( !pClassname || FClassnameIs(pEntity, pClassname) )
- {
- touchlink_t *root = ( touchlink_t * )pEntity->GetDataObject( TOUCHLINK );
- if ( root )
- {
- touchlink_t *link = root->nextLink;
- while ( link != root )
- {
- list.AddEntityToList( link->entityTouched );
- link = link->nextLink;
- }
- }
- }
- pEntity = gEntList.NextEnt( pEntity );
- }
- list.ReportEntityList();
-}
-
-CON_COMMAND(report_simthinklist, "Lists all simulating/thinking entities")
-{
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- CBaseEntity *pTmp[NUM_ENT_ENTRIES];
- int count = SimThink_ListCopy( pTmp, ARRAYSIZE(pTmp) );
-
- CSortedEntityList list;
- for ( int i = 0; i < count; i++ )
- {
- if ( !pTmp[i] )
- continue;
-
- list.AddEntityToList( pTmp[i] );
- }
- list.ReportEntityList();
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "entitylist.h"
+#include "utlvector.h"
+#include "igamesystem.h"
+#include "collisionutils.h"
+#include "UtlSortVector.h"
+#include "tier0/vprof.h"
+#include "mapentities.h"
+#include "client.h"
+#include "ai_initutils.h"
+#include "globalstate.h"
+#include "datacache/imdlcache.h"
+
+#ifdef HL2_DLL
+#include "npc_playercompanion.h"
+#endif // HL2_DLL
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+CBaseEntity *FindPickerEntity( CBasePlayer *pPlayer );
+void SceneManager_ClientActive( CBasePlayer *player );
+
+static CUtlVector<IServerNetworkable*> g_DeleteList;
+
+CGlobalEntityList gEntList;
+CBaseEntityList *g_pEntityList = &gEntList;
+
+class CAimTargetManager : public IEntityListener
+{
+public:
+ // Called by CEntityListSystem
+ void LevelInitPreEntity()
+ {
+ gEntList.AddListenerEntity( this );
+ Clear();
+ }
+ void LevelShutdownPostEntity()
+ {
+ gEntList.RemoveListenerEntity( this );
+ Clear();
+ }
+
+ void Clear()
+ {
+ m_targetList.Purge();
+ }
+
+ void ForceRepopulateList()
+ {
+ Clear();
+
+ CBaseEntity *pEnt = gEntList.FirstEnt();
+
+ while( pEnt )
+ {
+ if( ShouldAddEntity(pEnt) )
+ AddEntity(pEnt);
+
+ pEnt = gEntList.NextEnt( pEnt );
+ }
+ }
+
+ bool ShouldAddEntity( CBaseEntity *pEntity )
+ {
+ return ((pEntity->GetFlags() & FL_AIMTARGET) != 0);
+ }
+
+ // IEntityListener
+ virtual void OnEntityCreated( CBaseEntity *pEntity ) {}
+ virtual void OnEntityDeleted( CBaseEntity *pEntity )
+ {
+ if ( !(pEntity->GetFlags() & FL_AIMTARGET) )
+ return;
+ RemoveEntity(pEntity);
+ }
+ void AddEntity( CBaseEntity *pEntity )
+ {
+ if ( pEntity->IsMarkedForDeletion() )
+ return;
+ m_targetList.AddToTail( pEntity );
+ }
+ void RemoveEntity( CBaseEntity *pEntity )
+ {
+ int index = m_targetList.Find( pEntity );
+ if ( m_targetList.IsValidIndex(index) )
+ {
+ m_targetList.FastRemove( index );
+ }
+ }
+ int ListCount() { return m_targetList.Count(); }
+ int ListCopy( CBaseEntity *pList[], int listMax )
+ {
+ int count = MIN(listMax, ListCount() );
+ memcpy( pList, m_targetList.Base(), sizeof(CBaseEntity *) * count );
+ return count;
+ }
+
+private:
+ CUtlVector<CBaseEntity *> m_targetList;
+};
+
+static CAimTargetManager g_AimManager;
+
+int AimTarget_ListCount()
+{
+ return g_AimManager.ListCount();
+}
+int AimTarget_ListCopy( CBaseEntity *pList[], int listMax )
+{
+ return g_AimManager.ListCopy( pList, listMax );
+}
+void AimTarget_ForceRepopulateList()
+{
+ g_AimManager.ForceRepopulateList();
+}
+
+
+// Manages a list of all entities currently doing game simulation or thinking
+// NOTE: This is usually a small subset of the global entity list, so it's
+// an optimization to maintain this list incrementally rather than polling each
+// frame.
+struct simthinkentry_t
+{
+ unsigned short entEntry;
+ unsigned short unused0;
+ int nextThinkTick;
+};
+class CSimThinkManager : public IEntityListener
+{
+public:
+ CSimThinkManager()
+ {
+ Clear();
+ }
+ void Clear()
+ {
+ m_simThinkList.Purge();
+ for ( int i = 0; i < ARRAYSIZE(m_entinfoIndex); i++ )
+ {
+ m_entinfoIndex[i] = 0xFFFF;
+ }
+ }
+ void LevelInitPreEntity()
+ {
+ gEntList.AddListenerEntity( this );
+ }
+
+ void LevelShutdownPostEntity()
+ {
+ gEntList.RemoveListenerEntity( this );
+ Clear();
+ }
+
+ void OnEntityCreated( CBaseEntity *pEntity )
+ {
+ Assert( m_entinfoIndex[pEntity->GetRefEHandle().GetEntryIndex()] == 0xFFFF );
+ }
+ void OnEntityDeleted( CBaseEntity *pEntity )
+ {
+ RemoveEntinfoIndex( pEntity->GetRefEHandle().GetEntryIndex() );
+ }
+
+ void RemoveEntinfoIndex( int index )
+ {
+ int listHandle = m_entinfoIndex[index];
+ // If this guy is in the active list, remove him
+ if ( listHandle != 0xFFFF )
+ {
+ Assert(m_simThinkList[listHandle].entEntry == index);
+ m_simThinkList.FastRemove( listHandle );
+ m_entinfoIndex[index] = 0xFFFF;
+
+ // fast remove shifted someone, update that someone
+ if ( listHandle < m_simThinkList.Count() )
+ {
+ m_entinfoIndex[m_simThinkList[listHandle].entEntry] = listHandle;
+ }
+ }
+ }
+ int ListCount()
+ {
+ return m_simThinkList.Count();
+ }
+
+ int ListCopy( CBaseEntity *pList[], int listMax )
+ {
+ int count = MIN(listMax, ListCount());
+ int out = 0;
+ for ( int i = 0; i < count; i++ )
+ {
+ // only copy out entities that will simulate or think this frame
+ if ( m_simThinkList[i].nextThinkTick <= gpGlobals->tickcount )
+ {
+ Assert(m_simThinkList[i].nextThinkTick>=0);
+ int entinfoIndex = m_simThinkList[i].entEntry;
+ const CEntInfo *pInfo = gEntList.GetEntInfoPtrByIndex( entinfoIndex );
+ pList[out] = (CBaseEntity *)pInfo->m_pEntity;
+ Assert(m_simThinkList[i].nextThinkTick==0 || pList[out]->GetFirstThinkTick()==m_simThinkList[i].nextThinkTick);
+ Assert( gEntList.IsEntityPtr( pList[out] ) );
+ out++;
+ }
+ }
+
+ return out;
+ }
+
+ void EntityChanged( CBaseEntity *pEntity )
+ {
+ // might change after deletion, don't put back into the list
+ if ( pEntity->IsMarkedForDeletion() )
+ return;
+
+ const CBaseHandle &eh = pEntity->GetRefEHandle();
+ if ( !eh.IsValid() )
+ return;
+
+ int index = eh.GetEntryIndex();
+ if ( pEntity->IsEFlagSet( EFL_NO_THINK_FUNCTION ) && pEntity->IsEFlagSet( EFL_NO_GAME_PHYSICS_SIMULATION ) )
+ {
+ RemoveEntinfoIndex( index );
+ }
+ else
+ {
+ // already in the list? (had think or sim last time, now has both - or had both last time, now just one)
+ if ( m_entinfoIndex[index] == 0xFFFF )
+ {
+ MEM_ALLOC_CREDIT();
+ m_entinfoIndex[index] = m_simThinkList.AddToTail();
+ m_simThinkList[m_entinfoIndex[index]].entEntry = (unsigned short)index;
+ m_simThinkList[m_entinfoIndex[index]].nextThinkTick = 0;
+ if ( pEntity->IsEFlagSet(EFL_NO_GAME_PHYSICS_SIMULATION) )
+ {
+ m_simThinkList[m_entinfoIndex[index]].nextThinkTick = pEntity->GetFirstThinkTick();
+ Assert(m_simThinkList[m_entinfoIndex[index]].nextThinkTick>=0);
+ }
+ }
+ else
+ {
+ // updating existing entry - if no sim, reset think time
+ if ( pEntity->IsEFlagSet(EFL_NO_GAME_PHYSICS_SIMULATION) )
+ {
+ m_simThinkList[m_entinfoIndex[index]].nextThinkTick = pEntity->GetFirstThinkTick();
+ Assert(m_simThinkList[m_entinfoIndex[index]].nextThinkTick>=0);
+ }
+ else
+ {
+ m_simThinkList[m_entinfoIndex[index]].nextThinkTick = 0;
+ }
+ }
+ }
+ }
+
+private:
+ unsigned short m_entinfoIndex[NUM_ENT_ENTRIES];
+ CUtlVector<simthinkentry_t> m_simThinkList;
+};
+
+CSimThinkManager g_SimThinkManager;
+
+int SimThink_ListCount()
+{
+ return g_SimThinkManager.ListCount();
+}
+
+int SimThink_ListCopy( CBaseEntity *pList[], int listMax )
+{
+ return g_SimThinkManager.ListCopy( pList, listMax );
+}
+
+void SimThink_EntityChanged( CBaseEntity *pEntity )
+{
+ g_SimThinkManager.EntityChanged( pEntity );
+}
+
+static CBaseEntityClassList *s_pClassLists = NULL;
+CBaseEntityClassList::CBaseEntityClassList()
+{
+ m_pNextClassList = s_pClassLists;
+ s_pClassLists = this;
+}
+CBaseEntityClassList::~CBaseEntityClassList()
+{
+}
+
+CGlobalEntityList::CGlobalEntityList()
+{
+ m_iHighestEnt = m_iNumEnts = m_iNumEdicts = 0;
+ m_bClearingEntities = false;
+}
+
+
+// removes the entity from the global list
+// only called from with the CBaseEntity destructor
+static bool g_fInCleanupDelete;
+
+
+// mark an entity as deleted
+void CGlobalEntityList::AddToDeleteList( IServerNetworkable *ent )
+{
+ if ( ent && ent->GetEntityHandle()->GetRefEHandle() != INVALID_EHANDLE_INDEX )
+ {
+ g_DeleteList.AddToTail( ent );
+ }
+}
+
+extern bool g_bDisableEhandleAccess;
+// call this before and after each frame to delete all of the marked entities.
+void CGlobalEntityList::CleanupDeleteList( void )
+{
+ VPROF( "CGlobalEntityList::CleanupDeleteList" );
+ g_fInCleanupDelete = true;
+ // clean up the vphysics delete list as well
+ PhysOnCleanupDeleteList();
+
+ g_bDisableEhandleAccess = true;
+ for ( int i = 0; i < g_DeleteList.Count(); i++ )
+ {
+ g_DeleteList[i]->Release();
+ }
+ g_bDisableEhandleAccess = false;
+ g_DeleteList.RemoveAll();
+
+ g_fInCleanupDelete = false;
+}
+
+int CGlobalEntityList::ResetDeleteList( void )
+{
+ int result = g_DeleteList.Count();
+ g_DeleteList.RemoveAll();
+ return result;
+}
+
+
+ // add a class that gets notified of entity events
+void CGlobalEntityList::AddListenerEntity( IEntityListener *pListener )
+{
+ if ( m_entityListeners.Find( pListener ) >= 0 )
+ {
+ AssertMsg( 0, "Can't add listeners multiple times\n" );
+ return;
+ }
+ m_entityListeners.AddToTail( pListener );
+}
+
+void CGlobalEntityList::RemoveListenerEntity( IEntityListener *pListener )
+{
+ m_entityListeners.FindAndRemove( pListener );
+}
+
+void CGlobalEntityList::Clear( void )
+{
+ m_bClearingEntities = true;
+
+ // Add all remaining entities in the game to the delete list and call appropriate UpdateOnRemove
+ CBaseHandle hCur = FirstHandle();
+ while ( hCur != InvalidHandle() )
+ {
+ IServerNetworkable *ent = GetServerNetworkable( hCur );
+ if ( ent )
+ {
+ MDLCACHE_CRITICAL_SECTION();
+ // Force UpdateOnRemove to be called
+ UTIL_Remove( ent );
+ }
+ hCur = NextHandle( hCur );
+ }
+
+ CleanupDeleteList();
+ // free the memory
+ g_DeleteList.Purge();
+
+ CBaseEntity::m_nDebugPlayer = -1;
+ CBaseEntity::m_bInDebugSelect = false;
+ m_iHighestEnt = 0;
+ m_iNumEnts = 0;
+
+ m_bClearingEntities = false;
+}
+
+
+int CGlobalEntityList::NumberOfEntities( void )
+{
+ return m_iNumEnts;
+}
+
+int CGlobalEntityList::NumberOfEdicts( void )
+{
+ return m_iNumEdicts;
+}
+
+CBaseEntity *CGlobalEntityList::NextEnt( CBaseEntity *pCurrentEnt )
+{
+ if ( !pCurrentEnt )
+ {
+ const CEntInfo *pInfo = FirstEntInfo();
+ if ( !pInfo )
+ return NULL;
+
+ return (CBaseEntity *)pInfo->m_pEntity;
+ }
+
+ // Run through the list until we get a CBaseEntity.
+ const CEntInfo *pList = GetEntInfoPtr( pCurrentEnt->GetRefEHandle() );
+ if ( pList )
+ pList = NextEntInfo(pList);
+
+ while ( pList )
+ {
+#if 0
+ if ( pList->m_pEntity )
+ {
+ IServerUnknown *pUnk = static_cast<IServerUnknown*>(const_cast<IHandleEntity*>(pList->m_pEntity));
+ CBaseEntity *pRet = pUnk->GetBaseEntity();
+ if ( pRet )
+ return pRet;
+ }
+#else
+ return (CBaseEntity *)pList->m_pEntity;
+#endif
+ pList = pList->m_pNext;
+ }
+
+ return NULL;
+
+}
+
+
+void CGlobalEntityList::ReportEntityFlagsChanged( CBaseEntity *pEntity, unsigned int flagsOld, unsigned int flagsNow )
+{
+ if ( pEntity->IsMarkedForDeletion() )
+ return;
+ // UNDONE: Move this into IEntityListener instead?
+ unsigned int flagsChanged = flagsOld ^ flagsNow;
+ if ( flagsChanged & FL_AIMTARGET )
+ {
+ unsigned int flagsAdded = flagsNow & flagsChanged;
+ unsigned int flagsRemoved = flagsOld & flagsChanged;
+
+ if ( flagsAdded & FL_AIMTARGET )
+ {
+ g_AimManager.AddEntity( pEntity );
+ }
+ if ( flagsRemoved & FL_AIMTARGET )
+ {
+ g_AimManager.RemoveEntity( pEntity );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Used to confirm a pointer is a pointer to an entity, useful for
+// asserts.
+//-----------------------------------------------------------------------------
+bool CGlobalEntityList::IsEntityPtr( void *pTest )
+{
+ if ( pTest )
+ {
+ const CEntInfo *pInfo = FirstEntInfo();
+ for ( ;pInfo; pInfo = pInfo->m_pNext )
+ {
+ if ( pTest == (void *)pInfo->m_pEntity )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterates the entities with a given classname.
+// Input : pStartEntity - Last entity found, NULL to start a new iteration.
+// szName - Classname to search for.
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityByClassname( CBaseEntity *pStartEntity, const char *szName )
+{
+ const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
+
+ for ( ;pInfo; pInfo = pInfo->m_pNext )
+ {
+ CBaseEntity *pEntity = (CBaseEntity *)pInfo->m_pEntity;
+ if ( !pEntity )
+ {
+ DevWarning( "NULL entity in global entity list!\n" );
+ continue;
+ }
+
+ if ( pEntity->ClassMatches(szName) )
+ return pEntity;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds an entity given a procedural name.
+// Input : szName - The procedural name to search for, should start with '!'.
+// pSearchingEntity -
+// pActivator - The activator entity if this was called from an input
+// or Use handler.
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityProcedural( const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
+{
+ //
+ // Check for the name escape character.
+ //
+ if ( szName[0] == '!' )
+ {
+ const char *pName = szName + 1;
+
+ //
+ // It is a procedural name, look for the ones we understand.
+ //
+ if ( FStrEq( pName, "player" ) )
+ {
+ return (CBaseEntity *)UTIL_PlayerByIndex( 1 );
+ }
+ else if ( FStrEq( pName, "pvsplayer" ) )
+ {
+ if ( pSearchingEntity )
+ {
+ return CBaseEntity::Instance( UTIL_FindClientInPVS( pSearchingEntity->edict() ) );
+ }
+ else if ( pActivator )
+ {
+ // FIXME: error condition?
+ return CBaseEntity::Instance( UTIL_FindClientInPVS( pActivator->edict() ) );
+ }
+ else
+ {
+ // FIXME: error condition?
+ return (CBaseEntity *)UTIL_PlayerByIndex( 1 );
+ }
+
+ }
+ else if ( FStrEq( pName, "activator" ) )
+ {
+ return pActivator;
+ }
+ else if ( FStrEq( pName, "caller" ) )
+ {
+ return pCaller;
+ }
+ else if ( FStrEq( pName, "picker" ) )
+ {
+ return FindPickerEntity( UTIL_PlayerByIndex(1) );
+ }
+ else if ( FStrEq( pName, "self" ) )
+ {
+ return pSearchingEntity;
+ }
+ else
+ {
+ Warning( "Invalid entity search name %s\n", szName );
+ Assert(0);
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterates the entities with a given name.
+// Input : pStartEntity - Last entity found, NULL to start a new iteration.
+// szName - Name to search for.
+// pActivator - Activator entity if this was called from an input
+// handler or Use handler.
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityByName( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller, IEntityFindFilter *pFilter )
+{
+ if ( !szName || szName[0] == 0 )
+ return NULL;
+
+ if ( szName[0] == '!' )
+ {
+ //
+ // Avoid an infinite loop, only find one match per procedural search!
+ //
+ if (pStartEntity == NULL)
+ return FindEntityProcedural( szName, pSearchingEntity, pActivator, pCaller );
+
+ return NULL;
+ }
+
+ const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
+
+ for ( ;pInfo; pInfo = pInfo->m_pNext )
+ {
+ CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
+ if ( !ent )
+ {
+ DevWarning( "NULL entity in global entity list!\n" );
+ continue;
+ }
+
+ if ( !ent->m_iName )
+ continue;
+
+ if ( ent->NameMatches( szName ) )
+ {
+ if ( pFilter && !pFilter->ShouldFindEntity(ent) )
+ continue;
+
+ return ent;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pStartEntity -
+// szModelName -
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityByModel( CBaseEntity *pStartEntity, const char *szModelName )
+{
+ const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
+
+ for ( ;pInfo; pInfo = pInfo->m_pNext )
+ {
+ CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
+ if ( !ent )
+ {
+ DevWarning( "NULL entity in global entity list!\n" );
+ continue;
+ }
+
+ if ( !ent->edict() || !ent->GetModelName() )
+ continue;
+
+ if ( FStrEq( STRING(ent->GetModelName()), szModelName ) )
+ return ent;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterates the entities with a given target.
+// Input : pStartEntity -
+// szName -
+//-----------------------------------------------------------------------------
+// FIXME: obsolete, remove
+CBaseEntity *CGlobalEntityList::FindEntityByTarget( CBaseEntity *pStartEntity, const char *szName )
+{
+ const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
+
+ for ( ;pInfo; pInfo = pInfo->m_pNext )
+ {
+ CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
+ if ( !ent )
+ {
+ DevWarning( "NULL entity in global entity list!\n" );
+ continue;
+ }
+
+ if ( !ent->m_target )
+ continue;
+
+ if ( FStrEq( STRING(ent->m_target), szName ) )
+ return ent;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Used to iterate all the entities within a sphere.
+// Input : pStartEntity -
+// vecCenter -
+// flRadius -
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityInSphere( CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius )
+{
+ const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo();
+
+ for ( ;pInfo; pInfo = pInfo->m_pNext )
+ {
+ CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
+ if ( !ent )
+ {
+ DevWarning( "NULL entity in global entity list!\n" );
+ continue;
+ }
+
+ if ( !ent->edict() )
+ continue;
+
+ Vector vecRelativeCenter;
+ ent->CollisionProp()->WorldToCollisionSpace( vecCenter, &vecRelativeCenter );
+ if ( !IsBoxIntersectingSphere( ent->CollisionProp()->OBBMins(), ent->CollisionProp()->OBBMaxs(), vecRelativeCenter, flRadius ) )
+ continue;
+
+ return ent;
+ }
+
+ // nothing found
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the nearest entity by name within a radius
+// Input : szName - Entity name to search for.
+// vecSrc - Center of search radius.
+// flRadius - Search radius for classname search, 0 to search everywhere.
+// pSearchingEntity - The entity that is doing the search.
+// pActivator - The activator entity if this was called from an input
+// or Use handler, NULL otherwise.
+// Output : Returns a pointer to the found entity, NULL if none.
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityByNameNearest( const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
+{
+ CBaseEntity *pEntity = NULL;
+
+ //
+ // Check for matching class names within the search radius.
+ //
+ float flMaxDist2 = flRadius * flRadius;
+ if (flMaxDist2 == 0)
+ {
+ flMaxDist2 = MAX_TRACE_LENGTH * MAX_TRACE_LENGTH;
+ }
+
+ CBaseEntity *pSearch = NULL;
+ while ((pSearch = gEntList.FindEntityByName( pSearch, szName, pSearchingEntity, pActivator, pCaller )) != NULL)
+ {
+ if ( !pSearch->edict() )
+ continue;
+
+ float flDist2 = (pSearch->GetAbsOrigin() - vecSrc).LengthSqr();
+
+ if (flMaxDist2 > flDist2)
+ {
+ pEntity = pSearch;
+ flMaxDist2 = flDist2;
+ }
+ }
+
+ return pEntity;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the first entity by name within a radius
+// Input : pStartEntity - The entity to start from when doing the search.
+// szName - Entity name to search for.
+// vecSrc - Center of search radius.
+// flRadius - Search radius for classname search, 0 to search everywhere.
+// pSearchingEntity - The entity that is doing the search.
+// pActivator - The activator entity if this was called from an input
+// or Use handler, NULL otherwise.
+// Output : Returns a pointer to the found entity, NULL if none.
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityByNameWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
+{
+ //
+ // Check for matching class names within the search radius.
+ //
+ CBaseEntity *pEntity = pStartEntity;
+ float flMaxDist2 = flRadius * flRadius;
+ if (flMaxDist2 == 0)
+ {
+ return gEntList.FindEntityByName( pEntity, szName, pSearchingEntity, pActivator, pCaller );
+ }
+
+ while ((pEntity = gEntList.FindEntityByName( pEntity, szName, pSearchingEntity, pActivator, pCaller )) != NULL)
+ {
+ if ( !pEntity->edict() )
+ continue;
+
+ float flDist2 = (pEntity->GetAbsOrigin() - vecSrc).LengthSqr();
+
+ if (flMaxDist2 > flDist2)
+ {
+ return pEntity;
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the nearest entity by class name withing given search radius.
+// Input : szName - Entity name to search for. Treated as a target name first,
+// then as an entity class name, ie "info_target".
+// vecSrc - Center of search radius.
+// flRadius - Search radius for classname search, 0 to search everywhere.
+// Output : Returns a pointer to the found entity, NULL if none.
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityByClassnameNearest( const char *szName, const Vector &vecSrc, float flRadius )
+{
+ CBaseEntity *pEntity = NULL;
+
+ //
+ // Check for matching class names within the search radius.
+ //
+ float flMaxDist2 = flRadius * flRadius;
+ if (flMaxDist2 == 0)
+ {
+ flMaxDist2 = MAX_TRACE_LENGTH * MAX_TRACE_LENGTH;
+ }
+
+ CBaseEntity *pSearch = NULL;
+ while ((pSearch = gEntList.FindEntityByClassname( pSearch, szName )) != NULL)
+ {
+ if ( !pSearch->edict() )
+ continue;
+
+ float flDist2 = (pSearch->GetAbsOrigin() - vecSrc).LengthSqr();
+
+ if (flMaxDist2 > flDist2)
+ {
+ pEntity = pSearch;
+ flMaxDist2 = flDist2;
+ }
+ }
+
+ return pEntity;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the first entity within radius distance by class name.
+// Input : pStartEntity - The entity to start from when doing the search.
+// szName - Entity class name, ie "info_target".
+// vecSrc - Center of search radius.
+// flRadius - Search radius for classname search, 0 to search everywhere.
+// Output : Returns a pointer to the found entity, NULL if none.
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityByClassnameWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecSrc, float flRadius )
+{
+ //
+ // Check for matching class names within the search radius.
+ //
+ CBaseEntity *pEntity = pStartEntity;
+ float flMaxDist2 = flRadius * flRadius;
+ if (flMaxDist2 == 0)
+ {
+ return gEntList.FindEntityByClassname( pEntity, szName );
+ }
+
+ while ((pEntity = gEntList.FindEntityByClassname( pEntity, szName )) != NULL)
+ {
+ if ( !pEntity->edict() )
+ continue;
+
+ float flDist2 = (pEntity->GetAbsOrigin() - vecSrc).LengthSqr();
+
+ if (flMaxDist2 > flDist2)
+ {
+ return pEntity;
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the first entity within an extent by class name.
+// Input : pStartEntity - The entity to start from when doing the search.
+// szName - Entity class name, ie "info_target".
+// vecMins - Search mins.
+// vecMaxs - Search maxs.
+// Output : Returns a pointer to the found entity, NULL if none.
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityByClassnameWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecMins, const Vector &vecMaxs )
+{
+ //
+ // Check for matching class names within the search radius.
+ //
+ CBaseEntity *pEntity = pStartEntity;
+
+ while ((pEntity = gEntList.FindEntityByClassname( pEntity, szName )) != NULL)
+ {
+ if ( !pEntity->edict() && !pEntity->IsEFlagSet( EFL_SERVER_ONLY ) )
+ continue;
+
+ // check if the aabb intersects the search aabb.
+ Vector entMins, entMaxs;
+ pEntity->CollisionProp()->WorldSpaceAABB( &entMins, &entMaxs );
+ if ( IsBoxIntersectingBox( vecMins, vecMaxs, entMins, entMaxs ) )
+ {
+ return pEntity;
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds an entity by target name or class name.
+// Input : pStartEntity - The entity to start from when doing the search.
+// szName - Entity name to search for. Treated as a target name first,
+// then as an entity class name, ie "info_target".
+// vecSrc - Center of search radius.
+// flRadius - Search radius for classname search, 0 to search everywhere.
+// pSearchingEntity - The entity that is doing the search.
+// pActivator - The activator entity if this was called from an input
+// or Use handler, NULL otherwise.
+// Output : Returns a pointer to the found entity, NULL if none.
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityGeneric( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
+{
+ CBaseEntity *pEntity = NULL;
+
+ pEntity = gEntList.FindEntityByName( pStartEntity, szName, pSearchingEntity, pActivator, pCaller );
+ if (!pEntity)
+ {
+ pEntity = gEntList.FindEntityByClassname( pStartEntity, szName );
+ }
+
+ return pEntity;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the first entity by target name or class name within a radius
+// Input : pStartEntity - The entity to start from when doing the search.
+// szName - Entity name to search for. Treated as a target name first,
+// then as an entity class name, ie "info_target".
+// vecSrc - Center of search radius.
+// flRadius - Search radius for classname search, 0 to search everywhere.
+// pSearchingEntity - The entity that is doing the search.
+// pActivator - The activator entity if this was called from an input
+// or Use handler, NULL otherwise.
+// Output : Returns a pointer to the found entity, NULL if none.
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityGenericWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
+{
+ CBaseEntity *pEntity = NULL;
+
+ pEntity = gEntList.FindEntityByNameWithin( pStartEntity, szName, vecSrc, flRadius, pSearchingEntity, pActivator, pCaller );
+ if (!pEntity)
+ {
+ pEntity = gEntList.FindEntityByClassnameWithin( pStartEntity, szName, vecSrc, flRadius );
+ }
+
+ return pEntity;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the nearest entity by target name or class name within a radius.
+// Input : pStartEntity - The entity to start from when doing the search.
+// szName - Entity name to search for. Treated as a target name first,
+// then as an entity class name, ie "info_target".
+// vecSrc - Center of search radius.
+// flRadius - Search radius for classname search, 0 to search everywhere.
+// pSearchingEntity - The entity that is doing the search.
+// pActivator - The activator entity if this was called from an input
+// or Use handler, NULL otherwise.
+// Output : Returns a pointer to the found entity, NULL if none.
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityGenericNearest( const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
+{
+ CBaseEntity *pEntity = NULL;
+
+ pEntity = gEntList.FindEntityByNameNearest( szName, vecSrc, flRadius, pSearchingEntity, pActivator, pCaller );
+ if (!pEntity)
+ {
+ pEntity = gEntList.FindEntityByClassnameNearest( szName, vecSrc, flRadius );
+ }
+
+ return pEntity;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Find the nearest entity along the facing direction from the given origin
+// within the angular threshold (ignores worldspawn) with the
+// given classname.
+// Input : origin -
+// facing -
+// threshold -
+// classname -
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityClassNearestFacing( const Vector &origin, const Vector &facing, float threshold, char *classname)
+{
+ float bestDot = threshold;
+ CBaseEntity *best_ent = NULL;
+
+ const CEntInfo *pInfo = FirstEntInfo();
+
+ for ( ;pInfo; pInfo = pInfo->m_pNext )
+ {
+ CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
+ if ( !ent )
+ {
+ DevWarning( "NULL entity in global entity list!\n" );
+ continue;
+ }
+
+ // FIXME: why is this skipping pointsize entities?
+ if (ent->IsPointSized() )
+ continue;
+
+ // Make vector to entity
+ Vector to_ent = (ent->GetAbsOrigin() - origin);
+
+ VectorNormalize( to_ent );
+ float dot = DotProduct (facing , to_ent );
+ if (dot > bestDot)
+ {
+ if (FClassnameIs(ent,classname))
+ {
+ // Ignore if worldspawn
+ if (!FClassnameIs( ent, "worldspawn" ) && !FClassnameIs( ent, "soundent"))
+ {
+ bestDot = dot;
+ best_ent = ent;
+ }
+ }
+ }
+ }
+ return best_ent;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Find the nearest entity along the facing direction from the given origin
+// within the angular threshold (ignores worldspawn)
+// Input : origin -
+// facing -
+// threshold -
+//-----------------------------------------------------------------------------
+CBaseEntity *CGlobalEntityList::FindEntityNearestFacing( const Vector &origin, const Vector &facing, float threshold)
+{
+ float bestDot = threshold;
+ CBaseEntity *best_ent = NULL;
+
+ const CEntInfo *pInfo = FirstEntInfo();
+
+ for ( ;pInfo; pInfo = pInfo->m_pNext )
+ {
+ CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
+ if ( !ent )
+ {
+ DevWarning( "NULL entity in global entity list!\n" );
+ continue;
+ }
+
+ // Ignore logical entities
+ if (!ent->edict())
+ continue;
+
+ // Make vector to entity
+ Vector to_ent = ent->WorldSpaceCenter() - origin;
+ VectorNormalize(to_ent);
+
+ float dot = DotProduct( facing, to_ent );
+ if (dot <= bestDot)
+ continue;
+
+ // Ignore if worldspawn
+ if (!FStrEq( STRING(ent->m_iClassname), "worldspawn") && !FStrEq( STRING(ent->m_iClassname), "soundent"))
+ {
+ bestDot = dot;
+ best_ent = ent;
+ }
+ }
+ return best_ent;
+}
+
+
+void CGlobalEntityList::OnAddEntity( IHandleEntity *pEnt, CBaseHandle handle )
+{
+ int i = handle.GetEntryIndex();
+
+ // record current list details
+ m_iNumEnts++;
+ if ( i > m_iHighestEnt )
+ m_iHighestEnt = i;
+
+ // If it's a CBaseEntity, notify the listeners.
+ CBaseEntity *pBaseEnt = static_cast<IServerUnknown*>(pEnt)->GetBaseEntity();
+ if ( pBaseEnt->edict() )
+ m_iNumEdicts++;
+
+ // NOTE: Must be a CBaseEntity on server
+ Assert( pBaseEnt );
+ //DevMsg(2,"Created %s\n", pBaseEnt->GetClassname() );
+ for ( i = m_entityListeners.Count()-1; i >= 0; i-- )
+ {
+ m_entityListeners[i]->OnEntityCreated( pBaseEnt );
+ }
+}
+
+
+void CGlobalEntityList::OnRemoveEntity( IHandleEntity *pEnt, CBaseHandle handle )
+{
+#ifdef DEBUG
+ if ( !g_fInCleanupDelete )
+ {
+ int i;
+ for ( i = 0; i < g_DeleteList.Count(); i++ )
+ {
+ if ( g_DeleteList[i]->GetEntityHandle() == pEnt )
+ {
+ g_DeleteList.FastRemove( i );
+ Msg( "ERROR: Entity being destroyed but previously threaded on g_DeleteList\n" );
+ break;
+ }
+ }
+ }
+#endif
+
+ CBaseEntity *pBaseEnt = static_cast<IServerUnknown*>(pEnt)->GetBaseEntity();
+ if ( pBaseEnt->edict() )
+ m_iNumEdicts--;
+
+ m_iNumEnts--;
+}
+
+void CGlobalEntityList::NotifyCreateEntity( CBaseEntity *pEnt )
+{
+ if ( !pEnt )
+ return;
+
+ //DevMsg(2,"Deleted %s\n", pBaseEnt->GetClassname() );
+ for ( int i = m_entityListeners.Count()-1; i >= 0; i-- )
+ {
+ m_entityListeners[i]->OnEntityCreated( pEnt );
+ }
+}
+
+void CGlobalEntityList::NotifySpawn( CBaseEntity *pEnt )
+{
+ if ( !pEnt )
+ return;
+
+ //DevMsg(2,"Deleted %s\n", pBaseEnt->GetClassname() );
+ for ( int i = m_entityListeners.Count()-1; i >= 0; i-- )
+ {
+ m_entityListeners[i]->OnEntitySpawned( pEnt );
+ }
+}
+
+// NOTE: This doesn't happen in OnRemoveEntity() specifically because
+// listeners may want to reference the object as it's being deleted
+// OnRemoveEntity isn't called until the destructor and all data is invalid.
+void CGlobalEntityList::NotifyRemoveEntity( CBaseHandle hEnt )
+{
+ CBaseEntity *pBaseEnt = GetBaseEntity( hEnt );
+ if ( !pBaseEnt )
+ return;
+
+ //DevMsg(2,"Deleted %s\n", pBaseEnt->GetClassname() );
+ for ( int i = m_entityListeners.Count()-1; i >= 0; i-- )
+ {
+ m_entityListeners[i]->OnEntityDeleted( pBaseEnt );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// NOTIFY LIST
+//
+// Allows entities to get events fired when another entity changes
+//-----------------------------------------------------------------------------
+struct entitynotify_t
+{
+ CBaseEntity *pNotify;
+ CBaseEntity *pWatched;
+};
+class CNotifyList : public INotify, public IEntityListener
+{
+public:
+ // INotify
+ void AddEntity( CBaseEntity *pNotify, CBaseEntity *pWatched );
+ void RemoveEntity( CBaseEntity *pNotify, CBaseEntity *pWatched );
+ void ReportNamedEvent( CBaseEntity *pEntity, const char *pEventName );
+ void ClearEntity( CBaseEntity *pNotify );
+ void ReportSystemEvent( CBaseEntity *pEntity, notify_system_event_t eventType, const notify_system_event_params_t &params );
+
+ // IEntityListener
+ virtual void OnEntityCreated( CBaseEntity *pEntity );
+ virtual void OnEntityDeleted( CBaseEntity *pEntity );
+
+ // Called from CEntityListSystem
+ void LevelInitPreEntity();
+ void LevelShutdownPreEntity();
+
+private:
+ CUtlVector<entitynotify_t> m_notifyList;
+};
+
+void CNotifyList::AddEntity( CBaseEntity *pNotify, CBaseEntity *pWatched )
+{
+ // OPTIMIZE: Also flag pNotify for faster "RemoveAllNotify" ?
+ pWatched->AddEFlags( EFL_NOTIFY );
+ int index = m_notifyList.AddToTail();
+ entitynotify_t &notify = m_notifyList[index];
+ notify.pNotify = pNotify;
+ notify.pWatched = pWatched;
+}
+
+// Remove noitfication for an entity
+void CNotifyList::RemoveEntity( CBaseEntity *pNotify, CBaseEntity *pWatched )
+{
+ for ( int i = m_notifyList.Count(); --i >= 0; )
+ {
+ if ( m_notifyList[i].pNotify == pNotify && m_notifyList[i].pWatched == pWatched)
+ {
+ m_notifyList.FastRemove(i);
+ }
+ }
+}
+
+
+void CNotifyList::ReportNamedEvent( CBaseEntity *pEntity, const char *pInputName )
+{
+ variant_t emptyVariant;
+
+ if ( !pEntity->IsEFlagSet(EFL_NOTIFY) )
+ return;
+
+ for ( int i = 0; i < m_notifyList.Count(); i++ )
+ {
+ if ( m_notifyList[i].pWatched == pEntity )
+ {
+ m_notifyList[i].pNotify->AcceptInput( pInputName, pEntity, pEntity, emptyVariant, 0 );
+ }
+ }
+}
+
+void CNotifyList::LevelInitPreEntity()
+{
+ gEntList.AddListenerEntity( this );
+}
+
+void CNotifyList::LevelShutdownPreEntity( void )
+{
+ gEntList.RemoveListenerEntity( this );
+ m_notifyList.Purge();
+}
+
+void CNotifyList::OnEntityCreated( CBaseEntity *pEntity )
+{
+}
+
+void CNotifyList::OnEntityDeleted( CBaseEntity *pEntity )
+{
+ ReportDestroyEvent( pEntity );
+ ClearEntity( pEntity );
+}
+
+
+// UNDONE: Slow linear search?
+void CNotifyList::ClearEntity( CBaseEntity *pNotify )
+{
+ for ( int i = m_notifyList.Count(); --i >= 0; )
+ {
+ if ( m_notifyList[i].pNotify == pNotify || m_notifyList[i].pWatched == pNotify)
+ {
+ m_notifyList.FastRemove(i);
+ }
+ }
+}
+
+void CNotifyList::ReportSystemEvent( CBaseEntity *pEntity, notify_system_event_t eventType, const notify_system_event_params_t &params )
+{
+ if ( !pEntity->IsEFlagSet(EFL_NOTIFY) )
+ return;
+
+ for ( int i = 0; i < m_notifyList.Count(); i++ )
+ {
+ if ( m_notifyList[i].pWatched == pEntity )
+ {
+ m_notifyList[i].pNotify->NotifySystemEvent( pEntity, eventType, params );
+ }
+ }
+}
+
+static CNotifyList g_NotifyList;
+INotify *g_pNotify = &g_NotifyList;
+
+class CEntityTouchManager : public IEntityListener
+{
+public:
+ // called by CEntityListSystem
+ void LevelInitPreEntity()
+ {
+ gEntList.AddListenerEntity( this );
+ Clear();
+ }
+ void LevelShutdownPostEntity()
+ {
+ gEntList.RemoveListenerEntity( this );
+ Clear();
+ }
+ void FrameUpdatePostEntityThink();
+
+ void Clear()
+ {
+ m_updateList.Purge();
+ }
+
+ // IEntityListener
+ virtual void OnEntityCreated( CBaseEntity *pEntity ) {}
+ virtual void OnEntityDeleted( CBaseEntity *pEntity )
+ {
+ if ( !pEntity->GetCheckUntouch() )
+ return;
+ int index = m_updateList.Find( pEntity );
+ if ( m_updateList.IsValidIndex(index) )
+ {
+ m_updateList.FastRemove( index );
+ }
+ }
+ void AddEntity( CBaseEntity *pEntity )
+ {
+ if ( pEntity->IsMarkedForDeletion() )
+ return;
+ m_updateList.AddToTail( pEntity );
+ }
+
+private:
+ CUtlVector<CBaseEntity *> m_updateList;
+};
+
+static CEntityTouchManager g_TouchManager;
+
+void EntityTouch_Add( CBaseEntity *pEntity )
+{
+ g_TouchManager.AddEntity( pEntity );
+}
+
+
+void CEntityTouchManager::FrameUpdatePostEntityThink()
+{
+ VPROF( "CEntityTouchManager::FrameUpdatePostEntityThink" );
+ // Loop through all entities again, checking their untouch if flagged to do so
+
+ int count = m_updateList.Count();
+ if ( count )
+ {
+ // copy off the list
+ CBaseEntity **ents = (CBaseEntity **)stackalloc( sizeof(CBaseEntity *) * count );
+ memcpy( ents, m_updateList.Base(), sizeof(CBaseEntity *) * count );
+ // clear it
+ m_updateList.RemoveAll();
+
+ // now update those ents
+ for ( int i = 0; i < count; i++ )
+ {
+ //Assert( ents[i]->GetCheckUntouch() );
+ if ( ents[i]->GetCheckUntouch() )
+ {
+ ents[i]->PhysicsCheckForEntityUntouch();
+ }
+ }
+ stackfree( ents );
+ }
+}
+
+class CRespawnEntitiesFilter : public IMapEntityFilter
+{
+public:
+ virtual bool ShouldCreateEntity( const char *pClassname )
+ {
+ // Create everything but the world
+ return Q_stricmp( pClassname, "worldspawn" ) != 0;
+ }
+
+ virtual CBaseEntity* CreateNextEntity( const char *pClassname )
+ {
+ return CreateEntityByName( pClassname );
+ }
+};
+
+// One hook to rule them all...
+// Since most of the little list managers in here only need one or two of the game
+// system callbacks, this hook is a game system that passes them the appropriate callbacks
+class CEntityListSystem : public CAutoGameSystemPerFrame
+{
+public:
+ CEntityListSystem( char const *name ) : CAutoGameSystemPerFrame( name )
+ {
+ m_bRespawnAllEntities = false;
+ }
+ void LevelInitPreEntity()
+ {
+ g_NotifyList.LevelInitPreEntity();
+ g_TouchManager.LevelInitPreEntity();
+ g_AimManager.LevelInitPreEntity();
+ g_SimThinkManager.LevelInitPreEntity();
+#ifdef HL2_DLL
+ OverrideMoveCache_LevelInitPreEntity();
+#endif // HL2_DLL
+ }
+ void LevelShutdownPreEntity()
+ {
+ g_NotifyList.LevelShutdownPreEntity();
+ }
+ void LevelShutdownPostEntity()
+ {
+ g_TouchManager.LevelShutdownPostEntity();
+ g_AimManager.LevelShutdownPostEntity();
+ g_SimThinkManager.LevelShutdownPostEntity();
+#ifdef HL2_DLL
+ OverrideMoveCache_LevelShutdownPostEntity();
+#endif // HL2_DLL
+ CBaseEntityClassList *pClassList = s_pClassLists;
+ while ( pClassList )
+ {
+ pClassList->LevelShutdownPostEntity();
+ pClassList = pClassList->m_pNextClassList;
+ }
+ }
+
+ void FrameUpdatePostEntityThink()
+ {
+ g_TouchManager.FrameUpdatePostEntityThink();
+
+ if ( m_bRespawnAllEntities )
+ {
+ m_bRespawnAllEntities = false;
+
+ // Don't change globalstate owing to deletion here
+ GlobalEntity_EnableStateUpdates( false );
+
+ // Remove all entities
+ int nPlayerIndex = -1;
+ CBaseEntity *pEnt = gEntList.FirstEnt();
+ while ( pEnt )
+ {
+ CBaseEntity *pNextEnt = gEntList.NextEnt( pEnt );
+ if ( pEnt->IsPlayer() )
+ {
+ nPlayerIndex = pEnt->entindex();
+ }
+ if ( !pEnt->IsEFlagSet( EFL_KEEP_ON_RECREATE_ENTITIES ) )
+ {
+ UTIL_Remove( pEnt );
+ }
+ pEnt = pNextEnt;
+ }
+
+ gEntList.CleanupDeleteList();
+
+ GlobalEntity_EnableStateUpdates( true );
+
+ // Allows us to immediately re-use the edict indices we just freed to avoid edict overflow
+ engine->AllowImmediateEdictReuse();
+
+ // Reset node counter used during load
+ CNodeEnt::m_nNodeCount = 0;
+
+ CRespawnEntitiesFilter filter;
+ MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true );
+
+ // Allocate a CBasePlayer for pev, and call spawn
+ if ( nPlayerIndex >= 0 )
+ {
+ edict_t *pEdict = engine->PEntityOfEntIndex( nPlayerIndex );
+ ClientPutInServer( pEdict, "unnamed" );
+ ClientActive( pEdict, false );
+
+ CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
+ SceneManager_ClientActive( pPlayer );
+ }
+ }
+ }
+
+ bool m_bRespawnAllEntities;
+};
+
+static CEntityListSystem g_EntityListSystem( "CEntityListSystem" );
+
+//-----------------------------------------------------------------------------
+// Respawns all entities in the level
+//-----------------------------------------------------------------------------
+void RespawnEntities()
+{
+ g_EntityListSystem.m_bRespawnAllEntities = true;
+}
+
+static ConCommand restart_entities( "respawn_entities", RespawnEntities, "Respawn all the entities in the map.", FCVAR_CHEAT | FCVAR_SPONLY );
+
+class CSortedEntityList
+{
+public:
+ CSortedEntityList() : m_sortedList(), m_emptyCount(0) {}
+
+ typedef CBaseEntity *ENTITYPTR;
+ class CEntityReportLess
+ {
+ public:
+ bool Less( const ENTITYPTR &src1, const ENTITYPTR &src2, void *pCtx )
+ {
+ if ( stricmp( src1->GetClassname(), src2->GetClassname() ) < 0 )
+ return true;
+
+ return false;
+ }
+ };
+
+ void AddEntityToList( CBaseEntity *pEntity )
+ {
+ if ( !pEntity )
+ {
+ m_emptyCount++;
+ }
+ else
+ {
+ m_sortedList.Insert( pEntity );
+ }
+ }
+ void ReportEntityList()
+ {
+ const char *pLastClass = "";
+ int count = 0;
+ int edicts = 0;
+ for ( int i = 0; i < m_sortedList.Count(); i++ )
+ {
+ CBaseEntity *pEntity = m_sortedList[i];
+ if ( !pEntity )
+ continue;
+
+ if ( pEntity->edict() )
+ edicts++;
+
+ const char *pClassname = pEntity->GetClassname();
+ if ( !FStrEq( pClassname, pLastClass ) )
+ {
+ if ( count )
+ {
+ Msg("Class: %s (%d)\n", pLastClass, count );
+ }
+
+ pLastClass = pClassname;
+ count = 1;
+ }
+ else
+ count++;
+ }
+ if ( pLastClass[0] != 0 && count )
+ {
+ Msg("Class: %s (%d)\n", pLastClass, count );
+ }
+ if ( m_sortedList.Count() )
+ {
+ Msg("Total %d entities (%d empty, %d edicts)\n", m_sortedList.Count(), m_emptyCount, edicts );
+ }
+ }
+private:
+ CUtlSortVector< CBaseEntity *, CEntityReportLess > m_sortedList;
+ int m_emptyCount;
+};
+
+
+
+CON_COMMAND(report_entities, "Lists all entities")
+{
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ CSortedEntityList list;
+ CBaseEntity *pEntity = gEntList.FirstEnt();
+ while ( pEntity )
+ {
+ list.AddEntityToList( pEntity );
+ pEntity = gEntList.NextEnt( pEntity );
+ }
+ list.ReportEntityList();
+}
+
+
+CON_COMMAND(report_touchlinks, "Lists all touchlinks")
+{
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ CSortedEntityList list;
+ CBaseEntity *pEntity = gEntList.FirstEnt();
+ const char *pClassname = NULL;
+ if ( args.ArgC() > 1 )
+ {
+ pClassname = args.Arg(1);
+ }
+ while ( pEntity )
+ {
+ if ( !pClassname || FClassnameIs(pEntity, pClassname) )
+ {
+ touchlink_t *root = ( touchlink_t * )pEntity->GetDataObject( TOUCHLINK );
+ if ( root )
+ {
+ touchlink_t *link = root->nextLink;
+ while ( link != root )
+ {
+ list.AddEntityToList( link->entityTouched );
+ link = link->nextLink;
+ }
+ }
+ }
+ pEntity = gEntList.NextEnt( pEntity );
+ }
+ list.ReportEntityList();
+}
+
+CON_COMMAND(report_simthinklist, "Lists all simulating/thinking entities")
+{
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ CBaseEntity *pTmp[NUM_ENT_ENTRIES];
+ int count = SimThink_ListCopy( pTmp, ARRAYSIZE(pTmp) );
+
+ CSortedEntityList list;
+ for ( int i = 0; i < count; i++ )
+ {
+ if ( !pTmp[i] )
+ continue;
+
+ list.AddEntityToList( pTmp[i] );
+ }
+ list.ReportEntityList();
+}
+