Files
TeslaRel410/restoration/source410/BT/MECH.NOTES.md
T
CydandClaude Fable 5 3364b65ae8 BT410 Phase 5.3.1: Mech::Simulate motion core -- mech integrates motion per-frame
Installs Mech::Simulate as the mech's per-frame Performance (was DoNothingOnce).
Reconstructs the load-bearing spine of the 1995 Simulate (mech4.cpp @004ab430):
integrate the body velocity into localOrigin (linearPosition.AddScaled(..,
worldLinearVelocity, dt)) and commit the Origin to the world transform with the
engine idiom (localToWorld = localOrigin, per ENTITY.cpp:988 / MOVER.cpp:850).

Uses the NAMED engine base Mover fields (localOrigin/localToWorld/
worldLinearVelocity) -- BT411's MechBaseLayoutCheck proved the WinTesla decomp's
raw this+0x100/0x260 offsets actually stomp those base fields; the clean 1995
build addresses them by name.

- MECH.HPP: Mech Performance typedef + SetPerformance + Simulate() decl.
- MECH.CPP: SetPerformance(&Mech::Simulate) at ctor tail; Simulate body.

Verified headlessly (BT_MECH_LOG "[sim] mech pos=" 1Hz): BT_DRIVE="5,0,10"
advances the mech +5.0/s x, +10.0/s z (y fixed) = exactly the injected world
velocity; no BT_DRIVE -> worldLinearVelocity is 0, mech holds spawn pose. Zero
Fail/Exception either way. BT_DRIVE is a retained dev hook for headless motion.

Deferred (locomotion wave): gait-cycle self-propulsion feeding worldLinearVelocity
(IntegrateMotion->AdvanceBodyAnimation, steered by mapper throttle/turn), heading
integration, terrain-height drop, cockpit telemetry filters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:45:24 -05:00

11 KiB

MECH.CPP / .HPP — reconstruction notes

Status: Mech::Make (factory) reconstructed; the Mech constructor and the entire mech subsystem are the CURRENT FRONTIER — the largest remaining milestone in the reconstruction.

What is reconstructed

  • Mech::Make(MakeMessage *) = return new Mech(creation_message); — the factory the registry / MakeAndLinkViewpointEntity calls.
  • The static shared data (ClassDerivations / DefaultData) — from the earlier staging.
  • A staged Mech ctor that chains the real JointedMover base ctor (so the model skeleton/segments stream from BTL4.RES) and then Fails.

The spawn factory path is now validated live: BTPlayer::CreatePlayerVehicle builds the Mech::MakeMessage, Application::MakeAndLinkViewpointEntity routes it through the registry to Mech::Make -> new Mech -> the JointedMover base ctor (real engine code, streams the mech model) -> the Mech ctor Fail. Boot reaches mech.cpp:66.

Why the Mech ctor is a milestone, not a brick

Mech::Mech is @004a1674, 5690 bytes — the largest single function in the game. It walks the model's segment table and instantiates the full subsystem roster (power, heat, weapons, actuators, controls, tech, damage zones), and primes ~150 members. Reconstructing it requires three things the tree does not yet have:

  1. The complete 1995 Mech member layout as named fields. MECH.HPP currently carries int reserved[331] (1324 bytes) as a placeholder. BT411's Ghidra reconstruction sidesteps the layout entirely by writing raw offset pokes (Wword(0x104), *(void**)((char*)this + 0x388), this[0x14a]) — a WinTesla-port technique that CANNOT compile into a fresh 1995 build, where BC4.52 assigns offsets from the header declarations. So the layout must be derived as real members first.
  2. The subsystem class hierarchy. MechSubsystem + derived: sensor, generator (gnrator), gyro, torso, HUD, myomers, plus the weapon subsystems (mechweap, ppc, gauss, projweap, mislanch, ammobin...). Of these, only GAUSS, PPC, SENSOR survive as source in the 4.10 archive (CODE/BT/BT); GNRATOR is a partial .TCP. The rest are part of the measured "917 missing functions" and come only from BT411's decomp.
  3. The segment-table walk that reads the model resource and instantiates + wires each subsystem.

BT411's mech reconstruction is ~12,255 lines across mech.cpp / mech2 / mech3 / mech4 / mechsub alone, before the subsystem TUs — this is the bulk of the remaining game.

