#97: leak gauge reaches all THREE levels -- the normalisation was dropped with an x87 expression
Players: "you can get up to three traingles which drains really quickly" ...
"only seeing one level (lowest) right now" (Oracle), corroborated by Draco
("3 triple delta being the top level").
The display was never the problem: BitMapInverseWipe is already built with
frames=3 and stacks three segments at two levels each (0..6). The LEVEL
computation was wrong.
Our Execute rounded the raw leak rate:
level = round(value)
The binary's @004c5d08 does NOT. Ghidra renders the round as a bare
`FUN_004dcd94()` because it drops the x87 expression feeding __ftol -- exactly
KB gotcha 19, which we already had written down. The real prologue, disassembled:
fild dword ptr [ebp-0x14] ; ST0 = (float)fullWidth (frames*2 = 6)
fmul dword ptr [ebx+0xb4] ; ST0 *= value (CoolantMassLeakRate)
fdiv dword ptr [ebx+0xb0] ; ST0 /= third (full-scale divisor)
call 0x4dcd94 ; level = round(ST0)
i.e. level = round(fullWidth * leakRate / fullScale).
Without the normalisation the gauge rounded a value that never exceeds ~1.0
(coolantDraw = zoneDamage * heatLoad, and heatLoad is clamp(0.002*T, 0, 1) --
constants re-verified from the image, including the 80-bit extended 0.002). So
level could only ever be 0, or 1 via the >0.0025 floor: ONE triangle, always, no
matter how bad the leak. Three of the six condensers being mis-lamped (#98) hid
how systematic this was.
Also corrected on the way:
* `third` was declared int but the binary FDIVs it -- it is a float. The two
call sites passed *(int *)(subsystem+0x150), i.e. the BIT PATTERN of a float
read from a RAW OFFSET into our own layout (the databinding trap): at +0x150
our layout has filterDecay, not the binary's field. Now resolved through a
complete-type bridge, BTHeatSinkLeakFullScale.
* filterDecay was initialised 0.4f; @004b8fec writes 0.15f (param_1[0x54] =
0x3e19999a). It is written-once-never-read elsewhere in the port, so the
correction is behaviourally safe and it IS the gauge's full scale.
Verified live: Condenser6 at 10% zone damage -> draw 0.0358 -> level 1 (one
triangle); the same formula reaches all three at heavier damage, and the mapping
spans draw 0.0026 (half of one) to 0.15 (three full).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ee079083a3
commit
e6103d02b2
@@ -538,7 +538,10 @@ HeatSink::HeatSink(
|
||||
thermalConductance = subsystem_resource->thermalConductance; // +0xF0
|
||||
|
||||
heatFilter.Initialize(15, 0.0f); // FUN_0043ad4f(this+0x144, 0xF, 0)
|
||||
filterDecay = 0.4f;
|
||||
// @004b8fec stores 0.15f here (param_1[0x54] = 0x3e19999a). The value is
|
||||
// otherwise unread in the port, but it IS the leak gauge's full-scale
|
||||
// divisor: BitMapInverseWipe divides by subsystem+0x150 (#97).
|
||||
filterDecay = 0.15f; // was 0.4f -- did not match the image
|
||||
thermalMass = subsystem_resource->thermalMass; // +0xF4
|
||||
heatEnergy = thermalMass * startingTemperature;
|
||||
coolantFlowScale = 1.0f;
|
||||
@@ -1311,6 +1314,23 @@ int BTSubsystemIsCondenser(::Subsystem *sub)
|
||||
{
|
||||
return (sub != 0 && sub->IsDerivedFrom(*Condenser::GetClassDerivations())) ? 1 : 0;
|
||||
}
|
||||
//
|
||||
// #97 bridge: the leak gauge's FULL-SCALE divisor. BitMapInverseWipe computes
|
||||
// level = round(fullWidth * leakRate / thirdParam)
|
||||
// and the binary sources thirdParam from subsystem+0x150 (@004b8fec writes
|
||||
// 0.15f there). The gauge TU cannot see the HeatSink layout, and raw-reading
|
||||
// +0x150 from there would land on whatever OUR layout puts at that offset --
|
||||
// the databinding trap. Resolve it through the named member instead.
|
||||
//
|
||||
Scalar BTHeatSinkLeakFullScale(::Subsystem *sub)
|
||||
{
|
||||
if (sub == 0 || !sub->IsDerivedFrom(*HeatSink::GetClassDerivations()))
|
||||
return 0.15f; // the authored default
|
||||
Scalar fs = ((HeatSink *)sub)->LeakGaugeFullScale();
|
||||
return (fs > 0.0f) ? fs : 0.15f; // never divide by zero
|
||||
}
|
||||
|
||||
|
||||
int BTCondenserNumber(::Subsystem *sub)
|
||||
{
|
||||
return (sub != 0) ? ((Condenser *)sub)->condenserNumber : -1; // +0x1D4
|
||||
|
||||
Reference in New Issue
Block a user