# Red Planet — the six control presets on the map screen The six amber buttons down the right flank of the map display, labelled PRESET 1 … PRESET 6 on the glass itself. They are not a display option and they do not touch the map: **each one is a complete factory layout for the four mappable joystick buttons**, authored per vehicle and shipped in `RPL4.RES`. Pressing one swaps the whole stick over, live, mid-race. ``` map glass, right flank (vRIO 0x18..0x1D, amber) ┌──────────────┐ │ PRESET 1 ● │ Secondary7 press │ PRESET 2 ○ │ Secondary8 │ │ PRESET 3 ○ │ Secondary9 ▼ │ PRESET 4 ○ │ Secondary10 VTVRIOMapper::SelectPresetMessageHandler │ PRESET 5 ○ │ Secondary11 │ (keyboard 1-6 joins here) │ PRESET 6 ○ │ Secondary12 ▼ └──────────────┘ L4VTVControlsMapper::PresetEnable(n) ├─ remove old ModePresetN from the │ mode manager, add the new one │ │ │ ▼ │ every control mapping whose modeMask │ carries that bit goes live; the other │ five presets' mappings go dead │ └─ NotifyOfPresetChange(old, new) ▼ lamp old→dim, new→on ``` ## 1. What the player sees The map is the pod's portrait-mounted secondary monitor. Its gauge group paints the button legends itself — `tags` in `assets/RP411/GAUGE/L4GAUGE.CFG:1611`, six 16×102 strips at x=464: | legend | file | cell | |---|---|---| | PRESET 1 … PRESET 6 | `butpres1.pcc` … `butpres6.pcc` | one per legend cell down the right edge | | ZOOM + / ZOOM − / reticle / HORN | `butzmin`, `butzmout`, `butcross`, `buthorn` | left edge, top four cells | The legend grid is not `height/6`: the first cell starts 13 rows down and the six cells are 102 tall on a 105 pitch (13 + 6×102 + 5×3 = 640). vRIO's `MFDSplitView::LayoutButtons` scales exactly that grid so each on-screen button lands against its own painted label (`MUNGA_L4/L4MFDVIEW.cpp:683`, constants at `:103`). Addresses run **down** each column from the anchor — the map view is created with `SideColumns, 0x10, 0x18` (`MUNGA_L4/L4VB16.cpp:4450`), so the left column is Secondary1…6 top-to-bottom and the right column is Secondary7…12, i.e. **PRESET 1 is the top button, PRESET 6 the bottom**. That ordering is confirmed by the left column, where the code binds Secondary1→zoom in, 2→zoom out, 3→reticle, 4→horn (`RP_L4/RPL4MPPR.cpp:1857`) and the artwork paints ZOOM+/ZOOM−/cross/HORN in exactly that order, leaving the bottom two cells blank — the pod wires only 6 of each column's 8 addresses; 0x16/0x17 and 0x1E/0x1F are Tesla relays (`MUNGA_L4/L4CTRL.cpp:1901`). Preset 1 is lit at construction, the other five dim (`RP_L4/RPL4MPPR.cpp:1962`). These six lamps are driven explicitly by `NotifyOfPresetChange`; unlike most panel lamps they are not linked to their button's mapping state (`SetAutomaticOperation(False)` in `CreateControlledLamp`, `RP_L4/RPL4MPPR.cpp:47`). ## 2. The mechanism — a preset is a mode-mask bit The engine gates every control mapping behind a `ModeMask`. RP adds six bits purely for this (`RP_L4/RPL4MODE.h:30`, `nextModeBit` = 0 so the values are literal): | mode | bit | value | |---|---|---| | ModeNonConfig / ConfigReady / ConfigOn | 0–2 | 0x001 / 0x002 / 0x004 | | **ModePreset1 … ModePreset6** | **3–8** | **0x008 … 0x100** | | ModeBasic / ModeStandard / ModeIntercom | 9–11 | 0x200 / 0x400 / 0x800 | `PresetEnable` (`RP_L4/RPL4MPPR.cpp:685`) does one thing: removes the outgoing preset's bit from the application mode manager and adds the incoming one. `ControlsUpdateManager::Update` then only dispatches instances whose `modeMask` intersects the live mask (`MUNGA/CONTROLS.h:599` shows the same test in `GetMapState`), so five sixths of the authored stick mappings are simply invisible at any moment. Consequences that fall out of that design: - Presets cost nothing to switch — no rebuild, no allocation, one mask word. - They are exactly six because there are six switches: `presetCount = 6 // limited to number of switches!` (`RP_L4/RPL4MPPR.h:119`). - Nothing *but* control mappings is preset-scoped. No gauge, bitmap or strip in `L4GAUGE.CFG` references `ModePreset*` — the mode names exist in the lookup table (`RP_L4/RPL4MODE.cpp:23`) only so the resource compiler can resolve them in the control-mapping source. - Entering configuration state drops all six (`ModeAllNonConfig`) and restores the previously selected one on exit (`RP_L4/RPL4MPPR.cpp:483`). Keyboard `1`–`6` calls `PresetEnable` directly (`RP_L4/RPL4MPPR.cpp:1048`), the same entry point the flank switches use. Both paths therefore move the lamps, because `PresetEnable` announces the change itself through the virtual `NotifyOfPresetChange` (`:733`) rather than leaving it to the switch handler — see §7. ### One mask, two consumers The live mask is a single 32-bit word on the `ModeManager` (`MUNGA/MODE.h:65`), seeded once with `ModeInitial` — `ModeNonConfig | ModeBasic`, **0x201** — at `RP_L4/RPL4APP.cpp:539`, and changed only by `AddModeMask` / `RemoveModeMask` / `ReplaceModeMask` (`MUNGA/MODE.h:26`). Two subsystems read it, and both gate the same way — a bitwise AND, never an equality, so any shared bit passes and `ModeAlwaysActive` (`-1L`, all bits) always matches: | consumer | object | gate | |---|---|---| | controls | `ControlsInstance::modeMask` | `MUNGA/CONTROLS.cpp:132` in `ControlsMappingGroup::Update` — decides whether a mapping dispatches at all | | gauges | `GaugeBase::modeMask` | `MUNGA/GAUGREND.cpp:3625`, `:3782`, `:3852` — decides whether a drawable sits in the active list | The gauge half runs in `GaugeRenderer::ExecuteForeground` (`MUNGA/GAUGREND.cpp:3581`), which reads the mask once per pass, derives `change_mode_mask = current ^ previous` (`:3597`), files newly created gauges into the active or inactive chain (`:3625`), and — only when the mask actually moved (`:3661`) — shuffles objects between them (`ActivateGaugeBases` `:3782`, `DeactivateGaugeBases` `:3852`). Only the active chain is executed, so an unmatched drawable costs one list entry and nothing else. The lamp manager is handed the same word a few lines earlier (`:3604`), which is how the preset and mode lamps are filtered. A drawable whose bit is never set therefore never draws, and never complains — it is simply parked in `inactiveList` for the life of the process. That is exactly what happened to the intercom art (§3): its gauges are authored, resolved and loaded, and then sit in the inactive chain forever because nothing ever ORs `ModeIntercom` into the mask. ## 3. What is actually in a preset Only the four mappable stick buttons are ever preset-scoped — `FirstMappableButton`…`LastMappableButton` minus the hat (`MUNGA_L4/L4CTRL.h:497`): | element | button | |---|---| | 0x40 | trigger | | 0x45 | pinky | | 0x46 | thumb low | | 0x47 | thumb high (by the hat) | Everything else — throttle, pedals, stick axes, hat, the whole aux panel — is bound `ModeAlwaysActive` or `ModeNonConfig` and is identical in all six presets. The functions that can land on a stick button are the ones that also have a panel button and a "configure" partner: booster fire, chute, weapon trigger, LIFT CUT, SIDESLIP, HORN, and the intercom PTT — the last of which never works on a shipped pod, see below (`RP/VTVMPPR.cpp:144`, `RP/BOOSTER.cpp:50`, `RP/CHUTE.cpp:48`, `RP/WEAPSYS.cpp:103`). ### The intercom PTT binding is dead on shipped hardware The intercom press-to-talk needed a headset and a comms panel that never went past prototype cockpits. `ActivatePTTMessageID` is still bound in the authored data and still handled in the code, but no production pod can trigger it, so **the buttons it occupies are dead, and are reported as unbound in the tables here and on the roster page**. Anyone re-decoding `RPL4.RES` will find message ID 13 sitting on the pinky and wonder why the tables show it empty, hence this section. PTT occupies 26 preset rows across 13 vehicles — always the pinky, always presets 1 and 3, and only on lightly armed or unarmed vehicles, where the pinky was free. Two independent things in the shipped assets confirm the hardware never arrived: 1. **Its panel label was never drawn.** The tool panel (`auxUL2`) has four button-pair quadrants and `common_setup` fills three of them — LIFT CUT at (22,240), SIDE SLIP at (327,240), HORN at (327,54) (`assets/RP411/GAUGE/L4GAUGE.CFG:767`). The fourth, (22,54), is blank — and that is exactly the `AuxUpperLeft5/6` pair the PTT is bound to (`RP_L4/RPL4MPPR.cpp:2000`, between the LIFT CUT/SIDESLIP pairs at `:1978` and the HORN pair at `:2011`). Every other configurable system has a legend on the glass; the PTT has bare panel. 2. **Its display art never reaches the glass.** `GAUGE/RPAICOM.PCX` and `GAUGE/RPACOMM.PCX` are finished 640×480 intercom station screens that the gauge config never references at all. The edge strips `strpcom1/2.pcc` *are* declared — top and bottom of the centre aux display, `port = auxC` (`assets/RP411/GAUGE/L4GAUGE.CFG:706`) — but gated on `ModeIntercom`, and **nothing ever sets that bit**: there is no `AddModeMask(ModeIntercom)` anywhere in the tree, and the config emits no `enable` command for the data-driven path (`MUNGA/GAUGREND.cpp:1791`) to pick up either. The one block that would have wired the intercom buttons sits inside `#if 0` (`MUNGA_L4/L4ICOM.cpp:903`) and still names `L4ModeManager`, a class that no longer exists — so it was cut before the `RPL4ModeManager` rename and never revisited. None of this art has ever been on screen. Dropping it costs nothing on 12 of the 13 vehicles — presets 1 and 3 stay distinct because their thumb-low binding differs. The exception is the **quark**, whose presets 3 and 4 become identical, since the PTT was the only thing separating them. Four other vehicles ship preset pairs that were already identical before any of this: dragon, roach and spitter (3 ≡ 4) and puck (2 ≡ 5). **The roster page never mentions the PTT at all.** `docs/vtv-presets.html` is the player-facing artifact; naming a control that cannot be pressed would raise more questions than it answers, so those cells are simply blank there. ## 4. The shipped tables The mappings are streamed from the vehicle resource at mission start — `LBE4ControlsManager::CreateStreamedMappings` (`MUNGA_L4/L4CTRL.cpp:2153`), fed from the entity's ControlMappings list resource by name: `"L4"` for the pod RIO, `"Thrustmaster"` for the stick (`RP_L4/RPL4APP.cpp:761`). Each record is a `ControlsMapping` (`MUNGA/CONTROLS.h:783`) carrying its own mode mask, so a single button can appear six times with six different targets. **Every VTV in `assets/RP411/RPL4.RES` ships a full six-preset table, for both the RIO and the Thrustmaster** — 34 of them as of the resource file promoted from the airlock archive (see §8). Decoded, the *lepton* (two boosters, one chute, unarmed) reads: | | trigger | thumb high | thumb low | pinky | |---|---|---|---|---| | PRESET 1 | booster 1 | booster 2 | chute | — | | PRESET 2 | booster 1 | booster 2 | chute | **LIFT CUT** | | PRESET 3 | booster 1 | booster 2 | **LIFT CUT** | — | | PRESET 4 | booster 1 | booster 2 | LIFT CUT | chute | | PRESET 5 | booster 1 | booster 2 | **HORN** | LIFT CUT | | PRESET 6 | **LIFT CUT** | booster 1 | booster 2 | chute | An armed vehicle substitutes the weapon trigger for the primary booster. The *puck* (one booster, laser): | | trigger | thumb high | thumb low | pinky | |---|---|---|---|---| | PRESET 1 | laser | booster | HORN | — | | PRESET 2 | laser | booster | HORN | LIFT CUT | | PRESET 3 | laser | booster | LIFT CUT | — | | PRESET 4 | laser | booster | LIFT CUT | HORN | | PRESET 5 | laser | booster | HORN | LIFT CUT | | PRESET 6 | **LIFT CUT** | booster | — | laser | The authored intent is consistent across every vehicle in the file: 1. **Presets 1–5 keep the primary weapon (or lead booster) on the trigger** and shuffle the *secondary* duties — where LIFT CUT lives, whether the stick carries HORN, and whether the chute/third system stays on the stick at all. 2. **Preset 2 always puts LIFT CUT on the pinky** and **preset 6 always puts LIFT CUT on the trigger** — verified true for all 26 tables. Preset 6 is the outlier layout: everything shifts up a finger and the trigger becomes a handling control rather than a fire control. 3. Vehicles carrying fewer systems leave cells empty, and some presets then collapse into duplicates — on the puck, presets 2 and 5 are identical. 4. Heavily armed vehicles spend the freed cells on second and third weapons instead of HORN (bttlbrg, gator, roadblk). ## 5. Editing the active preset in flight The presets are also the storage for in-mission rebinding. With the CFG button on the upper-right MFD (`AuxUpperRight1/2`, registered under `ModeStandard` — `RP_L4/RPL4MPPR.cpp:1826`), the pod enters configuration state: the aux strips swap their legends from tool art to SET art (`strptol1/2.pcc` → `strpset1/2.pcc`, `L4GAUGE.CFG:715`), and every mappable button gets a temporary mapping to the chosen system's "choose" message (`RP_L4/RPL4MPPR.cpp:604`). Pressing a panel button arms a function; pressing a stick button toggles it via `AddOrErase`, which writes against **`previousPresetModeMask`** — the currently selected preset, and only that one (`RP_L4/RPL4MPPR.cpp:1538`). The `configMap` gauge on that display (`RPL4GAUG.cpp:3199`, placed at `L4GAUGE.CFG:749`) shows the state of all four stick buttons against the armed function, one icon each, using the same preset mask: `cfgNone` / `cfgOther` / `cfgMe` / `cfgBoth` for `unmapped` / `mappedByOthers` / `mappedByMe` / both (`MUNGA/CONTROLS.h:315`). Configuration is unavailable in Basic control mode — the CFG button and the `configMap` gauge are both `ModeStandard`, which is set by Standard, Veteran and Master but cleared by Basic (`RP_L4/RPL4MPPR.cpp:374`). **Nothing is persisted.** Edits live in the `ControlsMappingGroup` chains for the mission only; no code writes mappings back to disk, and the next mission re-streams the authored table from `RPL4.RES`. (Porting a real bindings file is still open — see the roadmap's Workstream A.) ## 6. The preset number goes out on the wire `PresetEnable` sets `mustMatch = preset_number` unconditionally, before the early-out, tagged `HACK - for backward watcher compatibility` (`RP_L4/RPL4MPPR.cpp:700`). `mustMatch` is a replicated attribute of the mapper (`RP/VTVMPPR.cpp:384`), so a watcher/spectator station sees which preset a pilot is on. It is the only preset state that leaves the machine. ## 7. Defects found while reading **Fixed (2026-08-06):** both lamp defects below. 1. **The preset loop wrote past `modeLamp`.** `modeLampCount` is 4, but the preset pass stored six lamps into `modeLamp[i]`, i = 0…5. The members are declared `configLamp[2], modeLamp[4], presetLamp[6]` in that order (`RP_L4/RPL4MPPR.h:312`), so indices 4 and 5 landed in `presetLamp[0..1]` and the whole thing was self-consistent by memory layout — the preset press handler read the same out-of-range slots. Two real consequences: an out-of-bounds write, and it **destroyed the four control-mode lamps** created immediately above (`:1921`), so `NotifyOfControlModeChange` drove PRESET 1–4's lamps on the map flank and the Basic/Standard/Veteran/Master lamps on the upper-right MFD were never lit at all. `presetLamp[]` was meanwhile never populated or read. The preset pass now fills `presetLamp[]` (`:1960`), which is what the array was always for. 2. **Keyboard preset switching desynchronized the lamps.** The lamp work lived in `SelectPresetMessageHandler`, so only the flank switches moved the lamps and keyboard `1`–`6` left the wrong one lit. It now lives in the virtual `NotifyOfPresetChange`, announced by `PresetEnable` itself (`:733`) — one place, every path. `VTVRIOMapper` overrides it to move the six flank lamps (`:1651`); the base mapper and the Thrustmaster mapper have no preset lamps and inherit the no-op (`:746`). Verified by reading the commanded RIO lamp states out of the running game (`PadRIO::lampState`, PadRIO + `TEST.EGG`, at rest in Basic mode). Before and after are byte-identical except for lamp 0x33: | lamps | before | after | |---|---|---| | 0x18 PRESET 1 / 0x19–0x1D PRESET 2–6 | 3c / 14 | 3c / 14 | | 0x30–0x32 MASTER/VETERAN/STANDARD | 14 | 14 | | **0x33 BASIC** | **14 (dim — never lit)** | **3c (lit)** | Still open, by design rather than by accident: `PresetEnable` early-outs on `preset_number == previousPresetNumber`, so re-pressing the lit switch is a no-op. Correct for switching, but it also means a preset can never be used as a "reset to authored bindings" after the player has edited it in configuration mode. ## 8. The promoted resource file, and the vehicles that came back `assets/RP411/RPL4.RES` is no longer the file RP412 inherited. It is the 1.25 MB resource from `assets/airlock_RP411/`, a 2014 community build, which verification showed to be a strict superset of ours: * nothing is lost — the only resources ours has and it does not are two unnamed "Not Used" placeholders; * same format version (`v1.3.0.2`); * all 26 base vehicles' `L4` and `Thrustmaster` mapping streams are **byte-identical** to the ones we shipped; * `vole` matches resource-for-resource, id for id and size for size; * it boots and runs against our own `GAUGE`/`VIDEO`/`AUDIO`, with a log identical to the baseline. Its `GAUGE/L4GAUGE.CFG` was promoted with it. That file is ours plus the new vehicles' blocks, plus one fix: `dragonInit` gains `twoBoosterInit`, so the dragon's two boosters finally have gauges — it always had them in its subsystem list and the panel never drew them. **What the roster gained.** The three vehicles cut after 4.10 are back — `dark` (*Blacker Puck*), `blkspk` (*Black Speck*) and `blktrn` (*Black Tarantula*) — with the same tables they shipped with: `blkspk`'s preset 4 still puts its third booster on the thumb-high, `dark`'s preset 5 still spends the HORN slot on its second demo pack. Beyond them the archive adds `neut` (*Neutrino*, four boosters and an `Eject` subsystem) and eight community "Blacker" variants, plus seven maps. **The `black` mystery was a renaming bug.** RP411's gauge config carried a `blackInit` block that nothing could ever select, because `GetGameModel()` returns a model name and the lookup is `Init`. The airlock config calls the same block — byte-identical body — `blktrnInit`, which is the model's real name. There was never a vehicle called "black"; the block had simply been renamed out of reach. `disk` remains the one genuine orphan: a panel layout with no vehicle behind it in any resource file, and no entry in the console's own config either. Four of the eight community variants have a `ControlsMappings List` but no mapping streams of their own; they reference another vehicle's by resource id, so they carry no preset table of their own and are absent from the roster page.