//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Clones a physics object (usually with a matrix transform applied)
//
// $NoKeywords: $
//=============================================================================//

#include "cbase.h"
#include "physicsshadowclone.h"
#include "portal_util_shared.h"
#include "vphysics/object_hash.h"
#include "trains.h"
#include "props.h"
#include "model_types.h"
#include "portal/weapon_physcannon.h" //grab controllers

#include "PortalSimulation.h"

#define MAX_SHADOW_CLONE_COUNT 200

static int g_iShadowCloneCount = 0;
ConVar sv_debug_physicsshadowclones("sv_debug_physicsshadowclones", "0", FCVAR_REPLICATED );
ConVar sv_use_shadow_clones( "sv_use_shadow_clones", "1", FCVAR_REPLICATED | FCVAR_CHEAT ); //should we create shadow clones?

static void DrawDebugOverlayForShadowClone( CPhysicsShadowClone *pClone );

LINK_ENTITY_TO_CLASS( physicsshadowclone, CPhysicsShadowClone );

static CUtlVector<CPhysicsShadowClone *> s_ActiveShadowClones;
CUtlVector<CPhysicsShadowClone *> const &CPhysicsShadowClone::g_ShadowCloneList = s_ActiveShadowClones;
static bool s_IsShadowClone[MAX_EDICTS] = { false };

static CPhysicsShadowCloneLL *s_EntityClones[MAX_EDICTS] = { NULL };
struct ShadowCloneLLEntryManager
{
	CPhysicsShadowCloneLL m_ShadowCloneLLEntries[MAX_SHADOW_CLONE_COUNT];
	CPhysicsShadowCloneLL *m_pFreeShadowCloneLLEntries[MAX_SHADOW_CLONE_COUNT];
	int m_iUsedEntryIndex;

	ShadowCloneLLEntryManager( void )
	{
		m_iUsedEntryIndex = 0;
		for( int i = 0; i != MAX_SHADOW_CLONE_COUNT; ++i )
		{
			m_pFreeShadowCloneLLEntries[i] = &m_ShadowCloneLLEntries[i];
		}
	}

	inline CPhysicsShadowCloneLL *Alloc( void )
	{
		return m_pFreeShadowCloneLLEntries[m_iUsedEntryIndex++];
	}

	inline void Free( CPhysicsShadowCloneLL *pFree )
	{
		m_pFreeShadowCloneLLEntries[--m_iUsedEntryIndex] = pFree;
	}
};
static ShadowCloneLLEntryManager s_SCLLManager;


CPhysicsShadowClone::CPhysicsShadowClone( void )
{
	m_matrixShadowTransform.Identity();
	m_matrixShadowTransform_Inverse.Identity();
	m_bShadowTransformIsIdentity = true;
	s_ActiveShadowClones.AddToTail( this );
}

CPhysicsShadowClone::~CPhysicsShadowClone( void )
{
	VPhysicsDestroyObject();
	VPhysicsSetObject( NULL );
	m_hClonedEntity = NULL;
	s_ActiveShadowClones.FindAndRemove( this ); //also removed in UpdateOnRemove()
	Assert( s_IsShadowClone[entindex()] == true );
	s_IsShadowClone[entindex()] = false;
}

void CPhysicsShadowClone::UpdateOnRemove( void )
{
	CBaseEntity *pSource = m_hClonedEntity;
	if( pSource )
	{
		CPhysicsShadowCloneLL *pCloneListHead = s_EntityClones[pSource->entindex()];
		Assert( pCloneListHead != NULL );

		CPhysicsShadowCloneLL *pFind = pCloneListHead;
		CPhysicsShadowCloneLL *pLast = pFind;
		while( pFind->pClone != this )
		{
			pLast = pFind;
			Assert( pFind->pNext != NULL );
			pFind = pFind->pNext;
		}

		if( pFind == pCloneListHead )
		{
			s_EntityClones[pSource->entindex()] = pFind->pNext;
		}
		else
		{
			pLast->pNext = pFind->pNext;
		}
		s_SCLLManager.Free( pFind );
	}
#ifdef _DEBUG
	else
	{
		//verify that it didn't weasel into a list somewhere and get left behind
		for( int i = 0; i != MAX_SHADOW_CLONE_COUNT; ++i )
		{
			CPhysicsShadowCloneLL *pCloneSearch = s_EntityClones[i];
			while( pCloneSearch )
			{
				Assert( pCloneSearch->pClone != this );
				pCloneSearch = pCloneSearch->pNext;
			}
		}
	}
#endif
	VPhysicsDestroyObject();
	VPhysicsSetObject( NULL );
	m_hClonedEntity = NULL;
	s_ActiveShadowClones.FindAndRemove( this ); //also removed in Destructor
	BaseClass::UpdateOnRemove();
}

