Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1057 lines
28 KiB

//========= 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 );
}