Commit Graph
125 Commits
Author SHA1 Message Date
Joe DiPrimaandClaude Fable 5 18418af8fd handoff: crouch complete
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 00:50:39 -05:00
Joe DiPrimaandClaude Fable 5 591d205b19 CROUCH complete: full cycle + MP replication verified
The WIP's 'pose does not hold' was a chain of bench-instrument errors, not a
code bug: every capture ran in COCKPIT view (the pilot cannot see their own
legs; the eye-height residual masked as reversion).  Joint probes prove the
park holds indefinitely (knee 1.138, root -2.219 steady); the 2-node bench
shows the observer's replicant fully crouched and held (duckmpA_031 -- the
type-3 state record carries it with zero new replication code); the second
scripted press (new BT_BTNTEST2 env) verifies RISE -> standing zeros.
MP button delivery confirmed mode-mask-clean (the one miss was round-start
jitter).  Diags added, all BT_DUCK_LOG-gated: SetLegAnimation re-arm tracer,
1 Hz joint probe, RIO press mode-mask, BT_TREE_LOG topology dump, and the
squat-park log.  RESIDUAL filed: pilot's own cockpit eye does not ride the
root drop (DPLEyeRenderable chain composition; cosmetic).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 00:50:12 -05:00
Joe DiPrimaandClaude Fable 5 d44fae2ae3 KB + handoff: searchlight reconstruction
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 19:46:09 -05:00
Joe DiPrimaandClaude Fable 5 412053d5af Searchlight reconstruction -- the pod's night kit, both halves
The subsystem (sim/toggle/attribute/replication) was already complete; this
lands the missing VISUALS, decoded from MakeMechRenderables @004cef28 case
0xbd8 (raw pseudocode part_014):

- COCKPIT: the 1995 searchlight is a FOG SWAP -- the @00456778/@00456814
  watcher switches DPLRenderer::SetFogStyle between the authored fog= (lit)
  and nosearchlightfog= (dark) sets per map/time page in BTDPL.INI.  The
  engine kept the whole system under its real names; completed the stubbed
  plane application (currentFogNear/Far) and transcribed the watcher (with
  its inverted-cache seed) into TickSearchlight.  CONSEQUENCE: night now
  STARTS on the authentic dark set (near-plane 5u on arena pages) -- our
  builds had rendered the searchlight-ON fog permanently.
- EXTERNAL: spot.bgf beam cone hung on the searchlight SITE joint, shown/
  hidden from the replicated LightOn attribute (@0045612c watcher).  Site
  segments now build geometry-less DCS children (posed + parentable, as the
  1995 graph did) -- previously they were skipped entirely.