void CPhysicsShadowClone::Spawn( void )
{
	AddFlag( FL_DONTTOUCH );
	AddEffects( EF_NODRAW | EF_NOSHADOW | EF_NORECEIVESHADOW );

	FullSync( false );
	m_bInAssumedSyncState = false;
	
	BaseClass::Spawn();

	s_IsShadowClone[entindex()] = true;
}


void CPhysicsShadowClone::FullSync( bool bAllowAssumedSync )
{
	Assert( IsMarkedForDeletion() == false );

	CBaseEntity *pClonedEntity = m_hClonedEntity.Get();

	if( pClonedEntity == NULL )
	{
		AssertMsg( VPhysicsGetObject() != NULL, "Been linkless for more than this update, something should have killed this clone." );
		SetMoveType( MOVETYPE_NONE );
		SetSolid( SOLID_NONE );
		SetSolidFlags( 0 );
		SetCollisionGroup( COLLISION_GROUP_NONE );
		VPhysicsDestroyObject();
		return;
	}

	SetGroundEntity( NULL );

	bool bIsSynced = bAllowAssumedSync;
	bool bBigChanges = true; //assume there are, and be proven wrong

	if( bAllowAssumedSync )
	{
		IPhysicsObject *pSourceObjects[1024];
		int iObjectCount = pClonedEntity->VPhysicsGetObjectList( pSourceObjects, 1024 );

		//scan for really big differences that would definitely require a full sync
		bBigChanges = ( iObjectCount != m_CloneLinks.Count() );
		if( !bBigChanges )
		{
			for( int i = 0; i != iObjectCount; ++i )
			{
				IPhysicsObject *pSourcePhysics = pSourceObjects[i];
				IPhysicsObject *pClonedPhysics = m_CloneLinks[i].pClone;

				if( (pSourcePhysics != m_CloneLinks[i].pSource) || 
					(pSourcePhysics->IsCollisionEnabled() != pClonedPhysics->IsCollisionEnabled()) )
				{
					bBigChanges = true;
					bIsSynced = false;
					break;
				}

				Vector ptSourcePosition, ptClonePosition;
				pSourcePhysics->GetPosition( &ptSourcePosition, NULL );
				if( !m_bShadowTransformIsIdentity )
					ptSourcePosition = m_matrixShadowTransform * ptSourcePosition;

				pClonedPhysics->GetPosition( &ptClonePosition, NULL );

				if( (ptClonePosition - ptSourcePosition).LengthSqr() > 2500.0f )
				{
					bBigChanges = true;
					bIsSynced = false;
					break;
				}
				
				//Vector vSourceVelocity, vCloneVelocity;


				if( !pSourcePhysics->IsAsleep() ) //only allow full syncrosity if the source entity is entirely asleep
					bIsSynced = false;

				if( m_bInAssumedSyncState && !pClonedPhysics->IsAsleep() )
					bIsSynced = false;
			}
		}
		else
		{
			bIsSynced = false;
		}

		bIsSynced = false;

		if( bIsSynced )
		{
			//good enough to skip a full update
			if( !m_bInAssumedSyncState )
			{
				//do one last sync
				PartialSync( true );

				//if we don't do this, objects just fall out of the world (it happens, I swear)
				
				for( int i = m_CloneLinks.Count(); --i >= 0; )
				{
					if( (m_CloneLinks[i].pSource->GetShadowController() == NULL) && m_CloneLinks[i].pClone->IsMotionEnabled() )
					{
						//m_CloneLinks[i].pClone->SetVelocityInstantaneous( &vec3_origin, &vec3_origin );
						//m_CloneLinks[i].pClone->SetVelocity( &vec3_origin, &vec3_origin );
						m_CloneLinks[i].pClone->EnableGravity( false );
						m_CloneLinks[i].pClone->EnableMotion( false );
						m_CloneLinks[i].pClone->Sleep();
					}
				}

				m_bInAssumedSyncState = true;
			}
			
			if( sv_debug_physicsshadowclones.GetBool() )
				DrawDebugOverlayForShadowClone( this );

			return;
		}
	}
	
	m_bInAssumedSyncState = false;
	


	

	//past this point, we're committed to a broad update

	if( bBigChanges )
	{
		MoveType_t sourceMoveType = pClonedEntity->GetMoveType();

			
		IPhysicsObject *pPhysObject = pClonedEntity->VPhysicsGetObject();
		if( (sourceMoveType == MOVETYPE_CUSTOM) || 
			(sourceMoveType == MOVETYPE_STEP) || 
			(sourceMoveType == MOVETYPE_WALK) ||
			(pPhysObject && 
				( 
					(pPhysObject->GetGameFlags() & FVPHYSICS_PLAYER_HELD) || 
					(pPhysObject->GetShadowController() != NULL) 
				)
			)
		  )
		{
//#ifdef _DEBUG
			SetMoveType( MOVETYPE_NONE ); //to kill an assert
//#endif
			//PUSH should be used sparingly, you can't stand on a MOVETYPE_PUSH object :/
			SetMoveType( MOVETYPE_VPHYSICS, pClonedEntity->GetMoveCollide() ); //either an unclonable movetype, or a shadow/held object
		}
		/*else if(sourceMoveType == MOVETYPE_STEP)
		{
			//SetMoveType( MOVETYPE_NONE ); //to kill an assert
			SetMoveType( MOVETYPE_VPHYSICS, pClonedEntity->GetMoveCollide() );
		}*/
		else
		{
			//if( m_bShadowTransformIsIdentity )
				SetMoveType( sourceMoveType, pClonedEntity->GetMoveCollide() );
			//else
			//{
			//	SetMoveType( MOVETYPE_NONE ); //to kill an assert
			//	SetMoveType( MOVETYPE_PUSH, pClonedEntity->GetMoveCollide() );
			//}
		}

		SolidType_t sourceSolidType = pClonedEntity->GetSolid();
		if( sourceSolidType == SOLID_BBOX )
			SetSolid( SOLID_VPHYSICS );
		else
			SetSolid( sourceSolidType );
		//SetSolid( SOLID_VPHYSICS );

		SetElasticity( pClonedEntity->GetElasticity() );
		SetFriction( pClonedEntity->GetFriction() );


		
		int iSolidFlags = pClonedEntity->GetSolidFlags() | FSOLID_CUSTOMRAYTEST;
		if( m_bShadowTransformIsIdentity )
			iSolidFlags |= FSOLID_CUSTOMBOXTEST; //need this at least for the player or they get stuck in themselves
		else
			iSolidFlags &= ~FSOLID_FORCE_WORLD_ALIGNED;
		/*if( pClonedEntity->IsPlayer() )
		{
			iSolidFlags |= FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST;
		}*/

		SetSolidFlags( iSolidFlags );



		SetEffects( pClonedEntity->GetEffects() | (EF_NODRAW | EF_NOSHADOW | EF_NORECEIVESHADOW) );

		SetCollisionGroup( pClonedEntity->GetCollisionGroup() );

		SetModelIndex( pClonedEntity->GetModelIndex() );
		SetModelName( pClonedEntity->GetModelName() );

		if( modelinfo->GetModelType( pClonedEntity->GetModel() ) == mod_studio )
			SetModel( STRING( pClonedEntity->GetModelName() ) );


		CCollisionProperty *pClonedCollisionProp = pClonedEntity->CollisionProp();
		SetSize( pClonedCollisionProp->OBBMins(), pClonedCollisionProp->OBBMaxs() );
	}

	FullSyncClonedPhysicsObjects( bBigChanges );
	SyncEntity( true );

	if( bBigChanges )
		CollisionRulesChanged();

	if( sv_debug_physicsshadowclones.GetBool() )
		DrawDebugOverlayForShadowClone( this );
}

