Fix proxy stop/land animation linger & snap
Prevent proxies from continuing jog/landing animations after a remote stops and fix snap desyncs. Treat network-authoritative idle (graphSpeed < 0) as definitive and stop falling back to measured lerp speed; pin/snap position when remote is not locomoting and ensure transforms snap on stop receive. Shorten sender-side hold times for movement and jump states to reduce stale motion in packets, and use isMoving (not stale graphSpeed) for landing resume decisions. Changes in plugin sources and main config, plus an updated dev log entry.
This commit is contained in:
@@ -4992,7 +4992,10 @@ bool IsConsolePlaceAtMeCandidateActor(const RE::Actor& a_actor, const RE::Player
|
||||
a_slot.puppetGraphSpeed,
|
||||
a_slot.puppetDirection);
|
||||
|
||||
const auto shouldSnap = ShouldSnapRemoteMovement(a_remotePlayer.movementType);
|
||||
const bool remoteLocomoting =
|
||||
a_remotePlayer.isMoving && a_remotePlayer.movementSpeed >= 1.0F;
|
||||
const auto shouldSnap =
|
||||
ShouldSnapRemoteMovement(a_remotePlayer.movementType) || !remoteLocomoting;
|
||||
ApplyRuntimeProxyTransform(a_proxy, targetPosition, a_remotePlayer.angleZ, a_remotePlayer, shouldSnap, &a_slot);
|
||||
|
||||
// Phase 4.3: Apply actor state flags before animation sync
|
||||
@@ -5400,10 +5403,20 @@ bool IsConsolePlaceAtMeCandidateActor(const RE::Actor& a_actor, const RE::Player
|
||||
slot.lastSmoothMoveTime = frameNow;
|
||||
frameDeltaSeconds = std::clamp(frameDeltaSeconds, 1.0F / 120.0F, 0.1F);
|
||||
|
||||
// Already at target: pin exactly and stop. This prevents the actor from
|
||||
// drifting/wandering around a fixed target while the remote is idle.
|
||||
if (distanceToTarget < 0.1F) {
|
||||
const bool remoteLocomoting =
|
||||
slot.lastRemotePlayerSnapshot &&
|
||||
slot.lastRemotePlayerSnapshot->isMoving &&
|
||||
slot.lastRemotePlayerSnapshot->movementSpeed >= 1.0F;
|
||||
|
||||
// Animation follows network state, not interpolation remainder. When the remote
|
||||
// stops, pin position immediately so we don't jog-in-place while lerping.
|
||||
if (!remoteLocomoting || distanceToTarget < 0.1F) {
|
||||
proxy->SetPosition(slot.targetPosition, true);
|
||||
slot.puppetGraphSpeed = -1.0F;
|
||||
F4T::ProxyPuppet::SetProxyLocomotionState(
|
||||
proxy->GetFormID(),
|
||||
-1.0F,
|
||||
slot.puppetDirection);
|
||||
F4T::ProxyPuppet::ApplyProxyLocomotionFrame(
|
||||
*proxy,
|
||||
RE::NiPoint3{},
|
||||
|
||||
@@ -536,8 +536,7 @@ namespace
|
||||
const auto landTier = a_previous.lastLocomotionTier.value_or(currentTier);
|
||||
fireEvent(GraphEvent::kJumpDown);
|
||||
fireEvent(GraphEvent::kJumpLand);
|
||||
const auto remoteMoving =
|
||||
a_desired.isMoving || a_desired.graphSpeed >= kIdleSpeedThreshold;
|
||||
const auto remoteMoving = a_desired.isMoving;
|
||||
if (remoteMoving) {
|
||||
if (landTier == F4T::ProxyAnimationSync::LocomotionTier::kRun) {
|
||||
fireEvent(GraphEvent::kJumpLandToRun);
|
||||
|
||||
@@ -259,9 +259,16 @@ namespace F4T::ProxyPuppet
|
||||
velocity.z = a_worldDelta.z / safeDelta;
|
||||
}
|
||||
|
||||
const float horizontalSpeed = std::sqrt((velocity.x * velocity.x) + (velocity.y * velocity.y));
|
||||
const float feedbackSpeed =
|
||||
(a_graphSpeed >= 0.0F && std::isfinite(a_graphSpeed)) ? a_graphSpeed : horizontalSpeed;
|
||||
// -1 means network-authoritative idle. Do not fall back to measured lerp speed:
|
||||
// convergence Move() deltas were re-driving jog cadence for ~1s after remote stop.
|
||||
if (a_graphSpeed < 0.0F || !std::isfinite(a_graphSpeed)) {
|
||||
const std::unique_lock lock{ g_proxyFormIdMutex };
|
||||
g_proxyLocomotion[a_actor.GetFormID()] = ProxyLocomotionState{ -1.0F, a_direction, {}, {} };
|
||||
SyncControllerMotionFields(a_actor, {}, 0.0F);
|
||||
return;
|
||||
}
|
||||
|
||||
const float feedbackSpeed = a_graphSpeed;
|
||||
|
||||
if (feedbackSpeed < 8.0F) {
|
||||
const std::unique_lock lock{ g_proxyFormIdMutex };
|
||||
|
||||
+4
-3
@@ -58,10 +58,11 @@ namespace
|
||||
constexpr auto kTransformSendRotationThreshold = 0.02F;
|
||||
constexpr auto kTeleportDistanceThreshold = 5000.0F;
|
||||
constexpr auto kMovingSpeedThreshold = 1.0F;
|
||||
constexpr auto kMovementStopHoldDuration = 300ms;
|
||||
// Short tail so stop packets still coalesce without keeping jog anim ~1s after halt.
|
||||
constexpr auto kMovementStopHoldDuration = 100ms;
|
||||
constexpr auto kJumpVerticalSpeedThreshold = 80.0F;
|
||||
// Tail hold after IsJumping() clears so transform packets still carry isJumping=true.
|
||||
constexpr auto kJumpStateHoldDuration = 400ms;
|
||||
// Brief tail after IsJumping() clears so landing packets are not missed.
|
||||
constexpr auto kJumpStateHoldDuration = 150ms;
|
||||
constexpr auto kJumpDiagnosticLogInterval = 1s;
|
||||
constexpr auto kSneakDiagnosticLogInterval = 2s;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user