summaryrefslogtreecommitdiff
path: root/game/shared/cstrike/cs_playeranimstate.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/cstrike/cs_playeranimstate.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/cstrike/cs_playeranimstate.cpp')
-rw-r--r--game/shared/cstrike/cs_playeranimstate.cpp1057
1 files changed, 1057 insertions, 0 deletions
diff --git a/game/shared/cstrike/cs_playeranimstate.cpp b/game/shared/cstrike/cs_playeranimstate.cpp
new file mode 100644
index 0000000..19c3a18
--- /dev/null
+++ b/game/shared/cstrike/cs_playeranimstate.cpp
@@ -0,0 +1,1057 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "cs_playeranimstate.h"
+#include "base_playeranimstate.h"
+#include "tier0/vprof.h"
+#include "animation.h"
+#include "weapon_csbase.h"
+#include "studio.h"
+#include "apparent_velocity_helper.h"
+#include "utldict.h"
+#include "weapon_basecsgrenade.h"
+#include "datacache/imdlcache.h"
+
+#ifdef CLIENT_DLL
+ #include "c_cs_player.h"
+ #include "bone_setup.h"
+ #include "interpolatedvar.h"
+ #include "c_cs_hostage.h"
+#else
+ #include "cs_player.h"
+ #include "cs_simple_hostage.h"
+ #include "cs_gamestats.h"
+#endif
+
+#define ANIM_TOPSPEED_WALK 100
+#define ANIM_TOPSPEED_RUN 250
+#define ANIM_TOPSPEED_RUN_CROUCH 85
+
+#define DEFAULT_IDLE_NAME "idle_upper_"
+#define DEFAULT_CROUCH_IDLE_NAME "crouch_idle_upper_"
+#define DEFAULT_CROUCH_WALK_NAME "crouch_walk_upper_"
+#define DEFAULT_WALK_NAME "walk_upper_"
+#define DEFAULT_RUN_NAME "run_upper_"
+
+#define DEFAULT_FIRE_IDLE_NAME "idle_shoot_"
+#define DEFAULT_FIRE_CROUCH_NAME "crouch_idle_shoot_"
+#define DEFAULT_FIRE_CROUCH_WALK_NAME "crouch_walk_shoot_"
+#define DEFAULT_FIRE_WALK_NAME "walk_shoot_"
+#define DEFAULT_FIRE_RUN_NAME "run_shoot_"
+
+
+#define FIRESEQUENCE_LAYER (AIMSEQUENCE_LAYER+NUM_AIMSEQUENCE_LAYERS+1)
+#define RELOADSEQUENCE_LAYER (FIRESEQUENCE_LAYER + 1)
+#define GRENADESEQUENCE_LAYER (RELOADSEQUENCE_LAYER + 1)
+#define NUM_LAYERS_WANTED (GRENADESEQUENCE_LAYER + 1)
+
+
+
+// ------------------------------------------------------------------------------------------------ //
+// CCSPlayerAnimState declaration.
+// ------------------------------------------------------------------------------------------------ //
+
+class CCSPlayerAnimState : public CBasePlayerAnimState, public ICSPlayerAnimState
+{
+public:
+ DECLARE_CLASS( CCSPlayerAnimState, CBasePlayerAnimState );
+ friend ICSPlayerAnimState* CreatePlayerAnimState( CBaseAnimatingOverlay *pEntity, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences );
+
+ CCSPlayerAnimState();
+
+ virtual void DoAnimationEvent( PlayerAnimEvent_t event, int nData );
+ virtual bool IsThrowingGrenade();
+ virtual int CalcAimLayerSequence( float *flCycle, float *flAimSequenceWeight, bool bForceIdle );
+ virtual void ClearAnimationState();
+ virtual bool CanThePlayerMove();
+ virtual float GetCurrentMaxGroundSpeed();
+ virtual Activity CalcMainActivity();
+ virtual void DebugShowAnimState( int iStartLine );
+ virtual void ComputeSequences( CStudioHdr *pStudioHdr );
+ virtual void ClearAnimationLayers();
+ virtual int SelectWeightedSequence( Activity activity );
+
+ void InitCS( CBaseAnimatingOverlay *pPlayer, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences );
+
+protected:
+
+ int CalcFireLayerSequence(PlayerAnimEvent_t event);
+ void ComputeFireSequence( CStudioHdr *pStudioHdr );
+
+ void ComputeReloadSequence( CStudioHdr *pStudioHdr );
+ int CalcReloadLayerSequence( PlayerAnimEvent_t event );
+
+ bool IsOuterGrenadePrimed();
+ void ComputeGrenadeSequence( CStudioHdr *pStudioHdr );
+ int CalcGrenadePrimeSequence();
+ int CalcGrenadeThrowSequence();
+ int GetOuterGrenadeThrowCounter();
+
+ const char* GetWeaponSuffix();
+ bool HandleJumping();
+
+ void UpdateLayerSequenceGeneric( CStudioHdr *pStudioHdr, int iLayer, bool &bEnabled, float &flCurCycle, int &iSequence, bool bWaitAtEnd );
+
+ virtual int CalcSequenceIndex( const char *pBaseName, ... );
+
+private:
+
+ // Current state variables.
+ bool m_bJumping; // Set on a jump event.
+ float m_flJumpStartTime;
+ bool m_bFirstJumpFrame;
+
+ // Aim sequence plays reload while this is on.
+ bool m_bReloading;
+ float m_flReloadCycle;
+ int m_iReloadSequence;
+ float m_flReloadHoldEndTime; // Intermediate shotgun reloads get held a fraction of a second
+
+ // This is set to true if ANY animation is being played in the fire layer.
+ bool m_bFiring; // If this is on, then it'll continue the fire animation in the fire layer
+ // until it completes.
+ int m_iFireSequence; // (For any sequences in the fire layer, including grenade throw).
+ float m_flFireCycle;
+ PlayerAnimEvent_t m_delayedFire; // if we fire while reloading, delay the fire by one frame so we can cancel the reload first
+
+ // These control grenade animations.
+ bool m_bThrowingGrenade;
+ bool m_bPrimingGrenade;
+ float m_flGrenadeCycle;
+ int m_iGrenadeSequence;
+ int m_iLastThrowGrenadeCounter; // used to detect when the guy threw the grenade.
+
+ CCSPlayer *m_pPlayer;
+
+ ICSPlayerAnimStateHelpers *m_pHelpers;
+
+ void CheckCachedSequenceValidity( void );
+
+ int m_sequenceCache[ ACT_CROUCHIDLE+1 ]; // Cache the first N sequences, since we don't have weights.
+ int m_cachedModelIndex; // Model index for which the sequence cache is valid.
+
+ CUtlDict<int,int> m_namedSequence; // Dictionary of sequences computed with CalcSequenceIndex. This is because LookupSequence is a performance hit - CS:S player models have 750+ sequences!
+};
+
+
+ICSPlayerAnimState* CreatePlayerAnimState( CBaseAnimatingOverlay *pEntity, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences )
+{
+ CCSPlayerAnimState *pRet = new CCSPlayerAnimState;
+ pRet->InitCS( pEntity, pHelpers, legAnimType, bUseAimSequences );
+ return pRet;
+}
+
+
+
+
+//----------------------------------------------------------------------------------------------
+/**
+ * Hostage animation mechanism
+ */
+class CCSHostageAnimState : public CCSPlayerAnimState
+{
+public:
+ DECLARE_CLASS( CCSHostageAnimState, CCSPlayerAnimState );
+
+ CCSHostageAnimState();
+
+ virtual Activity CalcMainActivity();
+
+ // No need to cache sequences, and we *do* have multiple sequences per activity
+ virtual int SelectWeightedSequence( Activity activity ) { return GetOuter()->SelectWeightedSequence( activity ); }
+};
+
+
+//----------------------------------------------------------------------------------------------
+ICSPlayerAnimState* CreateHostageAnimState( CBaseAnimatingOverlay *pEntity, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences )
+{
+ CCSHostageAnimState *anim = new CCSHostageAnimState;
+ anim->InitCS( pEntity, pHelpers, legAnimType, bUseAimSequences );
+ return anim;
+}
+
+
+//----------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------
+CCSHostageAnimState::CCSHostageAnimState()
+{
+}
+
+
+//----------------------------------------------------------------------------------------------
+/**
+ * Set hostage animation state
+ */
+Activity CCSHostageAnimState::CalcMainActivity()
+{
+ float flOuterSpeed = GetOuterXYSpeed();
+
+ if ( HandleJumping() )
+ {
+ return ACT_HOP;
+ }
+ else
+ {
+ Assert( dynamic_cast<CHostage*>( m_pOuter ) );
+ CHostage *me = (CHostage*)m_pOuter;
+
+ // if we have no leader, hang out
+ Activity idealActivity = me->GetLeader() ? ACT_IDLE : ACT_BUSY_QUEUE;
+
+ if ( m_pOuter->GetFlags() & FL_DUCKING )
+ {
+ if ( flOuterSpeed > MOVING_MINIMUM_SPEED )
+ idealActivity = ACT_RUN_CROUCH;
+ else
+ idealActivity = ACT_COVER_LOW;
+ }
+ else
+ {
+ if ( flOuterSpeed > MOVING_MINIMUM_SPEED )
+ {
+ if ( flOuterSpeed > ARBITRARY_RUN_SPEED )
+ idealActivity = ACT_RUN;
+ else
+ idealActivity = ACT_WALK;
+ }
+ }
+
+ return idealActivity;
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------ //
+// CCSPlayerAnimState implementation.
+// ------------------------------------------------------------------------------------------------ //
+
+CCSPlayerAnimState::CCSPlayerAnimState()
+{
+ m_pOuter = NULL;
+
+ m_bJumping = false;
+ m_flJumpStartTime = 0.0f;
+ m_bFirstJumpFrame = false;
+
+ m_bReloading = false;
+ m_flReloadCycle = 0.0f;
+ m_iReloadSequence = -1;
+ m_flReloadHoldEndTime = 0.0f;
+
+ m_bFiring = false;
+ m_iFireSequence = -1;
+ m_flFireCycle = 0.0f;
+ m_delayedFire = PLAYERANIMEVENT_COUNT;
+
+ m_bThrowingGrenade = false;
+ m_bPrimingGrenade = false;
+ m_flGrenadeCycle = 0.0f;
+ m_iGrenadeSequence = -1;
+ m_iLastThrowGrenadeCounter = 0;
+ m_cachedModelIndex = -1;
+
+ m_pPlayer = NULL;
+
+ m_pHelpers = NULL;
+}
+
+
+void CCSPlayerAnimState::InitCS( CBaseAnimatingOverlay *pEntity, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences )
+{
+ CModAnimConfig config;
+ config.m_flMaxBodyYawDegrees = 90;
+ config.m_LegAnimType = legAnimType;
+ config.m_bUseAimSequences = bUseAimSequences;
+
+ m_pPlayer = ToCSPlayer( pEntity );
+
+ m_pHelpers = pHelpers;
+
+ BaseClass::Init( pEntity, config );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CCSPlayerAnimState::CheckCachedSequenceValidity( void )
+{
+ if ( m_cachedModelIndex != GetOuter()->GetModelIndex() )
+ {
+ m_namedSequence.RemoveAll();
+
+ m_cachedModelIndex = GetOuter()->GetModelIndex();
+ for ( int i=0; i<=ACT_CROUCHIDLE; ++i )
+ {
+ m_sequenceCache[i] = -1;
+ }
+
+ // precache the sequences we'll be using for movement
+ if ( m_cachedModelIndex > 0 )
+ {
+ m_sequenceCache[ACT_HOP - 1] = GetOuter()->SelectWeightedSequence( ACT_HOP );
+ m_sequenceCache[ACT_IDLE - 1] = GetOuter()->SelectWeightedSequence( ACT_IDLE );
+ m_sequenceCache[ACT_RUN_CROUCH - 1] = GetOuter()->SelectWeightedSequence( ACT_RUN_CROUCH );
+ m_sequenceCache[ACT_CROUCHIDLE - 1] = GetOuter()->SelectWeightedSequence( ACT_CROUCHIDLE );
+ m_sequenceCache[ACT_RUN - 1] = GetOuter()->SelectWeightedSequence( ACT_RUN );
+ m_sequenceCache[ACT_WALK - 1] = GetOuter()->SelectWeightedSequence( ACT_WALK );
+ m_sequenceCache[ACT_IDLE - 1] = GetOuter()->SelectWeightedSequence( ACT_IDLE );
+ }
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Cache the sequence numbers for the first ACT_HOP activities, since the CS player doesn't have multiple
+ * sequences per activity.
+ */
+int CCSPlayerAnimState::SelectWeightedSequence( Activity activity )
+{
+ VPROF( "CCSPlayerAnimState::ComputeMainSequence" );
+
+ if ( activity > ACT_CROUCHIDLE || activity < 1 )
+ {
+ return GetOuter()->SelectWeightedSequence( activity );
+ }
+
+ CheckCachedSequenceValidity();
+
+ int sequence = m_sequenceCache[ activity - 1 ];
+ if ( sequence < 0 )
+ {
+ // just in case, look up the sequence if we didn't precache it above
+ sequence = m_sequenceCache[ activity - 1 ] = GetOuter()->SelectWeightedSequence( activity );
+ }
+
+#if defined(CLIENT_DLL) && defined(_DEBUG)
+ int realSequence = GetOuter()->SelectWeightedSequence( activity );
+ Assert( realSequence == sequence );
+#endif
+
+ return sequence;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Try to look up named sequences in a CUtlDict cache before falling back to the normal LookupSequence. It's
+ * best to avoid the normal LookupSequence when your models have 750+ sequences...
+ */
+int CCSPlayerAnimState::CalcSequenceIndex( const char *pBaseName, ... )
+{
+ VPROF( "CCSPlayerAnimState::CalcSequenceIndex" );
+
+ CheckCachedSequenceValidity();
+
+ char szFullName[512];
+ va_list marker;
+ va_start( marker, pBaseName );
+ Q_vsnprintf( szFullName, sizeof( szFullName ), pBaseName, marker );
+ va_end( marker );
+
+ int iSequence = m_namedSequence.Find( szFullName );
+ if ( iSequence == m_namedSequence.InvalidIndex() )
+ {
+ iSequence = GetOuter()->LookupSequence( szFullName );
+ m_namedSequence.Insert( szFullName, iSequence );
+ }
+ else
+ {
+ iSequence = m_namedSequence[iSequence];
+ }
+
+#if defined(CLIENT_DLL) && defined(_DEBUG)
+ int realSequence = GetOuter()->LookupSequence( szFullName );
+ Assert( realSequence == iSequence );
+#endif
+
+ // Show warnings if we can't find anything here.
+ if ( iSequence == -1 )
+ {
+ static CUtlDict<int,int> dict;
+ if ( dict.Find( szFullName ) == -1 )
+ {
+ dict.Insert( szFullName, 0 );
+ Warning( "CalcSequenceIndex: can't find '%s'.\n", szFullName );
+ }
+
+ iSequence = 0;
+ }
+
+ return iSequence;
+}
+
+
+void CCSPlayerAnimState::ClearAnimationState()
+{
+ m_bJumping = false;
+ m_bFiring = false;
+ m_bReloading = false;
+ m_flReloadHoldEndTime = 0.0f;
+ m_bThrowingGrenade = m_bPrimingGrenade = false;
+ m_iLastThrowGrenadeCounter = GetOuterGrenadeThrowCounter();
+
+ BaseClass::ClearAnimationState();
+}
+
+
+void CCSPlayerAnimState::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
+{
+ Assert( event != PLAYERANIMEVENT_THROW_GRENADE );
+
+ MDLCACHE_CRITICAL_SECTION();
+ switch ( event )
+ {
+ case PLAYERANIMEVENT_FIRE_GUN_PRIMARY:
+ case PLAYERANIMEVENT_FIRE_GUN_SECONDARY:
+ // Regardless of what we're doing in the fire layer, restart it.
+ m_flFireCycle = 0;
+ m_iFireSequence = CalcFireLayerSequence( event );
+ m_bFiring = m_iFireSequence != -1;
+
+ // If we are interrupting a (shotgun) reload, cancel the reload, and fire next frame.
+ if ( m_bFiring && m_bReloading )
+ {
+ m_bReloading = false;
+ m_iReloadSequence = -1;
+
+ m_delayedFire = event;
+ m_bFiring = false;
+ m_iFireSequence = -1;
+
+ CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( RELOADSEQUENCE_LAYER );
+ if ( pLayer )
+ {
+ pLayer->m_flWeight = 0.0f;
+ pLayer->m_nOrder = 15;
+ }
+ }
+
+#ifdef CLIENT_DLL
+ if ( m_bFiring && !m_bReloading )
+ {
+ if ( m_pPlayer )
+ {
+ m_pPlayer->ProcessMuzzleFlashEvent();
+ }
+ }
+#endif
+ break;
+
+ case PLAYERANIMEVENT_JUMP:
+ // Play the jump animation.
+ m_bJumping = true;
+ m_bFirstJumpFrame = true;
+ m_flJumpStartTime = gpGlobals->curtime;
+ break;
+
+ case PLAYERANIMEVENT_RELOAD:
+ {
+ // ignore normal reload events for shotguns - they get sent to trigger sounds etc only
+ CWeaponCSBase *pWeapon = m_pHelpers->CSAnim_GetActiveWeapon();
+ if ( pWeapon && pWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_SHOTGUN )
+ {
+ m_iReloadSequence = CalcReloadLayerSequence( event );
+ if ( m_iReloadSequence != -1 )
+ {
+ m_bReloading = true;
+ m_flReloadCycle = 0;
+ }
+ else
+ {
+ m_bReloading = false;
+ }
+ }
+ }
+ break;
+
+ case PLAYERANIMEVENT_RELOAD_START:
+ case PLAYERANIMEVENT_RELOAD_LOOP:
+ // Set the hold time for _start and _loop anims, then fall through to the _end case
+ m_flReloadHoldEndTime = gpGlobals->curtime + 0.75f;
+
+ case PLAYERANIMEVENT_RELOAD_END:
+ {
+ // ignore shotgun reload events for non-shotguns
+ CWeaponCSBase *pWeapon = m_pHelpers->CSAnim_GetActiveWeapon();
+ if ( pWeapon && pWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_SHOTGUN )
+ {
+ m_flReloadHoldEndTime = 0.0f; // clear this out in case we set it in _START or _LOOP above
+ }
+ else
+ {
+ m_iReloadSequence = CalcReloadLayerSequence( event );
+ if ( m_iReloadSequence != -1 )
+ {
+ m_bReloading = true;
+ m_flReloadCycle = 0;
+ }
+ else
+ {
+ m_bReloading = false;
+ }
+ }
+ }
+ break;
+
+ case PLAYERANIMEVENT_CLEAR_FIRING:
+ {
+ m_iFireSequence = -1;
+ }
+ break;
+
+ default:
+ Assert( !"CCSPlayerAnimState::DoAnimationEvent" );
+ }
+}
+
+
+float g_flThrowGrenadeFraction = 0.25;
+bool CCSPlayerAnimState::IsThrowingGrenade()
+{
+ if ( m_bThrowingGrenade )
+ {
+ // An animation event would be more appropriate here.
+ return m_flGrenadeCycle < g_flThrowGrenadeFraction;
+ }
+ else
+ {
+ bool bThrowPending = (m_iLastThrowGrenadeCounter != GetOuterGrenadeThrowCounter());
+ return bThrowPending || IsOuterGrenadePrimed();
+ }
+}
+
+
+int CCSPlayerAnimState::CalcReloadLayerSequence( PlayerAnimEvent_t event )
+{
+ if ( m_delayedFire != PLAYERANIMEVENT_COUNT )
+ return -1;
+
+ const char *weaponSuffix = GetWeaponSuffix();
+ if ( !weaponSuffix )
+ return -1;
+
+ CWeaponCSBase *pWeapon = m_pHelpers->CSAnim_GetActiveWeapon();
+ if ( !pWeapon )
+ return -1;
+
+ const char *prefix = "";
+ switch ( GetCurrentMainSequenceActivity() )
+ {
+ case ACT_PLAYER_RUN_FIRE:
+ case ACT_RUN:
+ prefix = "run";
+ break;
+
+ case ACT_PLAYER_WALK_FIRE:
+ case ACT_WALK:
+ prefix = "walk";
+ break;
+
+ case ACT_PLAYER_CROUCH_FIRE:
+ case ACT_CROUCHIDLE:
+ prefix = "crouch_idle";
+ break;
+
+ case ACT_PLAYER_CROUCH_WALK_FIRE:
+ case ACT_RUN_CROUCH:
+ prefix = "crouch_walk";
+ break;
+
+ default:
+ case ACT_PLAYER_IDLE_FIRE:
+ prefix = "idle";
+ break;
+ }
+
+ const char *reloadSuffix = "";
+ switch ( event )
+ {
+ case PLAYERANIMEVENT_RELOAD_START:
+ reloadSuffix = "_start";
+ break;
+
+ case PLAYERANIMEVENT_RELOAD_LOOP:
+ reloadSuffix = "_loop";
+ break;
+
+ case PLAYERANIMEVENT_RELOAD_END:
+ reloadSuffix = "_end";
+ break;
+ }
+
+ // First, look for <prefix>_reload_<weapon name><_start|_loop|_end>.
+ char szName[512];
+ Q_snprintf( szName, sizeof( szName ), "%s_reload_%s%s", prefix, weaponSuffix, reloadSuffix );
+ int iReloadSequence = m_pOuter->LookupSequence( szName );
+ if ( iReloadSequence != -1 )
+ return iReloadSequence;
+
+ // Next, look for reload_<weapon name><_start|_loop|_end>.
+ Q_snprintf( szName, sizeof( szName ), "reload_%s%s", weaponSuffix, reloadSuffix );
+ iReloadSequence = m_pOuter->LookupSequence( szName );
+ if ( iReloadSequence != -1 )
+ return iReloadSequence;
+
+ // Ok, look for generic categories.. pistol, shotgun, rifle, etc.
+ if ( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_PISTOL )
+ {
+ Q_snprintf( szName, sizeof( szName ), "reload_pistol" );
+ iReloadSequence = m_pOuter->LookupSequence( szName );
+ if ( iReloadSequence != -1 )
+ return iReloadSequence;
+ }
+
+ // Fall back to reload_m4.
+ iReloadSequence = CalcSequenceIndex( "reload_m4" );
+ if ( iReloadSequence > 0 )
+ return iReloadSequence;
+
+ return -1;
+}
+
+ void CCSPlayerAnimState::UpdateLayerSequenceGeneric( CStudioHdr *pStudioHdr, int iLayer, bool &bEnabled, float &flCurCycle, int &iSequence, bool bWaitAtEnd )
+{
+ if ( !bEnabled || iSequence < 0 )
+ return;
+
+ // Increment the fire sequence's cycle.
+ flCurCycle += m_pOuter->GetSequenceCycleRate( pStudioHdr, iSequence ) * gpGlobals->frametime;
+ if ( flCurCycle > 1 )
+ {
+ if ( bWaitAtEnd )
+ {
+ flCurCycle = 1;
+ }
+ else
+ {
+ // Not firing anymore.
+ bEnabled = false;
+ iSequence = 0;
+ return;
+ }
+ }
+
+ // Now dump the state into its animation layer.
+ CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( iLayer );
+
+ pLayer->m_flCycle = flCurCycle;
+ pLayer->m_nSequence = iSequence;
+
+ pLayer->m_flPlaybackRate = 1.0f;
+ pLayer->m_flWeight = 1.0f;
+ pLayer->m_nOrder = iLayer;
+#ifndef CLIENT_DLL
+ pLayer->m_fFlags |= ANIM_LAYER_ACTIVE;
+#endif
+}
+
+bool CCSPlayerAnimState::IsOuterGrenadePrimed()
+{
+ CBaseCombatCharacter *pChar = m_pOuter->MyCombatCharacterPointer();
+ if ( pChar )
+ {
+ CBaseCSGrenade *pGren = dynamic_cast<CBaseCSGrenade*>( pChar->GetActiveWeapon() );
+ return pGren && pGren->IsPinPulled();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+void CCSPlayerAnimState::ComputeGrenadeSequence( CStudioHdr *pStudioHdr )
+{
+ VPROF( "CCSPlayerAnimState::ComputeGrenadeSequence" );
+
+ if ( m_bThrowingGrenade )
+ {
+ UpdateLayerSequenceGeneric( pStudioHdr, GRENADESEQUENCE_LAYER, m_bThrowingGrenade, m_flGrenadeCycle, m_iGrenadeSequence, false );
+ }
+ else
+ {
+ if ( m_pPlayer )
+ {
+ CBaseCombatWeapon *pWeapon = m_pPlayer->GetActiveWeapon();
+ CBaseCSGrenade *pGren = dynamic_cast<CBaseCSGrenade*>( pWeapon );
+ if ( !pGren )
+ {
+ // The player no longer has a grenade equipped. Bail.
+ m_iLastThrowGrenadeCounter = GetOuterGrenadeThrowCounter();
+ return;
+ }
+ }
+
+ // Priming the grenade isn't an event.. we just watch the player for it.
+ // Also play the prime animation first if he wants to throw the grenade.
+ bool bThrowPending = (m_iLastThrowGrenadeCounter != GetOuterGrenadeThrowCounter());
+ if ( IsOuterGrenadePrimed() || bThrowPending )
+ {
+ if ( !m_bPrimingGrenade )
+ {
+ // If this guy just popped into our PVS, and he's got his grenade primed, then
+ // let's assume that it's all the way primed rather than playing the prime
+ // animation from the start.
+ if ( TimeSinceLastAnimationStateClear() < 0.4f )
+ {
+ m_flGrenadeCycle = 1;
+ }
+ else
+ {
+ m_flGrenadeCycle = 0;
+ }
+
+ m_iGrenadeSequence = CalcGrenadePrimeSequence();
+ m_bPrimingGrenade = true;
+ }
+
+ UpdateLayerSequenceGeneric( pStudioHdr, GRENADESEQUENCE_LAYER, m_bPrimingGrenade, m_flGrenadeCycle, m_iGrenadeSequence, true );
+
+ // If we're waiting to throw and we're done playing the prime animation...
+ if ( bThrowPending && m_flGrenadeCycle == 1 )
+ {
+ m_iLastThrowGrenadeCounter = GetOuterGrenadeThrowCounter();
+
+ // Now play the throw animation.
+ m_iGrenadeSequence = CalcGrenadeThrowSequence();
+ if ( m_iGrenadeSequence != -1 )
+ {
+ // Configure to start playing
+ m_bThrowingGrenade = true;
+ m_bPrimingGrenade = false;
+ m_flGrenadeCycle = 0;
+ }
+ }
+ }
+ else
+ {
+ m_bPrimingGrenade = false;
+ }
+ }
+}
+
+
+int CCSPlayerAnimState::CalcGrenadePrimeSequence()
+{
+ return CalcSequenceIndex( "idle_shoot_gren1" );
+}
+
+
+int CCSPlayerAnimState::CalcGrenadeThrowSequence()
+{
+ return CalcSequenceIndex( "idle_shoot_gren2" );
+}
+
+
+int CCSPlayerAnimState::GetOuterGrenadeThrowCounter()
+{
+ if ( m_pPlayer )
+ return m_pPlayer->m_iThrowGrenadeCounter;
+ else
+ return 0;
+}
+
+
+void CCSPlayerAnimState::ComputeReloadSequence( CStudioHdr *pStudioHdr )
+{
+ VPROF( "CCSPlayerAnimState::ComputeReloadSequence" );
+ bool hold = m_flReloadHoldEndTime > gpGlobals->curtime;
+ UpdateLayerSequenceGeneric( pStudioHdr, RELOADSEQUENCE_LAYER, m_bReloading, m_flReloadCycle, m_iReloadSequence, hold );
+ if ( !m_bReloading )
+ {
+ m_flReloadHoldEndTime = 0.0f;
+ }
+}
+
+
+int CCSPlayerAnimState::CalcAimLayerSequence( float *flCycle, float *flAimSequenceWeight, bool bForceIdle )
+{
+ VPROF( "CCSPlayerAnimState::CalcAimLayerSequence" );
+
+ const char *pSuffix = GetWeaponSuffix();
+ if ( !pSuffix )
+ return 0;
+
+ if ( bForceIdle )
+ {
+ switch ( GetCurrentMainSequenceActivity() )
+ {
+ case ACT_CROUCHIDLE:
+ case ACT_RUN_CROUCH:
+ return CalcSequenceIndex( "%s%s", DEFAULT_CROUCH_IDLE_NAME, pSuffix );
+
+ default:
+ return CalcSequenceIndex( "%s%s", DEFAULT_IDLE_NAME, pSuffix );
+ }
+ }
+ else
+ {
+ switch ( GetCurrentMainSequenceActivity() )
+ {
+ case ACT_RUN:
+ return CalcSequenceIndex( "%s%s", DEFAULT_RUN_NAME, pSuffix );
+
+ case ACT_WALK:
+ case ACT_RUNTOIDLE:
+ case ACT_IDLETORUN:
+ return CalcSequenceIndex( "%s%s", DEFAULT_WALK_NAME, pSuffix );
+
+ case ACT_CROUCHIDLE:
+ return CalcSequenceIndex( "%s%s", DEFAULT_CROUCH_IDLE_NAME, pSuffix );
+
+ case ACT_RUN_CROUCH:
+ return CalcSequenceIndex( "%s%s", DEFAULT_CROUCH_WALK_NAME, pSuffix );
+
+ case ACT_IDLE:
+ default:
+ return CalcSequenceIndex( "%s%s", DEFAULT_IDLE_NAME, pSuffix );
+ }
+ }
+}
+
+
+const char* CCSPlayerAnimState::GetWeaponSuffix()
+{
+ VPROF( "CCSPlayerAnimState::GetWeaponSuffix" );
+
+ // Figure out the weapon suffix.
+ CWeaponCSBase *pWeapon = m_pHelpers->CSAnim_GetActiveWeapon();
+ if ( !pWeapon )
+ return 0;
+
+ const char *pSuffix = pWeapon->GetCSWpnData().m_szAnimExtension;
+
+#ifdef CS_SHIELD_ENABLED
+ if ( m_pOuter->HasShield() == true )
+ {
+ if ( m_pOuter->IsShieldDrawn() == true )
+ pSuffix = "shield";
+ else
+ pSuffix = "shield_undeployed";
+ }
+#endif
+
+ return pSuffix;
+}
+
+
+int CCSPlayerAnimState::CalcFireLayerSequence(PlayerAnimEvent_t event)
+{
+ // Figure out the weapon suffix.
+ CWeaponCSBase *pWeapon = m_pHelpers->CSAnim_GetActiveWeapon();
+ if ( !pWeapon )
+ return -1;
+
+ const char *pSuffix = GetWeaponSuffix();
+ if ( !pSuffix )
+ return -1;
+
+ char tempsuffix[32];
+ if ( pWeapon->GetWeaponID() == WEAPON_ELITE )
+ {
+ bool bPrimary = (event == PLAYERANIMEVENT_FIRE_GUN_PRIMARY);
+ Q_snprintf( tempsuffix, sizeof(tempsuffix), "%s_%c", pSuffix, bPrimary?'r':'l' );
+ pSuffix = tempsuffix;
+ }
+
+ // Grenades handle their fire events separately
+ if ( event == PLAYERANIMEVENT_THROW_GRENADE ||
+ pWeapon->GetWeaponID() == WEAPON_HEGRENADE ||
+ pWeapon->GetWeaponID() == WEAPON_SMOKEGRENADE ||
+ pWeapon->GetWeaponID() == WEAPON_FLASHBANG )
+ {
+ return -1;
+ }
+
+ switch ( GetCurrentMainSequenceActivity() )
+ {
+ case ACT_PLAYER_RUN_FIRE:
+ case ACT_RUN:
+ return CalcSequenceIndex( "%s%s", DEFAULT_FIRE_RUN_NAME, pSuffix );
+
+ case ACT_PLAYER_WALK_FIRE:
+ case ACT_WALK:
+ return CalcSequenceIndex( "%s%s", DEFAULT_FIRE_WALK_NAME, pSuffix );
+
+ case ACT_PLAYER_CROUCH_FIRE:
+ case ACT_CROUCHIDLE:
+ return CalcSequenceIndex( "%s%s", DEFAULT_FIRE_CROUCH_NAME, pSuffix );
+
+ case ACT_PLAYER_CROUCH_WALK_FIRE:
+ case ACT_RUN_CROUCH:
+ return CalcSequenceIndex( "%s%s", DEFAULT_FIRE_CROUCH_WALK_NAME, pSuffix );
+
+ default:
+ case ACT_PLAYER_IDLE_FIRE:
+ return CalcSequenceIndex( "%s%s", DEFAULT_FIRE_IDLE_NAME, pSuffix );
+ }
+}
+
+
+bool CCSPlayerAnimState::CanThePlayerMove()
+{
+ return m_pHelpers->CSAnim_CanMove();
+}
+
+
+float CCSPlayerAnimState::GetCurrentMaxGroundSpeed()
+{
+ Activity currentActivity = m_pOuter->GetSequenceActivity( m_pOuter->GetSequence() );
+ if ( currentActivity == ACT_WALK || currentActivity == ACT_IDLE )
+ return ANIM_TOPSPEED_WALK;
+ else if ( currentActivity == ACT_RUN )
+ {
+ if ( m_pPlayer )
+ {
+ CBaseCombatWeapon *activeWeapon = m_pPlayer->GetActiveWeapon();
+ if ( activeWeapon )
+ {
+ CWeaponCSBase *csWeapon = dynamic_cast< CWeaponCSBase * >( activeWeapon );
+ if ( csWeapon )
+ {
+ return csWeapon->GetMaxSpeed();
+ }
+ }
+ }
+ return ANIM_TOPSPEED_RUN;
+ }
+ else if ( currentActivity == ACT_RUN_CROUCH )
+ return ANIM_TOPSPEED_RUN_CROUCH;
+ else
+ return 0;
+}
+
+
+bool CCSPlayerAnimState::HandleJumping()
+{
+ if ( m_bJumping )
+ {
+ if ( m_bFirstJumpFrame )
+ {
+
+#if !defined(CLIENT_DLL)
+ //=============================================================================
+ // HPE_BEGIN:
+ // [dwenger] Needed for fun-fact implementation
+ //=============================================================================
+
+ CCS_GameStats.IncrementStat(m_pPlayer, CSSTAT_TOTAL_JUMPS, 1);
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+#endif
+
+ m_bFirstJumpFrame = false;
+ RestartMainSequence(); // Reset the animation.
+ }
+
+ // Don't check if he's on the ground for a sec.. sometimes the client still has the
+ // on-ground flag set right when the message comes in.
+ if ( gpGlobals->curtime - m_flJumpStartTime > 0.2f )
+ {
+ if ( m_pOuter->GetFlags() & FL_ONGROUND )
+ {
+ m_bJumping = false;
+ RestartMainSequence(); // Reset the animation.
+ }
+ }
+ }
+
+ // Are we still jumping? If so, keep playing the jump animation.
+ return m_bJumping;
+}
+
+
+Activity CCSPlayerAnimState::CalcMainActivity()
+{
+ float flOuterSpeed = GetOuterXYSpeed();
+
+ if ( HandleJumping() )
+ {
+ return ACT_HOP;
+ }
+ else
+ {
+ Activity idealActivity = ACT_IDLE;
+
+ if ( m_pOuter->GetFlags() & FL_ANIMDUCKING )
+ {
+ if ( flOuterSpeed > MOVING_MINIMUM_SPEED )
+ idealActivity = ACT_RUN_CROUCH;
+ else
+ idealActivity = ACT_CROUCHIDLE;
+ }
+ else
+ {
+ if ( flOuterSpeed > MOVING_MINIMUM_SPEED )
+ {
+ if ( flOuterSpeed > ARBITRARY_RUN_SPEED )
+ idealActivity = ACT_RUN;
+ else
+ idealActivity = ACT_WALK;
+ }
+ else
+ {
+ idealActivity = ACT_IDLE;
+ }
+ }
+
+ return idealActivity;
+ }
+}
+
+
+void CCSPlayerAnimState::DebugShowAnimState( int iStartLine )
+{
+ engine->Con_NPrintf( iStartLine++, "fire : %s, cycle: %.2f\n", m_bFiring ? GetSequenceName( m_pOuter->GetModelPtr(), m_iFireSequence ) : "[not firing]", m_flFireCycle );
+ engine->Con_NPrintf( iStartLine++, "reload: %s, cycle: %.2f\n", m_bReloading ? GetSequenceName( m_pOuter->GetModelPtr(), m_iReloadSequence ) : "[not reloading]", m_flReloadCycle );
+ BaseClass::DebugShowAnimState( iStartLine );
+}
+
+
+void CCSPlayerAnimState::ComputeSequences( CStudioHdr *pStudioHdr )
+{
+ BaseClass::ComputeSequences( pStudioHdr );
+
+ VPROF( "CCSPlayerAnimState::ComputeSequences" );
+
+ ComputeFireSequence( pStudioHdr );
+ ComputeReloadSequence( pStudioHdr );
+ ComputeGrenadeSequence( pStudioHdr );
+}
+
+
+void CCSPlayerAnimState::ClearAnimationLayers()
+{
+ if ( !m_pOuter )
+ return;
+
+ m_pOuter->SetNumAnimOverlays( NUM_LAYERS_WANTED );
+ for ( int i=0; i < m_pOuter->GetNumAnimOverlays(); i++ )
+ {
+ // Client obeys Order of CBaseAnimatingOverlay::MAX_OVERLAYS (15), but server trusts only the ANIM_LAYER_ACTIVE flag.
+ m_pOuter->GetAnimOverlay( i )->SetOrder( CBaseAnimatingOverlay::MAX_OVERLAYS );
+#ifndef CLIENT_DLL
+ m_pOuter->GetAnimOverlay( i )->m_fFlags = 0;
+#endif
+ }
+}
+
+
+void CCSPlayerAnimState::ComputeFireSequence( CStudioHdr *pStudioHdr )
+{
+ VPROF( "CCSPlayerAnimState::ComputeFireSequence" );
+
+ if ( m_delayedFire != PLAYERANIMEVENT_COUNT )
+ {
+ DoAnimationEvent( m_delayedFire, 0 );
+ m_delayedFire = PLAYERANIMEVENT_COUNT;
+ }
+
+ UpdateLayerSequenceGeneric( pStudioHdr, FIRESEQUENCE_LAYER, m_bFiring, m_flFireCycle, m_iFireSequence, false );
+}