void CPhysicsShadowClone::SyncEntity( bool bPullChanges )
{
	m_bShouldUpSync = false;

	CBaseEntity *pSource, *pDest;
	VMatrix *pTransform;
	if( bPullChanges )
	{
		pSource = m_hClonedEntity.Get();
		pDest = this;
		pTransform = &m_matrixShadowTransform;

		if( pSource == NULL )
			return;
	}
	else
	{
		pSource = this;
		pDest = m_hClonedEntity.Get();
		pTransform = &m_matrixShadowTransform_Inverse;

		if( pDest == NULL )
			return;
	}


	Vector ptOrigin, vVelocity;
	QAngle qAngles;

	ptOrigin = pSource->GetAbsOrigin();
	qAngles = pSource->GetAbsAngles();
	vVelocity = pSource->GetAbsVelocity();

	if( !m_bShadowTransformIsIdentity )
	{
		ptOrigin = (*pTransform) * ptOrigin;
		qAngles = TransformAnglesToWorldSpace( qAngles, pTransform->As3x4() );
		vVelocity = pTransform->ApplyRotation( vVelocity );	
	}
	//else
	//{
	//	pDest->SetGroundEntity( pSource->GetGroundEntity() );
	//}

	if( (ptOrigin != pDest->GetAbsOrigin()) || (qAngles != pDest->GetAbsAngles()) )
	{
		pDest->Teleport( &ptOrigin, &qAngles, NULL );
	}
	
	if( vVelocity != pDest->GetAbsVelocity() )
	{
		//pDest->IncrementInterpolationFrame();
		pDest->SetAbsVelocity( vec3_origin ); //the two step process helps, I don't know why, but it does
		pDest->ApplyAbsVelocityImpulse( vVelocity );
	}
}


