aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/shared/physics_main_shared.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 /mp/src/game/shared/physics_main_shared.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 'mp/src/game/shared/physics_main_shared.cpp')
-rw-r--r--mp/src/game/shared/physics_main_shared.cpp4476
1 files changed, 2238 insertions, 2238 deletions
diff --git a/mp/src/game/shared/physics_main_shared.cpp b/mp/src/game/shared/physics_main_shared.cpp
index 99cfc311..c3719d77 100644
--- a/mp/src/game/shared/physics_main_shared.cpp
+++ b/mp/src/game/shared/physics_main_shared.cpp
@@ -1,2238 +1,2238 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//===========================================================================//
-
-#include "cbase.h"
-#include "engine/IEngineSound.h"
-#include "mempool.h"
-#include "movevars_shared.h"
-#include "utlrbtree.h"
-#include "tier0/vprof.h"
-#include "entitydatainstantiator.h"
-#include "positionwatcher.h"
-#include "movetype_push.h"
-#include "vphysicsupdateai.h"
-#include "igamesystem.h"
-#include "utlmultilist.h"
-#include "tier1/callqueue.h"
-
-#ifdef PORTAL
- #include "portal_util_shared.h"
-#endif
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-// memory pool for storing links between entities
-static CUtlMemoryPool g_EdictTouchLinks( sizeof(touchlink_t), MAX_EDICTS, CUtlMemoryPool::GROW_NONE, "g_EdictTouchLinks");
-static CUtlMemoryPool g_EntityGroundLinks( sizeof( groundlink_t ), MAX_EDICTS, CUtlMemoryPool::GROW_NONE, "g_EntityGroundLinks");
-
-struct watcher_t
-{
- EHANDLE hWatcher;
- IWatcherCallback *pWatcherCallback;
-};
-
-static CUtlMultiList<watcher_t, unsigned short> g_WatcherList;
-class CWatcherList
-{
-public:
- //CWatcherList(); NOTE: Dataobj doesn't support constructors - it zeros the memory
- ~CWatcherList(); // frees the positionwatcher_t's to the pool
- void Init();
-
- void NotifyPositionChanged( CBaseEntity *pEntity );
- void NotifyVPhysicsStateChanged( IPhysicsObject *pPhysics, CBaseEntity *pEntity, bool bAwake );
-
- void AddToList( CBaseEntity *pWatcher );
- void RemoveWatcher( CBaseEntity *pWatcher );
-
-private:
- int GetCallbackObjects( IWatcherCallback **pList, int listMax );
-
- unsigned short Find( CBaseEntity *pEntity );
- unsigned short m_list;
-};
-
-int linksallocated = 0;
-int groundlinksallocated = 0;
-
-// Prints warnings if any entity think functions take longer than this many milliseconds
-#ifdef _DEBUG
-#define DEF_THINK_LIMIT "20"
-#else
-#define DEF_THINK_LIMIT "10"
-#endif
-
-ConVar think_limit( "think_limit", DEF_THINK_LIMIT, FCVAR_REPLICATED, "Maximum think time in milliseconds, warning is printed if this is exceeded." );
-#ifndef CLIENT_DLL
-ConVar debug_touchlinks( "debug_touchlinks", "0", 0, "Spew touch link activity" );
-#define DebugTouchlinks() debug_touchlinks.GetBool()
-#else
-#define DebugTouchlinks() false
-#endif
-
-
-
-//-----------------------------------------------------------------------------
-// Portal-specific hack designed to eliminate re-entrancy in touch functions
-//-----------------------------------------------------------------------------
-class CPortalTouchScope
-{
-public:
- CPortalTouchScope();
- ~CPortalTouchScope();
-
-public:
- static int m_nDepth;
- static CCallQueue m_CallQueue;
-};
-
-int CPortalTouchScope::m_nDepth = 0;
-CCallQueue CPortalTouchScope::m_CallQueue;
-
-CCallQueue *GetPortalCallQueue()
-{
- return ( CPortalTouchScope::m_nDepth > 0 ) ? &CPortalTouchScope::m_CallQueue : NULL;
-}
-
-CPortalTouchScope::CPortalTouchScope()
-{
- ++m_nDepth;
-}
-
-CPortalTouchScope::~CPortalTouchScope()
-{
- Assert( m_nDepth >= 1 );
- if ( --m_nDepth == 0 )
- {
- m_CallQueue.CallQueued();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: System for hanging objects off of CBaseEntity, etc.
-// Externalized data objects ( see sharreddefs.h for enum )
-//-----------------------------------------------------------------------------
-class CDataObjectAccessSystem : public CAutoGameSystem
-{
-public:
-
- enum
- {
- MAX_ACCESSORS = 32,
- };
-
- CDataObjectAccessSystem()
- {
- // Cast to int to make it clear that we know we are comparing different enum types.
- COMPILE_TIME_ASSERT( (int)NUM_DATAOBJECT_TYPES <= (int)MAX_ACCESSORS );
-
- Q_memset( m_Accessors, 0, sizeof( m_Accessors ) );
- }
-
- virtual bool Init()
- {
- AddDataAccessor( TOUCHLINK, new CEntityDataInstantiator< touchlink_t > );
- AddDataAccessor( GROUNDLINK, new CEntityDataInstantiator< groundlink_t > );
- AddDataAccessor( STEPSIMULATION, new CEntityDataInstantiator< StepSimulationData > );
- AddDataAccessor( MODELSCALE, new CEntityDataInstantiator< ModelScale > );
- AddDataAccessor( POSITIONWATCHER, new CEntityDataInstantiator< CWatcherList > );
- AddDataAccessor( PHYSICSPUSHLIST, new CEntityDataInstantiator< physicspushlist_t > );
- AddDataAccessor( VPHYSICSUPDATEAI, new CEntityDataInstantiator< vphysicsupdateai_t > );
- AddDataAccessor( VPHYSICSWATCHER, new CEntityDataInstantiator< CWatcherList > );
-
- return true;
- }
-
- virtual void Shutdown()
- {
- for ( int i = 0; i < MAX_ACCESSORS; i++ )
- {
- delete m_Accessors[ i ];
- m_Accessors[ i ] = 0;
- }
- }
-
- void *GetDataObject( int type, const CBaseEntity *instance )
- {
- if ( !IsValidType( type ) )
- {
- Assert( !"Bogus type" );
- return NULL;
- }
- return m_Accessors[ type ]->GetDataObject( instance );
- }
-
- void *CreateDataObject( int type, CBaseEntity *instance )
- {
- if ( !IsValidType( type ) )
- {
- Assert( !"Bogus type" );
- return NULL;
- }
-
- return m_Accessors[ type ]->CreateDataObject( instance );
- }
-
- void DestroyDataObject( int type, CBaseEntity *instance )
- {
- if ( !IsValidType( type ) )
- {
- Assert( !"Bogus type" );
- return;
- }
-
- m_Accessors[ type ]->DestroyDataObject( instance );
- }
-
-private:
-
- bool IsValidType( int type ) const
- {
- if ( type < 0 || type >= MAX_ACCESSORS )
- return false;
-
- if ( m_Accessors[ type ] == NULL )
- return false;
- return true;
- }
-
- void AddDataAccessor( int type, IEntityDataInstantiator *instantiator )
- {
- if ( type < 0 || type >= MAX_ACCESSORS )
- {
- Assert( !"AddDataAccessor with out of range type!!!\n" );
- return;
- }
-
- Assert( instantiator );
-
- if ( m_Accessors[ type ] != NULL )
- {
- Assert( !"AddDataAccessor, duplicate adds!!!\n" );
- return;
- }
-
- m_Accessors[ type ] = instantiator;
- }
-
- IEntityDataInstantiator *m_Accessors[ MAX_ACCESSORS ];
-};
-
-static CDataObjectAccessSystem g_DataObjectAccessSystem;
-
-bool CBaseEntity::HasDataObjectType( int type ) const
-{
- Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
- return ( m_fDataObjectTypes & (1<<type) ) ? true : false;
-}
-
-void CBaseEntity::AddDataObjectType( int type )
-{
- Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
- m_fDataObjectTypes |= (1<<type);
-}
-
-void CBaseEntity::RemoveDataObjectType( int type )
-{
- Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
- m_fDataObjectTypes &= ~(1<<type);
-}
-
-void *CBaseEntity::GetDataObject( int type )
-{
- Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
- if ( !HasDataObjectType( type ) )
- return NULL;
- return g_DataObjectAccessSystem.GetDataObject( type, this );
-}
-
-void *CBaseEntity::CreateDataObject( int type )
-{
- Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
- AddDataObjectType( type );
- return g_DataObjectAccessSystem.CreateDataObject( type, this );
-}
-
-void CBaseEntity::DestroyDataObject( int type )
-{
- Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
- if ( !HasDataObjectType( type ) )
- return;
- g_DataObjectAccessSystem.DestroyDataObject( type, this );
- RemoveDataObjectType( type );
-}
-
-void CWatcherList::Init()
-{
- m_list = g_WatcherList.CreateList();
-}
-
-CWatcherList::~CWatcherList()
-{
- g_WatcherList.DestroyList( m_list );
-}
-
-int CWatcherList::GetCallbackObjects( IWatcherCallback **pList, int listMax )
-{
- int index = 0;
- unsigned short next = g_WatcherList.InvalidIndex();
- for ( unsigned short node = g_WatcherList.Head( m_list ); node != g_WatcherList.InvalidIndex(); node = next )
- {
- next = g_WatcherList.Next( node );
- watcher_t *pNode = &g_WatcherList.Element(node);
- if ( pNode->hWatcher.Get() )
- {
- pList[index] = pNode->pWatcherCallback;
- index++;
- if ( index >= listMax )
- {
- Assert(0);
- return index;
- }
- }
- else
- {
- g_WatcherList.Remove( m_list, node );
- }
- }
- return index;
-}
-
-void CWatcherList::NotifyPositionChanged( CBaseEntity *pEntity )
-{
- IWatcherCallback *pCallbacks[1024]; // HACKHACK: Assumes this list is big enough
- int count = GetCallbackObjects( pCallbacks, ARRAYSIZE(pCallbacks) );
- for ( int i = 0; i < count; i++ )
- {
- IPositionWatcher *pWatcher = assert_cast<IPositionWatcher *>(pCallbacks[i]);
- if ( pWatcher )
- {
- pWatcher->NotifyPositionChanged(pEntity);
- }
- }
-}
-
-void CWatcherList::NotifyVPhysicsStateChanged( IPhysicsObject *pPhysics, CBaseEntity *pEntity, bool bAwake )
-{
- IWatcherCallback *pCallbacks[1024]; // HACKHACK: Assumes this list is big enough!
- int count = GetCallbackObjects( pCallbacks, ARRAYSIZE(pCallbacks) );
- for ( int i = 0; i < count; i++ )
- {
- IVPhysicsWatcher *pWatcher = assert_cast<IVPhysicsWatcher *>(pCallbacks[i]);
- if ( pWatcher )
- {
- pWatcher->NotifyVPhysicsStateChanged(pPhysics, pEntity, bAwake);
- }
- }
-}
-
-unsigned short CWatcherList::Find( CBaseEntity *pEntity )
-{
- unsigned short next = g_WatcherList.InvalidIndex();
- for ( unsigned short node = g_WatcherList.Head( m_list ); node != g_WatcherList.InvalidIndex(); node = next )
- {
- next = g_WatcherList.Next( node );
- watcher_t *pNode = &g_WatcherList.Element(node);
- if ( pNode->hWatcher.Get() == pEntity )
- {
- return node;
- }
- }
- return g_WatcherList.InvalidIndex();
-}
-
-void CWatcherList::RemoveWatcher( CBaseEntity *pEntity )
-{
- unsigned short node = Find( pEntity );
- if ( node != g_WatcherList.InvalidIndex() )
- {
- g_WatcherList.Remove( m_list, node );
- }
-}
-
-
-void CWatcherList::AddToList( CBaseEntity *pWatcher )
-{
- unsigned short node = Find( pWatcher );
- if ( node == g_WatcherList.InvalidIndex() )
- {
- watcher_t watcher;
- watcher.hWatcher = pWatcher;
- // save this separately so we can use the EHANDLE to test for deletion
- watcher.pWatcherCallback = dynamic_cast<IWatcherCallback *> (pWatcher);
-
- if ( watcher.pWatcherCallback )
- {
- g_WatcherList.AddToTail( m_list, watcher );
- }
- }
-}
-
-static void AddWatcherToEntity( CBaseEntity *pWatcher, CBaseEntity *pEntity, int watcherType )
-{
- CWatcherList *pList = (CWatcherList *)pEntity->GetDataObject(watcherType);
- if ( !pList )
- {
- pList = ( CWatcherList * )pEntity->CreateDataObject( watcherType );
- pList->Init();
- }
-
- pList->AddToList( pWatcher );
-}
-
-static void RemoveWatcherFromEntity( CBaseEntity *pWatcher, CBaseEntity *pEntity, int watcherType )
-{
- CWatcherList *pList = (CWatcherList *)pEntity->GetDataObject(watcherType);
- if ( pList )
- {
- pList->RemoveWatcher( pWatcher );
- }
-}
-
-void WatchPositionChanges( CBaseEntity *pWatcher, CBaseEntity *pMovingEntity )
-{
- AddWatcherToEntity( pWatcher, pMovingEntity, POSITIONWATCHER );
-}
-
-void RemovePositionWatcher( CBaseEntity *pWatcher, CBaseEntity *pMovingEntity )
-{
- RemoveWatcherFromEntity( pWatcher, pMovingEntity, POSITIONWATCHER );
-}
-
-void ReportPositionChanged( CBaseEntity *pMovedEntity )
-{
- CWatcherList *pList = (CWatcherList *)pMovedEntity->GetDataObject(POSITIONWATCHER);
- if ( pList )
- {
- pList->NotifyPositionChanged( pMovedEntity );
- }
-}
-
-void WatchVPhysicsStateChanges( CBaseEntity *pWatcher, CBaseEntity *pPhysicsEntity )
-{
- AddWatcherToEntity( pWatcher, pPhysicsEntity, VPHYSICSWATCHER );
-}
-
-void RemoveVPhysicsStateWatcher( CBaseEntity *pWatcher, CBaseEntity *pPhysicsEntity )
-{
- AddWatcherToEntity( pWatcher, pPhysicsEntity, VPHYSICSWATCHER );
-}
-
-void ReportVPhysicsStateChanged( IPhysicsObject *pPhysics, CBaseEntity *pEntity, bool bAwake )
-{
- CWatcherList *pList = (CWatcherList *)pEntity->GetDataObject(VPHYSICSWATCHER);
- if ( pList )
- {
- pList->NotifyVPhysicsStateChanged( pPhysics, pEntity, bAwake );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBaseEntity::DestroyAllDataObjects( void )
-{
- int i;
- for ( i = 0; i < NUM_DATAOBJECT_TYPES; i++ )
- {
- if ( HasDataObjectType( i ) )
- {
- DestroyDataObject( i );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// For debugging
-//-----------------------------------------------------------------------------
-
-#ifdef GAME_DLL
-
-void SpewLinks()
-{
- int nCount = 0;
- for ( CBaseEntity *pClass = gEntList.FirstEnt(); pClass != NULL; pClass = gEntList.NextEnt(pClass) )
- {
- if ( pClass /*&& !pClass->IsDormant()*/ )
- {
- touchlink_t *root = ( touchlink_t * )pClass->GetDataObject( TOUCHLINK );
- if ( root )
- {
-
- // check if the edict is already in the list
- for ( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
- {
- ++nCount;
- Msg("[%d] (%d) Link %d (%s) -> %d (%s)\n", nCount, pClass->IsDormant(),
- pClass->entindex(), pClass->GetClassname(),
- link->entityTouched->entindex(), link->entityTouched->GetClassname() );
- }
- }
- }
- }
-}
-
-#endif
-
-//-----------------------------------------------------------------------------
-// Returns the actual gravity
-//-----------------------------------------------------------------------------
-static inline float GetActualGravity( CBaseEntity *pEnt )
-{
- float ent_gravity = pEnt->GetGravity();
- if ( ent_gravity == 0.0f )
- {
- ent_gravity = 1.0f;
- }
-
- return ent_gravity * GetCurrentGravity();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : inline touchlink_t
-//-----------------------------------------------------------------------------
-inline touchlink_t *AllocTouchLink( void )
-{
- touchlink_t *link = (touchlink_t*)g_EdictTouchLinks.Alloc( sizeof(touchlink_t) );
- if ( link )
- {
- ++linksallocated;
- }
- else
- {
- DevWarning( "AllocTouchLink: failed to allocate touchlink_t.\n" );
- }
-
- return link;
-}
-
-static touchlink_t *g_pNextLink = NULL;
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *link -
-// Output : inline void
-//-----------------------------------------------------------------------------
-inline void FreeTouchLink( touchlink_t *link )
-{
- if ( link )
- {
- if ( link == g_pNextLink )
- {
- g_pNextLink = link->nextLink;
- }
- --linksallocated;
- link->prevLink = link->nextLink = NULL;
- }
-
- // Necessary to catch crashes
- g_EdictTouchLinks.Free( link );
-}
-
-#ifdef STAGING_ONLY
-#ifndef CLIENT_DLL
-ConVar sv_groundlink_debug( "sv_groundlink_debug", "0", FCVAR_NONE, "Enable logging of alloc/free operations for debugging." );
-#endif
-#endif // STAGING_ONLY
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : inline groundlink_t
-//-----------------------------------------------------------------------------
-inline groundlink_t *AllocGroundLink( void )
-{
- groundlink_t *link = (groundlink_t*)g_EntityGroundLinks.Alloc( sizeof(groundlink_t) );
- if ( link )
- {
- ++groundlinksallocated;
- }
- else
- {
- DevMsg( "AllocGroundLink: failed to allocate groundlink_t.!!! groundlinksallocated=%d g_EntityGroundLinks.Count()=%d\n", groundlinksallocated, g_EntityGroundLinks.Count() );
- }
-
-#ifdef STAGING_ONLY
-#ifndef CLIENT_DLL
- if ( sv_groundlink_debug.GetBool() )
- {
- UTIL_LogPrintf( "Groundlink Alloc: %p at %d\n", link, groundlinksallocated );
- }
-#endif
-#endif // STAGING_ONLY
-
- return link;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *link -
-// Output : inline void
-//-----------------------------------------------------------------------------
-inline void FreeGroundLink( groundlink_t *link )
-{
-#ifdef STAGING_ONLY
-#ifndef CLIENT_DLL
- if ( sv_groundlink_debug.GetBool() )
- {
- UTIL_LogPrintf( "Groundlink Free: %p at %d\n", link, groundlinksallocated );
- }
-#endif
-#endif // STAGING_ONLY
-
- if ( link )
- {
- --groundlinksallocated;
- }
-
- g_EntityGroundLinks.Free( link );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseEntity::IsCurrentlyTouching( void ) const
-{
- if ( HasDataObjectType( TOUCHLINK ) )
- {
- return true;
- }
-
- return false;
-}
-
-static bool g_bCleanupDatObject = true;
-
-//-----------------------------------------------------------------------------
-// Purpose: Checks to see if any entities that have been touching this one
-// have stopped touching it, and notify the entity if so.
-// Called at the end of a frame, after all the entities have run
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsCheckForEntityUntouch( void )
-{
- Assert( g_pNextLink == NULL );
-
- touchlink_t *link;
-
- touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
- if ( root )
- {
-#ifdef PORTAL
- CPortalTouchScope scope;
-#endif
- bool saveCleanup = g_bCleanupDatObject;
- g_bCleanupDatObject = false;
-
- link = root->nextLink;
- while ( link != root )
- {
- g_pNextLink = link->nextLink;
-
- // these touchlinks are not polled. The ents are touching due to an outside
- // system that will add/delete them as necessary (vphysics in this case)
- if ( link->touchStamp == TOUCHSTAMP_EVENT_DRIVEN )
- {
- // refresh the touch call
- PhysicsTouch( link->entityTouched );
- }
- else
- {
- // check to see if the touch stamp is up to date
- if ( link->touchStamp != touchStamp )
- {
- // stamp is out of data, so entities are no longer touching
- // remove self from other entities touch list
- PhysicsNotifyOtherOfUntouch( this, link->entityTouched );
-
- // remove other entity from this list
- PhysicsRemoveToucher( this, link );
- }
- }
-
- link = g_pNextLink;
- }
-
- g_bCleanupDatObject = saveCleanup;
-
- // Nothing left in list, destroy root
- if ( root->nextLink == root &&
- root->prevLink == root )
- {
- DestroyDataObject( TOUCHLINK );
- }
- }
-
- g_pNextLink = NULL;
-
- SetCheckUntouch( false );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: notifies an entity than another touching entity has moved out of contact.
-// Input : *other - the entity to be acted upon
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsNotifyOtherOfUntouch( CBaseEntity *ent, CBaseEntity *other )
-{
- if ( !other )
- return;
-
- // loop through ed's touch list, looking for the notifier
- // remove and call untouch if found
- touchlink_t *root = ( touchlink_t * )other->GetDataObject( TOUCHLINK );
- if ( root )
- {
- touchlink_t *link = root->nextLink;
- while ( link != root )
- {
- if ( link->entityTouched == ent )
- {
- PhysicsRemoveToucher( other, link );
-
- // Check for complete removal
- if ( g_bCleanupDatObject &&
- root->nextLink == root &&
- root->prevLink == root )
- {
- other->DestroyDataObject( TOUCHLINK );
- }
- return;
- }
-
- link = link->nextLink;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: removes a toucher from the list
-// Input : *link - the link to remove
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsRemoveToucher( CBaseEntity *otherEntity, touchlink_t *link )
-{
- // Every start Touch gets a corresponding end touch
- if ( (link->flags & FTOUCHLINK_START_TOUCH) &&
- link->entityTouched != NULL &&
- otherEntity != NULL )
- {
- otherEntity->EndTouch( link->entityTouched );
- }
-
- link->nextLink->prevLink = link->prevLink;
- link->prevLink->nextLink = link->nextLink;
-
- if ( DebugTouchlinks() )
- Msg( "remove 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n", link, link->entityTouched->GetDebugName(), otherEntity->GetDebugName(), link->entityTouched->entindex(), otherEntity->entindex(), linksallocated, g_EdictTouchLinks.PeakCount() );
- FreeTouchLink( link );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Clears all touches from the list
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsRemoveTouchedList( CBaseEntity *ent )
-{
-#ifdef PORTAL
- CPortalTouchScope scope;
-#endif
-
- touchlink_t *link, *nextLink;
-
- touchlink_t *root = ( touchlink_t * )ent->GetDataObject( TOUCHLINK );
- if ( root )
- {
- link = root->nextLink;
- bool saveCleanup = g_bCleanupDatObject;
- g_bCleanupDatObject = false;
- while ( link && link != root )
- {
- nextLink = link->nextLink;
-
- // notify the other entity that this ent has gone away
- PhysicsNotifyOtherOfUntouch( ent, link->entityTouched );
-
- // kill it
- if ( DebugTouchlinks() )
- Msg( "remove 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n", link, ent->GetDebugName(), link->entityTouched->GetDebugName(), ent->entindex(), link->entityTouched->entindex(), linksallocated, g_EdictTouchLinks.PeakCount() );
- FreeTouchLink( link );
- link = nextLink;
- }
-
- g_bCleanupDatObject = saveCleanup;
- ent->DestroyDataObject( TOUCHLINK );
- }
-
- ent->touchStamp = 0;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *other -
-// Output : groundlink_t
-//-----------------------------------------------------------------------------
-groundlink_t *CBaseEntity::AddEntityToGroundList( CBaseEntity *other )
-{
- groundlink_t *link;
-
- if ( this == other )
- return NULL;
-
- // check if the edict is already in the list
- groundlink_t *root = ( groundlink_t * )GetDataObject( GROUNDLINK );
- if ( root )
- {
- for ( link = root->nextLink; link != root; link = link->nextLink )
- {
- if ( link->entity == other )
- {
- // no more to do
- return link;
- }
- }
- }
- else
- {
- root = ( groundlink_t * )CreateDataObject( GROUNDLINK );
- root->prevLink = root->nextLink = root;
- }
-
- // entity is not in list, so it's a new touch
- // add it to the touched list and then call the touch function
-
- // build new link
- link = AllocGroundLink();
- if ( !link )
- return NULL;
-
- link->entity = other;
- // add it to the list
- link->nextLink = root->nextLink;
- link->prevLink = root;
- link->prevLink->nextLink = link;
- link->nextLink->prevLink = link;
-
- PhysicsStartGroundContact( other );
-
- return link;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called whenever two entities come in contact
-// Input : *pentOther - the entity who it has touched
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsStartGroundContact( CBaseEntity *pentOther )
-{
- if ( !pentOther )
- return;
-
- if ( !(IsMarkedForDeletion() || pentOther->IsMarkedForDeletion()) )
- {
- pentOther->StartGroundContact( this );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: notifies an entity than another touching entity has moved out of contact.
-// Input : *other - the entity to be acted upon
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsNotifyOtherOfGroundRemoval( CBaseEntity *ent, CBaseEntity *other )
-{
- if ( !other )
- return;
-
- // loop through ed's touch list, looking for the notifier
- // remove and call untouch if found
- groundlink_t *root = ( groundlink_t * )other->GetDataObject( GROUNDLINK );
- if ( root )
- {
- groundlink_t *link = root->nextLink;
- while ( link != root )
- {
- if ( link->entity == ent )
- {
- PhysicsRemoveGround( other, link );
-
- if ( root->nextLink == root &&
- root->prevLink == root )
- {
- other->DestroyDataObject( GROUNDLINK );
- }
- return;
- }
-
- link = link->nextLink;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: removes a toucher from the list
-// Input : *link - the link to remove
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsRemoveGround( CBaseEntity *other, groundlink_t *link )
-{
- // Every start Touch gets a corresponding end touch
- if ( link->entity != NULL )
- {
- CBaseEntity *linkEntity = link->entity;
- CBaseEntity *otherEntity = other;
- if ( linkEntity && otherEntity )
- {
- linkEntity->EndGroundContact( otherEntity );
- }
- }
-
- link->nextLink->prevLink = link->prevLink;
- link->prevLink->nextLink = link->nextLink;
- FreeGroundLink( link );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: static method to remove ground list for an entity
-// Input : *ent -
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsRemoveGroundList( CBaseEntity *ent )
-{
- groundlink_t *link, *nextLink;
-
- groundlink_t *root = ( groundlink_t * )ent->GetDataObject( GROUNDLINK );
- if ( root )
- {
- link = root->nextLink;
- while ( link && link != root )
- {
- nextLink = link->nextLink;
-
- // notify the other entity that this ent has gone away
- PhysicsNotifyOtherOfGroundRemoval( ent, link->entity );
-
- // kill it
- FreeGroundLink( link );
-
- link = nextLink;
- }
-
- ent->DestroyDataObject( GROUNDLINK );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called every frame that two entities are touching
-// Input : *pentOther - the entity who it has touched
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsTouch( CBaseEntity *pentOther )
-{
- if ( pentOther )
- {
- if ( !(IsMarkedForDeletion() || pentOther->IsMarkedForDeletion()) )
- {
- Touch( pentOther );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called whenever two entities come in contact
-// Input : *pentOther - the entity who it has touched
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsStartTouch( CBaseEntity *pentOther )
-{
- if ( pentOther )
- {
- if ( !(IsMarkedForDeletion() || pentOther->IsMarkedForDeletion()) )
- {
- StartTouch( pentOther );
- Touch( pentOther );
- }
- }
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Marks in an entity that it is touching another entity, and calls
-// it's Touch() function if it is a new touch.
-// Stamps the touch link with the new time so that when we check for
-// untouch we know things haven't changed.
-// Input : *other - entity that it is in contact with
-//-----------------------------------------------------------------------------
-touchlink_t *CBaseEntity::PhysicsMarkEntityAsTouched( CBaseEntity *other )
-{
- touchlink_t *link;
-
- if ( this == other )
- return NULL;
-
- // Entities in hierarchy should not interact
- if ( (this->GetMoveParent() == other) || (this == other->GetMoveParent()) )
- return NULL;
-
- // check if either entity doesn't generate touch functions
- if ( (GetFlags() | other->GetFlags()) & FL_DONTTOUCH )
- return NULL;
-
- // Pure triggers should not touch each other
- if ( IsSolidFlagSet( FSOLID_TRIGGER ) && other->IsSolidFlagSet( FSOLID_TRIGGER ) )
- {
- if (!IsSolid() && !other->IsSolid())
- return NULL;
- }
-
- // Don't do touching if marked for deletion
- if ( other->IsMarkedForDeletion() )
- {
- return NULL;
- }
-
- if ( IsMarkedForDeletion() )
- {
- return NULL;
- }
-
-#ifdef PORTAL
- CPortalTouchScope scope;
-#endif
-
- // check if the edict is already in the list
- touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
- if ( root )
- {
- for ( link = root->nextLink; link != root; link = link->nextLink )
- {
- if ( link->entityTouched == other )
- {
- // update stamp
- link->touchStamp = touchStamp;
-
- if ( !CBaseEntity::sm_bDisableTouchFuncs )
- {
- PhysicsTouch( other );
- }
-
- // no more to do
- return link;
- }
- }
- }
- else
- {
- // Allocate the root object
- root = ( touchlink_t * )CreateDataObject( TOUCHLINK );
- root->nextLink = root->prevLink = root;
- }
-
- // entity is not in list, so it's a new touch
- // add it to the touched list and then call the touch function
-
- // build new link
- link = AllocTouchLink();
- if ( DebugTouchlinks() )
- Msg( "add 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n", link, GetDebugName(), other->GetDebugName(), entindex(), other->entindex(), linksallocated, g_EdictTouchLinks.PeakCount() );
- if ( !link )
- return NULL;
-
- link->touchStamp = touchStamp;
- link->entityTouched = other;
- link->flags = 0;
- // add it to the list
- link->nextLink = root->nextLink;
- link->prevLink = root;
- link->prevLink->nextLink = link;
- link->nextLink->prevLink = link;
-
- // non-solid entities don't get touched
- bool bShouldTouch = (IsSolid() && !IsSolidFlagSet(FSOLID_VOLUME_CONTENTS)) || IsSolidFlagSet(FSOLID_TRIGGER);
- if ( bShouldTouch && !other->IsSolidFlagSet(FSOLID_TRIGGER) )
- {
- link->flags |= FTOUCHLINK_START_TOUCH;
- if ( !CBaseEntity::sm_bDisableTouchFuncs )
- {
- PhysicsStartTouch( other );
- }
- }
-
- return link;
-}
-
-static trace_t g_TouchTrace;
-const trace_t &CBaseEntity::GetTouchTrace( void )
-{
- return g_TouchTrace;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Marks the fact that two edicts are in contact
-// Input : *other - other entity
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsMarkEntitiesAsTouching( CBaseEntity *other, trace_t &trace )
-{
- g_TouchTrace = trace;
- PhysicsMarkEntityAsTouched( other );
- other->PhysicsMarkEntityAsTouched( this );
-}
-
-void CBaseEntity::PhysicsMarkEntitiesAsTouchingEventDriven( CBaseEntity *other, trace_t &trace )
-{
- g_TouchTrace = trace;
- g_TouchTrace.m_pEnt = other;
-
- touchlink_t *link;
- link = this->PhysicsMarkEntityAsTouched( other );
- if ( link )
- {
- // mark these links as event driven so they aren't untouched the next frame
- // when the physics doesn't refresh them
- link->touchStamp = TOUCHSTAMP_EVENT_DRIVEN;
- }
- g_TouchTrace.m_pEnt = this;
- link = other->PhysicsMarkEntityAsTouched( this );
- if ( link )
- {
- link->touchStamp = TOUCHSTAMP_EVENT_DRIVEN;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Two entities have touched, so run their touch functions
-// Input : *other -
-// *ptrace -
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsImpact( CBaseEntity *other, trace_t &trace )
-{
- if ( !other )
- {
- return;
- }
-
- // If either of the entities is flagged to be deleted,
- // don't call the touch functions
- if ( ( GetFlags() | other->GetFlags() ) & FL_KILLME )
- {
- return;
- }
-
- PhysicsMarkEntitiesAsTouching( other, trace );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the mask of what is solid for the given entity
-// Output : unsigned int
-//-----------------------------------------------------------------------------
-unsigned int CBaseEntity::PhysicsSolidMaskForEntity( void ) const
-{
- return MASK_SOLID;
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes the water level + type
-//-----------------------------------------------------------------------------
-void CBaseEntity::UpdateWaterState()
-{
- // FIXME: This computation is nonsensical for rigid child attachments
- // Should we just grab the type + level of the parent?
- // Probably for rigid children anyways...
-
- // Compute the point to check for water state
- Vector point;
- CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &point );
-
- SetWaterLevel( 0 );
- SetWaterType( CONTENTS_EMPTY );
- int cont = UTIL_PointContents (point);
-
- if (( cont & MASK_WATER ) == 0)
- return;
-
- SetWaterType( cont );
- SetWaterLevel( 1 );
-
- // point sized entities are always fully submerged
- if ( IsPointSized() )
- {
- SetWaterLevel( 3 );
- }
- else
- {
- // Check the exact center of the box
- point[2] = WorldSpaceCenter().z;
-
- int midcont = UTIL_PointContents (point);
- if ( midcont & MASK_WATER )
- {
- // Now check where the eyes are...
- SetWaterLevel( 2 );
- point[2] = EyePosition().z;
-
- int eyecont = UTIL_PointContents (point);
- if ( eyecont & MASK_WATER )
- {
- SetWaterLevel( 3 );
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Check if entity is in the water and applies any current to velocity
-// and sets appropriate water flags
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseEntity::PhysicsCheckWater( void )
-{
- if (GetMoveParent())
- return GetWaterLevel() > 1;
-
- int cont = GetWaterType();
-
- // If we're not in water + don't have a current, we're done
- if ( ( cont & (MASK_WATER | MASK_CURRENT) ) != (MASK_WATER | MASK_CURRENT) )
- return GetWaterLevel() > 1;
-
- // Compute current direction
- Vector v( 0, 0, 0 );
- if ( cont & CONTENTS_CURRENT_0 )
- {
- v[0] += 1;
- }
- if ( cont & CONTENTS_CURRENT_90 )
- {
- v[1] += 1;
- }
- if ( cont & CONTENTS_CURRENT_180 )
- {
- v[0] -= 1;
- }
- if ( cont & CONTENTS_CURRENT_270 )
- {
- v[1] -= 1;
- }
- if ( cont & CONTENTS_CURRENT_UP )
- {
- v[2] += 1;
- }
- if ( cont & CONTENTS_CURRENT_DOWN )
- {
- v[2] -= 1;
- }
-
- // The deeper we are, the stronger the current.
- Vector newBaseVelocity;
- VectorMA (GetBaseVelocity(), 50.0*GetWaterLevel(), v, newBaseVelocity);
- SetBaseVelocity( newBaseVelocity );
-
- return GetWaterLevel() > 1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Bounds velocity
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsCheckVelocity( void )
-{
- Vector origin = GetAbsOrigin();
- Vector vecAbsVelocity = GetAbsVelocity();
-
- bool bReset = false;
- for ( int i=0 ; i<3 ; i++ )
- {
- if ( IS_NAN(vecAbsVelocity[i]) )
- {
- Msg( "Got a NaN velocity on %s\n", GetClassname() );
- vecAbsVelocity[i] = 0;
- bReset = true;
- }
- if ( IS_NAN(origin[i]) )
- {
- Msg( "Got a NaN origin on %s\n", GetClassname() );
- origin[i] = 0;
- bReset = true;
- }
-
- if ( vecAbsVelocity[i] > sv_maxvelocity.GetFloat() )
- {
-#ifdef _DEBUG
- DevWarning( 2, "Got a velocity too high on %s\n", GetClassname() );
-#endif
- vecAbsVelocity[i] = sv_maxvelocity.GetFloat();
- bReset = true;
- }
- else if ( vecAbsVelocity[i] < -sv_maxvelocity.GetFloat() )
- {
-#ifdef _DEBUG
- DevWarning( 2, "Got a velocity too low on %s\n", GetClassname() );
-#endif
- vecAbsVelocity[i] = -sv_maxvelocity.GetFloat();
- bReset = true;
- }
- }
-
- if (bReset)
- {
- SetAbsOrigin( origin );
- SetAbsVelocity( vecAbsVelocity );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Applies gravity to falling objects
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsAddGravityMove( Vector &move )
-{
- Vector vecAbsVelocity = GetAbsVelocity();
-
- move.x = (vecAbsVelocity.x + GetBaseVelocity().x ) * gpGlobals->frametime;
- move.y = (vecAbsVelocity.y + GetBaseVelocity().y ) * gpGlobals->frametime;
-
- if ( GetFlags() & FL_ONGROUND )
- {
- move.z = GetBaseVelocity().z * gpGlobals->frametime;
- return;
- }
-
- // linear acceleration due to gravity
- float newZVelocity = vecAbsVelocity.z - GetActualGravity( this ) * gpGlobals->frametime;
-
- move.z = ((vecAbsVelocity.z + newZVelocity) / 2.0 + GetBaseVelocity().z ) * gpGlobals->frametime;
-
- Vector vecBaseVelocity = GetBaseVelocity();
- vecBaseVelocity.z = 0.0f;
- SetBaseVelocity( vecBaseVelocity );
-
- vecAbsVelocity.z = newZVelocity;
- SetAbsVelocity( vecAbsVelocity );
-
- // Bound velocity
- PhysicsCheckVelocity();
-}
-
-
-#define STOP_EPSILON 0.1
-//-----------------------------------------------------------------------------
-// Purpose: Slide off of the impacting object. Returns the blocked flags (1 = floor, 2 = step / wall)
-// Input : in -
-// normal -
-// out -
-// overbounce -
-// Output : int
-//-----------------------------------------------------------------------------
-int CBaseEntity::PhysicsClipVelocity( const Vector& in, const Vector& normal, Vector& out, float overbounce )
-{
- float backoff;
- float change;
- float angle;
- int i, blocked;
-
- blocked = 0;
-
- angle = normal[ 2 ];
-
- if ( angle > 0 )
- {
- blocked |= 1; // floor
- }
- if ( !angle )
- {
- blocked |= 2; // step
- }
-
- backoff = DotProduct (in, normal) * overbounce;
-
- for ( i=0 ; i<3 ; i++ )
- {
- change = normal[i]*backoff;
- out[i] = in[i] - change;
- if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
- {
- out[i] = 0;
- }
- }
-
- return blocked;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBaseEntity::ResolveFlyCollisionBounce( trace_t &trace, Vector &vecVelocity, float flMinTotalElasticity )
-{
-#ifdef HL1_DLL
- flMinTotalElasticity = 0.3f;
-#endif//HL1_DLL
-
- // Get the impact surface's elasticity.
- float flSurfaceElasticity;
- physprops->GetPhysicsProperties( trace.surface.surfaceProps, NULL, NULL, NULL, &flSurfaceElasticity );
-
- float flTotalElasticity = GetElasticity() * flSurfaceElasticity;
- if ( flMinTotalElasticity > 0.9f )
- {
- flMinTotalElasticity = 0.9f;
- }
- flTotalElasticity = clamp( flTotalElasticity, flMinTotalElasticity, 0.9f );
-
- // NOTE: A backoff of 2.0f is a reflection
- Vector vecAbsVelocity;
- PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, 2.0f );
- vecAbsVelocity *= flTotalElasticity;
-
- // Get the total velocity (player + conveyors, etc.)
- VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
- float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
-
- // Stop if on ground.
- if ( trace.plane.normal.z > 0.7f ) // Floor
- {
- // Verify that we have an entity.
- CBaseEntity *pEntity = trace.m_pEnt;
- Assert( pEntity );
-
- // Are we on the ground?
- if ( vecVelocity.z < ( GetActualGravity( this ) * gpGlobals->frametime ) )
- {
- vecAbsVelocity.z = 0.0f;
-
- // Recompute speedsqr based on the new absvel
- VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
- flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
- }
-
- SetAbsVelocity( vecAbsVelocity );
-
- if ( flSpeedSqr < ( 30 * 30 ) )
- {
- if ( pEntity->IsStandable() )
- {
- SetGroundEntity( pEntity );
- }
-
- // Reset velocities.
- SetAbsVelocity( vec3_origin );
- SetLocalAngularVelocity( vec3_angle );
- }
- else
- {
- Vector vecDelta = GetBaseVelocity() - vecAbsVelocity;
- Vector vecBaseDir = GetBaseVelocity();
- VectorNormalize( vecBaseDir );
- float flScale = vecDelta.Dot( vecBaseDir );
-
- VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecVelocity );
- VectorMA( vecVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, GetBaseVelocity() * flScale, vecVelocity );
- PhysicsPushEntity( vecVelocity, &trace );
- }
- }
- else
- {
- // If we get *too* slow, we'll stick without ever coming to rest because
- // we'll get pushed down by gravity faster than we can escape from the wall.
- if ( flSpeedSqr < ( 30 * 30 ) )
- {
- // Reset velocities.
- SetAbsVelocity( vec3_origin );
- SetLocalAngularVelocity( vec3_angle );
- }
- else
- {
- SetAbsVelocity( vecAbsVelocity );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBaseEntity::ResolveFlyCollisionSlide( trace_t &trace, Vector &vecVelocity )
-{
- // Get the impact surface's friction.
- float flSurfaceFriction;
- physprops->GetPhysicsProperties( trace.surface.surfaceProps, NULL, NULL, &flSurfaceFriction, NULL );
-
- // A backoff of 1.0 is a slide.
- float flBackOff = 1.0f;
- Vector vecAbsVelocity;
- PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, flBackOff );
-
- if ( trace.plane.normal.z <= 0.7 ) // Floor
- {
- SetAbsVelocity( vecAbsVelocity );
- return;
- }
-
- // Stop if on ground.
- // Get the total velocity (player + conveyors, etc.)
- VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
- float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
-
- // Verify that we have an entity.
- CBaseEntity *pEntity = trace.m_pEnt;
- Assert( pEntity );
-
- // Are we on the ground?
- if ( vecVelocity.z < ( GetActualGravity( this ) * gpGlobals->frametime ) )
- {
- vecAbsVelocity.z = 0.0f;
-
- // Recompute speedsqr based on the new absvel
- VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
- flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
- }
- SetAbsVelocity( vecAbsVelocity );
-
- if ( flSpeedSqr < ( 30 * 30 ) )
- {
- if ( pEntity->IsStandable() )
- {
- SetGroundEntity( pEntity );
- }
-
- // Reset velocities.
- SetAbsVelocity( vec3_origin );
- SetLocalAngularVelocity( vec3_angle );
- }
- else
- {
- vecAbsVelocity += GetBaseVelocity();
- vecAbsVelocity *= ( 1.0f - trace.fraction ) * gpGlobals->frametime * flSurfaceFriction;
- PhysicsPushEntity( vecAbsVelocity, &trace );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBaseEntity::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity )
-{
- // Stop if on ground.
- if ( trace.plane.normal.z > 0.7 ) // Floor
- {
- // Get the total velocity (player + conveyors, etc.)
- VectorAdd( GetAbsVelocity(), GetBaseVelocity(), vecVelocity );
-
- // Verify that we have an entity.
- CBaseEntity *pEntity = trace.m_pEnt;
- Assert( pEntity );
-
- // Are we on the ground?
- if ( vecVelocity.z < ( GetActualGravity( this ) * gpGlobals->frametime ) )
- {
- Vector vecAbsVelocity = GetAbsVelocity();
- vecAbsVelocity.z = 0.0f;
- SetAbsVelocity( vecAbsVelocity );
- }
-
- if ( pEntity->IsStandable() )
- {
- SetGroundEntity( pEntity );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Performs the collision resolution for fliers.
-//-----------------------------------------------------------------------------
-void CBaseEntity::PerformFlyCollisionResolution( trace_t &trace, Vector &move )
-{
- switch( GetMoveCollide() )
- {
- case MOVECOLLIDE_FLY_CUSTOM:
- {
- ResolveFlyCollisionCustom( trace, move );
- break;
- }
-
- case MOVECOLLIDE_FLY_BOUNCE:
- {
- ResolveFlyCollisionBounce( trace, move );
- break;
- }
-
- case MOVECOLLIDE_FLY_SLIDE:
- case MOVECOLLIDE_DEFAULT:
- // NOTE: The default fly collision state is the same as a slide (for backward capatability).
- {
- ResolveFlyCollisionSlide( trace, move );
- break;
- }
-
- default:
- {
- // Invalid MOVECOLLIDE_<type>
- Assert( 0 );
- break;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Checks if an object has passed into or out of water and sets water info, alters velocity, plays splash sounds, etc.
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsCheckWaterTransition( void )
-{
- int oldcont = GetWaterType();
- UpdateWaterState();
- int cont = GetWaterType();
-
- // We can exit right out if we're a child... don't bother with this...
- if (GetMoveParent())
- return;
-
- if ( cont & MASK_WATER )
- {
- if (oldcont == CONTENTS_EMPTY)
- {
-#ifndef CLIENT_DLL
- Splash();
-#endif // !CLIENT_DLL
-
- // just crossed into water
- EmitSound( "BaseEntity.EnterWater" );
-
- if ( !IsEFlagSet( EFL_NO_WATER_VELOCITY_CHANGE ) )
- {
- Vector vecAbsVelocity = GetAbsVelocity();
- vecAbsVelocity[2] *= 0.5;
- SetAbsVelocity( vecAbsVelocity );
- }
- }
- }
- else
- {
- if ( oldcont != CONTENTS_EMPTY )
- {
- // just crossed out of water
- EmitSound( "BaseEntity.ExitWater" );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Computes new angles based on the angular velocity
-//-----------------------------------------------------------------------------
-void CBaseEntity::SimulateAngles( float flFrameTime )
-{
- // move angles
- QAngle angles;
- VectorMA ( GetLocalAngles(), flFrameTime, GetLocalAngularVelocity(), angles );
- SetLocalAngles( angles );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Toss, bounce, and fly movement. When onground, do nothing.
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsToss( void )
-{
- trace_t trace;
- Vector move;
-
- PhysicsCheckWater();
-
- // regular thinking
- if ( !PhysicsRunThink() )
- return;
-
- // Moving upward, off the ground, or resting on a client/monster, remove FL_ONGROUND
- if ( GetAbsVelocity()[2] > 0 || !GetGroundEntity() || !GetGroundEntity()->IsStandable() )
- {
- SetGroundEntity( NULL );
- }
-
- // Check to see if entity is on the ground at rest
- if ( GetFlags() & FL_ONGROUND )
- {
- if ( VectorCompare( GetAbsVelocity(), vec3_origin ) )
- {
- // Clear rotation if not moving (even if on a conveyor)
- SetLocalAngularVelocity( vec3_angle );
- if ( VectorCompare( GetBaseVelocity(), vec3_origin ) )
- return;
- }
- }
-
- PhysicsCheckVelocity();
-
- // add gravity
- if ( GetMoveType() == MOVETYPE_FLYGRAVITY && !(GetFlags() & FL_FLY) )
- {
- PhysicsAddGravityMove( move );
- }
- else
- {
- // Base velocity is not properly accounted for since this entity will move again after the bounce without
- // taking it into account
- Vector vecAbsVelocity = GetAbsVelocity();
- vecAbsVelocity += GetBaseVelocity();
- VectorScale(vecAbsVelocity, gpGlobals->frametime, move);
- PhysicsCheckVelocity( );
- }
-
- // move angles
- SimulateAngles( gpGlobals->frametime );
-
- // move origin
- PhysicsPushEntity( move, &trace );
-
-#if !defined( CLIENT_DLL )
- if ( VPhysicsGetObject() )
- {
- VPhysicsGetObject()->UpdateShadow( GetAbsOrigin(), vec3_angle, true, gpGlobals->frametime );
- }
-#endif
-
- PhysicsCheckVelocity();
-
- if (trace.allsolid )
- {
- // entity is trapped in another solid
- // UNDONE: does this entity needs to be removed?
- SetAbsVelocity(vec3_origin);
- SetLocalAngularVelocity(vec3_angle);
- return;
- }
-
-#if !defined( CLIENT_DLL )
- if (IsEdictFree())
- return;
-#endif
-
- if (trace.fraction != 1.0f)
- {
- PerformFlyCollisionResolution( trace, move );
- }
-
- // check for in water
- PhysicsCheckWaterTransition();
-}
-
-
-//-----------------------------------------------------------------------------
-// Simulation in local space of rigid children
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsRigidChild( void )
-{
- VPROF("CBaseEntity::PhysicsRigidChild");
- // NOTE: rigidly attached children do simulation in local space
- // Collision impulses will be handled either not at all, or by
- // forwarding the information to the highest move parent
-
- Vector vecPrevOrigin = GetAbsOrigin();
-
- // regular thinking
- if ( !PhysicsRunThink() )
- return;
-
- VPROF_SCOPE_BEGIN("CBaseEntity::PhysicsRigidChild-2");
-
-#if !defined( CLIENT_DLL )
- // Cause touch functions to be called
- PhysicsTouchTriggers( &vecPrevOrigin );
-
- // We have to do this regardless owing to hierarchy
- if ( VPhysicsGetObject() )
- {
- int solidType = GetSolid();
- bool bAxisAligned = ( solidType == SOLID_BBOX || solidType == SOLID_NONE ) ? true : false;
- VPhysicsGetObject()->UpdateShadow( GetAbsOrigin(), bAxisAligned ? vec3_angle : GetAbsAngles(), true, gpGlobals->frametime );
- }
-#endif
-
- VPROF_SCOPE_END();
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes the base velocity
-//-----------------------------------------------------------------------------
-void CBaseEntity::UpdateBaseVelocity( void )
-{
-#if !defined( CLIENT_DLL )
- if ( GetFlags() & FL_ONGROUND )
- {
- CBaseEntity *groundentity = GetGroundEntity();
- if ( groundentity )
- {
- // On conveyor belt that's moving?
- if ( groundentity->GetFlags() & FL_CONVEYOR )
- {
- Vector vecNewBaseVelocity;
- groundentity->GetGroundVelocityToApply( vecNewBaseVelocity );
- if ( GetFlags() & FL_BASEVELOCITY )
- {
- vecNewBaseVelocity += GetBaseVelocity();
- }
- AddFlag( FL_BASEVELOCITY );
- SetBaseVelocity( vecNewBaseVelocity );
- }
- }
- }
-#endif
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Runs a frame of physics for a specific edict (and all it's children)
-// Input : *ent - the thinking edict
-//-----------------------------------------------------------------------------
-void CBaseEntity::PhysicsSimulate( void )
-{
- VPROF( "CBaseEntity::PhysicsSimulate" );
- // NOTE: Players override PhysicsSimulate and drive through their CUserCmds at that point instead of
- // processng through this function call!!! They shouldn't chain to here ever.
- // Make sure not to simulate this guy twice per frame
- if (m_nSimulationTick == gpGlobals->tickcount)
- return;
-
- m_nSimulationTick = gpGlobals->tickcount;
-
- Assert( !IsPlayer() );
-
- // If we've got a moveparent, we must simulate that first.
- CBaseEntity *pMoveParent = GetMoveParent();
-
- if ( (GetMoveType() == MOVETYPE_NONE && !pMoveParent) || (GetMoveType() == MOVETYPE_VPHYSICS ) )
- {
- PhysicsNone();
- return;
- }
-
- // If ground entity goes away, make sure FL_ONGROUND is valid
- if ( !GetGroundEntity() )
- {
- RemoveFlag( FL_ONGROUND );
- }
-
- if (pMoveParent)
- {
- VPROF( "CBaseEntity::PhysicsSimulate-MoveParent" );
- pMoveParent->PhysicsSimulate();
- }
- else
- {
- VPROF( "CBaseEntity::PhysicsSimulate-BaseVelocity" );
-
- UpdateBaseVelocity();
-
- if ( ((GetFlags() & FL_BASEVELOCITY) == 0) && (GetBaseVelocity() != vec3_origin) )
- {
- // Apply momentum (add in half of the previous frame of velocity first)
- // BUGBUG: This will break with PhysicsStep() because of the timestep difference
- Vector vecAbsVelocity;
- VectorMA( GetAbsVelocity(), 1.0 + (gpGlobals->frametime*0.5), GetBaseVelocity(), vecAbsVelocity );
- SetAbsVelocity( vecAbsVelocity );
- SetBaseVelocity( vec3_origin );
- }
- RemoveFlag( FL_BASEVELOCITY );
- }
-
- switch( GetMoveType() )
- {
- case MOVETYPE_PUSH:
- {
- VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_PUSH" );
- PhysicsPusher();
- }
- break;
-
-
- case MOVETYPE_VPHYSICS:
- {
- }
- break;
-
- case MOVETYPE_NONE:
- {
- VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_NONE" );
- Assert(pMoveParent);
- PhysicsRigidChild();
- }
- break;
-
- case MOVETYPE_NOCLIP:
- {
- VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_NOCLIP" );
- PhysicsNoclip();
- }
- break;
-
- case MOVETYPE_STEP:
- {
- VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_STEP" );
- PhysicsStep();
- }
- break;
-
- case MOVETYPE_FLY:
- case MOVETYPE_FLYGRAVITY:
- {
- VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_FLY" );
- PhysicsToss();
- }
- break;
-
- case MOVETYPE_CUSTOM:
- {
- VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_CUSTOM" );
- PhysicsCustom();
- }
- break;
-
- default:
- Warning( "PhysicsSimulate: %s bad movetype %d", GetClassname(), GetMoveType() );
- Assert(0);
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Runs thinking code if time. There is some play in the exact time the think
-// function will be called, because it is called before any movement is done
-// in a frame. Not used for pushmove objects, because they must be exact.
-// Returns false if the entity removed itself.
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseEntity::PhysicsRunThink( thinkmethods_t thinkMethod )
-{
- if ( IsEFlagSet( EFL_NO_THINK_FUNCTION ) )
- return true;
-
- bool bAlive = true;
-
- // Don't fire the base if we're avoiding it
- if ( thinkMethod != THINK_FIRE_ALL_BUT_BASE )
- {
- bAlive = PhysicsRunSpecificThink( -1, &CBaseEntity::Think );
- if ( !bAlive )
- return false;
- }
-
- // Are we just firing the base think?
- if ( thinkMethod == THINK_FIRE_BASE_ONLY )
- return bAlive;
-
- // Fire the rest of 'em
- for ( int i = 0; i < m_aThinkFunctions.Count(); i++ )
- {
-#ifdef _DEBUG
- // Set the context
- m_iCurrentThinkContext = i;
-#endif
-
- bAlive = PhysicsRunSpecificThink( i, m_aThinkFunctions[i].m_pfnThink );
-
-#ifdef _DEBUG
- // Clear our context
- m_iCurrentThinkContext = NO_THINK_CONTEXT;
-#endif
-
- if ( !bAlive )
- return false;
- }
-
- return bAlive;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: For testing if all thinks are occuring at the same time
-//-----------------------------------------------------------------------------
-struct ThinkSync
-{
- float thinktime;
- int thinktick;
- CUtlVector< EHANDLE > entities;
-
- ThinkSync()
- {
- thinktime = 0;
- }
-
- ThinkSync( const ThinkSync& src )
- {
- thinktime = src.thinktime;
- thinktick = src.thinktick;
- int c = src.entities.Count();
- for ( int i = 0; i < c; i++ )
- {
- entities.AddToTail( src.entities[ i ] );
- }
- }
-};
-
-#if !defined( CLIENT_DLL )
-static ConVar sv_thinktimecheck( "sv_thinktimecheck", "0", 0, "Check for thinktimes all on same timestamp." );
-#endif
-
-//-----------------------------------------------------------------------------
-// Purpose: For testing if all thinks are occuring at the same time
-//-----------------------------------------------------------------------------
-class CThinkSyncTester
-{
-public:
- CThinkSyncTester() :
- m_Thinkers( 0, 0, ThinkLessFunc )
- {
- m_nLastFrameCount = -1;
- m_bShouldCheck = false;
- }
-
- void EntityThinking( int framecount, CBaseEntity *ent, float thinktime, int thinktick )
- {
-#if !defined( CLIENT_DLL )
- if ( m_nLastFrameCount != framecount )
- {
- if ( m_bShouldCheck )
- {
- // Report
- Report();
- m_Thinkers.RemoveAll();
- m_nLastFrameCount = framecount;
- }
-
- m_bShouldCheck = sv_thinktimecheck.GetBool();
- }
-
- if ( !m_bShouldCheck )
- return;
-
- ThinkSync *p = FindOrAddItem( ent, thinktime );
- if ( !p )
- {
- Assert( 0 );
- }
-
- p->thinktime = thinktime;
- p->thinktick = thinktick;
- EHANDLE h;
- h = ent;
- p->entities.AddToTail( h );
-#endif
- }
-
-private:
-
- static bool ThinkLessFunc( const ThinkSync& item1, const ThinkSync& item2 )
- {
- return item1.thinktime < item2.thinktime;
- }
-
- ThinkSync *FindOrAddItem( CBaseEntity *ent, float thinktime )
- {
- ThinkSync item;
- item.thinktime = thinktime;
-
- int idx = m_Thinkers.Find( item );
- if ( idx == m_Thinkers.InvalidIndex() )
- {
- idx = m_Thinkers.Insert( item );
- }
-
- return &m_Thinkers[ idx ];
- }
-
- void Report()
- {
- if ( m_Thinkers.Count() == 0 )
- return;
-
- Msg( "-----------------\nThink report frame %i\n", gpGlobals->tickcount );
-
- for ( int i = m_Thinkers.FirstInorder();
- i != m_Thinkers.InvalidIndex();
- i = m_Thinkers.NextInorder( i ) )
- {
- ThinkSync *p = &m_Thinkers[ i ];
- Assert( p );
- if ( !p )
- continue;
-
- int ecount = p->entities.Count();
- if ( !ecount )
- {
- continue;
- }
-
- Msg( "thinktime %f, %i entities\n", p->thinktime, ecount );
- for ( int j =0; j < ecount; j++ )
- {
- EHANDLE h = p->entities[ j ];
- int lastthinktick = 0;
- int nextthinktick = 0;
- CBaseEntity *e = h.Get();
- if ( e )
- {
- lastthinktick = e->m_nLastThinkTick;
- nextthinktick = e->m_nNextThinkTick;
- }
-
- Msg( " %p : %30s (last %5i/next %5i)\n", h.Get(), h.Get() ? h->GetClassname() : "NULL",
- lastthinktick, nextthinktick );
- }
- }
- }
-
- CUtlRBTree< ThinkSync > m_Thinkers;
- int m_nLastFrameCount;
- bool m_bShouldCheck;
-};
-
-static CThinkSyncTester g_ThinkChecker;
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CBaseEntity::PhysicsRunSpecificThink( int nContextIndex, BASEPTR thinkFunc )
-{
- int thinktick = GetNextThinkTick( nContextIndex );
-
- if ( thinktick <= 0 || thinktick > gpGlobals->tickcount )
- return true;
-
- float thinktime = thinktick * TICK_INTERVAL;
-
- // Don't let things stay in the past.
- // it is possible to start that way
- // by a trigger with a local time.
- if ( thinktime < gpGlobals->curtime )
- {
- thinktime = gpGlobals->curtime;
- }
-
- // Only do this on the game server
-#if !defined( CLIENT_DLL )
- g_ThinkChecker.EntityThinking( gpGlobals->tickcount, this, thinktime, m_nNextThinkTick );
-#endif
-
- SetNextThink( nContextIndex, TICK_NEVER_THINK );
-
- PhysicsDispatchThink( thinkFunc );
-
- SetLastThink( nContextIndex, gpGlobals->curtime );
-
- // Return whether entity is still valid
- return ( !IsMarkedForDeletion() );
-}
-
-void CBaseEntity::SetGroundEntity( CBaseEntity *ground )
-{
- if ( m_hGroundEntity.Get() == ground )
- return;
-
-#ifdef GAME_DLL
- // this can happen in-between updates to the held object controller (physcannon, +USE)
- // so trap it here and release held objects when they become player ground
- if ( ground && IsPlayer() && ground->GetMoveType()== MOVETYPE_VPHYSICS )
- {
- CBasePlayer *pPlayer = ToBasePlayer(this);
- IPhysicsObject *pPhysGround = ground->VPhysicsGetObject();
- if ( pPhysGround && pPlayer )
- {
- if ( pPhysGround->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
- {
- pPlayer->ForceDropOfCarriedPhysObjects( ground );
- }
- }
- }
-#endif
-
- CBaseEntity *oldGround = m_hGroundEntity;
- m_hGroundEntity = ground;
-
- // Just starting to touch
- if ( !oldGround && ground )
- {
- ground->AddEntityToGroundList( this );
- }
- // Just stopping touching
- else if ( oldGround && !ground )
- {
- PhysicsNotifyOtherOfGroundRemoval( this, oldGround );
- }
- // Changing out to new ground entity
- else
- {
- PhysicsNotifyOtherOfGroundRemoval( this, oldGround );
- ground->AddEntityToGroundList( this );
- }
-
- // HACK/PARANOID: This is redundant with the code above, but in case we get out of sync groundlist entries ever,
- // this will force the appropriate flags
- if ( ground )
- {
- AddFlag( FL_ONGROUND );
- }
- else
- {
- RemoveFlag( FL_ONGROUND );
- }
-}
-
-CBaseEntity *CBaseEntity::GetGroundEntity( void )
-{
- return m_hGroundEntity;
-}
-
-void CBaseEntity::StartGroundContact( CBaseEntity *ground )
-{
- AddFlag( FL_ONGROUND );
-// Msg( "+++ %s starting contact with ground %s\n", GetClassname(), ground->GetClassname() );
-}
-
-void CBaseEntity::EndGroundContact( CBaseEntity *ground )
-{
- RemoveFlag( FL_ONGROUND );
-// Msg( "--- %s ending contact with ground %s\n", GetClassname(), ground->GetClassname() );
-}
-
-
-void CBaseEntity::SetGroundChangeTime( float flTime )
-{
- m_flGroundChangeTime = flTime;
-}
-
-float CBaseEntity::GetGroundChangeTime( void )
-{
- return m_flGroundChangeTime;
-}
-
-
-
-// Remove this as ground entity for all object resting on this object
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBaseEntity::WakeRestingObjects()
-{
- // Unset this as ground entity for everything resting on this object
- // This calls endgroundcontact for everything on the list
- PhysicsRemoveGroundList( this );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *ent -
-//-----------------------------------------------------------------------------
-bool CBaseEntity::HasNPCsOnIt( void )
-{
- groundlink_t *link;
- groundlink_t *root = ( groundlink_t * )GetDataObject( GROUNDLINK );
- if ( root )
- {
- for ( link = root->nextLink; link != root; link = link->nextLink )
- {
- if ( link->entity && link->entity->MyNPCPointer() )
- return true;
- }
- }
-
- return false;
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include "cbase.h"
+#include "engine/IEngineSound.h"
+#include "mempool.h"
+#include "movevars_shared.h"
+#include "utlrbtree.h"
+#include "tier0/vprof.h"
+#include "entitydatainstantiator.h"
+#include "positionwatcher.h"
+#include "movetype_push.h"
+#include "vphysicsupdateai.h"
+#include "igamesystem.h"
+#include "utlmultilist.h"
+#include "tier1/callqueue.h"
+
+#ifdef PORTAL
+ #include "portal_util_shared.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// memory pool for storing links between entities
+static CUtlMemoryPool g_EdictTouchLinks( sizeof(touchlink_t), MAX_EDICTS, CUtlMemoryPool::GROW_NONE, "g_EdictTouchLinks");
+static CUtlMemoryPool g_EntityGroundLinks( sizeof( groundlink_t ), MAX_EDICTS, CUtlMemoryPool::GROW_NONE, "g_EntityGroundLinks");
+
+struct watcher_t
+{
+ EHANDLE hWatcher;
+ IWatcherCallback *pWatcherCallback;
+};
+
+static CUtlMultiList<watcher_t, unsigned short> g_WatcherList;
+class CWatcherList
+{
+public:
+ //CWatcherList(); NOTE: Dataobj doesn't support constructors - it zeros the memory
+ ~CWatcherList(); // frees the positionwatcher_t's to the pool
+ void Init();
+
+ void NotifyPositionChanged( CBaseEntity *pEntity );
+ void NotifyVPhysicsStateChanged( IPhysicsObject *pPhysics, CBaseEntity *pEntity, bool bAwake );
+
+ void AddToList( CBaseEntity *pWatcher );
+ void RemoveWatcher( CBaseEntity *pWatcher );
+
+private:
+ int GetCallbackObjects( IWatcherCallback **pList, int listMax );
+
+ unsigned short Find( CBaseEntity *pEntity );
+ unsigned short m_list;
+};
+
+int linksallocated = 0;
+int groundlinksallocated = 0;
+
+// Prints warnings if any entity think functions take longer than this many milliseconds
+#ifdef _DEBUG
+#define DEF_THINK_LIMIT "20"
+#else
+#define DEF_THINK_LIMIT "10"
+#endif
+
+ConVar think_limit( "think_limit", DEF_THINK_LIMIT, FCVAR_REPLICATED, "Maximum think time in milliseconds, warning is printed if this is exceeded." );
+#ifndef CLIENT_DLL
+ConVar debug_touchlinks( "debug_touchlinks", "0", 0, "Spew touch link activity" );
+#define DebugTouchlinks() debug_touchlinks.GetBool()
+#else
+#define DebugTouchlinks() false
+#endif
+
+
+
+//-----------------------------------------------------------------------------
+// Portal-specific hack designed to eliminate re-entrancy in touch functions
+//-----------------------------------------------------------------------------
+class CPortalTouchScope
+{
+public:
+ CPortalTouchScope();
+ ~CPortalTouchScope();
+
+public:
+ static int m_nDepth;
+ static CCallQueue m_CallQueue;
+};
+
+int CPortalTouchScope::m_nDepth = 0;
+CCallQueue CPortalTouchScope::m_CallQueue;
+
+CCallQueue *GetPortalCallQueue()
+{
+ return ( CPortalTouchScope::m_nDepth > 0 ) ? &CPortalTouchScope::m_CallQueue : NULL;
+}
+
+CPortalTouchScope::CPortalTouchScope()
+{
+ ++m_nDepth;
+}
+
+CPortalTouchScope::~CPortalTouchScope()
+{
+ Assert( m_nDepth >= 1 );
+ if ( --m_nDepth == 0 )
+ {
+ m_CallQueue.CallQueued();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: System for hanging objects off of CBaseEntity, etc.
+// Externalized data objects ( see sharreddefs.h for enum )
+//-----------------------------------------------------------------------------
+class CDataObjectAccessSystem : public CAutoGameSystem
+{
+public:
+
+ enum
+ {
+ MAX_ACCESSORS = 32,
+ };
+
+ CDataObjectAccessSystem()
+ {
+ // Cast to int to make it clear that we know we are comparing different enum types.
+ COMPILE_TIME_ASSERT( (int)NUM_DATAOBJECT_TYPES <= (int)MAX_ACCESSORS );
+
+ Q_memset( m_Accessors, 0, sizeof( m_Accessors ) );
+ }
+
+ virtual bool Init()
+ {
+ AddDataAccessor( TOUCHLINK, new CEntityDataInstantiator< touchlink_t > );
+ AddDataAccessor( GROUNDLINK, new CEntityDataInstantiator< groundlink_t > );
+ AddDataAccessor( STEPSIMULATION, new CEntityDataInstantiator< StepSimulationData > );
+ AddDataAccessor( MODELSCALE, new CEntityDataInstantiator< ModelScale > );
+ AddDataAccessor( POSITIONWATCHER, new CEntityDataInstantiator< CWatcherList > );
+ AddDataAccessor( PHYSICSPUSHLIST, new CEntityDataInstantiator< physicspushlist_t > );
+ AddDataAccessor( VPHYSICSUPDATEAI, new CEntityDataInstantiator< vphysicsupdateai_t > );
+ AddDataAccessor( VPHYSICSWATCHER, new CEntityDataInstantiator< CWatcherList > );
+
+ return true;
+ }
+
+ virtual void Shutdown()
+ {
+ for ( int i = 0; i < MAX_ACCESSORS; i++ )
+ {
+ delete m_Accessors[ i ];
+ m_Accessors[ i ] = 0;
+ }
+ }
+
+ void *GetDataObject( int type, const CBaseEntity *instance )
+ {
+ if ( !IsValidType( type ) )
+ {
+ Assert( !"Bogus type" );
+ return NULL;
+ }
+ return m_Accessors[ type ]->GetDataObject( instance );
+ }
+
+ void *CreateDataObject( int type, CBaseEntity *instance )
+ {
+ if ( !IsValidType( type ) )
+ {
+ Assert( !"Bogus type" );
+ return NULL;
+ }
+
+ return m_Accessors[ type ]->CreateDataObject( instance );
+ }
+
+ void DestroyDataObject( int type, CBaseEntity *instance )
+ {
+ if ( !IsValidType( type ) )
+ {
+ Assert( !"Bogus type" );
+ return;
+ }
+
+ m_Accessors[ type ]->DestroyDataObject( instance );
+ }
+
+private:
+
+ bool IsValidType( int type ) const
+ {
+ if ( type < 0 || type >= MAX_ACCESSORS )
+ return false;
+
+ if ( m_Accessors[ type ] == NULL )
+ return false;
+ return true;
+ }
+
+ void AddDataAccessor( int type, IEntityDataInstantiator *instantiator )
+ {
+ if ( type < 0 || type >= MAX_ACCESSORS )
+ {
+ Assert( !"AddDataAccessor with out of range type!!!\n" );
+ return;
+ }
+
+ Assert( instantiator );
+
+ if ( m_Accessors[ type ] != NULL )
+ {
+ Assert( !"AddDataAccessor, duplicate adds!!!\n" );
+ return;
+ }
+
+ m_Accessors[ type ] = instantiator;
+ }
+
+ IEntityDataInstantiator *m_Accessors[ MAX_ACCESSORS ];
+};
+
+static CDataObjectAccessSystem g_DataObjectAccessSystem;
+
+bool CBaseEntity::HasDataObjectType( int type ) const
+{
+ Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
+ return ( m_fDataObjectTypes & (1<<type) ) ? true : false;
+}
+
+void CBaseEntity::AddDataObjectType( int type )
+{
+ Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
+ m_fDataObjectTypes |= (1<<type);
+}
+
+void CBaseEntity::RemoveDataObjectType( int type )
+{
+ Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
+ m_fDataObjectTypes &= ~(1<<type);
+}
+
+void *CBaseEntity::GetDataObject( int type )
+{
+ Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
+ if ( !HasDataObjectType( type ) )
+ return NULL;
+ return g_DataObjectAccessSystem.GetDataObject( type, this );
+}
+
+void *CBaseEntity::CreateDataObject( int type )
+{
+ Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
+ AddDataObjectType( type );
+ return g_DataObjectAccessSystem.CreateDataObject( type, this );
+}
+
+void CBaseEntity::DestroyDataObject( int type )
+{
+ Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
+ if ( !HasDataObjectType( type ) )
+ return;
+ g_DataObjectAccessSystem.DestroyDataObject( type, this );
+ RemoveDataObjectType( type );
+}
+
+void CWatcherList::Init()
+{
+ m_list = g_WatcherList.CreateList();
+}
+
+CWatcherList::~CWatcherList()
+{
+ g_WatcherList.DestroyList( m_list );
+}
+
+int CWatcherList::GetCallbackObjects( IWatcherCallback **pList, int listMax )
+{
+ int index = 0;
+ unsigned short next = g_WatcherList.InvalidIndex();
+ for ( unsigned short node = g_WatcherList.Head( m_list ); node != g_WatcherList.InvalidIndex(); node = next )
+ {
+ next = g_WatcherList.Next( node );
+ watcher_t *pNode = &g_WatcherList.Element(node);
+ if ( pNode->hWatcher.Get() )
+ {
+ pList[index] = pNode->pWatcherCallback;
+ index++;
+ if ( index >= listMax )
+ {
+ Assert(0);
+ return index;
+ }
+ }
+ else
+ {
+ g_WatcherList.Remove( m_list, node );
+ }
+ }
+ return index;
+}
+
+void CWatcherList::NotifyPositionChanged( CBaseEntity *pEntity )
+{
+ IWatcherCallback *pCallbacks[1024]; // HACKHACK: Assumes this list is big enough
+ int count = GetCallbackObjects( pCallbacks, ARRAYSIZE(pCallbacks) );
+ for ( int i = 0; i < count; i++ )
+ {
+ IPositionWatcher *pWatcher = assert_cast<IPositionWatcher *>(pCallbacks[i]);
+ if ( pWatcher )
+ {
+ pWatcher->NotifyPositionChanged(pEntity);
+ }
+ }
+}
+
+void CWatcherList::NotifyVPhysicsStateChanged( IPhysicsObject *pPhysics, CBaseEntity *pEntity, bool bAwake )
+{
+ IWatcherCallback *pCallbacks[1024]; // HACKHACK: Assumes this list is big enough!
+ int count = GetCallbackObjects( pCallbacks, ARRAYSIZE(pCallbacks) );
+ for ( int i = 0; i < count; i++ )
+ {
+ IVPhysicsWatcher *pWatcher = assert_cast<IVPhysicsWatcher *>(pCallbacks[i]);
+ if ( pWatcher )
+ {
+ pWatcher->NotifyVPhysicsStateChanged(pPhysics, pEntity, bAwake);
+ }
+ }
+}
+
+unsigned short CWatcherList::Find( CBaseEntity *pEntity )
+{
+ unsigned short next = g_WatcherList.InvalidIndex();
+ for ( unsigned short node = g_WatcherList.Head( m_list ); node != g_WatcherList.InvalidIndex(); node = next )
+ {
+ next = g_WatcherList.Next( node );
+ watcher_t *pNode = &g_WatcherList.Element(node);
+ if ( pNode->hWatcher.Get() == pEntity )
+ {
+ return node;
+ }
+ }
+ return g_WatcherList.InvalidIndex();
+}
+
+void CWatcherList::RemoveWatcher( CBaseEntity *pEntity )
+{
+ unsigned short node = Find( pEntity );
+ if ( node != g_WatcherList.InvalidIndex() )
+ {
+ g_WatcherList.Remove( m_list, node );
+ }
+}
+
+
+void CWatcherList::AddToList( CBaseEntity *pWatcher )
+{
+ unsigned short node = Find( pWatcher );
+ if ( node == g_WatcherList.InvalidIndex() )
+ {
+ watcher_t watcher;
+ watcher.hWatcher = pWatcher;
+ // save this separately so we can use the EHANDLE to test for deletion
+ watcher.pWatcherCallback = dynamic_cast<IWatcherCallback *> (pWatcher);
+
+ if ( watcher.pWatcherCallback )
+ {
+ g_WatcherList.AddToTail( m_list, watcher );
+ }
+ }
+}
+
+static void AddWatcherToEntity( CBaseEntity *pWatcher, CBaseEntity *pEntity, int watcherType )
+{
+ CWatcherList *pList = (CWatcherList *)pEntity->GetDataObject(watcherType);
+ if ( !pList )
+ {
+ pList = ( CWatcherList * )pEntity->CreateDataObject( watcherType );
+ pList->Init();
+ }
+
+ pList->AddToList( pWatcher );
+}
+
+static void RemoveWatcherFromEntity( CBaseEntity *pWatcher, CBaseEntity *pEntity, int watcherType )
+{
+ CWatcherList *pList = (CWatcherList *)pEntity->GetDataObject(watcherType);
+ if ( pList )
+ {
+ pList->RemoveWatcher( pWatcher );
+ }
+}
+
+void WatchPositionChanges( CBaseEntity *pWatcher, CBaseEntity *pMovingEntity )
+{
+ AddWatcherToEntity( pWatcher, pMovingEntity, POSITIONWATCHER );
+}
+
+void RemovePositionWatcher( CBaseEntity *pWatcher, CBaseEntity *pMovingEntity )
+{
+ RemoveWatcherFromEntity( pWatcher, pMovingEntity, POSITIONWATCHER );
+}
+
+void ReportPositionChanged( CBaseEntity *pMovedEntity )
+{
+ CWatcherList *pList = (CWatcherList *)pMovedEntity->GetDataObject(POSITIONWATCHER);
+ if ( pList )
+ {
+ pList->NotifyPositionChanged( pMovedEntity );
+ }
+}
+
+void WatchVPhysicsStateChanges( CBaseEntity *pWatcher, CBaseEntity *pPhysicsEntity )
+{
+ AddWatcherToEntity( pWatcher, pPhysicsEntity, VPHYSICSWATCHER );
+}
+
+void RemoveVPhysicsStateWatcher( CBaseEntity *pWatcher, CBaseEntity *pPhysicsEntity )
+{
+ AddWatcherToEntity( pWatcher, pPhysicsEntity, VPHYSICSWATCHER );
+}
+
+void ReportVPhysicsStateChanged( IPhysicsObject *pPhysics, CBaseEntity *pEntity, bool bAwake )
+{
+ CWatcherList *pList = (CWatcherList *)pEntity->GetDataObject(VPHYSICSWATCHER);
+ if ( pList )
+ {
+ pList->NotifyVPhysicsStateChanged( pPhysics, pEntity, bAwake );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseEntity::DestroyAllDataObjects( void )
+{
+ int i;
+ for ( i = 0; i < NUM_DATAOBJECT_TYPES; i++ )
+ {
+ if ( HasDataObjectType( i ) )
+ {
+ DestroyDataObject( i );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// For debugging
+//-----------------------------------------------------------------------------
+
+#ifdef GAME_DLL
+
+void SpewLinks()
+{
+ int nCount = 0;
+ for ( CBaseEntity *pClass = gEntList.FirstEnt(); pClass != NULL; pClass = gEntList.NextEnt(pClass) )
+ {
+ if ( pClass /*&& !pClass->IsDormant()*/ )
+ {
+ touchlink_t *root = ( touchlink_t * )pClass->GetDataObject( TOUCHLINK );
+ if ( root )
+ {
+
+ // check if the edict is already in the list
+ for ( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
+ {
+ ++nCount;
+ Msg("[%d] (%d) Link %d (%s) -> %d (%s)\n", nCount, pClass->IsDormant(),
+ pClass->entindex(), pClass->GetClassname(),
+ link->entityTouched->entindex(), link->entityTouched->GetClassname() );
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Returns the actual gravity
+//-----------------------------------------------------------------------------
+static inline float GetActualGravity( CBaseEntity *pEnt )
+{
+ float ent_gravity = pEnt->GetGravity();
+ if ( ent_gravity == 0.0f )
+ {
+ ent_gravity = 1.0f;
+ }
+
+ return ent_gravity * GetCurrentGravity();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : inline touchlink_t
+//-----------------------------------------------------------------------------
+inline touchlink_t *AllocTouchLink( void )
+{
+ touchlink_t *link = (touchlink_t*)g_EdictTouchLinks.Alloc( sizeof(touchlink_t) );
+ if ( link )
+ {
+ ++linksallocated;
+ }
+ else
+ {
+ DevWarning( "AllocTouchLink: failed to allocate touchlink_t.\n" );
+ }
+
+ return link;
+}
+
+static touchlink_t *g_pNextLink = NULL;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *link -
+// Output : inline void
+//-----------------------------------------------------------------------------
+inline void FreeTouchLink( touchlink_t *link )
+{
+ if ( link )
+ {
+ if ( link == g_pNextLink )
+ {
+ g_pNextLink = link->nextLink;
+ }
+ --linksallocated;
+ link->prevLink = link->nextLink = NULL;
+ }
+
+ // Necessary to catch crashes
+ g_EdictTouchLinks.Free( link );
+}
+
+#ifdef STAGING_ONLY
+#ifndef CLIENT_DLL
+ConVar sv_groundlink_debug( "sv_groundlink_debug", "0", FCVAR_NONE, "Enable logging of alloc/free operations for debugging." );
+#endif
+#endif // STAGING_ONLY
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : inline groundlink_t
+//-----------------------------------------------------------------------------
+inline groundlink_t *AllocGroundLink( void )
+{
+ groundlink_t *link = (groundlink_t*)g_EntityGroundLinks.Alloc( sizeof(groundlink_t) );
+ if ( link )
+ {
+ ++groundlinksallocated;
+ }
+ else
+ {
+ DevMsg( "AllocGroundLink: failed to allocate groundlink_t.!!! groundlinksallocated=%d g_EntityGroundLinks.Count()=%d\n", groundlinksallocated, g_EntityGroundLinks.Count() );
+ }
+
+#ifdef STAGING_ONLY
+#ifndef CLIENT_DLL
+ if ( sv_groundlink_debug.GetBool() )
+ {
+ UTIL_LogPrintf( "Groundlink Alloc: %p at %d\n", link, groundlinksallocated );
+ }
+#endif
+#endif // STAGING_ONLY
+
+ return link;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *link -
+// Output : inline void
+//-----------------------------------------------------------------------------
+inline void FreeGroundLink( groundlink_t *link )
+{
+#ifdef STAGING_ONLY
+#ifndef CLIENT_DLL
+ if ( sv_groundlink_debug.GetBool() )
+ {
+ UTIL_LogPrintf( "Groundlink Free: %p at %d\n", link, groundlinksallocated );
+ }
+#endif
+#endif // STAGING_ONLY
+
+ if ( link )
+ {
+ --groundlinksallocated;
+ }
+
+ g_EntityGroundLinks.Free( link );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseEntity::IsCurrentlyTouching( void ) const
+{
+ if ( HasDataObjectType( TOUCHLINK ) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+static bool g_bCleanupDatObject = true;
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks to see if any entities that have been touching this one
+// have stopped touching it, and notify the entity if so.
+// Called at the end of a frame, after all the entities have run
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsCheckForEntityUntouch( void )
+{
+ Assert( g_pNextLink == NULL );
+
+ touchlink_t *link;
+
+ touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
+ if ( root )
+ {
+#ifdef PORTAL
+ CPortalTouchScope scope;
+#endif
+ bool saveCleanup = g_bCleanupDatObject;
+ g_bCleanupDatObject = false;
+
+ link = root->nextLink;
+ while ( link != root )
+ {
+ g_pNextLink = link->nextLink;
+
+ // these touchlinks are not polled. The ents are touching due to an outside
+ // system that will add/delete them as necessary (vphysics in this case)
+ if ( link->touchStamp == TOUCHSTAMP_EVENT_DRIVEN )
+ {
+ // refresh the touch call
+ PhysicsTouch( link->entityTouched );
+ }
+ else
+ {
+ // check to see if the touch stamp is up to date
+ if ( link->touchStamp != touchStamp )
+ {
+ // stamp is out of data, so entities are no longer touching
+ // remove self from other entities touch list
+ PhysicsNotifyOtherOfUntouch( this, link->entityTouched );
+
+ // remove other entity from this list
+ PhysicsRemoveToucher( this, link );
+ }
+ }
+
+ link = g_pNextLink;
+ }
+
+ g_bCleanupDatObject = saveCleanup;
+
+ // Nothing left in list, destroy root
+ if ( root->nextLink == root &&
+ root->prevLink == root )
+ {
+ DestroyDataObject( TOUCHLINK );
+ }
+ }
+
+ g_pNextLink = NULL;
+
+ SetCheckUntouch( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: notifies an entity than another touching entity has moved out of contact.
+// Input : *other - the entity to be acted upon
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsNotifyOtherOfUntouch( CBaseEntity *ent, CBaseEntity *other )
+{
+ if ( !other )
+ return;
+
+ // loop through ed's touch list, looking for the notifier
+ // remove and call untouch if found
+ touchlink_t *root = ( touchlink_t * )other->GetDataObject( TOUCHLINK );
+ if ( root )
+ {
+ touchlink_t *link = root->nextLink;
+ while ( link != root )
+ {
+ if ( link->entityTouched == ent )
+ {
+ PhysicsRemoveToucher( other, link );
+
+ // Check for complete removal
+ if ( g_bCleanupDatObject &&
+ root->nextLink == root &&
+ root->prevLink == root )
+ {
+ other->DestroyDataObject( TOUCHLINK );
+ }
+ return;
+ }
+
+ link = link->nextLink;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: removes a toucher from the list
+// Input : *link - the link to remove
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsRemoveToucher( CBaseEntity *otherEntity, touchlink_t *link )
+{
+ // Every start Touch gets a corresponding end touch
+ if ( (link->flags & FTOUCHLINK_START_TOUCH) &&
+ link->entityTouched != NULL &&
+ otherEntity != NULL )
+ {
+ otherEntity->EndTouch( link->entityTouched );
+ }
+
+ link->nextLink->prevLink = link->prevLink;
+ link->prevLink->nextLink = link->nextLink;
+
+ if ( DebugTouchlinks() )
+ Msg( "remove 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n", link, link->entityTouched->GetDebugName(), otherEntity->GetDebugName(), link->entityTouched->entindex(), otherEntity->entindex(), linksallocated, g_EdictTouchLinks.PeakCount() );
+ FreeTouchLink( link );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Clears all touches from the list
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsRemoveTouchedList( CBaseEntity *ent )
+{
+#ifdef PORTAL
+ CPortalTouchScope scope;
+#endif
+
+ touchlink_t *link, *nextLink;
+
+ touchlink_t *root = ( touchlink_t * )ent->GetDataObject( TOUCHLINK );
+ if ( root )
+ {
+ link = root->nextLink;
+ bool saveCleanup = g_bCleanupDatObject;
+ g_bCleanupDatObject = false;
+ while ( link && link != root )
+ {
+ nextLink = link->nextLink;
+
+ // notify the other entity that this ent has gone away
+ PhysicsNotifyOtherOfUntouch( ent, link->entityTouched );
+
+ // kill it
+ if ( DebugTouchlinks() )
+ Msg( "remove 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n", link, ent->GetDebugName(), link->entityTouched->GetDebugName(), ent->entindex(), link->entityTouched->entindex(), linksallocated, g_EdictTouchLinks.PeakCount() );
+ FreeTouchLink( link );
+ link = nextLink;
+ }
+
+ g_bCleanupDatObject = saveCleanup;
+ ent->DestroyDataObject( TOUCHLINK );
+ }
+
+ ent->touchStamp = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *other -
+// Output : groundlink_t
+//-----------------------------------------------------------------------------
+groundlink_t *CBaseEntity::AddEntityToGroundList( CBaseEntity *other )
+{
+ groundlink_t *link;
+
+ if ( this == other )
+ return NULL;
+
+ // check if the edict is already in the list
+ groundlink_t *root = ( groundlink_t * )GetDataObject( GROUNDLINK );
+ if ( root )
+ {
+ for ( link = root->nextLink; link != root; link = link->nextLink )
+ {
+ if ( link->entity == other )
+ {
+ // no more to do
+ return link;
+ }
+ }
+ }
+ else
+ {
+ root = ( groundlink_t * )CreateDataObject( GROUNDLINK );
+ root->prevLink = root->nextLink = root;
+ }
+
+ // entity is not in list, so it's a new touch
+ // add it to the touched list and then call the touch function
+
+ // build new link
+ link = AllocGroundLink();
+ if ( !link )
+ return NULL;
+
+ link->entity = other;
+ // add it to the list
+ link->nextLink = root->nextLink;
+ link->prevLink = root;
+ link->prevLink->nextLink = link;
+ link->nextLink->prevLink = link;
+
+ PhysicsStartGroundContact( other );
+
+ return link;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called whenever two entities come in contact
+// Input : *pentOther - the entity who it has touched
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsStartGroundContact( CBaseEntity *pentOther )
+{
+ if ( !pentOther )
+ return;
+
+ if ( !(IsMarkedForDeletion() || pentOther->IsMarkedForDeletion()) )
+ {
+ pentOther->StartGroundContact( this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: notifies an entity than another touching entity has moved out of contact.
+// Input : *other - the entity to be acted upon
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsNotifyOtherOfGroundRemoval( CBaseEntity *ent, CBaseEntity *other )
+{
+ if ( !other )
+ return;
+
+ // loop through ed's touch list, looking for the notifier
+ // remove and call untouch if found
+ groundlink_t *root = ( groundlink_t * )other->GetDataObject( GROUNDLINK );
+ if ( root )
+ {
+ groundlink_t *link = root->nextLink;
+ while ( link != root )
+ {
+ if ( link->entity == ent )
+ {
+ PhysicsRemoveGround( other, link );
+
+ if ( root->nextLink == root &&
+ root->prevLink == root )
+ {
+ other->DestroyDataObject( GROUNDLINK );
+ }
+ return;
+ }
+
+ link = link->nextLink;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: removes a toucher from the list
+// Input : *link - the link to remove
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsRemoveGround( CBaseEntity *other, groundlink_t *link )
+{
+ // Every start Touch gets a corresponding end touch
+ if ( link->entity != NULL )
+ {
+ CBaseEntity *linkEntity = link->entity;
+ CBaseEntity *otherEntity = other;
+ if ( linkEntity && otherEntity )
+ {
+ linkEntity->EndGroundContact( otherEntity );
+ }
+ }
+
+ link->nextLink->prevLink = link->prevLink;
+ link->prevLink->nextLink = link->nextLink;
+ FreeGroundLink( link );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: static method to remove ground list for an entity
+// Input : *ent -
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsRemoveGroundList( CBaseEntity *ent )
+{
+ groundlink_t *link, *nextLink;
+
+ groundlink_t *root = ( groundlink_t * )ent->GetDataObject( GROUNDLINK );
+ if ( root )
+ {
+ link = root->nextLink;
+ while ( link && link != root )
+ {
+ nextLink = link->nextLink;
+
+ // notify the other entity that this ent has gone away
+ PhysicsNotifyOtherOfGroundRemoval( ent, link->entity );
+
+ // kill it
+ FreeGroundLink( link );
+
+ link = nextLink;
+ }
+
+ ent->DestroyDataObject( GROUNDLINK );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame that two entities are touching
+// Input : *pentOther - the entity who it has touched
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsTouch( CBaseEntity *pentOther )
+{
+ if ( pentOther )
+ {
+ if ( !(IsMarkedForDeletion() || pentOther->IsMarkedForDeletion()) )
+ {
+ Touch( pentOther );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called whenever two entities come in contact
+// Input : *pentOther - the entity who it has touched
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsStartTouch( CBaseEntity *pentOther )
+{
+ if ( pentOther )
+ {
+ if ( !(IsMarkedForDeletion() || pentOther->IsMarkedForDeletion()) )
+ {
+ StartTouch( pentOther );
+ Touch( pentOther );
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Marks in an entity that it is touching another entity, and calls
+// it's Touch() function if it is a new touch.
+// Stamps the touch link with the new time so that when we check for
+// untouch we know things haven't changed.
+// Input : *other - entity that it is in contact with
+//-----------------------------------------------------------------------------
+touchlink_t *CBaseEntity::PhysicsMarkEntityAsTouched( CBaseEntity *other )
+{
+ touchlink_t *link;
+
+ if ( this == other )
+ return NULL;
+
+ // Entities in hierarchy should not interact
+ if ( (this->GetMoveParent() == other) || (this == other->GetMoveParent()) )
+ return NULL;
+
+ // check if either entity doesn't generate touch functions
+ if ( (GetFlags() | other->GetFlags()) & FL_DONTTOUCH )
+ return NULL;
+
+ // Pure triggers should not touch each other
+ if ( IsSolidFlagSet( FSOLID_TRIGGER ) && other->IsSolidFlagSet( FSOLID_TRIGGER ) )
+ {
+ if (!IsSolid() && !other->IsSolid())
+ return NULL;
+ }
+
+ // Don't do touching if marked for deletion
+ if ( other->IsMarkedForDeletion() )
+ {
+ return NULL;
+ }
+
+ if ( IsMarkedForDeletion() )
+ {
+ return NULL;
+ }
+
+#ifdef PORTAL
+ CPortalTouchScope scope;
+#endif
+
+ // check if the edict is already in the list
+ touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
+ if ( root )
+ {
+ for ( link = root->nextLink; link != root; link = link->nextLink )
+ {
+ if ( link->entityTouched == other )
+ {
+ // update stamp
+ link->touchStamp = touchStamp;
+
+ if ( !CBaseEntity::sm_bDisableTouchFuncs )
+ {
+ PhysicsTouch( other );
+ }
+
+ // no more to do
+ return link;
+ }
+ }
+ }
+ else
+ {
+ // Allocate the root object
+ root = ( touchlink_t * )CreateDataObject( TOUCHLINK );
+ root->nextLink = root->prevLink = root;
+ }
+
+ // entity is not in list, so it's a new touch
+ // add it to the touched list and then call the touch function
+
+ // build new link
+ link = AllocTouchLink();
+ if ( DebugTouchlinks() )
+ Msg( "add 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n", link, GetDebugName(), other->GetDebugName(), entindex(), other->entindex(), linksallocated, g_EdictTouchLinks.PeakCount() );
+ if ( !link )
+ return NULL;
+
+ link->touchStamp = touchStamp;
+ link->entityTouched = other;
+ link->flags = 0;
+ // add it to the list
+ link->nextLink = root->nextLink;
+ link->prevLink = root;
+ link->prevLink->nextLink = link;
+ link->nextLink->prevLink = link;
+
+ // non-solid entities don't get touched
+ bool bShouldTouch = (IsSolid() && !IsSolidFlagSet(FSOLID_VOLUME_CONTENTS)) || IsSolidFlagSet(FSOLID_TRIGGER);
+ if ( bShouldTouch && !other->IsSolidFlagSet(FSOLID_TRIGGER) )
+ {
+ link->flags |= FTOUCHLINK_START_TOUCH;
+ if ( !CBaseEntity::sm_bDisableTouchFuncs )
+ {
+ PhysicsStartTouch( other );
+ }
+ }
+
+ return link;
+}
+
+static trace_t g_TouchTrace;
+const trace_t &CBaseEntity::GetTouchTrace( void )
+{
+ return g_TouchTrace;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Marks the fact that two edicts are in contact
+// Input : *other - other entity
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsMarkEntitiesAsTouching( CBaseEntity *other, trace_t &trace )
+{
+ g_TouchTrace = trace;
+ PhysicsMarkEntityAsTouched( other );
+ other->PhysicsMarkEntityAsTouched( this );
+}
+
+void CBaseEntity::PhysicsMarkEntitiesAsTouchingEventDriven( CBaseEntity *other, trace_t &trace )
+{
+ g_TouchTrace = trace;
+ g_TouchTrace.m_pEnt = other;
+
+ touchlink_t *link;
+ link = this->PhysicsMarkEntityAsTouched( other );
+ if ( link )
+ {
+ // mark these links as event driven so they aren't untouched the next frame
+ // when the physics doesn't refresh them
+ link->touchStamp = TOUCHSTAMP_EVENT_DRIVEN;
+ }
+ g_TouchTrace.m_pEnt = this;
+ link = other->PhysicsMarkEntityAsTouched( this );
+ if ( link )
+ {
+ link->touchStamp = TOUCHSTAMP_EVENT_DRIVEN;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Two entities have touched, so run their touch functions
+// Input : *other -
+// *ptrace -
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsImpact( CBaseEntity *other, trace_t &trace )
+{
+ if ( !other )
+ {
+ return;
+ }
+
+ // If either of the entities is flagged to be deleted,
+ // don't call the touch functions
+ if ( ( GetFlags() | other->GetFlags() ) & FL_KILLME )
+ {
+ return;
+ }
+
+ PhysicsMarkEntitiesAsTouching( other, trace );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the mask of what is solid for the given entity
+// Output : unsigned int
+//-----------------------------------------------------------------------------
+unsigned int CBaseEntity::PhysicsSolidMaskForEntity( void ) const
+{
+ return MASK_SOLID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the water level + type
+//-----------------------------------------------------------------------------
+void CBaseEntity::UpdateWaterState()
+{
+ // FIXME: This computation is nonsensical for rigid child attachments
+ // Should we just grab the type + level of the parent?
+ // Probably for rigid children anyways...
+
+ // Compute the point to check for water state
+ Vector point;
+ CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &point );
+
+ SetWaterLevel( 0 );
+ SetWaterType( CONTENTS_EMPTY );
+ int cont = UTIL_PointContents (point);
+
+ if (( cont & MASK_WATER ) == 0)
+ return;
+
+ SetWaterType( cont );
+ SetWaterLevel( 1 );
+
+ // point sized entities are always fully submerged
+ if ( IsPointSized() )
+ {
+ SetWaterLevel( 3 );
+ }
+ else
+ {
+ // Check the exact center of the box
+ point[2] = WorldSpaceCenter().z;
+
+ int midcont = UTIL_PointContents (point);
+ if ( midcont & MASK_WATER )
+ {
+ // Now check where the eyes are...
+ SetWaterLevel( 2 );
+ point[2] = EyePosition().z;
+
+ int eyecont = UTIL_PointContents (point);
+ if ( eyecont & MASK_WATER )
+ {
+ SetWaterLevel( 3 );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if entity is in the water and applies any current to velocity
+// and sets appropriate water flags
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseEntity::PhysicsCheckWater( void )
+{
+ if (GetMoveParent())
+ return GetWaterLevel() > 1;
+
+ int cont = GetWaterType();
+
+ // If we're not in water + don't have a current, we're done
+ if ( ( cont & (MASK_WATER | MASK_CURRENT) ) != (MASK_WATER | MASK_CURRENT) )
+ return GetWaterLevel() > 1;
+
+ // Compute current direction
+ Vector v( 0, 0, 0 );
+ if ( cont & CONTENTS_CURRENT_0 )
+ {
+ v[0] += 1;
+ }
+ if ( cont & CONTENTS_CURRENT_90 )
+ {
+ v[1] += 1;
+ }
+ if ( cont & CONTENTS_CURRENT_180 )
+ {
+ v[0] -= 1;
+ }
+ if ( cont & CONTENTS_CURRENT_270 )
+ {
+ v[1] -= 1;
+ }
+ if ( cont & CONTENTS_CURRENT_UP )
+ {
+ v[2] += 1;
+ }
+ if ( cont & CONTENTS_CURRENT_DOWN )
+ {
+ v[2] -= 1;
+ }
+
+ // The deeper we are, the stronger the current.
+ Vector newBaseVelocity;
+ VectorMA (GetBaseVelocity(), 50.0*GetWaterLevel(), v, newBaseVelocity);
+ SetBaseVelocity( newBaseVelocity );
+
+ return GetWaterLevel() > 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Bounds velocity
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsCheckVelocity( void )
+{
+ Vector origin = GetAbsOrigin();
+ Vector vecAbsVelocity = GetAbsVelocity();
+
+ bool bReset = false;
+ for ( int i=0 ; i<3 ; i++ )
+ {
+ if ( IS_NAN(vecAbsVelocity[i]) )
+ {
+ Msg( "Got a NaN velocity on %s\n", GetClassname() );
+ vecAbsVelocity[i] = 0;
+ bReset = true;
+ }
+ if ( IS_NAN(origin[i]) )
+ {
+ Msg( "Got a NaN origin on %s\n", GetClassname() );
+ origin[i] = 0;
+ bReset = true;
+ }
+
+ if ( vecAbsVelocity[i] > sv_maxvelocity.GetFloat() )
+ {
+#ifdef _DEBUG
+ DevWarning( 2, "Got a velocity too high on %s\n", GetClassname() );
+#endif
+ vecAbsVelocity[i] = sv_maxvelocity.GetFloat();
+ bReset = true;
+ }
+ else if ( vecAbsVelocity[i] < -sv_maxvelocity.GetFloat() )
+ {
+#ifdef _DEBUG
+ DevWarning( 2, "Got a velocity too low on %s\n", GetClassname() );
+#endif
+ vecAbsVelocity[i] = -sv_maxvelocity.GetFloat();
+ bReset = true;
+ }
+ }
+
+ if (bReset)
+ {
+ SetAbsOrigin( origin );
+ SetAbsVelocity( vecAbsVelocity );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies gravity to falling objects
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsAddGravityMove( Vector &move )
+{
+ Vector vecAbsVelocity = GetAbsVelocity();
+
+ move.x = (vecAbsVelocity.x + GetBaseVelocity().x ) * gpGlobals->frametime;
+ move.y = (vecAbsVelocity.y + GetBaseVelocity().y ) * gpGlobals->frametime;
+
+ if ( GetFlags() & FL_ONGROUND )
+ {
+ move.z = GetBaseVelocity().z * gpGlobals->frametime;
+ return;
+ }
+
+ // linear acceleration due to gravity
+ float newZVelocity = vecAbsVelocity.z - GetActualGravity( this ) * gpGlobals->frametime;
+
+ move.z = ((vecAbsVelocity.z + newZVelocity) / 2.0 + GetBaseVelocity().z ) * gpGlobals->frametime;
+
+ Vector vecBaseVelocity = GetBaseVelocity();
+ vecBaseVelocity.z = 0.0f;
+ SetBaseVelocity( vecBaseVelocity );
+
+ vecAbsVelocity.z = newZVelocity;
+ SetAbsVelocity( vecAbsVelocity );
+
+ // Bound velocity
+ PhysicsCheckVelocity();
+}
+
+
+#define STOP_EPSILON 0.1
+//-----------------------------------------------------------------------------
+// Purpose: Slide off of the impacting object. Returns the blocked flags (1 = floor, 2 = step / wall)
+// Input : in -
+// normal -
+// out -
+// overbounce -
+// Output : int
+//-----------------------------------------------------------------------------
+int CBaseEntity::PhysicsClipVelocity( const Vector& in, const Vector& normal, Vector& out, float overbounce )
+{
+ float backoff;
+ float change;
+ float angle;
+ int i, blocked;
+
+ blocked = 0;
+
+ angle = normal[ 2 ];
+
+ if ( angle > 0 )
+ {
+ blocked |= 1; // floor
+ }
+ if ( !angle )
+ {
+ blocked |= 2; // step
+ }
+
+ backoff = DotProduct (in, normal) * overbounce;
+
+ for ( i=0 ; i<3 ; i++ )
+ {
+ change = normal[i]*backoff;
+ out[i] = in[i] - change;
+ if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
+ {
+ out[i] = 0;
+ }
+ }
+
+ return blocked;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseEntity::ResolveFlyCollisionBounce( trace_t &trace, Vector &vecVelocity, float flMinTotalElasticity )
+{
+#ifdef HL1_DLL
+ flMinTotalElasticity = 0.3f;
+#endif//HL1_DLL
+
+ // Get the impact surface's elasticity.
+ float flSurfaceElasticity;
+ physprops->GetPhysicsProperties( trace.surface.surfaceProps, NULL, NULL, NULL, &flSurfaceElasticity );
+
+ float flTotalElasticity = GetElasticity() * flSurfaceElasticity;
+ if ( flMinTotalElasticity > 0.9f )
+ {
+ flMinTotalElasticity = 0.9f;
+ }
+ flTotalElasticity = clamp( flTotalElasticity, flMinTotalElasticity, 0.9f );
+
+ // NOTE: A backoff of 2.0f is a reflection
+ Vector vecAbsVelocity;
+ PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, 2.0f );
+ vecAbsVelocity *= flTotalElasticity;
+
+ // Get the total velocity (player + conveyors, etc.)
+ VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
+ float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
+
+ // Stop if on ground.
+ if ( trace.plane.normal.z > 0.7f ) // Floor
+ {
+ // Verify that we have an entity.
+ CBaseEntity *pEntity = trace.m_pEnt;
+ Assert( pEntity );
+
+ // Are we on the ground?
+ if ( vecVelocity.z < ( GetActualGravity( this ) * gpGlobals->frametime ) )
+ {
+ vecAbsVelocity.z = 0.0f;
+
+ // Recompute speedsqr based on the new absvel
+ VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
+ flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
+ }
+
+ SetAbsVelocity( vecAbsVelocity );
+
+ if ( flSpeedSqr < ( 30 * 30 ) )
+ {
+ if ( pEntity->IsStandable() )
+ {
+ SetGroundEntity( pEntity );
+ }
+
+ // Reset velocities.
+ SetAbsVelocity( vec3_origin );
+ SetLocalAngularVelocity( vec3_angle );
+ }
+ else
+ {
+ Vector vecDelta = GetBaseVelocity() - vecAbsVelocity;
+ Vector vecBaseDir = GetBaseVelocity();
+ VectorNormalize( vecBaseDir );
+ float flScale = vecDelta.Dot( vecBaseDir );
+
+ VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecVelocity );
+ VectorMA( vecVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, GetBaseVelocity() * flScale, vecVelocity );
+ PhysicsPushEntity( vecVelocity, &trace );
+ }
+ }
+ else
+ {
+ // If we get *too* slow, we'll stick without ever coming to rest because
+ // we'll get pushed down by gravity faster than we can escape from the wall.
+ if ( flSpeedSqr < ( 30 * 30 ) )
+ {
+ // Reset velocities.
+ SetAbsVelocity( vec3_origin );
+ SetLocalAngularVelocity( vec3_angle );
+ }
+ else
+ {
+ SetAbsVelocity( vecAbsVelocity );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseEntity::ResolveFlyCollisionSlide( trace_t &trace, Vector &vecVelocity )
+{
+ // Get the impact surface's friction.
+ float flSurfaceFriction;
+ physprops->GetPhysicsProperties( trace.surface.surfaceProps, NULL, NULL, &flSurfaceFriction, NULL );
+
+ // A backoff of 1.0 is a slide.
+ float flBackOff = 1.0f;
+ Vector vecAbsVelocity;
+ PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, flBackOff );
+
+ if ( trace.plane.normal.z <= 0.7 ) // Floor
+ {
+ SetAbsVelocity( vecAbsVelocity );
+ return;
+ }
+
+ // Stop if on ground.
+ // Get the total velocity (player + conveyors, etc.)
+ VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
+ float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
+
+ // Verify that we have an entity.
+ CBaseEntity *pEntity = trace.m_pEnt;
+ Assert( pEntity );
+
+ // Are we on the ground?
+ if ( vecVelocity.z < ( GetActualGravity( this ) * gpGlobals->frametime ) )
+ {
+ vecAbsVelocity.z = 0.0f;
+
+ // Recompute speedsqr based on the new absvel
+ VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
+ flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
+ }
+ SetAbsVelocity( vecAbsVelocity );
+
+ if ( flSpeedSqr < ( 30 * 30 ) )
+ {
+ if ( pEntity->IsStandable() )
+ {
+ SetGroundEntity( pEntity );
+ }
+
+ // Reset velocities.
+ SetAbsVelocity( vec3_origin );
+ SetLocalAngularVelocity( vec3_angle );
+ }
+ else
+ {
+ vecAbsVelocity += GetBaseVelocity();
+ vecAbsVelocity *= ( 1.0f - trace.fraction ) * gpGlobals->frametime * flSurfaceFriction;
+ PhysicsPushEntity( vecAbsVelocity, &trace );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseEntity::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity )
+{
+ // Stop if on ground.
+ if ( trace.plane.normal.z > 0.7 ) // Floor
+ {
+ // Get the total velocity (player + conveyors, etc.)
+ VectorAdd( GetAbsVelocity(), GetBaseVelocity(), vecVelocity );
+
+ // Verify that we have an entity.
+ CBaseEntity *pEntity = trace.m_pEnt;
+ Assert( pEntity );
+
+ // Are we on the ground?
+ if ( vecVelocity.z < ( GetActualGravity( this ) * gpGlobals->frametime ) )
+ {
+ Vector vecAbsVelocity = GetAbsVelocity();
+ vecAbsVelocity.z = 0.0f;
+ SetAbsVelocity( vecAbsVelocity );
+ }
+
+ if ( pEntity->IsStandable() )
+ {
+ SetGroundEntity( pEntity );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Performs the collision resolution for fliers.
+//-----------------------------------------------------------------------------
+void CBaseEntity::PerformFlyCollisionResolution( trace_t &trace, Vector &move )
+{
+ switch( GetMoveCollide() )
+ {
+ case MOVECOLLIDE_FLY_CUSTOM:
+ {
+ ResolveFlyCollisionCustom( trace, move );
+ break;
+ }
+
+ case MOVECOLLIDE_FLY_BOUNCE:
+ {
+ ResolveFlyCollisionBounce( trace, move );
+ break;
+ }
+
+ case MOVECOLLIDE_FLY_SLIDE:
+ case MOVECOLLIDE_DEFAULT:
+ // NOTE: The default fly collision state is the same as a slide (for backward capatability).
+ {
+ ResolveFlyCollisionSlide( trace, move );
+ break;
+ }
+
+ default:
+ {
+ // Invalid MOVECOLLIDE_<type>
+ Assert( 0 );
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks if an object has passed into or out of water and sets water info, alters velocity, plays splash sounds, etc.
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsCheckWaterTransition( void )
+{
+ int oldcont = GetWaterType();
+ UpdateWaterState();
+ int cont = GetWaterType();
+
+ // We can exit right out if we're a child... don't bother with this...
+ if (GetMoveParent())
+ return;
+
+ if ( cont & MASK_WATER )
+ {
+ if (oldcont == CONTENTS_EMPTY)
+ {
+#ifndef CLIENT_DLL
+ Splash();
+#endif // !CLIENT_DLL
+
+ // just crossed into water
+ EmitSound( "BaseEntity.EnterWater" );
+
+ if ( !IsEFlagSet( EFL_NO_WATER_VELOCITY_CHANGE ) )
+ {
+ Vector vecAbsVelocity = GetAbsVelocity();
+ vecAbsVelocity[2] *= 0.5;
+ SetAbsVelocity( vecAbsVelocity );
+ }
+ }
+ }
+ else
+ {
+ if ( oldcont != CONTENTS_EMPTY )
+ {
+ // just crossed out of water
+ EmitSound( "BaseEntity.ExitWater" );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Computes new angles based on the angular velocity
+//-----------------------------------------------------------------------------
+void CBaseEntity::SimulateAngles( float flFrameTime )
+{
+ // move angles
+ QAngle angles;
+ VectorMA ( GetLocalAngles(), flFrameTime, GetLocalAngularVelocity(), angles );
+ SetLocalAngles( angles );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Toss, bounce, and fly movement. When onground, do nothing.
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsToss( void )
+{
+ trace_t trace;
+ Vector move;
+
+ PhysicsCheckWater();
+
+ // regular thinking
+ if ( !PhysicsRunThink() )
+ return;
+
+ // Moving upward, off the ground, or resting on a client/monster, remove FL_ONGROUND
+ if ( GetAbsVelocity()[2] > 0 || !GetGroundEntity() || !GetGroundEntity()->IsStandable() )
+ {
+ SetGroundEntity( NULL );
+ }
+
+ // Check to see if entity is on the ground at rest
+ if ( GetFlags() & FL_ONGROUND )
+ {
+ if ( VectorCompare( GetAbsVelocity(), vec3_origin ) )
+ {
+ // Clear rotation if not moving (even if on a conveyor)
+ SetLocalAngularVelocity( vec3_angle );
+ if ( VectorCompare( GetBaseVelocity(), vec3_origin ) )
+ return;
+ }
+ }
+
+ PhysicsCheckVelocity();
+
+ // add gravity
+ if ( GetMoveType() == MOVETYPE_FLYGRAVITY && !(GetFlags() & FL_FLY) )
+ {
+ PhysicsAddGravityMove( move );
+ }
+ else
+ {
+ // Base velocity is not properly accounted for since this entity will move again after the bounce without
+ // taking it into account
+ Vector vecAbsVelocity = GetAbsVelocity();
+ vecAbsVelocity += GetBaseVelocity();
+ VectorScale(vecAbsVelocity, gpGlobals->frametime, move);
+ PhysicsCheckVelocity( );
+ }
+
+ // move angles
+ SimulateAngles( gpGlobals->frametime );
+
+ // move origin
+ PhysicsPushEntity( move, &trace );
+
+#if !defined( CLIENT_DLL )
+ if ( VPhysicsGetObject() )
+ {
+ VPhysicsGetObject()->UpdateShadow( GetAbsOrigin(), vec3_angle, true, gpGlobals->frametime );
+ }
+#endif
+
+ PhysicsCheckVelocity();
+
+ if (trace.allsolid )
+ {
+ // entity is trapped in another solid
+ // UNDONE: does this entity needs to be removed?
+ SetAbsVelocity(vec3_origin);
+ SetLocalAngularVelocity(vec3_angle);
+ return;
+ }
+
+#if !defined( CLIENT_DLL )
+ if (IsEdictFree())
+ return;
+#endif
+
+ if (trace.fraction != 1.0f)
+ {
+ PerformFlyCollisionResolution( trace, move );
+ }
+
+ // check for in water
+ PhysicsCheckWaterTransition();
+}
+
+
+//-----------------------------------------------------------------------------
+// Simulation in local space of rigid children
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsRigidChild( void )
+{
+ VPROF("CBaseEntity::PhysicsRigidChild");
+ // NOTE: rigidly attached children do simulation in local space
+ // Collision impulses will be handled either not at all, or by
+ // forwarding the information to the highest move parent
+
+ Vector vecPrevOrigin = GetAbsOrigin();
+
+ // regular thinking
+ if ( !PhysicsRunThink() )
+ return;
+
+ VPROF_SCOPE_BEGIN("CBaseEntity::PhysicsRigidChild-2");
+
+#if !defined( CLIENT_DLL )
+ // Cause touch functions to be called
+ PhysicsTouchTriggers( &vecPrevOrigin );
+
+ // We have to do this regardless owing to hierarchy
+ if ( VPhysicsGetObject() )
+ {
+ int solidType = GetSolid();
+ bool bAxisAligned = ( solidType == SOLID_BBOX || solidType == SOLID_NONE ) ? true : false;
+ VPhysicsGetObject()->UpdateShadow( GetAbsOrigin(), bAxisAligned ? vec3_angle : GetAbsAngles(), true, gpGlobals->frametime );
+ }
+#endif
+
+ VPROF_SCOPE_END();
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the base velocity
+//-----------------------------------------------------------------------------
+void CBaseEntity::UpdateBaseVelocity( void )
+{
+#if !defined( CLIENT_DLL )
+ if ( GetFlags() & FL_ONGROUND )
+ {
+ CBaseEntity *groundentity = GetGroundEntity();
+ if ( groundentity )
+ {
+ // On conveyor belt that's moving?
+ if ( groundentity->GetFlags() & FL_CONVEYOR )
+ {
+ Vector vecNewBaseVelocity;
+ groundentity->GetGroundVelocityToApply( vecNewBaseVelocity );
+ if ( GetFlags() & FL_BASEVELOCITY )
+ {
+ vecNewBaseVelocity += GetBaseVelocity();
+ }
+ AddFlag( FL_BASEVELOCITY );
+ SetBaseVelocity( vecNewBaseVelocity );
+ }
+ }
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Runs a frame of physics for a specific edict (and all it's children)
+// Input : *ent - the thinking edict
+//-----------------------------------------------------------------------------
+void CBaseEntity::PhysicsSimulate( void )
+{
+ VPROF( "CBaseEntity::PhysicsSimulate" );
+ // NOTE: Players override PhysicsSimulate and drive through their CUserCmds at that point instead of
+ // processng through this function call!!! They shouldn't chain to here ever.
+ // Make sure not to simulate this guy twice per frame
+ if (m_nSimulationTick == gpGlobals->tickcount)
+ return;
+
+ m_nSimulationTick = gpGlobals->tickcount;
+
+ Assert( !IsPlayer() );
+
+ // If we've got a moveparent, we must simulate that first.
+ CBaseEntity *pMoveParent = GetMoveParent();
+
+ if ( (GetMoveType() == MOVETYPE_NONE && !pMoveParent) || (GetMoveType() == MOVETYPE_VPHYSICS ) )
+ {
+ PhysicsNone();
+ return;
+ }
+
+ // If ground entity goes away, make sure FL_ONGROUND is valid
+ if ( !GetGroundEntity() )
+ {
+ RemoveFlag( FL_ONGROUND );
+ }
+
+ if (pMoveParent)
+ {
+ VPROF( "CBaseEntity::PhysicsSimulate-MoveParent" );
+ pMoveParent->PhysicsSimulate();
+ }
+ else
+ {
+ VPROF( "CBaseEntity::PhysicsSimulate-BaseVelocity" );
+
+ UpdateBaseVelocity();
+
+ if ( ((GetFlags() & FL_BASEVELOCITY) == 0) && (GetBaseVelocity() != vec3_origin) )
+ {
+ // Apply momentum (add in half of the previous frame of velocity first)
+ // BUGBUG: This will break with PhysicsStep() because of the timestep difference
+ Vector vecAbsVelocity;
+ VectorMA( GetAbsVelocity(), 1.0 + (gpGlobals->frametime*0.5), GetBaseVelocity(), vecAbsVelocity );
+ SetAbsVelocity( vecAbsVelocity );
+ SetBaseVelocity( vec3_origin );
+ }
+ RemoveFlag( FL_BASEVELOCITY );
+ }
+
+ switch( GetMoveType() )
+ {
+ case MOVETYPE_PUSH:
+ {
+ VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_PUSH" );
+ PhysicsPusher();
+ }
+ break;
+
+
+ case MOVETYPE_VPHYSICS:
+ {
+ }
+ break;
+
+ case MOVETYPE_NONE:
+ {
+ VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_NONE" );
+ Assert(pMoveParent);
+ PhysicsRigidChild();
+ }
+ break;
+
+ case MOVETYPE_NOCLIP:
+ {
+ VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_NOCLIP" );
+ PhysicsNoclip();
+ }
+ break;
+
+ case MOVETYPE_STEP:
+ {
+ VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_STEP" );
+ PhysicsStep();
+ }
+ break;
+
+ case MOVETYPE_FLY:
+ case MOVETYPE_FLYGRAVITY:
+ {
+ VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_FLY" );
+ PhysicsToss();
+ }
+ break;
+
+ case MOVETYPE_CUSTOM:
+ {
+ VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_CUSTOM" );
+ PhysicsCustom();
+ }
+ break;
+
+ default:
+ Warning( "PhysicsSimulate: %s bad movetype %d", GetClassname(), GetMoveType() );
+ Assert(0);
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Runs thinking code if time. There is some play in the exact time the think
+// function will be called, because it is called before any movement is done
+// in a frame. Not used for pushmove objects, because they must be exact.
+// Returns false if the entity removed itself.
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseEntity::PhysicsRunThink( thinkmethods_t thinkMethod )
+{
+ if ( IsEFlagSet( EFL_NO_THINK_FUNCTION ) )
+ return true;
+
+ bool bAlive = true;
+
+ // Don't fire the base if we're avoiding it
+ if ( thinkMethod != THINK_FIRE_ALL_BUT_BASE )
+ {
+ bAlive = PhysicsRunSpecificThink( -1, &CBaseEntity::Think );
+ if ( !bAlive )
+ return false;
+ }
+
+ // Are we just firing the base think?
+ if ( thinkMethod == THINK_FIRE_BASE_ONLY )
+ return bAlive;
+
+ // Fire the rest of 'em
+ for ( int i = 0; i < m_aThinkFunctions.Count(); i++ )
+ {
+#ifdef _DEBUG
+ // Set the context
+ m_iCurrentThinkContext = i;
+#endif
+
+ bAlive = PhysicsRunSpecificThink( i, m_aThinkFunctions[i].m_pfnThink );
+
+#ifdef _DEBUG
+ // Clear our context
+ m_iCurrentThinkContext = NO_THINK_CONTEXT;
+#endif
+
+ if ( !bAlive )
+ return false;
+ }
+
+ return bAlive;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: For testing if all thinks are occuring at the same time
+//-----------------------------------------------------------------------------
+struct ThinkSync
+{
+ float thinktime;
+ int thinktick;
+ CUtlVector< EHANDLE > entities;
+
+ ThinkSync()
+ {
+ thinktime = 0;
+ }
+
+ ThinkSync( const ThinkSync& src )
+ {
+ thinktime = src.thinktime;
+ thinktick = src.thinktick;
+ int c = src.entities.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ entities.AddToTail( src.entities[ i ] );
+ }
+ }
+};
+
+#if !defined( CLIENT_DLL )
+static ConVar sv_thinktimecheck( "sv_thinktimecheck", "0", 0, "Check for thinktimes all on same timestamp." );
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: For testing if all thinks are occuring at the same time
+//-----------------------------------------------------------------------------
+class CThinkSyncTester
+{
+public:
+ CThinkSyncTester() :
+ m_Thinkers( 0, 0, ThinkLessFunc )
+ {
+ m_nLastFrameCount = -1;
+ m_bShouldCheck = false;
+ }
+
+ void EntityThinking( int framecount, CBaseEntity *ent, float thinktime, int thinktick )
+ {
+#if !defined( CLIENT_DLL )
+ if ( m_nLastFrameCount != framecount )
+ {
+ if ( m_bShouldCheck )
+ {
+ // Report
+ Report();
+ m_Thinkers.RemoveAll();
+ m_nLastFrameCount = framecount;
+ }
+
+ m_bShouldCheck = sv_thinktimecheck.GetBool();
+ }
+
+ if ( !m_bShouldCheck )
+ return;
+
+ ThinkSync *p = FindOrAddItem( ent, thinktime );
+ if ( !p )
+ {
+ Assert( 0 );
+ }
+
+ p->thinktime = thinktime;
+ p->thinktick = thinktick;
+ EHANDLE h;
+ h = ent;
+ p->entities.AddToTail( h );
+#endif
+ }
+
+private:
+
+ static bool ThinkLessFunc( const ThinkSync& item1, const ThinkSync& item2 )
+ {
+ return item1.thinktime < item2.thinktime;
+ }
+
+ ThinkSync *FindOrAddItem( CBaseEntity *ent, float thinktime )
+ {
+ ThinkSync item;
+ item.thinktime = thinktime;
+
+ int idx = m_Thinkers.Find( item );
+ if ( idx == m_Thinkers.InvalidIndex() )
+ {
+ idx = m_Thinkers.Insert( item );
+ }
+
+ return &m_Thinkers[ idx ];
+ }
+
+ void Report()
+ {
+ if ( m_Thinkers.Count() == 0 )
+ return;
+
+ Msg( "-----------------\nThink report frame %i\n", gpGlobals->tickcount );
+
+ for ( int i = m_Thinkers.FirstInorder();
+ i != m_Thinkers.InvalidIndex();
+ i = m_Thinkers.NextInorder( i ) )
+ {
+ ThinkSync *p = &m_Thinkers[ i ];
+ Assert( p );
+ if ( !p )
+ continue;
+
+ int ecount = p->entities.Count();
+ if ( !ecount )
+ {
+ continue;
+ }
+
+ Msg( "thinktime %f, %i entities\n", p->thinktime, ecount );
+ for ( int j =0; j < ecount; j++ )
+ {
+ EHANDLE h = p->entities[ j ];
+ int lastthinktick = 0;
+ int nextthinktick = 0;
+ CBaseEntity *e = h.Get();
+ if ( e )
+ {
+ lastthinktick = e->m_nLastThinkTick;
+ nextthinktick = e->m_nNextThinkTick;
+ }
+
+ Msg( " %p : %30s (last %5i/next %5i)\n", h.Get(), h.Get() ? h->GetClassname() : "NULL",
+ lastthinktick, nextthinktick );
+ }
+ }
+ }
+
+ CUtlRBTree< ThinkSync > m_Thinkers;
+ int m_nLastFrameCount;
+ bool m_bShouldCheck;
+};
+
+static CThinkSyncTester g_ThinkChecker;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseEntity::PhysicsRunSpecificThink( int nContextIndex, BASEPTR thinkFunc )
+{
+ int thinktick = GetNextThinkTick( nContextIndex );
+
+ if ( thinktick <= 0 || thinktick > gpGlobals->tickcount )
+ return true;
+
+ float thinktime = thinktick * TICK_INTERVAL;
+
+ // Don't let things stay in the past.
+ // it is possible to start that way
+ // by a trigger with a local time.
+ if ( thinktime < gpGlobals->curtime )
+ {
+ thinktime = gpGlobals->curtime;
+ }
+
+ // Only do this on the game server
+#if !defined( CLIENT_DLL )
+ g_ThinkChecker.EntityThinking( gpGlobals->tickcount, this, thinktime, m_nNextThinkTick );
+#endif
+
+ SetNextThink( nContextIndex, TICK_NEVER_THINK );
+
+ PhysicsDispatchThink( thinkFunc );
+
+ SetLastThink( nContextIndex, gpGlobals->curtime );
+
+ // Return whether entity is still valid
+ return ( !IsMarkedForDeletion() );
+}
+
+void CBaseEntity::SetGroundEntity( CBaseEntity *ground )
+{
+ if ( m_hGroundEntity.Get() == ground )
+ return;
+
+#ifdef GAME_DLL
+ // this can happen in-between updates to the held object controller (physcannon, +USE)
+ // so trap it here and release held objects when they become player ground
+ if ( ground && IsPlayer() && ground->GetMoveType()== MOVETYPE_VPHYSICS )
+ {
+ CBasePlayer *pPlayer = ToBasePlayer(this);
+ IPhysicsObject *pPhysGround = ground->VPhysicsGetObject();
+ if ( pPhysGround && pPlayer )
+ {
+ if ( pPhysGround->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
+ {
+ pPlayer->ForceDropOfCarriedPhysObjects( ground );
+ }
+ }
+ }
+#endif
+
+ CBaseEntity *oldGround = m_hGroundEntity;
+ m_hGroundEntity = ground;
+
+ // Just starting to touch
+ if ( !oldGround && ground )
+ {
+ ground->AddEntityToGroundList( this );
+ }
+ // Just stopping touching
+ else if ( oldGround && !ground )
+ {
+ PhysicsNotifyOtherOfGroundRemoval( this, oldGround );
+ }
+ // Changing out to new ground entity
+ else
+ {
+ PhysicsNotifyOtherOfGroundRemoval( this, oldGround );
+ ground->AddEntityToGroundList( this );
+ }
+
+ // HACK/PARANOID: This is redundant with the code above, but in case we get out of sync groundlist entries ever,
+ // this will force the appropriate flags
+ if ( ground )
+ {
+ AddFlag( FL_ONGROUND );
+ }
+ else
+ {
+ RemoveFlag( FL_ONGROUND );
+ }
+}
+
+CBaseEntity *CBaseEntity::GetGroundEntity( void )
+{
+ return m_hGroundEntity;
+}
+
+void CBaseEntity::StartGroundContact( CBaseEntity *ground )
+{
+ AddFlag( FL_ONGROUND );
+// Msg( "+++ %s starting contact with ground %s\n", GetClassname(), ground->GetClassname() );
+}
+
+void CBaseEntity::EndGroundContact( CBaseEntity *ground )
+{
+ RemoveFlag( FL_ONGROUND );
+// Msg( "--- %s ending contact with ground %s\n", GetClassname(), ground->GetClassname() );
+}
+
+
+void CBaseEntity::SetGroundChangeTime( float flTime )
+{
+ m_flGroundChangeTime = flTime;
+}
+
+float CBaseEntity::GetGroundChangeTime( void )
+{
+ return m_flGroundChangeTime;
+}
+
+
+
+// Remove this as ground entity for all object resting on this object
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseEntity::WakeRestingObjects()
+{
+ // Unset this as ground entity for everything resting on this object
+ // This calls endgroundcontact for everything on the list
+ PhysicsRemoveGroundList( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *ent -
+//-----------------------------------------------------------------------------
+bool CBaseEntity::HasNPCsOnIt( void )
+{
+ groundlink_t *link;
+ groundlink_t *root = ( groundlink_t * )GetDataObject( GROUNDLINK );
+ if ( root )
+ {
+ for ( link = root->nextLink; link != root; link = link->nextLink )
+ {
+ if ( link->entity && link->entity->MyNPCPointer() )
+ return true;
+ }
+ }
+
+ return false;
+}