From 1d6339b226c9ce9f03d782092f9db0fc598f1a55 Mon Sep 17 00:00:00 2001 From: arcattack Date: Sun, 19 Jul 2026 16:28:38 -0500 Subject: [PATCH] Gitea #6: secondary MFD Damage/Critical/Heat cycling -- reconstruct the NotifyOfDisplayModeChange override (vtbl+0x4C @4d1ae4) + wire desktop 'N' The secondary screen's schematic selector was mislabeled: @004d1ae4 (the bits-18..20 ModeSecondary* mask swap) was reconstructed as a non-virtual "SetControlMode" that nothing called, so the desktop stayed pinned on the Damage view. The binary's L4 vtable @0051e440 pins the truth: +0x48 = @004d1acc <- CycleControlModeMessageHandler (FUN_004afbe0): forwards to the base RET no-op @004b048c. A BAS/MID/ADV control-mode change never touches the secondary view (empirically confirmed: BT_MODECYCLE_TEST cycles the CONTROL MODE lamp, mask bits 18-20 unchanged, schematic stays ARMOR). +0x4C = @004d1ae4 <- CycleDisplayModeMessageHandler (FUN_004afcac): THE Damage/Critical/Heat selector, indexed by displayMode (table @0051dbe4 = ModeSecondaryDamage/Critical/Heat). Authentic pod inputs (streamed type-6 .CTL EventMappings, dumped via the new BT_CTRLMAP_LOG EVENT records): secondary-panel button 0x15 -> msg 0x15 CycleDisplayMode (manual p13, the "'Mech status Info center" bottom left of the secondary screen), button 0x18 -> msg 0x14 CycleControlMode (manual p6, top right), 0x10/0x11 -> ZoomIn/Out. The DOS keyboard fallbacks (Keypress 0x13d/0x13e = extended F3/F4) are dead under the WinTesla VK map, hence the desktop pin. Port wiring (the M/ModeCycle pattern): key N / pad RightThumb -> action DisplayCycle -> gBTDisplayCycle -> CycleDisplayModeNow() -- the same body the pod console button message drives. Both .MAP profiles + the compiled-in default updated. Verified live (docked gauges + BT_SHOT, BT_VIEWCYCLE_TEST): the sec panel cycles ARMOR DAMAGE silhouette -> CRITICAL DAMAGE subsystem list -> HEAT DAMAGE colored list, mask 0x450421 -> 0x490421 -> 0x510421; M control-mode cycling un-regressed (BAS/MID/ADV lamp cycles, view pinned). Diags: BT_MODE_LOG, BT_VIEWCYCLE_TEST=, BT_MODECYCLE_TEST=, BT_CTRLMAP_LOG now dumps EVENT records. KB: gauges-hud secondary-view section rewritten, CLASSMAP +0x48/+0x4C slots, decomp-reference env gates, GAUGE_COMPOSITE phase-4 entry resolved. Co-Authored-By: Claude Fable 5 --- content/CONTROLS.MAP | 6 ++- content/CONTROLS_NUMPAD.MAP | 2 + context/decomp-reference.md | 3 ++ context/gauges-hud.md | 35 +++++++++++++----- docs/GAUGE_COMPOSITE.md | 5 ++- engine/MUNGA_L4/L4CTRL.cpp | 21 +++++++++++ game/reconstructed/CLASSMAP.md | 7 +++- game/reconstructed/btinput.cpp | 5 +++ game/reconstructed/btinput.hpp | 1 + game/reconstructed/btl4mppr.cpp | 65 ++++++++++++++++++++++++++------- game/reconstructed/btl4mppr.hpp | 26 ++++++++++--- game/reconstructed/mech4.cpp | 42 +++++++++++++++++++++ game/reconstructed/mechmppr.cpp | 43 ++++++++++++++++++---- game/reconstructed/mechmppr.hpp | 7 ++++ 14 files changed, 229 insertions(+), 39 deletions(-) diff --git a/content/CONTROLS.MAP b/content/CONTROLS.MAP index 9083631..46f46d9 100644 --- a/content/CONTROLS.MAP +++ b/content/CONTROLS.MAP @@ -31,8 +31,8 @@ # LeftTrigger RightTrigger (as digital, >= 50% pressed) # LeftStickX LeftStickY RightStickX RightStickY # LeftTrigger RightTrigger -# action ViewToggle LookBehind AllStop ModeCycle Valve Flush ConfigHold -# Generator1-4 Reconnect +# action ViewToggle LookBehind AllStop ModeCycle DisplayCycle Valve Flush +# ConfigHold Generator1-4 Reconnect # pckey sends an authentic 1995 PC-keyboard hotkey (the in-cockpit # dispatcher): '+'/'-' = target zoom, 'w'..'o' = pilot select, # 'a','b','c','d','f','g','s','v','x','z' = display presets, ... @@ -75,6 +75,7 @@ key G action ConfigHold # hold + fire keys = regroup the weapon key C action Valve # coolant valve cycle key H action Flush # coolant flush (HOLD -- dumps reservoir coolant) key M action ModeCycle # Basic -> Standard -> Veteran controls +key N action DisplayCycle # secondary schematic: Damage -> Critical -> Heat key V action ViewToggle # cockpit <-> chase camera (dev) key B action LookBehind # HOLD = the pod's rear-view button key F5 action Generator1 @@ -102,4 +103,5 @@ pad B action AllStop pad Y action ViewToggle pad X action LookBehind pad Start action ModeCycle +pad RightThumb action DisplayCycle pad Back action Valve diff --git a/content/CONTROLS_NUMPAD.MAP b/content/CONTROLS_NUMPAD.MAP index 852336a..f8eb82c 100644 --- a/content/CONTROLS_NUMPAD.MAP +++ b/content/CONTROLS_NUMPAD.MAP @@ -44,6 +44,7 @@ key Right button 0x43 # Hat Right # ---- Systems ------------------------------------------------------------ key G action ConfigHold key Enter action ModeCycle +key N action DisplayCycle # secondary schematic: Damage -> Critical -> Heat key V action ViewToggle key B action LookBehind key F5 action Generator1 @@ -99,4 +100,5 @@ pad B action AllStop pad Y action ViewToggle pad X action LookBehind pad Start action ModeCycle +pad RightThumb action DisplayCycle pad Back action Valve diff --git a/context/decomp-reference.md b/context/decomp-reference.md index 3da8294..c606347 100644 --- a/context/decomp-reference.md +++ b/context/decomp-reference.md @@ -283,6 +283,9 @@ default-ON (`'0'` disables). | `BT_SELF=<[pilots] tag>` | claim a specific roster seat in relay mode; absent/`auto` = the relay assigns one (SEAT_REQUEST) | | `BT_RELAY_TCP_ONLY=1` | disable the relay UDP channel (all unreliable traffic rides the TCP relay conn) | | `BT_INPUT_LOG` | binding-engine trace: CONTROLS.MAP load, button/keypad emissions, pad connect, torso twist/elev axis ([[pod-hardware]] §Desktop input remap) | +| `BT_MODE_LOG` | `[mode] display notify` — the ModeManager mask after each secondary-view swap (btl4mppr.cpp NotifyOfDisplayModeChange, Gitea #6) | +| `BT_VIEWCYCLE_TEST=` / `BT_MODECYCLE_TEST=` | scripted verify (mech4.cpp): pulse a secondary-schematic cycle / a control-mode cycle at the frame and every 300 after — pixel-verifiable with `BT_DEV_GAUGES_DOCK`+`BT_SHOT` | +| `BT_CTRLMAP_LOG` | streamed `.CTL` record audit (L4CTRL.cpp CreateStreamedMappings): Direct records with the resolved member offset + EVENT records (button→subsystem message; found the pod's secondary-panel Cycle buttons 0x15/0x18) | | `BT_CAM_LOG` | camera-seat/broadcast trace: streamed camera network count, director pick + Players-group census, ship follow state, PlayerLink dispatch/receive, ranking-window render ([[multiplayer]] camera seat) | | `BT_SPEC_LOG` | manual spec-audit dump at subsystem ctor: torso speed/limits, heat-sink count, reservoir capacity, per-subsystem coolant loop ([[pod-hardware]] §Spec cross-check) | diff --git a/context/gauges-hud.md b/context/gauges-hud.md index a883052..07a414e 100644 --- a/context/gauges-hud.md +++ b/context/gauges-hud.md @@ -8,7 +8,7 @@ key_terms: [gauge, methodDescription, attribute-pointer, GaugeRenderer, MFD, PCC open_questions: - "MessageBoard LIVE 2026-07-12 (kill ticker); remaining: only strip 0 (kill) is produced -- survey the other btsmsgs.pcx strips for authentic producers" - "HUD binary attr table @005110c0 offsets conflict with the port HUD layout on 3 slots (0x1D8/0x1EC/0x1F8) -- re-dump before publishing" - - "Secondary-view cycling (Damage/Critical/Heat) unreachable from the desktop keyboard: the mapper's 0x13d/0x13e cases are dead DOS F3/F4 codes (VK_F3/F4 = 0x72/0x73 collide with 'r'/'s') -- wire a free key to CycleControlMode" + - "Secondary-view cycling RESOLVED 2026-07-19 (Gitea #6): the selector is the DISPLAY mode (CycleDisplayMode -> vtbl+0x4C override @4d1ae4), NOT CycleControlMode; desktop 'N' / pad RightThumb wired; pixel-verified dama->crit->heat" - "MP DEATHS resolved 2026-07-12 (observed-death tally + display clamp); remaining: verify multi-death tallies stay in sync across a long session (GAUGE_COMPOSITE.md)" --- @@ -70,7 +70,7 @@ over one shared `SVGA16` pixelBuffer; `SVGA16::DrawDevSurface`). On the POD they intact). The `overlay` port (SectorDisplay lives there) shares the `sec` physical surface via a different bit-plane (0x00C0). [T2] -## The secondary screen's THREE views (Damage / Critical / Heat) — mode-gated [T0/T1] +## The secondary screen's THREE views (Damage / Critical / Heat) — mode-gated [T0/T1/T2] The `sec` port stacks three mode-gated mech-schematic layers at offset (50,0) over the always-on radar/heading/speed/messageBoard (`Secondary1`): **Damage** (`ModeSecondaryDamage`, `dama.pcc` + `colorMapArmor`/`colorMapperMultiArmor` — 4 silhouettes front/left/right/back, @@ -80,13 +80,30 @@ pixel-plane ids 60-63, per-DAMAGE-ZONE `dz_*` tint through the adpal→adpal2 ra NonMapping=0x10000, Intercom=0x20000, **SecondaryDamage=0x40000, SecondaryCritical=0x80000, SecondaryHeat=0x100000** (bits 18-20). `ModeInitial` includes **SecondaryDamage** → the ARMOR view is the default-on layer (our port creates `BTL4ModeManager(ModeInitial)`, btl4app.cpp:303). -`L4MechControlsMapper::SetControlMode` @004d1ae4 is the SELECTOR — its mask table -{0x40000,0x80000,0x100000} clears bits 18-20 and sets one — despite the "control mode" name it -switches the secondary VIEW. ⚠ Desktop gap: the Keypress cases `0x13d`/`0x13e` -(CycleControlMode/CycleDisplayMode) are the DOS Tesla extended F3/F4 codes and never fire under -the WinTesla VK map (VK_F3=0x72 collides with 'r', VK_F4=0x73 with 's' — the same collision -class as the documented 'p'/VK_F1 drop), so the desktop is PINNED on the Damage view. The -schematic shows the pilot's OWN mech only — there is no target-damage readout in the cockpit. +The schematic shows the pilot's OWN mech only — there is no target-damage readout in the cockpit. + +**The selector is the DISPLAY mode, not the control mode (Gitea #6, RESOLVED 2026-07-19) [T1→T2].** +The L4 vtable @0051e440 pins the slots: **+0x4C = @004d1ae4** — dispatched by +`CycleDisplayModeMessageHandler` (FUN_004afcac) with the new `displayMode` (0/1/2) — is the +`NotifyOfDisplayModeChange` override that clears bits 18-20 and sets the mask from the table +@0051dbe4 {0x40000,0x80000,0x100000}. (The old "SetControlMode @004d1ae4 switches the secondary +VIEW" claim was the mislabel that kept the port's copy a never-called non-virtual.) **+0x48 = +@004d1acc** — dispatched by `CycleControlModeMessageHandler` (FUN_004afbe0) — just forwards to +the base RET no-op @004b048c: a BAS/MID/ADV control-mode change NEVER touches the secondary +view (empirically confirmed: M cycles the CONTROL MODE gauge lamp, mask bits 18-20 unchanged, +schematic stays on ARMOR DAMAGE). **Authentic pod inputs** (streamed type-6 `.CTL` +EventMappings, dumped live via `BT_CTRLMAP_LOG`): secondary-panel button **0x15 → msg 0x15 +CycleDisplayMode** (the manual-p13 "'Mech status Info center", bottom left of the secondary +screen: Armor/Critical/Heat Damage Schematic cycle), button **0x18 → msg 0x14 CycleControlMode** +(the manual-p6 mode button, top right), buttons 0x10/0x11 → ZoomIn/ZoomOut 0x12/0x13 (the map +zoom ± pair). The DOS keyboard fallbacks (Keypress `0x13d`/`0x13e` = extended F3/F4) never fire +under the WinTesla VK map (VK_F3=0x72 collides with 'r', VK_F4=0x73 with 's' — the 'p'/VK_F1 +collision class), so the desktop was PINNED on Damage. **Port wiring (mirrors the M/ModeCycle +pattern): 'N' / pad RightThumb → action DisplayCycle → gBTDisplayCycle → `CycleDisplayModeNow()`** +(mechmppr.cpp; the same body the pod button message drives). Pixel-verified live (docked gauges + +BT_SHOT): ARMOR DAMAGE silhouette → CRITICAL DAMAGE subsystem list → HEAT DAMAGE colored list, +mask 0x450421→0x490421→0x510421. Diags: `BT_MODE_LOG`, `BT_VIEWCYCLE_TEST=`, +`BT_MODECYCLE_TEST=`. ## pilotList (Comm KILLS/DEATHS) row semantics + the −1 [T1/T2] One ROW PER PILOT in the mission (2-player MP = 2 rows — not duplicate displays). KILLS = diff --git a/docs/GAUGE_COMPOSITE.md b/docs/GAUGE_COMPOSITE.md index c88504c..2520674 100644 --- a/docs/GAUGE_COMPOSITE.md +++ b/docs/GAUGE_COMPOSITE.md @@ -627,7 +627,10 @@ Byte-exact re-base of the PoweredSubsystem:HeatSink LEAF (Sensor/Emitter/Myomers (delete the sensor.cpp:287-288 guard — ✅ LANDED: the guard is deleted and the leaf is byte-exact, sensor.cpp:407-413) + the raw-offset cluster reads (partially locked since — MechWeapon byte-exact); radar pip/name symbology + staticEntities (task #17, under reconstruction 2026-07-12); -CycleDisplayMode→ModeManager mask (machinery reconstructed, gauge-layer toggle unverified [T3]); +CycleDisplayMode→ModeManager mask (✅ RESOLVED 2026-07-19, Gitea #6 [T2]: @4d1ae4 relabeled the +NotifyOfDisplayModeChange override (vtbl+0x4C) and made virtual; desktop 'N'/pad RightThumb → +CycleDisplayModeNow; secondary schematic swap dama→crit→heat pixel-verified via +BT_DEV_GAUGES_DOCK+BT_SHOT; pod buttons = streamed EventMappings 0x15→msg 0x15 / 0x18→msg 0x14); numericSpeed units (open). ### Authentically-static (do NOT "fix") diff --git a/engine/MUNGA_L4/L4CTRL.cpp b/engine/MUNGA_L4/L4CTRL.cpp index eb5334a..f0df062 100644 --- a/engine/MUNGA_L4/L4CTRL.cpp +++ b/engine/MUNGA_L4/L4CTRL.cpp @@ -2286,6 +2286,27 @@ void Receiver::MessageID msg_id = mapping->messageID; + // + // BT_CTRLMAP_LOG=1: dump the streamed EVENT records too (button -> + // subsystem message). Gitea #6 needed this to identify which pod + // console buttons carried the mapper's CycleControlMode (0x14) / + // CycleDisplayMode (0x15) messages. + // + { + static const int s_ctrlmapLog = + (getenv("BT_CTRLMAP_LOG") != 0 && *getenv("BT_CTRLMAP_LOG") != '0'); + if (s_ctrlmapLog) + { + DEBUG_STREAM << "[ctrlmap] rec " << i + << " subsys " << mapping->subsystemID + << " group " << (int)mapping->controlsGroup + << " elem 0x" << std::hex << element + << " mask 0x" << (unsigned)mode_mask + << " EVENT msg 0x" << (unsigned)msg_id + << std::dec << "\n" << std::flush; + } + } + switch (mapping->controlsGroup) { case ScalarGroup: diff --git a/game/reconstructed/CLASSMAP.md b/game/reconstructed/CLASSMAP.md index 595d59e..6f1033b 100644 --- a/game/reconstructed/CLASSMAP.md +++ b/game/reconstructed/CLASSMAP.md @@ -147,8 +147,11 @@ Subsystem (MUNGA base — have source: RP/MUNGA/SUBSYSTM.HPP) │ throttleForward@0x1ac/throttleReverse@0x1b0/leftPedal@0x1b4/rightPedal@0x1b8 (cleared by derived ctors). │ InterpretControls@4d196c (throttle detent snap 1.0±0.05, BuildPilotArray, review-mode bit 0x200000, │ target-range ramp range=250*2^exp, exp clamp[0,5]; then chains MechControlsMapper::InterpretControls @4afd10). -│ ZoomTargetRangeIn@4d1b64 / ZoomTargetRangeOut@4d1b9c (±1.0). NotifyOfDisplayModeChange-override@4d1ae4 (vtbl+0x4C; ex-"SetControlMode", (mode bits18-20, -│ table @0051dbe4={0x40000,0x80000,0x100000}). SetPresetMode@4d1b24 (group*5+item -> {clear,set} mask table @0051dbf0[15]). +│ ZoomTargetRangeIn@4d1b64 / ZoomTargetRangeOut@4d1b9c (±1.0). NotifyOfControlModeChange-override@4d1acc (vtbl+0x48; forwards to base no-op @4b048c — +│ control mode never touches the secondary view). NotifyOfDisplayModeChange-override@4d1ae4 (vtbl+0x4C; ex-"SetControlMode"; Gitea #6 reconstructed +│ 2026-07-19 = THE secondary Damage/Critical/Heat selector, mode bits18-20, table @0051dbe4={0x40000,0x80000,0x100000}; pod input = streamed +│ EventMapping secondary-panel button 0x15 -> msg 0x15 CycleDisplayMode; button 0x18 -> msg 0x14 CycleControlMode). +│ SetPresetMode@4d1b24 (group*5+item -> {clear,set} mask table @0051dbf0[15]). │ EnterConfiguration@4d1840 (+0x38) / ExitConfiguration@4d18dc (+0x3C) (task #6 relabel (buttons 0x40..0x47, hat-skip 0x41->0x45, │ mode 0x8000=ConfigReady). AddOrErase evt/dir RET no-ops @4d195c/@4d1964 (+0x40/+0x44; TM can't regroup). KeypressMessageHandler@4d1bf0 │ (shared dispatcher: zoom/preset/pilot-cycle/look keys + fake-button dev keys; default -> @4d141c). diff --git a/game/reconstructed/btinput.cpp b/game/reconstructed/btinput.cpp index a2c32e9..cf3b3ae 100644 --- a/game/reconstructed/btinput.cpp +++ b/game/reconstructed/btinput.cpp @@ -70,6 +70,7 @@ enum BTActionID BTActLookBehind, BTActAllStop, BTActModeCycle, + BTActDisplayCycle, // Gitea #6: secondary schematic cycle (Damage/Critical/Heat) BTActValve, BTActFlush, // Gitea #7: HELD = coolant flush (InjectCoolant) BTActConfigHold, @@ -193,6 +194,7 @@ static const BTName sActionNames[] = { {"ViewToggle", BTActViewToggle}, {"LookBehind", BTActLookBehind}, {"AllStop", BTActAllStop}, {"ModeCycle", BTActModeCycle}, + {"DisplayCycle", BTActDisplayCycle}, {"Valve", BTActValve}, {"Flush", BTActFlush}, {"ConfigHold", BTActConfigHold}, {"Generator1", BTActGenerator1}, {"Generator2", BTActGenerator2}, {"Generator3", BTActGenerator3}, {"Generator4", BTActGenerator4}, @@ -266,6 +268,7 @@ static const char *sDefaultProfile = "key C action Valve\n" "key H action Flush\n" "key M action ModeCycle\n" + "key N action DisplayCycle\n" "key V action ViewToggle\n" "key B action LookBehind\n" "key F5 action Generator1\n" @@ -289,6 +292,7 @@ static const char *sDefaultProfile = "pad Y action ViewToggle\n" "pad X action LookBehind\n" "pad Start action ModeCycle\n" + "pad RightThumb action DisplayCycle\n" "pad Back action Valve\n"; //============================================================================= @@ -963,6 +967,7 @@ void case BTActLookBehind: next.lookBehind = 1; break; case BTActAllStop: next.allStop = 1; break; case BTActModeCycle: next.modeCycle = 1; break; + case BTActDisplayCycle: next.displayCycle = 1; break; case BTActValve: next.valve = 1; break; case BTActFlush: next.flush = 1; break; case BTActConfigHold: next.configHold = 1; break; diff --git a/game/reconstructed/btinput.hpp b/game/reconstructed/btinput.hpp index 031ce12..d22517d 100644 --- a/game/reconstructed/btinput.hpp +++ b/game/reconstructed/btinput.hpp @@ -51,6 +51,7 @@ struct BTInputState int lookBehind; int allStop; int modeCycle; + int displayCycle; // Gitea #6: secondary schematic cycle (Damage/Critical/Heat) int valve; int flush; // Gitea #7: coolant flush HELD (InjectCoolant) int configHold; diff --git a/game/reconstructed/btl4mppr.cpp b/game/reconstructed/btl4mppr.cpp index 75c4baf..61d37ab 100644 --- a/game/reconstructed/btl4mppr.cpp +++ b/game/reconstructed/btl4mppr.cpp @@ -30,10 +30,12 @@ // @004d17ac ctor @004d1814 dtor // @004d196c InterpretControls (target-range ramp + review-mode watch) // @004d1b64 ZoomTargetRangeIn @004d1b9c ZoomTargetRangeOut -// @004d1ae4 SetControlMode (mode-mask switch; calls base NotifyOfDisplayMode) +// @004d1acc NotifyOfControlModeChange (+0x48; forwards to base no-op @004b048c) +// @004d1ae4 NotifyOfDisplayModeChange (+0x4C; the secondary-view mask swap +// -- Gitea #6 relabel; ex-"SetControlMode") // @004d1b24 SetPresetMode (group/item -> preset mode-mask) -// @004d1840 CreateTemporaryEventMappings @004d18dc RemoveTemporaryEventMappings -// @004d195c NotifyOfControlModeChange (no-op) @004d1964 NotifyOfConfigurationModeChange (no-op) +// @004d1840 EnterConfiguration @004d18dc ExitConfiguration +// @004d195c/@004d1964 AddOrErase evt/dir (+0x40/+0x44, RET no-ops) // @004d1bf0 KeypressMessageHandler (the shared L4 keyboard dispatcher) // MechThrustmasterMapper --------------------------- vtable @0051e3f0 // @004d21d0 ctor @004d22b4 dtor @004d22e0 TestInstance @@ -81,7 +83,8 @@ // _DAT_004d1ac8 = 0x437a0000 = 250.0f (target-range scale: range = 250*2^x) // _DAT_004d1b94 = 1.0f zoom-in step _DAT_004d1b98 = 0.0f min exponent // _DAT_004d1bcc = 1.0f zoom-out step _DAT_004d1bd0 = 5.0f max exponent -// DAT_0051dbe4[3] = {0x40000,0x80000,0x100000} control-mode mode-masks +// DAT_0051dbe4[3] = {0x40000,0x80000,0x100000} secondary-VIEW mode-masks +// (ModeSecondaryDamage/Critical/Heat, indexed by displayMode) // DAT_0051dbf0[15] = {clear,set} mode-mask pairs (group*5+item), preset select // DAT_0051dcd0[8] = {0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30} hotbox buttons // @@ -482,25 +485,61 @@ L4MechControlsMapper::MessageHandlerSet& // //############################################################################# -// SetControlMode @004d1ae4 +// NotifyOfControlModeChange @004d1acc (vtbl+0x48) +// NotifyOfDisplayModeChange @004d1ae4 (vtbl+0x4C) //############################################################################# // -// Swap the application mode-manager's control-mode bits (18..20) for the bit -// selected by new_mode, then notify the base of the implied display change. +// Gitea #6 RELABEL (2026-07-19). The old reconstruction carried @004d1ae4 as +// a non-virtual "SetControlMode" that nothing ever called, so the desktop's +// secondary MFD stayed pinned on the Damage view. The vtable @0051e440 +// (section_dump.txt:73033) pins the truth: +// +// +0x48 = @004d1acc -- dispatched by CycleControlModeMessageHandler +// (FUN_004afbe0) with the new controlMode. Capstone disasm: pushes +// its two args and forwards to the BASE no-op @004b048c. A +// control-mode (BAS/MID/ADV) change never touches the secondary +// view -- in the shipped binary OR here. +// +0x4C = @004d1ae4 -- dispatched by CycleDisplayModeMessageHandler +// (FUN_004afcac) with the new displayMode. THE secondary-view +// selector: clears mode bits 18..20 (& 0xffe3ffff = +// ~ModeSecondaryAll), sets the mask picked from the table @0051dbe4 +// = {ModeSecondaryDamage 0x40000, ModeSecondaryCritical 0x80000, +// ModeSecondaryHeat 0x100000}, then chains the base no-op +// @004b0494. displayMode 0/1/2 = Damage/Critical/Heat -- the +// manual-p13 "'Mech status Info center" schematic cycle (bottom +// left of the secondary screen). // void - L4MechControlsMapper::SetControlMode(int new_mode) + L4MechControlsMapper::NotifyOfControlModeChange(int new_mode) { - static const ModeMask control_mode_mask[] = // @0051dbe4 - { 0x40000, 0x80000, 0x100000 }; + MechControlsMapper::NotifyOfControlModeChange(new_mode); // @004b048c (no-op) + } + + void + L4MechControlsMapper::NotifyOfDisplayModeChange(int new_mode) + { + static const ModeMask display_mode_mask[] = // @0051dbe4 + { + BTL4ModeManager::ModeSecondaryDamage, // 0 -> 0x040000 + BTL4ModeManager::ModeSecondaryCritical, // 1 -> 0x080000 + BTL4ModeManager::ModeSecondaryHeat // 2 -> 0x100000 + }; BTL4ModeManager *mode_manager = (BTL4ModeManager*)application->GetModeManager(); // DAT_004efc94+0x50 - mode_manager->RemoveModeMask(0x001c0000); // clear bits 18..20 (~0xffe3ffff) - mode_manager->AddModeMask(control_mode_mask[new_mode]); + mode_manager->RemoveModeMask( // clear bits 18..20 + BTL4ModeManager::ModeSecondaryAll); // (& 0xffe3ffff) + mode_manager->AddModeMask(display_mode_mask[new_mode]); - NotifyOfDisplayModeChange(displayMode); // FUN_004b0494 + // DIAG (BT_MODE_LOG): the resulting manager mask -- proves the + // secondary-view bit actually flipped for the gauge layer gating. + if (getenv("BT_MODE_LOG")) + DEBUG_STREAM << "[mode] display notify " << new_mode + << " -> manager mask 0x" << std::hex + << mode_manager->GetModeMask() << std::dec << "\n" << std::flush; + + MechControlsMapper::NotifyOfDisplayModeChange(new_mode); // @004b0494 (no-op) } // diff --git a/game/reconstructed/btl4mppr.hpp b/game/reconstructed/btl4mppr.hpp index e6a15cf..e4aea7b 100644 --- a/game/reconstructed/btl4mppr.hpp +++ b/game/reconstructed/btl4mppr.hpp @@ -104,11 +104,27 @@ ZoomTargetRangeOut(); // @004d1b9c //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Control / display modes + // Control / display modes (Gitea #6 relabel -- the vtable pins the slots). + // + // The binary's L4 vtable @0051e440 (section_dump): +0x48 = @004d1acc, + // +0x4C = @004d1ae4. CycleControlModeMessageHandler (FUN_004afbe0) + // dispatches vtbl+0x48 with the new controlMode; CycleDisplayModeMessage- + // Handler (FUN_004afcac) dispatches vtbl+0x4C with the new displayMode. + // So @004d1ae4 -- the {0x40000,0x80000,0x100000} secondary-view mask swap + // the old reconstruction called "SetControlMode" (never called by anyone) + // -- is the NotifyOfDisplayModeChange OVERRIDE: the secondary screen's + // Damage / Critical / Heat schematic is selected by the DISPLAY mode + // (manual p13: the "'Mech status Info center", bottom left of the + // secondary screen), NOT by the BAS/MID/ADV control mode (manual p6: top + // right of the secondary screen). @004d1acc (NotifyOfControlModeChange) + // just forwards to the base no-op @004b048c -- a control-mode change + // never touches the secondary view. // public: - void - SetControlMode(int new_mode); // @004d1ae4 (mode-mask switch) + virtual void + NotifyOfControlModeChange(int new_mode); // @004d1acc (+0x48, chains base no-op) + virtual void + NotifyOfDisplayModeChange(int new_mode); // @004d1ae4 (+0x4C, secondary-view mask swap) void SetPresetMode(int group, int item); // @004d1b24 (preset mode-mask) @@ -119,8 +135,8 @@ // old reconstruction mislabeled "NotifyOfControlModeChange / // NotifyOfConfigurationModeChange". Only MechRIOMapper overrides the // AddOrErase pair with real bodies -- a Thrustmaster cockpit cannot - // regroup. (The REAL NotifyOf* L4 overrides are @004d1acc/@004d1ae4, - // vtable +0x48/+0x4C -- out of scope here.) + // regroup. (The REAL NotifyOf* L4 overrides @004d1acc/@004d1ae4, + // vtable +0x48/+0x4C, are declared in the mode section above.) // public: virtual void diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 3b5eea1..68adbb2 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -652,6 +652,7 @@ static int gBTPPCKey = 0; static int gBTMissileKey = 0; static int gBTPinkyKey = 0; // key '4' = the pod's 4th fire button (Pinky 0x45) int gBTModeCycle = 0; // 'M' edge: cycle the control mode (mapper consumes) +int gBTDisplayCycle = 0; // 'N' edge: cycle the secondary schematic (Gitea #6, mapper consumes) int gBTLookBehind = 0; // 'V' held: the pod's rear-view button (task #68) float gBTTwistAxis = 0.0f; // Q/E torso-twist deflection (assisted-mode stick X) float gBTElevAxis = 0.0f; // R/F torso-elevation (pitch aim, stick Y) @@ -2670,6 +2671,15 @@ void const int mNow = gBTInput.modeCycle; if (mNow && !sPrevM) gBTModeCycle = 1; // edge -> one cycle sPrevM = mNow; + // Gitea #6: 'N' cycles the secondary screen's schematic + // (Damage -> Critical -> Heat) -- the pod's status-info- + // center button (manual p13), CycleDisplayModeNow. + { + static int sPrevN = 0; + const int nNow = gBTInput.displayCycle; + if (nNow && !sPrevN) gBTDisplayCycle = 1; + sPrevN = nNow; + } // (task #68) LookBehind action HELD = the pod's rear-view // button (releases back to the forward view). Rear-mounted // weapons (blackhawk/owens back racks) fire only in it. @@ -5149,6 +5159,38 @@ void gBTValveKey = (s_vtFrame >= 600 && s_vtFrame < 610) ? 1 : 0; } + // Gitea #6 scripted verify (BT_VIEWCYCLE_TEST=): pulse one + // secondary-schematic cycle (Damage -> Critical -> Heat -> Damage) at + // the given frame and every 300 frames after -- with + // BT_DEV_GAUGES_DOCK + BT_SHOT the docked sec panel's schematic layer + // swap is pixel-verifiable. BT_MODECYCLE_TEST= does the same + // for the M-key CONTROL-mode cycle (the empirical proof that a + // control-mode change does NOT touch the secondary view). + if ((Entity *)this == application->GetViewpointEntity() + && getenv("BT_VIEWCYCLE_TEST")) + { + static int s_vcFrame = 0; + ++s_vcFrame; + int t0 = atoi(getenv("BT_VIEWCYCLE_TEST")); + if (t0 < 1) t0 = 600; + if (s_vcFrame >= t0 && (s_vcFrame - t0) % 300 == 0) + { + gBTDisplayCycle = 1; + } + } + if ((Entity *)this == application->GetViewpointEntity() + && getenv("BT_MODECYCLE_TEST")) + { + static int s_mcFrame = 0; + ++s_mcFrame; + int t0 = atoi(getenv("BT_MODECYCLE_TEST")); + if (t0 < 1) t0 = 600; + if (s_mcFrame >= t0 && (s_mcFrame - t0) % 300 == 0) + { + gBTModeCycle = 1; + } + } + // Gitea #7 scripted verify (BT_FLUSH_TEST=): hold the coolant // flush for ~60 sim frames starting at the given frame -- the // reservoir drains ([flush] log), the coolant vertBar drops, and the diff --git a/game/reconstructed/mechmppr.cpp b/game/reconstructed/mechmppr.cpp index 1668d53..715e182 100644 --- a/game/reconstructed/mechmppr.cpp +++ b/game/reconstructed/mechmppr.cpp @@ -521,7 +521,13 @@ void //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// @004afcac -- cycle the HUD display mode (0 -> 1 -> 2 -> 0). +// @004afcac -- cycle the DISPLAY mode (0 -> 1 -> 2 -> 0). Gitea #6: this is +// the secondary screen's Damage/Critical/Heat schematic cycle -- the L4 +// NotifyOfDisplayModeChange override (vtbl+0x4C, @004d1ae4) swaps the +// ModeSecondary* mask bits 18..20. The pod input was the "'Mech status Info +// center" button (manual p13, bottom left of the secondary screen); the DOS +// keyboard fallback was extended-F4 (Keypress 0x13e), dead under the WinTesla +// VK map. // void MechControlsMapper::CycleDisplayModeMessageHandler( @@ -533,16 +539,30 @@ void if (message->dataContents > 0) { - displayMode = displayMode + 1; - if (displayMode > 2) - { - displayMode = 0; - } - NotifyOfDisplayModeChange(displayMode); // vtable+0x4c + CycleDisplayModeNow(); } Check_Fpu(); } +// +// The display-cycle body, shared by the console-button message handler above +// and the desktop 'N' key (mech4 key poll -> gBTDisplayCycle) -- the same +// split as CycleControlModeNow. +// +void + MechControlsMapper::CycleDisplayModeNow() +{ + displayMode = displayMode + 1; + if (displayMode > 2) + { + displayMode = 0; + } + NotifyOfDisplayModeChange(displayMode); // vtable+0x4c + DEBUG_STREAM << "[mode] display mode -> " << displayMode + << " (0=Damage 1=Critical 2=Heat)" << std::endl; + Check_Fpu(); +} + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // @004afce8 -- toggle voice assist on the Mech's pilot subsystem. @@ -759,6 +779,7 @@ void // CONTROL MODE gauge tracks). { extern int gBTModeCycle; + extern int gBTDisplayCycle; extern float gBTTwistAxis; extern int gBTTorsoRecenter; if (gBTModeCycle) @@ -766,6 +787,14 @@ void gBTModeCycle = 0; CycleControlModeNow(); } + // Gitea #6: 'N' cycles the secondary screen's schematic + // (Damage -> Critical -> Heat) -- the same body the pod's + // status-info-center button message drives. + if (gBTDisplayCycle) + { + gBTDisplayCycle = 0; + CycleDisplayModeNow(); + } // TORSO ELEVATION (pitch): the pod stick's Y axis -- every // control mode routes stickPosition.y into // Torso::SetAnalogElevationAxis below, so the bridge feeds diff --git a/game/reconstructed/mechmppr.hpp b/game/reconstructed/mechmppr.hpp index f0bb261..d3f05f8 100644 --- a/game/reconstructed/mechmppr.hpp +++ b/game/reconstructed/mechmppr.hpp @@ -113,6 +113,13 @@ class Pilot; CycleDisplayModeMessageHandler( // @004afcac ReceiverDataMessageOf *message ); + + // The shared display-cycle body (Gitea #6: the pod console button + // message above + the desktop 'N' key both drive it) -- cycles the + // secondary screen's Damage -> Critical -> Heat schematic via the + // NotifyOfDisplayModeChange override (vtbl+0x4C, @004d1ae4). + void + CycleDisplayModeNow(); void ToggleVoiceAssistMessageHandler( // @004afce8 ReceiverDataMessageOf *message