subsystems: un-stub Wave 4 readouts + Wave 6 Myomers (10->15 factory cases)

WAVE 4 (standalone readouts) -- Sensor(0xBC3)/Searchlight(0xBD8)/
ThermalSight(0xBDE)/AmmoBin(0xBCB) un-stubbed via Create<Class>Subsystem
bridges + Torso-style de-shim (drop cross-family shadow fields, redirect
accessors to the real inherited base, static_assert layout locks).
- FIX: Searchlight/ThermalSight ctors gated their Performance on the shadow
  segmentFlags(=0) so it NEVER installed; switched to owner->simulationFlags.
- AmmoBin: retype ammoAlarm HeatAlarm->WatcherGaugeAlarm(0x54) + drop the
  statusState@0x40 shadow -> exact 0x22C layout.
- Guard HeatWatcher::WatchSimulation against the unresolved watchedLink
  (null-deref exposed once these sims run; faithful fix = resolve the link).
- Heat-leaf branch (Sensor) is not byte-exact -> overflow-lock only.

WAVE 6 (Myomers 0xBC6, mover-coupled) -- structural un-stub, gated
BT_MYOMERS (default on; =0 -> Actuator stub). Wired INERT: MyomersSimulation
early-returns (advanced-damage gate stubbed) + no-op mover feed, so the live
JointedMover is untouched and the gait cannot regress. De-shim drops the
owner*/segmentFlags shims to fit the exact-0x358 alloc. Authentic mover/heat
coupling deferred (needs messmgr 0xBD3 + reconciling the mover feed with the
gait cutover).

