Commit Graph
10 Commits
Author SHA1 Message Date
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