Glass input audit: RIO mapper id fix (panel banks live), keyboard reconciliation
Root cause of the dead on-screen panel: MechRIOMapper's message-id enum
chained off L4MechControlsMapper::NextMessageID (=0x19), registering all 18
RIO override handlers at 0x19-0x2a while the streamed .CTL EventMapping
records carry the binary's ids -- the binary RIO table @0051dd30 re-registers
the BASE aux/zoom ids 3..0x13 (Hotbox 0x1a @0051de98). Every MFD-bank/zoom
press hit the base ConfigureMappableMessageHandler FAIL trap (26/72 buttons
dead; an abort() on a trap-armed pod). Ids now pinned to the binary
numbering + static_assert-locked (btl4mppr.hpp) -- panel goes 26 -> 52
working buttons (8 await handler reconstruction, filed on Gitea; 12 have no
streamed mapping authored = authentically inert).
Keyboard reconciliation (glass-only): the merged map -- keyboard hosts the
~20 core gameplay actions on the CONTROLS.MAP keys, the panel covers every
pod address by click (right-click = hold latch):
- W/S throttle lever, A/D pedals, Q/E twist, R/F elevation, X all-stop,
arrows drive; numpad flight cluster kept (Cyd)
- 1/2/3/4/Space/LCTRL/RCTRL fire, LALT reverse, B look-behind (0x41)
- M=0x18 mode cycle, N=0x15 display cycle, H=0x2C coolant flush (hold),
C=0x2F Condenser1 valve, G=0x0E weapon-1 configure (Mfd1-Quad-gated)
- F5-F9 = Mfd2 bank 0x27-0x24 + 0x22 (page-gated gen A-D / gen mode)
- hardcoded: ` or V = view toggle, J/K/L = Mfd1/2/3 preset-page cycle
(PadRIO edges -> gBTPresetCycle; no pod "cycle" button exists)
- PadRIO::SuppressKey: bound keys are swallowed from the typed 1995 channel
(the btinput suppression pattern, glass side) -- ends the glass double
dispatch ('w'=pilot select 0, 'a'..'g'=MFD2 presets, NUMPAD/F-key key-up
VK aliases like VK_F5=0x74='t'). Unbound keys keep their authentic
meanings (5/z presets, t-o pilot select, +/- zoom, F1/F2 align).
DISPLACED from Cyd's default bindings (each reachable on the panel or by a
bindings.txt edit -- flagging for Cyd's veto): number row -> secondary panel
(1-4 now fire), QWERTY row -> pilot keypad (dropped), arrows -> hat looks
(now drive), V/C/B -> fire 0x47/0x46/0x45 (now view/valve/look-behind),
LCTRL -> throttle-down (now fire). XInput pad section untouched.
Audit by-products (KB updated): the always-active msg-4 records identified
(0x2C = Reservoir flush, 0x29-0x2F = condenser valves, 0x1A-0x1D =
GeneratorA-D ToggleGeneratorOnOff @004b1ed0 unreconstructed); 0x13 = Mech
DuckRequest (crouch), 0x28 = BalanceCoolant; Searchlight/ThermalSight
ToggleLamp handler-sets are default-constructed EMPTY; weapon Eng-page msgs
0x3/0xb = ToggleCooling / ToggleSeekVoltage / EjectAmmo, all unwired;
unhandled messages are SILENT (no [FAIL] -- census = BT_CTRLMAP_LOG dump x
handler tables).
Verified live (build-glass2, ARENA1): panel presets/zoom/display/flush;
W drive (speedDemand 61.5, gait advances), Q turn (BAS), M -> MID, A turn
(MID), Space fires, N display, J/K/L presets, H flush drain, C valve 1->5;
suppression ('w' swallowed, 't' flows, F5-keyup swallowed); BT_SHOT frame.
Un-regression: pod build (gates OFF) compiles 0 errors, forced-walk drives,
streamed ids unchanged; CONTROLS.MAP/btinput untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
cb190aa00a
commit
5dd35365c7
@@ -1605,6 +1605,15 @@ void
|
||||
{
|
||||
// swallowed: remapped by CONTROLS.MAP
|
||||
}
|
||||
#ifdef BT_GLASS
|
||||
else if (PadRIO::SuppressKey((unsigned int)keyValue,
|
||||
msg.message == WM_CHAR))
|
||||
{
|
||||
// swallowed: claimed by a bindings.txt row (PadRIO) --
|
||||
// the glass-side analog of the CONTROLS.MAP suppression
|
||||
// above (btinput stands down when PadRIO owns the path)
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
keyboardGroup[KeyboardPC].ForceUpdate(&keyValue, mode_mask);
|
||||
|
||||
@@ -139,6 +139,21 @@ static int
|
||||
// right/left), 0x45-0x47 pinky/thumb-low/thumb-high, 0x10-0x15 + 0x18-0x1D
|
||||
// the 12 secondary-panel buttons, 0x00-0x0F the lower aux banks, 0x20-0x37
|
||||
// the upper aux banks. Keypad unit 0 = the pilot's MFD keypad.
|
||||
//
|
||||
// KEYBOARD RECONCILIATION (2026-07-20, input-coverage audit): the keyboard
|
||||
// carries the ~20 core gameplay actions on the SAME keys the desktop
|
||||
// CONTROLS.MAP uses (W/S/A/D drive, Q/E twist, R/F elevation, X all-stop,
|
||||
// 1-4/Space/Ctrl fire, M/N mode/display cycle, H flush, C valve, B look
|
||||
// behind, G configure, V view); FULL pod-address coverage lives on the
|
||||
// on-screen panel (every one of the 72 buttons is one click away,
|
||||
// right-click = hold latch). Keys DISPLACED from the previous default
|
||||
// (each still reachable by editing bindings.txt or on the panel):
|
||||
// - number row 1-0/-/= -> secondary-panel 0x10-0x1D (1-4 now fire)
|
||||
// - QWERTY row Q..P -> pilot MFD keypad unit 0 (dropped)
|
||||
// - arrow keys -> hat looks 0x41-0x44 (arrows now drive)
|
||||
// - V/C/B -> fire 0x47/0x46/0x45 (now view/valve/behind)
|
||||
// - LCTRL -> throttle slew down (now fire 0x47)
|
||||
// The XInput pad section is unchanged.
|
||||
//###########################################################################
|
||||
|
||||
static const char *defaultProfileText =
|
||||
@@ -153,8 +168,33 @@ static const char *defaultProfileText =
|
||||
"# padaxis <PADAXIS> axis <channel> [invert] [slew <rate>]\n"
|
||||
"# Channels: Throttle JoystickX JoystickY LeftPedal RightPedal\n"
|
||||
"# Pad buttons: A B X Y LB RB BACK START LS RS DPAD_* Axes: LX LY RX RY LT RT\n"
|
||||
"#\n"
|
||||
"# LAYOUT: the keyboard hosts the core gameplay actions on the desktop\n"
|
||||
"# CONTROLS.MAP keys; the on-screen cockpit panel covers EVERY pod button\n"
|
||||
"# by click (right-click = press-and-hold latch). A key bound here is\n"
|
||||
"# REMOVED from the authentic 1995 typed-hotkey channel (no double\n"
|
||||
"# dispatch); unbound keys keep their 1995 meaning -- 5 = MFD1 Quad page,\n"
|
||||
"# z = MFD3 Eng1 page, t/y/u/i/o = pilot select 3-7, + / - = target zoom.\n"
|
||||
"# Hardcoded (not rebindable here): ` or V = view toggle, J/K/L = cycle\n"
|
||||
"# the Mfd1/Mfd2/Mfd3 preset page.\n"
|
||||
"\n"
|
||||
"# --- flight: numpad spring stick, pedals, throttle lever ---\n"
|
||||
"# --- drive: W/S sweep the throttle lever (sticks; X = all-stop detent),\n"
|
||||
"# --- A/D spring the turn pedals, Q/E twist the torso, R/F aim it up/down\n"
|
||||
"key W axis Throttle slew + 0.7\n"
|
||||
"key S axis Throttle slew - 0.7\n"
|
||||
"key UP axis Throttle slew + 0.7\n"
|
||||
"key DOWN axis Throttle slew - 0.7\n"
|
||||
"key A axis LeftPedal deflect + 2.5\n"
|
||||
"key D axis RightPedal deflect + 2.5\n"
|
||||
"key LEFT axis LeftPedal deflect + 2.5\n"
|
||||
"key RIGHT axis RightPedal deflect + 2.5\n"
|
||||
"key Q axis JoystickX deflect - 2.5\n"
|
||||
"key E axis JoystickX deflect + 2.5\n"
|
||||
"key R axis JoystickY deflect + 2.5\n"
|
||||
"key F axis JoystickY deflect - 2.5\n"
|
||||
"key X axis Throttle set 0\n"
|
||||
"\n"
|
||||
"# --- numpad flight cluster (alternative stick/pedals/lever) ---\n"
|
||||
"key NUMPAD8 axis JoystickY deflect + 2.5\n"
|
||||
"key NUMPAD2 axis JoystickY deflect - 2.5\n"
|
||||
"key NUMPAD4 axis JoystickX deflect - 2.5\n"
|
||||
@@ -162,51 +202,33 @@ static const char *defaultProfileText =
|
||||
"key NUMPAD7 axis LeftPedal deflect + 2.5\n"
|
||||
"key NUMPAD9 axis RightPedal deflect + 2.5\n"
|
||||
"key LSHIFT axis Throttle slew + 0.7\n"
|
||||
"key LCTRL axis Throttle slew - 0.7\n"
|
||||
"key NUMPAD5 axis Throttle set 0\n"
|
||||
"\n"
|
||||
"# --- weapons / hat / handle buttons ---\n"
|
||||
"# --- fire groups (the joystick's four mappable buttons) + reverse + look\n"
|
||||
"key 1 button 0x40\n"
|
||||
"key SPACE button 0x40\n"
|
||||
"key 2 button 0x46\n"
|
||||
"key 3 button 0x47\n"
|
||||
"key LCTRL button 0x47\n"
|
||||
"key RCTRL button 0x47\n"
|
||||
"key 4 button 0x45\n"
|
||||
"key LALT button 0x3F\n"
|
||||
"key V button 0x47\n"
|
||||
"key C button 0x46\n"
|
||||
"key B button 0x45\n"
|
||||
"key UP button 0x42\n"
|
||||
"key DOWN button 0x41\n"
|
||||
"key LEFT button 0x44\n"
|
||||
"key RIGHT button 0x43\n"
|
||||
"key B button 0x41\n"
|
||||
"\n"
|
||||
"# --- secondary panel (12) on the number row ---\n"
|
||||
"key 1 button 0x10\n"
|
||||
"key 2 button 0x11\n"
|
||||
"key 3 button 0x12\n"
|
||||
"key 4 button 0x13\n"
|
||||
"key 5 button 0x14\n"
|
||||
"key 6 button 0x15\n"
|
||||
"key 7 button 0x18\n"
|
||||
"key 8 button 0x19\n"
|
||||
"key 9 button 0x1A\n"
|
||||
"key 0 button 0x1B\n"
|
||||
"key MINUS button 0x1C\n"
|
||||
"key EQUALS button 0x1D\n"
|
||||
"# --- systems (pod console buttons) ---\n"
|
||||
"key M button 0x18 # cycle control mode (BAS/MID/ADV)\n"
|
||||
"key N button 0x15 # cycle secondary schematic (Damage/Critical/Heat)\n"
|
||||
"key H button 0x2C # HOLD = coolant flush (Reservoir InjectCoolant)\n"
|
||||
"key C button 0x2F # cycle Condenser1 coolant valve (MoveValve)\n"
|
||||
"key G button 0x0E # HOLD + fire key = regroup weapon 1 (Mfd1 Quad page)\n"
|
||||
"\n"
|
||||
"# --- aux banks on F5-F8 (upper center 1-4) ---\n"
|
||||
"# --- Mfd2 bank on F5-F8 + F9 (page-gated: Quad page = select the Eng\n"
|
||||
"# --- pages, Eng page = generator A-D select / F9 gen-mode toggle) ---\n"
|
||||
"key F5 button 0x27\n"
|
||||
"key F6 button 0x26\n"
|
||||
"key F7 button 0x25\n"
|
||||
"key F8 button 0x24\n"
|
||||
"\n"
|
||||
"# --- pilot MFD keypad on QWERTY row (unit 0, keys 0-9) ---\n"
|
||||
"key Q keypad 0 1\n"
|
||||
"key W keypad 0 2\n"
|
||||
"key E keypad 0 3\n"
|
||||
"key R keypad 0 4\n"
|
||||
"key T keypad 0 5\n"
|
||||
"key Y keypad 0 6\n"
|
||||
"key U keypad 0 7\n"
|
||||
"key I keypad 0 8\n"
|
||||
"key O keypad 0 9\n"
|
||||
"key P keypad 0 0\n"
|
||||
"key F9 button 0x22\n"
|
||||
"\n"
|
||||
"# --- XInput pad ---\n"
|
||||
"padaxis LX axis JoystickX\n"
|
||||
|
||||
@@ -21,11 +21,23 @@
|
||||
PadRIO *PadRIO::activeInstance = NULL;
|
||||
|
||||
//
|
||||
// Pending backtick view-toggle edges (set in Poll, consumed by the game's
|
||||
// Pending backtick/V view-toggle edges (set in Poll, consumed by the game's
|
||||
// view-toggle block through BTPadViewToggleEdge).
|
||||
//
|
||||
int gBTPadViewToggleEdges = 0;
|
||||
|
||||
//
|
||||
// The desktop per-MFD preset-page cycle edges (J/K/L -> Mfd1/2/3), consumed
|
||||
// by L4MechControlsMapper::InterpretControls (btl4mppr.cpp step 3b -- the
|
||||
// same seam the dev-build mech4 poll feeds). Defined in mech4.cpp (always
|
||||
// compiled), so the glass TU externs it -- keyboard reconciliation
|
||||
// 2026-07-20: J/K/L are the CONTROLS.MAP muscle-memory keys and there is no
|
||||
// single pod button that "cycles" an MFD (the pod's bank buttons are
|
||||
// mode-mask-gated direct selects), so the cycle stays a port-side sender
|
||||
// with the authentic SetPresetMode body.
|
||||
//
|
||||
extern int gBTPresetCycle[3];
|
||||
|
||||
int
|
||||
BTPadViewToggleEdge(void)
|
||||
{
|
||||
@@ -118,6 +130,7 @@ PadRIO::PadRIO():
|
||||
memset(lampState, 0, sizeof(lampState));
|
||||
|
||||
bindings.Load();
|
||||
BuildKeySuppression();
|
||||
|
||||
//
|
||||
// Per-channel spring return rate = the fastest deflect rate bound to
|
||||
@@ -173,6 +186,118 @@ PadRIO::~PadRIO()
|
||||
}
|
||||
}
|
||||
|
||||
//###########################################################################
|
||||
// Typed-channel suppression (the btinput sSuppressChar/sSuppressKeyUp
|
||||
// pattern -- btinput.cpp AddSuppression -- rebuilt here because btinput
|
||||
// stands down whenever a cockpit device owns the input path). A bound key
|
||||
// must NOT also reach the 1995 in-cockpit keyboard dispatcher: 'w' selects
|
||||
// pilot 0, 'a'/'s'/'d'/'f'/'g' flip MFD2 preset pages, letter/numpad
|
||||
// KEY-UP VK values alias onto lowercase hotkeys (VK_F5==0x74=='t' = pilot
|
||||
// select 3, VK_NUMPAD2==0x62=='b' = MFD3 Quad, ...). Unbound keys keep
|
||||
// their authentic 1995 typed meaning.
|
||||
//###########################################################################
|
||||
|
||||
void
|
||||
PadRIO::AddKeySuppression(int virtual_key)
|
||||
{
|
||||
//
|
||||
// WM_KEYUP delivers the raw VK; every consumer downstream compares
|
||||
// typed CHARACTERS, so the VK value itself is the alias to swallow.
|
||||
//
|
||||
if (virtual_key >= 0 && virtual_key < 256)
|
||||
{
|
||||
suppressKeyUp[virtual_key] = 1;
|
||||
}
|
||||
//
|
||||
// WM_CHAR delivers typed characters: both cases of a letter, the digit
|
||||
// itself (main row AND numpad -- VK_NUMPAD0..9 type '0'..'9'), space,
|
||||
// and the base punctuation of the Oem keys.
|
||||
//
|
||||
if (virtual_key >= 'A' && virtual_key <= 'Z')
|
||||
{
|
||||
suppressChar[virtual_key + ('a' - 'A')] = 1;
|
||||
suppressChar[virtual_key] = 1;
|
||||
}
|
||||
else if (virtual_key >= '0' && virtual_key <= '9')
|
||||
{
|
||||
suppressChar[virtual_key] = 1;
|
||||
}
|
||||
else if (virtual_key >= VK_NUMPAD0 && virtual_key <= VK_NUMPAD9)
|
||||
{
|
||||
suppressChar['0' + (virtual_key - VK_NUMPAD0)] = 1;
|
||||
}
|
||||
else if (virtual_key == VK_SPACE)
|
||||
{
|
||||
suppressChar[' '] = 1;
|
||||
suppressKeyUp[VK_SPACE] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
static const struct { int vk; char ch; } oem[] =
|
||||
{
|
||||
{ VK_OEM_MINUS, '-' }, { VK_OEM_PLUS, '=' },
|
||||
{ VK_OEM_COMMA, ',' }, { VK_OEM_PERIOD, '.' },
|
||||
{ VK_OEM_2, '/' }, { VK_OEM_3, '`' },
|
||||
{ VK_OEM_4, '[' }, { VK_OEM_5, '\\' },
|
||||
{ VK_OEM_6, ']' }, { VK_OEM_1, ';' },
|
||||
{ VK_OEM_7, '\'' }, { VK_RETURN, '\r' },
|
||||
{ VK_TAB, '\t' }, { VK_BACK, '\b' },
|
||||
};
|
||||
for (int i = 0; i < (int)(sizeof(oem) / sizeof(oem[0])); ++i)
|
||||
{
|
||||
if (oem[i].vk == virtual_key)
|
||||
{
|
||||
suppressChar[(unsigned char)oem[i].ch] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// The generic modifier VK is what WM_KEYUP reports for L/R variants.
|
||||
//
|
||||
if (virtual_key == VK_LSHIFT || virtual_key == VK_RSHIFT)
|
||||
{
|
||||
suppressKeyUp[VK_SHIFT] = 1;
|
||||
}
|
||||
if (virtual_key == VK_LCONTROL || virtual_key == VK_RCONTROL)
|
||||
{
|
||||
suppressKeyUp[VK_CONTROL] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PadRIO::BuildKeySuppression()
|
||||
{
|
||||
memset(suppressChar, 0, sizeof(suppressChar));
|
||||
memset(suppressKeyUp, 0, sizeof(suppressKeyUp));
|
||||
|
||||
for (int k = 0; k < bindings.keyBindingCount; ++k)
|
||||
{
|
||||
AddKeySuppression(bindings.keyBindings[k].virtualKey);
|
||||
}
|
||||
|
||||
//
|
||||
// The hardcoded keys (Poll): backtick + V = view toggle, J/K/L = the
|
||||
// per-MFD preset-page cycle.
|
||||
//
|
||||
AddKeySuppression(VK_OEM_3);
|
||||
AddKeySuppression('V');
|
||||
AddKeySuppression('J');
|
||||
AddKeySuppression('K');
|
||||
AddKeySuppression('L');
|
||||
}
|
||||
|
||||
int
|
||||
PadRIO::SuppressKey(unsigned int key_value, int is_char)
|
||||
{
|
||||
if (activeInstance == NULL || key_value > 255)
|
||||
{
|
||||
return 0; // no glass device / ALT_BIT-tagged value
|
||||
}
|
||||
return is_char
|
||||
? activeInstance->suppressChar[key_value]
|
||||
: activeInstance->suppressKeyUp[key_value];
|
||||
}
|
||||
|
||||
//###########################################################################
|
||||
// Event queue
|
||||
//###########################################################################
|
||||
@@ -278,13 +403,16 @@ void
|
||||
|
||||
//
|
||||
// The backtick view toggle (per Cyd: ` = 1st/3rd person in the glass
|
||||
// cockpit). Edge-detected here (async poll, message-path-free) and
|
||||
// consumed by the game's view-toggle block via BTPadViewToggleEdge.
|
||||
// cockpit) + V (the CONTROLS.MAP ViewToggle key -- keyboard
|
||||
// reconciliation 2026-07-20). Edge-detected here (async poll,
|
||||
// message-path-free) and consumed by the game's view-toggle block via
|
||||
// BTPadViewToggleEdge.
|
||||
//
|
||||
{
|
||||
static int s_backtickWas = 0;
|
||||
int backtick_held = focused &&
|
||||
(GetAsyncKeyState(VK_OEM_3) & 0x8000) != 0;
|
||||
(((GetAsyncKeyState(VK_OEM_3) & 0x8000) != 0) ||
|
||||
((GetAsyncKeyState('V') & 0x8000) != 0));
|
||||
if (backtick_held && !s_backtickWas)
|
||||
{
|
||||
extern int gBTPadViewToggleEdges;
|
||||
@@ -293,6 +421,26 @@ void
|
||||
s_backtickWas = backtick_held;
|
||||
}
|
||||
|
||||
//
|
||||
// J/K/L: cycle the Mfd1/Mfd2/Mfd3 preset page (the CONTROLS.MAP keys;
|
||||
// the L4 mapper consumes gBTPresetCycle and runs the authentic
|
||||
// SetPresetMode body -- btl4mppr.cpp CyclePresetModeNow).
|
||||
//
|
||||
{
|
||||
static int s_presetWas[3] = { 0, 0, 0 };
|
||||
static const int s_presetKey[3] = { 'J', 'K', 'L' };
|
||||
for (int g = 0; g < 3; ++g)
|
||||
{
|
||||
int held = focused &&
|
||||
(GetAsyncKeyState(s_presetKey[g]) & 0x8000) != 0;
|
||||
if (held && !s_presetWas[g])
|
||||
{
|
||||
gBTPresetCycle[g] = 1;
|
||||
}
|
||||
s_presetWas[g] = held;
|
||||
}
|
||||
}
|
||||
|
||||
float slewDelta[PadBindingProfile::ChannelCount];
|
||||
int deflectHeld[PadBindingProfile::ChannelCount];
|
||||
memset(slewDelta, 0, sizeof(slewDelta));
|
||||
|
||||
@@ -58,6 +58,20 @@ public:
|
||||
static int
|
||||
GetLampState(int unit);
|
||||
|
||||
//
|
||||
// Legacy typed-channel suppression (the btinput BTInputSuppressKey
|
||||
// pattern, glass-side). A key CLAIMED by a bindings.txt row (or by the
|
||||
// hardcoded `/V view-toggle and J/K/L preset-cycle keys) is swallowed
|
||||
// from the engine's WM_CHAR/WM_KEYUP key-command feed (L4CTRL.cpp:1506
|
||||
// block) -- otherwise every bound letter double-dispatches into the 1995
|
||||
// in-cockpit dispatcher ('w' = pilot select 0, 'a'/'s'/'d'/'f' = MFD2
|
||||
// preset pages, NUMPAD key-up VKs alias to 'b'/'d'/'e' hotkeys, ...).
|
||||
// Unbound keys keep their authentic 1995 typed meaning. Returns 0 when
|
||||
// no PadRIO is active (the feed flows untouched on dev/pod builds).
|
||||
//
|
||||
static int
|
||||
SuppressKey(unsigned int key_value, int is_char);
|
||||
|
||||
enum { LampCount = 128 };
|
||||
|
||||
protected:
|
||||
@@ -105,6 +119,19 @@ protected:
|
||||
int
|
||||
lampState[LampCount];
|
||||
|
||||
//
|
||||
// Typed-channel suppression tables (built from the loaded bindings +
|
||||
// the hardcoded view/preset keys; see SuppressKey).
|
||||
//
|
||||
unsigned char
|
||||
suppressChar[256]; // WM_CHAR values
|
||||
unsigned char
|
||||
suppressKeyUp[256]; // WM_KEYUP values (VK aliases)
|
||||
void
|
||||
AddKeySuppression(int virtual_key);
|
||||
void
|
||||
BuildKeySuppression();
|
||||
|
||||
static PadRIO
|
||||
*activeInstance;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user