//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
# include "cbase.h"
# include "base_playeranimstate.h"
# include "tier0/vprof.h"
# include "animation.h"
# include "studio.h"
# include "apparent_velocity_helper.h"
# include "utldict.h"
# include "filesystem.h"
# ifdef CLIENT_DLL
# include "c_baseplayer.h"
# include "engine/ivdebugoverlay.h"
ConVar cl_showanimstate ( " cl_showanimstate " , " -1 " , FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY , " Show the (client) animation state for the specified entity ( - 1 for none ) . " ) ;
ConVar showanimstate_log ( " cl_showanimstate_log " , " 0 " , FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY , " 1 to output cl_showanimstate to Msg() . 2 to store in AnimStateClient . log . 3 for both . " ) ;
# else
# include "player.h"
ConVar sv_showanimstate ( " sv_showanimstate " , " -1 " , FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY , " Show the (server) animation state for the specified entity ( - 1 for none ) . " ) ;
ConVar showanimstate_log ( " sv_showanimstate_log " , " 0 " , FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY , " 1 to output sv_showanimstate to Msg() . 2 to store in AnimStateServer . log . 3 for both . " ) ;
# endif
// Below this many degrees, slow down turning rate linearly
# define FADE_TURN_DEGREES 45.0f
// After this, need to start turning feet
# define MAX_TORSO_ANGLE 70.0f
// Below this amount, don't play a turning animation/perform IK
# define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f
ConVar mp_feetyawrate (
" mp_feetyawrate " ,
" 720 " ,
FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY ,
" How many degrees per second that we can turn our feet or upper body. " ) ;
ConVar mp_facefronttime (
" mp_facefronttime " ,
" 3 " ,
FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY ,
" After this amount of time of standing in place but aiming to one side, go ahead and move feet to face upper body. " ) ;
ConVar mp_ik ( " mp_ik " , " 1 " , FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY , " Use IK on in-place turns. " ) ;
// Pose parameters stored for debugging.
float g_flLastBodyPitch , g_flLastBodyYaw , m_flLastMoveYaw ;
// ------------------------------------------------------------------------------------------------ //
// CBasePlayerAnimState implementation.
// ------------------------------------------------------------------------------------------------ //
CBasePlayerAnimState : : CBasePlayerAnimState ( )
{
m_flEyeYaw = 0.0f ;
m_flEyePitch = 0.0f ;
m_bCurrentFeetYawInitialized = false ;
m_flCurrentTorsoYaw = 0.0f ;
m_nTurningInPlace = TURN_NONE ;
m_flMaxGroundSpeed = 0.0f ;
m_flStoredCycle = 0.0f ;
m_flGaitYaw = 0.0f ;
m_flGoalFeetYaw = 0.0f ;
m_flCurrentFeetYaw = 0.0f ;
m_flLastYaw = 0.0f ;
m_flLastTurnTime = 0.0f ;
m_angRender . Init ( ) ;
m_vLastMovePose . Init ( ) ;
m_iCurrent8WayIdleSequence = - 1 ;
m_iCurrent8WayCrouchIdleSequence = - 1 ;
m_pOuter = NULL ;
m_eCurrentMainSequenceActivity = ACT_IDLE ;
m_flLastAnimationStateClearTime = 0.0f ;
}
CBasePlayerAnimState : : ~ CBasePlayerAnimState ( )
{
}
void CBasePlayerAnimState : : Init ( CBaseAnimatingOverlay * pPlayer , const CModAnimConfig & config )
{
m_pOuter = pPlayer ;
m_AnimConfig = config ;
ClearAnimationState ( ) ;
}
void CBasePlayerAnimState : : Release ( )
{
delete this ;
}
void CBasePlayerAnimState : : ClearAnimationState ( )
{
ClearAnimationLayers ( ) ;
m_bCurrentFeetYawInitialized = false ;
m_flLastAnimationStateClearTime = gpGlobals - > curtime ;
}
float CBasePlayerAnimState : : TimeSinceLastAnimationStateClear ( ) const
{
return gpGlobals - > curtime - m_flLastAnimationStateClearTime ;
}
void CBasePlayerAnimState : : Update ( float eyeYaw , float eyePitch )
{
VPROF ( " CBasePlayerAnimState::Update " ) ;
// Clear animation overlays because we're about to completely reconstruct them.
ClearAnimationLayers ( ) ;
// Some mods don't want to update the player's animation state if they're dead and ragdolled.
if ( ! ShouldUpdateAnimState ( ) )
{
ClearAnimationState ( ) ;
return ;
}
CStudioHdr * pStudioHdr = GetOuter ( ) - > GetModelPtr ( ) ;
// Store these. All the calculations are based on them.
m_flEyeYaw = AngleNormalize ( eyeYaw ) ;
m_flEyePitch = AngleNormalize ( eyePitch ) ;
// Compute sequences for all the layers.
ComputeSequences ( pStudioHdr ) ;
// Compute all the pose params.
ComputePoseParam_BodyPitch ( pStudioHdr ) ; // Look up/down.
ComputePoseParam_BodyYaw ( ) ; // Torso rotation.
ComputePoseParam_MoveYaw ( pStudioHdr ) ; // What direction his legs are running in.
ComputePlaybackRate ( ) ;
# ifdef CLIENT_DLL
if ( cl_showanimstate . GetInt ( ) = = m_pOuter - > entindex ( ) )
{
DebugShowAnimStateFull ( 5 ) ;
}
else if ( cl_showanimstate . GetInt ( ) = = - 2 )
{
C_BasePlayer * targetPlayer = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( targetPlayer & & ( targetPlayer - > GetObserverMode ( ) = = OBS_MODE_IN_EYE | | targetPlayer - > GetObserverMode ( ) = = OBS_MODE_CHASE ) )
{
C_BaseEntity * target = targetPlayer - > GetObserverTarget ( ) ;
if ( target & & target - > IsPlayer ( ) )
{
targetPlayer = ToBasePlayer ( target ) ;
}
}
if ( m_pOuter = = targetPlayer )
{
DebugShowAnimStateFull ( 6 ) ;
}
}
# else
if ( sv_showanimstate . GetInt ( ) = = m_pOuter - > entindex ( ) )
{
DebugShowAnimState ( 20 ) ;
}
# endif
}
bool CBasePlayerAnimState : : ShouldUpdateAnimState ( )
{
// By default, don't update their animation state when they're dead because they're
// either a ragdoll or they're not drawn.
return GetOuter ( ) - > IsAlive ( ) ;
}
bool CBasePlayerAnimState : : ShouldChangeSequences ( void ) const
{
return true ;
}
void CBasePlayerAnimState : : SetOuterPoseParameter ( int iParam , float flValue )
{
// Make sure to set all the history values too, otherwise the server can overwrite them.
GetOuter ( ) - > SetPoseParameter ( iParam , flValue ) ;
}
void CBasePlayerAnimState : : ClearAnimationLayers ( )
{
VPROF ( " CBasePlayerAnimState::ClearAnimationLayers " ) ;
if ( ! m_pOuter )
return ;
m_pOuter - > SetNumAnimOverlays ( AIMSEQUENCE_LAYER + NUM_AIMSEQUENCE_LAYERS ) ;
for ( int i = 0 ; i < m_pOuter - > GetNumAnimOverlays ( ) ; i + + )
{
m_pOuter - > GetAnimOverlay ( i ) - > SetOrder ( CBaseAnimatingOverlay : : MAX_OVERLAYS ) ;
# ifndef CLIENT_DLL
m_pOuter - > GetAnimOverlay ( i ) - > m_fFlags = 0 ;
# endif
}
}
void CBasePlayerAnimState : : RestartMainSequence ( )
{
CBaseAnimatingOverlay * pPlayer = GetOuter ( ) ;
pPlayer - > m_flAnimTime = gpGlobals - > curtime ;
pPlayer - > SetCycle ( 0 ) ;
}
void CBasePlayerAnimState : : ComputeSequences ( CStudioHdr * pStudioHdr )
{
VPROF ( " CBasePlayerAnimState::ComputeSequences " ) ;
ComputeMainSequence ( ) ; // Lower body (walk/run/idle).
UpdateInterpolators ( ) ; // The groundspeed interpolator uses the main sequence info.
if ( m_AnimConfig . m_bUseAimSequences )
{
ComputeAimSequence ( ) ; // Upper body, based on weapon type.
}
}
void CBasePlayerAnimState : : ResetGroundSpeed ( void )
{
m_flMaxGroundSpeed = GetCurrentMaxGroundSpeed ( ) ;
}
void CBasePlayerAnimState : : ComputeMainSequence ( )
{
VPROF ( " CBasePlayerAnimState::ComputeMainSequence " ) ;
CBaseAnimatingOverlay * pPlayer = GetOuter ( ) ;
// Have our class or the mod-specific class determine what the current activity is.
Activity idealActivity = CalcMainActivity ( ) ;
# ifdef CLIENT_DLL
Activity oldActivity = m_eCurrentMainSequenceActivity ;
# endif
// Store our current activity so the aim and fire layers know what to do.
m_eCurrentMainSequenceActivity = idealActivity ;
// Export to our outer class..
int animDesired = SelectWeightedSequence ( TranslateActivity ( idealActivity ) ) ;
# if !defined( HL1_CLIENT_DLL ) && !defined ( HL1_DLL )
if ( pPlayer - > GetSequenceActivity ( pPlayer - > GetSequence ( ) ) = = pPlayer - > GetSequenceActivity ( animDesired ) )
return ;
# endif
if ( animDesired < 0 )
animDesired = 0 ;
pPlayer - > ResetSequence ( animDesired ) ;
# ifdef CLIENT_DLL
// If we went from idle to walk, reset the interpolation history.
// Kind of hacky putting this here.. it might belong outside the base class.
if ( ( oldActivity = = ACT_CROUCHIDLE | | oldActivity = = ACT_IDLE ) & &
( idealActivity = = ACT_WALK | | idealActivity = = ACT_RUN_CROUCH ) )
{
ResetGroundSpeed ( ) ;
}
# endif
}
void CBasePlayerAnimState : : UpdateAimSequenceLayers (
float flCycle ,
int iFirstLayer ,
bool bForceIdle ,
CSequenceTransitioner * pTransitioner ,
float flWeightScale
)
{
float flAimSequenceWeight = 1 ;
int iAimSequence = CalcAimLayerSequence ( & flCycle , & flAimSequenceWeight , bForceIdle ) ;
if ( iAimSequence = = - 1 )
iAimSequence = 0 ;
// Feed the current state of the animation parameters to the sequence transitioner.
// It will hand back either 1 or 2 animations in the queue to set, depending on whether
// it's transitioning or not. We just dump those into the animation layers.
pTransitioner - > CheckForSequenceChange (
m_pOuter - > GetModelPtr ( ) ,
iAimSequence ,
false , // don't force transitions on the same anim
true // yes, interpolate when transitioning
) ;
pTransitioner - > UpdateCurrent (
m_pOuter - > GetModelPtr ( ) ,
iAimSequence ,
flCycle ,
GetOuter ( ) - > GetPlaybackRate ( ) ,
gpGlobals - > curtime
) ;
CAnimationLayer * pDest0 = m_pOuter - > GetAnimOverlay ( iFirstLayer ) ;
CAnimationLayer * pDest1 = m_pOuter - > GetAnimOverlay ( iFirstLayer + 1 ) ;
if ( pTransitioner - > m_animationQueue . Count ( ) = = 1 )
{
// If only 1 animation, then blend it in fully.
CAnimationLayer * pSource0 = & pTransitioner - > m_animationQueue [ 0 ] ;
* pDest0 = * pSource0 ;
pDest0 - > m_flWeight = 1 ;
pDest1 - > m_flWeight = 0 ;
pDest0 - > m_nOrder = iFirstLayer ;
# ifndef CLIENT_DLL
pDest0 - > m_fFlags | = ANIM_LAYER_ACTIVE ;
# endif
}
else if ( pTransitioner - > m_animationQueue . Count ( ) > = 2 )
{
// The first one should be fading out. Fade in the new one inversely.
CAnimationLayer * pSource0 = & pTransitioner - > m_animationQueue [ 0 ] ;
CAnimationLayer * pSource1 = & pTransitioner - > m_animationQueue [ 1 ] ;
* pDest0 = * pSource0 ;
* pDest1 = * pSource1 ;
Assert ( pDest0 - > m_flWeight > = 0.0f & & pDest0 - > m_flWeight < = 1.0f ) ;
pDest1 - > m_flWeight = 1 - pDest0 - > m_flWeight ; // This layer just mirrors the other layer's weight (one fades in while the other fades out).
pDest0 - > m_nOrder = iFirstLayer ;
pDest1 - > m_nOrder = iFirstLayer + 1 ;
# ifndef CLIENT_DLL
pDest0 - > m_fFlags | = ANIM_LAYER_ACTIVE ;
pDest1 - > m_fFlags | = ANIM_LAYER_ACTIVE ;
# endif
}
pDest0 - > m_flWeight * = flWeightScale * flAimSequenceWeight ;
pDest0 - > m_flWeight = clamp ( ( float ) pDest0 - > m_flWeight , 0.0f , 1.0f ) ;
pDest1 - > m_flWeight * = flWeightScale * flAimSequenceWeight ;
pDest1 - > m_flWeight = clamp ( ( float ) pDest1 - > m_flWeight , 0.0f , 1.0f ) ;
pDest0 - > m_flCycle = pDest1 - > m_flCycle = flCycle ;
}
void CBasePlayerAnimState : : OptimizeLayerWeights ( int iFirstLayer , int nLayers )
{
int i ;
// Find the total weight of the blended layers, not including the idle layer (iFirstLayer)
float totalWeight = 0.0f ;
for ( i = 1 ; i < nLayers ; i + + )
{
CAnimationLayer * pLayer = m_pOuter - > GetAnimOverlay ( iFirstLayer + i ) ;
if ( pLayer - > IsActive ( ) & & pLayer - > m_flWeight > 0.0f )
{
totalWeight + = pLayer - > m_flWeight ;
}
}
// Set the idle layer's weight to be 1 minus the sum of other layer weights
CAnimationLayer * pLayer = m_pOuter - > GetAnimOverlay ( iFirstLayer ) ;
if ( pLayer - > IsActive ( ) & & pLayer - > m_flWeight > 0.0f )
{
pLayer - > m_flWeight = 1.0f - totalWeight ;
pLayer - > m_flWeight = MAX ( ( float ) pLayer - > m_flWeight , 0.0f ) ;
}
// This part is just an optimization. Since we have the walk/run animations weighted on top of
// the idle animations, all this does is disable the idle animations if the walk/runs are at
// full weighting, which is whenever a guy is at full speed.
//
// So it saves us blending a couple animation layers whenever a guy is walking or running full speed.
int iLastOne = - 1 ;
for ( i = 0 ; i < nLayers ; i + + )
{
CAnimationLayer * pLayer = m_pOuter - > GetAnimOverlay ( iFirstLayer + i ) ;
if ( pLayer - > IsActive ( ) & & pLayer - > m_flWeight > 0.99 )
iLastOne = i ;
}
if ( iLastOne ! = - 1 )
{
for ( int i = iLastOne - 1 ; i > = 0 ; i - - )
{
CAnimationLayer * pLayer = m_pOuter - > GetAnimOverlay ( iFirstLayer + i ) ;
# ifdef CLIENT_DLL
pLayer - > m_nOrder = CBaseAnimatingOverlay : : MAX_OVERLAYS ;
# else
pLayer - > m_nOrder . Set ( CBaseAnimatingOverlay : : MAX_OVERLAYS ) ;
pLayer - > m_fFlags = 0 ;
# endif
}
}
}
bool CBasePlayerAnimState : : ShouldBlendAimSequenceToIdle ( )
{
Activity act = GetCurrentMainSequenceActivity ( ) ;
return ( act = = ACT_RUN | | act = = ACT_WALK | | act = = ACT_RUNTOIDLE | | act = = ACT_RUN_CROUCH ) ;
}
void CBasePlayerAnimState : : ComputeAimSequence ( )
{
VPROF ( " CBasePlayerAnimState::ComputeAimSequence " ) ;
// Synchronize the lower and upper body cycles.
float flCycle = m_pOuter - > GetCycle ( ) ;
// Figure out the new cycle time.
UpdateAimSequenceLayers ( flCycle , AIMSEQUENCE_LAYER , true , & m_IdleSequenceTransitioner , 1 ) ;
if ( ShouldBlendAimSequenceToIdle ( ) )
{
// What we do here is blend between the idle upper body animation (like where he's got the dual elites
// held out in front of him but he's not moving) and his walk/run/crouchrun upper body animation,
// weighting it based on how fast he's moving. That way, when he's moving slowly, his upper
// body doesn't jiggle all around.
bool bIsMoving ;
float flPlaybackRate = CalcMovementPlaybackRate ( & bIsMoving ) ;
if ( bIsMoving )
UpdateAimSequenceLayers ( flCycle , AIMSEQUENCE_LAYER + 2 , false , & m_SequenceTransitioner , flPlaybackRate ) ;
}
OptimizeLayerWeights ( AIMSEQUENCE_LAYER , NUM_AIMSEQUENCE_LAYERS ) ;
}
int CBasePlayerAnimState : : CalcSequenceIndex ( const char * pBaseName , . . . )
{
char szFullName [ 512 ] ;
va_list marker ;
va_start ( marker , pBaseName ) ;
Q_vsnprintf ( szFullName , sizeof ( szFullName ) , pBaseName , marker ) ;
va_end ( marker ) ;
int iSequence = GetOuter ( ) - > LookupSequence ( szFullName ) ;
// Show warnings if we can't find anything here.
if ( iSequence = = - 1 )
{
static CUtlDict < int , int > dict ;
if ( dict . Find ( szFullName ) = = - 1 )
{
dict . Insert ( szFullName , 0 ) ;
Warning ( " CalcSequenceIndex: can't find '%s'. \n " , szFullName ) ;
}
iSequence = 0 ;
}
return iSequence ;
}
void CBasePlayerAnimState : : UpdateInterpolators ( )
{
VPROF ( " CBasePlayerAnimState::UpdateInterpolators " ) ;
// First, figure out their current max speed based on their current activity.
float flCurMaxSpeed = GetCurrentMaxGroundSpeed ( ) ;
m_flMaxGroundSpeed = flCurMaxSpeed ;
}
float CBasePlayerAnimState : : GetInterpolatedGroundSpeed ( )
{
return m_flMaxGroundSpeed ;
}
float CBasePlayerAnimState : : CalcMovementPlaybackRate ( bool * bIsMoving )
{
// Determine ideal playback rate
Vector vel ;
GetOuterAbsVelocity ( vel ) ;
float speed = vel . Length2D ( ) ;
bool isMoving = ( speed > MOVING_MINIMUM_SPEED ) ;
* bIsMoving = false ;
float flReturnValue = 1 ;
if ( isMoving & & CanThePlayerMove ( ) )
{
float flGroundSpeed = GetInterpolatedGroundSpeed ( ) ;
if ( flGroundSpeed < 0.001f )
{
flReturnValue = 0.01 ;
}
else
{
// Note this gets set back to 1.0 if sequence changes due to ResetSequenceInfo below
flReturnValue = speed / flGroundSpeed ;
flReturnValue = clamp ( flReturnValue , 0.01f , 10.f ) ; // don't go nuts here.
}
* bIsMoving = true ;
}
return flReturnValue ;
}
bool CBasePlayerAnimState : : CanThePlayerMove ( )
{
return true ;
}
void CBasePlayerAnimState : : ComputePlaybackRate ( )
{
VPROF ( " CBasePlayerAnimState::ComputePlaybackRate " ) ;
if ( m_AnimConfig . m_LegAnimType ! = LEGANIM_9WAY & & m_AnimConfig . m_LegAnimType ! = LEGANIM_8WAY )
{
// When using a 9-way blend, playback rate is always 1 and we just scale the pose params
// to speed up or slow down the animation.
bool bIsMoving ;
float flRate = CalcMovementPlaybackRate ( & bIsMoving ) ;
if ( bIsMoving )
GetOuter ( ) - > SetPlaybackRate ( flRate ) ;
else
GetOuter ( ) - > SetPlaybackRate ( 1 ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : CBasePlayer
//-----------------------------------------------------------------------------
CBaseAnimatingOverlay * CBasePlayerAnimState : : GetOuter ( ) const
{
return m_pOuter ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : dt -
//-----------------------------------------------------------------------------
void CBasePlayerAnimState : : EstimateYaw ( )
{
Vector est_velocity ;
GetOuterAbsVelocity ( est_velocity ) ;
float flLength = est_velocity . Length2D ( ) ;
if ( flLength > MOVING_MINIMUM_SPEED )
{
m_flGaitYaw = atan2 ( est_velocity [ 1 ] , est_velocity [ 0 ] ) ;
m_flGaitYaw = RAD2DEG ( m_flGaitYaw ) ;
m_flGaitYaw = AngleNormalize ( m_flGaitYaw ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Override for backpeddling
// Input : dt -
//-----------------------------------------------------------------------------
void CBasePlayerAnimState : : ComputePoseParam_MoveYaw ( CStudioHdr * pStudioHdr )
{
VPROF ( " CBasePlayerAnimState::ComputePoseParam_MoveYaw " ) ;
//Matt: Goldsrc style animations need to not rotate the model
if ( m_AnimConfig . m_LegAnimType = = LEGANIM_GOLDSRC )
{
# ifndef CLIENT_DLL
//Adrian: Make the model's angle match the legs so the hitboxes match on both sides.
GetOuter ( ) - > SetLocalAngles ( QAngle ( 0 , m_flCurrentFeetYaw , 0 ) ) ;
# endif
}
// If using goldsrc-style animations where he's moving in the direction that his feet are facing,
// we don't use move yaw.
if ( m_AnimConfig . m_LegAnimType ! = LEGANIM_9WAY & & m_AnimConfig . m_LegAnimType ! = LEGANIM_8WAY )
return ;
// view direction relative to movement
float flYaw ;
EstimateYaw ( ) ;
float ang = m_flEyeYaw ;
if ( ang > 180.0f )
{
ang - = 360.0f ;
}
else if ( ang < - 180.0f )
{
ang + = 360.0f ;
}
// calc side to side turning
flYaw = ang - m_flGaitYaw ;
// Invert for mapping into 8way blend
flYaw = - flYaw ;
flYaw = flYaw - ( int ) ( flYaw / 360 ) * 360 ;
if ( flYaw < - 180 )
{
flYaw = flYaw + 360 ;
}
else if ( flYaw > 180 )
{
flYaw = flYaw - 360 ;
}
if ( m_AnimConfig . m_LegAnimType = = LEGANIM_9WAY )
{
# ifndef CLIENT_DLL
//Adrian: Make the model's angle match the legs so the hitboxes match on both sides.
GetOuter ( ) - > SetLocalAngles ( QAngle ( 0 , m_flCurrentFeetYaw , 0 ) ) ;
# endif
int iMoveX = GetOuter ( ) - > LookupPoseParameter ( pStudioHdr , " move_x " ) ;
int iMoveY = GetOuter ( ) - > LookupPoseParameter ( pStudioHdr , " move_y " ) ;
if ( iMoveX < 0 | | iMoveY < 0 )
return ;
bool bIsMoving ;
float flPlaybackRate = CalcMovementPlaybackRate ( & bIsMoving ) ;
// Setup the 9-way blend parameters based on our speed and direction.
Vector2D vCurMovePose ( 0 , 0 ) ;
if ( bIsMoving )
{
vCurMovePose . x = cos ( DEG2RAD ( flYaw ) ) * flPlaybackRate ;
vCurMovePose . y = - sin ( DEG2RAD ( flYaw ) ) * flPlaybackRate ;
}
GetOuter ( ) - > SetPoseParameter ( pStudioHdr , iMoveX , vCurMovePose . x ) ;
GetOuter ( ) - > SetPoseParameter ( pStudioHdr , iMoveY , vCurMovePose . y ) ;
m_vLastMovePose = vCurMovePose ;
}
else
{
int iMoveYaw = GetOuter ( ) - > LookupPoseParameter ( pStudioHdr , " move_yaw " ) ;
if ( iMoveYaw > = 0 )
{
GetOuter ( ) - > SetPoseParameter ( pStudioHdr , iMoveYaw , flYaw ) ;
m_flLastMoveYaw = flYaw ;
// Now blend in his idle animation.
// This makes the 8-way blend act like a 9-way blend by blending to
// an idle sequence as he slows down.
# if defined(CLIENT_DLL)
bool bIsMoving ;
CAnimationLayer * pLayer = m_pOuter - > GetAnimOverlay ( MAIN_IDLE_SEQUENCE_LAYER ) ;
pLayer - > m_flWeight = 1 - CalcMovementPlaybackRate ( & bIsMoving ) ;
if ( ! bIsMoving )
{
pLayer - > m_flWeight = 1 ;
}
if ( ShouldChangeSequences ( ) )
{
// Whenever this layer stops blending, we can choose a new idle sequence to blend to, so he
// doesn't always use the same idle.
if ( pLayer - > m_flWeight < 0.02f | | m_iCurrent8WayIdleSequence = = - 1 )
{
m_iCurrent8WayIdleSequence = m_pOuter - > SelectWeightedSequence ( ACT_IDLE ) ;
m_iCurrent8WayCrouchIdleSequence = m_pOuter - > SelectWeightedSequence ( ACT_CROUCHIDLE ) ;
}
if ( m_eCurrentMainSequenceActivity = = ACT_CROUCHIDLE | | m_eCurrentMainSequenceActivity = = ACT_RUN_CROUCH )
pLayer - > m_nSequence = m_iCurrent8WayCrouchIdleSequence ;
else
pLayer - > m_nSequence = m_iCurrent8WayIdleSequence ;
}
pLayer - > m_flPlaybackRate = 1 ;
pLayer - > m_flCycle + = m_pOuter - > GetSequenceCycleRate ( pStudioHdr , pLayer - > m_nSequence ) * gpGlobals - > frametime ;
pLayer - > m_flCycle = fmod ( pLayer - > m_flCycle , 1 ) ;
pLayer - > m_nOrder = MAIN_IDLE_SEQUENCE_LAYER ;
# endif
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayerAnimState : : ComputePoseParam_BodyPitch ( CStudioHdr * pStudioHdr )
{
VPROF ( " CBasePlayerAnimState::ComputePoseParam_BodyPitch " ) ;
// Get pitch from v_angle
float flPitch = m_flEyePitch ;
if ( flPitch > 180.0f )
{
flPitch - = 360.0f ;
}
flPitch = clamp ( flPitch , - 90.f , 90.f ) ;
// See if we have a blender for pitch
int pitch = GetOuter ( ) - > LookupPoseParameter ( pStudioHdr , " body_pitch " ) ;
if ( pitch < 0 )
return ;
GetOuter ( ) - > SetPoseParameter ( pStudioHdr , pitch , flPitch ) ;
g_flLastBodyPitch = flPitch ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : goal -
// maxrate -
// dt -
// current -
// Output : int
//-----------------------------------------------------------------------------
int CBasePlayerAnimState : : ConvergeAngles ( float goal , float maxrate , float maxgap , float dt , float & current )
{
int direction = TURN_NONE ;
float anglediff = goal - current ;
anglediff = AngleNormalize ( anglediff ) ;
float anglediffabs = fabs ( anglediff ) ;
float scale = 1.0f ;
if ( anglediffabs < = FADE_TURN_DEGREES )
{
scale = anglediffabs / FADE_TURN_DEGREES ;
// Always do at least a bit of the turn ( 1% )
scale = clamp ( scale , 0.01f , 1.0f ) ;
}
float maxmove = maxrate * dt * scale ;
if ( anglediffabs > maxgap )
{
// gap is too big, jump
maxmove = ( anglediffabs - maxgap ) ;
}
if ( anglediffabs < maxmove )
{
// we are close enought, just set the final value
current = goal ;
}
else
{
// adjust value up or down
if ( anglediff > 0 )
{
current + = maxmove ;
direction = TURN_LEFT ;
}
else
{
current - = maxmove ;
direction = TURN_RIGHT ;
}
}
current = AngleNormalize ( current ) ;
return direction ;
}
void CBasePlayerAnimState : : ComputePoseParam_BodyYaw ( )
{
VPROF ( " CBasePlayerAnimState::ComputePoseParam_BodyYaw " ) ;
// Find out which way he's running (m_flEyeYaw is the way he's looking).
Vector vel ;
GetOuterAbsVelocity ( vel ) ;
bool bIsMoving = vel . Length2D ( ) > MOVING_MINIMUM_SPEED ;
// If we just initialized this guy (maybe he just came into the PVS), then immediately
// set his feet in the right direction, otherwise they'll spin around from 0 to the
// right direction every time someone switches spectator targets.
if ( ! m_bCurrentFeetYawInitialized )
{
m_bCurrentFeetYawInitialized = true ;
m_flGoalFeetYaw = m_flCurrentFeetYaw = m_flEyeYaw ;
m_flLastTurnTime = 0.0f ;
}
else if ( bIsMoving )
{
// player is moving, feet yaw = aiming yaw
if ( m_AnimConfig . m_LegAnimType = = LEGANIM_9WAY | | m_AnimConfig . m_LegAnimType = = LEGANIM_8WAY )
{
// His feet point in the direction his eyes are, but they can run in any direction.
m_flGoalFeetYaw = m_flEyeYaw ;
}
else
{
m_flGoalFeetYaw = RAD2DEG ( atan2 ( vel . y , vel . x ) ) ;
// If he's running backwards, flip his feet backwards.
Vector vEyeYaw ( cos ( DEG2RAD ( m_flEyeYaw ) ) , sin ( DEG2RAD ( m_flEyeYaw ) ) , 0 ) ;
Vector vFeetYaw ( cos ( DEG2RAD ( m_flGoalFeetYaw ) ) , sin ( DEG2RAD ( m_flGoalFeetYaw ) ) , 0 ) ;
if ( vEyeYaw . Dot ( vFeetYaw ) < - 0.01 )
{
m_flGoalFeetYaw + = 180 ;
}
}
}
else if ( ( gpGlobals - > curtime - m_flLastTurnTime ) > mp_facefronttime . GetFloat ( ) )
{
// player didn't move & turn for quite some time
m_flGoalFeetYaw = m_flEyeYaw ;
}
else
{
// If he's rotated his view further than the model can turn, make him face forward.
float flDiff = AngleNormalize ( m_flGoalFeetYaw - m_flEyeYaw ) ;
if ( fabs ( flDiff ) > m_AnimConfig . m_flMaxBodyYawDegrees )
{
if ( flDiff > 0 )
m_flGoalFeetYaw - = m_AnimConfig . m_flMaxBodyYawDegrees ;
else
m_flGoalFeetYaw + = m_AnimConfig . m_flMaxBodyYawDegrees ;
}
}
m_flGoalFeetYaw = AngleNormalize ( m_flGoalFeetYaw ) ;
if ( m_flCurrentFeetYaw ! = m_flGoalFeetYaw )
{
ConvergeAngles ( m_flGoalFeetYaw , mp_feetyawrate . GetFloat ( ) , m_AnimConfig . m_flMaxBodyYawDegrees ,
gpGlobals - > frametime , m_flCurrentFeetYaw ) ;
m_flLastTurnTime = gpGlobals - > curtime ;
}
float flCurrentTorsoYaw = AngleNormalize ( m_flEyeYaw - m_flCurrentFeetYaw ) ;
// Rotate entire body into position
m_angRender [ YAW ] = m_flCurrentFeetYaw ;
m_angRender [ PITCH ] = m_angRender [ ROLL ] = 0 ;
SetOuterBodyYaw ( flCurrentTorsoYaw ) ;
g_flLastBodyYaw = flCurrentTorsoYaw ;
}
float CBasePlayerAnimState : : SetOuterBodyYaw ( float flValue )
{
int body_yaw = GetOuter ( ) - > LookupPoseParameter ( " body_yaw " ) ;
if ( body_yaw < 0 )
{
return 0 ;
}
SetOuterPoseParameter ( body_yaw , flValue ) ;
return flValue ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : activity -
// Output : Activity
//-----------------------------------------------------------------------------
Activity CBasePlayerAnimState : : BodyYawTranslateActivity ( Activity activity )
{
// Not even standing still, sigh
if ( activity ! = ACT_IDLE )
return activity ;
// Not turning
switch ( m_nTurningInPlace )
{
default :
case TURN_NONE :
return activity ;
case TURN_RIGHT :
case TURN_LEFT :
return mp_ik . GetBool ( ) ? ACT_TURN : activity ;
}
Assert ( 0 ) ;
return activity ;
}
const QAngle & CBasePlayerAnimState : : GetRenderAngles ( )
{
return m_angRender ;
}
void CBasePlayerAnimState : : GetOuterAbsVelocity ( Vector & vel ) const
{
# if defined( CLIENT_DLL )
GetOuter ( ) - > EstimateAbsVelocity ( vel ) ;
# else
vel = GetOuter ( ) - > GetAbsVelocity ( ) ;
# endif
}
float CBasePlayerAnimState : : GetOuterXYSpeed ( ) const
{
Vector vel ;
GetOuterAbsVelocity ( vel ) ;
return vel . Length2D ( ) ;
}
// -----------------------------------------------------------------------------
void CBasePlayerAnimState : : AnimStateLog ( const char * pMsg , . . . )
{
// Format the string.
char str [ 4096 ] ;
va_list marker ;
va_start ( marker , pMsg ) ;
Q_vsnprintf ( str , sizeof ( str ) , pMsg , marker ) ;
va_end ( marker ) ;
// Log it?
if ( showanimstate_log . GetInt ( ) = = 1 | | showanimstate_log . GetInt ( ) = = 3 )
{
Msg ( " %s " , str ) ;
}
if ( showanimstate_log . GetInt ( ) > 1 )
{
# ifdef CLIENT_DLL
const char * fname = " AnimStateClient.log " ;
# else
const char * fname = " AnimStateServer.log " ;
# endif
static FileHandle_t hFile = filesystem - > Open ( fname , " wt " ) ;
filesystem - > FPrintf ( hFile , " %s " , str ) ;
filesystem - > Flush ( hFile ) ;
}
}
// -----------------------------------------------------------------------------
void CBasePlayerAnimState : : AnimStatePrintf ( int iLine , const char * pMsg , . . . )
{
// Format the string.
char str [ 4096 ] ;
va_list marker ;
va_start ( marker , pMsg ) ;
Q_vsnprintf ( str , sizeof ( str ) , pMsg , marker ) ;
va_end ( marker ) ;
// Show it with Con_NPrintf.
engine - > Con_NPrintf ( iLine , " %s " , str ) ;
// Log it.
AnimStateLog ( " %s \n " , str ) ;
}
// -----------------------------------------------------------------------------
void CBasePlayerAnimState : : DebugShowAnimState ( int iStartLine )
{
Vector vOuterVel ;
GetOuterAbsVelocity ( vOuterVel ) ;
int iLine = iStartLine ;
AnimStatePrintf ( iLine + + , " main: %s(%d), cycle: %.2f cyclerate: %.2f playbackrate: %.2f \n " ,
GetSequenceName ( m_pOuter - > GetModelPtr ( ) , m_pOuter - > GetSequence ( ) ) ,
m_pOuter - > GetSequence ( ) ,
m_pOuter - > GetCycle ( ) ,
m_pOuter - > GetSequenceCycleRate ( m_pOuter - > GetModelPtr ( ) , m_pOuter - > GetSequence ( ) ) ,
m_pOuter - > GetPlaybackRate ( )
) ;
if ( m_AnimConfig . m_LegAnimType = = LEGANIM_8WAY )
{
CAnimationLayer * pLayer = m_pOuter - > GetAnimOverlay ( MAIN_IDLE_SEQUENCE_LAYER ) ;
AnimStatePrintf ( iLine + + , " idle: %s, weight: %.2f \n " ,
GetSequenceName ( m_pOuter - > GetModelPtr ( ) , pLayer - > m_nSequence ) ,
( float ) pLayer - > m_flWeight ) ;
}
for ( int i = 0 ; i < m_pOuter - > GetNumAnimOverlays ( ) - 1 ; i + + )
{
CAnimationLayer * pLayer = m_pOuter - > GetAnimOverlay ( AIMSEQUENCE_LAYER + i ) ;
# ifdef CLIENT_DLL
AnimStatePrintf ( iLine + + , " %s(%d), weight: %.2f, cycle: %.2f, order (%d), aim (%d) " ,
! pLayer - > IsActive ( ) ? " -- " : ( pLayer - > m_nSequence = = 0 ? " -- " : GetSequenceName ( m_pOuter - > GetModelPtr ( ) , pLayer - > m_nSequence ) ) ,
! pLayer - > IsActive ( ) ? 0 : ( int ) pLayer - > m_nSequence ,
! pLayer - > IsActive ( ) ? 0 : ( float ) pLayer - > m_flWeight ,
! pLayer - > IsActive ( ) ? 0 : ( float ) pLayer - > m_flCycle ,
! pLayer - > IsActive ( ) ? 0 : ( int ) pLayer - > m_nOrder ,
i
) ;
# else
AnimStatePrintf ( iLine + + , " %s(%d), flags (%d), weight: %.2f, cycle: %.2f, order (%d), aim (%d) " ,
! pLayer - > IsActive ( ) ? " -- " : ( pLayer - > m_nSequence = = 0 ? " -- " : GetSequenceName ( m_pOuter - > GetModelPtr ( ) , pLayer - > m_nSequence ) ) ,
! pLayer - > IsActive ( ) ? 0 : ( int ) pLayer - > m_nSequence ,
! pLayer - > IsActive ( ) ? 0 : ( int ) pLayer - > m_fFlags , // Doesn't exist on client
! pLayer - > IsActive ( ) ? 0 : ( float ) pLayer - > m_flWeight ,
! pLayer - > IsActive ( ) ? 0 : ( float ) pLayer - > m_flCycle ,
! pLayer - > IsActive ( ) ? 0 : ( int ) pLayer - > m_nOrder ,
i
) ;
# endif
}
AnimStatePrintf ( iLine + + , " vel: %.2f, time: %.2f, max: %.2f, animspeed: %.2f " ,
vOuterVel . Length2D ( ) , gpGlobals - > curtime , GetInterpolatedGroundSpeed ( ) , m_pOuter - > GetSequenceGroundSpeed ( m_pOuter - > GetSequence ( ) ) ) ;
if ( m_AnimConfig . m_LegAnimType = = LEGANIM_8WAY )
{
AnimStatePrintf ( iLine + + , " ent yaw: %.2f, body_yaw: %.2f, move_yaw: %.2f, gait_yaw: %.2f, body_pitch: %.2f " ,
m_angRender [ YAW ] , g_flLastBodyYaw , m_flLastMoveYaw , m_flGaitYaw , g_flLastBodyPitch ) ;
}
else
{
AnimStatePrintf ( iLine + + , " ent yaw: %.2f, body_yaw: %.2f, body_pitch: %.2f, move_x: %.2f, move_y: %.2f " ,
m_angRender [ YAW ] , g_flLastBodyYaw , g_flLastBodyPitch , m_vLastMovePose . x , m_vLastMovePose . y ) ;
}
// Draw a red triangle on the ground for the eye yaw.
float flBaseSize = 10 ;
float flHeight = 80 ;
Vector vBasePos = GetOuter ( ) - > GetAbsOrigin ( ) + Vector ( 0 , 0 , 3 ) ;
QAngle angles ( 0 , 0 , 0 ) ;
angles [ YAW ] = m_flEyeYaw ;
Vector vForward , vRight , vUp ;
AngleVectors ( angles , & vForward , & vRight , & vUp ) ;
debugoverlay - > AddTriangleOverlay ( vBasePos + vRight * flBaseSize / 2 , vBasePos - vRight * flBaseSize / 2 , vBasePos + vForward * flHeight , 255 , 0 , 0 , 255 , false , 0.01 ) ;
// Draw a blue triangle on the ground for the body yaw.
angles [ YAW ] = m_angRender [ YAW ] ;
AngleVectors ( angles , & vForward , & vRight , & vUp ) ;
debugoverlay - > AddTriangleOverlay ( vBasePos + vRight * flBaseSize / 2 , vBasePos - vRight * flBaseSize / 2 , vBasePos + vForward * flHeight , 0 , 0 , 255 , 255 , false , 0.01 ) ;
}
// -----------------------------------------------------------------------------
void CBasePlayerAnimState : : DebugShowAnimStateFull ( int iStartLine )
{
AnimStateLog ( " ----------------- frame %d ----------------- \n " , gpGlobals - > framecount ) ;
DebugShowAnimState ( iStartLine ) ;
AnimStateLog ( " -------------------------------------------- \n \n " ) ;
}
// -----------------------------------------------------------------------------
int CBasePlayerAnimState : : SelectWeightedSequence ( Activity activity )
{
return GetOuter ( ) - > SelectWeightedSequence ( activity ) ;
}