Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1716 lines
52 KiB

  1. //========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "baseanimating.h"
  8. #include "studio.h"
  9. #include "physics.h"
  10. #include "physics_saverestore.h"
  11. #include "ai_basenpc.h"
  12. #include "vphysics/constraints.h"
  13. #include "datacache/imdlcache.h"
  14. #include "bone_setup.h"
  15. #include "physics_prop_ragdoll.h"
  16. #include "keyvalues.h"
  17. #include "props.h"
  18. #include "RagdollBoogie.h"
  19. #include "ai_criteria.h"
  20. #include "ragdoll_shared.h"
  21. #include "hierarchy.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. //-----------------------------------------------------------------------------
  25. // Forward declarations
  26. //-----------------------------------------------------------------------------
  27. const char *GetMassEquivalent(float flMass);
  28. #define RAGDOLL_VISUALIZE 0
  29. //-----------------------------------------------------------------------------
  30. // ThinkContext
  31. //-----------------------------------------------------------------------------
  32. const char *s_pFadeOutContext = "RagdollFadeOutContext";
  33. const char *s_pDebrisContext = "DebrisContext";
  34. const float ATTACHED_DAMPING_SCALE = 50.0f;
  35. //-----------------------------------------------------------------------------
  36. // Spawnflags
  37. //-----------------------------------------------------------------------------
  38. #define SF_RAGDOLLPROP_DEBRIS 0x0004
  39. #define SF_RAGDOLLPROP_USE_LRU_RETIREMENT 0x1000
  40. #define SF_RAGDOLLPROP_ALLOW_DISSOLVE 0x2000 // Allow this prop to be dissolved
  41. #define SF_RAGDOLLPROP_MOTIONDISABLED 0x4000
  42. #define SF_RAGDOLLPROP_ALLOW_STRETCH 0x8000
  43. #define SF_RAGDOLLPROP_STARTASLEEP 0x10000
  44. //-----------------------------------------------------------------------------
  45. // Networking
  46. //-----------------------------------------------------------------------------
  47. LINK_ENTITY_TO_CLASS( physics_prop_ragdoll, CRagdollProp );
  48. LINK_ENTITY_TO_CLASS( prop_ragdoll, CRagdollProp );
  49. EXTERN_SEND_TABLE(DT_Ragdoll)
  50. IMPLEMENT_SERVERCLASS_ST(CRagdollProp, DT_Ragdoll)
  51. SendPropArray (SendPropQAngles(SENDINFO_ARRAY(m_ragAngles), 13, 0 ), m_ragAngles),
  52. SendPropArray (SendPropVector(SENDINFO_ARRAY(m_ragPos), -1, SPROP_COORD ), m_ragPos),
  53. SendPropEHandle(SENDINFO( m_hUnragdoll ) ),
  54. SendPropFloat(SENDINFO(m_flBlendWeight), 8, SPROP_ROUNDDOWN, 0.0f, 1.0f ),
  55. SendPropInt(SENDINFO(m_nOverlaySequence), 11),
  56. END_SEND_TABLE()
  57. #define DEFINE_RAGDOLL_ELEMENT( i ) \
  58. DEFINE_FIELD( m_ragdoll.list[i].originParentSpace, FIELD_VECTOR ), \
  59. DEFINE_PHYSPTR( m_ragdoll.list[i].pObject ), \
  60. DEFINE_PHYSPTR( m_ragdoll.list[i].pConstraint ), \
  61. DEFINE_FIELD( m_ragdoll.list[i].parentIndex, FIELD_INTEGER )
  62. BEGIN_DATADESC(CRagdollProp)
  63. // m_ragdoll (custom handling)
  64. DEFINE_AUTO_ARRAY ( m_ragdoll.boneIndex, FIELD_INTEGER ),
  65. DEFINE_AUTO_ARRAY ( m_ragPos, FIELD_POSITION_VECTOR ),
  66. DEFINE_AUTO_ARRAY ( m_ragAngles, FIELD_VECTOR ),
  67. DEFINE_KEYFIELD(m_anglesOverrideString, FIELD_STRING, "angleOverride" ),
  68. DEFINE_FIELD( m_lastUpdateTickCount, FIELD_INTEGER ),
  69. DEFINE_FIELD( m_allAsleep, FIELD_BOOLEAN ),
  70. DEFINE_FIELD( m_hDamageEntity, FIELD_EHANDLE ),
  71. DEFINE_FIELD( m_hKiller, FIELD_EHANDLE ),
  72. DEFINE_KEYFIELD( m_bStartDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  73. DEFINE_INPUTFUNC( FIELD_VOID, "StartRagdollBoogie", InputStartRadgollBoogie ),
  74. DEFINE_INPUTFUNC( FIELD_VOID, "EnableMotion", InputEnableMotion ),
  75. DEFINE_INPUTFUNC( FIELD_VOID, "DisableMotion", InputDisableMotion ),
  76. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputTurnOn ),
  77. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputTurnOff ),
  78. DEFINE_INPUTFUNC( FIELD_FLOAT, "FadeAndRemove", InputFadeAndRemove ),
  79. DEFINE_FIELD( m_hUnragdoll, FIELD_EHANDLE ),
  80. DEFINE_FIELD( m_bFirstCollisionAfterLaunch, FIELD_BOOLEAN ),
  81. DEFINE_FIELD( m_flBlendWeight, FIELD_FLOAT ),
  82. DEFINE_FIELD( m_nOverlaySequence, FIELD_INTEGER ),
  83. DEFINE_AUTO_ARRAY( m_ragdollMins, FIELD_VECTOR ),
  84. DEFINE_AUTO_ARRAY( m_ragdollMaxs, FIELD_VECTOR ),
  85. // Physics Influence
  86. DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ),
  87. DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ),
  88. DEFINE_FIELD( m_flFadeOutStartTime, FIELD_TIME ),
  89. DEFINE_FIELD( m_flFadeTime, FIELD_FLOAT),
  90. DEFINE_FIELD( m_strSourceClassName, FIELD_STRING ),
  91. DEFINE_FIELD( m_bHasBeenPhysgunned, FIELD_BOOLEAN ),
  92. // think functions
  93. DEFINE_THINKFUNC( SetDebrisThink ),
  94. DEFINE_THINKFUNC( ClearFlagsThink ),
  95. DEFINE_THINKFUNC( FadeOutThink ),
  96. DEFINE_FIELD( m_ragdoll.listCount, FIELD_INTEGER ),
  97. DEFINE_FIELD( m_ragdoll.allowStretch, FIELD_BOOLEAN ),
  98. DEFINE_PHYSPTR( m_ragdoll.pGroup ),
  99. DEFINE_FIELD( m_flDefaultFadeScale, FIELD_FLOAT ),
  100. //DEFINE_RAGDOLL_ELEMENT( 0 ),
  101. DEFINE_RAGDOLL_ELEMENT( 1 ),
  102. DEFINE_RAGDOLL_ELEMENT( 2 ),
  103. DEFINE_RAGDOLL_ELEMENT( 3 ),
  104. DEFINE_RAGDOLL_ELEMENT( 4 ),
  105. DEFINE_RAGDOLL_ELEMENT( 5 ),
  106. DEFINE_RAGDOLL_ELEMENT( 6 ),
  107. DEFINE_RAGDOLL_ELEMENT( 7 ),
  108. DEFINE_RAGDOLL_ELEMENT( 8 ),
  109. DEFINE_RAGDOLL_ELEMENT( 9 ),
  110. DEFINE_RAGDOLL_ELEMENT( 10 ),
  111. DEFINE_RAGDOLL_ELEMENT( 11 ),
  112. DEFINE_RAGDOLL_ELEMENT( 12 ),
  113. DEFINE_RAGDOLL_ELEMENT( 13 ),
  114. DEFINE_RAGDOLL_ELEMENT( 14 ),
  115. DEFINE_RAGDOLL_ELEMENT( 15 ),
  116. DEFINE_RAGDOLL_ELEMENT( 16 ),
  117. DEFINE_RAGDOLL_ELEMENT( 17 ),
  118. DEFINE_RAGDOLL_ELEMENT( 18 ),
  119. DEFINE_RAGDOLL_ELEMENT( 19 ),
  120. DEFINE_RAGDOLL_ELEMENT( 20 ),
  121. DEFINE_RAGDOLL_ELEMENT( 21 ),
  122. DEFINE_RAGDOLL_ELEMENT( 22 ),
  123. DEFINE_RAGDOLL_ELEMENT( 23 ),
  124. END_DATADESC()
  125. //-----------------------------------------------------------------------------
  126. // Disable auto fading under dx7 or when level fades are specified
  127. //-----------------------------------------------------------------------------
  128. void CRagdollProp::DisableAutoFade()
  129. {
  130. SetGlobalFadeScale( 0.0f );
  131. m_flDefaultFadeScale = 0;
  132. }
  133. void CRagdollProp::Spawn( void )
  134. {
  135. // Starts out as the default fade scale value
  136. m_flDefaultFadeScale = GetGlobalFadeScale();
  137. // NOTE: If this fires, then the assert or the datadesc is wrong! (see DEFINE_RAGDOLL_ELEMENT above)
  138. Assert( RAGDOLL_MAX_ELEMENTS == 24 );
  139. Precache();
  140. SetModel( STRING( GetModelName() ) );
  141. CStudioHdr *pStudioHdr = GetModelPtr( );
  142. if ( pStudioHdr->flags() & STUDIOHDR_FLAGS_NO_FORCED_FADE )
  143. {
  144. DisableAutoFade();
  145. }
  146. else
  147. {
  148. SetGlobalFadeScale( m_flDefaultFadeScale );
  149. }
  150. matrix3x4a_t pBoneToWorld[MAXSTUDIOBONES];
  151. BaseClass::SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING ); // FIXME: shouldn't this be a subset of the bones
  152. // this is useless info after the initial conditions are set
  153. SetAbsAngles( vec3_angle );
  154. int collisionGroup = (m_spawnflags & SF_RAGDOLLPROP_DEBRIS) ? COLLISION_GROUP_DEBRIS : COLLISION_GROUP_NONE;
  155. bool bWake = (m_spawnflags & SF_RAGDOLLPROP_STARTASLEEP) ? false : true;
  156. InitRagdoll( vec3_origin, 0, vec3_origin, pBoneToWorld, pBoneToWorld, 0, collisionGroup, true, bWake );
  157. m_lastUpdateTickCount = 0;
  158. m_flBlendWeight = 0.0f;
  159. m_nOverlaySequence = -1;
  160. // Unless specified, do not allow this to be dissolved
  161. if ( HasSpawnFlags( SF_RAGDOLLPROP_ALLOW_DISSOLVE ) == false )
  162. {
  163. AddEFlags( EFL_NO_DISSOLVE );
  164. }
  165. if ( HasSpawnFlags(SF_RAGDOLLPROP_MOTIONDISABLED) )
  166. {
  167. DisableMotion();
  168. }
  169. if( m_bStartDisabled )
  170. {
  171. AddEffects( EF_NODRAW );
  172. }
  173. }
  174. void CRagdollProp::SetSourceClassName( const char *pClassname )
  175. {
  176. m_strSourceClassName = MAKE_STRING( pClassname );
  177. }
  178. void CRagdollProp::OnSave( IEntitySaveUtils *pUtils )
  179. {
  180. if ( !m_ragdoll.listCount )
  181. return;
  182. // Don't save ragdoll element 0, base class saves the pointer in
  183. // m_pPhysicsObject
  184. Assert( m_ragdoll.list[0].parentIndex == -1 );
  185. Assert( m_ragdoll.list[0].pConstraint == NULL );
  186. Assert( m_ragdoll.list[0].originParentSpace == vec3_origin );
  187. Assert( m_ragdoll.list[0].pObject != NULL );
  188. VPhysicsSetObject( NULL ); // squelch a warning message
  189. VPhysicsSetObject( m_ragdoll.list[0].pObject ); // make sure object zero is saved by CBaseEntity
  190. BaseClass::OnSave( pUtils );
  191. }
  192. void CRagdollProp::OnRestore()
  193. {
  194. // rebuild element 0 since it isn't saved
  195. // NOTE: This breaks the rules - the pointer needs to get fixed in Restore()
  196. m_ragdoll.list[0].pObject = VPhysicsGetObject();
  197. m_ragdoll.list[0].parentIndex = -1;
  198. m_ragdoll.list[0].originParentSpace.Init();
  199. BaseClass::OnRestore();
  200. if ( !m_ragdoll.listCount )
  201. return;
  202. // JAY: Reset collision relationships
  203. RagdollSetupCollisions( m_ragdoll, modelinfo->GetVCollide( GetModelIndex() ), GetModelIndex() );
  204. VPhysicsUpdate( VPhysicsGetObject() );
  205. }
  206. void CRagdollProp::CalcRagdollSize( void )
  207. {
  208. CollisionProp()->SetSurroundingBoundsType( USE_HITBOXES );
  209. CollisionProp()->RemoveSolidFlags( FSOLID_FORCE_WORLD_ALIGNED );
  210. }
  211. void CRagdollProp::UpdateOnRemove( void )
  212. {
  213. for ( int i = 0; i < m_ragdoll.listCount; i++ )
  214. {
  215. if ( m_ragdoll.list[i].pObject )
  216. {
  217. g_pPhysSaveRestoreManager->ForgetModel( m_ragdoll.list[i].pObject );
  218. }
  219. }
  220. // Set to null so that the destructor's call to DestroyObject won't destroy
  221. // m_pObjects[ 0 ] twice since that's the physics object for the prop
  222. VPhysicsSetObject( NULL );
  223. RagdollDestroy( m_ragdoll );
  224. // Chain to base after doing our own cleanup to mimic
  225. // destructor unwind order
  226. BaseClass::UpdateOnRemove();
  227. }
  228. CRagdollProp::CRagdollProp( void )
  229. {
  230. m_strSourceClassName = NULL_STRING;
  231. m_anglesOverrideString = NULL_STRING;
  232. m_ragdoll.listCount = 0;
  233. Assert( (1<<RAGDOLL_INDEX_BITS) >=RAGDOLL_MAX_ELEMENTS );
  234. m_allAsleep = false;
  235. SetGlobalFadeScale( 1.0f );
  236. m_flDefaultFadeScale = 1.0f;
  237. }
  238. CRagdollProp::~CRagdollProp( void )
  239. {
  240. }
  241. void CRagdollProp::Precache( void )
  242. {
  243. PrecacheModel( STRING( GetModelName() ) );
  244. BaseClass::Precache();
  245. }
  246. int CRagdollProp::ObjectCaps()
  247. {
  248. return BaseClass::ObjectCaps() | FCAP_WCEDIT_POSITION;
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Purpose:
  252. //-----------------------------------------------------------------------------
  253. void CRagdollProp::InitRagdollAnimation()
  254. {
  255. m_flAnimTime = gpGlobals->curtime;
  256. m_flPlaybackRate = 0.0;
  257. SetCycle( 0 );
  258. // put into ACT_DIERAGDOLL if it exists, otherwise use sequence 0
  259. int nSequence = SelectWeightedSequence( ACT_DIERAGDOLL );
  260. if ( nSequence < 0 )
  261. {
  262. ResetSequence( 0 );
  263. }
  264. else
  265. {
  266. ResetSequence( nSequence );
  267. }
  268. }
  269. //-----------------------------------------------------------------------------
  270. // Response system stuff
  271. //-----------------------------------------------------------------------------
  272. IResponseSystem *CRagdollProp::GetResponseSystem()
  273. {
  274. extern IResponseSystem *g_pResponseSystem;
  275. // Just use the general NPC response system; we often come from NPCs after all
  276. return g_pResponseSystem;
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Purpose:
  280. //-----------------------------------------------------------------------------
  281. void CRagdollProp::ModifyOrAppendCriteria( AI_CriteriaSet& set )
  282. {
  283. BaseClass::ModifyOrAppendCriteria( set );
  284. if ( m_strSourceClassName != NULL_STRING )
  285. {
  286. set.RemoveCriteria( "classname" );
  287. set.AppendCriteria( "classname", STRING(m_strSourceClassName) );
  288. set.AppendCriteria( "ragdoll", "1" );
  289. }
  290. }
  291. //-----------------------------------------------------------------------------
  292. // Purpose:
  293. //-----------------------------------------------------------------------------
  294. void CRagdollProp::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  295. {
  296. m_hPhysicsAttacker = pPhysGunUser;
  297. m_flLastPhysicsInfluenceTime = gpGlobals->curtime;
  298. // Clear out the classname if we've been physgunned before
  299. // so that the screams, etc. don't happen. Simulate that the first
  300. // major punt or throw has been enough to kill him.
  301. if ( m_bHasBeenPhysgunned )
  302. {
  303. m_strSourceClassName = NULL_STRING;
  304. }
  305. m_bHasBeenPhysgunned = true;
  306. if( HasPhysgunInteraction( "onpickup", "boogie" ) )
  307. {
  308. if ( reason == PUNTED_BY_CANNON )
  309. {
  310. CRagdollBoogie::Create( this, 150, gpGlobals->curtime, 3.0f, SF_RAGDOLL_BOOGIE_ELECTRICAL );
  311. }
  312. else
  313. {
  314. CRagdollBoogie::Create( this, 150, gpGlobals->curtime, 2.0f, 0.0f );
  315. }
  316. }
  317. if ( HasSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT ) )
  318. {
  319. s_RagdollLRU.MoveToTopOfLRU( this );
  320. }
  321. if ( !HasSpawnFlags( SF_PHYSPROP_ENABLE_ON_PHYSCANNON ) )
  322. return;
  323. ragdoll_t *pRagdollPhys = GetRagdoll( );
  324. for ( int j = 0; j < pRagdollPhys->listCount; ++j )
  325. {
  326. pRagdollPhys->list[j].pObject->Wake();
  327. pRagdollPhys->list[j].pObject->EnableMotion( true );
  328. }
  329. }
  330. //-----------------------------------------------------------------------------
  331. // Purpose:
  332. //-----------------------------------------------------------------------------
  333. void CRagdollProp::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
  334. {
  335. CDefaultPlayerPickupVPhysics::OnPhysGunDrop( pPhysGunUser, Reason );
  336. m_hPhysicsAttacker = pPhysGunUser;
  337. m_flLastPhysicsInfluenceTime = gpGlobals->curtime;
  338. if( HasPhysgunInteraction( "onpickup", "boogie" ) )
  339. {
  340. CRagdollBoogie::Create( this, 150, gpGlobals->curtime, 3.0f, SF_RAGDOLL_BOOGIE_ELECTRICAL );
  341. }
  342. if ( HasSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT ) )
  343. {
  344. s_RagdollLRU.MoveToTopOfLRU( this );
  345. }
  346. // Make sure it's interactive debris for at most 5 seconds
  347. if ( GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  348. {
  349. SetContextThink( &CRagdollProp::SetDebrisThink, gpGlobals->curtime + 5, s_pDebrisContext );
  350. }
  351. if ( Reason != LAUNCHED_BY_CANNON )
  352. return;
  353. if( HasPhysgunInteraction( "onlaunch", "spin_zaxis" ) )
  354. {
  355. Vector vecAverageCenter( 0, 0, 0 );
  356. // Get the average position, apply forces to produce a spin
  357. int j;
  358. ragdoll_t *pRagdollPhys = GetRagdoll( );
  359. for ( j = 0; j < pRagdollPhys->listCount; ++j )
  360. {
  361. Vector vecCenter;
  362. pRagdollPhys->list[j].pObject->GetPosition( &vecCenter, NULL );
  363. vecAverageCenter += vecCenter;
  364. }
  365. vecAverageCenter /= pRagdollPhys->listCount;
  366. Vector vecZAxis( 0, 0, 1 );
  367. for ( j = 0; j < pRagdollPhys->listCount; ++j )
  368. {
  369. Vector vecDelta;
  370. pRagdollPhys->list[j].pObject->GetPosition( &vecDelta, NULL );
  371. vecDelta -= vecAverageCenter;
  372. Vector vecDir;
  373. CrossProduct( vecZAxis, vecDelta, vecDir );
  374. vecDir *= 100;
  375. pRagdollPhys->list[j].pObject->AddVelocity( &vecDir, NULL );
  376. }
  377. }
  378. PhysSetGameFlags( VPhysicsGetObject(), FVPHYSICS_WAS_THROWN );
  379. m_bFirstCollisionAfterLaunch = true;
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Physics attacker
  383. //-----------------------------------------------------------------------------
  384. CBasePlayer *CRagdollProp::HasPhysicsAttacker( float dt )
  385. {
  386. if (gpGlobals->curtime - dt <= m_flLastPhysicsInfluenceTime)
  387. {
  388. return m_hPhysicsAttacker;
  389. }
  390. return NULL;
  391. }
  392. //-----------------------------------------------------------------------------
  393. // Purpose:
  394. //-----------------------------------------------------------------------------
  395. void CRagdollProp::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  396. {
  397. BaseClass::VPhysicsCollision( index, pEvent );
  398. CBaseEntity *pHitEntity = pEvent->pEntities[!index];
  399. if ( pHitEntity == this )
  400. return;
  401. // Don't take physics damage from whoever's holding him with the physcannon.
  402. if ( VPhysicsGetObject() && (VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) )
  403. {
  404. if ( pHitEntity && (pHitEntity == HasPhysicsAttacker( FLT_MAX )) )
  405. return;
  406. }
  407. // Don't bother taking damage from the physics attacker
  408. if ( pHitEntity && HasPhysicsAttacker( 0.5f ) == pHitEntity )
  409. return;
  410. if( m_bFirstCollisionAfterLaunch )
  411. {
  412. HandleFirstCollisionInteractions( index, pEvent );
  413. }
  414. if ( m_takedamage != DAMAGE_NO )
  415. {
  416. int damageType = 0;
  417. float damage = CalculateDefaultPhysicsDamage( index, pEvent, 1.0f, true, damageType );
  418. if ( damage > 0 )
  419. {
  420. // Take extra damage after we're punted by the physcannon
  421. if ( m_bFirstCollisionAfterLaunch )
  422. {
  423. damage *= 10;
  424. }
  425. CBaseEntity *pHitEntity = pEvent->pEntities[!index];
  426. if ( !pHitEntity )
  427. {
  428. // hit world
  429. pHitEntity = GetContainingEntity( INDEXENT(0) );
  430. }
  431. Vector damagePos;
  432. pEvent->pInternalData->GetContactPoint( damagePos );
  433. Vector damageForce = pEvent->postVelocity[index] * pEvent->pObjects[index]->GetMass();
  434. if ( damageForce == vec3_origin )
  435. {
  436. // This can happen if this entity is motion disabled, and can't move.
  437. // Use the velocity of the entity that hit us instead.
  438. damageForce = pEvent->postVelocity[!index] * pEvent->pObjects[!index]->GetMass();
  439. }
  440. // FIXME: this doesn't pass in who is responsible if some other entity "caused" this collision
  441. PhysCallbackDamage( this, CTakeDamageInfo( pHitEntity, pHitEntity, damageForce, damagePos, damage, damageType ), *pEvent, index );
  442. }
  443. }
  444. if ( m_bFirstCollisionAfterLaunch )
  445. {
  446. // Setup the think function to remove the flags
  447. SetThink( &CRagdollProp::ClearFlagsThink );
  448. SetNextThink( gpGlobals->curtime );
  449. }
  450. }
  451. //-----------------------------------------------------------------------------
  452. //-----------------------------------------------------------------------------
  453. bool CRagdollProp::HasPhysgunInteraction( const char *pszKeyName, const char *pszValue )
  454. {
  455. KeyValues *modelKeyValues = new KeyValues("");
  456. if ( modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), modelinfo->GetModelKeyValueText( GetModel() ) ) )
  457. {
  458. KeyValues *pkvPropData = modelKeyValues->FindKey("physgun_interactions");
  459. if ( pkvPropData )
  460. {
  461. char const *pszBase = pkvPropData->GetString( pszKeyName );
  462. if ( pszBase && pszBase[0] && !stricmp( pszBase, pszValue ) )
  463. {
  464. modelKeyValues->deleteThis();
  465. return true;
  466. }
  467. }
  468. }
  469. modelKeyValues->deleteThis();
  470. return false;
  471. }
  472. //-----------------------------------------------------------------------------
  473. //-----------------------------------------------------------------------------
  474. void CRagdollProp::HandleFirstCollisionInteractions( int index, gamevcollisionevent_t *pEvent )
  475. {
  476. IPhysicsObject *pObj = VPhysicsGetObject();
  477. if ( !pObj)
  478. return;
  479. if( HasPhysgunInteraction( "onfirstimpact", "break" ) )
  480. {
  481. // Looks like it's best to break by having the object damage itself.
  482. CTakeDamageInfo info;
  483. info.SetDamage( m_iHealth );
  484. info.SetAttacker( this );
  485. info.SetInflictor( this );
  486. info.SetDamageType( DMG_GENERIC );
  487. Vector vecPosition;
  488. Vector vecVelocity;
  489. VPhysicsGetObject()->GetVelocity( &vecVelocity, NULL );
  490. VPhysicsGetObject()->GetPosition( &vecPosition, NULL );
  491. info.SetDamageForce( vecVelocity );
  492. info.SetDamagePosition( vecPosition );
  493. TakeDamage( info );
  494. return;
  495. }
  496. if( HasPhysgunInteraction( "onfirstimpact", "paintsplat" ) )
  497. {
  498. IPhysicsObject *pObj = VPhysicsGetObject();
  499. Vector vecPos;
  500. pObj->GetPosition( &vecPos, NULL );
  501. trace_t tr;
  502. UTIL_TraceLine( vecPos, vecPos + pEvent->preVelocity[0] * 1.5, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  503. switch( random->RandomInt( 1, 3 ) )
  504. {
  505. case 1:
  506. UTIL_DecalTrace( &tr, "PaintSplatBlue" );
  507. break;
  508. case 2:
  509. UTIL_DecalTrace( &tr, "PaintSplatGreen" );
  510. break;
  511. case 3:
  512. UTIL_DecalTrace( &tr, "PaintSplatPink" );
  513. break;
  514. }
  515. }
  516. bool bAlienBloodSplat = HasPhysgunInteraction( "onfirstimpact", "alienbloodsplat" );
  517. if( bAlienBloodSplat || HasPhysgunInteraction( "onfirstimpact", "bloodsplat" ) )
  518. {
  519. IPhysicsObject *pObj = VPhysicsGetObject();
  520. Vector vecPos;
  521. pObj->GetPosition( &vecPos, NULL );
  522. trace_t tr;
  523. UTIL_TraceLine( vecPos, vecPos + pEvent->preVelocity[0] * 1.5, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  524. UTIL_BloodDecalTrace( &tr, bAlienBloodSplat ? BLOOD_COLOR_GREEN : BLOOD_COLOR_RED );
  525. }
  526. }
  527. //-----------------------------------------------------------------------------
  528. // Purpose:
  529. //-----------------------------------------------------------------------------
  530. void CRagdollProp::ClearFlagsThink( void )
  531. {
  532. PhysClearGameFlags( VPhysicsGetObject(), FVPHYSICS_WAS_THROWN );
  533. m_bFirstCollisionAfterLaunch = false;
  534. SetThink( NULL );
  535. }
  536. //-----------------------------------------------------------------------------
  537. //-----------------------------------------------------------------------------
  538. AngularImpulse CRagdollProp::PhysGunLaunchAngularImpulse()
  539. {
  540. if( HasPhysgunInteraction( "onlaunch", "spin_zaxis" ) )
  541. {
  542. // Don't add in random angular impulse if this object is supposed to spin in a specific way.
  543. AngularImpulse ang( 0, 0, 0 );
  544. return ang;
  545. }
  546. return CDefaultPlayerPickupVPhysics::PhysGunLaunchAngularImpulse();
  547. }
  548. //-----------------------------------------------------------------------------
  549. // Purpose:
  550. // Input : activity -
  551. //-----------------------------------------------------------------------------
  552. void CRagdollProp::SetOverlaySequence( Activity activity )
  553. {
  554. int seq = SelectWeightedSequence( activity );
  555. if ( seq < 0 )
  556. {
  557. m_nOverlaySequence = -1;
  558. }
  559. else
  560. {
  561. m_nOverlaySequence = seq;
  562. }
  563. }
  564. 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 )
  565. {
  566. SetCollisionGroup( collisionGroup );
  567. // Make sure it's interactive debris for at most 5 seconds
  568. if ( collisionGroup == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  569. {
  570. SetContextThink( &CRagdollProp::SetDebrisThink, gpGlobals->curtime + 5, s_pDebrisContext );
  571. }
  572. SetMoveType( MOVETYPE_VPHYSICS );
  573. SetSolid( SOLID_VPHYSICS );
  574. AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
  575. m_takedamage = DAMAGE_EVENTS_ONLY;
  576. ragdollparams_t params;
  577. params.pGameData = static_cast<void *>( static_cast<CBaseEntity *>(this) );
  578. params.modelIndex = GetModelIndex();
  579. params.pCollide = modelinfo->GetVCollide( params.modelIndex );
  580. params.pStudioHdr = GetModelPtr();
  581. params.forceVector = forceVector;
  582. params.forceBoneIndex = forceBone;
  583. params.forcePosition = forcePos;
  584. params.pCurrentBones = pBoneToWorld;
  585. params.jointFrictionScale = 1.0;
  586. params.allowStretch = HasSpawnFlags(SF_RAGDOLLPROP_ALLOW_STRETCH);
  587. params.fixedConstraints = false;
  588. RagdollCreate( m_ragdoll, params, physenv );
  589. RagdollApplyAnimationAsVelocity( m_ragdoll, pPrevBones, pBoneToWorld, dt );
  590. if ( m_anglesOverrideString != NULL_STRING && Q_strlen(m_anglesOverrideString.ToCStr()) > 0 )
  591. {
  592. char szToken[2048];
  593. const char *pStr = nexttoken(szToken, STRING(m_anglesOverrideString), ',');
  594. // anglesOverride is index,angles,index,angles (e.g. "1, 22.5 123.0 0.0, 2, 0 0 0, 3, 0 0 180.0")
  595. while ( szToken[0] != 0 )
  596. {
  597. int objectIndex = atoi(szToken);
  598. // sanity check to make sure this token is an integer
  599. Assert( atof(szToken) == ((float)objectIndex) );
  600. pStr = nexttoken(szToken, pStr, ',');
  601. Assert( szToken[0] );
  602. if ( objectIndex >= m_ragdoll.listCount )
  603. {
  604. Warning("Bad ragdoll pose in entity %s, model (%s) at %s, model changed?\n", GetDebugName(), GetModelName().ToCStr(), VecToString(GetAbsOrigin()) );
  605. }
  606. else if ( szToken[0] != 0 )
  607. {
  608. QAngle angles;
  609. Assert( objectIndex >= 0 && objectIndex < RAGDOLL_MAX_ELEMENTS );
  610. UTIL_StringToVector( angles.Base(), szToken );
  611. int boneIndex = m_ragdoll.boneIndex[objectIndex];
  612. AngleMatrix( angles, pBoneToWorld[boneIndex] );
  613. const ragdollelement_t &element = m_ragdoll.list[objectIndex];
  614. Vector out;
  615. if ( element.parentIndex >= 0 )
  616. {
  617. int parentBoneIndex = m_ragdoll.boneIndex[element.parentIndex];
  618. VectorTransform( element.originParentSpace, pBoneToWorld[parentBoneIndex], out );
  619. }
  620. else
  621. {
  622. out = GetAbsOrigin();
  623. }
  624. MatrixSetColumn( out, 3, pBoneToWorld[boneIndex] );
  625. element.pObject->SetPositionMatrix( pBoneToWorld[boneIndex], true );
  626. }
  627. pStr = nexttoken(szToken, pStr, ',');
  628. }
  629. }
  630. if ( activateRagdoll )
  631. {
  632. MEM_ALLOC_CREDIT();
  633. RagdollActivate( m_ragdoll, params.pCollide, GetModelIndex(), bWakeRagdoll );
  634. }
  635. for ( int i = 0; i < m_ragdoll.listCount; i++ )
  636. {
  637. UpdateNetworkDataFromVPhysics( m_ragdoll.list[i].pObject, i );
  638. g_pPhysSaveRestoreManager->AssociateModel( m_ragdoll.list[i].pObject, GetModelIndex() );
  639. physcollision->CollideGetAABB( &m_ragdollMins[i], &m_ragdollMaxs[i], m_ragdoll.list[i].pObject->GetCollide(), vec3_origin, vec3_angle );
  640. }
  641. VPhysicsSetObject( m_ragdoll.list[0].pObject );
  642. CalcRagdollSize();
  643. }
  644. void CRagdollProp::SetDebrisThink()
  645. {
  646. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  647. RecheckCollisionFilter();
  648. }
  649. void CRagdollProp::SetDamageEntity( CBaseEntity *pEntity )
  650. {
  651. // Damage passing
  652. m_hDamageEntity = pEntity;
  653. // Set our takedamage to match it
  654. if ( pEntity )
  655. {
  656. m_takedamage = pEntity->m_takedamage;
  657. }
  658. else
  659. {
  660. m_takedamage = DAMAGE_EVENTS_ONLY;
  661. }
  662. }
  663. //-----------------------------------------------------------------------------
  664. // Purpose:
  665. //-----------------------------------------------------------------------------
  666. int CRagdollProp::OnTakeDamage( const CTakeDamageInfo &info )
  667. {
  668. // If we have a damage entity, we want to pass damage to it. Add the
  669. // Never Ragdoll flag, on the assumption that if the entity dies, we'll
  670. // actually be taking the role of its ragdoll.
  671. if ( m_hDamageEntity.Get() )
  672. {
  673. CTakeDamageInfo subInfo = info;
  674. subInfo.AddDamageType( DMG_REMOVENORAGDOLL );
  675. return m_hDamageEntity->OnTakeDamage( subInfo );
  676. }
  677. return BaseClass::OnTakeDamage( info );
  678. }
  679. //-----------------------------------------------------------------------------
  680. // Purpose: Force all the ragdoll's bone's physics objects to recheck their collision filters
  681. //-----------------------------------------------------------------------------
  682. void CRagdollProp::RecheckCollisionFilter( void )
  683. {
  684. for ( int i = 0; i < m_ragdoll.listCount; i++ )
  685. {
  686. m_ragdoll.list[i].pObject->RecheckCollisionFilter();
  687. }
  688. }
  689. void CRagdollProp::TraceAttack( const CTakeDamageInfo &info, const Vector &dir, trace_t *ptr )
  690. {
  691. if ( ptr->physicsbone >= 0 && ptr->physicsbone < m_ragdoll.listCount )
  692. {
  693. VPhysicsSwapObject( m_ragdoll.list[ptr->physicsbone].pObject );
  694. }
  695. BaseClass::TraceAttack( info, dir, ptr );
  696. }
  697. void CRagdollProp::SetupBones( matrix3x4a_t *pBoneToWorld, int boneMask )
  698. {
  699. // no ragdoll, fall through to base class
  700. if ( !m_ragdoll.listCount )
  701. {
  702. BaseClass::SetupBones( pBoneToWorld, boneMask );
  703. return;
  704. }
  705. // Not really ideal, but it'll work for now
  706. UpdateModelScale();
  707. MDLCACHE_CRITICAL_SECTION();
  708. CStudioHdr *pStudioHdr = GetModelPtr( );
  709. bool sim[MAXSTUDIOBONES];
  710. memset( sim, 0, pStudioHdr->numbones() );
  711. int i;
  712. CBoneAccessor boneaccessor( pBoneToWorld );
  713. for ( i = 0; i < m_ragdoll.listCount; i++ )
  714. {
  715. // during restore this may be NULL
  716. if ( !m_ragdoll.list[i].pObject )
  717. continue;
  718. if ( RagdollGetBoneMatrix( m_ragdoll, boneaccessor, i ) )
  719. {
  720. sim[m_ragdoll.boneIndex[i]] = true;
  721. }
  722. }
  723. const mstudiobone_t *pbones = pStudioHdr->pBone( 0 );
  724. for ( i = 0; i < pStudioHdr->numbones(); i++ )
  725. {
  726. if ( sim[i] )
  727. continue;
  728. if ( !(pStudioHdr->boneFlags(i) & boneMask) )
  729. continue;
  730. matrix3x4_t matBoneLocal;
  731. AngleMatrix( pbones[i].rot, pbones[i].pos, matBoneLocal );
  732. ConcatTransforms( pBoneToWorld[pbones[i].parent], matBoneLocal, pBoneToWorld[i]);
  733. }
  734. }
  735. bool CRagdollProp::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
  736. {
  737. #if 0
  738. // PERFORMANCE: Use hitboxes for rays instead of vcollides if this is a performance problem
  739. if ( ray.m_IsRay )
  740. {
  741. return BaseClass::TestCollision( ray, mask, trace );
  742. }
  743. #endif
  744. MDLCACHE_CRITICAL_SECTION();
  745. CStudioHdr *pStudioHdr = GetModelPtr( );
  746. if (!pStudioHdr)
  747. return false;
  748. // Just iterate all of the elements and trace the box against each one.
  749. // NOTE: This is pretty expensive for small/dense characters
  750. trace_t tr;
  751. for ( int i = 0; i < m_ragdoll.listCount; i++ )
  752. {
  753. Vector position;
  754. QAngle angles;
  755. if( m_ragdoll.list[i].pObject )
  756. {
  757. m_ragdoll.list[i].pObject->GetPosition( &position, &angles );
  758. physcollision->TraceBox( ray, m_ragdoll.list[i].pObject->GetCollide(), position, angles, &tr );
  759. if ( tr.fraction < trace.fraction )
  760. {
  761. tr.physicsbone = i;
  762. tr.surface.surfaceProps = m_ragdoll.list[i].pObject->GetMaterialIndex();
  763. trace = tr;
  764. }
  765. }
  766. else
  767. {
  768. DevWarning("Bogus object in Ragdoll Prop's ragdoll list!\n");
  769. }
  770. }
  771. if ( trace.fraction >= 1 )
  772. {
  773. return false;
  774. }
  775. return true;
  776. }
  777. void CRagdollProp::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity, bool bUseSlowHighAccuracyContacts )
  778. {
  779. // newAngles is a relative transform for the entity
  780. // But a ragdoll entity has identity orientation by design
  781. // so we compute a relative transform here based on the previous transform
  782. matrix3x4_t startMatrixInv;
  783. MatrixInvert( EntityToWorldTransform(), startMatrixInv );
  784. matrix3x4_t endMatrix;
  785. MatrixCopy( EntityToWorldTransform(), endMatrix );
  786. if ( newAngles )
  787. {
  788. AngleMatrix( *newAngles, endMatrix );
  789. }
  790. if ( newPosition )
  791. {
  792. PositionMatrix( *newPosition, endMatrix );
  793. }
  794. // now endMatrix is the refernce matrix for the entity at the target position
  795. matrix3x4_t xform;
  796. ConcatTransforms( endMatrix, startMatrixInv, xform );
  797. // now xform is the relative transform the entity must undergo
  798. // we need to call the base class and it will teleport our vphysics object,
  799. // so set object 0 up and compute the origin/angles for its new position (base implementation has side effects)
  800. VPhysicsSwapObject( m_ragdoll.list[0].pObject );
  801. matrix3x4_t obj0source, obj0Target;
  802. m_ragdoll.list[0].pObject->GetPositionMatrix( &obj0source );
  803. ConcatTransforms( xform, obj0source, obj0Target );
  804. Vector obj0Pos;
  805. QAngle obj0Angles;
  806. MatrixAngles( obj0Target, obj0Angles, obj0Pos );
  807. BaseClass::Teleport( &obj0Pos, &obj0Angles, newVelocity, bUseSlowHighAccuracyContacts );
  808. for ( int i = 1; i < m_ragdoll.listCount; i++ )
  809. {
  810. matrix3x4_t matrix, newMatrix;
  811. m_ragdoll.list[i].pObject->GetPositionMatrix( &matrix );
  812. ConcatTransforms( xform, matrix, newMatrix );
  813. m_ragdoll.list[i].pObject->SetPositionMatrix( newMatrix, true );
  814. UpdateNetworkDataFromVPhysics( m_ragdoll.list[i].pObject, i );
  815. }
  816. // fixup/relink object 0
  817. UpdateNetworkDataFromVPhysics( m_ragdoll.list[0].pObject, 0 );
  818. }
  819. void CRagdollProp::VPhysicsUpdate( IPhysicsObject *pPhysics )
  820. {
  821. if ( m_lastUpdateTickCount == (unsigned int)gpGlobals->tickcount )
  822. return;
  823. m_lastUpdateTickCount = gpGlobals->tickcount;
  824. //NetworkStateChanged();
  825. matrix3x4a_t boneToWorld[MAXSTUDIOBONES];
  826. QAngle angles;
  827. Vector surroundingMins, surroundingMaxs;
  828. int i;
  829. for ( i = 0; i < m_ragdoll.listCount; i++ )
  830. {
  831. CBoneAccessor boneaccessor( boneToWorld );
  832. if ( RagdollGetBoneMatrix( m_ragdoll, boneaccessor, i ) )
  833. {
  834. Vector vNewPos;
  835. MatrixAngles( boneToWorld[m_ragdoll.boneIndex[i]], angles, vNewPos );
  836. m_ragPos.Set( i, vNewPos );
  837. m_ragAngles.Set( i, angles );
  838. }
  839. else
  840. {
  841. m_ragPos.GetForModify(i).Init();
  842. m_ragAngles.GetForModify(i).Init();
  843. }
  844. }
  845. // BUGBUG: Use the ragdollmins/maxs to do this instead of the collides
  846. m_allAsleep = RagdollIsAsleep( m_ragdoll );
  847. // Don't scream after you've come to rest
  848. if ( m_allAsleep )
  849. {
  850. m_strSourceClassName = NULL_STRING;
  851. }
  852. else
  853. {
  854. if ( m_ragdoll.pGroup->IsInErrorState() )
  855. {
  856. RagdollSolveSeparation( m_ragdoll, this );
  857. }
  858. }
  859. // Interactive debris converts back to debris when it comes to rest
  860. if ( m_allAsleep && GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  861. {
  862. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  863. RecheckCollisionFilter();
  864. SetContextThink( NULL, gpGlobals->curtime, s_pDebrisContext );
  865. }
  866. Vector vecFullMins, vecFullMaxs;
  867. vecFullMins = m_ragPos[0];
  868. vecFullMaxs = m_ragPos[0];
  869. for ( i = 0; i < m_ragdoll.listCount; i++ )
  870. {
  871. Vector mins, maxs;
  872. matrix3x4_t update;
  873. if ( !m_ragdoll.list[i].pObject )
  874. {
  875. m_ragdollMins[i].Init();
  876. m_ragdollMaxs[i].Init();
  877. continue;
  878. }
  879. m_ragdoll.list[i].pObject->GetPositionMatrix( &update );
  880. TransformAABB( update, m_ragdollMins[i], m_ragdollMaxs[i], mins, maxs );
  881. for ( int j = 0; j < 3; j++ )
  882. {
  883. if ( mins[j] < vecFullMins[j] )
  884. {
  885. vecFullMins[j] = mins[j];
  886. }
  887. if ( maxs[j] > vecFullMaxs[j] )
  888. {
  889. vecFullMaxs[j] = maxs[j];
  890. }
  891. }
  892. }
  893. SetAbsOrigin( m_ragPos[0] );
  894. SetAbsAngles( vec3_angle );
  895. const Vector &vecOrigin = CollisionProp()->GetCollisionOrigin();
  896. CollisionProp()->AddSolidFlags( FSOLID_FORCE_WORLD_ALIGNED );
  897. CollisionProp()->SetSurroundingBoundsType( USE_COLLISION_BOUNDS_NEVER_VPHYSICS );
  898. SetCollisionBounds( vecFullMins - vecOrigin, vecFullMaxs - vecOrigin );
  899. CollisionProp()->MarkSurroundingBoundsDirty();
  900. PhysicsTouchTriggers();
  901. }
  902. int CRagdollProp::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax )
  903. {
  904. for ( int i = 0; i < m_ragdoll.listCount; i++ )
  905. {
  906. if ( i < listMax )
  907. {
  908. pList[i] = m_ragdoll.list[i].pObject;
  909. }
  910. }
  911. return m_ragdoll.listCount;
  912. }
  913. void CRagdollProp::UpdateNetworkDataFromVPhysics( IPhysicsObject *pPhysics, int index )
  914. {
  915. Assert(index < m_ragdoll.listCount);
  916. QAngle angles;
  917. Vector vPos;
  918. m_ragdoll.list[index].pObject->GetPosition( &vPos, &angles );
  919. m_ragPos.Set( index, vPos );
  920. m_ragAngles.Set( index, angles );
  921. // move/relink if root moved
  922. if ( index == 0 )
  923. {
  924. SetAbsOrigin( m_ragPos[0] );
  925. PhysicsTouchTriggers();
  926. }
  927. }
  928. //-----------------------------------------------------------------------------
  929. // Fade out due to the LRU telling it do
  930. //-----------------------------------------------------------------------------
  931. #define FADE_OUT_LENGTH 0.5f
  932. void CRagdollProp::FadeOut( float flDelay, float fadeTime )
  933. {
  934. if ( IsFading() )
  935. return;
  936. m_flFadeTime = ( fadeTime == -1 ) ? FADE_OUT_LENGTH : fadeTime;
  937. m_flFadeOutStartTime = gpGlobals->curtime + flDelay;
  938. SetGlobalFadeScale( 0 );
  939. SetContextThink( &CRagdollProp::FadeOutThink, gpGlobals->curtime + flDelay + 0.01f, s_pFadeOutContext );
  940. }
  941. bool CRagdollProp::IsFading()
  942. {
  943. return ( GetNextThink( s_pFadeOutContext ) >= gpGlobals->curtime );
  944. }
  945. void CRagdollProp::FadeOutThink(void)
  946. {
  947. float dt = gpGlobals->curtime - m_flFadeOutStartTime;
  948. if ( dt < 0 )
  949. {
  950. SetContextThink( &CRagdollProp::FadeOutThink, gpGlobals->curtime + 0.1, s_pFadeOutContext );
  951. }
  952. else if ( dt < m_flFadeTime )
  953. {
  954. float alpha = 1.0f - dt / m_flFadeTime;
  955. int nFade = (int)(alpha * 255.0f);
  956. m_nRenderMode = kRenderTransTexture;
  957. SetRenderAlpha( nFade );
  958. NetworkStateChanged();
  959. SetContextThink( &CRagdollProp::FadeOutThink, gpGlobals->curtime + TICK_INTERVAL, s_pFadeOutContext );
  960. }
  961. else
  962. {
  963. // Necessary to cause it to do the appropriate death cleanup
  964. // Yeah, the player may have nothing to do with it, but
  965. // passing NULL to TakeDamage causes bad things to happen
  966. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  967. CTakeDamageInfo info( pPlayer, pPlayer, 10000.0, DMG_GENERIC );
  968. TakeDamage( info );
  969. UTIL_Remove( this );
  970. }
  971. }
  972. //-----------------------------------------------------------------------------
  973. // Purpose: Draw any debug text overlays
  974. // Output : Current text offset from the top
  975. //-----------------------------------------------------------------------------
  976. int CRagdollProp::DrawDebugTextOverlays(void)
  977. {
  978. int text_offset = BaseClass::DrawDebugTextOverlays();
  979. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  980. {
  981. if (m_ragdoll.listCount)
  982. {
  983. float mass = 0;
  984. for ( int i = 0; i < m_ragdoll.listCount; i++ )
  985. {
  986. if ( m_ragdoll.list[i].pObject != NULL )
  987. {
  988. mass += m_ragdoll.list[i].pObject->GetMass();
  989. }
  990. }
  991. char tempstr[512];
  992. Q_snprintf(tempstr, sizeof(tempstr),"Mass: %.2f kg / %.2f lb (%s)", mass, kg2lbs(mass), GetMassEquivalent(mass) );
  993. EntityText( text_offset, tempstr, 0);
  994. text_offset++;
  995. }
  996. }
  997. return text_offset;
  998. }
  999. void CRagdollProp::DrawDebugGeometryOverlays()
  1000. {
  1001. if (m_debugOverlays & OVERLAY_BBOX_BIT)
  1002. {
  1003. DrawServerHitboxes();
  1004. }
  1005. if (m_debugOverlays & OVERLAY_PIVOT_BIT)
  1006. {
  1007. for ( int i = 0; i < m_ragdoll.listCount; i++ )
  1008. {
  1009. if ( m_ragdoll.list[i].pObject )
  1010. {
  1011. float mass = m_ragdoll.list[i].pObject->GetMass();
  1012. Vector pos;
  1013. m_ragdoll.list[i].pObject->GetPosition( &pos, NULL );
  1014. CFmtStr str("mass %.1f", mass );
  1015. NDebugOverlay::EntityTextAtPosition( pos, 0, str.Access(), 0, 0, 255, 0, 255 );
  1016. }
  1017. }
  1018. }
  1019. BaseClass::DrawDebugGeometryOverlays();
  1020. }
  1021. //-----------------------------------------------------------------------------
  1022. // Purpose:
  1023. // Input : *pOther -
  1024. //-----------------------------------------------------------------------------
  1025. void CRagdollProp::SetUnragdoll( CBaseAnimating *pOther )
  1026. {
  1027. m_hUnragdoll = pOther;
  1028. }
  1029. //===============================================================================================================
  1030. // RagdollPropAttached
  1031. //===============================================================================================================
  1032. class CRagdollPropAttached : public CRagdollProp
  1033. {
  1034. DECLARE_CLASS( CRagdollPropAttached, CRagdollProp );
  1035. public:
  1036. CRagdollPropAttached()
  1037. {
  1038. m_bShouldDetach = false;
  1039. }
  1040. ~CRagdollPropAttached()
  1041. {
  1042. physenv->DestroyConstraint( m_pAttachConstraint );
  1043. m_pAttachConstraint = NULL;
  1044. }
  1045. 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 );
  1046. void DetachOnNextUpdate();
  1047. void VPhysicsUpdate( IPhysicsObject *pPhysics );
  1048. DECLARE_SERVERCLASS();
  1049. DECLARE_DATADESC();
  1050. private:
  1051. void Detach();
  1052. CNetworkVar( int, m_boneIndexAttached );
  1053. CNetworkVar( int, m_ragdollAttachedObjectIndex );
  1054. CNetworkVector( m_attachmentPointBoneSpace );
  1055. CNetworkVector( m_attachmentPointRagdollSpace );
  1056. bool m_bShouldDetach;
  1057. IPhysicsConstraint *m_pAttachConstraint;
  1058. };
  1059. LINK_ENTITY_TO_CLASS( prop_ragdoll_attached, CRagdollPropAttached );
  1060. EXTERN_SEND_TABLE(DT_Ragdoll_Attached)
  1061. IMPLEMENT_SERVERCLASS_ST(CRagdollPropAttached, DT_Ragdoll_Attached)
  1062. SendPropInt( SENDINFO( m_boneIndexAttached ), MAXSTUDIOBONEBITS, SPROP_UNSIGNED ),
  1063. SendPropInt( SENDINFO( m_ragdollAttachedObjectIndex ), RAGDOLL_INDEX_BITS, SPROP_UNSIGNED ),
  1064. SendPropVector(SENDINFO(m_attachmentPointBoneSpace), -1, SPROP_COORD ),
  1065. SendPropVector(SENDINFO(m_attachmentPointRagdollSpace), -1, SPROP_COORD ),
  1066. END_SEND_TABLE()
  1067. BEGIN_DATADESC(CRagdollPropAttached)
  1068. DEFINE_FIELD( m_boneIndexAttached, FIELD_INTEGER ),
  1069. DEFINE_FIELD( m_ragdollAttachedObjectIndex, FIELD_INTEGER ),
  1070. DEFINE_FIELD( m_attachmentPointBoneSpace, FIELD_VECTOR ),
  1071. DEFINE_FIELD( m_attachmentPointRagdollSpace, FIELD_VECTOR ),
  1072. DEFINE_FIELD( m_bShouldDetach, FIELD_BOOLEAN ),
  1073. DEFINE_PHYSPTR( m_pAttachConstraint ),
  1074. END_DATADESC()
  1075. static void SyncAnimatingWithPhysics( CBaseAnimating *pAnimating )
  1076. {
  1077. IPhysicsObject *pPhysics = pAnimating->VPhysicsGetObject();
  1078. if ( pPhysics )
  1079. {
  1080. Vector pos;
  1081. pPhysics->GetShadowPosition( &pos, NULL );
  1082. pAnimating->SetAbsOrigin( pos );
  1083. }
  1084. }
  1085. CBaseAnimating *CreateServerRagdollSubmodel( CBaseAnimating *pOwner, const char *pModelName, const Vector &position, const QAngle &angles, int collisionGroup )
  1086. {
  1087. CRagdollProp *pRagdoll = (CRagdollProp *)CBaseEntity::CreateNoSpawn( "prop_ragdoll", position, angles, pOwner );
  1088. pRagdoll->SetModelName( AllocPooledString( pModelName ) );
  1089. pRagdoll->SetModel( STRING(pRagdoll->GetModelName()) );
  1090. matrix3x4a_t pBoneToWorld[MAXSTUDIOBONES];
  1091. matrix3x4a_t pBoneToWorldNext[MAXSTUDIOBONES];
  1092. pRagdoll->ResetSequence( 0 );
  1093. // let bone merging do the work of copying everything over for us
  1094. pRagdoll->SetParent( pOwner );
  1095. pRagdoll->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
  1096. // HACKHACK: don't want this parent anymore
  1097. pRagdoll->SetParent( NULL );
  1098. memcpy( pBoneToWorldNext, pBoneToWorld, sizeof(pBoneToWorld) );
  1099. pRagdoll->InitRagdoll( vec3_origin, -1, vec3_origin, pBoneToWorld, pBoneToWorldNext, 0.1, collisionGroup, true );
  1100. return pRagdoll;
  1101. }
  1102. CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, const CTakeDamageInfo &info, int collisionGroup, bool bUseLRURetirement )
  1103. {
  1104. if ( info.GetDamageType() & (DMG_VEHICLE|DMG_CRUSH) )
  1105. {
  1106. // if the entity was killed by physics or a vehicle, move to the vphysics shadow position before creating the ragdoll.
  1107. SyncAnimatingWithPhysics( pAnimating );
  1108. }
  1109. CRagdollProp *pRagdoll = (CRagdollProp *)CBaseEntity::CreateNoSpawn( "prop_ragdoll", pAnimating->GetAbsOrigin(), vec3_angle, NULL );
  1110. pRagdoll->CopyAnimationDataFrom( pAnimating );
  1111. pRagdoll->SetOwnerEntity( pAnimating );
  1112. pRagdoll->InitRagdollAnimation();
  1113. matrix3x4a_t pBoneToWorld[MAXSTUDIOBONES];
  1114. matrix3x4a_t pBoneToWorldNext[MAXSTUDIOBONES];
  1115. float dt = 0.1f;
  1116. // Copy over dissolve state...
  1117. if ( pAnimating->IsEFlagSet( EFL_NO_DISSOLVE ) )
  1118. {
  1119. pRagdoll->AddEFlags( EFL_NO_DISSOLVE );
  1120. }
  1121. // NOTE: This currently is only necessary to prevent manhacks from
  1122. // colliding with server ragdolls they kill
  1123. pRagdoll->SetKiller( info.GetInflictor() );
  1124. pRagdoll->SetSourceClassName( pAnimating->GetClassname() );
  1125. // NPC_STATE_DEAD npc's will have their COND_IN_PVS cleared, so this needs to force SetupBones to happen
  1126. unsigned short fPrevFlags = pAnimating->GetBoneCacheFlags();
  1127. pAnimating->SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
  1128. // UNDONE: Extract velocity from bones via animation (like we do on the client)
  1129. // UNDONE: For now, just move each bone by the total entity velocity if set.
  1130. // Get Bones positions before
  1131. // Store current cycle
  1132. float fSequenceDuration = pAnimating->SequenceDuration( pAnimating->GetSequence() );
  1133. float fSequenceTime = pAnimating->GetCycle() * fSequenceDuration;
  1134. if( fSequenceTime <= dt && fSequenceTime > 0.0f )
  1135. {
  1136. // Avoid having negative cycle
  1137. dt = fSequenceTime;
  1138. }
  1139. float fPreviousCycle = clamp(pAnimating->GetCycle()-( dt * ( 1 / fSequenceDuration ) ),0.f,1.f);
  1140. float fCurCycle = pAnimating->GetCycle();
  1141. // Get current bones positions
  1142. pAnimating->SetupBones( pBoneToWorldNext, BONE_USED_BY_ANYTHING );
  1143. // Get previous bones positions
  1144. pAnimating->SetCycle( fPreviousCycle );
  1145. pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
  1146. // Restore current cycle
  1147. pAnimating->SetCycle( fCurCycle );
  1148. // Reset previous bone flags
  1149. pAnimating->ClearBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
  1150. pAnimating->SetBoneCacheFlags( fPrevFlags );
  1151. Vector vel = pAnimating->GetAbsVelocity();
  1152. if( ( vel.Length() == 0 ) && ( dt > 0 ) )
  1153. {
  1154. // Compute animation velocity
  1155. CStudioHdr *pstudiohdr = pAnimating->GetModelPtr();
  1156. if ( pstudiohdr )
  1157. {
  1158. Vector deltaPos;
  1159. QAngle deltaAngles;
  1160. if (Studio_SeqMovement( pstudiohdr,
  1161. pAnimating->GetSequence(),
  1162. fPreviousCycle,
  1163. pAnimating->GetCycle(),
  1164. pAnimating->GetPoseParameterArray(),
  1165. deltaPos,
  1166. deltaAngles ))
  1167. {
  1168. VectorRotate( deltaPos, pAnimating->EntityToWorldTransform(), vel );
  1169. vel /= dt;
  1170. }
  1171. }
  1172. }
  1173. if ( vel.LengthSqr() > 0 )
  1174. {
  1175. int numbones = pAnimating->GetModelPtr()->numbones();
  1176. vel *= dt;
  1177. for ( int i = 0; i < numbones; i++ )
  1178. {
  1179. Vector pos;
  1180. MatrixGetColumn( pBoneToWorld[i], 3, pos );
  1181. pos -= vel;
  1182. MatrixSetColumn( pos, 3, pBoneToWorld[i] );
  1183. }
  1184. }
  1185. #if RAGDOLL_VISUALIZE
  1186. pAnimating->DrawRawSkeleton( pBoneToWorld, BONE_USED_BY_ANYTHING, true, 20, false );
  1187. pAnimating->DrawRawSkeleton( pBoneToWorldNext, BONE_USED_BY_ANYTHING, true, 20, true );
  1188. #endif
  1189. // Is this a vehicle / NPC collision?
  1190. if ( (info.GetDamageType() & DMG_VEHICLE) && pAnimating->MyNPCPointer() )
  1191. {
  1192. // init the ragdoll with no forces
  1193. pRagdoll->InitRagdoll( vec3_origin, -1, vec3_origin, pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true );
  1194. // apply vehicle forces
  1195. // Get a list of bones with hitboxes below the plane of impact
  1196. int boxList[128];
  1197. Vector normal(0,0,-1);
  1198. int count = pAnimating->GetHitboxesFrontside( boxList, ARRAYSIZE(boxList), normal, DotProduct( normal, info.GetDamagePosition() ) );
  1199. // distribute force over mass of entire character
  1200. float massScale = Studio_GetMass(pAnimating->GetModelPtr());
  1201. massScale = clamp( massScale, 1, 1e4 );
  1202. massScale = 1 / massScale;
  1203. // distribute the force
  1204. // BUGBUG: This will hit the same bone twice if it has two hitboxes!!!!
  1205. ragdoll_t *pRagInfo = pRagdoll->GetRagdoll();
  1206. for ( int i = 0; i < count; i++ )
  1207. {
  1208. int physBone = pAnimating->GetPhysicsBone( pAnimating->GetHitboxBone( boxList[i] ) );
  1209. IPhysicsObject *pPhysics = pRagInfo->list[physBone].pObject;
  1210. pPhysics->ApplyForceCenter( info.GetDamageForce() * pPhysics->GetMass() * massScale );
  1211. }
  1212. }
  1213. else
  1214. {
  1215. pRagdoll->InitRagdoll( info.GetDamageForce(), forceBone, info.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true );
  1216. }
  1217. // Are we dissolving?
  1218. if ( pAnimating->IsDissolving() )
  1219. {
  1220. pRagdoll->TransferDissolveFrom( pAnimating );
  1221. }
  1222. else if ( bUseLRURetirement )
  1223. {
  1224. pRagdoll->AddSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT );
  1225. s_RagdollLRU.MoveToTopOfLRU( pRagdoll );
  1226. }
  1227. // Tracker 22598: If we don't set the OBB mins/maxs to something valid here, then the client will have a zero sized hull
  1228. // for the ragdoll for one frame until Vphysics updates the real obb bounds after the first simulation frame. Having
  1229. // 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
  1230. // the ragdoll doesn't draw initially.
  1231. Vector mins, maxs;
  1232. mins = pAnimating->CollisionProp()->OBBMins();
  1233. maxs = pAnimating->CollisionProp()->OBBMaxs();
  1234. pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs );
  1235. return pRagdoll;
  1236. }
  1237. void CRagdollPropAttached::DetachOnNextUpdate()
  1238. {
  1239. m_bShouldDetach = true;
  1240. }
  1241. void CRagdollPropAttached::VPhysicsUpdate( IPhysicsObject *pPhysics )
  1242. {
  1243. if ( m_bShouldDetach )
  1244. {
  1245. Detach();
  1246. m_bShouldDetach = false;
  1247. }
  1248. BaseClass::VPhysicsUpdate( pPhysics );
  1249. }
  1250. void CRagdollPropAttached::Detach()
  1251. {
  1252. SetParent(NULL);
  1253. SetOwnerEntity( NULL );
  1254. SetAbsAngles( vec3_angle );
  1255. SetMoveType( MOVETYPE_VPHYSICS );
  1256. RemoveSolidFlags( FSOLID_NOT_SOLID );
  1257. physenv->DestroyConstraint( m_pAttachConstraint );
  1258. m_pAttachConstraint = NULL;
  1259. const float dampingScale = 1.0f / ATTACHED_DAMPING_SCALE;
  1260. for ( int i = 0; i < m_ragdoll.listCount; i++ )
  1261. {
  1262. float damping, rotdamping;
  1263. m_ragdoll.list[i].pObject->GetDamping( &damping, &rotdamping );
  1264. damping *= dampingScale;
  1265. rotdamping *= dampingScale;
  1266. m_ragdoll.list[i].pObject->SetDamping( &damping, &damping );
  1267. }
  1268. // Go non-solid
  1269. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  1270. RecheckCollisionFilter();
  1271. }
  1272. void CRagdollPropAttached::InitRagdollAttached(
  1273. IPhysicsObject *pAttached,
  1274. const Vector &forceVector,
  1275. int forceBone,
  1276. matrix3x4_t *pPrevBones,
  1277. matrix3x4_t *pBoneToWorld,
  1278. float dt,
  1279. int collisionGroup,
  1280. CBaseAnimating *pFollow,
  1281. int boneIndexRoot,
  1282. const Vector &boneLocalOrigin,
  1283. int parentBoneAttach,
  1284. const Vector &worldAttachOrigin )
  1285. {
  1286. int ragdollAttachedIndex = 0;
  1287. if ( parentBoneAttach > 0 )
  1288. {
  1289. CStudioHdr *pStudioHdr = GetModelPtr();
  1290. const mstudiobone_t *pBone = pStudioHdr->pBone( parentBoneAttach );
  1291. ragdollAttachedIndex = pBone->physicsbone;
  1292. }
  1293. InitRagdoll( forceVector, forceBone, vec3_origin, pPrevBones, pBoneToWorld, dt, collisionGroup, false );
  1294. IPhysicsObject *pRefObject = m_ragdoll.list[ragdollAttachedIndex].pObject;
  1295. Vector attachmentPointRagdollSpace;
  1296. pRefObject->WorldToLocal( &attachmentPointRagdollSpace, worldAttachOrigin );
  1297. constraint_ragdollparams_t constraint;
  1298. constraint.Defaults();
  1299. matrix3x4_t tmp, worldToAttached, worldToReference, constraintToWorld;
  1300. Vector offsetWS;
  1301. pAttached->LocalToWorld( &offsetWS, boneLocalOrigin );
  1302. QAngle followAng = QAngle(0, pFollow->GetAbsAngles().y, 0 );
  1303. AngleMatrix( followAng, offsetWS, constraintToWorld );
  1304. constraint.axes[0].SetAxisFriction( -2, 2, 20 );
  1305. constraint.axes[1].SetAxisFriction( 0, 0, 0 );
  1306. constraint.axes[2].SetAxisFriction( -15, 15, 20 );
  1307. // Exaggerate the bone's ability to pull the mass of the ragdoll around
  1308. constraint.constraint.bodyMassScale[1] = 50.0f;
  1309. pAttached->GetPositionMatrix( &tmp );
  1310. MatrixInvert( tmp, worldToAttached );
  1311. pRefObject->GetPositionMatrix( &tmp );
  1312. MatrixInvert( tmp, worldToReference );
  1313. ConcatTransforms( worldToReference, constraintToWorld, constraint.constraintToReference );
  1314. ConcatTransforms( worldToAttached, constraintToWorld, constraint.constraintToAttached );
  1315. // for now, just slam this to be the passed in value
  1316. MatrixSetColumn( attachmentPointRagdollSpace, 3, constraint.constraintToReference );
  1317. PhysDisableEntityCollisions( pAttached, m_ragdoll.list[0].pObject );
  1318. m_pAttachConstraint = physenv->CreateRagdollConstraint( pRefObject, pAttached, m_ragdoll.pGroup, constraint );
  1319. SetParent( pFollow );
  1320. SetOwnerEntity( pFollow );
  1321. RagdollActivate( m_ragdoll, modelinfo->GetVCollide( GetModelIndex() ), GetModelIndex() );
  1322. // add a bunch of dampening to the ragdoll
  1323. for ( int i = 0; i < m_ragdoll.listCount; i++ )
  1324. {
  1325. float damping, rotdamping;
  1326. m_ragdoll.list[i].pObject->GetDamping( &damping, &rotdamping );
  1327. damping *= ATTACHED_DAMPING_SCALE;
  1328. rotdamping *= ATTACHED_DAMPING_SCALE;
  1329. m_ragdoll.list[i].pObject->SetDamping( &damping, &rotdamping );
  1330. }
  1331. m_boneIndexAttached = boneIndexRoot;
  1332. m_ragdollAttachedObjectIndex = ragdollAttachedIndex;
  1333. m_attachmentPointBoneSpace = boneLocalOrigin;
  1334. Vector vTemp;
  1335. MatrixGetColumn( constraint.constraintToReference, 3, vTemp );
  1336. m_attachmentPointRagdollSpace = vTemp;
  1337. }
  1338. 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 )
  1339. {
  1340. // Return immediately if the model doesn't have a vcollide
  1341. if ( modelinfo->GetVCollide( pAnimating->GetModelIndex() ) == NULL )
  1342. return NULL;
  1343. CRagdollPropAttached *pRagdoll = (CRagdollPropAttached *)CBaseEntity::CreateNoSpawn( "prop_ragdoll_attached", pAnimating->GetAbsOrigin(), vec3_angle, NULL );
  1344. pRagdoll->CopyAnimationDataFrom( pAnimating );
  1345. pRagdoll->InitRagdollAnimation();
  1346. matrix3x4a_t pBoneToWorld[MAXSTUDIOBONES];
  1347. pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
  1348. pRagdoll->InitRagdollAttached( pAttached, vecForce, forceBone, pBoneToWorld, pBoneToWorld, 0.1, collisionGroup, pParentEntity, boneAttach, boneOrigin, parentBoneAttach, originAttached );
  1349. return pRagdoll;
  1350. }
  1351. void DetachAttachedRagdoll( CBaseEntity *pRagdollIn )
  1352. {
  1353. CRagdollPropAttached *pRagdoll = dynamic_cast<CRagdollPropAttached *>(pRagdollIn);
  1354. if ( pRagdoll )
  1355. {
  1356. pRagdoll->DetachOnNextUpdate();
  1357. }
  1358. }
  1359. void DetachAttachedRagdollsForEntity( CBaseEntity *pRagdollParent )
  1360. {
  1361. CUtlVector<CBaseEntity *> list;
  1362. GetAllChildren( pRagdollParent, list );
  1363. for ( int i = list.Count()-1; i >= 0; --i )
  1364. {
  1365. DetachAttachedRagdoll( list[i] );
  1366. }
  1367. }
  1368. bool Ragdoll_IsPropRagdoll( CBaseEntity *pEntity )
  1369. {
  1370. if ( dynamic_cast<CRagdollProp *>(pEntity) != NULL )
  1371. return true;
  1372. return false;
  1373. }
  1374. ragdoll_t *Ragdoll_GetRagdoll( CBaseEntity *pEntity )
  1375. {
  1376. CRagdollProp *pProp = dynamic_cast<CRagdollProp *>(pEntity);
  1377. if ( pProp )
  1378. return pProp->GetRagdoll();
  1379. return NULL;
  1380. }
  1381. void CRagdollProp::GetAngleOverrideFromCurrentState( char *pOut, int size )
  1382. {
  1383. pOut[0] = 0;
  1384. for ( int i = 0; i < m_ragdoll.listCount; i++ )
  1385. {
  1386. if ( i != 0 )
  1387. {
  1388. Q_strncat( pOut, ",", size, COPY_ALL_CHARACTERS );
  1389. }
  1390. CFmtStr str("%d,%.2f %.2f %.2f", i, m_ragAngles[i].x, m_ragAngles[i].y, m_ragAngles[i].z );
  1391. Q_strncat( pOut, str, size, COPY_ALL_CHARACTERS );
  1392. }
  1393. }
  1394. void CRagdollProp::DisableMotion( void )
  1395. {
  1396. for ( int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll )
  1397. {
  1398. IPhysicsObject *pPhysicsObject = m_ragdoll.list[ iRagdoll ].pObject;
  1399. if ( pPhysicsObject != NULL )
  1400. {
  1401. pPhysicsObject->EnableMotion( false );
  1402. }
  1403. }
  1404. }
  1405. void CRagdollProp::InputStartRadgollBoogie( inputdata_t &inputdata )
  1406. {
  1407. float duration = inputdata.value.Float();
  1408. if( duration <= 0.0f )
  1409. {
  1410. duration = 5.0f;
  1411. }
  1412. CRagdollBoogie::Create( this, 100, gpGlobals->curtime, duration, 0 );
  1413. }
  1414. //-----------------------------------------------------------------------------
  1415. // Purpose: Enable physics motion and collision response (on by default)
  1416. //-----------------------------------------------------------------------------
  1417. void CRagdollProp::InputEnableMotion( inputdata_t &inputdata )
  1418. {
  1419. for ( int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll )
  1420. {
  1421. IPhysicsObject *pPhysicsObject = m_ragdoll.list[ iRagdoll ].pObject;
  1422. if ( pPhysicsObject != NULL )
  1423. {
  1424. pPhysicsObject->EnableMotion( true );
  1425. pPhysicsObject->Wake();
  1426. }
  1427. }
  1428. }
  1429. //-----------------------------------------------------------------------------
  1430. // Purpose: Disable any physics motion or collision response
  1431. //-----------------------------------------------------------------------------
  1432. void CRagdollProp::InputDisableMotion( inputdata_t &inputdata )
  1433. {
  1434. DisableMotion();
  1435. }
  1436. void CRagdollProp::InputTurnOn( inputdata_t &inputdata )
  1437. {
  1438. RemoveEffects( EF_NODRAW );
  1439. }
  1440. void CRagdollProp::InputTurnOff( inputdata_t &inputdata )
  1441. {
  1442. AddEffects( EF_NODRAW );
  1443. }
  1444. void CRagdollProp::InputFadeAndRemove( inputdata_t &inputdata )
  1445. {
  1446. float flFadeDuration = inputdata.value.Float();
  1447. if( flFadeDuration == 0.0f )
  1448. flFadeDuration = 1.0f;
  1449. FadeOut( 0.0f, flFadeDuration );
  1450. }
  1451. void Ragdoll_GetAngleOverrideString( char *pOut, int size, CBaseEntity *pEntity )
  1452. {
  1453. CRagdollProp *pRagdoll = dynamic_cast<CRagdollProp *>(pEntity);
  1454. if ( pRagdoll )
  1455. {
  1456. pRagdoll->GetAngleOverrideFromCurrentState( pOut, size );
  1457. }
  1458. }