Files
BT412/context/gauges-hud.md
T
arcattackandClaude Fable 5 3c34ae6de6 KB: full staleness audit + sweep -- 35 verified corrections across 16 topics, 15 new glossary terms
Adversarially-verified audit (18 agents) against the task #46-#56 landings:
MP/current-state understatements, superseded punch readings, retired env
gates, dead paths, line-cite drift; env-gate hub table completed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 10:56:51 -05:00

152 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 `<Name>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<int> 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]
## Cockpit HUD reticle (main screen, inside view) — LIVE
`BTReticleRenderable` (0x358 bytes, ctor @004cc40c) draws over the finished 3D frame in cockpit
view only, via the recovered **dpl2d** 2D display-list API (recorders @0x487f34-0x488630; opcode
map + coordinate model in `phases/phase-02-dpl2d-reticle.md`; port: `game/reconstructed/dpl2d.cpp`).
Geometry is the ctor's hardcoded calibration (originX 0.35, originY 0.25, scaleY 0.5, right range
ladder 0..1200 m, bottom heading tape, center cross + dot; tick ladders via FUN_004cd938). The
range caret binds to the live target range (`BTSetHudTargetRange`, fed by mech4's targeting step).
**Weapon pips:** the build loop (part_014.c:5386) registers EVERY subsystem
`IsDerivedFrom(0x511830 = MechWeapon)` — lasers, PPCs AND missile launchers (BLH = 7 pips) — via
`AddWeapon` @004cdac0 (verified store map in btl4vid.hpp).
**The per-frame `Execute` @004cdcf0 is RECOVERED (task #37, capstone disasm via
`tools/disas2.py` — the full annotated read: the task-37 commit + btl4vid.cpp comments) [T1],
and every instrument is now live [T2]:**
- **Right ladder** = range 01200 m: a YELLOW width-2 BAR from ladder-top to the caret + a
**GREEN width-1 caret triangle** (ctor @4550-4551 sets green/1 AFTER the yellow bar call —
transcription color bug caught by a period reference screenshot, 2026-07-09; same for the
bottom bowtie carets @4569-4570); pegs at 1200 with no target; the DISPLAYED range slides at
**500 m/s** toward the true pick range (HudSimulation :5652 [T1]). ⚠ A period pod screenshot
(`C:\git\image.webp`, likely a DIFFERENT pod revision — its crosshair is ~2.5× taller with
arrowhead arms, NOT our binary's ±0.04..0.16 program) structurally CONFIRMS our layout: yellow
bar + green caret + colored pip dots on the right ladder, bottom tape + green bowtie, rotating
compass circle bottom-left, and a mid-ladder range reading with no lock (the world-pick
terrain range). Our glyph constants remain [T1] from OUR 4.10 binary. **The weapon pips sit on
this same ladder at each weapon's authored max-range mark** — caret below a pip = that weapon
reaches the target. BLH authored data (live dump): 3× ER-M laser red @**500 m** (two stacked
at one column; PipExtendedRange=1), 2× missile amber (0.6,0.4,0) @**800 m**, 2× PPC blue
@**900 m** — so the 7 pips read as 3 weapon-SYSTEM groups. (The engineering-panel "RANGE 500M"
labels are panel text; the authoritative reach is the streamed WeaponRange.)
- **Bottom 21-tick tape = the TORSO-TWIST indicator** (NOT a heading tape): deflection line +
carets at `∓(span/2)·(RotationOfTorsoHorizontal / HorizontalTorsoLimit)` (HUD attrs 4/5/6).
Fixed-torso BLH: centred (authentic static).
- **Circle-with-stem = the COMPASS** (HUD attr 0xD CompassHeading, rad→deg rotation) at
`(botX, botY3·tickMajor0.03)`; the **THREAT trail** (attr 0xC ThreatVector) draws inside its
rotated frame: 0.05-unit attack-direction marks, fresh <2 s red, expiring at 6 s, 1 s blink tick.
Port feed: the player's TakeDamage handler pushes the impact direction.
- **Pips** (composed into subB6, master-called): hidden when the weapon's DAMAGE state == 1
(destroyed, attr 1); LIT (A) when the FIRE-CYCLE state == 2 (loaded, attr 0x1c; port source
rechargeLevel ≥ 1) else dark ring (B, charging); filtered by `weaponMode & elementMask&0xF`
(the weapon-GROUP bits Front/Rear/Left/Right). **Range plays NO part** — Execute never reads
the stored TargetWithinRange slots.
- **Lock ring** = subB9 (ring+cross) at frame centre, **SPINNING 4°/frame** while the Lock attr
(0xA) is up. The Lock PRODUCER is the authentic HudSimulation rule (part_013.c:5619-5634 [T1],
wired task #38): lock requires a target AND your own HUD's host zone damage < **0.75** (a
shot-up targeting computer loses lock) AND the targeted zone's damage < **1.0** (whole-mech
target checks zone 0 — so a wreck's dead zone can't re-lock). The hotbox stays visible without
lock (box = HotBoxVector, ring = Lock — separate signals). The binary also hangs the PNAMEx.bgf
player-name mesh on the 3D marker here (3D chain still deferred).
- **Simple-X mode** (PrimaryHudOn off, mask 0x20): the minimal reticle — a small green cross
(±0.02..0.08 arms) riding the aim translate (ctor @4689-4705 [T1]) — swapped for the full HUD
by Draw's state switch.
- **Target HOTBOX** (attr 0xB HotBoxVector) = a rectangle hugging the projected extents — x±4
around the hotbox point, +1/11.5 vertical (K=2.8145 baked projection; the port uses the live
per-axis projection) — switching to the left/right edge ARROW past ±1.6 or behind.
- Reticle state Off/On + `PrimaryHudOn` (mask 0x20) picks full HUD vs the "simple X" list;
the aim group translates by `reticlePosition` = the TORSO BORESIGHT (SetMatrix; NOT a free
cursor — see [[combat-damage]] Targeting: the stick twists the torso, no mouse-aim exists).
This recovery also CONFIRMS the HUD attr-table ids 4/5/6/8/0xA/0xB/0xC/0xD name↔use pairings
(hud.hpp had flagged them uncertain). Deferred: PNAME1-8.bgf 3D marker chain. (The canopy shell
is now authentic and shows by default — see [[cockpit-view]]; `BT_HIDE_COCKPIT=1` hides it.)
## Key Relationships
- Full history: `docs/GAUGE_COMPOSITE.md`; reticle recovery: `phases/phase-02-dpl2d-reticle.md`.
- Uses: [[attribute-pointer]] + [[reconstruction-gotchas]]; reads [[subsystems]] state.
- Renders on: [[pod-hardware]] MFD surfaces.