Issue #19: wire ToggleSeekVoltage (Myomers id 9 + energy weapons id 0xb)
Root cause -- NOT issue #2 (the experience mis-seed pins everyone to VETERAN, which PASSES the novice lockout; the gates were never the blocker). Two unwired handlers, the same silent-swallow pattern ToggleCooling had (Receiver::Receive -> NullHandler for an unregistered id): - Myomers (handler table @0x51158C {9,"ToggleSeekVoltage"@004b8a48}): the reconstruction had an EMPTY stub (the address falls in the untagged decomp gap 0x4b8837..0x4b8a8c) and never registered the id. Body reconstructed faithfully from the raw disassembly (tools/disas2.py 0x4b8a48): novice lockout (@4ac9c8) -> heat-model gate (@4ad7d4) -> press-only (msg+0xc>0) -> currentSeekVoltageIndex@0x320 = (idx+1) % (maxSeekVoltageIndex@0x32C + 1). Modulo from zero, min index not consulted, no ResetFiringState. - Emitter (energy-weapon table @0x511DB8 {0xb,"ToggleSeekVoltage"@004ba478}): the faithful body already existed as the orphaned "AdvanceSeekVoltage" (ZERO callers) -- converted to the registered message handler (same guards; cycle + ResetFiringState when not wrapping, disasm re-verified). PPC and GaussRifle copy-inherit the set; the ammo branch keeps its own id 0xb == EjectAmmo (still unwired, tracked separately). Both registered via MESSAGE_ENTRY on top of the inherited chains. Permanent diagnostics: [seek] gear log (BT_SEEK_LOG) + BT_SEEKTEST scripted dispatch harness (mech4.cpp, the cooltoggle pattern). VERIFIED headless: [seek] Myomers -> 3,0,1,2,3... and [seek] PPC_1 -> 3,0,1,2,3... (modulo-4 wrap, matching the binary's arithmetic). Awaiting the tester's live ADV-mode confirmation for the real MFD button route (the routing layer is the same streamed-EventMapping path ToggleCooling verified). KB: decomp-reference.md message-table rows marked wired. 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
c18d2b0213
commit
4aab10ba89
@@ -87,7 +87,16 @@ Derivation
|
||||
Receiver::MessageHandlerSet&
|
||||
Myomers::GetMessageHandlers()
|
||||
{
|
||||
// issue #19 (2026-07-21): register the binary's Myomers handler table
|
||||
// @0x51158C {9, "ToggleSeekVoltage" -> @004b8a48} on top of the inherited
|
||||
// powered ids -- the id was unregistered, so the ADV-mode seek-gear press
|
||||
// was silently swallowed (Receiver::Receive NullHandler).
|
||||
static const Receiver::HandlerEntry entries[]=
|
||||
{
|
||||
MESSAGE_ENTRY(Myomers, ToggleSeekVoltage) // id 9 @004b8a48
|
||||
};
|
||||
static Receiver::MessageHandlerSet messageHandlers(
|
||||
ELEMENTS(entries), entries,
|
||||
PoweredSubsystem::GetMessageHandlers()); // copy-inherit (ids 4-8)
|
||||
return messageHandlers;
|
||||
}
|
||||
@@ -563,16 +572,32 @@ void Myomers::DisconnectFromMover(SubsystemMessage &message)
|
||||
|
||||
|
||||
//***************************************************************************
|
||||
// Myomers::ToggleSeekVoltage @004b8a48
|
||||
// Myomers::ToggleSeekVoltageMessageHandler @004b8a48
|
||||
//***************************************************************************
|
||||
// Mappable command (attribute id 0x09 "ToggleSeekVoltage", bound in the
|
||||
// @00511588 table to handler @004b8a48). Cycles currentSeekVoltageIndex
|
||||
// @0x320 between minSeekVoltageIndex@0x328 and maxSeekVoltageIndex@0x32C.
|
||||
// BEST-EFFORT: present in the binary but not captured in the decompiled
|
||||
// shards (the address falls in the untagged gap 0x4b8837..0x4b8a8c).
|
||||
void Myomers::ToggleSeekVoltage()
|
||||
// Handler table @0x51158C: {9, "ToggleSeekVoltage"}. Reconstructed FAITHFULLY
|
||||
// from the raw disassembly (tools/disas2.py 0x4b8a48; the decomp shards missed
|
||||
// the untagged gap 0x4b8837..0x4b8a8c) -- issue #19 fix, 2026-07-21:
|
||||
// 0x4b8a50 call 0x4ac9c8 novice lockout (BTPlayerRoleLocksAdvanced) -> ret
|
||||
// 0x4b8a5b call 0x4ad7d4 heat-model experience gate -> ret if OFF
|
||||
// 0x4b8a68 msg+0xc <= 0 press only (release is negative)
|
||||
// 0x4b8a6f idx@0x320 = (idx + 1) % (maxSeekVoltageIndex@0x32C + 1)
|
||||
// Note: modulo (max+1) from ZERO -- minSeekVoltageIndex is NOT consulted, and
|
||||
// there is no ResetFiringState (both unlike the Emitter sibling @004ba478).
|
||||
void Myomers::ToggleSeekVoltageMessageHandler(ReceiverDataMessageOf<int> *message)
|
||||
{
|
||||
// cycle currentSeekVoltageIndex within [minSeekVoltageIndex, maxSeekVoltageIndex]
|
||||
if (IsDamaged()) // FUN_004ac9c8 -- NOVICE lockout bridge
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!HeatModelActive() || message->dataContents <= 0) // FUN_004ad7d4; msg+0xc
|
||||
{
|
||||
return;
|
||||
}
|
||||
currentSeekVoltageIndex = // @0x320
|
||||
(currentSeekVoltageIndex + 1) % (maxSeekVoltageIndex + 1); // @0x32C
|
||||
if (getenv("BT_SEEK_LOG"))
|
||||
DEBUG_STREAM << "[seek] " << GetName() << " -> gear "
|
||||
<< currentSeekVoltageIndex << std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user