static void FullSyncPhysicsObject( IPhysicsObject *pSource, IPhysicsObject *pDest, const VMatrix *pTransform, bool bTeleport )
{
	CGrabController *pGrabController = NULL;

	if( !pSource->IsAsleep() )
		pDest->Wake();

	float fSavedMass = 0.0f, fSavedRotationalDamping; //setting mass to 0.0f purely to kill a warning that I can't seem to kill with pragmas
	if( pSource->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
	{
		//CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
		//Assert( pPlayer );

		CBaseEntity *pLookingForEntity = (CBaseEntity *)pSource->GetGameData();

		CBasePlayer *pHoldingPlayer = GetPlayerHoldingEntity( pLookingForEntity );
		if( pHoldingPlayer )
		{
			pGrabController = GetGrabControllerForPlayer( pHoldingPlayer );

			if ( !pGrabController )
				pGrabController = GetGrabControllerForPhysCannon( pHoldingPlayer->GetActiveWeapon() );
		}

		AssertMsg( pGrabController, "Physics object is held, but we can't find the holding controller." );
		GetSavedParamsForCarriedPhysObject( pGrabController, pSource, &fSavedMass, &fSavedRotationalDamping );
	}

	//Boiler plate
	{
		pDest->SetGameIndex( pSource->GetGameIndex() ); //what's it do?
		pDest->SetCallbackFlags( pSource->GetCallbackFlags() ); //wise?
		pDest->SetGameFlags( pSource->GetGameFlags() | FVPHYSICS_NO_SELF_COLLISIONS | FVPHYSICS_IS_SHADOWCLONE );
		pDest->SetMaterialIndex( pSource->GetMaterialIndex() );
		pDest->SetContents( pSource->GetContents() );

		pDest->EnableCollisions( pSource->IsCollisionEnabled() );
		pDest->EnableGravity( pSource->IsGravityEnabled() );
		pDest->EnableDrag( pSource->IsDragEnabled() );
		pDest->EnableMotion( pSource->IsMotionEnabled() );
	}	

	//Damping
	{
		float fSpeedDamp, fRotDamp;
		if( pGrabController )
		{
			pSource->GetDamping( &fSpeedDamp, NULL );
			pDest->SetDamping( &fSpeedDamp, &fSavedRotationalDamping );
		}
		else
		{
			pSource->GetDamping( &fSpeedDamp, &fRotDamp );
			pDest->SetDamping( &fSpeedDamp, &fRotDamp );
		}		
	}

	//stuff that we really care about
	{
		if( pGrabController )
			pDest->SetMass( fSavedMass );
		else
			pDest->SetMass( pSource->GetMass() );

		Vector ptOrigin, vVelocity, vAngularVelocity, vInertia;
		QAngle qAngles;

		pSource->GetPosition( &ptOrigin, &qAngles );
		pSource->GetVelocity( &vVelocity, &vAngularVelocity );
		vInertia = pSource->GetInertia();

		if( pTransform )
		{
#if 0
			pDest->SetPositionMatrix( pTransform->As3x4(), true ); //works like we think?
#else		
			ptOrigin = (*pTransform) * ptOrigin;
			qAngles = TransformAnglesToWorldSpace( qAngles, pTransform->As3x4() );
			vVelocity = pTransform->ApplyRotation( vVelocity );
			vAngularVelocity = pTransform->ApplyRotation( vAngularVelocity );
#endif
		}

		//avoid oversetting variables (I think that even setting them to the same value they already are disrupts the delicate physics balance)
		if( vInertia != pDest->GetInertia() )		
			pDest->SetInertia( vInertia );

		Vector ptDestOrigin, vDestVelocity, vDestAngularVelocity;
		QAngle qDestAngles;
		pDest->GetPosition( &ptDestOrigin, &qDestAngles );

		if( (ptOrigin != ptDestOrigin) || (qAngles != qDestAngles) )
			pDest->SetPosition( ptOrigin, qAngles, bTeleport );

		//pDest->SetVelocityInstantaneous( &vec3_origin, &vec3_origin );
		//pDest->Sleep();

		pDest->GetVelocity( &vDestVelocity, &vDestAngularVelocity );

		if( (vVelocity != vDestVelocity) || (vAngularVelocity != vDestAngularVelocity) )
			pDest->SetVelocityInstantaneous( &vVelocity, &vAngularVelocity );

		IPhysicsShadowController *pSourceController = pSource->GetShadowController();
		if( pSourceController == NULL )
		{
			if( pDest->GetShadowController() != NULL )
			{
				//we don't need a shadow controller anymore
				pDest->RemoveShadowController();
			}
		}
		else
		{
			IPhysicsShadowController *pDestController = pDest->GetShadowController();
			if( pDestController == NULL )
			{
				//we need a shadow controller
				float fMaxSpeed, fMaxAngularSpeed;
				pSourceController->GetMaxSpeed( &fMaxSpeed, &fMaxAngularSpeed );

				pDest->SetShadow( fMaxSpeed, fMaxAngularSpeed, pSourceController->AllowsTranslation(), pSourceController->AllowsRotation() );
				pDestController = pDest->GetShadowController();
				pDestController->SetTeleportDistance( pSourceController->GetTeleportDistance() );
				pDestController->SetPhysicallyControlled( pSourceController->IsPhysicallyControlled() );
			}

			//sync shadow controllers
			float fTimeOffset;
			Vector ptTargetPosition;
			QAngle qTargetAngles;
			fTimeOffset = pSourceController->GetTargetPosition( &ptTargetPosition, &qTargetAngles );

			if( pTransform )
			{
				ptTargetPosition = (*pTransform) * ptTargetPosition;
				qTargetAngles = TransformAnglesToWorldSpace( qTargetAngles, pTransform->As3x4() );
			}

			pDestController->Update( ptTargetPosition, qTargetAngles, fTimeOffset );
		}

		
	}

	//pDest->RecheckContactPoints();
}

static void PartialSyncPhysicsObject( IPhysicsObject *pSource, IPhysicsObject *pDest, const VMatrix *pTransform )
{
	Vector ptOrigin, vVelocity, vAngularVelocity, vInertia;
	QAngle qAngles;

	pSource->GetPosition( &ptOrigin, &qAngles );
	pSource->GetVelocity( &vVelocity, &vAngularVelocity );
	vInertia = pSource->GetInertia();

	if( pTransform )
	{
#if 0
		//pDest->SetPositionMatrix( matTransform.As3x4(), true ); //works like we think?
#else	
		ptOrigin = (*pTransform) * ptOrigin;
		qAngles = TransformAnglesToWorldSpace( qAngles, pTransform->As3x4() );
		vVelocity = pTransform->ApplyRotation( vVelocity );
		vAngularVelocity = pTransform->ApplyRotation( vAngularVelocity );
#endif
	}

	//avoid oversetting variables (I think that even setting them to the same value they already are disrupts the delicate physics balance)
	if( vInertia != pDest->GetInertia() )
		pDest->SetInertia( vInertia );

	Vector ptDestOrigin, vDestVelocity, vDestAngularVelocity;
	QAngle qDestAngles;
	pDest->GetPosition( &ptDestOrigin, &qDestAngles );
	pDest->GetVelocity( &vDestVelocity, &vDestAngularVelocity );


	if( (ptOrigin != ptDestOrigin) || (qAngles != qDestAngles) )
		pDest->SetPosition( ptOrigin, qAngles, false );

	if( (vVelocity != vDestVelocity) || (vAngularVelocity != vDestAngularVelocity) )
		pDest->SetVelocity( &vVelocity, &vAngularVelocity );

	pDest->EnableCollisions( pSource->IsCollisionEnabled() );
}



void CPhysicsShadowClone::FullSyncClonedPhysicsObjects( bool bTeleport )
{
	CBaseEntity *pClonedEntity = m_hClonedEntity.Get();
	if( pClonedEntity == NULL )
	{
		VPhysicsDestroyObject();
		return;
	}

	VMatrix *pTransform;
	if( m_bShadowTransformIsIdentity )
		pTransform = NULL;
	else
		pTransform = &m_matrixShadowTransform;

	IPhysicsObject *(pSourceObjects[1024]);
	int iObjectCount = pClonedEntity->VPhysicsGetObjectList( pSourceObjects, 1024 );

	//easy out if nothing has changed
	if( iObjectCount == m_CloneLinks.Count() )
	{
		int i;
		for( i = 0; i != iObjectCount; ++i )
		{
			if( pSourceObjects[i] == NULL )
				break;

			if( pSourceObjects[i] != m_CloneLinks[i].pSource )
				break;			
		}

		if( i == iObjectCount ) //no changes
		{
			for( i = 0; i != iObjectCount; ++i )
				FullSyncPhysicsObject( m_CloneLinks[i].pSource, m_CloneLinks[i].pClone, pTransform, bTeleport );

			return;
		}
	}



	//copy the existing list of clone links to a temp array, we're going to be starting from scratch and copying links as we need them
	PhysicsObjectCloneLink_t *pExistingLinks = NULL;
	int iExistingLinkCount = m_CloneLinks.Count();
	if( iExistingLinkCount != 0 )
	{
		pExistingLinks = (PhysicsObjectCloneLink_t *)stackalloc( sizeof(PhysicsObjectCloneLink_t) * m_CloneLinks.Count() );
		memcpy( pExistingLinks, m_CloneLinks.Base(), sizeof(PhysicsObjectCloneLink_t) * m_CloneLinks.Count() );
	}
	m_CloneLinks.RemoveAll();

	//now, go over the object list we just got from the source entity, and either copy or create links as necessary
	int i;
	for( i = 0; i != iObjectCount; ++i )
	{
		IPhysicsObject *pSource = pSourceObjects[i];

		if( pSource == NULL ) //this really shouldn't happen, but it does >_<
			continue;

		PhysicsObjectCloneLink_t cloneLink;

		int j;
		for( j = 0; j != iExistingLinkCount; ++j )
		{
			if( pExistingLinks[j].pSource == pSource )
				break;
		}

		if( j != iExistingLinkCount )
		{
			//copyable link found
			cloneLink = pExistingLinks[j];
			memset( &pExistingLinks[j], 0, sizeof( PhysicsObjectCloneLink_t ) ); //zero out this slot so we don't destroy it in cleanup
		}
		else
		{
			//no link found to copy, create a new one
			cloneLink.pSource = pSource;

			//apparently some collision code gets called on creation before we've set extra game flags, so we're going to cheat a bit and temporarily set our extra flags on the source
			unsigned int iOldGameFlags = pSource->GetGameFlags();
			pSource->SetGameFlags( iOldGameFlags | FVPHYSICS_IS_SHADOWCLONE );

			unsigned int size = physenv->GetObjectSerializeSize(pSource);
			byte *pBuffer = (byte *)stackalloc(size);
			memset( pBuffer, 0, size );

			physenv->SerializeObjectToBuffer( pSource, pBuffer, size ); //this should work across physics environments because the serializer doesn't write anything about itself to the template
			pSource->SetGameFlags( iOldGameFlags );
			cloneLink.pClone = m_pOwnerPhysEnvironment->UnserializeObjectFromBuffer( this, pBuffer, size, false ); //unserializer has to be in the target environment
			assert( cloneLink.pClone ); //there should be absolutely no case where we can't clone a valid existing physics object
	
			stackfree(pBuffer);
		}

		FullSyncPhysicsObject( cloneLink.pSource, cloneLink.pClone, pTransform, bTeleport );

		//cloneLink.pClone->Wake();

		m_CloneLinks.AddToTail( cloneLink );
	}


	//now go over the existing links, if any of them haven't been nullified, they need to be deleted
	for( i = 0; i != iExistingLinkCount; ++i )
	{
		if( pExistingLinks[i].pClone )
			m_pOwnerPhysEnvironment->DestroyObject(	pExistingLinks[i].pClone ); //also destroys shadow controller
	}


	VPhysicsSetObject( NULL );

	IPhysicsObject *pSource = m_hClonedEntity->VPhysicsGetObject();

	for( i = m_CloneLinks.Count(); --i >= 0; )
	{
		if( m_CloneLinks[i].pSource == pSource )
		{
			//m_CloneLinks[i].pClone->Wake();
			VPhysicsSetObject( m_CloneLinks[i].pClone );
			break;
		}
	}

	if( (i < 0) && (m_CloneLinks.Count() != 0) )
	{
		VPhysicsSetObject( m_CloneLinks[0].pClone );
	}

	stackfree( pExistingLinks );

	//CollisionRulesChanged();
}



void CPhysicsShadowClone::PartialSync( bool bPullChanges )
{
	VMatrix *pTransform;
	
	if( bPullChanges )
	{
		if( m_bShadowTransformIsIdentity )
			pTransform = NULL;
		else
			pTransform = &m_matrixShadowTransform;

		for( int i = m_CloneLinks.Count(); --i >= 0; )
			PartialSyncPhysicsObject( m_CloneLinks[i].pSource, m_CloneLinks[i].pClone, pTransform );
	}
	else
	{
		if( m_bShadowTransformIsIdentity )
			pTransform = NULL;
		else
			pTransform = &m_matrixShadowTransform_Inverse;

		for( int i = m_CloneLinks.Count(); --i >= 0; )
			PartialSyncPhysicsObject( m_CloneLinks[i].pClone, m_CloneLinks[i].pSource, pTransform );
	}

	SyncEntity( bPullChanges );
}



int CPhysicsShadowClone::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax )
{
	int iCountStop = m_CloneLinks.Count();
	if( iCountStop > listMax ) 
		iCountStop = listMax;

	for( int i = 0; i != iCountStop; ++i, ++pList )
		*pList = m_CloneLinks[i].pClone;

	return iCountStop;
}


