Gitea #62 FIXED: AutoConnect can re-attach again -- slot 16 is HasVoltage(source), not GetStatusFlags()

A weapon detached by two eng-page BUS MODE presses stayed dark for the REST OF
THE MISSION: no voltage, blank recharge arc, dead ready dot, will not fire.
The arcade recovers by pressing bus-mode back to Auto and letting the per-frame
auto-hunt re-tap a generator.  In the port that hunt's body was UNREACHABLE.

THE DEFECT, visible in three lines of powersub.cpp:

    if (modeAlarm == AutoConnect && GetStatusFlags() == 0)   // outer: NOT damaged
        for (...)
            if (... && GetStatusFlags() != 0 && Attach(sub))  // inner: DAMAGED?!

Both call sites modelled the no-argument GetStatusFlags(), so the outer demanded
"no status flags" and the inner "some status flag", with nothing in between able
to change the value -- mutually exclusive, body never entered.  The binary's
@004b0bd0 calls vtable slot +0x40 (slot 16) with TWO SHAPES: slot16(this, 0) == 0
("am I unpowered?") and slot16(this, candidate) != 0 ("would THIS generator
supply me?").

WHAT WAS ACTUALLY WRONG was narrower than the issue assumed -- the BODY was
already a faithful transcription of @004b0b5c (complete-type accessors, no raw
offsets).  Only its NAME and its WIRING were wrong:
  * it was called `IsSourceShorted`, asserting the INVERSE of what it computes:
    state 2 is the generator's ON-LINE state (Generator::GeneratorReady, what
    @004b215c's stateAlarm->2 sets) and the fabsf test requires meaningfully
    non-zero output voltage.  True means "live and supplying".
  * it was declared NON-VIRTUAL, so it could not be the slot-16 body, and a weak
    stand-in (`electricalStateAlarm == Ready`, no source arg, no voltage test)
    occupied `HasVoltage()` instead.

FIX: rename to `virtual Logical HasVoltage(Subsystem *source = 0)`, delete the
stand-in, restore the two AutoConnect call shapes, and rename Myomers' slot-16
override (`HasAdequateVoltage` -> `HasVoltage`) so it actually overrides -- it
tightens the test to "at least the SELECTED SEEK voltage", which is why it
exists.

BLAST RADIUS (the issue's warning): GetStatusFlags ORs the BadPower bit 0x40 on
!HasVoltage(), and that bit feeds #47's annunciator -- so power, firing and
annunciation semantics moved together.  Checked: a healthy solo mission shows
zero spurious BadPower, subsystems simulate normally, no faults.

VERIFIED with a new diagnostic hook in the codebase's existing family
(BT_POWER_DETACH_TEST=1, off by default): it reproduces the player's exact state
-- drop the voltage link and force Auto, i.e. what the second BUS MODE press
does -- so the fix can be tested without the eng-page UI.  Live result:

    [power] TEST: detaching Avionics (forcing Auto) -- the auto-hunt must recover it
    [power] AutoConnect RE-ATTACHED Avionics -> generator GeneratorA

Before the fix that second line was impossible.  BT_POWER_LOG=1 prints every
re-attach; both hooks stay for regression use.

(The file-static one-shot is deliberate: PoweredSubsystem's layout is byte-locked
by PoweredSubsystemLayoutCheck, so a test flag as an instance member would break
sizeof and every offset assert downstream.)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-27 08:34:00 -05:00
co-authored by Claude Opus 5
parent ef8e449a17
commit e26f4e6285
4 changed files with 74 additions and 25 deletions
+6 -3
View File
@@ -420,10 +420,13 @@ Scalar BTMyomersSeekSample(void *subsystem, Scalar voltage)
//***************************************************************************
// Myomers::HasAdequateVoltage slot 16 @004b8f3c
// Myomers::HasVoltage slot 16 @004b8f3c
//***************************************************************************
//
// Override of PoweredSubsystem::IsSourceShorted (slot 16, base @004b0b5c).
// Override of PoweredSubsystem::HasVoltage (slot 16, base @004b0b5c) -- Gitea
// #62 rename: the base was misnamed IsSourceShorted, asserting the inverse of
// what it computes. Myomers tightens it: adequate = at least the SELECTED SEEK
// voltage, not merely non-zero.
// Returns True only when the (resolved) voltage source can supply at least
// the currently-selected seek voltage AND is in the Ready state (state==2).
//
@@ -432,7 +435,7 @@ Scalar BTMyomersSeekSample(void *subsystem, Scalar voltage)
// return seekVoltage[currentSeekVoltageIndex] <= source->outputVoltage
// && source->state == 2; // source+0x1DC, source+0x210
//
Logical Myomers::HasAdequateVoltage(Subsystem *source)
Logical Myomers::HasVoltage(Subsystem *source)
{
Generator *gen = source ? (Generator *)source
: (Generator *)ResolveVoltageSource(); // FUN_00417ab4(this+0x1D0)