Files
BT411/engine/MUNGA_L4/L4PADBINDINGS.h
T
arcattackandClaude Opus 4.8 76eaddc4a8 Issue #25: composite Turn channel -- twin-stick gamepad turning
Gamepad players expect stick X = turn; the pod-faithful default maps it
to torso twist, and the pedal pair couldn't take a single signed axis.
New bindings channel "Turn": a signed composite that decomposes into
the pedal pair at the RIO publish (+ = right pedal, - = left), ADDING
to direct pedal bindings so mixed rigs compose, clamped 0..1 per pedal.
The default bindings.txt stays pod-faithful and documents the
twin-stick alternative inline:

    padaxis LX axis Turn
    padaxis RX axis JoystickX

Also covers the HOTAS ask (any signed axis can now drive turning).
Verified: regenerated defaults carry the doc lines; a twin-stick-edited
bindings.txt parses and boots clean.  Awaiting live pad verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 00:55:45 -05:00

118 lines
3.3 KiB
C++

#pragma once
//###########################################################################
//
// L4PADBINDINGS -- the PadRIO input-binding profile (BT_GLASS only).
//
// Maps PC keyboard keys and XInput pad controls onto the pod's abstract
// control surface: RIO button addresses (buttonGroup elements), MFD keypad
// units, and the five analog channels (Throttle, JoystickX/Y, Left/Right
// Pedal). The profile is read from "bindings.txt" in the working directory
// (content\); a self-documenting default file is written on first run.
// Grammar (one binding per line, '#' comments):
//
// key <KEYNAME> button <addr>
// key <KEYNAME> keypad <unit> <key>
// key <KEYNAME> axis <channel> deflect <+|-> <rate>
// key <KEYNAME> axis <channel> slew <+|-> <rate>
// key <KEYNAME> axis <channel> set <value>
// pad <PADBTN> button <addr>
// padaxis <PADAXIS> axis <channel> [invert] [slew <rate>]
//
// deflect = held drives the channel toward +/-1 at <rate>/s, auto-centers
// on release (the spring-centered stick model).
// slew = held moves the channel at <rate>/s and it STAYS (the throttle
// lever model).
// set = press jumps the channel to <value> (all-stop detent).
//
//###########################################################################
class PadBindingProfile
{
public:
enum Channel {
ChannelThrottle = 0,
ChannelJoystickX,
ChannelJoystickY,
ChannelLeftPedal,
ChannelRightPedal,
// issue #25: composite -- a SIGNED axis that decomposes into the
// pedal pair (positive = right pedal, negative = left pedal) so a
// gamepad stick can drive the turn ("twin-stick" profile).
ChannelTurn,
ChannelCount
};
enum ActionKind {
ActionButton, // RIO button address (buttonGroup element)
ActionKeypad, // MFD keypad unit + key
ActionAxisDeflect,
ActionAxisSlew,
ActionAxisSet
};
struct Action
{
ActionKind kind;
int address; // ActionButton: RIO address; ActionKeypad: unit
int key; // ActionKeypad: key number (0-9=digits, 10+=A..)
int channel; // ActionAxis*: Channel
float rate; // deflect/slew: units per second (signed);
// set: the target value
};
struct KeyBinding
{
int virtualKey; // Win32 VK code
Action action;
};
struct PadButtonBinding
{
unsigned buttonMask; // XINPUT_GAMEPAD_* bit
Action action; // ActionButton / ActionKeypad only
};
enum PadAxis {
PadAxisLX = 0, PadAxisLY, PadAxisRX, PadAxisRY,
PadAxisLT, PadAxisRT,
PadAxisCount
};
struct PadAxisBinding
{
int axis; // PadAxis
int channel; // Channel
int invert; // flip the sign
int slew; // 0 = direct absolute, 1 = value drives d/dt
float slewRate; // full-deflection slew speed (units/s)
};
PadBindingProfile();
~PadBindingProfile();
//
// Load "bindings.txt" from the working directory; if it does not exist,
// write the default profile there first (self-documenting). Always
// leaves a usable profile (falls back to built-in defaults on a
// malformed file, logging each rejected line).
//
void
Load();
int keyBindingCount;
KeyBinding keyBindings[192];
int padButtonBindingCount;
PadButtonBinding padButtonBindings[32];
int padAxisBindingCount;
PadAxisBinding padAxisBindings[12];
protected:
void
LoadDefaults();
void
WriteDefaultFile(const char *path);
int
ParseLine(char *line, int line_number);
};