Added support for ragdoll prop gibs + related ragdoll LRU forced fade from Alien Swarm SDK

This commit is contained in:
Blixibon 2021-11-27 15:47:13 -06:00
parent 4bc56b214b
commit cea38f03ec
5 changed files with 263 additions and 7 deletions

View File

@ -259,6 +259,9 @@ LINK_ENTITY_TO_CLASS( client_ragdoll, C_ClientRagdoll );
BEGIN_DATADESC( C_ClientRagdoll )
DEFINE_FIELD( m_bFadeOut, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bImportant, FIELD_BOOLEAN ),
#ifdef MAPBASE
DEFINE_FIELD( m_flForcedRetireTime, FIELD_FLOAT ),
#endif
DEFINE_FIELD( m_iCurrentFriction, FIELD_INTEGER ),
DEFINE_FIELD( m_iMinFriction, FIELD_INTEGER ),
DEFINE_FIELD( m_iMaxFriction, FIELD_INTEGER ),
@ -377,6 +380,9 @@ C_ClientRagdoll::C_ClientRagdoll( bool bRestoring )
m_bFadeOut = false;
m_bFadingOut = false;
m_bImportant = false;
#ifdef MAPBASE
m_flForcedRetireTime = 0.0f;
#endif
m_bNoModelParticles = false;
SetClassname("client_ragdoll");
@ -457,7 +463,11 @@ void C_ClientRagdoll::OnRestore( void )
if ( m_bFadeOut == true )
{
#ifdef MAPBASE
s_RagdollLRU.MoveToTopOfLRU( this, m_bImportant, m_flForcedRetireTime );
#else
s_RagdollLRU.MoveToTopOfLRU( this, m_bImportant );
#endif
}
NoteRagdollCreationTick( this );

View File

@ -737,6 +737,10 @@ public:
bool m_bFadeOut;
bool m_bImportant;
#ifdef MAPBASE
// Required to save/restore Alien Swarm SDK ragdoll LRU forced fade
float m_flForcedRetireTime;
#endif
float m_flEffectTime;
private:

View File

