Commit Graph
346 Commits
Author SHA1 Message Date
CydandClaude Fable 5 64e208f121 BT410 Phase 5.3.26: THE COCKPIT GAUGE BLOCK -- the first rendered milestone
Six TUs born in one wave (4-agent re-hosting workflow from the BT411 donors
+ inline integration): BTL4GAUG (16 widget primitives + 5 connections, the
TU anchor @004c3f6c), BTL4GAU2 (13 composite panel clusters), BTL4GAU3
(PilotList/PlayerStatus/SectorDisplay/MessageBoard/mapping group), BTL4RDR
(the MapDisplay radar vs the authentic surviving header), BTL4GRND (the
real renderer: L4LampManager + L4Warehouse + BuildConfigurationFile with
the 19-entry BT registry chained onto the engine table).  btl4.lib grows
7 -> 11 members; all 50 game TUs compile; link clean.

VERIFIED LIVE: with L4GAUGE=640x480x16 the interpreter parses the full
authentic L4GAUGE.CFG, Bhk1Init runs, ColorMapperArmor binds the live
mech's damage zones by name (cross-chassis names take the authentic inert
path), and the complete mutual missile fight runs with the cockpit stack
updating from real combat -- 16/16 rounds, 138 zone hits, zero faults.
Gauges-off regressions untouched.

Engine bring-up deviation (documented): ParseAttribute normalizes
unpublished attribute names to a shared writable zero cell -- the 1995
GaugeConnectionDirectOf ctor hard-derefs its source (first boot crashed
there); widgets on unpublished names draw static zeros and light up
automatically as the attribute waves publish the 1995 CFG spellings the
bindings already carry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 19:02:04 -05:00
CydandClaude Fable 5 f11cc0247e BT410: gauge worksheet donor path fixed (exact-byte repair, forward slashes)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 17:41:28 -05:00
CydandClaude Fable 5 b02ce77f06 BT410: byte-level fix for the gauge worksheet donor path (CR/BS escapes)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 17:40:26 -05:00
CydandClaude Fable 5 7bfbece12d BT410: fix a mangled donor path in the gauge worksheet header
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 17:39:36 -05:00
CydandClaude Fable 5 2e0c8a0681 BT410: the gauge-block worksheet -- per-class re-hosting dossiers banked
Three-dossier workflow output (65KB, verbatim): every class across the six
missing gauge TUs audited -- engine base, the L4GAUGE.CFG keyword + the
attribute each widget binds (and which reconstructed subsystem publishes
it), the port-shim -> 1995-native mapping, BC++4.52 hazards, and the
binary @ADDR anchors.  This is the implementing session's hot-start
worksheet for the first rendered milestone (see RENDER-ROADMAP.NOTES.md).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 17:39:16 -05:00
CydandClaude Fable 5 fdb1cfcf6c BT410: the render roadmap -- headless to visible, researched and mapped
Four-dossier workflow synthesis: the engine half of BOTH render paths
(DPLRenderer/libDPL for the division-card 3D view; the L4 gauge stack for
the cockpit instruments) ALREADY compiles and links into btl4opt.exe from
the CODE originals, and the emulator's VPX board-side device is
EXE-agnostic (pure wire-level).  What's missing is game-TU work with
complete BT411 donors: the 6 gauge TUs (~302 fns, the first rendered
milestone -- diffable against the shipped exe on the same GAUGE content)
and then btl4vid (the 3D world renderable factory).  Full protocol map,
env gates, and the staged plan in RENDER-ROADMAP.NOTES.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 17:28:54 -05:00
CydandClaude Fable 5 f65a3259ce BT410 Phase 5.3.25: scoring + the COMPLETE mission lifecycle -- egg to GAME-RC
Scoring roles bound (binary ctor tail @004c0bc8): the mission role dict ->
Player::scenarioRole; TEST.EGG's Role::Default = the baked dfltrole
(lives=1000, killBonus=500) -- authored data flowing.  The three score
handlers are live per the decomp derivations (@004c02e4 / @004c0200 /
@004c02a8 + CalcInflictedScore @004c052c; tonnage/bias staged); the
producers post damage scores per delivered hit and the kill credit from
the death transition with the latched killing-blow magnitude.  First live
kill: award=3.43, kills=1, score=123.6.

Out of lives -> the mission END: the binary's +10s review post sits in the
one decomp gap, so the staged stand-in enters the engine's own cascade
(StopMissionMessage +10s) -- EndingMission -> the 3s fade -> the second
Stop -> Application::Stop -> RunMissions exits.  The BTL4/L4 stop layers
run authentically (plasma score display off, egress lamps).

VERIFIED END-TO-END: a 2-lives egg (role-page return=2 override) -- death
#1 debits and respawns healed; death #2 is OUT OF LIVES; +10s later the
mission ends and BTL4OPT.EXE exits CLEANLY to DOS (GAME-RC prints).  The
full 1995 pod mission loop -- egg, boot, spawn, fight, score, die,
respawn, die, mission end, exit -- now runs from reconstructed source.
Zero faults; fight/smoke/novice regressions green.  Dev knob:
BT_PLAYER_PASSIVE=1 (piloted mechs hold force-fire).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 17:18:41 -05:00
CydandClaude Fable 5 623c872c92 BT410 Phase 5.3.24: the death -> respawn cycle -- mechs DIE and COME BACK
The full circuit, workflow-researched (5 parallel dossiers over BT411 + the
engine) and adversarially reviewed (3 lenses) before landing:

Mech side: the once-per-death transition in Simulate (freeze, motion kill,
DeathShutdown sweep, MASTER-only VehicleDead(-1) to the player link);
Mech::Reset @0049fb74 (reposition + heal every hull zone + the roster
DeathReset sweep + PreRun -- the reset-based respawn that REUSES the entity);
dead-owner terms in both weapon hard gates (wrecks fall silent).

Player side: the VehicleDead(-1) branch (deathPending dedup, deathCount
bump+stamp, scenario-role life debit, +5s re-post -> the engine drop-zone
hunt) and the DropZoneReply respawn branch (reset the dead mech at the
replied drop zone; probes on a live mech are moot).

Subsystem side: the engine base virtual DeathReset implemented across the
family -- MechSubsystem (zone heal + DestroyedState clear), MechWeapon (full
powered/thermal restore + fresh Loading cycle), AmmoBin (restock from the
ctor-cached count), HeatSink/HeatWatcher/PowerWatcher/Generator/Sensor/
MissileLauncher.

Review catches fixed before commit: AmmoBin restocked through the FREED
padBuffer pointer (use-after-free -> initialAmmoCount; MechSubsystem::
resource is now documented as never-deref-post-ctor); died-hot weapons
respawned at FailureHeat (now full thermal re-init); Generator tap counts
desynced across reset (preserved); Sensor skipped the base heal; PowerWatcher
inherited a statically-bound reset; cooling toggle + connect mode restored.

Verified: kill -> wreck (0 shots while dead) -> +5s -> drop-zone hunt ->
HEALED + placed at a real drop zone -> 134 shots after respawn incl. 12 SRM
(restock proof); unowned enemy wreck settles silently; fight/smoke/novice
regressions green, zero faults.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 16:35:05 -05:00
CydandClaude Fable 5 2f0d3b6239 BT410 Phase 5.3.23: the cylinder hit-location table -- unaimed hits land WHERE THEY STRIKE
DMGTABLE born (type-29 streams, BT411 byte-verified format): the height x
angle grid -- DamageLookupTable rows by height, PieSlice cells by angle
(rotate-with-torso rows add the live twist), DamageZonePercentTable = the
cumulative hit-distribution roll (first threshold above the roll -- the
BattleTech dice scatter).  Loaded in the Mech ctor by the DamageZoneStream
member's NAME (mech+0x444, resident); Mech::TakeDamageMessageHandler
resolves invalidDamageZone hits through it (binary @0x4a0264 tail).

The missile fuse becomes the authentic unaimed producer: zone -1 + the
round's world position as the impact point (ENTITY3.HPP: "damage zones are
only valid via reticle based weapons").

