text: //========= Copyright Valve Corporation, All rights reserved. ============//
  //
  // Purpose: 
  //
  //=============================================================================//
  
  #include "cbase.h"
  #include "baseanimating.h"
  #include "studio.h"
  #include "physics.h"
  #include "physics_saverestore.h"
  #include "ai_basenpc.h"
  #include "vphysics/constraints.h"
  #include "datacache/imdlcache.h"
  #include "bone_setup.h"
  #include "physics_prop_ragdoll.h"
  #include "KeyValues.h"
  #include "props.h"
  #include "RagdollBoogie.h"
  #include "AI_Criteria.h"
  #include "ragdoll_shared.h"
  #include "hierarchy.h"
  
  // memdbgon must be the last include file in a .cpp file!!!
  #include "tier0/memdbgon.h"
  
  //-----------------------------------------------------------------------------
  // Forward declarations
  //-----------------------------------------------------------------------------
  const char *GetMassEquivalent(float flMass);
  
  #define RAGDOLL_VISUALIZE 0
  
  //-----------------------------------------------------------------------------
  // ThinkContext
  //-----------------------------------------------------------------------------
  const char *s_pFadeOutContext = "RagdollFadeOutContext";
  const char *s_pDebrisContext = "DebrisContext";
  
  const float ATTACHED_DAMPING_SCALE = 50.0f;
  
  //-----------------------------------------------------------------------------
  // Spawnflags
  //-----------------------------------------------------------------------------
  #define	SF_RAGDOLLPROP_DEBRIS		0x0004
  #define SF_RAGDOLLPROP_USE_LRU_RETIREMENT	0x1000
  #define	SF_RAGDOLLPROP_ALLOW_DISSOLVE		0x2000	// Allow this prop to be dissolved
  #define	SF_RAGDOLLPROP_MOTIONDISABLED		0x4000
  #define	SF_RAGDOLLPROP_ALLOW_STRETCH		0x8000
  #define	SF_RAGDOLLPROP_STARTASLEEP			0x10000
  
  //-----------------------------------------------------------------------------
  // Networking
  //-----------------------------------------------------------------------------
  LINK_ENTITY_TO_CLASS( physics_prop_ragdoll, CRagdollProp );
  LINK_ENTITY_TO_CLASS( prop_ragdoll, CRagdollProp );
  EXTERN_SEND_TABLE(DT_Ragdoll)
  
  IMPLEMENT_SERVERCLASS_ST(CRagdollProp, DT_Ragdoll)
  	SendPropArray	(SendPropQAngles(SENDINFO_ARRAY(m_ragAngles), 13, 0 ), m_ragAngles),
  	SendPropArray	(SendPropVector(SENDINFO_ARRAY(m_ragPos), -1, SPROP_COORD ), m_ragPos),
  	SendPropEHandle(SENDINFO( m_hUnragdoll ) ),
  	SendPropFloat(SENDINFO(m_flBlendWeight), 8, SPROP_ROUNDDOWN, 0.0f, 1.0f ),
  	SendPropInt(SENDINFO(m_nOverlaySequence), 11),
  END_SEND_TABLE()
  
  #define DEFINE_RAGDOLL_ELEMENT( i ) \
  	DEFINE_FIELD( m_ragdoll.list[i].originParentSpace, FIELD_VECTOR ), \
  	DEFINE_PHYSPTR( m_ragdoll.list[i].pObject ), \
  	DEFINE_PHYSPTR( m_ragdoll.list[i].pConstraint ), \
  	DEFINE_FIELD( m_ragdoll.list[i].parentIndex, FIELD_INTEGER )
  
  BEGIN_DATADESC(CRagdollProp)
  //					m_ragdoll (custom handling)
  	DEFINE_AUTO_ARRAY	( m_ragdoll.boneIndex,	FIELD_INTEGER	),
  	DEFINE_AUTO_ARRAY	( m_ragPos,		FIELD_POSITION_VECTOR	),
  	DEFINE_AUTO_ARRAY	( m_ragAngles,	FIELD_VECTOR	),
  	DEFINE_KEYFIELD(m_anglesOverrideString,	FIELD_STRING, "angleOverride" ),
  	DEFINE_FIELD( m_lastUpdateTickCount, FIELD_INTEGER ),
  	DEFINE_FIELD( m_allAsleep, FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_hDamageEntity, FIELD_EHANDLE ),
  	DEFINE_FIELD( m_hKiller, FIELD_EHANDLE ),
  
  	DEFINE_KEYFIELD( m_bStartDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  
  	DEFINE_INPUTFUNC( FIELD_VOID, "StartRagdollBoogie", InputStartRadgollBoogie ),
  	DEFINE_INPUTFUNC( FIELD_VOID, "EnableMotion", InputEnableMotion ),
  	DEFINE_INPUTFUNC( FIELD_VOID, "DisableMotion", InputDisableMotion ),
  	DEFINE_INPUTFUNC( FIELD_VOID, "Enable",		InputTurnOn ),
  	DEFINE_INPUTFUNC( FIELD_VOID, "Disable",	InputTurnOff ),
  	DEFINE_INPUTFUNC( FIELD_FLOAT, "FadeAndRemove", InputFadeAndRemove ),
  
  	DEFINE_FIELD( m_hUnragdoll, FIELD_EHANDLE ),
  	DEFINE_FIELD( m_bFirstCollisionAfterLaunch, FIELD_BOOLEAN ),
  
  	DEFINE_FIELD( m_flBlendWeight, FIELD_FLOAT ),
  	DEFINE_FIELD( m_nOverlaySequence, FIELD_INTEGER ),
  	DEFINE_AUTO_ARRAY( m_ragdollMins, FIELD_VECTOR ),
  	DEFINE_AUTO_ARRAY( m_ragdollMaxs, FIELD_VECTOR ),
  
  	// Physics Influence
  	DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ),
  	DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ),
  	DEFINE_FIELD( m_flFadeOutStartTime, FIELD_TIME ),
  	DEFINE_FIELD( m_flFadeTime,	FIELD_FLOAT),
  	DEFINE_FIELD( m_strSourceClassName, FIELD_STRING ),
  	DEFINE_FIELD( m_bHasBeenPhysgunned, FIELD_BOOLEAN ),
  
  	// think functions
  	DEFINE_THINKFUNC( SetDebrisThink ),
  	DEFINE_THINKFUNC( ClearFlagsThink ),
  	DEFINE_THINKFUNC( FadeOutThink ),
  
  	DEFINE_FIELD( m_ragdoll.listCount, FIELD_INTEGER ),
  	DEFINE_FIELD( m_ragdoll.allowStretch, FIELD_BOOLEAN ),
  	DEFINE_PHYSPTR( m_ragdoll.pGroup ),
  	DEFINE_FIELD( m_flDefaultFadeScale, FIELD_FLOAT ),
  
  	//DEFINE_RAGDOLL_ELEMENT( 0 ),
  	DEFINE_RAGDOLL_ELEMENT( 1 ),
  	DEFINE_RAGDOLL_ELEMENT( 2 ),
  	DEFINE_RAGDOLL_ELEMENT( 3 ),
  	DEFINE_RAGDOLL_ELEMENT( 4 ),
  	DEFINE_RAGDOLL_ELEMENT( 5 ),
  	DEFINE_RAGDOLL_ELEMENT( 6 ),
  	DEFINE_RAGDOLL_ELEMENT( 7 ),
  	DEFINE_RAGDOLL_ELEMENT( 8 ),
  	DEFINE_RAGDOLL_ELEMENT( 9 ),
  	DEFINE_RAGDOLL_ELEMENT( 10 ),
  	DEFINE_RAGDOLL_ELEMENT( 11 ),
  	DEFINE_RAGDOLL_ELEMENT( 12 ),
  	DEFINE_RAGDOLL_ELEMENT( 13 ),
  	DEFINE_RAGDOLL_ELEMENT( 14 ),
  	DEFINE_RAGDOLL_ELEMENT( 15 ),
  	DEFINE_RAGDOLL_ELEMENT( 16 ),
  	DEFINE_RAGDOLL_ELEMENT( 17 ),
  	DEFINE_RAGDOLL_ELEMENT( 18 ),
  	DEFINE_RAGDOLL_ELEMENT( 19 ),
  	DEFINE_RAGDOLL_ELEMENT( 20 ),
  	DEFINE_RAGDOLL_ELEMENT( 21 ),
  	DEFINE_RAGDOLL_ELEMENT( 22 ),
  	DEFINE_RAGDOLL_ELEMENT( 23 ),
  
  END_DATADESC()
  
  //-----------------------------------------------------------------------------
  // Disable auto fading under dx7 or when level fades are specified
  //-----------------------------------------------------------------------------
  void CRagdollProp::DisableAutoFade()
  {
  	m_flFadeScale = 0;
  	m_flDefaultFadeScale = 0;
  }
  
  	
  void CRagdollProp::Spawn( void )
  {
  	// Starts out as the default fade scale value
  	m_flDefaultFadeScale = m_flFadeScale;
  
  	// NOTE: If this fires, then the assert or the datadesc is wrong!  (see DEFINE_RAGDOLL_ELEMENT above)
  	Assert( RAGDOLL_MAX_ELEMENTS == 24 );
  	Precache();
  	SetModel( STRING( GetModelName() ) );
  
  	CStudioHdr *pStudioHdr = GetModelPtr( );
  	if ( pStudioHdr->flags() & STUDIOHDR_FLAGS_NO_FORCED_FADE )
  	{
  		DisableAutoFade();
  	}
  	else
  	{
  		m_flFadeScale = m_flDefaultFadeScale;
  	}
  
  	matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
  	BaseClass::SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING ); // FIXME: shouldn't this be a subset of the bones
  	// this is useless info after the initial conditions are set
  	SetAbsAngles( vec3_angle );
  	int collisionGroup = (m_spawnflags & SF_RAGDOLLPROP_DEBRIS) ? COLLISION_GROUP_DEBRIS : COLLISION_GROUP_NONE;
  	bool bWake = (m_spawnflags & SF_RAGDOLLPROP_STARTASLEEP) ? false : true;
  	InitRagdoll( vec3_origin, 0, vec3_origin, pBoneToWorld, pBoneToWorld, 0, collisionGroup, true, bWake );
  	m_lastUpdateTickCount = 0;
  	m_flBlendWeight = 0.0f;
  	m_nOverlaySequence = -1;
  
  	// Unless specified, do not allow this to be dissolved
  	if ( HasSpawnFlags( SF_RAGDOLLPROP_ALLOW_DISSOLVE ) == false )
  	{
  		AddEFlags( EFL_NO_DISSOLVE );
  	}
  
  	if ( HasSpawnFlags(SF_RAGDOLLPROP_MOTIONDISABLED) )
  	{
  		DisableMotion();
  	}
  
  	if( m_bStartDisabled )
  	{
  		AddEffects( EF_NODRAW );
  	}
  }
  
  void CRagdollProp::SetSourceClassName( const char *pClassname )
  {
  	m_strSourceClassName = MAKE_STRING( pClassname );
  }
  
  
  void CRagdollProp::OnSave( IEntitySaveUtils *pUtils )
  {
  	if ( !m_ragdoll.listCount )
  		return;
  
  	// Don't save ragdoll element 0, base class saves the pointer in 
  	// m_pPhysicsObject
  	Assert( m_ragdoll.list[0].parentIndex == -1 );
  	Assert( m_ragdoll.list[0].pConstraint == NULL );
  	Assert( m_ragdoll.list[0].originParentSpace == vec3_origin );
  	Assert( m_ragdoll.list[0].pObject != NULL );
  	VPhysicsSetObject( NULL );	// squelch a warning message
  	VPhysicsSetObject( m_ragdoll.list[0].pObject );	// make sure object zero is saved by CBaseEntity
  	BaseClass::OnSave( pUtils );
  }
  
  void CRagdollProp::OnRestore()
  {
  	// rebuild element 0 since it isn't saved
  	// NOTE: This breaks the rules - the pointer needs to get fixed in Restore()
  	m_ragdoll.list[0].pObject = VPhysicsGetObject();
  	m_ragdoll.list[0].parentIndex = -1;
  	m_ragdoll.list[0].originParentSpace.Init();
  
  	BaseClass::OnRestore();
  	if ( !m_ragdoll.listCount )
  		return;
  
  	// JAY: Reset collision relationships
  	RagdollSetupCollisions( m_ragdoll, modelinfo->GetVCollide( GetModelIndex() ), GetModelIndex() );
  	VPhysicsUpdate( VPhysicsGetObject() );
  }
  
  void CRagdollProp::CalcRagdollSize( void )
  {
  	CollisionProp()->SetSurroundingBoundsType( USE_HITBOXES );
  	CollisionProp()->RemoveSolidFlags( FSOLID_FORCE_WORLD_ALIGNED );
  }
  
  void CRagdollProp::UpdateOnRemove( void )
  {
  	for ( int i = 0; i < m_ragdoll.listCount; i++ )
  	{
  		if ( m_ragdoll.list[i].pObject )
  		{
  			g_pPhysSaveRestoreManager->ForgetModel( m_ragdoll.list[i].pObject );
  		}
  	}
  
  	// Set to null so that the destructor's call to DestroyObject won't destroy
  	//  m_pObjects[ 0 ] twice since that's the physics object for the prop
  	VPhysicsSetObject( NULL );
  
  	RagdollDestroy( m_ragdoll );
  	// Chain to base after doing our own cleanup to mimic
  	//  destructor unwind order
  	BaseClass::UpdateOnRemove();
  }
  
  CRagdollProp::CRagdollProp( void )
  {
  	m_strSourceClassName = NULL_STRING;
  	m_anglesOverrideString = NULL_STRING;
  	m_ragdoll.listCount = 0;
  	Assert( (1<<RAGDOLL_INDEX_BITS) >=RAGDOLL_MAX_ELEMENTS );
  	m_allAsleep = false;
  	m_flFadeScale = 1;
  	m_flDefaultFadeScale = 1;
  }
  
  CRagdollProp::~CRagdollProp( void )
  {
  }
  
  void CRagdollProp::Precache( void )
  {
  	PrecacheModel( STRING( GetModelName() ) );
  	BaseClass::Precache();
  }
  
  int CRagdollProp::ObjectCaps()
  {
  	return BaseClass::ObjectCaps() | FCAP_WCEDIT_POSITION | FCAP_IMPULSE_USE;
  }
  
  void CRagdollProp::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  {
  	CBasePlayer *pPlayer = ToBasePlayer( pActivator );
  	if ( pPlayer )
  	{
  
  		pPlayer->PickupObject( this );
  	}
  }
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CRagdollProp::InitRagdollAnimation()
  {
  	m_flAnimTime = gpGlobals->curtime;
  	m_flPlaybackRate = 0.0;
  	SetCycle( 0 );
  	
  	// put into ACT_DIERAGDOLL if it exists, otherwise use sequence 0
  	int nSequence = SelectWeightedSequence( ACT_DIERAGDOLL );
  	if ( nSequence < 0 )
  	{
  		ResetSequence( 0 );
  	}
  	else
  	{
  		ResetSequence( nSequence );
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Response system stuff
  //-----------------------------------------------------------------------------
  IResponseSystem *CRagdollProp::GetResponseSystem()
  {
  	extern IResponseSystem *g_pResponseSystem;
  
  	// Just use the general NPC response system; we often come from NPCs after all
  	return g_pResponseSystem;
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CRagdollProp::ModifyOrAppendCriteria( AI_CriteriaSet& set )
  {
  	BaseClass::ModifyOrAppendCriteria( set );
  
  	if ( m_strSourceClassName != NULL_STRING )
  	{
  		set.RemoveCriteria( "classname" );
  		set.AppendCriteria( "classname", STRING(m_strSourceClassName) );
  		set.AppendCriteria( "ragdoll", "1" );
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CRagdollProp::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  {
  	m_hPhysicsAttacker = pPhysGunUser;
  	m_flLastPhysicsInfluenceTime = gpGlobals->curtime;
  
  	// Clear out the classname if we've been physgunned before
  	// so that the screams, etc. don't happen. Simulate that the first
  	// major punt or throw has been enough to kill him.
  	if ( m_bHasBeenPhysgunned )
  	{
  		m_strSourceClassName = NULL_STRING;
  	}
  	m_bHasBeenPhysgunned = true;
  
  	if( HasPhysgunInteraction( "onpickup", "boogie" ) )
  	{
  		if ( reason == PUNTED_BY_CANNON )
  		{
  			CRagdollBoogie::Create( this, 150, gpGlobals->curtime, 3.0f, SF_RAGDOLL_BOOGIE_ELECTRICAL );
  		}
  		else
  		{
  			CRagdollBoogie::Create( this, 150, gpGlobals->curtime, 2.0f, 0.0f );
  		}
  	}
  
  	if ( HasSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT ) )
  	{
  		s_RagdollLRU.MoveToTopOfLRU( this );
  	}
  
  	if ( !HasSpawnFlags( SF_PHYSPROP_ENABLE_ON_PHYSCANNON ) )
  		return;
  
  	ragdoll_t *pRagdollPhys = GetRagdoll( );
  	for ( int j = 0; j < pRagdollPhys->listCount; ++j )
  	{
  		pRagdollPhys->list[j].pObject->Wake();
  		pRagdollPhys->list[j].pObject->EnableMotion( true );
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CRagdollProp::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
  {
  	CDefaultPlayerPickupVPhysics::OnPhysGunDrop( pPhysGunUser, Reason );
  	m_hPhysicsAttacker = pPhysGunUser;
  	m_flLastPhysicsInfluenceTime = gpGlobals->curtime;
  
  	if( HasPhysgunInteraction( "onpickup", "boogie" ) )
  	{
  		CRagdollBoogie::Create( this, 150, gpGlobals->curtime, 3.0f, SF_RAGDOLL_BOOGIE_ELECTRICAL );
  	}
  
  	if ( HasSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT ) )
  	{
  		s_RagdollLRU.MoveToTopOfLRU( this );
  	}
  
  	// Make sure it's interactive debris for at most 5 seconds
  	if ( GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  	{
  		SetContextThink( &CRagdollProp::SetDebrisThink, gpGlobals->curtime + 5, s_pDebrisContext );
  	}
  
  	if ( Reason != LAUNCHED_BY_CANNON )
  		return;
  
  	if( HasPhysgunInteraction( "onlaunch", "spin_zaxis" ) )
  	{
  		Vector vecAverageCenter( 0, 0, 0 );
  
  		// Get the average position, apply forces to produce a spin
  		int j;
  		ragdoll_t *pRagdollPhys = GetRagdoll( );
  		for ( j = 0; j < pRagdollPhys->listCount; ++j )
  		{
  			Vector vecCenter;
  			pRagdollPhys->list[j].pObject->GetPosition( &vecCenter, NULL );
  			vecAverageCenter += vecCenter;
  		}
  
  		vecAverageCenter /= pRagdollPhys->listCount;
  
  		Vector vecZAxis( 0, 0, 1 );
  		for ( j = 0; j < pRagdollPhys->listCount; ++j )
  		{
  			Vector vecDelta;
  			pRagdollPhys->list[j].pObject->GetPosition( &vecDelta, NULL );
  			vecDelta -= vecAverageCenter;
  
  			Vector vecDir;
  			CrossProduct( vecZAxis, vecDelta, vecDir );
  			vecDir *= 100;
  			pRagdollPhys->list[j].pObject->AddVelocity( &vecDir, NULL );
  		}
  	}
  
  	PhysSetGameFlags( VPhysicsGetObject(), FVPHYSICS_WAS_THROWN );
  	m_bFirstCollisionAfterLaunch = true;
  }
  
  
  //-----------------------------------------------------------------------------
  // Physics attacker
  //-----------------------------------------------------------------------------
  CBasePlayer *CRagdollProp::HasPhysicsAttacker( float dt )
  {
  	if (gpGlobals->curtime - dt <= m_flLastPhysicsInfluenceTime)
  	{
  		return m_hPhysicsAttacker;
  	}
  	return NULL;
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CRagdollProp::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  {
  	BaseClass::VPhysicsCollision( index, pEvent );
  
  	CBaseEntity *pHitEntity = pEvent->pEntities[!index];
  	if ( pHitEntity == this )
  		return;
  
  	// Don't take physics damage from whoever's holding him with the physcannon.
  	if ( VPhysicsGetObject() && (VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) )
  	{
  		if ( pHitEntity && (pHitEntity == HasPhysicsAttacker( FLT_MAX )) )
  			return;
  	}
  
  	// Don't bother taking damage from the physics attacker
  	if ( pHitEntity && HasPhysicsAttacker( 0.5f ) == pHitEntity )
  		return;
  
  	if( m_bFirstCollisionAfterLaunch )
  	{
  		HandleFirstCollisionInteractions( index, pEvent );
  	}
  	
  	if ( m_takedamage != DAMAGE_NO )
  	{
  		int damageType = 0;
  		float damage = CalculateDefaultPhysicsDamage( index, pEvent, 1.0f, true, damageType );
  		if ( damage > 0 )
  		{
  			// Take extra damage after we're punted by the physcannon
  			if ( m_bFirstCollisionAfterLaunch )
  			{
  				damage *= 10;
  			}
  
  			CBaseEntity *pHitEntity = pEvent->pEntities[!index];
  			if ( !pHitEntity )
  			{
  				// hit world
  				pHitEntity = GetContainingEntity( INDEXENT(0) );
  			}
  			Vector damagePos;
  			pEvent->pInternalData->GetContactPoint( damagePos );
  			Vector damageForce = pEvent->postVelocity[index] * pEvent->pObjects[index]->GetMass();
  			if ( damageForce == vec3_origin )
  			{
  				// This can happen if this entity is motion disabled, and can't move.
  				// Use the velocity of the entity that hit us instead.
  				damageForce = pEvent->postVelocity[!index] * pEvent->pObjects[!index]->GetMass();
  			}
  
  			// FIXME: this doesn't pass in who is responsible if some other entity "caused" this collision
  			PhysCallbackDamage( this, CTakeDamageInfo( pHitEntity, pHitEntity, damageForce, damagePos, damage, damageType ), *pEvent, index );
  		}
  	}
  
  	if ( m_bFirstCollisionAfterLaunch )
  	{
  		// Setup the think function to remove the flags
  		SetThink( &CRagdollProp::ClearFlagsThink );
  		SetNextThink( gpGlobals->curtime );
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  //-----------------------------------------------------------------------------
  bool CRagdollProp::HasPhysgunInteraction( const char *pszKeyName, const char *pszValue )
  {
  	KeyValues *modelKeyValues = new KeyValues("");
  	if ( modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), modelinfo->GetModelKeyValueText( GetModel() ) ) )
  	{
  		KeyValues *pkvPropData = modelKeyValues->FindKey("physgun_interactions");
  		if ( pkvPropData )
  		{
  			char const *pszBase = pkvPropData->GetString( pszKeyName );
  
  			if ( pszBase && pszBase[0] && !stricmp( pszBase, pszValue ) )
  			{
  				modelKeyValues->deleteThis();
  				return true;
  			}
  		}
  	}
  
  	modelKeyValues->deleteThis();
  	return false;
  }
  
  
  //-----------------------------------------------------------------------------
  //-----------------------------------------------------------------------------
  void CRagdollProp::HandleFirstCollisionInteractions( int index, gamevcollisionevent_t *pEvent )
  {
  	IPhysicsObject *pObj = VPhysicsGetObject();
  	if ( !pObj)
  		return;
  
  	if( HasPhysgunInteraction( "onfirstimpact", "break" ) )
  	{
  		// Looks like it's best to break by having the object damage itself. 
  		CTakeDamageInfo info;
  
  		info.SetDamage( m_iHealth );
  		info.SetAttacker( this );
  		info.SetInflictor( this );
  		info.SetDamageType( DMG_GENERIC );
  
  		Vector vecPosition;
  		Vector vecVelocity;
  
  		VPhysicsGetObject()->GetVelocity( &vecVelocity, NULL );
  		VPhysicsGetObject()->GetPosition( &vecPosition, NULL );
  
  		info.SetDamageForce( vecVelocity );
  		info.SetDamagePosition( vecPosition );
  
  		TakeDamage( info );
  		return;
  	}
  
  	if( HasPhysgunInteraction( "onfirstimpact", "paintsplat" ) )
  	{
  		IPhysicsObject *pObj = VPhysicsGetObject();
   
  		Vector vecPos;
  		pObj->GetPosition( &vecPos, NULL );
   
  		trace_t tr;
  		UTIL_TraceLine( vecPos, vecPos + pEvent->preVelocity[0] * 1.5, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  
  		switch( random->RandomInt( 1, 3 ) )
  		{
  		case 1:
  			UTIL_DecalTrace( &tr, "PaintSplatBlue" );
  			break;
  
  		case 2:
  			UTIL_DecalTrace( &tr, "PaintSplatGreen" );
  			break;
  
  		case 3:
  			UTIL_DecalTrace( &tr, "PaintSplatPink" );
  			break;
  		}
  	}
  
  	bool bAlienBloodSplat = HasPhysgunInteraction( "onfirstimpact", "alienbloodsplat" );
  	if( bAlienBloodSplat || HasPhysgunInteraction( "onfirstimpact", "bloodsplat" ) )
  	{
  		IPhysicsObject *pObj = VPhysicsGetObject();
   
  		Vector vecPos;
  		pObj->GetPosition( &vecPos, NULL );
   
  		trace_t tr;
  		UTIL_TraceLine( vecPos, vecPos + pEvent->preVelocity[0] * 1.5, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  
  		UTIL_BloodDecalTrace( &tr, bAlienBloodSplat ? BLOOD_COLOR_GREEN : BLOOD_COLOR_RED );
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CRagdollProp::ClearFlagsThink( void )
  {
  	PhysClearGameFlags( VPhysicsGetObject(), FVPHYSICS_WAS_THROWN );
  	m_bFirstCollisionAfterLaunch = false;
  	SetThink( NULL );
  }
  
  
  //-----------------------------------------------------------------------------
  //-----------------------------------------------------------------------------
  AngularImpulse CRagdollProp::PhysGunLaunchAngularImpulse()
  {
  	if( HasPhysgunInteraction( "onlaunch", "spin_zaxis" ) )
  	{
  		// Don't add in random angular impulse if this object is supposed to spin in a specific way.
  		AngularImpulse ang( 0, 0, 0 );
  		return ang;
  	}
  
  	return CDefaultPlayerPickupVPhysics::PhysGunLaunchAngularImpulse();
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  // Input  : activity - 
  //-----------------------------------------------------------------------------
  void CRagdollProp::SetOverlaySequence( Activity activity )
  {
  	int seq = SelectWeightedSequence( activity );
  	if ( seq < 0 )
  	{
  		m_nOverlaySequence = -1;
  	}
  	else
  	{
  		m_nOverlaySequence = seq;
  	}
  }
  
  void CRagdollProp::InitRagdoll( const Vector &forceVector, int forceBone, const Vector &forcePos, matrix3x4_t *pPrevBones, matrix3x4_t *pBoneToWorld, float dt, int collisionGroup, bool activateRagdoll, bool bWakeRagdoll )
  {
  	SetCollisionGroup( collisionGroup );
  
  	// Make sure it's interactive debris for at most 5 seconds
  	if ( collisionGroup == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  	{
  		SetContextThink( &CRagdollProp::SetDebrisThink, gpGlobals->curtime + 5, s_pDebrisContext );
  	}
  
  	SetMoveType( MOVETYPE_VPHYSICS );
  	SetSolid( SOLID_VPHYSICS );
  	AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
  	m_takedamage = DAMAGE_EVENTS_ONLY;
  
  	ragdollparams_t params;
  	params.pGameData = static_cast<void *>( static_cast<CBaseEntity *>(this) );
  	params.modelIndex = GetModelIndex();
  	params.pCollide = modelinfo->GetVCollide( params.modelIndex );
  	params.pStudioHdr = GetModelPtr();
  	params.forceVector = forceVector;
  	params.forceBoneIndex = forceBone;
  	params.forcePosition = forcePos;
  	params.pCurrentBones = pBoneToWorld;
  	params.jointFrictionScale = 1.0;
  	params.allowStretch = HasSpawnFlags(SF_RAGDOLLPROP_ALLOW_STRETCH);
  	params.fixedConstraints = false;
  	RagdollCreate( m_ragdoll, params, physenv );
  	RagdollApplyAnimationAsVelocity( m_ragdoll, pPrevBones, pBoneToWorld, dt );
  	if ( m_anglesOverrideString != NULL_STRING && Q_strlen(m_anglesOverrideString.ToCStr()) > 0 )
  	{
  		char szToken[2048];
  		const char *pStr = nexttoken(szToken, STRING(m_anglesOverrideString), ',');
  		// anglesOverride is index,angles,index,angles (e.g. "1, 22.5 123.0 0.0, 2, 0 0 0, 3, 0 0 180.0")
  		while ( szToken[0] != 0 )
  		{
  			int objectIndex = atoi(szToken);
  			// sanity check to make sure this token is an integer
  			Assert( atof(szToken) == ((float)objectIndex) );
  			pStr = nexttoken(szToken, pStr, ',');
  			Assert( szToken[0] );
  			if ( objectIndex >= m_ragdoll.listCount )
  			{
  				Warning("Bad ragdoll pose in entity %s, model (%s) at %s, model changed?\n", GetDebugName(), GetModelName().ToCStr(), VecToString(GetAbsOrigin()) );
  			}
  			else if ( szToken[0] != 0 )
  			{
  				QAngle angles;
  				Assert( objectIndex >= 0 && objectIndex < RAGDOLL_MAX_ELEMENTS );
  				UTIL_StringToVector( angles.Base(), szToken );
  				int boneIndex = m_ragdoll.boneIndex[objectIndex];
  				AngleMatrix( angles, pBoneToWorld[boneIndex] );
  				const ragdollelement_t &element = m_ragdoll.list[objectIndex];
  				Vector out;
  				if ( element.parentIndex >= 0 )
  				{
  					int parentBoneIndex = m_ragdoll.boneIndex[element.parentIndex];
  					VectorTransform( element.originParentSpace, pBoneToWorld[parentBoneIndex], out );
  				}
  				else
  				{
  					out = GetAbsOrigin();
  				}
  				MatrixSetColumn( out, 3, pBoneToWorld[boneIndex] );
  				element.pObject->SetPositionMatrix( pBoneToWorld[boneIndex], true );
  			}
  			pStr = nexttoken(szToken, pStr, ',');
  		}
  	}
  
  	if ( activateRagdoll )
  	{
  		MEM_ALLOC_CREDIT();
  		RagdollActivate( m_ragdoll, params.pCollide, GetModelIndex(), bWakeRagdoll );
  	}
  
  	for ( int i = 0; i < m_ragdoll.listCount; i++ )
  	{
  		UpdateNetworkDataFromVPhysics( m_ragdoll.list[i].pObject, i );
  		g_pPhysSaveRestoreManager->AssociateModel( m_ragdoll.list[i].pObject, GetModelIndex() );
  		physcollision->CollideGetAABB( &m_ragdollMins[i], &m_ragdollMaxs[i], m_ragdoll.list[i].pObject->GetCollide(), vec3_origin, vec3_angle );
  	}
  	VPhysicsSetObject( m_ragdoll.list[0].pObject );
  
  	CalcRagdollSize();
  }
  
  void CRagdollProp::SetDebrisThink()
  {
  	SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  	RecheckCollisionFilter();
  }
  
  void CRagdollProp::SetDamageEntity( CBaseEntity *pEntity )
  {
  	// Damage passing
  	m_hDamageEntity = pEntity;
  
  	// Set our takedamage to match it
  	if ( pEntity )
  	{
  		m_takedamage = pEntity->m_takedamage;
  	}
  	else
  	{
  		m_takedamage = DAMAGE_EVENTS_ONLY;
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  int	CRagdollProp::OnTakeDamage( const CTakeDamageInfo &info )
  {
  	// If we have a damage entity, we want to pass damage to it. Add the
  	// Never Ragdoll flag, on the assumption that if the entity dies, we'll
  	// actually be taking the role of its ragdoll.
  	if ( m_hDamageEntity.Get() )
  	{
  		CTakeDamageInfo subInfo = info;
  		subInfo.AddDamageType( DMG_REMOVENORAGDOLL );
  		return m_hDamageEntity->OnTakeDamage( subInfo );
  	}
  
  	return BaseClass::OnTakeDamage( info );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Force all the ragdoll's bone's physics objects to recheck their collision filters
  //-----------------------------------------------------------------------------
  void CRagdollProp::RecheckCollisionFilter( void )
  {
  	for ( int i = 0; i < m_ragdoll.listCount; i++ )
  	{
  		m_ragdoll.list[i].pObject->RecheckCollisionFilter();
  	}
  }
  
  
  void CRagdollProp::TraceAttack( const CTakeDamageInfo &info, const Vector &dir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  {
  	if ( ptr->physicsbone >= 0 && ptr->physicsbone < m_ragdoll.listCount )
  	{
  		VPhysicsSwapObject( m_ragdoll.list[ptr->physicsbone].pObject );
  	}
  	BaseClass::TraceAttack( info, dir, ptr, pAccumulator );
  }
  
  void CRagdollProp::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask )
  {
  	// no ragdoll, fall through to base class 
  	if ( !m_ragdoll.listCount )
  	{
  		BaseClass::SetupBones( pBoneToWorld, boneMask );
  		return;
  	}
  
  	// Not really ideal, but it'll work for now
  	UpdateModelScale();
  
  	MDLCACHE_CRITICAL_SECTION();
  	CStudioHdr *pStudioHdr = GetModelPtr( );
  	bool sim[MAXSTUDIOBONES];
  	memset( sim, 0, pStudioHdr->numbones() );
  
  	int i;
  
  	CBoneAccessor boneaccessor( pBoneToWorld );
  	for ( i = 0; i < m_ragdoll.listCount; i++ )
  	{
  		// during restore this may be NULL
  		if ( !m_ragdoll.list[i].pObject )
  			continue;
  
  		if ( RagdollGetBoneMatrix( m_ragdoll, boneaccessor, i ) )
  		{
  			sim[m_ragdoll.boneIndex[i]] = true;
  		}
  	}
  
  	mstudiobone_t *pbones = pStudioHdr->pBone( 0 );
  	for ( i = 0; i < pStudioHdr->numbones(); i++ )
  	{
  		if ( sim[i] )
  			continue;
  		
  		if ( !(pStudioHdr->boneFlags(i) & boneMask) )
  			continue;
  
  		matrix3x4_t matBoneLocal;
  		AngleMatrix( pbones[i].rot, pbones[i].pos, matBoneLocal );
  		ConcatTransforms( pBoneToWorld[pbones[i].parent], matBoneLocal, pBoneToWorld[i]);
  	}
  }
  
  bool CRagdollProp::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
  {
  #if 0
  	// PERFORMANCE: Use hitboxes for rays instead of vcollides if this is a performance problem
  	if ( ray.m_IsRay )
  	{
  		return BaseClass::TestCollision( ray, mask, trace );
  	}
  #endif
  
  	CStudioHdr *pStudioHdr = GetModelPtr( );
  	if (!pStudioHdr)
  		return false;
  
  	// Just iterate all of the elements and trace the box against each one.
  	// NOTE: This is pretty expensive for small/dense characters
  	trace_t tr;
  	for ( int i = 0; i < m_ragdoll.listCount; i++ )
  	{
  		Vector position;
  		QAngle angles;
  
  		if( m_ragdoll.list[i].pObject )
  		{
  			m_ragdoll.list[i].pObject->GetPosition( &position, &angles );
  			physcollision->TraceBox( ray, m_ragdoll.list[i].pObject->GetCollide(), position, angles, &tr );
  
  			if ( tr.fraction < trace.fraction )
  			{
  				tr.physicsbone = i;
  				tr.surface.surfaceProps = m_ragdoll.list[i].pObject->GetMaterialIndex();
  				trace = tr;
  			}
  		}
  		else
  		{
  			DevWarning("Bogus object in Ragdoll Prop's ragdoll list!\n");
  		}
  	}
  
  	if ( trace.fraction >= 1 )
  	{
  		return false;
  	}
  
  	return true;
  }
  
  
  void CRagdollProp::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
  {
  	// newAngles is a relative transform for the entity
  	// But a ragdoll entity has identity orientation by design
  	// so we compute a relative transform here based on the previous transform
  	matrix3x4_t startMatrixInv;
  	MatrixInvert( EntityToWorldTransform(), startMatrixInv );
  	matrix3x4_t endMatrix;
  	MatrixCopy( EntityToWorldTransform(), endMatrix );
  	if ( newAngles )
  	{
  		AngleMatrix( *newAngles, endMatrix );
  	}
  	if ( newPosition )
  	{
  		PositionMatrix( *newPosition, endMatrix );
  	}
  	// now endMatrix is the refernce matrix for the entity at the target position
  	matrix3x4_t xform;
  	ConcatTransforms( endMatrix, startMatrixInv, xform );
  	// now xform is the relative transform the entity must undergo
  
  	// we need to call the base class and it will teleport our vphysics object, 
  	// so set object 0 up and compute the origin/angles for its new position (base implementation has side effects)
  	VPhysicsSwapObject( m_ragdoll.list[0].pObject );
  	matrix3x4_t obj0source, obj0Target;
  	m_ragdoll.list[0].pObject->GetPositionMatrix( &obj0source );
  	ConcatTransforms( xform, obj0source, obj0Target );
  	Vector obj0Pos;
  	QAngle obj0Angles;
  	MatrixAngles( obj0Target, obj0Angles, obj0Pos );
  	BaseClass::Teleport( &obj0Pos, &obj0Angles, newVelocity );
  	
  	for ( int i = 1; i < m_ragdoll.listCount; i++ )
  	{
  		matrix3x4_t matrix, newMatrix;
  		m_ragdoll.list[i].pObject->GetPositionMatrix( &matrix );
  		ConcatTransforms( xform, matrix, newMatrix );
  		m_ragdoll.list[i].pObject->SetPositionMatrix( newMatrix, true );
  		UpdateNetworkDataFromVPhysics( m_ragdoll.list[i].pObject, i );
  	}
  	// fixup/relink object 0
  	UpdateNetworkDataFromVPhysics( m_ragdoll.list[0].pObject, 0 );
  }
  
  void CRagdollProp::VPhysicsUpdate( IPhysicsObject *pPhysics )
  {
  	if ( m_lastUpdateTickCount == (unsigned int)gpGlobals->tickcount )
  		return;
  
  	m_lastUpdateTickCount = gpGlobals->tickcount;
  	//NetworkStateChanged();
  
  	matrix3x4_t boneToWorld[MAXSTUDIOBONES];
  	QAngle angles;
  	Vector surroundingMins, surroundingMaxs;
  
  	int i;
  	for ( i = 0; i < m_ragdoll.listCount; i++ )
  	{
  		CBoneAccessor boneaccessor( boneToWorld );
  		if ( RagdollGetBoneMatrix( m_ragdoll, boneaccessor, i ) )
  		{
  			Vector vNewPos;
  			MatrixAngles( boneToWorld[m_ragdoll.boneIndex[i]], angles, vNewPos );
  			m_ragPos.Set( i, vNewPos );
  			m_ragAngles.Set( i, angles );
  		}
  		else
  		{
  			m_ragPos.GetForModify(i).Init();
  			m_ragAngles.GetForModify(i).Init();
  		}
  	}
  
  	// BUGBUG: Use the ragdollmins/maxs to do this instead of the collides
  	m_allAsleep = RagdollIsAsleep( m_ragdoll );
  
  	// Don't scream after you've come to rest
  	if ( m_allAsleep )
  	{
  		m_strSourceClassName = NULL_STRING;
  	}
  	else
  	{
  		if ( m_ragdoll.pGroup->IsInErrorState() )
  		{
  			RagdollSolveSeparation( m_ragdoll, this );
  		}
  	}
  	
  	// Interactive debris converts back to debris when it comes to rest
  	if ( m_allAsleep && GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  	{
  		SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  		RecheckCollisionFilter();
  		SetContextThink( NULL, gpGlobals->curtime, s_pDebrisContext );
  	}
  
  	Vector vecFullMins, vecFullMaxs;
  	vecFullMins = m_ragPos[0];
  	vecFullMaxs = m_ragPos[0];
  	for ( i = 0; i < m_ragdoll.listCount; i++ )
  	{
  		Vector mins, maxs;
  		matrix3x4_t update;
  		if ( !m_ragdoll.list[i].pObject )
  		{
  			m_ragdollMins[i].Init();
  			m_ragdollMaxs[i].Init();
  			continue;
  		}
  		m_ragdoll.list[i].pObject->GetPositionMatrix( &update );
  		TransformAABB( update, m_ragdollMins[i], m_ragdollMaxs[i], mins, maxs );
  		for ( int j = 0; j < 3; j++ )
  		{
  			if ( mins[j] < vecFullMins[j] )
  			{
  				vecFullMins[j] = mins[j];
  			}
  			if ( maxs[j] > vecFullMaxs[j] )
  			{
  				vecFullMaxs[j] = maxs[j];
  			}
  		}
  	}
  
  	SetAbsOrigin( m_ragPos[0] );
  	SetAbsAngles( vec3_angle );
  	const Vector &vecOrigin = CollisionProp()->GetCollisionOrigin();
  	CollisionProp()->AddSolidFlags( FSOLID_FORCE_WORLD_ALIGNED );
  	CollisionProp()->SetSurroundingBoundsType( USE_COLLISION_BOUNDS_NEVER_VPHYSICS );
  	SetCollisionBounds( vecFullMins - vecOrigin, vecFullMaxs - vecOrigin );
  	CollisionProp()->MarkSurroundingBoundsDirty();
  
  	PhysicsTouchTriggers();
  }
  
  int CRagdollProp::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax )
  {
  	for ( int i = 0; i < m_ragdoll.listCount; i++ )
  	{
  		if ( i < listMax )
  		{
  			pList[i] = m_ragdoll.list[i].pObject;
  		}
  	}
  
  	return m_ragdoll.listCount;
  }
  
  void CRagdollProp::UpdateNetworkDataFromVPhysics( IPhysicsObject *pPhysics, int index )
  {
  	Assert(index < m_ragdoll.listCount);
  
  	QAngle angles;
  	Vector vPos;
  	m_ragdoll.list[index].pObject->GetPosition( &vPos, &angles );
  	m_ragPos.Set( index, vPos );
  	m_ragAngles.Set( index, angles );
  
  	// move/relink if root moved
  	if ( index == 0 )
  	{
  		SetAbsOrigin( m_ragPos[0] );
  		PhysicsTouchTriggers();
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Fade out due to the LRU telling it do
  //-----------------------------------------------------------------------------
  #define FADE_OUT_LENGTH 0.5f
  
  void CRagdollProp::FadeOut( float flDelay, float fadeTime ) 
  {
  	if ( IsFading() )
  		return;
  
  	m_flFadeTime = ( fadeTime == -1 ) ? FADE_OUT_LENGTH : fadeTime;
  
  	m_flFadeOutStartTime = gpGlobals->curtime + flDelay;
  	m_flFadeScale = 0;
  	SetContextThink( &CRagdollProp::FadeOutThink, gpGlobals->curtime + flDelay + 0.01f, s_pFadeOutContext );
  }
  
  bool CRagdollProp::IsFading()
  {
  	return ( GetNextThink( s_pFadeOutContext ) >= gpGlobals->curtime );
  }
  
  void CRagdollProp::FadeOutThink(void) 
  {
  	float dt = gpGlobals->curtime - m_flFadeOutStartTime;
  	if ( dt < 0 )
  	{
  		SetContextThink( &CRagdollProp::FadeOutThink, gpGlobals->curtime + 0.1, s_pFadeOutContext );
  	}
  	else if ( dt < m_flFadeTime )
  	{
  		float alpha = 1.0f - dt / m_flFadeTime;
  		int nFade = (int)(alpha * 255.0f);
  		m_nRenderMode = kRenderTransTexture;
  		SetRenderColorA( nFade );
  		NetworkStateChanged();
  		SetContextThink( &CRagdollProp::FadeOutThink, gpGlobals->curtime + TICK_INTERVAL, s_pFadeOutContext );
  	}
  	else
  	{
  		// Necessary to cause it to do the appropriate death cleanup
  		// Yeah, the player may have nothing to do with it, but
  		// passing NULL to TakeDamage causes bad things to happen
  		CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  		CTakeDamageInfo info( pPlayer, pPlayer, 10000.0, DMG_GENERIC );
  		TakeDamage( info );
  		UTIL_Remove( this );
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: Draw any debug text overlays
  // Output : Current text offset from the top
  //-----------------------------------------------------------------------------
  int CRagdollProp::DrawDebugTextOverlays(void) 
  {
  	int text_offset = BaseClass::DrawDebugTextOverlays();
  
  	if (m_debugOverlays & OVERLAY_TEXT_BIT) 
  	{
  		if (m_ragdoll.listCount)
  		{
  			float mass = 0;
  			for ( int i = 0; i < m_ragdoll.listCount; i++ )
  			{
  				if ( m_ragdoll.list[i].pObject != NULL )
  				{
  					mass += m_ragdoll.list[i].pObject->GetMass();
  				}
  			}
  
  			char tempstr[512];
  			Q_snprintf(tempstr, sizeof(tempstr),"Mass: %.2f kg / %.2f lb (%s)", mass, kg2lbs(mass), GetMassEquivalent(mass) );
  			EntityText( text_offset, tempstr, 0);
  			text_offset++;
  		}
  	}
  
  	return text_offset;
  }
  
  void CRagdollProp::DrawDebugGeometryOverlays() 
  {
  	if (m_debugOverlays & OVERLAY_BBOX_BIT) 
  	{
  		DrawServerHitboxes();
  	}
  	if (m_debugOverlays & OVERLAY_PIVOT_BIT)
  	{
  		for ( int i = 0; i < m_ragdoll.listCount; i++ )
  		{
  			if ( m_ragdoll.list[i].pObject )
  			{
  				float mass = m_ragdoll.list[i].pObject->GetMass();
  				Vector pos;
  				m_ragdoll.list[i].pObject->GetPosition( &pos, NULL );
  				CFmtStr str("mass %.1f", mass );
  				NDebugOverlay::EntityTextAtPosition( pos, 0, str.Access(), 0, 0, 255, 0, 255 );
  			}
  		}
  	} 
  	BaseClass::DrawDebugGeometryOverlays();
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  // Input  : *pOther - 
  //-----------------------------------------------------------------------------
  void CRagdollProp::SetUnragdoll( CBaseAnimating *pOther )
  {
  	m_hUnragdoll = pOther;
  }
  
  //===============================================================================================================
  // RagdollPropAttached
  //===============================================================================================================
  class CRagdollPropAttached : public CRagdollProp
  {
  	DECLARE_CLASS( CRagdollPropAttached, CRagdollProp );
  public:
  
  	CRagdollPropAttached()
  	{
  		m_bShouldDetach = false;
  	}
  
  	~CRagdollPropAttached()
  	{
  		physenv->DestroyConstraint( m_pAttachConstraint );
  		m_pAttachConstraint = NULL;
  	}
  
  	void InitRagdollAttached( IPhysicsObject *pAttached, const Vector &forceVector, int forceBone, matrix3x4_t *pPrevBones, matrix3x4_t *pBoneToWorld, float dt, int collisionGroup, CBaseAnimating *pFollow, int boneIndexRoot, const Vector &boneLocalOrigin, int parentBoneAttach, const Vector &worldAttachOrigin );
  	void DetachOnNextUpdate();
  	void VPhysicsUpdate( IPhysicsObject *pPhysics );
  
  	DECLARE_SERVERCLASS();
  	DECLARE_DATADESC();
  
  private:
  	void Detach();
  	CNetworkVar( int, m_boneIndexAttached );
  	CNetworkVar( int, m_ragdollAttachedObjectIndex );
  	CNetworkVector( m_attachmentPointBoneSpace );
  	CNetworkVector( m_attachmentPointRagdollSpace );
  	bool		m_bShouldDetach;
  	IPhysicsConstraint	*m_pAttachConstraint;
  };
  
  LINK_ENTITY_TO_CLASS( prop_ragdoll_attached, CRagdollPropAttached );
  EXTERN_SEND_TABLE(DT_Ragdoll_Attached)
  
  IMPLEMENT_SERVERCLASS_ST(CRagdollPropAttached, DT_Ragdoll_Attached)
  	SendPropInt( SENDINFO( m_boneIndexAttached ), MAXSTUDIOBONEBITS, SPROP_UNSIGNED ),
  	SendPropInt( SENDINFO( m_ragdollAttachedObjectIndex ), RAGDOLL_INDEX_BITS, SPROP_UNSIGNED ),
  	SendPropVector(SENDINFO(m_attachmentPointBoneSpace), -1,  SPROP_COORD ),
  	SendPropVector(SENDINFO(m_attachmentPointRagdollSpace), -1,  SPROP_COORD ),
  END_SEND_TABLE()
  
  BEGIN_DATADESC(CRagdollPropAttached)
  	DEFINE_FIELD( m_boneIndexAttached,	FIELD_INTEGER ),
  	DEFINE_FIELD( m_ragdollAttachedObjectIndex, FIELD_INTEGER ),
  	DEFINE_FIELD( m_attachmentPointBoneSpace,	FIELD_VECTOR ),
  	DEFINE_FIELD( m_attachmentPointRagdollSpace, FIELD_VECTOR ),
  	DEFINE_FIELD( m_bShouldDetach, FIELD_BOOLEAN ),
  	DEFINE_PHYSPTR( m_pAttachConstraint ),
  END_DATADESC()
  
  
  static void SyncAnimatingWithPhysics( CBaseAnimating *pAnimating )
  {
  	IPhysicsObject *pPhysics = pAnimating->VPhysicsGetObject();
  	if ( pPhysics )
  	{
  		Vector pos;
  		pPhysics->GetShadowPosition( &pos, NULL );
  		pAnimating->SetAbsOrigin( pos );
  	}
  }
  
  	
  CBaseAnimating *CreateServerRagdollSubmodel( CBaseAnimating *pOwner, const char *pModelName, const Vector &position, const QAngle &angles, int collisionGroup )
  {
  	CRagdollProp *pRagdoll = (CRagdollProp *)CBaseEntity::CreateNoSpawn( "prop_ragdoll", position, angles, pOwner );
  	pRagdoll->SetModelName( AllocPooledString( pModelName ) );
  	pRagdoll->SetModel( STRING(pRagdoll->GetModelName()) );
  	matrix3x4_t pBoneToWorld[MAXSTUDIOBONES], pBoneToWorldNext[MAXSTUDIOBONES];
  	pRagdoll->ResetSequence( 0 );
  
  	// let bone merging do the work of copying everything over for us
  	pRagdoll->SetParent( pOwner );
  	pRagdoll->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
  	// HACKHACK: don't want this parent anymore
  	pRagdoll->SetParent( NULL );
  
  	memcpy( pBoneToWorldNext, pBoneToWorld, sizeof(pBoneToWorld) );
  
  	pRagdoll->InitRagdoll( vec3_origin, -1, vec3_origin, pBoneToWorld, pBoneToWorldNext, 0.1, collisionGroup, true );
  	return pRagdoll;
  }
  
  
  CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, const CTakeDamageInfo &info, int collisionGroup, bool bUseLRURetirement )
  {
  	if ( info.GetDamageType() & (DMG_VEHICLE|DMG_CRUSH) )
  	{
  		// if the entity was killed by physics or a vehicle, move to the vphysics shadow position before creating the ragdoll.
  		SyncAnimatingWithPhysics( pAnimating );
  	}
  	CRagdollProp *pRagdoll = (CRagdollProp *)CBaseEntity::CreateNoSpawn( "prop_ragdoll", pAnimating->GetAbsOrigin(), vec3_angle, NULL );
  	pRagdoll->CopyAnimationDataFrom( pAnimating );
  	pRagdoll->SetOwnerEntity( pAnimating );
  
  	pRagdoll->InitRagdollAnimation();
  	matrix3x4_t pBoneToWorld[MAXSTUDIOBONES], pBoneToWorldNext[MAXSTUDIOBONES];
  	
  	float dt = 0.1f;
  
  	// Copy over dissolve state...
  	if ( pAnimating->IsEFlagSet( EFL_NO_DISSOLVE ) )
  	{
  		pRagdoll->AddEFlags( EFL_NO_DISSOLVE );
  	}
  
  	// NOTE: This currently is only necessary to prevent manhacks from
  	// colliding with server ragdolls they kill
  	pRagdoll->SetKiller( info.GetInflictor() );
  	pRagdoll->SetSourceClassName( pAnimating->GetClassname() );
  
  	// NPC_STATE_DEAD npc's will have their COND_IN_PVS cleared, so this needs to force SetupBones to happen
  	unsigned short fPrevFlags = pAnimating->GetBoneCacheFlags();
  	pAnimating->SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
  
  	// UNDONE: Extract velocity from bones via animation (like we do on the client)
  	// UNDONE: For now, just move each bone by the total entity velocity if set.
  	// Get Bones positions before
  	// Store current cycle
  	float fSequenceDuration = pAnimating->SequenceDuration( pAnimating->GetSequence() );
  	float fSequenceTime = pAnimating->GetCycle() * fSequenceDuration;		
  
  	if( fSequenceTime <= dt && fSequenceTime > 0.0f )
  	{
  		// Avoid having negative cycle
  		dt = fSequenceTime;
  	}
  
  	float fPreviousCycle = clamp(pAnimating->GetCycle()-( dt * ( 1 / fSequenceDuration ) ),0.f,1.f);
  	float fCurCycle = pAnimating->GetCycle();
  	// Get current bones positions
  	pAnimating->SetupBones( pBoneToWorldNext, BONE_USED_BY_ANYTHING );
  	// Get previous bones positions
  	pAnimating->SetCycle( fPreviousCycle );
  	pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );		
  	// Restore current cycle
  	pAnimating->SetCycle( fCurCycle );
  
  	// Reset previous bone flags
  	pAnimating->ClearBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
  	pAnimating->SetBoneCacheFlags( fPrevFlags );
  
  	Vector vel = pAnimating->GetAbsVelocity();
  	if( ( vel.Length() == 0 ) && ( dt > 0 ) )
  	{
  		// Compute animation velocity
  		CStudioHdr *pstudiohdr = pAnimating->GetModelPtr();
  		if ( pstudiohdr )
  		{
  			Vector deltaPos;
  			QAngle deltaAngles;
  			if (Studio_SeqMovement( pstudiohdr, 
  				pAnimating->GetSequence(), 
  				fPreviousCycle, 
  				pAnimating->GetCycle(), 
  				pAnimating->GetPoseParameterArray(), 
  				deltaPos, 
  				deltaAngles ))
  			{
  				VectorRotate( deltaPos, pAnimating->EntityToWorldTransform(), vel );
  				vel /= dt;
  			}
  		}
  	}
  
  	if ( vel.LengthSqr() > 0 )
  	{
  		int numbones = pAnimating->GetModelPtr()->numbones();
  		vel *= dt;
  		for ( int i = 0; i < numbones; i++ )
  		{
  			Vector pos;
  			MatrixGetColumn( pBoneToWorld[i], 3, pos );
  			pos -= vel;
  			MatrixSetColumn( pos, 3, pBoneToWorld[i] );
  		}
  	}
  
  #if RAGDOLL_VISUALIZE
  	pAnimating->DrawRawSkeleton( pBoneToWorld, BONE_USED_BY_ANYTHING, true, 20, false );
  	pAnimating->DrawRawSkeleton( pBoneToWorldNext, BONE_USED_BY_ANYTHING, true, 20, true );
  #endif
  	// Is this a vehicle / NPC collision?
  	if ( (info.GetDamageType() & DMG_VEHICLE) && pAnimating->MyNPCPointer() )
  	{
  		// init the ragdoll with no forces
  		pRagdoll->InitRagdoll( vec3_origin, -1, vec3_origin, pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true );
  
  		// apply vehicle forces
  		// Get a list of bones with hitboxes below the plane of impact
  		int boxList[128];
  		Vector normal(0,0,-1);
  		int count = pAnimating->GetHitboxesFrontside( boxList, ARRAYSIZE(boxList), normal, DotProduct( normal, info.GetDamagePosition() ) );
  		
  		// distribute force over mass of entire character
  		float massScale = Studio_GetMass(pAnimating->GetModelPtr());
  		massScale = clamp( massScale, 1.f, 1.e4f );
  		massScale = 1.f / massScale;
  
  		// distribute the force
  		// BUGBUG: This will hit the same bone twice if it has two hitboxes!!!!
  		ragdoll_t *pRagInfo = pRagdoll->GetRagdoll();
  		for ( int i = 0; i < count; i++ )
  		{
  			int physBone = pAnimating->GetPhysicsBone( pAnimating->GetHitboxBone( boxList[i] ) );
  			IPhysicsObject *pPhysics = pRagInfo->list[physBone].pObject;
  			pPhysics->ApplyForceCenter( info.GetDamageForce() * pPhysics->GetMass() * massScale );
  		}
  	}
  	else
  	{
  		pRagdoll->InitRagdoll( info.GetDamageForce(), forceBone, info.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true );
  	}
  
  	// Are we dissolving?
  	if ( pAnimating->IsDissolving() )
  	{
  		pRagdoll->TransferDissolveFrom( pAnimating );
  	}
  	else if ( bUseLRURetirement )
  	{
  		pRagdoll->AddSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT );
  		s_RagdollLRU.MoveToTopOfLRU( pRagdoll );
  	}
  
  	// Tracker 22598:  If we don't set the OBB mins/maxs to something valid here, then the client will have a zero sized hull
  	//  for the ragdoll for one frame until Vphysics updates the real obb bounds after the first simulation frame.  Having
  	//  a zero sized hull makes the ragdoll think it should be faded/alpha'd to zero for a frame, so you get a blink where
  	//  the ragdoll doesn't draw initially.
  	Vector mins, maxs;
  	mins = pAnimating->CollisionProp()->OBBMins();
  	maxs = pAnimating->CollisionProp()->OBBMaxs();
  	pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs );
  
  	return pRagdoll;
  }
  
  void CRagdollPropAttached::DetachOnNextUpdate()
  {
  	m_bShouldDetach = true;
  }
  
  void CRagdollPropAttached::VPhysicsUpdate( IPhysicsObject *pPhysics )
  {
  	if ( m_bShouldDetach )
  	{
  		Detach();
  		m_bShouldDetach = false;
  	}
  	BaseClass::VPhysicsUpdate( pPhysics );
  }
  
  void CRagdollPropAttached::Detach()
  {
  	SetParent(NULL);
  	SetOwnerEntity( NULL );
  	SetAbsAngles( vec3_angle );
  	SetMoveType( MOVETYPE_VPHYSICS );
  	RemoveSolidFlags( FSOLID_NOT_SOLID );
  	physenv->DestroyConstraint( m_pAttachConstraint );
  	m_pAttachConstraint = NULL;
  	const float dampingScale = 1.0f / ATTACHED_DAMPING_SCALE;
  	for ( int i = 0; i < m_ragdoll.listCount; i++ )
  	{
  		float damping, rotdamping;
  		m_ragdoll.list[i].pObject->GetDamping( &damping, &rotdamping );
  		damping *= dampingScale;
  		rotdamping *= dampingScale;
  		m_ragdoll.list[i].pObject->SetDamping( &damping, &damping );
  	}
  
  	// Go non-solid
  	SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  	RecheckCollisionFilter();
  }
  
  void CRagdollPropAttached::InitRagdollAttached( 
  	IPhysicsObject *pAttached, 
  	const Vector &forceVector, 
  	int forceBone, 
  	matrix3x4_t *pPrevBones, 
  	matrix3x4_t *pBoneToWorld, 
  	float dt, 
  	int collisionGroup, 
  	CBaseAnimating *pFollow, 
  	int boneIndexRoot, 
  	const Vector &boneLocalOrigin, 
  	int parentBoneAttach, 
  	const Vector &worldAttachOrigin )
  {
  	int ragdollAttachedIndex = 0;
  	if ( parentBoneAttach > 0 )
  	{
  		CStudioHdr *pStudioHdr = GetModelPtr();
  		mstudiobone_t *pBone = pStudioHdr->pBone( parentBoneAttach );
  		ragdollAttachedIndex = pBone->physicsbone;
  	}
  
  	InitRagdoll( forceVector, forceBone, vec3_origin, pPrevBones, pBoneToWorld, dt, collisionGroup, false );
  	
  	IPhysicsObject *pRefObject = m_ragdoll.list[ragdollAttachedIndex].pObject;
  
  	Vector attachmentPointRagdollSpace;
  	pRefObject->WorldToLocal( &attachmentPointRagdollSpace, worldAttachOrigin );
  
  	constraint_ragdollparams_t constraint;
  	constraint.Defaults();
  	matrix3x4_t tmp, worldToAttached, worldToReference, constraintToWorld;
  
  	Vector offsetWS;
  	pAttached->LocalToWorld( &offsetWS, boneLocalOrigin );
  
  	QAngle followAng = QAngle(0, pFollow->GetAbsAngles().y, 0 );
  	AngleMatrix( followAng, offsetWS, constraintToWorld );
  
  	constraint.axes[0].SetAxisFriction( -2, 2, 20 );
  	constraint.axes[1].SetAxisFriction( 0, 0, 0 );
  	constraint.axes[2].SetAxisFriction( -15, 15, 20 );
  
  	// Exaggerate the bone's ability to pull the mass of the ragdoll around
  	constraint.constraint.bodyMassScale[1] = 50.0f;
  
  	pAttached->GetPositionMatrix( &tmp );
  	MatrixInvert( tmp, worldToAttached );
  
  	pRefObject->GetPositionMatrix( &tmp );
  	MatrixInvert( tmp, worldToReference );
  
  	ConcatTransforms( worldToReference, constraintToWorld, constraint.constraintToReference );
  	ConcatTransforms( worldToAttached, constraintToWorld, constraint.constraintToAttached );
  
  	// for now, just slam this to be the passed in value
  	MatrixSetColumn( attachmentPointRagdollSpace, 3, constraint.constraintToReference );
  
  	PhysDisableEntityCollisions( pAttached, m_ragdoll.list[0].pObject );
  	m_pAttachConstraint = physenv->CreateRagdollConstraint( pRefObject, pAttached, m_ragdoll.pGroup, constraint );
  
  	SetParent( pFollow );
  	SetOwnerEntity( pFollow );
  
  	RagdollActivate( m_ragdoll, modelinfo->GetVCollide( GetModelIndex() ), GetModelIndex() );
  
  	// add a bunch of dampening to the ragdoll
  	for ( int i = 0; i < m_ragdoll.listCount; i++ )
  	{
  		float damping, rotdamping;
  		m_ragdoll.list[i].pObject->GetDamping( &damping, &rotdamping );
  		damping *= ATTACHED_DAMPING_SCALE;
  		rotdamping *= ATTACHED_DAMPING_SCALE;
  		m_ragdoll.list[i].pObject->SetDamping( &damping, &rotdamping );
  	}
  
  	m_boneIndexAttached = boneIndexRoot;
  	m_ragdollAttachedObjectIndex = ragdollAttachedIndex;
  	m_attachmentPointBoneSpace = boneLocalOrigin;
  	
  	Vector vTemp;
  	MatrixGetColumn( constraint.constraintToReference, 3, vTemp );
  	m_attachmentPointRagdollSpace = vTemp;
  }
  
  CRagdollProp *CreateServerRagdollAttached( CBaseAnimating *pAnimating, const Vector &vecForce, int forceBone, int collisionGroup, IPhysicsObject *pAttached, CBaseAnimating *pParentEntity, int boneAttach, const Vector &originAttached, int parentBoneAttach, const Vector &boneOrigin )
  {
  	// Return immediately if the model doesn't have a vcollide
  	if ( modelinfo->GetVCollide( pAnimating->GetModelIndex() ) == NULL )
  		return NULL;
  
  	CRagdollPropAttached *pRagdoll = (CRagdollPropAttached *)CBaseEntity::CreateNoSpawn( "prop_ragdoll_attached", pAnimating->GetAbsOrigin(), vec3_angle, NULL );
  	pRagdoll->CopyAnimationDataFrom( pAnimating );
  
  	pRagdoll->InitRagdollAnimation();
  	matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
  	pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
  	pRagdoll->InitRagdollAttached( pAttached, vecForce, forceBone, pBoneToWorld, pBoneToWorld, 0.1, collisionGroup, pParentEntity, boneAttach, boneOrigin, parentBoneAttach, originAttached );
  	
  	return pRagdoll;
  }
  
  void DetachAttachedRagdoll( CBaseEntity *pRagdollIn )
  {
  	CRagdollPropAttached *pRagdoll = dynamic_cast<CRagdollPropAttached *>(pRagdollIn);
  
  	if ( pRagdoll )
  	{
  		pRagdoll->DetachOnNextUpdate();
  	}
  }
  
  void DetachAttachedRagdollsForEntity( CBaseEntity *pRagdollParent )
  {
  	CUtlVector<CBaseEntity *> list;
  	GetAllChildren( pRagdollParent, list );
  	for ( int i = list.Count()-1; i >= 0; --i )
  	{
  		DetachAttachedRagdoll( list[i] );
  	}
  }
  
  bool Ragdoll_IsPropRagdoll( CBaseEntity *pEntity )
  {
  	if ( dynamic_cast<CRagdollProp *>(pEntity) != NULL )
  		return true;
  	return false;
  }
  
  ragdoll_t *Ragdoll_GetRagdoll( CBaseEntity *pEntity )
  {
  	CRagdollProp *pProp = dynamic_cast<CRagdollProp *>(pEntity);
  	if ( pProp )
  		return pProp->GetRagdoll();
  	return NULL;
  }
  
  void CRagdollProp::GetAngleOverrideFromCurrentState( char *pOut, int size )
  {
  	pOut[0] = 0;
  	for ( int i = 0; i < m_ragdoll.listCount; i++ )
  	{
  		if ( i != 0 )
  		{
  			Q_strncat( pOut, ",", size, COPY_ALL_CHARACTERS );
  
  		}
  		CFmtStr str("%d,%.2f %.2f %.2f", i, m_ragAngles[i].x, m_ragAngles[i].y, m_ragAngles[i].z );
  		Q_strncat( pOut, str, size, COPY_ALL_CHARACTERS );
  	}
  }
  
  void CRagdollProp::DisableMotion( void )
  {
  	for ( int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll )
  	{
  		IPhysicsObject *pPhysicsObject = m_ragdoll.list[ iRagdoll ].pObject;
  		if ( pPhysicsObject != NULL )
  		{
  			pPhysicsObject->EnableMotion( false );
  		}
  	}
  }
  
  void CRagdollProp::InputStartRadgollBoogie( inputdata_t &inputdata )
  {
  	float duration = inputdata.value.Float();
  
  	if( duration <= 0.0f )
  	{
  		duration = 5.0f;
  	}
  
  	CRagdollBoogie::Create( this, 100, gpGlobals->curtime, duration, 0 );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Enable physics motion and collision response (on by default)
  //-----------------------------------------------------------------------------
  void CRagdollProp::InputEnableMotion( inputdata_t &inputdata )
  {
  	for ( int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll )
  	{
  		IPhysicsObject *pPhysicsObject = m_ragdoll.list[ iRagdoll ].pObject;
  		if ( pPhysicsObject != NULL )
  		{
  			pPhysicsObject->EnableMotion( true );
  			pPhysicsObject->Wake();
  		}
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Disable any physics motion or collision response
  //-----------------------------------------------------------------------------
  void CRagdollProp::InputDisableMotion( inputdata_t &inputdata )
  {
  	DisableMotion();
  }
  
  void CRagdollProp::InputTurnOn( inputdata_t &inputdata )
  {
  	RemoveEffects( EF_NODRAW );
  }
  
  void CRagdollProp::InputTurnOff( inputdata_t &inputdata )
  {
  	AddEffects( EF_NODRAW );
  }
  
  void CRagdollProp::InputFadeAndRemove( inputdata_t &inputdata )
  {
  	float flFadeDuration = inputdata.value.Float();
  	
  	if( flFadeDuration == 0.0f )
  		flFadeDuration = 1.0f;
  
  	FadeOut( 0.0f, flFadeDuration );
  }
  
  void Ragdoll_GetAngleOverrideString( char *pOut, int size, CBaseEntity *pEntity )
  {
  	CRagdollProp *pRagdoll = dynamic_cast<CRagdollProp *>(pEntity);
  	if ( pRagdoll )
  	{
  		pRagdoll->GetAngleOverrideFromCurrentState( pOut, size );
  	}
  }
  