BT410 5.3.114: the replicant torso aims -- TorsoCopySimulation + ComputeTargetTwist, decomp-verified, two donor drifts corrected
The damaged-copy twist path (@004b65f8 + @004b6510), reconstructed from the raw decomp rather than the BT411 donor -- which drifted twice: its ease aged by lastUpdate (+0x14) where the binary reads lastPerformance (+0x10), and it calls the joint-write helper from the copy sim where 1995 does not (port-era addition, dropped). The extrapolator predicts targetTwist = twistAtUpdate + twistRate * elapsed on two time bases (settled = this frame's window, slewing = the command window), and the copy sim snaps when settled or eases by the remaining-time form dt/((stamp - now) + dt) -- landing exactly when the clock reaches the command stamp. The decomp's DAT_0052140c divide is just Time::operator- inlined; no hand conversion existed. Members carved from the dynamicsState reserve (16->12): targetTwist @0x218, twistAtUpdate @0x21C, twistRate @0x238, lastUpdateTime @0x254. The ctor now registers the copy Performance on replicants (PTR @00510c1c); [T2] the gate mirrors the binary's simulationFlags 0xC/0x100 pair with the instance test, same as the whole watcher family, until the stream builders that seed those bits are reconstructed. Feeders (Read/WriteUpdateRecord) stay staged -- zero-init eases a copy to centre, correct-by-vacuity in single-pod. Stubs: 16 across 10 files. Regression: clean mission soak on the rig (no faults, wheel turning, pacing steady). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -159,13 +159,30 @@ Torso::Torso(
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Install the per-frame twist/elevation Performance (the replicant copy is
|
// Twist-replication state (binary @0x218/@0x21C/@0x238/@0x254) -- see the
|
||||||
// driven by console updates via TorsoCopySimulation instead, still staged).
|
// header note. Zero until an update record stamps it.
|
||||||
|
//
|
||||||
|
targetTwist = 0.0f;
|
||||||
|
twistAtUpdate = 0.0f;
|
||||||
|
twistRate = 0.0f;
|
||||||
|
lastUpdateTime = 0L;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Install the per-frame Performance. The binary's gate @004b6b0c is
|
||||||
|
// (owner->simulationFlags & 0xC) == 0 && (owner->simulationFlags & 0x100)
|
||||||
|
// != 0 -- the master-segment flag pair seeded by the mech stream builders,
|
||||||
|
// which are not reconstructed yet. [T2] Until they land, the whole
|
||||||
|
// watcher family mirrors that gate with the instance test (same sites:
|
||||||
|
// heat.cpp / powersub.cpp / gyro.cpp registrations); revisit together.
|
||||||
//
|
//
|
||||||
if (owner->GetInstance() != Entity::ReplicantInstance)
|
if (owner->GetInstance() != Entity::ReplicantInstance)
|
||||||
{
|
{
|
||||||
SetPerformance(&Torso::TorsoSimulation);
|
SetPerformance(&Torso::TorsoSimulation);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetPerformance(&Torso::TorsoCopySimulation); // binary PTR @00510c1c
|
||||||
|
}
|
||||||
|
|
||||||
Check_Fpu();
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
@@ -391,8 +408,107 @@ void
|
|||||||
Check_Fpu();
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
//
|
||||||
Torso::TorsoCopySimulation(Scalar)
|
//#############################################################################
|
||||||
|
// ComputeTargetTwist -- binary @004b6510 (232 bytes), verified against the
|
||||||
|
// raw decomp (part_013.c).
|
||||||
|
//
|
||||||
|
// Extrapolate where the master's torso should be by now. Two time bases:
|
||||||
|
//
|
||||||
|
// settled (not a copy, or the command stamp is not ahead of the clock):
|
||||||
|
// elapsed = lastPerformance - lastUpdate (this frame's window)
|
||||||
|
// slewing (a copy with a command stamp still in the future):
|
||||||
|
// elapsed = lastUpdateTime - lastUpdate (the command window)
|
||||||
|
//
|
||||||
|
// then targetTwist = twistAtUpdate + twistRate * elapsed, clamped to the
|
||||||
|
// twist limits (left = upper bound, right = lower -- same convention as the
|
||||||
|
// master sim). Returns True while slewing. Time-Time subtraction is the
|
||||||
|
// engine's operator- (ticks delta / SystemClock::ticksPerSecond) -- the
|
||||||
|
// decomp's DAT_0052140c divide is that operator inlined.
|
||||||
|
//#############################################################################
|
||||||
|
//
|
||||||
|
Logical
|
||||||
|
Torso::ComputeTargetTwist()
|
||||||
{
|
{
|
||||||
Fail("Torso::TorsoCopySimulation -- torso.cpp not yet reconstructed");
|
Check(this);
|
||||||
|
|
||||||
|
Scalar
|
||||||
|
elapsed;
|
||||||
|
Logical
|
||||||
|
slewing;
|
||||||
|
|
||||||
|
if (isDamagedCopy == 0 || lastUpdateTime <= lastPerformance)
|
||||||
|
{
|
||||||
|
elapsed = lastPerformance - lastUpdate;
|
||||||
|
slewing = False;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
elapsed = lastUpdateTime - lastUpdate;
|
||||||
|
slewing = True;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetTwist = twistAtUpdate + twistRate * elapsed;
|
||||||
|
|
||||||
|
if (targetTwist > horizontalLimitLeft)
|
||||||
|
{
|
||||||
|
targetTwist = horizontalLimitLeft;
|
||||||
|
}
|
||||||
|
if (targetTwist < horizontalLimitRight)
|
||||||
|
{
|
||||||
|
targetTwist = horizontalLimitRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return slewing;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//#############################################################################
|
||||||
|
// TorsoCopySimulation -- binary @004b65f8 (188 bytes), verified against the
|
||||||
|
// raw decomp. The damaged-copy Performance (replicant registration in the
|
||||||
|
// ctor): no commands, no dynamics -- just ease currentTwist onto the
|
||||||
|
// extrapolated target.
|
||||||
|
//
|
||||||
|
// settled: snap currentTwist = targetTwist
|
||||||
|
// slewing: ease currentTwist = Lerp(current, target,
|
||||||
|
// dt / ((lastUpdateTime - lastPerformance) + dt))
|
||||||
|
// -- the remaining-time form: the ease lands exactly when the
|
||||||
|
// clock reaches the command stamp.
|
||||||
|
//
|
||||||
|
// The binary then re-clamps targetTwist (not currentTwist) with the same
|
||||||
|
// limit pair. NOTE two donor drifts corrected here against the decomp: the
|
||||||
|
// ease's age term reads lastPerformance (+0x10), not lastUpdate (+0x14); and
|
||||||
|
// the binary does NOT call the joint-write helper (@004b67ec) from the copy
|
||||||
|
// sim -- that call in the BT411 port is a port-era addition, not 1995 code.
|
||||||
|
//#############################################################################
|
||||||
|
//
|
||||||
|
void
|
||||||
|
Torso::TorsoCopySimulation(Scalar time_slice)
|
||||||
|
{
|
||||||
|
Check(this);
|
||||||
|
|
||||||
|
if (!ComputeTargetTwist())
|
||||||
|
{
|
||||||
|
currentTwist = targetTwist;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentTwist =
|
||||||
|
Lerp(
|
||||||
|
currentTwist,
|
||||||
|
targetTwist,
|
||||||
|
time_slice / ((lastUpdateTime - lastPerformance) + time_slice)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetTwist > horizontalLimitLeft)
|
||||||
|
{
|
||||||
|
targetTwist = horizontalLimitLeft;
|
||||||
|
}
|
||||||
|
if (targetTwist < horizontalLimitRight)
|
||||||
|
{
|
||||||
|
targetTwist = horizontalLimitRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,14 @@
|
|||||||
|
|
||||||
void
|
void
|
||||||
TorsoSimulation(Scalar time_slice);
|
TorsoSimulation(Scalar time_slice);
|
||||||
|
//
|
||||||
|
// The extrapolation helper (binary @004b6510): predict targetTwist
|
||||||
|
// from the last update snapshot, clamp to the twist limits, return
|
||||||
|
// True while the copy is still slewing toward a future command time.
|
||||||
|
//
|
||||||
|
Logical
|
||||||
|
ComputeTargetTwist();
|
||||||
|
|
||||||
void
|
void
|
||||||
TorsoCopySimulation(Scalar time_slice);
|
TorsoCopySimulation(Scalar time_slice);
|
||||||
|
|
||||||
@@ -200,10 +208,25 @@
|
|||||||
//
|
//
|
||||||
int recenterActive;
|
int recenterActive;
|
||||||
//
|
//
|
||||||
|
// TWIST REPLICATION (binary @0x218/@0x21C/@0x238/@0x254) -- the
|
||||||
|
// damaged-copy dead-reckoning state. A console update stamps a twist
|
||||||
|
// snapshot (twistAtUpdate) + rate (twistRate) + its command time
|
||||||
|
// (lastUpdateTime); ComputeTargetTwist extrapolates targetTwist from
|
||||||
|
// them and TorsoCopySimulation eases currentTwist onto it. The
|
||||||
|
// UPDATE-RECORD feeders (Read/WriteUpdateRecord, torso.cpp census
|
||||||
|
// remainder) are still staged -- zero-init means a copy eases to
|
||||||
|
// centre, which is correct-by-vacuity until MP updates arrive.
|
||||||
|
// Carved from the dynamicsState reserve; class size unchanged.
|
||||||
|
//
|
||||||
|
Scalar targetTwist;
|
||||||
|
Scalar twistAtUpdate;
|
||||||
|
Scalar twistRate;
|
||||||
|
Time lastUpdateTime;
|
||||||
|
//
|
||||||
// Twist/elevation dynamics accumulators advanced by TorsoSimulation.
|
// Twist/elevation dynamics accumulators advanced by TorsoSimulation.
|
||||||
// Reserved until the per-frame sim is reconstructed (phase 5).
|
// Reserved until the per-frame sim is reconstructed (phase 5).
|
||||||
//
|
//
|
||||||
Scalar dynamicsState[16];
|
Scalar dynamicsState[12];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -47,9 +47,47 @@ needed to keep advancing the scalar state, which it already did; the missing
|
|||||||
piece was the CONSUMER (`Mech::Simulate`'s eyepoint composition), now
|
piece was the CONSUMER (`Mech::Simulate`'s eyepoint composition), now
|
||||||
reconstructed and verified (`torsoElev` == `eyePitch` every frame).
|
reconstructed and verified (`torsoElev` == `eyePitch` every frame).
|
||||||
|
|
||||||
|
## TorsoCopySimulation + ComputeTargetTwist (2026-08-04, 5.3.114)
|
||||||
|
|
||||||
|
The replicant (damaged-copy) twist path is real, verified line-by-line against
|
||||||
|
the raw decomp (part_013.c):
|
||||||
|
|
||||||
|
- `ComputeTargetTwist` @004b6510 (232B): two time bases — settled uses
|
||||||
|
`lastPerformance - lastUpdate` (the engine `Simulation` fields at +0x10/+0x14),
|
||||||
|
slewing (a copy whose command stamp `lastUpdateTime` @0x254 is ahead of the
|
||||||
|
clock) uses `lastUpdateTime - lastUpdate`. `targetTwist = twistAtUpdate +
|
||||||
|
twistRate * elapsed`, clamped (left = upper, right = lower). Returns the
|
||||||
|
slewing flag. The decomp's `DAT_0052140c` divide is `Time::operator-`
|
||||||
|
(ticks / SystemClock::ticksPerSecond) inlined — NOT a hand-written
|
||||||
|
MsPerSecond conversion.
|
||||||
|
- `TorsoCopySimulation` @004b65f8 (188B): settled → snap to target; slewing →
|
||||||
|
`Lerp(current, target, dt / ((lastUpdateTime - lastPerformance) + dt))` — the
|
||||||
|
remaining-time ease that lands exactly when the clock reaches the command
|
||||||
|
stamp. Then the same clamp pair applied to `targetTwist` again.
|
||||||
|
|
||||||
|
**Two BT411 donor drifts corrected against the decomp**: the donor's ease aged
|
||||||
|
by `lastUpdate` (+0x14) where the binary reads `lastPerformance` (+0x10); and
|
||||||
|
the donor calls the joint-write helper (@004b67ec) from the copy sim — the
|
||||||
|
binary does not (port-era addition; our copy sim leaves joints to the master
|
||||||
|
path exactly as 1995 shipped).
|
||||||
|
|
||||||
|
Members carved from `dynamicsState[16]` → `[12]`: `targetTwist` @0x218,
|
||||||
|
`twistAtUpdate` @0x21C, `twistRate` @0x238, `lastUpdateTime` @0x254 (`Time`,
|
||||||
|
init `0L` — plain `0` is ambiguous between the long/float assignment operators
|
||||||
|
under BC4.52). Ctor now registers the copy Performance on replicants
|
||||||
|
(binary PTR @00510c1c). [T2] The registration gate mirrors the binary's
|
||||||
|
`(owner->simulationFlags & 0xC) == 0 && (flags & 0x100) != 0` with the
|
||||||
|
instance test, consistent with the whole watcher family, until the mech
|
||||||
|
stream builders (which seed those flag bits) are reconstructed.
|
||||||
|
|
||||||
|
**Staged feeders**: `Read/WriteUpdateRecord` (the census remainder in this TU)
|
||||||
|
stamp `twistAtUpdate`/`twistRate`/`lastUpdateTime` from console updates.
|
||||||
|
Zero-init means a copy eases to centre — correct-by-vacuity in single-pod,
|
||||||
|
inert until MP replication lands.
|
||||||
|
|
||||||
## Deferred
|
## Deferred
|
||||||
|
|
||||||
- HUD free-aim slew, the look-button state machine (`BTCommitLookState` /
|
- HUD free-aim slew, the look-button state machine (`BTCommitLookState` /
|
||||||
`gBTLookPitch`/`gBTLookYaw` — composes with elevation into the full
|
`gBTLookPitch`/`gBTLookYaw` — composes with elevation into the full
|
||||||
eyepoint), `TorsoCopySimulation` (replicant console-driven aim) — still
|
eyepoint) — still staged.
|
||||||
staged.
|
- `Torso::Read/WriteUpdateRecord` — the copy sim's update feeders (see above).
|
||||||
|
|||||||
Reference in New Issue
Block a user