gauge-complete P4d: Condenser::MoveValve reconstructed from the REAL @0x4ae464 (was a mislabel)

The reconstructed Condenser::MoveValve carried FUN_004afbe0's body -- a CONTROLS-mapper
display-mode cycler (cycles field@0x190 through 0/1/2 + repoints owner HUD gauges), NOT
the condenser valve. The real valve handler @0x4ae464 was not captured by the
assert-anchored exporter; disassembled it (tools/disas2.py) and reconstructed faithfully:
cycle valveState (@0x1D0) 1 -> 5 -> 50 -> 0 -> 1 (byte-verified @4ae480-4ae4bf), then
RecomputeCondenserValves(owner) to redistribute flow so every ValveSetting gauge updates.

DORMANT by design: the binary guards on owner->messageManager(+0x190)+0x274 (FUN_004ac9c8,
the 0xBD3 SubsystemMessageManager) and the message that invokes MoveValve arrives through
that same manager -- both DEFERRED to WAVE 8. Reproducing the raw owner+0x190 deref would
be a databinding trap in our layout, so the handler is faithful-but-uninvoked; the valve
gauge shows the authentic static 1/N (RecomputeCondenserValves at ctor) until 0xBD3 lands.
No V-key bring-up stand-in added.

Build green; combat TARGET DESTROYED, un-regressed (MoveValve is not yet routed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-07 20:29:25 -05:00
co-authored by Claude Opus 4.8
parent b91057aadc
commit aae3ce29d8
2 changed files with 30 additions and 26 deletions
+1 -1
View File
@@ -554,7 +554,7 @@ inline int
void
RefrigerationSimulation(Scalar time_slice); // @4ae4d8 (vtable slot 9)
void
MoveValve(int message); // @4afbe0 (best-effort)
MoveValve(int message); // @4ae464 (valve cycle 1->5->50->0; dormant until 0xBD3)
virtual void
SetValveSetting(int setting) { valveState = setting; }
+29 -25
View File
@@ -47,6 +47,9 @@
//
static const Scalar CoolantCapacityScale = 1.738f; // _DAT_004af518 (~ master-sink capacity scale)
// FUN_0049f788 -- defined near the foot of this file; forward-declared for MoveValve.
void BTRecomputeCondenserValves(Entity *owner);
//###########################################################################
//###########################################################################
// Condenser (COMPLETION of heat.cpp best-effort)
@@ -142,36 +145,37 @@ void
}
//
// @4afbe0 -- BEST-EFFORT. The "MoveValve" handler: cycles a 3-position valve
// (0->1->2->0), tells the model (virtual slot 18, vtable+0x48) the new
// setting, and re-points two HUD graphic gauges on the owner. Mapped to the
// Condenser by its "MoveValve"/"ValveSetting" strings and the valveState word.
// @4ae464 -- the REAL "MoveValve" message handler (disassembled; the assert-anchored
// exporter did not capture it). Cycles the coolant valve through its FOUR settings
// 1 -> 5 -> 50 -> 0 -> 1 (NOT the 0/1/2 the earlier draft guessed -- that draft had
// FUN_004afbe0's body, which is a CONTROLS-mapper display-mode cycler @0x190, a
// mislabel). After changing valveState it redistributes coolant flow across all
// condensers (RecomputeCondenserValves) so every ValveSetting gauge updates.
//
// ROUTE/GUARD are DEFERRED to WAVE 8: the binary guards on
// owner->messageManager(+0x190)->field_0x274 == 0 (FUN_004ac9c8, the 0xBD3
// SubsystemMessageManager, unreconstructed) and the message that CALLS this arrives
// through that same manager. Reproducing the raw owner+0x190 deref would be a
// databinding trap in our layout, and there is no route to invoke this yet, so the
// handler is faithful-but-DORMANT: the valve gauge shows the authentic static 1/N
// (RecomputeCondenserValves at ctor) until the message manager is reconstructed.
//
void
Condenser::MoveValve(int message) // FUN_004afbe0
Condenser::MoveValve(int message) // FUN_004ae464
{
if (message /*param_2->count*/ > 0)
{
valveState = (valveState + 1) % 3; // this[100] cycle 0..2
SetValveSetting(valveState); // (**(*this+0x48))(this, valveState)
// binary: if (FUN_004ac9c8(this)) return; -- messmgr guard, deferred (see note)
if (message /* *(int*)(param_2+0xc) = msg->count */ <= 0)
return;
// HUD gauge re-point: owner->field_438 / owner->field_5b4 are the two
// cockpit graphic gauges this valve drives. Resolving them is a Mech
// game-layer concern (CROSS-FAMILY); the branch bodies below are stubs.
(void)this->owner; // this[0x34]
void *gaugeA = 0;
void *gaugeB = 0;
(void)gaugeA; (void)gaugeB;
if (valveState == 0)
{
// gaugeA: reset + select primary read-out, signal gaugeB.
// (offsets +0x1f0/+0x274/+0x220/+0x224/+0x228/+0x22c/+0x2a0)
}
else /* valveState == 1 || 2 */
{
// gaugeA: select alternate read-out (+0x230/+0x234).
}
switch (valveState) // this[0x74] @0x1D0
{
case 0: valveState = 1; break; // @4ae499
case 1: valveState = 5; break; // @4ae4a5
case 5: valveState = 50; break; // @4ae4b1 (0x32)
case 50: valveState = 0; break; // @4ae4bd
default: return; // @4ae497 unknown -> no change / no recompute
}
BTRecomputeCondenserValves((Entity *)owner); // FUN_0049f788(this[0xd0]=owner Mech)
}
//