void CPhysicsShadowClone::VPhysicsDestroyObject( void )
{
	VPhysicsSetObject( NULL );
	
	for( int i = m_CloneLinks.Count(); --i >= 0; )
	{
		Assert( m_CloneLinks[i].pClone != NULL );
		m_pOwnerPhysEnvironment->DestroyObject(	m_CloneLinks[i].pClone );
	}
	m_CloneLinks.RemoveAll();

	SetMoveType( MOVETYPE_NONE );
	SetSolid( SOLID_NONE );
	SetSolidFlags( 0 );
	SetCollisionGroup( COLLISION_GROUP_NONE );

	BaseClass::VPhysicsDestroyObject();
}






bool CPhysicsShadowClone::ShouldCollide( int collisionGroup, int contentsMask ) const
{
	CBaseEntity *pClonedEntity = m_hClonedEntity.Get();

	if( pClonedEntity )
		return pClonedEntity->ShouldCollide( collisionGroup, contentsMask );
	else
		return false;
}

bool CPhysicsShadowClone::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& trace )
{
	return false;

	/*CBaseEntity *pSourceEntity = m_hClonedEntity.Get();
	if( pSourceEntity == NULL )
		return false;

	enginetrace->ClipRayToEntity( ray, fContentsMask, pSourceEntity, &trace );
	return trace.DidHit();*/
}