- searchlight.hpp: commandedOn @0x1DC identified as mountSegment (resource
  segmentIndex; the cone's mount joint).

Benches: searchfog.sh (solo cockpit: first-tick dark sync, F5/0x14 press ->
SetFogStyle(2), red-fog probe end-to-end), spotcone.sh (2-node: B's button ->
lightState replication -> A logs "[spot] cone SHOWN (seg 20)").  Cone look
(size/aim on the mount) pending an eyeball pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 19:44:23 -05:00
Joe DiPrimaandClaude Fable 5 b31c6c4527 handoff: score report tail reconstruction
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 18:38:48 -05:00
Joe DiPrimaandClaude Fable 5 91bd28669e #45/#134 authentic score/death report tail -- replaces the scoring stand-ins
Reconstructs the dark-gap tail of Mech::TakeDamageMessageHandler
(@0x4a02f4-0x4a0890, raw disasm): the three id-0x16 score reports (kill to
the shooter's player / type-0 wire-fidelity / received to the victim's
player) and the BT 0x38-byte VehicleDeadMessage extension {killed-by player,
kill zone} dispatched from the death tail.  Retires BTPostDamageScore /
BTPostKillScore and the per-hit inflicted credit (never existed in 1995:
@0x4c0200 is bound in no handler-table entry -- byte-scan receipt in
decomp-reference).  Suicides now dispatch and the handler negates the award
(the #134 panic penalty).  Collision divert falls through to the death tail
per @0x4a0375 (wall deaths respawn + blast; no score).  ScoreMessage fields
renamed to decoded truth (vitalHit/zoneIndex/subsysID) + wire asserts;
console VTVDamaged points_transfered corrected (Round(award), not Now()).

Benches: scorekill.sh cross-node kill (kills=1 award=4.88, killedBy=2:1
zone=3, single death cycle), scoreself.sh suicide (type=2 award=-39.00
kills=0), deathblast2.sh re-verified (72 bursts at ~9u).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 18:36:58 -05:00
Joe DiPrima 06a8edbff2 bench: #89 gate-OFF negative verified (advDmg=0 -> gated, zero splash) 2026-08-05 17:04:58 -05:00
Joe DiPrima 5a6ee51f3a handoff: #89 death blast 2026-08-05 16:53:05 -05:00
Joe DiPrimaandClaude Fable 5 297127d0d7 #89 DEATH BLAST reconstructed: a dying mech splashes its neighborhood
The missing half of Advanced Damage, found by call-scanning
Explosion::SplashDamage @0042fad0: TWO callers, not one -- Missile::Perform
(the known #62 path) and 0x4a0bda, the UN-EXPORTED tail of
Mech::TakeDamageMessageHandler itself.  Raw disasm @0x4a07b8-0x4a0bda:
when the victim ENTERS dead(9)/eject(10) during the applications, the
binary sets the wreck burning (id 0x17, deferred -- handler not yet
reconstructed), spawns the death Explosion (model 0x31 -- our death-list
visuals stand in), and SPLASHES:
  gates : owning player's advancedDamageOn (+0x264) AND NOT
          suppressConsole (+0x258 -- eject sets it: PUNCH-OUTS NEVER
          BLAST, the authentic anti-suicide-bomb rule)
  damage: type 2 Explosive, amount = deathSplashDamage (mech+0x520),
          bursts = round(0.001 * moverMass * 15.0) -- scales with tonnage
  radius: deathSplashRadius (mech+0x524); per-victim falloff
          bursts/dist^1.25 in the shared core
Draco's collision-divert suspicion is settled: the blast is TYPE 2, the
divert never touched it -- the tail was simply never reconstructed.

Port: deathSplashDamage/Radius PROMOTED from the Wword scratch bank to
named Mech members (the bank is one GLOBAL array -- authored per-chassis
values were clobbered to the last-loaded mech); BTSplashCore split out of
the #62 weapon splash and shared; BTApplyDeathSplash + the death-edge arm
in the handler tail; BTPlayerConsoleSuppressed bridge (friend).

Bench (2-node, B parked 8.9u from a self-destructing A): blast fired with
authored madcat data (radius=50, amount=5, mass=75000 -> 1125 base
bursts), B took 73 bursts (falloff exact: 1125/8.91^1.25), cross-pod
delivery + cylinder spray verified on B's own log ([dmghit] type=2
burst=73 across zones).  ~365 damage at 9u -- Draco's 'double kills on
drops' economy restored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 16:52:32 -05:00
Joe DiPrima 85d6ef4a59 handoff: skate campaign closed, ready to cut 2026-08-05 13:58:03 -05:00
Joe DiPrimaandClaude Fable 5 6dd1212e0c #52 campaign close: full transition matrix PASSES at 2 nodes -- field instrument rides
Provocations run under the verified [skate] detector: sustained healthy
walking, 7x respawn-while-moving, gimp onset at speed, ~21s of sustained
GIMPED walking (aimed-leg self-damage: BT_SELF_DAMAGE_ZONE=dz_ldleg),
leg-destruction death (authentic: lvl 1.0 -> leg gone -> fall/death; the
mid-session "died of the gimp edge" reading was a capped-print artifact,
retracted), and respawn.  ZERO skate anywhere.  Peer gimp replication
VERIFIED live: observer reads gl=3 + sim=3 with the gimp bodyStates
cycling for the whole master limp window (#82 remains fixed).  Conclusion:
the field skating does not reproduce at lab scale; the detector + SKATE
matchlog record ship with the next cut and the field names the failing
case.  Bench scripts archived (skatebench3-7; 7 is the clean-room one --
the sed-derived chains dropped envs twice).

Also: BT_LAMP_LOG=1 joins the field bats (#135 -- lamp/annunciator edge
forensics; near-zero noise, answers leak-no-flash reports in one grep).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 13:58:01 -05:00
Joe DiPrima 869d885eb2 handoff: skate detector state 2026-08-05 12:41:57 -05:00
Joe DiPrimaandClaude Fable 5 9604f4c492 #52 SKATE detector (ungated field forensic) -- negative-verified; local repro eludes
The night-12 field logs eliminated record starvation (zero [ghost] during
three observed skating windows), so the bug lives in gait APPLICATION on
peers.  This adds the [skate] detector to the death-handler tick: a
replicant moving >0.08 u/frame for 90+ frames with BOTH animation
channels idle (legCycleSpeed + bodyCycleSpeed ~ 0) logs one line per
episode + a SKATE matchlog record carrying the discriminating inputs
(legCyc/bodyCyc/cmdSpd/destroyed/mode).

Honest history: the first build keyed on legCycleSpeed alone and
false-fired on every healthy movement phase -- the current peer
architecture poses joints from the BODY channel (s_peerLegCh=0,
AdvanceBodyAnimation mj=1), so legCycleSpeed==0 is NORMAL there.  Caught
same-session by the [gimpfeed] silence (AdvanceLegAnimation never runs
on peers); corrected to channel-agnostic before anything shipped.

Bench (skatebench2.sh, 2-node, autodrive walker + kill every ~40s):
7 death/respawn cycles, ZERO skate hits either side -- no false fires,
and light local conditions do NOT reproduce the field skating.  Next
provocations: leg-GIMPED walker (the #82 family transition) and 6-player
load; otherwise the detector rides the next cut and the field names the
failing case for us.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 12:41:55 -05:00
Joe DiPrima 6d7946a264 handoff: night-12 log scan results 2026-08-05 11:47:30 -05:00
Joe DiPrima 754518b587 handoff: #131 fixed with receipts + build ritual note 2026-08-05 11:13:17 -05:00
Joe DiPrimaandClaude Fable 5 546aabd5ba #131 false lock FIXED: miss-means-miss -- the pick answers only for drawn geometry
Night-12 field report (Ronin/Conn Man/Oracle, blackhawk-correlated): lock
ring lit with the reticle visibly off the mech + no-reg complaints.  Root
cause: TWO port stand-ins answered where the 1995 card (which cast against
the DRAWN geometry) would miss -- the pick's any-object sphere fallback and
the caller's whole-mech AABB fallback.  The regime that exposes them: a
LEVEL boresight over a SHORT mech -- the blackhawk's mesh tops out below
eye-ray height, so the ray clears every triangle but pierces the fat cull
spheres; the ring lights with the reticle above the mech's head (the
operator watched exactly this on the sweep bench).  Careful aimed-down fire
rides triangles, which is why Oracle's per-panel audit passed on the same
build.

Fix: MechSegmentPick returns 1=drawn-geometry hit / 0=TRUE MISS / -1=no
render tree; the sphere may answer ONLY for a mesh the reader cannot parse
(pm==0 -- currently none exist: counters objs/invFail/noTri all clean);
the AABB survives ONLY as the pre-tree replicant grace.  A readable mesh
the ray misses is a MISS -- no lock.

Verified (2-node vs bhk1 at 100u, all runs on force-relinked string-
verified exes after today's stale-link flake):
- LEVEL lock-sweep: 0 locks all run (pre-fix: lock band from 168 sphere
  answers; picksrc tri=0 sphereFB=168).
- DOWN-PITCHED sweep: locks return 100%% tri-sourced (tri=158 sphereFB=0),
  landing on real parts (rarm/ldleg/rdleg) with honest gaps.
- Full zone-walk matrix: tri=18874 sphereFB=0 box=0; victim took 156 hits
  across 16 zones incl. both side torsos -- combat un-regressed.

New instruments (all env-gated): BT_LOCK_SWEEP=<axis> torso pan (the
operator-visible lock-envelope bench), [locksweep] transition log,
BT_LOCK_ENVELOPE synthetic unit-sweep probe, [picksrc]/[pickbox] source
telemetry with objs/invFail/noTri localization counters.
Bench: scratchpad/night12/zonewalk_bhk.sh.

NOTE for the field: locking is now strictly TIGHTER (ring = reticle truly
on the machine).  If era testers feel the pods were more forgiving,
Draco's "slight lock linger" memory becomes a deliberate investigation
(sourced hysteresis), not an accidental sphere halo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 11:12:43 -05:00
Joe DiPrima f279e38707 handoff: night-12 field results -- 10 closed, 8 filed (#131-#138) 2026-08-05 09:35:24 -05:00
Joe DiPrima 8b887f6cec handoff: #124 per-panel correction 2026-08-04 17:58:09 -05:00
Joe DiPrimaandClaude Fable 5 1d3616cceb #124 CORRECTION: aimed hull hits resolve the struck dz_* PANEL -- players were right
The 08-03 zone rule (struck SEGMENT's SKL dzone, ALWAYS -> every aimed
torso hit = center torso) was wrong, and era players' pushback caught it.
Byte-level proof: MAD_TOR.BGF zone-tags the hull PER PANEL (dz_utorso x36,
dz_ltorso/rtorso x18, dz_dtorso x16, all four rear panels, searchlight);
the dpl hit result kept GEOGROUP granularity (dplHitGeoGroup, T0); and the
binary's segment->zone map @49db20 has NO runtime caller (raw call-scan:
sole caller = CreateStreamedDamageZone, load time) -- no segment-level
collapse mechanism exists.  Oracle's night-10 'only LCT gets hits' audit
was the BUG's fingerprint, not the pod's design.

Fix: MechSegmentPick attributes the struck triangle to its draw op (index
range) and takes the op's .DZM-bound zone -- the #87 armour-darkening
bindings, the same authored patch->zone mapping that already paints the
panels -- with the segment dzone as the untagged fallback.  ZoneAimPoint
now aims hull zones at their patch CENTROIDS (all hull zones previously
shared the chest cull-center), which also upgrades the zone walker.

Bench (2-node zone-walk vs spinning madcat, zonewalk_madcat.sh): every
hull panel resolves individually -- utorso 11/12 in-zone, no L/R
mirroring, all four rear panels register; misses are the panel facing the
shooter mid-spin (correct geometry, not misattribution).  Victim applied
254 hits spread across every panel family.  Was 102/102 hull aims ->
dtorso before the fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 17:57:14 -05:00
Joe DiPrima 98234c8b15 handoff: 4.11.771 re-cut with Cyd's lamp sweep 2026-08-04 16:56:10 -05:00
Joe DiPrima 26b5743f9a handoff: 4.11.767 cut -- field-night checklist 2026-08-04 16:37:17 -05:00
Joe DiPrima c38377a6d2 handoff: respawn-reset audit complete 2026-08-04 16:14:52 -05:00
Joe DiPrimaandClaude Fable 5 26ab2fee3a Respawn-reset audit: valve restore + peer smoke cleanup (both binary-grounded)
Operator reports audited vs the binary (full matrix in RESPAWN_REARM_PLAN
addendum):
- VALVES (real gap): Condenser reset @004ae534 was missing from the decomp
  export -- raw disasm shows it chains HEATSINK (coolant refill runs; the
  old body chained HeatableSubsystem per the stale TCP shard) then, respawn-
  side, resets valveState to detent 1 and restores massScale from
  refrigerationFactor.  Mech::Reset now also runs the binary's tail call
  (@0049f788 BTRecomputeCondenserValves) so flow fractions rebuild from the
  reset detents.  Bench: detent 5 -> death -> "[respawn] Condenser1 valve
  detent 5 -> 1".
- #129 SMOKE (real gap, peers-only): the replicant un-wreck edge rebuilt
  the model without the @004d0c14 per-entity effect cleanup, so the
  observer's last 10s wreck-plume window rode the teleport onto the fresh
  mech.  BTStopEntityPfx now runs on the edge; bench shows no plume line
  after any un-wreck until the next death.
- AUTHENTIC (no fix): weapon->generator taps persist (@004b0e6c only
  resolves the link) and MFD display/control modes persist (mapper vtables
  0050f45c/0051e440 slots 8-11 = plain root bodies, read from the exe).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 16:14:10 -05:00
Joe DiPrima 9493fba04d handoff: #124 twist-sign verified correct 2026-08-04 15:50:17 -05:00
Joe DiPrimaandClaude Fable 5 1f995ee35a #124 twist-sign VERIFIED correct (live missile bench) + the rig to do it
The frame-adapter's inferred twist-sign flip (SelectSlice theta -= twist,
vs the binary's += pre-reflection) was the last unverified half of #124 --
every earlier probe ran at twist 0.  Bench: stationary madcat target with
the torso PINNED at 0 / +140 / -140 deg, LRM salvos from a fixed shooter
(missiles = the authentic cylinder path; the binary DROPPED zone -1 beam
damage).  Result: slice picks track the physically-facing flank in BOTH
directions (twist-left -> right-family zones for left-flank impacts,
twist-right -> left-family), deterministic, wrong-sign outcome (slice 7
vs observed slice 1) clearly excluded.  No game-code change needed.

Instrumentation added (all env-gated):
- torso.cpp BT_FORCE_TWIST=<-1..1>: HOLD the sim's analogTwistAxis (the
  input-level pin was dead -- live input rides the CONTROLS.MAP device
  push, and Basic mode auto-centers; sim-level is plumbing-independent).
- dmgtable.cpp [slice] line: rot flag, live twist, thetaIn/thetaAdj,
  chosen slice -- the weighted leaf roll made zone-only logs ambiguous.
- [dmgresolve] now names the zone (BTMechZoneSegAndName).
- mech4.cpp BT_FORCE_TWIST input pin + one-shot mode cycle (kept as doc
  of the dead path), mechmppr [mppr] mode probe.
- scratchpad/night11/twistsign.sh: the 3-config bench.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 15:49:14 -05:00
Joe DiPrima 952f10b18d handoff: #92 already fixed (zone-walk receipts) + night11 benches 2026-08-04 14:31:13 -05:00
Joe DiPrima ac3729b41b handoff: #91 decode (own missile pod, blakskn material-key fix) 2026-08-04 12:44:18 -05:00
Joe DiPrima dcd8583449 handoff: gate rejections are player-visible now 2026-08-04 11:42:51 -05:00
Joe DiPrima edff8fdb96 handoff: Steam build gate shipped (both halves) + LAN caveat 2026-08-04 11:36:28 -05:00
Joe DiPrimaandClaude Fable 5 15a1cf5d32 handoff: #108 version-skew confound + build-handshake item
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 11:12:27 -05:00
Joe DiPrimaandClaude Fable 5 24b0d2eaf7 handoff: day-2 addendum -- eject epilogue, stale-tracker sweep, #108/K-D instrumentation plan
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 10:24:54 -05:00
Joe DiPrimaandClaude Fable 5 675fe68cb0 #118 correction sweep: the ARCADE's eject was LIVE -- 'dead code in 4.10' was wrong
The absolute-pointer scan missed the E8-relative call; the later byte-scan
found it (FUN_004a9b5c+0x10 -- the master performance evaluates eject
permission per frame in the shipped binary). Under deadline pressure the
disproven 'unfinished/dead code' claim leaked back into three comments and
the handoff; swept per the correction mandate.

Settled press model, now stated correctly everywhere: pilot eject = the
pilot KEYPAD bank while armed (+ the PANIC key reporting through that
matrix). The MFD soft keys NEVER pilot-eject in the binary -- every page
routes them to authored functions; the flashing engEject cell is the
INVITE LAMP. The port's page-gated eng-key eject is a MARKED CONVENIENCE
deviation (operator-requested), and the coolant hysteresis is a MARKED
smoothing deviation for a degenerate boundary oscillation -- not
completions of unfinished code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 02:40:39 -05:00
Joe DiPrimaandClaude Fable 5 5cd1947b65 handoff: evening eject sprint + MFD corruption arc addendum
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 21:07:31 -05:00
Joe DiPrimaandClaude Fable 5 440cee1e4f #87 dig closure: host response is linear (raw decomp re-read); the pixel curve was div-card firmware
Re-read the material damage watcher RAW (@004573e4 ctor / @00457784
change-push): 16 floats snapshotted, damaged = pristine x 0.1 (literal at
the MakeMechRenderables call site, watching zone+0x158), linear lerp, 13
floats written back, then FUN_0048d4d4 -- which is "flush_material": a dpl
command-stream marshal (opcode 100) to the i860 division card. The
material->ramp->texel response curve was CARD firmware and is not in
BTL4OPT.EXE; logged as an open question (ask: firmware/DIV docs/period
screenshots).

Field reconciliation, measured live: one 25-pt hit (ERLG class) snaps a
77-pt arm 0 -> 0.3247 in a frame -- "a single missile leaves visible
armor damage" holds under the linear curve for heavy hits; 2-pt ERS creep
(the leg-audit weapon) is the invisible case.

Bench additions: BT_WALK_ZONES=<name-list> walker filter (concentrated
per-region audits, scratchpad/night10/legwalk.sh) + BindArmourDamage
ORPHAN inventory under BT_ARMOR_LOG (proved every drawn op is
zone-claimed on the full tree).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 13:10:00 -05:00
Joe DiPrimaandClaude Fable 5 fe48accb6b #87 root cause: the level-crossing zone-record send was missing -- observers never saw damage
The binary's effect watcher (FUN_0042aa2c) raises
ForceUpdate(DamageZoneUpdateModelFlag) when a zone's damageLevel CROSSES a
band-descriptor threshold (FUN_0042a5f4, master-gated on entity+0x28 & 0xc)
-- that send is how every other pod's replicant learns zone levels
mid-fight. The port's band hub kept only the graphic-state branch, so an
observer's copy sat at 0.0 until destruction: no enemy hull darkening, no
doll movement, ever (the night-10 "no armour discoloration" report).

Restored the level branch with the already-reconstructed DescriptorCrossed
(@0042a5f4) + the master gate on both branches (mesh swap still runs on
every instance). 2-node verified: A-side armour watcher pushed 114 level
changes; every replicant peak matched the master's finals to 4 decimals
(dtorso 0.9321 == 0.9321).

Render path cleared separately (gotcha #23 discharged): BT_ARMOR_FORCE 0/1
A/B captures prove the tint renders (hull -> charcoal at 1.0). Solo
perception is the authentic economy: 2-25 pt lasers vs 68-185 pt pools.

Bench: zonewalk.sh node A now carries BT_ARMOR_LOG + BT_SHOT_EVERY (the
standing zone-replication receipt) + digest_walk.py; armorpx.sh is the
force-pair pixel rig.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 12:24:36 -05:00
Joe DiPrimaandClaude Fable 5 00dfbf8f9f #124 RESOLVED against the authored skeleton: the pick's zone is the
struck drawn segment's dzone, ALWAYS -- the cylinder embellishment removed

The open question ('what zone did 1995 designate for hull hits') was
already answered in the KB's #73 dig [T0+T1]: no software pick writer
exists -- the pod's pick was a dpl scene intersection, struck triangle
-> DCS segment -> its SKL-authored dzone; the cylinder lottery was only
ever the UNAIMED path (missiles/splash/rams). The live segpick dump
completes it with the authored data: the madcat's pickable skeleton is
18 segments -- hip, shoulders, guns, thighs, knees, ankles+toes, and
ONE hull piece (jointshakey -> dtorso). No ltorso/rtorso/utorso/rear/
door/searchlight segments exist. Aimed fire at any torso panel
authentically designates CENTER TORSO; the side/rear/upper panels strip
via the unaimed path.

Changes:
* the shared-carrier -> cylinder override (yesterday's embellishment)
  is REMOVED: triangle hit -> that segment's dzone, period;
* SegAimPoint: the walker's hull anchor moves from the joint ORIGIN
  (crotch gap -- rays sailed between the legs to the terrain sentinel)
  to the carrier segment's cull center (the chest);
* [picktri]/[pickwin] diagnostics grew tri/seg/zone fields -- three
  consecutive 'mystery' regressions in this stretch were the STALE-EXE
  trap (CWD-relative cmake --build from content/ resolved to a
  nonexistent dir and the old binary kept running) -- the harness doc's
  warning, hit again.

Final zone-walk matrix (spinning target, real MP, full cycles):
limbs 55/70 in-zone (legs/feet 6/6; arm strays = hull/occlusion);
hull-family aims -> dtorso 56/69; victim consumption matches. The
night-10 field table now reads as this model's fingerprint: 'only LCT
gets hits' from aimed energy IS the pod -- one hull piece, dtorso.
What was actually broken and is now fixed: the sphere mis-picks
(rgun-from-a-dtorso-aim class), the unaimed path's 180-degree frame,
and the vertical scatter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 11:31:45 -05:00
Joe DiPrimaandClaude Fable 5 1a3c268278 #124: the aimed pick now intersects the DRAWN GEOMETRY -- sphere
approximation retired; shared-hull hits route through the cylinder

The zone-walk matrix caught the pick red-handed: with the servo
verifiably holding the reticle on the dtorso segment, the per-segment
BOUNDING-SPHERE pick returned rgun/ruleg -- the gun pods and legs
thread the ray before the torso from most angles (its own comments
admitted a foot could be unhittable behind its own knee). The 1995
pick was a dpl scene intersection against the drawn geometry.

Restored that semantic:
* BTGetPickMesh caches each segment d3d_OBJECT's triangles CPU-side
  once (its own BGF VB/IB, managed-pool locks); MechSegmentPick now
  runs sphere PRE-FILTER -> Moller-Trumbore nearest-hit across the
  threaded segments' posed meshes; the old smallest-sphere selection
  survives only as the no-triangle fallback.
* Segments claimed as CARRIER by 2+ zones (the shared hull: madcat
  seg 4 carries dtorso+ltorso+rtorso+utorso+rears) cannot resolve one
  zone from geometry -- those hits return zone -1 WITH the accurate
  triangle point, and the victim's bit-verified (frame-fixed) cylinder
  assigns the panel by band/wedge. Unique carriers (legs, feet, arms,
  gun pods) keep the direct zone.
* ZoneAimPoint: the walker aims at a zone's VISUAL center (largest
  pick object's cull-center) instead of the segment origin -- joint
  origins made feet/lower legs strike the part above.
* Walker upgrades from live operation: 3-column truth (aim/pick/land),
  engage gate, damped servo with polarity watchdog, approach port.

Zone-walk verdict (full cycles, spinning target, real MP): limbs 5-6/6
direct in-zone; every hull panel routes CYL; the victim's landed zones
now include ltorso/rtorso/reardtorso/rearutorso/rearrtorso -- the
panels night-10 reported unhittable. Residuals tracked on #124:
rtorso/utorso aim-anchor placement, doors/searchlight small-zone
sample, twisted-torso twist-sign verify.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 10:38:24 -05:00
Joe DiPrimaandClaude Fable 5 16e75d6e1a #124 bench: the ZONE-WALK MATRIX -- systematic per-panel targeting, operator-watchable
The precision rig the operator specified: two nodes, target visible,
every zone aimed at deliberately through the REAL reticle/pick path.

* BT_SPIN_SELF=<deg/s> (target node): rotates the node's own viewpoint
  mech in place (the BT_SPAWN_AT write pattern per frame) so every
  aspect passes the shooter's boresight; the operator watches this
  node's paper doll take the hits.
* BT_ZONE_WALK=<secs/zone> (shooter node): walks the target's damage
  zones in order, resolves each zone's carrier segment's live world
  position (BTResolveSegmentWorld + the new BTMechZoneSegAndName
  bridge), SERVOS the torso twist + aim elevation until the centered
  reticle ray (BTGetAimRay) points at the segment, fires 3 laser
  pulses, advances. [walk] ZONE/FIRE/HOLD on A pairs with [dmghit]
  zone/level lines on B.
* scratchpad/night10/zonewalk.sh: launch both + relay, map sed'd to
  grass/day, NO kill timer -- the session stays up for observation.

Hard-won servo constraints (documented in test-harness.md so they are
never relearned): the ENGAGE GATE (ray live + range<150 + bearing<1.1;
outside it RELAX the twist -- servoing at the twist limit while the
goto marches is a limit-clamp fight that visibly shakes the mech, and
a 0.55 gate deadlocks against the goto's ~0.55 resting bearing); YAW
POLARITY -1 (the twist cell's angular sense is opposite atan2(x,-z)
world yaw -- operator-observed live, the SECOND witness for the #124
SelectSlice twist-sign flip) with a divergence watchdog that
self-flips; damped correction (gain .40, cap .025/frame -- the aim ray
lags the twist write a frame); and a one-shot approach PORT to 100u
off the target instead of a cross-map march.

First live run: walker cycles zones, settles, fires; the target's
luleg climbed 0.458->0.523 under its own aimed pulses -- aimed shot,
correct panel, damage consumed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 10:04:03 -05:00
Joe DiPrimaandClaude Opus 5 47d9a61402 hit-location cylinder: raw-bytes audit of the shipped tables + chassis->table map + wedge orientation
Audit of Cyd's hit-model artifact against THE BINARY (user mandate: not vs the
port).  res29_scan.py parses the type-29 streams straight from BTL4.RES bytes:
18 streams / 8 distinct confirmed; the 7-wedge cells (BLH band 6, VUL band 4)
and the all-fixed BLH/OWN twist patterns are authored fact; every artifact
spot-check cell matched the raw bytes verbatim.  Chassis->table resolved by
live-dump matching (MadCat and Avatar ride DIFFERENT same-zone-set tables).
Authored slice names fix the wedge orientation: FRONT = the +Z arc, W1|W2 seam
= dead ahead -- corrects the #92 comment's W6-frontal claim.  Collision type-0
divert re-grounded at 0x4a0361/0x4a036d: rams never touch the cylinder.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 12:52:20 -05:00
Joe DiPrimaandClaude Opus 5 07072fc9f0 hit-location GROUND TRUTH: all 8 chassis' authored cylinder tables, named + fingerprinted
For verifying Cyd's hit-location artifact (the platform serves it to this
session as an unreadable public artifact, so the diff base is published here
instead).  scratchpad/night9/hitmodel_groundtruth.txt: every chassis's 7
layers x per-layer slices with zone NAMES and percentages, from the live
BT_DMGTABLE_LOG dump joined against [zone-armor] names.

Structure fingerprints (rot = rotateWithTorso per layer 0..6):
    ava/lok/mad/snd/thr/vul   rot=0001111  (legs hull-fixed, torso bands twist)
    bhk/own                   rot=0000000  (chicken-walkers: NOTHING twists)
    slices: 8 everywhere EXCEPT bhk layer 6 = 7 and vul layer 4 = 7
Variants share tables: lok2==lok1, mad2==mad1 -- '8 distinct chassis' is exact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 12:27:19 -05:00
Joe DiPrimaandClaude Opus 5 6dbeb3d266 wreck smoke dies WITH the wreck; the audio/effect clock is calibrated to the byte-proven 28
USER REPORT: "when you destroy a mech, it continues to smoke for a very long
time after the mech disappears... up to 30secs to a minute."

MEASURED (new [pfx] timed census with per-def particle attribution): the
death plume (psfx 1 DDTHSMK) was re-armed on a 10s cadence as a WORLD effect
(ownerTag=0) -- so neither the burial transition nor the respawn cleanup
(StopAllEntityEffects) could touch it.  The last-armed window kept emitting
over the empty spot for up to 10s after the hulk vanished, plus 6+-2s authored
particle lives, and back-to-back windows stretched the visible tail to the
reported range.  The re-arm itself is a marked PORT ADDITION [T3]; the binary
fired DDTHSMK once -- its 10s window + particle lives ended right around the
~17s hulk sink.  The authored design: the smoke dies WITH the burial.

FIX: the plume is spawned ATTACHED (BTStartPfxAttached, tagged to the dying
entity -- it also rides the sinking hulk now), and the burial transition calls
BTStopEntityPfx alongside the respawn cleanup that already did.  Verified over
a 175s run containing FOUR wreck events (the enemy kill + three player bay-
fire deaths): every plume's emission ends at its wreck's burial or respawn,
particles fade within ~8-11s, no orphan emitters remain.

THE COUPLING (user relay of Oracle: "prolonged smoke and explosion INCLUDING
SOUND... same lever?") -- CONFIRMED as a family: the binary's audio layer
times in FRAME COUNTS converted by the engine frame-rate global:
    AudioTime::Seconds_To_Frames == fmul [0x52140c]=28.0; fadd 0.5; round
    (@0x42c611, @0x42dd86 -- the 0x42xxxx cluster of the ~90 global consumers)
Our engine's DefaultRendererRate said 30: every audio duration, sequence
delay and compression window ran ~7% fast.  Calibrated to 28 [T1].  NOT yet
explained by this: a truly SUSTAINED/looping explosion sound (7% is not
"prolonged") -- the effect stop-path trace stays open on #114/#51.

Tooling: BTPfxParticle carries defIndex; [pfx] census (BT_PFX_LOG, 2s) prints
emitters + particles PER EFFECT SLOT with a timestamp; wrecksmoke.sh.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 11:25:49 -05:00
Joe DiPrimaandClaude Opus 5 5d6ded73f0 #110: cross-pod verified BOTH ways -- the cascade replicates, and peer fire alone drives it
Two two-node benches close the last stated gap ("no two-node run has watched a
peer's view of an arm death"), the second at the user's insistence on the REAL
field composition after the first used the self-damage scalpel:

1. mp_armchain.sh (scalpel): B self-destroys dz_rarm mid-fight.  A's screen
   receives the whole cascade -- [zone-repl] dz_rarm lvl 0->1 AND the cascaded
   dz_rgun lvl 0->1 -- plus the respawn reset coming back clean (1->0).  B's
   post-cascade AFC pushes turned out to be the RESPAWN RE-ARM, correct
   behavior, not a gate failure.  (Also confirmed: the self-damage harness
   dispatches a real Entity::TakeDamageMessage -- the identical path a peer's
   round takes, so the scalpel and the field share every line from the handler
   down.)

2. mp_armgrind.sh (the field composition): stand-off at 180, NO self-damage
   anywhere, A grinding B with real cross-pod fire for six minutes.  On B:
     * 25 incremental arm-zone hits from A's weapons via the cylinder tables
       (dz_rgun reached 1.0 from pure accumulation -- 15 cascades on rehits)
     * dz_rarm finished by B's OWN AFC bay cooking off (AmmoBinAFC100 BAY FIRE
       350, half into dz_rarm) -- itself lit by A's fire crits: peer-originated
       end to end
     * [cascade] zone 9 -> AFC100 + AmmoBinAFC100 + Condenser6 force-failed,
       fire gate refusals logged
   On A: [zone-repl] dz_rarm 0->1 and dz_rgun 0->1 -- the shooter SEES the arm
   and pod die.  B symmetrically received A's torso deaths.

Also: DamageZone::ReadUpdateRecord gets a change-only [zone-repl] diagnostic
(BT_MP_LOG) -- the probe that made the peer side measurable; mp_armgrind.sh
opens with the stale-node taskkill the other benches already had (a leftover
node from an aborted launch joined a later lobby as a third instance).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 09:09:49 -05:00
Joe DiPrimaandClaude Opus 5 614849b4a6 #110 coverage: all 8 chassis + all 3 weapon-class gates verified through ZONE death
Answering "are you 100% positive this works for every weapon type" -- it was
not a yes when asked, and the gaps were closed with measurement rather than
assurance:

1. AUTHORED DATA, all eight chassis (zonesweep.sh; the [zone-armor] dump now
   carries the zone NAME):  every arm zone AND every missile-rack zone on every
   chassis authors descend=1 (ava/bhk/lok/mad/own/snd/thr/vul); every gun/rack
   zone lists 1-4 critical subsystems; doors are inert (0/0/0); bhk uniquely
   adds destroySibs=1 on its arm chain.  No chassis is missing the cascade
   authoring.

2. WEAPON-CLASS GATES, end to end through a ZONE death (not direct weapon
   destruction, which was #86's verification gap):
     ballistic  AFC100  (projweap gate1)  -- dz_rarm,    ava1   [prior commit]
     energy     PPC     (emitter gate)    -- dz_larm,    ava1   [prior commit]
     missile    NRK5_2  (projweap gate1)  -- dz_rmissle, own1   [this pass]
        [cascade] zone 12 -> NRK5_2 + AmmoBinNRK5_2 force-failed
        [ammo] NRK5_2 -> NoAmmo (gate1): destroyed=1
   All three fire-gate code paths are now exercised by the cascade.

3. One anomaly chased to ground rather than waved off: the Owens rack cascade
   also killed Avionics/HUD/Searchlight/ThermalSight -- zone 15 dz_searchlight,
   an authored CHILD SEGMENT of the missile rack (the sensor mast sits on the
   shoulder pod).  Authored skeleton + authored descend flag, walked faithfully.

Remaining surface NOT bench-verified, stated plainly: cross-pod -- the cascade
is master-authoritative (crit plugs bind on masters only) and the resulting
alarm states ride the existing weapon update records, but no two-node run has
watched a peer's view of an arm death.  The field re-test (Lynx/Conn Man's own
audit, in MP) covers exactly that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 08:43:33 -05:00
Joe DiPrimaandClaude Opus 5 06adaee523 #110: the zone destruction cascade is LIVE -- an arm now takes its gun pod with it
Lynx: "You can destroy an upper arm, and the gun pod is intact and can still
fire."  Conn Man's audit: three chassis, both arms, reproducible.  And the user
is right that this family was claimed fixed before: #86 gated fire on the
WEAPON's own destruction and was verified by destroying the weapon directly
(BT_KILL_SUBSYS) -- the field scenario was the ARM ZONE dying with the weapon
subsystem healthy, which that bench never reproduced.  Oracle's 693-night "I can
still fire destroyed energy weapons" was almost certainly this mechanism.

ROOT CAUSE -- a silent stub.  Mech__DamageZone::RecurseSegmentTable @0049cad4
was transcribed faithfully... against three local shim types whose iterators
unconditionally return NULL:
    struct SegmentIterator { DZRef *Next() { return 0; } };
    struct SegTableX       { SegmentRecord *operator[](int) { return 0; } };
The cascade fired on zone death (bench: 72 [cascade] lines), "descended", and
touched nothing.  Every line read correct; none of it did anything.

THE REAL WALK (decomp re-read, FUN_0049cad4): iterate the mech's segment table
(mech+0x300, vtbl+0x34 GetNth) to this zone's segment, then
    destroySiblingsOnDestruction (Wword 0x68): recurse every other zone on the
        SAME segment (seg+0xD0), setting graphic state 2 (Gone)
    descendOnDestruction (Wword 0x67): recurse every zone on every CHILD
        segment (seg+0xE8 -> child+0xD0) -- the arm takes the gun
The engine ALREADY HAS both structures: EntitySegment::damageZoneTable @0xD0 /
childIndexTable @0xE8 (TableOf is 0x18 -- byte-identical to the decomp offsets),
populated at stream time by JMOVER.cpp:373 [T0].  The stub was never necessary.
Implemented with the engine iterators via two new read accessors on
EntitySegment (SEGMENT.h); the binary's asymmetry is reproduced as-is (the
sibling loop checks graphic state != 1, the child loop does not; re-hits on a
1.0 zone re-run the cascade -- idempotent in effect).

VERIFIED (ava1, self-damage to one zone, autofire everything):
  dz_rarm -> [cascade] zone 9 -> zone 17; AFC100 + AmmoBinAFC100 + Condenser6
      ForceCriticalFailure'd; "[ammo] AFC100 -> NoAmmo (gate1): destroyed=1";
      torso LRM5/SRM2 keep firing (correct)
  dz_larm -> [cascade] zone 2 -> zone 6; PPC + Condenser4 force-failed;
      "[emitter] 'PPC' fire REFUSED (destroyed=1)" x4998 -- the emitter gate now
      logs refusals BY NAME (the FIRED line never named its weapon, which is how
      #86's verification gap survived)
  regression smoke: frame time 7.13/7.20ms, 0 asserts, 0 cascades in ordinary
      combat (they fire only on genuine zone deaths)

Authored data (now dumped under BT_DMG_LOG): descend=1 on exactly the four arm
zones (ava1: 2/6/9/17), destroySibs=0 everywhere -- so legs/torso behavior is
untouched by this change.

KB: combat-damage.md cascade section; reconstruction-gotchas #26 (the
silent-stub trap: a shim that returns EMPTY converts a reconstruction into
fiction that reads correct -- shims must Fail() loudly or log their emptiness).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 08:26:53 -05:00
Joe DiPrimaandClaude Opus 5 da70bd58b2 #84: the replicant salvo now HOMES AT THE LIVE TARGET -- the stale-point double detonation is gone
Oracle (674): "missile appear to register hit explosions twice, once where
target was and again where the target is".  Rajel (693): "once where the mech
was when I fired and on the expected impact".

MECHANISM, measured on two nodes (BT_PROJ_LOG "DET at" now logs EVERY contact
detonation, including damage-0 rounds, which were invisible before): the
replicant salvo mirror pushed rounds with NO entity handle at the FROZEN
fire-time aim point, and the pool's contact test is proximity to p.targetPos --
so mirror rounds "contacted" the empty air where the target USED to be and
detonated there, on every peer, every salvo (45 salvos -> 234 frozen-point
air bursts in a 200s bench).  The true impact appears at the live position
(the shooter's own homing rounds on his node; the victim's damage reaction on
peers) -- two sites per salvo, exactly as reported.

THE BINARY'S MODEL (CLASSMAP, Missile flight-entity cluster): missiles are
ENTITIES with authoritative/GHOST Performance variants (Projectile
PTR_LAB_005129e8 / 005129f4) and their own WriteUpdateRecord (@4bef4c, slot 7)
-- peers ran ghost missiles updated from the wire and saw the TRUE trajectory.
One explosion, correct place, every node.  The frozen-point mirror is the port
infidelity (the entity Missile itself stays blocked by the documented 2007
Entity-base mismatch; mislanch.cpp:301).

FIX: the MissileLauncher update record now carries the locked target's EntityID
(salvoTargetID; EntityID::Null = point fire).  The mirror resolves it on the
receiving node via HostManager::GetEntityPointer (engine T0, an index-socket
Find -- NULL-safe on any ID), refuses non-mechs via BTIsRegisteredMech, and
pushes the mirror rounds WITH the handle -- they re-lead on the live local
replicant exactly as the master's own rounds do, and detonate at the true
position.  Guards: record-length gate (a short record from an older build has
no ID field -- its tail would be garbage and must not resolve) + Null check +
registered-mech check.

VERIFIED (two nodes, 200s, missile autofire both ways):
  * every record arrives len=52/52 with sane aims ([mlrec] diag)
  * 22/46 salvos resolve (tgtID=3:22 -> the victim's local master) and their
    mirror rounds home: the frozen-air DET population fell 234 -> 102, the
    live-homing population rose to 155/207 per node
  * the unresolved remainder is BENIGN and correct: EntityID::Null point fire
    plus hostID=-1 LOCAL entities -- i.e. the victim's WRECK, which autofire
    keeps shooting after the kill; a wreck does not move, so the frozen aim IS
    its true position
Downstream expectations: Ronin's smoke-screen (#114) should drop by ~the mirror
half, and the audio census (#32) loses the duplicate Static3DPatchSource
explosion sounds on peers -- both to be read from the next field logs.

Also in this commit: [projectile] DET diagnostic (every contact detonation with
position/damage/handle/aim), [mlrec] record-arrival diagnostic (len/recID/
subsys/counter/aim, capped 200), scratchpad/night9/mp_double.sh.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 02:29:58 -05:00
Joe DiPrimaandClaude Opus 5 3354db4bd1 #32: RETRACT the retention diagnosis; count what matters (steals + true drops); name the saturating class
The reopen said "the pool fills and never returns a source".  The 30s census in
the SAME field logs disproves it: free returns to ~227-230 between bursts and
reuses climbs ~20/s all session.  free=0 on the failure line is true by
DEFINITION at the instant of a failed acquire -- the third instance of the
counter-sampling trap (live=256-vs-6, then hsparm greps, now this), read off the
alarm line instead of the trend line.

WHAT THE LOGS ACTUALLY SHOW
  * The pool cycles; release-on-stop exists and works (the engine steal loop).
  * During firefights CONCURRENT demand exceeds 240 and the priority steal loop
    services each new sound by killing an old one -- continuously through
    combat (failures spread across every decile of every combat session).
  * Idle standing demand is ~13 sources.  My "each component holds its SourceSet
    to entity teardown, ~20-25 per mech" narrative was wrong.
  * The raw ACQUIRE FAILED line count (6.8k-19k per log) is NOISE: the steal
    loop retries after every failed attempt, so lines accumulate per EVENT and
    most events still play via a steal.  True drops were never counted.

CHANGES
  1. Census now carries steals= and drops= (drops = the steal loop ran dry and
     the sound NEVER played) plus a per-class drop histogram
     ("[audio] dropped by class: {class 1005 x4v: N} ...") -- all ungated, so
     the next field logs are decisive instead of suggestive.
  2. The ACQUIRE FAILED print is rate-limited to 1/30s and now names the
     requesting class + voice count.  19k-line log spam distorted this triage.
  3. BT_AUDIO_SOURCES=<n> now raises the POOL cap too (it previously raised the
     AL context budget while the pool stayed at 240, making the field
     experiment impossible to run).

MEASURED (9 mechs, missile autofire, 150s)
  * cap 240: peak 130 sources, 0 fails -- demand tracks SHOOTER count, not mech
    count; one shooter cannot saturate.  A 6-shooter lobby pins 240.
  * cap 48 (BT_AUDIO_SOURCES=64): saturation reproduced -- census
    steals=223 drops=455, histogram names the classes.
  * Dominant field requester (requested=4) = class 1005 Static3DPatchSource:
    world-placed effect sounds, i.e. EXPLOSIONS.  1001 DirectPatchSource x1v
    dominates drops at low cap; 1002 Dynamic3DPatchSource x3v present.

CONSEQUENCE FOR THE FIX ORDER: #84's stale-aim double detonation duplicates
exactly the saturating class on observer nodes.  Fix #84 FIRST, then re-read the
field census; only if it still saturates does the budget experiment
(BT_AUDIO_SOURCES with frame time measured) become the play.

KB: the wrong night-9 entry in open-questions.md replaced with the corrected
diagnosis; gotcha candidate noted -- an alarm-line counter is not a trend.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 01:42:10 -05:00
Joe DiPrimaandClaude Opus 5 202ebbf84a KB + tracker: night-9 playtest (build 4.11.693)
CONFIRMED FIXED and closed: #95 (missile salvo damage), #97 (leak gauge 3 levels),
#98 (loop-6 lamp), #99 (leak alarm audible).

#95 deserves a note: a tester reported "LRMs still doing 3.3 damage", but that is
the PER-MISSILE figure.  382 impacts across three 693 logs show the cluster being
delivered correctly -- LRM20 3.25 x burst 5..20 (total up to 65), LRM15 3.33 x
3..15, LRM10 3.50 x 2..10 -- and those burst ranges match the binary's
Random(n)+n/4 roll exactly.

#32 REOPENED.  The pooling change removed the alloc/free churn (the ~10% frame
time is real) but every 693 field log still shows
    ACQUIRE FAILED (requested=4 live=240 pooled=240 free=0 ...)
19,182 fails (Sauron) / 15,333 (Rajel) / 6,818 (Ronin), high-water 225 of 240,
starting ~1% into every session.  `free=0` is the diagnosis: sources are never
returned.  This is precisely the risk flagged when the fix landed -- retention,
not pool size.  Players called audio "fine" the same night; the log disagrees.

#84 still present, but the surviving explosion is a DIFFERENT mechanism from the
bundled one that was fixed: "where the mech was when I fired" is the stale
fire-time aim point already recorded as an open in rendering.md, i.e. an MP
visual, not the message-manager path.  Needs a two-node bench logging the
OBSERVER -- the earlier two-node run verified damage delivery but never the visual.

New: #108 ghost mech (peer never sees the death; shots still register, so it is a
visual/replication desync, the inverse of #94), #109 panic/eject, #110 arm
parent/child linkage (gun pod survives its mount -- three chassis, both arms;
parentArtifactZone exists but only drives LOD), #111 death warp bubble shown to
peers, #112 AFC shotgun spread (may be authentic Ultra-AFC burst), #113 LRM burn
duration, #114 missile smoke as a smoke screen, #115 hot box, #116 Standard-vs-
Expert heat gating, #117 per-chassis cockpit bounce.

⚠ A MEMBER OF THE ORIGINAL TEAM IS NOW TESTING -- "Ronin" (log user torak).  He
worked on the 1995 game and has played the pods recently.  Treat his pod
comparisons as primary evidence above reconstruction inference.  First two
contributions recorded in the KB: per-chassis cockpit bounce was deliberate, and
NARC likely shipped as data without an implementation.

⚠ Conn Man ran the OLD build (.674) for all seven of his sessions, so his reports
are not evidence about 693.  His arm-linkage audit is still valuable -- nothing in
693 touched that path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 01:15:45 -05:00
Joe DiPrimaandClaude Opus 5 5a1a407ed1 pre-ship: resolve the new audio diagnostic gates ONCE instead of per frame
RunSequence and AudioIdleWatcher::Execute run per sequence per frame, so the
BT_ATTRBIND_LOG probes added while chasing #99 were calling getenv ~2700x/sec on
a 15-sequence mech.  Small, but this is the same path the source-pooling fix
(#32) just bought 10% of frame time in, and an unset diagnostic should cost
nothing.  Cached in function-static flags.

Pre-ship smoke (combat, 2 kills, a respawn, clean teardown):
  frame time 7.18 / 8.27 / 8.71 ms  -- matches the post-pooling 7.79ms baseline
  0 asserts, 0 access violations, 0 audio acquireFails

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 20:11:06 -05:00
Joe DiPrimaandClaude Opus 5 5a425320b7 #92: map the direct-fire hit->zone path; the FOOT is pickable over only 3% of a mech's height
Oracle, night 7: "Unable to damage the foot panels on Loki via direct fire from
front or side.  Was able to damage on Thor."

FIRST, THE MECHANISM -- it is not what the issue assumed (missing collision
geometry).  A direct-fire hit gets its zone one of two ways, decided at
Mech::TakeDamageMessageHandler by `invalidDamageZone`:

  AIMED   (invalidDamageZone=0): the zone rides in on the message from the
          per-part aim pick, MechSegmentPick (#73, btl4vid.cpp).
  UNAIMED (invalidDamageZone=1): DamageLookupTable::ResolveHit runs the authored
          cylinder lottery -- impact HEIGHT picks a layer, ANGLE picks a pie
          slice, and a weighted random roll picks the zone from that slice.

Measured live, both paths are in use: the Thor logged 16 unaimed hits
(invalidZone=1 zone=-1 -> ResolveHit) alongside aimed ones carrying real zones.

THE FINDING.  MechSegmentPick is a BOUNDING-SPHERE test whose primary key is
SMALLEST RADIUS WINS -- a larger sphere can never beat a smaller one the ray also
grazes.  The leg spheres, dumped in world space and IDENTICAL on both chassis:

    knee  centre y=1.868  r=1.505     spans y 0.364 .. 3.373
    toe   centre y=0.302  r=1.689     spans y -1.387 .. 1.991

The toe sphere is BIGGER than the knee's and they overlap heavily, so the toe can
only win where the ray misses the knee sphere outright -- i.e. below y=0.364.
Against a reference height of 11.16 that is a 0.36-unit window, **3.3% of the
mech's height**, and it sits right on the ground.  Everywhere else a shot at the
foot is credited to the LEG.  That is the reported symptom.

This is a PORT ARTIFACT, not authentic: btl4vid.cpp's own comment concedes the
sphere test approximates "the per-part semantic the 1995 mesh intersection
produced".  Real mesh intersection has no such interference -- aiming at the foot
mesh hits the foot.

NOT EXPLAINED, and stated plainly: the per-CHASSIS asymmetry.  Loki and Thor have
identical leg spheres, identical foot geometry (LOK_LFOT.BGF and THR_LFOT.BGF are
both 3082 bytes with the same token layout), and identical foot layers in their
damage tables (only the upper/cockpit layers differ).  So nothing found here says
the Loki should behave differently from the Thor.  The bench could not settle it
because BT_AIM moves the drawn RETICLE, not the pick ray -- there is currently no
harness to aim the pick at a chosen height.  That harness is the next step.

Diagnostics added:
  [pickgeom]   one-shot dump of every pick sphere in WORLD space (zone, r, centre)
  [pickcand]   which spheres a ray actually threaded, their perpendicular d, and
               which won -- the probe that makes "smallest wins" visible
  [dmgtable]   the whole authored DamageLookupTable: layers, slices, zone weights
  [dmgresolve] per hit: localY, heightRef, layer, theta, resolved zone
  [cylgate]    invalidDamageZone / table pointer / incoming zone at the gate
(the last three under BT_DMGTABLE_LOG, the first two under BT_PICK_LOG)
plus scratchpad/night8/footpick.sh.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 19:47:30 -05:00
Joe DiPrimaandClaude Opus 5 e058b6acb7 #99: trace the coolant-leak alarm end to end -- it IS implemented; found the real defect
Oracle: "alarm is not sounding despite an active leak" ... "was sounding,
resolved a leak, but there was another".

RETRACTION FIRST.  I previously told the user the coolant alarm was "genuinely
unbuilt -- there is no alarm implementation to starve".  That was wrong, and it
was asserted without checking.  The alarm is fully implemented and the entire
chain works.  Traced live, in order:

  Condenser6.ReportLeak (= HeatSink::coolantActive) changes 0 -> 1
    -> [watchpoll] CHANGE on that exact address
    -> [matchfire] val=1 -> ctl 1  (StartAudioControlID)
    -> [seqstart] the alarm AudioControlSequence, events=25 looped=1
    -> [seqsend] ctl 8/16 (select), 6/1 (volume), 1/0 (START), 2/0 (STOP), ...
       a three-part looping alarm: two chirps then an 8s sustained tone.

The authored design, read from BTL4.RES: 19 subsystems per mech each bind TWO
AudioLogicalTriggers to their ReportLeak flag -- match 1 -> Start, match 0 ->
Stop -- and ALL of them drive ONE shared alarm sequence.

SO WHY WAS IT SILENT IN .674?  Almost certainly the audio source pool, fixed
after that build in ad9dfad.  Every player log from .674 is saturated with
ACQUIRE FAILED (3k-6.5k lines each, starting ~10% in and never recovering); an
alarm that cannot acquire an OpenAL source is silent.  The bench here shows 0
acquire failures on the current build.  ⚠ NOT PROVEN: this bench has no audio
device (pool census reads live=0 pooled=0), so the control chain is verified but
final playback is not.  Field confirmation needed.

THE SECOND SYMPTOM IS A REAL, REPRODUCED DEFECT AND IT IS IN THE AUTHORED DATA.
All 19 subsystems -- Condenser1-6, GeneratorA-D, Myomers, PPC_1/2, ERMLaser_1-3,
SRM6_1/2, Avionics -- share the single alarm sequence, and EACH one's leak-clear
sends an UNCONDITIONAL Stop.  So the moment any one of them stops leaking the
alarm goes silent, even while others are still leaking.  That is exactly what was
reported.  Since both the authoring and the MUNGA watcher code are authentic,
this is 1995 behaviour, and changing it is a deliberate divergence -- flagged for
a fidelity call rather than "fixed" unilaterally.

Diagnostics added (all under the existing BT_ATTRBIND_LOG gate):
  [seqcfg]  extended: the sequence's authored event list
  [seqstart] extended: events / looped / tempo / divisionsPerBeat
  [seqrun]  RunSequence entry state (isRunning / iterator / current event)
  [seqwait] an event exists but is not yet ready, with the time delta
  [seqsend] the sequence actually emitting a control
  [idlewatch] which components receive the idle tick that advances sequences
  [hsparm] extended with &ReportLeak so audio bindings can be correlated
plus scratchpad/night8/leakaudio.sh.

NOTE ON THE PROBES, because it cost real time twice today: [seqrun] first used a
single shared static counter, which the busy sequences consumed so the alarm
sequence never printed -- reading as "RunSequence is never called".  It is now
throttled PER SEQUENCE (runProbeCount).  A capped diagnostic that is silent is
not evidence of absence; the same trap produced a false "seqsend x0" and a false
"the trigger never fires" earlier in this investigation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 18:07:40 -05:00