MP: FIX walk+turn peer skip -- incremental heading integration + scalar yaw mirror (task #50)

The user-keyboard regime (steering WHILE walking) fired BOTH dense record streams
at once and exposed the last divergence: our peer heading used the engine
Mover::DeadReckon slerp-toward-projection, whose angular projection reads the
SHARED lastUpdate/nextUpdate timebase.  The dense type-0 pose stream resets that
timebase every frame while walking while RESTORING a stale orientation (the
authentic case-0 strip, verified against FUN_004a1232 case 0) -- so the angular
target barely advances from a stale base and the slerp DRAGS the heading back
every frame.  Measured: peer yaw advancing at ~40% rate with half the frames
stepping BACKWARD.  Pure-spin and pure-walk tests never showed it (single
stream) -- why autonomous looked smooth while keyboard play skipped.

AUTHENTIC FIX (decomp FUN_004ab1c8 -> FUN_004ab188/FUN_00409f58): the original
replicant integrates its heading INCREMENTALLY from the CURRENT pose -- exact
rotation of (replicated yaw rate * dt) composed on each frame -- and re-anchors
on type-4 receipt.  It never slerps toward a projected angular target.
 - mech4.cpp peer branch: save heading, let DeadReckon own LINEAR only, then
   integrate heading incrementally (ReconQuatIntegrate); on angSyncLatch (new
   type-4) re-anchor to updateOrigin.
 - mech.hpp/mech.cpp: angSyncLatch member (angular analog of poseSyncLatch),
   armed by ReadUpdateRecord case 4.
 - SCALAR peer-yaw mirror (angMirrorYaw/Rate/Time, re-based in the type-4
   writer): replaces the quaternion projectedOrigin mirror for the ANGLE
   deadband -- the old one was recomputed each frame by the master's own
   reckoner from timing it does not control and false-fired in pi-waves
   (measured maxAng~=pi bursts -> periodic resync floods).
 - Dense-rot type-4 send REMOVED (was masking the old crude projection; not
   authentic; churned the shared horizon).  Orientation now rides the sparse
   angle/velocity deadband resyncs exactly as the binary's.

Verified live-autonomous:
 - pure spin: 59/59 perfectly regular peer yaw steps; master resyncs 0/s with
   mirror drift ~5e-7 (records near-silent, authentic sparse model).
 - walk+turn circle (the user regime): peer sim yaw monotonic at exactly the
   master's rate (0.00556/frame @ 0.327 rad/s), no backward steps, no stalls.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-14 18:52:15 -05:00
co-authored by Claude Opus 4.8
parent b7be95b584
commit d78e77bf84
3 changed files with 83 additions and 47 deletions
+14
View File
@@ -915,6 +915,10 @@ Mech::Mech(
Wword(0xe6) = 0;
poseSyncLatch = 0; // @0x77c (was absorbed Wword(0x1df))
heatLevelSnapshot = 0; // @0x780 (type-7 deadband baseline)
angMirrorYaw = 0.0f; // scalar peer-yaw mirror (port addition)
angMirrorRate = 0.0f;
angMirrorValid = 0;
angSyncLatch = 0; // peer heading re-anchor (type-4 receive)
Wword(0xfe) = Wword(0xfc) = Wword(0x105) = 0;
mechName.Copy(&DAT_004e0f8c); // FUN_00408e90(this+0xd8,"")
Wword(0x160) = Wword(0x15f) = 0;
@@ -1893,6 +1897,7 @@ void
}
}
bodyTargetSpeed = record->speedDemand; // rec+0x28
angSyncLatch = 1; // arm the peer heading re-anchor
}
break;
@@ -2114,6 +2119,15 @@ void
// master perf; the writer-side re-base is the coherent reconstruction.)
projectedOrigin.angularPosition = localOrigin.angularPosition;
projectedVelocity.angularMotion = localVelocity.angularMotion;
// SCALAR peer-yaw mirror re-base (see mech.hpp): what the peer will now
// extrapolate -- this yaw, at this rate, from this moment.
{
YawPitchRoll _my; _my = localOrigin.angularPosition;
angMirrorYaw = (Scalar)_my.yaw;
angMirrorRate = (Scalar)localVelocity.angularMotion.y;
angMirrorTime = lastPerformance;
angMirrorValid = 1;
}
record->speedDemand = speedDemand; // rec+0x28
bodyTargetSpeed = speedDemand;
}