Commit Graph
26 Commits
Author SHA1 Message Date
arcattackandClaude Opus 5 12d6862c1f keylight: unship it -- opt-in (BT_KEYLIGHT=1), gone from all player docs
Operator decision: the player base is not on Windows 11, so the RGB keyboard
mirror should not be shipped or promised.  The feature is NOT deleted -- Cyd's
implementation stays intact behind the gate for whoever wants it -- it is
simply off by default and invisible:

  * PadRIO gate flips from opt-out (BT_KEYLIGHT=0 disables) to OPT-IN
    (BT_KEYLIGHT=1 enables).  Default boots produce zero keylight lines, and a
    stub build and a real build now behave identically for players: nothing.
  * Removed from every user-facing artifact: the README paragraph, the
    CONTROLS.md section, the CONTROLS.html footer line.
  * environ.ini template: the entry stays (it is the discoverable home for the
    opt-in) but now says OFF-unless-1, Windows 11 22H2+, and why it is
    unshipped.
  * mkdist's cut-time stub warning removed -- it existed to protect a README
    promise that no longer exists.
  * context/glass-cockpit.md records the decision, the opt-in, and the
    build-time SDK stub gate in one place.

VERIFIED: default boot logs zero keylight lines; BT_KEYLIGHT=1 engages the
feature (the stub on this machine: its one honest log line + the key map).
All three console suites pass; checkctx CLEAN.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 17:06:00 -05:00
arcattackandClaude Opus 5 8edce41f88 bindings: one-time migration to the board-2 layout -- the two-population fork is gone
Operator decision: "make this version devour their custom settings and re-output
the correct binding file with the updates -- I'd like to eliminate competing
code paths."  Before this, the preserved-bindings.txt convention meant upgraders
stayed frozen on whatever layout their file was written under, forever: two
player populations, two sets of true documentation, and every future default
improvement reaching only fresh installs.

THE MIGRATION (PadBindingProfile::MigrateBindingsFile, run once at load):
  * A file carrying the new "# bindings-board 2" marker is left alone -- the
    check is the first thing that runs, so this is a no-op forever after.
  * Otherwise: every row matching the UNION OF EVERY DEFAULT SET EVER SHIPPED
    (85 canonical rows, harvested from c2ee729..1cda880 and embedded as a
    table; comments stripped, whitespace collapsed, uppercased) is dropped --
    those rows were never a player's choice, they were just the old board.
  * Rows the player actually authored are CARRIED with their original text and
    comments, and WIN over the new defaults: the losing default row is emitted
    commented out as "# (yours wins) ...", keyed by the control ("KEY T",
    "PAD A", "PADAXIS LX") so key rows only displace key rows.
  * The joystick wizard's marker-delimited section is preserved VERBATIM (the
    same markers L4JOY rewrites between), so HOTAS players migrate without
    re-running the wizard.
  * The previous file is saved as bindings.old.txt first.  Restoring it over
    bindings.txt just re-migrates next boot -- deliberate: the old WORLD is not
    restorable, only the player's own rows persist.  That is the point.

VERIFIED end-to-end with the real exe (three planted scenarios):
  A. old-world file (8 old-default rows + 2 authored rows incl. a rate tweak +
     a wizard section): both authored rows carried with comments, both
     conflicting new defaults stood down with "(yours wins)", wizard section
     byte-preserved, backup written, log line states all counts, and the
     migrated file parses with ZERO malformed lines.
  B. already-migrated file: byte-identical md5 after a full boot, no migration
     line, no backup touched.  (First attempt at this check clobbered its own
     evidence by running case C first -- re-run properly.)
  C. no file: fresh defaults now carry the marker as line 1, no migration.
  The operator's real bindings.txt was stashed before testing and restored
  untouched; it will migrate on their own next launch, as intended.

Docs flipped to the new reality: the README's "your keys DO NOT change"
upgrade promise is replaced by the migration story (customs + stick kept, old
stock keys replaced, bindings.old.txt escape hatch); CONTROLS.md gains the same
note; context/glass-cockpit.md records the mechanism, the 85-row table's
provenance, and why restoring the backup re-migrates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 14:19:13 -05:00
CydandClaude Opus 5 5f88a4eeb2 The keyboard becomes the button board (RP412 bindings port) + a controls page
Ported RP412's bindings design and made it the DEFAULT: the letter and number
rows are the MFD button banks laid out WHERE THEY SIT ON THE PANEL, flight
moves to the numpad so the board stays free, F1-F12 are the map's two columns,
and G/B stay unbound as the physical gap between the lower clusters.  Expressed
in BT's OWN grammar (slew/deflect/set, the Turn channel) -- bindings.txt is a
documented compatibility surface and was not touched.

