diff options
Diffstat (limited to 'game/shared/tf/tf_playeranimstate.cpp')
| -rw-r--r-- | game/shared/tf/tf_playeranimstate.cpp | 1445 |
1 files changed, 1445 insertions, 0 deletions
diff --git a/game/shared/tf/tf_playeranimstate.cpp b/game/shared/tf/tf_playeranimstate.cpp new file mode 100644 index 0000000..3639b74 --- /dev/null +++ b/game/shared/tf/tf_playeranimstate.cpp @@ -0,0 +1,1445 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "tier0/vprof.h" +#include "animation.h" +#include "studio.h" +#include "apparent_velocity_helper.h" +#include "utldict.h" +#include "tf_playeranimstate.h" +#include "base_playeranimstate.h" +#include "datacache/imdlcache.h" +#include "tf_gamerules.h" +#include "in_buttons.h" +#include "debugoverlay_shared.h" +#include "tf_weapon_passtime_gun.h" + +#ifdef CLIENT_DLL +#include "c_tf_player.h" +#include "c_func_capture_zone.h" +#include "tf_gcmessages.h" + +#define CCaptureZone C_CaptureZone +#else +#include "tf_player.h" +#include "func_capture_zone.h" +#endif + +#define TF_RUN_SPEED 320.0f +#define TF_WALK_SPEED 75.0f +#define TF_CROUCHWALK_SPEED 110.0f + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pPlayer - +// Output : CMultiPlayerAnimState* +//----------------------------------------------------------------------------- +CTFPlayerAnimState* CreateTFPlayerAnimState( CTFPlayer *pPlayer ) +{ + MDLCACHE_CRITICAL_SECTION(); + + // Setup the movement data. + MultiPlayerMovementData_t movementData; + movementData.m_flBodyYawRate = 720.0f; + movementData.m_flRunSpeed = TF_RUN_SPEED; + movementData.m_flWalkSpeed = TF_WALK_SPEED; + movementData.m_flSprintSpeed = -1.0f; + + // Create animation state for this player. + CTFPlayerAnimState *pRet = new CTFPlayerAnimState( pPlayer, movementData ); + + // Specific TF player initialization. + pRet->InitTF( pPlayer ); + + return pRet; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +CTFPlayerAnimState::CTFPlayerAnimState() +{ + m_pTFPlayer = NULL; + + // Don't initialize TF specific variables here. Init them in InitTF() +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pPlayer - +// &movementData - +//----------------------------------------------------------------------------- +CTFPlayerAnimState::CTFPlayerAnimState( CBasePlayer *pPlayer, MultiPlayerMovementData_t &movementData ) +: CMultiPlayerAnimState( pPlayer, movementData ) +{ + m_pTFPlayer = NULL; + + // Don't initialize TF specific variables here. Init them in InitTF() +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +CTFPlayerAnimState::~CTFPlayerAnimState() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Initialize Team Fortress specific animation state. +// Input : *pPlayer - +//----------------------------------------------------------------------------- +void CTFPlayerAnimState::InitTF( CTFPlayer *pPlayer ) +{ + m_pTFPlayer = pPlayer; + m_bInAirWalk = false; + m_flHoldDeployedPoseUntilTime = 0.0f; + m_flTauntMoveX = 0.f; + m_flTauntMoveY = 0.f; + m_vecSmoothedUp = Vector( 0.f, 0.f, 1.f ); + m_flVehicleLeanVel = 0.f; + m_flVehicleLeanPos = 0.f; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFPlayerAnimState::ClearAnimationState( void ) +{ + m_bInAirWalk = false; + + BaseClass::ClearAnimationState(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : actDesired - +// Output : Activity +//----------------------------------------------------------------------------- +Activity CTFPlayerAnimState::TranslateActivity( Activity actDesired ) +{ + Activity translateActivity = BaseClass::TranslateActivity( actDesired ); + + translateActivity = ActivityOverride( translateActivity, NULL ); + + CBaseCombatWeapon *pWeapon = GetTFPlayer()->GetActiveWeapon(); + if ( pWeapon ) + { + translateActivity = pWeapon->ActivityOverride( translateActivity, NULL ); + + CEconItemView *pEconItemView = pWeapon->GetAttributeContainer()->GetItem(); + if ( pEconItemView ) + { + translateActivity = pEconItemView->GetStaticData()->GetActivityOverride( GetTFPlayer()->GetTeamNumber(), translateActivity ); + } + } + + CTFPlayer *pPlayer = GetTFPlayer(); + if ( pPlayer->m_Shared.InCond( TF_COND_COMPETITIVE_WINNER ) ) + { + if ( translateActivity == ACT_MP_STAND_PRIMARY || + ( pPlayer->IsPlayerClass( TF_CLASS_SPY ) && ( translateActivity == ACT_MP_STAND_MELEE ) ) || + ( pPlayer->IsPlayerClass( TF_CLASS_DEMOMAN ) && ( translateActivity == ACT_MP_STAND_SECONDARY ) ) ) + { + translateActivity = ACT_MP_COMPETITIVE_WINNERSTATE; + } + } + + return translateActivity; +} + + +static acttable_t s_acttableKartState[] = +{ + { ACT_MP_STAND_IDLE, ACT_KART_IDLE, false }, + { ACT_MP_RUN, ACT_KART_IDLE, false }, + { ACT_MP_WALK, ACT_KART_IDLE, false }, + { ACT_MP_AIRWALK, ACT_KART_IDLE, false }, + { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_KART_ACTION_SHOOT, false }, + { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_KART_ACTION_SHOOT, false }, + { ACT_MP_JUMP_START, ACT_KART_JUMP_START, false }, + { ACT_MP_JUMP_FLOAT, ACT_KART_JUMP_FLOAT, false }, + { ACT_MP_JUMP_LAND, ACT_KART_JUMP_LAND, false }, +}; + +static acttable_t s_acttableLoserState[] = +{ + { ACT_MP_STAND_IDLE, ACT_MP_STAND_LOSERSTATE, false }, + { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_LOSERSTATE, false }, + { ACT_MP_RUN, ACT_MP_RUN_LOSERSTATE, false }, + { ACT_MP_WALK, ACT_MP_WALK_LOSERSTATE, false }, + { ACT_MP_AIRWALK, ACT_MP_AIRWALK_LOSERSTATE, false }, + { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_LOSERSTATE, false }, + { ACT_MP_JUMP, ACT_MP_JUMP_LOSERSTATE, false }, + { ACT_MP_JUMP_START, ACT_MP_JUMP_START_LOSERSTATE, false }, + { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_LOSERSTATE, false }, + { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_LOSERSTATE, false }, + { ACT_MP_SWIM, ACT_MP_SWIM_LOSERSTATE, false }, + { ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_LOSERSTATE,false }, +}; + +static acttable_t s_acttableCompetitiveLoserState[] = +{ + { ACT_MP_STAND_IDLE, ACT_MP_COMPETITIVE_LOSERSTATE, false }, +}; + +static acttable_t s_acttableBuildingDeployed[] = +{ + { ACT_MP_STAND_IDLE, ACT_MP_STAND_BUILDING_DEPLOYED, false }, + { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_BUILDING_DEPLOYED, false }, + { ACT_MP_RUN, ACT_MP_RUN_BUILDING_DEPLOYED, false }, + { ACT_MP_WALK, ACT_MP_WALK_BUILDING_DEPLOYED, false }, + { ACT_MP_AIRWALK, ACT_MP_AIRWALK_BUILDING_DEPLOYED, false }, + { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_BUILDING_DEPLOYED, false }, + { ACT_MP_JUMP, ACT_MP_JUMP_BUILDING_DEPLOYED, false }, + { ACT_MP_JUMP_START, ACT_MP_JUMP_START_BUILDING_DEPLOYED, false }, + { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_BUILDING_DEPLOYED, false }, + { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_BUILDING_DEPLOYED, false }, + { ACT_MP_SWIM, ACT_MP_SWIM_BUILDING_DEPLOYED, false }, + + { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_BUILDING_DEPLOYED, false }, + { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_BUILDING_DEPLOYED, false }, + { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_BUILDING_DEPLOYED, false }, + { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_BUILDING_DEPLOYED, false }, + + { ACT_MP_ATTACK_STAND_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING_DEPLOYED, false }, + { ACT_MP_ATTACK_CROUCH_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING_DEPLOYED, false }, + { ACT_MP_ATTACK_SWIM_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING_DEPLOYED, false }, + { ACT_MP_ATTACK_AIRWALK_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING_DEPLOYED, false }, + + { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_BUILDING, false }, + { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_BUILDING, false }, + { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_BUILDING, false }, + { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_BUILDING, false }, + { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_BUILDING, false }, + { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_BUILDING, false }, +}; + +Activity CTFPlayerAnimState::ActivityOverride( Activity baseAct, bool *pRequired ) +{ + acttable_t *pTable = NULL; + int iActivityCount = 0; + + CTFPlayer *pPlayer = GetTFPlayer(); + + // Override if we're in a kart + if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) ) + { + pTable = s_acttableKartState; + iActivityCount = ARRAYSIZE( s_acttableKartState ); + } + else + { + if ( pPlayer->m_Shared.InCond( TF_COND_COMPETITIVE_LOSER ) ) + { + iActivityCount = ARRAYSIZE( s_acttableCompetitiveLoserState ); + pTable = s_acttableCompetitiveLoserState; + } + else if ( pPlayer->m_Shared.IsLoser() ) + { + iActivityCount = ARRAYSIZE( s_acttableLoserState ); + pTable = s_acttableLoserState; + } + else if ( pPlayer->m_Shared.IsCarryingObject() ) + { + iActivityCount = ARRAYSIZE( s_acttableBuildingDeployed ); + pTable = s_acttableBuildingDeployed; + } + } + + for ( int i = 0; i < iActivityCount; i++ ) + { + const acttable_t& act = pTable[i]; + if ( baseAct == act.baseAct ) + { + if (pRequired) + { + *pRequired = act.required; + } + return (Activity)act.weaponAct; + } + } + + return baseAct; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CTFPlayerAnimState::ShouldUpdateAnimState( void ) +{ + CTFPlayer *pTFPlayer = GetTFPlayer(); + if ( pTFPlayer ) + { + // Stop animating if we have a custom player model that doesn't use the normal class animations + if ( pTFPlayer->GetPlayerClass()->HasCustomModel() && !pTFPlayer->GetPlayerClass()->CustomModelUsesClassAnimations() ) + return false; + } + + return BaseClass::ShouldUpdateAnimState(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFPlayerAnimState::GetOuterAbsVelocity( Vector& vel ) +{ +#ifdef CLIENT_DLL + if ( IsItemTestingBot() ) + { + switch ( TFGameRules()->ItemTesting_GetBotAnim() ) + { + default: + case TI_BOTANIM_IDLE: + case TI_BOTANIM_CROUCH: + case TI_BOTANIM_JUMP: + break; + + case TI_BOTANIM_CROUCH_WALK: + case TI_BOTANIM_RUN: + { + QAngle angles( 0, 0, 0 ); + angles[YAW] = m_angRender[YAW]; + Vector vForward, vRight, vUp; + AngleVectors( angles, &vForward, &vRight, &vUp ); + vel = vForward * GetCurrentMaxGroundSpeed(); + } + break; + } + + return; + } +#endif + + BaseClass::GetOuterAbsVelocity( vel ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFPlayerAnimState::Update( float eyeYaw, float eyePitch ) +{ + // Profile the animation update. + VPROF( "CMultiPlayerAnimState::Update" ); + + // Get the TF player. + CTFPlayer *pTFPlayer = GetTFPlayer(); + if ( !pTFPlayer ) + return; + + // Get the studio header for the player. + CStudioHdr *pStudioHdr = pTFPlayer->GetModelPtr(); + if ( !pStudioHdr ) + return; + + if ( pTFPlayer->GetPlayerClass()->HasCustomModel() ) + { + if ( !pTFPlayer->GetPlayerClass()->CustomModelUsesClassAnimations() ) + { + if ( pTFPlayer->GetPlayerClass()->CustomModelRotates() ) + { + if ( pTFPlayer->GetPlayerClass()->CustomModelRotationSet() ) + { + QAngle angRot = pTFPlayer->GetPlayerClass()->GetCustomModelRotation(); + m_angRender = angRot; + } + else + { + m_angRender = vec3_angle; + m_angRender[YAW] = AngleNormalize( eyeYaw ); + } + } + + // Restart our animation whenever we change models + if ( pTFPlayer->GetPlayerClass()->CustomModelHasChanged() ) + { + RestartMainSequence(); + } + + ClearAnimationState(); + return; + } + } + + // Check to see if we should be updating the animation state - dead, ragdolled? + if ( !ShouldUpdateAnimState() ) + { + ClearAnimationState(); + return; + } + + // Store the eye angles. + m_flEyeYaw = AngleNormalize( eyeYaw ); + m_flEyePitch = AngleNormalize( eyePitch ); + + // Compute the player sequences. + ComputeSequences( pStudioHdr ); + + CTFPlayer *pTauntPartner = pTFPlayer->GetTauntPartner(); + + Vector vPositionToFace = ( pTauntPartner ? pTauntPartner->GetAbsOrigin() : vec3_origin ); + bool bInTaunt = pTFPlayer->m_Shared.InCond( TF_COND_TAUNTING ); + bool bInKart = pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ); + bool bIsImmobilized = bInTaunt || pTFPlayer->m_Shared.IsControlStunned(); + + if ( SetupPoseParameters( pStudioHdr ) ) + { + if ( !bIsImmobilized ) + { + // Pose parameter - what direction are the player's legs running in. + ComputePoseParam_MoveYaw( pStudioHdr ); + } + + if ( bInTaunt ) + { + // If you are forcing aim yaw, your code is almost definitely broken if you don't include a delay between + // teleporting and forcing yaw. This is due to an unfortunate interaction between the command lookback window, + // and the fact that m_flEyeYaw is never propogated from the server to the client. + // TODO: Fix this after Halloween 2014. + m_bForceAimYaw = true; + m_flEyeYaw = pTFPlayer->GetTauntYaw(); + + Taunt_ComputePoseParam_MoveX( pStudioHdr ); + Taunt_ComputePoseParam_MoveY( pStudioHdr ); + } + else if ( bInKart ) + { + // If you are forcing aim yaw, your code is almost definitely broken if you don't include a delay between + // teleporting and forcing yaw. This is due to an unfortunate interaction between the command lookback window, + // and the fact that m_flEyeYaw is never propogated from the server to the client. + // TODO: Fix this after Halloween 2014. + m_bForceAimYaw = true; // This makes it so our "legs" dont lag behind our eyes when standing still. + Vehicle_ComputePoseParam_MoveYaw( pStudioHdr ); + Vehicle_ComputePoseParam_AccelLean( pStudioHdr ); + + // Trace down a bit for the ground + trace_t tr; + //UTIL_TraceLine( pTFPlayer->GetAbsOrigin(), pTFPlayer->GetAbsOrigin() - Vector(0,0,20), MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr ); + UTIL_TraceLine( pTFPlayer->GetAbsOrigin(), pTFPlayer->GetAbsOrigin() - Vector(0,0,64), MASK_SOLID, pTFPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); + + // Use the ground normal if we hit, else abs up + Vector vSurfaceNormal = tr.DidHit() ? tr.plane.normal : Vector( 0.f, 0.f, 1.f ); + + // Have smoothed up approach the surface normal + m_vecSmoothedUp[ 0 ] = Approach( vSurfaceNormal[ 0 ], m_vecSmoothedUp[ 0 ], 0.2f * gpGlobals->frametime ); + m_vecSmoothedUp[ 1 ] = Approach( vSurfaceNormal[ 1 ], m_vecSmoothedUp[ 1 ], 0.2f * gpGlobals->frametime ); + m_vecSmoothedUp[ 2 ] = Approach( vSurfaceNormal[ 2 ], m_vecSmoothedUp[ 2 ], 0.2f * gpGlobals->frametime ); + + // Get player's forward + Vector vOldForward; + QAngle vTauntAngles = pTFPlayer->GetAbsAngles(); + vTauntAngles[ YAW ] = pTFPlayer->GetTauntYaw(); + AngleVectors( vTauntAngles, &vOldForward, NULL, NULL ); + + // Construct basis + Vector vRight = vOldForward.Cross( m_vecSmoothedUp ); + Vector vForward = m_vecSmoothedUp.Cross( vRight ); + // Set angles + VectorAngles( vForward, m_vecSmoothedUp, m_angRender ); + +#if 0 + if ( tr.DidHit() ) + { +#ifdef GAME_DLL + NDebugOverlay::Line( tr.endpos, tr.endpos + tr.plane.normal * 100, 255, 0, 0, true, 1.f ); + NDebugOverlay::Line( pTFPlayer->GetAbsOrigin(), pTFPlayer->GetAbsOrigin() + m_vecSmoothedUp * 100, 255, 0, 0, true, 1.f ); + NDebugOverlay::Line( pTFPlayer->GetAbsOrigin(), pTFPlayer->GetAbsOrigin() + vForward * 100, 255, 0, 0, true, 1.f ); +#else + NDebugOverlay::Line( tr.endpos, tr.endpos + tr.plane.normal * 100, 0, 0, 255, true, 1.f ); + NDebugOverlay::Line( pTFPlayer->GetAbsOrigin(), pTFPlayer->GetAbsOrigin() + m_vecSmoothedUp * 100, 0, 0, 255, true, 1.f ); + NDebugOverlay::Line( pTFPlayer->GetAbsOrigin(), pTFPlayer->GetAbsOrigin() + vForward * 100, 0, 0, 255, true, 1.f ); +#endif + } +#endif + } + else if ( TFGameRules()->PlayersAreOnMatchSummaryStage() ) + { + m_bForceAimYaw = true; + m_flEyeYaw = pTFPlayer->GetTauntYaw(); + } + + if ( !bIsImmobilized || bInTaunt || bInKart ) + { + // Pose parameter - Torso aiming (up/down). + ComputePoseParam_AimPitch( pStudioHdr ); + + // Pose parameter - Torso aiming (rotation). + ComputePoseParam_AimYaw( pStudioHdr ); + } + } + +#ifdef CLIENT_DLL + if ( C_BasePlayer::ShouldDrawLocalPlayer() ) + { + GetBasePlayer()->SetPlaybackRate( 1.0f ); + } + + if ( IsItemTestingBot() ) + { + GetBasePlayer()->SetPlaybackRate( TFGameRules()->ItemTesting_GetBotAnimSpeed() ); + } + +#endif +} + +//----------------------------------------------------------------------------- +// Updates animation state if we are throwing the passtime ball +//----------------------------------------------------------------------------- +void CTFPlayerAnimState::CheckPasstimeThrowAnimation() +{ + CTFPlayer *pPlayer = GetTFPlayer(); + if ( !pPlayer ) + { + return; + } + + // FIXME: there must be a better way of doing this... + CPasstimeGun *pGun = dynamic_cast< CPasstimeGun * >( pPlayer->GetEntityForLoadoutSlot( LOADOUT_POSITION_UTILITY ) ); + if ( !pGun ) + { + return; + } + + if ( pGun->GetCurrentCharge() > 0 ) + { + if ( pPlayer->m_Shared.m_iPasstimeThrowAnimState == PASSTIME_THROW_ANIM_NONE ) + { + int iSeq = pPlayer->SelectWeightedSequence( ACT_MP_PASSTIME_THROW_BEGIN ); + pPlayer->m_Shared.m_flPasstimeThrowAnimStateTime = gpGlobals->curtime + pPlayer->SequenceDuration( iSeq ); + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_PASSTIME_THROW_BEGIN ); + pPlayer->m_Shared.m_iPasstimeThrowAnimState = PASSTIME_THROW_ANIM_LOOP; + } + else if ( pPlayer->m_Shared.m_iPasstimeThrowAnimState == PASSTIME_THROW_ANIM_LOOP ) + { + if ( gpGlobals->curtime > pPlayer->m_Shared.m_flPasstimeThrowAnimStateTime ) + { + int iSeq = pPlayer->SelectWeightedSequence( ACT_MP_PASSTIME_THROW_MIDDLE ); + pPlayer->m_Shared.m_flPasstimeThrowAnimStateTime = gpGlobals->curtime + pPlayer->SequenceDuration( iSeq ); + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_PASSTIME_THROW_MIDDLE ); + } + } + } + else // not charging + { + if ( pPlayer->m_Shared.m_iPasstimeThrowAnimState == PASSTIME_THROW_ANIM_CANCEL ) + { + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_PASSTIME_THROW_CANCEL ); + pPlayer->m_Shared.m_iPasstimeThrowAnimState = PASSTIME_THROW_ANIM_NONE; + } + else if ( pPlayer->m_Shared.m_iPasstimeThrowAnimState == PASSTIME_THROW_ANIM_LOOP ) + { + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_PASSTIME_THROW_END ); + int iSeq = pPlayer->SelectWeightedSequence( ACT_MP_PASSTIME_THROW_END ); + pPlayer->m_Shared.m_flPasstimeThrowAnimStateTime = gpGlobals->curtime + pPlayer->SequenceDuration( iSeq ); + pPlayer->m_Shared.m_iPasstimeThrowAnimState = PASSTIME_THROW_ANIM_END; + } + else if ( pPlayer->m_Shared.m_iPasstimeThrowAnimState == PASSTIME_THROW_ANIM_END ) + { + if ( gpGlobals->curtime > pPlayer->m_Shared.m_flPasstimeThrowAnimStateTime ) + { + pPlayer->m_Shared.m_iPasstimeThrowAnimState = PASSTIME_THROW_ANIM_NONE; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Updates animation state if we're stunned. +//----------------------------------------------------------------------------- +void CTFPlayerAnimState::CheckStunAnimation() +{ + CTFPlayer *pPlayer = GetTFPlayer(); + if ( !pPlayer ) + return; + + // do not play stun anims if in kart + if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) ) + return; + + // State machine to determine the correct stun activity. + if ( !pPlayer->m_Shared.IsControlStunned() && + (pPlayer->m_Shared.m_iStunAnimState == STUN_ANIM_LOOP) ) + { + // Clean up if the condition went away before we finished. + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_STUN_END ); + pPlayer->m_Shared.m_iStunAnimState = STUN_ANIM_NONE; + } + else if ( pPlayer->m_Shared.IsControlStunned() && + (pPlayer->m_Shared.m_iStunAnimState == STUN_ANIM_NONE) && + (gpGlobals->curtime < pPlayer->m_Shared.GetStunExpireTime()) ) + { + // Play the start up animation. + int iSeq = pPlayer->SelectWeightedSequence( ACT_MP_STUN_BEGIN ); + pPlayer->m_Shared.m_flStunMid = gpGlobals->curtime + pPlayer->SequenceDuration( iSeq ); + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_STUN_BEGIN ); + pPlayer->m_Shared.m_iStunAnimState = STUN_ANIM_LOOP; + } + else if ( pPlayer->m_Shared.m_iStunAnimState == STUN_ANIM_LOOP ) + { + // We are playing the looping part of the stun animation cycle. + if ( gpGlobals->curtime > pPlayer->m_Shared.m_flStunFade ) + { + // Gameplay is telling us to fade out. Time for the end anim. + int iSeq = pPlayer->SelectWeightedSequence( ACT_MP_STUN_END ); + pPlayer->m_Shared.SetStunExpireTime( gpGlobals->curtime + pPlayer->SequenceDuration( iSeq ) ); + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_STUN_END ); + pPlayer->m_Shared.m_iStunAnimState = STUN_ANIM_END; + } + else if ( gpGlobals->curtime > pPlayer->m_Shared.m_flStunMid ) + { + // Loop again. + int iSeq = pPlayer->SelectWeightedSequence( ACT_MP_STUN_MIDDLE ); + pPlayer->m_Shared.m_flStunMid = gpGlobals->curtime + pPlayer->SequenceDuration( iSeq ); + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_STUN_MIDDLE ); + } + } + else if ( pPlayer->m_Shared.m_iStunAnimState == STUN_ANIM_END ) + { + if ( gpGlobals->curtime > pPlayer->m_Shared.GetStunExpireTime() ) + { + // The animation loop is over. + pPlayer->m_Shared.m_iStunAnimState = STUN_ANIM_NONE; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CTFPlayerAnimState::CalcMainActivity() +{ + CheckStunAnimation(); + CheckPasstimeThrowAnimation(); + +#ifdef CLIENT_DLL + bool bIsAiming = m_pTFPlayer->m_Shared.IsAiming(); + + if ( IsItemTestingBot() ) + { + switch ( TFGameRules()->ItemTesting_GetBotAnim() ) + { + default: + case TI_BOTANIM_JUMP: + break; + + case TI_BOTANIM_IDLE: + if ( bIsAiming ) + return ACT_MP_DEPLOYED_IDLE; + return ACT_MP_STAND_IDLE; + + case TI_BOTANIM_CROUCH: + if ( bIsAiming ) + return ACT_MP_CROUCH_DEPLOYED_IDLE; + return ACT_MP_CROUCH_IDLE; + + case TI_BOTANIM_CROUCH_WALK: + if ( bIsAiming ) + return ACT_MP_CROUCH_DEPLOYED; + return ACT_MP_CROUCHWALK; + + case TI_BOTANIM_RUN: + if ( bIsAiming ) + return ACT_MP_DEPLOYED; + return ACT_MP_RUN; + } + } +#endif + + return BaseClass::CalcMainActivity(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFPlayerAnimState::ComputePoseParam_AimYaw( CStudioHdr *pStudioHdr ) +{ + if ( IsItemTestingBot() ) + { + if ( TFGameRules()->ItemTesting_GetBotViewScan() ) + { + static float flDeltaYaw = 0.4f; + static float flCurrentYaw = 0.0f; + static float flDeltaPitch = 0.4f; + static float flCurrentPitch = 0.0f; + + // Pan left & right + flCurrentYaw = flCurrentYaw + ( flDeltaYaw * TFGameRules()->ItemTesting_GetBotAnimSpeed() ); + if ( fabs(flCurrentYaw) >= 45 ) + { + flDeltaYaw *= -1; + } + flCurrentYaw = AngleNormalize( flCurrentYaw ); + GetBasePlayer()->SetPoseParameter( pStudioHdr, m_PoseParameterData.m_iAimYaw, -flCurrentYaw ); + + // Pan up & down + flCurrentPitch = AngleNormalize( flCurrentPitch + ( flDeltaPitch * TFGameRules()->ItemTesting_GetBotAnimSpeed() ) ); + if ( fabs(flCurrentPitch) >= 150 ) + { + flDeltaPitch *= -1; + } + flCurrentPitch = AngleNormalize( flCurrentPitch ); + flCurrentPitch = clamp(flCurrentPitch, -45.f, 90.f ); + GetBasePlayer()->SetPoseParameter( pStudioHdr, m_PoseParameterData.m_iAimPitch, -flCurrentPitch ); + + return; + } + + // Rotating on the spot? + if ( TFGameRules()->ItemTesting_GetBotTurntable() ) + { + m_flGoalFeetYaw = m_flEyeYaw; + m_flCurrentFeetYaw = m_flGoalFeetYaw; + m_angRender[YAW] = m_flCurrentFeetYaw; + float flAimYaw = m_flEyeYaw - m_flCurrentFeetYaw; + flAimYaw = AngleNormalize( flAimYaw ); + GetBasePlayer()->SetPoseParameter( pStudioHdr, m_PoseParameterData.m_iAimYaw, -flAimYaw ); + return; + } + } + + BaseClass::ComputePoseParam_AimYaw( pStudioHdr ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFPlayerAnimState::Taunt_ComputePoseParam_MoveX( CStudioHdr *pStudioHdr ) +{ + CTFPlayer *pTFPlayer = GetTFPlayer(); + if ( pTFPlayer->IsTaunting() && pTFPlayer->CanMoveDuringTaunt() ) + { + int iMove = 0; + iMove += pTFPlayer->m_nButtons & IN_FORWARD ? 1 : 0; + iMove += pTFPlayer->m_nButtons & IN_BACK ? -1 : 0; + + float fl_move_x = 1.f; + if ( pTFPlayer->GetTauntMoveAcceleration() > 0.f ) + { + fl_move_x = Sign( iMove ) * ( gpGlobals->frametime / pTFPlayer->GetTauntMoveAcceleration() ); + } + + // turning? + if ( iMove != 0.f ) + { + m_flTauntMoveX = clamp( m_flTauntMoveX + fl_move_x, -1.f, 1.f ); + } + else if ( m_flTauntMoveX != 0.f ) + { + // smooth the value back to 0 + if ( m_flTauntMoveX < 0.f ) + { + m_flTauntMoveX = clamp( m_flTauntMoveX + fabs( fl_move_x ), -1.f, 0.f ); + } + if ( m_flTauntMoveX > 0.f ) + { + m_flTauntMoveX = clamp( m_flTauntMoveX - fabs( fl_move_x ), 0.f, 1.f ); + } + } + } + else + { + m_flTauntMoveX = 0.f; + } + + pTFPlayer->SetPoseParameter( pStudioHdr, m_PoseParameterData.m_iMoveX, Sign( m_flTauntMoveX ) * SimpleSpline( fabs( m_flTauntMoveX ) ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFPlayerAnimState::Taunt_ComputePoseParam_MoveY( CStudioHdr *pStudioHdr ) +{ + CTFPlayer *pTFPlayer = GetTFPlayer(); + if ( pTFPlayer->IsTaunting() && pTFPlayer->CanMoveDuringTaunt() ) + { + float flTauntYawDiff = pTFPlayer->GetTauntYaw() - pTFPlayer->GetPrevTauntYaw(); + float fl_move_y = 1.f; + if ( pTFPlayer->GetTauntTurnAccelerationTime() > 0.f ) + { + fl_move_y = Sign( flTauntYawDiff ) * ( gpGlobals->frametime / pTFPlayer->GetTauntTurnAccelerationTime() ); + } + + // turning? + if ( flTauntYawDiff != 0.f ) + { + m_flTauntMoveY = clamp( m_flTauntMoveY + fl_move_y, -1.f, 1.f ); + } + else if ( m_flTauntMoveY != 0.f ) + { + // smooth the value back to 0 + if ( m_flTauntMoveY < 0.f ) + { + m_flTauntMoveY = clamp( m_flTauntMoveY + fabs( fl_move_y ), -1.f, 0.f ); + } + if ( m_flTauntMoveY > 0.f ) + { + m_flTauntMoveY = clamp( m_flTauntMoveY - fabs( fl_move_y ), 0.f, 1.f ); + } + } + } + else + { + m_flTauntMoveY = 0.f; + } + pTFPlayer->SetPoseParameter( pStudioHdr, m_PoseParameterData.m_iMoveY, Sign( m_flTauntMoveY ) * SimpleSpline( fabs( m_flTauntMoveY ) ) ); +} + +extern ConVar tf_halloween_kart_slow_turn_accel_speed; +void CTFPlayerAnimState::Vehicle_ComputePoseParam_MoveYaw( CStudioHdr *pStudioHdr ) +{ + float flValue = -m_pTFPlayer->m_Shared.GetVehicleTurnPoseAmount() / tf_halloween_kart_slow_turn_accel_speed.GetFloat(); + if ( m_pTFPlayer->GetTauntMoveSpeed() < 0.f ) + { + flValue = -flValue; + } + + flValue *= 0.5f; + +#ifdef DEBUG + #ifdef CLIENT_DLL + engine->Con_NPrintf( 10, "CLIENT Pose: %3.2f", flValue ); + #else + engine->Con_NPrintf( 11, "SERVER Pose: %3.2f", flValue ); + #endif +#endif + + m_pTFPlayer->SetPoseParameter( pStudioHdr, m_PoseParameterData.m_iMoveY, flValue ); +} + +extern ConVar tf_halloween_kart_dash_speed; +extern ConVar tf_halloween_kart_brake_speed; + +void CTFPlayerAnimState::Vehicle_ComputePoseParam_AccelLean( CStudioHdr *pStudioHdr ) +{ + m_pTFPlayer->SetPoseParameter( pStudioHdr, m_PoseParameterData.m_iMoveX, m_flVehicleLeanPos ); +} + +void CTFPlayerAnimState::Vehicle_LeanAccel( float flInAccel ) +{ + // Accelerate our lean vel + float flDiff = flInAccel - m_flVehicleLeanPos; + float flAccel = 0.1f * flDiff - 1.5f * m_flVehicleLeanVel; + m_flVehicleLeanVel += flAccel * gpGlobals->frametime; + + // Move our lean pos by our lean vel + m_flVehicleLeanPos += m_flVehicleLeanVel * gpGlobals->frametime; + // Decay it a bit + m_flVehicleLeanPos -= m_flVehicleLeanPos * 0.1f; + m_flVehicleLeanPos = clamp( m_flVehicleLeanPos, -1.f, 1.f ); + +#ifdef DEBUG + #ifdef CLIENT_DLL + engine->Con_NPrintf( 16, "CLIENT Acc: %.2f Vel: %.2f Pose: %.2f", flAccel, m_flVehicleLeanVel, m_flVehicleLeanPos ); + #else + engine->Con_NPrintf( 17, "SERVER Acc: %.2f Vel: %.2f Pose: %.2f", flAccel, m_flVehicleLeanVel, m_flVehicleLeanPos ); + #endif +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFPlayerAnimState::RestartGesture( int iGestureSlot, Activity iGestureActivity, bool bAutoKill ) +{ + Activity translatedActivity = iGestureActivity; + + CTFPlayer *pPlayer = GetTFPlayer(); + if ( pPlayer ) + { + // Allow the weapon to override the activity. + CTFWeaponBase *pWeapon = pPlayer->GetActiveTFWeapon(); + + if ( pWeapon ) + { + CEconItemView *pWeaponEconItemView = pWeapon->GetAttributeContainer()->GetItem(); + if ( pWeaponEconItemView ) + { + translatedActivity = pWeaponEconItemView->GetStaticData()->GetActivityOverride( pPlayer->GetTeamNumber(), translatedActivity ); + } + } + } + + BaseClass::RestartGesture( iGestureSlot, translatedActivity, bAutoKill ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : event - +//----------------------------------------------------------------------------- +void CTFPlayerAnimState::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) +{ + bool bInDuck = ( m_pTFPlayer->GetFlags() & FL_DUCKING ) ? true : false; + if ( bInDuck && SelectWeightedSequence( TranslateActivity( ACT_MP_CROUCHWALK ) ) < 0 ) + { + bInDuck = false; + } + + Activity iGestureActivity = ACT_INVALID; + + switch( event ) + { + case PLAYERANIMEVENT_ATTACK_PRIMARY: + { + CTFPlayer *pPlayer = GetTFPlayer(); + if ( !pPlayer ) + return; + + CTFWeaponBase *pWpn = pPlayer->GetActiveTFWeapon(); + bool bIsMinigun = ( pWpn && pWpn->GetWeaponID() == TF_WEAPON_MINIGUN ); + bool bIsSniperRifle = ( pWpn && WeaponID_IsSniperRifleOrBow( pWpn->GetWeaponID() ) ); + + // Heavy weapons primary fire. + if ( bIsMinigun ) + { + // Play standing primary fire. + iGestureActivity = ACT_MP_ATTACK_STAND_PRIMARYFIRE; + + if ( m_bInSwim ) + { + // Play swimming primary fire. + iGestureActivity = ACT_MP_ATTACK_SWIM_PRIMARYFIRE; + } + else if ( bInDuck ) + { + // Play crouching primary fire. + iGestureActivity = ACT_MP_ATTACK_CROUCH_PRIMARYFIRE; + } + + if ( !IsGestureSlotPlaying( GESTURE_SLOT_ATTACK_AND_RELOAD, TranslateActivity(iGestureActivity) ) ) + { + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, iGestureActivity ); + } + } + else if ( bIsSniperRifle && pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) ) + { + // Weapon primary fire, zoomed in + if ( bInDuck ) + { + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, ACT_MP_ATTACK_CROUCH_PRIMARYFIRE_DEPLOYED ); + } + else + { + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, ACT_MP_ATTACK_STAND_PRIMARYFIRE_DEPLOYED ); + } + + iGestureActivity = ACT_VM_PRIMARYATTACK; + + // Hold our deployed pose for a few seconds + m_flHoldDeployedPoseUntilTime = gpGlobals->curtime + 2.0; + } + else + { + Activity baseActivity = bInDuck ? ACT_MP_ATTACK_CROUCH_PRIMARYFIRE : ACT_MP_ATTACK_STAND_PRIMARYFIRE; + + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, baseActivity ); +// iGestureActivity = ACT_VM_PRIMARYATTACK; + } + + break; + } + + case PLAYERANIMEVENT_ATTACK_PRIMARY_SUPER: + { + Activity baseActivity = bInDuck ? ACT_MP_ATTACK_CROUCH_PRIMARY_SUPER : ACT_MP_ATTACK_STAND_PRIMARY_SUPER; + + if ( m_bInSwim ) + baseActivity = ACT_MP_ATTACK_SWIM_PRIMARY_SUPER; + + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, baseActivity ); +// iGestureActivity = ACT_VM_PRIMARYATTACK; + } + break; + + case PLAYERANIMEVENT_VOICE_COMMAND_GESTURE: + { + if ( !IsGestureSlotActive( GESTURE_SLOT_ATTACK_AND_RELOAD ) ) + { + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, (Activity)nData ); + } + break; + } + case PLAYERANIMEVENT_ATTACK_SECONDARY: + { + Activity baseActivity = bInDuck ? ACT_MP_ATTACK_CROUCH_SECONDARYFIRE : ACT_MP_ATTACK_STAND_SECONDARYFIRE; + if ( GetBasePlayer()->GetWaterLevel() >= WL_Waist ) + { + baseActivity = ACT_MP_ATTACK_SWIM_SECONDARYFIRE; + } + + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, baseActivity ); + iGestureActivity = ACT_VM_SECONDARYATTACK; + break; + } + case PLAYERANIMEVENT_ATTACK_PRE: + { + CTFPlayer *pPlayer = GetTFPlayer(); + if ( !pPlayer ) + return; + + CTFWeaponBase *pWpn = pPlayer->GetActiveTFWeapon(); + bool bIsMinigun = ( pWpn && pWpn->GetWeaponID() == TF_WEAPON_MINIGUN ); + + bool bAutoKillPreFire = false; + if ( bIsMinigun ) + { + bAutoKillPreFire = true; + } + + if ( m_bInSwim && bIsMinigun ) + { + // Weapon pre-fire. Used for minigun windup while swimming + iGestureActivity = ACT_MP_ATTACK_SWIM_PREFIRE; + } + else if ( bInDuck ) + { + // Weapon pre-fire. Used for minigun windup, sniper aiming start, etc in crouch. + iGestureActivity = ACT_MP_ATTACK_CROUCH_PREFIRE; + } + else + { + // Weapon pre-fire. Used for minigun windup, sniper aiming start, etc. + iGestureActivity = ACT_MP_ATTACK_STAND_PREFIRE; + } + + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, iGestureActivity, bAutoKillPreFire ); + + break; + } + case PLAYERANIMEVENT_ATTACK_POST: + { + CTFPlayer *pPlayer = GetTFPlayer(); + if ( !pPlayer ) + return; + + CTFWeaponBase *pWpn = pPlayer->GetActiveTFWeapon(); + bool bIsMinigun = ( pWpn && pWpn->GetWeaponID() == TF_WEAPON_MINIGUN ); + + if ( m_bInSwim && bIsMinigun ) + { + // Weapon pre-fire. Used for minigun winddown while swimming + iGestureActivity = ACT_MP_ATTACK_SWIM_POSTFIRE; + } + else if ( bInDuck ) + { + // Weapon post-fire. Used for minigun winddown in crouch. + iGestureActivity = ACT_MP_ATTACK_CROUCH_POSTFIRE; + } + else + { + // Weapon post-fire. Used for minigun winddown. + iGestureActivity = ACT_MP_ATTACK_STAND_POSTFIRE; + } + + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, iGestureActivity ); + + break; + } + + case PLAYERANIMEVENT_RELOAD: + { + // Weapon reload. + if ( m_bInAirWalk ) + { + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, ACT_MP_RELOAD_AIRWALK ); + } + else + { + BaseClass::DoAnimationEvent( event, nData ); + } + break; + } + case PLAYERANIMEVENT_RELOAD_LOOP: + { + // Weapon reload. + if ( m_bInAirWalk ) + { + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, ACT_MP_RELOAD_AIRWALK_LOOP ); + } + else + { + BaseClass::DoAnimationEvent( event, nData ); + } + break; + } + case PLAYERANIMEVENT_RELOAD_END: + { + // Weapon reload. + if ( m_bInAirWalk ) + { + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, ACT_MP_RELOAD_AIRWALK_END ); + } + else + { + BaseClass::DoAnimationEvent( event, nData ); + } + break; + } + case PLAYERANIMEVENT_DOUBLEJUMP: + { + CTFPlayer *pPlayer = GetTFPlayer(); + if ( !pPlayer ) + return; + + // Check to see if we are jumping! + if ( !m_bJumping ) + { + m_bJumping = true; + m_bFirstJumpFrame = true; + m_flJumpStartTime = gpGlobals->curtime; + RestartMainSequence(); + } + + // Force the air walk off. + m_bInAirWalk = false; + + // Player the air dash gesture. + if ( pPlayer->m_Shared.IsLoser() ) + { + RestartGesture( GESTURE_SLOT_JUMP, ACT_MP_DOUBLEJUMP_LOSERSTATE ); + } + else + { + RestartGesture( GESTURE_SLOT_JUMP, ACT_MP_DOUBLEJUMP ); + } + break; + } + case PLAYERANIMEVENT_DOUBLEJUMP_CROUCH: +// RestartGesture( GESTURE_SLOT_JUMP, ACT_MP_DOUBLEJUMP_CROUCH ); +// m_aGestureSlots[GESTURE_SLOT_JUMP].m_pAnimLayer->m_flBlendIn = 0.4f; +// m_aGestureSlots[GESTURE_SLOT_JUMP].m_pAnimLayer->m_flBlendOut = 0.4f; +#ifdef CLIENT_DLL +// m_aGestureSlots[GESTURE_SLOT_JUMP].m_pAnimLayer->m_bClientBlend = true; +#endif + break; + case PLAYERANIMEVENT_STUN_BEGIN: + RestartGesture( GESTURE_SLOT_CUSTOM, ACT_MP_STUN_BEGIN, false ); + break; + case PLAYERANIMEVENT_STUN_MIDDLE: + RestartGesture( GESTURE_SLOT_CUSTOM, ACT_MP_STUN_MIDDLE, false ); + break; + case PLAYERANIMEVENT_STUN_END: + RestartGesture( GESTURE_SLOT_CUSTOM, ACT_MP_STUN_END ); + break; + case PLAYERANIMEVENT_PASSTIME_THROW_BEGIN: + RestartGesture( GESTURE_SLOT_CUSTOM, ACT_MP_PASSTIME_THROW_BEGIN, false ); + break; + case PLAYERANIMEVENT_PASSTIME_THROW_MIDDLE: + RestartGesture( GESTURE_SLOT_CUSTOM, ACT_MP_PASSTIME_THROW_MIDDLE, false ); + break; + case PLAYERANIMEVENT_PASSTIME_THROW_END: + RestartGesture( GESTURE_SLOT_CUSTOM, ACT_MP_PASSTIME_THROW_END ); + break; + case PLAYERANIMEVENT_PASSTIME_THROW_CANCEL: + RestartGesture( GESTURE_SLOT_CUSTOM, ACT_MP_PASSTIME_THROW_CANCEL ); + break; + default: + { + BaseClass::DoAnimationEvent( event, nData ); + break; + } + } + +#ifdef CLIENT_DLL + // Make the weapon play the animation as well + if ( iGestureActivity != ACT_INVALID ) + { + CBaseCombatWeapon *pWeapon = GetTFPlayer()->GetActiveWeapon(); + if ( pWeapon ) + { + pWeapon->SendWeaponAnim( iGestureActivity ); + } + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *idealActivity - +//----------------------------------------------------------------------------- +bool CTFPlayerAnimState::HandleSwimming( Activity &idealActivity ) +{ + bool bInWater = BaseClass::HandleSwimming( idealActivity ); + + if ( bInWater ) + { + if ( m_pTFPlayer->m_Shared.IsAiming() ) + { + CTFWeaponBase *pWpn = m_pTFPlayer->GetActiveTFWeapon(); + if ( pWpn && pWpn->GetWeaponID() == TF_WEAPON_MINIGUN ) + { + idealActivity = ACT_MP_SWIM_DEPLOYED; + } + // Check for sniper deployed underwater - should only be when standing on something + else if ( pWpn && WeaponID_IsSniperRifle( pWpn->GetWeaponID() ) ) + { + if ( m_pTFPlayer->m_Shared.InCond( TF_COND_ZOOMED ) ) + { + idealActivity = ACT_MP_SWIM_DEPLOYED; + } + } + } + } + + return bInWater; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *idealActivity - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CTFPlayerAnimState::HandleMoving( Activity &idealActivity ) +{ + float flSpeed = GetOuterXYSpeed(); + + // If we move, cancel the deployed anim hold + if ( flSpeed > MOVING_MINIMUM_SPEED ) + { + m_flHoldDeployedPoseUntilTime = 0.0; + } + + if ( m_pTFPlayer->m_Shared.IsLoser() ) + { + return BaseClass::HandleMoving( idealActivity ); + } + + if ( m_pTFPlayer->m_Shared.IsAiming() ) + { + if ( flSpeed > MOVING_MINIMUM_SPEED ) + { + idealActivity = ACT_MP_DEPLOYED; + } + else + { + idealActivity = ACT_MP_DEPLOYED_IDLE; + } + } + else if ( m_flHoldDeployedPoseUntilTime > gpGlobals->curtime ) + { + // Unless we move, hold the deployed pose for a number of seconds after being deployed + idealActivity = ACT_MP_DEPLOYED_IDLE; + } + else + { + return BaseClass::HandleMoving( idealActivity ); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *idealActivity - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CTFPlayerAnimState::HandleDucking( Activity &idealActivity ) +{ + bool bInDuck = ( m_pTFPlayer->GetFlags() & FL_DUCKING ) ? true : false; + if ( bInDuck && SelectWeightedSequence( TranslateActivity( ACT_MP_CROUCHWALK ) ) < 0 && !m_pTFPlayer->m_Shared.IsLoser() ) + { + bInDuck = false; + } + + if ( bInDuck ) + { + if ( GetOuterXYSpeed() < MOVING_MINIMUM_SPEED || m_pTFPlayer->m_Shared.IsLoser() ) + { + idealActivity = ACT_MP_CROUCH_IDLE; + if ( m_pTFPlayer->m_Shared.IsAiming() || m_flHoldDeployedPoseUntilTime > gpGlobals->curtime ) + { + idealActivity = ACT_MP_CROUCH_DEPLOYED_IDLE; + } + } + else + { + if ( m_pTFPlayer->m_Shared.GetAirDash() > 0 ) + { + idealActivity = ACT_MP_DOUBLEJUMP_CROUCH; + } + else + { + idealActivity = ACT_MP_CROUCHWALK; + } + + if ( m_pTFPlayer->m_Shared.IsAiming() ) + { + // Don't do this for the heavy! we don't usually let him deployed crouch walk + bool bIsMinigun = false; + + CTFPlayer *pPlayer = GetTFPlayer(); + if ( pPlayer && pPlayer->GetActiveTFWeapon() ) + { + bIsMinigun = ( pPlayer->GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_MINIGUN ); + } + + if ( !bIsMinigun ) + { + idealActivity = ACT_MP_CROUCH_DEPLOYED; + } + } + } + + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float CTFPlayerAnimState::GetCurrentMaxGroundSpeed() +{ + float flSpeed = BaseClass::GetCurrentMaxGroundSpeed(); + + if ( m_pTFPlayer->m_Shared.GetAirDash() > 0 ) + { + return 1.f; + } + else + { + return flSpeed; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float CTFPlayerAnimState::GetGesturePlaybackRate( void ) +{ + if ( IsItemTestingBot() ) + return TFGameRules()->ItemTesting_GetBotAnimSpeed(); + + float flPlaybackRate = 1.f; + CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pTFPlayer, flPlaybackRate, mult_gesture_time ); + return flPlaybackRate; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CTFPlayerAnimState::HandleJumping( Activity &idealActivity ) +{ + bool bInDuck = ( m_pTFPlayer->GetFlags() & FL_DUCKING ) ? true : false; + if ( bInDuck && SelectWeightedSequence( TranslateActivity( ACT_MP_CROUCHWALK ) ) < 0 ) + { + bInDuck = false; + } + + Vector vecVelocity; + GetOuterAbsVelocity( vecVelocity ); + + // Don't allow a firing heavy to jump or air walk. + if ( m_pTFPlayer->GetPlayerClass()->IsClass( TF_CLASS_HEAVYWEAPONS ) && m_pTFPlayer->m_Shared.InCond( TF_COND_AIMING ) ) + return false; + + // Handle air walking before handling jumping - air walking supersedes jump + TFPlayerClassData_t *pData = m_pTFPlayer->GetPlayerClass()->GetData(); + bool bValidAirWalkClass = ( pData && pData->m_bDontDoAirwalk == false ); + + if ( bValidAirWalkClass && ( vecVelocity.z > 300.0f || m_bInAirWalk || m_pTFPlayer->GetGrapplingHookTarget() != NULL ) && !bInDuck ) + { + // Check to see if we were in an airwalk and now we are basically on the ground. + if ( ( GetBasePlayer()->GetFlags() & FL_ONGROUND ) && m_bInAirWalk ) + { + m_bInAirWalk = false; + RestartMainSequence(); + RestartGesture( GESTURE_SLOT_JUMP, ACT_MP_JUMP_LAND ); + } + else if ( GetBasePlayer()->GetWaterLevel() >= WL_Waist ) + { + // Turn off air walking and reset the animation. + m_bInAirWalk = false; + RestartMainSequence(); + } + else if ( ( GetBasePlayer()->GetFlags() & FL_ONGROUND ) == 0 ) + { + // In an air walk. + idealActivity = ACT_MP_AIRWALK; + m_bInAirWalk = true; + } + } + // Jumping. + else + { + if ( m_bJumping ) + { + // Remove me once all classes are doing the new jump + TFPlayerClassData_t *pDataJump = m_pTFPlayer->GetPlayerClass()->GetData(); + bool bNewJump = (pDataJump && pDataJump->m_bDontDoNewJump == false ); + + if ( m_bFirstJumpFrame ) + { + m_bFirstJumpFrame = false; + RestartMainSequence(); // Reset the animation. + } + + // Reset if we hit water and start swimming. + if ( GetBasePlayer()->GetWaterLevel() >= WL_Waist ) + { + m_bJumping = false; + RestartMainSequence(); + } + // Don't check if he's on the ground for a sec.. sometimes the client still has the + // on-ground flag set right when the message comes in. + else if ( gpGlobals->curtime - m_flJumpStartTime > 0.2f ) + { + if ( GetBasePlayer()->GetFlags() & FL_ONGROUND ) + { + m_bJumping = false; + RestartMainSequence(); + + if ( bNewJump ) + { + RestartGesture( GESTURE_SLOT_JUMP, ACT_MP_JUMP_LAND ); + } + } + } + + // if we're still jumping + if ( m_bJumping ) + { + if ( bNewJump ) + { + if ( gpGlobals->curtime - m_flJumpStartTime > 0.5 ) + { + idealActivity = ACT_MP_JUMP_FLOAT; + } + else + { + idealActivity = ACT_MP_JUMP_START; + } + } + else + { + idealActivity = ACT_MP_JUMP; + } + } + } + } + + if ( m_bJumping || m_bInAirWalk ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CTFPlayerAnimState::IsItemTestingBot( void ) +{ + if ( TFGameRules()->IsInItemTestingMode() ) + { + // Clients don't know what's a bot. Assume the first player is the non-bat. + return ( m_pTFPlayer->entindex() > 1 ); + } + return false; +}
\ No newline at end of file |