1841c5575ebd3e232933386775fd7ffc53b5e591
261
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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>
|
||
|
|
babf5b1e77 |
Keymap: sync CONTROLS.MAP to the bindings.txt layout (one map everywhere)
The user standardized on the "newer" layout (numpad aim cluster, arrows = throttle/pedals, Alt = reverse thrust). That layout lived only in bindings.txt (PadRIO, GLASS-profile boots); CONTROLS.MAP (btinput, DEV/pod boots) lacked the numpad cluster entirely and had NO reverse binding -- so the felt keymap flip-flopped with the boot flavor. Added to CONTROLS.MAP + the byte-identical compiled-in default profile (btinput.cpp sDefaultProfile): Alt -> button 0x3F (throttle-head REVERSE THRUST) NumPad8/2 -> JoystickY aim up/down NumPad4/6 -> JoystickX torso twist NumPad7/9 -> pedals (turn) NumPad5 -> AllStop Shift -> throttle up (alt) Parse-verified: [input] loaded 61 binding(s) from CONTROLS.MAP, no warnings. Both engines now carry the same layout, so DEV/pod boots (MP join bats) and GLASS boots (play_solo) feel identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4215b98655 |
Trigger config: root-cause the dead cockpit clicks (missing BT_PLATFORM=glass)
The user held the MFD PROGRAM button and nothing happened (weapons kept
firing). Diagnosis, verified end-to-end:
- The cockpit mouse handler DID register the click ([cockpit] CLICK addr=0x8
press) and 0x8 IS a streamed PROGRAM element (EVENT msg 0x9 -> weapon,
Mfd1 quad page mask).
- But every launch ran the DEV platform profile (no BT_PLATFORM) ->
L4CONTROLS=KEYBOARD -> no PadRIO instance -> PadRIO::SetScreenButton
no-ops (activeInstance NULL) -> the press never entered the RIO queue ->
ConfigureMappables (id 9) never dispatched -> the mode never flipped ->
fire keys kept firing. The config machinery itself was never broken.
- With BT_PLATFORM=glass (the GLASS profile: L4CONTROLS=PAD -> PadRIO), the
full chain now proves out headlessly:
[btntest] PRESS 0x8 -> [cfgmap] ENTER session on ERMLaser_1
mode 0x450421 -> 0x448421 (NonMapping 0x10000 -> Mapping 0x8000)
[btntest] RELEASE 0x8 -> [cfgmap] EXIT (mode restored)
Landed:
- mechweap.cpp: [cfgmap] BT_FIRE_LOG diagnostics in the real
ConfigureMappables/ChooseButton handlers (they were silent -- the G-key
harness had logs but the authentic button path had none).
- L4PADRIO.cpp: BT_BTNTEST="addr,pressPoll,releasePoll" scripted screen-
button harness through the REAL click seam (EmitButton -> RIO queue ->
manager drain -> buttonGroup mapping) for headless verification.
- play_solo.bat: prefer the glass build when present AND set
BT_PLATFORM=glass so PadRIO exists and cockpit clicks work.
Side finding (the user's keymap "flip-flop"): CONTROLS.MAP (btinput, DEV/pod
profile) and bindings.txt (PadRIO, GLASS profile) are two parallel binding
engines; which one runs depends on the platform profile, so the felt keymap
changes with the boot flavor. Unification pending (user decision).
Awaiting live verification of the full hold-PROGRAM + tap-fire regroup flow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
89b4f53046 |
Gauges: trigger-config joystick is LIVE -- @004c6ee0 is LinkToEntity, not SetColor
The weapon panel's btjoy trigger-config display (ConfigMapGauge) never rendered because the reconstruction labelled @004c6ee0 as "SetColor(int)" and, finding no caller, concluded the gauge was authentically dormant (task #6), gating it behind a BT_CONFIGMAP dev env. The user challenged this (the PROGRAM/TRIGGER CONFIG button implies the display visualizes your config). Re-verification found the old analysis failed twice: the "no caller" search looked for direct calls to a VIRTUAL, and its slot math used the wrong vtable copy. The real ConfigMapGauge vtable @0051a1b8, matched against the T0 GaugeBase virtual roster (GAUGE.h), pins @004c6ee0 at slot 9 (+0x24) == GaugeBase::LinkToEntity, and @0x94 is the linkedEntity -- the Execute gate is "not yet linked", not "disabled". The engine broadcasts LinkToEntity(viewpointEntity) to every gauge at viewpoint bind (APP.cpp:1277 -> GaugeRenderer::LinkToEntity GAUGREND.cpp:3011), arming the gate -- the joystick DOES render in the shipped game, showing per-trigger mapping state (solid = mapped, matching the DOSBox reference). Port fix: SetColor(int color) -> LinkToEntity(Entity*), color -> linkedEntity, BT_CONFIGMAP dev enable deleted (the authentic path lights it). Render-verified with no env override: the joystick + mapped-trigger lamp draw on weapon panels. KB swept per the correction mandate: gauges-hud.md, decomp-reference.md, open-questions.md, GAUGE_COMPOSITE.md, VEHICLE_SUBSYSTEMS.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4fbc911f55 |
Gauges: fix temp/status bar colours (black remainder, not green)
Follow-up to the tiled-render fix: the two colour params were swapped. The binary caller (@004c8269) pushes the colours 0xff then 0; they land at [this+0xA0]=fillColor (0xff green) and [this+0xA4]=backgroundColor (0 black). The binary uses the GREEN for zone 1 (tile SetColor / dots) and zone 2 (the over-degrade fill), and BLACK only for zone 3 (the unfilled remainder [valPix,width]). The port had them reversed -- zone 3 used fillColor -- so the whole remainder of the bar (most of it, since valPix is small when cold) rendered solid green instead of black. Swapped: zone 1 + zone 2 = fillColor (green), zone 3 = backgroundColor (black). Render-verified: black background + green dotted tick scale + a green hatch fill growing from the left, matching the reference. Both builds clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4d4436ed03 |
Gauges: temp/status bar tiles the striped pattern from x=0 (HorizTwoPartBar)
The per-weapon TEMP/STATUS bar (HorizTwoPartBar) rendered as a solid block starting at the warn-threshold pixel -- it "started in the middle" and was never striped. The port's Execute had been rewritten with DrawFilledRectangle and never used the interned tile image, unlike the (correct) VertTwoPartBar which tiles via DrawTiledBitmap. Restored the binary's three-zone render (disasm @004c4340), along X: [0, warnPix) DrawTiledBitmap(tileImage) -- the striped/dotted pattern [warnPix, valPix) backgroundColor solid (only when value > low) [valPix, width) fillColor solid So the bar now fills the striped tile from the beginning and matches the reference (hatched fill block on the left + dotted tick scale). Render- verified vs the DOSBox reference capture. Both builds clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
66dcdf23db |
Gauges: ammo count stencils out of the fire-ready dot (BallisticWeaponCluster)
The base-page missile/round count sat on the fire-ready "dot" (the cluster image blitted green when charged) as green digits on an opaque black box -- a messy dark rectangle punched into the solid green dot. The binary swaps the ammo count's colours with the dot state. Verified from the decomp: BallisticWeaponCluster overrides DrawWarningLamp (@004c9b50) -- it chains the base (@004c932c, which blits the dot in on-colour 0xff / off-colour 0) and then calls the ammo NumericDisplayInteger's SetColors: dot ABSENT -> SetColors(0, 0xff) == green digits on black dot PRESENT -> SetColors(0xff, 0) == BLACK digits cut out of the green dot so the count is always legible (a stencil against the solid dot -- the same black-on-green look as the loop/generator squares). SetColors ForceUpdate()s the numeric so it repaints over the freshly drawn dot. The port had only the base WeaponCluster::DrawWarningLamp (non-virtual, no swap), so the count stayed green-on-black and clashed with the green dot. Made DrawWarningLamp virtual and added the BallisticWeaponCluster override. Render-verified (STREAK "0024" now black-cut-out of the solid dot); energy weapons (no ammo count) unaffected. Both builds clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f2eaf3d0ac |
Gauges: convert generator-voltage bar to named-member bridge
The eng-page generator-voltage bar (evolt.pcc, ScalarBarGauge @004c721c) and the MyomerCluster seek-voltage graph were fed by GeneratorVoltageConnection, which walked the same dead path as the generator-letter lamp: ResolveLink(AttributePointerOf(subsystem,"InputVoltage")) -> read resolved +0x1DC == Generator::outputVoltage. The BT_DEV_GAUGES audio attribute rows shifted the chained attribute ids so AttributePointerOf no longer lands on voltageSource@0x1D0 -> the bar always read 0. New BTGeneratorVoltage(subsystem) bridge (powersub.cpp) reads the NAMED member via PoweredSubsystem::ResolveVoltageSource()->Generator::MeasuredVoltage() (outputVoltage@0x1DC), bypassing the attribute table -- same pattern as BTPowerSourceFrame/BTCoolingLoopFrame. Both GeneratorVoltageConnection callers (the ScalarBarGauge param + the MyomerCluster seekValue) now pass subsystem_in. [voltfeed]-verified: Myomers -> 10000V from GeneratorD (was 0 on the dead path). Both pod + glass builds clean. Eng-page render awaiting live playtest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e634709e5d |
Gauges: fix blank per-weapon loop/generator lamps (4A/1B/5D boxes)
The SubsystemCluster per-weapon MFD panels render two image-strip lamps in the TEMP/STATUS area: the cooling-loop number (btploop, 1..6) and the generator letter (btpbus, A..D). Both drew as completely empty boxes. Three stacked databinding bugs, all now fixed: 1. Color drop (btl4gau2.cpp): AnimatedSubsystemLamp/AnimatedSourceLamp ctors dropped the bg/fg color params the binary passes (bg=0xff,fg=0, same as the neighboring temp bar) and hardcoded 0,0 -> OneOfSeveral:: Execute did SetColor(0)+DrawBitMapOpaque(0), i.e. black-on-black. Restored 0xff,0 (verified vs @004c70a4 / caller SubsystemCluster @004c8140). 2. Shadow-field trap (btl4gau2.hpp, gotcha #2): both lamp classes re-declared `int selected;` while already inheriting it from OneOfSeveral (@0xAC). The CoolingLoop/PowerSource connection wrote the derived shadow copy while OneOfSeveral::Execute read the base @0xAC (always 0) -> every lamp stuck on frame 0 ("OFF"). Removed the redeclarations so &selected binds the inherited member. Loop numbers now render (verified: PPC->4, Myomers->5, ERMLaser->1/6, etc.). 3. Attribute-table shift (powersub.cpp + btl4gau2.cpp, gotcha #8): the generator-letter lamp resolved its source via ResolveLink(AttributePointerOf(subsystem,"InputVoltage")), but the BT_DEV_GAUGES audio attribute rows shifted the chained attribute ids so AttributePointerOf no longer landed on voltageSource@0x1D0 -> the link always resolved to 0 (OFF) even though the master PoweredSubsystem ctor DID bind voltageSource to its Generator. New BTPowerSourceFrame bridge reads the NAMED member via PoweredSubsystem::ResolveVoltageSource() and returns Generator::generatorNumber (@0x1E0), bypassing the attribute table -- the same pattern as the BTCoolingLoopFrame fix. Generator letters now render (verified: PPC_1->GeneratorA="A", Myomers-> GeneratorD="D", SRM6->GeneratorB="B"). Result: the "4 A / 1 B / 5 D" boxes render with both the loop number and generator letter, matching the reference. Kept BT_LOOP_LOG-gated [loopfeed]/[busfeed] feed diagnostics; both pod + glass builds clean. Awaiting live playtest verification. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
cbc5ff9532 |
Wire ToggleCooling (msg 3): restore the coolant on/off button + handler chain
Coolant priority (Lynx: weapon MFD -> Display -> Coolant) was unreachable: the ToggleCooling handler (@004ad6f8, HeatableSubsystem table @0x50E41C id 3) was undefined AND the handler chain skipped it -- PoweredSubsystem::GetMessageHandlers chained straight to the Receiver root, bypassing HeatSink/HeatableSubsystem, so a weapon's dispatch never saw id 3. Reconstructed ToggleCooling from the disassembly (tools/disas2.py 0x4ad6f8): a per-subsystem coolant on/off TOGGLE -- novice-locked (owner->BTPlayer-> roleClassIndex+0x274==0, the same guard as MoveValve), press-only, then flip coolantAvailable(+0x134) 0<->1 and coolantFlowScale(+0x15C) 0.0f<->1.0f. Cutting a system's cooling frees the shared loop for the rest -- the emergent "coolant priority" (the mechanism is a toggle, not a multi-level cycle). - heat.hpp / heatfamily_reslice.cpp: HeatSink::ToggleCoolingMessageHandler (id 3) + HeatSink::GetMessageHandlers. Registered at HeatSink (the abstract HeatableSubsystem never instantiates; every concrete heatable subsystem is a HeatSink, where the coolant fields live) -- identical coverage to the binary's base-table registration, no downcast. - powersub.cpp + Condenser/Reservoir: chain their handler sets through HeatSink::GetMessageHandlers so id 3 reaches every heatable subsystem. - mech4.cpp: BT_COOLTOGGLE_TEST scripted inject (dispatch id 3 to the first weapon) for verification. Verified (BT_COOLTOGGLE_TEST + BT_COOL_LOG, expert egg): "[cool] PPC_1 ToggleCooling reached" then coolant OFF(flow 0.0) <-> ON(flow 1.0) each press -- chain routes, handler toggles, novice guard honored. Both build/ + build-glass/ compile clean; existing ids 4-8 unaffected (found before id 3). NEXT: the #2 MFD button routing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
89ddd49283 |
Add [ammo] fire trace (BT_AMMO_LOG); reproduces the missile FailureHeat->NoAmmo brick
Playtest: "lost missiles mid-fight, no pips, sad noise -- out of ammo?" The default build didn't trace ammo, so it couldn't be answered from the log. Added a [ammo] trace (env BT_AMMO_LOG) at the four ProjectileWeaponSimulation decision points -- FIRED+rounds-left, gate1/gate2 dry-transition edge, dry-fire blip, and denial -- plus a public AmmoBin::GetAmmoCount() const getter. Reproduced the actual cause: NOT ammo depletion. The SRM6 trips gate 1 FailureHeat (heatLvl=2) after ~5 volleys and latches into the permanent NoAmmo roach-motel with 19 rounds still in the bin: [ammo] SRM6_2 FIRED, rounds left=19 [ammo] SRM6_2 -> NoAmmo (gate1): destroyed=0 failHeat=1 heatLvl=2 mechDisabled=0 Logged as the concrete instance of the heat-economy open question (open-questions.md). Trace-only; no behavior change (all output gated on BT_AMMO_LOG). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
129b27499d |
Cockpit surround: composite the gauges around the 3D view (default desktop layout)
Under BT_DEV_GAUGES the DEFAULT is now the pod-faithful cockpit surround: the 3D world CENTERED with the six gauge surfaces composited AROUND it in the single main window at 1/2 native scale, plus clickable RIO button lamps. Iterated as a visual reference with the user, then landed in the engine. - L4VB16.cpp/.h: BTCockpitLayout + BTCockpitComputeLayout (layout single source of truth, computed from the backbuffer canvas); BTDrawCockpitPanels (band fill -> button lamp quads -> 9-entry surface loop, phosphor-green mono / amber palette sec, Eng-sibling slot map -> GDI-atlas flight labels; D3DSBT_ALL state-block isolation = the floating-rocks trap); BTCockpitMouseDown/Up (client->bb hit-test + glass press/release/right-latch contract); BTApplyWorldViewport + DevGaugeDocked cockpit branches; shared BTLampBrightnessOf inline. Hooked at the top of BTDrawGaugeInset (before BT_SHOT, so shots include it). - btl4main.cpp: glass preset (cockpit default, per-display panels opt-in); mode resolution -> gBTGaugeCockpit with work-area-clamped window sizing (-res = view size); WndProc WM_L/RBUTTON routing. - L4VIDEO.cpp: BTWorldAspectOf cockpit branch (view rect on-screen aspect). - L4VIDRND.cpp: CameraShipHUDRenderable::Render made viewport-relative. Buttons = the L4GLASSWIN address banks x0.5 (Heat 0x2F, Mfd2 0x27, Comm 0x37, Mfd1 0x0F, Mfd3 0x07 red; radar rails 0x10-0x1B yellow; flight 0x38-0x47 blue, labeled). Full rect is the hit target; the surface draws over it so only the lamp edge shows. Precedence: BT_GLASS_PANELS=1 stands cockpit down > BT_COCKPIT=1 > _WINDOW/_DOCK opt-out > cockpit default; BT_COCKPIT=0 = dock-bottom. Green tunable via BT_COCKPIT_TINT. Renders in ALL builds; only the PadRIO click/lamp seam is BT_GLASS-gated. Verified (awaiting playtest): both build/ + build-glass/ compile (0 error C); glass BT_SHOT matches the mockup; 500-click storm during a live mission survived (Gitea #18 Receiver-gap fix holds); BT_COCKPIT=0 dock-bottom unregressed; BT_GLASS_PANELS=1 stands cockpit down; pod build renders the panels. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
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 |
||
|
|
70eea6c1a4 |
Boresight parallax FIXED + the #4/#5 verdict instrumentation (Gitea #16, #4, #5)
PARALLAX (#16): the pick/fire ray was anchored at mech.y+5.0 (a port improvisation) while the sight line ran from the eyepoint (y=6.23) -- two parallel rays whose constant offset grew into the reported low-miss as range closed (measured ry +0.072 @50u -> +1.54 @2.7u). The decomp's sight and pick share the eye origin (HudSimulation @4b7830 chain). Fix: the viewpoint mech's cockpit eye owns the aim-camera publish in BOTH views, origin = its own eye translation; leveling + deliberate elevation untouched; chase view now converges to cockpit ballistics (V cannot change where shots land). After: pick pinned to the crosshair (ry <= 5e-6) from 50u to point-blank; 26 laser + 8 missile center-mass hits at 3/4-screen. Awaiting the reporters' approach-test. VERDICT INSTRUMENTATION (#4 closed authentic, #5 verdict posted): BT_RANGE_LOG per-frame pick tracing + BTGroundRayHitExact analytic cross-check (0/8000 arena fall-throughs; cavern 6/8400 single-frame grazes -- the 'crazy sliding' is the authentic world-pick + 500 m/s slide over depth discontinuities); BT_AUD_TAIL StopNote/fade timing + BT_FIRE_PULSE single-shot driver (the energy 'buzz' is the AUTHORED charging loop: crescendo through recharge, 1.309s authored release, measured within one frame). CAVERN.EGG: solo cavern test egg. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d7158f1f82 |
Experience levels WIRED: the egg field drives the simulation tier (Gitea #2)
BTPlayer's master ctor now seeds the flag block from btMission->ExperienceLevel() + AdvancedDamageOn() (accessors from the SURVIVED 1995 BTMSSN.HPP [T0]) instead of the NULL-stubbed scenario role -- the egg experience knob works for the first time. Both switch slot errors fixed vs the binary @4c0bc8; phantom btMission duplicate member removed (+0x1f8 IS Player::playerMission). SEVEN gate stubs unified onto the real flags (two found empirically): HeatModelActive, OwnerAdvancedDamage, LiveFireEnabled, ControlsAllowLights, powersub @4b0efc, emitter's file-local FUN_004ad7d4 + FUN_004ac9c8 stubs. FUN_004ac9c8 family swept (incl. a live raw-offset databinding trap in MechSubsystem::IsDamaged). Flag block renamed to its true semantics (simLive/heatModelOn/...), scoring-era names kept as comments. Includes the #5 [aud-tail] diag in emitter.cpp. ALL 19 shipped eggs say experience=expert -> default behavior UNCHANGED (per-tier verified: novice/standard = no heat/jams, authentic; veteran/ expert = today's exact behavior). Real delta: egg advancedDamage=1 now reaches player+0x264/0x268 (was hardwired 0). Follow-up flagged: the glass FE defaults empty experience to veteran (one tier below shipped). Awaiting human verification. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
4a38fba2c9 | Merge remote-tracking branch 'origin/master' into glass-cockpit | ||
|
|
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>
|
||
|
|
2cbfaef177 |
FE: the full authentic BattleTech mission-option catalog (TeslaSuite console)
The mission-control menu was missing player experience + many options. Added the complete set recovered from the Mac 4.10 operator console's BT:: adventure tree (TeslaSuite/410console/.../Console.ini) -- the tag= values are the egg fields; cross-checked vs the port spec + reference/cavern.egg: NEW groups: SCENARIO (Free For All / No Return respawn role), EXPERIENCE (novice/standard/veteran/expert), BADGE (7 team emblems), PATCH (8 badge colours), DROP ZONE (one..five), ADV. DAMAGE (on/off). CORRECTED: TIME (evening, not 'dusk'); COLOR (full 7: +Brown/Green/Grey/Tan, Red->Crimson, Gray->Grey); MECH (full 18 with console friendly names -> tags, was 8). btl4fe.hpp: BTFePilot gains experience/badge/patch/dropzone/advancedDamage; BTFeMission gains roleKey/roleModel. The egg writer emits the selected values (were hardcoded expert/VGL/Yellow/one/advancedDamage=1); No-Return role model = the game's 8.3 resource 'noretun'. 5-column layout (mission | mech | pilot | emblem/patch | session+controls); loadout persists across the relaunch (fe_last.ini, all 14 groups). Verified: menu drives (click Expert+Davion -> egg experience=expert badge=Davion, persisted), and the generated egg parses + runs a full mission (no egg errors, drive 61.501). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0d032029a0 |
Fix 2 (Gitea #12): gauge tree survives the in-session mech re-stream
The pre-launch host drop/rejoin re-stream re-creates every mech at mission launch; the LOBBY-built gauge tree kept attribute pointers into the freed first-stream mechs, every widget Execute AV'd, and the BT_DEV_GAUGES SEH guard (Gauge::GuardedExecute, GAUGE.cpp:618) disabled each one PERMANENTLY (rate=0, no rebind) -- the frozen dev-gauges window of incident #12. The authentic engine flow only tears the tree down at mission transitions (Application::Shutdown -> gaugeRenderer->Shutdown() -> ShutdownImplementation -> Remove(0), APP.cpp:787 / GAUGREND.cpp:3264); the re-stream bypassed it. Fix: BTL4GaugeRenderer::TearDownForViewpointRestream() performs the ENTITY-BOUND half of that same ShutdownImplementation sequence, in order (gaugeAlarmManager->RemoveAllAlarms() -> Remove(0) -> moving/static entity grid Clear), keeping the mission-scoped state (warehouse, graphics ports, interpreter, controls-owned L4Lamps -- RemoveAllLamps would delete them behind the buttonGroup's &lamp->automaticValue registrations). Called from BTL4Application::MakeViewpointEntity before ConfigureForModel("Init") on any viewpoint RE-make -- that handler runs once per stream (the incident log's [ctrlmap] installing x2) -- so the tree rebuilds bound to the NEW mech via the renderer's existing lazy build. Sentinel: "[gauge] viewpoint re-stream: tearing the gauge tree down for rebuild". Verified with the 2-instance relay harness (port 15600, MP_RELAY.EGG, the incident sequence): join A (dev gauges) + B, kill B by exact PID at WAITING FOR OPERATOR LAUNCH, rejoin B, operator launch. Pod A re-streamed (2x ctrlmap install, 4x zonebuild), logged the sentinel, and post-launch the docked composite is fully live (mission clock/heading/radar advancing between BT_SHOT frames) with ZERO [gauge-fault] DISABLED lines (the incident had 11). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2edde29671 |
Fix 1 (Gitea #12): disasm-exact ProjectileWeaponSimulation @004bbd04 + the dirty-bit sweep
Replace the RivetGun-modeled ProjectileWeaponSimulation body with the fully recovered binary machine (capstone disasm, every branch address-cited): - gate 1 @4bbd36: simulationState@0x40==1 (destroyed) || heatAlarm==FailureHeat || owner disabled -> recoil=rechargeRate + alarm 7 (was: simulationFlags==1) - gate 2 @4bbd71: bin alarm 2/3 or bin destroyed -> alarm 7 re-pinned per frame - Loaded @4bbec2: DENIED shots (viewFireEnable off / no owner target) blip SetLevel(4);SetLevel(2) and STAY LOADED, no ammo pull (the old FireWeapon early-returns under an unconditionally-cycling caller faked a full firing pip cycle -- the "missiles cycle but never launch" incident mechanic); the ammo pull is bin->FeedAmmo @4bbee6 in the CALLER; both updateModel|=1 marks (@4bbf05/@4bbf4c = ForceUpdate) bracket FireWeapon; recoil set @4bbf51 - Loading @4bbdd2: recoil bleeds ONLY here at electrical Ready and CLAMPS at 0 (it ran to -114 before); bin Loaded -> weapon Loaded - 7 @4bbe4d: binary-faithful roach motel (re-asserted unconditionally; recovery = ResetToInitialState only) - slot 17 @004b9c9c reconstructed as MechWeapon::ComputeOutputVoltage (rechargeLevel=(rechargeRate-recoil)/rechargeRate, Emitter overrides with @4ba738) and called from Loading/7 -- the launcher recharge dial ANIMATES (the old "authentically static" claim was wrong) FireWeapon bodies stripped to heat+spawn only per @004bcc60 (no view gate, no ConsumeRound, no recoil); ConsumeRound retired (not a binary method). Gotcha #20 sweep (all sites disasm-verified): simulationFlags|=0x1 == engine DelayWatchersFlag (audio watchers muted forever) removed everywhere -- projweap/mislanch fire marks, ProjectileWeapon::ResetToInitialState (@4bbb47 = updateModel|=1); Emitter::SetDirty retired and split per binary site into ForceUpdate() (@4bafaa/@4ba55d) vs ExecuteOnUpdate() (@4ba99a/@4ba943); Emitter fault gate GetFlags()==1 -> simulationState==1 (@4baab9). AmmoBin::GetAmmoState() accessor added (named-member read of ammoAlarm@0x1A8). BT_LOOK_TEST=<frame> scripted verify added (mech4.cpp): holds the rear-view button 300f on/300f off to drive viewFireEnable headlessly. Verified solo ARENA1 (bhk1, autofire): 54-60 [projectile] PUSH + impact smoke; look-back denial window = trigger pulses, state stays 2, zero launches, resumes on release; enemy (disabled) mech launchers pinned 7 + full recoil; STREAK-6 recharge dial sweeps across BT_SHOT frames; lasers fire throughout; zero gauge faults. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
63b168cb92 |
SeekVoltageGraph: full reconstruction -- the eng-page POWER graph + top-box eraser (Gitea #11)
The #10 audit's one WRONG: the port Execute was a bring-up no-op, so the emitter/myomer engineering pages never erased their top data box -> stale sibling-page ghosts on the shared Eng bit-plane (SYSTEM 10 PPC showing the Streak ammo box, etc). Full faithful widget landed: - btl4gau2: ctor/BecameActive/Execute/clear/ticks/cursor recovered from the capstone disasm of @004c6798/@004c6920/@004c6934/@004c6be4/@004c6c30/ @004c6c6c (Ghidra dropped every x87 arg). Plot: v=0..12000 step 1200, x=Round(response(v)*230), y=Round(v*(1/12000)*187) (ld80 @004c6bd0/@004c6d74 = exactly 1/12000); change-test samples the response at 12000V vs the 9999 activation sentinel; XOR op for tick/cursor move-by-redraw; destroyed branch centres edestryd.pcc and revives via own vtbl+0xC (BecameActive, slot 3 of PTR_0051a1fc -- vtable-dump verified). - The vtbl+0x3C sampler identified from the binary vtables: Emitter slot 15 @004bb42c = sqrt(P(v)/2.0e8), P @004bb3f4 = damageFraction*v^2*0.5* energyCoefficient; Myomers slot 15 @004b8f94 = sqrt(AvailableOutput(v)*3.6/ 350). FUN_004dd138 == sqrt (part_015.c:4026): myomers' fabs reading corrected, GetSpeedReading renamed SeekVoltageResponse. Port dispatch via complete-type bridges BTSeekVoltageSample/BTMyomersSeekSample + BTSubsystemDestroyed (databinding rule). - Emitter's AUTHENTIC attribute table recovered (binary @0x511dd4, ids 0x1D-0x25) and published: Laser*/Seek*/OutputVoltage@0x414 (currentLevel, RAW volts -- the live-cursor feed). Field renames per the table: 0x3F8 minSeekVoltageIndex / 0x3FC maxSeekVoltageIndex (static_assert-locked). The MechWeapon 0x1D OutputVoltage PORT ALIAS retired (binary table ends 0x1C; Find walks lowest-id-first, the alias shadowed the authentic row). PPC::DefaultData -> Emitter::GetAttributeIndex(). - mech4: BT_PRESET_HOLD=<n> (freeze the #9 preset cycler after n pulses) for steady-state pixel verification; BT_SEEK_LOG diag. Verified live (BLH, autofire): both #10 repro pairs held ghost-free for minutes (SYS09->SYS10 PPC, SYS02->SYS04 ERMed, SYS05->SYS06 Myomers); curves draw with live cursors; one replot per activation; quad panels/J-K-L/sec panel un-regressed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
904c75aff9 |
Gitea #10 audit fixes: HUD attr table re-based (@5110b8, one-record shift) + BT_SHOT dock capture
- hud.hpp/hud.cpp/CLASSMAP.md: the binary HUD AttributePointers table starts at @5110b8 with a FULL id-3 FlickerRate record (the old transcription read it as a label and shifted every offset one slot). Re-based member names/offsets: rotationOfTorsoHorizontal@0x1DC (<- Torso+0x1D8 twist), limits@0x1E0/0x1E4, speed@0x1E8, rangeToTarget@0x1EC, @0x1F0 unbound, Visible@0x1F4 (init 1), Lock@0x1F8 (init 0), HotBoxVector P3D@0x1FC, ThreatVector P3D@0x208, CompassHeading Scalar@0x214 (= yaw euler[0] + twist, HudSimulation :5676). Cross-checked vs ctor @004b7f94 + HudSimulation @004b7830 (Lock rule writes @0x1F8 :5622/:5633; range default 1200 + 500 m/s slide @0x1EC). SetCompassHeading renamed SetThreatVector (@004b7810 writes @0x208 = the threat vector; no port caller). No behavioral change: the reticle feeds via port globals and the CFG never binds HUD attrs. Resolves the gauges-hud open question (Gitea #10 entry b). - L4VIDEO.cpp: BT_SHOT capture moved AFTER BTDrawGaugeInset -- the 2026-07-18 reorder had left it before the dock blit, so BT_DEV_GAUGES_DOCK screenshots silently omitted the gauge panel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
6783619069 |
Gitea #9: the upper-MFD PRESET pages (3 MFDs x 5) live -- SetPresetMode
table re-decoded to the ModeMFD bits + desktop J/K/L page cycle
The preset system was unwired by ONE defect in the message layer: the
SetPresetMode @004d1b24 table @0051dbf0 had been transcribed from the
section dump as BIG-endian dwords ({0x1e,0x01000000} instead of
{0x1e,0x01}), so a preset press set a garbage high bit -- and for group 1
items 3-4 / group 2 items 0-2 stomped the LIVE NonMapping / Intercom /
ModeSecondary* bits -- while the real page bits never moved. Ground
truth (section_dump.txt:72901-72908, little-endian + BTL4MODE.HPP [T0]):
set = ModeMFD{1,2,3}{Quad,Eng1-4} = 1<<(group*5+item), bits 0-14, fully
disjoint from the #6 secondary trio (bits 18-20); group 2 is MFD3, NOT a
duplicate of the secondary views.
What the 15 presets show (l4gauge.cfg): group = the MFD (Mfd1 lower left
/ Mfd2 upper center / Mfd3 lower right), item 0 = the btquad.pcx Quad
overview (up to 4 vehicleSubSystems cluster panels), items 1-4 = the
full-screen engineering-detail pages (bteng.pcx + prepEngr screens
group*4+1..4 + the cluster eng children: GENERATOR SELECT A-D, POWER
graph, COOLING loop, DAMAGE, ammo). Empty screens are authored per mech
(Blackhawk: 3/11/12 empty).
Authentic dispatch (streamed "L4" .CTL EventMappings, BT_CTRLMAP_LOG
dump): each MFD owns the 8-button RIO bank around it, MODE-MASK-gated --
Mfd1 = 0x08-0x0F, Mfd2 = 0x20-0x27, Mfd3 = 0x00-0x07. Quad page ->
direct-select buttons for the POPULATED eng pages (mapper msgs
Aux1Eng1-4 0x4-0x7 / Aux2* 0x9-0xC / Aux3* 0xE-0x11); eng page -> one
back-to-Quad button (0x3/0x8/0xD) + per-subsystem controls. These
records already install and fire on desktop (btinput passes the live
manager mask), so the NUMPAD profile's 0x20-0x27 keys page MFD2
authentically.
Port wiring (the #6 pattern): keys J/K/L -> actions Mfd1/2/3Cycle ->
gBTPresetCycle -> L4MechControlsMapper::CyclePresetModeNow(group) -- a
documented desktop shim (24 mode-dependent pod buttons don't fit a
keyboard) that cycles Quad -> populated Eng pages -> Quad, visiting
exactly the pod-reachable set; the body is the authentic SetPresetMode.
Dev-composite: BTDrawGaugeSurfaces now draws the Eng1-3 planes at their
sibling cells and skips any mono plane whose channel is BlankColor,
honoring the mode-driven reconfigure (RemapGraphicsPort) -- each dev
cell shows the ACTIVE page like the pod monitor. This supersedes and
removes the 2026-07-12 GAUGREND "frozen-dial" scaffold (forced all 15
page bits active under BT_DEV_GAUGES; it pinned the shared Eng plane on
the highest screen and ate the page flips).
Pixel-verified (BT_PRESET_TEST + BT_DEV_GAUGES_DOCK + BT_SHOT,
Blackhawk): all three MFDs page Quad -> SYSTEM NN eng details -> back to
Quad in lockstep with the [mode] preset mask log; group 0 skips the
authored-empty screen 3, group 2 skips 11/12. Un-regressed: N display
cycle (0x450421->0x490421->0x510421, page bits intact), M control mode,
CONTROLS.MAP 52 bindings parse clean.
Diags: BT_MODE_LOG ([mode] preset), BT_PRESET_TEST=<frame>.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
1d6339b226 |
Gitea #6: secondary MFD Damage/Critical/Heat cycling -- reconstruct the
NotifyOfDisplayModeChange override (vtbl+0x4C @4d1ae4) + wire desktop 'N'
The secondary screen's schematic selector was mislabeled: @004d1ae4 (the
bits-18..20 ModeSecondary* mask swap) was reconstructed as a non-virtual
"SetControlMode" that nothing called, so the desktop stayed pinned on the
Damage view. The binary's L4 vtable @0051e440 pins the truth:
+0x48 = @004d1acc <- CycleControlModeMessageHandler (FUN_004afbe0):
forwards to the base RET no-op @004b048c. A BAS/MID/ADV
control-mode change never touches the secondary view
(empirically confirmed: BT_MODECYCLE_TEST cycles the CONTROL
MODE lamp, mask bits 18-20 unchanged, schematic stays ARMOR).
+0x4C = @004d1ae4 <- CycleDisplayModeMessageHandler (FUN_004afcac):
THE Damage/Critical/Heat selector, indexed by displayMode
(table @0051dbe4 = ModeSecondaryDamage/Critical/Heat).
Authentic pod inputs (streamed type-6 .CTL EventMappings, dumped via the
new BT_CTRLMAP_LOG EVENT records): secondary-panel button 0x15 -> msg
0x15 CycleDisplayMode (manual p13, the "'Mech status Info center" bottom
left of the secondary screen), button 0x18 -> msg 0x14 CycleControlMode
(manual p6, top right), 0x10/0x11 -> ZoomIn/Out. The DOS keyboard
fallbacks (Keypress 0x13d/0x13e = extended F3/F4) are dead under the
WinTesla VK map, hence the desktop pin.
Port wiring (the M/ModeCycle pattern): key N / pad RightThumb -> action
DisplayCycle -> gBTDisplayCycle -> CycleDisplayModeNow() -- the same body
the pod console button message drives. Both .MAP profiles + the
compiled-in default updated.
Verified live (docked gauges + BT_SHOT, BT_VIEWCYCLE_TEST): the sec
panel cycles ARMOR DAMAGE silhouette -> CRITICAL DAMAGE subsystem list
-> HEAT DAMAGE colored list, mask 0x450421 -> 0x490421 -> 0x510421; M
control-mode cycling un-regressed (BAS/MID/ADV lamp cycles, view pinned).
Diags: BT_MODE_LOG, BT_VIEWCYCLE_TEST=<frame>, BT_MODECYCLE_TEST=<frame>,
BT_CTRLMAP_LOG now dumps EVENT records. KB: gauges-hud secondary-view
section rewritten, CLASSMAP +0x48/+0x4C slots, decomp-reference env
gates, GAUGE_COMPOSITE phase-4 entry resolved.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
2ae9bd43ae |
Coolant Flush end-to-end (Gitea #7): InjectCoolant id-4 handler + drain + FLUSH.PFX cloud + 'H' key
The playtest report (sound plays, no gauge drop, no cloud) traced to the
Reservoir's InjectCoolant chain being dead in three places:
- The handler was never REGISTERED: the binary's Reservoir handler table
@0x50e680 has one entry {4, "InjectCoolant", @4aee70}; added
Reservoir::GetMessageHandlers + the press/release handler (press starts
the flush gated on coolantLevel@0x12C > 0 -- the old body misread +0x12C
as currentTemperature; release stops it; novice lockout via FUN_004ac9c8).
- Reservoir::InjectCoolant (@4aefa4, 1019 bytes) was an empty stub -- the
drain the coolant gauge reads. Reconstructed in full: work list =
condenser/weapon/heatable chains (+0x7cc/+0x7bc/+0x7ac, roster-walk
emulation with the binary's duplicate-visit weighting; HeatSink-filtered
[T2 guarded]) + the linked master sink; per sink with flowScale != 0 move
squirtMass x flowScale x dt (clamped to the tank / sink capacity) and
credit pendingHeat with the negative carried-heat delta capped at
sinkMass x reservoir startingTemperature -- the set%-biased flush of the
manual (p24), riding the existing heat model.
- Two latent ctor decode bugs surfaced and fixed: the master gate @4af408
(and @4aeb40 HeatWatcher, swept) reads the OWNER MECH's simulationFlags
(*(param_2+0x28)), not the resource's subsystemFlags (the misread left
the CoolantSimulation Performance unregistered); and the capacity scale
FILDs the bank's INTEGER HeatSinkCount ((float10)*(int*)(link+0x1d0)) --
the float reinterpret gave ~1e-44 -> a permanently empty tank.
Capacity = 0.05 x heatSinkCount x streamed CoolantCapacity (BLH: 6.0).
Visual: the binary's mode-1 coolant-effect renderable (FUN_00456a68, built
for classID 0xBC0 on "ReservoirState", part_014.c:5439; tick @part_007.c:
8780) starts psfx 19 = FLUSH.PFX ("Coolant flush", BTDPL.INI) when the
state changes to 1 -- BTSpawnFlushCloud (mech4.cpp) spawns it on the same
alarm edge as an attached emitter at torso height.
Input: new CONTROLS.MAP action "Flush" on 'H' (HELD; press+release both
dispatch, the held-button payload). Diags: BT_FLUSH_LOG, BT_FLUSH_TEST.
Verified live (FOGDAY): [flush] Reservoir level 6 -> 0.13 -> 0 over ~0.6 s
held (= the manual's 3-4 punches to empty), the coolant vertBar source
Reservoir/CoolantMass drains, and the bluish condensation cloud rises from
the mech (scratchpad/flush_cloud.png vs flush_before.png).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
d9ceddb12a |
Aim ray follows the torso elevation (the boresight leveling erased it)
Follow-through on the pitch fix: the view pitched but shots still flew level (user report). Cause: the task-#48 AIM BORESIGHT leveling in the eye's aim-camera publish (L4VIDRND) strips ALL pitch from the pick-ray direction -- correct for the TERRAIN body pitch it was built against, but it also erased the pilot's deliberate R/F elevation. Fix: mech4's per-frame eye compose publishes the torso elevation (gBTEyeElev); the publisher re-applies it onto the LEVELED forward (fwd = (cos e * level, sin e)) before building the basis -- terrain pitch stays stripped, the deliberate aim survives into BTGetAimRay. Sign matches the pixel-calibrated view compose (+ = up). Harness evidence: pinned-down elevation visibly pitches the chase eye into the terrain (the ray basis moves); a full headless aimed-kill could not be driven (the autofire gate needs a designated target and the random spawns wedged short of the truck row) -- aim-at-truck kill needs the human pass. Awaiting human verification (issue #8). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b85afed925 |
Torso-elevation aim LIVE: R/F now pitches the view + aim ray (pitch was inert)
User + tester reports: pitch does not work. Root cause: the Torso sim integrated the R/F / stick-Y axis into currentElevation (authored limits and rates all correct) but NOTHING consumed it -- eyepointRotation's only writer was the look-state switch, so neither the cockpit view nor the aim ray (camera basis) ever pitched. Shots always flew level; low targets (trucks) were unhittable. Fix (faithful: the pod's stick-Y pitches the EYE; the torso geometry does not tilt): per-frame compose in the HUD tick -- eyepointRotation = EulerAngles(lookPitch + torsoElevation, lookYaw, 0); BTCommitLookState now stores its look component in gBTLookPitch/Yaw; new complete-type bridge BTGetTorsoElevation (torso.cpp, mirrors BTGetTorsoTwist). DPLEyeRenderable consumes it every frame, so the view and the boresight aim pitch together (crosshair stays screen-centred). Sign PIXEL-CALIBRATED via new diag BT_FORCE_ELEV=<-1..1> (pins the axis headless): screenshots confirm axis +1 (key R) = aim UP, -1 (F) = down. X still recenters. Awaiting human verification. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
6f6a39b8c9 |
Issue #3 (b)+(c): weapon damage reaches cultural icons -> full trkdead explosion + authored burn fire
(b) The 'tiny explosion' on the ram kill is AUTHORED: crunch res 31 = 'stephit'
(one video object, effect 1008 = ddam5 damage smoke) -- a step/ram squash is
small by design. The FULL explosion res 32 = 'trkdead' = psfx 15 dtrkboom
(fiery omni burst) + psfx 16 dtrkburn (the burning-wreck fire), reached by
WEAPON kills -- which the 1995 binary dispatches [T1]:
- MechWeapon::SendDamageMessage @004b9728 (part_013.c:6765) gates only
"target NOT derived from Mech@0x50bdb4 OR aimed zone set": a non-Mech
boresight target takes the zone=-1 damage UNCONDITIONALLY;
- Missile contact @004be078 dispatches at the struck solid's OWNER entity
with no class test at all.
Port wiring: Mech::WorldStructurePick returns the struck solid's owning entity
(BoxedSolid::GetOwningSimulation; TERRAIN.cpp:107/246 build every static solid
with its Terrain/CulturalIcon/Door entity as owner); the mech4 pick block
designates IT instead of the gBTTerrainEntity sentinel (sentinel = fallback);
the projectile contact path grew the non-mech damage-zoned else-branch
(@004be078 mirror, direct Dispatch). Plain terrain ignores the damage
(ENTITY.cpp:885 zone==-1 guard); an icon's handler maps -1 -> 0 and dies.
Truck armor is WeaponDamagePoints=1 (TRK.DMG): one laser = one dead truck.
(c) The burning fire is dtrkburn.pfx, authored INTO the death package (every
icon family carries 1016: trkdead/bigdead/meddead/msldead/twrdead). There is
NO looping BurningState fire in the binary: the damage-zone effect watcher
ctor @0042a984 has exactly one call site (the Mech ctor, part_012.c:10405), so
icon ExplosionTables are inert, and CulturalIcon has no Performance.
Documented as authentic -- nothing invented.
Verified live (BT_SHOT pixel capture): 26 laser kills, [cult] TakeDamage
type=3 -> DYING res=32 -> DPLIndependantEffect 1015+1016 at the icon origin;
frames show the orange dtrkboom fireball, dtrkburn flames on the fresh wreck,
and the rubble aftermath; ram harness re-run still fires crunch res 31.
Diags: BT_FIRE_AT_ICON (designate nearest ahead icon; live-icon census in
CULTURAL.cpp), BT_FX_TEST="1015,1016".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
0bcba26213 |
KB: land the EXPERIENCE-LEVELS decode from glass-cockpit (Cyd)
Brings Cyd's experience-level / simulation-mode research onto master (the KB-decode portion of glass-cockpit 4e01e83; the bundled BT410 source manifest is left for the full glass-cockpit merge). - context/experience-levels.md (NEW): the egg per-pilot 'experience' field (novice/standard/veteran/expert) is the pod's SIMULATION-FIDELITY tier, decoded end-to-end. FUN_004c0bc8 reads btMission->experienceLevel(+0xe4) and fans it into the +0x25c/+0x260/+0x26c/+0x270/+0x274 flag block: +0x25c 'sim live' (novice lockout: jams/searchlight/powersub), +0x260 the HEAT-MODEL master switch (veteran+expert; FUN_004ad7d4), +0x274 raw level (FUN_004ac9c8 = ==0 novice predicate; the valve/advanced-cockpit lockout). 4.0->4.10 drift: the viewscreen hunting-aid gate is GONE in 4.10 (HUD reads none of these flags), and movement heat is veteran+expert not expert-only. - btplayer.cpp/.hpp: CORRECTED comments/labels -- the +0x25c..+0x274 block is seeded from btMission experienceLevel/advancedDamageOn, NOT the scenario role's returnFromDeath; the 'roleClassIndex/showKills' names are scoring-era mislabels. Code logic UNCHANGED (comment-only): the ctor still reads scenarioRole (STAND-IN, defaults 2 = veteran) pending the wiring task. - Corrections swept into gauges-hud.md (ROOKIE->novice lockout), open-questions.md (player+0x260/0x274 semantics now PINNED), pod-hardware.md, subsystems.md, decomp-reference.md; CLAUDE.md router row. No code-logic change -> no rebuild needed (comment + markdown only). The runtime wiring (seed from BTMission::ExperienceLevel()) remains TODO. Co-Authored-By: Cyd <cyd@falloutshelterarcade.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
171c993147 |
HUD: missile/AC weapon pips blink on fire again -- read authentic WeaponState (attr 0x1C)
Reported: the reticle's missile pips no longer momentarily disappear when
firing. Traced it: the pip's 'loaded' flag has read MechWeapon::rechargeLevel
(>= 0.999) since the reticle Execute was recovered (task #37). That's a port
APPROXIMATION of the binary's authentic attr 0x1C (WeaponState @0x350). It
works for emitters -- rechargeLevel is charge-driven and tops off at 1.0 when
Loaded -- but projectile weapons (MissileLauncher/autocannon) never write
rechargeLevel (it's authentically static at 1.0; the recharge DIAL draws full
and never moves). So a missile pip's 'loaded' was permanently true and the pip
never blinked. NOT an audio regression -- it never worked in the port; the
audio wave (
|
||
|
|
122fb7bccb |
Fix the backtick crash (a /FORCE-hidden linkage bug) + backtick = view toggle
The crash (any typed key in a glass session; found via the backtick report): the merge-reconciliation stand-down declared extern BTRIODevicePresent at BLOCK scope inside the extern-C BTInputSuppressKey -- the declaration inherited C linkage, _BTRIODevicePresent went UNRESOLVED, and /FORCE bound the call to garbage (cdb: wild call into Sensor::DefaultData from LBE4ControlsManager::Execute's suppression check). The tolerated-LNK2019 batch hid the new unresolved external -- the exact CLAUDE.md /FORCE trap. Fix: the extern moved to file scope (C++ linkage); link verified clean of _BTRIODevicePresent. Backtick feature (per Cyd: backtick = 1st/3rd person): PadRIO edge-detects VK_OEM_3 in its poll (focus-guarded, message-path-free) and the mech4 view block consumes it beside the V action (which stands down with the binding engine in glass mode). Verified live: two real presses -> [view] COCKPIT eyepoint -> [view] external chase, game alive. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1e6f5fd347 |
FE: the menu process applies NO platform profile -- the preset leak fix
Steam-host report ('no glass panel, plasma yes'): a direct btl4.exe start ran
the MENU under the DEV profile, whose putenv(L4CONTROLS=KEYBOARD) leaked into
the relaunched mission child -- the child's glass preset (sets-when-unset)
could then never select PAD: no PadRIO, no panel; the plasma appeared only
because DEV never touches L4PLASMA. Menu-mode is now detected FIRST and the
front-end process skips the profile putenvs entirely (profiles belong to game
processes); boot line reads 'MENU (front end -- no profile applied)'.
Verified: zero-arg start -> menu -> launch -> child boots GLASS, PadRIO up,
72-control panel present, plasma present.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
8cf2cc1f07 |
FE: the RP412 menu + lobby-room pattern; flat single-exe dist (per Cyd)
Menu (btl4fe, modeled on RP412 RP_L4/RPL4FE.cpp): full catalogs as always-visible clickable LISTS in three columns with friendly names -- 8 maps, the canonical 8 certified mechs, colors, times, weather, lengths, modes -- bright/dim green with a selection chevron, double-buffered paint, real EDIT controls for pilot name + LAN peers (green-on-black via WM_CTLCOLOREDIT), framed LAUNCH / HOST STEAM / JOIN STEAM buttons, and the loadout persisted across the per-mission relaunch (fe_last.ini, gitignored implicitly by *.ini? no -- plain file beside the eggs). ENTER/ESC kept. Lobby room (RP412 RPL4LOBBY look): pilot roster with loadouts (mech, color), framed L A U N C H M I S S I O N (owner) + LEAVE LOBBY buttons, clickable. Dist: FLAT layout (the RP412 convention) -- ONE btl4.exe entry point at the folder root beside the content tree (the shape TeslaConsole manages), DLLs + launchers at root, content flattened from git-tracked files. Verified: the staged flat tree boots the Mission Console zero-arg from its own root; the click-driven menu launches a full marshaled mission (row click -> length=60 in the egg, LAUNCH click -> ladder -> 41+ ticks, fe_last.ini written). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f889e24ce0 |
Merge origin/master: the D1 relay/operator line + input remap meet the glass layer
33 master commits in (relay TCP/UDP + PySide6 operator console, CONTROLS.MAP +XInput binding engine, camera seats, torso pitch aim, sign fixes, the 1995 manual, version stamping, 18-mech certification). Conflicts: .gitignore + CLAUDE.md router rows (combined). SEMANTIC RECONCILIATION (the one real overlap): masters btinput binding engine (ungated, CONTROLS.MAP) and the glass PadRIO (gated, bindings.txt) would both read the keyboard/pad in a glass+PAD session. btinput now joins the stand-down convention: BTInputPoll yields (and BTInputSuppressKey claims NOTHING, so authentic hotkeys flow) when an operational cockpit device owns the input path -- BTRIODevicePresent, BT_KEY_BRIDGE force-override honored, forced harness exempt. One input system per mode: btinput on pod/dev desktops, PadRIO on glass. The mechmppr/mech4 bridge merges composed clean (masters negate-once sign fix inside our device-gated bridge). The D1 relay keeps its own raw sockets by design (an alternative LAN wire; Steam and relay are separate modes). Verified post-merge: all 3 configs build; glass boots with [input] binding engine standing down + PadRIO owning input (30 ticks); pod forced-walk speedDemand=61.501 with btinput ACTIVE; 2-node loopback MP full 31/31 mission, 76/76 ticks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ff5260ce98 |
Coolant-loop cross-check vs the 1995 manual: structure strongly faithful
Dumped each mech's subsystem->coolant-loop (BT_SPEC_LOG in mech4: walks the roster once, resolves each subsystem's linked-condenser number via BTCoolingLoopFrame) and diffed all 6 manual mechs' COOLANT LOOPS tables. RESULT -- the structure survives the 4.0->4.10 gap remarkably well: - BACKBONE identical on ALL 6: Generator A/B/C/D on loops 1/2/3/5, Sensors(our Avionics) on loop 2, Myomers on loop 5, LRMs on 1&3, autocannon on 4, big energy weapon on 6 -- exact match - weapon LOADOUT identical on 5 of 6 (Thor/Vulture/MadCat/Owens/ Blackhawk); Loki is the one full rework (4.0 PPCx2/AFC100/SRM6 -> 4.10 AFC50x2/ER Medium x2/SRM4) - the consistent 4.0->4.10 change is a small-laser REDISTRIBUTION: the 2 ER Small Lasers moved off the sensor/heavy loops onto the energy loops 4&6 (Thor/MadCat/Vulture); Owens near-perfect (loops 1&3 exact) - Loop 0 = correctly UNCOOLED infrastructure (condensers, reservoir, gyro, torso, HUD, ammo bins, ...) -- not a coolant loop CONCLUSION: the coolant-loop reconstruction is faithful; every diff is 4.0->4.10 balance tuning, not a bug. BT_SPEC_LOG retained. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f4cf1e631a |
Spec cross-check vs the 1995 manual: reads CORRECT, numbers are 4.0-vs-4.10 drift
Audited our live streamed subsystem config against the manual's per-mech stat sheets (new BT_SPEC_LOG dump: torso speed/limits, heat-sink count, reservoir capacity, at subsystem ctor). RESULT: structure + semantics align perfectly; the tuning NUMBERS differ -- because the manual is Tesla 4.0 and our content is release 4.10. Point-release balance drift, NOT a reconstruction bug: - Owens + Blackhawk are the two fixed-torso mechs in BOTH (speed 0, limit ~0, torsoHorizontalEnabled=0); the other four twist-enabled -- our reads correctly identify every case - torso speed/limit + heat-sink count exist per-mech exactly as the manual documents; only the values were retuned 4.0->4.10 (e.g. torso speed MadCat 80->50, heat sinks Loki 38->15) - reservoir coolantCapacity reads a flat 20 = the coolant THERMAL capacity (game units), NOT the manual's per-mech 'liters' (a display/flavor quantity with no single subsystem field) -- different measures, not a bug CONCLUSION: do NOT retune content to the manual -- BTL4.RES is the authentic 4.10 shipping data; the 4.0 manual's numbers are an earlier pass. The manual CONFIRMS our structural fidelity. BT_SPEC_LOG retained for re-auditing. Follow-up (structural, less drift-prone): the per-mech coolant-loop weapon/generator assignments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
cb82d8c1d0 |
Turn/twist/free-aim sign family: negate ONCE at the key bridge (user-verified)
User live-tested: 'if i push the left arrow the mech turns right' -- correct, and it invalidated my earlier screenshot-forensics 'D=right verified' claim (the yaw telemetry actually agreed with the user all along: D gave POSITIVE yaw = CCW = LEFT; the sim uses math convention, positive = counter-clockwise, for turn AND twist). On the pod the RIO Ranger owned the hardware sign; the desktop bridge now negates in ONE place per channel family: - key_turn = -gBTDrive.turn (forced/BT_GOTO harness demands stay sim-frame, un-negated) - stickPosition.x = -gBTTwistAxis (Standard/Veteran torso stick) - SetFreeAimSlew call-site negations REMOVED (the bridge negation now flows through; double-flip removed) Matches the manual (p8): 'pulling your joystick to the right torso twists your Mech to the right.' User verified live: arrows steer correctly (Blackhawk, basic mode) and MadCat torso twist is correct in middle mode. LESSON (recorded): large-rotation screenshot comparisons are AMBIGUOUS (both directions put 'new scenery at an edge'); trust tracked landmarks, numeric telemetry with an established convention, or the user's live observation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
af80e62070 |
Fixed-torso free-aim left/right was reversed (tester report confirmed)
Field report: 'left/right is reversed'. Investigation with screenshot + numeric verification found it channel-specific: - A/D leg steering: CORRECT (D tap -> scenery slides left = turns right; verified via cockpit screenshots) - Torso TWIST (turning-torso mechs, e.g. MadCat): CORRECT all along (E -> currentTwist +0.27 = right -- consistent with weeks of validated play; an interim blanket negation that broke this is reverted) - Fixed-torso FREE-AIM slew (Blackhawk & friends): REVERSED -- E/right panned the view LEFT. The slew consumer pans opposite the twist convention; on the pod the RIO layer owned the hardware sign. Fix: negate stick_x ONLY at the two SetFreeAimSlew call sites (Standard + Veteran branches). Verified after fix: MadCat E -> currentTwist +0.270 (right); Blackhawk E -> view pans right. New BT_INPUT_LOG diags: [input] twist in/currentTwist (Standard branch) + mechYaw in the Basic elev line. Note for testers: the tester was almost certainly flying a fixed-torso mech -- 'reversed' reports should always record WHICH mech. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1271d3bc76 |
Torso elevation (pitch aim) wired: the pod stick's Y axis lives
The mechs could always tilt their aim up/down -- Torso models the full vertical axis (currentElevation, rate, VerticalLimitTop/Bottom, recenter) and EVERY 1995 control mode routes stickPosition.y into Torso::SetAnalogElevationAxis -- but the desktop bridge hard-zeroed stick Y, so the axis was dead on a keyboard rig. The one pod control the remap left unwired. - btinput: JoystickY axis -> elevTarget/elevActive/elevAbsolute - mech4 shim: sElev integrator (same walk/spring model as the twist; X recenters pitch too via gBTElevRecenter) - mechmppr bridge: feeds stickPosition.y every bridged frame (the old unconditional zero removed); both mode branches covered - CONTROLS.MAP (+ numpad profile + compiled default): R/F = aim up/down, pad LeftStickY = elevation (was unused) - torso.hpp: CurrentElevation()/ElevationVelocity() accessors (diag) - [mppr] trace gains stickY (note: the trace reads AFTER the next frame's device push re-zeroes the stick -- input flows regardless) Verified live: R held -> torso elevation climbs at the authored rate and clamps at 0.349066 rad = exactly 20.0 deg (the Blackhawk's VerticalLimitTop); release holds the aim. The eyepoint correctly stays level -- pitch aims the GUNS and reads on the HUD's vertical elevation tape, as in the pod. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4459262b9c |
Camera seat LIVE + Mech::PlayerLinkMessageHandler reconstructed (@0049f624)
The spectator/broadcast seat works end-to-end: a hostType=1 +
vehicle=camera pilot page boots a CameraShip whose 62-camera arena1
network loads from the BTL4.RES type-27 resource (the engine's
CreateStreamedCameraInstances existed all along -- the file probe falls
back to it), the BTCameraDirector locks onto the first live mech, and
the ship TRACKS it (sees=1, goal advancing) and CUTS between authored
camera positions (screenshot-verified: wide arena shot -> close
tracking shot).
The missing piece was the Mech override of PlayerLinkMessageHandler
(@0049f624), which the reconstruction never filled. The engine base
resolves mech->player; BT's override adds:
1. the REVERSE link player->playerVehicle = this on EVERY node
(replicants included) -- how the camera director and scoreboard
find a REMOTE player's mech; without it a spectator parks forever
('NO goal entity')
2. clears NonScoringPlayerFlag (0x4000 = bit 14): a pilot with a
vehicle is a SCORING player -- this admits REPLICATED players to
the ranking pass, so cross-node rank/score displays work
3. master only: seeds the heat bank's ambientTemperature (bank
@0x1d4) from the mission [mission] temperature= (BTMission+0xf4)
-- correcting the heat family's 'frozen 300' deviation note (it
was never frozen; THIS is the writer)
Bridge BTSetBankAmbientTemperature lives in heatfamily_reslice.cpp
(mech.cpp cannot include subsystem headers -- local-stub collision).
BT_CAM_LOG diagnostics: camera-network count, director pick + Players
group census, ship follow state, link dispatch/receive.
Verified live: 2-seat (mech + camera) relay session -- replicated
player shows +veh, director goal locked w/ 30s timer, ship tracking a
moving mech through camera cuts; standard 2-node mech smoke PASS
(un-regressed). Lesson re-learned: a 'clean' build filtered on 'error
C' missed a linker file-lock failure -- one whole test cycle ran
against a stale exe (the /FORCE gotcha's cousin; grep -i error, not
error C).
Remaining (task open): ranking-window overlay draw (L4VIDRND stubs),
operator-app camera-seat row, shot polish.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
17176058cc |
First 4-pod session VERIFIED + the EntityID-cast diagnostic trap
4 pods, relay-assigned seats, 4-pilot MP4.EGG (eggmodel-generated incl. callsign bitmaps): 4 distinct seats, full ladder, every node replicates all 3 foreign mechs (host-qualified [repl] sets are exact complements), 4 distinct spawn positions (no dropzone stacking), UDP at 4-pod fan-out (relay tx=3x rx, zero drops), all pods alive through the session. Per-host [net-rx] census symmetric: 36 NewDynamicEntity makes (mech + subsystem roster) from every host to every node. DIAGNOSTIC TRAP that cost a false bug-hunt: EntityID::operator int() returns localID ONLY, so the [repl] log's (long)GetEntityID() collapsed different hosts' mechs onto one number and read as 'missing replicants'. The log now streams the EntityID object (host:local via its operator<<). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
fad962bec8 |
Version stamping: 4.11.<git commit count> (<hash>[+])
4.10 = the 1995 arcade release; 4.11 = the win32 reconstruction; the
build number is the git commit count -- monotonic, zero-maintenance,
and every exe pins to exact source via the short hash ('+' = built
from an uncommitted tree). tools/btversion.cmake regenerates
build/btversion.h on every build (write-if-changed, no rebuild churn);
the stamp shows in the boot banner (btl4.log head, replacing the stale
'v4.10' line) and the window title (incl. the MP node tag).
Verified: build stamps 4.11.311 (980c9cd+), banner + title correct on
a live boot.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
980c9cd7e5 |
Input remap: CONTROLS.MAP binding engine + XInput gamepad support
The pod's controls reach the game through the RIO serial board; on a
desktop that hardware is a keyboard shim of hardcoded GetAsyncKeyState
reads. New binding engine (game/reconstructed/btinput.cpp) maps PC keys
and an XInput pad onto the authentic channels through a user-editable
file (content/CONTROLS.MAP, community-suggested grammar, corrected):
key/pad -> button <addr> real buttonGroup emissions, mirroring the
RIO convention exactly (press a+1 w/ mode
mask saved, release -a-1 w/ saved mask;
L4CTRL.cpp:2470-2520) -- aux/preset banks,
hotbox, panic, reverse thrust all reachable
key/pad -> axis Throttle rate (lever), pedals/JoystickX
deflect (turn/twist) into the existing
virtual-controls integrators (feel, gait
detent, spring-centering all unchanged)
keypad pilot|external <n> keyboardGroup key values ('0'-'F')
pckey <char> any authentic 1995 typed hotkey
action <name> port dev controls (view, all-stop, ...)
Keys claimed by a binding are SUPPRESSED from the legacy WM_CHAR/KEYUP
feed -- ends the historic double-dispatch ('w' drove AND selected pilot
0; F5's key-up value 0x74 aliased to the 't' hotkey; letter key-ups fed
the developer fake-event dispatcher). Unbound keys keep their authentic
meaning. The dual-use 'V' is split: V = view toggle, B = look behind.
Default profile = WASD classic (compiled-in twin; delete the file to
restore). CONTROLS_NUMPAD.MAP ships the corrected community layout
(keypads are NOT buttons 0x50-0x6F -- that space doesn't exist; no
clickable cockpit exists so everything needs a binding; keyboard fire
buttons added; 0x36/0x37 are hotbox not 'config'; missiles moved off
Ctrl). XInput loads dynamically (1_4 -> 9_1_0), disconnected-pad
probing rate-limited; pad sticks write the axes as absolute positions
(the spring is physical), triggers/buttons per the file.
Verified live: bindings load (43), W and NumPad8 drive (speedDemand 0 ->
61.5), X all-stop (spd -> 0), aux-bank emission (D1 -> [input] 0x2f
PRESS/release under the numpad profile), suppression both directions
(posted 'r' reaches [keych], posted 'w' swallowed), full-keyspace
WM_CHAR+WM_KEYUP fuzz x3 survived with sim advancing, XInput graceful
with no pad, 2-node relay session un-regressed (full ladder + UDP).
Pad-in-hand testing still needs a physical controller.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
f703bb1d56 |
Steam: the lobby + IDENTITY-TOKEN roster -- internet MP code-complete (step 4c)
NEW gated (BT_STEAM) game/glass/btl4lobby: an ISteamMatchmaking room replacing the arcade Site-Management screen -- host creates (lobby data btl4=1), members join by filter, pilots publish name/mech/color, the host ENTER mints the roster and signals GO. THE FAKEIP LESSON (live-verified, prior-art assumption DISPROVEN): a fresh process gets a DIFFERENT FakeIP, so menu-time fake addresses go stale by mission time -- the first cycle timed out connecting to its own stale address. Redesign: roster addresses are opaque ipv4-shaped TOKENS (169.254.77.N with ports 1501/1502) minted by the host at GO, mapped to Steam IDENTITIES; connections ride ConnectP2P(identity, channel) with console=0/game=1 virtual ports; the map reaches mission processes via env (BT_FE_MYFAKE + BT_FE_STEAMMAP); the GetMyAddress seam feeds the pod its own token for roster self-match; CheckSocket reports peers by token. L4STEAMNET reworked (no FakeIP allocation at all); the marshal gained the Steam-wire branch. Verified single-machine (ON/ON, live Steam): menu -> HOST STEAM LOBBY -> room -> GO -> token map minted -> egg roster carries the token -> mission process up with 1 roster token incl. self -> pod self-matches -> stock console ladder -> mission RUNS (46 ticks). Remaining: the live 2-account cross-machine session (docs/STEAM_TEST.md). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
48ede2f7d7 |
Net: the Steam transport -- FakeIP + SDR behind the wire seam (step 4b)
NEW gated (BT_STEAM) TU L4STEAMNET: ISteamNetworkingSockets with FakeIP -- pseudo-SOCKET handles (0x5EA0xxxx) behind the L4NET BTNet* seam; the engine TCP byte stream rides reliable-NoNagle Steam messages re-assembled into per-connection rings (nonblocking recv semantics preserved: empty -> WSAEWOULDBLOCK, peer-closed -> 0); two FakeIP ports mirror the arcade console/game channel convention; listen sockets + accept queues via the global status-changed callback; graceful degrade to Winsock at every step. Seam completions in L4NET.CPP: CheckSocket reports a pseudo-socket peer as its FAKE ipv4:port (the lobby egg roster matches unchanged); OpenConnection TCP_OPEN delegates FakeIP targets to BTSteamNet_Connect. WinMain installs on BT_STEAM_NET=1 (lobby launch path / by hand). CMake: gated SDK include/ lib/dll-copy. Both pumps (SteamAPI_RunCallbacks + sockets RunCallbacks) drive it -- the FakeIP result rides the GENERAL dispatch (first attempt with only the sockets pump timed out). Verified live (ON/ON build, Steam client running): SteamAPI_Init OK, steam_appid.txt(480) auto-written, FakeIP allocated (169.254.36.58, console 54464 / game 54465); without BT_STEAM_NET or without Steam the game stays pure Winsock. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c4ca87b808 |
Miniconsole: the front end + LocalConsole marshal -- self-launched missions (step 3)
NEW gated dir game/glass/: btl4fe (green-on-black GDI catalog menu -- map/ time/weather/length/mech/color/pilot/mode/port/peers; egg writer emitting the FULL console egg shape, golden-tested vs MP.EGG: identical section sequence at identical line offsets, the 4 static ordinal pages verbatim, GDI-rendered 128x32/64x16 pilot-name plasma bitmaps in MP.EGG framing) and btl4console (the marshal: a worker-thread console CLIENT speaking the btconsole.py wire protocol verbatim -- 1040-byte chunked egg, 20s settle, RunMission x2, STAY CONNECTED, own the clock, StopMission at expiry; logs marshal.log). The engine sees a real connected console and runs the 100% stock ladder -- NO engine hooks (less invasive than planned: the L4APP.H setters proved unnecessary; launches go through real argv on relaunch). WinMain (one gated block): zero-mission-arg glass launch -> menu -> relaunch self with the mission argv (per-mission process relaunch; BT_FE_* env arms the marshal); post-RunMissions BT_FE_LOOP tail returns to the menu. Menu relaunches CLEAR the BT_FE_* handoff (found live: the inherited env re-armed a marshal instead of showing the menu). Verified end-to-end (synthetic menu drive): menu -> frontend.egg -> relaunch -> marshal feeds self over loopback -> stock console ladder -> mission RUNS (58+ ticks) -> 60s clock -> StopMission -> clean menu return, no env leak. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
418acf07e4 |
Panel: the vRIO layout + coloration, plasma flip fix, button-trap guard
Per Cyd: the on-screen panel now mirrors vRIO (C:\VWE\vrio -- CockpitLayout.cs
+ PanelCanvas.cs, itself the RIOJoy profile-editor layout from the original
Win32 RIO mockup): five 4x2 MFD clusters (addresses descending), four 1x8
board columns (Throttle/Secondary/Screen/Joystick), the two 4x4 hex keypads
(0x50/0x60 -- cells emit real keypad KeyEvents: internal=pilot unit, external=
operator unit), physical names (Panic/Main/Hat*/Pinky...), vRIO colors: red
lamp cells + yellow Secondary/Screen + neutral-blue keypads, flash half-
periods 500/250/125ms, right-click latch (gold outline). 104 controls.
PlasmaWindow: the gauge renderer writes the 128x32 buffer TOP-DOWN -- the
bottom-up flip rendered the marquee upside down (user-reported); copy straight.
Button-trap guard (the
|