From 034e55c7f5338fee91c0ee60a603aa1bc1223679 Mon Sep 17 00:00:00 2001 From: Joe DiPrima Date: Tue, 28 Jul 2026 13:35:21 -0500 Subject: [PATCH] prove generator FAILOVER, not just re-attach: BT_POWER_DETACH_TEST takes a subsystem name The #62 verification so far only showed a subsystem detaching and re-attaching to the SAME generator it started on. That exercises the roster walk but never the case players actually hit: your generator dies and Auto has to find a DIFFERENT one. (Raised by the operator, who also pointed out solo has no way to damage a generator -- but it does not need one: a generator has its own on/off button.) BT_POWER_DETACH_TEST now accepts a NAME (=PPC_1, =ERSLaser_1, ...) instead of firing on whichever powered subsystem happens to tick first, so the scenario can be aimed at a real weapon. "1" keeps the old first-one behaviour. Verified end-to-end, everything through real paths -- the detach via DetachFromVoltageSource @004b0e30, the generator kill via its actual RIO button 0x1A through the click seam (EmitButton -> RIO queue -> manager drain -> Generator::ToggleGeneratorOnOff @004b1ed0), no state pokes: BT_POWER_DETACH_TEST=PPC_1 BT_BTNTEST=0x1a,300,320 BT_POWER_LOG=1 [power] TEST: detaching PPC_1 (forcing Auto) [power] AutoConnect RE-ATTACHED PPC_1 -> generator GeneratorA [btntest] PRESS 0x1a at poll 300 <- GeneratorA switched OFF [power] AutoConnect RE-ATTACHED PPC_1 -> generator GeneratorB So the hunt skips the dead generator (HasVoltage requires GeneratorStateOf()==2 plus real measured voltage, powersub.cpp:860-876) and finds a live one. Same result first observed on Avionics; the named form proves it on a WEAPON, which is the player-visible case. Second proof in the same output: EXACTLY ONE re-attach line, then silence. The hunt re-runs every frame while `mode==Auto && !HasVoltage()`, so an attached-but-dark weapon (the #21 symptom) would spam that line forever. One line then quiet means GeneratorB is really supplying it. Co-Authored-By: Claude Opus 5 (1M context) --- game/reconstructed/powersub.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/game/reconstructed/powersub.cpp b/game/reconstructed/powersub.cpp index ae8ca76..82a66dc 100644 --- a/game/reconstructed/powersub.cpp +++ b/game/reconstructed/powersub.cpp @@ -367,8 +367,18 @@ void // (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.) + // BT_POWER_DETACH_TEST=1 detaches the FIRST powered subsystem to tick; + // BT_POWER_DETACH_TEST= targets one by name (e.g. PPC_1, ERSLaser_1), + // which is what proves the failover on a WEAPON rather than on Avionics. + // Pair it with BT_BTNTEST=0x1a,, to switch GeneratorA off + // mid-mission through its real RIO button: the hunt must then re-attach the + // subsystem to a DIFFERENT generator, which is the case players actually hit + // (their generator dies) and the one a same-generator re-attach never tests. static int s_detachTestDone = 0; - if (getenv("BT_POWER_DETACH_TEST") && !s_detachTestDone) + const char *detach_want = getenv("BT_POWER_DETACH_TEST"); + if (detach_want != 0 && !s_detachTestDone + && (detach_want[0] == '\0' || detach_want[1] == '\0' // "1" / empty = first one + || (GetName() != 0 && strcmp(GetName(), detach_want) == 0))) { s_detachTestDone = 1; DEBUG_STREAM << "[power] TEST: detaching " << GetName()