# `vehicleSubSystems` — the engineering-screen subsystem cluster panels (reconstruction plan)

> Status: **✅ DONE — reconstructed, registered, and RENDERING** (Phase 1 + Phase 2 complete). The whole
> engineering-screen (MFD) subsystem-panel system is live: each subsystem builds its authentic cluster
> panel on its real aux-screen position. Render-verified (docked dev-gauge composite) — the panels show
> **SENSOR CLUSTER** (HeatSinkCluster), **MYOMERS** (MyomerCluster), **ER MED LASER · RANGE 500M** ×3 +
> **PPC · RANGE 500M** ×2 (Energy/Ballistic weapon clusters) with bar gauges, recharge dials, lamps, and
> the coolant/heat panel. 0 crashes, combat un-regressed. Full decomp captured in the session scratchpad
> (`vss_full.txt`/`vss_sub.txt`/`vss_kids.txt`, Ghidra headless of `BTL4OPT.EXE`). Remaining polish noted
> at the bottom.

## What it is
The config primitive `vehicleSubSystems(qXXX0.pcc .. qXXX7.pcc)` (26 uses in `L4GAUGE.CFG`, one per
vehicle init block after `MechInit;`) registers a **Make dispatcher** (`FUN_004cbaf0`,
methodDescription @`0x51c080`, 8 `typeString` params = the 8 strip bitmaps). At cockpit-build time the
Make walks the mech's subsystem roster (`entity+0x124` count / `+0x128` array, **skipping slot 0** = the
controls mapper) and, for each subsystem, builds a **status cluster panel** onto one of 12 auxiliary MFD
positions, dispatching on the subsystem's `classID` (`sub+4`):

| classID | subsystem | child cluster | ctor | new size | id string |
|---|---|---|---|---|---|
| 0xBC3 | Sensor | `HeatSinkCluster` (looks up the `"HUD"` subsystem via `FindSubsystem`, `FUN_0041f98c`) | `FUN_004c8a6c` | 0xf0 | "SensorCluster" |
| 0xBC6 | Myomers | `MyomerCluster` | `FUN_004c8df4` | 0xd8 | "MyomerCluster" |
| 0xBC8 / 0xBD4 | Emitter / PPC | `EnergyWeaponCluster` | `FUN_004c93b0` | 0xf0 | "EnergyWeaponCluster" |
| 0xBCD / 0xBD0 | Projectile / Missile | `BallisticWeaponCluster` | `FUN_004c9558` | 0x11c | "ProjWeaponCluster" |
| 0xBCE | Gauss | — (error "Gauss rifle not yet supported") | — | — | — |
| all others | | skip | | | |

Type filter before dispatch: `FUN_0041a1a4(**(sub+0xc), "")` (an IsA/registry check) must pass.

## The per-group geometry table @`0x51bf34` (12 groups × 7 dwords, keyed by `sub[0x1dc]-1`)
Each group = `{ X, Y, planeMask, subBit, mfdPortName, engPortName, planeIndex }`:
```
grp  X     Y    planeMask  subBit  mfdPort  engPort  idx
 1   0    240    0x1        0x2     mfd1     eng1     0
 2   322  240    0x1        0x4     mfd1     eng1     0
 3   0    28     0x1        0x8     mfd1     eng1     0
 4   322  28     0x1        0x10    mfd1     eng1     1
 5   0    240    0x20       0x40    mfd2     eng2     1
 6   322  240    0x20       0x80    mfd2     eng2     1
 7   0    28     0x20       0x100   mfd2     eng2     1
 8   322  28     0x20       0x200   mfd2     eng2     2
 9   0    240    0x400      0x800   mfd3     eng3     2
10   322  240    0x400      0x1000  mfd3     eng3     2
11   0    28     0x400      0x2000  mfd3     eng3     2
12   322  28     0x400      0x4000  mfd3     eng3     (self)
```
`mfdPort` → `renderer->GetGraphicsPort(name)` (the visible MFD surface); `engPort` (string) is the
engineering-screen render port name passed to the cluster sub-gauges.