@ -704,9 +704,157 @@ void C_PhysPropClientside::ParseAllEntities(const char *pMapData)
}
}
#ifdef MAPBASE
CBaseAnimating *BreakModelCreate_Ragdoll( CBaseEntity *pOwnerEnt, breakmodel_t *pModel, const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity )
{
C_BaseAnimating *pOwner = dynamic_cast<C_BaseAnimating *>( pOwnerEnt );
if ( !pOwner )
return NULL;
C_ClientRagdoll *pRagdoll = new C_ClientRagdoll( false );
if ( pRagdoll == NULL )
return NULL;
const char *pModelName = pModel->modelName;
if ( pRagdoll->InitializeAsClientEntity( pModelName, RENDER_GROUP_OPAQUE_ENTITY ) == false )
{
pRagdoll->Release();
return NULL;
}
pRagdoll->SetAbsOrigin( position );
pRagdoll->SetAbsAngles( angles );
matrix3x4_t boneDelta0[MAXSTUDIOBONES];
matrix3x4_t boneDelta1[MAXSTUDIOBONES];
matrix3x4_t currentBones[MAXSTUDIOBONES];
const float boneDt = 0.1f;
pRagdoll->SetParent( pOwner );
pRagdoll->ForceSetupBonesAtTime( boneDelta0, gpGlobals->curtime - boneDt );
pRagdoll->ForceSetupBonesAtTime( boneDelta1, gpGlobals->curtime );
pRagdoll->ForceSetupBonesAtTime( currentBones, gpGlobals->curtime );
pRagdoll->SetParent( NULL );
// We need to take these from the entity
//pRagdoll->SetAbsOrigin( position );
//pRagdoll->SetAbsAngles( angles );
pRagdoll->IgniteRagdoll( pOwner );
pRagdoll->TransferDissolveFrom( pOwner );
pRagdoll->InitModelEffects();
if ( pOwner->IsEffectActive( EF_NOSHADOW ) )
{
pRagdoll->AddEffects( EF_NOSHADOW );
}
pRagdoll->m_nRenderFX = kRenderFxRagdoll;
pRagdoll->SetRenderMode( pOwner->GetRenderMode() );
pRagdoll->SetRenderColor( pOwner->GetRenderColor().r, pOwner->GetRenderColor().g, pOwner->GetRenderColor().b, pOwner->GetRenderColor().a );
//pRagdoll->SetGlobalFadeScale( pOwner->GetGlobalFadeScale() );
pRagdoll->SetSkin( pOwner->GetSkin() );
//pRagdoll->m_vecForce = pOwner->m_vecForce;
//pRagdoll->m_nForceBone = 0; //pOwner->m_nForceBone;
pRagdoll->SetNextClientThink( CLIENT_THINK_ALWAYS );
pRagdoll->SetModelName( AllocPooledString( pModelName ) );
pRagdoll->ResetSequence( 0 );
pRagdoll->SetModelScale( pOwner->GetModelScale() );
pRagdoll->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
//pRagdoll->m_builtRagdoll = true;
CStudioHdr *hdr = pRagdoll->GetModelPtr();
if ( !hdr )
{
pRagdoll->Release();
Warning( "Couldn't create ragdoll gib for %s (no model pointer)\n", pModel->modelName );
return NULL;
}
pRagdoll->m_pRagdoll = CreateRagdoll(
pRagdoll,
hdr,
vec3_origin,
0,
boneDelta0,
boneDelta1,
currentBones,
boneDt );
if ( !pRagdoll->m_pRagdoll )
{
pRagdoll->Release();
Warning( "Couldn't create ragdoll gib for %s\n", pModel->modelName );
return NULL;
}
IPhysicsObject *pPhysicsObject = pRagdoll->VPhysicsGetObject();
if ( pPhysicsObject )
{
// randomize velocity by 5%
float rndf = RandomFloat( -0.025, 0.025 );
Vector rndVel = velocity + rndf*velocity;
pPhysicsObject->AddVelocity( &rndVel, &angVelocity );
}
pRagdoll->ApplyLocalAngularVelocityImpulse( angVelocity );
if ( pRagdoll->m_pRagdoll )
{
pRagdoll->m_bImportant = false;
pRagdoll->m_flForcedRetireTime = pModel->fadeTime > 0.0f ? gpGlobals->curtime + pModel->fadeTime : 0.0f;
s_RagdollLRU.MoveToTopOfLRU( pRagdoll, pRagdoll->m_bImportant, pRagdoll->m_flForcedRetireTime );
pRagdoll->m_bFadeOut = true;
}
// Cause the entity to recompute its shadow type and make a
// version which only updates when physics state changes
// NOTE: We have to do this after m_pRagdoll is assigned above
// because that's what ShadowCastType uses to figure out which type of shadow to use.
pRagdoll->DestroyShadow();
pRagdoll->CreateShadow();
pRagdoll->SetAbsOrigin( position );
pRagdoll->SetAbsAngles( angles );
pRagdoll->SetPlaybackRate( 0 );
pRagdoll->SetCycle( 0 );
// put into ACT_DIERAGDOLL if it exists, otherwise use sequence 0
int nSequence = pRagdoll->SelectWeightedSequence( ACT_DIERAGDOLL );
if ( nSequence < 0 )
{
pRagdoll->ResetSequence( 0 );
}
else
{
pRagdoll->ResetSequence( nSequence );
}
pRagdoll->UpdatePartitionListEntry();
pRagdoll->MarkRenderHandleDirty();
NoteRagdollCreationTick( pRagdoll );
//pRagdoll->InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt );
return pRagdoll;
}
#endif
CBaseEntity *BreakModelCreateSingle( CBaseEntity *pOwner, breakmodel_t *pModel, const Vector &position,
const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, int nSkin, const breakablepropparams_t &params )
{
#ifdef MAPBASE
if ( pModel->isRagdoll )
{
CBaseEntity *pEntity = BreakModelCreate_Ragdoll( pOwner, pModel, position, angles, velocity, angVelocity );
return pEntity;
}
#endif
C_PhysPropClientside *pEntity = C_PhysPropClientside::CreateNew();
if ( !pEntity )
@ -778,10 +926,12 @@ CBaseEntity *BreakModelCreateSingle( CBaseEntity *pOwner, breakmodel_t *pModel,
pEntity->SetFadeMinMax( pModel->fadeMinDist, pModel->fadeMaxDist );
}
#ifndef MAPBASE
if ( pModel->isRagdoll )
{
DevMsg( "BreakModelCreateSingle: clientside doesn't support ragdoll breakmodels.\n" );
}
#endif
IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();

View File

@ -830,6 +830,33 @@ void CRagdollLRURetirement::Update( float frametime ) // EPISODIC VERSION
m_iRagdollCount = 0;
m_iSimulatedRagdollCount = 0;
#ifdef MAPBASE // From Alien Swarm SDK
// 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);
}
#endif
// 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 )
@ -847,12 +874,12 @@ void CRagdollLRURetirement::Update( float frametime ) // EPISODIC VERSION
if ( m_LRU.Count() > iMaxRagdollCount )
{
//Found one, we're done.
if ( ShouldRemoveThisRagdoll( m_LRU[i] ) == true )
if ( ShouldRemoveThisRagdoll( pRagdoll ) == true )
{
#ifdef CLIENT_DLL
m_LRU[ i ]->SUB_Remove();
pRagdoll->SUB_Remove();
#else
m_LRU[ i ]->SUB_StartFadeOut( 0 );
pRagdoll->SUB_StartFadeOut( 0 );
#endif
m_LRU.Remove(i);
@ -933,10 +960,11 @@ void CRagdollLRURetirement::Update( float frametime ) // EPISODIC VERSION
}
}
CBaseAnimating *pRemoveRagdoll = m_LRU[ furthestOne ].Get();
#ifdef CLIENT_DLL
m_LRU[ furthestOne ]->SUB_Remove();
pRemoveRagdoll->SUB_Remove();
#else
m_LRU[ furthestOne ]->SUB_StartFadeOut( 0 );
pRemoveRagdoll->SUB_StartFadeOut( 0 );
#endif
}
@ -957,9 +985,9 @@ void CRagdollLRURetirement::Update( float frametime ) // EPISODIC VERSION
continue;
#ifdef CLIENT_DLL
m_LRU[ i ]->SUB_Remove();
pRagdoll->SUB_Remove();
#else
m_LRU[ i ]->SUB_StartFadeOut( 0 );
pRagdoll->SUB_StartFadeOut( 0 );
#endif
m_LRU.Remove(i);
}
@ -989,6 +1017,33 @@ void CRagdollLRURetirement::Update( float frametime ) // Non-episodic version
m_iRagdollCount = 0;
m_iSimulatedRagdollCount = 0;
#ifdef MAPBASE // From Alien Swarm SDK
// 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);
}
#endif
for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next )
{
next = m_LRU.Next(i);
@ -1074,11 +1129,19 @@ ConVar g_ragdoll_important_maxcount( "g_ragdoll_important_maxcount", "2", FCVAR_
//-----------------------------------------------------------------------------
// Move it to the top of the LRU
//-----------------------------------------------------------------------------
#ifdef MAPBASE // From Alien Swarm SDK
void CRagdollLRURetirement::MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant, float flForcedRetireTime )
#else
void CRagdollLRURetirement::MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant )
#endif
{
if ( bImportant )
{
#ifdef MAPBASE // From Alien Swarm SDK
m_LRUImportantRagdolls.AddToTail( CRagdollEntry( pRagdoll, flForcedRetireTime ) );
#else
m_LRUImportantRagdolls.AddToTail( pRagdoll );
#endif
if ( m_LRUImportantRagdolls.Count() > g_ragdoll_important_maxcount.GetInt() )
{
@ -1108,7 +1171,11 @@ void CRagdollLRURetirement::MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImpo
}
}
#ifdef MAPBASE // From Alien Swarm SDK
m_LRU.AddToTail( CRagdollEntry( pRagdoll, flForcedRetireTime ) );
#else
m_LRU.AddToTail( pRagdoll );
#endif
}

