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>
5.1 KiB
TORSO.CPP / .HPP — reconstruction notes
Torso (: PowerWatcher) drives torso twist (horizontal) and weapon elevation
(vertical). Tuning rates/limits stream from the resource
(horizontal/verticalRotationPerSecond, horizontal/verticalLimit*,
torsoHorizontalEnabled).
Reconstructed
- Ctor: streams the base rates (deg→rad) + limits +
horizontalEnabled, primescurrentTwist/currentElevation/analog axes to 0, and installsTorsoSimulationas the per-frame Performance (the replicant copy is console-driven viaTorsoCopySimulation, still staged). analogElevationAxis/analogTwistAxismembers +SetAnalogElevationAxis/SetAnalogTwistAxissetters (written byMechControlsMapper::InterpretControls).CurrentTwist()/CurrentElevation()/GetHorizontalEnabled()accessors.TorsoSimulation(Scalar)— per-frame aim integration (authentic core):currentElevation += analogElevationAxis · baseElevationRate · dt, clamped to [verticalLimitBottom, verticalLimitTop];- if
horizontalEnabled:currentTwist += analogTwistAxis · baseTwistRate · dt, clamped to [horizontalLimitLeft, horizontalLimitRight].
VERIFIED (BT_MECH_LOG [mppr] torsoElev=, forced-input BT_FORCE_ELEV):
stick pitch 0.8 slews the elevation up and clamps at the authentic
verticalLimitTop = 0.349 rad = 20°. Zero Fail.
Skeleton-joint binding (2026-07-21)
The twist joints are now RESOLVED and BOUND: the ctor calls
owner->ResolveJoint(torsoHorizontalJoint / torsoHorizontalShadowJoint) (the
skeleton is live — see MECH.NOTES.md), stores them as Joint*, and
TorsoSimulation pushes currentTwist onto horizontalJointNode via
Joint::SetRotation (hinge → Radian; ball → EulerAngles yaw).
The bring-up TEST.EGG mech has a FIXED torso (horizJoint='', enabled=0), so
its twist path is inert — correctly guarded (null joint → no apply, no crash). A
torso-twist mech drives the joint. Verified headlessly; the VISUAL rotation
needs the renderer.
Elevation does NOT drive a gun joint (corrected 2026-07-21)
Checked BT411 before reconstructing a "gun-elevation joint": no such joint
binding exists anywhere in the authentic engine. currentElevation instead
composes into Mech::eyepointRotation (the cockpit-eye / weapon-boresight
pitch) — see MECH.NOTES.md "PHASE 5.3 INCREMENT 5". TorsoSimulation only
needed to keep advancing the scalar state, which it already did; the missing
piece was the CONSUMER (Mech::Simulate's eyepoint composition), now
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 useslastPerformance - lastUpdate(the engineSimulationfields at +0x10/+0x14), slewing (a copy whose command stamplastUpdateTime@0x254 is ahead of the clock) useslastUpdateTime - lastUpdate.targetTwist = twistAtUpdate + twistRate * elapsed, clamped (left = upper, right = lower). Returns the slewing flag. The decomp'sDAT_0052140cdivide isTime::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 totargetTwistagain.
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
- HUD free-aim slew, the look-button state machine (
BTCommitLookState/gBTLookPitch/gBTLookYaw— composes with elevation into the full eyepoint) — still staged. Torso::Read/WriteUpdateRecord— the copy sim's update feeders (see above).