Same binary (md5 identical), different run: Eng1/Eng2/Comm read
17981/17981/15656 on some runs and 19011/19011/16909 on others. The shipped
binary is STABLE across three runs, so this is ours. Eleven consecutive
clean runs is what let it hide, and the dirty numbers are exactly the
pre-gate numbers from 5.3.32 -- the same artifact recurring.
Read off the raw words rather than inferred: in the artifact rect the shipped
binary writes ONE masked plane bit per pixel (0x4000 = Heat, 0x002D = sec),
while we write arbitrary 16-bit words (0x8BD6 / 0x8AD6 / 0x8AC0) that light
sec+overlay+Mfd1+Eng1+Eng2+Comm at once. Raw values going into a
plane-packed buffer -- which is why one bad draw appears as extra pixels on
six heads and as 483 MISSING on Heat at the same time.
Intermittent because these widgets are change-driven: on runs where the
coolant value never moves after the first paint, the bad draw never happens.
The six fixes of 5.3.33-39 stand (each moved specific attributable pixels).
What must be restated is the claim that four heads are exact -- true only
when this defect does not fire. The rig now needs a run-variance check
before any per-head number is believed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
y+0xb3 -> y+0xb4. Earlier this session I measured 0xb3 as the optimum and
wrote that the banner was at most 1-2px off -- that reading was confounded:
the strip-art misplacement (5.3.35) was ten times larger and overlapped the
same bands, so it dominated the metric and hid the one-row error. With the
strip art correct the title fringe isolated cleanly at 553/553 missing/extra
per head, and +1 collapsed it.
head missing extra
Mfd1 1107 -> 140 1646 -> 679
Mfd2 1616 -> 97 1981 -> 462
Mfd3 584 -> 31 1014 -> 461
Cockpit: 97.9% identical / 97% coverage, from 89.7%/92% at the start of the
session. Method note for the worksheet: fix the largest error on a head
FIRST, then re-measure the small ones -- a big overlapping defect makes a
small one measure as noise, or as already-optimal.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CoolingLoopConnection::Update wrote a constant 0 -- the documented inert stub
-- because HeatSink::coolantAvailable, HeatSink::linkedSinks and
Condenser::condenserNumber are protected with no accessor, so every MFD panel
showed OFF where the shipped cockpit shows the loop number.
heat.hpp now publishes GetCoolantAvailable(), ResolveCoolingMaster() and
GetCondenserNumber(), and the connection follows the binary: a sink with
coolant reports the loop number of the Condenser it is plumbed to, otherwise
frame 0. Same shape as PowerSourceConnection beside it, which was already
live and already matched.
The box now reads 4 / A against the shipped binary's 4 / A, pixel for pixel.
head missing extra
Mfd1 1331 -> 1107 1776 -> 1646
Mfd2 1908 -> 1616 2196 -> 1981
Mfd3 730 -> 584 1119 -> 1014
Cockpit: 96.6% identical / 96% coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
image_names[auxScreenPlacement] (qblh0..qblh6) is the mech diagram with THIS
subsystem's segment lit, and SubsystemCluster drew it at the panel origin.
Shipped puts it in the quadrant's middle.
Solved from the framebuffer rather than guessed: our extra pixels clustered
at panel x 0..70 against shipped's missing at x 140..230, and a
cross-correlation of the two sets peaked at dx=+144 dy=-54 independently on
all three panels of the head. +144 is 0x90 exactly; y is bottom-up in this
space, so up the screen is +0x36.
head missing extra
Mfd1 4908 -> 1331 3960 -> 1776
Mfd2 6870 -> 1908 5280 -> 2196
Mfd3 3091 -> 730 2565 -> 1119
~10.9K missing and ~6.7K extra pixels recovered. The whole cockpit is now
96.4% pixel-identical / 95% coverage against the shipped binary, from
89.7%/92% at the start of the session -- and the MFD weapon panels are
visually indistinguishable apart from the state box, which still reads OFF
where shipped shows a digit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BallisticWeaponCluster's two round counters pointed at UnboundIntegerSource
because ProjectileWeapon::ammoBinLink is protected -- the documented inert
gap in the ctor ledger. ProjectileWeapon now exposes GetAmmoCount() (the
resolved bin's rounds, -1 with no bin) and the cluster copies it into a live
cell each Execute, the same pattern it already uses for jammed and
reloadSeconds. The binary pointed its NumericDisplayInteger straight at the
resolved bin's count.
The STREAK 6 panel now reads 0024 against the shipped binary's 0024, in the
same font, stencilled into the fire-ready disc the same way.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MissileLauncher::DefaultData passed Subsystem::MessageHandlers and
Subsystem::AttributeIndex where every sibling weapon passes MechWeapon's --
and a launcher publishes no tables of its own, so it must chain MechWeapon's
exactly as ProjectileWeapon (its own base) does. Wired to Subsystem's, EVERY
weapon attribute on a missile launcher resolved to nothing and the gauge read
the unbound zero cell: PercentDone, TriggerState, WeaponState,
DistanceToTarget, RearFiring.
The cockpit is what found it. The shipped binary lights a fire-ready disc on
the STREAK 6 panel (WeaponCluster gates it on PercentDone >= 0.99) and ours
never did -- while the launcher itself reported state=Loaded recoil=0 level=1
bin=24. A healthy weapon behind a dead binding.
head missing before -> after extra
Mfd1 8807 -> 4991 4063 -> 4003
Mfd3 6935 -> 3174 2659 -> 2608
Mfd2 6882 -> 6870 (unchanged -- it hosts no missile panel)
~7.6K missing pixels recovered. The disc, its rays and the stencilled ammo
cells now draw; the digits themselves stay blank pending the ammo feed, which
is the documented inert gap in the BallisticWeaponCluster ledger.
Traces added, all env-gated on BT_VIS_LOG and kept as tooling: [warn]
(cluster percentDone/warn state) and [proj-state] (launcher state, recoil,
recharge rate, bin count) -- the pair that separated "broken weapon" from
"broken binding" in one run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The gauge framebuffer is PLANE-PACKED: L4GAUGE.CFG gives each port a bit
mask, not a rectangle, so all six heads share the same x/y and are separated
only by bit -- the packing the VDB splits into the pod's physical screens.
Comparing RGB was therefore measuring nothing useful; the "magenta artifact"
just meant the wrong heads were lit at that pixel.
New instruments (emulator/render-bridge/gauge-ab/, with a README):
planes.py per-head scoreboard -- shipped vs ours, missing vs extra, per
port mask, recovering the 16-bit word from DOSBox's RGB565
BT_VIS_LOG a complete cockpit widget map from the single dispatch every
gauge widget is built through (MethodDescription::Execute),
printing name + port + authored position
ab.sh stages the fresh build over BTL4REC.EXE and kills any running
DOSBox before launching -- see below
The fix: the Comm page's pilot roster is the POD roster (the viewpoint mech's
ControlsMapper pilot array, mapper attributes 15/16), not the mission's
player list. It is zero in a solo mission, so the shipped page draws empty;
ours resolved the local player and painted its icon in colour 0xff. The gate
belongs in the row source, not PilotList::Execute -- Execute's empty branch
still has to run, because erasing is what the shipped page draws.
head shipped ours missing extra (was)
Eng1 17981 17981 0 0 (0 / 1030)
Eng2 17981 17981 0 0
Eng3 17981 17981 0 0
Comm 15656 13557 2099 0 (2099 / 1253)
Heat 21374 20913 566 105 (566 / 990)
Title-band magenta 404 -> 0. The 2099 Comm pixels we are missing were
missing before the gate, so it is a strict improvement.
Method note, which cost more than the bug: the rig's confs run BTL4REC.EXE
out of the mount while the build writes build410/btl4opt.exe, and nothing
connected the two. Three measurements ran an hour-old binary, so a change
that "did nothing" three times had never been tested -- and the correct first
hypothesis was discarded on that evidence. ab.sh now re-stages every launch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reading the art settled where the titles live: qsensors/qmyomers/qermlas/
qppc/qstrk6.pcc are 299x26 TITLE BANNERS, while the qblh0..7.pcc strings
the cfg hands vehicleSubSystems are 77x120 MECH ICONS (already lit by the
5.3.28 placement fix). So the authored auxScreenLabel IS the panel title
-- it just belongs on the MFD panel, not only the ENG page.
SubsystemCluster now builds a titleBanner child from the cached label.
Placement was MEASURED against the shipped framebuffer through the A/B rig
(BackgroundBitmap places by left/BOTTOM): a bring-up BT_TITLE_DY knob, then
a green-pixel correlation over the title band, then baked at x+0x09,
y+0xb3 from the panel origin. "SENSOR CLUSTER", "MYOMERS", "TYPE II 65
TONS" and both "ERMED LASER RANGE 500M" banners now render where the
shipped binary puts them.
Score progression vs the shipped cockpit on identical GAUGE content:
5.3.26 birth 90.1% identical / 82% coverage
5.3.28 authored screens 93.2% / 85%
5.3.29 titles tuned 94.8% / 91%
Also: grab.ps1 now focuses the target window before capturing -- the
screen-copy grabbed whatever was in front, which cost two junk captures
(a white frame and an unrelated 3-D app window).
Gauge fight 11/11 rounds and smoke both clean, zero faults.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ran the reconstructed exe and the shipped ALPHA_1 binary against the SAME
GAUGE content, same mount, gauges on, and diffed the 640x480x16
framebuffers. OUR EXE DRAWS THE COCKPIT: 90.1% pixel-identical on the
first run, and every difference traced to a single root cause.
The authored aux-screen block (screen number / background placement /
label) lives in the subsystem resource -- which is freed with the Mech
ctor's stream buffer (the 5.3.24 use-after-free landmine). Both this tree
and the BT411 port had stubbed it, which is why BT411's displays show the
same artifacting. PoweredSubsystem now caches the block at ctor time with
accessors, and btl4gau2 uses the AUTHORED screen instead of a roster-order
stand-in and builds the placement strip art. Panels snapped onto their
authored screens -- weapon dials now land on the same panels as the
shipped exe, mech icons appear on the sensor/myomer panels, generator
letters correct -- taking the match to 93.2% identical / 85% coverage.
Remaining differences are donor-inherited stubs: the panel title art (now
proven to come from the q-strip statusImage path, not auxScreenLabel --
the label blit is wired and guarded regardless), the pilot name bitmap,
and some lamp frames.
The A/B rig is banked at emulator/render-bridge/gauge-ab/ (both confs +
the window-grab script) so the comparison is repeatable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>