The 'mysterious flag pair' gating every master-only registration was never BT-specific state waiting on unreconstructed stream builders. It is the Entity base itself: InstanceBits=2 puts the instance field at mask 0xC (ReplicantInstance=4), DynamicBit=8 makes DynamicFlag 0x100 -- so the binary's (flags & 0xC)==0 && (flags & 0x100)!=0 reads 'a master-instance dynamic entity', spelled MasterInstance + DynamicFlag in the 1995 headers. Mover::DefaultFlags = DynamicFlag|MasterInstance, ENTITY.CPP:978 seeds simulationFlags from the MakeMessage's instanceFlags, and our spawn recipe has passed Mech::DefaultFlags since the boot ladder -- the authentic gate was live all along. The BT411 donor's 'MasterHeatSinkFlag' name was a fabrication that made an engine constant look like a missing subsystem feature (donor drift #12). Eleven sites flip from the GetInstance() mirror to the binary pair: the heat-family registrations (x4), powersub (x3), gyro, hud, myomers, emitter, and the Torso ctor's master/copy selection (@004b6b0c verbatim, isDamagedCopy set in both branches). The MECHWEAP score-post and MECH view gates are different families and stay as they are. Statically proven (DefaultFlags carries both bits) and mission-soaked clean. No [T2] divergence markers remain in the tree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
99 lines
5.4 KiB
Markdown
99 lines
5.4 KiB
Markdown
# 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).
|