int	CPhysicsShadowClone::ObjectCaps( void )
{
	return ((BaseClass::ObjectCaps() | FCAP_DONT_SAVE) & ~(FCAP_FORCE_TRANSITION | FCAP_ACROSS_TRANSITION | FCAP_MUST_SPAWN | FCAP_SAVE_NON_NETWORKABLE));
}





void CPhysicsShadowClone::SetCloneTransformationMatrix( const matrix3x4_t &sourceMatrix )
{
	m_matrixShadowTransform = sourceMatrix;
	m_bShadowTransformIsIdentity = m_matrixShadowTransform.IsIdentity();

	if( m_matrixShadowTransform.InverseGeneral( m_matrixShadowTransform_Inverse ) == false )
	{
		m_matrixShadowTransform.InverseTR( m_matrixShadowTransform_Inverse ); //probably not the right matrix, but we're out of options
	}

	FullSync();
	//PartialSync( true );
}






void CPhysicsShadowClone::SetClonedEntity( EHANDLE hEntToClone )
{
	VPhysicsDestroyObject();
	
	m_hClonedEntity = hEntToClone;

	//FullSyncClonedPhysicsObjects();
}

EHANDLE CPhysicsShadowClone::GetClonedEntity( void )
{
	return m_hClonedEntity;
}




