--- id: gauges-hud title: "Cockpit Gauges / MFD HUD system" status: established source_sections: "docs/GAUGE_COMPOSITE.md (full map); CLAUDE.md §8" related_topics: [reconstruction-gotchas, decomp-reference, subsystems, pod-hardware] key_terms: [gauge, methodDescription, attribute-pointer, GaugeRenderer, MFD, PCC] open_questions: - "Condenser valve control gates on the owning BTPlayer game-mode flag (mech+0x190), likely off in basic missions" - "Status-message queue (StatusMessagePool) is a NULL stub -> MessageBoard is empty" --- # Cockpit Gauges / MFD HUD The pod's cockpit instruments: the secondary MFD (radar/heat/comm), the 5 mono MFDs, and the engineering screens. Full map + per-widget history in **`docs/GAUGE_COMPOSITE.md`**. **The gauge system is COMPLETE** at the registration+binding level: all 50 config attribute bindings resolve (0 NULL) and every config gauge primitive is registered + built (0 parse-skips). [T2] ## Architecture (three layers) 1. **The config** `content/GAUGE/l4gauge.cfg` — a text file the engine `GaugeInterpreter` parses (`BuildConfigurationFile → Initialize → GetProcedureBody → ParsePrimitive`). Each mech's gauge tree is built from the label `GetGameModel()+"Init"` (e.g. `bhk1Init`), which invokes shared blocks (`MechInit`, `Secondary1`) that `configure` the ports and call gauge primitives. [T1] 2. **Widgets** — each `keyword(...)` in the config resolves to a `MethodDescription` in `BTL4MethodDescription[]` (btl4grnd.cpp). An UNREGISTERED keyword is **parse-skipped** (never built). Base primitives (`numeric`/`digitalClock`/`rankAndScore`/`vertBar`-base/`segmentArc`) are ENGINE (L4GAUGE.cpp); BT-specific ones (`vertBar`/`map`/`pilotList`/`GeneratorCluster`/ `sectorDisplay`/`prepEngr`/`messageBoard`/`vehicleSubSystems`/the ColorMapper family) are reconstructed + registered. [T2] 3. **Data binding** — gauges bind to game state by NAME via the engine [[attribute-pointer]] system: the config's `Subsystem/Attribute` (e.g. `HeatSink/CurrentTemperature`) resolves through `ParseAttribute → FindSubsystem (stricmp GetName()) → GetAttributePointer`. A class PUBLISHES an attribute via a `AttributeID` enum + `static AttributePointers[]` (`ATTRIBUTE_ENTRY`) + `GetAttributeIndex()` chained to its parent. ⚠ DENSE-TABLE HAZARD (see [[reconstruction-gotchas]] §11). [T2] ## Reconstructing a widget — the recipe `MethodDescription Class::methodDescription = { "keyword", Class::Make, { ParameterDescription rows } };` + a line in `BTL4MethodDescription[]` before `&BTL4ChainToPrevious`. The static `Make(int port, Vector2DOf pos, Entity*, GaugeRenderer*)` reads `methodDescription.parameterList[]`, allocs (`operator new(binSize)` or plain `new`), placement-news the ctor. Then ctor/dtor/ TestInstance/BecameActive/Execute. **Gotchas that bite EVERY widget:** - Container-Execute must override (§10 gotchas) — else `Gauge::Execute` aborts. - `/FORCE` trap — a prose-only slot AVs on first call. - Databinding trap — never raw-read owner offsets; use a bridge (`BTGetSubsystemAuxScreen`, …). - ReconStream no-op — use `DEBUG_STREAM` for logs, not `DebugStream`. - Lazy build — wait for the gauge window before concluding "not built" (`BT_GAUGE_SKIP_LOG`). [T2] ## The completed widgets (this project's gauge wave) - **Attributes published:** HeatSink table (Degradation/Failure/NormalizedPressure/CoolantMassLeakRate/ ValveSetting/…), PoweredSubsystem (InputVoltage), MechWeapon (OutputVoltage), Mech (LinearSpeed, radar Position/Quaternion), Sensor (RadarPercent), **AggregateHeatSink (AmbientTemperature=300 — the LAST NULL)**. [T2] - **Widgets reconstructed + registered:** ColorMapper family (cmHeat/cmCrit/cmArmor/multiArmor), headingPointer, vertBar (VertTwoPartBar), segmentArcRatio, oneOfSeveralPixInt, map (radar), PlayerStatus, vehicleSubSystems (the engineering cluster panels), LeakGauge, vertNormalSlider, **pilotList** (Comm KILLS/DEATHS), **GeneratorCluster**, **SectorDisplay** (radar SECTOR X/Z read-out — live), **PrepEngrScreen** (12 engineering-screen label overlays), **MessageBoard** (comm ticker — deferred-empty). [T2] - **Condenser valve gauge:** ValveSetting→coolantFlowScale reads the authentic **1/N** (`RecomputeCondenserValves`, FUN_0049f788, was a no-op stub). [T2] ## Dev composite (off-pod) `BT_DEV_GAUGES` renders the 6 pod [[MFD]] surfaces in a separate 960×384 window (bit-plane masks over one shared `SVGA16` pixelBuffer; `SVGA16::DrawDevSurface`). On the POD they come from `SETENV.BAT`/`L4GAUGE` on the real multi-adapter hardware (`FindBestAdapterIndices`/`BuildWindows`, intact). The `overlay` port (SectorDisplay lives there) shares the `sec` physical surface via a different bit-plane (0x00C0). [T2] ## Authentically-static (do NOT "fix") `currentTemperature ~77` (huge thermalMass ~1.39e6 → a 3.5-heat shot rounds to zero); Degradation/Failure temps are fixed markers; AmbientTemperature 300; CoolantMassLeakRate 0 on a pristine mech (damage-gated); cmArmor/cmCrit all-green (undamaged solo player — BT is PvP-only); RANK 1 solo; MessageBoard empty (no status messages exist in bring-up). The Heat MFD is authentically NEAR-STATIC, not a live heat-accumulation display. [T2] ## Remaining = DATA FEEDS, not widgets (deferred systems) The **condenser valve CONTROL** gates on the owning **BTPlayer**'s game-mode flag (`mech+0x190`, `GetPlayerLink()`, via `MechSubsystem::IsDamaged`/`FUN_004ac9c8` reading `player+0x274`) — NOT the 0xBD3 messmgr (a common earlier misconception; 0xBD3 is a damage/explosion hub at `Mech[0x10d]`=0x434). In a basic mission the valve/mode flags are likely off, so the dormant valve is plausibly AUTHENTIC — see [[open-questions]] `mech+0x190`. The **MessageBoard** feed needs **StatusMessagePool** (a NULL stub) + the per-player status queue. `SeekVoltageGraph`'s 4 Seek* attrs are a cluster-child (not config-called). [T2] ## Key Relationships - Full history: `docs/GAUGE_COMPOSITE.md`. - Uses: [[attribute-pointer]] + [[reconstruction-gotchas]]; reads [[subsystems]] state. - Renders on: [[pod-hardware]] MFD surfaces.