#pragma once #include "F4TRemotePlayerState.h" #include #include #include #include namespace RE { class Actor; } namespace F4T::ProxyAnimationSync { // Authoritative FO4 humanoid movement-behavior graph variable names, taken from // MTBehavior.hkx (hkbBehaviorGraphStringData::variableNames, with types from the // parallel variableInfos array) via the F4-Animation-Research unpack: // Speed, SpeedSmoothed, Direction -> VARIABLE_TYPE_REAL (float) // IsSprinting, bInJumpState -> VARIABLE_TYPE_BOOL (bool) // iIsInSneak -> VARIABLE_TYPE_INT32 (int) // Earlier code guessed names like "speed"/"speedSampled"/"direction" that do not exist // on this graph, so every SetGraphVariable* call silently failed. namespace GraphVar { inline constexpr const char* kSpeed = "Speed"; inline constexpr const char* kSpeedSmoothed = "SpeedSmoothed"; inline constexpr const char* kDirection = "Direction"; inline constexpr const char* kIsSprinting = "IsSprinting"; inline constexpr const char* kInJumpState = "bInJumpState"; inline constexpr const char* kIsInSneak = "iIsInSneak"; inline constexpr const char* kSyncWalkRun = "iSyncWalkRun"; inline constexpr const char* kSyncLocomotionSpeed = "iSyncLocomotionSpeed"; inline constexpr const char* kSyncJumpState = "iSyncJumpState"; } // MTLocomotionBlend_SM sub-states: Walk(0), Jog(1), Run(2). Selected by graph events // "Walk"/"Jog"/"Run" and mirrored in iSyncLocomotionSpeed. enum class LocomotionTier : std::int32_t { kWalk = 0, kJog = 1, kRun = 2, }; LocomotionTier ComputeLocomotionTier(float a_graphSpeed, bool a_isSprinting); // Maps network movementSpeed (units/sec) to graph Speed. Used when animationGraphSpeed // is missing or unreliable. float MapMovementSpeedToGraphSpeed(float a_movementSpeed, bool a_isSprinting); // Picks the graph Speed to drive locomotion blending. Prefers animationGraphSpeed only // when it is above the idle threshold; a read of 0.0 while moving is common and must // not override the position-derived movementSpeed (which carries slow-walk/jog tiers). float ResolveLocomotionGraphSpeed( float a_movementSpeed, float a_animationGraphSpeed, bool a_isMoving, bool a_isSprinting); struct ProxyAppliedAnimationState { bool hasAppliedGraphState{ false }; bool lastIsMoving{ false }; bool lastIsSprinting{ false }; bool lastIsSneaking{ false }; bool lastIsJumping{ false }; bool lastIsCrouching{ false }; bool lastWeaponDrawn{ false }; float lastGraphSpeed{ 0.0F }; float lastDirection{ 0.0F }; std::optional lastLocomotionTier{}; std::chrono::steady_clock::time_point movingHoldUntil{}; std::chrono::steady_clock::time_point jumpHoldUntil{}; }; void ResetAppliedAnimationState(ProxyAppliedAnimationState& a_state); void ApplyProxyAnimationFromRemoteState( ProxyAppliedAnimationState& a_state, RE::Actor& a_proxy, const RemotePlayerState::RemotePlayerState& a_remotePlayer); void ClearProxyAnimationState( ProxyAppliedAnimationState& a_state, RE::Actor& a_proxy, std::uint32_t a_remotePlayerId, std::string_view a_reason); }