diff --git a/sp/src/game/client/c_baseanimating.cpp b/sp/src/game/client/c_baseanimating.cpp index 0dd620d7..1c867472 100644 --- a/sp/src/game/client/c_baseanimating.cpp +++ b/sp/src/game/client/c_baseanimating.cpp @@ -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 ); diff --git a/sp/src/game/client/c_baseanimating.h b/sp/src/game/client/c_baseanimating.h index 6082be1f..0f2f57df 100644 --- a/sp/src/game/client/c_baseanimating.h +++ b/sp/src/game/client/c_baseanimating.h @@ -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: diff --git a/sp/src/game/client/physpropclientside.cpp b/sp/src/game/client/physpropclientside.cpp index 3dd2d7b5..3db90618 100644 --- a/sp/src/game/client/physpropclientside.cpp +++ b/sp/src/game/client/physpropclientside.cpp @@ -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( 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 ¶ms ) { +#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(); diff --git a/sp/src/game/shared/ragdoll_shared.cpp b/sp/src/game/shared/ragdoll_shared.cpp index ded93773..86938039 100644 --- a/sp/src/game/shared/ragdoll_shared.cpp +++ b/sp/src/game/shared/ragdoll_shared.cpp @@ -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 } diff --git a/sp/src/game/shared/ragdoll_shared.h b/sp/src/game/shared/ragdoll_shared.h index 758f4719..5f4f7058 100644 --- a/sp/src/game/shared/ragdoll_shared.h +++ b/sp/src/game/shared/ragdoll_shared.h @@ -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 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 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;