Gitea #55: respawn now restores COOLANT / heat / generator state (+ a Generator reset re-transcribed from the binary)
THE GAP: Mech::Reset sweeps every subsystem with the virtual DeathReset (mech4.cpp:1789), but only 7 weapon/ammo classes overrode it -- the entire heat/coolant/power family fell through to the empty Subsystem::DeathReset base (SUBSYSTM.h:161), so the respawn reset was a SILENT NO-OP for them. All the ResetToInitialState bodies already existed; nothing ever called them. Hence the field report: 'respawned with drained coolant'. Added DeathReset forwarders (ResetToInitialState is not virtual, so each class with its own body needs one): HeatableSubsystem, HeatSink, Condenser, PoweredSubsystem, Generator. HeatSink's also serves Reservoir -- the coolant tank -- which has no body of its own; that is the one that refills coolant. TWO MIS-TRANSCRIPTIONS FIXED, both verified against the binary with capstone: 1. HeatSink::ResetToInitialState chained HeatableSubsystem::ResetToInitialState with the comment 'FUN_004ac22c'. But @004ac22c is Subsystem's terminus -- disassembled: it reads the damage zone at this+0xe0, clears zone+0x158, and Set_Alarm_Levels zone+0x10 and this+0x2c to 0. And the binary's HeatSink @004ad760 calls only @004ad884 (ClearHeatFilter), @004ad7f0 (UpdateHeatLoad) and @004ac22c -- never HeatableSubsystem. Worse, HeatableSubsystem's body sets currentTemperature = 300.0f, CLOBBERING the startingTemperature that HeatSink assigned four lines earlier. Dropped the call; the terminus work is already done for every subsystem by MechSubsystem::RespawnRepair (mech4.cpp:1790). 2. Generator::ResetToInitialState chained HeatableSubsystem and set outputVoltage = 0 -- which matches GNRATOR.TCP, but BT's BINARY DIVERGES from the TCP source, and the binary is what shipped. Disassembled @004b215c instruction by instruction: it chains HeatSink (so the coolant refill DOES run for a generator), then generatorOn=1, startTimer=0, stateAlarm 0 then 2, **outputVoltage = ratedVoltage**, coolantAvailable=1, coolantFlowScale=1.0, startTimer=startTime. Every offset matches our declared members exactly. The old version brought generators back from a respawn at ZERO output voltage, so energy weapons could never charge -- no recharge ring, no ready dot, nothing damaged. That is a strong candidate for #54 (David's three dead lasers). LIVE VERIFICATION (sim3.py, 3 mechs + force damage): 4 death/respawn cycles, and every heat sink logged coolant == capacity on every respawn, with temp restored to startingTemperature (77) rather than the clobbered 300. Left as a silent regression guard that only speaks if a respawn leaves coolant short. KNOWN REMAINING [T3, noted in code]: Condenser's body chains HeatableSubsystem rather than HeatSink, so a coolant loop's VALVE DETENT may still persist across a respawn -- the binary's Condenser reset is not yet decompiled. Do not claim valves are fixed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0166KTsC7ADm7VXEi1HF1jNg
This commit is contained in:
co-authored by
Claude Opus 5
parent
2518e43719
commit
08977ff128
@@ -180,6 +180,17 @@ void
|
||||
heatLoad = 0.0f;
|
||||
}
|
||||
|
||||
//
|
||||
// DeathReset (Gitea #55): the respawn sweep's entry for a bare HeatableSubsystem.
|
||||
// Before this, the sweep hit the empty `Subsystem::DeathReset` base and nothing in
|
||||
// the heat family was reset on respawn at all.
|
||||
//
|
||||
void
|
||||
HeatableSubsystem::DeathReset(int /*reset_command*/)
|
||||
{
|
||||
ResetToInitialState(True);
|
||||
}
|
||||
|
||||
//###########################################################################
|
||||
// TestClass -- HeatableSubsystem
|
||||
//
|
||||
@@ -337,6 +348,21 @@ void
|
||||
HeatableSubsystem::ResetToInitialState(True);
|
||||
}
|
||||
|
||||
//
|
||||
// DeathReset (Gitea #55): the respawn sweep's entry for a Condenser -- a coolant
|
||||
// loop's valve. NOTE the body above chains HeatableSubsystem's (temperature +
|
||||
// heatLoad) and so does NOT run HeatSink's coolant refill, even though Condenser
|
||||
// derives from HeatSink. That mirrors the existing reconstruction; whether the
|
||||
// binary's Condenser reset also restores the VALVE SETTING is not established from
|
||||
// the decomp yet, so valve detents may still persist across a respawn.
|
||||
// [T3 -- do NOT claim valves are fixed; tracked on the issue.]
|
||||
//
|
||||
void
|
||||
Condenser::DeathReset(int /*reset_command*/)
|
||||
{
|
||||
ResetToInitialState(True);
|
||||
}
|
||||
|
||||
//###########################################################################
|
||||
// TestClass / TestInstance / CreateStreamedSubsystem -- Condenser
|
||||
//
|
||||
@@ -646,7 +672,50 @@ void
|
||||
UpdateHeatLoad(); // FUN_004ad7f0
|
||||
coolantActive = 0;
|
||||
|
||||
HeatableSubsystem::ResetToInitialState(True); // FUN_004ac22c
|
||||
//
|
||||
// CHAIN CORRECTION (Gitea #55). This used to call
|
||||
// `HeatableSubsystem::ResetToInitialState(True)` with the comment
|
||||
// "FUN_004ac22c" -- but @004ac22c is NOT HeatableSubsystem's body.
|
||||
// Disassembled: @004ac22c reads the subsystem's damage zone (`this+0xe0`),
|
||||
// clears `zone+0x158`, then `Set_Alarm_Level(zone+0x10, 0)` and
|
||||
// `Set_Alarm_Level(this+0x2c, 0)` -- it is **Subsystem::ResetToInitialState**,
|
||||
// the terminus that heals the subsystem's own crit zone and status alarm.
|
||||
// And the binary's own HeatSink @004ad760 calls exactly three things:
|
||||
// @004ad884 (ClearHeatFilter), @004ad7f0 (UpdateHeatLoad), @004ac22c -- it
|
||||
// never touches HeatableSubsystem's body.
|
||||
//
|
||||
// Calling HeatableSubsystem's body here was actively harmful: it sets
|
||||
// `currentTemperature = 300.0f`, **clobbering the `startingTemperature` this
|
||||
// function assigned four lines above**.
|
||||
//
|
||||
// The terminus work (zone heal + status alarm) is already done for every
|
||||
// subsystem by `MechSubsystem::RespawnRepair()`, which `Mech::Reset` calls
|
||||
// immediately after `DeathReset` in the same loop (mech4.cpp:1790) -- so the
|
||||
// chain ends here rather than duplicating it.
|
||||
//
|
||||
}
|
||||
|
||||
//
|
||||
// DeathReset -- the respawn sweep's entry point (Gitea #55). Serves HeatSink and
|
||||
// every derived class that has no reset body of its own -- notably **Reservoir**
|
||||
// (the coolant tank), which is what refills coolant on respawn. Before this, the
|
||||
// sweep hit the empty `Subsystem::DeathReset` base and you respawned with whatever
|
||||
// coolant your previous life had left (user-reported: "respawned with drained
|
||||
// coolant").
|
||||
//
|
||||
void
|
||||
HeatSink::DeathReset(int /*reset_command*/)
|
||||
{
|
||||
ResetToInitialState(True); // @004ad760
|
||||
// Gitea #55 regression guard: the respawn refill must actually land. Verified
|
||||
// live 2026-07-25 -- coolant == capacity on every heat sink across 4 respawn
|
||||
// cycles, and the temperature restored to startingTemperature (77) instead of
|
||||
// the 300 the old HeatableSubsystem chain clobbered it with. Silent unless it
|
||||
// regresses.
|
||||
if (coolantLevel < thermalCapacity)
|
||||
DEBUG_STREAM << "[heat] BUG (Gitea #55): respawn left coolant "
|
||||
<< coolantLevel << " / " << thermalCapacity << " -- not refilled\n"
|
||||
<< std::flush;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user