diff --git a/game/reconstructed/powersub.cpp b/game/reconstructed/powersub.cpp index 258976b..ffa1b97 100644 --- a/game/reconstructed/powersub.cpp +++ b/game/reconstructed/powersub.cpp @@ -1085,11 +1085,100 @@ Logical Generator::TestClass(Mech &) { return True; } Logical Generator::TestInstance() const { return IsDerivedFrom(*GetClassDerivations()); } // -// Generator vtable slot 10 -- recovered VERBATIM from the surviving GNRATOR.TCP -// fragment (the one Generator method the @0x4b02f0.. cluster decomp omitted; see -// gnrator.cpp). Chains the HeatableSubsystem reset (NOT the HeatSink one -- -// matching the inlined base chain seen across the HeatSink family) and forces -// the generator output cold. +Receiver::MessageHandlerSet& + Generator::GetMessageHandlers() +{ + // + // Gitea #53. Chains **HeatSink**, deliberately NOT PoweredSubsystem: id 4 on + // PoweredSubsystem is `SelectGeneratorA` (the weapon-side generator SELECTION, + // which already works), so chaining there would make the four ON/OFF buttons + // invoke the wrong function instead of doing nothing. HeatSink owns id 3 + // (ToggleCooling) and chains onward to the root, leaving id 4 free for us. + // + static const Receiver::HandlerEntry entries[]= + { + MESSAGE_ENTRY(Generator, ToggleGeneratorOnOff) // id 4 @004b1ed0 + }; + static Receiver::MessageHandlerSet messageHandlers( + ELEMENTS(entries), entries, + HeatSink::GetMessageHandlers() + ); + return messageHandlers; +} + +// +// @004b1ed0 -- ToggleGeneratorOnOff: the pod's Generator A-D ON/OFF buttons (RIO +// addresses 0x1A-0x1D on the radar rail, one per generator). Transcribed +// instruction-for-instruction from the binary; the body did not exist before +// (Gitea #53 -- 0 references in game/). +// +// call 0x4ac9c8(this); if (ret) return; -> BTPlayerRoleLocksAdvanced: a +// NOVICE pilot cannot touch it +// if ([msg+0xc] <= 0) return; -> press only, ignore the release +// if ([this+0x1d4] != 0) goto OFF -> generatorOn -> we are turning it off +// +// ON: eax = [this+0x184] -> heatAlarm level +// if (eax >= 1) { [this+0x1f0] = 0; } -> heat degraded/failed: clear the +// start timer but LEAVE the state +// alarm in its fault level +// else { [this+0x1f0] = 0; +// Set_Alarm_Level(this+0x1fc, 0); } +// -> healthy: also drop the state alarm +// to Starting so it spins up +// [this+0x1d4] = 1 -> generatorOn = 1 +// [this+0x134] = 1 -> coolantAvailable = 1 +// [this+0x15c] = 1.0f -> coolantFlowScale = 1.0 +// +// OFF: Set_Alarm_Level(this+0x1fc, 1) -> stateAlarm = GeneratorIdle +// [this+0x1d4] = 0 -> generatorOn = 0 +// [this+0x134] = 0 -> coolantAvailable = 0 +// [this+0x15c] = 0.0f -> coolantFlowScale = 0 (its coolant +// share is released to the loop) +// +// So taking a generator off line idles it, drops its coolant draw, and -- via the +// state machine reading generatorOn -- stops it supplying voltage to its taps. +// That is the authentic heat-management trade the manual describes. +// +void + Generator::ToggleGeneratorOnOffMessageHandler(ReceiverDataMessageOf *message) +{ + if (BTPlayerRoleLocksAdvanced(owner)) // @004ac9c8 -- NOVICE lock + { + return; + } + if (message->dataContents <= 0) // press only + { + return; + } + + if (generatorOn == 0) + { + // ---- turning it ON ---- + startTimer = 0.0f; // @0x1F0 + if ((unsigned)heatAlarm.GetLevel() < 1) // @0x184 -- healthy + { + stateAlarm.SetLevel(0); // @0x1FC -> Starting (spin up) + } + generatorOn = 1; // @0x1D4 + coolantAvailable = 1; // @0x134 + coolantFlowScale = 1.0f; // @0x15C + } + else + { + // ---- taking it OFF LINE ---- + stateAlarm.SetLevel(1); // @0x1FC -> GeneratorIdle + generatorOn = 0; // @0x1D4 + coolantAvailable = 0; // @0x134 + coolantFlowScale = 0.0f; // @0x15C + } + + if (getenv("BT_FIRE_LOG") || getenv("BT_HEAT_LOG")) + DEBUG_STREAM << "[gensel] " << GetName() << " generator " + << (generatorOn ? "ON LINE" : "OFF LINE") + << " (stateAlarm " << stateAlarm.GetLevel() + << ", coolantFlowScale " << coolantFlowScale << ")" << std::endl; +} + // void Generator::ResetToInitialState(Logical powered) diff --git a/game/reconstructed/powersub.hpp b/game/reconstructed/powersub.hpp index 2822dd7..42ccaae 100644 --- a/game/reconstructed/powersub.hpp +++ b/game/reconstructed/powersub.hpp @@ -480,6 +480,28 @@ class Generator; return (ownZone != 0) ? (Scalar)ownZone->damageLevel : (Scalar)0.0f; // +0x158 } + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Message handling (Gitea #53 -- the Generator A-D ON/OFF buttons). + // + // Generator had NO handler set of its own, so every message aimed at it hit a + // default-constructed blackhole and was dropped in silence. Proved live with + // the new unhandled-message trace by pressing button 0x1A: + // [msg] UNHANDLED: Generator has no handler for message id 4 + // + // The set MUST chain HeatSink, not PoweredSubsystem: on PoweredSubsystem id 4 + // is `SelectGeneratorA` (a completely different function -- the weapon-side + // generator SELECTION that already works), so chaining there would make these + // four buttons do the WRONG thing rather than nothing. HeatSink owns id 3 + // (ToggleCooling), leaving id 4 free. + // + public: + enum { ToggleGeneratorOnOffMessageID = 4 }; // table @0050fb90, fn @004b1ed0 + + static Receiver::MessageHandlerSet& GetMessageHandlers(); + + void + ToggleGeneratorOnOffMessageHandler(ReceiverDataMessageOf *message); + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Local data. //