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