//damage relays to source entity
bool CPhysicsShadowClone::PassesDamageFilter( const CTakeDamageInfo &info )
{
	CBaseEntity *pClonedEntity = m_hClonedEntity.Get();

	if( pClonedEntity )
		return pClonedEntity->PassesDamageFilter( info );
	else
		return BaseClass::PassesDamageFilter( info );
}

bool CPhysicsShadowClone::CanBeHitByMeleeAttack( CBaseEntity *pAttacker )
{
	CBaseEntity *pClonedEntity = m_hClonedEntity.Get();

	if( pClonedEntity )
		return pClonedEntity->CanBeHitByMeleeAttack( pAttacker );
	else
		return BaseClass::CanBeHitByMeleeAttack( pAttacker );
}

int CPhysicsShadowClone::OnTakeDamage( const CTakeDamageInfo &info )
{
	CBaseEntity *pClonedEntity = m_hClonedEntity.Get();

	if( pClonedEntity )
		return pClonedEntity->OnTakeDamage( info );
	else
		return BaseClass::OnTakeDamage( info );
}

int CPhysicsShadowClone::TakeHealth( float flHealth, int bitsDamageType )
{
	CBaseEntity *pClonedEntity = m_hClonedEntity.Get();

	if( pClonedEntity )
		return pClonedEntity->TakeHealth( flHealth, bitsDamageType );
	else
		return BaseClass::TakeHealth( flHealth, bitsDamageType );
}

void CPhysicsShadowClone::Event_Killed( const CTakeDamageInfo &info )
{
	CBaseEntity *pClonedEntity = m_hClonedEntity.Get();

	if( pClonedEntity )
		pClonedEntity->Event_Killed( info );
	else
		BaseClass::Event_Killed( info );
}

