# 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`, primes `currentTwist`/`currentElevation`/analog axes to 0, and installs `TorsoSimulation` as the per-frame Performance (the replicant copy is console-driven via `TorsoCopySimulation`, still staged). - `analogElevationAxis` / `analogTwistAxis` members + `SetAnalogElevationAxis` / `SetAnalogTwistAxis` setters (written by `MechControlsMapper::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 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 RETIRED 5.3.119] The gate is now the binary's own pair, decoded as engine enum identities: `(flags & 0xC)` is the Entity INSTANCE field (InstanceBits=2, ReplicantInstance=4) and `0x100` is Entity::DynamicFlag (DynamicBit=8). Mover::DefaultFlags = DynamicFlag|MasterInstance and ENTITY.CPP seeds simulationFlags from the MakeMessage's instanceFlags, so the authentic gate was live all along -- no stream-builder dependency existed, and the BT411 donor's "MasterHeatSinkFlag" name was a fabrication. The whole registration family (heat x4, powersub x3, gyro, hud, myomers, emitter) flipped with it. **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).