From bf9f8417065547e3802521cbbbe7595dfb60c29a Mon Sep 17 00:00:00 2001 From: Cyd Date: Fri, 17 Jul 2026 15:45:53 -0500 Subject: [PATCH] Handoff: retire handoff/padrio -- superseded by the merge-back into BT411 The handoff bundle was the interim vehicle for carrying PadRIO to BT411 by hand. With the whole steamification line now merged back (BT411 fast-forwards onto this history), the real modules live in the shared tree behind the BT412 compile gate -- a second copy in the same repo would only drift. The bundle stays available in history at 2e475f4. Co-Authored-By: Claude Fable 5 --- handoff/padrio/PADRIO-IMPLEMENTATION.md | 315 -------------- handoff/padrio/bindings.default.txt | 136 ------ handoff/padrio/reference/L4RIO.h | 389 ----------------- handoff/padrio/src/L4PADBINDINGS.cpp | 542 ----------------------- handoff/padrio/src/L4PADBINDINGS.h | 116 ----- handoff/padrio/src/L4PADRIO.cpp | 546 ------------------------ handoff/padrio/src/L4PADRIO.h | 122 ------ 7 files changed, 2166 deletions(-) delete mode 100644 handoff/padrio/PADRIO-IMPLEMENTATION.md delete mode 100644 handoff/padrio/bindings.default.txt delete mode 100644 handoff/padrio/reference/L4RIO.h delete mode 100644 handoff/padrio/src/L4PADBINDINGS.cpp delete mode 100644 handoff/padrio/src/L4PADBINDINGS.h delete mode 100644 handoff/padrio/src/L4PADRIO.cpp delete mode 100644 handoff/padrio/src/L4PADRIO.h diff --git a/handoff/padrio/PADRIO-IMPLEMENTATION.md b/handoff/padrio/PADRIO-IMPLEMENTATION.md deleted file mode 100644 index 3835e9d..0000000 --- a/handoff/padrio/PADRIO-IMPLEMENTATION.md +++ /dev/null @@ -1,315 +0,0 @@ -# PadRIO — implementation guide for BattleTech (BT411) - -**What it gives you:** *cockpit-less play.* PadRIO synthesizes the pod's RIO -control surface from an **XInput controller + the PC keyboard**, so the full -stock RIO path — the `MechRIOMapper`, the lamp commands, the button banks — -runs unchanged with no serial hardware attached. Selected at runtime with -`L4CONTROLS=PAD`. Every input is rebindable through a `bindings.txt` file. - -This kit was built in **BT412** (the Steam fork) and is a straight port of the -PadRIO from **RP412** (Red Planet); the RIO surface is shared MUNGA/L4 code, so -it drops into BT411 with the same three-file seam. It is a point-in-time -snapshot — treat the sources here as the reference and adapt to your tree. - -> Serial-hardware RIO is untouched: `L4CONTROLS=RIO` still builds a real -> `RIO` on COM1 exactly as before. PadRIO is an *additional* implementation of -> the same base class, not a replacement. - ---- - -## 1. The design — the `RIOBase` seam - -The pod code already consumes an **abstract control surface**, it just wasn't -factored out. The monolithic `RIO` class (serial packet transport + the control -surface the game reads) is split into two: - -``` - RIOBase (abstract control surface: RIOEvent stream + 5 analog Scalars + SetLamp) - / \ - RIO PadRIO - (serial) (XInput + keyboard) <-- new -``` - -Everything the game reads from a RIO — `GetNextEvent()` (button press/release, -keypad key, analog update), the five analog `Scalar`s (`Throttle`, `LeftPedal`, -`RightPedal`, `JoystickX`, `JoystickY`), and `SetLamp()` — is declared **pure -virtual on `RIOBase`**. `RIO` implements it over the serial packet layer; -`PadRIO` implements it over XInput + the keyboard. The controls manager holds a -`RIOBase*`, so the mapper/lamp/button code above the seam never changes. - -That is the whole trick. No game logic moves; you add one subclass and re-point -one pointer. - ---- - -## 2. What's in this kit - -``` -handoff/padrio/ -├── PADRIO-IMPLEMENTATION.md <- this guide -├── src/ -│ ├── L4PADRIO.h L4PADRIO.cpp <- NEW: the pad/keyboard RIO -│ └── L4PADBINDINGS.h L4PADBINDINGS.cpp <- NEW: the bindings.txt parser/profile -├── reference/ -│ └── L4RIO.h <- the header AFTER the RIOBase split (your reference) -└── bindings.default.txt <- a sample of the profile the game writes on first run -``` - -Two **new** files to add, three **existing** files to edit (`L4RIO.h`, -`L4RIO.cpp`, `L4CTRL.h`, `L4CTRL.cpp`), one build-system change, and **two -game-side gotchas** you will hit because PadRIO is the first thing to drive a -real device through your mapper (§5 — read those, they include a *latent -real-pod bug* PadRIO exposes). - ---- - -## 3. Integration steps - -### 3a. Split `RIO` into `RIOBase` + `RIO` (`L4RIO.h`, `L4RIO.cpp`) - -In `L4RIO.h`, introduce `RIOBase` and re-parent `RIO` onto it. See -`reference/L4RIO.h` for the exact end state. The essentials: - -- **`RIOBase`** owns the shared surface: the enums (`RIOStatusType`, - `LampState`, `RIOEventType`, `RIOKeyPair`, `RIOEvent`), the state members - `TestModeActive`, `Throttle`, `LeftPedal`, `RightPedal`, `JoystickX`, - `JoystickY`, `MajorRevision`, `MinorRevision`, and the virtual interface: - - ```cpp - virtual ~RIOBase() {} - virtual Logical GetNextEvent(RIOEvent *destinationPointer) = 0; // pure - virtual void SetLamp(int lampNumber, int state) = 0; // pure - virtual void RequestAnalogUpdate() {} // + the - virtual void GeneralReset() {} /* ...ResetThrottle, deadbands... */ - ``` - - The base ctor zero-initializes the shared members (they used to live on `RIO`). - -- **`RIO`** now `: public RIOBase, public PCSerialPacket` and simply **removes** - the members that moved up (`TestModeActive`, the five analog `Scalar`s, - `Major/MinorRevision` are now inherited). Its methods keep the same - signatures; they are now overrides. - -- In `L4RIO.cpp`, delete the moved-member initializations from the `RIO` - constructor (the `RIOBase` ctor does them now). Nothing else in `RIO` changes. - -### 3b. Re-type the controls-manager pointer (`L4CTRL.h`) - -The manager's RIO pointer becomes a base pointer: - -```cpp - //------------------------------------------------------------------- - // RIO data (serial hardware or the PadRIO pad/keyboard synthesizer) - //------------------------------------------------------------------- - RIOBase - *rioPointer; -``` - -`L4CTRL.h` already `#include "l4rio.h"`, so `RIOBase` is in scope. Every existing -`rioPointer->…` call is part of the `RIOBase` interface, so nothing else in the -manager changes. - -### 3c. Add the `PAD` token (`L4CTRL.cpp`) - -At the top: `#include "l4padrio.h"`. Then in the `L4CONTROLS` token loop, beside -the existing `RIO` / joystick tokens, add: - -```cpp - else if (strcmpi(temp, "PAD") == 0) - { - //------------------------------------------------------ - // Cockpit-less play: PadRIO speaks the RIO control - // surface from an XInput pad + the PC keyboard, so the - // full RIO mapper/lamp/button path runs unchanged. - //------------------------------------------------------ - rioPointer = new PadRIO(); - Check(rioPointer); - Register_Object(rioPointer); - flags.RIOExists = 1; - primaryControlType = LBE4ControlsManager::PrimaryRIO; - } -``` - -**The critical line is `primaryControlType = PrimaryRIO`.** That is what makes -the stock `MechRIOMapper` engage against PadRIO exactly as it would against -serial hardware — no mapper change, no new control-type. (In BT this selection -is the switch on `controls->primaryControlType` in -`btl4app.cpp MakeViewpointEntity`; leaving it `PrimaryRIO` means `MechRIOMapper` -is chosen for the viewpoint mech, unchanged.) - -### 3d. Add the two new files - -Drop `L4PADRIO.{h,cpp}` and `L4PADBINDINGS.{h,cpp}` into `MUNGA_L4/`. - -- **`L4PADRIO`** is the `RIOBase` subclass: it polls XInput + `GetAsyncKeyState`, - turns edges into `RIOEvent`s (`GetNextEvent`), integrates the analog axes - (deflect vs. rate semantics), records commanded lamp state (`SetLamp`), and - re-probes for a hot-plugged pad every ~3 s. -- **`L4PADBINDINGS`** parses `bindings.txt` into a `PadBindingProfile` and writes - the self-documenting default file on first run. - -XInput links itself — `L4PADRIO.cpp` carries -`#pragma comment(lib, "xinput9_1_0.lib")`, so you do **not** need a linker-line -change if your toolchain honors `#pragma comment` (MSVC does). - -**Optional dependency — KeyLight.** `L4PADRIO.cpp` includes `l4keylight.h` to -mirror lamp state onto an RGB keyboard (Windows Dynamic Lighting), gated on the -`BT412KEYLIGHT` env var. If you do not want that feature, either take -`L4KEYLIGHT.*` too, or make a **minimal build**: delete the `#include -"l4keylight.h"` and the `keyLightActive` block (the `KeyLight_*` calls at the -ctor, dtor, and the two `UpdateLamps` sites). PadRIO is fully functional without -it. - -### 3e. Build system - -Add the two `.cpp` files to your build (this repo uses explicit CMake -`add_library` source lists — no globs): - -```cmake - "engine/MUNGA_L4/L4PADRIO.cpp" - "engine/MUNGA_L4/L4PADBINDINGS.cpp" -``` - -No extra `target_link_libraries` for XInput (the `#pragma comment` handles it). -If your toolchain ignores `#pragma comment`, add `xinput9_1_0.lib` to the link. - ---- - -## 4. Two game-side gotchas you WILL hit (shared game code) - -PadRIO is very likely the **first thing that drives a real device through your -`MechControlsMapper`**. That surfaces two issues in the *game* layer (not in -PadRIO). BT411 shares this code, so both apply to you. - -### 4a. The keyboard bring-up bridge must stand down - -Any dev/bring-up "keyboard → mapper" bridge you added to move the mech without a -device (e.g. a WASD stand-in that writes the mapper's analog attributes, or an -`mechmppr.cpp` bring-up bridge) will now **clobber the real device push every -frame** — the bridge and PadRIO both write the same attributes, and the bridge -wins last. Gate every such bridge to stand down when a device exists: - -```cpp - LBE4ControlsManager *bridge_controls = /* the active controls manager */; - bool device_present = - (bridge_controls != 0 && bridge_controls->rioPointer != 0); - if (!device_present) { - // ...the keyboard bring-up writes... - } -``` - -Keep the pure conveniences (mode-cycle, recenter) live if you like; it is only -the **analog/attribute writes** that must yield to the device. In BT412 this is -`mech4.cpp` (the mapper-attr write) and `mechmppr.cpp` (`BT_KEY_BRIDGE`). - -### 4b. The `.CTL` mapping resolves one slot early — a LATENT REAL-POD BUG - -This one is subtle and important: **PadRIO exposes a pre-existing bug that a real -serial RIO would also have hit** (you just may never have driven one under -WinTesla yet). - -`MechControlsMapper` publishes its mappable members through the attribute system, -and `AttributeIndexSet::Find(ID)` is **positional** — it indexes -`attributeIndex[ID - 1]`. The binary's streamed control map (the `.CTL` / -`CreateStreamedMappings` path) uses fixed numeric IDs from the **DOS** class -chain (e.g. stick = 3, throttle = 4, because the DOS `Subsystem` base carried two -attributes). The **WinTesla** parent chain carries *fewer* base attributes (one: -`SimulationState`), so every child attribute resolves **one slot early**: the -stick mapping lands in `throttlePosition`, the throttle mapping lands in -`pedalsPosition`, and so on. No crash — values silently write the **wrong -member**, so the mech won't steer/throttle correctly under any real device. - -Only the **numeric-ID** path is affected (`CreateStreamedMappings` / `.CTL`); -gauge databinding resolves by **name** and never saw it, which is why it stayed -latent. - -**Fix:** put a **named pad slot at the front** of the mapper's -`AttributePointers[]` (`mechmppr.cpp`) so the positional index lines up with the -binary's numbering, and lock the attribute-ID enum to the binary's values -(`static_assert` them). This is BT412 reconstruction-gotchas §11 ("short parent -chain / positional trap"). - -**Diagnostic:** dump each streamed mapping's *resolved pointer* and compare it to -`&mapper->member`. BT412 gates this behind `BT_CTRLMAP_LOG=1`; the per-frame -`[mppr]` log prints `&stick` / `&thr` so you can see the mapping land in the -right member. - ---- - -## 5. The `bindings.txt` format - -`bindings.txt` (beside the exe, working directory) maps keys and pad controls to -RIO input addresses and the five analog axes. It is written with the full, -self-documenting default profile on first run; delete it to restore defaults. -See `bindings.default.txt` for a complete example. Grammar: - -``` - key button [toggle] - key axis deflect # hold at n while down, spring back - key axis rate # walk by n/sec while down, position holds - pad