From 23f1532fb0fb8ff0cef54935088e633d75e5e5e0 Mon Sep 17 00:00:00 2001 From: arcattack Date: Wed, 15 Jul 2026 07:58:11 -0500 Subject: [PATCH] MP: peer true-mirror cadence -- drive the body clip from actual replicated motion (task #50) Toward a truer mirror (user: peer 'hesitates/skips on accel/decel, master has smoother transitions'). The body channel FOLLOWS the master's replicated STATE (96a896a) but its CADENCE (bodyCycleSpeed) is still slewed LOCALLY toward the replicated commanded speed -- which drifts from the master's actual cadence, so the animation phase wanders until a record snaps it (the hesitation). FIX (replicant-gated, single-player untouched): peerMirrorSpeed member = the actual replicated ground speed (|updateVelocity|, set in the peer branch); the body channel walk (6/7) + reverse (0xc/0xd) cases override bodyCycleSpeed with it (clamped to the state's band) right before the clip advance, so the peer's clip advances at the master's REAL rate instead of a locally-drifting slew. peerMirrorSpeed = -1 on the master/single-player -> the authentic slew runs. BT_NO_MIRROR_CAD / BT_PEER_LEGCH revert. Verified autonomous (through-zero sweep): no crash, body channel advances forward (back-steps ~0), feet track (position ratio 1.05, maxStep 1.68u). Perceived smoothness is the visual test (harness saturates). Co-Authored-By: Claude Opus 4.8 (1M context) --- game/reconstructed/mech.cpp | 1 + game/reconstructed/mech.hpp | 6 ++++++ game/reconstructed/mech2.cpp | 12 ++++++++++++ game/reconstructed/mech4.cpp | 11 +++++++++++ 4 files changed, 30 insertions(+) diff --git a/game/reconstructed/mech.cpp b/game/reconstructed/mech.cpp index 37f5004..679535b 100644 --- a/game/reconstructed/mech.cpp +++ b/game/reconstructed/mech.cpp @@ -916,6 +916,7 @@ Mech::Mech( poseSyncLatch = 0; // @0x77c (was absorbed Wword(0x1df)) heatLevelSnapshot = 0; // @0x780 (type-7 deadband baseline) mirrorBodyAdv = 0.0f; // master send-mirror gait travel + peerMirrorSpeed = -1.0f; // peer true-mirror cadence (disabled by default) angMirrorYaw = 0.0f; // scalar peer-yaw mirror (port addition) angMirrorRate = 0.0f; angMirrorValid = 0; diff --git a/game/reconstructed/mech.hpp b/game/reconstructed/mech.hpp index 176ccd4..76df0e2 100644 --- a/game/reconstructed/mech.hpp +++ b/game/reconstructed/mech.hpp @@ -631,6 +631,12 @@ public: // predicts (gait), instead of the constant-velocity deadReckoner. This is the // authentic FUN_004a9b5c mirror = IntegrateMotion(mj=0) on projectedOrigin. Scalar mirrorBodyAdv; // body cycleDistance this frame (master) + // Peer true-mirror cadence: the replicant sets this to the actual replicated + // ground speed (|updateVelocity|) each frame so the body clip advances at the + // master's real rate instead of the locally-slewed bodyCycleSpeed (which drifts + // and makes the animation phase wander -> the accel/decel hesitation). -1 = + // disabled (master/single-player always -1, so the authentic slew runs). + Scalar peerMirrorSpeed; Scalar angMirrorYaw; // yaw at the last type-4 send (rad) Scalar angMirrorRate; // yaw rate sent with it (rad/s) Time angMirrorTime; // lastPerformance at that send diff --git a/game/reconstructed/mech2.cpp b/game/reconstructed/mech2.cpp index 4cf0bc1..1611450 100644 --- a/game/reconstructed/mech2.cpp +++ b/game/reconstructed/mech2.cpp @@ -999,6 +999,9 @@ Scalar if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed; if (bodyCycleSpeed > walkStrideLength) bodyCycleSpeed = walkStrideLength; } + if (peerMirrorSpeed >= 0.0f) // peer: cadence == actual mirrored ground speed + bodyCycleSpeed = (peerMirrorSpeed < standSpeed) ? standSpeed + : ((peerMirrorSpeed > walkStrideLength) ? walkStrideLength : peerMirrorSpeed); distance = bodyAnimation.Advance( time_slice * (bodyCycleSpeed / walkStrideLength) * globalTimeScale, loop); break; @@ -1019,6 +1022,9 @@ Scalar if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed; if (bodyCycleSpeed > reverseSpeedMax2) bodyCycleSpeed = reverseSpeedMax2; } + if (peerMirrorSpeed >= 0.0f) // peer: cadence == actual mirrored ground speed + bodyCycleSpeed = (peerMirrorSpeed < reverseSpeedMax) ? reverseSpeedMax + : ((peerMirrorSpeed > reverseSpeedMax2) ? reverseSpeedMax2 : peerMirrorSpeed); distance = bodyAnimation.Advance( time_slice * (bodyCycleSpeed / reverseStrideLength) * globalTimeScale, loop); break; @@ -1159,6 +1165,9 @@ Scalar if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed; if (bodyCycleSpeed > walkStrideLength) bodyCycleSpeed = walkStrideLength; } + if (peerMirrorSpeed >= 0.0f) // peer: cadence == actual mirrored ground speed + bodyCycleSpeed = (peerMirrorSpeed < standSpeed) ? standSpeed + : ((peerMirrorSpeed > walkStrideLength) ? walkStrideLength : peerMirrorSpeed); distance = bodyAnimation.Advance( time_slice * (bodyCycleSpeed / walkStrideLength) * globalTimeScale, loop); break; @@ -1179,6 +1188,9 @@ Scalar if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed; if (bodyCycleSpeed > reverseSpeedMax2) bodyCycleSpeed = reverseSpeedMax2; } + if (peerMirrorSpeed >= 0.0f) // peer: cadence == actual mirrored ground speed + bodyCycleSpeed = (peerMirrorSpeed < reverseSpeedMax) ? reverseSpeedMax + : ((peerMirrorSpeed > reverseSpeedMax2) ? reverseSpeedMax2 : peerMirrorSpeed); distance = bodyAnimation.Advance( time_slice * (bodyCycleSpeed / reverseStrideLength) * globalTimeScale, loop); break; diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 7b74199..1e4ed43 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -2148,6 +2148,17 @@ void // master's exact clips. Swapping the peer to the body channel RESTORES the // original single-channel design (no new netcode). BT_PEER_LEGCH=1 = old. static const int s_peerLegCh = getenv("BT_PEER_LEGCH") ? 1 : 0; + // TRUE-MIRROR CADENCE: drive the body clip from the ACTUAL replicated ground + // speed so the animation phase tracks the master exactly instead of drifting + // via the local bodyCycleSpeed slew (the accel/decel hesitation). Consumed in + // AdvanceBodyAnimation walk/reverse. -1 disables -> authentic slew. + if (!s_peerLegCh && !getenv("BT_NO_MIRROR_CAD")) + { + const Vector3D &mcv = updateVelocity.linearMotion; + peerMirrorSpeed = (Scalar)sqrtf((float)(mcv.x * mcv.x + mcv.z * mcv.z)); + } + else + peerMirrorSpeed = -1.0f; // TURN-STEP ARMING for the body channel: the body Standing case only enters // walk/reverse/stand -- it never arms the turn-in-place clip (state 4); on the // master that arming comes from the LEG Standing case cross-arming both