in the leak chain HeatSink::UpdateCoolant prices the damage-driven leak from the QUALIFIED engine member (Subsystem::damageZone @0xE0): coolantDraw = zoneDamage * heatLoad, with the coolantActive hysteresis (= the ReportLeak attribute the 19 authored leak watchers ride). But the MechSubsystem ctor only ever filled its re-declared SHADOW member (gitea #64, gotcha #1), so the engine member stayed NULL forever, zoneDamage pinned 0, and a coolant leak was STRUCTURALLY IMPOSSIBLE no matter how much damage landed -- Oracle's night-7 report exactly. The #80 crit fix and the #83 collision rattle write real subsystem-zone damage, but into an object the leak reader could never see. Fix: alias the ENGINE base member to the same zone in both MechSubsystem ctors. Every shadow reader keeps working (same object); the engine base Subsystem::TakeDamage latent null-AV (#64 consequence 2) is disarmed; the full #64 de-shadow sweep remains the long-term cleanup. Verified live (solo, BT_COOL_LOG/BT_CRIT_LOG): collision rattle drove a damaged Myomers to [cool] draw = dmg*heatLoad with the level draining; sustained weapon fire rolled real crits ([critroll]) and produced 588 leak-pricing lines. Authored routing extracted from BTL4.RES: collision rattle targets HeatSinkBank 0.3 / Gyro 0.35 / Torso 0.25 / Myomers 0.35 (Condenser+Reservoir authored 0 for collisions); weapon crits select via the zone's crit-entry list. Known-remaining fidelity item (documented): HeatSink::DrawCoolant (slot 14, central top-up) is still a return-0 TODO. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Zh7PTkFy4KwTzVighLR9J
This commit is contained in:
co-authored by
Claude Fable 5
parent
1570983fde
commit
a8a0042f28
@@ -167,6 +167,24 @@ its electrical state. The full chain, byte-verified [T1]:
|
||||
- **STILL DEAD:** factory loops 2-4 (heatable/weapon/damageable capability rosters) go through the
|
||||
`SubProxy` stub whose `IsDerivedFrom` returns 0 — they add NOTHING. See [[open-questions]].
|
||||
|
||||
## Coolant LEAKS (Gitea #88 fix, 2026-07-31) — the #64 shadow was the single break [T2]
|
||||
`HeatSink::UpdateCoolant` (@004adbf8) prices the damage-driven leak: `coolantDraw =
|
||||
ownZone->damageLevel × heatLoad`, floor 0.0025, and the `coolantActive@0x138` hysteresis
|
||||
(ON >0.003) IS the `ReportLeak` attribute the 19 authored leak watchers (3-note warning) ride.
|
||||
The read is the **qualified ENGINE member** `Subsystem::damageZone@0xE0` — which the port's
|
||||
MechSubsystem ctor never assigned (it filled only its re-declared shadow, gitea #64 / gotcha #1),
|
||||
so `zoneDamage` pinned 0 and a leak was **structurally impossible** regardless of damage. Fixed by
|
||||
ALIASING the engine base member to the same zone in both MechSubsystem ctors (mechsub.cpp) — every
|
||||
shadow reader is untouched (same object), the engine base `Subsystem::TakeDamage` null-AV is
|
||||
disarmed, and the #64 full de-shadow sweep remains the long-term cleanup. Verified live: collision
|
||||
rattle and weapon crits (`[critroll]`) both drive `[cool]` leak lines (draw ≈ dmg×heatLoad, level
|
||||
draining, hysteresis arming). **Authored damage routing** (BTL4.RES): collision rattle targets
|
||||
HeatSinkBank 0.3 / Gyro 0.35 / Torso 0.25 / Myomers 0.35 — Condenser + Reservoir are authored 0
|
||||
for collisions; weapon crits select via the ZONE's crit-entry list. Remaining fidelity item:
|
||||
`HeatSink::DrawCoolant` (vtable slot 14, the central-system top-up) is still a `return 0` TODO, so
|
||||
a leaking subsystem drains its own loop; the pull-through to the central Reservoir gauge rides the
|
||||
loop-flow machinery.
|
||||
|
||||
## The coolant FLUSH (Gitea #7, 2026-07-19) — Reservoir InjectCoolant end-to-end [T2 live-verified]
|
||||
The manual-p24 coolant button (coolant MFD top-right; punch or HOLD): message id **4 "InjectCoolant"**
|
||||
on the Reservoir (handler table @**0x50e680**, one entry → @4aee70 — same per-receiver id space as the
|
||||
|
||||
@@ -952,8 +952,12 @@ void
|
||||
// to empty every frame -- the opposite of the authentic near-static behavior.
|
||||
// An undamaged subsystem has damageLevel 0 -> coolantDraw 0 -> NO leak (the coolant
|
||||
// bars stay full on a pristine mech); the draw rises only as the heat sink /
|
||||
// condenser itself takes battle damage. (Read the qualified engine zone -- the
|
||||
// nearer MechSubsystem::damageZone is a shim SHADOW; see mechweap.cpp:252.)
|
||||
// condenser itself takes battle damage. (#88 fix 2026-07-31: this qualified
|
||||
// engine-member read was NULL forever -- the MechSubsystem ctor only filled its
|
||||
// re-declared SHADOW, so `zoneDamage` pinned 0 and a leak was structurally
|
||||
// impossible no matter how much damage landed. The ctor now ALIASES the engine
|
||||
// base member to the same zone (mechsub.cpp, the #64 half-fix), so this read
|
||||
// sees the real crit/rattle damage.)
|
||||
::DamageZone *ownZone = this->Subsystem::damageZone; // @0xE0 (word 0x38)
|
||||
Scalar zoneDamage = (ownZone != 0) ? ownZone->damageLevel : 0.0f; // +0x158
|
||||
coolantDraw = zoneDamage * heatLoad; // *(this[0x38]+0x158) * this[0x48]
|
||||
|
||||
@@ -152,6 +152,16 @@ MechSubsystem::MechSubsystem(
|
||||
printSimulationState = 0; // this[0x41]
|
||||
configureActivePress = -1; // this[0x44] configure-session idle
|
||||
damageZone = (ReconDamageZone *)new DamageZone(this, 0); // this[0x38] = FUN_0041de1c(new 0x160, this, 0)
|
||||
// #64/#88: ALIAS the ENGINE base member to the same zone. The binary has
|
||||
// ONE cell (subsystem word 0x38); the port's re-declared shadow left
|
||||
// Subsystem::damageZone null forever, so every engine-member reader --
|
||||
// HeatSink::UpdateCoolant's leak feed (`coolantDraw = zoneDamage *
|
||||
// heatLoad`), the engine base TakeDamage -- saw a pristine/null zone.
|
||||
// With the crit sink + collision rattle now writing real damage into the
|
||||
// (same) zone, the null base was the single break in the coolant-leak
|
||||
// chain. Aliasing (not moving) keeps every shadow reader intact; the
|
||||
// full #64 de-shadow sweep remains the long-term cleanup.
|
||||
Subsystem::damageZone = (::DamageZone *)damageZone;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -190,6 +200,7 @@ MechSubsystem::MechSubsystem(
|
||||
collisionCriticalHitWeight = subsystem_resource->collisionCriticalHitWeight; // res+0xdc
|
||||
|
||||
damageZone = (ReconDamageZone *)new DamageZone(this, 0); // this[0x38]
|
||||
Subsystem::damageZone = (::DamageZone *)damageZone; // #64/#88 alias -- see the sibling ctor
|
||||
BindName((char *)damageZone + 0x15c, GetName()); // FUN_00402a98(., dz+0x15c, name)
|
||||
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user