View File

@ -83,6 +83,22 @@ struct ragdollparams_t
bool fixedConstraints;
};
#ifdef MAPBASE // From Alien Swarm SDK
class CRagdollEntry
{
public:
CRagdollEntry( CBaseAnimating *pRagdoll, float flForcedRetireTime ) : m_hRagdoll( pRagdoll ), m_flForcedRetireTime( flForcedRetireTime )
{
}
CBaseAnimating* Get() { return m_hRagdoll.Get(); }
float GetForcedRetireTime() { return m_flForcedRetireTime; }
private:
CHandle<CBaseAnimating> m_hRagdoll;
float m_flForcedRetireTime;
};
#endif
//-----------------------------------------------------------------------------
// This hooks the main game systems callbacks to allow the AI system to manage memory
//-----------------------------------------------------------------------------
@ -98,7 +114,11 @@ public:
virtual void FrameUpdatePostEntityThink( void );
// Move it to the top of the LRU
#ifdef MAPBASE // From Alien Swarm SDK
void MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant = false, float flForcedRetireTime = 0.0f );
#else
void MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant = false );
#endif
void SetMaxRagdollCount( int iMaxCount ){ m_iMaxRagdolls = iMaxCount; }
virtual void LevelInitPreEntity( void );
@ -106,8 +126,13 @@ public:
private:
typedef CHandle<CBaseAnimating> CRagdollHandle;
#ifdef MAPBASE
CUtlLinkedList< CRagdollEntry > m_LRU;
CUtlLinkedList< CRagdollEntry > m_LRUImportantRagdolls;
#else
CUtlLinkedList< CRagdollHandle > m_LRU;
CUtlLinkedList< CRagdollHandle > m_LRUImportantRagdolls;
#endif
int m_iMaxRagdolls;
int m_iSimulatedRagdollCount;