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.

1335 lines
37 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "ragdoll_shared.h"
  8. #include "bone_setup.h"
  9. #include "vphysics/constraints.h"
  10. #include "vphysics/collision_set.h"
  11. #include "vcollide_parse.h"
  12. #include "vphysics_interface.h"
  13. #include "tier0/vprof.h"
  14. #include "engine/ivdebugoverlay.h"
  15. #include "solidsetdefaults.h"
  16. //CLIENT
  17. #ifdef CLIENT_DLL
  18. #include "c_entityflame.h"
  19. #include "c_fire_smoke.h"
  20. #include "c_entitydissolve.h"
  21. #include "engine/IEngineSound.h"
  22. #endif
  23. //SERVER
  24. #if !defined( CLIENT_DLL )
  25. #include "util.h"
  26. #include "EntityFlame.h"
  27. #include "EntityDissolve.h"
  28. #endif
  29. // memdbgon must be the last include file in a .cpp file!!!
  30. #include "tier0/memdbgon.h"
  31. CRagdollLowViolenceManager g_RagdollLVManager;
  32. void CRagdollLowViolenceManager::SetLowViolence( const char *pMapName )
  33. {
  34. // set the value using the engine's low violence settings
  35. m_bLowViolence = UTIL_IsLowViolence();
  36. #if !defined( CLIENT_DLL )
  37. // the server doesn't worry about low violence during multiplayer games
  38. if ( g_pGameRules && g_pGameRules->IsMultiplayer() )
  39. {
  40. m_bLowViolence = false;
  41. }
  42. #endif
  43. // Turn the low violence ragdoll stuff off if we're in the HL2 Citadel maps because
  44. // the player has the super gravity gun and fading ragdolls will break things.
  45. if( hl2_episodic.GetBool() )
  46. {
  47. if ( Q_stricmp( pMapName, "ep1_citadel_02" ) == 0 ||
  48. Q_stricmp( pMapName, "ep1_citadel_02b" ) == 0 ||
  49. Q_stricmp( pMapName, "ep1_citadel_03" ) == 0 )
  50. {
  51. m_bLowViolence = false;
  52. }
  53. }
  54. else
  55. {
  56. if ( Q_stricmp( pMapName, "d3_citadel_03" ) == 0 ||
  57. Q_stricmp( pMapName, "d3_citadel_04" ) == 0 ||
  58. Q_stricmp( pMapName, "d3_citadel_05" ) == 0 ||
  59. Q_stricmp( pMapName, "d3_breen_01" ) == 0 )
  60. {
  61. m_bLowViolence = false;
  62. }
  63. }
  64. }
  65. // A simple cache to store the ragdoll's data after it has been parsed. Avoid re-parsing on every create
  66. struct cache_ragdollsolid_t
  67. {
  68. objectparams_t params;
  69. int surfacePropIndex;
  70. short boneIndex;
  71. short collideIndex;
  72. };
  73. struct cache_ragdollconstraint_t
  74. {
  75. constraint_axislimit_t axes[3];
  76. matrix3x4_t constraintToAttached;
  77. short parentIndex;
  78. short childIndex;
  79. };
  80. // store the ragdoll as a single allocation header, then solids array, then constraints array linearly in memory
  81. struct cache_ragdoll_t
  82. {
  83. IPhysicsCollisionSet *pCollisionSet;
  84. ragdollanimatedfriction_t animfriction;
  85. short solidCount;
  86. short constraintCount;
  87. const cache_ragdollsolid_t *GetSolids() { return (cache_ragdollsolid_t *)(this+1); }
  88. const cache_ragdollconstraint_t *GetConstraints() { return (cache_ragdollconstraint_t *)(GetSolids()+solidCount); }
  89. };
  90. cache_ragdoll_t *CreateRagdollCache( vcollide_t *pOutput, cache_ragdollsolid_t *pSolids, cache_ragdollconstraint_t *pConstraints, cache_ragdoll_t *pRagdoll )
  91. {
  92. size_t memSize = sizeof(cache_ragdoll_t);
  93. size_t solidSize = sizeof(cache_ragdollsolid_t) * pRagdoll->solidCount;
  94. size_t constraintSize = sizeof(cache_ragdollconstraint_t) * pRagdoll->constraintCount;
  95. cache_ragdoll_t *pMem = (cache_ragdoll_t *)physcollision->VCollideAllocUserData( pOutput, memSize + solidSize + constraintSize );
  96. V_memcpy( pMem, pRagdoll, sizeof(*pMem) );
  97. V_memcpy( (void *)pMem->GetSolids(), pSolids, solidSize );
  98. V_memcpy( (void *)pMem->GetConstraints(), pConstraints, constraintSize );
  99. return pMem;
  100. }
  101. // OPTIMIZE: Slow, hopefully this is only called by save/load
  102. void RagdollSetupAnimatedFriction( IPhysicsEnvironment *pPhysEnv, ragdoll_t *ragdoll, int iModelIndex )
  103. {
  104. vcollide_t* pCollide = modelinfo->GetVCollide( iModelIndex );
  105. if ( pCollide )
  106. {
  107. IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide );
  108. while ( !pParse->Finished() )
  109. {
  110. const char *pBlock = pParse->GetCurrentBlockName();
  111. if ( !strcmpi( pBlock, "animatedfriction") )
  112. {
  113. pParse->ParseRagdollAnimatedFriction( &ragdoll->animfriction, NULL );
  114. }
  115. else
  116. {
  117. pParse->SkipBlock();
  118. }
  119. }
  120. physcollision->VPhysicsKeyParserDestroy( pParse );
  121. }
  122. }
  123. static void RagdollAddSolids( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, const ragdollparams_t &params, cache_ragdollsolid_t *pSolids, int solidCount, const cache_ragdollconstraint_t *pConstraints, int constraintCount )
  124. {
  125. const char *pszName = params.pStudioHdr->pszName();
  126. Vector position;
  127. matrix3x4_t xform;
  128. // init parent index
  129. for ( int i = 0; i < solidCount; i++ )
  130. {
  131. ragdoll.list[i].parentIndex = -1;
  132. }
  133. // now set from constraints
  134. for ( int i = 0; i < constraintCount; i++ )
  135. {
  136. // save parent index
  137. ragdoll.list[pConstraints[i].childIndex].parentIndex = pConstraints[i].parentIndex;
  138. MatrixGetColumn( pConstraints[i].constraintToAttached, 3, ragdoll.list[pConstraints[i].childIndex].originParentSpace );
  139. }
  140. // now setup the solids, using parent indices
  141. for ( int i = 0; i < solidCount; i++ )
  142. {
  143. if ( params.fixedConstraints )
  144. {
  145. pSolids[i].params.mass = 1000.f;
  146. }
  147. ragdoll.boneIndex[i] = pSolids[i].boneIndex;
  148. pSolids[i].params.pName = pszName;
  149. pSolids[i].params.pGameData = params.pGameData;
  150. ragdoll.list[i].pObject = pPhysEnv->CreatePolyObject( params.pCollide->solids[pSolids[i].collideIndex], pSolids[i].surfacePropIndex, vec3_origin, vec3_angle, &pSolids[i].params );
  151. ragdoll.list[i].pObject->SetGameIndex( i );
  152. int parentIndex = ragdoll.list[i].parentIndex;
  153. MatrixCopy( params.pCurrentBones[ragdoll.boneIndex[i]], xform );
  154. if ( parentIndex >= 0 )
  155. {
  156. Assert(parentIndex<i);
  157. ragdoll.list[parentIndex].pObject->LocalToWorld( &position, ragdoll.list[i].originParentSpace );
  158. MatrixSetColumn( position, 3, xform );
  159. }
  160. ragdoll.list[i].pObject->SetPositionMatrix( xform, true );
  161. PhysSetGameFlags( ragdoll.list[i].pObject, FVPHYSICS_PART_OF_RAGDOLL );
  162. }
  163. ragdoll.listCount = solidCount;
  164. }
  165. static void RagdollAddConstraints( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, const ragdollparams_t &params, const cache_ragdollconstraint_t *pConstraints, int constraintCount )
  166. {
  167. constraint_ragdollparams_t constraint;
  168. for ( int i = 0; i < constraintCount; i++ )
  169. {
  170. constraint.Defaults();
  171. V_memcpy( constraint.axes, pConstraints[i].axes, sizeof(constraint.axes) );
  172. if ( params.jointFrictionScale > 0 )
  173. {
  174. for ( int k = 0; k < 3; k++ )
  175. {
  176. constraint.axes[k].torque *= params.jointFrictionScale;
  177. }
  178. }
  179. int parentIndex = pConstraints[i].parentIndex;
  180. int childIndex = pConstraints[i].childIndex;
  181. constraint.childIndex = childIndex;
  182. constraint.parentIndex = parentIndex;
  183. constraint.useClockwiseRotations = true;
  184. constraint.constraintToAttached = pConstraints[i].constraintToAttached;
  185. // UNDONE: We could transform the constraint limit axes relative to the bone space
  186. // using this data. Do we need that feature?
  187. SetIdentityMatrix( constraint.constraintToReference );
  188. if ( params.fixedConstraints )
  189. {
  190. // Makes the ragdoll a statue...
  191. constraint_fixedparams_t fixed;
  192. fixed.Defaults();
  193. fixed.InitWithCurrentObjectState( ragdoll.list[childIndex].pObject, ragdoll.list[constraint.parentIndex].pObject );
  194. fixed.constraint.Defaults();
  195. ragdoll.list[childIndex].pConstraint = pPhysEnv->CreateFixedConstraint( ragdoll.list[childIndex].pObject, ragdoll.list[parentIndex].pObject, ragdoll.pGroup, fixed );
  196. }
  197. else
  198. {
  199. ragdoll.list[childIndex].pConstraint = pPhysEnv->CreateRagdollConstraint( ragdoll.list[childIndex].pObject, ragdoll.list[parentIndex].pObject, ragdoll.pGroup, constraint );
  200. }
  201. }
  202. }
  203. static cache_ragdoll_t *ParseRagdollIntoCache( CStudioHdr *pStudioHdr, vcollide_t *pCollide, int modelIndex )
  204. {
  205. IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide );
  206. cache_ragdollsolid_t solidList[RAGDOLL_MAX_ELEMENTS];
  207. cache_ragdollconstraint_t constraintList[RAGDOLL_MAX_ELEMENTS];
  208. solid_t solid;
  209. int constraintCount = 0;
  210. int solidCount = 0;
  211. cache_ragdoll_t cache;
  212. V_memset( &cache, 0, sizeof(cache) );
  213. while ( !pParse->Finished() )
  214. {
  215. const char *pBlock = pParse->GetCurrentBlockName();
  216. if ( !strcmpi( pBlock, "solid" ) )
  217. {
  218. pParse->ParseSolid( &solid, &g_SolidSetup );
  219. cache_ragdollsolid_t *pSolid = &solidList[solidCount];
  220. pSolid->boneIndex = Studio_BoneIndexByName( pStudioHdr, solid.name );
  221. if ( pSolid->boneIndex >= 0 )
  222. {
  223. pSolid->collideIndex = solid.index;
  224. pSolid->surfacePropIndex = physprops->GetSurfaceIndex( solid.surfaceprop );
  225. if ( pSolid->surfacePropIndex < 0 )
  226. {
  227. pSolid->surfacePropIndex = physprops->GetSurfaceIndex( "default" );
  228. }
  229. pSolid->params = solid.params;
  230. pSolid->params.enableCollisions = false;
  231. solidCount++;
  232. }
  233. else
  234. {
  235. Msg( "ParseRagdollIntoCache: Couldn't Lookup Bone %s\n", solid.name );
  236. }
  237. }
  238. else if ( !strcmpi( pBlock, "ragdollconstraint" ) )
  239. {
  240. constraint_ragdollparams_t constraint;
  241. pParse->ParseRagdollConstraint( &constraint, NULL );
  242. if( constraint.childIndex != constraint.parentIndex && constraint.childIndex >= 0 && constraint.parentIndex >= 0)
  243. {
  244. cache_ragdollconstraint_t *pOut = &constraintList[constraintCount];
  245. constraintCount++;
  246. V_memcpy( pOut->axes, constraint.axes, sizeof(constraint.axes) );
  247. pOut->parentIndex = constraint.parentIndex;
  248. pOut->childIndex = constraint.childIndex;
  249. Studio_CalcBoneToBoneTransform( pStudioHdr, solidList[constraint.childIndex].boneIndex, solidList[constraint.parentIndex].boneIndex, pOut->constraintToAttached );
  250. }
  251. }
  252. else if ( !strcmpi( pBlock, "collisionrules" ) )
  253. {
  254. ragdollcollisionrules_t rules;
  255. IPhysicsCollisionSet *pSet = physics->FindOrCreateCollisionSet( modelIndex, pCollide->solidCount );
  256. rules.Defaults(physics, pSet);
  257. pParse->ParseCollisionRules( &rules, NULL );
  258. cache.pCollisionSet = rules.pCollisionSet;
  259. }
  260. else if ( !strcmpi( pBlock, "animatedfriction") )
  261. {
  262. pParse->ParseRagdollAnimatedFriction( &cache.animfriction, NULL );
  263. }
  264. else
  265. {
  266. pParse->SkipBlock();
  267. }
  268. }
  269. physcollision->VPhysicsKeyParserDestroy( pParse );
  270. cache.solidCount = solidCount;
  271. cache.constraintCount = constraintCount;
  272. return CreateRagdollCache( pCollide, solidList, constraintList, &cache );
  273. }
  274. static void RagdollCreateObjects( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, const ragdollparams_t &params )
  275. {
  276. ragdoll.listCount = 0;
  277. ragdoll.pGroup = NULL;
  278. ragdoll.allowStretch = params.allowStretch;
  279. memset( ragdoll.list, 0, sizeof(ragdoll.list) );
  280. memset( &ragdoll.animfriction, 0, sizeof(ragdoll.animfriction) );
  281. if ( !params.pCollide )
  282. {
  283. Warning( "Ragdoll has no pCollide!" );
  284. Assert( false );
  285. return;
  286. }
  287. if ( params.pCollide->solidCount > RAGDOLL_MAX_ELEMENTS )
  288. {
  289. Warning( "Ragdoll solid count %d exceeds maximum limit of %d - Ragdoll not created", params.pCollide->solidCount, RAGDOLL_MAX_ELEMENTS );
  290. Assert( false );
  291. return;
  292. }
  293. cache_ragdoll_t *pCache = (cache_ragdoll_t *)params.pCollide->pUserData;
  294. if ( !pCache )
  295. {
  296. pCache = ParseRagdollIntoCache(params.pStudioHdr, params.pCollide, params.modelIndex);
  297. }
  298. constraint_groupparams_t group;
  299. group.Defaults();
  300. ragdoll.pGroup = pPhysEnv->CreateConstraintGroup( group );
  301. RagdollAddSolids( pPhysEnv, ragdoll, params, const_cast<cache_ragdollsolid_t *>(pCache->GetSolids()), pCache->solidCount, pCache->GetConstraints(), pCache->constraintCount );
  302. RagdollAddConstraints( pPhysEnv, ragdoll, params, pCache->GetConstraints(), pCache->constraintCount );
  303. }
  304. void RagdollSetupCollisions( ragdoll_t &ragdoll, vcollide_t *pCollide, int modelIndex )
  305. {
  306. Assert(pCollide);
  307. if (!pCollide)
  308. return;
  309. IPhysicsCollisionSet *pSet = physics->FindCollisionSet( modelIndex );
  310. if ( !pSet )
  311. {
  312. pSet = physics->FindOrCreateCollisionSet( modelIndex, pCollide->solidCount );
  313. if ( !pSet )
  314. return;
  315. bool bFoundRules = false;
  316. IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide );
  317. while ( !pParse->Finished() )
  318. {
  319. const char *pBlock = pParse->GetCurrentBlockName();
  320. if ( !strcmpi( pBlock, "collisionrules" ) )
  321. {
  322. ragdollcollisionrules_t rules;
  323. rules.Defaults(physics, pSet);
  324. pParse->ParseCollisionRules( &rules, NULL );
  325. Assert(rules.pCollisionSet == pSet);
  326. bFoundRules = true;
  327. }
  328. else
  329. {
  330. pParse->SkipBlock();
  331. }
  332. }
  333. physcollision->VPhysicsKeyParserDestroy( pParse );
  334. if ( !bFoundRules )
  335. {
  336. // these are the default rules - each piece collides with everything
  337. // except immediate parent/constrained object.
  338. int i;
  339. for ( i = 0; i < ragdoll.listCount; i++ )
  340. {
  341. for ( int j = i+1; j < ragdoll.listCount; j++ )
  342. {
  343. pSet->EnableCollisions( i, j );
  344. }
  345. }
  346. for ( i = 0; i < ragdoll.listCount; i++ )
  347. {
  348. int parent = ragdoll.list[i].parentIndex;
  349. if ( parent >= 0 )
  350. {
  351. Assert( ragdoll.list[i].pObject );
  352. Assert( ragdoll.list[i].pConstraint );
  353. pSet->DisableCollisions( i, parent );
  354. }
  355. }
  356. }
  357. }
  358. }
  359. void RagdollActivate( ragdoll_t &ragdoll, vcollide_t *pCollide, int modelIndex, bool bForceWake )
  360. {
  361. RagdollSetupCollisions( ragdoll, pCollide, modelIndex );
  362. for ( int i = 0; i < ragdoll.listCount; i++ )
  363. {
  364. PhysSetGameFlags( ragdoll.list[i].pObject, FVPHYSICS_MULTIOBJECT_ENTITY );
  365. // now that the relationships are set, activate the collision system
  366. ragdoll.list[i].pObject->EnableCollisions( true );
  367. if ( bForceWake == true )
  368. {
  369. ragdoll.list[i].pObject->Wake();
  370. }
  371. }
  372. if ( ragdoll.pGroup )
  373. {
  374. // NOTE: This also wakes the objects
  375. ragdoll.pGroup->Activate();
  376. // so if we didn't want that, we'll need to put them back to sleep here
  377. if ( !bForceWake )
  378. {
  379. for ( int i = 0; i < ragdoll.listCount; i++ )
  380. {
  381. ragdoll.list[i].pObject->Sleep();
  382. }
  383. }
  384. }
  385. }
  386. bool RagdollCreate( ragdoll_t &ragdoll, const ragdollparams_t &params, IPhysicsEnvironment *pPhysEnv )
  387. {
  388. RagdollCreateObjects( pPhysEnv, ragdoll, params );
  389. if ( !ragdoll.listCount )
  390. return false;
  391. int forceBone = params.forceBoneIndex;
  392. int i;
  393. float totalMass = 0;
  394. for ( i = 0; i < ragdoll.listCount; i++ )
  395. {
  396. totalMass += ragdoll.list[i].pObject->GetMass();
  397. }
  398. totalMass = MAX(totalMass,1);
  399. // apply force to the model
  400. Vector nudgeForce = params.forceVector;
  401. Vector forcePosition = params.forcePosition;
  402. // UNDONE: Test scaling the force by total mass on all bones
  403. // UNDONE: forcebone can be out of range when a body part breaks off - it uses the shared force bone from the original model
  404. // UNDONE: Remap this?
  405. if ( forceBone >= 0 && forceBone < ragdoll.listCount )
  406. {
  407. ragdoll.list[forceBone].pObject->ApplyForceCenter( nudgeForce );
  408. //nudgeForce *= 0.5;
  409. ragdoll.list[forceBone].pObject->GetPosition( &forcePosition, NULL );
  410. }
  411. if ( forcePosition != vec3_origin )
  412. {
  413. for ( i = 0; i < ragdoll.listCount; i++ )
  414. {
  415. if ( forceBone != i )
  416. {
  417. float scale = ragdoll.list[i].pObject->GetMass() / totalMass;
  418. ragdoll.list[i].pObject->ApplyForceOffset( scale * nudgeForce, forcePosition );
  419. }
  420. }
  421. }
  422. return true;
  423. }
  424. void RagdollApplyAnimationAsVelocity( ragdoll_t &ragdoll, const matrix3x4_t *pPrevBones, const matrix3x4_t *pCurrentBones, float dt )
  425. {
  426. for ( int i = 0; i < ragdoll.listCount; i++ )
  427. {
  428. Vector velocity;
  429. AngularImpulse angVel;
  430. int boneIndex = ragdoll.boneIndex[i];
  431. CalcBoneDerivatives( velocity, angVel, pPrevBones[boneIndex], pCurrentBones[boneIndex], dt );
  432. AngularImpulse localAngVelocity;
  433. // Angular velocity is always applied in local space in vphysics
  434. ragdoll.list[i].pObject->WorldToLocalVector( &localAngVelocity, angVel );
  435. ragdoll.list[i].pObject->AddVelocity( &velocity, &localAngVelocity );
  436. }
  437. }
  438. void RagdollApplyAnimationAsVelocity( ragdoll_t &ragdoll, const matrix3x4_t *pBoneToWorld )
  439. {
  440. for ( int i = 0; i < ragdoll.listCount; i++ )
  441. {
  442. matrix3x4_t inverse;
  443. MatrixInvert( pBoneToWorld[i], inverse );
  444. Quaternion q;
  445. Vector pos;
  446. MatrixAngles( inverse, q, pos );
  447. Vector velocity;
  448. AngularImpulse angVel;
  449. float flSpin;
  450. Vector localVelocity;
  451. AngularImpulse localAngVelocity;
  452. QuaternionAxisAngle( q, localAngVelocity, flSpin );
  453. localAngVelocity *= flSpin;
  454. localVelocity = pos;
  455. // move those bone-local coords back to world space using the ragdoll transform
  456. ragdoll.list[i].pObject->LocalToWorldVector( &velocity, localVelocity );
  457. ragdoll.list[i].pObject->AddVelocity( &velocity, &localAngVelocity );
  458. }
  459. }
  460. void RagdollDestroy( ragdoll_t &ragdoll )
  461. {
  462. if ( !ragdoll.listCount )
  463. return;
  464. int i;
  465. for ( i = 0; i < ragdoll.listCount; i++ )
  466. {
  467. physenv->DestroyConstraint( ragdoll.list[i].pConstraint );
  468. ragdoll.list[i].pConstraint = NULL;
  469. }
  470. for ( i = 0; i < ragdoll.listCount; i++ )
  471. {
  472. // during level transitions these can get temporarily loaded without physics objects
  473. // purely for the purpose of testing for PVS of transition. If they fail they get
  474. // deleted before the physics objects are loaded. The list count will be nonzero
  475. // since that is saved separately.
  476. if ( ragdoll.list[i].pObject )
  477. {
  478. physenv->DestroyObject( ragdoll.list[i].pObject );
  479. }
  480. ragdoll.list[i].pObject = NULL;
  481. }
  482. physenv->DestroyConstraintGroup( ragdoll.pGroup );
  483. ragdoll.pGroup = NULL;
  484. ragdoll.listCount = 0;
  485. }
  486. // Parse the ragdoll and obtain the mapping from each physics element index to a bone index
  487. // returns num phys elements
  488. int RagdollExtractBoneIndices( int *boneIndexOut, CStudioHdr *pStudioHdr, vcollide_t *pCollide )
  489. {
  490. int elementCount = 0;
  491. IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide );
  492. while ( !pParse->Finished() )
  493. {
  494. const char *pBlock = pParse->GetCurrentBlockName();
  495. if ( !strcmpi( pBlock, "solid" ) )
  496. {
  497. solid_t solid;
  498. pParse->ParseSolid( &solid, NULL );
  499. if ( elementCount < RAGDOLL_MAX_ELEMENTS )
  500. {
  501. boneIndexOut[elementCount] = Studio_BoneIndexByName( pStudioHdr, solid.name );
  502. elementCount++;
  503. }
  504. }
  505. else
  506. {
  507. pParse->SkipBlock();
  508. }
  509. }
  510. physcollision->VPhysicsKeyParserDestroy( pParse );
  511. return elementCount;
  512. }
  513. bool RagdollGetBoneMatrix( const ragdoll_t &ragdoll, CBoneAccessor &pBoneToWorld, int objectIndex )
  514. {
  515. int boneIndex = ragdoll.boneIndex[objectIndex];
  516. if ( boneIndex < 0 )
  517. return false;
  518. const ragdollelement_t &element = ragdoll.list[objectIndex];
  519. // during restore if a model has changed since the file was saved, this could be NULL
  520. if ( !element.pObject )
  521. return false;
  522. element.pObject->GetPositionMatrix( &pBoneToWorld.GetBoneForWrite( boneIndex ) );
  523. if ( element.parentIndex >= 0 && !ragdoll.allowStretch )
  524. {
  525. // overwrite the position from physics to force rigid attachment
  526. // NOTE: On the client we actually override this with the proper parent bone in each LOD
  527. int parentBoneIndex = ragdoll.boneIndex[element.parentIndex];
  528. Vector out;
  529. VectorTransform( element.originParentSpace, pBoneToWorld.GetBone( parentBoneIndex ), out );
  530. MatrixSetColumn( out, 3, pBoneToWorld.GetBoneForWrite( boneIndex ) );
  531. }
  532. return true;
  533. }
  534. void RagdollComputeExactBbox( const ragdoll_t &ragdoll, const Vector &origin, Vector &outMins, Vector &outMaxs )
  535. {
  536. outMins = origin;
  537. outMaxs = origin;
  538. for ( int i = 0; i < ragdoll.listCount; i++ )
  539. {
  540. Vector mins, maxs;
  541. IPhysicsObject *pObject = ragdoll.list[i].pObject;
  542. Vector objectOrg;
  543. QAngle objectAng;
  544. pObject->GetPosition( &objectOrg, &objectAng );
  545. physcollision->CollideGetAABB( &mins, &maxs, pObject->GetCollide(), objectOrg, objectAng );
  546. for ( int j = 0; j < 3; j++ )
  547. {
  548. if ( mins[j] < outMins[j] )
  549. {
  550. outMins[j] = mins[j];
  551. }
  552. if ( maxs[j] > outMaxs[j] )
  553. {
  554. outMaxs[j] = maxs[j];
  555. }
  556. }
  557. }
  558. }
  559. void RagdollComputeApproximateBbox( const ragdoll_t &ragdoll, const Vector &origin, Vector &outMins, Vector &outMaxs )
  560. {
  561. Vector mins, maxs;
  562. ClearBounds(mins,maxs);
  563. for ( int i = 0; i < ragdoll.listCount; i++ )
  564. {
  565. Vector objectOrg;
  566. ragdoll.list[i].pObject->GetPosition( &objectOrg, NULL );
  567. float radius = physcollision->CollideGetRadius( ragdoll.list[i].pObject->GetCollide() );
  568. for ( int k = 0; k < 3; k++ )
  569. {
  570. float ext = objectOrg[k] + radius;
  571. maxs[k] = fpmax( maxs[k], ext );
  572. ext = objectOrg[k] - radius;
  573. mins[k] = fpmin( mins[k], ext );
  574. }
  575. }
  576. outMins = mins;
  577. outMaxs = maxs;
  578. }
  579. bool RagdollIsAsleep( const ragdoll_t &ragdoll )
  580. {
  581. for ( int i = 0; i < ragdoll.listCount; i++ )
  582. {
  583. if ( ragdoll.list[i].pObject && !ragdoll.list[i].pObject->IsAsleep() )
  584. return false;
  585. }
  586. return true;
  587. }
  588. void RagdollSolveSeparation( ragdoll_t &ragdoll, CBaseEntity *pEntity )
  589. {
  590. byte needsFix[256];
  591. int fixCount = 0;
  592. Assert(ragdoll.listCount<=ARRAYSIZE(needsFix));
  593. for ( int i = 0; i < ragdoll.listCount; i++ )
  594. {
  595. needsFix[i] = 0;
  596. const ragdollelement_t &element = ragdoll.list[i];
  597. if ( element.pConstraint && element.parentIndex >= 0 )
  598. {
  599. Vector start, target;
  600. element.pObject->GetPosition( &start, NULL );
  601. ragdoll.list[element.parentIndex].pObject->LocalToWorld( &target, element.originParentSpace );
  602. if ( needsFix[element.parentIndex] )
  603. {
  604. needsFix[i] = 1;
  605. ++fixCount;
  606. continue;
  607. }
  608. Vector dir = target-start;
  609. if ( dir.LengthSqr() > 1.0f )
  610. {
  611. // this fixes a bug in ep2 with antlion grubs, but causes problems in TF2 - revisit, but disable for TF now
  612. #if !defined(TF_CLIENT_DLL)
  613. // heuristic: guess that anything separated and small mass ratio is in some state that's
  614. // keeping the solver from fixing it
  615. float mass = element.pObject->GetMass();
  616. float massParent = ragdoll.list[element.parentIndex].pObject->GetMass();
  617. if ( mass*2.0f < massParent )
  618. {
  619. // if this is <0.5 mass of parent and still separated it's attached to something heavy or
  620. // in a bad state
  621. needsFix[i] = 1;
  622. ++fixCount;
  623. continue;
  624. }
  625. #endif
  626. if ( PhysHasContactWithOtherInDirection(element.pObject, dir) )
  627. {
  628. Ray_t ray;
  629. trace_t tr;
  630. ray.Init( target, start );
  631. UTIL_TraceRay( ray, MASK_SOLID, pEntity, COLLISION_GROUP_NONE, &tr );
  632. if ( tr.DidHit() )
  633. {
  634. needsFix[i] = 1;
  635. ++fixCount;
  636. }
  637. }
  638. }
  639. }
  640. }
  641. if ( fixCount )
  642. {
  643. for ( int i = 0; i < ragdoll.listCount; i++ )
  644. {
  645. if ( !needsFix[i] )
  646. continue;
  647. const ragdollelement_t &element = ragdoll.list[i];
  648. Vector target, velocity;
  649. ragdoll.list[element.parentIndex].pObject->LocalToWorld( &target, element.originParentSpace );
  650. ragdoll.list[element.parentIndex].pObject->GetVelocityAtPoint( target, &velocity );
  651. matrix3x4_t xform;
  652. element.pObject->GetPositionMatrix( &xform );
  653. MatrixSetColumn( target, 3, xform );
  654. element.pObject->SetPositionMatrix( xform, true );
  655. element.pObject->SetVelocity( &velocity, &vec3_origin );
  656. }
  657. DevMsg(2, "TICK:%5d:Ragdoll separation count: %d\n", gpGlobals->tickcount, fixCount );
  658. }
  659. else
  660. {
  661. ragdoll.pGroup->ClearErrorState();
  662. }
  663. }
  664. //-----------------------------------------------------------------------------
  665. // LRU
  666. //-----------------------------------------------------------------------------
  667. #ifdef _XBOX
  668. // xbox defaults to 4 ragdolls max
  669. ConVar g_ragdoll_maxcount("g_ragdoll_maxcount", "4", FCVAR_REPLICATED );
  670. #else
  671. ConVar g_ragdoll_maxcount("g_ragdoll_maxcount", "8", FCVAR_REPLICATED );
  672. #endif
  673. ConVar g_debug_ragdoll_removal("g_debug_ragdoll_removal", "0", FCVAR_REPLICATED |FCVAR_CHEAT );
  674. CRagdollLRURetirement s_RagdollLRU( "CRagdollLRURetirement" );
  675. void CRagdollLRURetirement::LevelInitPreEntity( void )
  676. {
  677. m_iMaxRagdolls = -1;
  678. m_LRUImportantRagdolls.RemoveAll();
  679. m_LRU.RemoveAll();
  680. }
  681. bool ShouldRemoveThisRagdoll( CBaseAnimating *pRagdoll )
  682. {
  683. if ( g_RagdollLVManager.IsLowViolence() )
  684. {
  685. return true;
  686. }
  687. #ifdef CLIENT_DLL
  688. /* we no longer ignore enemies just because they are on fire -- a ragdoll in front of me
  689. is always a higher priority for retention than a flaming zombie behind me. At the
  690. time I put this in, the ragdolls do clean up their own effects if culled via SUB_Remove().
  691. If you're encountering trouble with ragdolls leaving effects behind, try renabling the code below.
  692. /////////////////////
  693. //Just ignore it until we're done burning/dissolving.
  694. if ( pRagdoll->GetEffectEntity() )
  695. return false;
  696. */
  697. Vector vMins, vMaxs;
  698. Vector origin = pRagdoll->m_pRagdoll->GetRagdollOrigin();
  699. pRagdoll->m_pRagdoll->GetRagdollBounds( vMins, vMaxs );
  700. if( engine->IsBoxInViewCluster( vMins + origin, vMaxs + origin) == false )
  701. {
  702. if ( g_debug_ragdoll_removal.GetBool() )
  703. {
  704. debugoverlay->AddBoxOverlay( origin, vMins, vMaxs, QAngle( 0, 0, 0 ), 0, 255, 0, 16, 5 );
  705. debugoverlay->AddLineOverlay( origin, origin + Vector( 0, 0, 64 ), 0, 255, 0, true, 5 );
  706. }
  707. return true;
  708. }
  709. else if( engine->CullBox( vMins + origin, vMaxs + origin ) == true )
  710. {
  711. if ( g_debug_ragdoll_removal.GetBool() )
  712. {
  713. debugoverlay->AddBoxOverlay( origin, vMins, vMaxs, QAngle( 0, 0, 0 ), 0, 0, 255, 16, 5 );
  714. debugoverlay->AddLineOverlay( origin, origin + Vector( 0, 0, 64 ), 0, 0, 255, true, 5 );
  715. }
  716. return true;
  717. }
  718. #else
  719. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  720. if( !UTIL_FindClientInPVS( pRagdoll->edict() ) )
  721. {
  722. if ( g_debug_ragdoll_removal.GetBool() )
  723. NDebugOverlay::Line( pRagdoll->GetAbsOrigin(), pRagdoll->GetAbsOrigin() + Vector( 0, 0, 64 ), 0, 255, 0, true, 5 );
  724. return true;
  725. }
  726. else if( !pPlayer->FInViewCone( pRagdoll ) )
  727. {
  728. if ( g_debug_ragdoll_removal.GetBool() )
  729. NDebugOverlay::Line( pRagdoll->GetAbsOrigin(), pRagdoll->GetAbsOrigin() + Vector( 0, 0, 64 ), 0, 0, 255, true, 5 );
  730. return true;
  731. }
  732. #endif
  733. return false;
  734. }
  735. //-----------------------------------------------------------------------------
  736. // Cull stale ragdolls. There is an ifdef here: one version for episodic,
  737. // one for everything else.
  738. //-----------------------------------------------------------------------------
  739. #if HL2_EPISODIC
  740. void CRagdollLRURetirement::Update( float frametime ) // EPISODIC VERSION
  741. {
  742. VPROF( "CRagdollLRURetirement::Update" );
  743. // Compress out dead items
  744. int i, next;
  745. int iMaxRagdollCount = m_iMaxRagdolls;
  746. if ( iMaxRagdollCount == -1 )
  747. {
  748. iMaxRagdollCount = g_ragdoll_maxcount.GetInt();
  749. }
  750. // fade them all for the low violence version
  751. if ( g_RagdollLVManager.IsLowViolence() )
  752. {
  753. iMaxRagdollCount = 0;
  754. }
  755. m_iRagdollCount = 0;
  756. m_iSimulatedRagdollCount = 0;
  757. // First, find ragdolls that are good candidates for deletion because they are not
  758. // visible at all, or are in a culled visibility box
  759. for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next )
  760. {
  761. next = m_LRU.Next(i);
  762. CBaseAnimating *pRagdoll = m_LRU[i].Get();
  763. if ( pRagdoll )
  764. {
  765. m_iRagdollCount++;
  766. IPhysicsObject *pObject = pRagdoll->VPhysicsGetObject();
  767. if (pObject && !pObject->IsAsleep())
  768. {
  769. m_iSimulatedRagdollCount++;
  770. }
  771. if ( m_LRU.Count() > iMaxRagdollCount )
  772. {
  773. //Found one, we're done.
  774. if ( ShouldRemoveThisRagdoll( pRagdoll ) == true )
  775. {
  776. #ifdef CLIENT_DLL
  777. pRagdoll->SUB_Remove();
  778. #else
  779. pRagdoll->SUB_StartFadeOut( 0 );
  780. #endif
  781. m_LRU.Remove(i);
  782. return;
  783. }
  784. }
  785. }
  786. else
  787. {
  788. m_LRU.Remove(i);
  789. }
  790. }
  791. //////////////////////////////
  792. /// EPISODIC ALGORITHM ///
  793. //////////////////////////////
  794. // If we get here, it means we couldn't find a suitable ragdoll to remove,
  795. // so just remove the furthest one.
  796. int furthestOne = m_LRU.Head();
  797. float furthestDistSq = 0;
  798. #ifdef CLIENT_DLL
  799. ACTIVE_SPLITSCREEN_PLAYER_GUARD( 0 );
  800. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  801. #else
  802. CBasePlayer *pPlayer = g_pGameRules->IsMultiplayer() ? NULL : UTIL_GetLocalPlayer();
  803. #endif
  804. if (pPlayer && m_LRU.Count() > iMaxRagdollCount) // find the furthest one algorithm
  805. {
  806. Vector PlayerOrigin = pPlayer->GetAbsOrigin();
  807. // const CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  808. for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next )
  809. {
  810. CBaseAnimating *pRagdoll = m_LRU[i].Get();
  811. next = m_LRU.Next(i);
  812. IPhysicsObject *pObject = pRagdoll->VPhysicsGetObject();
  813. if ( pRagdoll && (pRagdoll->GetEffectEntity() || ( pObject && !pObject->IsAsleep()) ) )
  814. continue;
  815. if ( pRagdoll )
  816. {
  817. // float distToPlayer = (pPlayer->GetAbsOrigin() - pRagdoll->GetAbsOrigin()).LengthSqr();
  818. float distToPlayer = (PlayerOrigin - pRagdoll->GetAbsOrigin()).LengthSqr();
  819. if (distToPlayer > furthestDistSq)
  820. {
  821. furthestOne = i;
  822. furthestDistSq = distToPlayer;
  823. }
  824. }
  825. else // delete bad rags first.
  826. {
  827. furthestOne = i;
  828. break;
  829. }
  830. }
  831. CBaseAnimating *pRemoveRagdoll = m_LRU[ furthestOne ].Get();
  832. #ifdef CLIENT_DLL
  833. pRemoveRagdoll->SUB_Remove();
  834. #else
  835. pRemoveRagdoll->SUB_StartFadeOut( 0 );
  836. #endif
  837. }
  838. else // fall back on old-style pick the oldest one algorithm
  839. {
  840. for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next )
  841. {
  842. if ( m_LRU.Count() <= iMaxRagdollCount )
  843. break;
  844. next = m_LRU.Next(i);
  845. CBaseAnimating *pRagdoll = m_LRU[i].Get();
  846. //Just ignore it until we're done burning/dissolving.
  847. IPhysicsObject *pObject = pRagdoll->VPhysicsGetObject();
  848. if ( pRagdoll && (pRagdoll->GetEffectEntity() || ( pObject && !pObject->IsAsleep()) ) )
  849. continue;
  850. #ifdef CLIENT_DLL
  851. pRagdoll->SUB_Remove();
  852. #else
  853. pRagdoll->SUB_StartFadeOut( 0 );
  854. #endif
  855. m_LRU.Remove(i);
  856. }
  857. }
  858. }
  859. #else
  860. void CRagdollLRURetirement::Update( float frametime ) // Non-episodic version
  861. {
  862. VPROF( "CRagdollLRURetirement::Update" );
  863. // Compress out dead items
  864. int i, next;
  865. int iMaxRagdollCount = m_iMaxRagdolls;
  866. if ( iMaxRagdollCount == -1 )
  867. {
  868. iMaxRagdollCount = g_ragdoll_maxcount.GetInt();
  869. }
  870. // fade them all for the low violence version
  871. if ( g_RagdollLVManager.IsLowViolence() )
  872. {
  873. iMaxRagdollCount = 0;
  874. }
  875. m_iRagdollCount = 0;
  876. m_iSimulatedRagdollCount = 0;
  877. // remove ragdolls with a forced retire time
  878. for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next )
  879. {
  880. next = m_LRU.Next(i);
  881. CBaseAnimating *pRagdoll = m_LRU[i].Get();
  882. //Just ignore it until we're done burning/dissolving.
  883. if ( pRagdoll && pRagdoll->GetEffectEntity() )
  884. continue;
  885. // ignore if it's not time to force retire this ragdoll
  886. if ( m_LRU[i].GetForcedRetireTime() == 0.0f || gpGlobals->curtime < m_LRU[i].GetForcedRetireTime() )
  887. continue;
  888. //Msg(" Removing ragdoll %s due to forced retire time of %f (now = %f)\n", pRagdoll->GetModelName(), m_LRU[i].GetForcedRetireTime(), gpGlobals->curtime );
  889. #ifdef CLIENT_DLL
  890. pRagdoll->SUB_Remove();
  891. #else
  892. pRagdoll->SUB_StartFadeOut( 0 );
  893. #endif
  894. m_LRU.Remove(i);
  895. }
  896. for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next )
  897. {
  898. next = m_LRU.Next(i);
  899. CBaseAnimating *pRagdoll = m_LRU[i].Get();
  900. if ( pRagdoll )
  901. {
  902. m_iRagdollCount++;
  903. IPhysicsObject *pObject = pRagdoll->VPhysicsGetObject();
  904. if (pObject && !pObject->IsAsleep())
  905. {
  906. m_iSimulatedRagdollCount++;
  907. }
  908. if ( m_LRU.Count() > iMaxRagdollCount )
  909. {
  910. //Found one, we're done.
  911. if ( ShouldRemoveThisRagdoll( pRagdoll ) == true )
  912. {
  913. #ifdef CLIENT_DLL
  914. pRagdoll->SUB_Remove();
  915. #else
  916. pRagdoll->SUB_StartFadeOut( 0 );
  917. #endif
  918. m_LRU.Remove(i);
  919. return;
  920. }
  921. }
  922. }
  923. else
  924. {
  925. m_LRU.Remove(i);
  926. }
  927. }
  928. //////////////////////////////
  929. /// ORIGINAL ALGORITHM ///
  930. //////////////////////////////
  931. // not episodic -- this is the original mechanism
  932. for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next )
  933. {
  934. if ( m_LRU.Count() <= iMaxRagdollCount )
  935. break;
  936. next = m_LRU.Next(i);
  937. CBaseAnimating *pRagdoll = m_LRU[i].Get();
  938. //Just ignore it until we're done burning/dissolving.
  939. if ( pRagdoll && pRagdoll->GetEffectEntity() )
  940. continue;
  941. #ifdef CLIENT_DLL
  942. pRagdoll->SUB_Remove();
  943. #else
  944. pRagdoll->SUB_StartFadeOut( 0 );
  945. #endif
  946. m_LRU.Remove(i);
  947. }
  948. }
  949. #endif // HL2_EPISODIC
  950. //This is pretty hacky, it's only called on the server so it just calls the update method.
  951. void CRagdollLRURetirement::FrameUpdatePostEntityThink( void )
  952. {
  953. Update( 0 );
  954. }
  955. ConVar g_ragdoll_important_maxcount( "g_ragdoll_important_maxcount", "2", FCVAR_REPLICATED );
  956. //-----------------------------------------------------------------------------
  957. // Move it to the top of the LRU
  958. //-----------------------------------------------------------------------------
  959. void CRagdollLRURetirement::MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant, float flForcedRetireTime )
  960. {
  961. if ( bImportant )
  962. {
  963. m_LRUImportantRagdolls.AddToTail( CRagdollEntry( pRagdoll, flForcedRetireTime ) );
  964. if ( m_LRUImportantRagdolls.Count() > g_ragdoll_important_maxcount.GetInt() )
  965. {
  966. int iIndex = m_LRUImportantRagdolls.Head();
  967. CBaseAnimating *pRagdoll = m_LRUImportantRagdolls[iIndex].Get();
  968. if ( pRagdoll )
  969. {
  970. #ifdef CLIENT_DLL
  971. pRagdoll->SUB_Remove();
  972. #else
  973. pRagdoll->SUB_StartFadeOut( 0 );
  974. #endif
  975. m_LRUImportantRagdolls.Remove(iIndex);
  976. }
  977. }
  978. return;
  979. }
  980. for ( int i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = m_LRU.Next(i) )
  981. {
  982. if ( m_LRU[i].Get() == pRagdoll )
  983. {
  984. m_LRU.Remove(i);
  985. break;
  986. }
  987. }
  988. m_LRU.AddToTail( CRagdollEntry( pRagdoll, flForcedRetireTime ) );
  989. }
  990. //EFFECT/ENTITY TRANSFERS
  991. //CLIENT
  992. #ifdef CLIENT_DLL
  993. #define DEFAULT_FADE_START 2.0f
  994. #define DEFAULT_MODEL_FADE_START 1.9f
  995. #define DEFAULT_MODEL_FADE_LENGTH 0.1f
  996. #define DEFAULT_FADEIN_LENGTH 1.0f
  997. C_EntityDissolve *DissolveEffect( C_BaseAnimating *pTarget, float flTime )
  998. {
  999. C_EntityDissolve *pDissolve = new C_EntityDissolve;
  1000. if ( pDissolve->InitializeAsClientEntity( "sprites/blueglow1.vmt", false ) == false )
  1001. {
  1002. UTIL_Remove( pDissolve );
  1003. return NULL;
  1004. }
  1005. if ( pDissolve != NULL )
  1006. {
  1007. pTarget->AddFlag( FL_DISSOLVING );
  1008. pDissolve->SetParent( pTarget );
  1009. pDissolve->OnDataChanged( DATA_UPDATE_CREATED );
  1010. pDissolve->SetAbsOrigin( pTarget->GetAbsOrigin() );
  1011. pDissolve->m_flStartTime = flTime;
  1012. pDissolve->m_flFadeOutStart = DEFAULT_FADE_START;
  1013. pDissolve->m_flFadeOutModelStart = DEFAULT_MODEL_FADE_START;
  1014. pDissolve->m_flFadeOutModelLength = DEFAULT_MODEL_FADE_LENGTH;
  1015. pDissolve->m_flFadeInLength = DEFAULT_FADEIN_LENGTH;
  1016. pDissolve->m_nDissolveType = 0;
  1017. pDissolve->m_flNextSparkTime = 0.0f;
  1018. pDissolve->m_flFadeOutLength = 0.0f;
  1019. pDissolve->m_flFadeInStart = 0.0f;
  1020. // Let this entity know it needs to delete itself when it's done
  1021. pDissolve->SetServerLinkState( false );
  1022. pTarget->SetEffectEntity( pDissolve );
  1023. }
  1024. return pDissolve;
  1025. }
  1026. C_EntityFlame *FireEffect( C_BaseAnimating *pTarget, C_BaseEntity *pServerFire, float *flScaleEnd, float *flTimeStart, float *flTimeEnd )
  1027. {
  1028. C_EntityFlame *pFire = new C_EntityFlame;
  1029. if ( pFire->InitializeAsClientEntity( NULL, false ) == false )
  1030. {
  1031. UTIL_Remove( pFire );
  1032. return NULL;
  1033. }
  1034. if ( pFire != NULL )
  1035. {
  1036. pFire->RemoveFromLeafSystem();
  1037. pTarget->AddFlag( FL_ONFIRE );
  1038. pFire->SetParent( pTarget );
  1039. pFire->m_hEntAttached = (C_BaseEntity *) pTarget;
  1040. pFire->OnDataChanged( DATA_UPDATE_CREATED );
  1041. pFire->SetAbsOrigin( pTarget->GetAbsOrigin() );
  1042. #ifdef HL2_EPISODIC
  1043. if ( pServerFire )
  1044. {
  1045. if ( pServerFire->IsEffectActive(EF_DIMLIGHT) )
  1046. {
  1047. pFire->AddEffects( EF_DIMLIGHT );
  1048. }
  1049. if ( pServerFire->IsEffectActive(EF_BRIGHTLIGHT) )
  1050. {
  1051. pFire->AddEffects( EF_BRIGHTLIGHT );
  1052. }
  1053. }
  1054. #endif
  1055. //Play a sound
  1056. CBroadcastRecipientFilter filter;
  1057. pTarget->EmitSound( filter, pTarget->GetSoundSourceIndex(), "General.BurningFlesh" );
  1058. pFire->SetNextClientThink( gpGlobals->curtime + 7.0f );
  1059. }
  1060. return pFire;
  1061. }
  1062. void C_BaseAnimating::IgniteRagdoll( C_BaseAnimating *pSource )
  1063. {
  1064. C_BaseEntity *pChild = pSource->GetEffectEntity();
  1065. if ( pChild )
  1066. {
  1067. C_EntityFlame *pFireChild = dynamic_cast<C_EntityFlame *>( pChild );
  1068. C_ClientRagdoll *pRagdoll = dynamic_cast< C_ClientRagdoll * > ( this );
  1069. if ( pFireChild )
  1070. {
  1071. pRagdoll->SetEffectEntity ( FireEffect( pRagdoll, pFireChild, NULL, NULL, NULL ) );
  1072. }
  1073. }
  1074. }
  1075. void C_BaseAnimating::TransferDissolveFrom( C_BaseAnimating *pSource )
  1076. {
  1077. C_BaseEntity *pChild = pSource->GetEffectEntity();
  1078. if ( pChild )
  1079. {
  1080. C_EntityDissolve *pDissolveChild = dynamic_cast<C_EntityDissolve *>( pChild );
  1081. if ( pDissolveChild )
  1082. {
  1083. C_ClientRagdoll *pRagdoll = dynamic_cast< C_ClientRagdoll * > ( this );
  1084. if ( pRagdoll )
  1085. {
  1086. pRagdoll->m_flEffectTime = pDissolveChild->m_flStartTime;
  1087. C_EntityDissolve *pDissolve = DissolveEffect( pRagdoll, pRagdoll->m_flEffectTime );
  1088. if ( pDissolve )
  1089. {
  1090. pDissolve->SetRenderMode( pDissolveChild->GetRenderMode() );
  1091. pDissolve->SetRenderFX( pDissolveChild->GetRenderFX() );
  1092. pDissolve->SetRenderColor( 255, 255, 255 );
  1093. pDissolve->SetRenderAlpha( 255 );
  1094. pDissolveChild->SetRenderAlpha( 0 );
  1095. pDissolve->m_vDissolverOrigin = pDissolveChild->m_vDissolverOrigin;
  1096. pDissolve->m_nDissolveType = pDissolveChild->m_nDissolveType;
  1097. if ( pDissolve->m_nDissolveType == ENTITY_DISSOLVE_CORE )
  1098. {
  1099. pDissolve->m_nMagnitude = pDissolveChild->m_nMagnitude;
  1100. pDissolve->m_flFadeOutStart = CORE_DISSOLVE_FADE_START;
  1101. pDissolve->m_flFadeOutModelStart = CORE_DISSOLVE_MODEL_FADE_START;
  1102. pDissolve->m_flFadeOutModelLength = CORE_DISSOLVE_MODEL_FADE_LENGTH;
  1103. pDissolve->m_flFadeInLength = CORE_DISSOLVE_FADEIN_LENGTH;
  1104. }
  1105. }
  1106. }
  1107. }
  1108. }
  1109. }
  1110. #endif
  1111. //SERVER
  1112. #if !defined( CLIENT_DLL )
  1113. //-----------------------------------------------------------------------------
  1114. // Transfer dissolve
  1115. //-----------------------------------------------------------------------------
  1116. void CBaseAnimating::TransferDissolveFrom( CBaseAnimating *pAnim )
  1117. {
  1118. if ( !pAnim || !pAnim->IsDissolving() )
  1119. return;
  1120. CEntityDissolve *pDissolve = CEntityDissolve::Create( this, pAnim );
  1121. if (pDissolve)
  1122. {
  1123. AddFlag( FL_DISSOLVING );
  1124. m_flDissolveStartTime = pAnim->m_flDissolveStartTime;
  1125. CEntityDissolve *pDissolveFrom = dynamic_cast < CEntityDissolve * > (pAnim->GetEffectEntity());
  1126. if ( pDissolveFrom )
  1127. {
  1128. pDissolve->SetDissolverOrigin( pDissolveFrom->GetDissolverOrigin() );
  1129. pDissolve->SetDissolveType( pDissolveFrom->GetDissolveType() );
  1130. if ( pDissolveFrom->GetDissolveType() == ENTITY_DISSOLVE_CORE )
  1131. {
  1132. pDissolve->SetMagnitude( pDissolveFrom->GetMagnitude() );
  1133. pDissolve->m_flFadeOutStart = CORE_DISSOLVE_FADE_START;
  1134. pDissolve->m_flFadeOutModelStart = CORE_DISSOLVE_MODEL_FADE_START;
  1135. pDissolve->m_flFadeOutModelLength = CORE_DISSOLVE_MODEL_FADE_LENGTH;
  1136. pDissolve->m_flFadeInLength = CORE_DISSOLVE_FADEIN_LENGTH;
  1137. }
  1138. }
  1139. }
  1140. }
  1141. #endif