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.
2920 lines
101 KiB
2920 lines
101 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "csgo_playeranimstate.h"
|
|
#include "iplayeranimstate.h"
|
|
#include "animation.h"
|
|
#include "weapon_csbase.h"
|
|
#include "gamemovement.h"
|
|
#include "in_buttons.h"
|
|
|
|
#ifdef CLIENT_DLL
|
|
#include "c_cs_player.h"
|
|
#else
|
|
#include "cs_player.h"
|
|
#include "ilagcompensationmanager.h"
|
|
#endif
|
|
|
|
extern CUtlSymbolTable g_ActivityModifiersTable;
|
|
|
|
float ClampCycle( float flCycleIn )
|
|
{
|
|
flCycleIn -= int(flCycleIn);
|
|
|
|
if ( flCycleIn < 0 )
|
|
{
|
|
flCycleIn += 1;
|
|
}
|
|
else if ( flCycleIn > 1 )
|
|
{
|
|
flCycleIn -= 1;
|
|
}
|
|
|
|
return flCycleIn;
|
|
}
|
|
|
|
CCSGOPlayerAnimState *CreateCSGOPlayerAnimstate( CBaseAnimatingOverlay *pEntity )
|
|
{
|
|
CCSPlayer *pPlayer = ToCSPlayer( pEntity );
|
|
Assert( pPlayer );
|
|
|
|
CCSGOPlayerAnimState *pRet = new CCSGOPlayerAnimState( pPlayer );
|
|
return pRet;
|
|
}
|
|
|
|
#define CURRENT_ANIMSTATE_VERSION 2
|
|
CCSGOPlayerAnimState::CCSGOPlayerAnimState( CCSPlayer *pPlayer )
|
|
{
|
|
m_pPlayer = pPlayer;
|
|
Assert( m_pPlayer );
|
|
|
|
m_cachedModelIndex = -1;
|
|
m_nAnimstateModelVersion = CURRENT_ANIMSTATE_VERSION;
|
|
Reset();
|
|
}
|
|
|
|
#define CSGO_ANIM_AIMMATRIX_DEFAULT_YAW_MAX 58.0f
|
|
#define CSGO_ANIM_AIMMATRIX_DEFAULT_YAW_MIN -58.0f
|
|
#define CSGO_ANIM_AIMMATRIX_DEFAULT_PITCH_MAX 90.0f
|
|
#define CSGO_ANIM_AIMMATRIX_DEFAULT_PITCH_MIN -90.0f
|
|
|
|
void CCSGOPlayerAnimState::Reset( void )
|
|
{
|
|
Assert( m_pPlayer );
|
|
|
|
#ifndef CLIENT_DLL
|
|
m_pPlayer->SetNumAnimOverlays( ANIMATION_LAYER_COUNT );
|
|
#endif
|
|
ApplyLayerOrderPreset( get_animlayerpreset( Default ), true );
|
|
|
|
#ifdef CLIENT_DLL
|
|
m_iLastUpdateFrame = 0;
|
|
m_flStepHeightLeft = 0;
|
|
m_flStepHeightRight = 0;
|
|
#endif
|
|
|
|
#ifndef CLIENT_DLL
|
|
m_flFlashedAmountEaseOutStart = 0;
|
|
m_flFlashedAmountEaseOutEnd = 0;
|
|
#endif
|
|
|
|
m_pWeapon = m_pPlayer->GetActiveCSWeapon();
|
|
m_pWeaponLast = m_pWeapon;
|
|
|
|
#ifdef CLIENT_DLL
|
|
m_pWeaponLastBoneSetup = m_pWeapon;
|
|
m_flEyePositionSmoothLerp = 0;
|
|
m_flStrafeChangeWeightSmoothFalloff = 0;
|
|
m_bFirstFootPlantSinceInit = true;
|
|
#endif
|
|
|
|
m_flLastUpdateTime = 0;
|
|
m_flLastUpdateIncrement = 0;
|
|
|
|
m_flEyeYaw = 0;
|
|
m_flEyePitch = 0;
|
|
m_flFootYaw = 0;
|
|
m_flFootYawLast = 0;
|
|
m_flMoveYaw = 0;
|
|
m_flMoveYawIdeal = 0;
|
|
m_flMoveYawCurrentToIdeal = 0;
|
|
|
|
#ifndef CLIENT_DLL
|
|
m_pPlayer->m_flLowerBodyYawTarget.Set( 0 );
|
|
m_flLowerBodyRealignTimer = 0;
|
|
#endif
|
|
|
|
m_tStandWalkAim.Init();
|
|
m_tStandWalkAim.m_flHowLongToWaitUntilTransitionCanBlendIn = 0.4f;
|
|
m_tStandWalkAim.m_flHowLongToWaitUntilTransitionCanBlendOut = 0.2f;
|
|
m_tStandRunAim.Init();
|
|
m_tStandRunAim.m_flHowLongToWaitUntilTransitionCanBlendIn = 0.2f;
|
|
m_tStandRunAim.m_flHowLongToWaitUntilTransitionCanBlendOut = 0.4f;
|
|
m_tCrouchWalkAim.Init();
|
|
m_tCrouchWalkAim.m_flHowLongToWaitUntilTransitionCanBlendIn = 0.3f;
|
|
m_tCrouchWalkAim.m_flHowLongToWaitUntilTransitionCanBlendOut = 0.3f;
|
|
|
|
m_flPrimaryCycle = 0;
|
|
m_flMoveWeight = 0;
|
|
m_flMoveWeightSmoothed = 0;
|
|
m_flAnimDuckAmount = 0;
|
|
m_flDuckAdditional = 0; // for when we duck a bit after landing from a jump
|
|
m_flRecrouchWeight = 0;
|
|
|
|
m_vecPositionCurrent.Init();
|
|
m_vecPositionLast.Init();
|
|
|
|
m_vecVelocity.Init();
|
|
m_vecVelocityNormalized.Init();
|
|
m_vecVelocityNormalizedNonZero.Init();
|
|
m_flVelocityLengthXY = 0;
|
|
m_flVelocityLengthZ = 0;
|
|
|
|
m_flSpeedAsPortionOfRunTopSpeed = 0;
|
|
m_flSpeedAsPortionOfWalkTopSpeed = 0;
|
|
m_flSpeedAsPortionOfCrouchTopSpeed = 0;
|
|
|
|
m_flDurationMoving = 0;
|
|
m_flDurationStill = 0;
|
|
|
|
m_bOnGround = true;
|
|
#ifndef CLIENT_DLL
|
|
m_bJumping = false;
|
|
#endif
|
|
m_flLandAnimMultiplier = 1.0f;
|
|
m_flLeftGroundHeight = 0;
|
|
m_bLanding = false;
|
|
m_flJumpToFall = 0;
|
|
m_flDurationInAir = 0;
|
|
|
|
m_flWalkToRunTransition = 0;
|
|
|
|
m_bLandedOnGroundThisFrame = false;
|
|
m_bLeftTheGroundThisFrame = false;
|
|
m_flInAirSmoothValue = 0;
|
|
|
|
m_bOnLadder = false;
|
|
m_flLadderWeight = 0;
|
|
m_flLadderSpeed = 0;
|
|
|
|
m_bWalkToRunTransitionState = ANIM_TRANSITION_WALK_TO_RUN;
|
|
|
|
m_bDefuseStarted = false;
|
|
m_bPlantAnimStarted = false;
|
|
m_bTwitchAnimStarted = false;
|
|
m_bAdjustStarted = false;
|
|
|
|
m_flNextTwitchTime = 0;
|
|
|
|
m_flTimeOfLastKnownInjury = 0;
|
|
|
|
m_flLastVelocityTestTime = 0;
|
|
m_vecVelocityLast.Init();
|
|
m_vecTargetAcceleration.Init();
|
|
m_vecAcceleration.Init();
|
|
m_flAccelerationWeight = 0;
|
|
|
|
m_flAimMatrixTransition = 0;
|
|
m_flAimMatrixTransitionDelay = 0;
|
|
|
|
m_bFlashed = 0;
|
|
|
|
|
|
m_flStrafeChangeWeight = 0;
|
|
m_flStrafeChangeTargetWeight = 0;
|
|
m_flStrafeChangeCycle = 0;
|
|
m_nStrafeSequence = -1;
|
|
m_bStrafeChanging = false;
|
|
m_flDurationStrafing = 0;
|
|
|
|
m_flFootLerp = 0;
|
|
|
|
m_bFeetCrossed = false;
|
|
|
|
m_bPlayerIsAccelerating = false;
|
|
|
|
#ifndef CLIENT_DLL
|
|
m_bDeployRateLimiting = false;
|
|
#endif
|
|
|
|
m_flDurationMoveWeightIsTooHigh = 0;
|
|
m_flStaticApproachSpeed = 80;
|
|
|
|
m_flStutterStep = 0;
|
|
m_nPreviousMoveState = 0;
|
|
|
|
m_flActionWeightBiasRemainder = 0;
|
|
|
|
m_flAimYawMin = CSGO_ANIM_AIMMATRIX_DEFAULT_YAW_MIN;
|
|
m_flAimYawMax = CSGO_ANIM_AIMMATRIX_DEFAULT_YAW_MAX;
|
|
m_flAimPitchMin = CSGO_ANIM_AIMMATRIX_DEFAULT_PITCH_MIN;
|
|
m_flAimPitchMax = CSGO_ANIM_AIMMATRIX_DEFAULT_PITCH_MAX;
|
|
|
|
//m_flMoveWalkWeight = 0;
|
|
//m_flMoveCrouchWalkWeight = 0;
|
|
//m_flMoveRunWeight = 0;
|
|
|
|
m_ActivityModifiers.Purge();
|
|
|
|
m_bFirstRunSinceInit = true;
|
|
|
|
#ifdef CLIENT_DLL
|
|
m_flCameraSmoothHeight = 0;
|
|
m_bSmoothHeightValid = false;
|
|
m_flLastTimeVelocityOverTen = 0;
|
|
|
|
m_pPlayer->ClearAnimLODflags();
|
|
#endif
|
|
}
|
|
|
|
#define bonesnapshot_def( _name, _val ) const float _name = _val;
|
|
#define bonesnapshot_get( _name ) _name
|
|
|
|
bonesnapshot_def( cl_bonesnapshot_speed_weaponchange, 0.25 )
|
|
bonesnapshot_def( cl_bonesnapshot_speed_strafestart, 0.15 )
|
|
bonesnapshot_def( cl_bonesnapshot_speed_movebegin, 0.3 )
|
|
bonesnapshot_def( cl_bonesnapshot_speed_ladderenter, 0.25 )
|
|
bonesnapshot_def( cl_bonesnapshot_speed_ladderexit, 0.1 )
|
|
|
|
#define CSGO_ANIM_DEPLOY_RATELIMIT 0.15f
|
|
|
|
#define CSGO_ANIM_DUCK_APPROACH_SPEED_DOWN 3.1f
|
|
#define CSGO_ANIM_DUCK_APPROACH_SPEED_UP 6.0f
|
|
|
|
void CCSGOPlayerAnimState::Update( float eyeYaw, float eyePitch, bool bForce )
|
|
{
|
|
if ( !m_pPlayer )
|
|
return;
|
|
|
|
if ( !m_pPlayer->IsAlive() )
|
|
return;
|
|
|
|
if ( !CacheSequences() )
|
|
return;
|
|
|
|
{
|
|
// Apply recoil angle to aim matrix so bullets still come out of the gun straight while spraying
|
|
eyePitch = AngleNormalize( eyePitch + m_pPlayer->m_flThirdpersonRecoil );
|
|
}
|
|
|
|
|
|
// don't need to update animstate if we already have this curtime
|
|
if ( !bForce && ( m_flLastUpdateTime == gpGlobals->curtime || m_nLastUpdateFrame == gpGlobals->framecount ) )
|
|
return;
|
|
m_flLastUpdateIncrement = Max( 0.0f, gpGlobals->curtime - m_flLastUpdateTime ); // negative values possible when clocks on client and server go out of sync..
|
|
|
|
#ifdef CLIENT_DLL
|
|
// suspend bonecache invalidation
|
|
C_BaseAnimating::EnableInvalidateBoneCache( false );
|
|
#endif
|
|
|
|
m_flEyeYaw = AngleNormalize( eyeYaw );
|
|
m_flEyePitch = AngleNormalize( eyePitch );
|
|
m_vecPositionCurrent = m_pPlayer->GetAbsOrigin();
|
|
m_pWeapon = m_pPlayer->GetActiveCSWeapon();
|
|
|
|
|
|
// purge layer dispatches on weapon change and init
|
|
if ( m_pWeapon != m_pWeaponLast || m_bFirstRunSinceInit )
|
|
{
|
|
|
|
#ifdef CLIENT_DLL
|
|
// changing weapons will change the pose of leafy bones like fingers. The next time we
|
|
// set up this player's bones, treat it like a clean first setup.
|
|
m_pPlayer->m_nComputedLODframe = 0;
|
|
#endif
|
|
|
|
for ( int i=0; i < ANIMATION_LAYER_COUNT; i++ )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( i, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
{
|
|
pLayer->m_pDispatchedStudioHdr = NULL;
|
|
pLayer->m_nDispatchedSrc = ACT_INVALID;
|
|
pLayer->m_nDispatchedDst = ACT_INVALID;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
if ( IsPreCrouchUpdateDemo() )
|
|
{
|
|
// compatibility for old demos using old crouch values
|
|
float flTargetDuck = (m_pPlayer->GetFlags() & ( FL_ANIMDUCKING )) ? 1.0f : m_flDuckAdditional;
|
|
m_flAnimDuckAmount = Approach( flTargetDuck, m_flAnimDuckAmount, m_flLastUpdateIncrement * ( (m_flAnimDuckAmount < flTargetDuck) ? CSGO_ANIM_DUCK_APPROACH_SPEED_DOWN : CSGO_ANIM_DUCK_APPROACH_SPEED_UP ) );
|
|
m_flAnimDuckAmount = clamp( m_flAnimDuckAmount, 0, 1 );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
m_flAnimDuckAmount = clamp( Approach( clamp( m_pPlayer->m_flDuckAmount + m_flDuckAdditional, 0, 1), m_flAnimDuckAmount, m_flLastUpdateIncrement * 6.0f ), 0, 1 );
|
|
}
|
|
|
|
// no matter what, we're always playing 'default' underneath
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
m_pPlayer->SetSequence( 0 );
|
|
m_pPlayer->SetPlaybackRate( 0 );
|
|
m_pPlayer->SetCycle( 0 );
|
|
}
|
|
|
|
// all the layers get set up here
|
|
SetUpVelocity(); // calculate speed and set up body yaw values
|
|
SetUpAimMatrix(); // aim matrices are full body, so they not only point the weapon down the eye dir, they can crouch the idle player
|
|
SetUpWeaponAction(); // firing, reloading, silencer-swapping, deploying
|
|
SetUpMovement(); // jumping, climbing, ground locomotion, post-weapon crouch-stand
|
|
SetUpAliveloop(); // breathe and fidget
|
|
SetUpWholeBodyAction(); // defusing, planting, whole-body custom events
|
|
SetUpFlashedReaction(); // shield eyes from flashbang
|
|
SetUpFlinch(); // flinch when shot
|
|
SetUpLean(); // lean into acceleration
|
|
|
|
#ifdef CLIENT_DLL
|
|
// zero-sequences are un-set and should have zero weight on the client
|
|
for ( int i=0; i < ANIMATION_LAYER_COUNT; i++ )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( i, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer && pLayer->GetSequence() == 0 )
|
|
pLayer->SetWeight(0);
|
|
}
|
|
#endif
|
|
|
|
// force abs angles on client and server to line up hitboxes
|
|
m_pPlayer->SetAbsAngles( QAngle( 0, m_flFootYaw, 0 ) );
|
|
|
|
#ifdef CLIENT_DLL
|
|
// restore bonecache invalidation
|
|
C_BaseAnimating::EnableInvalidateBoneCache( true );
|
|
#endif
|
|
|
|
m_pWeaponLast = m_pWeapon;
|
|
m_vecPositionLast = m_vecPositionCurrent;
|
|
m_bFirstRunSinceInit = false;
|
|
m_flLastUpdateTime = gpGlobals->curtime;
|
|
m_nLastUpdateFrame = gpGlobals->framecount;
|
|
}
|
|
|
|
float CCSGOPlayerAnimState::LerpCrouchWalkRun( float flCrouchVal, float flWalkVal, float flRunVal )
|
|
{
|
|
// lerp between three separate magic numbers intended for each state
|
|
return Lerp( m_flAnimDuckAmount, Lerp( m_flWalkToRunTransition, flWalkVal, flRunVal ), flCrouchVal );
|
|
}
|
|
|
|
#define HYPEREXTENSION_LIMIT 34.2 // The length of a CS player's leg (hip to ankle) when nearly fully extended
|
|
#define HYPEREXTENSION_LIMIT_SQR HYPEREXTENSION_LIMIT * HYPEREXTENSION_LIMIT
|
|
#define FOOT_SAFE_ZONE_LEFT_SQR 7 * 7
|
|
#define FOOT_SAFE_ZONE_RIGHT_SQR 8 * 8
|
|
#define ANKLE_HEIGHT 4.5f
|
|
#define FOOT_PROXIMITY_LIMIT_SQR 12 * 12 // don't let feet come to rest this close together
|
|
#define LATERAL_BLENDOUT_HEIGHT 1
|
|
#define LATERAL_BLENDIN_TIME 20
|
|
#define LATERAL_BLENDOUT_TIME 6
|
|
|
|
|
|
#define FIRSTPERSON_TO_THIRDPERSON_VERTICAL_TOLERANCE_MIN 4.0f
|
|
#define FIRSTPERSON_TO_THIRDPERSON_VERTICAL_TOLERANCE_MAX 10.0f
|
|
#ifdef CLIENT_DLL
|
|
extern ConVar cl_camera_height_restriction_debug;
|
|
#endif
|
|
void CCSGOPlayerAnimState::ModifyEyePosition( Vector& vecInputEyePos )
|
|
{
|
|
if ( !m_pPlayer )
|
|
return;
|
|
|
|
#ifdef CLIENT_DLL
|
|
if ( IsPreCrouchUpdateDemo() )
|
|
return;
|
|
#endif
|
|
|
|
// The local player sets up their third-person bones to locate the position of their head,
|
|
// then this position is used to softly bound the vertical camera position for the client.
|
|
|
|
if ( !m_bLanding && m_flAnimDuckAmount == 0 )
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
m_bSmoothHeightValid = false;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
int nHeadBone = m_pPlayer->LookupBone( "head_0" );
|
|
if ( nHeadBone != -1 )
|
|
{
|
|
Vector vecHeadPos;
|
|
QAngle temp;
|
|
m_pPlayer->GetBonePosition( nHeadBone, vecHeadPos, temp );
|
|
vecHeadPos.z += 1.7f;
|
|
|
|
#ifdef CLIENT_DLL
|
|
if ( cl_camera_height_restriction_debug.GetBool() )
|
|
{
|
|
Vector vecTemp = Vector( vecInputEyePos.x, vecInputEyePos.y, vecHeadPos.z + FIRSTPERSON_TO_THIRDPERSON_VERTICAL_TOLERANCE_MAX );
|
|
debugoverlay->AddLineOverlay( vecTemp, vecTemp + Vector(0,20,0), 255, 0, 255, true, gpGlobals->frametime );
|
|
vecTemp = Vector( vecInputEyePos.x, vecInputEyePos.y, vecHeadPos.z + FIRSTPERSON_TO_THIRDPERSON_VERTICAL_TOLERANCE_MIN );
|
|
debugoverlay->AddLineOverlay( vecTemp, vecTemp + Vector(0,20,0), 0, 0, 255, true, gpGlobals->frametime );
|
|
vecTemp = Vector( vecInputEyePos.x, vecInputEyePos.y, vecInputEyePos.z );
|
|
debugoverlay->AddLineOverlay( vecTemp, vecTemp + Vector(0,20,0), 255, 0, 0, true, gpGlobals->frametime );
|
|
//vecTemp = Vector( vecInputEyePos.x, vecInputEyePos.y, vecHeadPos.z - FIRSTPERSON_TO_THIRDPERSON_VERTICAL_TOLERANCE_MIN );
|
|
//debugoverlay->AddLineOverlay( vecTemp, vecTemp + Vector(0,20,0), 0, 0, 255, true, gpGlobals->frametime );
|
|
//vecTemp = Vector( vecInputEyePos.x, vecInputEyePos.y, vecHeadPos.z - FIRSTPERSON_TO_THIRDPERSON_VERTICAL_TOLERANCE_MAX );
|
|
//debugoverlay->AddLineOverlay( vecTemp, vecTemp + Vector(0,20,0), 255, 0, 255, true, gpGlobals->frametime );
|
|
}
|
|
#endif
|
|
|
|
// Only correct the eye if the camera is ABOVE the head. If the camera is below the head, that's unlikely
|
|
// to be advantageous for the local player.
|
|
if ( vecHeadPos.z < vecInputEyePos.z )
|
|
{
|
|
|
|
float flLerp = SimpleSplineRemapValClamped( abs( vecInputEyePos.z - vecHeadPos.z ),
|
|
FIRSTPERSON_TO_THIRDPERSON_VERTICAL_TOLERANCE_MIN,
|
|
FIRSTPERSON_TO_THIRDPERSON_VERTICAL_TOLERANCE_MAX,
|
|
0.0f, 1.0f );
|
|
|
|
vecInputEyePos.z = Lerp( flLerp, vecInputEyePos.z, vecHeadPos.z );
|
|
|
|
}
|
|
|
|
#ifdef CLIENT_DLL
|
|
if ( cl_camera_height_restriction_debug.GetBool() )
|
|
{
|
|
Vector vecTemp = Vector( vecInputEyePos.x, vecInputEyePos.y, vecInputEyePos.z );
|
|
debugoverlay->AddLineOverlay( vecTemp, vecTemp + Vector(0,20,0), 0, 255, 0, true, gpGlobals->frametime );
|
|
}
|
|
|
|
// when fully crouched on the client, floor the camera height under the input z within a vertical range so it doesn't 'bob' continuously
|
|
if ( m_flAnimDuckAmount >= 1 )
|
|
{
|
|
if ( m_bSmoothHeightValid )
|
|
{
|
|
float flHardCamHeight = m_pPlayer->GetAbsOrigin().z + VEC_DUCK_VIEW.z;
|
|
|
|
if ( vecInputEyePos.z < flHardCamHeight )
|
|
{
|
|
m_flCameraSmoothHeight = clamp( MIN( m_flCameraSmoothHeight, vecInputEyePos.z ), vecInputEyePos.z - clamp( flHardCamHeight - vecInputEyePos.z, 0.0f, 3.0f ), vecInputEyePos.z );
|
|
vecInputEyePos.z = m_flCameraSmoothHeight;
|
|
}
|
|
else
|
|
{
|
|
m_flCameraSmoothHeight = flHardCamHeight;
|
|
}
|
|
|
|
if ( cl_camera_height_restriction_debug.GetBool() )
|
|
{
|
|
Vector vecTemp = Vector( vecInputEyePos.x, vecInputEyePos.y, vecInputEyePos.z );
|
|
debugoverlay->AddLineOverlay( vecTemp, vecTemp + Vector(0,30,0), 255, 255, 255, true, gpGlobals->frametime );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
m_flCameraSmoothHeight = vecInputEyePos.z;
|
|
m_bSmoothHeightValid = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_bSmoothHeightValid = false;
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
void CCSGOPlayerAnimState::OnClientWeaponChange( CWeaponCSBase* pCurrentWeapon )
|
|
{
|
|
if ( !m_bFirstRunSinceInit && ( pCurrentWeapon != m_pWeaponLastBoneSetup ) &&
|
|
!m_pPlayer->IsAnyBoneSnapshotPending() &&
|
|
m_pPlayer->m_boneSnapshots[ BONESNAPSHOT_UPPER_BODY ].GetCurrentWeight() <= 0 &&
|
|
m_pPlayer->m_boneSnapshots[ BONESNAPSHOT_ENTIRE_BODY ].GetCurrentWeight() <= 0 )
|
|
{
|
|
m_pWeaponLastBoneSetup = pCurrentWeapon;
|
|
if ( m_flSpeedAsPortionOfWalkTopSpeed > 0.25f )
|
|
{
|
|
m_pPlayer->m_boneSnapshots[ BONESNAPSHOT_UPPER_BODY ].SetShouldCapture( bonesnapshot_get( cl_bonesnapshot_speed_weaponchange ) );
|
|
}
|
|
else
|
|
{
|
|
m_pPlayer->m_boneSnapshots[ BONESNAPSHOT_ENTIRE_BODY ].SetShouldCapture( bonesnapshot_get( cl_bonesnapshot_speed_weaponchange ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
inline bool CCSGOPlayerAnimState::LayerToIndex( const CAnimationLayer* pLayer, int &nIndex )
|
|
{
|
|
for ( int i=0; i < ANIMATION_LAYER_COUNT; i++ )
|
|
{
|
|
if ( pLayer == m_pPlayer->GetAnimOverlay( m_pLayerOrderPreset[i], USE_ANIMLAYER_RAW_INDEX ) )
|
|
{
|
|
nIndex = i;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::NotifyOnLayerChangeSequence( const CAnimationLayer* pLayer, const int nNewSequence )
|
|
{
|
|
int nIndex;
|
|
if ( !LayerToIndex( pLayer, nIndex ) )
|
|
return;
|
|
animstate_layer_t nLayerIndex = (animstate_layer_t)nIndex;
|
|
|
|
//todo: more hooks for pre-bonesetup sequence changes
|
|
|
|
// bone snapshot land/climb transitions
|
|
if ( !m_bFirstRunSinceInit && nLayerIndex == ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB )
|
|
{
|
|
int nLastSequence = pLayer->GetSequence();
|
|
if ( nLastSequence > 0 && nLastSequence != nNewSequence )
|
|
{
|
|
if ( m_pPlayer->GetSequenceActivity( nLastSequence ) == ACT_CSGO_CLIMB_LADDER )
|
|
{
|
|
m_pPlayer->m_boneSnapshots[BONESNAPSHOT_ENTIRE_BODY].SetShouldCapture( bonesnapshot_get( cl_bonesnapshot_speed_ladderexit ) );
|
|
}
|
|
else if ( m_pPlayer->GetSequenceActivity( nNewSequence ) == ACT_CSGO_CLIMB_LADDER )
|
|
{
|
|
m_pPlayer->m_boneSnapshots[BONESNAPSHOT_ENTIRE_BODY].SetShouldCapture( bonesnapshot_get( cl_bonesnapshot_speed_ladderenter ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::NotifyOnLayerChangeWeight( const CAnimationLayer* pLayer, const float flNewWeight )
|
|
{
|
|
int nIndex;
|
|
if ( !LayerToIndex( pLayer, nIndex ) )
|
|
return;
|
|
animstate_layer_t nLayerIndex = (animstate_layer_t)nIndex;
|
|
|
|
//todo: more hooks for pre-bonesetup sequence changes
|
|
|
|
if ( nLayerIndex == ANIMATION_LAYER_MOVEMENT_MOVE )
|
|
{
|
|
m_flMoveWeight = flNewWeight;
|
|
}
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::NotifyOnLayerChangeCycle( const CAnimationLayer* pLayer, const float flNewcycle )
|
|
{
|
|
int nIndex;
|
|
if ( !LayerToIndex( pLayer, nIndex ) )
|
|
return;
|
|
animstate_layer_t nLayerIndex = (animstate_layer_t)nIndex;
|
|
|
|
//todo: more hooks for pre-bonesetup sequence changes
|
|
|
|
if ( nLayerIndex == ANIMATION_LAYER_MOVEMENT_MOVE )
|
|
{
|
|
m_flPrimaryCycle = flNewcycle;
|
|
}
|
|
}
|
|
|
|
inline float CCSGOPlayerAnimState::FootBarrierEq( float flIn, float flMinWidth )
|
|
{
|
|
return (( Sqr(flIn) * 0.02f ) + MIN( flMinWidth, 3 )) * MIN( m_flSpeedAsPortionOfCrouchTopSpeed, 1 );
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::DoProceduralFootPlant( matrix3x4a_t boneToWorld[], mstudioikchain_t *pLeftFootChain, mstudioikchain_t *pRightFootChain, BoneVector pos[] )
|
|
{
|
|
if ( !m_pPlayer )
|
|
return;
|
|
|
|
if ( m_bOnGround && m_flInAirSmoothValue >= 1 && !m_bOnLadder )
|
|
{
|
|
|
|
if ( m_pPlayer->GetGroundEntity() && !m_pPlayer->GetGroundEntity()->IsWorld() )
|
|
return;
|
|
|
|
int nLeftFootBoneIndex = pLeftFootChain->pLink( 2 )->bone;
|
|
int nRightFootBoneIndex = pRightFootChain->pLink( 2 )->bone;
|
|
|
|
int nLeftLockIndex = m_pPlayer->LookupBone( "lfoot_lock" );
|
|
int nRightLockIndex = m_pPlayer->LookupBone( "rfoot_lock" );
|
|
|
|
if ( nLeftLockIndex > 0 && nRightLockIndex > 0 )
|
|
{
|
|
m_footLeft.m_vecPosAnim = boneToWorld[nLeftFootBoneIndex].GetOrigin();
|
|
m_footRight.m_vecPosAnim = boneToWorld[nRightFootBoneIndex].GetOrigin();
|
|
|
|
//debugoverlay->AddBoxOverlay( m_footLeft.m_vecPosAnim, Vector(-5,-5,0), Vector(5,5,0), QAngle(0,0,0), 255, 0, 0, 0, 0 );
|
|
//debugoverlay->AddBoxOverlay( m_footRight.m_vecPosAnim, Vector(-5,-5,0), Vector(5,5,0), QAngle(0,0,0), 255, 0, 0, 0, 0 );
|
|
|
|
Vector vecPlayerOrigin = m_pPlayer->GetAbsOrigin();
|
|
|
|
// reset the procedural locations if this is the first computation or they haven't been computed recently
|
|
if ( m_bFirstFootPlantSinceInit || abs(gpGlobals->framecount - m_iLastUpdateFrame) > 10 )
|
|
{
|
|
m_footLeft.Init( m_footLeft.m_vecPosAnim );
|
|
m_footRight.Init( m_footRight.m_vecPosAnim );
|
|
|
|
m_footLeft.m_vecPlantVel = m_vecVelocityNormalizedNonZero;
|
|
m_footRight.m_vecPlantVel = m_vecVelocityNormalizedNonZero;
|
|
|
|
m_iLastUpdateFrame = gpGlobals->framecount;
|
|
m_bFirstFootPlantSinceInit = false;
|
|
return;
|
|
}
|
|
|
|
// use the ik lock driver bones to determine if a new foot plant location has been triggered
|
|
|
|
#define FOOT_LOCK_THRESHOLD 0.7f
|
|
|
|
bool bLeftFootLockWasBelowThreshold = m_footLeft.m_flLockAmount < FOOT_LOCK_THRESHOLD;
|
|
bool bRightFootLockWasBelowThreshold = m_footRight.m_flLockAmount < FOOT_LOCK_THRESHOLD;
|
|
|
|
m_footLeft.m_flLockAmount = Approach( clamp( abs(pos[nLeftLockIndex].y), 0, 1 ), m_footLeft.m_flLockAmount, gpGlobals->frametime * 10.0f );
|
|
m_footRight.m_flLockAmount = Approach( clamp( abs(pos[nRightLockIndex].y), 0, 1 ), m_footRight.m_flLockAmount, gpGlobals->frametime * 10.0f );
|
|
|
|
bool bLeftFootLockIsAboveThreshold = m_footLeft.m_flLockAmount >= FOOT_LOCK_THRESHOLD;
|
|
bool bRightFootLockIsAboveThreshold = m_footRight.m_flLockAmount >= FOOT_LOCK_THRESHOLD;
|
|
|
|
// for a short duration after each foot-plant, the feet are not allowed to deviate laterally from the player velocity at the time of the plant
|
|
|
|
float flLeftTimeLerp = RemapValClamped( m_footLeft.m_flLastPlantTime, gpGlobals->curtime, gpGlobals->curtime-0.4f, 1.0f, 0.0f );
|
|
float flRightTimeLerp = RemapValClamped( m_footRight.m_flLastPlantTime, gpGlobals->curtime, gpGlobals->curtime-0.4f, 1.0f, 0.0f );
|
|
|
|
flLeftTimeLerp = Gain( flLeftTimeLerp, 0.8f );
|
|
flRightTimeLerp = Gain( flRightTimeLerp, 0.8f );
|
|
|
|
//debugoverlay->AddBoxOverlay( m_footLeft.m_vecPosPlant, Vector(-5,-5,0)*flLeftTimeLerp, Vector(5,5,0)*flLeftTimeLerp, QAngle(0,0,0), 0, 255, 255, 0, 0 );
|
|
//debugoverlay->AddBoxOverlay( m_footRight.m_vecPosPlant, Vector(-5,-5,0)*flRightTimeLerp, Vector(5,5,0)*flRightTimeLerp, QAngle(0,0,0), 255, 255, 0, 0, 0 );
|
|
|
|
Vector vecLeftPtOnVelLine;
|
|
Vector vecRightPtOnVelLine;
|
|
|
|
CalcClosestPointOnLine( m_footLeft.m_vecPosAnim, m_footLeft.m_vecPosPlant, m_footLeft.m_vecPosPlant + m_footLeft.m_vecPlantVel, vecLeftPtOnVelLine );
|
|
CalcClosestPointOnLine( m_footRight.m_vecPosAnim, m_footRight.m_vecPosPlant, m_footRight.m_vecPosPlant + m_footRight.m_vecPlantVel, vecRightPtOnVelLine );
|
|
|
|
Vector vecLeftTarget = Lerp( flLeftTimeLerp, m_footLeft.m_vecPosAnim, vecLeftPtOnVelLine );
|
|
Vector vecRightTarget = Lerp( flRightTimeLerp, m_footRight.m_vecPosAnim, vecRightPtOnVelLine );
|
|
|
|
|
|
// check for foot plants for next frame
|
|
|
|
if ( bLeftFootLockIsAboveThreshold && bLeftFootLockWasBelowThreshold )
|
|
{
|
|
m_footLeft.m_vecPosPlant = vecLeftTarget;
|
|
m_footLeft.m_flLastPlantTime = gpGlobals->curtime;
|
|
m_footLeft.m_vecPlantVel = m_vecVelocityNormalizedNonZero;
|
|
}
|
|
|
|
if ( bRightFootLockIsAboveThreshold && bRightFootLockWasBelowThreshold )
|
|
{
|
|
m_footRight.m_vecPosPlant = vecRightTarget;
|
|
m_footRight.m_flLastPlantTime = gpGlobals->curtime;
|
|
m_footRight.m_vecPlantVel = m_vecVelocityNormalizedNonZero;
|
|
}
|
|
|
|
// always inherit animated z (only foot-stepping below modifies this)
|
|
vecLeftTarget.z = m_footLeft.m_vecPosAnim.z;
|
|
vecRightTarget.z = m_footRight.m_vecPosAnim.z;
|
|
|
|
|
|
// the feet are not allowed to exceed 3x the instantaneous velocity of the player (prevents pose-popping when mashing keys)
|
|
|
|
float flFrametime = MAX( gpGlobals->frametime, 0.0001f );
|
|
float flLeftDeltaVel = MAX( ( m_footLeft.m_vecPosAnimLast - vecLeftTarget ).Length() / flFrametime, 0.0001f );
|
|
float flRightDeltaVel = MAX( ( m_footRight.m_vecPosAnimLast - vecRightTarget ).Length() / flFrametime, 0.0001f );
|
|
|
|
float flVelLimitLeft = m_flVelocityLengthXY * 3.0f;
|
|
float flVelLimitRight = m_flVelocityLengthXY * 3.0f;
|
|
|
|
if ( m_flVelocityLengthXY > 10.0f )
|
|
{
|
|
m_flLastTimeVelocityOverTen = gpGlobals->curtime;
|
|
}
|
|
float flTimeStill = gpGlobals->curtime - m_flLastTimeVelocityOverTen;
|
|
|
|
// when standing mostly still, alternately raise the velocity limit floor, which makes the feet 'shuffle-step'
|
|
if ( m_flVelocityLengthXY <= 10.0f )
|
|
{
|
|
|
|
float flEyeFootAngleDiff = abs( AngleDiff(m_flEyeYaw, m_flFootYaw) );
|
|
if ( flEyeFootAngleDiff > 56.0f || flTimeStill < 1.0f ) // when turning rapidly, allow the feet to step faster
|
|
{
|
|
float flFmod = fmod( gpGlobals->curtime, 0.33f );
|
|
if ( flFmod < 0.16f )
|
|
{
|
|
flVelLimitLeft = 110.0f;
|
|
}
|
|
else if ( flFmod >= 0.17f )
|
|
{
|
|
flVelLimitRight = 130.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float flFmod = fmod( gpGlobals->curtime, 0.66f );
|
|
if ( flFmod > 0.02 && flFmod < 0.31f )
|
|
{
|
|
flVelLimitLeft = 80.0f;
|
|
}
|
|
else if ( flFmod > 0.35 && flFmod < 0.64f )
|
|
{
|
|
flVelLimitRight = 70.0f;
|
|
}
|
|
}
|
|
|
|
// less fudge-factor when crouching, so allow the feet to catch up faster
|
|
if ( m_flAnimDuckAmount > 0.5f )
|
|
{
|
|
flVelLimitLeft *= 2.0f;
|
|
flVelLimitRight *= 2.0f;
|
|
}
|
|
|
|
// also lift the feet a bit on their way to their target
|
|
//if ( flVelLimitLeft > 0 )
|
|
//{
|
|
// float flDistToLeft = RemapValClamped( m_footLeft.m_vecPosAnimLast.DistTo( m_footLeft.m_vecPosAnim ), 0.0f, 30.0f, 0.0f, 1.0f );
|
|
// vecLeftTarget.z += SmoothCurve( flDistToLeft ) * 14.0f;
|
|
//}
|
|
//if ( flVelLimitRight> 0 )
|
|
//{
|
|
// float flDistToRight = RemapValClamped( m_footRight.m_vecPosAnimLast.DistTo( m_footRight.m_vecPosAnim ), 0.0f, 30.0f, 0.0f, 1.0f );
|
|
// vecRightTarget.z += SmoothCurve( flDistToRight ) * 14.0f;
|
|
//}
|
|
|
|
}
|
|
|
|
// restrict foot velocity
|
|
|
|
if ( flLeftDeltaVel > flVelLimitLeft )
|
|
vecLeftTarget = Lerp( flVelLimitLeft / flLeftDeltaVel, m_footLeft.m_vecPosAnimLast, vecLeftTarget );
|
|
|
|
if ( flRightDeltaVel > flVelLimitRight )
|
|
vecRightTarget = Lerp( flVelLimitRight / flRightDeltaVel, m_footRight.m_vecPosAnimLast, vecRightTarget );
|
|
|
|
|
|
// spawn oddities like lowering the player artificially without allowing them to fall can move the player
|
|
// but it doesn't count as a teleport, so their velocity remains zero... long story short this causes the
|
|
// target z values to catch up instead of reset and it looks weird. I'm clamping their range here:
|
|
vecLeftTarget.z = clamp( vecLeftTarget.z, m_footLeft.m_vecPosAnim.z - 2.0f, m_footLeft.m_vecPosAnim.z + 6.0f );
|
|
vecRightTarget.z = clamp( vecRightTarget.z, m_footRight.m_vecPosAnim.z - 2.0f, m_footRight.m_vecPosAnim.z + 6.0f );
|
|
|
|
|
|
// sanity-check the result and throw out the positions if they're super weird
|
|
// (we might have been teleported or pvs went nuts or there was a bunch of packet loss -
|
|
// point being it's easier and more reliable to error check than try to prevent the input cases.
|
|
|
|
int nLeftHipBoneIndex = pLeftFootChain->pLink( 0 )->bone;
|
|
int nRightHipBoneIndex = pRightFootChain->pLink( 0 )->bone;
|
|
if ( nLeftHipBoneIndex > 0 && nRightHipBoneIndex > 0 )
|
|
{
|
|
if ( boneToWorld[nLeftHipBoneIndex].GetOrigin().DistToSqr( vecLeftTarget ) > 1400 ||
|
|
boneToWorld[nRightHipBoneIndex].GetOrigin().DistToSqr( vecRightTarget ) > 1400 )
|
|
{
|
|
|
|
//debugoverlay->AddLineOverlay( boneToWorld[nLeftHipBoneIndex].GetOrigin(), vecLeftTarget, 255,0,0, true, 5 );
|
|
//debugoverlay->AddLineOverlay( boneToWorld[nRightHipBoneIndex].GetOrigin(), vecRightTarget, 255,0,0, true, 5 );
|
|
|
|
// if either foot is way out of range, bail and reset them both
|
|
m_footLeft.Init( m_footLeft.m_vecPosAnim );
|
|
m_footRight.Init( m_footRight.m_vecPosAnim );
|
|
|
|
m_footLeft.m_vecPlantVel = m_vecVelocityNormalizedNonZero;
|
|
m_footRight.m_vecPlantVel = m_vecVelocityNormalizedNonZero;
|
|
|
|
m_iLastUpdateFrame = gpGlobals->framecount;
|
|
m_bFirstFootPlantSinceInit = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( vecLeftTarget.DistToSqr( m_footLeft.m_vecPosAnim ) > (12 * 12) )
|
|
{
|
|
vecLeftTarget = m_footLeft.m_vecPosAnim + (( vecLeftTarget - m_footLeft.m_vecPosAnim ).Normalized() * 12.0f);
|
|
//debugoverlay->AddLineOverlay( vecLeftTarget, m_footLeft.m_vecPosAnim, 0, 255, 0, true, 0 );
|
|
}
|
|
|
|
if ( vecRightTarget.DistToSqr( m_footRight.m_vecPosAnim ) > (12 * 12) )
|
|
{
|
|
vecRightTarget = m_footRight.m_vecPosAnim + ((vecRightTarget - m_footRight.m_vecPosAnim).Normalized() * 12.0f);
|
|
//debugoverlay->AddLineOverlay( vecRightTarget, m_footRight.m_vecPosAnim, 0, 255, 0, true, 0 );
|
|
}
|
|
|
|
// place the foot bones at the newly computed locations (ik will solve the knees)
|
|
|
|
boneToWorld[nLeftFootBoneIndex].SetOrigin( vecLeftTarget );
|
|
boneToWorld[nRightFootBoneIndex].SetOrigin( vecRightTarget );
|
|
|
|
//debugoverlay->AddBoxOverlay( vecLeftTarget, Vector(-5,-5,0), Vector(5,5,0), QAngle(0,0,0), 0, 255, 0, 0, 0 );
|
|
//debugoverlay->AddBoxOverlay(vecRightTarget, Vector(-5,-5,0), Vector(5,5,0), QAngle(0,0,0), 0, 255, 0, 0, 0 );
|
|
|
|
m_footLeft.m_vecPosAnimLast = vecLeftTarget;
|
|
m_footRight.m_vecPosAnimLast = vecRightTarget;
|
|
|
|
}
|
|
|
|
if ( m_iLastUpdateFrame < gpGlobals->framecount )
|
|
{
|
|
m_iLastUpdateFrame = gpGlobals->framecount;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CCSGOPlayerAnimState::SetUpLean( void )
|
|
{
|
|
// lean the body into velocity derivative (acceleration) to simulate maintaining a center of gravity
|
|
float flInterval = gpGlobals->curtime - m_flLastVelocityTestTime;
|
|
if ( flInterval > 0.025f )
|
|
{
|
|
flInterval = MIN( flInterval, 0.1f );
|
|
m_flLastVelocityTestTime = gpGlobals->curtime;
|
|
|
|
m_vecTargetAcceleration = ( m_pPlayer->GetLocalVelocity() - m_vecVelocityLast ) / flInterval;
|
|
m_vecTargetAcceleration.z = 0;
|
|
|
|
m_vecVelocityLast = m_pPlayer->GetLocalVelocity();
|
|
}
|
|
|
|
m_vecAcceleration = Approach( m_vecTargetAcceleration, m_vecAcceleration, m_flLastUpdateIncrement * 800.0f );
|
|
|
|
//#ifdef CLIENT_DLL
|
|
// debugoverlay->AddLineOverlay( m_vecPositionCurrent, m_vecPositionCurrent + m_vecAcceleration, 255,0,0, 255, 1, m_flLastUpdateIncrement );
|
|
//#else
|
|
// debugoverlay->AddLineOverlay( m_vecPositionCurrent, m_vecPositionCurrent + m_vecAcceleration, 0,0,255, 255, 1.5, m_flLastUpdateIncrement );
|
|
//#endif
|
|
|
|
QAngle temp;
|
|
VectorAngles( m_vecAcceleration, Vector(0,0,1), temp );
|
|
|
|
m_flAccelerationWeight = clamp( (m_vecAcceleration.Length() / CS_PLAYER_SPEED_RUN) * m_flSpeedAsPortionOfRunTopSpeed, 0, 1 );
|
|
m_flAccelerationWeight *= (1.0f - m_flLadderWeight);
|
|
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_LEAN_YAW ].SetValue( m_pPlayer, AngleNormalize( m_flFootYaw - temp[YAW] ) );
|
|
|
|
if ( GetLayerSequence( ANIMATION_LAYER_LEAN ) <= 0 )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
SetLayerSequence( ANIMATION_LAYER_LEAN, m_pPlayer->LookupSequence( "lean" ) );
|
|
}
|
|
|
|
SetLayerWeight( ANIMATION_LAYER_LEAN, m_flAccelerationWeight );
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::SetUpFlinch( void )
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
if ( m_flTimeOfLastKnownInjury < m_pPlayer->GetTimeOfLastInjury() )
|
|
{
|
|
m_flTimeOfLastKnownInjury = m_pPlayer->GetTimeOfLastInjury();
|
|
|
|
// flinches override flinches of their own priority
|
|
bool bNoFlinchIsPlaying = ( IsLayerSequenceCompleted( ANIMATION_LAYER_FLINCH ) || GetLayerWeight( ANIMATION_LAYER_FLINCH ) <= 0 );
|
|
bool bHeadshotIsPlaying = ( !bNoFlinchIsPlaying && GetLayerActivity(ANIMATION_LAYER_FLINCH) == ACT_CSGO_FLINCH_HEAD );
|
|
|
|
if ( m_pPlayer->GetLastDamageTypeFlags() & DMG_BURN )
|
|
{
|
|
if ( bNoFlinchIsPlaying )
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_FLINCH, SelectSequenceFromActMods( ACT_CSGO_FLINCH_MOLOTOV ) );
|
|
|
|
// clear out all the flinch-related actmods now we selected a sequence
|
|
UpdateActivityModifiers();
|
|
}
|
|
}
|
|
else if ( bNoFlinchIsPlaying || !bHeadshotIsPlaying || m_pPlayer->LastHitGroup() == HITGROUP_HEAD )
|
|
{
|
|
RelativeDamagedDirection_t damageDir = m_pPlayer->GetLastInjuryRelativeDirection();
|
|
bool bLeft = false;
|
|
bool bRight = false;
|
|
switch (damageDir) {
|
|
case DAMAGED_DIR_NONE:
|
|
case DAMAGED_DIR_FRONT:
|
|
{
|
|
AddActivityModifier( "front" );
|
|
break;
|
|
}
|
|
case DAMAGED_DIR_BACK:
|
|
{
|
|
AddActivityModifier( "back" );
|
|
break;
|
|
}
|
|
case DAMAGED_DIR_LEFT:
|
|
{
|
|
AddActivityModifier( "left" );
|
|
bLeft = true;
|
|
break;
|
|
}
|
|
case DAMAGED_DIR_RIGHT:
|
|
{
|
|
AddActivityModifier( "right" );
|
|
bRight = true;
|
|
break;
|
|
}
|
|
}
|
|
switch (m_pPlayer->LastHitGroup()) {
|
|
case HITGROUP_HEAD:
|
|
{
|
|
AddActivityModifier( "head" );
|
|
break;
|
|
}
|
|
case HITGROUP_CHEST:
|
|
{
|
|
AddActivityModifier( "chest" );
|
|
break;
|
|
}
|
|
case HITGROUP_LEFTARM:
|
|
{
|
|
if ( !bLeft ) { AddActivityModifier( "left" ); }
|
|
AddActivityModifier( "arm" );
|
|
break;
|
|
}
|
|
case HITGROUP_RIGHTARM:
|
|
{
|
|
if ( !bRight ) { AddActivityModifier( "right" ); }
|
|
AddActivityModifier( "arm" );
|
|
break;
|
|
}
|
|
case HITGROUP_GENERIC:
|
|
case HITGROUP_STOMACH:
|
|
{
|
|
AddActivityModifier( "gut" );
|
|
break;
|
|
}
|
|
case HITGROUP_LEFTLEG:
|
|
{
|
|
if ( !bLeft ) { AddActivityModifier( "left" ); }
|
|
AddActivityModifier( "leg" );
|
|
break;
|
|
}
|
|
case HITGROUP_RIGHTLEG:
|
|
{
|
|
if ( !bRight ) { AddActivityModifier( "right" ); }
|
|
AddActivityModifier( "leg" );
|
|
break;
|
|
}
|
|
}
|
|
SetLayerSequence( ANIMATION_LAYER_FLINCH, SelectSequenceFromActMods( (m_pPlayer->LastHitGroup() == HITGROUP_HEAD) ? ACT_CSGO_FLINCH_HEAD : ACT_CSGO_FLINCH ) );
|
|
|
|
// clear out all the flinch-related actmods now we selected a sequence
|
|
UpdateActivityModifiers();
|
|
}
|
|
|
|
}
|
|
|
|
if ( GetLayerSequence( ANIMATION_LAYER_FLINCH ) > 0 )
|
|
{
|
|
SetLayerWeight( ANIMATION_LAYER_FLINCH, GetLayerIdealWeightFromSeqCycle( ANIMATION_LAYER_FLINCH ) );
|
|
}
|
|
else
|
|
{
|
|
SetLayerWeight( ANIMATION_LAYER_FLINCH, 0 );
|
|
}
|
|
|
|
#endif
|
|
|
|
IncrementLayerCycle( ANIMATION_LAYER_FLINCH, false );
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::SetUpFlashedReaction( void )
|
|
{
|
|
animstate_layer_t nLayer = ANIMATION_LAYER_FLASHED;
|
|
|
|
#ifndef CLIENT_DLL
|
|
|
|
if ( m_flFlashedAmountEaseOutEnd < gpGlobals->curtime )
|
|
{
|
|
SetLayerWeight( nLayer, 0 );
|
|
m_bFlashed = false;
|
|
}
|
|
else
|
|
{
|
|
|
|
if ( !m_bFlashed )
|
|
{
|
|
SetLayerSequence( nLayer, SelectSequenceFromActMods( ACT_CSGO_FLASHBANG_REACTION ) );
|
|
m_bFlashed = true;
|
|
}
|
|
|
|
float flFlashedAmount = RemapValClamped( gpGlobals->curtime, m_flFlashedAmountEaseOutStart, m_flFlashedAmountEaseOutEnd, 1, 0 );
|
|
|
|
// TODO: make flashed anims look nicer by using a cycle, like the old ones
|
|
//SetLayerCycle( nLayer, 1.0f - flFlashedAmount );
|
|
|
|
SetLayerCycle( nLayer, 0 );
|
|
SetLayerRate( nLayer, 0 );
|
|
|
|
float flWeightPrevious = GetLayerWeight( nLayer );
|
|
float flWeightNew = SimpleSpline(flFlashedAmount);
|
|
|
|
SetLayerWeight( nLayer, flWeightNew );
|
|
SetLayerWeightRate( nLayer, (flWeightNew >= flWeightPrevious) ? 0 : flWeightPrevious );
|
|
}
|
|
|
|
#else
|
|
|
|
if ( GetLayerWeight( nLayer ) > 0 )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayer, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer && pLayer->GetWeightDeltaRate() < 0 )
|
|
IncrementLayerWeight( nLayer );
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
#define CSGO_ANIM_BOMBPLANT_ABORT_SPEED 12.0f
|
|
#define CSGO_ANIM_DEFUSE_ABORT_SPEED 8.0f
|
|
#define CSGO_ANIM_TWITCH_ABORT_SPEED 6.0f
|
|
#define CSGO_ANIM_BOMBPLANT_BLEND_RATE 1.2f
|
|
void CCSGOPlayerAnimState::SetUpWholeBodyAction( void )
|
|
{
|
|
animstate_layer_t nLayer = ANIMATION_LAYER_WHOLE_BODY;
|
|
|
|
#ifndef CLIENT_DLL
|
|
|
|
// Whole body anims are for custom events that typically take over the entire body of the player.
|
|
// At the moment, they're used for idle twitches in freeze time, defusing and bomb-planting,
|
|
// and probably taunts in the near future.
|
|
|
|
|
|
//if ( !m_bDefuseStarted &&
|
|
// !m_bPlantAnimStarted &&
|
|
// m_flNextTwitchTime <= gpGlobals->curtime &&
|
|
// AreTwitchesAllowed() )
|
|
//{
|
|
// // roll for a twitch anim
|
|
// bool bFirstInit = ( m_flNextTwitchTime == 0 );
|
|
// m_flNextTwitchTime = gpGlobals->curtime + RandomFloat( 8, 16 );
|
|
//
|
|
// if ( !bFirstInit && !m_bTwitchAnimStarted && m_bOnGround )
|
|
// {
|
|
// // start the twitch anim
|
|
// SetLayerSequence( nLayer, SelectSequenceFromActMods( ACT_CSGO_TWITCH_BUYZONE ) );
|
|
// m_bTwitchAnimStarted = true;
|
|
// }
|
|
//}
|
|
|
|
|
|
if ( m_pPlayer->GetTeamNumber() == TEAM_CT && m_pPlayer->m_bIsDefusing ) // should be defusing
|
|
{
|
|
if ( !m_bDefuseStarted )
|
|
{
|
|
// we are now defusing and need to start the anim
|
|
SetLayerSequence( nLayer, SelectSequenceFromActMods( m_pPlayer->HasDefuser() ? ACT_CSGO_DEFUSE_WITH_KIT : ACT_CSGO_DEFUSE ) );
|
|
m_bDefuseStarted = true;
|
|
}
|
|
else
|
|
{
|
|
IncrementLayerCycleWeightRateGeneric(nLayer);
|
|
}
|
|
}
|
|
else if ( GetLayerActivity( nLayer ) == ACT_CSGO_DEFUSE || GetLayerActivity( nLayer ) == ACT_CSGO_DEFUSE_WITH_KIT )
|
|
{
|
|
// should NOT be defusing but IS
|
|
if ( GetLayerWeight( nLayer ) > 0 )
|
|
{
|
|
float flCurrentWeight = GetLayerWeight( nLayer );
|
|
SetLayerWeight( nLayer, Approach( 0, flCurrentWeight, m_flLastUpdateIncrement * CSGO_ANIM_DEFUSE_ABORT_SPEED ) );
|
|
SetLayerWeightRate( nLayer, flCurrentWeight );
|
|
}
|
|
m_bDefuseStarted = false;
|
|
}
|
|
else if ( GetLayerActivity( nLayer ) == ACT_CSGO_PLANT_BOMB ) // is planting
|
|
{
|
|
if ( m_pWeapon && !m_pWeapon->IsA( WEAPON_C4 ) )
|
|
m_bPlantAnimStarted = false; // cancel planting if we're not holding c4
|
|
|
|
if ( m_bPlantAnimStarted ) // plant in progress
|
|
{
|
|
// Inlined IncrementLayerCycleWeightRateGeneric() so we can tune the layering weight when crouch-planting the bomb.
|
|
//
|
|
// Instead of setting the weight to GetLayerIdealWeightFromSeqCycle, we approach that value which smoothly blends to
|
|
// the bomb plant animation. This means that the 'standing' part of the animation doesn't get overly blended to when
|
|
// planting the animation from a crouched position. In addition, if you are in thirdperson camera, the crouch is
|
|
// predicted but the plant animation is server-side. So we do this blend regardless of the crouch state because it
|
|
// could differ between the client and the server.
|
|
//
|
|
// This does mean that fine detail in the beginning of the plant animation is lost. Fortunately there isn't much of
|
|
// that at the moment.
|
|
float flWeightPrevious = GetLayerWeight( nLayer );
|
|
IncrementLayerCycle( nLayer, false );
|
|
SetLayerWeight( nLayer, Approach( GetLayerIdealWeightFromSeqCycle( nLayer ), flWeightPrevious, m_flLastUpdateIncrement * CSGO_ANIM_BOMBPLANT_BLEND_RATE ) );
|
|
SetLayerWeightRate( nLayer, flWeightPrevious );
|
|
|
|
m_bPlantAnimStarted = !( IsLayerSequenceCompleted( nLayer ) );
|
|
}
|
|
else
|
|
{
|
|
if ( GetLayerWeight( nLayer ) > 0 )
|
|
{
|
|
//bomb plant aborted, pull out the weight
|
|
float flCurrentWeight = GetLayerWeight( nLayer );
|
|
SetLayerWeight( nLayer, Approach( 0, flCurrentWeight, m_flLastUpdateIncrement * CSGO_ANIM_BOMBPLANT_ABORT_SPEED ) );
|
|
SetLayerWeightRate( nLayer, flCurrentWeight );
|
|
}
|
|
m_bPlantAnimStarted = false;
|
|
}
|
|
}
|
|
//else if ( GetLayerActivity( nLayer ) == ACT_CSGO_TWITCH_BUYZONE || GetLayerActivity( nLayer ) == ACT_CSGO_TWITCH ) // twitching
|
|
//{
|
|
// if ( m_pWeapon && m_pWeapon != m_pWeaponLast )
|
|
// m_bTwitchAnimStarted = false; // cancel twitches if weapon changes
|
|
//
|
|
// if ( m_bTwitchAnimStarted && AreTwitchesAllowed() )
|
|
// {
|
|
// IncrementLayerCycleWeightRateGeneric(nLayer);
|
|
// m_bTwitchAnimStarted = !( IsLayerSequenceCompleted( nLayer ) );
|
|
// }
|
|
// else
|
|
// {
|
|
// if ( GetLayerWeight( nLayer ) > 0 )
|
|
// {
|
|
// float flCurrentWeight = GetLayerWeight( nLayer );
|
|
// SetLayerWeight( nLayer, Approach( 0, flCurrentWeight, m_flLastUpdateIncrement * CSGO_ANIM_TWITCH_ABORT_SPEED ) );
|
|
// SetLayerWeightRate( nLayer, flCurrentWeight );
|
|
// }
|
|
// m_bTwitchAnimStarted = false;
|
|
// }
|
|
//}
|
|
else
|
|
{
|
|
// fallback
|
|
SetLayerCycle( nLayer, 0.999f );
|
|
SetLayerWeight( nLayer, 0 );
|
|
SetLayerWeightRate( nLayer, 0 );
|
|
}
|
|
|
|
#else
|
|
|
|
if ( GetLayerWeight( nLayer ) > 0 )
|
|
{
|
|
IncrementLayerCycle( nLayer, false );
|
|
IncrementLayerWeight( nLayer );
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::SetUpAliveloop( void )
|
|
{
|
|
animstate_layer_t nLayer = ANIMATION_LAYER_ALIVELOOP;
|
|
|
|
#ifndef CLIENT_DLL
|
|
if ( GetLayerActivity( nLayer ) != ACT_CSGO_ALIVE_LOOP )
|
|
{
|
|
// first time init
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
SetLayerSequence( nLayer, SelectSequenceFromActMods( ACT_CSGO_ALIVE_LOOP ) );
|
|
SetLayerCycle( nLayer, RandomFloat( 0, 1 ) );
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayer, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
{
|
|
float flNewRate = m_pPlayer->GetSequenceCycleRate( m_pPlayer->GetModelPtr(), pLayer->GetSequence() );
|
|
flNewRate *= RandomFloat( 0.8f, 1.1f );
|
|
SetLayerRate( nLayer, flNewRate );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( m_pWeapon && m_pWeapon != m_pWeaponLast )
|
|
{
|
|
//re-roll act on weapon change
|
|
float flRetainCycle = GetLayerCycle( nLayer );
|
|
SetLayerSequence( nLayer, SelectSequenceFromActMods( ACT_CSGO_ALIVE_LOOP ) );
|
|
SetLayerCycle( nLayer, flRetainCycle );
|
|
}
|
|
else if ( IsLayerSequenceCompleted( nLayer ) )
|
|
{
|
|
//re-roll rate
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayer, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
{
|
|
float flNewRate = m_pPlayer->GetSequenceCycleRate( m_pPlayer->GetModelPtr(), pLayer->GetSequence() );
|
|
flNewRate *= RandomFloat( 0.8f, 1.1f );
|
|
SetLayerRate( nLayer, flNewRate );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float flWeightOutPoseBreaker = RemapValClamped( m_flSpeedAsPortionOfRunTopSpeed, 0.55f, 0.9f, 1.0f, 0.0f );
|
|
SetLayerWeight( nLayer, flWeightOutPoseBreaker );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
IncrementLayerCycle( nLayer, true );
|
|
//SetLayerWeight( nLayer, 1 );
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::SetUpWeaponAction( void )
|
|
{
|
|
animstate_layer_t nLayer = ANIMATION_LAYER_WEAPON_ACTION;
|
|
|
|
#ifndef CLIENT_DLL
|
|
|
|
bool bDoIncrement = true;
|
|
|
|
if ( m_pWeapon && m_bDeployRateLimiting && GetLayerActivity( nLayer ) == ACT_CSGO_DEPLOY )
|
|
{
|
|
m_pWeapon->ShowWeaponWorldModel( false );
|
|
|
|
if ( GetLayerCycle( nLayer ) >= CSGO_ANIM_DEPLOY_RATELIMIT )
|
|
{
|
|
m_bDeployRateLimiting = false;
|
|
SetLayerSequence( nLayer, SelectSequenceFromActMods( ACT_CSGO_DEPLOY ) );
|
|
//SetLayerRate( nLayer, GetLayerRate( nLayer ) * 1.5f );
|
|
SetLayerWeight( nLayer, 0 );
|
|
bDoIncrement = false;
|
|
}
|
|
}
|
|
|
|
// fixme: this is a hack to fix all-body weapon actions that need to transition into crouch or stand poses they weren't built for.
|
|
// This only matters for idle animation - the move layer is itself a kind of 're-crouch' and 're-stand' layer itself.
|
|
|
|
if ( m_nAnimstateModelVersion < 2 )
|
|
{
|
|
// old re-crouch behavior
|
|
|
|
// fixme: this is a hack to fix the all-body weapon action that wants to crouch case. There's no fix for the crouching all-body action that wants to stand
|
|
if ( m_flAnimDuckAmount > 0 && GetLayerWeight(nLayer) > 0 && !LayerSequenceHasActMod( nLayer, "crouch" ) )
|
|
{
|
|
if ( GetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION_RECROUCH ) <= 0 )
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION_RECROUCH, m_pPlayer->LookupSequence( "recrouch_generic" ) );
|
|
SetLayerWeight( ANIMATION_LAYER_WEAPON_ACTION_RECROUCH, GetLayerWeight(nLayer) * m_flAnimDuckAmount );
|
|
}
|
|
else
|
|
{
|
|
SetLayerWeight( ANIMATION_LAYER_WEAPON_ACTION_RECROUCH, 0 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// newer version with "re-stand" blended into the re-crouch.
|
|
|
|
float flTargetRecrouchWeight = 0;
|
|
if ( GetLayerWeight(nLayer) > 0 )
|
|
{
|
|
if ( GetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION_RECROUCH ) <= 0 )
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION_RECROUCH, m_pPlayer->LookupSequence( "recrouch_generic" ) );
|
|
|
|
if ( LayerSequenceHasActMod( nLayer, "crouch" ) )
|
|
{
|
|
// this is a crouching anim. It might be the only anim available, or it's just lasted long enough that the
|
|
// player stood up after it started. If we're standing up at all, we need to force the stand pose artificially.
|
|
if ( m_flAnimDuckAmount < 1 )
|
|
flTargetRecrouchWeight = GetLayerWeight(nLayer) * (1.0f - m_flAnimDuckAmount);
|
|
}
|
|
else
|
|
{
|
|
// this is NOT a crouching anim. Still if it's not a whole-body anim it might work fine when crouched though,
|
|
// and not actually need the re-crouch. How to detect this?
|
|
|
|
// We can't trust this anim to crouch the player since it's not tagged as a crouch anim. So we need to force the
|
|
// crouch pose artificially.
|
|
if ( m_flAnimDuckAmount > 0 )
|
|
flTargetRecrouchWeight = GetLayerWeight(nLayer) * m_flAnimDuckAmount;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( GetLayerWeight( ANIMATION_LAYER_WEAPON_ACTION_RECROUCH ) > 0 )
|
|
flTargetRecrouchWeight = Approach( 0, GetLayerWeight( ANIMATION_LAYER_WEAPON_ACTION_RECROUCH ), m_flLastUpdateIncrement * 4 );
|
|
}
|
|
SetLayerWeight( ANIMATION_LAYER_WEAPON_ACTION_RECROUCH, flTargetRecrouchWeight );
|
|
}
|
|
|
|
if ( bDoIncrement )
|
|
{
|
|
// increment the action
|
|
IncrementLayerCycle( nLayer, false );
|
|
float flWeightPrev = GetLayerWeight( nLayer );
|
|
float flDesiredWeight = GetLayerIdealWeightFromSeqCycle( nLayer );
|
|
|
|
SetLayerWeight( nLayer, flDesiredWeight );
|
|
SetLayerWeightRate( nLayer, flWeightPrev );
|
|
}
|
|
|
|
#else
|
|
|
|
// client
|
|
|
|
if ( GetLayerWeight( nLayer ) > 0 )
|
|
{
|
|
IncrementLayerCycle( nLayer, false );
|
|
IncrementLayerWeight( nLayer );
|
|
}
|
|
|
|
#endif
|
|
|
|
// set weapon sequence and cycle so dispatched events hit
|
|
CAnimationLayer *pWeaponLayer = m_pPlayer->GetAnimOverlay( nLayer, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pWeaponLayer && m_pWeapon )
|
|
{
|
|
CBaseWeaponWorldModel *pWeaponWorldModel = m_pWeapon->m_hWeaponWorldModel.Get();
|
|
if ( pWeaponWorldModel )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
if ( pWeaponLayer->m_nDispatchedDst > 0 && pWeaponLayer->GetWeight() > 0 ) // fixme: does the weight check make 0-frame events fail? Added a check below just in case.
|
|
{
|
|
pWeaponWorldModel->SetSequence( pWeaponLayer->m_nDispatchedDst );
|
|
pWeaponWorldModel->SetCycle( pWeaponLayer->GetCycle() );
|
|
pWeaponWorldModel->SetPlaybackRate( pWeaponLayer->GetPlaybackRate() );
|
|
#ifndef CLIENT_DLL
|
|
pWeaponWorldModel->DispatchAnimEvents( pWeaponWorldModel );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
if ( pWeaponWorldModel->GetCycle() != 0 )
|
|
pWeaponWorldModel->DispatchAnimEvents( pWeaponWorldModel );
|
|
#endif
|
|
pWeaponWorldModel->SetSequence( 0 );
|
|
pWeaponWorldModel->SetCycle( 0 );
|
|
pWeaponWorldModel->SetPlaybackRate( 0 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#define CSGO_ANIM_WALK_TO_RUN_TRANSITION_SPEED 2.0f
|
|
#define CSGO_ANIM_ONGROUND_FUZZY_APPROACH 8.0f
|
|
#define CSGO_ANIM_ONGROUND_FUZZY_APPROACH_CROUCH 16.0f
|
|
#define CSGO_ANIM_LADDER_CLIMB_COVERAGE 100.0f
|
|
#define CSGO_ANIM_RUN_ANIM_PLAYBACK_MULTIPLIER 0.85f
|
|
void CCSGOPlayerAnimState::SetUpMovement( void )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
if ( m_flWalkToRunTransition > 0 && m_flWalkToRunTransition < 1 )
|
|
{
|
|
//currently transitioning between walk and run
|
|
if ( m_bWalkToRunTransitionState == ANIM_TRANSITION_WALK_TO_RUN )
|
|
{
|
|
m_flWalkToRunTransition += m_flLastUpdateIncrement * CSGO_ANIM_WALK_TO_RUN_TRANSITION_SPEED;
|
|
}
|
|
else // m_bWalkToRunTransitionState == ANIM_TRANSITION_RUN_TO_WALK
|
|
{
|
|
m_flWalkToRunTransition -= m_flLastUpdateIncrement * CSGO_ANIM_WALK_TO_RUN_TRANSITION_SPEED;
|
|
}
|
|
m_flWalkToRunTransition = clamp( m_flWalkToRunTransition, 0, 1 );
|
|
}
|
|
|
|
if ( m_flVelocityLengthXY > (CS_PLAYER_SPEED_RUN*CS_PLAYER_SPEED_WALK_MODIFIER) && m_bWalkToRunTransitionState == ANIM_TRANSITION_RUN_TO_WALK )
|
|
{
|
|
//crossed the walk to run threshold
|
|
m_bWalkToRunTransitionState = ANIM_TRANSITION_WALK_TO_RUN;
|
|
m_flWalkToRunTransition = MAX( 0.01f, m_flWalkToRunTransition );
|
|
}
|
|
else if ( m_flVelocityLengthXY < (CS_PLAYER_SPEED_RUN*CS_PLAYER_SPEED_WALK_MODIFIER) && m_bWalkToRunTransitionState == ANIM_TRANSITION_WALK_TO_RUN )
|
|
{
|
|
//crossed the run to walk threshold
|
|
m_bWalkToRunTransitionState = ANIM_TRANSITION_RUN_TO_WALK;
|
|
m_flWalkToRunTransition = MIN( 0.99f, m_flWalkToRunTransition );
|
|
}
|
|
|
|
if ( m_nAnimstateModelVersion < 2 )
|
|
{
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_RUN ].SetValue( m_pPlayer, m_flWalkToRunTransition );
|
|
}
|
|
else
|
|
{
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_MOVE_BLEND_WALK ].SetValue( m_pPlayer, (1.0f - m_flWalkToRunTransition) * (1.0f - m_flAnimDuckAmount) );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_MOVE_BLEND_RUN ].SetValue( m_pPlayer, (m_flWalkToRunTransition) * (1.0f - m_flAnimDuckAmount) );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_MOVE_BLEND_CROUCH_WALK ].SetValue( m_pPlayer, m_flAnimDuckAmount );
|
|
}
|
|
|
|
char szWeaponMoveSeq[MAX_ANIMSTATE_ANIMNAME_CHARS];
|
|
V_sprintf_safe( szWeaponMoveSeq, "move_%s", GetWeaponPrefix() );
|
|
|
|
int nWeaponMoveSeq = m_pPlayer->LookupSequence( szWeaponMoveSeq );
|
|
if ( nWeaponMoveSeq == -1 )
|
|
{
|
|
nWeaponMoveSeq = m_pPlayer->LookupSequence( "move" );
|
|
}
|
|
Assert( nWeaponMoveSeq > 0 );
|
|
|
|
|
|
if ( m_pPlayer->m_iMoveState != m_nPreviousMoveState )
|
|
{
|
|
m_flStutterStep += 10;
|
|
}
|
|
m_nPreviousMoveState = m_pPlayer->m_iMoveState;
|
|
m_flStutterStep = clamp( Approach( 0, m_flStutterStep, m_flLastUpdateIncrement * 40 ), 0, 100 );
|
|
|
|
// recompute moveweight
|
|
|
|
float flTargetMoveWeight = Lerp( m_flAnimDuckAmount, clamp(m_flSpeedAsPortionOfWalkTopSpeed, 0, 1), clamp(m_flSpeedAsPortionOfCrouchTopSpeed, 0, 1) );
|
|
//flTargetMoveWeight *= RemapValClamped( m_flStutterStep, 90, 100, 1, 0 );
|
|
|
|
if ( m_flMoveWeight <= flTargetMoveWeight )
|
|
{
|
|
m_flMoveWeight = flTargetMoveWeight;
|
|
}
|
|
else
|
|
{
|
|
m_flMoveWeight = Approach( flTargetMoveWeight, m_flMoveWeight, m_flLastUpdateIncrement * RemapValClamped( m_flStutterStep, 0.0f, 100.0f, 2, 20 ) );
|
|
}
|
|
|
|
Vector vecMoveYawDir;
|
|
AngleVectors( QAngle(0, AngleNormalize( m_flFootYaw + m_flMoveYaw + 180 ), 0), &vecMoveYawDir );
|
|
float flYawDeltaAbsDot = abs( DotProduct( m_vecVelocityNormalizedNonZero, vecMoveYawDir ) );
|
|
m_flMoveWeight *= Bias( flYawDeltaAbsDot, 0.2 );
|
|
|
|
float flMoveWeightWithAirSmooth = m_flMoveWeight * m_flInAirSmoothValue;
|
|
|
|
// dampen move weight for landings
|
|
flMoveWeightWithAirSmooth *= MAX( (1.0f - GetLayerWeight( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB ) ), 0.55f );
|
|
|
|
float flMoveCycleRate = 0;
|
|
if ( m_flVelocityLengthXY > 0 )
|
|
{
|
|
flMoveCycleRate = m_pPlayer->GetSequenceCycleRate( m_pPlayer->GetModelPtr(), nWeaponMoveSeq );
|
|
float flSequenceGroundSpeed = MAX( m_pPlayer->GetSequenceMoveDist( m_pPlayer->GetModelPtr(), nWeaponMoveSeq ) / ( 1.0f / flMoveCycleRate ), 0.001f );
|
|
flMoveCycleRate *= m_flVelocityLengthXY / flSequenceGroundSpeed;
|
|
|
|
flMoveCycleRate *= Lerp( m_flWalkToRunTransition, 1.0f, CSGO_ANIM_RUN_ANIM_PLAYBACK_MULTIPLIER );
|
|
}
|
|
|
|
float flLocalCycleIncrement = (flMoveCycleRate * m_flLastUpdateIncrement);
|
|
m_flPrimaryCycle = ClampCycle( m_flPrimaryCycle + flLocalCycleIncrement );
|
|
|
|
flMoveWeightWithAirSmooth = clamp( flMoveWeightWithAirSmooth, 0, 1 );
|
|
UpdateAnimLayer( ANIMATION_LAYER_MOVEMENT_MOVE, nWeaponMoveSeq, flLocalCycleIncrement, flMoveWeightWithAirSmooth, m_flPrimaryCycle );
|
|
|
|
|
|
#ifndef CLIENT_DLL
|
|
// blend in a strafe direction-change pose when the player changes strafe dir
|
|
|
|
// get the user's left and right button pressed states
|
|
bool moveRight = ( m_pPlayer->m_nButtons & ( IN_MOVERIGHT ) ) != 0;
|
|
bool moveLeft = ( m_pPlayer->m_nButtons & ( IN_MOVELEFT ) ) != 0;
|
|
bool moveForward = ( m_pPlayer->m_nButtons & ( IN_FORWARD ) ) != 0;
|
|
bool moveBackward = ( m_pPlayer->m_nButtons & ( IN_BACK ) ) != 0;
|
|
|
|
//Vector vForward, vRight;
|
|
//AngleVectors( QAngle(0,m_flEyeYaw,0), &vForward, &vRight, NULL );
|
|
//vForward *= 10;
|
|
//vRight *= 10;
|
|
//if ( moveRight )
|
|
// debugoverlay->AddTriangleOverlay( m_vecPositionCurrent + vRight * 2, m_vecPositionCurrent - vForward, m_vecPositionCurrent + vForward, 200, 0, 0, 255, true, 0 );
|
|
//if ( moveLeft )
|
|
// debugoverlay->AddTriangleOverlay( m_vecPositionCurrent - vRight * 2, m_vecPositionCurrent + vForward, m_vecPositionCurrent - vForward, 200, 0, 0, 255, true, 0 );
|
|
//if ( moveForward )
|
|
// debugoverlay->AddTriangleOverlay( m_vecPositionCurrent + vForward * 2, m_vecPositionCurrent + vRight, m_vecPositionCurrent - vRight, 200, 0, 0, 255, true, 0 );
|
|
//if ( moveBackward )
|
|
// debugoverlay->AddTriangleOverlay( m_vecPositionCurrent - vForward * 2, m_vecPositionCurrent + vRight, m_vecPositionCurrent - vRight, 200, 0, 0, 255, true, 0 );
|
|
|
|
Vector vecForward;
|
|
Vector vecRight;
|
|
AngleVectors( QAngle(0,m_flFootYaw,0), &vecForward, &vecRight, NULL );
|
|
vecRight.NormalizeInPlace();
|
|
float flVelToRightDot = DotProduct( m_vecVelocityNormalizedNonZero, vecRight );
|
|
float flVelToForwardDot = DotProduct( m_vecVelocityNormalizedNonZero, vecForward );
|
|
|
|
// We're interested in if the player's desired direction (indicated by their held buttons) is opposite their current velocity.
|
|
// This indicates a strafing direction change in progress.
|
|
|
|
bool bStrafeRight = ( m_flSpeedAsPortionOfWalkTopSpeed >= 0.73f && moveRight && !moveLeft && flVelToRightDot < -0.63f );
|
|
bool bStrafeLeft = ( m_flSpeedAsPortionOfWalkTopSpeed >= 0.73f && moveLeft && !moveRight && flVelToRightDot > 0.63f );
|
|
bool bStrafeForward = ( m_flSpeedAsPortionOfWalkTopSpeed >= 0.65f && moveForward && !moveBackward && flVelToForwardDot < -0.55f );
|
|
bool bStrafeBackward = ( m_flSpeedAsPortionOfWalkTopSpeed >= 0.65f && moveBackward && !moveForward && flVelToForwardDot > 0.55f );
|
|
|
|
m_pPlayer->m_bStrafing = ( bStrafeRight || bStrafeLeft || bStrafeForward || bStrafeBackward );
|
|
#endif
|
|
|
|
if ( m_pPlayer->m_bStrafing )
|
|
{
|
|
if ( !m_bStrafeChanging )
|
|
{
|
|
m_flDurationStrafing = 0;
|
|
|
|
#ifdef CLIENT_DLL
|
|
if ( !m_bFirstRunSinceInit && !m_bStrafeChanging && m_bOnGround && m_pPlayer->m_boneSnapshots[BONESNAPSHOT_UPPER_BODY].GetCurrentWeight() <= 0 )
|
|
{
|
|
m_pPlayer->m_boneSnapshots[BONESNAPSHOT_UPPER_BODY].SetShouldCapture( bonesnapshot_get( cl_bonesnapshot_speed_strafestart ) );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
m_bStrafeChanging = true;
|
|
|
|
m_flStrafeChangeWeight = Approach( 1, m_flStrafeChangeWeight, m_flLastUpdateIncrement * 20 );
|
|
m_flStrafeChangeCycle = Approach( 0, m_flStrafeChangeCycle, m_flLastUpdateIncrement * 10 );
|
|
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_STRAFE_DIR ].SetValue( m_pPlayer, AngleNormalize( m_flMoveYaw ) );
|
|
|
|
//float flCross = m_tPoseParamMappings[ PLAYER_POSE_PARAM_STRAFE_CROSS ].GetValue( m_pPlayer );
|
|
//flCross = clamp( Approach( m_bFeetCrossed ? 1 : 0, flCross, m_flLastUpdateIncrement * 10 ), 0, 1);
|
|
//m_tPoseParamMappings[ PLAYER_POSE_PARAM_STRAFE_CROSS ].SetValue( m_pPlayer, flCross );
|
|
//m_tPoseParamMappings[ PLAYER_POSE_PARAM_STRAFE_CROSS ].SetValue( m_pPlayer, 0 );
|
|
|
|
}
|
|
else if ( m_flStrafeChangeWeight > 0 )
|
|
{
|
|
m_flDurationStrafing += m_flLastUpdateIncrement;
|
|
|
|
if ( m_flDurationStrafing > 0.08f )
|
|
m_flStrafeChangeWeight = Approach( 0, m_flStrafeChangeWeight, m_flLastUpdateIncrement * 5 );
|
|
|
|
m_nStrafeSequence = m_pPlayer->LookupSequence( "strafe" );
|
|
float flRate = m_pPlayer->GetSequenceCycleRate( m_pPlayer->GetModelPtr(), m_nStrafeSequence );
|
|
m_flStrafeChangeCycle = clamp( m_flStrafeChangeCycle + m_flLastUpdateIncrement * flRate, 0, 1 );
|
|
}
|
|
|
|
if ( m_flStrafeChangeWeight <= 0 )
|
|
{
|
|
m_bStrafeChanging = false;
|
|
}
|
|
|
|
|
|
|
|
// keep track of if the player is on the ground, and if the player has just touched or left the ground since the last check
|
|
bool bPreviousGroundState = m_bOnGround;
|
|
m_bOnGround = ( m_pPlayer->GetFlags() & FL_ONGROUND );
|
|
|
|
m_bLandedOnGroundThisFrame = ( !m_bFirstRunSinceInit && bPreviousGroundState != m_bOnGround && m_bOnGround );
|
|
m_bLeftTheGroundThisFrame = ( bPreviousGroundState != m_bOnGround && !m_bOnGround );
|
|
|
|
float flDistanceFell = 0;
|
|
if ( m_bLeftTheGroundThisFrame )
|
|
{
|
|
m_flLeftGroundHeight = m_vecPositionCurrent.z;
|
|
}
|
|
|
|
if ( m_bLandedOnGroundThisFrame )
|
|
{
|
|
flDistanceFell = abs( m_flLeftGroundHeight - m_vecPositionCurrent.z );
|
|
float flDistanceFallNormalizedBiasRange = Bias( RemapValClamped( flDistanceFell, 12.0f, 72.0f, 0.0f, 1.0f ), 0.4f );
|
|
|
|
//Msg( "Fell %f units, ratio is %f. ", flDistanceFell, flDistanceFallNormalizedBiasRange );
|
|
//Msg( "Fell for %f secs, multiplier is %f\n", m_flDurationInAir, m_flLandAnimMultiplier );
|
|
|
|
m_flLandAnimMultiplier = clamp( Bias( m_flDurationInAir, 0.3f ), 0.1f, 1.0f );
|
|
m_flDuckAdditional = MAX( m_flLandAnimMultiplier, flDistanceFallNormalizedBiasRange );
|
|
|
|
//Msg( "m_flDuckAdditional is %f\n", m_flDuckAdditional );
|
|
}
|
|
else
|
|
{
|
|
m_flDuckAdditional = Approach( 0, m_flDuckAdditional, m_flLastUpdateIncrement * 2 );
|
|
}
|
|
|
|
// the in-air smooth value is a fuzzier representation of if the player is on the ground or not.
|
|
// It will approach 1 when the player is on the ground and 0 when in the air. Useful for blending jump animations.
|
|
m_flInAirSmoothValue = Approach( m_bOnGround ? 1 : 0, m_flInAirSmoothValue, Lerp( m_flAnimDuckAmount, CSGO_ANIM_ONGROUND_FUZZY_APPROACH, CSGO_ANIM_ONGROUND_FUZZY_APPROACH_CROUCH ) * m_flLastUpdateIncrement );
|
|
m_flInAirSmoothValue = clamp( m_flInAirSmoothValue, 0, 1 );
|
|
|
|
|
|
|
|
m_flStrafeChangeWeight *= ( 1.0f - m_flAnimDuckAmount );
|
|
m_flStrafeChangeWeight *= m_flInAirSmoothValue;
|
|
m_flStrafeChangeWeight = clamp( m_flStrafeChangeWeight, 0, 1 );
|
|
|
|
//if ( m_flStrafeChangeWeight > 0 && flMoveWeightWithAirSmooth <= 0.01f )
|
|
//{
|
|
// if ( flStrafePose > 0.5f )
|
|
// {
|
|
// m_flPrimaryCycle = m_pPlayer->GetFirstSequenceAnimTag( nWeaponMoveSeq, ANIMTAG_STARTCYCLE_W, 0, 1 );
|
|
// }
|
|
// else
|
|
// {
|
|
// m_flPrimaryCycle = m_pPlayer->GetFirstSequenceAnimTag( nWeaponMoveSeq, ANIMTAG_STARTCYCLE_E, 0, 1 );
|
|
// }
|
|
//}
|
|
|
|
if ( m_nStrafeSequence != -1 )
|
|
UpdateAnimLayer( ANIMATION_LAYER_MOVEMENT_STRAFECHANGE, m_nStrafeSequence, 0, m_flStrafeChangeWeight, m_flStrafeChangeCycle );
|
|
|
|
|
|
|
|
//ladders
|
|
bool bPreviouslyOnLadder = m_bOnLadder;
|
|
m_bOnLadder = !m_bOnGround && m_pPlayer->GetMoveType() == MOVETYPE_LADDER;
|
|
bool bStartedLadderingThisFrame = ( !bPreviouslyOnLadder && m_bOnLadder );
|
|
bool bStoppedLadderingThisFrame = ( bPreviouslyOnLadder && !m_bOnLadder );
|
|
|
|
|
|
if ( bStartedLadderingThisFrame || bStoppedLadderingThisFrame )
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
//m_footLeft.m_flLateralWeight = 0;
|
|
//m_footRight.m_flLateralWeight = 0;
|
|
#endif
|
|
}
|
|
|
|
if ( m_flLadderWeight > 0 || m_bOnLadder )
|
|
{
|
|
|
|
#ifndef CLIENT_DLL
|
|
if ( bStartedLadderingThisFrame )
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB, SelectSequenceFromActMods( ACT_CSGO_CLIMB_LADDER ) );
|
|
}
|
|
#endif
|
|
|
|
if ( abs(m_flVelocityLengthZ) > 100 )
|
|
{
|
|
m_flLadderSpeed = Approach( 1, m_flLadderSpeed, m_flLastUpdateIncrement * 10.0f );
|
|
}
|
|
else
|
|
{
|
|
m_flLadderSpeed = Approach( 0, m_flLadderSpeed, m_flLastUpdateIncrement * 10.0f );
|
|
}
|
|
m_flLadderSpeed = clamp( m_flLadderSpeed, 0, 1 );
|
|
|
|
if ( m_bOnLadder )
|
|
{
|
|
m_flLadderWeight = Approach( 1, m_flLadderWeight, m_flLastUpdateIncrement * 5.0f );
|
|
}
|
|
else
|
|
{
|
|
m_flLadderWeight = Approach( 0, m_flLadderWeight, m_flLastUpdateIncrement * 10.0f );
|
|
}
|
|
m_flLadderWeight = clamp( m_flLadderWeight, 0, 1 );
|
|
|
|
Vector vecLadderNormal = m_pPlayer->GetLadderNormal();
|
|
QAngle angLadder;
|
|
VectorAngles( vecLadderNormal, angLadder );
|
|
float flLadderYaw = AngleDiff( angLadder.y, m_flFootYaw );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_LADDER_YAW ].SetValue( m_pPlayer, flLadderYaw );
|
|
|
|
//float flPlayerZ = m_pPlayer->GetAbsOrigin().z;
|
|
//float flLadderClimbCycle = fmod( abs(flPlayerZ), 80.0f ) / 80.0f;
|
|
//flLadderClimbCycle = ClampCycle( flPlayerZ < 0 ? (1.0f - flLadderClimbCycle) : flLadderClimbCycle );
|
|
|
|
|
|
float flLadderClimbCycle = GetLayerCycle( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB );
|
|
flLadderClimbCycle += (m_vecPositionCurrent.z - m_vecPositionLast.z) * Lerp( m_flLadderSpeed, 0.010f, 0.004f );
|
|
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_LADDER_SPEED ].SetValue( m_pPlayer, m_flLadderSpeed );
|
|
|
|
if ( GetLayerActivity( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB ) == ACT_CSGO_CLIMB_LADDER )
|
|
{
|
|
SetLayerWeight( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB, m_flLadderWeight );
|
|
}
|
|
|
|
SetLayerCycle( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB, flLadderClimbCycle );
|
|
|
|
// fade out jump if we're climbing
|
|
if ( m_bOnLadder )
|
|
{
|
|
float flIdealJumpWeight = 1.0f - m_flLadderWeight;
|
|
if ( GetLayerWeight( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL ) > flIdealJumpWeight )
|
|
{
|
|
SetLayerWeight( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL, flIdealJumpWeight );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_flLadderSpeed = 0;
|
|
}
|
|
|
|
|
|
//jumping
|
|
if ( m_bOnGround )
|
|
{
|
|
if ( !m_bLanding && (m_bLandedOnGroundThisFrame || bStoppedLadderingThisFrame) )
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
SetLayerSequence( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB, SelectSequenceFromActMods( (m_flDurationInAir>1) ? ACT_CSGO_LAND_HEAVY : ACT_CSGO_LAND_LIGHT ) );
|
|
#endif
|
|
SetLayerCycle( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB, 0 );
|
|
m_bLanding = true;
|
|
}
|
|
m_flDurationInAir = 0;
|
|
|
|
if ( m_bLanding && GetLayerActivity(ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB) != ACT_CSGO_CLIMB_LADDER )
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
m_bJumping = false;
|
|
#endif
|
|
|
|
IncrementLayerCycle( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB, false );
|
|
IncrementLayerCycle( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL, false );
|
|
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_JUMP_FALL ].SetValue( m_pPlayer, 0 );
|
|
|
|
if ( IsLayerSequenceCompleted( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB ) )
|
|
{
|
|
m_bLanding = false;
|
|
SetLayerWeight( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB, 0 );
|
|
//SetLayerRate( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB, 1.0f );
|
|
SetLayerWeight( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL, 0 );
|
|
m_flLandAnimMultiplier = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
|
|
float flLandWeight = GetLayerIdealWeightFromSeqCycle( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB ) * m_flLandAnimMultiplier;
|
|
|
|
// if we hit the ground crouched, reduce the land animation as a function of crouch, since the land animations move the head up a bit ( and this is undesirable )
|
|
flLandWeight *= clamp( (1.0f - m_flAnimDuckAmount), 0.2f, 1.0f );
|
|
|
|
SetLayerWeight( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB, flLandWeight );
|
|
|
|
// fade out jump because land is taking over
|
|
float flCurrentJumpFallWeight = GetLayerWeight( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL );
|
|
if ( flCurrentJumpFallWeight > 0 )
|
|
{
|
|
flCurrentJumpFallWeight = Approach( 0, flCurrentJumpFallWeight, m_flLastUpdateIncrement * 10.0f );
|
|
SetLayerWeight( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL, flCurrentJumpFallWeight );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
if ( !m_bLanding && !m_bJumping && m_flLadderWeight <= 0 )
|
|
{
|
|
SetLayerWeight( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB, 0 );
|
|
}
|
|
#endif
|
|
}
|
|
else if ( !m_bOnLadder )
|
|
{
|
|
m_bLanding = false;
|
|
|
|
// we're in the air
|
|
if ( m_bLeftTheGroundThisFrame || bStoppedLadderingThisFrame )
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
// If entered the air by jumping, then we already set the jump activity.
|
|
// But if we're in the air because we strolled off a ledge or the floor collapsed or something,
|
|
// we need to set the fall activity here.
|
|
if ( !m_bJumping )
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL, SelectSequenceFromActMods( ACT_CSGO_FALL ) );
|
|
}
|
|
#endif
|
|
m_flDurationInAir = 0;
|
|
}
|
|
|
|
m_flDurationInAir += m_flLastUpdateIncrement;
|
|
|
|
//#ifndef CLIENT_DLL
|
|
// Msg( "%f\n", m_flDurationInAir );
|
|
//#endif
|
|
|
|
IncrementLayerCycle( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL, false );
|
|
|
|
// increase jump weight
|
|
float flJumpWeight = GetLayerWeight( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL );
|
|
float flNextJumpWeight = GetLayerIdealWeightFromSeqCycle( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL );
|
|
if ( flNextJumpWeight > flJumpWeight )
|
|
{
|
|
SetLayerWeight( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL, flNextJumpWeight );
|
|
}
|
|
|
|
// bash any lingering land weight to zero
|
|
float flLingeringLandWeight = GetLayerWeight( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB );
|
|
if ( flLingeringLandWeight > 0 )
|
|
{
|
|
flLingeringLandWeight *= smoothstep_bounds( 0.2f, 0.0f, m_flDurationInAir );
|
|
SetLayerWeight( ANIMATION_LAYER_MOVEMENT_LAND_OR_CLIMB, flLingeringLandWeight );
|
|
}
|
|
|
|
// blend jump into fall. This is a no-op if we're playing a fall anim.
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_JUMP_FALL ].SetValue( m_pPlayer, clamp(smoothstep_bounds( 0.72f, 1.52f, m_flDurationInAir ),0,1) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Activity CCSGOPlayerAnimState::GetLayerActivity( animstate_layer_t nLayerIndex )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
return (Activity)GetSequenceActivity( m_pPlayer->GetModelPtr(), pLayer->GetSequence() );
|
|
return ACT_INVALID;
|
|
}
|
|
|
|
bool CCSGOPlayerAnimState::IsLayerSequenceCompleted( animstate_layer_t nLayerIndex )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
return ( (pLayer->GetCycle() + (m_flLastUpdateIncrement * pLayer->GetPlaybackRate())) >= 1 );
|
|
return false;
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::SetLayerRate( animstate_layer_t nLayerIndex, float flRate )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
pLayer->SetPlaybackRate( flRate );
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::SetLayerCycle( animstate_layer_t nLayerIndex, float flCycle )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
pLayer->SetCycle( ClampCycle( flCycle ) );
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::IncrementLayerCycleWeightRateGeneric( animstate_layer_t nLayerIndex )
|
|
{
|
|
float flWeightPrevious = GetLayerWeight( nLayerIndex );
|
|
IncrementLayerCycle( nLayerIndex, false );
|
|
SetLayerWeight( nLayerIndex, GetLayerIdealWeightFromSeqCycle( nLayerIndex ) );
|
|
SetLayerWeightRate( nLayerIndex, flWeightPrevious );
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::IncrementLayerCycle( animstate_layer_t nLayerIndex, bool bAllowLoop )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
|
|
if ( !pLayer )
|
|
return;
|
|
|
|
if ( abs(pLayer->GetPlaybackRate()) <= 0 )
|
|
return;
|
|
|
|
float flCurrentCycle = pLayer->GetCycle();
|
|
flCurrentCycle += m_flLastUpdateIncrement * pLayer->GetPlaybackRate();
|
|
|
|
if ( !bAllowLoop && flCurrentCycle >= 1 )
|
|
{
|
|
flCurrentCycle = 0.999f;
|
|
}
|
|
|
|
pLayer->SetCycle( ClampCycle( flCurrentCycle ) );
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::IncrementLayerWeight( animstate_layer_t nLayerIndex )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
|
|
if ( !pLayer )
|
|
return;
|
|
|
|
if ( abs(pLayer->GetWeightDeltaRate()) <= 0 )
|
|
return;
|
|
|
|
float flCurrentWeight = pLayer->GetWeight();
|
|
flCurrentWeight += m_flLastUpdateIncrement * pLayer->GetWeightDeltaRate();
|
|
flCurrentWeight = clamp( flCurrentWeight, 0, 1 );
|
|
|
|
pLayer->SetWeight( flCurrentWeight );
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::SetLayerSequence( animstate_layer_t nLayerIndex, int nSequence )
|
|
{
|
|
Assert( nSequence > 1 );
|
|
if ( nSequence > 1 )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
|
|
if ( !pLayer )
|
|
return;
|
|
|
|
pLayer->SetSequence( nSequence );
|
|
float flPlaybackRate = m_pPlayer->GetLayerSequenceCycleRate( pLayer, nSequence );
|
|
pLayer->SetPlaybackRate( flPlaybackRate );
|
|
pLayer->SetCycle( 0 );
|
|
pLayer->SetWeight( 0 );
|
|
UpdateLayerOrderPreset( nLayerIndex, nSequence );
|
|
#ifndef CLIENT_DLL
|
|
pLayer->m_fFlags |= ANIM_LAYER_ACTIVE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
float CCSGOPlayerAnimState::GetLayerIdealWeightFromSeqCycle( animstate_layer_t nLayerIndex )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
|
|
if ( !pLayer )
|
|
return 0;
|
|
|
|
mstudioseqdesc_t &seqdesc = m_pPlayer->GetModelPtr()->pSeqdesc( pLayer->GetSequence() );
|
|
|
|
float flCycle = pLayer->GetCycle();
|
|
if ( flCycle >= 0.999f )
|
|
flCycle = 1;
|
|
float flEaseIn = seqdesc.fadeintime;
|
|
float flEaseOut = seqdesc.fadeouttime;
|
|
float flIdealWeight = 1;
|
|
|
|
if ( flEaseIn > 0 && flCycle < flEaseIn )
|
|
{
|
|
flIdealWeight = smoothstep_bounds( 0, flEaseIn, flCycle );
|
|
}
|
|
else if ( flEaseOut < 1 && flCycle > flEaseOut )
|
|
{
|
|
flIdealWeight = smoothstep_bounds( 1.0f, flEaseOut, flCycle );
|
|
}
|
|
|
|
if ( flIdealWeight < 0.0015f )
|
|
return 0;
|
|
|
|
return (clamp( flIdealWeight, 0, 1));
|
|
}
|
|
|
|
float CCSGOPlayerAnimState::GetLayerCycle( animstate_layer_t nLayerIndex )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
return pLayer->GetCycle();
|
|
return 0;
|
|
}
|
|
|
|
int CCSGOPlayerAnimState::GetLayerSequence( animstate_layer_t nLayerIndex )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
return pLayer->GetSequence();
|
|
return 0;
|
|
}
|
|
|
|
float CCSGOPlayerAnimState::GetLayerRate( animstate_layer_t nLayerIndex )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
return pLayer->GetPlaybackRate();
|
|
return 0;
|
|
}
|
|
|
|
float CCSGOPlayerAnimState::GetLayerWeight( animstate_layer_t nLayerIndex )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
return pLayer->GetWeight();
|
|
return 0;
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::SetLayerWeight( animstate_layer_t nLayerIndex, float flWeight )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( !pLayer )
|
|
return;
|
|
pLayer->SetWeight( clamp( flWeight, 0, 1) );
|
|
#ifndef CLIENT_DLL
|
|
pLayer->m_fFlags |= ANIM_LAYER_ACTIVE;
|
|
#endif
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::SetLayerWeightRate( animstate_layer_t nLayerIndex, float flPrevious )
|
|
{
|
|
if ( m_flLastUpdateIncrement == 0 )
|
|
return;
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( !pLayer )
|
|
return;
|
|
float flNewRate = ( pLayer->GetWeight() - flPrevious ) / m_flLastUpdateIncrement;
|
|
pLayer->SetWeightDeltaRate( flNewRate );
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::UpdateAnimLayer( animstate_layer_t nLayerIndex, int nSequence, float flPlaybackRate, float flWeight, float flCycle )
|
|
{
|
|
AssertOnce( flWeight >= 0 && flWeight <= 1 );
|
|
AssertOnce( flCycle >= 0 && flCycle <= 1 );
|
|
Assert( nSequence > 1 );
|
|
|
|
Assert( nSequence > 1 );
|
|
if ( nSequence > 1 )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( !pLayer )
|
|
return;
|
|
pLayer->SetSequence( nSequence );
|
|
pLayer->SetPlaybackRate( flPlaybackRate );
|
|
pLayer->SetCycle( clamp( flCycle, 0, 1) );
|
|
pLayer->SetWeight( clamp( flWeight, 0, 1) );
|
|
UpdateLayerOrderPreset( nLayerIndex, nSequence );
|
|
#ifndef CLIENT_DLL
|
|
pLayer->m_fFlags |= ANIM_LAYER_ACTIVE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::ApplyLayerOrderPreset( animlayerpreset nNewPreset, bool bForce )
|
|
{
|
|
if ( !bForce && m_pLayerOrderPreset == nNewPreset )
|
|
return;
|
|
|
|
m_pLayerOrderPreset = nNewPreset;
|
|
|
|
for ( int i=0; i < ANIMATION_LAYER_COUNT; i++ )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( m_pLayerOrderPreset[i], USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pLayer )
|
|
{
|
|
pLayer->SetOrder( i );
|
|
|
|
// purge dispatch info too
|
|
pLayer->m_pDispatchedStudioHdr = NULL;
|
|
pLayer->m_nDispatchedSrc = ACT_INVALID;
|
|
pLayer->m_nDispatchedDst = ACT_INVALID;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::UpdateLayerOrderPreset( animstate_layer_t nLayerIndex, int nSequence )
|
|
{
|
|
if ( !m_pPlayer || nLayerIndex != ANIMATION_LAYER_WEAPON_ACTION )
|
|
return;
|
|
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
if ( m_pPlayer->GetAnySequenceAnimTag( nSequence, ANIMTAG_WEAPON_POSTLAYER, 0 ) != 0 )
|
|
{
|
|
ApplyLayerOrderPreset( get_animlayerpreset( WeaponPost ) );
|
|
}
|
|
else
|
|
{
|
|
ApplyLayerOrderPreset( get_animlayerpreset( Default ) );
|
|
}
|
|
}
|
|
|
|
bool CCSGOPlayerAnimState::LayerSequenceHasActMod( animstate_layer_t nLayerIndex, const char* szActMod )
|
|
{
|
|
CAnimationLayer *pLayer = m_pPlayer->GetAnimOverlay( nLayerIndex, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( !pLayer )
|
|
return false;
|
|
//CUtlSymbol sym = g_ActivityModifiersTable.Find( szActMod );
|
|
//if ( sym.IsValid() )
|
|
//{
|
|
mstudioseqdesc_t &seqdesc = m_pPlayer->GetModelPtr()->pSeqdesc( pLayer->GetSequence() );
|
|
for ( int i=0; i<seqdesc.numactivitymodifiers; i++ )
|
|
{
|
|
mstudioactivitymodifier_t *mod = seqdesc.pActivityModifier(i);
|
|
|
|
if ( !V_strcmp( mod->pszName(), szActMod ) )
|
|
return true;
|
|
}
|
|
//}
|
|
return false;
|
|
}
|
|
|
|
#define CSGO_ANIM_SPEED_TO_CHANGE_AIM_MATRIX 0.8f
|
|
#define CSGO_ANIM_SPEED_TO_CHANGE_AIM_MATRIX_SCOPED 4.2f
|
|
void CCSGOPlayerAnimState::SetUpAimMatrix( void )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
if ( m_flAnimDuckAmount <= 0 || m_flAnimDuckAmount >= 1 ) // only transition aim pose when fully ducked or fully standing
|
|
{
|
|
bool bPlayerIsWalking = ( m_pPlayer && m_pPlayer->m_bIsWalking );
|
|
bool bPlayerIsScoped = ( m_pPlayer && m_pPlayer->m_bIsScoped );
|
|
|
|
float flTransitionSpeed = m_flLastUpdateIncrement * ( bPlayerIsScoped ? CSGO_ANIM_SPEED_TO_CHANGE_AIM_MATRIX_SCOPED : CSGO_ANIM_SPEED_TO_CHANGE_AIM_MATRIX );
|
|
|
|
if ( bPlayerIsScoped ) // hacky: just tell all the transitions they've been invalid too long so all transitions clear as soon as the player starts scoping
|
|
{
|
|
m_tStandWalkAim.m_flDurationStateHasBeenInValid = m_tStandWalkAim.m_flHowLongToWaitUntilTransitionCanBlendOut;
|
|
m_tStandRunAim.m_flDurationStateHasBeenInValid = m_tStandRunAim.m_flHowLongToWaitUntilTransitionCanBlendOut;
|
|
m_tCrouchWalkAim.m_flDurationStateHasBeenInValid = m_tCrouchWalkAim.m_flHowLongToWaitUntilTransitionCanBlendOut;
|
|
}
|
|
|
|
m_tStandWalkAim.UpdateTransitionState( bPlayerIsWalking && !bPlayerIsScoped && m_flSpeedAsPortionOfWalkTopSpeed > 0.7f && m_flSpeedAsPortionOfRunTopSpeed < 0.7,
|
|
m_flLastUpdateIncrement, flTransitionSpeed );
|
|
|
|
m_tStandRunAim.UpdateTransitionState( !bPlayerIsScoped && m_flSpeedAsPortionOfRunTopSpeed >= 0.7,
|
|
m_flLastUpdateIncrement, flTransitionSpeed );
|
|
|
|
m_tCrouchWalkAim.UpdateTransitionState( !bPlayerIsScoped && m_flSpeedAsPortionOfCrouchTopSpeed >= 0.5,
|
|
m_flLastUpdateIncrement, flTransitionSpeed );
|
|
}
|
|
|
|
// Set aims to zero weight if they're underneath aims with 100% weight, for animation perf optimization.
|
|
// Also set aims to full weight if their overlapping aims aren't enough to cover them, because cross-fades don't sum to 100% weight.
|
|
|
|
float flStandIdleWeight = 1;
|
|
float flStandWalkWeight = m_tStandWalkAim.m_flBlendValue;
|
|
float flStandRunWeight = m_tStandRunAim.m_flBlendValue;
|
|
float flCrouchIdleWeight = 1;
|
|
float flCrouchWalkWeight = m_tCrouchWalkAim.m_flBlendValue;
|
|
|
|
if ( flStandWalkWeight >= 1 )
|
|
flStandIdleWeight = 0;
|
|
|
|
if ( flStandRunWeight >= 1 )
|
|
{
|
|
flStandIdleWeight = 0;
|
|
flStandWalkWeight = 0;
|
|
}
|
|
|
|
if ( flCrouchWalkWeight >= 1 )
|
|
flCrouchIdleWeight = 0;
|
|
|
|
if ( m_flAnimDuckAmount >= 1 )
|
|
{
|
|
flStandIdleWeight = 0;
|
|
flStandWalkWeight = 0;
|
|
flStandRunWeight = 0;
|
|
}
|
|
else if ( m_flAnimDuckAmount <= 0 )
|
|
{
|
|
flCrouchIdleWeight = 0;
|
|
flCrouchWalkWeight = 0;
|
|
}
|
|
|
|
float flOneMinusDuckAmount = 1.0f - m_flAnimDuckAmount;
|
|
|
|
flCrouchIdleWeight *= m_flAnimDuckAmount;
|
|
flCrouchWalkWeight *= m_flAnimDuckAmount;
|
|
flStandWalkWeight *= flOneMinusDuckAmount;
|
|
flStandRunWeight *= flOneMinusDuckAmount;
|
|
|
|
// make sure idle is present underneath cross-fades
|
|
if ( flCrouchIdleWeight < 1 && flCrouchWalkWeight < 1 && flStandWalkWeight < 1 && flStandRunWeight < 1 )
|
|
flStandIdleWeight = 1;
|
|
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_STAND_IDLE ].SetValue( m_pPlayer, flStandIdleWeight );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_STAND_WALK ].SetValue( m_pPlayer, flStandWalkWeight );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_STAND_RUN ].SetValue( m_pPlayer, flStandRunWeight );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_CROUCH_IDLE ].SetValue( m_pPlayer, flCrouchIdleWeight );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_CROUCH_WALK ].SetValue( m_pPlayer, flCrouchWalkWeight );
|
|
|
|
char szTransitionStandAimMatrix[MAX_ANIMSTATE_ANIMNAME_CHARS];
|
|
V_sprintf_safe( szTransitionStandAimMatrix, "%s_aim", GetWeaponPrefix() );
|
|
int nSeqStand = m_pPlayer->LookupSequence( szTransitionStandAimMatrix );
|
|
|
|
{
|
|
// use data-driven aim matrix limits
|
|
|
|
CAnimationLayer *pAimLayer = m_pPlayer->GetAnimOverlay( ANIMATION_LAYER_AIMMATRIX, USE_ANIMLAYER_RAW_INDEX );
|
|
if ( pAimLayer && m_pWeapon )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
CBaseAnimating *pAimMatrixHolder = m_pPlayer;
|
|
int nSeq = nSeqStand;
|
|
|
|
CBaseWeaponWorldModel *pWeaponWorldModel = m_pWeapon->m_hWeaponWorldModel.Get();
|
|
if ( pWeaponWorldModel && pAimLayer->m_nDispatchedDst != ACT_INVALID )
|
|
{
|
|
pAimMatrixHolder = pWeaponWorldModel;
|
|
nSeq = pAimLayer->m_nDispatchedDst;
|
|
}
|
|
|
|
if ( nSeq > 0 )
|
|
{
|
|
float flYawIdleMin = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_YAWMIN_IDLE, CSGO_ANIM_AIMMATRIX_DEFAULT_YAW_MIN );
|
|
float flYawIdleMax = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_YAWMAX_IDLE, CSGO_ANIM_AIMMATRIX_DEFAULT_YAW_MAX );
|
|
float flYawWalkMin = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_YAWMIN_WALK, flYawIdleMin );
|
|
float flYawWalkMax = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_YAWMAX_WALK, flYawIdleMax );
|
|
float flYawRunMin = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_YAWMIN_RUN, flYawWalkMin );
|
|
float flYawRunMax = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_YAWMAX_RUN, flYawWalkMax );
|
|
float flYawCrouchIdleMin = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_YAWMIN_CROUCHIDLE, CSGO_ANIM_AIMMATRIX_DEFAULT_YAW_MIN );
|
|
float flYawCrouchIdleMax = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_YAWMAX_CROUCHIDLE, CSGO_ANIM_AIMMATRIX_DEFAULT_YAW_MAX );
|
|
float flYawCrouchWalkMin = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_YAWMIN_CROUCHWALK, flYawCrouchIdleMin );
|
|
float flYawCrouchWalkMax = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_YAWMAX_CROUCHWALK, flYawCrouchIdleMax );
|
|
|
|
float flWalkAmt = m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_STAND_WALK ].GetValue( m_pPlayer );
|
|
float flRunAmt = m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_STAND_RUN ].GetValue( m_pPlayer );
|
|
float flCrouchWalkAmt = m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_CROUCH_WALK ].GetValue( m_pPlayer );
|
|
|
|
m_flAimYawMin = Lerp( m_flAnimDuckAmount,
|
|
Lerp( flRunAmt, Lerp( flWalkAmt, flYawIdleMin, flYawWalkMin ), flYawRunMin ),
|
|
Lerp( flCrouchWalkAmt, flYawCrouchIdleMin, flYawCrouchWalkMin ) );
|
|
m_flAimYawMax = Lerp( m_flAnimDuckAmount,
|
|
Lerp( flRunAmt, Lerp( flWalkAmt, flYawIdleMax, flYawWalkMax ), flYawRunMax ),
|
|
Lerp( flCrouchWalkAmt, flYawCrouchIdleMax, flYawCrouchWalkMax ) );
|
|
|
|
float flPitchIdleMin = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_PITCHMIN_IDLE, CSGO_ANIM_AIMMATRIX_DEFAULT_PITCH_MIN );
|
|
float flPitchIdleMax = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_PITCHMAX_IDLE, CSGO_ANIM_AIMMATRIX_DEFAULT_PITCH_MAX );
|
|
float flPitchWalkRunMin = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_PITCHMIN_WALKRUN, flPitchIdleMin );
|
|
float flPitchWalkRunMax = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_PITCHMAX_WALKRUN, flPitchIdleMax );
|
|
float flPitchCrouchMin = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_PITCHMIN_CROUCH, CSGO_ANIM_AIMMATRIX_DEFAULT_PITCH_MIN );
|
|
float flPitchCrouchMax = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_PITCHMAX_CROUCH, CSGO_ANIM_AIMMATRIX_DEFAULT_PITCH_MAX );
|
|
float flPitchCrouchWalkMin = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_PITCHMIN_CROUCHWALK, flPitchCrouchMin );
|
|
float flPitchCrouchWalkMax = pAimMatrixHolder->GetAnySequenceAnimTag( nSeq, ANIMTAG_AIMLIMIT_PITCHMAX_CROUCHWALK, flPitchCrouchMax );
|
|
|
|
m_flAimPitchMin = Lerp( m_flAnimDuckAmount, Lerp( flWalkAmt, flPitchIdleMin, flPitchWalkRunMin ), Lerp( flCrouchWalkAmt, flPitchCrouchMin, flPitchCrouchWalkMin ) );
|
|
m_flAimPitchMax = Lerp( m_flAnimDuckAmount, Lerp( flWalkAmt, flPitchIdleMax, flPitchWalkRunMax ), Lerp( flCrouchWalkAmt, flPitchCrouchMax, flPitchCrouchWalkMax ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateAnimLayer( ANIMATION_LAYER_AIMMATRIX, nSeqStand, 0, 1, 0 );
|
|
}
|
|
|
|
#define CSGO_ANIM_LOWER_CATCHUP_IDLE 100.0f
|
|
#define CSGO_ANIM_AIM_NARROW_WALK 0.8f
|
|
#define CSGO_ANIM_AIM_NARROW_RUN 0.5f
|
|
#define CSGO_ANIM_AIM_NARROW_CROUCHMOVING 0.5f
|
|
#define CSGO_ANIM_LOWER_CATCHUP_WITHIN 3.0f
|
|
#define CSGO_ANIM_LOWER_REALIGN_DELAY 1.1f
|
|
#define CSGO_ANIM_READJUST_THRESHOLD 120.0f
|
|
#define EIGHT_WAY_WIDTH 22.5f
|
|
void CCSGOPlayerAnimState::SetUpVelocity( void )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
// update the local velocity variables so we don't recalculate them unnecessarily
|
|
|
|
#ifndef CLIENT_DLL
|
|
Vector vecAbsVelocity = m_pPlayer->GetAbsVelocity();
|
|
#else
|
|
Vector vecAbsVelocity = m_vecVelocity;
|
|
|
|
if ( engine->IsHLTV() || engine->IsPlayingDemo() )
|
|
{
|
|
// Estimating velocity when playing demos is prone to fail, especially in POVs. Fall back to GetAbsVelocity.
|
|
vecAbsVelocity = m_pPlayer->GetAbsVelocity();
|
|
}
|
|
else
|
|
{
|
|
m_pPlayer->EstimateAbsVelocity( vecAbsVelocity ); // Using this accessor if the client is starved of information,
|
|
// the player doesn't run on the spot. Note this is unreliable
|
|
// and could fail to populate the value if prediction fails.
|
|
}
|
|
|
|
// prevent the client input velocity vector from exceeding a reasonable magnitude
|
|
#define CSGO_ANIM_MAX_VEL_LIMIT 1.2f
|
|
if ( vecAbsVelocity.LengthSqr() > Sqr( CS_PLAYER_SPEED_RUN * CSGO_ANIM_MAX_VEL_LIMIT ) )
|
|
vecAbsVelocity = vecAbsVelocity.Normalized() * (CS_PLAYER_SPEED_RUN * CSGO_ANIM_MAX_VEL_LIMIT);
|
|
|
|
#endif
|
|
|
|
// save vertical velocity component
|
|
m_flVelocityLengthZ = vecAbsVelocity.z;
|
|
|
|
// discard z component
|
|
vecAbsVelocity.z = 0;
|
|
|
|
// remember if the player is accelerating.
|
|
m_bPlayerIsAccelerating = ( m_vecVelocityLast.LengthSqr() < vecAbsVelocity.LengthSqr() );
|
|
|
|
// rapidly approach ideal velocity instead of instantly adopt it. This helps smooth out instant velocity changes, like
|
|
// when the player runs headlong into a wall and their velocity instantly becomes zero.
|
|
m_vecVelocity = Approach( vecAbsVelocity, m_vecVelocity, m_flLastUpdateIncrement * 2000 );
|
|
m_vecVelocityNormalized = m_vecVelocity.Normalized();
|
|
|
|
// save horizontal velocity length
|
|
m_flVelocityLengthXY = MIN( m_vecVelocity.Length(), CS_PLAYER_SPEED_RUN );
|
|
|
|
if ( m_flVelocityLengthXY > 0 )
|
|
{
|
|
m_vecVelocityNormalizedNonZero = m_vecVelocityNormalized;
|
|
}
|
|
|
|
//compute speed in various normalized forms
|
|
float flMaxSpeedRun = m_pWeapon ? MAX( m_pWeapon->GetMaxSpeed(), 0.001f ) : CS_PLAYER_SPEED_RUN;
|
|
Assert( flMaxSpeedRun > 0 );
|
|
|
|
m_flSpeedAsPortionOfRunTopSpeed = clamp( m_flVelocityLengthXY / flMaxSpeedRun, 0, 1 );
|
|
m_flSpeedAsPortionOfWalkTopSpeed = m_flVelocityLengthXY / (flMaxSpeedRun * CS_PLAYER_SPEED_WALK_MODIFIER);
|
|
m_flSpeedAsPortionOfCrouchTopSpeed = m_flVelocityLengthXY / (flMaxSpeedRun * CS_PLAYER_SPEED_DUCK_MODIFIER);
|
|
|
|
|
|
if ( m_flSpeedAsPortionOfWalkTopSpeed >= 1 )
|
|
{
|
|
m_flStaticApproachSpeed = m_flVelocityLengthXY;
|
|
}
|
|
else if ( m_flSpeedAsPortionOfWalkTopSpeed < 0.5f )
|
|
{
|
|
m_flStaticApproachSpeed = Approach( 80, m_flStaticApproachSpeed, m_flLastUpdateIncrement * 60 );
|
|
}
|
|
|
|
|
|
bool bStartedMovingThisFrame = false;
|
|
bool bStoppedMovingThisFrame = false;
|
|
|
|
if ( m_flVelocityLengthXY > 0 )
|
|
{
|
|
bStartedMovingThisFrame = ( m_flDurationMoving <= 0 );
|
|
m_flDurationStill = 0;
|
|
m_flDurationMoving += m_flLastUpdateIncrement;
|
|
}
|
|
else
|
|
{
|
|
bStoppedMovingThisFrame = ( m_flDurationStill <= 0 );
|
|
m_flDurationMoving = 0;
|
|
m_flDurationStill += m_flLastUpdateIncrement;
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
if ( !m_bAdjustStarted && bStoppedMovingThisFrame && m_bOnGround && !m_bOnLadder && !m_bLanding && m_flStutterStep < 50 )
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_ADJUST, SelectSequenceFromActMods( ACT_CSGO_IDLE_ADJUST_STOPPEDMOVING ) );
|
|
m_bAdjustStarted = true;
|
|
}
|
|
|
|
if ( GetLayerActivity( ANIMATION_LAYER_ADJUST ) == ACT_CSGO_IDLE_ADJUST_STOPPEDMOVING ||
|
|
GetLayerActivity( ANIMATION_LAYER_ADJUST ) == ACT_CSGO_IDLE_TURN_BALANCEADJUST )
|
|
{
|
|
if ( m_bAdjustStarted && m_flSpeedAsPortionOfCrouchTopSpeed <= 0.25f )
|
|
{
|
|
IncrementLayerCycleWeightRateGeneric( ANIMATION_LAYER_ADJUST );
|
|
m_bAdjustStarted = !( IsLayerSequenceCompleted( ANIMATION_LAYER_ADJUST ) );
|
|
}
|
|
else
|
|
{
|
|
m_bAdjustStarted = false;
|
|
float flWeight = GetLayerWeight( ANIMATION_LAYER_ADJUST );
|
|
SetLayerWeight( ANIMATION_LAYER_ADJUST, Approach( 0, flWeight, m_flLastUpdateIncrement * 5 ) );
|
|
SetLayerWeightRate( ANIMATION_LAYER_ADJUST, flWeight );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// if the player is looking far enough to either side, turn the feet to keep them within the extent of the aim matrix
|
|
m_flFootYawLast = m_flFootYaw;
|
|
m_flFootYaw = clamp( m_flFootYaw, -360, 360 );
|
|
float flEyeFootDelta = AngleDiff(m_flEyeYaw, m_flFootYaw);
|
|
|
|
// narrow the available aim matrix width as speed increases
|
|
float flAimMatrixWidthRange = Lerp( clamp(m_flSpeedAsPortionOfWalkTopSpeed,0,1), 1.0f, Lerp( m_flWalkToRunTransition, CSGO_ANIM_AIM_NARROW_WALK, CSGO_ANIM_AIM_NARROW_RUN ) );
|
|
|
|
if ( m_flAnimDuckAmount > 0 )
|
|
{
|
|
flAimMatrixWidthRange = Lerp( m_flAnimDuckAmount * clamp( m_flSpeedAsPortionOfCrouchTopSpeed, 0, 1 ), flAimMatrixWidthRange, CSGO_ANIM_AIM_NARROW_CROUCHMOVING );
|
|
}
|
|
|
|
float flTempYawMax = m_flAimYawMax * flAimMatrixWidthRange;
|
|
float flTempYawMin = m_flAimYawMin * flAimMatrixWidthRange;
|
|
|
|
if ( flEyeFootDelta > flTempYawMax )
|
|
{
|
|
m_flFootYaw = m_flEyeYaw - abs(flTempYawMax);
|
|
}
|
|
else if ( flEyeFootDelta < flTempYawMin )
|
|
{
|
|
m_flFootYaw = m_flEyeYaw + abs(flTempYawMin);
|
|
}
|
|
m_flFootYaw = AngleNormalize( m_flFootYaw );
|
|
|
|
|
|
// pull the lower body direction towards the eye direction, but only when the player is moving
|
|
if ( m_bOnGround )
|
|
{
|
|
if ( m_flVelocityLengthXY > 0.1f )
|
|
{
|
|
m_flFootYaw = ApproachAngle( m_flEyeYaw, m_flFootYaw, m_flLastUpdateIncrement * (30.0f + 20.0f * m_flWalkToRunTransition) );
|
|
|
|
#ifndef CLIENT_DLL
|
|
m_flLowerBodyRealignTimer = gpGlobals->curtime + ( CSGO_ANIM_LOWER_REALIGN_DELAY * 0.2f );
|
|
m_pPlayer->m_flLowerBodyYawTarget.Set( m_flEyeYaw );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
m_flFootYaw = ApproachAngle( m_pPlayer->m_flLowerBodyYawTarget.Get(), m_flFootYaw, m_flLastUpdateIncrement * CSGO_ANIM_LOWER_CATCHUP_IDLE );
|
|
|
|
#ifndef CLIENT_DLL
|
|
if ( gpGlobals->curtime > m_flLowerBodyRealignTimer && abs( AngleDiff( m_flFootYaw, m_flEyeYaw ) ) > 35.0f )
|
|
{
|
|
m_flLowerBodyRealignTimer = gpGlobals->curtime + CSGO_ANIM_LOWER_REALIGN_DELAY;
|
|
m_pPlayer->m_flLowerBodyYawTarget.Set( m_flEyeYaw );
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
if ( m_flVelocityLengthXY <= CS_PLAYER_SPEED_STOPPED && m_bOnGround && !m_bOnLadder && !m_bLanding && m_flLastUpdateIncrement > 0 && abs( AngleDiff( m_flFootYawLast, m_flFootYaw ) / m_flLastUpdateIncrement > CSGO_ANIM_READJUST_THRESHOLD ) )
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_ADJUST, SelectSequenceFromActMods( ACT_CSGO_IDLE_TURN_BALANCEADJUST ) );
|
|
m_bAdjustStarted = true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CLIENT_DLL
|
|
if ( GetLayerWeight( ANIMATION_LAYER_ADJUST ) > 0 )
|
|
{
|
|
IncrementLayerCycle( ANIMATION_LAYER_ADJUST, false );
|
|
IncrementLayerWeight( ANIMATION_LAYER_ADJUST );
|
|
}
|
|
#endif
|
|
|
|
// the final model render yaw is aligned to the foot yaw
|
|
|
|
if ( m_flVelocityLengthXY > 0 && m_bOnGround )
|
|
{
|
|
// convert horizontal velocity vec to angular yaw
|
|
float flRawYawIdeal = (atan2(-m_vecVelocity[1], -m_vecVelocity[0]) * 180 / M_PI);
|
|
if (flRawYawIdeal < 0)
|
|
flRawYawIdeal += 360;
|
|
|
|
m_flMoveYawIdeal = AngleNormalize( AngleDiff( flRawYawIdeal, m_flFootYaw ) );
|
|
}
|
|
|
|
// delta between current yaw and ideal velocity derived target (possibly negative!)
|
|
m_flMoveYawCurrentToIdeal = AngleNormalize( AngleDiff( m_flMoveYawIdeal, m_flMoveYaw ) );
|
|
|
|
|
|
if ( bStartedMovingThisFrame && m_flMoveWeight <= 0 )
|
|
{
|
|
m_flMoveYaw = m_flMoveYawIdeal;
|
|
|
|
// select a special starting cycle that's set by the animator in content
|
|
int nMoveSeq = GetLayerSequence( ANIMATION_LAYER_MOVEMENT_MOVE );
|
|
if ( nMoveSeq != -1 )
|
|
{
|
|
mstudioseqdesc_t &seqdesc = m_pPlayer->GetModelPtr()->pSeqdesc( nMoveSeq );
|
|
if ( seqdesc.numanimtags > 0 )
|
|
{
|
|
if ( abs( AngleDiff( m_flMoveYaw, 180 ) ) <= EIGHT_WAY_WIDTH ) //N
|
|
{
|
|
m_flPrimaryCycle = m_pPlayer->GetFirstSequenceAnimTag( nMoveSeq, ANIMTAG_STARTCYCLE_N, 0, 1 );
|
|
}
|
|
else if ( abs( AngleDiff( m_flMoveYaw, 135 ) ) <= EIGHT_WAY_WIDTH ) //NE
|
|
{
|
|
m_flPrimaryCycle = m_pPlayer->GetFirstSequenceAnimTag( nMoveSeq, ANIMTAG_STARTCYCLE_NE, 0, 1 );
|
|
}
|
|
else if ( abs( AngleDiff( m_flMoveYaw, 90 ) ) <= EIGHT_WAY_WIDTH ) //E
|
|
{
|
|
m_flPrimaryCycle = m_pPlayer->GetFirstSequenceAnimTag( nMoveSeq, ANIMTAG_STARTCYCLE_E, 0, 1 );
|
|
}
|
|
else if ( abs( AngleDiff( m_flMoveYaw, 45 ) ) <= EIGHT_WAY_WIDTH ) //SE
|
|
{
|
|
m_flPrimaryCycle = m_pPlayer->GetFirstSequenceAnimTag( nMoveSeq, ANIMTAG_STARTCYCLE_SE, 0, 1 );
|
|
}
|
|
else if ( abs( AngleDiff( m_flMoveYaw, 0 ) ) <= EIGHT_WAY_WIDTH ) //S
|
|
{
|
|
m_flPrimaryCycle = m_pPlayer->GetFirstSequenceAnimTag( nMoveSeq, ANIMTAG_STARTCYCLE_S, 0, 1 );
|
|
}
|
|
else if ( abs( AngleDiff( m_flMoveYaw, -45 ) ) <= EIGHT_WAY_WIDTH ) //SW
|
|
{
|
|
m_flPrimaryCycle = m_pPlayer->GetFirstSequenceAnimTag( nMoveSeq, ANIMTAG_STARTCYCLE_SW, 0, 1 );
|
|
}
|
|
else if ( abs( AngleDiff( m_flMoveYaw, -90 ) ) <= EIGHT_WAY_WIDTH ) //W
|
|
{
|
|
m_flPrimaryCycle = m_pPlayer->GetFirstSequenceAnimTag( nMoveSeq, ANIMTAG_STARTCYCLE_W, 0, 1 );
|
|
}
|
|
else if ( abs( AngleDiff( m_flMoveYaw, -135 ) ) <= EIGHT_WAY_WIDTH ) //NW
|
|
{
|
|
m_flPrimaryCycle = m_pPlayer->GetFirstSequenceAnimTag( nMoveSeq, ANIMTAG_STARTCYCLE_NW, 0, 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CLIENT_DLL
|
|
if ( m_flInAirSmoothValue >= 1 && !m_bFirstRunSinceInit && abs(m_flMoveYawCurrentToIdeal) > 45 && m_bOnGround && m_pPlayer->m_boneSnapshots[BONESNAPSHOT_ENTIRE_BODY].GetCurrentWeight() <= 0 )
|
|
{
|
|
m_pPlayer->m_boneSnapshots[BONESNAPSHOT_ENTIRE_BODY].SetShouldCapture( bonesnapshot_get( cl_bonesnapshot_speed_movebegin ) );
|
|
}
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{
|
|
if ( GetLayerWeight( ANIMATION_LAYER_MOVEMENT_STRAFECHANGE ) >= 1 )
|
|
{
|
|
m_flMoveYaw = m_flMoveYawIdeal;
|
|
}
|
|
else
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
if ( m_flInAirSmoothValue >= 1 && !m_bFirstRunSinceInit && abs(m_flMoveYawCurrentToIdeal) > 100 && m_bOnGround && m_pPlayer->m_boneSnapshots[BONESNAPSHOT_ENTIRE_BODY].GetCurrentWeight() <= 0 )
|
|
{
|
|
m_pPlayer->m_boneSnapshots[BONESNAPSHOT_ENTIRE_BODY].SetShouldCapture( bonesnapshot_get( cl_bonesnapshot_speed_movebegin ) );
|
|
}
|
|
#endif
|
|
|
|
float flMoveWeight = Lerp( m_flAnimDuckAmount, clamp(m_flSpeedAsPortionOfWalkTopSpeed, 0, 1), clamp(m_flSpeedAsPortionOfCrouchTopSpeed, 0, 1) );
|
|
float flRatio = Bias( flMoveWeight, 0.18f ) + 0.1f;
|
|
|
|
m_flMoveYaw = AngleNormalize( m_flMoveYaw + ( m_flMoveYawCurrentToIdeal * flRatio ) );
|
|
}
|
|
}
|
|
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_MOVE_YAW ].SetValue( m_pPlayer, m_flMoveYaw );
|
|
|
|
float flAimYaw = AngleDiff( m_flEyeYaw, m_flFootYaw );
|
|
if ( flAimYaw >= 0 && m_flAimYawMax != 0 )
|
|
{
|
|
flAimYaw = (flAimYaw / m_flAimYawMax) * 60.0f;
|
|
}
|
|
else if ( m_flAimYawMin != 0 )
|
|
{
|
|
flAimYaw = (flAimYaw / m_flAimYawMin) * -60.0f;
|
|
}
|
|
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_BODY_YAW ].SetValue( m_pPlayer, flAimYaw );
|
|
|
|
// we need non-symmetrical arbitrary min/max bounds for vertical aim (pitch) too
|
|
float flPitch = AngleDiff( m_flEyePitch, 0 );
|
|
if ( flPitch > 0 )
|
|
{
|
|
flPitch = (flPitch / m_flAimPitchMax) * CSGO_ANIM_AIMMATRIX_DEFAULT_PITCH_MAX;
|
|
}
|
|
else
|
|
{
|
|
flPitch = (flPitch / m_flAimPitchMin) * CSGO_ANIM_AIMMATRIX_DEFAULT_PITCH_MIN;
|
|
}
|
|
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_BODY_PITCH ].SetValue( m_pPlayer, flPitch );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_SPEED ].SetValue( m_pPlayer, m_flSpeedAsPortionOfWalkTopSpeed );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_STAND ].SetValue( m_pPlayer, 1.0f - (m_flAnimDuckAmount*m_flInAirSmoothValue) );
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
int CCSGOPlayerAnimState::SelectSequenceFromActMods( Activity iAct )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
Assert( m_pPlayer );
|
|
int nSelectedActivity = m_pPlayer->SelectWeightedSequenceFromModifiers( iAct, m_ActivityModifiers.Base(), m_ActivityModifiers.Count() );
|
|
|
|
Assert( nSelectedActivity != ACT_INVALID );
|
|
return nSelectedActivity;
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::DoAnimationEvent( PlayerAnimEvent_t animEvent, int nData )
|
|
{
|
|
UpdateActivityModifiers();
|
|
|
|
switch (animEvent) {
|
|
case PLAYERANIMEVENT_THROW_GRENADE_UNDERHAND:
|
|
{
|
|
AddActivityModifier( "underhand" );
|
|
}
|
|
case PLAYERANIMEVENT_FIRE_GUN_PRIMARY:
|
|
case PLAYERANIMEVENT_THROW_GRENADE:
|
|
{
|
|
if ( m_pWeapon && animEvent == PLAYERANIMEVENT_FIRE_GUN_PRIMARY && m_pWeapon->IsA( WEAPON_C4 ) )
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WHOLE_BODY, SelectSequenceFromActMods( ACT_CSGO_PLANT_BOMB ) );
|
|
m_bPlantAnimStarted = true;
|
|
}
|
|
//else if ( m_pWeapon->GetWeaponType() == WEAPONTYPE_KNIFE )
|
|
//{
|
|
// // HACK: knives keep previous firing weight, if any
|
|
// float flSavedWeight = GetLayerWeight( ANIMATION_LAYER_WEAPON_ACTION );
|
|
// SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_FIRE_PRIMARY ) );
|
|
// SetLayerWeight( ANIMATION_LAYER_WEAPON_ACTION, flSavedWeight );
|
|
//}
|
|
else
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_FIRE_PRIMARY ) );
|
|
}
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_FIRE_GUN_PRIMARY_OPT:
|
|
{
|
|
//if ( m_pWeapon->GetWeaponType() == WEAPONTYPE_KNIFE )
|
|
//{
|
|
// // HACK: knives keep previous firing weight, if any
|
|
// float flSavedWeight = GetLayerWeight( ANIMATION_LAYER_WEAPON_ACTION );
|
|
// SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_FIRE_PRIMARY_OPT_1 ) );
|
|
// SetLayerWeight( ANIMATION_LAYER_WEAPON_ACTION, flSavedWeight );
|
|
//}
|
|
//else
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_FIRE_PRIMARY_OPT_1 ) );
|
|
}
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_FIRE_GUN_PRIMARY_SPECIAL1:
|
|
case PLAYERANIMEVENT_FIRE_GUN_PRIMARY_OPT_SPECIAL1:
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_FIRE_PRIMARY_OPT_2 ) );
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_FIRE_GUN_SECONDARY:
|
|
{
|
|
if ( m_pWeapon->GetWeaponType() == WEAPONTYPE_SNIPER_RIFLE )
|
|
{
|
|
// hack: sniper rifles use primary fire anim when 'alt' firing, meaning scoped.
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_FIRE_PRIMARY ) );
|
|
}
|
|
else
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_FIRE_SECONDARY ) );
|
|
}
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_FIRE_GUN_SECONDARY_SPECIAL1:
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_FIRE_SECONDARY_OPT_1 ) );
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_GRENADE_PULL_PIN:
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_OPERATE ) );
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_SILENCER_ATTACH:
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_SILENCER_ATTACH ) );
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_SILENCER_DETACH:
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_SILENCER_DETACH ) );
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_RELOAD:
|
|
{
|
|
if ( m_pWeapon && m_pWeapon->GetWeaponType() == WEAPONTYPE_SHOTGUN && m_pWeapon->GetCSWeaponID() != WEAPON_MAG7 )
|
|
break;
|
|
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_RELOAD ) );
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_RELOAD_START:
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_RELOAD_START ) );
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_RELOAD_LOOP:
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_RELOAD_LOOP ) );
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_RELOAD_END:
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_RELOAD_END ) );
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_CATCH_WEAPON:
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_CATCH ) );
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_CLEAR_FIRING:
|
|
{
|
|
m_bPlantAnimStarted = false;
|
|
break;
|
|
}
|
|
//case PLAYERANIMEVENT_TAUNT:
|
|
//{
|
|
// //not implemented yet
|
|
// break;
|
|
//}
|
|
case PLAYERANIMEVENT_DEPLOY:
|
|
{
|
|
if ( m_pWeapon && GetLayerActivity( ANIMATION_LAYER_WEAPON_ACTION ) == ACT_CSGO_DEPLOY &&
|
|
GetLayerCycle( ANIMATION_LAYER_WEAPON_ACTION ) < CSGO_ANIM_DEPLOY_RATELIMIT )
|
|
{
|
|
// we're already deploying
|
|
m_bDeployRateLimiting = true;
|
|
}
|
|
else
|
|
{
|
|
SetLayerSequence( ANIMATION_LAYER_WEAPON_ACTION, SelectSequenceFromActMods( ACT_CSGO_DEPLOY ) );
|
|
}
|
|
break;
|
|
}
|
|
case PLAYERANIMEVENT_JUMP:
|
|
{
|
|
// note: this event means a jump is definitely happening, not just that a jump is desired
|
|
m_bJumping = true;
|
|
SetLayerSequence( ANIMATION_LAYER_MOVEMENT_JUMP_OR_FALL, SelectSequenceFromActMods( ACT_CSGO_JUMP ) );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::AddActivityModifier( const char *szName )
|
|
{
|
|
if ( szName == NULL )
|
|
{
|
|
Assert(0);
|
|
return;
|
|
}
|
|
|
|
char szLookup[32];
|
|
V_strcpy_safe( szLookup, szName );
|
|
|
|
CUtlSymbol sym = g_ActivityModifiersTable.Find( szLookup );
|
|
if ( !sym.IsValid() )
|
|
{
|
|
sym = g_ActivityModifiersTable.AddString( szLookup );
|
|
}
|
|
m_ActivityModifiers.AddToTail( sym );
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::UpdateActivityModifiers( void )
|
|
{
|
|
m_ActivityModifiers.Purge();
|
|
|
|
AddActivityModifier( GetWeaponPrefix() );
|
|
|
|
if ( m_flSpeedAsPortionOfWalkTopSpeed > 0.25f )
|
|
{
|
|
AddActivityModifier( "moving" );
|
|
}
|
|
|
|
if ( m_flAnimDuckAmount > 0.55f )
|
|
{
|
|
AddActivityModifier( "crouch" );
|
|
}
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::SelectDeathPose( const CTakeDamageInfo &info, int hitgroup, Activity& activity, float& yaw )
|
|
{
|
|
activity = ACT_INVALID;
|
|
yaw = 0;
|
|
|
|
if ( !m_pPlayer )
|
|
return;
|
|
|
|
|
|
if ( hitgroup == HITGROUP_HEAD )
|
|
{
|
|
activity = ( m_flAnimDuckAmount > 0.5f ) ? ACT_DIE_CROUCH_HEADSHOT : ACT_DIE_STAND_HEADSHOT;
|
|
}
|
|
else
|
|
{
|
|
activity = ( m_flAnimDuckAmount > 0.5f ) ? ACT_DIE_CROUCH : ACT_DIE_STAND;
|
|
}
|
|
|
|
|
|
Vector vecDamageDir = info.GetDamageForce();
|
|
VectorNormalize( vecDamageDir );
|
|
|
|
Vector vecDamagePlusPlayerVel = vecDamageDir + ( m_vecVelocity.Normalized() * MIN(m_vecVelocity.Length() / CS_PLAYER_SPEED_RUN, 1) );
|
|
VectorNormalize( vecDamagePlusPlayerVel );
|
|
|
|
|
|
QAngle angDamageAnglePlusCurrentMoveVelocity;
|
|
VectorAngles( vecDamageDir, Vector(0,0,1), angDamageAnglePlusCurrentMoveVelocity );
|
|
|
|
float flPlayerRelativeDamageAngle = AngleDiff( angDamageAnglePlusCurrentMoveVelocity[YAW], m_flFootYaw );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_DEATH_YAW ].SetValue( m_pPlayer, clamp( flPlayerRelativeDamageAngle, -180, 180 ) );
|
|
|
|
yaw = flPlayerRelativeDamageAngle;
|
|
|
|
////float flBaseSize = 10;
|
|
////float flHeight = 80;
|
|
////Vector vBasePos = m_pPlayer->GetAbsOrigin() + Vector( 0, 0, 3 );
|
|
////Vector vForward, vRight, vUp;
|
|
////AngleVectors( angDamageAnglePlusCurrentMoveVelocity, &vForward, &vRight, &vUp );
|
|
////debugoverlay->AddTriangleOverlay( vBasePos+vRight*flBaseSize/2, vBasePos-vRight*flBaseSize/2, vBasePos+vForward*flHeight, 255, 0, 0, 255, false, 10 );
|
|
}
|
|
#endif
|
|
|
|
const char* CCSGOPlayerAnimState::GetWeaponPrefix( void )
|
|
{
|
|
int nWeaponType = 0; // knife
|
|
|
|
m_pWeapon = m_pPlayer->GetActiveCSWeapon();
|
|
if ( m_pWeapon )
|
|
{
|
|
nWeaponType = (int)m_pWeapon->GetWeaponType();
|
|
|
|
int nWeaponID = m_pWeapon->GetCSWeaponID();
|
|
if ( nWeaponID == WEAPON_MAG7 )
|
|
{
|
|
nWeaponType = WEAPONTYPE_RIFLE;
|
|
}
|
|
else if ( nWeaponID == WEAPON_TASER )
|
|
{
|
|
nWeaponType = WEAPONTYPE_PISTOL;
|
|
}
|
|
|
|
if ( nWeaponType == WEAPONTYPE_STACKABLEITEM )
|
|
{
|
|
nWeaponType = WEAPONTYPE_GRENADE; // redirect healthshot, adrenaline, etc to the grenade archetype
|
|
}
|
|
}
|
|
|
|
return g_szWeaponPrefixLookupTable[clamp( nWeaponType, 0, 9 )];
|
|
}
|
|
|
|
void CCSGOPlayerAnimState::WriteAnimstateDebugBuffer( char *pDest )
|
|
{
|
|
}
|
|
|
|
bool CCSGOPlayerAnimState::CacheSequences( void )
|
|
{
|
|
if ( !m_pPlayer )
|
|
return false;
|
|
|
|
if ( m_cachedModelIndex != m_pPlayer->GetModelIndex() )
|
|
{
|
|
// read animset version from keyvalues
|
|
m_nAnimstateModelVersion = 0;
|
|
|
|
CUtlBuffer buf( 1024, 0, CUtlBuffer::TEXT_BUFFER );
|
|
buf.PutString( "keyvalues {\n" ); // trick the kv loader into thinking this is one big block
|
|
if ( modelinfo->GetModelKeyValue( m_pPlayer->GetModel(), buf ) )
|
|
{
|
|
buf.PutString( "\n}\0" ); // end trickery
|
|
KeyValues *modelKeyValues = new KeyValues("");
|
|
KeyValues::AutoDelete autodelete_key( modelKeyValues );
|
|
if ( modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( m_pPlayer->GetModel() ), buf ) )
|
|
{
|
|
FOR_EACH_SUBKEY( modelKeyValues, pSubKey )
|
|
{
|
|
//KeyValuesDumpAsDevMsg( pSubKey );
|
|
if ( pSubKey->FindKey( "animset_version" ) )
|
|
{
|
|
m_nAnimstateModelVersion = pSubKey->GetInt( "animset_version", CURRENT_ANIMSTATE_VERSION );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
AssertMsgOnce( m_nAnimstateModelVersion == CURRENT_ANIMSTATE_VERSION, "Animset version is out of date!" );
|
|
|
|
// cache pose param indices
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_LEAN_YAW ].Init( m_pPlayer, "lean_yaw" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_SPEED ].Init( m_pPlayer, "speed" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_LADDER_SPEED ].Init( m_pPlayer, "ladder_speed" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_LADDER_YAW ].Init( m_pPlayer, "ladder_yaw" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_MOVE_YAW ].Init( m_pPlayer, "move_yaw" );
|
|
|
|
if ( m_nAnimstateModelVersion < 2 )
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_RUN ].Init( m_pPlayer, "run" );
|
|
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_BODY_YAW ].Init( m_pPlayer, "body_yaw" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_BODY_PITCH ].Init( m_pPlayer, "body_pitch" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_DEATH_YAW ].Init( m_pPlayer, "death_yaw" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_STAND ].Init( m_pPlayer, "stand" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_JUMP_FALL ].Init( m_pPlayer, "jump_fall" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_STAND_IDLE ].Init( m_pPlayer, "aim_blend_stand_idle" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_CROUCH_IDLE ].Init( m_pPlayer, "aim_blend_crouch_idle" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_STRAFE_DIR ].Init( m_pPlayer, "strafe_yaw" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_STAND_WALK ].Init( m_pPlayer, "aim_blend_stand_walk" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_STAND_RUN ].Init( m_pPlayer, "aim_blend_stand_run" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_AIM_BLEND_CROUCH_WALK ].Init( m_pPlayer, "aim_blend_crouch_walk" );
|
|
|
|
if ( m_nAnimstateModelVersion > 0 )
|
|
{
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_MOVE_BLEND_WALK ].Init( m_pPlayer, "move_blend_walk" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_MOVE_BLEND_RUN ].Init( m_pPlayer, "move_blend_run" );
|
|
m_tPoseParamMappings[ PLAYER_POSE_PARAM_MOVE_BLEND_CROUCH_WALK ].Init( m_pPlayer, "move_blend_crouch" );
|
|
}
|
|
|
|
m_cachedModelIndex = m_pPlayer->GetModelIndex();
|
|
|
|
//// precache the sequences we'll be using for movement
|
|
//if ( m_cachedModelIndex > 0 && m_pPlayer->GetModelPtr() )
|
|
//{
|
|
// bool bSuccess = true;
|
|
// bSuccess = bSuccess && PopulateSequenceCache( "move", ANIMCACHE_MOVE );
|
|
// bSuccess = bSuccess && PopulateSequenceCache( "additive_posebreaker", ANIMCACHE_ALIVELOOP );
|
|
// bSuccess = bSuccess && PopulateSequenceCache( "jump", ANIMCACHE_JUMP );
|
|
// bSuccess = bSuccess && PopulateSequenceCache( "jump_moving", ANIMCACHE_JUMP_MOVING );
|
|
// bSuccess = bSuccess && PopulateSequenceCache( "fall", ANIMCACHE_FALL );
|
|
// bSuccess = bSuccess && PopulateSequenceCache( "land_light", ANIMCACHE_LANDLIGHT );
|
|
// bSuccess = bSuccess && PopulateSequenceCache( "land_heavy", ANIMCACHE_LANDHEAVY );
|
|
// bSuccess = bSuccess && PopulateSequenceCache( "land_light_crouched", ANIMCACHE_LANDLIGHT_CROUCHED );
|
|
// bSuccess = bSuccess && PopulateSequenceCache( "land_heavy_crouched", ANIMCACHE_LANDHEAVY_CROUCHED );
|
|
// bSuccess = bSuccess && PopulateSequenceCache( "ladder_climb", ANIMCACHE_LADDERCLIMB );
|
|
// bSuccess = bSuccess && PopulateSequenceCache( "lean", ANIMCACHE_LEAN );
|
|
// return bSuccess;
|
|
//}
|
|
}
|
|
return m_cachedModelIndex > 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
int animstate_pose_param_cache_t::GetIndex( void )
|
|
{
|
|
Assert( m_bInitialized );
|
|
return m_nIndex;
|
|
}
|
|
|
|
float animstate_pose_param_cache_t::GetValue( CCSPlayer* pPlayer )
|
|
{
|
|
if ( !m_bInitialized )
|
|
{
|
|
Init( pPlayer, m_szName );
|
|
}
|
|
if ( m_bInitialized && pPlayer )
|
|
{
|
|
return pPlayer->GetPoseParameter( m_nIndex );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void animstate_pose_param_cache_t::SetValue( CCSPlayer* pPlayer, float flValue )
|
|
{
|
|
if ( !m_bInitialized )
|
|
{
|
|
Init( pPlayer, m_szName );
|
|
}
|
|
if ( m_bInitialized && pPlayer )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
pPlayer->SetPoseParameter( m_nIndex, flValue );
|
|
}
|
|
}
|
|
|
|
bool animstate_pose_param_cache_t::Init( CCSPlayer* pPlayer, const char* szPoseParamName )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
m_szName = szPoseParamName;
|
|
m_nIndex = pPlayer->LookupPoseParameter( szPoseParamName );
|
|
if ( m_nIndex != -1 )
|
|
{
|
|
m_bInitialized = true;
|
|
}
|
|
return m_bInitialized;
|
|
}
|
|
//----------------------------------------------------------------------------------
|