Reconstruction plan (for the dedicated mech milestone)

  1. Derive the Mech member layout. Turn reserved[331] into named members. Sources: BT411's offset comments (this[0x14a] gyroSubsystem, +0x388 target entity, etc.) give the binary offsets; the embedded member types (NameFilter, AlarmIndicator, Slot/Socket, Reticle, Filter, CString, StateIndicator) come from the surviving MUNGA headers. Cross-check total size against the binary's new Mech allocation.
  2. Reconstruct MechSubsystem base + the subsystem roster, backdating BT411 mechsub.cpp + the per-subsystem TUs; fold in the surviving 4.10 GAUSS/PPC/ SENSOR source where it exists (authoritative over the decomp).
  3. Reconstruct the Mech ctor against the named layout: embedded-member construction, the segment-table walk, subsystem instantiation + wiring.
  4. Then the per-frame path (mech2 animation SM, mech3, mech4 locomotion/ targeting) and the damage/weapon handlers.

Until then the ctor Fails cleanly at the frontier; everything below it (engine, app, network, mission, player, factory, jointed-mover base) runs real reconstructed code.

PHASE 4 DONE + PHASE 5 FRONTIER (2026-07-20)

The Mech constructor WORKS. Live boot: new Mech -> segment walk instantiates the full subsystem roster into the base Entity subsystemArray (dispatch on the real VDATA classIDs; unknown IDs -> base MechSubsystem so no NULL slots) -> caches sensor/gyro/sinkSource/hud -> mech links as viewpoint -> SetMappingSubsystem installs the MechControlsMapper in slot 0 -> mission startup. All structural.

Phase-5 frontier (the behavioral integration): boot now crashes (real NULL deref, not a staged Fail) in Simulation::GetAttributePointer reached via Simulation::PerformAndWatch (the per-frame execute + watcher update). Root: the reconstructed subsystems reuse the base Subsystem::AttributeIndex, so they publish NONE of their real attributes (Sensor RadarPercent/SelfTest/BadVoltage, Generator OutputVoltage, Emitter ChargeLevel, HUD/Torso/Gyro readouts, ...). The watcher/gauge binding + per-frame PerformAndWatch resolve those attribute IDs and hit a bad/NULL activeAttributeIndex path.

Phase-5 work, in order:

  1. Per-subsystem AttributeIndex: define each subsystem's AttributePointers[] (ATTRIBUTE_ENTRY(class, Name, member)) + a real AttributeIndex chained to the parent, and pass it to that subsystem's DefaultData (instead of the base). The surviving CODE headers (SENSOR.HPP, PPC.HPP, GAUSS.HPP) list the attribute enums; the decomp AttributePointers[] tables give the rest.
  2. Watcher / capability-chain / plug wiring in the Mech ctor (controllable/ heatable/weaponRoster/damageable) so PerformAndWatch has valid targets.
  3. The per-frame simulation itself: mech2 (animation SM), mech3, mech4 (locomotion/targeting) -- ~9K lines, currently all staged Fail.
  4. Rendering integration (the DPL renderer bridge) + playable verification.

Everything through the Mech ctor + viewpoint link runs real reconstructed code.

PHASE-5 CRASH LOCALIZED (2026-07-20)

Trace-confirmed: the Mech ctor FULLY constructs on the real pod TEST.EGG mech -- subsystemCount=33 weaponCount=7 (BT_MECH_LOG). The crash is AFTER construction, in BTL4Application::MakeViewpointEntity's "Load the control mappings" block (btl4app.cpp ~420): it SearchLists the ControlMappingsListResourceType resource and binds each streamed mapping to a subsystem attribute -- via an AttributeWatcher -> Simulation::GetAttributePointer(attributeID). The reconstructed subsystems publish NONE of their real attributes (they reuse the base Subsystem::AttributeIndex), so the streamed attribute IDs don't resolve.

Phase-5 step 1 = per-subsystem AttributeIndex, and it's a precision task:

  • BT411 has the AttributePointers[] tables (mechsub 1, heat/HeatSink 12, powersub 4, Generator, sensor 3, torso 13, weapons, ...) -- but ATTRIBUTE_ENTRY(class,Name,member) needs each member NAMED + accessible (several of ours sit in reserved dynamics pads -- Gyroscope/Torso/HUD -- and must be broken out).
  • The attribute-ID enums chain Simulation::NextAttributeID -> Entity -> Mover -> ... -> subsystem, and MUST match the 4.10 binary IDs (the streamed control mappings reference them by number). Then: watcher/plug/capability-chain wiring, then the mech2/3/4 per-frame sim (~9K lines, staged), then rendering. This is the behavioral half.

