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:
co-authored by
Claude Opus 5
parent
ef8e449a17
commit
e26f4e6285
@@ -353,9 +353,40 @@ void
|
||||
// damaged and currently unpowered, scan every GeneratorClassID segment in
|
||||
// the mech and attach to the first one that will accept a tap.
|
||||
//
|
||||
//
|
||||
// GITEA #62 VERIFICATION HOOK (BT_POWER_DETACH_TEST=1, off by default).
|
||||
// The player-visible bug needs a DETACHED subsystem to show itself, and a
|
||||
// detach only happens via two eng-page BUS MODE presses. This reproduces
|
||||
// that state directly: once per subsystem, drop the voltage link and force
|
||||
// Auto mode -- exactly what the second BUS MODE press does -- so the
|
||||
// auto-hunt below must re-attach it on a later frame. Before the fix the
|
||||
// hunt's body was unreachable and the part stayed dark forever; with the
|
||||
// fix a "[power] AutoConnect RE-ATTACHED" line follows within a frame or
|
||||
// two. Same diagnostic-hook family as BT_VALVE / BT_MP_FORCE_DMG.
|
||||
//
|
||||
// (file-static one-shot, NOT an instance member: the PoweredSubsystem layout
|
||||
// is byte-locked by PoweredSubsystemLayoutCheck -- adding a field would
|
||||
// break sizeof and every offset assert downstream.)
|
||||
static int s_detachTestDone = 0;
|
||||
if (getenv("BT_POWER_DETACH_TEST") && !s_detachTestDone)
|
||||
{
|
||||
s_detachTestDone = 1;
|
||||
DEBUG_STREAM << "[power] TEST: detaching " << GetName()
|
||||
<< " (forcing Auto) -- the auto-hunt must recover it\n" << std::flush;
|
||||
DetachFromVoltageSource(); // @004b0e30 (the BUS MODE press)
|
||||
modeAlarm.SetLevel(AutoConnect); // ...and back to Auto
|
||||
}
|
||||
|
||||
// GITEA #62: the two call shapes restored. Both sites modelled the
|
||||
// no-argument `GetStatusFlags()`, so the outer demanded "no status flags at
|
||||
// all" while the inner demanded "some status flag" -- mutually exclusive
|
||||
// with nothing in between able to change the value, so the roster walk's
|
||||
// body was UNREACHABLE and a subsystem detached by the BUS MODE button
|
||||
// could never re-attach (dark recharge arc for the rest of the mission).
|
||||
// Outer = "am I currently unpowered?" (slot16(this, 0) == 0)
|
||||
if (
|
||||
modeAlarm.GetLevel() == AutoConnect // this[0xb3] @0x2cc == 2
|
||||
&& GetStatusFlags() == 0 // (*this[0x40])(this,0) == 0
|
||||
&& !HasVoltage() // (*this[0x40])(this,0) == 0
|
||||
)
|
||||
{
|
||||
// task #12 ROSTER CORRECTION: the raw @004b0bd0 loop walks owner+0x124/
|
||||
@@ -367,12 +398,20 @@ void
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
Subsystem *sub = mech->GetSubsystem(i); // owner+0x128[i]
|
||||
// Inner = "would THIS generator supply me?" (slot16(this, sub) != 0)
|
||||
if (sub != 0
|
||||
&& sub->GetClassID() == RegisteredClass::GeneratorClassID // +4 == 0xbc1
|
||||
&& GetStatusFlags() != 0
|
||||
&& HasVoltage(sub) // (*this[0x40])(this,sub) != 0
|
||||
&& AttachToVoltageSource(sub) != -1 // FUN_004b0dd8
|
||||
)
|
||||
{
|
||||
// Gitea #62 reachability probe (BT_POWER_LOG): this body was
|
||||
// UNREACHABLE before the two call shapes were restored, so a
|
||||
// single line here is the proof the auto-hunt runs at all.
|
||||
if (getenv("BT_POWER_LOG"))
|
||||
DEBUG_STREAM << "[power] AutoConnect RE-ATTACHED "
|
||||
<< GetName() << " -> generator "
|
||||
<< sub->GetName() << "\n" << std::flush;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -524,16 +563,6 @@ LWord
|
||||
return flags;
|
||||
}
|
||||
|
||||
//
|
||||
// Virtual "has usable voltage" query (vtable+0x40). Base powered subsystem
|
||||
// reports voltage present whenever the electrical state machine is Ready.
|
||||
//
|
||||
Logical
|
||||
PoweredSubsystem::HasVoltage()
|
||||
{
|
||||
return (electricalStateAlarm.GetLevel() == Ready) ? True : False;
|
||||
}
|
||||
|
||||
//
|
||||
// @004b1224 -- prints "<name> = <electrical state>".
|
||||
//
|
||||
@@ -813,12 +842,22 @@ void
|
||||
}
|
||||
|
||||
//
|
||||
// @004b0b5c -- True when the (resolved or supplied) source is in the shorted
|
||||
// state (state 2 in this query) and its measured voltage exceeds the short
|
||||
// threshold.
|
||||
// @004b0b5c -- SLOT 16 (vtable+0x40): the one-argument USABLE-VOLTAGE query.
|
||||
//
|
||||
// Gitea #62 RENAME (the body was already a faithful transcription; only its
|
||||
// name and its wiring were wrong). The old name `IsSourceShorted` asserted the
|
||||
// opposite of what these instructions compute: state 2 is the generator's
|
||||
// ON-LINE state (Generator::GeneratorReady, set by @004b215c's
|
||||
// stateAlarm->2), and the fabsf test requires the output voltage to be
|
||||
// meaningfully non-zero. So a True return means "this source is live and
|
||||
// supplying", not "shorted".
|
||||
//
|
||||
// if (source == 0) source = resolve(this+0x1d0); // MY own link
|
||||
// return source && source->state(+0x210) == 2
|
||||
// && fabsf(source->outputVoltage(+0x1dc) - eps) > eps;
|
||||
//
|
||||
Logical
|
||||
PoweredSubsystem::IsSourceShorted(Subsystem *source)
|
||||
PoweredSubsystem::HasVoltage(Subsystem *source)
|
||||
{
|
||||
if (source == 0)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user