Coverage: 61 of 72 addresses on the keyboard, and the 11 absent ones are
exactly those the pod never wired (0x16/0x17/0x1E/0x1F column gaps, 0x38-0x3E
intercom/door).  All 72 stay clickable on the panel.

It lands on BT's addresses unreasonably well: 1-4 + QWER are the ENTIRE coolant
system (Condensers 1-6, flush, balance), F6/F7 the display and control-mode
cycles, F9-F12 Generators A-D, F4 the crouch button reconstructed earlier
today.

THE DELIBERATE COST.  A bound key is removed from the authentic 1995
typed-hotkey channel so it cannot double-dispatch, and this board binds nearly
everything -- so 5 (Quad page), z (Eng1), t/y/u/i/o (pilot select) and +/-
(target zoom) are given up.  Unbind a key to get its 1995 meaning back.
Documented in the file header, the markdown and the page.

NEW RULE: A BINDINGS ROW WINS OVER A BUILT-IN CONVENIENCE KEY
(PadRIO::KeyHasBinding).  V and J/K/L are board buttons now, and those polls
read GetAsyncKeyState directly -- without this they would have fired BOTH the
button and the built-in.  View toggle lives on BACKTICK alone.

CONTROLS.MAP rewritten to mirror the board so glass/pod/dev still feel
identical (the 2026-07-21 settlement): 90 bindings, 0 parse complaints.

HAT LABELS CORRECTED [T1].  INPUT_PATH_AUDIT flagged 0x41-0x44 and was right.
Settled from the streamed mapping (BT_CTRLMAP_LOG): elem 66 -> subsys 17
attrID 14, i.e. 0x42 is the TORSO subsystem, not a look; 0x44/0x43/0x41 are the
mapper's LookLeft/LookRight/LookBehind (attrID 10/11/12).  The .RES has no
"TORSO CENTER" string -- its names are LookBehind/Down/Forward/Left/Right -- so
the audit's wording was loose but its substance correct.  Swept L4GLASSWIN,
L4PADPANEL and L4VB16.

docs/CONTROLS.html: interactive keyboard (hover a key for its address and
meaning, MFD clusters outlined), pad diagram, panel map, the under-glass
press-target figure, radar placement thumbnails and the environ.ini table --
modelled on RP412's page, rewritten for BT's addresses and hazards.  mkdist
wraps the fragment in a doctype/charset shell and ships it beside CONTROLS.txt.

Verified: bindings.txt regenerates and loads (74 keys, 0 errors); CONTROLS.MAP
90 bindings, 0 errors; 72/72 panel addresses still dispatch; surround /
exploded / dock / pod / dev boot and simulate with zero faults.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 03:23:27 -05:00
CydandClaude Opus 5 02b1b50e45 RGB keyboard lamp mirror + the shipped controls reference
KEYBOARD LAMP MIRROR (L4KEYLIGHT).  Ported from RP412, itself a port of vRIO's
KeyboardLampMirror.  Keys bound to a lamp address in bindings.txt glow with the
panel palette through Windows Dynamic Lighting -- yellow for the map's side
columns (0x10-0x1F), red for the rest -- flashing in step with the on-screen
buttons (its LampLevel copy matches the FIXED BTLampBrightnessOf).  Per-key
boards light each bound key; zone-lit boards mirror the strongest lamp
board-wide.  All WinRT runs on a private worker thread: watcher, claim, and a
100 ms paint loop that repaints only on change.

RP412's packing hazard does NOT transfer: it forces default struct packing there
because its engine is /Zp1, which would break the WinRT ABI.  BT411's BT_OPTS
carries no /Zp, so only the dialect flags are needed -- /std:c++17
/permissive- per-file, since the project otherwise builds C++14 /permissive.
The scalars-only interface is kept anyway so the isolation survives if packing
is ever added.