## Class family (all btl4gau2, all DECLARED in btl4gau2.hpp, NONE reconstructed pre-this-effort
except `SeekVoltageGraph`)
- **`SubsystemCluster`** base @`0x4c8140` (vt `0x51a020`) : `GraphicGauge` — builds ~11 sub-gauges
  (HorizTwoPartBar temp bar, CoolingLoop ×2, PowerSource ×2, BackgroundBitmap, OneOfSeveralInt modeLamp,
  GeneratorVoltage/ScalarBarGauge, OneOfSeveralStates stateLamp, VertTwoPartBar ×2, LeakGauge). dtor
  `0x4c87dc`, Execute `0x4c88e4`, draw-bg helper `0x4c89c4`, BecameActive-draw `0x4c8820`, TestInstance
  `0x4c8990`, ShowInstance `0x4c8a28`. Reads attributes InputVoltage/HeatSink/CurrentTemperature/
  DegradationTemperature/FailureTemperature/CoolantMassLeakRate via `FUN_0041bfc0` (=`GetAttributeIndex`
  by name, returns `owner + idx - 1` or 0).
- **`HeatSinkCluster`** @`0x4c8a6c` (vt `0x519fd4`) : SubsystemCluster — adds 4 fail TwoStates
  (btehfail/btepfail/btesfail) + a NumericDisplayScalar. Execute `0x4c8db0` scales `this[0x35]` by
  `_DAT_004c8df0` (=100.0). dtor `0x4c8d18`.
- **`MyomerCluster`** @`0x4c8df4` (vt `0x519f88`) : SubsystemCluster — adds a SeekVoltageGraph
  ("EngrGraph", edestryd.pcc) + a seek-step OneOfSeveralInt (bteseek.pcc, `subsys+0x800`). Execute =
  base. dtor `0x4c8f54`.
- **`WeaponCluster`** @`0x4c8fc4` (vt `0x519f38`) : SubsystemCluster — adds SegmentArc270 recharge dial
  (reads "PercentDone") + ConfigMapGauge. Make-time warns "WeaponCluster missing <name>". Execute
  `0x4c9290` toggles a warn lamp when `this[0x36] > _DAT_004c92e0`. DrawWarningLamp `0x4c932c`,
  BecameActive `0x4c9258`, dtor `0x4c91d4`, slot17 `0x4c92e4`.
  - **`EnergyWeaponCluster`** @`0x4c93b0` (vt `0x519ee8`) : WeaponCluster — adds SeekVoltageGraph
    (OutputVoltage) + seek-step OneOfSeveralInt (`subsys+0x3f0`). dtor `0x4c94dc`.
  - **`BallisticWeaponCluster`** @`0x4c9558` (vt `0x519e98`) : WeaponCluster — adds 2× NumericDisplayInteger
    (ammo, `link+0x180`), jam/fire TwoStates (btejam/btefire), NumericDisplayScalarTwoState, eject
    BitMapInverseWipeScalar (bteejtm, `subsys+0x3f8`), a GraphicsViewRecord erase tracker. Execute
    `0x4c9a38` tracks weaponAlarm==5 (Jammed, `subsys+0x364`) + the reload clock. dtor `0x4c9940`,
    BecameActive `0x4c99cc`, TestInstance `0x4c9adc`, slots `0x4c9b24`/`0x4c9b50`.

## Sub-gauge classes to reconstruct (btl4gau2, declared not defined)
- **`AnimatedSubsystemLamp`** (CoolingLoop) @`0x4c70a4` (vt `0x51a174`) : OneOfSeveral + connection
  `FUN_004c3134` (vt `0x518ea8`, subsystem-state driven) at `+0x2b`. dtor `0x4c7134`.
- **`AnimatedSourceLamp`** (PowerSource) @`0x4c7160` (vt `0x51a130`) : OneOfSeveral + connection
  `FUN_004c31ec` (vt `0x518e9c`) at `+0x2b`. dtor `0x4c71f0`.