CPhysicsShadowClone *CPhysicsShadowClone::CreateShadowClone( IPhysicsEnvironment *pInPhysicsEnvironment, EHANDLE hEntToClone, const char *szDebugMarker, const matrix3x4_t *pTransformationMatrix /*= NULL*/ )
{
	AssertMsg( szDebugMarker != NULL, "All shadow clones must have a debug marker for where it came from in debug builds." );

	if( !sv_use_shadow_clones.GetBool() )
		return NULL;

	CBaseEntity *pClonedEntity = hEntToClone.Get();
	if( pClonedEntity == NULL )
		return NULL;

	AssertMsg( IsShadowClone( pClonedEntity ) == false, "Shouldn't attempt to clone clones" );

	if( pClonedEntity->IsMarkedForDeletion() )
		return NULL;

	//if( pClonedEntity->IsPlayer() )
	//	return NULL;

	IPhysicsObject *pPhysics = pClonedEntity->VPhysicsGetObject();

	if( pPhysics == NULL )
		return NULL;

	if( pPhysics->IsStatic() )
		return NULL;

	if( pClonedEntity->GetSolid() == SOLID_BSP )
		return NULL;

	if( pClonedEntity->GetSolidFlags() & (FSOLID_NOT_SOLID | FSOLID_TRIGGER) )
		return NULL;

	if( pClonedEntity->GetFlags() & (FL_WORLDBRUSH | FL_STATICPROP) )
		return NULL;

	/*if( FClassnameIs( pClonedEntity, "func_door" ) )
	{
		//only clone func_door's that are in front of the portal
		
		return NULL;
	}*/

	// Too many shadow clones breaks the game (too many entities)
	if( g_iShadowCloneCount >= MAX_SHADOW_CLONE_COUNT )
	{
		AssertMsg( false, "Too many shadow clones, consider upping the limit or reducing the level's physics props" );
		return NULL;
	}
	++g_iShadowCloneCount;

	CPhysicsShadowClone *pClone = (CPhysicsShadowClone*)CreateEntityByName("physicsshadowclone");
	s_IsShadowClone[pClone->entindex()] = true;
	pClone->m_pOwnerPhysEnvironment = pInPhysicsEnvironment;
	pClone->m_hClonedEntity = hEntToClone;
	DBG_CODE_NOSCOPE( pClone->m_szDebugMarker = szDebugMarker; );

	CPhysicsShadowCloneLL *pCloneLLEntry = s_SCLLManager.Alloc();
	pCloneLLEntry->pClone = pClone;
	pCloneLLEntry->pNext = s_EntityClones[pClonedEntity->entindex()];
	s_EntityClones[pClonedEntity->entindex()] = pCloneLLEntry;

	if( pTransformationMatrix )
	{
		pClone->m_matrixShadowTransform = *pTransformationMatrix;
		pClone->m_bShadowTransformIsIdentity = pClone->m_matrixShadowTransform.IsIdentity();

		if( !pClone->m_bShadowTransformIsIdentity )
		{
			if( pClone->m_matrixShadowTransform.InverseGeneral( pClone->m_matrixShadowTransform_Inverse ) == false )
			{
				pClone->m_matrixShadowTransform.InverseTR( pClone->m_matrixShadowTransform_Inverse ); //probably not the right matrix, but we're out of options
			}
		}
	}

	DispatchSpawn( pClone );

	return pClone;
}

void CPhysicsShadowClone::Free( void )
{
	VPhysicsDestroyObject();

	UTIL_Remove( this );

	//Too many shadow clones breaks the game (too many entities)
	--g_iShadowCloneCount;
}


void CPhysicsShadowClone::FullSyncAllClones( void )
{
	for( int i = s_ActiveShadowClones.Count(); --i >= 0; )
	{
		s_ActiveShadowClones[i]->FullSync( true );
	}
}


IPhysicsObject *CPhysicsShadowClone::TranslatePhysicsToClonedEnt( const IPhysicsObject *pPhysics )
{
	if( m_hClonedEntity.Get() != NULL )
	{
		for( int i = m_CloneLinks.Count(); --i >= 0; )
		{
			if( m_CloneLinks[i].pClone == pPhysics )
				return m_CloneLinks[i].pSource;
		}
	}

	return NULL;
}


void CPhysicsShadowClone::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
{
	//the baseclass just screenshakes, makes sounds, and outputs dust, we rely on the original entity to do this when applicable
}




bool CPhysicsShadowClone::IsShadowClone( const CBaseEntity *pEntity )
{
	return s_IsShadowClone[pEntity->entindex()];
}

CPhysicsShadowCloneLL *CPhysicsShadowClone::GetClonesOfEntity( const CBaseEntity *pEntity )
{
	return s_EntityClones[pEntity->entindex()];
}



static void DrawDebugOverlayForShadowClone( CPhysicsShadowClone *pClone )
{
	unsigned char iColorIntensity = (pClone->IsInAssumedSyncState())?(127):(255);

	int iRed = (pClone->IsUntransformedClone())?(0):(iColorIntensity);
	int iGreen = iColorIntensity;
	int iBlue = iColorIntensity;

	NDebugOverlay::EntityBounds( pClone, iRed, iGreen, iBlue, (iColorIntensity>>2), 0.05f );
}


bool CTraceFilterTranslateClones::ShouldHitEntity( IHandleEntity *pEntity, int contentsMask )
{
	CBaseEntity *pEnt = EntityFromEntityHandle( pEntity );
	if( CPhysicsShadowClone::IsShadowClone( pEnt ) )
	{
		CBaseEntity *pClonedEntity = ((CPhysicsShadowClone *)pEnt)->GetClonedEntity();
		CPortalSimulator *pSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pClonedEntity );
		if( pSimulator->m_DataAccess.Simulation.Dynamic.EntFlags[pClonedEntity->entindex()] & PSEF_IS_IN_PORTAL_HOLE )
			return m_pActualFilter->ShouldHitEntity( pClonedEntity, contentsMask );
		else
			return false;
	}
	else
	{
		return m_pActualFilter->ShouldHitEntity( pEntity, contentsMask );
	}
}

TraceType_t	CTraceFilterTranslateClones::GetTraceType() const
{
	return m_pActualFilter->GetTraceType();
}