Wired in PadRIO: map built from bindings.keyBindings (ActionButton binds only,
first-binding-wins per key), fed from PadRIO::SetLamp, stopped in the dtor
(which hands the LEDs back to Windows).  BT_KEYLIGHT=0 opts out; a machine
without Dynamic Lighting logs once and stays dormant.

Verified live: claimed this machine's 24-zone keyboard and mirrors 25 bound
keys; BT_KEYLIGHT=0 and the pod profile produce zero keylight lines and run
clean.

SHIPPED CONTROLS REFERENCE.  docs/CONTROLS.md carries the keyboard table plus
the full 72-BUTTON POD MAP, grouped by the display each bank surrounds, with the
coolant-valve detent warning (1-5-50-CLOSED, one press past max shuts the loop)
and the jam/eject procedure.  mkdist flattens it to ASCII as CONTROLS.txt at the
zip root, the README's own idiom.  players/README.txt gained pointers to it, to
environ.ini, to -fit, and to the RGB mirror.

KB: context/glass-cockpit.md and docs/GLASS_COCKPIT.md updated for the whole
branch -- layout modes, L4RIOBANK and the under-glass rule, the letterbox fit
and its ordering trap, player-tunable displays, the measured legend grid, the
closed dead-button backlog, and this mirror.

Verified: five-mode regression (surround/exploded/dock/pod/dev) boots and
simulates with zero faults; mkdist writes a 5149-byte pure-ASCII CONTROLS.txt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 02:24:30 -05:00
arcattack a235e2464d KB convention: bindings.txt grammar is a compatibility surface (player customizations must survive upgrades) 2026-07-24 12:07:45 -05:00
arcattack 72a8b124f9 KB: direct-axis release-edge gotcha (issue #36) 2026-07-23 23:41:30 -05:00
arcattackandClaude Opus 4.8 c2ee7299b2 Generic joystick / HOTAS / pedals support: DirectInput 8 layer + capture wizard
Tester ask: configure non-Xbox controllers.  New L4JOY layer (DI8):
enumerates every non-XInput game device (up to 4), DIJOYSTATE2 polling
with range normalization, reacquire-on-loss, 3s hot-plug re-enum.
XInput devices are excluded via the documented RawInput IG_ VID/PID
check (live-verified skipping a real XBOX360 pad -- no double-feed).

bindings.txt grows joydev/joyaxis/joybutton/joyhat rows (device slots
by name substring or ordinal; axes X..RZ/SL0/SL1 with invert/slew/
deadzone; hats as 4-direction buttons with diagonal chords).  PadRIO
runs them through the existing channel/event machinery -- per-binding
edge arrays release automatically on detach (the #24 rule), and a
direct throttle axis maps full lever travel onto the 0..1 channel
while the gait detent still snaps a lever parked in the walk/run dead
band.

BT_JOYCONFIG=1 (joyconfig.bat) runs a console capture wizard: move
each control when prompted (stick/twist/throttle/fire), invert derived
from the move direction, hat look cluster auto-added, output written
between markers in bindings.txt with the rest of the file preserved.

Legacy L4DINPUT DIJoystick (L4CONTROLS=DIJOYSTICK) untouched.
Verified: grammar accept/reject per line, XInput-exclusion live,
30s in-mission no-device polling clean.  Awaiting a tester with real
stick hardware for axis verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 16:36:32 -05:00
arcattackandClaude Opus 4.8 44f7c39ee1 Issue #24: release held pad buttons on controller disconnect (look latch)
XInput button emission is edge-based against previousPadButtons; the
disconnect path zeroed that memory WITHOUT emitting release edges, so
any button held when a controller (wrapper) died stayed pressed
game-side forever -- and the zeroing also erased what a reconnect would
have released.  Field signature (playtest night): a PS3-pad-in-xbox-
mode wrapper died mid-hat-hold -> the look-view machine (hold-based,
mppr) latched a side view with the authored look pitch ("stuck side
view + pitch at ground, no recovery"); the keyboard-only run was clean.

On the connected->disconnected transition PadRIO now emits
EmitButton(address, 0) for every binding whose mask was held, then
forgets.  Not reproducible headless (needs a real hardware unplug
mid-hold) -- awaiting the reporter's controller for live verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 00:49:41 -05:00
arcattackandClaude Opus 4.8 c421ec4af8 Wait screen: paint into the backbuffer, not after Present (load flicker)
The pre-mission overlay painted the window DC AFTER ExecuteIdle's
Present -- every cycle flashed the black D3D frame before the GDI
redraw, visible as flicker while the game loads (user report).  The
overlay now lands IN the presented frame: the backbuffer refuses GetDC
on this driver (D3DERR_INVALIDCALL without LOCKABLE_BACKBUFFER, not
worth forcing game-wide), so the text+spinner GDI-paint goes into a
cached offscreen plain surface (GetDC always legal there) and
StretchRects onto the backbuffer before Present.  D3DPOOL_DEFAULT cache
released at every device-Reset site; window-DC path remains for the
pre-device seat wait and as the fallback; one-shot [waitscreen] boot log
says which path is live.

Verified: 14 rapid PrintWindow captures of the waiting state all carry
the text (pre-fix runs alternated black frames).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 16:33:41 -05:00
arcattackandClaude Opus 4.8 eb618fe9e6 BT_EXPIRE: tester builds stop working 14 days after build (compile gate)
Distributed test zips go stale and stray.  BT_EXPIRE (CMake option,
DEFAULT ON so a zip can never accidentally ship without it) makes the
exe refuse to start BT_EXPIRE_DAYS (default 14) after its build day:
boot logs the remaining window, an expired build logs + shows a
"test build expired -- ask the operator for the current build" box and
exits.  btversion.h now stamps BT_BUILD_UNIX (UTC-midnight-rounded so
the only-on-change header churns once per day, not every build) +
BT_BUILD_DATE.  Dev is unaffected: every build re-stamps the day.

-DBT_EXPIRE=OFF builds a non-expiring exe; mkdist reads the cache and
marks such a zip "-noexpire" + prints a warning, and the tester README
expiry note is {EXPIRE}-marker-gated like the steam lines.  Second
compile-gate exception after BT_STEAM (convention updated in
glass-cockpit.md).  Quality gate, not DRM -- a clock rollback defeats it.

Verified live: default build logs "test build window: 14 day(s) left";
a -DBT_EXPIRE_DAYS=0 build logs TEST BUILD EXPIRED and parks on the
explanation box (window title checked) without starting the game;
restored to 14 and rebuilt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 13:39:19 -05:00
arcattackandClaude Opus 4.8 3097d269b4 KB: menu-gate bullet updated for the glass desktop default
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 09:18:42 -05:00
arcattackandClaude Opus 4.8 9e85f6dc56 Desktop default platform = GLASS (kills the recurring "old keymap" trap)
Root cause of the keymap whack-a-mole: three input layers exist (PadRIO/
bindings.txt under glass; btinput/CONTROLS.MAP under DEV; the bring-up
drive bridge also under DEV, which clobbers e.g. Alt-reverse every frame),
and DEV was the DEFAULT -- so every launcher that forgot BT_PLATFORM=glass
booted the old stack: the play bats, then play_steam, then the operator
console's local instances, each fixed with the same one line.

Omission can't resurrect it now: with no BT_PLATFORM, desktop boots GLASS
(and the menu on a zero-arg launch -- bare double-click of btl4.exe opens
the mission menu).  BT_PLATFORM=dev explicitly opts into the legacy DEV
profile for debugging.  BT_PLATFORM=pod is the cabinet profile; the real
pod's SETENV.BAT must set it (contract recorded in glass-cockpit.md --
the old "bare boot == cabinet shape" menu-guard assumption is retired).

Verified: no-env mission launch boots GLASS; bare no-env launch opens the
MENU; BT_PLATFORM=dev boots DEV.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 09:18:19 -05:00
arcattackandClaude Opus 4.8 1841c5575e KB: fix the convention line (BT_STEAM is the one compile-gate exception)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 10:40:24 -05:00
arcattackandClaude Opus 4.8 d53fcf61ad BT_STEAM restored as THE compile gate (license) -- default OFF
The unification flattened BT_STEAM into the always-on exe; the user flagged
the constraint that makes it the one legitimate compile gate: the Steamworks
SDK license bars a public release with Steam inside, so a release build must
exclude it entirely.

- CMakeLists: option(BT_STEAM OFF) restored around the Steamworks block --
  an OFF configure touches no SDK headers, no steam_api.lib, no DLL copy.
  BT_GLASS stays retired (always defined); ONE build dir either way.
  Dev checkout: build/ configured once with -DBT_STEAM=ON.
- mkdist: reads BT_STEAM from build/CMakeCache.txt.  Steam-OFF zips exclude
  play_steam.bat + steam_api.dll (explicitly filtering a STALE DLL left by
  an earlier ON build), strip the README steam section ({STEAM} line
  markers), and take a -nosteam name suffix so the flavors never clobber
  (found live: both stamped 4.11.436 and the release zip overwrote the dev
  zip).

Verified both flavors:
  ON  (dev build):  steam smoke degrades to Winsock without the client;
      zip carries play_steam.bat + steam_api.dll + README steam lines.
  OFF (scratch build-relcheck, deleted after): 0 compile errors; exe dir =
      btl4.exe + OpenAL32.dll only; boots + simulates; BT_STEAM_NET=1 inert
      (transport not in the binary); end-to-end zip has zero steam bits and
      a clean README.

KB: context/glass-cockpit.md unified-build section records the license gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 10:40:03 -05:00
arcattackandClaude Opus 4.8 10ed66bd7c UNIFY: one exe -- retire the pod/glass/steam compile split
The three build dirs (build\ pod, build-glass\, build-steam\) were compile-
time splits of the same game; the tax was real: a stale steam exe shipped 2
days behind, the felt keymap flipped with the boot flavor, and cockpit
clicks were silently dead in the pod build (the trigger-config incident).

Now everything compiles into the single build\ exe:
- CMakeLists: BT_GLASS/BT_STEAM options removed; the glass TUs (PadRIO,
  bindings, panel, glass windows, plasma) + FE/console + Steam transport
  compile unconditionally; both macros always defined (seam markers).
- Steam: steam_api.lib linked with /DELAYLOAD:steam_api.dll + delayimp --
  the DLL maps only when SteamAPI is first called, and every call site
  gates on BT_STEAM_NET=1 (BTSteamNet_* short-circuit on steamActive), so
  machines without the DLL run everything but Steam sessions.
- CABINET GUARD (btl4main): the miniconsole menu previously claimed any
  zero-arg launch -- unified, that would hijack the pod cabinet's boot
  shape.  The menu now requires BT_PLATFORM=glass (or BT_FE_MENU=1);
  platform parse moved above the fe_menu_mode computation.  A bare boot
  behaves exactly like the old pod build (DEV profile, wait for egg).
- Bats (play_solo/join/join_lan/play_steam) + the btoperator.py template:
  single build\ path + BT_PLATFORM=glass.  mkdist: one exe (+DLL scan
  picks up OpenAL32 + steam_api).  build-glass\/build-steam\/build-glass2\
  (a stale Jul-20 cache) deleted.

Verified matrix on the unified exe:
  bare zero-arg boot == old pod exe (same DEV profile + egg-load path;
    the segfault on a missing egg is pre-existing, baselined vs the zip exe)
  glass + BT_BTNTEST scripted click -> [cfgmap] ENTER/EXIT (trigger config)
  runs with steam_api.dll ABSENT (delay-load proven)
  glass zero-arg -> miniconsole menu
  2-node loopback MP: 145 ticks each, cross-connected
  BT_STEAM_NET=1 without Steam client -> "staying on Winsock", game runs

KB: context/glass-cockpit.md compile-gates section rewritten for the
unified model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 10:28:51 -05:00
arcattackandClaude Opus 4.8 0e013fd82d KB: trigger-config + keymap unification human-verified
context/glass-cockpit.md: record the BT_PLATFORM=glass/PadRIO click gating
(the dead-clicks root cause), the verified live regroup loop, the [cfgmap]/
BT_BTNTEST diagnostics, and the one-keymap settlement (babf5b1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 09:41:37 -05:00
arcattackandClaude Fable 5 5ba5697a08 Fix glass panel dead-button crash: null-init MessageHandlerSet gap slots (Gitea #18)
Clicking a per-display glass Engineering panel button (0x21 on an Eng page)
hard-crashed via a wild call through an uninitialised pointer (eip=cdcdcdcd
debug / 0x01048748 release), zero btl4 frames above Receiver::Receive.

Root cause is the dense message-handler-table gap hazard (reconstruction-
gotchas #11), the message-side twin of the attribute-table one -- NOT a /FORCE
unresolved external (the glass link log carried only the known-benign mech3.obj
CreateStreamedSubsystem set). Receiver::MessageHandlerSet::Find(id) returns
messageHandlers[id-1].entryHandler for any id <= entryCount with no populated-
check, and Receive() calls it when != NullHandler. The streamed Eng-page .CTL
dispatches subsystem msg id 3/0xb to whatever subsystem is shown; a weapon
(Emitter) registers only PoweredSubsystem 4-8 + MechWeapon 9-10, so id 3 is a
gap below entryCount 10. Build did new HandlerEntry[entryCount] and left gaps
uninitialised -> (this->*garbage)(msg). The 1995 binary has the identical non-
zeroing new[] (part_002.c Build) and survived only on fresh-heap-zero luck: a
weapon receiving id 3 was always meant to IGNORE it (id 3 = a Condenser/
Reservoir action in a different subsystem branch; the uniform Eng-page button
template makes buttons for unimplemented actions authentically inert). The new
per-display windows just made the id reachable live.

Fix (engine, class-wide, faithful): Build now null-inits every slot
(entryID=0/entryName=""/entryHandler=NullHandler) before copying inherited /
placing supplied, so a gap is deterministically NullHandler -> Receive drops it
(the authentic "Receiver ignores unhandled messages"), with an empty never-NULL
name so the name-based Find() strcmp can't deref a gap. Correct dispatch
contract, not a glass-path guard -> protects the whole dead-button class (#14).

Also lands the [glasswin] CLICK crash-forensics log (names the button before
dispatch).

Verified under cdb: click-soaked every MFD bank (Engineering/L+R Weapons/Heat/
Comm, ~130 clicks through Quad<->Eng page cycles) -> zero AVs, reconstructed
mapper preset selects still fire ([mode] preset (1,1)/(2,1)...); pod build +
solo mission un-regressed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 15:59:37 -05:00
arcattack bc5ac3a0db Merge glass-per-display-windows: per-display cockpit windows (Cyd, BT_GLASS_PANELS)
One window per pod display (Heat/Engineering/Comm/Weapons x2/radar +
Flight Controls), each carrying its surface with its RIO button bank
arranged pod-faithfully; CPU-expanded surfaces, buttons under the
imagery.  Runtime gate BT_GLASS_PANELS (=0 = the legacy single panel).
Merged on top of today's landed fix pile (audio-crash, experience,
parallax); the L4PADRIO overlap with the gait-detent commit resolved
preserving BOTH (his window refactor + our at-rest band snap).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

# Conflicts:
#	context/glass-cockpit.md
2026-07-20 14:52:38 -05:00
CydandClaude Opus 4.8 f338595685 Glass cockpit: per-display windows (BT_GLASS_PANELS)
Break the desktop glass cockpit's secondary displays out of the single
combined pad panel + D3D gauge strip into ONE window per pod display, each
carrying that display's surface with its RIO button bank, arranged
pod-faithfully around the main view.  New TU engine/MUNGA_L4/L4GLASSWIN.*
(BT_GLASS-only), selected by the -platform glass preset via the new runtime
gate BT_GLASS_PANELS (=0 falls back to the legacy single panel + dock).

- Surfaces are CPU-expanded from the shared gauge buffer
  (SVGA16::ExpandPlaneToBGRA, no D3D) and StretchDIBits'd in -- the D3D
  dev-composite path (dock / window / overlay) stands down while active.
- Buttons sit UNDER the imagery: big hidden click targets that reach into
  the display, with only a small protruding edge showing as a lamp light.
  Red MFD banks tile the top/bottom; radar rails run the sides + a bottom
  row; a Flight Controls window hosts the no-display banks.
- Windows: Heat / Engineering / Comm / Left Weapons / Right Weapons / radar
  + Flight Controls; edge-to-edge (no in-client margin/title; OS caption
  labels each).
- Fix blank surfaces: application/ghWnd are duplicate-defined and bind
  non-deterministically under /FORCE, so the fresh L4GLASSWIN TU read NULL.
  Reach the renderer/window via BTResolveGaugeRenderer/BTResolveMainWindow
  defined in btl4main.cpp off the real btl4App/hWnd pointers.  New gotcha:
  reconstruction-gotchas.md S6 duplicate-GLOBAL corollary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 14:43:40 -05:00
arcattackandClaude Fable 5 6ad1e074cc Glass lever: port the pod-build GAIT DETENT to the PadRIO throttle slew
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>
2026-07-20 10:57:34 -05:00
arcattackandClaude Fable 5 1535e3e48d Glass 'keymap regression' triage: stale build-glass exe, not code — rebuild + full re-verify
The live-reported glass regressions (arrows dead / lever+detent missing /
V+J/K/L+N inert / weapons into the ground) were build-glass/Release/btl4.exe
built at 07:47 running the pre-5dd3536 image: no suppression tables, no V/J/K/L
keys, unpinned RIO ids.  content\bindings.txt already carried the merged map
(arrows = W/S lever + A/D pedals) and parsed clean.  Clean rebuild closed every
symptom; SendInput re-verification: lever holds on release + stops at zero,
LEFT/RIGHT turn in MID (wire-sign correct), V toggles with BT_SHOT pair, level
boresight at spawn, R/F elevation drive; pod build rebuilt + smoked un-regressed.

Code delta: BT_MPPR_TRACE [mppr-c] line enriched with mode/pedals/stick/turn/elev
so one env-gated line proves the whole control surface.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 10:36:16 -05:00
arcattackandClaude Fable 5 5dd35365c7 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>
2026-07-20 08:35:55 -05:00
CydandClaude Fable 5 75f136e9ff KB: step-4 closeout -- Steam wire documented, live-test procedure, router
docs/GLASS_COCKPIT.md gains the step-4 ledger (seam/transport/lobby + THE
FAKEIP LESSON); NEW context/steam-networking.md topic (+ router row) and
docs/STEAM_TEST.md (the 2-machine live procedure = the step-4 exit criterion
on user hardware).  context/glass-cockpit.md: steps 3+4 status.  Final purity
re-check: pod build clean, forced-walk mission runs, ZERO glass/steam tags in
the pod log.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 00:05:51 -05:00
CydandClaude Fable 5 82b53b7ad7 KB: step-2 closeout -- verification matrix + ledger detail (glass cockpit)
docs/GLASS_COCKPIT.md gains the 2b-2e detail + the full verification matrix
(purity build + seam audit, pod forced-walk, 2-node loopback MP via btconsole
relay on the POD build -- both nodes reach the running mission, PadRIO
authentic-path input, panel-click survival).  context/glass-cockpit.md status
updated: step 2 DONE [T2]; steps 3-4 pending.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 22:34:20 -05:00
CydandClaude Fable 5 b26e8205e3 Controls: the .CTL positional-id off-by-one -- CONFIRMED live and FIXED (step 2a)
The streamed L4 mapping resource carries the binary's positional attribute ids
(stick=3, throttle=4) but MechControlsMapper chained from Subsystem::NextAttributeID
== 2 -- every streamed record resolved ONE MEMBER LATE (attr 4 -> pedalsPosition,
verified via the new permanent BT_CTRLMAP_LOG diagnostic in CreateStreamedMappings).
A latent real-pod bug: the serial RIO throttle would drive the pedals member; the
dev keyboard bridge masked it. Fix per the mechweap/mech attrPad idiom: ids pinned
to the binary numbering, id-2 gap padded, static_assert-locked. Torso + weapon
chains verified already aligned. Regression: BT_FORCE_THROTTLE headless walk clean
(speedDemand=61.501 through authentic InterpretControls, gait cycles, no faults).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 21:59:51 -05:00
CydandClaude Fable 5 3f49915d76 Build: the BT_GLASS/BT_STEAM compile gates -- glass-cockpit dev-layer scaffolding
Step 1 of the desktop developer layer: option(BT_GLASS OFF) + option(BT_STEAM OFF)
(BT_STEAM requires BT_GLASS, configure-time FATAL_ERROR), defines flow to all three
targets via BT_DEFS. Defaults build = the pure pod game, verified: 3-config configure
matrix + negative test + defaults Debug build (known mech3 /FORCE externals only).
New topic context/glass-cockpit.md + ledger docs/GLASS_COCKPIT.md; router rows added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 21:48:35 -05:00