Fight-verified on the authentic 7-row bhk1 table: flat-flying SRMs strike
low -> row 0 -> FEET AND LEG zones, side-correct by impact angle (+x right,
-x left), identical geometry spread across the cell distribution by the
roll.  17/17 missile lifecycle, zero faults; smoke + novice green.  Also
fixed: the ctor's reservedState zero-loop counted past the shrunken array.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:46:39 -05:00
CydandClaude Fable 5 bfff7fe874 BT410 Phase 5.3.22: the cockpit weapon-button column -- generator panel LIVE
The full per-receiver message chain, contiguous ids 3..0xb: PoweredSubsystem
4-8 (SelectGeneratorA-D + ToggleGeneratorMode, binary table @0x50F4EC)
chained on HeatSink's ToggleCooling(3); MechWeapon 9/10 (config buttons,
staged latch bodies); Emitter 0xb ToggleSeekVoltage (@0x511DB8).

SelectGenerator = release the old tap -> roster walk by generatorNumber ->
AttachToVoltageSource -> Connected; mode toggle cycles Manual->Auto->
(detach)->Manual; the seek dial steps the authored voltage ladder with wrap
(a step up re-enters Loading, charge persists).  All novice-locked.
Generator::UntapVoltageSource + PoweredSubsystem::DetachFromVoltageSource
added.  Dev harness: BT_PRESS_GEN=1..4 / BT_PRESS_SEEK.

Verified: PPC_1 retaps GeneratorB (tapped); seek index 2->3, target 9900V
(authored fraction x rated); novice presses INERT; missile fight 14/14 and
smoke green, zero faults.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:36:32 -05:00
CydandClaude Fable 5 1a01aa6f43 BT410 Phase 5.3.21: the muzzle wave -- rounds leave FROM THE GUN
MechWeapon::GetMuzzlePoint (binary @004b9948): the mount segment's world
frame via GetSegmentToWorld (segmentToEntity x localToWorld), translation
extracted by transforming the segment-space origin.  The missile spawn now
launches from the rack mount -- verified live: SRM6_1/SRM6_2 leave from
distinct world positions 4.0 units above the hull origin, laterally offset
per rack and rotated through the mech's yaw, on BOTH mechs.  19/19
launch-detonate over 100s, zero faults; smoke green.

(The authored MuzzleVelocity up-tilt composed in the mount frame + the mech
velocity term remain with the beam/render wave -- the seeker converges from
the aimed initial velocity either way.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:24:02 -05:00
CydandClaude Fable 5 76acc516e4 BT410 Phase 5.3.20: missiles fly on the AUTHENTIC 'mslhit' family
The explosionModelFile decode: SRM6's streamed value 17 is a real resource
FAMILY id -- 'mslhit', 3 members: VideoModel(225), AudioStreamList(227) and
an 8-byte GameModel(228) = { int 498, maxTimeOfFlight 5.0 }.  The round now
spawns on that family and reads the AUTHORED 5.0s lifetime (range 800 at
~160 u/s checks out); the owner-family fallback stays for content without a
projectile family (sanity band keeps the default there).

Two engine truths closed the wave: (1) collision volumes are the Mover
DEFAULT -- a projectile spawn MUST pass NoCollisionVolumeFlag or the ctor
demands a BoxedSolidStream member the family doesn't carry (SearchList
NULL -> Lock() page fault -- the crash that originally masqueraded as a
"model-list index"); (2) the @0x414938 SearchList crash is the
missing-MEMBER case (Check compiled out) -- probe families manually before
handing them to engine paths.  Resource type numbering mapped and recorded
(15=GameModel, 10=VideoModel, 20=DamageZoneStream, ...).  BT_MODEL_DUMP=1
keeps the raw family/record dump as a decode bench.

Verified: 20 launches -> 20 detonations on the mslhit family over 110s,
zero faults; smoke + novice green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:17:04 -05:00
CydandClaude Fable 5 fdf26d6e55 BT410 Phase 5.3.19: teardown SOLVED -- flying missiles are the DEFAULT
The death-row page fault was the THIRD strike of the uninitialized
zone-array trap: the staged spawn rides the mech family's resourceID, whose
DamageZoneStream makes the Entity base ctor allocate the 20-pointer
damageZones[] array with UNINITIALIZED slots -- the Missile never fills it,
and ~Entity's teardown deleted 20 garbage pointers through trash vtables
(the dtor-chain bisect showed every dtor completing, then EIP-in-heap).
Fix: the Projectile ctor NULLs every inherited zone slot.  RULE, now
three-crashes-proven: any entity subclass that does not FILL the allocated
zone array must NULL it -- the engine never initializes the slots, on
construction OR destruction.

Missiles are now the default SRM delivery (BT_MISSILE_INSTANT=1 = the
5.3.18a instant-hit as an A/B opt-out).  Verified: 20 launches -> 20
detonations -> 20 clean death-row teardowns over a 110s mutual fight, zero
faults; smoke + novice regressions green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 13:39:18 -05:00
CydandClaude Opus 4.8 844c11f792 emulator: in-fork plasma display (serial2=plasma) window in the explode layout
Bring the pod's 128x32 plasma readout in-fork, completing the self-contained
glass cockpit -- no vPLASMA process, no pipe.

- serialplasma.{cpp,h}: CSerialPlasma, a serial<n>=plasma backend that parses
  the game's ESC P graphics stream (L4PLASMA streams whole changed rows;
  receive-only, no replies) into a 128x32 1bpp framebuffer. Transcribed from
  vPLASMA's PlasmaProtocol + VPlasmaDevice (graphics path); the factory
  text-mode/fonts aren't used by the game and aren't rendered, but their escape
  operands are still consumed so the parser can't desync. Seam PLASMA_GetFrame()
  for the renderer.
- vpxlog.cpp: draw the framebuffer as an amber-on-black window in the explode
  layout, lazily created once a plasma port exists, parked centered under the
  lower-left MFD (VPX_PLASMA="x,y,w,h" overrides); WS_EX_NOACTIVATE so it can't
  steal DOSBox focus.
- Register SERIAL_TYPE_PLASMA (serialport.h/.cpp, dosbox.cpp -- documented in
  the vpx-device README step 3d, since those stock files live in the git-ignored
  src tree).
- Confs: serial2 in the _rio confs + deploy templates switches from
  namedpipe pipe:vplasma to the in-fork plasma.

Live-validated 2026-07-24. Real pods keep directserial realport:COM2; the
standalone vPLASMA app keeps namedpipe pipe:vplasma.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 12:48:07 -05:00
CydandClaude Fable 5 e6eba36713 emulator: net_diag.conf refresh + dev-rig run artifacts
net_diag.conf brought in line with the current dev-rig conf family (NIC
repoint era); LAST.EGG / FAILURE.LOG are the latest pod-run leftovers the
archive tracks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 12:02:49 -05:00
CydandClaude Fable 5 c26d7baf8d docs: published team credits + archive context index
README gains "The team" -- the published VWE BattleTech credits roster
(scan supplied July 2026) cross-referenced against the 1994-96 source in
this tree; HISTORY.md links the podbay developer-script names to the
credited team members.  context/ adds the archive index README and its
generator script.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 12:02:49 -05:00
CydandClaude Fable 5 bd082d8c4a PlasmaNew: ESC X/x box commands in the replica + MW4 bench stream
Implements the firmware-disasm-confirmed Firestorm box protocol in the
MatrixPortal replica (ESC X outline / ESC x mode-fill via a BOXOP operand
collector, plus the ESC Y region op), updates FIRMWARE.md with the findings,
and adds FS-plasma-bench.ps1 -- a simulated MW4 serial stream for benching
the display without a pod.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 12:02:37 -05:00
CydandClaude Fable 5 110ecec2ca BT410 Phase 5.3.18: flying missiles -- spawn/home/detonate LIVE, teardown open
The Missile entity family is reconstructed: Projectile : Mover gets a real
make-path ctor; Missile hosts the Seeker + MissleThruster roster (the
AUTHENTIC surviving SEEKER.HPP / MISTHRST.HPP interfaces -- the 1995
misspelling included) and flies the binary's guided integrator (@004bef78):
age/lifetime, seeker re-lead with the decoded loft constants
(200/50/300/0.1), thruster burn, steering (gain 4.0, deadband 1e-4), and
the proximity fuse delivering the cluster damage ONCE with the launcher as
inflictor.  Live-verified end-to-end: LAUNCH -> flight telemetry (thruster
visibly accelerating) -> DETONATE on the target ~0.9s out.

OPEN: the first ~Missile off the death row completes, then the frame
page-faults with EIP in the heap -- the first entity/subsystem teardown the
reconstructed sim has ever run.  The spawn path is therefore OPT-IN
(BT_MISSILE_FLIGHT=1); default SRM delivery stays the 5.3.18a instant-hit
cluster (verified 1:1, zero exceptions).  Forensics + engine truths
(SearchList crashes on non-directory ids; MakeMessages need FAMILY resource
ids; explosionModelFile is a model-list INDEX) in MISSILE.NOTES.md.