- **`ScalarBarGauge`** (GeneratorVoltage) @`0x4c721c` (vt `0x51a0e4`) : engine bar `FUN_00472ef0`
  (VertNormalSlider-ish) + connection `FUN_004c3288` (vt `0x518e90`) at `+0x2d`. 2nd ctor `0x4c72ac`
  (uses the engine `FUN_00474855` scalar-attr connection). dtor `0x4c733c`.
- **`ConfigMapGauge`** @`0x4c6d80` (vt `0x51a1b8`) : GraphicGauge — 5 pcc names (btjoy/cm_off/cm_other/
  cm_only/cm_both) interned into the warehouse; Execute `0x4c6f1c` reads the mapping state from
  `app->GetControlsManager()` (`DAT_004efc94+0x3c` == the LBE4ControlsManager — NOT the ModeManager,
  which is `+0x50`): `buttonGroup[btn]` (array base `+0x1c0`, stride `0x20`) `->GetMapState(*(subsys+0xE8),
  subsys, *(subsys+0xEC), 0x10000)` [T0 CONTROLS.h `ControlsUpdateManager<T>::GetMapState`, returns
  0 unmapped / 1 mappedByOthers / 2 mappedByMe / 3 both == the cm_off/other/only/both frame index].
  The 4-slot table `DAT_00518eb4..d0` is RECOVERED from the image ({y,button} pairs, blit x=0xc):
  {0x0d,0x45 Pinky} {0x25,0x46 ThumbLow} {0x3d,0x40 Trigger} {0x55,0x47 ThumbHigh}  [T1 PE bytes].
  SetColor `0x4c6ee0` (writes `this+0x94`; Execute is inert while 0 — the activation caller is
  unrecovered in the export). dtor `0x4c6e54`.

Engine primitives it calls (ALREADY in `munga_engine.lib`, callable — do NOT reconstruct): `TwoState`
(`FUN_004739d8`, vt `0x4fb740`), `NumericDisplayScalar` (`0x470888`, vt `0x4fbb40`),
`NumericDisplayInteger` (`0x470cfc`), `NumericDisplayScalarTwoState` (`0x473c94`), `BackgroundBitmap`
(`0x471d00`), and the GaugeConnection ctors `FUN_00474855`/`FUN_004749de`. Already reconstructed:
HorizTwoPartBar/VertTwoPartBar/OneOfSeveralInt/OneOfSeveralStates/BitMapInverseWipe(Scalar)/SegmentArc270/
SeekVoltageGraph.

## ⚠ THE BLOCKER — core `MechSubsystem` layout (Phase 2)
The Make reads `subsystem[0x1dc]` (auxiliary-screen position, INT 1..12; 0/miss → the panel is skipped
with "Auxiliary screen position = zero"), `[0x1e4]` (display label), `[0x1e0]` (damage-zone index),
`[0x224]` (a name). Our reconstructed `MechSubsystem` ends at **0x114** — these are **unreconstructed base
fields not populated from the resource parse**. So the panels render NOTHING until the core subsystem
layout is extended + populated (from `MechSubsystem::CreateStreamedSubsystem` / the resource ctor
`FUN_004ac644`) — a change to the WORKING combat/heat subsystem code (regression risk per the §10 systemic
rules). The bytes exist in the allocation (subclass layouts are locked to binary offsets, so there is
padding between 0x114 and the subclass fields); the task is to CARVE named fields out of that region + wire
the resource parse, verifying combat/heat un-regressed. **This is the final, sign-off-gated step.**

## Phasing — BOTH DONE
- **✅ Phase 1 (DONE):** reconstructed the cluster family (base `SubsystemCluster` + 5 subclasses) + the 4
  sub-gauge classes (`AnimatedSubsystemLamp`/`AnimatedSourceLamp`/`ScalarBarGauge`/`ConfigMapGauge`) + the 3
  connections + the Make dispatcher + registered in `BTL4MethodDescription[]`. **Plus** 4 previously
  declared-but-undefined base gauges the clusters build (`HorizTwoPartBar`, `OneOfSeveralInt`,
  `OneOfSeveralStates`, `BitMapInverseWipe`/LeakGauge) + `StateConnection` + `SeekVoltageGraph`'s virtuals —
  their `/FORCE`-stubbed ctors were segfaulting when a cluster built them.
