2023-10-03 14:23:56 +00:00
//===== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
2020-04-22 16:56:21 +00:00
//
// Purpose:
//
2023-10-03 14:23:56 +00:00
//===========================================================================//
2020-04-22 16:56:21 +00:00
# include "cbase.h"
# include "ragdoll_shared.h"
# include "bone_setup.h"
# include "vphysics/constraints.h"
# include "vphysics/collision_set.h"
# include "vcollide_parse.h"
# include "vphysics_interface.h"
# include "tier0/vprof.h"
# include "engine/ivdebugoverlay.h"
# include "solidsetdefaults.h"
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
//CLIENT
# ifdef CLIENT_DLL
2023-10-03 14:23:56 +00:00
# include "c_entityflame.h"
2020-04-22 16:56:21 +00:00
# include "c_fire_smoke.h"
# include "c_entitydissolve.h"
# include "engine/IEngineSound.h"
# endif
//SERVER
# if !defined( CLIENT_DLL )
# include "util.h"
# include "EntityFlame.h"
# include "EntityDissolve.h"
# endif
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
CRagdollLowViolenceManager g_RagdollLVManager ;
void CRagdollLowViolenceManager : : SetLowViolence ( const char * pMapName )
{
// set the value using the engine's low violence settings
m_bLowViolence = UTIL_IsLowViolence ( ) ;
# if !defined( CLIENT_DLL )
// the server doesn't worry about low violence during multiplayer games
2022-06-04 22:44:42 +00:00
if ( g_pGameRules & & g_pGameRules - > IsMultiplayer ( ) )
2020-04-22 16:56:21 +00:00
{
m_bLowViolence = false ;
}
# endif
// Turn the low violence ragdoll stuff off if we're in the HL2 Citadel maps because
// the player has the super gravity gun and fading ragdolls will break things.
if ( hl2_episodic . GetBool ( ) )
{
if ( Q_stricmp ( pMapName , " ep1_citadel_02 " ) = = 0 | |
Q_stricmp ( pMapName , " ep1_citadel_02b " ) = = 0 | |
Q_stricmp ( pMapName , " ep1_citadel_03 " ) = = 0 )
{
m_bLowViolence = false ;
}
}
else
{
if ( Q_stricmp ( pMapName , " d3_citadel_03 " ) = = 0 | |
Q_stricmp ( pMapName , " d3_citadel_04 " ) = = 0 | |
Q_stricmp ( pMapName , " d3_citadel_05 " ) = = 0 | |
Q_stricmp ( pMapName , " d3_breen_01 " ) = = 0 )
{
m_bLowViolence = false ;
}
}
}
2023-10-03 14:23:56 +00:00
// A simple cache to store the ragdoll's data after it has been parsed. Avoid re-parsing on every create
struct cache_ragdollsolid_t
{
objectparams_t params ;
int surfacePropIndex ;
short boneIndex ;
short collideIndex ;
2020-04-22 16:56:21 +00:00
} ;
2023-10-03 14:23:56 +00:00
struct cache_ragdollconstraint_t
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
constraint_axislimit_t axes [ 3 ] ;
matrix3x4_t constraintToAttached ;
short parentIndex ;
short childIndex ;
} ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// store the ragdoll as a single allocation header, then solids array, then constraints array linearly in memory
struct cache_ragdoll_t
{
IPhysicsCollisionSet * pCollisionSet ;
ragdollanimatedfriction_t animfriction ;
short solidCount ;
short constraintCount ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
const cache_ragdollsolid_t * GetSolids ( ) { return ( cache_ragdollsolid_t * ) ( this + 1 ) ; }
const cache_ragdollconstraint_t * GetConstraints ( ) { return ( cache_ragdollconstraint_t * ) ( GetSolids ( ) + solidCount ) ; }
2020-04-22 16:56:21 +00:00
} ;
2023-10-03 14:23:56 +00:00
cache_ragdoll_t * CreateRagdollCache ( vcollide_t * pOutput , cache_ragdollsolid_t * pSolids , cache_ragdollconstraint_t * pConstraints , cache_ragdoll_t * pRagdoll )
{
size_t memSize = sizeof ( cache_ragdoll_t ) ;
size_t solidSize = sizeof ( cache_ragdollsolid_t ) * pRagdoll - > solidCount ;
size_t constraintSize = sizeof ( cache_ragdollconstraint_t ) * pRagdoll - > constraintCount ;
cache_ragdoll_t * pMem = ( cache_ragdoll_t * ) physcollision - > VCollideAllocUserData ( pOutput , memSize + solidSize + constraintSize ) ;
V_memcpy ( pMem , pRagdoll , sizeof ( * pMem ) ) ;
V_memcpy ( ( void * ) pMem - > GetSolids ( ) , pSolids , solidSize ) ;
V_memcpy ( ( void * ) pMem - > GetConstraints ( ) , pConstraints , constraintSize ) ;
return pMem ;
}
// OPTIMIZE: Slow, hopefully this is only called by save/load
2020-04-22 16:56:21 +00:00
void RagdollSetupAnimatedFriction ( IPhysicsEnvironment * pPhysEnv , ragdoll_t * ragdoll , int iModelIndex )
{
vcollide_t * pCollide = modelinfo - > GetVCollide ( iModelIndex ) ;
if ( pCollide )
{
2023-10-03 14:23:56 +00:00
IVPhysicsKeyParser * pParse = physcollision - > VPhysicsKeyParserCreate ( pCollide ) ;
2020-04-22 16:56:21 +00:00
while ( ! pParse - > Finished ( ) )
{
const char * pBlock = pParse - > GetCurrentBlockName ( ) ;
if ( ! strcmpi ( pBlock , " animatedfriction " ) )
{
2023-10-03 14:23:56 +00:00
pParse - > ParseRagdollAnimatedFriction ( & ragdoll - > animfriction , NULL ) ;
2020-04-22 16:56:21 +00:00
}
else
{
pParse - > SkipBlock ( ) ;
}
}
physcollision - > VPhysicsKeyParserDestroy ( pParse ) ;
}
}
2023-10-03 14:23:56 +00:00
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 )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
const char * pszName = params . pStudioHdr - > pszName ( ) ;
Vector position ;
matrix3x4_t xform ;
// init parent index
for ( int i = 0 ; i < solidCount ; i + + )
{
ragdoll . list [ i ] . parentIndex = - 1 ;
}
// now set from constraints
for ( int i = 0 ; i < constraintCount ; i + + )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
// save parent index
ragdoll . list [ pConstraints [ i ] . childIndex ] . parentIndex = pConstraints [ i ] . parentIndex ;
MatrixGetColumn ( pConstraints [ i ] . constraintToAttached , 3 , ragdoll . list [ pConstraints [ i ] . childIndex ] . originParentSpace ) ;
}
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// now setup the solids, using parent indices
for ( int i = 0 ; i < solidCount ; i + + )
{
ragdoll . boneIndex [ i ] = pSolids [ i ] . boneIndex ;
pSolids [ i ] . params . pName = pszName ;
pSolids [ i ] . params . pGameData = params . pGameData ;
ragdoll . list [ i ] . pObject = pPhysEnv - > CreatePolyObject ( params . pCollide - > solids [ pSolids [ i ] . collideIndex ] , pSolids [ i ] . surfacePropIndex , vec3_origin , vec3_angle , & pSolids [ i ] . params ) ;
ragdoll . list [ i ] . pObject - > SetGameIndex ( i ) ;
int parentIndex = ragdoll . list [ i ] . parentIndex ;
MatrixCopy ( params . pCurrentBones [ ragdoll . boneIndex [ i ] ] , xform ) ;
if ( parentIndex > = 0 )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
Assert ( parentIndex < i ) ;
ragdoll . list [ parentIndex ] . pObject - > LocalToWorld ( & position , ragdoll . list [ i ] . originParentSpace ) ;
MatrixSetColumn ( position , 3 , xform ) ;
}
ragdoll . list [ i ] . pObject - > SetPositionMatrix ( xform , true ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
PhysSetGameFlags ( ragdoll . list [ i ] . pObject , FVPHYSICS_PART_OF_RAGDOLL ) ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
ragdoll . listCount = solidCount ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
static void RagdollAddConstraints ( IPhysicsEnvironment * pPhysEnv , ragdoll_t & ragdoll , const ragdollparams_t & params , const cache_ragdollconstraint_t * pConstraints , int constraintCount )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
constraint_ragdollparams_t constraint ;
for ( int i = 0 ; i < constraintCount ; i + + )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
constraint . Defaults ( ) ;
V_memcpy ( constraint . axes , pConstraints [ i ] . axes , sizeof ( constraint . axes ) ) ;
2020-04-22 16:56:21 +00:00
if ( params . jointFrictionScale > 0 )
{
for ( int k = 0 ; k < 3 ; k + + )
{
constraint . axes [ k ] . torque * = params . jointFrictionScale ;
}
}
2023-10-03 14:23:56 +00:00
int parentIndex = pConstraints [ i ] . parentIndex ;
int childIndex = pConstraints [ i ] . childIndex ;
constraint . childIndex = childIndex ;
constraint . parentIndex = parentIndex ;
constraint . useClockwiseRotations = true ;
constraint . constraintToAttached = pConstraints [ i ] . constraintToAttached ;
2020-04-22 16:56:21 +00:00
// UNDONE: We could transform the constraint limit axes relative to the bone space
// using this data. Do we need that feature?
SetIdentityMatrix ( constraint . constraintToReference ) ;
2023-10-03 14:23:56 +00:00
ragdoll . list [ childIndex ] . pConstraint = pPhysEnv - > CreateRagdollConstraint ( ragdoll . list [ childIndex ] . pObject , ragdoll . list [ parentIndex ] . pObject , ragdoll . pGroup , constraint ) ;
2020-04-22 16:56:21 +00:00
}
}
2023-10-03 14:23:56 +00:00
static cache_ragdoll_t * ParseRagdollIntoCache ( CStudioHdr * pStudioHdr , vcollide_t * pCollide , int modelIndex )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
IVPhysicsKeyParser * pParse = physcollision - > VPhysicsKeyParserCreate ( pCollide ) ;
cache_ragdollsolid_t solidList [ RAGDOLL_MAX_ELEMENTS ] ;
cache_ragdollconstraint_t constraintList [ RAGDOLL_MAX_ELEMENTS ] ;
solid_t solid ;
int constraintCount = 0 ;
int solidCount = 0 ;
cache_ragdoll_t cache ;
V_memset ( & cache , 0 , sizeof ( cache ) ) ;
2020-04-22 16:56:21 +00:00
while ( ! pParse - > Finished ( ) )
{
const char * pBlock = pParse - > GetCurrentBlockName ( ) ;
if ( ! strcmpi ( pBlock , " solid " ) )
{
pParse - > ParseSolid ( & solid , & g_SolidSetup ) ;
2023-10-03 14:23:56 +00:00
cache_ragdollsolid_t * pSolid = & solidList [ solidCount ] ;
pSolid - > boneIndex = Studio_BoneIndexByName ( pStudioHdr , solid . name ) ;
if ( pSolid - > boneIndex > = 0 )
{
pSolid - > collideIndex = solid . index ;
pSolid - > surfacePropIndex = physprops - > GetSurfaceIndex ( solid . surfaceprop ) ;
if ( pSolid - > surfacePropIndex < 0 )
{
pSolid - > surfacePropIndex = physprops - > GetSurfaceIndex ( " default " ) ;
}
pSolid - > params = solid . params ;
pSolid - > params . enableCollisions = false ;
solidCount + + ;
}
else
{
Msg ( " ParseRagdollIntoCache: Couldn't Lookup Bone %s \n " , solid . name ) ;
}
2020-04-22 16:56:21 +00:00
}
else if ( ! strcmpi ( pBlock , " ragdollconstraint " ) )
{
constraint_ragdollparams_t constraint ;
pParse - > ParseRagdollConstraint ( & constraint , NULL ) ;
2023-10-03 14:23:56 +00:00
if ( constraint . childIndex ! = constraint . parentIndex & & constraint . childIndex > = 0 & & constraint . parentIndex > = 0 )
{
cache_ragdollconstraint_t * pOut = & constraintList [ constraintCount ] ;
constraintCount + + ;
V_memcpy ( pOut - > axes , constraint . axes , sizeof ( constraint . axes ) ) ;
pOut - > parentIndex = constraint . parentIndex ;
pOut - > childIndex = constraint . childIndex ;
Studio_CalcBoneToBoneTransform ( pStudioHdr , solidList [ constraint . childIndex ] . boneIndex , solidList [ constraint . parentIndex ] . boneIndex , pOut - > constraintToAttached ) ;
}
2020-04-22 16:56:21 +00:00
}
else if ( ! strcmpi ( pBlock , " collisionrules " ) )
{
2023-10-03 14:23:56 +00:00
ragdollcollisionrules_t rules ;
IPhysicsCollisionSet * pSet = physics - > FindOrCreateCollisionSet ( modelIndex , pCollide - > solidCount ) ;
rules . Defaults ( physics , pSet ) ;
pParse - > ParseCollisionRules ( & rules , NULL ) ;
cache . pCollisionSet = rules . pCollisionSet ;
2020-04-22 16:56:21 +00:00
}
else if ( ! strcmpi ( pBlock , " animatedfriction " ) )
{
2023-10-03 14:23:56 +00:00
pParse - > ParseRagdollAnimatedFriction ( & cache . animfriction , NULL ) ;
2020-04-22 16:56:21 +00:00
}
else
{
pParse - > SkipBlock ( ) ;
}
}
physcollision - > VPhysicsKeyParserDestroy ( pParse ) ;
2023-10-03 14:23:56 +00:00
cache . solidCount = solidCount ;
cache . constraintCount = constraintCount ;
return CreateRagdollCache ( pCollide , solidList , constraintList , & cache ) ;
}
static void RagdollCreateObjects ( IPhysicsEnvironment * pPhysEnv , ragdoll_t & ragdoll , const ragdollparams_t & params )
{
ragdoll . listCount = 0 ;
ragdoll . pGroup = NULL ;
ragdoll . allowStretch = params . allowStretch ;
memset ( ragdoll . list , 0 , sizeof ( ragdoll . list ) ) ;
memset ( & ragdoll . animfriction , 0 , sizeof ( ragdoll . animfriction ) ) ;
if ( ! params . pCollide | | params . pCollide - > solidCount > RAGDOLL_MAX_ELEMENTS )
{
Warning ( " Ragdoll solid count %d exceeds maximum limit of %d - Ragdoll not created " , params . pCollide - > solidCount , RAGDOLL_MAX_ELEMENTS ) ;
Assert ( false ) ;
return ;
}
cache_ragdoll_t * pCache = ( cache_ragdoll_t * ) params . pCollide - > pUserData ;
if ( ! pCache )
{
pCache = ParseRagdollIntoCache ( params . pStudioHdr , params . pCollide , params . modelIndex ) ;
}
constraint_groupparams_t group ;
group . Defaults ( ) ;
ragdoll . pGroup = pPhysEnv - > CreateConstraintGroup ( group ) ;
RagdollAddSolids ( pPhysEnv , ragdoll , params , const_cast < cache_ragdollsolid_t * > ( pCache - > GetSolids ( ) ) , pCache - > solidCount , pCache - > GetConstraints ( ) , pCache - > constraintCount ) ;
RagdollAddConstraints ( pPhysEnv , ragdoll , params , pCache - > GetConstraints ( ) , pCache - > constraintCount ) ;
2020-04-22 16:56:21 +00:00
}
void RagdollSetupCollisions ( ragdoll_t & ragdoll , vcollide_t * pCollide , int modelIndex )
{
Assert ( pCollide ) ;
if ( ! pCollide )
return ;
IPhysicsCollisionSet * pSet = physics - > FindCollisionSet ( modelIndex ) ;
if ( ! pSet )
{
2023-10-03 14:23:56 +00:00
pSet = physics - > FindOrCreateCollisionSet ( modelIndex , pCollide - > solidCount ) ;
2020-04-22 16:56:21 +00:00
if ( ! pSet )
return ;
bool bFoundRules = false ;
2023-10-03 14:23:56 +00:00
IVPhysicsKeyParser * pParse = physcollision - > VPhysicsKeyParserCreate ( pCollide ) ;
2020-04-22 16:56:21 +00:00
while ( ! pParse - > Finished ( ) )
{
const char * pBlock = pParse - > GetCurrentBlockName ( ) ;
if ( ! strcmpi ( pBlock , " collisionrules " ) )
{
2023-10-03 14:23:56 +00:00
ragdollcollisionrules_t rules ;
rules . Defaults ( physics , pSet ) ;
pParse - > ParseCollisionRules ( & rules , NULL ) ;
Assert ( rules . pCollisionSet = = pSet ) ;
2020-04-22 16:56:21 +00:00
bFoundRules = true ;
}
else
{
pParse - > SkipBlock ( ) ;
}
}
physcollision - > VPhysicsKeyParserDestroy ( pParse ) ;
if ( ! bFoundRules )
{
// these are the default rules - each piece collides with everything
// except immediate parent/constrained object.
int i ;
for ( i = 0 ; i < ragdoll . listCount ; i + + )
{
for ( int j = i + 1 ; j < ragdoll . listCount ; j + + )
{
pSet - > EnableCollisions ( i , j ) ;
}
}
for ( i = 0 ; i < ragdoll . listCount ; i + + )
{
int parent = ragdoll . list [ i ] . parentIndex ;
if ( parent > = 0 )
{
Assert ( ragdoll . list [ i ] . pObject ) ;
Assert ( ragdoll . list [ i ] . pConstraint ) ;
pSet - > DisableCollisions ( i , parent ) ;
}
}
}
}
}
void RagdollActivate ( ragdoll_t & ragdoll , vcollide_t * pCollide , int modelIndex , bool bForceWake )
{
RagdollSetupCollisions ( ragdoll , pCollide , modelIndex ) ;
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
for ( int i = 0 ; i < ragdoll . listCount ; i + + )
{
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
PhysSetGameFlags ( ragdoll . list [ i ] . pObject , FVPHYSICS_MULTIOBJECT_ENTITY ) ;
// now that the relationships are set, activate the collision system
ragdoll . list [ i ] . pObject - > EnableCollisions ( true ) ;
if ( bForceWake = = true )
{
ragdoll . list [ i ] . pObject - > Wake ( ) ;
}
}
if ( ragdoll . pGroup )
{
// NOTE: This also wakes the objects
ragdoll . pGroup - > Activate ( ) ;
// so if we didn't want that, we'll need to put them back to sleep here
if ( ! bForceWake )
{
for ( int i = 0 ; i < ragdoll . listCount ; i + + )
{
ragdoll . list [ i ] . pObject - > Sleep ( ) ;
}
}
}
}
bool RagdollCreate ( ragdoll_t & ragdoll , const ragdollparams_t & params , IPhysicsEnvironment * pPhysEnv )
{
RagdollCreateObjects ( pPhysEnv , ragdoll , params ) ;
if ( ! ragdoll . listCount )
return false ;
int forceBone = params . forceBoneIndex ;
int i ;
float totalMass = 0 ;
for ( i = 0 ; i < ragdoll . listCount ; i + + )
{
totalMass + = ragdoll . list [ i ] . pObject - > GetMass ( ) ;
}
totalMass = MAX ( totalMass , 1 ) ;
// apply force to the model
Vector nudgeForce = params . forceVector ;
Vector forcePosition = params . forcePosition ;
// UNDONE: Test scaling the force by total mass on all bones
2023-10-03 14:23:56 +00:00
// UNDONE: forcebone can be out of range when a body part breaks off - it uses the shared force bone from the original model
// UNDONE: Remap this?
2020-04-22 16:56:21 +00:00
if ( forceBone > = 0 & & forceBone < ragdoll . listCount )
{
ragdoll . list [ forceBone ] . pObject - > ApplyForceCenter ( nudgeForce ) ;
//nudgeForce *= 0.5;
ragdoll . list [ forceBone ] . pObject - > GetPosition ( & forcePosition , NULL ) ;
}
if ( forcePosition ! = vec3_origin )
{
for ( i = 0 ; i < ragdoll . listCount ; i + + )
{
if ( forceBone ! = i )
{
float scale = ragdoll . list [ i ] . pObject - > GetMass ( ) / totalMass ;
ragdoll . list [ i ] . pObject - > ApplyForceOffset ( scale * nudgeForce , forcePosition ) ;
}
}
}
return true ;
}
void RagdollApplyAnimationAsVelocity ( ragdoll_t & ragdoll , const matrix3x4_t * pPrevBones , const matrix3x4_t * pCurrentBones , float dt )
{
for ( int i = 0 ; i < ragdoll . listCount ; i + + )
{
Vector velocity ;
AngularImpulse angVel ;
int boneIndex = ragdoll . boneIndex [ i ] ;
CalcBoneDerivatives ( velocity , angVel , pPrevBones [ boneIndex ] , pCurrentBones [ boneIndex ] , dt ) ;
AngularImpulse localAngVelocity ;
// Angular velocity is always applied in local space in vphysics
ragdoll . list [ i ] . pObject - > WorldToLocalVector ( & localAngVelocity , angVel ) ;
ragdoll . list [ i ] . pObject - > AddVelocity ( & velocity , & localAngVelocity ) ;
}
}
void RagdollApplyAnimationAsVelocity ( ragdoll_t & ragdoll , const matrix3x4_t * pBoneToWorld )
{
for ( int i = 0 ; i < ragdoll . listCount ; i + + )
{
matrix3x4_t inverse ;
MatrixInvert ( pBoneToWorld [ i ] , inverse ) ;
Quaternion q ;
Vector pos ;
MatrixAngles ( inverse , q , pos ) ;
Vector velocity ;
AngularImpulse angVel ;
float flSpin ;
Vector localVelocity ;
AngularImpulse localAngVelocity ;
QuaternionAxisAngle ( q , localAngVelocity , flSpin ) ;
localAngVelocity * = flSpin ;
localVelocity = pos ;
// move those bone-local coords back to world space using the ragdoll transform
ragdoll . list [ i ] . pObject - > LocalToWorldVector ( & velocity , localVelocity ) ;
ragdoll . list [ i ] . pObject - > AddVelocity ( & velocity , & localAngVelocity ) ;
}
}
void RagdollDestroy ( ragdoll_t & ragdoll )
{
if ( ! ragdoll . listCount )
return ;
int i ;
for ( i = 0 ; i < ragdoll . listCount ; i + + )
{
physenv - > DestroyConstraint ( ragdoll . list [ i ] . pConstraint ) ;
ragdoll . list [ i ] . pConstraint = NULL ;
}
for ( i = 0 ; i < ragdoll . listCount ; i + + )
{
// during level transitions these can get temporarily loaded without physics objects
// purely for the purpose of testing for PVS of transition. If they fail they get
// deleted before the physics objects are loaded. The list count will be nonzero
// since that is saved separately.
if ( ragdoll . list [ i ] . pObject )
{
physenv - > DestroyObject ( ragdoll . list [ i ] . pObject ) ;
}
ragdoll . list [ i ] . pObject = NULL ;
}
physenv - > DestroyConstraintGroup ( ragdoll . pGroup ) ;
ragdoll . pGroup = NULL ;
ragdoll . listCount = 0 ;
}
// Parse the ragdoll and obtain the mapping from each physics element index to a bone index
// returns num phys elements
int RagdollExtractBoneIndices ( int * boneIndexOut , CStudioHdr * pStudioHdr , vcollide_t * pCollide )
{
int elementCount = 0 ;
2023-10-03 14:23:56 +00:00
IVPhysicsKeyParser * pParse = physcollision - > VPhysicsKeyParserCreate ( pCollide ) ;
2020-04-22 16:56:21 +00:00
while ( ! pParse - > Finished ( ) )
{
const char * pBlock = pParse - > GetCurrentBlockName ( ) ;
if ( ! strcmpi ( pBlock , " solid " ) )
{
solid_t solid ;
pParse - > ParseSolid ( & solid , NULL ) ;
if ( elementCount < RAGDOLL_MAX_ELEMENTS )
{
boneIndexOut [ elementCount ] = Studio_BoneIndexByName ( pStudioHdr , solid . name ) ;
elementCount + + ;
}
}
else
{
pParse - > SkipBlock ( ) ;
}
}
physcollision - > VPhysicsKeyParserDestroy ( pParse ) ;
return elementCount ;
}
bool RagdollGetBoneMatrix ( const ragdoll_t & ragdoll , CBoneAccessor & pBoneToWorld , int objectIndex )
{
int boneIndex = ragdoll . boneIndex [ objectIndex ] ;
if ( boneIndex < 0 )
return false ;
const ragdollelement_t & element = ragdoll . list [ objectIndex ] ;
// during restore if a model has changed since the file was saved, this could be NULL
if ( ! element . pObject )
return false ;
element . pObject - > GetPositionMatrix ( & pBoneToWorld . GetBoneForWrite ( boneIndex ) ) ;
if ( element . parentIndex > = 0 & & ! ragdoll . allowStretch )
{
// overwrite the position from physics to force rigid attachment
2023-10-03 14:23:56 +00:00
// NOTE: On the client we actually override this with the proper parent bone in each LOD
2020-04-22 16:56:21 +00:00
int parentBoneIndex = ragdoll . boneIndex [ element . parentIndex ] ;
Vector out ;
VectorTransform ( element . originParentSpace , pBoneToWorld . GetBone ( parentBoneIndex ) , out ) ;
MatrixSetColumn ( out , 3 , pBoneToWorld . GetBoneForWrite ( boneIndex ) ) ;
}
return true ;
}
void RagdollComputeExactBbox ( const ragdoll_t & ragdoll , const Vector & origin , Vector & outMins , Vector & outMaxs )
{
outMins = origin ;
outMaxs = origin ;
for ( int i = 0 ; i < ragdoll . listCount ; i + + )
{
Vector mins , maxs ;
2023-10-03 14:23:56 +00:00
IPhysicsObject * pObject = ragdoll . list [ i ] . pObject ;
2020-04-22 16:56:21 +00:00
Vector objectOrg ;
QAngle objectAng ;
pObject - > GetPosition ( & objectOrg , & objectAng ) ;
physcollision - > CollideGetAABB ( & mins , & maxs , pObject - > GetCollide ( ) , objectOrg , objectAng ) ;
for ( int j = 0 ; j < 3 ; j + + )
{
if ( mins [ j ] < outMins [ j ] )
{
outMins [ j ] = mins [ j ] ;
}
if ( maxs [ j ] > outMaxs [ j ] )
{
outMaxs [ j ] = maxs [ j ] ;
}
}
}
}
2023-10-03 14:23:56 +00:00
void RagdollComputeApproximateBbox ( const ragdoll_t & ragdoll , const Vector & origin , Vector & outMins , Vector & outMaxs )
{
Vector mins , maxs ;
ClearBounds ( mins , maxs ) ;
for ( int i = 0 ; i < ragdoll . listCount ; i + + )
{
Vector objectOrg ;
ragdoll . list [ i ] . pObject - > GetPosition ( & objectOrg , NULL ) ;
float radius = physcollision - > CollideGetRadius ( ragdoll . list [ i ] . pObject - > GetCollide ( ) ) ;
for ( int k = 0 ; k < 3 ; k + + )
{
float ext = objectOrg [ k ] + radius ;
maxs [ k ] = fpmax ( maxs [ k ] , ext ) ;
ext = objectOrg [ k ] - radius ;
mins [ k ] = fpmin ( mins [ k ] , ext ) ;
}
}
outMins = mins ;
outMaxs = maxs ;
}
2020-04-22 16:56:21 +00:00
bool RagdollIsAsleep ( const ragdoll_t & ragdoll )
{
for ( int i = 0 ; i < ragdoll . listCount ; i + + )
{
if ( ragdoll . list [ i ] . pObject & & ! ragdoll . list [ i ] . pObject - > IsAsleep ( ) )
return false ;
}
return true ;
}
void RagdollSolveSeparation ( ragdoll_t & ragdoll , CBaseEntity * pEntity )
{
byte needsFix [ 256 ] ;
int fixCount = 0 ;
Assert ( ragdoll . listCount < = ARRAYSIZE ( needsFix ) ) ;
for ( int i = 0 ; i < ragdoll . listCount ; i + + )
{
needsFix [ i ] = 0 ;
const ragdollelement_t & element = ragdoll . list [ i ] ;
if ( element . pConstraint & & element . parentIndex > = 0 )
{
Vector start , target ;
element . pObject - > GetPosition ( & start , NULL ) ;
ragdoll . list [ element . parentIndex ] . pObject - > LocalToWorld ( & target , element . originParentSpace ) ;
if ( needsFix [ element . parentIndex ] )
{
needsFix [ i ] = 1 ;
+ + fixCount ;
continue ;
}
Vector dir = target - start ;
if ( dir . LengthSqr ( ) > 1.0f )
{
// this fixes a bug in ep2 with antlion grubs, but causes problems in TF2 - revisit, but disable for TF now
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
// heuristic: guess that anything separated and small mass ratio is in some state that's
// keeping the solver from fixing it
float mass = element . pObject - > GetMass ( ) ;
float massParent = ragdoll . list [ element . parentIndex ] . pObject - > GetMass ( ) ;
if ( mass * 2.0f < massParent )
{
// if this is <0.5 mass of parent and still separated it's attached to something heavy or
// in a bad state
needsFix [ i ] = 1 ;
+ + fixCount ;
continue ;
}
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
if ( PhysHasContactWithOtherInDirection ( element . pObject , dir ) )
{
Ray_t ray ;
trace_t tr ;
ray . Init ( target , start ) ;
UTIL_TraceRay ( ray , MASK_SOLID , pEntity , COLLISION_GROUP_NONE , & tr ) ;
if ( tr . DidHit ( ) )
{
needsFix [ i ] = 1 ;
+ + fixCount ;
}
}
}
}
}
if ( fixCount )
{
for ( int i = 0 ; i < ragdoll . listCount ; i + + )
{
if ( ! needsFix [ i ] )
continue ;
const ragdollelement_t & element = ragdoll . list [ i ] ;
Vector target , velocity ;
ragdoll . list [ element . parentIndex ] . pObject - > LocalToWorld ( & target , element . originParentSpace ) ;
ragdoll . list [ element . parentIndex ] . pObject - > GetVelocityAtPoint ( target , & velocity ) ;
matrix3x4_t xform ;
element . pObject - > GetPositionMatrix ( & xform ) ;
MatrixSetColumn ( target , 3 , xform ) ;
element . pObject - > SetPositionMatrix ( xform , true ) ;
element . pObject - > SetVelocity ( & velocity , & vec3_origin ) ;
}
DevMsg ( 2 , " TICK:%5d:Ragdoll separation count: %d \n " , gpGlobals - > tickcount , fixCount ) ;
}
else
{
ragdoll . pGroup - > ClearErrorState ( ) ;
}
}
//-----------------------------------------------------------------------------
// LRU
//-----------------------------------------------------------------------------
# ifdef _XBOX
// xbox defaults to 4 ragdolls max
ConVar g_ragdoll_maxcount ( " g_ragdoll_maxcount " , " 4 " , FCVAR_REPLICATED ) ;
# else
ConVar g_ragdoll_maxcount ( " g_ragdoll_maxcount " , " 8 " , FCVAR_REPLICATED ) ;
# endif
ConVar g_debug_ragdoll_removal ( " g_debug_ragdoll_removal " , " 0 " , FCVAR_REPLICATED | FCVAR_CHEAT ) ;
CRagdollLRURetirement s_RagdollLRU ( " CRagdollLRURetirement " ) ;
void CRagdollLRURetirement : : LevelInitPreEntity ( void )
{
m_iMaxRagdolls = - 1 ;
m_LRUImportantRagdolls . RemoveAll ( ) ;
m_LRU . RemoveAll ( ) ;
}
bool ShouldRemoveThisRagdoll ( CBaseAnimating * pRagdoll )
{
if ( g_RagdollLVManager . IsLowViolence ( ) )
{
return true ;
}
# ifdef CLIENT_DLL
/* we no longer ignore enemies just because they are on fire -- a ragdoll in front of me
is always a higher priority for retention than a flaming zombie behind me . At the
time I put this in , the ragdolls do clean up their own effects if culled via SUB_Remove ( ) .
If you ' re encountering trouble with ragdolls leaving effects behind , try renabling the code below .
/////////////////////
//Just ignore it until we're done burning/dissolving.
if ( pRagdoll - > GetEffectEntity ( ) )
return false ;
*/
Vector vMins , vMaxs ;
2022-03-01 20:00:42 +00:00
2020-04-22 16:56:21 +00:00
Vector origin = pRagdoll - > m_pRagdoll - > GetRagdollOrigin ( ) ;
pRagdoll - > m_pRagdoll - > GetRagdollBounds ( vMins , vMaxs ) ;
if ( engine - > IsBoxInViewCluster ( vMins + origin , vMaxs + origin ) = = false )
{
if ( g_debug_ragdoll_removal . GetBool ( ) )
{
2022-03-01 20:00:42 +00:00
debugoverlay - > AddBoxOverlay ( origin , vMins , vMaxs , QAngle ( 0 , 0 , 0 ) , 0 , 255 , 0 , 16 , 5 ) ;
debugoverlay - > AddLineOverlay ( origin , origin + Vector ( 0 , 0 , 64 ) , 0 , 255 , 0 , true , 5 ) ;
2020-04-22 16:56:21 +00:00
}
return true ;
}
else if ( engine - > CullBox ( vMins + origin , vMaxs + origin ) = = true )
{
if ( g_debug_ragdoll_removal . GetBool ( ) )
{
2022-03-01 20:00:42 +00:00
debugoverlay - > AddBoxOverlay ( origin , vMins , vMaxs , QAngle ( 0 , 0 , 0 ) , 0 , 0 , 255 , 16 , 5 ) ;
debugoverlay - > AddLineOverlay ( origin , origin + Vector ( 0 , 0 , 64 ) , 0 , 0 , 255 , true , 5 ) ;
2020-04-22 16:56:21 +00:00
}
return true ;
}
# else
CBasePlayer * pPlayer = UTIL_GetLocalPlayer ( ) ;
if ( ! UTIL_FindClientInPVS ( pRagdoll - > edict ( ) ) )
{
if ( g_debug_ragdoll_removal . GetBool ( ) )
NDebugOverlay : : Line ( pRagdoll - > GetAbsOrigin ( ) , pRagdoll - > GetAbsOrigin ( ) + Vector ( 0 , 0 , 64 ) , 0 , 255 , 0 , true , 5 ) ;
return true ;
}
else if ( ! pPlayer - > FInViewCone ( pRagdoll ) )
{
if ( g_debug_ragdoll_removal . GetBool ( ) )
NDebugOverlay : : Line ( pRagdoll - > GetAbsOrigin ( ) , pRagdoll - > GetAbsOrigin ( ) + Vector ( 0 , 0 , 64 ) , 0 , 0 , 255 , true , 5 ) ;
return true ;
}
# endif
return false ;
}
//-----------------------------------------------------------------------------
// Cull stale ragdolls. There is an ifdef here: one version for episodic,
// one for everything else.
//-----------------------------------------------------------------------------
# if HL2_EPISODIC
void CRagdollLRURetirement : : Update ( float frametime ) // EPISODIC VERSION
{
VPROF ( " CRagdollLRURetirement::Update " ) ;
// Compress out dead items
int i , next ;
int iMaxRagdollCount = m_iMaxRagdolls ;
if ( iMaxRagdollCount = = - 1 )
{
iMaxRagdollCount = g_ragdoll_maxcount . GetInt ( ) ;
}
// fade them all for the low violence version
if ( g_RagdollLVManager . IsLowViolence ( ) )
{
iMaxRagdollCount = 0 ;
}
m_iRagdollCount = 0 ;
m_iSimulatedRagdollCount = 0 ;
// First, find ragdolls that are good candidates for deletion because they are not
// visible at all, or are in a culled visibility box
for ( i = m_LRU . Head ( ) ; i < m_LRU . InvalidIndex ( ) ; i = next )
{
next = m_LRU . Next ( i ) ;
CBaseAnimating * pRagdoll = m_LRU [ i ] . Get ( ) ;
if ( pRagdoll )
{
m_iRagdollCount + + ;
IPhysicsObject * pObject = pRagdoll - > VPhysicsGetObject ( ) ;
if ( pObject & & ! pObject - > IsAsleep ( ) )
{
m_iSimulatedRagdollCount + + ;
}
if ( m_LRU . Count ( ) > iMaxRagdollCount )
{
//Found one, we're done.
2023-10-03 14:23:56 +00:00
if ( ShouldRemoveThisRagdoll ( pRagdoll ) = = true )
2020-04-22 16:56:21 +00:00
{
# ifdef CLIENT_DLL
2023-10-03 14:23:56 +00:00
pRagdoll - > SUB_Remove ( ) ;
2020-04-22 16:56:21 +00:00
# else
2023-10-03 14:23:56 +00:00
pRagdoll - > SUB_StartFadeOut ( 0 ) ;
2020-04-22 16:56:21 +00:00
# endif
m_LRU . Remove ( i ) ;
return ;
}
}
}
else
{
m_LRU . Remove ( i ) ;
}
}
//////////////////////////////
/// EPISODIC ALGORITHM ///
//////////////////////////////
// If we get here, it means we couldn't find a suitable ragdoll to remove,
// so just remove the furthest one.
int furthestOne = m_LRU . Head ( ) ;
float furthestDistSq = 0 ;
# ifdef CLIENT_DLL
2023-10-03 14:23:56 +00:00
ACTIVE_SPLITSCREEN_PLAYER_GUARD ( 0 ) ;
2020-04-22 16:56:21 +00:00
C_BasePlayer * pPlayer = C_BasePlayer : : GetLocalPlayer ( ) ;
# else
2023-10-03 14:23:56 +00:00
CBasePlayer * pPlayer = g_pGameRules - > IsMultiplayer ( ) ? NULL : UTIL_GetLocalPlayer ( ) ;
2020-04-22 16:56:21 +00:00
# endif
if ( pPlayer & & m_LRU . Count ( ) > iMaxRagdollCount ) // find the furthest one algorithm
{
Vector PlayerOrigin = pPlayer - > GetAbsOrigin ( ) ;
// const CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
for ( i = m_LRU . Head ( ) ; i < m_LRU . InvalidIndex ( ) ; i = next )
{
CBaseAnimating * pRagdoll = m_LRU [ i ] . Get ( ) ;
next = m_LRU . Next ( i ) ;
IPhysicsObject * pObject = pRagdoll - > VPhysicsGetObject ( ) ;
if ( pRagdoll & & ( pRagdoll - > GetEffectEntity ( ) | | ( pObject & & ! pObject - > IsAsleep ( ) ) ) )
continue ;
if ( pRagdoll )
{
// float distToPlayer = (pPlayer->GetAbsOrigin() - pRagdoll->GetAbsOrigin()).LengthSqr();
float distToPlayer = ( PlayerOrigin - pRagdoll - > GetAbsOrigin ( ) ) . LengthSqr ( ) ;
if ( distToPlayer > furthestDistSq )
{
furthestOne = i ;
furthestDistSq = distToPlayer ;
}
}
else // delete bad rags first.
{
furthestOne = i ;
break ;
}
}
2023-10-03 14:23:56 +00:00
CBaseAnimating * pRemoveRagdoll = m_LRU [ furthestOne ] . Get ( ) ;
2020-04-22 16:56:21 +00:00
# ifdef CLIENT_DLL
2023-10-03 14:23:56 +00:00
pRemoveRagdoll - > SUB_Remove ( ) ;
2020-04-22 16:56:21 +00:00
# else
2023-10-03 14:23:56 +00:00
pRemoveRagdoll - > SUB_StartFadeOut ( 0 ) ;
2020-04-22 16:56:21 +00:00
# endif
}
else // fall back on old-style pick the oldest one algorithm
{
for ( i = m_LRU . Head ( ) ; i < m_LRU . InvalidIndex ( ) ; i = next )
{
if ( m_LRU . Count ( ) < = iMaxRagdollCount )
break ;
next = m_LRU . Next ( i ) ;
CBaseAnimating * pRagdoll = m_LRU [ i ] . Get ( ) ;
//Just ignore it until we're done burning/dissolving.
IPhysicsObject * pObject = pRagdoll - > VPhysicsGetObject ( ) ;
if ( pRagdoll & & ( pRagdoll - > GetEffectEntity ( ) | | ( pObject & & ! pObject - > IsAsleep ( ) ) ) )
continue ;
# ifdef CLIENT_DLL
2023-10-03 14:23:56 +00:00
pRagdoll - > SUB_Remove ( ) ;
2020-04-22 16:56:21 +00:00
# else
2023-10-03 14:23:56 +00:00
pRagdoll - > SUB_StartFadeOut ( 0 ) ;
2020-04-22 16:56:21 +00:00
# endif
m_LRU . Remove ( i ) ;
}
}
}
# else
void CRagdollLRURetirement : : Update ( float frametime ) // Non-episodic version
{
VPROF ( " CRagdollLRURetirement::Update " ) ;
// Compress out dead items
int i , next ;
int iMaxRagdollCount = m_iMaxRagdolls ;
if ( iMaxRagdollCount = = - 1 )
{
iMaxRagdollCount = g_ragdoll_maxcount . GetInt ( ) ;
}
// fade them all for the low violence version
if ( g_RagdollLVManager . IsLowViolence ( ) )
{
iMaxRagdollCount = 0 ;
}
m_iRagdollCount = 0 ;
m_iSimulatedRagdollCount = 0 ;
2023-10-03 14:23:56 +00:00
// remove ragdolls with a forced retire time
for ( i = m_LRU . Head ( ) ; i < m_LRU . InvalidIndex ( ) ; i = next )
{
next = m_LRU . Next ( i ) ;
CBaseAnimating * pRagdoll = m_LRU [ i ] . Get ( ) ;
//Just ignore it until we're done burning/dissolving.
if ( pRagdoll & & pRagdoll - > GetEffectEntity ( ) )
continue ;
// ignore if it's not time to force retire this ragdoll
if ( m_LRU [ i ] . GetForcedRetireTime ( ) = = 0.0f | | gpGlobals - > curtime < m_LRU [ i ] . GetForcedRetireTime ( ) )
continue ;
//Msg(" Removing ragdoll %s due to forced retire time of %f (now = %f)\n", pRagdoll->GetModelName(), m_LRU[i].GetForcedRetireTime(), gpGlobals->curtime );
# ifdef CLIENT_DLL
pRagdoll - > SUB_Remove ( ) ;
# else
pRagdoll - > SUB_StartFadeOut ( 0 ) ;
# endif
m_LRU . Remove ( i ) ;
}
2020-04-22 16:56:21 +00:00
for ( i = m_LRU . Head ( ) ; i < m_LRU . InvalidIndex ( ) ; i = next )
{
next = m_LRU . Next ( i ) ;
CBaseAnimating * pRagdoll = m_LRU [ i ] . Get ( ) ;
if ( pRagdoll )
{
m_iRagdollCount + + ;
IPhysicsObject * pObject = pRagdoll - > VPhysicsGetObject ( ) ;
if ( pObject & & ! pObject - > IsAsleep ( ) )
{
m_iSimulatedRagdollCount + + ;
}
if ( m_LRU . Count ( ) > iMaxRagdollCount )
{
//Found one, we're done.
2023-10-03 14:23:56 +00:00
if ( ShouldRemoveThisRagdoll ( pRagdoll ) = = true )
2020-04-22 16:56:21 +00:00
{
# ifdef CLIENT_DLL
2023-10-03 14:23:56 +00:00
pRagdoll - > SUB_Remove ( ) ;
2020-04-22 16:56:21 +00:00
# else
2023-10-03 14:23:56 +00:00
pRagdoll - > SUB_StartFadeOut ( 0 ) ;
2020-04-22 16:56:21 +00:00
# endif
m_LRU . Remove ( i ) ;
return ;
}
}
}
else
{
m_LRU . Remove ( i ) ;
}
}
//////////////////////////////
/// ORIGINAL ALGORITHM ///
//////////////////////////////
// not episodic -- this is the original mechanism
for ( i = m_LRU . Head ( ) ; i < m_LRU . InvalidIndex ( ) ; i = next )
{
if ( m_LRU . Count ( ) < = iMaxRagdollCount )
break ;
next = m_LRU . Next ( i ) ;
CBaseAnimating * pRagdoll = m_LRU [ i ] . Get ( ) ;
//Just ignore it until we're done burning/dissolving.
if ( pRagdoll & & pRagdoll - > GetEffectEntity ( ) )
continue ;
# ifdef CLIENT_DLL
2023-10-03 14:23:56 +00:00
pRagdoll - > SUB_Remove ( ) ;
2020-04-22 16:56:21 +00:00
# else
2023-10-03 14:23:56 +00:00
pRagdoll - > SUB_StartFadeOut ( 0 ) ;
2020-04-22 16:56:21 +00:00
# endif
m_LRU . Remove ( i ) ;
}
}
# endif // HL2_EPISODIC
//This is pretty hacky, it's only called on the server so it just calls the update method.
void CRagdollLRURetirement : : FrameUpdatePostEntityThink ( void )
{
Update ( 0 ) ;
}
ConVar g_ragdoll_important_maxcount ( " g_ragdoll_important_maxcount " , " 2 " , FCVAR_REPLICATED ) ;
//-----------------------------------------------------------------------------
// Move it to the top of the LRU
//-----------------------------------------------------------------------------
2023-10-03 14:23:56 +00:00
void CRagdollLRURetirement : : MoveToTopOfLRU ( CBaseAnimating * pRagdoll , bool bImportant , float flForcedRetireTime )
2020-04-22 16:56:21 +00:00
{
if ( bImportant )
{
2023-10-03 14:23:56 +00:00
m_LRUImportantRagdolls . AddToTail ( CRagdollEntry ( pRagdoll , flForcedRetireTime ) ) ;
2020-04-22 16:56:21 +00:00
if ( m_LRUImportantRagdolls . Count ( ) > g_ragdoll_important_maxcount . GetInt ( ) )
{
int iIndex = m_LRUImportantRagdolls . Head ( ) ;
2022-03-01 20:00:42 +00:00
CBaseAnimating * pRagdoll = m_LRUImportantRagdolls [ iIndex ] . Get ( ) ;
2020-04-22 16:56:21 +00:00
2022-03-01 20:00:42 +00:00
if ( pRagdoll )
2020-04-22 16:56:21 +00:00
{
# ifdef CLIENT_DLL
2022-03-01 20:00:42 +00:00
pRagdoll - > SUB_Remove ( ) ;
2020-04-22 16:56:21 +00:00
# else
2022-03-01 20:00:42 +00:00
pRagdoll - > SUB_StartFadeOut ( 0 ) ;
2020-04-22 16:56:21 +00:00
# endif
m_LRUImportantRagdolls . Remove ( iIndex ) ;
}
}
return ;
}
for ( int i = m_LRU . Head ( ) ; i < m_LRU . InvalidIndex ( ) ; i = m_LRU . Next ( i ) )
{
if ( m_LRU [ i ] . Get ( ) = = pRagdoll )
{
m_LRU . Remove ( i ) ;
break ;
}
}
2023-10-03 14:23:56 +00:00
m_LRU . AddToTail ( CRagdollEntry ( pRagdoll , flForcedRetireTime ) ) ;
2020-04-22 16:56:21 +00:00
}
//EFFECT/ENTITY TRANSFERS
//CLIENT
# ifdef CLIENT_DLL
# define DEFAULT_FADE_START 2.0f
# define DEFAULT_MODEL_FADE_START 1.9f
# define DEFAULT_MODEL_FADE_LENGTH 0.1f
# define DEFAULT_FADEIN_LENGTH 1.0f
2023-10-03 14:23:56 +00:00
C_EntityDissolve * DissolveEffect ( C_BaseAnimating * pTarget , float flTime )
2020-04-22 16:56:21 +00:00
{
C_EntityDissolve * pDissolve = new C_EntityDissolve ;
2023-10-03 14:23:56 +00:00
if ( pDissolve - > InitializeAsClientEntity ( " sprites/blueglow1.vmt " , false ) = = false )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
UTIL_Remove ( pDissolve ) ;
2020-04-22 16:56:21 +00:00
return NULL ;
}
if ( pDissolve ! = NULL )
{
pTarget - > AddFlag ( FL_DISSOLVING ) ;
pDissolve - > SetParent ( pTarget ) ;
pDissolve - > OnDataChanged ( DATA_UPDATE_CREATED ) ;
pDissolve - > SetAbsOrigin ( pTarget - > GetAbsOrigin ( ) ) ;
pDissolve - > m_flStartTime = flTime ;
pDissolve - > m_flFadeOutStart = DEFAULT_FADE_START ;
pDissolve - > m_flFadeOutModelStart = DEFAULT_MODEL_FADE_START ;
pDissolve - > m_flFadeOutModelLength = DEFAULT_MODEL_FADE_LENGTH ;
pDissolve - > m_flFadeInLength = DEFAULT_FADEIN_LENGTH ;
pDissolve - > m_nDissolveType = 0 ;
pDissolve - > m_flNextSparkTime = 0.0f ;
pDissolve - > m_flFadeOutLength = 0.0f ;
pDissolve - > m_flFadeInStart = 0.0f ;
// Let this entity know it needs to delete itself when it's done
pDissolve - > SetServerLinkState ( false ) ;
pTarget - > SetEffectEntity ( pDissolve ) ;
}
return pDissolve ;
}
C_EntityFlame * FireEffect ( C_BaseAnimating * pTarget , C_BaseEntity * pServerFire , float * flScaleEnd , float * flTimeStart , float * flTimeEnd )
{
C_EntityFlame * pFire = new C_EntityFlame ;
2023-10-03 14:23:56 +00:00
if ( pFire - > InitializeAsClientEntity ( NULL , false ) = = false )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
UTIL_Remove ( pFire ) ;
2020-04-22 16:56:21 +00:00
return NULL ;
}
if ( pFire ! = NULL )
{
pFire - > RemoveFromLeafSystem ( ) ;
pTarget - > AddFlag ( FL_ONFIRE ) ;
pFire - > SetParent ( pTarget ) ;
pFire - > m_hEntAttached = ( C_BaseEntity * ) pTarget ;
pFire - > OnDataChanged ( DATA_UPDATE_CREATED ) ;
pFire - > SetAbsOrigin ( pTarget - > GetAbsOrigin ( ) ) ;
# ifdef HL2_EPISODIC
if ( pServerFire )
{
if ( pServerFire - > IsEffectActive ( EF_DIMLIGHT ) )
{
pFire - > AddEffects ( EF_DIMLIGHT ) ;
}
if ( pServerFire - > IsEffectActive ( EF_BRIGHTLIGHT ) )
{
pFire - > AddEffects ( EF_BRIGHTLIGHT ) ;
}
}
# endif
//Play a sound
CPASAttenuationFilter filter ( pTarget ) ;
pTarget - > EmitSound ( filter , pTarget - > GetSoundSourceIndex ( ) , " General.BurningFlesh " ) ;
pFire - > SetNextClientThink ( gpGlobals - > curtime + 7.0f ) ;
}
return pFire ;
}
void C_BaseAnimating : : IgniteRagdoll ( C_BaseAnimating * pSource )
{
C_BaseEntity * pChild = pSource - > GetEffectEntity ( ) ;
if ( pChild )
{
C_EntityFlame * pFireChild = dynamic_cast < C_EntityFlame * > ( pChild ) ;
C_ClientRagdoll * pRagdoll = dynamic_cast < C_ClientRagdoll * > ( this ) ;
if ( pFireChild )
{
pRagdoll - > SetEffectEntity ( FireEffect ( pRagdoll , pFireChild , NULL , NULL , NULL ) ) ;
}
}
}
void C_BaseAnimating : : TransferDissolveFrom ( C_BaseAnimating * pSource )
{
C_BaseEntity * pChild = pSource - > GetEffectEntity ( ) ;
if ( pChild )
{
C_EntityDissolve * pDissolveChild = dynamic_cast < C_EntityDissolve * > ( pChild ) ;
if ( pDissolveChild )
{
C_ClientRagdoll * pRagdoll = dynamic_cast < C_ClientRagdoll * > ( this ) ;
if ( pRagdoll )
{
pRagdoll - > m_flEffectTime = pDissolveChild - > m_flStartTime ;
C_EntityDissolve * pDissolve = DissolveEffect ( pRagdoll , pRagdoll - > m_flEffectTime ) ;
if ( pDissolve )
{
pDissolve - > SetRenderMode ( pDissolveChild - > GetRenderMode ( ) ) ;
2023-10-03 14:23:56 +00:00
pDissolve - > SetRenderFX ( pDissolveChild - > GetRenderFX ( ) ) ;
pDissolve - > SetRenderColor ( 255 , 255 , 255 ) ;
pDissolve - > SetRenderAlpha ( 255 ) ;
pDissolveChild - > SetRenderAlpha ( 0 ) ;
2020-04-22 16:56:21 +00:00
pDissolve - > m_vDissolverOrigin = pDissolveChild - > m_vDissolverOrigin ;
pDissolve - > m_nDissolveType = pDissolveChild - > m_nDissolveType ;
if ( pDissolve - > m_nDissolveType = = ENTITY_DISSOLVE_CORE )
{
pDissolve - > m_nMagnitude = pDissolveChild - > m_nMagnitude ;
pDissolve - > m_flFadeOutStart = CORE_DISSOLVE_FADE_START ;
pDissolve - > m_flFadeOutModelStart = CORE_DISSOLVE_MODEL_FADE_START ;
pDissolve - > m_flFadeOutModelLength = CORE_DISSOLVE_MODEL_FADE_LENGTH ;
pDissolve - > m_flFadeInLength = CORE_DISSOLVE_FADEIN_LENGTH ;
}
}
}
}
}
}
# endif
//SERVER
# if !defined( CLIENT_DLL )
//-----------------------------------------------------------------------------
// Transfer dissolve
//-----------------------------------------------------------------------------
void CBaseAnimating : : TransferDissolveFrom ( CBaseAnimating * pAnim )
{
if ( ! pAnim | | ! pAnim - > IsDissolving ( ) )
return ;
CEntityDissolve * pDissolve = CEntityDissolve : : Create ( this , pAnim ) ;
if ( pDissolve )
{
AddFlag ( FL_DISSOLVING ) ;
m_flDissolveStartTime = pAnim - > m_flDissolveStartTime ;
CEntityDissolve * pDissolveFrom = dynamic_cast < CEntityDissolve * > ( pAnim - > GetEffectEntity ( ) ) ;
if ( pDissolveFrom )
{
pDissolve - > SetDissolverOrigin ( pDissolveFrom - > GetDissolverOrigin ( ) ) ;
pDissolve - > SetDissolveType ( pDissolveFrom - > GetDissolveType ( ) ) ;
if ( pDissolveFrom - > GetDissolveType ( ) = = ENTITY_DISSOLVE_CORE )
{
pDissolve - > SetMagnitude ( pDissolveFrom - > GetMagnitude ( ) ) ;
pDissolve - > m_flFadeOutStart = CORE_DISSOLVE_FADE_START ;
pDissolve - > m_flFadeOutModelStart = CORE_DISSOLVE_MODEL_FADE_START ;
pDissolve - > m_flFadeOutModelLength = CORE_DISSOLVE_MODEL_FADE_LENGTH ;
pDissolve - > m_flFadeInLength = CORE_DISSOLVE_FADEIN_LENGTH ;
}
}
}
}
# endif