From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/shared/ragdoll_shared.cpp | 2532 ++++++++++++++++----------------- 1 file changed, 1266 insertions(+), 1266 deletions(-) (limited to 'mp/src/game/shared/ragdoll_shared.cpp') diff --git a/mp/src/game/shared/ragdoll_shared.cpp b/mp/src/game/shared/ragdoll_shared.cpp index 5ffd405d..e77e9a75 100644 --- a/mp/src/game/shared/ragdoll_shared.cpp +++ b/mp/src/game/shared/ragdoll_shared.cpp @@ -1,1266 +1,1266 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#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" -//CLIENT -#ifdef CLIENT_DLL -#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 - if ( g_pGameRules->IsMultiplayer() ) - { - 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; - } - } -} - -class CRagdollCollisionRules : public IVPhysicsKeyHandler -{ -public: - CRagdollCollisionRules( IPhysicsCollisionSet *pSet ) - { - m_pSet = pSet; - m_bSelfCollisions = true; - } - virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue ) - { - if ( !strcmpi( pKey, "selfcollisions" ) ) - { - // keys disabled by default - Assert( atoi(pValue) == 0 ); - m_bSelfCollisions = false; - } - else if ( !strcmpi( pKey, "collisionpair" ) ) - { - if ( m_bSelfCollisions ) - { - char szToken[256]; - const char *pStr = nexttoken(szToken, pValue, ','); - int index0 = atoi(szToken); - nexttoken( szToken, pStr, ',' ); - int index1 = atoi(szToken); - - m_pSet->EnableCollisions( index0, index1 ); - } - else - { - Assert(0); - } - } - } - virtual void SetDefaults( void *pData ) {} - -private: - IPhysicsCollisionSet *m_pSet; - bool m_bSelfCollisions; -}; - -class CRagdollAnimatedFriction : public IVPhysicsKeyHandler -{ -public: - CRagdollAnimatedFriction( ragdoll_t *ragdoll ) - { - m_ragdoll = ragdoll; - } - virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue ) - { - if ( !strcmpi( pKey, "animfrictionmin" ) ) - { - m_ragdoll->animfriction.iMinAnimatedFriction = atoi( pValue ); - } - else if ( !strcmpi( pKey, "animfrictionmax" ) ) - { - m_ragdoll->animfriction.iMaxAnimatedFriction = atoi( pValue ); - } - else if ( !strcmpi( pKey, "animfrictiontimein" ) ) - { - m_ragdoll->animfriction.flFrictionTimeIn = atof( pValue ); - } - else if ( !strcmpi( pKey, "animfrictiontimeout" ) ) - { - m_ragdoll->animfriction.flFrictionTimeOut = atof( pValue ); - } - else if ( !strcmpi( pKey, "animfrictiontimehold" ) ) - { - m_ragdoll->animfriction.flFrictionTimeHold = atof( pValue ); - } - } - - virtual void SetDefaults( void *pData ) {} - -private: - ragdoll_t *m_ragdoll; -}; - -void RagdollSetupAnimatedFriction( IPhysicsEnvironment *pPhysEnv, ragdoll_t *ragdoll, int iModelIndex ) -{ - vcollide_t* pCollide = modelinfo->GetVCollide( iModelIndex ); - - if ( pCollide ) - { - IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues ); - - while ( !pParse->Finished() ) - { - const char *pBlock = pParse->GetCurrentBlockName(); - - if ( !strcmpi( pBlock, "animatedfriction") ) - { - CRagdollAnimatedFriction friction( ragdoll ); - pParse->ParseCustom( (void*)&friction, &friction ); - } - else - { - pParse->SkipBlock(); - } - } - - physcollision->VPhysicsKeyParserDestroy( pParse ); - } -} - -static void RagdollAddSolid( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, const ragdollparams_t ¶ms, solid_t &solid ) -{ - if ( solid.index >= 0 && solid.index < params.pCollide->solidCount) - { - Assert( ragdoll.listCount == solid.index ); - int boneIndex = Studio_BoneIndexByName( params.pStudioHdr, solid.name ); - ragdoll.boneIndex[ragdoll.listCount] = boneIndex; - - if ( boneIndex >= 0 ) - { - if ( params.fixedConstraints ) - { - solid.params.mass = 1000.f; - } - - solid.params.rotInertiaLimit = 0.1; - solid.params.pGameData = params.pGameData; - int surfaceData = physprops->GetSurfaceIndex( solid.surfaceprop ); - - if ( surfaceData < 0 ) - surfaceData = physprops->GetSurfaceIndex( "default" ); - - solid.params.pName = params.pStudioHdr->pszName(); - ragdoll.list[ragdoll.listCount].pObject = pPhysEnv->CreatePolyObject( params.pCollide->solids[solid.index], surfaceData, vec3_origin, vec3_angle, &solid.params ); - ragdoll.list[ragdoll.listCount].pObject->SetPositionMatrix( params.pCurrentBones[boneIndex], true ); - ragdoll.list[ragdoll.listCount].parentIndex = -1; - ragdoll.list[ragdoll.listCount].pObject->SetGameIndex( ragdoll.listCount ); - - ragdoll.listCount++; - } - else - { - Msg( "CRagdollProp::CreateObjects: Couldn't Lookup Bone %s\n", solid.name ); - } - } -} - - -static void RagdollAddConstraint( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, const ragdollparams_t ¶ms, constraint_ragdollparams_t &constraint ) -{ - if( constraint.childIndex == constraint.parentIndex ) - { - DevMsg( 1, "Bogus constraint on ragdoll %s\n", params.pStudioHdr->pszName() ); - constraint.childIndex = -1; - constraint.parentIndex = -1; - } - if ( constraint.childIndex >= 0 && constraint.parentIndex >= 0 ) - { - Assert(constraint.childIndex 0 ) - { - for ( int k = 0; k < 3; k++ ) - { - constraint.axes[k].torque *= params.jointFrictionScale; - } - } - // this parent/child pair is not usually a parent/child pair in the skeleton. There - // are often bones in between that are collapsed for simulation. So we need to compute - // the transform. - Studio_CalcBoneToBoneTransform( params.pStudioHdr, ragdoll.boneIndex[constraint.childIndex], ragdoll.boneIndex[constraint.parentIndex], constraint.constraintToAttached ); - MatrixGetColumn( constraint.constraintToAttached, 3, childElement.originParentSpace ); - // UNDONE: We could transform the constraint limit axes relative to the bone space - // using this data. Do we need that feature? - SetIdentityMatrix( constraint.constraintToReference ); - if ( params.fixedConstraints ) - { - // Makes the ragdoll a statue... - constraint_fixedparams_t fixed; - fixed.Defaults(); - fixed.InitWithCurrentObjectState( childElement.pObject, ragdoll.list[constraint.parentIndex].pObject ); - fixed.constraint.Defaults(); - childElement.pConstraint = pPhysEnv->CreateFixedConstraint( childElement.pObject, ragdoll.list[constraint.parentIndex].pObject, ragdoll.pGroup, fixed ); - } - else - { - childElement.pConstraint = pPhysEnv->CreateRagdollConstraint( childElement.pObject, ragdoll.list[constraint.parentIndex].pObject, ragdoll.pGroup, constraint ); - } - } -} - - -static void RagdollCreateObjects( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, const ragdollparams_t ¶ms ) -{ - 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 ) - return; - - constraint_groupparams_t group; - group.Defaults(); - ragdoll.pGroup = pPhysEnv->CreateConstraintGroup( group ); - - IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( params.pCollide->pKeyValues ); - while ( !pParse->Finished() ) - { - const char *pBlock = pParse->GetCurrentBlockName(); - if ( !strcmpi( pBlock, "solid" ) ) - { - solid_t solid; - - pParse->ParseSolid( &solid, &g_SolidSetup ); - RagdollAddSolid( pPhysEnv, ragdoll, params, solid ); - } - else if ( !strcmpi( pBlock, "ragdollconstraint" ) ) - { - constraint_ragdollparams_t constraint; - pParse->ParseRagdollConstraint( &constraint, NULL ); - RagdollAddConstraint( pPhysEnv, ragdoll, params, constraint ); - } - else if ( !strcmpi( pBlock, "collisionrules" ) ) - { - IPhysicsCollisionSet *pSet = physics->FindOrCreateCollisionSet( params.modelIndex, ragdoll.listCount ); - CRagdollCollisionRules rules(pSet); - pParse->ParseCustom( (void *)&rules, &rules ); - } - else if ( !strcmpi( pBlock, "animatedfriction") ) - { - CRagdollAnimatedFriction friction( &ragdoll ); - pParse->ParseCustom( (void*)&friction, &friction ); - } - else - { - pParse->SkipBlock(); - } - } - physcollision->VPhysicsKeyParserDestroy( pParse ); -} - -void RagdollSetupCollisions( ragdoll_t &ragdoll, vcollide_t *pCollide, int modelIndex ) -{ - Assert(pCollide); - if (!pCollide) - return; - - IPhysicsCollisionSet *pSet = physics->FindCollisionSet( modelIndex ); - if ( !pSet ) - { - pSet = physics->FindOrCreateCollisionSet( modelIndex, ragdoll.listCount ); - if ( !pSet ) - return; - - bool bFoundRules = false; - - IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues ); - while ( !pParse->Finished() ) - { - const char *pBlock = pParse->GetCurrentBlockName(); - if ( !strcmpi( pBlock, "collisionrules" ) ) - { - IPhysicsCollisionSet *pSet = physics->FindOrCreateCollisionSet( modelIndex, ragdoll.listCount ); - CRagdollCollisionRules rules(pSet); - pParse->ParseCustom( (void *)&rules, &rules ); - 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 ); - for ( int i = 0; i < ragdoll.listCount; i++ ) - { - ragdoll.list[i].pObject->SetGameIndex( i ); - 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 ¶ms, 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 - - Assert( forceBone < ragdoll.listCount ); - - if ( forceBone >= 0 && forceBone < ragdoll.listCount ) - { - ragdoll.list[forceBone].pObject->ApplyForceCenter( nudgeForce ); - //nudgeForce *= 0.5; - ragdoll.list[forceBone].pObject->GetPosition( &forcePosition, NULL ); - } - - for ( i = 0; i < ragdoll.listCount; i++ ) - { - PhysSetGameFlags( ragdoll.list[i].pObject, FVPHYSICS_PART_OF_RAGDOLL ); - } - - 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; - - IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues ); - 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 - // UNDONE: If we support other types of constraints (or multiple constraints per object) - // make sure these don't fight ! - 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; - Vector objectOrg; - QAngle objectAng; - IPhysicsObject *pObject = ragdoll.list[i].pObject; - 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]; - } - } - } -} - -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 -#if !defined(TF_CLIENT_DLL) - // 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; - } -#endif - - 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; - - 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() ) - { - 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 ); - } - - return true; - } - else if( engine->CullBox( vMins + origin, vMaxs + origin ) == true ) - { - if ( g_debug_ragdoll_removal.GetBool() ) - { - 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 ); - } - - 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. - if ( ShouldRemoveThisRagdoll( m_LRU[i] ) == true ) - { -#ifdef CLIENT_DLL - m_LRU[ i ]->SUB_Remove(); -#else - m_LRU[ i ]->SUB_StartFadeOut( 0 ); -#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 - C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); -#else - CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); -#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; - } - } - -#ifdef CLIENT_DLL - m_LRU[ furthestOne ]->SUB_Remove(); -#else - m_LRU[ furthestOne ]->SUB_StartFadeOut( 0 ); -#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 - m_LRU[ i ]->SUB_Remove(); - #else - m_LRU[ i ]->SUB_StartFadeOut( 0 ); - #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; - - 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. - if ( ShouldRemoveThisRagdoll( m_LRU[i] ) == true ) - { -#ifdef CLIENT_DLL - m_LRU[ i ]->SUB_Remove(); -#else - m_LRU[ i ]->SUB_StartFadeOut( 0 ); -#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 - m_LRU[ i ]->SUB_Remove(); -#else - m_LRU[ i ]->SUB_StartFadeOut( 0 ); -#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 -//----------------------------------------------------------------------------- -void CRagdollLRURetirement::MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant ) -{ - if ( bImportant ) - { - m_LRUImportantRagdolls.AddToTail( pRagdoll ); - - if ( m_LRUImportantRagdolls.Count() > g_ragdoll_important_maxcount.GetInt() ) - { - int iIndex = m_LRUImportantRagdolls.Head(); - - CBaseAnimating *pRagdoll = m_LRUImportantRagdolls[iIndex].Get(); - - if ( pRagdoll ) - { -#ifdef CLIENT_DLL - pRagdoll->SUB_Remove(); -#else - pRagdoll->SUB_StartFadeOut( 0 ); -#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; - } - } - - m_LRU.AddToTail( pRagdoll ); -} - - -//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 - - - -C_EntityDissolve *DissolveEffect( C_BaseEntity *pTarget, float flTime ) -{ - C_EntityDissolve *pDissolve = new C_EntityDissolve; - - if ( pDissolve->InitializeAsClientEntity( "sprites/blueglow1.vmt", RENDER_GROUP_TRANSLUCENT_ENTITY ) == false ) - { - pDissolve->Release(); - 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; - - if ( pFire->InitializeAsClientEntity( NULL, RENDER_GROUP_TRANSLUCENT_ENTITY ) == false ) - { - pFire->Release(); - 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( 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( 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() ); - pDissolve->m_nRenderFX = pDissolveChild->m_nRenderFX; - pDissolve->SetRenderColor( 255, 255, 255, 255 ); - pDissolveChild->SetRenderColorA( 0 ); - - 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 +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#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" +//CLIENT +#ifdef CLIENT_DLL +#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 + if ( g_pGameRules->IsMultiplayer() ) + { + 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; + } + } +} + +class CRagdollCollisionRules : public IVPhysicsKeyHandler +{ +public: + CRagdollCollisionRules( IPhysicsCollisionSet *pSet ) + { + m_pSet = pSet; + m_bSelfCollisions = true; + } + virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue ) + { + if ( !strcmpi( pKey, "selfcollisions" ) ) + { + // keys disabled by default + Assert( atoi(pValue) == 0 ); + m_bSelfCollisions = false; + } + else if ( !strcmpi( pKey, "collisionpair" ) ) + { + if ( m_bSelfCollisions ) + { + char szToken[256]; + const char *pStr = nexttoken(szToken, pValue, ','); + int index0 = atoi(szToken); + nexttoken( szToken, pStr, ',' ); + int index1 = atoi(szToken); + + m_pSet->EnableCollisions( index0, index1 ); + } + else + { + Assert(0); + } + } + } + virtual void SetDefaults( void *pData ) {} + +private: + IPhysicsCollisionSet *m_pSet; + bool m_bSelfCollisions; +}; + +class CRagdollAnimatedFriction : public IVPhysicsKeyHandler +{ +public: + CRagdollAnimatedFriction( ragdoll_t *ragdoll ) + { + m_ragdoll = ragdoll; + } + virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue ) + { + if ( !strcmpi( pKey, "animfrictionmin" ) ) + { + m_ragdoll->animfriction.iMinAnimatedFriction = atoi( pValue ); + } + else if ( !strcmpi( pKey, "animfrictionmax" ) ) + { + m_ragdoll->animfriction.iMaxAnimatedFriction = atoi( pValue ); + } + else if ( !strcmpi( pKey, "animfrictiontimein" ) ) + { + m_ragdoll->animfriction.flFrictionTimeIn = atof( pValue ); + } + else if ( !strcmpi( pKey, "animfrictiontimeout" ) ) + { + m_ragdoll->animfriction.flFrictionTimeOut = atof( pValue ); + } + else if ( !strcmpi( pKey, "animfrictiontimehold" ) ) + { + m_ragdoll->animfriction.flFrictionTimeHold = atof( pValue ); + } + } + + virtual void SetDefaults( void *pData ) {} + +private: + ragdoll_t *m_ragdoll; +}; + +void RagdollSetupAnimatedFriction( IPhysicsEnvironment *pPhysEnv, ragdoll_t *ragdoll, int iModelIndex ) +{ + vcollide_t* pCollide = modelinfo->GetVCollide( iModelIndex ); + + if ( pCollide ) + { + IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues ); + + while ( !pParse->Finished() ) + { + const char *pBlock = pParse->GetCurrentBlockName(); + + if ( !strcmpi( pBlock, "animatedfriction") ) + { + CRagdollAnimatedFriction friction( ragdoll ); + pParse->ParseCustom( (void*)&friction, &friction ); + } + else + { + pParse->SkipBlock(); + } + } + + physcollision->VPhysicsKeyParserDestroy( pParse ); + } +} + +static void RagdollAddSolid( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, const ragdollparams_t ¶ms, solid_t &solid ) +{ + if ( solid.index >= 0 && solid.index < params.pCollide->solidCount) + { + Assert( ragdoll.listCount == solid.index ); + int boneIndex = Studio_BoneIndexByName( params.pStudioHdr, solid.name ); + ragdoll.boneIndex[ragdoll.listCount] = boneIndex; + + if ( boneIndex >= 0 ) + { + if ( params.fixedConstraints ) + { + solid.params.mass = 1000.f; + } + + solid.params.rotInertiaLimit = 0.1; + solid.params.pGameData = params.pGameData; + int surfaceData = physprops->GetSurfaceIndex( solid.surfaceprop ); + + if ( surfaceData < 0 ) + surfaceData = physprops->GetSurfaceIndex( "default" ); + + solid.params.pName = params.pStudioHdr->pszName(); + ragdoll.list[ragdoll.listCount].pObject = pPhysEnv->CreatePolyObject( params.pCollide->solids[solid.index], surfaceData, vec3_origin, vec3_angle, &solid.params ); + ragdoll.list[ragdoll.listCount].pObject->SetPositionMatrix( params.pCurrentBones[boneIndex], true ); + ragdoll.list[ragdoll.listCount].parentIndex = -1; + ragdoll.list[ragdoll.listCount].pObject->SetGameIndex( ragdoll.listCount ); + + ragdoll.listCount++; + } + else + { + Msg( "CRagdollProp::CreateObjects: Couldn't Lookup Bone %s\n", solid.name ); + } + } +} + + +static void RagdollAddConstraint( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, const ragdollparams_t ¶ms, constraint_ragdollparams_t &constraint ) +{ + if( constraint.childIndex == constraint.parentIndex ) + { + DevMsg( 1, "Bogus constraint on ragdoll %s\n", params.pStudioHdr->pszName() ); + constraint.childIndex = -1; + constraint.parentIndex = -1; + } + if ( constraint.childIndex >= 0 && constraint.parentIndex >= 0 ) + { + Assert(constraint.childIndex 0 ) + { + for ( int k = 0; k < 3; k++ ) + { + constraint.axes[k].torque *= params.jointFrictionScale; + } + } + // this parent/child pair is not usually a parent/child pair in the skeleton. There + // are often bones in between that are collapsed for simulation. So we need to compute + // the transform. + Studio_CalcBoneToBoneTransform( params.pStudioHdr, ragdoll.boneIndex[constraint.childIndex], ragdoll.boneIndex[constraint.parentIndex], constraint.constraintToAttached ); + MatrixGetColumn( constraint.constraintToAttached, 3, childElement.originParentSpace ); + // UNDONE: We could transform the constraint limit axes relative to the bone space + // using this data. Do we need that feature? + SetIdentityMatrix( constraint.constraintToReference ); + if ( params.fixedConstraints ) + { + // Makes the ragdoll a statue... + constraint_fixedparams_t fixed; + fixed.Defaults(); + fixed.InitWithCurrentObjectState( childElement.pObject, ragdoll.list[constraint.parentIndex].pObject ); + fixed.constraint.Defaults(); + childElement.pConstraint = pPhysEnv->CreateFixedConstraint( childElement.pObject, ragdoll.list[constraint.parentIndex].pObject, ragdoll.pGroup, fixed ); + } + else + { + childElement.pConstraint = pPhysEnv->CreateRagdollConstraint( childElement.pObject, ragdoll.list[constraint.parentIndex].pObject, ragdoll.pGroup, constraint ); + } + } +} + + +static void RagdollCreateObjects( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, const ragdollparams_t ¶ms ) +{ + 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 ) + return; + + constraint_groupparams_t group; + group.Defaults(); + ragdoll.pGroup = pPhysEnv->CreateConstraintGroup( group ); + + IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( params.pCollide->pKeyValues ); + while ( !pParse->Finished() ) + { + const char *pBlock = pParse->GetCurrentBlockName(); + if ( !strcmpi( pBlock, "solid" ) ) + { + solid_t solid; + + pParse->ParseSolid( &solid, &g_SolidSetup ); + RagdollAddSolid( pPhysEnv, ragdoll, params, solid ); + } + else if ( !strcmpi( pBlock, "ragdollconstraint" ) ) + { + constraint_ragdollparams_t constraint; + pParse->ParseRagdollConstraint( &constraint, NULL ); + RagdollAddConstraint( pPhysEnv, ragdoll, params, constraint ); + } + else if ( !strcmpi( pBlock, "collisionrules" ) ) + { + IPhysicsCollisionSet *pSet = physics->FindOrCreateCollisionSet( params.modelIndex, ragdoll.listCount ); + CRagdollCollisionRules rules(pSet); + pParse->ParseCustom( (void *)&rules, &rules ); + } + else if ( !strcmpi( pBlock, "animatedfriction") ) + { + CRagdollAnimatedFriction friction( &ragdoll ); + pParse->ParseCustom( (void*)&friction, &friction ); + } + else + { + pParse->SkipBlock(); + } + } + physcollision->VPhysicsKeyParserDestroy( pParse ); +} + +void RagdollSetupCollisions( ragdoll_t &ragdoll, vcollide_t *pCollide, int modelIndex ) +{ + Assert(pCollide); + if (!pCollide) + return; + + IPhysicsCollisionSet *pSet = physics->FindCollisionSet( modelIndex ); + if ( !pSet ) + { + pSet = physics->FindOrCreateCollisionSet( modelIndex, ragdoll.listCount ); + if ( !pSet ) + return; + + bool bFoundRules = false; + + IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues ); + while ( !pParse->Finished() ) + { + const char *pBlock = pParse->GetCurrentBlockName(); + if ( !strcmpi( pBlock, "collisionrules" ) ) + { + IPhysicsCollisionSet *pSet = physics->FindOrCreateCollisionSet( modelIndex, ragdoll.listCount ); + CRagdollCollisionRules rules(pSet); + pParse->ParseCustom( (void *)&rules, &rules ); + 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 ); + for ( int i = 0; i < ragdoll.listCount; i++ ) + { + ragdoll.list[i].pObject->SetGameIndex( i ); + 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 ¶ms, 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 + + Assert( forceBone < ragdoll.listCount ); + + if ( forceBone >= 0 && forceBone < ragdoll.listCount ) + { + ragdoll.list[forceBone].pObject->ApplyForceCenter( nudgeForce ); + //nudgeForce *= 0.5; + ragdoll.list[forceBone].pObject->GetPosition( &forcePosition, NULL ); + } + + for ( i = 0; i < ragdoll.listCount; i++ ) + { + PhysSetGameFlags( ragdoll.list[i].pObject, FVPHYSICS_PART_OF_RAGDOLL ); + } + + 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; + + IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues ); + 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 + // UNDONE: If we support other types of constraints (or multiple constraints per object) + // make sure these don't fight ! + 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; + Vector objectOrg; + QAngle objectAng; + IPhysicsObject *pObject = ragdoll.list[i].pObject; + 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]; + } + } + } +} + +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 +#if !defined(TF_CLIENT_DLL) + // 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; + } +#endif + + 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; + + 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() ) + { + 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 ); + } + + return true; + } + else if( engine->CullBox( vMins + origin, vMaxs + origin ) == true ) + { + if ( g_debug_ragdoll_removal.GetBool() ) + { + 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 ); + } + + 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. + if ( ShouldRemoveThisRagdoll( m_LRU[i] ) == true ) + { +#ifdef CLIENT_DLL + m_LRU[ i ]->SUB_Remove(); +#else + m_LRU[ i ]->SUB_StartFadeOut( 0 ); +#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 + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); +#else + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); +#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; + } + } + +#ifdef CLIENT_DLL + m_LRU[ furthestOne ]->SUB_Remove(); +#else + m_LRU[ furthestOne ]->SUB_StartFadeOut( 0 ); +#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 + m_LRU[ i ]->SUB_Remove(); + #else + m_LRU[ i ]->SUB_StartFadeOut( 0 ); + #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; + + 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. + if ( ShouldRemoveThisRagdoll( m_LRU[i] ) == true ) + { +#ifdef CLIENT_DLL + m_LRU[ i ]->SUB_Remove(); +#else + m_LRU[ i ]->SUB_StartFadeOut( 0 ); +#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 + m_LRU[ i ]->SUB_Remove(); +#else + m_LRU[ i ]->SUB_StartFadeOut( 0 ); +#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 +//----------------------------------------------------------------------------- +void CRagdollLRURetirement::MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant ) +{ + if ( bImportant ) + { + m_LRUImportantRagdolls.AddToTail( pRagdoll ); + + if ( m_LRUImportantRagdolls.Count() > g_ragdoll_important_maxcount.GetInt() ) + { + int iIndex = m_LRUImportantRagdolls.Head(); + + CBaseAnimating *pRagdoll = m_LRUImportantRagdolls[iIndex].Get(); + + if ( pRagdoll ) + { +#ifdef CLIENT_DLL + pRagdoll->SUB_Remove(); +#else + pRagdoll->SUB_StartFadeOut( 0 ); +#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; + } + } + + m_LRU.AddToTail( pRagdoll ); +} + + +//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 + + + +C_EntityDissolve *DissolveEffect( C_BaseEntity *pTarget, float flTime ) +{ + C_EntityDissolve *pDissolve = new C_EntityDissolve; + + if ( pDissolve->InitializeAsClientEntity( "sprites/blueglow1.vmt", RENDER_GROUP_TRANSLUCENT_ENTITY ) == false ) + { + pDissolve->Release(); + 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; + + if ( pFire->InitializeAsClientEntity( NULL, RENDER_GROUP_TRANSLUCENT_ENTITY ) == false ) + { + pFire->Release(); + 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( 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( 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() ); + pDissolve->m_nRenderFX = pDissolveChild->m_nRenderFX; + pDissolve->SetRenderColor( 255, 255, 255, 255 ); + pDissolveChild->SetRenderColorA( 0 ); + + 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 -- cgit v1.2.3