- **✅ Phase 2 (DONE — NOT the feared core-layout re-base):** the blocker resolved cleanly. The aux-screen
  assignment turned out to be `PoweredSubsystem` fields (`auxScreenNumber`/`auxScreenPlacement`/
  `auxScreenLabel`, resource +0x104/+0x108/+0x10C) that **our ctor already populated** — the raw `sub[0x1dc]`
  reads were garbage only because the reconstructed heat-leaf branch isn't byte-exact. Fix = a bridge
  `BTGetSubsystemAuxScreen()` (powersub.cpp) that casts through the real `PoweredSubsystem` type + returns
  the NAMED fields (and is the `FUN_0041a1a4`/`0x50f4bc` type filter); the Make + `SubsystemCluster` read via
  the bridge, not raw offsets. **No `MechSubsystem` re-base was needed.** Render-verified: 7 authentic panels
  build with real aux-screen positions/labels/placements, 0 crashes, combat un-regressed.

## Remaining polish (non-blocking follow-ups)
- ✅ **`SegmentArc270` recharge dial DONE** — reconstructed (@004c6244, engine SegmentArc + a Scalar
  connection + the 0.75 span factor) + wired into WeaponCluster, reading the real `PercentDone` attribute.
  Render-verified: the green segmented recharge circles draw in the weapon panels.
- ✅ **`BitMapInverseWipe::Execute` (LeakGauge) DONE** — the authentic vertical-segment reveal wipe
  (@004c5d08), driven by the `CoolantMassLeakRate` attribute.
- **`SeekVoltageGraph::Execute`** (the seek-voltage curve plot in the Energy/Myomer clusters) — still a
  minimal-safe no-op. BLOCKED: it needs (a) the four seek-voltage attribute indices resolved (the ctor's
  `FindAttributeIndex` still returns -1 — the summarised stub) and (b) the subsystem's live seek-voltage
  sampler (`Emitter` vtbl+0x3c) — so even a reconstructed plot would draw nothing until those land.
- **`BitMapInverseWipeScalar` eject wipe** (BallisticWeaponCluster) — still a marked NULL. Needs a new base
  class (the unnamed @004c5e84 wipe base, vtable 0x518a58) + the @004c61c8 ctor. Very low value (the eject
  indicator only shows during a reload).
- **Secondary raw value-pointer reads** — the fail/jam/fire lamps + ammo counters read raw subsystem offsets
  (0x31c/0x320/0x324/0x334/0x364/0x43c/…) on the non-byte-exact heat-leaf branch → they render but show
  wrong/zero data. NOTE: the KEY gauges already read via the engine ATTRIBUTE system and show real data
  (temperature bars, the recharge dial, generator voltage, coolant leak); only these secondary lamps/counters
  are affected. The authentic fix is the heat-leaf-branch re-base (the same §10 deferred item for the Sensor
  RadarPercent gauge) — a large, regression-risky change to the working combat/heat subsystem code — or a set
  of per-field bridges like the aux-screen one.
- **`ConfigMapGauge`'s control-mapper state overlay** — ✅ RECONSTRUCTED (task #6): the state loop is
  live in btl4gau2.cpp (recovered `DAT_00518eb4` table; `buttonGroup[btn].GetMapState(...,0x10000)` via
  the complete-type bridge `BTSubsystemControlFeed`; `MechSubsystem` +0xE8/+0xEC fixed to
  controlDestination/controlMessageID, MechWeapon ctor defaults the destination to `&fireImpulse`).
  The gauge stays authentically DORMANT (the shipped binary never calls SetColor `0x4c6ee0` — no caller
  exists); `BT_CONFIGMAP=1` is the port's dev enable.
- The **separate** dev-gauge window (960×384) didn't appear in the headless test run (the panels render in
  `BT_DEV_GAUGES_DOCK=1` docked mode); the separate-window path is existing dev-gauge infra, unrelated to
  this reconstruction.