PHASE-5.1 CRASH SOLVED + BOOT REACHES THE GAME LOOP (2026-07-20)

The GetAttributePointer fault was localized (per-slot probe over the roster) to roster slot 22 = PPC_1, an energy weapon: its GetAttributePointer read entryCount off a NULL activeAttributeIndex. Root cause: our reconstructed EMITTER.HPP DECLARES static AttributeIndexSet AttributeIndex (with a ChargeLevelAttributeID) but EMITTER.CPP never DEFINED it — and the surviving 4.10 PPC.CPP binds PPC::DefaultData to the inherited PPC::AttributeIndex (= Emitter::AttributeIndex), so the SharedData ctor's activeAttributeIndex(&attribute_index) captured a null/zero symbol. The surviving GAUSS.CPP also chains Emitter::AttributeIndex as its inheritance, so the same hole corrupts GaussRifle. (Sensor et al. never crashed: their DefaultData uses the defined Subsystem::AttributeIndex.)

Fix (EMITTER.CPP): define Emitter::AttributePointers[] (one entry, ATTRIBUTE_ENTRY(Emitter, ChargeLevel, chargeLevel)) and Emitter::AttributeIndex chained to Subsystem::AttributeIndex, and point Emitter::DefaultData at it. This is the correct 1995 reconstruction (Emitter publishes the beam charge level for the HUD gauge; PPC binds it; Gauss chains it). Static-init order is satisfied by ORDER_bt (... emitter ppc ... gauss) with subsystm in munga.lib (constructs first). After the fix the per-slot probe walks all 33 slots (PPC_1/2, ERMLaser_1..3, AmmoBinSRM6_1/2, SRM6_1/2, MessageManager, MechTech, ...) cleanly, and the control-mapping binding block completes.

Result: with the Emitter fix + BTPlayer::InitializePlayerLink + VehicleDeadMessageHandler(non-death), the reconstructed tree now boots with zero Fail() all the way into the live per-frame game loop (LBE4ControlsManager::Execute spinning). See BTPLAYER.NOTES.md. The remaining gap to "playable" is behavioral (mech2/3/4 per-frame sim + renderer + real control input), not structural — the boot path is complete.

PHASE 5.3 INCREMENT 1: Mech::Simulate MOTION CORE (2026-07-21)

Mech::Simulate(Scalar) is reconstructed and installed as the mech's per-frame Performance (SetPerformance(&Mech::Simulate) at the end of the ctor). Until now the mech ran the base DoNothingOnce; it now runs real body code every frame (dispatched via Mover→Entity→Simulation::PerformAndWatch once RunningMission).

This increment is the load-bearing spine of the 1995 Simulate (mech4.cpp @004ab430): integrate the body velocity into localOrigin (linearPosition.AddScaled(linearPosition, worldLinearVelocity, dt)) and commit the Origin to the world transform with the engine's own idiom (localToWorld = localOrigin, per ENTITY.CPP:988 / MOVER.CPP:850). BT411's RE was decisive here: the mech's motion operates on the ENGINE BASE Mover fields (localOrigin/projectedOrigin/previousOrigin/projectedVelocity/ localToWorld) — the raw this+0x100/0x260/0x26c offsets in the WinTesla decomp actually stomp those base fields (their MechBaseLayoutCheck static_asserts). So the clean reconstruction uses the NAMED base members directly.

Verified headlessly (BT_MECH_LOG [sim] mech pos= 1 Hz): with BT_DRIVE="5,0,10" the mech advances +5.0/s in x and +10.0/s in z (y fixed), exactly the injected world velocity; with no BT_DRIVE worldLinearVelocity is zero and the mech holds its spawn pose — no regression, zero Fail. BT_DRIVE is a retained DEV hook (world-velocity injection) for headless motion tests.

Deferred to the next increments (the locomotion wave):

  1. gait-cycle self-propulsion that FEEDS worldLinearVelocity — the authentic model derives forward speed from the walk/run animation cycle (IntegrateMotionAdvanceBodyAnimation→cycleDistance) steered by the control-mapper throttle/turn demands, NOT a raw velocity;
  2. heading integration (rotate localOrigin.angularPosition by the turn rate);
  3. terrain-height drop (BoundingBoxTreeNode::FindBoundingBoxUnder) resting the feet on the ground;
  4. cockpit telemetry FilteredScalars (head/aim/leg/torso angular rates).