//========= 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;
}
