# 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. ## 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 intercom PTT (`RP/VTVMPPR.cpp:144`, `RP/BOOSTER.cpp:50`, `RP/CHUTE.cpp:48`, `RP/WEAPSYS.cpp:103`). ## 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. **All 26 VTVs in `assets/RP411/RPL4.RES` ship a full six-preset table, for both the RIO and the Thrustmaster.** Decoded, the *lepton* (two boosters, one chute, unarmed) reads: | | trigger | pinky | thumb low | thumb high | |---|---|---|---|---| | PRESET 1 | booster 1 | **PTT** | chute | booster 2 | | PRESET 2 | booster 1 | **LIFT CUT** | chute | booster 2 | | PRESET 3 | booster 1 | PTT | **LIFT CUT** | booster 2 | | PRESET 4 | booster 1 | chute | LIFT CUT | booster 2 | | PRESET 5 | booster 1 | LIFT CUT | **HORN** | booster 2 | | PRESET 6 | **LIFT CUT** | chute | booster 2 | booster 1 | An armed vehicle substitutes the weapon trigger for the primary booster. The *puck* (one booster, laser): | | trigger | pinky | thumb low | thumb high | |---|---|---|---|---| | PRESET 1 | laser | — | HORN | booster | | PRESET 2 | laser | LIFT CUT | HORN | booster | | PRESET 3 | laser | — | LIFT CUT | booster | | PRESET 4 | laser | HORN | LIFT CUT | booster | | PRESET 5 | laser | LIFT CUT | HORN | booster | | PRESET 6 | **LIFT CUT** | laser | — | booster | 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 PTT or 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.