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