The MadCat torso twists, the view turns with it, and targeting follows. Three reconstruction fronts closed: THE ELECTRICAL WATCHDOG CHAIN (why the torso never powered up): - PowerWatcher::UpdateWatch reconstructed (@004b181c, the REAL registered Performance -- PTR @0050f5fc; Ghidra missed the fn start): the watchdog MIRRORS the watched subsystem's electrical level (+0x278), brownout downgrade when gen output <= minVoltage% x rated. @004b1804 relabeled ResetToInitialState (slot 10) -- the old "Simulation" tag was wrong. - The factory watcher-CONNECT pass reconstructed (vtable slot +0x38, @004aee2c/@004b1a40 byte-identical, recovered from raw exe bytes): watchedLink.Add(roster[watchedSubsystem]) on the master node. Was the SubProxy::Start() no-op -- every watchdog sat at 0 forever. - MinVoltageScale = 0.01 (a 10-byte x87 literal @0x4b1924; was 1.0f = permanent brownout) and PowerWatcher's Derivation chains its REAL base HeatWatcher (the HeatableSubsystem stand-in broke IsDerivedFrom for the whole Torso/Searchlight/ThermalSight family). - KB correction swept: derivation tag 0x50e604 = HEATWATCHER (not "HeatSink"); the btl4gaug heat-widget gate now tests it via the BTIsHeatWatcher bridge. THE CROSSHAIR (task #58 forensics, 6-agent workflow + live probes): - The VIEW is TORSO-MOUNTED: jointtorso -> jointeye -> siteeyepoint in every twist-capable .SKL; the camera + canopy ride the same hinge subtree through HingeRenderable's live matrix-stack compose -- ALREADY WORKING in the port. The crosshair stays screen-centered (center IS the boresight); the twist reads on the tape carets/compass/radar. - The real bug was the port's gBTAimX = tan(twist) slew (the falsified "body-mounted view" model): the camera already carried the twist, so the crosshair counter-slid to hull-forward and the fire ray with it. Deleted; the pick ray inherits the twist from the yawing eye basis. - Two instrumentation traps documented (chase-eye-as-default-camera, BT_FORCE_TORSO clobbering real joints -> the hook now only fills unresolved ones); an over-correcting explicit eye compose was added on those false readings and retired the same day. CONTROLS + REPLICATION: - Q/E spring-center on release (the axis is a twist-RATE demand; the old hold-deflection model drifted forever); X also zeroes the axis and pulses the authentic torso Recenter (@004b6918). M cycles control mode via the real CycleControlMode body. - Torso update-record DIRECTION fixed: engine truth is Write=serialize / Read=apply; @004b6a78 is the READ (was mislabeled Write) and the missing WRITE @004b6a1c recovered from raw disasm (recordLength 0x1C, twist/vel/rate at +0x10/14/18) -- kills the replicant's 0xCDCDCDCD -140-degree ghost twist. - Marching-ghost desync: 4 Standing-case guards zero stale reverse cycleSpeed (negative cadence passed the <= ZeroSpeed stop gate). - Kill credit rerouted to the OBSERVED killer (lastInflictingID -> killer's player link) -- kills count, target K/D populates. KB: subsystems.md (watcher chain), multiplayer.md (record direction), combat-damage.md + gauges-hud.md + cockpit-view.md (torso-mounted view re-correction), decomp-reference.md (new addresses + tag fix), open-questions.md (dead capability-roster loops 2-4, snapshot CD read). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7.4 KiB
id, title, status, source_sections, related_topics, key_terms, open_questions
| id | title | status | source_sections | related_topics | key_terms | open_questions | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| subsystems | Subsystems — the factory + the reconstruction waves | established | PROGRESS_LOG.md §10d; docs/SUBSYS_PLAN.md; docs/VEHICLE_SUBSYSTEMS.md; docs/RESOURCE_AUDIT.md |
|
|
|
Subsystems
The mech's components (heat/power/weapons/sensor/gyro/torso/myomers/…), built by the Mech-ctor
factory. Full per-family plan: docs/SUBSYS_PLAN.md; the engineering-cluster panels:
docs/VEHICLE_SUBSYSTEMS.md; the layout audit: docs/RESOURCE_AUDIT.md. ClassID map:
decomp-reference §2.
The factory
The Mech ctor switches on the resource ClassID to build each subsystem into the subsystem-roster
(subsystemArray@0x128). The case <Name>ClassID LABELS are systematically MISLABELED — the
real class is the // FUN_004xxxxx ctor-address comment reconciled via CLASSMAP.md, not the case
name (e.g. 0xBBE case="Sensor" is actually AggregateHeatSink). Each real class is wired via a
Create<Class>Subsystem(Mech*,int,void*) bridge in the class's own .cpp (do NOT #include the
real subsystem headers into mech.cpp — the local stubs collide). Keep the alloc SIZE + special-cache.
[T2]
Class hierarchy (two branches, shared only at MechSubsystem)
- Heat leaf:
PoweredSubsystem : HeatSink : HeatableSubsystem : MechSubsystem. Emitter/PPC/ Sensor/Myomers/ProjectileWeapon/MissileLauncher/GaussRifle chain here. Uses 0xCSubsystemConnection- 0x54
GaugeAlarm54(alarm level at +0x14). [T1]
- 0x54
- Watcher branch:
Torso/Gyroscope/Searchlight/ThermalSight/HUD : PowerWatcher : HeatWatcher : MechSubsystem;AmmoBin : HeatWatcher. Uses a 0xCWatchedConnection+ 0x54WatcherGaugeAlarm. [T1] Making a base byte-exact GROWS every subclass — they must be re-based TOGETHER in one build, eachstatic_assert-locked (the P7 alarm-unification: HeatSink 0x1D0 → PoweredSubsystem 0x31C → MechWeapon 0x3F0 → Emitter 0x478). [T2]
Reconstruction waves (state: ALL 20 factory cases wired to real classes — 0xBD3 = the real SubsystemMessageManager since task #7, 2026-07-11; the mapper lives in roster slot 0)
- WAVE 1 — HeatableSubsystem re-based onto MechSubsystem (de-shadow cascade).
- WAVE 2 — heat family (Condenser/AggregateHeatSink/Reservoir) + HUD + MechTech (un-swap).
- WAVE 3 — power bus (Generator/PoweredSubsystem) + Emitter/PPC fire-path (end-to-end fire; heat conducts to the central sink via the linked-sink roster).
- WAVE 4 — standalone readouts: Sensor/Searchlight/ThermalSight/AmmoBin (de-shim, gate fixes).
- WAVE 5 — Torso (aim twist/elevation; joint-I/O reconstructed; the BLH record disables torso → faithfully no visible twist).
- WAVE 6 — Myomers (mover-coupled; structural un-stub is INERT — no live mover/heat coupling).
- WAVE 7 — projectile/missile weapons (byte-exact; flying projectiles are a PORT reconstruction — the 2007 Entity is 0x1BC vs the binary's 0x300, so raw base-offset reads fail).
- TASK #56 (2026-07-11) — Gyroscope LIVE byte-exact (ctor @004b3778, sizeof 0x3D0 locked;
integrators +
FUN_004b2980damage fan-out; joint dispatch from the Mech performance tail @0x4aaf74/83;BT_GYRO_LOG/BT_GYRO_TRACE) — see cockpit-view. - DEFERRED — SubsystemMessageManager 0xBD3 (WAVE 8 — a
damage/explosion consolidation hub cached to
Mech[0x10d]=0x434; recon MISLABELS the slotcontrolsMapper+ the drive squats there while the real mapper is roster slot 0, so wiring it needs an untangle. NOT the valve/advanced-damage gate — that's the owning BTPlayer @mech+0x190, see combat-damage/open-questions). [T2]
The watcher electrical chain (task #57, 2026-07-13 — the torso power gate)
The Watcher branch is POWERED indirectly: a watcher WATCHES another roster subsystem and mirrors its electrical state. The full chain, byte-verified [T1]:
- Data: the model entry
WatchedSubsystem=<name>→ segment index +2 at resource+0xE4 (HeatWatcher::CreateStreamedSubsystem@004aec54); the ctor @004aeb40 stores it at watcher+0x128 (watchedSubsystem). - Bind (factory post-roster loop 1): vtable slot 14 (+0x38) —
@004aee2c(HeatWatcher) /@004b1a40(PowerWatcher/Torso override, byte-identical); Ghidra missed both function starts (recovered from raw bytes; the vtables.tsv rows have GAPS where the exporter skipped slots — dump the exe bytes at vtable+slot*4 when a slot looks missing). Master-gated ((owner->simulationFlags & 0xC)==0 && (flags & 0x100)); bindswatchedLink(+0x114).Add( owner->roster[+0x128][watchedSubsystem]). Port:BTWatcherWatchedIndex/BTWatcherBindTargetbridges (heatfamily_reslice.cpp) called from the mech.cpp factory loop. - Tick:
@004b181c= the REALPowerWatcherPerformance (PTR @0050f5fc → 004b181c) =UpdateWatch(): heat mirror (FUN_004aeac4) +watchdogAlarm.SetLevel(watched->electrical level @+0x278)+ brownout downgrade to 1 whengen->outputVoltage(+0x1DC) <= minVoltage(+0x180) × gen->ratedVoltage(+0x1D8). The Torso sims (@004b5cf0/@004b65f8) call it first-line. (@004b1804is slot-10 ResetToInitialState, NOT the Simulation — old recon mislabel, fixed.) - MinVoltageScale = 0.01 — a 10-byte x87 literal at 0x4b1924 (
0a d7 a3 70 3d 0a d7 a3 f8 3f); the port had 1.0f, making minVoltage 100× too big → the brownout latched every watchdog at 1. - PowerWatcher::GetClassDerivations chains HeatWatcher (real base) — the old HeatableSubsystem
stand-in broke
IsDerivedFrom(HeatWatcher)for Torso/Searchlight/ThermalSight and silently skipped them in the connect pass. - Effect:
Torso::ElectricalStateLevel()==Ready(4)un-gateseffectiveTwistRate— the MadCat torso twists at its authored 50°/s (±140° limits, roster 17 watching 15 → generator @10000V); the BLH is authentically fixed (horizontalEnabled=0, limits ±0.01°). [T2 live-verified] - STILL DEAD: factory loops 2-4 (heatable/weapon/damageable capability rosters) go through the
SubProxystub whoseIsDerivedFromreturns 0 — they add NOTHING. See open-questions.
The four systemic checks (every subsystem)
See reconstruction-gotchas: (1) shadowing (re-declared engine-base fields), (2) the Wword trap,
(3) message-handler chaining, (4) entity validity. Plus resource-struct layout (must mirror the class
hierarchy, byte-exact — the RESOURCE_AUDIT found 8 bugs) and object-layout (alias/phantom/interior
fields). A +0x128-style owner offset is the ROSTER, not the segment table (the heat-link type
confusion that caused a heap-corruption via OOB ConductHeat writes). [T2]
Engineering cluster panels (vehicleSubSystems)
Not a widget — a per-subsystem FACTORY (FUN_004cbaf0) building a status CLUSTER onto one of 12 aux
MFD positions, dispatching on classID → HeatSinkCluster/MyomerCluster/EnergyWeaponCluster/
BallisticWeaponCluster. Reads PoweredSubsystem::auxScreenNumber/Placement/Label (res +0x104/8/C) via
the BTGetSubsystemAuxScreen bridge. See docs/VEHICLE_SUBSYSTEMS.md + gauges-hud. [T2]
Key Relationships
- Data: decomp-reference (ClassIDs/hierarchy). Bugs: reconstruction-gotchas.
- Feeds: combat-damage (weapons/damage), gauges-hud (attribute state).
- Plan:
docs/SUBSYS_PLAN.md.