Also fixed on the way: the Subsystem NAME ctor left damageZone
UNINITIALIZED (latent garbage for every name-built subsystem).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 11:58:40 -05:00
CydandClaude Opus 4.8 9dd0aa8702 emulator: RIO panel click-to-press + lamp flash blink
Make the in-fork glass cockpit fully interactive and finish the lamp model.

- Click-to-press: clicking a button's region in a VDB head window presses that
  RIO address into the game (lights white-hot while held, releases on mouse-up).
  vpxlog rt_wndproc hit-tests the click against the bezel geometry (MFD 4 top /
  4 bottom, radar 6 left / 6 right; the radar bottom indicators are display-only,
  not clickable); head windows are tagged via GWLP_USERDATA. Clicks arrive on the
  VPX render thread, so the seam serialrio RIO_HostButton() queues them under a
  mutex and pollInput() drains them on the emu tick through the same incHold/
  decHold path as the keyboard/pad -- so a click also lights the bezel.
- Flash blink: decode the lamp byte's flash bits (RioLampState 1 slow / 2 med /
  3 fast) -- an unpressed flashing lamp now toggles between its brightness level
  and off (half-periods 500/250/125ms); solid lamps unchanged, a press still wins.
- Focus: head windows are WS_EX_NOACTIVATE so a button click never pulls
  foreground off DOSBox (which would demote the emu thread and stop SDL polling
  the pad); a click uses MA_NOACTIVATE too. Do NOT enable SDL background joystick
  events -- that raced the emu-thread controller poll against SDL's main-thread
  pump and crashed the process.

Explode layout only, and only when a serial=rio port is present.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 11:50:53 -05:00
CydandClaude Fable 5 f2424e9fdc BT410 Phase 5.3.18a: salvo economy corrected -- one cluster hit per trigger
BT411 task-#62 truth: the arcade fires ONE cluster Missile per salvo and its
zone damage lands EXACTLY ONCE (burstCount feeds only the gyro bounce +
splash falloff, never the zone armor formula). The 5.3.16 per-missile
delivery loop was ~6x too lethal. MISLANCH now sends a single salvo-split
damage message (verified 1:1 FIRED-to-damage in the mutual fight; zero
exceptions). The cluster splash joins the Missile entity wave.

Also: BT_FORCE_ZONE=n dev hook (pin every hit -- the cascade verification
knob) landed with 5.3.17's runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 11:19:27 -05:00
CydandClaude Fable 5 d4f0aef6e1 BT410 Phase 5.3.17: the destruction cascade -- zone deaths DESTROY things
Mech::DamageZone::TakeDamage is the full binary cascade (@0049c690): LOD
artifact router with same-attacker clustering (@0049c40c, hysteresis 0.33),
artifact parent level = mean of children, Energy-hit generator shorts
through the crit plug binding (novice-exempt), weighted CriticalHit
(@0049ccc4), zone-death allotment push (SendSubsystemDamage @0049c9a8) and
the segment-table destruction descent (RecurseSegmentTable @0049cad4 --
SIBS + DESCEND via the engine EntitySegment iterators).

Supporting bricks: Mech's OWN handler table with the TakeDamage override
(latches lastInflictingID @0x43c; gyro feed staged; cylinder table
deferred); friend class Mech__DamageZone (authentic); subsystem private
zones ARMED (structureReference + armorByFacing[5] normalized -- crits are
measurable); ApplyDamageAndMeasure; ForceCriticalFailure sets
DestroyedState so the weapon FSMs' GetSimulationState()==1 hard gate went
live; Generator::ForceShort + PoweredSubsystem::ForceShortRecovery.

Fight-verified (mutual BT_SPAWN_ENEMY + BT_FORCE_ZONE=8 concentrated fire):
arm zone to 1.0 -> sibling searchlight zone dies (Searchlight2/ThermalSight
crits destroyed, HUD degrading) -> DESCEND kills the gun zone -> PPC_2 +
ERMLaser_2 + Condenser6 DESTROYED and FALL SILENT (0 shots after death;
undamaged PPC_1 keeps firing); vital-zone hit = MECH KILL; PPC hit shorted
GeneratorD. This art has zero artifact zones (redirect=0 across all 20 --
router verified dormant-correct). Smoke + novice regressions clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 11:13:28 -05:00
CydandClaude Opus 4.8 86d6b950e5 emulator: B2 RIO lamp/button bezel on the VDB heads + explode-view polish
Draw the cockpit RIO buttons around the mono-MFD and radar heads (explode
layout), lit by the host-commanded lamp state -- the display side of the
in-fork glass cockpit. Live-validated 2026-07-24 (operator).

- vpxlog.cpp: per-head button bezel in pal_draw, reading serialrio's new
  RIO_GetPanelState seam. The 5 MFD heads get red buttons (4 top / 4 bottom,
  100px tall tucked under a grown 640x500 display so a 10px lip shows); the
  radar gets amber Secondary/Screen side columns (6x 104px each) plus a
  centered bottom indicator strip (the 4 spares). Lamp byte decoded to
  off/dim/bright (vRIO RioLampState, brighter of the two brightness fields);
  a press shows white-hot. No labels -- the MFD shows each button's function.
  Also swapped the two upper-outer MFD window NAMES to match their (already
  position-swapped) desktop locations.
- serialrio.{cpp,h}: RIO_GetPanelState(lamps, pressed) accessor -- returns
  false when no rio port, so non-rio configs and other heads render normally.
- pod-launch: in explode (dev) layout the Division bridge is a normal, freely
  movable window -- not pinned topmost (Focus.cs) and nudged to 8,40 so its
  title bar clears the top of the screen (its client was at 0,0, pushing the
  frame off-screen). Cockpit/kiosk keeps the topmost + 0,0 borderless look.

Only affects the explode layout and only when a serial=rio port is present.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:43:41 -05:00
CydandClaude Opus 4.8 11f0a082e3 emulator: repoint dev-rig confs to the current NIC (realnic F70BADD7A)
The old Network Bridge adapter (DB5521D) no longer exists on the dev rig;
pcap now binds the physical Realtek directly. Update the tracked rendered
confs so a networked launch from the repo tree reaches the host. Symptom of
the stale value was the guest looping "NO PACKET DRIVER FOUND" (pcap open
fails -> NE2000 absent -> ODI stack can't bind). net_diag.conf left alone
(unrelated pre-existing churn).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:43:41 -05:00
CydandClaude Opus 4.8 73b597df35 emulator: in-fork virtual RIO cockpit board (serial1=rio)
Emulate the cockpit RIO board natively inside the DOSBox-X fork, driven by
an SDL game controller + host keyboard -- no external vRIO process and no
named pipe. The game<->board round trip runs in-process at the emulated
UART's own cadence, so the serial round-trip dropouts (livelock, TXMAXIDLE,
rxburst, 15s retry) can't occur.

- serialrio.{cpp,h}: RIO 9600-8N1 device state machine + protocol codec +
  input mapping, transcribed from the vRIO app (VRioDevice + Protocol +
  InputRouter/BindingProfileFormat), minus transport/pacer/UI/locks.
  B0 = gamepad (5 axes + joystick column). B1 = keyboard field: MFD banks on
  the letter rows, F-keys = Secondary/Screen, numpad = flight controls,
  LShift/LCtrl throttle slew; PAUSE/ScrollLock toggle panel<->DOS; vRIO-
  grammar bindings file via bindings:/VWE_RIO_BINDINGS.
- vpx-device/README.md: device notes + apply steps (the DOSBox-X src tree is
  git-ignored, so this dir is the tracked source of truth; the three sdlmain
  keyboard-hook edits are documented there).
- net_{loop,rp}_rio.conf + deploy templates; render-bridge/
  gauge_arena_rio_sound.conf (standalone -egg trim); pod-launch --rio
  (mutually exclusive with --pipe).

Additive: real pods keep directserial realport:COM1; vRIO-over-pipe keeps
namedpipe pipe:vrio. Validated live 2026-07-22 (pad + keyboard field +
console networking) on the dist install.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:43:41 -05:00
CydandClaude Fable 5 76cfc64353 BT410 Phase 5.3.16: targeting + damage -- two-mech fights are LIVE
The simulation is now a FIGHT. Mech target slot (+0x388), authentic
Loaded->Firing gate (viewFireEnable && HasActiveTarget), UpdateTargeting
range refresh, and SendDamage via the engine TakeDamageMessage.

MECHDMG.CPP is BORN against the AUTHENTIC surviving CODE/BT/BT/MECHDMG.HPP:
the Mech ctor's Pass-3 hull zone fill constructs Mech__DamageZone per zone
(the Entity base only allocates the raw pointer array -- unfilled slots were
crash #1; a base-class fill parses each record short and skews the stream --
crash #2). Streamed ctor parses the full BT tail (flags / Scalar-weighted
criticals with the Master+Dynamic plug gate / LOD redirect table) and
normalizes the armor economy: scale[type]=1/(raw x armorPoints), legs
halved -- BT411-verified @0049ce50.

Live two-mech verification (BT_SPAWN_ENEMY harness in DropZoneReply):
20 named hull zones stream on both mechs; PPC lands 11.77/hit (12 x
chargeRatio^2, type 4), ER-M laser 3.43 (type 3), SRM6 salvos 5.83 x 6
rounds on independent zones (type 2, one message per missile -- burstCount
is NOT in the zone formula); repeat hits climb linearly, leg zones absorb
at half scale, zones reach 1.0 Burning/Destroyed. No-target and novice
regressions hold (weapons load but never fire without a target).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 10:21:43 -05:00
CydandClaude Fable 5 b772e716fe BT410 Phase 5.3.15: the electrical charge model -- real charge, closed feedback loop
The emitter charge cycle is now the authentic electrical model end to end.

- WIRE-VERIFIED resource fix: the Emitter block is graphicLength /
  dischargeTime / seekVoltage[5] (FRACTIONS of the generator's rated voltage,
  -1 sentinel) / seekVoltageRecommendedIndex. The old two-field guess read
  seekVoltage[3]=0.99 as "dischargeTime 0.99s" -- the true PPC discharge is
  0.2s, and the calibration reads seekV={6000,7000,8000,9900} x rated 10000,
  recommended gear 2: the documented curve exactly.
- Ctor calibration (@004bb120): energyTotal=(dmg+heat)x1e7; EC pins
  E=0.5V^2EC to energyTotal at the recommended gear; voltageScale calibrates
  the exponential charge to reach seekV[rec] in the authored RechargeRate
  seconds cold; damageFraction = the damage share.
- TrackSeekVoltage (@004ba838) + ChargeTimeScale (@004b0d50): the charge
  integrates from the generator through the heat-stretched time scale, and
  the I2R loss heats the GENERATOR -- verified GeneratorA at 477.9K charging
  its PPC (BT411 predicted ~+480K) while the avionics generator idles at 77.
  Hot generators charge slower: the heat/firepower feedback loop is CLOSED.
- Emitter::ComputeOutputVoltage override (now virtual, as in the binary
  vtable): dial = level/seekV[gear], 0.01 snap, over-1 clamp.
- Sub-stepped Loading (1/60s slices -- the BT411 weapon-brick fix) + the
  documented-divergence overcharge rescue. VERIFIED: the PPC loads at level
  ~7920, inside the binary's exact [7920,8080] snap window.
- FireWeapon energy algebra (@004bace8): damage/heat = energy shares x
  chargeRatio^2. VERIFIED dmg=11.78 / heat=1.079e8 at ratio 0.9906. The
  heatCost x 1e7 partial is retired -- heat now comes from the energy.

Zero Fail; jams/shutdowns regressions stay live under spam.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 09:14:56 -05:00
CydandClaude Fable 5 6451a0a38d BT410 Phase 5.3.14: AggregateHeatSink -- the bank + the ambient radiator (the heat EXIT)
The central heat bank is now its real class: the binary's 0x0BBE
AggregateHeatSink (the value our VDATA enum named HeatSinkClassID -- there is
no streamed plain HeatSink; the segment walk now builds the bank).

- Ctor (@4ae8d0): heatSinkCount from res +0xFC (bhk1 = 6, matching its six
  condensers); thermalConductance x 0.1 x count (231000 -> 138600); ambient
  setpoint 300 (the mission [mission] temperature overwrite joins the
  Mech-PlayerLink wave).
- RadiatorSimulation (@4ae73c) replaces the base heat step on the bank -- THE
  system's only heat exit: relax toward the ambient target with rate
  k = conductance x (1-damage) x (coolant/capacity) x flowScale / mass; tail
  tops the bank's coolant from the attached store via the DrawCoolant virtual
  (base 0 until the reservoir-attach wave). VERIFIED signed-correct: the bank
  warms 77 -> 300 from the cold start, then flips to actively radiating
  (-48K..-167K/step) once fired heat pushes it past ambient. Until now heat
  only ever POOLED in the central sink; the mech now genuinely sheds it.
- Reservoir master path: capacity = 0.05 x bankCount x streamed = 6 (the
  authentic tank), refilled.

Zero Fail; the expert economy regression stays green (202 heat events under
forced spam).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 08:54:21 -05:00
CydandClaude Fable 5 c01e57ab22 BT410 Phase 5.3.13: cockpit-button message layer -- valves, cooling, flush LIVE
The subsystem-family cockpit buttons now work through the authentic Receiver
dispatch -> per-class handler-table path. The id space decodes cleanly:
Receiver::NextMessageID == 3, so ToggleCooling = 3 on the HeatSink chain and
the per-class id 4 is MoveValve on a Condenser / InjectCoolant on the
Reservoir -- same number, different class, the binary's per-receiver-class
convention.

- HeatSink::ToggleCoolingMessageHandler (@004ad6f8, id 3): novice-locked,
  press-only; toggles coolantAvailable + coolantFlowScale together.
- Condenser::MoveValveMessageHandler (@4ae464, id 4): novice-locked; cycles
  the valve 1->5->50->0->1 and calls Condenser::RecomputeValves (@0049f788):
  every condenser's coolantFlowScale = valve / sum-of-valves. The ctor now
  streams the AUTHENTIC flowScale=0; the Mech ctor seeds equal shares once at
  spawn -- the flowScale=1 interim is retired.
- Reservoir::InjectCoolantMessageHandler (@4aee70, id 4): novice-locked;
  press arms the flush when the tank holds charge, release drops it.
- MechSubsystem::NoviceLockout() (@4ac9c8): owner -> playerLink -> experience
  == novice; unlinked mechs read unlocked.
- DEV harness BT_PRESS_VALVE / BT_PRESS_FLUSH: dispatch the REAL messages ~6s
  into the mission from Mech::Simulate.

VERIFIED: spawn shares 6 x 0.166667; one MoveValve press -> Condenser1
valve=5 flow=0.5, others 0.1 (valve/sum exact); flush arms via the real
button message; NOVICE locks both presses (valve lines stay spawn-only, zero
flush). Zero Fail throughout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 08:43:01 -05:00
CydandClaude Fable 5 e1e5a9a6db BT410 Phase 5.3.12: experience gates + coolant system -- novice/expert modes REAL
The player-experience system now gates the entire heat/jam economy, verified in
BOTH directions with a one-token egg edit (TESTNOV.EGG, experience=novice):
NOVICE = 212 fire cycles all pinned at T=77, zero jams, zero shutdowns (the
heat model authentically absent); EXPERT = the full economy (duty cycles, 119
shutdowns + 2 authentic jams under forced fire). Zero Fail in both.

- BTPLAYER: the flag block renamed to its TRUE semantics (simLive @0x25c,
  heatModelOn @0x260 -- the FUN_004ad7d4 master switch, advancedDamageOn pair,
  levelFlag26c/270, experienceLevel); the ctor rows were already
  binary-accurate (nov 0000 / std 1011 / vet 1111 / exp 1101); accessors +
  [exp] sentinel.
- HeatSink::HeatModelActive() + ProjectileWeapon::LiveFireEnabled(): owner
  mech -> Entity::GetPlayerLink() -> the BTPlayer flags, NULL-permissive.
  Gates: both HeatSinkSimulation phases, every weapon fire-heat dump, and
  CheckForJam is now the AUTHENTIC form (LiveFireEnabled + heatLoad<=0
  early-outs + the minJamChance floor; the interim heat-degraded gate retired).

- THE LOAD-BEARING FIX: Mech ctor SetValidFlag(). Every 1995 entity ctor tail
  marks itself valid; ours didn't -- Entity::Dispatch routes messages to an
  INVALID entity into the deferred event queue, so the PlayerLink bind (and
  every directly-dispatched mech message) silently never landed and the gates
  read a NULL player forever.

- THE COOLANT SYSTEM (authentic bodies, byte-verified constants: HeatLoadScale
  0.002 -> heatLoad now in [0,1]; equalize eps 1e-4; draw floor/ON
  0.0025/0.003): UpdateCoolant (damage-scaled draw -- an undamaged mech leaks
  nothing), BalanceCoolant (full clamp chain from ConductHeat), DrawCoolant
  base=0 with the RESERVOIR override as THE SOURCE; Reservoir reconstructed
  (capacity overlays thermalCapacity, CoolantSimulation + the full
  InjectCoolant flush distribution, BT_FORCE_FLUSH dev hook); Condenser
  RefrigerationSimulation (massScale = (1-damage)*refrigerationFactor >= 1 --
  the heat pump that chills the bank; valveState inits 1; digit-suffix number
  fix). Deferred: AggregateHeatSink family, cockpit-button handlers
  (MoveValve/ToggleCooling/InjectCoolant), TrackSeekVoltage charge model.

Research driven by a 4-agent workflow dossier over the BT411 RE (verbatim
bodies for every function above + the egg experience plumbing).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 22:15:07 -05:00
CydandClaude Fable 5 b1cbbe1c9b BT410 Phase 5.3.11: weapon heat consequences -- jams + thermal shutdowns LIVE
The heat economy now bites back, verified under sustained fire:

- Ballistic gate 1 (@4bbd36): destroyed-state or own-sink FailureHeat pins
  full recoil + the unavailable alarm (the NoAmmo roach-motel re-asserts).
- CheckForJam (@4bbfcc): p = 0.41*T/failT clamped [minJamChance, 1.0], rolled
  per granted shot against the MUNGA uniform Random. Interim heat-degraded
  gate stands in for the deferred LiveFireEnabled novice switch (the exact
  spurious-cold-jam trap the BT411 port documented). VERIFIED: SRM6s jam at
  T~1200-1320 after riding past the authored 1000-degree threshold, and stay
  jammed (mission-reset recovery only) -- authentic.
- Emitter hard gate (@4baab9): FailureHeat drops the beam state + charge each
  frame; SELF-RECOVERING once conduction cools the sink below failure.
  VERIFIED: the PPC settles into an emergent thermal duty cycle -- fire at
  ~1930K, spike to 2562K, shutdown, cool, refire -- firing exactly as fast as
  its sink sheds heat; the lasers oscillate around ~2200K.

Zero Fail. Deferred: LiveFireEnabled/HeatModelOff experience gates,
mech-disabled gate halves, coolant depletion, jam recovery via mission reset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 21:20:37 -05:00
CydandClaude Fable 5 4391ddd322 restoration: import PlasmaNew — the plasma display replica effort
Moved from the vRIO repository to live with the other hardware
restoration work. Contains the PD01D221 reverse-engineering reference
(FIRMWARE.md, recovered from the TMS27PC512 EPROM dump) and the working
Matrix Portal S3 + HUB75 replica firmware (flashed and bench-tested
2026-07-22, including the G/B pin swap for these panels, the
PIN_BUTTON_UP/DOWN fix, and power-on cursor rendering).

The vPLASMA reference oracle (src/VPlasma.Core) and the VPlasma.Wire
differential-test tool stay in the vRIO repo; PlasmaNew pre-move commit
history is there too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:20:25 -05:00
CydandClaude Fable 5 57507cb15c BT410 Phase 5.3.10: power/heat wave -- the thermal + electrical economy is LIVE
Weapons dump firing heat into their own sinks, sinks conduct through the
Condenser bank into the central HeatSink, temperatures drive the degradation/
failure alarms, and every powered subsystem tracks its generator through the
electrical state machine. Verified: the authentic fire-discipline game -- a PPC
spikes 77->709K per shot, relaxes to 441K across its 5s reload, and sustained
fire climbs 441->674->838->960K, brushing the authored 1000K degradation
threshold. All calibration values match the BT411 audit exactly (central bank
1.39e6, PPC sink 174000, thresholds 77/1000/2000).

- HEAT: HeatSinkSimulation (@004ad924 absorb->T->load->conduct->alarm),
  ConductHeat/ComputeHeatFlow (@004ad8ac/@004ad9ec two-body equilibrium
  relaxation; flow==0 exactly at uniform T), UpdateHeatLoad (15-sample filter);
  heat-state enum + accessors; installed by the HeatSink ctor.
- WIRE-VERIFIED: HeatSink resource gains linkedSinkIndex -- THE missing
  ancestry int that shifted every descendant block +1 (voltageSourceIndex had
  been reading the linked-sink index: "power sources" appeared to be
  Condensers). Conduction topology wired from it at construction:
  weapons->Condensers1-6->central; Generators->Condensers; Reservoir->central.
- MECHWEAP resource: authentic pip tail (pipPosition int + pipColor 3 floats +
  pipExtendedRange int) per the BT411 verified overlay; with linkedSinkIndex
  this closes the whole +3 alignment mystery (ProjectileWeapon pad deleted).
  True bhk1 reads: PPC recharge 5.0s (not 1.0), discharge 0.99s, range 900.
- POWERSUB: ctor resolves voltageSourceIndex -> GeneratorA-D (wire-verified),
  taps via Generator::TapVoltageSource (-1 when full); PoweredSubsystemSimulation
  (@004b0bd0) electrical FSM (Starting/NoVoltage/Shorted/GeneratorOff/Ready);
  GeneratorSimulation partial (start/short-recovery timers).
- Weapons: power step at sim head; Loading recharge gated on electrical Ready
  (authentic @4bbdf5); FireWeapon dumps firing heat. HEAT UNITS: the chain is
  1e7-native with TWO authoring conventions -- energy weapons store small
  (PPC=11, x1e7 from the energy algebra), ballistics store native (SRM6=5.06e7,
  dumped raw; double-scaling it was the bring-up runaway-temperature bug).
- SENSOR: authentic gating (@004b1c4c) -- power step, electrical-Ready gate,
  heat-state switch (Degradation x0.5 / Failure 0). Verified Ready + 100%.

Deferred: coolant depletion/venting, the novice HeatModelOff gate, the charge
model (TrackSeekVoltage/voltage sag/I2R), weapon gate 1 + jam roll, the central
sink's forward-linked drain. Zero Fail across fire + neutral runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 23:55:48 -05:00
CydandClaude Fable 5 ab7191dc38 BT410 Phase 5.3.9: ballistic fire path -- SRM launchers fire salvos + run dry
The SRM6 MissileLaunchers now run the authentic ballistic fire cycle against
their linked AmmoBins, wire-verified end to end.

- PROJWEAP ProjectileWeaponSimulation (PARTIAL, binary @004bbd04 -- fully
  recovered in the BT411 RE): one trigger-edge sample; the dry-bin gate pins
  NoAmmo(7) every frame; Loaded(2) -- the AMMO PULL LIVES IN THE CALLER (a
  denied shot does the 4->2 denial blip with NO ammo pulled -- early-returning
  gates from FireWeapon while the caller cycles the alarm is the 1995 "denied
  shot fakes a full firing cycle" defect class); granted -> Firing ->
  FireWeapon -> Loading (or NoAmmo on the emptying pull), recoil=rechargeRate;
  Loading bleeds recoil -> Loaded when a round is chambered, dial animates;
  Jammed(5) held; NoAmmo(7) = the roach-motel (re-asserts unconditionally).
  Deferred: electrical step, gate 1, eject, jam roll, HasActiveTarget.
- AMMOBIN: FeedAmmo() + GetAmmoState() (1 round-ready / 2 EMPTY).
- ammoBinLink wired from the resource's ammoBinIndex (the bin's roster slot).
- WIRE-VERIFIED RESOURCE ALIGNMENT: a raw-stream dump pinned the
  ProjectileWeapon field block +3 ints past our staged struct (the MechWeapon
  resource ancestry runs 3 ints short -- the pip-family fields). Fixed with
  resourceAlignPad[3]; phantom muzzleVelocity/missileCount tail dropped.
  Confirmed on-wire: ammoBinIndex=27/29 (the exact bin slots), tracerInterval=1,
  minTOF=0.5, minVolt%=0.3, minJam=0.05, missileCount=6 (an SRM6!).
- MECHWEAP: weapon-state enum extended to the full 1995 set (DryTrigger 1,
  Jammed 5, TriggerDuringJam 6, NoAmmo 7; count 8).
- MISLANCH FireWeapon PARTIAL (@004bcc60: heat + salvo spawn only -- spawn
  needs the entity/targeting waves).

VERIFIED headlessly (BT_FORCE_FIRE): bins resolve with the authored 24 rounds;
SRM6s fire 6-missile salvos at the authored 1s cadence, rounds 24->0; after
the 24th salvo the launcher goes DRY and stays silent across ~3,000 subsequent
fire events while the energy weapons keep cycling. Zero Fail.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 23:18:39 -05:00
CydandClaude Fable 5 896bbbed4d BT410 Phase 5.3.8: weapon fire path -- authentic attribute table + fire state machine
The energy weapons now run a live fire cycle, and the REAL trigger wiring is in
place: the 1995 MechWeapon attribute table (binary @0x511890) is published with
PINNED IDs, so the streamed per-mech fire-button mappings bind TriggerState
(0x13) -> fireImpulse and the controls push writes the trigger into the weapon.

- MECHWEAP: attribute enum + table (PercentDone 0x12, TriggerState 0x13,
  DistanceToTarget 0x14, TargetWithinRange 0x15, WeaponRange 0x16,
  EstimatedReadyTime 0x1A, RearFiring 0x1B, WeaponState 0x1C). Pads bridge the
  chain gap 2..0x11 -- LOAD-BEARING: AttributeIndexSet::Build leaves uncovered
  gap slots as uninitialized garbage, so every ID up to the max must be covered
  (they shrink to the authentic 0x0F..0x11 when the parent attribute waves land;
  SENSOR.HPP pins PoweredSubsystem::NextAttributeID at 0x0F).
- MECHWEAP: fire-machine members (fireImpulse/previousFireImpulse,
  rechargeLevel, rangeToTarget, effectiveRange, estimatedReadyTime, recoil,
  weaponAlarm 0/2/3/4); CheckFireEdge (@004b9608, BIT-PATTERN int compare --
  TriggerState carries raw ControlsButton ints whose release value is a float
  NaN; IEEE compare would latch the detector shut); ComputeOutputVoltage
  (@004b9c9c recharge dial).
- EMITTER: EmitterSimulation (PARTIAL @004baa88) -- Firing/Loaded/Loading FSM
  on the weapon alarm, viewFireEnable gate at Loaded->Firing, recoil-decay
  recharge over the authored RechargeRate seconds; FireWeapon (PARTIAL
  @004bace8) discharge bookkeeping; ChargeLevel re-pinned to the authentic
  0x1D; index re-chained to MechWeapon's. Deferred: electrical charge model
  (TrackSeekVoltage), heat dump, HasActiveTarget gate, beam/damage submission.
- BT_FORCE_FIRE dev hook: trigger pulse whenever Loaded (auto-fire cadence).

