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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user