Gitea #9: the upper-MFD PRESET pages (3 MFDs x 5) live -- SetPresetMode
table re-decoded to the ModeMFD bits + desktop J/K/L page cycle
The preset system was unwired by ONE defect in the message layer: the
SetPresetMode @004d1b24 table @0051dbf0 had been transcribed from the
section dump as BIG-endian dwords ({0x1e,0x01000000} instead of
{0x1e,0x01}), so a preset press set a garbage high bit -- and for group 1
items 3-4 / group 2 items 0-2 stomped the LIVE NonMapping / Intercom /
ModeSecondary* bits -- while the real page bits never moved. Ground
truth (section_dump.txt:72901-72908, little-endian + BTL4MODE.HPP [T0]):
set = ModeMFD{1,2,3}{Quad,Eng1-4} = 1<<(group*5+item), bits 0-14, fully
disjoint from the #6 secondary trio (bits 18-20); group 2 is MFD3, NOT a
duplicate of the secondary views.
What the 15 presets show (l4gauge.cfg): group = the MFD (Mfd1 lower left
/ Mfd2 upper center / Mfd3 lower right), item 0 = the btquad.pcx Quad
overview (up to 4 vehicleSubSystems cluster panels), items 1-4 = the
full-screen engineering-detail pages (bteng.pcx + prepEngr screens
group*4+1..4 + the cluster eng children: GENERATOR SELECT A-D, POWER
graph, COOLING loop, DAMAGE, ammo). Empty screens are authored per mech
(Blackhawk: 3/11/12 empty).
Authentic dispatch (streamed "L4" .CTL EventMappings, BT_CTRLMAP_LOG
dump): each MFD owns the 8-button RIO bank around it, MODE-MASK-gated --
Mfd1 = 0x08-0x0F, Mfd2 = 0x20-0x27, Mfd3 = 0x00-0x07. Quad page ->
direct-select buttons for the POPULATED eng pages (mapper msgs
Aux1Eng1-4 0x4-0x7 / Aux2* 0x9-0xC / Aux3* 0xE-0x11); eng page -> one
back-to-Quad button (0x3/0x8/0xD) + per-subsystem controls. These
records already install and fire on desktop (btinput passes the live
manager mask), so the NUMPAD profile's 0x20-0x27 keys page MFD2
authentically.
Port wiring (the #6 pattern): keys J/K/L -> actions Mfd1/2/3Cycle ->
gBTPresetCycle -> L4MechControlsMapper::CyclePresetModeNow(group) -- a
documented desktop shim (24 mode-dependent pod buttons don't fit a
keyboard) that cycles Quad -> populated Eng pages -> Quad, visiting
exactly the pod-reachable set; the body is the authentic SetPresetMode.
Dev-composite: BTDrawGaugeSurfaces now draws the Eng1-3 planes at their
sibling cells and skips any mono plane whose channel is BlankColor,
honoring the mode-driven reconfigure (RemapGraphicsPort) -- each dev
cell shows the ACTIVE page like the pod monitor. This supersedes and
removes the 2026-07-12 GAUGREND "frozen-dial" scaffold (forced all 15
page bits active under BT_DEV_GAUGES; it pinned the shared Eng plane on
the highest screen and ate the page flips).
Pixel-verified (BT_PRESET_TEST + BT_DEV_GAUGES_DOCK + BT_SHOT,
Blackhawk): all three MFDs page Quad -> SYSTEM NN eng details -> back to
Quad in lockstep with the [mode] preset mask log; group 0 skips the
authored-empty screen 3, group 2 skips 11/12. Un-regressed: N display
cycle (0x450421->0x490421->0x510421, page bits intact), M control mode,
CONTROLS.MAP 52 bindings parse clean.
Diags: BT_MODE_LOG ([mode] preset), BT_PRESET_TEST=<frame>.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
1d6339b226
commit
6783619069
@@ -32,7 +32,7 @@
|
||||
# <src> LeftStickX LeftStickY RightStickX RightStickY
|
||||
# LeftTrigger RightTrigger
|
||||
# action ViewToggle LookBehind AllStop ModeCycle DisplayCycle Valve Flush
|
||||
# ConfigHold Generator1-4 Reconnect
|
||||
# ConfigHold Generator1-4 Reconnect Mfd1Cycle Mfd2Cycle Mfd3Cycle
|
||||
# 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, ...
|
||||
@@ -76,6 +76,9 @@ 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 J action Mfd1Cycle # lower-left MFD page: Quad -> Eng detail pages
|
||||
key K action Mfd2Cycle # upper-center MFD page (engineering)
|
||||
key L action Mfd3Cycle # lower-right MFD page
|
||||
key V action ViewToggle # cockpit <-> chase camera (dev)
|
||||
key B action LookBehind # HOLD = the pod's rear-view button
|
||||
key F5 action Generator1
|
||||
|
||||
@@ -53,6 +53,9 @@ key F7 action Generator3
|
||||
key F8 action Generator4
|
||||
key F9 action Reconnect
|
||||
key F10 action Valve
|
||||
key J action Mfd1Cycle # lower-left MFD page: Quad -> Eng detail pages
|
||||
key K action Mfd2Cycle # upper-center MFD page (engineering)
|
||||
key L action Mfd3Cycle # lower-right MFD page
|
||||
|
||||
# ---- Upper MFD banks: number row = the top aux row, QWERTY row = the row
|
||||
# ---- under it, left to right across the Left / Center / Right panels.
|
||||
|
||||
@@ -283,8 +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_MODE_LOG` | `[mode] display notify` / `[mode] preset (g,i)` — the ModeManager mask after each secondary-view swap (Gitea #6) or upper-MFD preset-page swap (SetPresetMode, Gitea #9) (btl4mppr.cpp) |
|
||||
| `BT_VIEWCYCLE_TEST=<frame>` / `BT_MODECYCLE_TEST=<frame>` | 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_PRESET_TEST=<frame>` | scripted verify (mech4.cpp, Gitea #9): from the frame, pulse an upper-MFD preset-page cycle on all three MFDs every 120 frames (Quad → populated Eng pages → Quad) — 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) |
|
||||
|
||||
@@ -9,6 +9,7 @@ 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 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"
|
||||
- "Upper-MFD PRESET pages RESOLVED 2026-07-19 (Gitea #9): SetPresetMode table @0051dbf0 re-decoded (little-endian -> ModeMFD bits 0-14), per-MFD pod button banks identified from the .CTL dump, desktop J/K/L cycle wired; remaining: the always-active 0x28-0x2F / 0x1A-0x1D per-subsystem msg-4 records (subs 3-13 -- likely the condenser-valve/coolant buttons) unidentified"
|
||||
- "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)"
|
||||
---
|
||||
|
||||
@@ -105,6 +106,58 @@ BT_SHOT): ARMOR DAMAGE silhouette → CRITICAL DAMAGE subsystem list → HEAT DA
|
||||
mask 0x450421→0x490421→0x510421. Diags: `BT_MODE_LOG`, `BT_VIEWCYCLE_TEST=<frame>`,
|
||||
`BT_MODECYCLE_TEST=<frame>`.
|
||||
|
||||
## The upper-MFD PRESET pages — 3 MFDs × 5 pages (Gitea #9, RESOLVED 2026-07-19) [T0/T1/T2]
|
||||
The three preset-able MFDs are **Mfd1 (lower left) / Mfd2 (upper center) / Mfd3 (lower right)**,
|
||||
each a PAIR of bit-planes on one physical monitor: the base **Quad** plane (`Mfd1/2/3`, masks
|
||||
0x0100/0x0400/0x1000, `btquad.pcx`) and the **engineering-page** plane (`Eng1/2/3`, masks
|
||||
0x0200/0x0800/0x2000, `bteng.pcx`). Mode bits (BTL4MODE.HPP [T0], bits 0-14):
|
||||
`ModeMFD{1,2,3}{Quad,Eng1-4}` = `1<<(group*5+item)` — **fully disjoint** from Mapping/NonMapping
|
||||
(15/16), Intercom (17) and the #6 Secondary* trio (18-20); `ModeInitial` puts all three MFDs on
|
||||
Quad. **What the pages show:** Quad = up to four `vehicleSubSystems` cluster mini-panels (the
|
||||
quadrants, geometry table @0x51bf34); Eng item i = the FULL-SCREEN engineering detail of the
|
||||
subsystem streamed onto aux screen `group*4+i` (`sub+0x1dc`; `prepEngr` screens 1-12: "SYSTEM NN"
|
||||
+ per-class label cells + the cluster's eng child — GENERATOR SELECT A-D, POWER graph, COOLING
|
||||
loop, DAMAGE (MJ), ammo count …). Unpopulated screens are authored-empty per mech (Blackhawk:
|
||||
9 of 12 — scr 1/2/4=PPC/Streak6/ERMed, 5-8=Sensors/Myomers/ERMed/ERMed, 9/10=Streak6/PPC;
|
||||
screens 3, 11, 12 empty).
|
||||
|
||||
**`SetPresetMode(group,item)` @004d1b24** swaps the page: table @0051dbf0 = 15 `{clear,set}`
|
||||
pairs — set = the page's ModeMFD bit, clear = the group's other four (item 0) or all five
|
||||
(items 1-4). ⚠ The old reconstruction had transcribed the little-endian set column as
|
||||
BIG-endian dwords (0x01→0x01000000 …), so a preset press set a garbage high bit — and for
|
||||
group 1 items 3-4 / group 2 items 0-2 STOMPED the live NonMapping/Intercom/Secondary* bits —
|
||||
while the page bits never moved (the "presets unwired" defect). Fixed against
|
||||
section_dump.txt:72901-72908; the "does group 2 duplicate #6's secondary views?" concern is
|
||||
resolved: **no** — group 2 = MFD3, bits 10-14.
|
||||
|
||||
**Authentic dispatch (streamed type-19 "L4" .CTL, 121 records dumped via `BT_CTRLMAP_LOG`):**
|
||||
every MFD has its own 8-button RIO bank whose meanings are MODE-MASK-gated — **Mfd1 =
|
||||
buttons 0x08-0x0F (AuxLowerLeft), Mfd2 = 0x20-0x27 (AuxUpperCenter), Mfd3 = 0x00-0x07
|
||||
(AuxLowerRight)**. On a Quad page the bank's buttons DIRECT-SELECT the populated Eng pages
|
||||
(mapper EventMappings → `MechRIOMapper` messages `Aux1Eng1-4`=0x4-0x7, `Aux2Eng1-4`=0x9-0xC,
|
||||
`Aux3Eng1-4`=0xE-0x11 → SetPresetMode; no button is streamed for an empty screen); on an Eng
|
||||
page one button returns to Quad (`Aux1/2/3Quad` = 0x3/0x8/0xD) and the rest remap to the SHOWN
|
||||
subsystem (per-subsystem msgs: 0x4-0x7 = SelectGeneratorA-D, 0x8 = ToggleGeneratorMode, 0x9 =
|
||||
ConfigureMappables, 0x3/0xb = unjam/eject-class functions). The `MechRIOMapper` keyboard cases
|
||||
in @004d1bf0 mirror it as three key rows: `1-4`=MFD1 Eng1-4/`5`=Quad, `a s d f`/`g`,
|
||||
`z x c v`/`b` — mostly claimed by the port's WASD bindings, hence dead on desktop.
|
||||
**Port wiring:** the streamed records install and fire on desktop (btinput passes the LIVE
|
||||
manager mask on button press, so the NUMPAD profile's 0x20-0x27 keys page MFD2 authentically);
|
||||
the default WASD profile adds **J/K/L → actions Mfd1/2/3Cycle → gBTPresetCycle →
|
||||
`CyclePresetModeNow(group)`** (btl4mppr.cpp; a port cycle-key shim — 24 mode-dependent pod
|
||||
buttons don't fit a keyboard — that visits exactly the pod-reachable set: Quad + populated Eng
|
||||
pages; the body is the authentic SetPresetMode).
|
||||
|
||||
**Dev-composite change:** `BTDrawGaugeSurfaces` (L4VB16.cpp) now draws the Eng1-3 planes into
|
||||
their sibling's cell and SKIPS any mono plane whose port channel is currently `BlankColor` —
|
||||
honoring the mode-driven `reconfigure` (RemapGraphicsPort) so each dev cell shows the ACTIVE
|
||||
page, like the pod monitor. This SUPERSEDED the 2026-07-12 "frozen-dial" scaffold
|
||||
(GAUGREND.cpp force-activated all 15 page bits under BT_DEV_GAUGES — removed; it made every eng
|
||||
screen paint over the shared Eng plane, pinning it on the highest screen). Pixel-verified live
|
||||
(BT_PRESET_TEST): all three MFDs page Quad → eng details → back to Quad in lockstep with the
|
||||
[mode] mask log; N/M un-regressed. Diags: `BT_MODE_LOG` ([mode] preset lines),
|
||||
`BT_PRESET_TEST=<frame>`, `BT_CTRLMAP_LOG`.
|
||||
|
||||
## 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 =
|
||||
`killCount` (the victim's ScoreMessageHandler credits the shooter cross-player, works for both
|
||||
|
||||
+12
-2
@@ -27,8 +27,8 @@ the `MechControlsMapper` interprets them ([[locomotion]]). [T2]
|
||||
|
||||
### The button space + lamps [T0, L4CTRL.h enum]
|
||||
buttonGroup addresses 0x00-0x47: `AuxLowerRight/Left 1-8` (0x00-0x0F), `Secondary1-12` (0x10-0x1B),
|
||||
`AuxUpperCenter/Left/Right 1-8` (0x20-0x37 — the preset/eng "display eng data" banks; **0x30-0x37 =
|
||||
the target HOTBOX**, pilot select), icom/door 0x39-0x3C, `Panic` 0x3D (the config-mode lamp),
|
||||
`AuxUpperCenter/Left/Right 1-8` (0x20-0x37; **0x30-0x37 = the target HOTBOX**, pilot select),
|
||||
icom/door 0x39-0x3C, `Panic` 0x3D (the config-mode lamp),
|
||||
`Throttle1` 0x3F (throttle-head = REVERSE THRUST), joystick cluster 0x40-0x47 (Main trigger 0x40,
|
||||
hat 0x41-0x44, Pinky/ThumbLow/ThumbHigh 0x45-0x47 — the four MAPPABLE fire buttons). "Lamp buttons"
|
||||
are literal: physical illuminated pushbuttons in panels around the screens; the RIO `LampRequest`
|
||||
@@ -37,6 +37,16 @@ per-lamp names). A lit button = active. Keypads are NOT buttons: `keyboardGroup[
|
||||
KeyboardExternal]` carry key VALUES ('0'-'9','A'-'F', L4CTRL.cpp:2526). RIO event convention:
|
||||
press = `buttonGroup[a].Update(a+1, modeMask)` (mask saved), release = `Update(-a-1, savedMask)`
|
||||
(L4CTRL.cpp:2470-2520).
|
||||
**Bank→MFD assignment (Gitea #9, from the streamed "L4" .CTL EventMapping dump [T1]):** each
|
||||
preset-able MFD owns the 8-button bank AROUND it, mode-mask-gated per page — **Mfd1 (lower
|
||||
left) = 0x08-0x0F (AuxLowerLeft), Mfd2 (upper center) = 0x20-0x27 (AuxUpperCenter), Mfd3
|
||||
(lower right) = 0x00-0x07 (AuxLowerRight)**: Quad page → the outer buttons direct-select the
|
||||
populated Eng pages; Eng page → one button back to Quad, the rest drive the shown subsystem
|
||||
(generator select A-D, gen mode, weapon config/unjam). Always-active: **0x28-0x2F
|
||||
(AuxUpperLeft) → per-subsystem msg 0x4 to roster subs 3-9** and secondary-panel 0x1A-0x1D →
|
||||
subs 10-13 msg 0x4 (identities unverified — likely the condenser-valve / coolant buttons
|
||||
[T4]); 0x10/0x11 = map ZoomIn/Out, 0x15 = CycleDisplayMode, 0x18 = CycleControlMode (#6).
|
||||
Full decode: [[gauges-hud]] §preset pages.
|
||||
|
||||
### Desktop input remap — CONTROLS.MAP + XInput (2026-07-18) [T2 live]
|
||||
`game/reconstructed/btinput.cpp` + `content/CONTROLS.MAP` (WASD-classic default, compiled-in twin;
|
||||
|
||||
+10
-16
@@ -3677,22 +3677,16 @@ void
|
||||
Check(application->GetModeManager());
|
||||
ModeMask
|
||||
current_mode_mask = application->GetModeManager()->GetModeMask();
|
||||
// DEV-COMPOSITE (2026-07-12, the frozen-dial fix): page-gated gauges (mode
|
||||
// = their MFD page bit) freeze when their page isn't mode-active -- on the
|
||||
// pod you FLIP pages, but the dev window shows every page surface at once,
|
||||
// so six of seven weapon dials sat frozen on screen (user-hit; the arcs
|
||||
// executed exactly 8 times at startup then never again). Under
|
||||
// BT_DEV_GAUGES force the 15 MFD page-plane bits (BTL4 mode bits 0..14 =
|
||||
// MFD1/2/3 Quad+Eng1-4) active so every visible page runs. The secondary
|
||||
// trio (bits 18-20) stays authentic -- those views SHARE pixels and are
|
||||
// exclusive by design.
|
||||
{
|
||||
static int s_devAllPages = -1;
|
||||
if (s_devAllPages < 0)
|
||||
s_devAllPages = (getenv("BT_DEV_GAUGES") != NULL) ? 1 : 0;
|
||||
if (s_devAllPages)
|
||||
current_mode_mask |= (ModeMask)0x7FFF;
|
||||
}
|
||||
// DEV-COMPOSITE history: a 2026-07-12 scaffold ("the frozen-dial fix")
|
||||
// forced the 15 MFD page-plane bits (BTL4 mode bits 0..14 = MFD1/2/3
|
||||
// Quad+Eng1-4) active under BT_DEV_GAUGES, because the dev panel then drew
|
||||
// every page plane at once and page-gated dials froze. REMOVED for Gitea
|
||||
// #9 (2026-07-19): the dev composite now honors the mode-driven
|
||||
// `reconfigure` (a BlankColor plane is skipped -- L4VB16.cpp
|
||||
// BTDrawGaugeSurfaces), so exactly the ACTIVE page of each MFD is shown,
|
||||
// like the pod -- and the working SetPresetMode page switching NEEDS the
|
||||
// authentic gating (the force made every eng screen paint over the shared
|
||||
// Eng plane, pinning it on the highest screen and ignoring page flips).
|
||||
ModeMask
|
||||
previous_mode_mask = application->GetModeManager()->GetPreviousModeMask();
|
||||
ModeMask
|
||||
|
||||
@@ -167,7 +167,7 @@ void SVGA16::DrawDevSurface(LPDIRECT3DDEVICE9 device, int slot, int mask, int pa
|
||||
{
|
||||
int w = pixelBuffer.Data.Size.x; // 640
|
||||
int h = pixelBuffer.Data.Size.y; // 480
|
||||
if (device == NULL || slot < 0 || slot >= 8 || w <= 0 || h <= 0) return;
|
||||
if (device == NULL || slot < 0 || slot >= 10 || w <= 0 || h <= 0) return;
|
||||
|
||||
// Lazily create this surface's texture on the MAIN device (MANAGED -> lockable +
|
||||
// survives device reset).
|
||||
@@ -289,7 +289,13 @@ struct BTGaugeSurfaceDesc
|
||||
// buffer, so past ~1.5x you magnify, not sharpen.
|
||||
static const float kBTPanelW = 1320.0f;
|
||||
static const float kBTPanelH = 480.0f;
|
||||
static const BTGaugeSurfaceDesc kBTGaugeSurfaces[6] =
|
||||
// Gitea #9: each preset-able MFD is TWO bit-planes sharing one physical
|
||||
// monitor -- the base Quad plane (Mfd1/2/3) and the engineering-page plane
|
||||
// (Eng1/2/3). The mode-driven `reconfigure` swaps which of the pair is
|
||||
// channel-enabled and which is BlankColor; the draw loop below skips the
|
||||
// blanked plane, so the dev cell shows exactly what the pod monitor shows.
|
||||
// The Eng entries share their sibling's cell rect and FOLLOW it in the list.
|
||||
static const BTGaugeSurfaceDesc kBTGaugeSurfaces[9] =
|
||||
{
|
||||
{ "Heat", 0xFFFF, 0.0f / kBTPanelW, 0.0f, 320.0f / kBTPanelW, 0.5f, 0 },
|
||||
{ "Mfd2", 0xFFFF, 320.0f / kBTPanelW, 0.0f, 320.0f / kBTPanelW, 0.5f, 0 },
|
||||
@@ -297,6 +303,9 @@ static const BTGaugeSurfaceDesc kBTGaugeSurfaces[6] =
|
||||
{ "Mfd3", 0xFFFF, 320.0f / kBTPanelW, 0.5f, 320.0f / kBTPanelW, 0.5f, 0 },
|
||||
{ "sec", -1, 640.0f / kBTPanelW, 0.0f, 360.0f / kBTPanelW, 1.0f, 1 },
|
||||
{ "Comm", 0xFFFF, 1000.0f / kBTPanelW, 0.0f, 320.0f / kBTPanelW, 0.5f, 0 },
|
||||
{ "Eng2", 0xFFFF, 320.0f / kBTPanelW, 0.0f, 320.0f / kBTPanelW, 0.5f, 0 },
|
||||
{ "Eng1", 0xFFFF, 0.0f / kBTPanelW, 0.5f, 320.0f / kBTPanelW, 0.5f, 0 },
|
||||
{ "Eng3", 0xFFFF, 320.0f / kBTPanelW, 0.5f, 320.0f / kBTPanelW, 0.5f, 0 },
|
||||
};
|
||||
|
||||
//
|
||||
@@ -345,11 +354,17 @@ void BTDrawGaugeSurfaces(LPDIRECT3DDEVICE9 device, float px, float py, float pw,
|
||||
}
|
||||
if (svga == NULL) return;
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
const BTGaugeSurfaceDesc &d = kBTGaugeSurfaces[i];
|
||||
L4GraphicsPort *port = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(d.portName));
|
||||
if (port == NULL) continue; // this surface's port isn't configured -> skip
|
||||
// Gitea #9: honor the live mode-driven `reconfigure` -- a mono plane
|
||||
// whose channel is currently BlankColor contributes nothing on the pod
|
||||
// monitor (its palette bits translate to black), so skip it; its
|
||||
// non-blank sibling (Mfd<n> or Eng<n>, same cell rect) draws instead.
|
||||
if (d.monoTint >= 0 && port->GetEnableID() == L4GraphicsPort::BlankColor)
|
||||
continue;
|
||||
// Secondary-CRT unrotation direction is env-flippable (BT_GAUGE_SEC_ROT=3
|
||||
// for the other way) in case the assumed pod mounting is mirrored.
|
||||
int rot = d.rotateCCW;
|
||||
@@ -4309,7 +4324,7 @@ SVGA16::SVGA16(
|
||||
):Video16BitBuffered(init_width, init_height)
|
||||
{
|
||||
BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index);
|
||||
for (int _i = 0; _i < 8; _i++) // DEV-COMPOSITE: lazily created on first surface draw
|
||||
for (int _i = 0; _i < 10; _i++) // DEV-COMPOSITE: lazily created on first surface draw
|
||||
mDevSurfaceTex[_i] = NULL;
|
||||
//STUBBED: VIDEO RB 1/15/07
|
||||
# if defined(DEBUG)
|
||||
|
||||
@@ -292,8 +292,9 @@ private:
|
||||
|
||||
int mDisplayToUpdate;
|
||||
|
||||
LPDIRECT3DTEXTURE9 mDevSurfaceTex[8]; // DEV-COMPOSITE: one texture per gauge surface
|
||||
// (sec/radar + the 5 MFD bit-planes), on the MAIN device.
|
||||
LPDIRECT3DTEXTURE9 mDevSurfaceTex[10]; // DEV-COMPOSITE: one texture per gauge surface
|
||||
// (sec/radar + the 5 MFD bit-planes + the Eng1-3
|
||||
// preset-page planes, Gitea #9), on the MAIN device.
|
||||
public:
|
||||
// DEV-COMPOSITE: extract ONE gauge surface from the shared pixelBuffer into
|
||||
// mDevSurfaceTex[slot] on the given (main) device + draw it as a quad at (dstX,dstY,
|
||||
@@ -462,6 +463,15 @@ public:
|
||||
Check(this);
|
||||
return bitMask;
|
||||
}
|
||||
// DEV-COMPOSITE (Gitea #9): the dock honors the live reconfigure state --
|
||||
// a plane whose channel is currently BlankColor contributes nothing on the
|
||||
// pod's shared-word palette LUT, so the dev panel skips drawing it.
|
||||
L4GraphicsPort::ChannelEnableID
|
||||
GetEnableID()
|
||||
{
|
||||
Check(this);
|
||||
return channelEnable;
|
||||
}
|
||||
SVGA16::PaletteID
|
||||
paletteID;
|
||||
|
||||
|
||||
@@ -151,7 +151,9 @@ Subsystem (MUNGA base — have source: RP/MUNGA/SUBSYSTM.HPP)
|
||||
│ 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]).
|
||||
│ SetPresetMode@4d1b24 (group*5+item -> {clear,set} mask table @0051dbf0[15]; Gitea #9 decode: set = ModeMFD{1,2,3}{Quad,Eng1-4}
|
||||
│ = 1<<(group*5+item), bits 0-14 [section_dump:72901-72908 little-endian]; clear = the group's other/all page bits.
|
||||
│ Group = the MFD (0/1/2 = lower-left/upper-center/lower-right), item 0 = Quad overview, 1-4 = eng-detail pages).
|
||||
│ 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).
|
||||
|
||||
@@ -79,6 +79,9 @@ enum BTActionID
|
||||
BTActGenerator3,
|
||||
BTActGenerator4,
|
||||
BTActReconnect,
|
||||
BTActMfd1Cycle, // Gitea #9: cycle the lower-left MFD preset page
|
||||
BTActMfd2Cycle, // Gitea #9: cycle the upper-center MFD preset page
|
||||
BTActMfd3Cycle, // Gitea #9: cycle the lower-right MFD preset page
|
||||
BTActCount
|
||||
};
|
||||
|
||||
@@ -199,6 +202,8 @@ static const BTName sActionNames[] =
|
||||
{"Generator1", BTActGenerator1}, {"Generator2", BTActGenerator2},
|
||||
{"Generator3", BTActGenerator3}, {"Generator4", BTActGenerator4},
|
||||
{"Reconnect", BTActReconnect},
|
||||
{"Mfd1Cycle", BTActMfd1Cycle}, {"Mfd2Cycle", BTActMfd2Cycle},
|
||||
{"Mfd3Cycle", BTActMfd3Cycle},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
@@ -269,6 +274,9 @@ static const char *sDefaultProfile =
|
||||
"key H action Flush\n"
|
||||
"key M action ModeCycle\n"
|
||||
"key N action DisplayCycle\n"
|
||||
"key J action Mfd1Cycle\n"
|
||||
"key K action Mfd2Cycle\n"
|
||||
"key L action Mfd3Cycle\n"
|
||||
"key V action ViewToggle\n"
|
||||
"key B action LookBehind\n"
|
||||
"key F5 action Generator1\n"
|
||||
@@ -971,6 +979,9 @@ void
|
||||
case BTActValve: next.valve = 1; break;
|
||||
case BTActFlush: next.flush = 1; break;
|
||||
case BTActConfigHold: next.configHold = 1; break;
|
||||
case BTActMfd1Cycle: next.mfdCycle[0] = 1; break;
|
||||
case BTActMfd2Cycle: next.mfdCycle[1] = 1; break;
|
||||
case BTActMfd3Cycle: next.mfdCycle[2] = 1; break;
|
||||
case BTActGenerator1: next.genSel = 4; break;
|
||||
case BTActGenerator2: next.genSel = 5; break;
|
||||
case BTActGenerator3: next.genSel = 6; break;
|
||||
|
||||
@@ -56,6 +56,9 @@ struct BTInputState
|
||||
int flush; // Gitea #7: coolant flush HELD (InjectCoolant)
|
||||
int configHold;
|
||||
int genSel; // 0 = none, 4..7 = Generator A..D, 8 = reconnect
|
||||
// Gitea #9: per-MFD preset-page cycle (desktop senders for SetPresetMode;
|
||||
// index 0/1/2 = Mfd1 lower-left / Mfd2 upper-center / Mfd3 lower-right)
|
||||
int mfdCycle[3];
|
||||
};
|
||||
|
||||
extern BTInputState gBTInput;
|
||||
|
||||
@@ -86,6 +86,8 @@
|
||||
// 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
|
||||
// (little-endian: set = ModeMFD{1,2,3}{Quad,Eng1-4} bits 0-14
|
||||
// -- Gitea #9; see SetPresetMode below)
|
||||
// DAT_0051dcd0[8] = {0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30} hotbox buttons
|
||||
//
|
||||
// ModeManager (app+0x50): +0x4 currentMode mask, +0x8 savedMode mask.
|
||||
@@ -425,6 +427,23 @@ L4MechControlsMapper::MessageHandlerSet&
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// (3b) PORT (Gitea #9): the desktop J/K/L per-MFD preset-page cycle
|
||||
// edges (mech4 poll) -- the sender for the pod's per-MFD page-button
|
||||
// banks; the body is the authentic SetPresetMode.
|
||||
//
|
||||
{
|
||||
extern int gBTPresetCycle[3];
|
||||
for (int mfd_group = 0; mfd_group < 3; ++mfd_group)
|
||||
{
|
||||
if (gBTPresetCycle[mfd_group])
|
||||
{
|
||||
gBTPresetCycle[mfd_group] = 0;
|
||||
CyclePresetModeNow(mfd_group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// (4) Target-range zoom: slew current exponent toward demand, then set
|
||||
// the Mech's target range = 250 * 2^exponent.
|
||||
@@ -547,9 +566,24 @@ L4MechControlsMapper::MessageHandlerSet&
|
||||
// SetPresetMode @004d1b24
|
||||
//#############################################################################
|
||||
//
|
||||
// Each (group,item) selects a preset; the table @0051dbf0 holds {clear,set}
|
||||
// mode-mask pairs that remove the previous preset of that group and enable the
|
||||
// chosen one.
|
||||
// Each (group,item) selects an upper-MFD PRESET page; the table @0051dbf0
|
||||
// holds {clear,set} mode-mask pairs that remove the previous preset of that
|
||||
// group and enable the chosen one. Group = the MFD (0/1/2 = Mfd1 lower-left /
|
||||
// Mfd2 upper-center / Mfd3 lower-right); item 0 = the Quad overview page
|
||||
// (four subsystem cluster panels), items 1-4 = the four full-screen
|
||||
// engineering-detail pages (aux screens group*4+1..group*4+4, prepEngr).
|
||||
//
|
||||
// Gitea #9 TABLE FIX (2026-07-19): the set column had been transcribed from
|
||||
// the section dump as BIG-endian dwords ({0x1e,0x01000000} instead of
|
||||
// {0x1e,0x01}, etc.) -- so a preset press set a garbage high bit (and, for
|
||||
// group 1 items 3-4 / group 2 items 0-2, stomped the LIVE ModeNonMapping /
|
||||
// ModeIntercom / ModeSecondary* bits) while the ModeMFD* page bits never
|
||||
// moved: the panels stayed pinned on their Quad pages. Ground truth
|
||||
// (section_dump.txt:72901-72908, little-endian) decodes the table to exactly
|
||||
// the BTL4MODE.HPP [T0] ModeMFD{1,2,3}{Quad,Eng1-4} allocation, bits 0-14:
|
||||
// set = 1 << (group*5 + item); clear = the group's other four page bits
|
||||
// (item 0) or all five (items 1-4). The presets are fully disjoint from the
|
||||
// Mapping/NonMapping (15/16), Intercom (17) and Secondary* (18-20) bits.
|
||||
//
|
||||
void
|
||||
L4MechControlsMapper::SetPresetMode(int group, int item)
|
||||
@@ -557,14 +591,31 @@ L4MechControlsMapper::MessageHandlerSet&
|
||||
struct PresetMaskPair { ModeMask clear, set; };
|
||||
static const PresetMaskPair preset_mask[15] = // @0051dbf0 (group*5+item)
|
||||
{
|
||||
{0x1e, 0x01000000}, {0x1f, 0x02000000}, {0x1f, 0x04000000},
|
||||
{0x1f, 0x08000000}, {0x1f, 0x10000000},
|
||||
{0x3c0, 0x20000000}, {0x3e0, 0x40000000}, {0x3e0, 0x80000000},
|
||||
{0x3e0, 0x00010000}, {0x3e0, 0x00020000},
|
||||
{0x7800,0x00040000}, {0x7c00,0x00080000}, {0x7c00,0x00100000},
|
||||
{0x7c00,0x00200000}, {0x7c00,0x00400000}
|
||||
// group 0 = Mfd1 (lower left)
|
||||
{0x001e, BTL4ModeManager::ModeMFD1Quad}, // 0x0001
|
||||
{0x001f, BTL4ModeManager::ModeMFD1Eng1}, // 0x0002
|
||||
{0x001f, BTL4ModeManager::ModeMFD1Eng2}, // 0x0004
|
||||
{0x001f, BTL4ModeManager::ModeMFD1Eng3}, // 0x0008
|
||||
{0x001f, BTL4ModeManager::ModeMFD1Eng4}, // 0x0010
|
||||
// group 1 = Mfd2 (upper center)
|
||||
{0x03c0, BTL4ModeManager::ModeMFD2Quad}, // 0x0020
|
||||
{0x03e0, BTL4ModeManager::ModeMFD2Eng1}, // 0x0040
|
||||
{0x03e0, BTL4ModeManager::ModeMFD2Eng2}, // 0x0080
|
||||
{0x03e0, BTL4ModeManager::ModeMFD2Eng3}, // 0x0100
|
||||
{0x03e0, BTL4ModeManager::ModeMFD2Eng4}, // 0x0200
|
||||
// group 2 = Mfd3 (lower right)
|
||||
{0x7800, BTL4ModeManager::ModeMFD3Quad}, // 0x0400
|
||||
{0x7c00, BTL4ModeManager::ModeMFD3Eng1}, // 0x0800
|
||||
{0x7c00, BTL4ModeManager::ModeMFD3Eng2}, // 0x1000
|
||||
{0x7c00, BTL4ModeManager::ModeMFD3Eng3}, // 0x2000
|
||||
{0x7c00, BTL4ModeManager::ModeMFD3Eng4} // 0x4000
|
||||
};
|
||||
|
||||
if (group < 0 || group > 2 || item < 0 || item > 4)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int index = group * 5 + item;
|
||||
|
||||
BTL4ModeManager *mode_manager =
|
||||
@@ -572,6 +623,110 @@ L4MechControlsMapper::MessageHandlerSet&
|
||||
|
||||
mode_manager->RemoveModeMask(preset_mask[index].clear);
|
||||
mode_manager->AddModeMask(preset_mask[index].set);
|
||||
|
||||
// DIAG (BT_MODE_LOG): prove the ModeMFD* page bit actually flipped for
|
||||
// the gauge reconfigure/prepEngr layer gating.
|
||||
if (getenv("BT_MODE_LOG"))
|
||||
DEBUG_STREAM << "[mode] preset (" << group << "," << item
|
||||
<< ") -> manager mask 0x" << std::hex
|
||||
<< mode_manager->GetModeMask() << std::dec << "\n" << std::flush;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// CyclePresetModeNow (PORT desktop sender -- Gitea #9, no binary analog)
|
||||
//#############################################################################
|
||||
//
|
||||
// On the pod every MFD has its own 8-button bank (Mfd1 lower-left = RIO
|
||||
// buttons 0x08-0x0F, Mfd2 upper-center = 0x20-0x27, Mfd3 lower-right =
|
||||
// 0x00-0x07) whose meanings are MODE-MASK-gated by the streamed "L4" .CTL
|
||||
// EventMappings: on a Quad page the bank's outer buttons DIRECT-SELECT the
|
||||
// populated Eng pages (mapper messages Aux1Eng1..Aux3Eng4 = 0x4-0x7 / 0x9-0xC
|
||||
// / 0xE-0x11 -> SetPresetMode), on an Eng page one button returns to Quad
|
||||
// (Aux1Quad/Aux2Quad/Aux3Quad = 0x3/0x8/0xD) and the rest drive the shown
|
||||
// subsystem. A desktop keyboard cannot host 24 mode-dependent buttons, so
|
||||
// the port cycles each MFD with one key (J/K/L -> mech4 edge poll ->
|
||||
// gBTPresetCycle -> here); the message BODY is the authentic SetPresetMode.
|
||||
// The cycle visits exactly the pages the pod's streamed buttons could reach:
|
||||
// Quad always, an Eng page only if a roster subsystem is assigned to that
|
||||
// aux screen (screens group*4+1 .. group*4+4; the shipped .CTL carries NO
|
||||
// select button for an unpopulated screen -- e.g. the Blackhawk's screens
|
||||
// 3, 11 and 12 are authored empty).
|
||||
//
|
||||
// The vehicleSubSystems/prepEngr aux-screen bridge (powersub.cpp).
|
||||
extern bool BTGetSubsystemAuxScreen(
|
||||
Subsystem *sub, int *screen, int *placement, char *label64);
|
||||
|
||||
void
|
||||
L4MechControlsMapper::CyclePresetModeNow(int group)
|
||||
{
|
||||
if (group < 0 || group > 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BTL4ModeManager *mode_manager =
|
||||
(BTL4ModeManager*)application->GetModeManager();
|
||||
|
||||
//
|
||||
// Current page = the set bit among the group's five ModeMFD* bits.
|
||||
//
|
||||
ModeMask mask = mode_manager->GetModeMask();
|
||||
int current = 0; // default: Quad
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
if (mask & (1 << (group * 5 + i)))
|
||||
{
|
||||
current = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Which aux screens are populated on this mech (streamed sub+0x1dc)?
|
||||
//
|
||||
Mech *mech = GetMech();
|
||||
bool populated[5] = { true, false, false, false, false }; // Quad always
|
||||
if (mech != NULL)
|
||||
{
|
||||
int count = mech->GetSubsystemCount();
|
||||
for (int i = 1; i < count; ++i)
|
||||
{
|
||||
Subsystem *sub = mech->GetSubsystem(i);
|
||||
if (sub == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int screen = 0, placement = -1;
|
||||
if (!BTGetSubsystemAuxScreen(sub, &screen, &placement, NULL))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int item = screen - group * 4; // Eng item 1..4 of THIS group?
|
||||
if (item >= 1 && item <= 4)
|
||||
{
|
||||
populated[item] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Advance to the next reachable page (wraps through Quad).
|
||||
//
|
||||
int next = current;
|
||||
for (int step = 1; step <= 5; ++step)
|
||||
{
|
||||
int candidate = (current + step) % 5;
|
||||
if (populated[candidate])
|
||||
{
|
||||
next = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (next != current)
|
||||
{
|
||||
SetPresetMode(group, next);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -127,6 +127,12 @@
|
||||
NotifyOfDisplayModeChange(int new_mode); // @004d1ae4 (+0x4C, secondary-view mask swap)
|
||||
void
|
||||
SetPresetMode(int group, int item); // @004d1b24 (preset mode-mask)
|
||||
void
|
||||
CyclePresetModeNow(int group); // PORT (Gitea #9): desktop J/K/L
|
||||
// per-MFD page cycle; visits Quad +
|
||||
// the POPULATED Eng pages only (the
|
||||
// reachable set of the pod's streamed
|
||||
// page buttons); body = SetPresetMode
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Configuration support (overrides the MechControlsMapper traps; task #6
|
||||
|
||||
@@ -653,6 +653,7 @@ 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 gBTPresetCycle[3] = {0,0,0}; // J/K/L edges: cycle an upper-MFD preset page (Gitea #9, L4 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)
|
||||
@@ -2680,6 +2681,19 @@ void
|
||||
if (nNow && !sPrevN) gBTDisplayCycle = 1;
|
||||
sPrevN = nNow;
|
||||
}
|
||||
// Gitea #9: J/K/L cycle an upper-MFD preset page
|
||||
// (Quad -> populated Eng pages -> Quad) -- the desktop
|
||||
// sender for the pod's per-MFD page-button banks
|
||||
// (SetPresetMode, consumed by the L4 mapper).
|
||||
{
|
||||
static int sPrevMfd[3] = {0,0,0};
|
||||
for (int mg = 0; mg < 3; ++mg)
|
||||
{
|
||||
const int gNow = gBTInput.mfdCycle[mg];
|
||||
if (gNow && !sPrevMfd[mg]) gBTPresetCycle[mg] = 1;
|
||||
sPrevMfd[mg] = gNow;
|
||||
}
|
||||
}
|
||||
// (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.
|
||||
@@ -5191,6 +5205,26 @@ void
|
||||
}
|
||||
}
|
||||
|
||||
// Gitea #9 scripted verify (BT_PRESET_TEST=<frame>): from the given
|
||||
// frame, pulse a preset-page cycle on ALL THREE upper MFDs every 120
|
||||
// frames (Quad -> populated Eng pages -> Quad, per MFD) -- with
|
||||
// BT_DEV_GAUGES_DOCK + BT_SHOT the Mfd1/Mfd2/Mfd3 page swaps are
|
||||
// pixel-verifiable ([mode] preset log lines under BT_MODE_LOG).
|
||||
if ((Entity *)this == application->GetViewpointEntity()
|
||||
&& getenv("BT_PRESET_TEST"))
|
||||
{
|
||||
static int s_ptFrame = 0;
|
||||
++s_ptFrame;
|
||||
int t0 = atoi(getenv("BT_PRESET_TEST"));
|
||||
if (t0 < 1) t0 = 600;
|
||||
if (s_ptFrame >= t0 && (s_ptFrame - t0) % 120 == 0)
|
||||
{
|
||||
gBTPresetCycle[0] = 1;
|
||||
gBTPresetCycle[1] = 1;
|
||||
gBTPresetCycle[2] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Gitea #7 scripted verify (BT_FLUSH_TEST=<frame>): hold the coolant
|
||||
// flush for ~60 sim frames starting at the given frame -- the
|
||||
// reservoir drains ([flush] log), the coolant vertBar drops, and the
|
||||
|
||||
Reference in New Issue
Block a user