diff --git a/context/decomp-reference.md b/context/decomp-reference.md index e9964e4..a73fd0d 100644 --- a/context/decomp-reference.md +++ b/context/decomp-reference.md @@ -273,7 +273,19 @@ From the weapon `.SUB` records + the charge-curve `.data` constants (PE-parsed a novice + @4ad7d4 heat-model + press-only, then idx@0x320=(idx+1)%(max@0x32C+1), NO 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] -> gear N` cycling. EjectAmmo remains unwired. + BT_SEEK_LOG=1 → `[seek] -> gear N` cycling. EjectAmmo remains unwired (#31). + **EjectAmmo @004bb9b8 DECODED 2026-07-23 (raw disasm; the fn sits in a Ghidra export gap) + [T1]** — old-timer testimony ("tap ejects a round, hold ejects the bay, eject clears a jam") + matched the bytes exactly: novice guard @4ac9c8 → **PRESS** (`msg+0xc > 0`): bin alarm + (`bin+0x194`) → 3, `timeToEject=total`, `percentOfEject=0`, `ejectState=0` (UpdateEject + @004bbb50 runs the ~3 s countdown; completion = DumpAmmo @004bd588 whole-bay → + weaponAlarm 7). **RELEASE** with the countdown still live (**TAP**): bin alarm 4→1, + `EjectOneRound` @004bd4f4 (`count--`, feedTimer reset, count<1 → bin state 2); bin + empty → weaponAlarm 7, else **weaponAlarm 3 (Loading) + reloadTimer restart — the + weapon RETURNS TO SERVICE. A TAP is the in-mission UNJAM** (state 5 has no exit inside + the Execute machine, but this handler overwrites it). The earlier "jams are + mission-permanent / no unjam" claim was WRONG (read only the hold path) — corrected + + swept 2026-07-23 (projweap.cpp case-5/jam-log comments, players/README.txt). **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 diff --git a/context/open-questions.md b/context/open-questions.md index 90e7548..c8d7b24 100644 --- a/context/open-questions.md +++ b/context/open-questions.md @@ -664,7 +664,12 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR Loaded case with the authentic 4-blip stay-Loaded denial; recoil bleeds ONLY in Loading at electrical-Ready and clamps at 0; slot-17 @004b9c9c called from Loading/7 (the launcher recharge dial now ANIMATES). State 7 kept binary-faithful (re-asserted unconditionally — - permanent once entered; recovery only via ResetToInitialState/mission reset). Solo-verified + no exit INSIDE the Execute machine; recovery via ResetToInitialState/mission reset, OR — per + the 2026-07-23 EjectAmmo @004bb9b8 disasm, [[decomp-reference]] §message-tables — an EJECT + TAP, which overwrites the alarm from outside the machine: bin still holding rounds → + weaponAlarm 3 + reload restart, so even a FailureHeat 7 recovers while ammo remains; the + empty-bin gate 2 re-pins 7 only when the bay is actually dry. Same handler: state-5 jams + clear the same way — the earlier "no in-mission unjam" claim was wrong). Solo-verified on 4.11.388+: 54-60 `[projectile] PUSH` + impact smoke; denial window (BT_LOOK_TEST look-back) = trig pulses, state STAYS 2, zero launches, resumes on release; disabled-mech gate pins enemy launchers at 7+full recoil; STREAK-6 dial sweeps across screenshots; lasers diff --git a/game/reconstructed/projweap.cpp b/game/reconstructed/projweap.cpp index dfa62aa..5fe3d11 100644 --- a/game/reconstructed/projweap.cpp +++ b/game/reconstructed/projweap.cpp @@ -551,8 +551,11 @@ Logical // so gate the roll on the weapon actually being in a degraded HEAT STATE (heatAlarm // level >= 1) instead of the raw heatLoad. In bring-up the alarm stays 0 (temp << // degradation threshold) -> weapons fire reliably; real jams return once the heat model - // drives true temps. (The old FOLLOW-UP suggesting an "unjam delay" is RETIRED -- - // 2026-07-23 decomp check: the binary has NO in-mission unjam; permanence is authentic.) + // drives true temps. (2026-07-23 CORRECTION, 2nd pass: an in-mission unjam DOES + // exist -- the EjectAmmo handler @004bb9b8 (msg 0xb, TAP = eject one round -> + // weaponAlarm 3 Loading + reload restart). The earlier "no unjam" claim read only + // the HOLD path (UpdateEject -> DumpAmmo -> NoAmmo); the tap path sat in a decomp + // export gap. Old-timer testimony matched the bytes: tap = round, hold = bay.) if (heatAlarm.GetLevel() < HeatSink::DegradationHeat) // was: if (heatLoad <= 0.0f) return False; @@ -710,11 +713,11 @@ void weaponAlarm.SetLevel(7); // -> unavailable (gate 2 re-pins it) else if (CheckForJam()) // @4bbf2a call 0x4bbfcc (heat-scaled roll) { - // ALWAYS log a jam (rare + mission-permanent; field 2026-07-23) + // ALWAYS log a jam (rare; cleared by an EJECT tap -- @004bb9b8) DEBUG_STREAM << "[weap] " << GetName() << " JAMMED (T=" << currentTemperature << "/" << failureTemperature << " heatAlarm=" << (int)heatAlarm.GetLevel() - << ") -- permanent until mission end\n" << std::flush; + << ") -- EJECT tap to clear\n" << std::flush; weaponAlarm.SetLevel(JammedState); // @4bbf34 -> 5 } else @@ -775,13 +778,14 @@ void // -- the recharge dial ANIMATES break; - case 5: // Jammed @4bbe8e -- cleared ONLY by ResetToInitialState (mission - // reset/respawn). ⚠ CORRECTED 2026-07-23: msg 2 CAUSES a jam - // (HandleMessage @004bcabc: SetLevel(this+0x350, 5) -- a - // malfunction inducer), it does NOT clear one; the old comment - // had it backwards. There is NO in-mission unjam in the - // binary -- the EJECT cycle (UpdateEject) is the anti-cook-off - // ammo JETTISON (DumpAmmo -> NoAmmo), not an unjam. + case 5: // Jammed @4bbe8e. Within THIS state machine nothing exits 5, but + // the EJECT button does (⚠ 2nd CORRECTION 2026-07-23, disasm of the + // export-gap handler @004bb9b8, msg 0xb EjectAmmo, novice-guarded): + // TAP (press then release) ejects ONE round (@4bd4f4 count--) and, + // with ammo remaining, SetLevel(+0x350, 3) + reload restart -- the + // weapon RETURNS TO SERVICE. HOLD ~3s runs UpdateEject to DumpAmmo + // (whole bay -> NoAmmo 7). So: tap = unjam, hold = jettison the bay + // (anti-cook-off). msg 2 still CAUSES a jam (@004bcabc inducer). if (trigger) { weaponAlarm.SetLevel(TriggerDuringJamState); // @4bbe95 blip 6 diff --git a/players/README.txt b/players/README.txt index c5eb71c..79dcc90 100644 --- a/players/README.txt +++ b/players/README.txt @@ -44,8 +44,11 @@ CONTROLS (keyboard): - / = ................ game volume down/up (saved between sessions) A weapon gone quiet with ammo left has probably JAMMED (heat makes ballistic weapons jam -- authentic!). Check its panel's ENG DATA page - for the jam lamp (a cocked round with warning bolts). Jams last the - mission; a respawn clears them. Watch the TEMP strip and pace salvos. + for the jam lamp (a cocked round with warning bolts). The pod's fix + is the EJECT button: TAP to eject the jammed round (weapon reloads and + returns to service), HOLD ~3s to jettison the whole bay before it + cooks off. (Wiring in progress -- until it lands, a respawn clears + jams.) Watch the TEMP strip and pace salvos. G (hold) + fire key .. regroup a weapon onto that trigger -- or hold a weapon panel's red PROGRAM button with the mouse and tap a fire key; the joystick diagram on the panel shows the binding live.