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
+40 -10
View File
@@ -82,21 +82,20 @@ Searchlight::Searchlight(
Check(owner);
Check_Pointer(subsystem_resource);
segmentFlags = 0;
hostShutDown = False;
watchedVoltageLevel = 4; // Ready
heatStateLevel = 0; // NormalHeat
controlsAllowLights = True;
graphicsDirty = False;
instanceFlags = 0;
// (WAVE 4 de-shim) the 7 cross-family shim fields are gone; the accessors
// read the inherited base state directly. Only the own fields init here.
lightState = 0; // @0x1D8
requestedOn = 0; // @0x1E0
stateAlarm.Initialize(2); // FUN_0041b9ec(this+0x1E4, 2)
commandedOn = subsystem_resource->segmentIndex; // @0x1DC <- inherited resource +0x28
if (((GetSegmentFlags() & 0xC) == 0) &&
((GetSegmentFlags() & 0x100) != 0))
// GATE FIX (the functional bug the de-shim unblocks): the binary reads OWNER
// simulationFlags (param_2+0x28, raw part_013.c:6012), NOT the old shadow
// `segmentFlags` -- which was seeded 0, so the gate was ALWAYS FALSE and
// SearchlightSimulation NEVER installed. A live master segment arms it; a
// copy/replicant gets DontExecute (flag 2). Mirrors torso.cpp:181-182.
if (((owner->simulationFlags & 0xC) == 0) &&
((owner->simulationFlags & 0x100) != 0))
{
SetPerformance(&Searchlight::SearchlightSimulation); // this[7..9] <- PTR_FUN_00511200
}
@@ -256,3 +255,34 @@ int
return True;
}
//===========================================================================//
// WAVE 4 -- compile-time layout locks. PowerWatcher is Torso-proven at 0x1D8,
// so the own fields land at their EXACT binary offsets; sizeof is bounded to
// the factory alloc. (BtAlarm is a 4-byte stand-in for the binary's 0x54
// GaugeAlarm -- immaterial: nothing reads Searchlight at a raw offset past its
// own fields, and the shared BT_LOCAL_ALARM_SHIM stays 4 bytes because
// thermalsight.hpp shares that guard.)
//===========================================================================//
struct SearchlightLayoutCheck
{
static_assert(offsetof(Searchlight, lightState) == 0x1D8, "Searchlight lightState @0x1D8 (PowerWatcher ends 0x1D8)");
static_assert(offsetof(Searchlight, commandedOn) == 0x1DC, "Searchlight commandedOn @0x1DC (res+0x28)");
static_assert(offsetof(Searchlight, requestedOn) == 0x1E0, "Searchlight requestedOn @0x1E0");
static_assert(offsetof(Searchlight, stateAlarm) == 0x1E4, "Searchlight stateAlarm @0x1E4 (proves base ends 0x1D8)");
static_assert(sizeof(Searchlight) <= 0x238, "sizeof(Searchlight) must fit the factory Memory::Allocate(0x238)");
};
//===========================================================================//
// WAVE 4 factory bridge -- Searchlight (factory case 0xBD8, "LegSubsystem" label).
// The real class at 0xBD8 (ctor @004b84dc) is Searchlight; the factory built a
// LegSubsystem RECON_SUBSYS stub. No cache write; not a weapon (raw
// part_012.c:10166-10175 stores roster-only).
//===========================================================================//
Subsystem *CreateSearchlightSubsystem(Mech *owner, int id, void *seg)
{
return (Subsystem *) new (Memory::Allocate(0x238))
Searchlight(owner, id, (Searchlight::SubsystemResource *)seg, Searchlight::DefaultData);
}