Human re-test on the fresh build: zero-stop OK, but the lever could PARK inside
the walk/run hysteresis band (walkStrideLength@0x534 .. reverseSpeedMax@0x538)
where the gait SM has no stable state -- walk<->run hunt, EngineShiftFwd/Rev
retriggering ("mixed walk/run mode + repeated acceleration sample"). The
pod-build keyboard lever already snaps out of the band to the NEARER edge at
key-rest (mech4.cpp GAIT DETENT); the PadRIO slew channel lacked it.
Faithful port, not an invention: mech4.cpp publishes the band in lever units
every driven frame (gBTGaitDetentLo/Hi seam, same guards + the same +0.002
engage margin); L4PADRIO Poll() applies the identical nearer-edge snap to the
Throttle channel when the slew rests (no slew key held, no pad slew axis
deflected). Sweeping through the band while held stays continuous (the
authentic moving lever, one authentic shift).
Verified (DEV.EGG glass, graduated parks each ~0.10 of the sweep,
BT_GAIT_TRACE+BT_MPPR_TRACE): band [0.358026,0.50388) = demand 22.02-30.87;
in-band parks snap both directions (0.4137->lo, 0.465126->hi); no at-rest
sample in the band; exactly ONE walk->run clip (state 10) up and ONE run->walk
(state 14) down across the whole sweep; zero-stop unaffected; pod build
rebuilt + BT_AUTODRIVE smoke clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
351 lines
25 KiB
Markdown
351 lines
25 KiB
Markdown
# GLASS_COCKPIT — running ledger (desktop developer layer)
|
||
|
||
Detailed running log for the `glass-cockpit` branch work: PadRIO, on-screen panel/plasma,
|
||
miniconsole, Steam transport/lobby. Curated digest: `context/glass-cockpit.md` (read that
|
||
first); this file carries the step-by-step detail, evidence, and verification transcripts.
|
||
|
||
---
|
||
|
||
## 2026-07-17 — Step 1: branch + compile gates + scaffolding
|
||
|
||
- Branch `glass-cockpit` created from master @ `e2c21c4`.
|
||
- `CMakeLists.txt`: `option(BT_GLASS OFF)` + `option(BT_STEAM OFF)`; `BT_STEAM` without
|
||
`BT_GLASS` is a configure-time FATAL_ERROR; gates append to `BT_DEFS` (flows to all three
|
||
targets); `STATUS` line prints gate state at configure. Gated `target_sources` blocks will be
|
||
added per-TU as they land (CMake would reject nonexistent source files now).
|
||
- `.gitignore`: `build-pod/`, `build-steam/`, `content/bindings.txt`, `content/frontend.egg`,
|
||
`content/steam_appid.txt`.
|
||
- Verification (this step): defaults configure prints `BT_GLASS=OFF BT_STEAM=OFF` and builds
|
||
(purity build); `-DBT_GLASS=ON` and ON/ON configures print correct gate state; the
|
||
BT_STEAM-without-BT_GLASS negative test FATAL_ERRORs. Full 3-config BUILD matrix becomes
|
||
meaningful from step 2 (first gated code) and is re-run there.
|
||
|
||
## 2026-07-17 — Step 2a: the .CTL positional-attribute off-by-one — CONFIRMED and FIXED (ungated)
|
||
|
||
**The bug [T2, live-verified]:** the streamed `.CTL` "L4" mapping resource carries the 1995
|
||
binary's positional attribute ids (stick=3, throttle=4, ...), resolved at install time by
|
||
`Simulation::GetAttributePointer(id)` → `AttributeIndexSet::Find(id)` = `attributeIndex[id-1]`
|
||
(positional). `MechControlsMapper` chained its enum on `Subsystem::NextAttributeID == 2`, but
|
||
the binary's chain reaches 3 (one base attribute we don't publish) — so EVERY streamed mapper
|
||
record landed one member late.
|
||
|
||
**Evidence (new `BT_CTRLMAP_LOG=1` diagnostic in `LBE4ControlsManager::CreateStreamedMappings`,
|
||
`engine/MUNGA_L4/L4CTRL.cpp` — permanent, env-gated, default off).** Before the fix
|
||
(`build-pod` Debug, `-egg DEV.EGG`, dev platform):
|
||
```
|
||
rec 0 subsys 0 group 1(Joy) elem 0 attrID 3 -> +0xe4 (= throttlePosition: WRONG, stick intended)
|
||
rec 1 subsys 0 group 0(Scalar) elem 0 attrID 4 -> +0xe8 (= pedalsPosition: WRONG, throttle intended)
|
||
rec 2 subsys 0 group 3(Button) elem 63 attrID 6 -> +0xf0 (= speedDemand: WRONG, reverse intended)
|
||
rec 4-6 attrID 10/11/12 -> lookRight/lookBehind/lookDown (one late; LookLeft/Right/Behind intended)
|
||
```
|
||
Spacing attr3→attr4 was 4 bytes (Scalar) where stick→throttle must be 8 (ControlsJoystick).
|
||
The weapons chain (attrID 19 → +0x31c TriggerState) was already correct — mechweap was
|
||
pad-aligned in task #16 (gotcha #11). Torso (subsys 17) also aligned (MechSubsystem publishes
|
||
binary id 2). ONLY the mapper chain (subsys 0) was off. On the dev box the keyboard INPUT
|
||
BRIDGE (writes the mapper members directly, after the engine push) masked it; on a REAL POD the
|
||
serial RIO throttle scalar would have driven `pedalsPosition` and the stick x/y would have
|
||
driven throttle+pedals — the Phase-8 landmine prior art predicted.
|
||
|
||
**The fix (ungated, pod-correctness — user-approved 2026-07-17):** `mechmppr.hpp` pins the enum
|
||
to the binary numbering (`MechControlsMapperPadFirstAttributeID = 2`, stick=3 ... PilotArray=
|
||
0x16); `mechmppr.cpp` pads id 2 with a valid never-bound entry (the mech.cpp attrPad idiom) and
|
||
locks the base + pinned ids with `static_assert`s. `L4MechControlsMapper::TargetRangeExponent`
|
||
(chained) moves 0x16→0x17, matching the binary's own arithmetic; it is bound by name (audio
|
||
zoom-blip), so no consumer shifts.
|
||
|
||
**After the fix:** `attrID 3 -> +0xdc` (stick), `attrID 4 -> +0xe4` (throttle, 8-byte spacing
|
||
✓), `attrID 6 -> +0xec` (reverse), `10/11/12 -> +0xfc/+0x100/+0x104` (the look trio); torso +
|
||
weapons unchanged. Regression: headless forced-walk run (`BT_FORCE_THROTTLE=1`) drives normally
|
||
— `[mppr] thr=1 -> speedDemand=61.501`, gait cycles, position advances, 31/31 subsystems
|
||
simulated, no faults.
|
||
|
||
## 2026-07-17 — Steps 2b-2e: PadRIO, stand-downs, displays, preset — DONE + verified
|
||
|
||
**2b (RIOBase + PadRIO, commit ac57a47):** `L4RIO.h` splits abstract `RIOBase` (enums, the five
|
||
analog Scalars, pure `GetNextEvent`/`SetLamp`, no-op serial ops, NEW `IsOperational()`) out of
|
||
serial `RIO` (`: PCSerialPacket, RIOBase` — ctor assigns as before, zero behavioral delta);
|
||
`L4CTRL.h` `rioPointer` retyped `RIOBase*`; gated `L4CONTROLS=PAD` factory arm. NEW gated TUs
|
||
`L4PADRIO.*` (XInput+keyboard → the surface; 3s hot-plug; focus-guarded keys; per-poll
|
||
AnalogEvent heartbeat time-gated so the manager's drain loop terminates; `lampState[]`;
|
||
static `SetScreenButton` — button addresses AND the keypad space 0x50-0x6F → KeyEvents) and
|
||
`L4PADBINDINGS.*` (`content\bindings.txt`, default written on first run; deflect/slew/set axis
|
||
model; addresses validated vs ButtonCount).
|
||
|
||
**2c (stand-downs, commit 9f35a80):** engine query `BTRIODevicePresent()` (ungated —
|
||
pod-correct); both dev bridges (`mech4.cpp` attr writes, `mechmppr.cpp` BT_KEY_BRIDGE) gate
|
||
their WRITES: unset=auto / "0"=off / else on; forced harness always rides. **Definitive live
|
||
test:** OS-injected LSHIFT hold slewed PadRIO throttle 0→0.33→1.0 with `pre==thr` every frame —
|
||
the value arrives via the ENGINE push through the streamed `.CTL` binding (bridge stood down),
|
||
`speedDemand=61.501` from authentic InterpretControls.
|
||
|
||
**2d/2e (displays + preset, commits db9ac94 + 418acf0):** the on-screen panel follows **vRIO**
|
||
(`C:\VWE\vrio` — `CockpitLayout.cs`/`PanelCanvas.cs`, per Cyd): five 4x2 MFD clusters
|
||
(descending addresses), Throttle/Secondary/Screen/Joystick 1x8 columns, two 4x4 hex keypads
|
||
(0x50 internal→pilot unit, 0x60 external→operator unit), physical names, red/yellow/blue
|
||
coloration, 500/250/125ms flash, right-click latch — 104 controls. `PlasmaWindow :
|
||
Video8BitBuffered` renders the 128x32 plasma via the SAME gauge-renderer path as serial
|
||
(buffer is TOP-DOWN — the flip rendered it upside down, user-reported + fixed). MFD surfaces =
|
||
the EXISTING dev-gauge modes (no new code, no L4VB16 exposure). `-platform glass` preset +
|
||
run.cmd token. **Button-trap guard (e2c21c4 pattern):** the authentic base
|
||
`ConfigureMappableMessageHandler` FAIL trap is `abort()` under DEBUGOFF — any aux/zoom button
|
||
without its reconstructed L4 override KILLED the game on press (panel makes all reachable).
|
||
Default now log+ignore; `BT_BUTTON_TRAP=1` restores the hard trap. Verified: synthetic panel
|
||
click → 2 `[FAIL]` (press+release through panel→PadRIO→ProcessRIOEvent→streamed mapping), game
|
||
survives. NOTE: each `[FAIL]` marks a missing L4 override — reconstruction backlog, see
|
||
[[open-questions]].
|
||
|
||
**Step-2 verification matrix (all passed 2026-07-17):**
|
||
1. Purity: defaults `build-pod/` compiles (known mech3 /FORCE externals only); seam audit —
|
||
`BT_GLASS`/`BT_STEAM` mentions ONLY in CMakeLists, btl4main.cpp preset region, L4CTRL.cpp
|
||
(include+PAD arm), L4GREND.cpp (include+SCREEN branch), gated TUs, run.cmd docs.
|
||
2. Pod solo forced-walk clean (§2a).
|
||
3. **2-node loopback MP on the pod build:** A `-egg MP.EGG -net 1501` + B `-net 1601` +
|
||
`btconsole.py` relay — both nodes launch into the running mission (77/76 ticks, mechs at
|
||
their drop zones, gait/target loops live).
|
||
4. Glass build: PadRIO drives through the authentic mapper path; panel click path live; plasma
|
||
window; all boot lines present.
|
||
Deferred to the user (visual-only): panel/plasma appearance vs vRIO on screen, XInput stick
|
||
feel, `L4PADFLIP` polarity.
|
||
|
||
## 2026-07-17 — Step 3: the miniconsole — DONE + verified (commit c4ca87b)
|
||
|
||
**Architecture: the console stays a wire peer.** `game/glass/btl4fe.*` (GDI catalog menu + the
|
||
console-shape egg writer) and `btl4console.*` (the LocalConsole marshal: a worker-thread console
|
||
CLIENT speaking the `tools/btconsole.py` protocol verbatim — 1040-byte chunked
|
||
`ReceiveEggFileMessage`, 20s settle, `RunMission`×2, stay connected, own the clock,
|
||
`StopMission`(id 6)@expiry; logs `content\marshal.log`). The engine runs the 100% stock console
|
||
ladder — ZERO engine hooks; the planned `L4APP.H` setters proved unnecessary (launches pass real
|
||
argv on relaunch). WinMain: one gated block — zero-mission-arg glass launch → menu → relaunch
|
||
self with mission argv (`BT_FE_EGG/PODS/SECS` arm the marshal; `BT_FE_LOOP` returns exits to the
|
||
menu; menu relaunches CLEAR the handoff — the leak re-armed a marshal in cycle test 1).
|
||
|
||
**Verified (synthetic menu drives, all autonomous):**
|
||
- Golden egg: `frontend.egg` section sequence + line offsets IDENTICAL to `MP.EGG`; ordinal
|
||
pages verbatim; GDI name-glyph pages in MP.EGG framing (32/8 rows, 16 bytes/line).
|
||
- Solo cycle: menu → egg → relaunch → marshal → stock ladder → mission RUNS (58+ ticks) → 60s
|
||
clock → stop → menu returns with clean env.
|
||
- **HOST LAN 2-node:** peer started manually (`-net 1601`); menu-hosted launch → marshal feeds
|
||
BOTH pods (2-pilot egg, 10 chunks each) → both in-mission (65/18 ticks) → stop both → host
|
||
returns to the menu, peer idles at console-wait.
|
||
|
||
## 2026-07-17 — Step 4 pre: Steamworks SDK 1.64 vendored (commit 23c0019)
|
||
|
||
Official Valve zip (provided by Cyd at the repo root), extracted to
|
||
`extern/steamworks_sdk_164/` and trimmed to `public/` + `redistributable_bin/` + Readme
|
||
(12.5 MB); zip removed. Consumed only under `-DBT_STEAM=ON`.
|
||
|
||
## 2026-07-17/18 — Step 4: the Steam wire — CODE COMPLETE (commits e79e8fa, 48ede2f, f703bb1)
|
||
|
||
**4a (e79e8fa) — the wire seam:** file-local `BTNetSend/Recv/Accept/Close` wrappers around the
|
||
9 raw Winsock sites in `L4NET.CPP`; OFF builds are pure code motion. Regression: 2-node loopback
|
||
MP through the seam — mesh forms, full 31/31 mission on both nodes.
|
||
|
||
**4b (48ede2f) — the transport:** gated TU `L4STEAMNET` — pseudo-SOCKETs (0x5EA0xxxx) behind the
|
||
seam; the engine's TCP byte stream rides reliable-NoNagle Steam messages re-assembled into
|
||
per-connection rings (recv semantics preserved: empty→WSAEWOULDBLOCK, closed→0); seam
|
||
completions: `CheckSocket` (peer = token address), `OpenConnection` TCP_OPEN delegate,
|
||
`GetMyAddress` self-token. Install on `BT_STEAM_NET=1`; graceful degrade to Winsock at every
|
||
step. BOTH pumps needed (`SteamAPI_RunCallbacks` + sockets `RunCallbacks`).
|
||
|
||
**4c (f703bb1) — the lobby + THE FAKEIP LESSON [T2]:** prior art assumed FakeIP survives the
|
||
per-mission process relaunch — DISPROVEN live (menu process got 169.254.124.110, the mission
|
||
process got 169.254.54.234; the pod timed out connecting to its own stale address). Redesign:
|
||
roster addresses are opaque ipv4-shaped TOKENS (`169.254.77.N`, ports 1501 console / 1502 game)
|
||
minted by the host at GO and mapped to Steam IDENTITIES; connections ride
|
||
`ConnectP2P(identity, channel)` (console=0, game=1); the map hands off via env
|
||
(`BT_FE_MYFAKE`/`BT_FE_STEAMMAP`). `btl4lobby`: ISteamMatchmaking room (`btl4=1` filter),
|
||
pilots publish name/mech/color, host GO mints tokens + writes `btl4map` + `btl4go`.
|
||
|
||
**Verified single-machine (ON/ON, live Steam client):** menu → HOST STEAM LOBBY → room → GO →
|
||
map `169.254.77.1=<steamID64>` → egg roster carries the token → mission process
|
||
`[steamnet] up ... 1 roster token(s) incl. self` → pod self-matches → stock console ladder →
|
||
mission RUNS (46 ticks). 3-config build matrix green throughout. **Remaining (user hardware):**
|
||
the live 2-account cross-machine session — procedure in `docs/STEAM_TEST.md`.
|
||
|
||
## 2026-07-20 — Glass input-coverage audit + keyboard reconciliation
|
||
|
||
**Trigger:** making BT_GLASS the permanent player experience — familiar desktop keys dead, "most
|
||
panel buttons do nothing". Full audit of every input surface (bindings.txt keys, the 72-button
|
||
panel, the streamed .CTL EventMapping records, the typed 1995 channel), then the fix set.
|
||
|
||
### Finding 1 — THE panel-dead-buttons root cause: MechRIOMapper message ids shifted [T1→T2 fixed]
|
||
`MechRIOMapper`'s enum chained `Aux1QuadMessageID = L4MechControlsMapper::NextMessageID` (=0x19),
|
||
registering all 18 RIO handlers at ids 0x19-0x2a. The binary RIO table @0051dd30 RE-REGISTERS the
|
||
base aux/zoom ids **3..0x13** with the override handlers (Hotbox = 0x1a @0051de98) — that
|
||
re-registration over the base FAIL-trap ids is the whole point of the derived table. Streamed
|
||
`.CTL` events carry the binary ids, so every MFD-bank/zoom press found only the base
|
||
`ConfigureMappableMessageHandler` → `[FAIL]` (26 of 72 panel buttons dead; on a real pod with the
|
||
hard trap, an abort()). Hotbox worked by accident (sender + entry both used the same wrong 0x2a).
|
||
Fixed: ids pinned to the binary numbering + static_assert locks (`btl4mppr.hpp`). Verified live:
|
||
panel 0x0F/0x08/0x27/0x20 → `[mode] preset` lines, 0x10/0x11 zoom no longer FAIL. (Residual
|
||
fidelity gap: the binary RIO table also carries its OWN Keypress id 0x19 → @004d2514,
|
||
unreconstructed; we still register the shared L4 Keypress 0x17 — works, logged in the backlog.)
|
||
|
||
### Finding 2 — the always-active msg-4 records IDENTIFIED [T1] (closes the #9 open question)
|
||
Binary message tables decoded from section_dump (16-byte {id,name,fn,0} entries): per-receiver-
|
||
class id spaces. Roster (ARENA1 MadCat): subs 3=Reservoir, 4-9=Condenser1-6, 10-13=GeneratorA-D.
|
||
So: **0x2C = the COOLANT FLUSH button** (Reservoir InjectCoolant, WORKS), **0x2F/0x2E/0x2D/0x2B/
|
||
0x2A/0x29 = per-condenser VALVE buttons** (MoveValve, WORK), **0x1A-0x1D = Generator A-D ON/OFF**
|
||
(`ToggleGeneratorOnOff` id 4, table @0050fb90 fn @004b1ed0 — DEAD, unreconstructed). Also new:
|
||
0x13 → Mech **DuckRequest** (0x1a @0049fa00 — the manual's CROUCH button!), 0x28 → Mech
|
||
**BalanceCoolant** (0x16 @0049f728), 0x12 → ThermalSight toggle (id 3), 0x14 → Searchlight +
|
||
Searchlight2 **ToggleLamp** (id 3 @004b860c — body reconstructed but `Searchlight::
|
||
MessageHandlers` is default-constructed EMPTY, so it never fires: the quick win). Weapon
|
||
Eng-page extras: msg 0x3 = **ToggleCooling** (HeatableSubsystem @0050e41c fn @004ad6f8, unwired),
|
||
msg 0xb = **ToggleSeekVoltage** (energy @004ba478) / **EjectAmmo** (ammo weapons @004bb9b8),
|
||
Myomers msg 9 = ToggleSeekVoltage (@004b8a48; body exists, unwired). Unhandled messages are
|
||
SILENT (Receiver::Receive ignores an unfound id) — the dead-button census cannot come from
|
||
[FAIL] logs alone; it comes from the BT_CTRLMAP_LOG record dump × the handler tables.
|
||
|
||
### Finding 3 — glass keyboard: stand-down + double dispatch [T2]
|
||
ALL btinput actions (drive keys, M/N/J/K/L/H/C/V/B/G/F5-F9) stand down when PadRIO owns the path
|
||
— nothing on the game side polls keys directly (BTInputPoll is the only GetAsyncKeyState
|
||
consumer). Meanwhile the typed 1995 channel stays LIVE (glass preset = `PAD,KEYBOARD`) with NO
|
||
suppression — so Cyd's old bindings double-dispatched: W (keypad row) also typed 'w' = pilot
|
||
select 0; C (fire middle) also typed 'c' = MFD3 Eng3; NUMPAD key-UPS alias to letter hotkeys
|
||
(VK_NUMPAD2=0x62='b' = MFD3 Quad, VK_F5=0x74='t' = pilot select 3). This is the historic
|
||
pre-btinput double-dispatch, back on the glass side.
|
||
|
||
### The fix set (landed)
|
||
1. `btl4mppr.hpp` — RIO id pinning (ungated, pod-correctness; the step-2a idiom).
|
||
2. `L4PADRIO.*` — typed-channel suppression tables built from the loaded bindings (+hardcoded
|
||
keys), `PadRIO::SuppressKey` consumed by a `#ifdef BT_GLASS` arm next to the btinput hook in
|
||
`L4CTRL.cpp:1506` block; V joins backtick on the view-toggle edge; J/K/L fire
|
||
`gBTPresetCycle[3]` (the same seam the dev poll feeds — no binary analog exists for a "cycle"
|
||
button; the body is the authentic SetPresetMode).
|
||
3. `L4PADBINDINGS.cpp` + `content\bindings.txt` — the MERGED default map (user decision:
|
||
keyboard = the ~20 core gameplay actions on the CONTROLS.MAP keys; the PANEL covers all 72
|
||
pod buttons by click/latch). W/S throttle, A/D pedals, Q/E twist, R/F elevation, X all-stop,
|
||
1-4/Space/Ctrl fire + LALT reverse + B look-behind, M=0x18, N=0x15, H=0x2C (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/mode), numpad flight cluster kept. DISPLACED from Cyd's
|
||
map (each reachable on the panel or by editing bindings.txt): number row→secondary panel,
|
||
QWERTY row→pilot keypad (dropped), arrows→hat looks (arrows now drive), V/C/B→fire
|
||
0x47/0x46/0x45, LCTRL→throttle-down. XInput pad section untouched.
|
||
NOTE (control modes, authentic): in BAS mode the pod turns with the STICK (Q/E here) and the
|
||
pedals are inactive; A/D turn in MID/ADV — exactly the manual's behavior, unlike the desktop
|
||
bridge which re-routes A/D per mode.
|
||
4. Un-touched: CONTROLS.MAP, btinput defaults, pod serial path. `bindings.txt` is read only by
|
||
`PadBindingProfile` inside the gated PadRIO TU — the pod build never opens it.
|
||
|
||
### Verification (2026-07-20, build-glass2, ARENA1 solo, all logs in-session)
|
||
- Panel: 0x0F→preset(0,1), 0x08→(0,0), 0x27→(1,1), 0x20→(1,0), zoom no-FAIL; 0x15 display
|
||
1→2→0; 0x2C hold → `[flush] Reservoir level 6→0`; dead-class buttons (0x13/0x1A/0x12/0x14)
|
||
silent as predicted. ZERO [FAIL] lines in the whole session (was 2 per bank press).
|
||
- Keyboard: W hold → thr 0→1, speedDemand 61.501, gait advances; Q → turnDemand 0.94 (BAS);
|
||
M → `control mode -> 1`; A hold → turnDemand 1.0 (MID); Space → `[emitter] FIRED`; N →
|
||
`display notify 1`; J/K/L → presets (0,1)/(1,1)/(2,1); H → reservoir drain; C → condenser#1
|
||
valveState 1→5. BT_SHOT frame confirms visually: MID lit on the control-mode column, CRITICAL
|
||
DAMAGE schematic up, MFDs on Eng pages, heading moved. V not re-injected (user took
|
||
foreground mid-run) — code-identical to the verified backtick edge [T3].
|
||
- Suppression: typed 'w' swallowed (no [keych]), typed 't' flows ([keych] 0x74), WM_KEYUP
|
||
VK_F5 swallowed (the 't'-alias hazard).
|
||
- Un-regression: build-pod (gates OFF) compiles 0 errors; forced-walk run drives
|
||
(speedDemand=61.501, 32 subsystems, streamed ids 3→+0xdc / 4→+0xe4 unchanged).
|
||
|
||
### 2026-07-20 (later) — the "keymap regression" live report: STALE EXE, not code [T2]
|
||
|
||
**Report (human, live glass session):** arrows dead/half-dead (UP accelerated, no detent;
|
||
LEFT/RIGHT no turn), throttle not a lever, V no view toggle, J/K/L/N inert, weapons firing into
|
||
the ground. **Root cause: `build-glass/Release/btl4.exe` was built 07:47 — BEFORE commit
|
||
`5dd3536` (08:35) landed the whole audit fix set** (SuppressKey tables, V/J/K/L hardcoded keys,
|
||
the RIO-id pinning that makes 0x15/N work) — the session was relaunched without rebuilding, and
|
||
kept running the pre-audit image. `content\bindings.txt` (08:23) already WAS the merged map and
|
||
the loader parsed it fine (`[padrio] bindings loaded: 40 keys, 10 pad buttons, 5 pad axes`), so
|
||
every bindings-file symptom analysis was a red herring; the binary was simply old. The
|
||
ground-fire symptom is the same session: with the R/F/X semantic chaos of the unsuppressed typed
|
||
channel, the torso elevation had been driven down and nothing recentered it.
|
||
|
||
**Fix:** `rm build-glass/Release/btl4.exe && cmake --build build-glass --config Release` (0
|
||
`error C`). No input-code change was needed — arrows/lever/detent/V were all already correct at
|
||
HEAD. One diagnostic enrichment landed: the `BT_MPPR_TRACE` `[mppr-c]` line now also prints
|
||
`mode/pedals/stick/turn/elev`, so one env-gated line proves the whole control surface.
|
||
|
||
**Re-verification (build-glass, DEV.EGG solo, SendInput-injected keys, own PID only):**
|
||
- UP hold 0.9 s → thr 0→0.63, dmd 38.70; released → **holds** 0.63 for 3 s (persistent lever).
|
||
- DOWN hold 2.5 s → thr 0.36→0.0028→0, rev=0 throughout — **stops at zero, no reverse
|
||
crash-through** (the wire throttle is the authentic 0..1 lever; reverse is the 0x3F button).
|
||
- M → `[mode] control mode -> 1`; LEFT → pedals +1 / turnDemand +1 (CCW=left, the wire sign);
|
||
RIGHT → pedals −1 / turnDemand −1; both auto-center on release. (In BAS the pedals are
|
||
authentically inactive — turns are the stick, Q/E.)
|
||
- R/F → stick.y ±1, torso elev 0→+0.349→−0.195, holds where released (positional torso,
|
||
matches the btinput path); **boresight level at spawn (elev=0)**.
|
||
- V → `[view] COCKPIT eyepoint` ↔ `[view] external chase`, BT_SHOT pair confirms
|
||
(chase mech ↔ canopy + reticle). Closes the audit's one T3 (V edge) as [T2].
|
||
- SPACE → `[emitter] FIRED #1`. Pod build rebuilt + smoked: BT_AUTODRIVE=0.8 →
|
||
dmd=49.2008, clean log, no cross-contamination.
|
||
|
||
**Lesson (procedural):** after landing an engine/input commit, REBUILD every live build dir
|
||
before relaunching — a stale `build-glass` exe reproduces "the whole keymap is broken" exactly.
|
||
|
||
### 2026-07-20 (later still) — human re-test: GAIT DETENT ported to the PadRIO lever [T2]
|
||
|
||
**Report (fresh build):** zero-stop confirmed working, but "OUR detent is still missing — it
|
||
goes into that mixed walk/run mode and repeats the acceleration sample." That is the documented
|
||
gait DEAD BAND (locomotion.md [T1]): the SM has no stable state for a demand in
|
||
(`walkStrideLength`@0x534, `reverseSpeedMax`@0x538) — parked there it cycles 7→10→14→7 forever,
|
||
retriggering EngineShiftFwd/Rev each swing. The pod-build keyboard lever carries an
|
||
accommodation (mech4.cpp "GAIT DETENT": at KEY-REST, snap the lever out of the band to the
|
||
NEARER edge; sweeping through while held stays continuous) — the PadRIO slew channel had no
|
||
such protection, so the glass lever could park in-band.
|
||
|
||
**Port (faithful copy, not an invention):** mech4.cpp publishes the band in lever units every
|
||
driven frame through a new seam (`gBTGaitDetentLo/Hi`, same guards + the same +0.002 engage
|
||
margin; hi<=lo = unknown); `L4PADRIO.cpp Poll()` applies the IDENTICAL nearer-edge snap to the
|
||
Throttle channel when the slew is at rest (no slew key held, no pad slew axis deflected) —
|
||
same rule, same log tag, gated `BT_GAIT_TRACE`. The rest decision uses PadRIO's own held-state
|
||
(the lever's owner on glass), not gBTDrive.keyFwd/Back.
|
||
|
||
**Verified (build-glass, DEV.EGG solo, graduated UP-taps parking the lever every ~0.10 across
|
||
0..1, BT_GAIT_TRACE+BT_MPPR_TRACE):** band [0.358026, 0.50388) = demand 22.02→30.87 (the
|
||
locomotion.md Blackhawk numbers); park at 0.4137 → snapped DOWN to 0.358026, park at 0.465126 →
|
||
snapped UP to 0.50388 (both nearer-edge directions); rest-position histogram shows NO at-rest
|
||
sample inside the band; gait states: ONE walk→run clip (state 10, once) on the whole ascending
|
||
sweep, ONE run→walk (state 14, once) descending, normal 6↔7 / 12↔13 cycling between — the
|
||
accel/shift sample can fire only once per real transition. Zero-stop unaffected (thr=0 rest,
|
||
state 0). Pod build rebuilt + BT_AUTODRIVE smoke clean (the seam publish is a pure global
|
||
write on the pod path).
|
||
|
||
## 2026-07-18 — Merge origin/master (commit f889e24): btinput meets PadRIO
|
||
|
||
Master's 33-commit run landed (D1 relay/operator-console line, CONTROLS.MAP+XInput binding
|
||
engine `btinput.cpp`, camera seats, torso pitch, sign fixes, the 1995 manual, version stamping).
|
||
**The reconciliation:** btinput (ungated, dev/pod desktops) joined the stand-down convention —
|
||
`BTInputPoll` yields and `BTInputSuppressKey` claims nothing when an operational cockpit device
|
||
(serial RIO / PadRIO) owns the input path (`BTRIODevicePresent`; `BT_KEY_BRIDGE` override;
|
||
forced harness exempt). **One input system per mode:** `CONTROLS.MAP`/btinput on dev+pod
|
||
desktops, `bindings.txt`/PadRIO on glass. The D1 relay keeps its own raw sockets by design
|
||
(relay and Steam are alternative wires; the BTNet seam covers the arcade mesh both need).
|
||
Verified: 3-config build, glass stand-down live, pod forced-walk with btinput active, 2-node
|
||
loopback MP 31/31.
|
||
|
||
## 2026-07-19 — Mission-control options: the AUTHORITATIVE catalog (TeslaSuite console.ini)
|
||
|
||
Source: `C:\VWE\TeslaSuite\410console\4_10-console-extracted\Console.ini`, the Mac 4.10 operator
|
||
console's `BT::` adventure tree (the `tag=` values are the egg field values; display names are the
|
||
console's). Cross-checked vs `410console/battletech-port/BATTLETECH-PORT-SPEC.md` + `reference/
|
||
cavern.egg`. This is every option the original console exposed for a BattleTech mission.
|
||
|
||
**Mission-level** — `[mission]`: `adventure=BattleTech` (fixed), `temperature=27` (fixed),
|
||
`scenario=freeforall` (the tag is always freeforall; the RESPAWN choice is the ROLE):
|
||
- map (8): cavern, grass, rav, polar3, polar4, arena1, arena2, dbase
|
||
- time (4): morning, day, evening, night ← was day/night/morning/**dusk** (wrong: evening)
|
||
- weather (3): clear, fog (light), soup (heavy)
|
||
- role (2): Default (`model=dfltrole`, respawn) / NoReturn (`model=noreturn`, one life)
|
||
- length: seconds
|
||
|
||
**Per-pilot** — participant block:
|
||
- experience (4 [T1 game decode]): novice, standard, veteran, expert ← was hardcoded `expert`
|
||
- vehicle (18, friendly→tag): Black Hawk=blkhawk, Loki=loki, Nova=bhk1, Mad Cat=madcat, Thor=thor,
|
||
Owens=owens, Kotori=own1, Summoner=thr1, Hellbringer=lok1, Timber Wolf=mad1, Avatar=avatar,
|
||
Satyr=ava1, Sunder=sunder, Sunder V1=snd1, Vulture=vulture, Mad Dog=vul1, Mischief=lok2,
|
||
Zanin Neko=mad2 (the 18 ModelList names; matches the 2026-07-18 vehicle sweep)
|
||
- color/camo (7, display→tag): White=White, Black=Black, Brown=Brown, Red=**Crimson**, Green=Green,
|
||
Gray=**Grey**, Tan=Tan ← was White/Black/Crimson only
|
||
- badge/emblem (7, the `badge=` field): VGL, Davion, Kurita, Liao, Marik, None, Steiner ← was `VGL`
|
||
- patch/badge-color (8, the `patch=` field): Yellow, Blue, Grey, Green, Violet, Red, White, Black
|
||
← was `Yellow`
|
||
- dropzone (5, the `dropzone=` field): one, two, three, four, five ← was `one`
|
||
- advancedDamage (0/1): the advanced damage model toggle ← was hardcoded `1`
|
||
- vehicleValue: 1000 (const), loadzones=1 (const), bitmapindex (per-pilot ordinal)
|