Verified: BLH tick 20->27, Mad Cat 24, combat DESTROYED un-regressed,
locomotion un-regressed, 0 crashes, 0 heap detections under BT_HEAPCHECK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-05 23:01:31 -05:00
co-authored by Claude Opus 4.8
parent 5aa791b245
commit 8b36440d05
14 changed files with 421 additions and 141 deletions
+38 -15
View File
@@ -196,6 +196,11 @@ extern Subsystem *CreateGeneratorSubsystem(Mech *, int, void *); // 0xBC1 (WAVE
extern Subsystem *CreatePoweredSubsystem(Mech *, int, void *); // 0xBC2 (WAVE 3a)
extern Subsystem *CreateEmitterSubsystem(Mech *, int, void *); // 0xBC8/0xBD4 (WAVE 3b)
extern Subsystem *CreateTorsoSubsystem(Mech *, int, void *); // 0xBC5 (WAVE 4 -- base re-based, layout locked)
extern Subsystem *CreateSensorSubsystem(Mech *, int, void *); // 0xBC3 (WAVE 4 readouts -- de-shimmed, layout locked)
extern Subsystem *CreateSearchlightSubsystem(Mech *, int, void *); // 0xBD8 (WAVE 4 readouts -- de-shimmed + gate fixed)
extern Subsystem *CreateThermalSightSubsystem(Mech *, int, void *); // 0xBDE (WAVE 4 readouts -- de-shimmed + gate fixed)
extern Subsystem *CreateAmmoBinSubsystem(Mech *, int, void *); // 0xBCB (WAVE 4 -- AmmoBin : HeatWatcher, layout locked)
extern Subsystem *CreateMyomersSubsystem(Mech *, int, void *); // 0xBC6 (WAVE 6 -- mover-coupled drive; INERT, gated BT_MYOMERS)
// CreateGyroSubsystem (0xBC4) is reconstructed + ready in gyro.cpp (layout re-based,
// joint I/O real), but NOT wired -- the gyro ctor/integrator reconstruction is
// incomplete (garbage output); see the 0xBC4 case below.
@@ -689,9 +694,11 @@ Mech::Mech(
subsystemArray[id] = CreatePoweredSubsystem(this, id, seg); // FUN_004b0f74
break;
case MyomersClassID: // 0xBC3
subsystemArray[id] = (Subsystem *)
new (Memory::Allocate(0x328)) Myomers(this, id, seg); // FUN_004b1d18
case MyomersClassID: // 0xBC3 -> real class Sensor (ctor @004b1d18, "Myomers" mislabel)
// WAVE 4: the real class at 0xBC3 is Sensor (string pool @0050fae0 +
// SENSOR.HPP); MyomersClassID is a factory-enum mislabel (real Myomers
// is 0xBC6). De-shimmed + layout-locked (radarPercent@0x31C, sizeof==0x328).
subsystemArray[id] = CreateSensorSubsystem(this, id, seg); // FUN_004b1d18
break;
case GyroClassID: // 0xBC4 -> real class Gyroscope (ctor @004b3778)
@@ -717,9 +724,19 @@ Mech::Mech(
sinkSourceSubsystem = subsystemArray[id]; // Wword(0x10e) -- gyro links to +0x1D8
break;
case ActuatorClassID: // 0xBC6
subsystemArray[id] = (Subsystem *)
new (Memory::Allocate(0x358)) Actuator(this, id, seg); // FUN_004b8fec
case ActuatorClassID: // 0xBC6 -> real class Myomers (ctor @004b8fec, "Actuator" stub)
// WAVE 6: the real class at 0xBC6 is Myomers (the artificial-muscle
// drive). GATED BT_MYOMERS (default ON): the real class constructs +
// ticks but is INERT w.r.t. locomotion/heat (no-op mover feed + the
// advanced-damage sim gate is False), so it can't regress the gait.
// BT_MYOMERS=0 falls back to the Actuator stub. Authentic mover/heat
// coupling is a follow-up (needs real Mech accessors + messmgr 0xBD3,
// and must reconcile the mover feed with the gait cutover first).
if (BTEnvOn("BT_MYOMERS", 1))
subsystemArray[id] = CreateMyomersSubsystem(this, id, seg); // FUN_004b8fec
else
subsystemArray[id] = (Subsystem *)
new (Memory::Allocate(0x358)) Actuator(this, id, seg);
break;
case WeaponEmitterClassID: // 0xBC8 -> real Emitter beam weapon
@@ -727,9 +744,11 @@ Mech::Mech(
++weaponCount;
break;
case JumpJetClassID: // 0xBCB
subsystemArray[id] = (Subsystem *)
new (Memory::Allocate(0x22c)) JumpJet(this, id, seg); // FUN_004bd5c4
case JumpJetClassID: // 0xBCB -> real class AmmoBin (ctor @004bd5c4, mislabeled)
// WAVE 4: AmmoBin : HeatWatcher : MechSubsystem; own block @0x180,
// sizeof 0x22C compile-time locked. Feeds a ProjectileWeapon/
// MissileLauncher (0xBCD/0xBD0) via a SharedData connection (still stubbed).
subsystemArray[id] = CreateAmmoBinSubsystem(this, id, seg); // FUN_004bd5c4
break;
case MechWeaponClassID: // 0xBCD
@@ -766,9 +785,11 @@ Mech::Mech(
hudSubsystem = subsystemArray[id]; // raw part_012.c:10164 param_1[0x16d] = HUD
break;
case LegSubsystemClassID: // 0xBD8
subsystemArray[id] = (Subsystem *)
new (Memory::Allocate(0x238)) LegSubsystem(this, id, seg); // FUN_004b84dc
case LegSubsystemClassID: // 0xBD8 -> real class Searchlight (ctor @004b84dc, mislabeled)
// WAVE 4: de-shimmed + GATE FIXED (was gating on the shadow
// segmentFlags -> Performance never installed). Searchlight :
// PowerWatcher; own fields layout-locked at 0x1D8+.
subsystemArray[id] = CreateSearchlightSubsystem(this, id, seg); // FUN_004b84dc
break;
case HeatableClassID: // 0xBDC -> real class MechTech (mislabeled).
@@ -778,9 +799,11 @@ Mech::Mech(
// the 0x5b4 cache belongs to case 0xbd6 (the HUD) -- earlier recon had it swapped.
break;
case DisplayClassID: // 0xBDE
subsystemArray[id] = (Subsystem *)
new (Memory::Allocate(0x234)) MechDisplay(this, id, seg); // FUN_004b8718
case DisplayClassID: // 0xBDE -> real class ThermalSight (ctor @004b8718, "MechDisplay" stub)
// WAVE 4: de-shimmed + GATE FIXED. ThermalSight : PowerWatcher;
// own fields layout-locked at 0x1D8+. The cockpit IR view-mode
// render hooks (pvision/viewport) are clearly-marked no-ops.
subsystemArray[id] = CreateThermalSightSubsystem(this, id, seg); // FUN_004b8718
break;
default: