299 Commits
Author SHA1 Message Date
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
CydandClaude Opus 4.8 b447e71864 launch_live.ps1: enlarge exploded cockpit windows +25% (0.75 scale)
Per user request, bumped the cockpit MFD/radar windows from 0.6 to 0.75 of
native 640x480 (MFD 384->480, radar 288->360 logical). Live-verified on the
secondary: MFDs now render 331x266 (was 267x218, ~+24% after the primary's DPI
downscale), radar 251x346, all six still fit the 1707x1067 secondary with
margin, renderer unchanged on the primary. No wire stall.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 11:37:43 -05:00
CydandClaude Opus 4.8 f5fc241603 Clear the renderer background to the fog color, not black
User-reported: a hard black strip at the horizon, between the arena walls and
the sky geometry. Those pixels aren't covered by any polygon -- the game relies
on the board's own background clear there (the sky dome doesn't extend down to
the wall tops). Our renderer cleared the framebuffer to black, so the gap read
as a black band.

Fill the background with the FOG far-color instead: anything no polygon covers
is at effectively infinite distance = fully fogged, so the horizon gap now
blends seamlessly into the same haze the distant geometry fogs toward (filled
far pixels -> FOG_RGB via the fog blend; unfilled background = FOG_RGB directly
-> continuous). Offline-verified: the black strip becomes a smooth blue-purple
haze band. (The game's actual view-flush back_color may differ slightly from
the fog far-color; the fog color is the physically-consistent proxy until
back_color is parsed off the wire -- noted in code.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 11:33:09 -05:00
CydandClaude Opus 4.8 20dd8440d3 launch_live.ps1: -Explode puts the cockpit VDB windows on the secondary display
Adds -Explode: the pentapus cockpit instruments (radar + 5 mono MFDs, the VDB
video-splitter decode) render on the SECONDARY monitor while the reconstructed
i860+GPU 3D renderer stays the main out-the-window view on the primary. The
window rects auto-fit to whatever the non-primary display is (detected via
System.Windows.Forms.Screen, so it survives a monitor rearrange), scaled to
~0.6 and offset into that display's virtual-desktop region; the DOSBox SVGA
screen parks clear of the cluster.

The cockpit windows live in vpxlog's rt_main thread, which only starts under
VPX_RENDER=1 -- the same flag whose DOSBox-thread scene-graph decode previously
stalled wire delivery (fixed earlier by removing it). Feared this would bite
again; MEASURED and it does NOT: with VPX_EXPLODE + VPX_NOMAIN the heavy main-
window 3D render (rt_draw, gated on the main window existing) is skipped, so
rt_main only does the cheap per-tick MFD/radar blits and the DOSBox thread keeps
up. Live-verified: all 6 cockpit windows placed on the secondary (DISPLAY1),
renderer on the primary, wire advancing normally to cmd 23k+ (no command-8
stall), frames flowing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 11:25:47 -05:00
CydandClaude Fable 5 11680bcb3e Phase 5.1: mapper attributes VERIFIED resolving (throttle/controlMode non-NULL)
Trace confirms the MechControlsMapper (slot 0) publishes its control attributes
correctly: throttleAttr/controlModeAttr resolve to non-NULL pointers. So the
mapper's AttributeIndex works. The post-construction crash (GetAttributePointer
attr=0x13) is on a DIFFERENT object (EAX heap addr after the mapper) -- a streamed
watcher targeting another simulation, not the mapper. Localizing next. (BT_MECH_LOG
mapper trace retained.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 11:14:34 -05:00
CydandClaude Opus 4.8 66cef329d1 Implement fog: source-derived depth formula, not a guess
BTDPL.INI's fog values for this mission (testarn.egg = arena1/day/clear,
[ardayclear] leaf: fog=200.0 1250.0 0.32 0.3 0.5, clip=0.25 1300.0) are exact,
verified ground truth. The depth term took two tries: `tz` (texz) looked like
the right candidate (BT411 says fog is linear on "W") but measured live it
DECREASES with distance -- the wrong direction/shape. Re-derived from the
actual firmware source instead of guessing again: AS860/XFPROJ.SS's own
comment on the projection code ("pz=wz*invz, where pz=1 at near clip, 0 at
infinity") establishes the z-buffer value as a normalized hither/distance
quantity, giving distance = hither * 2^20 / raw_zbuf_value.

gpu_raster.py: added a 10th shader output channel carrying the raw z-buffer
float (previously only a filled/empty flag existed), and the fog blend using
this formula + BTDPL.INI's exact color. Verified offline: distance climbs
smoothly (394->1051) across a receding ceiling, landing inside the authored
200-1250 range with no further fudging; visually the horizon shows the
authored blue-purple haze, correctly absent near the camera.

IG-SHADING-MODEL.md: documented the false start (tz) and the source-grounded
fix, with the honest caveats that remain (2^20 exact scale, hither as a fixed
per-mission constant vs. per-view from the wire).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 11:13:55 -05:00
CydandClaude Fable 5 1f2f8ac42a Phase 5.1: MechControlsMapper publishes its 20 control-input attributes
Reconstructed the MechControlsMapper attribute table (StickPosition..PilotArray,
IDs 3-0x16 chained from Subsystem::NextAttributeID==2) + the real members
(stickPosition ControlsJoystick, throttle/pedals/speed/turn Scalars, the look/
torso ControlsButtons, control/display/pilotArray ints) + AttributePointers[] +
AttributeIndex (chains Subsystem::AttributeIndex). Fixed the mapper hierarchy
statics in BTL4MPPR.CPP: L4/RIO/Thrustmaster ClassDerivations now chain correctly
(L4 defined first for static-init order) and all use MechControlsMapper::
AttributeIndex so the mapper instance publishes the control attributes the
streamed mappings bind to.

Mech still constructs (33 subsystems). The post-construction crash is UNCHANGED
(GetAttributePointer, attribute=0x13) -> so it's NOT the mapper; a streamed
AttributeWatcher (numeric ID 19) resolves on another object via GetSimulation.
Next diagnostic: which object/subsystemID the watcher targets. BT: 42 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 11:10:44 -05:00
CydandClaude Opus 4.8 9ebdfc4610 IG-SHADING-MODEL.md: document tonight's ground-truth findings for BT411
Extends the existing shading-model reference (already the established pattern
for this cross-check) with tonight's live-pod findings, explicitly framed for
the BT411 modernization team's consumption per the user's stated purpose for
this whole pipeline: a hardware-grounded source of truth where the software
renderer has open or hard-to-fix questions.

Added: a cross-reference index into BT411's context/ docs (rendering.md,
open-questions.md, combat-damage.md); the lit-color-plane finding (real
mechanism, confirmed values, wrong when used as a texture tint -- reverted
same session); fog (BT411's complete model, not yet ported here); and honest
open/uninvestigated status for HUD, weapon-fire rendering, and cockpit-cage,
each pointing at where BT411 has probably already solved the adjacent
question so it isn't re-derived from scratch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 11:02:48 -05:00
CydandClaude Opus 4.8 0527022ecd Revert texture tinting by the lit-color plane -- it double-colours
Found my own prior documentation (IG-SHADING-MODEL.md sec 2, written earlier
this session before compaction) that already establishes: the wire's 0x1a
texture uploads arrive ALREADY colourised (the material ramp is pre-baked on
this path, per the shipped libDPL header model cross-referenced against
BT411's context/rendering.md). Tinting them again with the lit-color plane
double-colours the result -- exactly the "more colors but neither correct"
outcome reported live (sky/ground now differ, but neither right).

Keep the lit-color plane for FLAT (untextured) polys only, where it replaces
a hardcoded placeholder with real per-polygon data; textured polys sample
unmodified, as documented. The underlying r24/g24/b24 mechanism itself is
still real and worth understanding precisely (see open-questions tracking),
but applying it as a texture tint was the wrong model.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 11:01:27 -05:00
CydandClaude Opus 4.8 3473f5facf live_render: skip the per-frame PNG write once a window is showing
Image.fromarray(...).save() ran on EVERY frame regardless of whether a
window was already displaying it -- pure disk-I/O overhead adding to the
render-vs-game latency gap. Only write PNGs when there is no window (the
offline/headless case, where they are the actual output).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 10:43:30 -05:00
CydandClaude Opus 4.8 854ec0b216 Live-view fixes: stale-frame skip, mid-mission catch-up, lit-color plane
Three real issues found while watching the live-render session against the
actual running game and fixed:

1. STALE-FRAME SKIP (live_render.py): the renderer treated every single
   draw_scene as a frame to fully render+present. The game ticks at a
   measured ~28 draw_scene/s (matches the documented FAST 28Hz SOS clock),
   faster than our render, so a backlog built continuously -- we were
   perpetually rendering OLD content, which read as "frozen or 30s+ behind."
   Fix: SockSource.has_pending() detects an already-buffered next record;
   when backlogged, skip render+present (still consume/step normally) so we
   always show the freshest available state instead of crawling through
   history. pump() now drains aggressively so the backlog check is honest.

2. MID-MISSION CATCH-UP (live_render.py --catchup): reconnecting to an
   already-running pod produced a black screen. Root cause: draw_scene only
   means "render what's already loaded" -- it carries no geometry itself.
   The socket tee forwards only NEW traffic to a fresh client, so a from-
   scratch firmware boot has an empty scene graph. Fix: replay vpxlog's
   archival VPX_FIFODUMP (a second, independent sink of the same wire,
   written since pod boot) as the initial queue before going live -- exactly
   the same catch-up vpxlog's OWN native bridge already relies on for this
   same reason.

3. LIT-COLOR PLANE (gpu_raster.py): user-reported "textures are mostly
   grayscale" and "no fog effect." Found the actual mechanism in the real
   firmware source (sda4 DIVPXMAP.H + EOF.C): TREEclmpintoMEM (op 0x5a)
   writes dvpx_r24/g24/b24 -- a per-polygon lit color -- which EOF.C later
   copies into dvpx_eofr/g/b (the final screen color) via a straight 24-bit
   CPY in the simple (unfogged) case. Our renderer decoded this instruction
   but never used it, instead hardcoding flat polys to a placeholder
   (60,60,70) and sampling textures with no lighting modulation at all.
   Verified live on the wire: the value is a DIRECT 0-255-ish brightness
   (not a [0,1] float to rescale -- observed range ~49..395 with real
   per-polygon variation, only the brightest few clipping), consistent with
   a straight clamp-to-8-bit write. Now decoded (addr 118/126/134, empirically
   confirmed against DIVPXMAP.H's r24/g24/b24 spacing) and applied: flat polys
   get the real lit color instead of the placeholder; textured polys are
   tinted by it (texture * lit/255). Offline-tested (no crash, visibly
   plausible result: a lavender-tinted floor with real near/far gradient
   replacing the flat gray-blue placeholder) -- NOT yet cross-checked
   against a ground-truth screenshot, so treat the exact hue as provisional
   even though the underlying mechanism is verified real.

Honest scope note: this changes gpu_raster's rendered colors on purpose, so
it no longer bit-matches the OLDER frame_*.png CPU reference from before this
fix (that reference predates lit-color decode). The geometry/perspective/
raster correctness those bit-identity tests proved is unaffected -- only the
color post-process changed, layered on top.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 10:42:51 -05:00
CydandClaude Opus 4.8 ef7db9f2e4 Fix live-render lag: bulk memory read + masked texture compositing (~2.1x)
User-visible bug: connected the reconstructed renderer to the REAL running pod
for the first time (M4d demo) and the render fell increasingly behind the live
game, plus looked near-grayscale. Root-caused with direct profiling instead of
guessing:

1. VPX_RENDER=1 was set in launch_live.ps1 (copied from launch_pod.ps1's
   defaults without thinking it through) -- it starts vpxlog.cpp's OWN native
   GL render thread (rt_main), which contends with our renderer for the same
   CPU/GL resources. Live-caught: with it set, wire delivery stalled at wire
   command 8 for 90+s; removing it, the same mission reached command 21,488
   in the same window. This was the dominant cause of the visible lag.

2. gpu_raster.build_prims() scanned the whole per-draw program window
   (0x08158000-0x08170000, ~24k words) via a Python-level r32() call per word
   every frame. Added emu860c.dump_range(lo,hi)->bytes (one bulk C read) and
   rewrote build_prims to index a numpy array instead -- verified byte-identical
   dump_range output first, then reconfirmed the whole render is STILL
   bit-identical to the CPU reference (frames 0/5/11, differ>24 = 0.000%).

3. Profiling after (1) showed the per-texture compositing loop was the real
   remaining cost: 62ms/frame doing full-832x512-frame fancy-indexed texture
   sampling for EACH of 13 textures, regardless of how few pixels use each one.
   Added dpl_sampler.composite_masked (sample+composite only the pixels a
   texture actually covers; verified equivalent to composite() on a partial
   mask in dpl_sampler's own conformance test) and hoisted the invariant
    out of the loop.

Combined: render time 95.8ms -> 54.9ms/frame (~1.75x from this change alone,
~2.1x vs the original ~117ms/frame baseline), still bit-identical to the CPU
reference throughout.

launch_live.ps1: removed VPX_RENDER, renamed the automatic-variable shadow
 ->  (flagged by PSScriptAnalyzer), added -Pin <fifodump> to
pre-load the full texture set (bit-identical to offline) instead of the
incremental default, for a demo that wants correct color immediately rather
than waiting on the live mission's own upload order.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 10:07:55 -05:00
CydandClaude Opus 4.8 bf234842cb Commit the texel-format fix + shared dpl_sampler module (was uncommitted)
These changes were made and verified earlier this session (the all-cyan bug
fix is already recorded as SOLVED in project memory) but never got committed --
render_final.py has referenced dpl_sampler as a module in its docstring since
ac4142e7, yet dpl_sampler.py itself was never added, leaving the repo unable
to run the M4b/M4c/M4d renderer scripts that all import it.

texstore.py: fix the texel word format. SVT texels are [pad,B,G,R] (ground
truth: dpl3-revive patha/vrboard.py do_texels + stage_assets.py); reading RGB
as bytes (0,1,2)=(pad,B,G) zeroed red, producing the all-cyan render. Correct
read is bytes (3,2,1).

dpl_sampler.py: the authentic IG-board texture-value model (wrap repeat/clamp,
near-black CUT keying) factored into its own tested module, distilled from the
shipped libDPL headers -- see IG-SHADING-MODEL.md (also added, referenced by
render_final.py's docstring but likewise never committed).

igc_exec.py: OP48/OP49 candidate opcodes (pixel global-X/Y seed into mem) added
to the CPU golden-model executor alongside the existing SCMEMA seed handling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 10:07:16 -05:00
CydandClaude Opus 4.8 a689bce660 Fix launch_live.ps1: don't enable Dave's native render thread
VPX_RENDER=1 was copied in from launch_pod.ps1's defaults without thinking it
through -- it starts vpxlog.cpp's OWN native GL render thread (rt_main),
which is redundant with (and contends against) our reconstructed renderer
riding the FIFOSOCK tee. Live-caught: with it set, wire delivery stalled at
command 8 for 90+s (window rendered, but slowly/poorly per live observation);
removing it, the same mission reached command 21,488 (10 frames) in the same
window. My two earlier successful ad-hoc bring-up tests had already removed
this env var by hand -- the script just hadn't caught up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 09:53:58 -05:00
CydandClaude Opus 4.8 7b1866a5cb M4d: real-pod live bring-up WORKS + namedpipe serial (retire realport)
The reconstructed renderer now runs live behind the REAL game: dosbox-x.exe
(arena mission) streams its wire over vpxlog's VPX_FIFOSOCK and live_render.py
renders it in real time -- 18-20 live frames from the actual running game,
coherent perspective-correct arena. The real binary opens the FIFOSOCK listener
at device init and accepts our client (verified standalone first).

launch_live.ps1: turnkey one-command bring-up (lean pod + reconstructed renderer
on the socket, no pentapus/bridge; -Sound / -Port / -PodOnly / -Present).

Serial: switch the render-bridge confs from 'directserial realport:COM1/COM2'
(com0com retired, COM1/COM2 gone) to the fork's namedpipe backend
(serial1=namedpipe pipe:vrio, serial2=namedpipe pipe:vplasma). DOSBox is the
pipe client; the vRIO/vPLASMA apps are servers; an unconnected pipe = unplugged
cable so the mission still renders. Live-verified: both pipes CONNECTED to
running servers (Serial1 -> \.\pipe\vrio, Serial2 -> \.\pipe\vplasma), RIO
input flowing, game rendered faster than the COM-less run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 09:33:57 -05:00
CydandClaude Opus 4.8 e0a2073dff M4c-device: the LIVE socket seam -- proven bit-identical over VPX_FIFOSOCK
live_render.py connects to vpxlog.cpp's existing VPX_FIFOSOCK tee (the DOSBox-X
C012 device), consumes the game's wire AS IT ARRIVES, and runs the firmware and
the socket CONCURRENTLY -- pumping the socket whenever the firmware's receive
point drains -- rendering each draw with the shared verified GPU raster.

gpu_raster.py factors the M4c-raster renderer out of m4b_gpu so offline and live
share ONE render path (the 6-edge fp64 raster + verified texel decode).

feed_sock.py mimics vpxlog's FIFOSOCK server (streams a real capture in delayed
chunks) so the live path is validated end-to-end WITHOUT the flaky live pod.
Result: 12 frames streamed live at 2.3 fps; with --pin (equalized texture
availability) the live frames are BIT-IDENTICAL to the offline reference
(12/12 differ>24 = 0.000%) -- the seam is faithful. The default incremental
texture model is more authentic (the board only has texels it has received);
its divergence from offline is purely the M5-B tid%ntex placeholder, orthogonal.

Live topology now closed end-to-end offline:
  game/capture -> C012 VPX_FIFOSOCK -> live_render -> firmware -> GPU -> frames.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 09:22:36 -05:00
CydandClaude Opus 4.8 b688e0e4bd M4c-raster: GPU render BIT-IDENTICAL to the CPU reference, ~50x faster
m4b_gpu.py moves the per-draw rasterization to a GPU compute shader (nearest-z
winner per pixel) with the verified M5 texel decode as a vectorized numpy
post-pass. Output matches the M5-verified CPU render (frame_*.png) bit-for-bit
across all 12 frames (differ>24 = 0.000%), at 1.4s/12 frames vs 69.9s (~50x;
the firmware's 3.7s now dominates -- the render keeps up with real-time).

Reaching bit-identity took finding two real bugs (honest trail in M4B-RESULTS.md):
  1. 4-edge clip: the shader tested only edges[:4] while the CPU clips with ALL
     edges (up to 6); 5-6-edge polys bled past their boundary and overwrote
     neighbours -- the 18% region divergence on the receding walls/floor. Fixed
     with 6 edge slots.
  2. float32 planes: A*x+B*y+C in float32 flipped the z-test winner vs the CPU's
     float64 at contested depths. Fixed with fp64 in the shader.

exp_precision.py is the diagnostic that REJECTED int-truncation as a reconciler
(it worsens float32/64 sensitivity to ~60%; true fixed-point width is a separate
spec item). Verified honestly by measuring, not eyeballing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 09:14:18 -05:00
CydandClaude Opus 4.8 695aacec30 M4b: offline end-to-end seam -- live mission -> faithful frame SEQUENCE
m4b_frames.py fuses the two proven halves: battle_frames.py's live per-draw
program capture (from running-firmware C memory) + render_final.py's verified
M5 texel render (perspective divide + real-texture decode). Result: the whole
renderer chain runs offline from a real fifodump -- production VREND.MNG on the
C i860 core -> per-draw coefficient program -> faithful frame -> PNG sequence.

Verified over netdeath-20260708.fifodump: 12 frames, scene assembles draw by
draw (26->48->78->87->127 quads), frame 11 = coherent perspective-correct arena
interior (tiled floor to vanishing point, ceiling, side structures). This is
the dress rehearsal for the live DOSBox seam -- identical pipeline, wire from a
file instead of the C012 device.

FINDING: firmware is real-time-capable (3.7s), the CPU numpy per-poly render is
the bottleneck (69.9s/12 frames, ~5.8s/frame). The fix already exists: the
conformant GPU tile path (igc_gpu). Moving the per-draw raster onto it is the
M4b->M4c bridge to real-time. Full writeup in M4B-RESULTS.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 09:01:50 -05:00
CydandClaude Fable 5 0248a78840 Phase-5 crash localized: control-mapping binding needs subsystem attribute tables
The Mech ctor fully constructs (33 subsystems / 7 weapons on real TEST.EGG data,
trace-confirmed). The crash is the control-mapping resource binding in
MakeViewpointEntity resolving subsystem attribute IDs via GetAttributePointer --
the subsystems publish none (reuse base AttributeIndex). Documented the precise
phase-5 step-1 scope (per-subsystem AttributeIndex + the ID chain) in MECH.NOTES.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 08:58:47 -05:00
CydandClaude Fable 5 b8f5928887 Mech ctor PROVEN: constructs 33 subsystems + 7 weapons from real model data
Live trace (BT_MECH_LOG) on the pod TEST.EGG mech: '[mech] segment walk done:
subsystemCount=33 weaponCount=7' -- the segment walk instantiates the ENTIRE real
subsystem roster (31 subsystems + 2 sentinels, 7 weapons) from the actual streamed
model, no crash. The structural reconstruction is proven end-to-end on real data.

The crash is AFTER full construction, in MakeAndLinkViewpointEntity's attribute-
watcher setup (an AttributeWatcher resolving a subsystem attribute via
GetAttributePointer). Phase-5 frontier confirmed = per-subsystem AttributeIndex
tables + watcher wiring. Trace gated behind BT_MECH_LOG.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 08:55:42 -05:00
CydandClaude Fable 5 961d27233c Mech phase 4 complete + phase-5 frontier documented
Records that the Mech constructor works live (segment walk instantiates the full
roster, mech links as viewpoint, mapper installs) and the precise phase-5
frontier: Simulation::GetAttributePointer via PerformAndWatch, because the
reconstructed subsystems reuse the base AttributeIndex and publish none of their
real attributes. Phase-5 plan (per-subsystem AttributeIndex -> watcher/chain/plug
wiring -> mech2/3/4 per-frame sim -> rendering) in MECH.NOTES.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 08:44:53 -05:00
CydandClaude Opus 4.8 ac4142e7c7 M5-B finding: the teal arena is FAITHFUL -- the game's texture palette is teal
Investigated per-surface binding: TXDN sources are the coefficient-program
transfer (0x815f region), not texel fetch -- texture selection is in the
board's texture unit (unmodeled), so exact texid->handle needs the board
tex-RAM model or wire scene-graph tracking. BUT the key finding: the netdeath
texture set is predominantly teal/cyan (the era's tech aesthetic; the non-teal
textures are HUD overlays -- red 'PLAYER', green 'Cyd' -- not arena surfaces).
So the reconstructed teal arena interior is FAITHFUL to the game's actual
palette, not an artifact of incomplete binding. Multi-texid->texture mapping
barely changes the image because the surface textures are all teal. Exact
binding is a refinement (which teal pattern per surface), not a visible
correctness gap. The visible reconstruction is complete and faithful.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 07:50:13 -05:00
CydandClaude Fable 5 b93c5d4bdd Mech phase 4: non-NULL default roster slots -- boot reaches the attribute binding
Unrecognised subsystem classIDs now get a base MechSubsystem (Laser/ParticleCannon
-> Emitter) so control/damage bindings that resolve a subsystemID find a real
subsystem, not a NULL plug. This moved the boot PAST the first Link::AddToPlug
NULL-deref; it now runs to Simulation::GetAttributePointer -- the subsystem
ATTRIBUTE system. Each reconstructed subsystem currently reuses the base
Subsystem::AttributeIndex, but the streamed control-mapping / gauge bindings
reference subsystem-specific published attributes (Sensor RadarPercent, Generator
OutputVoltage, Emitter ChargeLevel, ...) that aren't in the base index -> the
attribute lookup walks off the end. Reconstructing each subsystem's AttributeIndex
(+ plug/capability-chain wiring) is the phase-5 integration. BT: 42 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 07:15:00 -05:00
CydandClaude Fable 5 77041a1539 Mech phase 4: SetMappingSubsystem + MechControlsMapper ctor -- mech links as viewpoint
SetMappingSubsystem installs the control mapper into roster slot 0 (the streamed
control-mapping binds DirectMappings to subsystemID 0). MechControlsMapper ctor
constructs (per-frame InterpretControls deferred to phase 5). With RIO controls,
MakeViewpointEntity now builds the MechRIOMapper, installs it, and gets PAST the
controls check -- the mech constructs AND links as the viewpoint entity. Boot now
advances into the mission-startup / control-binding path (next: a real NULL-deref
there, deeper than the staged Fails). BT: 42 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 07:11:41 -05:00
CydandClaude Fable 5 c9aff18191 Mech phase 4: the Mech constructor + segment walk WORKS -- roster instantiates live
The Mech ctor now finds the model's subsystem-model-stream resource
(SearchList by SubsystemModelStreamResourceType), wraps it in a MemoryStream,
and walks the segments -- switching on seg->classID to  into the base Entity's subsystemArray, caching sensor/gyro/sinkSource/hud
and counting weapons.

KEY FINDING: the source410 VDATA ClassID values MATCH the 4.10 binary for the
core subsystems (0xBBD=Condenser, 0xBC3=Sensor, 0xBC5=Torso, 0xBD6=HUD, ...) --
BT411's 'mislabels' were its OWN wrong enum names. So the walk switches on the
real VDATA names cleanly. Unrecognised classIDs leave a NULL slot (roster stays
aligned) rather than aborting.

LIVE: boot now runs the full Mech ctor -- segment walk instantiates the roster,
no crash -- and advances PAST it to BTL4Application::MakeViewpointEntity, which
halts at 'Mech has no controls mapping!' (the slot-0 control mapper /
SetMappingSubsystem is the next brick). The largest function in the game
executes. BT: 42 ok; tree links clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 07:07:39 -05:00
CydandClaude Fable 5 4d175c959a Mech phase 3: named member layout + embedded-member construction
Replaced MECH.HPP reserved[331] with the named Mech-own members: cached subsystem
pointers (sensor/gyro/sinkSource/hud) + messageManager + weaponCount, the 5
capability chains (ChainOf<Subsystem*>), and the embedded status/animation state
(4 AlarmIndicators, Reticle, 3 StateIndicators, 2 SequenceControllers, NameFilter,
telemetryFilter[5]=AverageOf<Scalar>, 3 CStrings) + a reservedState pad for the
phase-5 per-frame fields. The subsystem ROSTER (subsystemArray/subsystemCount)
lives in the base Entity (GetSubsystem/GetSubsystemCount), so the Mech only caches
back-pointers.

The Mech ctor now CONSTRUCTS all embedded members (alarms Initialize'd, reticle
armed, state indicators sized, telemetry filters sized, sequence controllers
Init'd) -- compile-verified -- then halts at the segment walk (the remaining
piece). BT: 42 ok; tree links clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 07:00:51 -05:00
CydandClaude Fable 5 30c77fc9fc Mech phase 2: reconstruct the last 6 segment-walk subsystem classes
Completes every class the Mech-ctor segment walk instantiates: Reservoir
(: HeatSink), Searchlight + ThermalSight (: PowerWatcher), MechTech + Subsystem-
MessageManager (: Subsystem, surviving CODE headers), Actuator (: MechSubsystem
fallback). All ctors chain their parents; per-frame methods staged. Full subsystem
roster (~22 classes) reconstructed. BT: 42 ok; tree links clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 00:37:45 -05:00
CydandClaude Fable 5 9e0f045273 Mech phase 2->4: roster core complete (16 classes) + segment-walk blueprint
Records the Mech-ctor segment-walk factory (mech.cpp:1146-1330) with the full
MISLABELED ClassID->real-class map (VDATA enum names differ from the real
classes). 16 roster classes done; 7 remain for a complete walk (Reservoir,
HeatSink-bank, Searchlight, MechTech, ThermalSight, SubsystemMessageManager,
Actuator). Blueprint in MECHSUB.NOTES.md guides phase 4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 00:29:39 -05:00
CydandClaude Fable 5 1cab46ae72 Mech phase 2: reconstruct ProjectileWeapon + MissileLauncher + AmmoBin -- weapon family complete
ProjectileWeapon (: MechWeapon) -- ballistic (ammo-fed) base: ammo-bin link,
tracer/eject/jam/lead members. MissileLauncher (: ProjectileWeapon) -- salvo
launcher, using the surviving CODE MISLANCH.HPP (missileCount + per-salvo damage
split). AmmoBin (: HeatWatcher) -- ammo store with cook-off heat. All ctors chain
their parents + init from resource; fire paths staged.

Weapon family now COMPLETE: MechWeapon -> {Emitter -> PPC, GaussRifle;
ProjectileWeapon -> MissileLauncher} + AmmoBin. The surviving PPC.CPP/GAUSS.CPP
link against the reconstructed Emitter base. BT: 36 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 00:27:08 -05:00
CydandClaude Fable 5 7338b10474 Mech phase 2: reconstruct Emitter body -- PPC + GaussRifle now link
Emitter : MechWeapon -- energy-weapon (beam) base. Both surviving weapon leaves
(PPC.CPP, GAUSS.CPP/GaussRifle) derive from it and now compile against the real
base. Ctor chains MechWeapon + inits chargeLevel/dischargeTime; statics/dtor/test
real; FireWeapon (beam discharge) + CreateStreamedSubsystem staged. BT: 33 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 00:20:37 -05:00
CydandClaude Fable 5 9df4176956 Mech phase 2: reconstruct MechWeapon base (weapon subtree root)
MechWeapon : PoweredSubsystem -- base of all weapons. Ctor chains PoweredSubsystem
+ copies resource (recharge/range/damage/heatCost) into members + damageData.
FireWeapon is the virtual abstract stub (Fail 'should not be here'; PPC/Gauss/
MissileLauncher override it); SendDamage + CreateStreamedSubsystem staged.
BT: 32 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 00:15:31 -05:00
CydandClaude Fable 5 52cdf75348 Mech phase 2: reconstruct Myomers + Condenser leaves
Myomers (: PoweredSubsystem) -- locomotion muscle: seek-voltage drive table
(scaled by the generator's rated voltage via ResolveVoltageSource), efficiency +
heat-range members; MyomersSimulation staged. Condenser (: HeatSink) -- active
refrigeration in a coolant loop; valveState/refrigerationFactor/condenserNumber.
Both ctors chain their parents + init from resource; statics/dtor/test real.

Heat/power subsystem family now COMPLETE (11 classes): MechSubsystem,
HeatableSubsystem, HeatSink, PoweredSubsystem, Generator, Condenser, Sensor,
Myomers, HeatWatcher, PowerWatcher, Gyroscope, HUD, Torso. BT: 31 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 00:11:04 -05:00
CydandClaude Fable 5 4e16c49443 Mech phase 2: reconstruct Torso (: PowerWatcher) -- Mech sinkSourceSubsystem
Torso twist / weapon-elevation subsystem. Named tuning members (rates/limits in
radians, button-accel) + twist/elevation dynamics pad; ctor chains PowerWatcher,
copies tuning (RAD_PER_DEG), skips skeleton-joint resolution (deferred to the live
Mech link). statics/dtor/test real; TorsoSimulation/TorsoCopySimulation staged.
Cockpit leaf trio complete (Gyroscope/HUD/Torso). BT: 30 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 00:07:58 -05:00
CydandClaude Fable 5 a36e775c7c Mech phase 2: reconstruct HUD (: PowerWatcher) -- Mech hudSubsystem
Head-up display / targeting reticle subsystem, full named member set (reticle/
compass/lock/flicker/torso-readout state). Ctor chains PowerWatcher + inits per
the binary (visible/blinkState True, vectors zeroed, resource flicker/horizontal
copied); statusAlarm.Initialize(2). Added AlarmIndicator::Initialize(levels).
statics/dtor/test real; HudSimulation staged. BT: 29 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 00:05:00 -05:00
CydandClaude Fable 5 f63981ca3b Mech phase 2: reconstruct Gyroscope (: PowerWatcher) -- Mech gyroSubsystem
Eye/body stabilisation subsystem. Full resource struct + tuning members
(springs/damping/noise/percentages/damage multipliers) reconstructed; the
eye/body dynamics accumulators held in a reserved pad until the per-frame
GyroscopeSimulation is reconstructed (phase 5). Ctor chains PowerWatcher + copies
tuning; statics/dtor/test/reset real; sim staged. BT: 28 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 00:02:58 -05:00
CydandClaude Fable 5 84d43e6512 Mech phase 2: reconstruct PowerWatcher (: HeatWatcher)
Base of the Torso/HUD/Gyroscope cockpit subsystems. Ctor chains HeatWatcher +
watchdogAlarm(5) + minVoltage; statics/dtor/test/reset real; Simulation staged.
BT: 27 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 23:59:28 -05:00
CydandClaude Fable 5 999c98fe91 Mech phase 2: reconstruct HeatWatcher (: MechSubsystem) subsystem
Watches another subsystem's temperature, drives a 3-level alarm. Sibling branch
to HeatableSubsystem (derives straight from MechSubsystem). Ctor + statics + dtor
+ TestClass/TestInstance/ResetToInitialState real; WatchSimulation (per-frame)
staged. Unblocks PowerWatcher -> Torso/HUD/Gyroscope. BT: 27 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 23:57:50 -05:00
CydandClaude Fable 5 e9b51cd6ee Mech milestone phase 2: map the complete subsystem family tree
Records the full mech-subsystem hierarchy discovered while reconstructing the
roster, with per-class build status. 6 classes done + verified (MechSubsystem,
HeatableSubsystem, HeatSink, PoweredSubsystem, Sensor, Generator); the tree shows
the remaining intermediates (HeatWatcher : MechSubsystem, PowerWatcher :
HeatWatcher) that unblock the Torso/HUD/Gyroscope branch (the Mech's directly-
referenced subsystems), plus Myomers/Condenser leaves and the separate weapon
subtree. The reconstruction pattern is now a proven template (documented). Enables
efficient continuation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 23:51:55 -05:00
CydandClaude Fable 5 b5e0c10737 Mech milestone phase 2: reconstruct Generator (power source) -- power pair complete
Generator : public HeatSink (classID 0xBC3) -- the voltage SOURCE that
PoweredSubsystems tap (currentTapCount = attached loads). Reconstructed from the
BT411 decomp + the surviving GNRATOR.TCP partial: ctor chains HeatSink, seeds
ratedVoltage/outputVoltage/maxTapCount/startTime/shortRecoveryTime + stateAlarm(5)
= AlarmIndicator, derives generatorNumber from the segment-name suffix. Statics,
dtor, TestClass/TestInstance/ResetToInitialState real (ResetToInitialState mirrors
the .TCP); GeneratorSimulation (per-frame voltage/short) staged. Master-instance
SetPerformance install deferred (per-frame sim staged anyway).

Power system pair now in place: Generator (source) + PoweredSubsystem (load).
Roster classes so far: MechSubsystem, HeatableSubsystem, HeatSink,
PoweredSubsystem, Sensor, Generator. BT: 27 ok; tree links clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 23:49:53 -05:00
CydandClaude Fable 5 218afe3329 Mech milestone phase 2: reconstruct Sensor (first roster leaf) -- vertical slice complete
Sensor : public PoweredSubsystem -- the radar/targeting subsystem, using the
SURVIVING 4.10 interface (CODE/BT/BT/SENSOR.HPP) with a reconstructed SENSOR.CPP
body (from BT411 decomp). Ctor chains PoweredSubsystem, inits radarPercent/
selfTest/badVoltage, and installs the per-frame performance unless the owner is a
replicant (owner->GetInstance() != ReplicantInstance -- the clean form of the
binary's (flags & 0xC) != 4). Statics, dtor, TestClass/TestInstance,
ResetToInitialState, TakeDamage, DeathReset real; SensorSimulation (per-frame
radar) + CreateStreamedSubsystem staged.

Completes the first full vertical slice of the subsystem roster, all
compile-verified: MechSubsystem -> HeatableSubsystem -> HeatSink ->
PoweredSubsystem -> Sensor. The hierarchy backbone is proven; remaining leaves
(Generator, Gyro, Torso, HUD, Myomers, weapons) follow the same pattern.
BT: 27 ok; tree links clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 23:25:46 -05:00
CydandClaude Fable 5 6e96b5b570 Mech milestone phase 2: reconstruct PoweredSubsystem (roster layer 4)
Fixes the staged POWERSUB.HPP base (HeatableSubsystem -> HeatSink, the real
hierarchy) and reconstructs PoweredSubsystem: a HeatSink that draws electrical
power. Added members (voltageSource = SubsystemConnection, electricalStateAlarm =
AlarmIndicator(5), modeAlarm = AlarmIndicator(3), input/output/ratedVoltage) and
corrected the resource struct (voltageSourceIndex + thermalResistivityCoefficient
+ startTime). Ctor chains HeatSink and primes the electrical state; statics, dtor,
ResetToInitialState, TestClass/TestInstance real.

The voltage-source resolution (indexing the owner mech's subsystem ROSTER for the
powering generator) + master-instance electrical sim + AttachToVoltageSource are
deferred to the segment-walk phase (phase 4) where the roster is populated -- the
subsystem constructs with no attached source until then. Verified slice now:
MechSubsystem -> HeatableSubsystem -> HeatSink -> PoweredSubsystem. Compile-verified
(BT: 26 ok); tree links clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 23:22:49 -05:00
CydandClaude Fable 5 648f4e4705 Mech milestone phase 2: reconstruct HeatSink (roster layer 3)
HeatSink : public HeatableSubsystem (confirmed hierarchy) -- a thermal mass with
a coolant loop. Added to HEAT.HPP/.CPP: the class + its SubsystemConnection slot
helper + HeatSink__SubsystemResource. Ctor chains HeatableSubsystem and seeds the
thermal state from the resource (startingTemperature/degradation/failure/
thermalConductance/thermalMass), heatAlarm(3) = AlarmIndicator, heatFilter =
AverageOf<Scalar>(15). Statics, dtor, ResetToInitialState, TestClass/TestInstance
real; the per-frame thermal sim (HeatSinkSimulation/DrawCoolant) staged.

Also reconciled HeatableSubsystem to the 4.10 fields: its resource struct now
carries startingTemperature/degradationTemperature/failureTemperature/
thermalConductance/thermalMass (was the BT411-mislabeled thermalMass/degrade/fail/
coolingLoop set), and degradationTemperature/failureTemperature are base members
(HeatSink reads them). Verified slice now: MechSubsystem -> HeatableSubsystem ->
HeatSink. Compile-verified (BT: 25 ok); tree links clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 23:20:18 -05:00
CydandClaude Fable 5 82de438dfb Mech milestone phase 2: reconstruct HeatableSubsystem + map the roster hierarchy
HeatableSubsystem (HEAT.CPP) reconstructed + compile-verified (BT: 25 ok):
statics, ctor (chains MechSubsystem resource form + ResetToInitialState ->
currentTemperature=300, heatLoad=0), dtor, TestClass/TestInstance,
CreateStreamedSubsystem staged. Verified slice now: Subsystem -> MechSubsystem
-> HeatableSubsystem.

Also mapped the roster hierarchy from BT411, which is DEEPER than the staged
headers (recorded in MECHSUB.NOTES.md):
  MechSubsystem -> HeatableSubsystem -> HeatSink -> PoweredSubsystem -> {Sensor,
  Generator, ...}
Findings for the next steps: (1) HeatSink is MISSING entirely from the staged
tree and must be added between HeatableSubsystem and PoweredSubsystem;
(2) staged POWERSUB.HPP wrongly derives PoweredSubsystem from HeatableSubsystem
(real base = HeatSink) -- fix it; (3) PoweredSubsystem has inter-subsystem wiring
(resolves its voltage-source generator from the owner mech's subsystem roster),
imposing a segment-walk ordering constraint for the phase-4 ctor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 23:14:49 -05:00
CydandClaude Fable 5 0e274d3dde Mech milestone phase 2: reconstruct MechSubsystem base class (the roster foundation)
The base all ~30 mech subsystems derive from. Reconstructed against the 4.10
structure per the MECHSUB.NOTES analysis (NOT backdated from BT411's divergent
version):

- Header: expanded with the real mech-specific member set (statusAlarm =
  AlarmIndicator, vitalSubsystem, alarmModel, criticalReference,
  collisionCriticalHitWeight, printSimulationState, configureActivePress,
  resource). Dropped the speculative MessageHandlers static -- MechSubsystem
  reuses the base Subsystem sets (inherited Simulation handlers/attrs/statecount).
- CPP: statics (ClassDerivations/DefaultData), both ctors (light name+classID and
  resource), dtor, TestClass/TestInstance, and the damage API on the 4.10
  per-type model -- GetSubsystemDamageLevel/SetSubsystemDamageLevel read/write the
  real DamageZone::damageLevel [0,1]; ForceCriticalFailure; TakeDamage delegates
  to the zone. Both ctors build the damage zone the base leaves NULL.

Deferred (documented): the Mech__DamageZone per-type armour STREAM wiring (ctors
use a trivial armour-free base DamageZone placeholder for now) and
CreateStreamedSubsystem (model-load stream format). These are refinements above
the ctor frontier, not boot blockers.

Compile-verified (BT: 24 ok); full tree still links clean. Next: the subsystem
roster (GAUSS/PPC/SENSOR source + .TCP partials + decomp).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 23:10:11 -05:00
CydandClaude Fable 5 02a6355f0e Mech milestone phase 2 kickoff: MechSubsystem base analysis (damage-model de-risk)
Begins phase 2 (MechSubsystem base + subsystem roster) with the base-class
analysis that keeps the reconstruction on the 4.10 damage model. Key findings:

- All base deps survive: Subsystem (SUBSYSTM.HPP + staged SUBSYSTM.CPP),
  DamageZone (DAMAGE), Mech__DamageZone + BT damage model (MECHDMG.HPP),
  AlarmIndicator (just built).
- Finding 1: the staged base Subsystem ctor sets damageZone=NULL, so the derived
  MechSubsystem creates the Mech__DamageZone (BT411's zone-creation is right).
- Finding 2 (CRITICAL): the damage model DIVERGED. 4.10 is per-damage-TYPE --
  DamageZone has defaultArmorPoints + damageScale[5] {collision/ballistic/
  explosive/laser/energy} and STREAMS its own armour (DAMAGE.CPP:335,413).
  BT411 reconstructed a per-FACING armorByFacing[5]/structureReference model that
  MechSubsystem hand-seeds -- those fields exist only in BT411's ReconDamageZone
  proxy, NOT in the 4.10 zone. So BT411's MechSubsystem ctor CANNOT be backdated
  verbatim; doing so would install a damage model the 4.10 engine doesn't
  implement, corrupting every subsystem.

Corrected approach (now fully scoped, ready to write): chain base Subsystem,
create Mech__DamageZone, add mech-specific members + TakeDamage override, let the
zone stream per-type armour. Full detail in MECHSUB.NOTES.md; the staged
MechSubsystem__SubsystemResource (per-facing, BT411-copied) is flagged for
re-derivation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 22:17:43 -05:00
CydandClaude Opus 4.8 a24210adf0 M5 integrate: recognizable arena interior -- verified divide makes texturing land
render_final.py applies the oracle-verified perspective divide (texel8 =
texu/texz*2048, the *8 from the '3 integer bits') with per-quad texel tiling.
The battle frame now reads as a real first-person arena interior: a tiled
floor receding correctly to the vanishing point (panels shrinking toward the
horizon), a paneled ceiling, and a horizon band of structures. The verified
divide fixed the tiling frequency that washed out earlier attempts.

Monochrome because surfaces map to one texture family -- the exact
texid->handle binding needs the board texture-RAM model (texels go to board
tex memory via TXDN/io, not watchable DRAM; confirmed by the storage trace).
Geometry, perspective, texel decode, and the divide are all solved from the
live wire; per-surface texture selection is the last remaining piece.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 22:15:44 -05:00
CydandClaude Opus 4.8 b74b0c89e8 M5-A: the faithful IGC perspective divide, verified against the divlogo oracle
igc_divide.py implements EOF.C's texdivide bit-serial restoring division on
the pixel bit-memory, and extracts the texel per the '3 int | 8 texel | 5
subtexel' result layout ((field>>9)&0xff). Verified against the divlogo's
KNOWN mapping (u:0.001->0.999 across screen x[532,660]): the extracted texel
sweeps monotonically 0->251, and a fast float equivalent (texu/texz*2048)
tracks the bit-serial result to +/-1. This fixes the missing scale factor
(the '3 integer bits' = x8) that made earlier renders wash out. M5 plan in
M5-TEXTURING.md. Next: M5-B scene-graph material binding, then integrate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 22:13:10 -05:00
CydandClaude Fable 5 efc41f165b Mech milestone: reconstruct NameFilter (helper 3/3) -- helpers phase complete
NameFilter is the Mech's mechNameFilter (@0x36c), a small fixed debounce record
the ctor initialises. Reconstructed from FUN_00435a7c: a 10-word (0x28) struct,
Initialize() sets [2]=1 / [8]=[9]=-1 / rest 0 exactly. Header-only. Field
semantics unresolved (only caller is Initialize; reader is the unbuilt HUD) --
names best-effort, but size + init values are binary-exact, which is all the
Mech layout + ctor need.

All 3 missing embedded helper classes now reconstructed + compile-verified under
BC4.52 (AlarmIndicator, SequenceController struct+Init, NameFilter). MECH-LAYOUT.md
step 1 marked done. Next: MechSubsystem base + the subsystem roster, then finalize
MECH.HPP (probe sizeof==0x854), then the ctor + segment-table walk.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 20:56:06 -05:00