VERIFIED headlessly: forward view -- PPC_1/2 + ERMLaser_1 cycle FIRED->LOADED
at the authored 0.6s/1s cadence, rear lasers silent; BT_FORCE_LOOK=3 -- ONLY
the rear lasers (ERMLaser_2/3) fire. The view-fire x fire-FSM interlock holds
both directions. Zero Fail. (SRM6 ballistic FSM = a later increment.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 23:01:57 -05:00
CydandClaude Fable 5 d422dfffc0 BT410 Phase 5.3.7: weapon view-fire gating + build header-stamp hardening
Completes the weapon-side half of the look commit:

- MECHWEAP: real rearFiring/viewFireEnable members (binary @0x334/@0x3E0) +
  accessors. Ctor resolves REAR-FIRING authentically (@004b99a8 tail): the
  mount SEGMENT site name is tested for the 'b' (back) marker -- only the back
  gun ports (sitelbgunport/siterbgunport) carry it. Spawn = forward-armed.
- MECH CommitLookState: re-arms every roster weapon per view (forward =
  non-rear, LOOK-BACK = rear-mounted, side/down = none) and flips the HUD
  reticle pip group with the authentic Reticle::Front/RearFiringWeaponsOn flags
  (BT411's raw "bits 1/2" resolved to their 1995 names).
- MECHWEAP: defined MechWeapon::MessageHandlers (0-entry set inheriting the
  Subsystem chain) -- it was declared-but-undefined, and the surviving CODE
  PPC.CPP binds the inherited name into PPC::DefaultData; the zero-filled
  common block was a latent NULL deref (same trap as Emitter::AttributeIndex).

Discovery: the TEST.EGG mech genuinely mounts TWO REAR LASERS (ERMLaser_2/3 on
the b-ports). Verified on a full-clean baseline (engine 181 + bt 42, 0 fail):
look-behind arms exactly the rear lasers, disarms the other five,
pipMask=0xfffffffe; neutral run holds forward-armed; zero Fail.

build410.sh HARDENED against the stale-obj layout-skew trap this wave exposed:
cc() skipped recompiles on obj-vs-.cpp timestamps only, so the MECHWEAP.HPP
member additions left emitter.obj on the OLD MechWeapon layout -- its
chargeLevel=0.0f write landed exactly on the new rearFiring (a silent cross-TU
struct skew, heisenbug-grade). cc() now honors header stamps: a source410
engine-header edit invalidates every bucket, a BT/BT_L4 header edit
additionally invalidates the game buckets. CODE/ is immutable, no stamp needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 22:43:38 -05:00
CydandClaude Fable 5 eb8bba3195 BT410 Phase 5.3.6: look-button state machine -- full eyepoint composition
Reconstructs the binary's five-state LOOK machine (controls mapper tail,
part_013.c:396-459) as proper members/methods:

- MECHMPPR: LookState enum (None/Left/Right/Behind/Down) + lookState/
  previousLookState (out of reserved[24] -> [22]). InterpretControls picks the
  state from the look buttons each frame and on a CHANGE calls
  Mech::CommitLookState. BT_FORCE_LOOK=<1..4> dev hook.
- MECH: authored look angles lookLeft/Right/Front/BackAngle (deg->rad from the
  GameModel resource, guarded) + lookPitch/lookYaw + CommitLookState(int):
  side looks yaw by the authored angle, look-behind = yaw pi + lookBackAngle
  pitch, look-down = lookFrontAngle pitch, forward = identity. The per-frame
  eyepoint compose in Simulate adds the live Torso elevation on top.
  (reservedState [208] -> [202].)
- Deferred inside the commit (needs MechWeapon viewFireEnable/rearFiring):
  per-view weapon fire re-arm + HUD pip group-mask flip.

Verified headlessly: model reads clean authored angles (L=60 R=-60 F=-10 B=0
deg -- ModelResource layout confirmed again); BT_FORCE_LOOK=3 fires ONE commit
([look] state=3 yaw=3.14159 pitch=0) and the per-frame compose holds eyeYaw=pi
with eyePitch=0.349066 (lookBackAngle + the 20deg-clamped elevation). Zero Fail.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 22:08:50 -05:00
CydandClaude Fable 5 87b1c048cf BT410 Phase 5.3.5: eyepointRotation -- torso elevation pitches the eye/aim-ray, not a joint
Investigated the next planned binding (elevation -> gun joints jointlgun/
jointrgun) before implementing it, and found it was the wrong target: BT411's
own reverse-engineering history records that NO gun/arm elevation joint exists
in the authentic engine. The pod's stick-Y aim pitches the COCKPIT EYE and the
weapon boresight directly (mech4.cpp @~5219, pixel-calibrated against real
screenshots) -- the torso geometry never tilts for elevation on this mech
family. Reconstructing a gun-joint binding would have been invented behavior.

- MECH.HPP: EulerAngles eyepointRotation member (out of reservedState, now
  [208]) + GetEyepointRotation() accessor.
- MECH.CPP: composed each frame in Simulate -- eyepointRotation =
  EulerAngles(Radian(Normalize(lookPitch+elevation)), Radian(Normalize(lookYaw)),
  Radian(0)), reading Torso::CurrentElevation() via sinkSourceSubsystem.
  lookPitch/lookYaw are 0 until the look-button wave (BTCommitLookState) lands.

Verified: BT_FORCE_ELEV=0.8 -> torsoElev and eyePitch both read 0.349066 (the
authentic 20 degree resource limit) every frame, exact match; neutral -> 0.
Zero Fail. Corrected TORSO.NOTES.md's prior (wrong) "next: gun joints" note.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 21:51:28 -05:00
CydandClaude Fable 5 9fff0e969e BT410 Phase 5.3.4: Mech::ResolveJoint + skeleton-joint binding (torso twist)
Reconstructs the shared skeleton-joint resolver and binds the first subsystem
(Torso) to the live skeleton.

- Mech::ResolveJoint(name) (mech.cpp @00424b60): GetSegment(name) -> segment
  joint index -> GetJointSubsystem()->GetJoint(idx).
- Verified the JointedMover base streams the full skeleton headlessly:
  jointCount=19, 40 named segments (gun joints jointlgun/jointrgun, shoulders,
  hip, leg joints jointlthigh..jointrankle). BT_MECH_LOG [skel] summary;
  BT_SKEL_DUMP lists every segment/jointIdx.
- Torso resolves + binds its twist joint (ResolveJoint(torsoHorizontalJoint))
  and pushes currentTwist onto it via Joint::SetRotation (hinge->Radian,
  ball->EulerAngles yaw). The TEST.EGG mech has a fixed torso (horizJoint='',
  enabled=0) so its twist path is inert -- correct + guarded, zero Fail.

Next joint piece: elevation -> gun joints (jointlgun/jointrgun, the fixed-torso
aim mechanism) via weapon-joint binding; then gait leg animation. Both are
VISIBLE only under the renderer; headlessly the joint transforms are loggable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 16:17:37 -05:00
CydandClaude Fable 5 3a0920acc0 BT410 Phase 5.3.3: torso weapon-elevation aim -- completes the control surface
Reconstructs Torso::TorsoSimulation (installed as the Torso's per-frame
Performance) and wires the torso aim into MechControlsMapper::InterpretControls,
so the control surface now covers aiming as well as locomotion.

- TORSO: analogElevationAxis/analogTwistAxis + SetAnalog* setters,
  CurrentElevation()/GetHorizontalEnabled() accessors. TorsoSimulation slews
  currentElevation += analogElevationAxis*baseElevationRate*dt clamped to the
  vertical limits, and (if horizontalEnabled) currentTwist by the twist axis
  clamped to the horizontal limits (authentic torso.cpp core; the skeleton-joint
  application is deferred with the render wave).
- InterpretControls: routes stick pitch (stick_y, squared) into the torso
  elevation every mode; Std/Vet route stick yaw into the twist. BT_FORCE_ELEV
  dev hook.

Verified: BT_FORCE_ELEV=0.8 -> the elevation slews up and clamps at the authentic
verticalLimitTop = 0.349 rad = 20deg; neutral -> 0; zero Fail.

Deferred: skeleton-joint application (render wave), HUD free-aim slew,
look/eyepoint commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 16:02:54 -05:00
CydandClaude Opus 4.8 84ea322a18 Document the weapon-fire/pick-range investigation, paused per user
Records tonight's finding before stopping this thread: the user correctly
pushed back on an oversimplification (the renderer isn't a purely passive
wire tap -- the protocol has a real synchronous pick-query shape, sect_pixel/
sect_vector, where the board runs its own Moller-Trumbore ray-triangle test
and replies with range+identity). Checked directly against the REAL firmware
(not just the reference software renderer's notes):

- sect_pixel/sect_vector never appear in this capture -- unused by this build.
- Action 0x23 (the notes' fire/pick candidate) gets no reply from the real
  firmware either (checked via the h_reply hook, which fires on ANY
  firmware-issued reply) -- but its payload doesn't match the documented
  FLYK format at all (no screen coords; a counter-tagged discharge-event
  shape instead), and its wire context (list_remove + zone-color + a
  PSFX-shaped 0x1d) reads as a weapon-discharge notification with visual
  consequences, not a pick query.

Net: open question, not resolved. BT4.10/MUNGA's real pick/range mechanism
(if board-side at all) is not yet identified. Paused per the user rather
than continue an open-ended search without a specific pointer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 15:58:48 -05:00
CydandClaude Fable 5 0cc7713865 BT410 Phase 5.3.2e: source authentic per-mech locomotion params from GameModel resource
The Mech ctor now reads walkingTurnRate / runningTurnRate / maxAcceleration /
throttleAdjustment from the GameModel resource (SearchList(GameModelResourceType)
-> ModelResource), replacing the bring-up-default constants for those fields.

Each read is sanity-guarded (out-of-band -> keep default) since BT411 flags parts
of the ModelResource layout as mis-decoded -- but the live values came back clean
and authentic: walkTR=75deg/s, runTR=50deg/s, maxAcc=30 u/s^2, throttleAdj=1.0
(walking turns faster than running; maxAcc matches the madcat note), confirming
our reconstructed struct layout is correct for these fields.

Verified: at walk speed the authentic 75deg/s tightens the turn circle to r~6.9
(=speed/turnRate); zero Fail. Still on defaults (pending LoadLocomotionClips):
reverseStrideLength (top speed) / walkStrideLength / reverseSpeedMax -- measured
from the walk/run animation clips.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 15:52:20 -05:00
CydandClaude Opus 4.8 ccc6ee6914 Fix HUD vertical flip: dpl2d Y grows DOWN, not up
User-reported live: the torso-twist tape appeared at the TOP of the reticle
(should be bottom) and the range ladder filled the wrong direction. I had
assumed dpl2d used a standard math y-up convention (y0=bottom, y1=top) and
inverted accordingly.

Found the exact, already-solved answer in vrview_gl.py's own HUD vertex
shader comment: "dpl2d Y grows DOWN (screen convention): period VHS footage
shows the twist dial UNDER the reticle and the range bar filling top-to-
bottom; mapping straight onto GL's Y-up NDC rendered the whole HUD flipped"
-- the exact same bug, already diagnosed and fixed there. y0 (the smaller
raw view-rect value) is the TOP of screen, y1 the bottom -- direct mapping,
no inversion needed.

Verified offline: the composited reticle now shows the twist tape (with its
bowtie shape) at the bottom and the range ladder/pips reading top-to-bottom,
matching BT411's documented layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 15:46:03 -05:00
CydandClaude Opus 4.8 3e766c13c1 Wire the HUD reticle into live_render.py
Integrates hud2d_overlay.HudTracker into the live pipeline: fed exactly one
wire record per firmware consumption (synchronized with r.qi, not just queue
arrival, so hud2d_root's view/display-list state matches what the firmware
has actually processed at render time), composited onto each rendered frame
before present.

Verified via the same integration pattern (truncated direct-queue boot, no
socket overhead) against netdeath-20260708.fifodump: the reticle composites
starting at wire command 22906 (frame 22) -- identical resolution point and
visual result to the standalone hud2d_overlay test. A full-mission run
through the live socket path is genuinely slow around this capture's "battle"
content (multi-billion firmware steps per command in that section, pre-
existing and unrelated to this change) but not stalled -- confirmed by
reaching the same cmd/frame numbers via the direct path in 14.7s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 14:48:14 -05:00
CydandClaude Fable 5 ebf0d8c23f BT410 Phase 5.3.2: DRIVABLE mech -- authentic control interpretation + locomotion
The mech now drives from the authentic control chain (faithful route). Verified
headlessly: full throttle -> walks straight at top speed (30 u/s) along heading;
throttle+turn -> walks a circle (radius = speed/turnRate); neutral -> holds pose;
zero Fail.

MechControlsMapper::InterpretControls (mechmppr.cpp @004afd10) -- installed as the
mapper's per-frame Performance (roster slot 0, ticks before the mech):
- speedDemand = topSpeed*throttle*fwdScale (reverse inverts); soft stick (square)
  / pedal (cube) response; Basic=stick turn, Std/Vet=pedal turn; speed clamped
  while turning hard. Reads owner stride via Mech accessors.
- BT_FORCE_THROTTLE/BT_FORCE_TURN dev hooks for headless verification.

Mech::Simulate drive -- consumes the mapper demands:
- accelerate currentBodySpeed toward speedDemand (maxBodyAcceleration);
- authTurnRate = lerp(walkingTurnRate, runningTurnRate) by ground speed + over-run
  falloff (mech4.cpp master-perf @0x4aa3d3);
- integrate heading via Quaternion::Add(prevPose, (0,turn*rate*dt,0));
- facing = world -Z basis (GetFromAxis(Z_Axis)); worldLinearVelocity = facing*spd;
- integrate position (increment-1 core).

MECH.HPP: 9 named locomotion members out of reservedState (now [211]) --
walking/runningTurnRate, reverse/walkStrideLength, reverseSpeedMax,
forwardThrottleScale, maxBodyAcceleration, body/currentBodySpeed. BRING-UP
DEFAULTS (authentic values come from the model resource + LoadLocomotionClips --
next refinement).

Deferred: gait-clip-exact advance + leg anim (needs animation subsystem/renderer),
model-resource sourcing, terrain drop, torso/free-look aim, telemetry filters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 14:46:30 -05:00
CydandClaude Opus 4.8 859529d6f2 HUD reticle: the missing wire mechanism found + composited (verified live)
The user asked to investigate why HUD never renders, reasoning it would also
inform the weapon-fire/targeting question. Root cause: the cockpit HUD (reticle
+ weapon pips) is NOT part of the 3D scene-graph wire traffic this renderer has
been parsing all session -- it's a completely separate 2D display-list protocol
(dpl2d_*, wire actions 0x29 NewDisplayList / 0x2b FlushDisplayList), which this
pipeline has never looked at.

BT411's context/gauges-hud.md + phases/phase-02-dpl2d-reticle.md already fully
documented this from the game's decompiled source (BTReticleRenderable, the
dpl2d opcode table). More directly useful: dpl3-revive/patha/vrview.py (the
OTHER renderer) already has a COMPLETE, TESTED interpreter for the wire-level
opcodes (hud2d_prims/hud2d_root) -- reused directly here rather than re-derived.

hud2d_overlay.py: HudTracker feeds incrementally from the same wire records
already flowing through this pipeline (minimal node type+body tracking, the
dcs->view list_add edge, and 0x29/0x2b display-list accumulation -- NOT a full
scene-graph, just what hud2d_root/hud2d_prims need). composite_hud() maps the
resolved primitives from view-rect space (x0..x1, y0..y1, y up -- confirmed
live: x[-1,1] y[-0.615,0.615], hither=0.25/yon=1300/zeye=1.732=cot(30 deg) --
independently cross-validating the earlier fog-formula derivation from live
wire data, not just the static BTDPL.INI) into pixel space and draws them.

Verified offline (netdeath capture): the HUD binding resolves at wire record
~22661 (later in the mission than earlier test frames, which is why it never
appeared in this session's spot-checks); composited frame shows a green
crosshair + tick-mark cross, a ring, and a right-side range ladder with
weapon-pip circles -- an exact match for BT411's documented reticle layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 14:40:31 -05:00
CydandClaude Fable 5 3364b65ae8 BT410 Phase 5.3.1: Mech::Simulate motion core -- mech integrates motion per-frame
Installs Mech::Simulate as the mech's per-frame Performance (was DoNothingOnce).
Reconstructs the load-bearing spine of the 1995 Simulate (mech4.cpp @004ab430):
integrate the body velocity into localOrigin (linearPosition.AddScaled(..,
worldLinearVelocity, dt)) and commit the Origin to the world transform with the
engine idiom (localToWorld = localOrigin, per ENTITY.cpp:988 / MOVER.cpp:850).

Uses the NAMED engine base Mover fields (localOrigin/localToWorld/
worldLinearVelocity) -- BT411's MechBaseLayoutCheck proved the WinTesla decomp's
raw this+0x100/0x260 offsets actually stomp those base fields; the clean 1995
build addresses them by name.

- MECH.HPP: Mech Performance typedef + SetPerformance + Simulate() decl.
- MECH.CPP: SetPerformance(&Mech::Simulate) at ctor tail; Simulate body.

Verified headlessly (BT_MECH_LOG "[sim] mech pos=" 1Hz): BT_DRIVE="5,0,10"
advances the mech +5.0/s x, +10.0/s z (y fixed) = exactly the injected world
velocity; no BT_DRIVE -> worldLinearVelocity is 0, mech holds spawn pose. Zero
Fail/Exception either way. BT_DRIVE is a retained dev hook for headless motion.

Deferred (locomotion wave): gait-cycle self-propulsion feeding worldLinearVelocity
(IntegrateMotion->AdvanceBodyAnimation, steered by mapper throttle/turn), heading
integration, terrain-height drop, cockpit telemetry filters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:45:24 -05:00
CydandClaude Opus 4.8 6d827cd9c8 VDB-NOTES: add canvas->6-displays data-flow overview + mermaid diagram
Narrative on-ramp before the register-level RE: two independent video
systems, the 16bpp byte-lane split (low=radar / high=5 mono MFDs), the
three Bt477 heads, and the octopus fan-out to the six cockpit displays.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 22:43:36 -05:00
CydandClaude Fable 5 3610ff1ecd BT410: entity simulation goes LIVE -- mission reaches RunningMission, mech + roster tick per-frame
The world now executes. Entity::Execute only PerformAndWatch's entities in
application state RunningMission, reached by two RunMissionMessages
(WaitingForLaunch->LaunchingMission->RunningMission). The 2nd is dispatched by
Player::ManageApplicationStatus when the launch fade expires -- but that only
runs inside PlayerSimulation, not the launch-phase HuntForDropZone. So the
player must switch Performance onto PlayerSimulation after it spawns.

- BTPLAYER.CPP DropZoneReplyMessageHandler: after CreatePlayerVehicle +
  InitializePlayerLink, choose the per-vehicle Performance by class -- Mech ->
  SetPerformance(PlayerSimulation)+SetScoringPlayerFlag; else CameraShipSimulation.
- BTPLAYER.CPP PlayerSimulation: chains the authentic base Player::PlayerSimulation
  (CalcRanking + ManageApplicationStatus + vehicle-pos copy + status service);
  console SCORE-delta flush deferred to the scoring wave.
- SENSOR.CPP SensorSimulation: minimal-safe partial (radarPercent = 1 -
  GetSubsystemDamageLevel, sensor healthy) so the roster tick path can't Fail
  once RunningMission engages. The heat/electrical gating awaits the power/heat
  sim wave (PoweredSubsystem/Generator/HeatSink/HeatWatcher *Simulation staged).

Verified: BT_MECH_LOG run reaches "[tick] roster live (first Sensor frame)",
two RunMissionHandler transitions, zero Fail/Exception. The mech is executed
each frame; its BODY Performance is still DoNothingOnce (real Mech::Simulate =
Phase 5.3, now the frontier).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 12:20:23 -05:00
CydandClaude Fable 5 812ca99652 BT410: reconstructed source BOOTS into the live game loop (zero Fail)
Phase-5.1 crash solved + two BTPlayer bricks reconstructed. The tree now
builds AND boots end-to-end with no staged Fail() reached, into the running
per-frame game loop.

- EMITTER.CPP: define Emitter::AttributePointers[]/AttributeIndex (ChargeLevel,
  chained to Subsystem::AttributeIndex) and point Emitter::DefaultData at it.
  EMITTER.HPP declared the index but the .cpp never defined it, so the surviving
  PPC.CPP (PPC::DefaultData binds inherited Emitter::AttributeIndex) and
  GAUSS.CPP (chains it) captured a NULL activeAttributeIndex -> GetAttributePointer
  faulted on the first PPC (roster slot 22). Fix clears all 33 roster slots and
  the control-mapping binding block.

- BTPLAYER.CPP InitializePlayerLink: real body -- dispatch PlayerLinkMessage
  (our EntityID) to playerVehicle + replicants (mirrors engine
  CameraDirector::CreateCameraShip). No staging.

- BTPLAYER.CPP VehicleDeadMessageHandler: non-death flavours (-2 drop-zone probe,
  >=0 respawn re-post) delegate to the authentic Player base handler; only the
  -1 death cycle stays staged (needs mech4 + scoring roles).

Verified: 516-line smoke run, zero Fail/Exception/abort; boot reaches
LBE4ControlsManager::Execute per-frame, waiting only on absent RIO hardware.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 11:51:04 -05:00
CydandClaude Opus 4.8 a89d08e1c9 Commit the vrend410 firmware asset + RE tooling; gitignore run artifacts
- vrend410.mng: the production firmware, a HARD dependency of the committed
  live pipeline (emu_main.py MAPS['vrend410'].fw loads it) -- the repo couldn't
  run the live renderer without it.
- txdn_probe.py / texstore_trace.py / texel_trace.py: standalone reverse-
  engineering probes (texture-source/DRAM-write/texel tracing) documenting the
  texid-binding and texel-format investigations.
- .gitignore: exclude the firmware-decomp run artifacts (emu860c.pyd rebuildable
  from source, *.log/*.pkl/*.png renderer outputs + caches), editor junk, and
  the game runtime state/logs written by running the pod.

Left untouched deliberately: the parallel Mech session's in-progress
restoration/source410/BT/{EMITTER,BTPLAYER}.CPP edits (theirs to commit) and
the tracked game runtime state (LAST.EGG/CTMIX.CFG etc. -- transient).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 11:43:46 -05:00