Issue #21: overcharge-bricked weapons -- latent ARCADE bug, deliberate divergence
The tester's ER small laser died permanently (recharge arc + ready dot dark, no fire) after idle seek-gear clicking. Root cause, byte-verified + headless- reproduced -- a latent bug in the SHIPPED ARCADE BINARY: - The charge integrates toward the GENERATOR voltage (@004ba838), not the gear target; "full" is only detected inside the +-1% snap window around seekVoltage[idx] (@004ba738), and overcharge reads ZERO (the byte-verified _DAT_004ba830 = 0.0 clamp). - Toggle seek while a charge is in flight and the level can land ABOVE the new gear's window: rechargeLevel pins to 0, the ==1.0 Loaded test can never fire, and charging continues to the generator ceiling where EVERY gear reads overcharged. Permanent brick; generator reselect cannot help. - The arcade dodged it by circumstance (locked 60 fps, rare seek use); the port's clickable seek button hits it in seconds of casual clicking. Fix (marked divergence in the Loading tick): overcharge counts as fully charged -> Loaded. The arcade's own discharge algebra says full == the gear's seek voltage, so an overcharged weapon IS full. Verified: the same 2.5-min BT_SEEKTEST abuse that bricked the laser pre-fix now fires 38 overcharge rescues and ends Loaded/pct=1. [seek] log now also prints the per-gear voltage table + gen voltages (diagnosis instrumentation). Ruled out on the way (documented in #21): overheat fuse (no firing occurred; a 4-min autofire+coolant-abuse run wouldn't fuse), generator detach (gen cycling + healthy siblings), seek-table corruption (table {6000,7000,8000, 9900} healthy). KB: decomp-reference.md records the arcade-latent-bug analysis. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
e9db161404
commit
21b2bfc1c9
@@ -274,6 +274,20 @@ From the weapon `.SUB` records + the charge-curve `.data` constants (PE-parsed a
|
||||
ResetFiringState); Emitter's faithful body existed as the orphaned "AdvanceSeekVoltage" (zero
|
||||
callers) -- both registered via MESSAGE_ENTRY (ids 9/0xb). Verify: BT_SEEKTEST=1 +
|
||||
BT_SEEK_LOG=1 → `[seek] <name> -> gear N` cycling. EjectAmmo remains unwired.
|
||||
**LATENT ARCADE BUG found + diverged (issue #21, 2026-07-21) [T1 byte-verified + T2 repro]:**
|
||||
the Emitter charge integrates toward the GENERATOR voltage (@004ba838 `fld [src+0x1dc]`), full
|
||||
is detected only inside the +-1% snap window around `seekVoltage[idx]` (ComputeOutputVoltage
|
||||
@004ba738: `|rl-1|<=0.01 -> 1.0`), and OVERCHARGE reads ZERO (`rl>1.01 -> 0`,
|
||||
`_DAT_004ba830 = 0.0` byte-verified). Toggle the seek gear while a charge is in flight and
|
||||
the level lands ABOVE the new gear's window -> rechargeLevel pinned 0, the `==1.0` Loaded test
|
||||
(`_DAT_004bac04`) never fires, charging continues to the generator ceiling where EVERY gear
|
||||
reads overcharged -> the weapon is PERMANENTLY bricked (arc dark, ready-dot dark, no fire; gen
|
||||
re-select cannot help). The arcade shipped this (locked 60 fps + rare seek use hid it); the
|
||||
port's clickable seek button triggers it in seconds. **Deliberate divergence** (emitter.cpp
|
||||
Loading tick): overcharge (`currentLevel > seekVoltage[idx]`) counts as fully charged ->
|
||||
Loaded ("full == the gear's seek voltage" is the arcade's own discharge algebra). Repro +
|
||||
verify: BT_SEEKTEST seek-abuse -- pre-fix deadlocks at pct=0/alarm=3; post-fix 38 rescues,
|
||||
ends Loaded/pct=1. `[seek]` log prints the per-gear table + rescue events.
|
||||
- **MechRIOMapper** @0x51DD30: RE-REGISTERS the base ids 3..0x13 (Aux1Quad..ZoomOut) with the
|
||||
RIO override handlers @004d22fc..@004d24f8, + {0x19, Keypress→@004d2514} (RIO's OWN keypress,
|
||||
unreconstructed — we use the shared L4 0x17 @004d1bf0) + {0x1a, Hotbox→@004d2574} @0x51DE98.
|
||||
|
||||
@@ -510,6 +510,32 @@ void
|
||||
weaponAlarm.SetLevel(2); // -> Loaded
|
||||
break;
|
||||
}
|
||||
// OVERCHARGE RESCUE (issue #21, 2026-07-21) -- a DELIBERATE
|
||||
// divergence from the binary, which DEADLOCKS here: change the
|
||||
// seek gear while a charge is in flight and the level can land
|
||||
// ABOVE the new gear's snap window (level/seekV > 1.01) -- the
|
||||
// byte-verified ComputeOutputVoltage clamp then zeroes
|
||||
// rechargeLevel (_DAT_004ba830 = 0.0) and the ==1.0 Loaded test
|
||||
// can never fire again while TrackSeekVoltage keeps charging
|
||||
// toward the generator voltage: the weapon is PERMANENTLY
|
||||
// bricked (arc dark, dot dark, never fires -- reproduced
|
||||
// headless, and trivial to trigger from the clickable seek
|
||||
// button; the pod's locked 60 fps + rare seek use hid it).
|
||||
// The arcade's own math says "full == the gear's seek voltage"
|
||||
// (discharge energy computes from seekV[rec]), so an
|
||||
// overcharged weapon IS fully charged -- treat it as Loaded.
|
||||
if (currentLevel > seekVoltage[seekVoltageIndex]
|
||||
&& seekVoltage[seekVoltageIndex] > 0.0f)
|
||||
{
|
||||
rechargeLevel = 1.0f; // full (the display/damage ratio)
|
||||
weaponAlarm.SetLevel(2); // -> Loaded
|
||||
if (getenv("BT_SEEK_LOG"))
|
||||
DEBUG_STREAM << "[seek] " << GetName()
|
||||
<< " overcharge rescue: level=" << currentLevel
|
||||
<< " > seekV=" << seekVoltage[seekVoltageIndex]
|
||||
<< " -> Loaded" << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -683,7 +709,16 @@ void
|
||||
ResetFiringState(); // @004ba9a8
|
||||
}
|
||||
if (getenv("BT_SEEK_LOG"))
|
||||
DEBUG_STREAM << "[seek] " << GetName() << " -> gear " << next << std::endl;
|
||||
{
|
||||
Generator *src = (Generator *)ResolveVoltageSource();
|
||||
DEBUG_STREAM << "[seek] " << GetName() << " -> gear " << next
|
||||
<< " seekV[" << next << "]=" << seekVoltage[next]
|
||||
<< " table={" << seekVoltage[0] << "," << seekVoltage[1] << ","
|
||||
<< seekVoltage[2] << "," << seekVoltage[3] << "," << seekVoltage[4]
|
||||
<< "} max=" << maxSeekVoltageIndex << " level=" << currentLevel
|
||||
<< " genV=" << (src ? src->MeasuredVoltage() : -1.0f)
|
||||
<< " genRated=" << (src ? src->RatedVoltageOf() : -1.0f) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user