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:
arcattack
2026-07-21 15:39:56 -05:00
co-authored by Claude Opus 4.8
parent e9db161404
commit 21b2bfc1c9
2 changed files with 50 additions and 1 deletions
+36 -1
View File
@@ -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;
}
}