Renders BT's 128x32 plasma display into a desktop window instead of
streaming to serial hardware. L4PLASMASCREEN.* copied verbatim from
RP412; the L4GREND.cpp selector diff applied (BT's only divergence
there is a comment -- no collision).
Verified: a 'Plasma Display' window opens and the game ticks normally
beside it (L4GAUGE=640x480x16 L4PLASMA=SCREEN).
Phase 3b (single-window MFD-split cockpit) is DEFERRED -- blocked on
the missing BT cockpit-layout geometry and BT's unfinished upstream MFD
dev-composite; the L4VB16 hand-merge is HIGH-risk (BT's dev-gauge
docking overlaps RP412's split rewrite). See context/steamification.md
and docs/BT412-ROADMAP.md. (Phase 3 of the roadmap)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BT412 Steamification — status, seams, and merge notes
living
docs/BT412-ROADMAP.md; RP412 repo (C:\vwe\RP412) git history + docs
pod-hardware
multiplayer
gauges-hud
build-and-run
wintesla-port
egg
RIO
console
BT cockpit button-bank geometry source (no vRIO CockpitLayout exists for BT): l4gauge.cfg / pod photos / Nick
Does BT's rebuilt L4D3D still have RP's NULL-texture AddRef crash pattern?
Does L4CONTROLS=PAD set primaryControlType=PrimaryRIO so MechRIOMapper engages unchanged?
BT412 Steamification — status, seams, and merge notes
BT412 = BT411 made distributable on Steam and playable over the internet with no pod
hardware, following the recipe RP412 proved end-to-end (RP411 → RP412). The full plan
and phase order: docs/BT412-ROADMAP.md. RP412 (C:\vwe\RP412) is the reference
implementation — its git history from commit 467934c onward is the steamification,
grouped roughly: toolchain → PadRIO input → MFD-split cockpit → front end/LocalConsole
→ NetTransport/Steam sockets → lobby → bindings/KeyLight → packaging.
Repo rule [T0]:origin = VWE/BT412.git only. This repo must never touch the
BT411 remote (no cross-pull remote — deliberate difference from RP412's rp411
pattern). Forked at BT411 4e72f0c (2026-07-14).
Status ledger
Phase 0 bootstrap — DONE 2026-07-14. Tree copied (working tree + full history,
build/ excluded), origin re-pointed, fast-forwarded to 4e72f0c, dev-state
run/run.cmd working-tree edit discarded (committed version is the documented solo
launcher), identity pass (README/CLAUDE.md/CMake project(bt412)), this topic +
roadmap authored.
Phase 1 (VS2022 v143) — DONE 2026-07-14. Generator moved to
Visual Studio 17 2022 (v143, Win32). One real change was forced: the v143 linker
fails the image on unresolved externals even under /FORCE, so the dead
offline-tool ladders in game/reconstructed/mech3.cpp are neutralized behind
BT412_OFFLINE_TOOLS (default off) and the exe now links /FORCE:MULTIPLE
(UNRESOLVED retired — real unresolved symbols are hard errors now). None of RP412's
other v143 source fixes (TIME.h, NETWORK.h, L4DINPUT, CAMMGR) were needed — BT's
tree was already v142-clean. Verified: clean build; solo DEV.EGG runs (gait/target/
31-subsystem ticks); two-instance loopback MP via btconsole.py — mesh forms
(console + 1502↔1602 game ports, "All connections completed!"), mission runs both
sides, each world ticks its master + the remote replicant. [T2]
Phase 2 (PadRIO input layer) — DONE 2026-07-14. Ported from RP412:
L4RIO.h (RIOBase split; serial RIO re-parented), L4CTRL.h (rioPointer is
RIOBase*), L4CTRL.cpp PAD-token hook (→ primaryControlType=PrimaryRIO, so the
stock MechRIOMapper engages — confirmed), new L4PADRIO.* / L4PADBINDINGS.* /
L4KEYLIGHT.* (gates renamed BT412KEYLIGHT; KeyLight TU builds /std:c++17 /permissive- per-file). Default bindings.txt = the shared Tesla board layout
(numpad flight, letter rows = MFD banks, F-keys = Secondary columns, joystick head
0x40-0x47 on Space/arrows/pad). content/bindings.txt is gitignored (written on
first PAD run).
BT-side changes PadRIO forced: (1) both keyboard input bridges (mech4.cpp write
of mapper attrs; mechmppr.cpp BT_KEY_BRIDGE) now stand down when a RIO device
exists (bridge_controls->rioPointer != 0) — they clobbered the engine push every
frame; M mode-cycle / X recenter conveniences stay live. (2) the mapper attribute
chain was off by one — see reconstruction-gotchas §11 (short-parent-chain
positional trap): the .CTL stick mapping wrote throttlePosition. Fixed with a
pad slot + binary-locked enum. This was a latent REAL-POD bug (Phase 8 would have
hit it).
Verified live: L4CONTROLS=PAD → throttle lever ramps (rate axis, sticky), stick
deflects (turnDemand=1 + the authentic speed-vs-turn clamp: 61.5→22.0 u/s while
turning), mech drives across the map, XInput pad detected + 3 s re-probe, bindings
parse (59 key buttons/8 axes/12 pad buttons/5 pad axes). Keyboard fallback
regression-verified headless (BT_FORCE_THROTTLE=1 walks). New diagnostics:
BT_CTRLMAP_LOG=1 (streamed-mapping dump), BT_STICK_LOG=1 (RIO stick push trace).
Phase 3a (PlasmaScreen, L4PLASMA=SCREEN) — DONE 2026-07-14.L4PLASMASCREEN.*
copied verbatim; L4GREND.cpp selector diff applied (BT's only divergence there is a
comment at line 440 — no collision). Renders BT's 128×32 plasma into a desktop window.
Verified: a window titled "Plasma Display" opens and the game ticks normally beside it
(L4GAUGE=640x480x16 L4PLASMA=SCREEN). Note: the L4GAUGE gauge path binds a listen
socket at startup → a first-run Windows Firewall prompt (expected; unrelated to plasma).
Phase 3b (single-window MFD-split cockpit, L4MFDSPLIT=1) — DEFERRED 2026-07-14.
Two blockers make this the wrong thing to land now:
No BT cockpit-layout geometry. RP412's L4MFDVIEW button-bank anchors/addresses
come from vRIO's CockpitLayout for the Red Planet pod; vRIO has zero BattleTech
data. The BT pod's panel geometry has no reference (needs pod photos / l4gauge.cfg
mining / Nick) — the clickable lamp-lit banks can't be authentically placed.
BT's MFD compositing is an unfinished upstream beachhead. Per docs/GAUGE_COMPOSITE.md,
only the radar/secondary surface composites on a dev box; the five mono MFDs don't
render yet (port-name mismatch, Step 2). RP412's split panes would be empty until that
upstream work lands.
Merge is HIGH-risk (quantified above): BT's 521-line L4VB16 divergence (dev-gauge
docking + a 476-line block) overlaps RP412's 402-line split rewrite in the ctor and
Update regions — a true hand-merge, not a drop-in. The 3-way diffs are staged in the
scratchpad (rp412-L4VB16.diff, bt-L4VB16.diff).
Prerequisite order when resumed: finish BT's MFD dev-composite (GAUGE_COMPOSITE.md Steps
2–3) → obtain BT cockpit geometry → then hand-merge MFDSplitView. Until then the game
ships single-window with the dev-gauge dock (BT_DEV_GAUGES) as its cockpit composite.
Phases 4–7: Phase 4 (NetTransport seam) next — unblocked and independent of 3b.
The seams (what plugs in where) [T0 unless noted]
Input: LBE4ControlsManager PUSH model; L4CTRL.cpp parses L4CONTROLS tokens;
RP412 adds token PAD → new PadRIO() (a RIOBase implementation from
XInput+keyboard), and the stock RIO mapper runs against it unchanged. BT's mapper
selection: btl4app.cpp MakeViewpointEntity switch on
controls->primaryControlType (+0xc98) → MechRIOMapper for PrimaryRIO(4).
Plasma: BT's plasma is identical hardware to RP's (128×32, L4PLASMA.h);
RP412's PlasmaScreen renders the same Video8BitBuffered surface into a desktop
window, selected in L4GREND.cpp on L4PLASMA=SCREEN.
Displays: BT pod = main 800×600 3D view + 640×480 radar + five mono MFDs on one
spanned 1280×480 surface (+ plasma). All cockpit surfaces are bit-plane masks over
one shared SVGA16 pixelBuffer (see gauges-hud, pod-hardware). RP412's
MFDSplitView (L4MFDVIEW.* + L4VB16.cpp split mode, L4MFDSPLIT=1) composes
child panes on a fixed 1920×1080 canvas; 3D presents into the viewscreen pane via
Present(hDestWindowOverride).
Networking: arcade stack unchanged (see multiplayer); RP412 moved the wire
behind NetTransport (L4NETTRANSPORT.*), default WinsockNetTransport, Steam
implementation L4STEAMTRANSPORT.* (ISteamNetworkingSockets, FakeIP + SDR,
degrade-to-TCP on any failure). Addresses stay SOCKADDR_IN-shaped so the egg
[pilots] ip[:port] roster works over both wires.
Session: console protocol (egg msgID 3, 1040-byte chunks + ACK; RunMission
msgID 5 ×2; console must stay connected) — tools/btconsole.py is the working
feeder and the spec for the in-process LocalConsole marshal (Phase 5).
Known merge risks (from the 3-way BT411 / RP411-baseline / RP412 comparison)
Quantified 2026-07-14 [T1] — BT-vs-RP411-baseline divergence measured with
diff --strip-trailing-cr (baseline extracted from RP412 history, git show 3b8b729:MUNGA_L4/<f>); RP412-side change sizes from git diff 3b8b729..HEAD:
File
BT vs baseline
RP412 change
Treatment
L4RIO.h
identical
125 lines (RIOBase split)
RP412 verbatim
L4RIO.cpp
identical
0
nothing to do
L4CTRL.h, L4NET.H, L4PLASMA.*, L4KEYBD.h
identical
small
RP412 verbatim
L4CTRL.cpp
29 diff lines
17 (PadRIO hook)
apply RP412 diff
L4GREND.cpp
6 diff lines
15 (PlasmaScreen selector)
apply RP412 diff
L4VIDEO.cpp
2725 diff lines (BT render work)
only 14
apply RP412 diff — small despite BT divergence
L4VIDEO.h
29 diff lines
7
apply RP412 diff
L4NET.CPP
23 diff lines (BT_NET_TRACE ×5)
345 (seam refactor)
MEDIUM — apply seam refactor, re-site traces
L4VB16.cpp
514 diff lines (dev-gauge dock)
350 (split view)
HIGH — true hand-merge
L4VB16.h
14 diff lines
32
hand-merge with L4VB16.cpp
L4VB16.cpp — HIGH. RP412 rebuilt ~3846–4046 (splitViews/canvas/present); BT
rebuilt the same window-management region for dev-gauge docking (BT_DEV_GAUGES,
BT_DEV_GAUGES_WINDOW, BT_DEV_GAUGES_DOCK, BT_GAUGE_SCALE, BT_GAUGE_SEC_ROT,
BT_ALL — 15 sites). Hand-merge; write the 3-way diff notes HERE before editing.
L4NET.CPP — MEDIUM. RP412 rerouted all ~22–26 Winsock call sites through
NetTransport_Get(); BT's 5 BT_NET_TRACE diagnostic blocks sit exactly on those
send/receive sites and must be re-sited onto the seam calls.
CMake: engine sources are explicit per-file lines (no globs) — every ported
.cpp needs its own add_library line or it silently doesn't compile. New deps:
xinput9_1_0.lib (PadRIO), C++/WinRT (KeyLight, per-file C++17), Steamworks SDK +
steam_api.lib behind option(BT412_STEAM).
No /Zp1 in BT's build (RP's engine builds /Zp1): RP412's packing-mismatch
workarounds (WINDOWS_IGNORE_PACKING_MISMATCH, KeyLight default-packing carve-out,
pack(push,8) around Steam headers) are no-ops here — keep them for verbatim-ness,
but eyeball any copied code for /Zp1-layout assumptions.