gauge-complete P4c: Condenser dedup + RecomputeCondenserValves -> ValveSetting gauge reads authentic 1/N
Two coupled fixes so the condenser valve gauge (ValveSetting -> coolantFlowScale @0x15C) reads its authentic value instead of garbage/zero. STEP 7 (gating dedup): heat.cpp carried STUB Condenser ctor/dtor/TestClass/ TestInstance/CreateStreamedSubsystem that ODR-duplicated the REAL bodies in heatfamily_reslice.cpp and WON under /FORCE (heat.obj links first) -> the real ctor (which sets valveState=1) was shadowed, leaving valveState=0xCDCDCDCD. #if 0'd the heat.cpp stubs so the reslice ctor (@4ae568, byte-verified: valveState=1, coolantFlowScale=0, massScale=refrigerationFactor, condenserNumber from name) is the sole definition. DefaultData/GetClassDerivations/ResetToInitialState (not duplicated) stay in heat.cpp. STEP 9 (the real writer): FinishConstruction() at the Mech ctor tail was a no-op template stub in place of FUN_0049f788 = RecomputeCondenserValves -> coolantFlowScale was never written (stayed 0). Reconstructed it (byte-verified vs part_012.c:9264): distribute coolant flow across the mech's condensers, coolantFlowScale_i = valveState_i / sum(valveState), with the condenserAlarm@0x1DC change pulse (2-if-flow<=old-else-1, then 0). Walks the populated subsystem roster filtering Condensers via IsDerivedFrom (behaviorally identical to the binary's @mech+0x7cc condenser chain, GUID 0x50e4fc). Wired as BTRecomputeCondenserValves(this) at the Mech ctor post-init pass (binary @9457). _DAT_0049f850 fallback confirmed 0.0f (PE read). Verified: [valve] the Blackhawk's 6 condensers each read flow=0.166667 (=1/6, total=6) for both player + spawned enemy, no every-mech crash; combat TARGET DESTROYED, un-regressed. Diagnostic BT_VALVE_LOG added. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
16f5f545ce
commit
b91057aadc
@@ -242,6 +242,13 @@ Derivation*
|
|||||||
return &classDerivations;
|
return &classDerivations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ~~~ Condenser ctor/dtor: the REAL bodies live in heatfamily_reslice.cpp
|
||||||
|
// (@4ae568, which sets valveState=1 / refrigerationFactor / condenserNumber).
|
||||||
|
// These STUBs were ODR-duplicates that WON under /FORCE (heat.obj links before
|
||||||
|
// heatfamily_reslice.obj) -> valveState was left 0xCDCDCDCD (garbage valve gauge).
|
||||||
|
// #if 0'd so the real ctor is the sole definition. DefaultData / GetClassDerivations
|
||||||
|
// / ResetToInitialState below are NOT duplicated in the reslice TU -- kept here.
|
||||||
|
#if 0
|
||||||
Condenser::Condenser(
|
Condenser::Condenser(
|
||||||
Mech *owner,
|
Mech *owner,
|
||||||
int subsystem_ID,
|
int subsystem_ID,
|
||||||
@@ -260,6 +267,7 @@ Condenser::~Condenser()
|
|||||||
Check(this);
|
Check(this);
|
||||||
Check_Fpu();
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
// ResetToInitialState -- Condenser (HEAT.TCP)
|
// ResetToInitialState -- Condenser (HEAT.TCP)
|
||||||
@@ -271,8 +279,13 @@ void
|
|||||||
}
|
}
|
||||||
|
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
// TestClass -- Condenser (HEAT.TCP)
|
// TestClass / TestInstance / CreateStreamedSubsystem -- Condenser
|
||||||
//
|
//
|
||||||
|
// The REAL bodies live in heatfamily_reslice.cpp (@4ae63c / @4ae658, the latter
|
||||||
|
// parsing "RefrigerationFactor"). These STUBs were ODR-duplicates -- #if 0'd so
|
||||||
|
// the reslice definitions are the sole ones (see the ctor note above).
|
||||||
|
//
|
||||||
|
#if 0
|
||||||
Logical
|
Logical
|
||||||
Condenser::TestClass(Mech &)
|
Condenser::TestClass(Mech &)
|
||||||
{
|
{
|
||||||
@@ -310,6 +323,7 @@ int
|
|||||||
Check_Fpu();
|
Check_Fpu();
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
|
|||||||
@@ -811,3 +811,53 @@ Subsystem *CreateHeatSinkBankSubsystem(Mech *owner, int id, void *seg)
|
|||||||
AggregateHeatSink(owner, id,
|
AggregateHeatSink(owner, id,
|
||||||
(AggregateHeatSink::SubsystemResource *)seg, AggregateHeatSink::DefaultData);
|
(AggregateHeatSink::SubsystemResource *)seg, AggregateHeatSink::DefaultData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===========================================================================//
|
||||||
|
// @0049f788 -- RecomputeCondenserValves. Distribute coolant flow across the
|
||||||
|
// mech's condensers so each one's ValveSetting gauge (coolantFlowScale@0x15C)
|
||||||
|
// reads its share of the total valve opening: flow_i = valveState_i / sum(valveState).
|
||||||
|
//
|
||||||
|
// The binary iterates the condenser chain @mech+0x7cc (GUID 0x50e4fc == Condenser);
|
||||||
|
// we walk the populated subsystem roster and filter for Condensers via IsDerivedFrom
|
||||||
|
// -- behaviorally identical (the chain holds exactly the condensers) and independent
|
||||||
|
// of whether the @0x7cc chain is wired. Called at the end of the Mech ctor (binary
|
||||||
|
// @9457, the post-init pass) so the valve gauge shows the authentic 1/N per condenser
|
||||||
|
// instead of 0 (the ctor leaves coolantFlowScale=0 by design; this is its first writer).
|
||||||
|
//
|
||||||
|
// The per-condenser condenserAlarm@0x1DC toggle (2 if flow<=old else 1, then 0) is the
|
||||||
|
// binary's change-notification pulse; reproduced exactly (FUN_0041bbd8 == SetLevel).
|
||||||
|
//===========================================================================//
|
||||||
|
void BTRecomputeCondenserValves(Entity *owner)
|
||||||
|
{
|
||||||
|
Check(owner);
|
||||||
|
Derivation &condClass = *Condenser::GetClassDerivations();
|
||||||
|
int count = owner->GetSubsystemCount();
|
||||||
|
|
||||||
|
// pass 1: total valve opening across all condensers
|
||||||
|
int total = 0;
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
Subsystem *s = owner->GetSubsystem(i);
|
||||||
|
if (s != 0 && s->IsDerivedFrom(condClass))
|
||||||
|
total += ((Condenser *)s)->valveState; // iVar3 += *(int*)(iVar1+0x1d0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass 2: each condenser's flow = its share of the total
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
Subsystem *s = owner->GetSubsystem(i);
|
||||||
|
if (s == 0 || !s->IsDerivedFrom(condClass))
|
||||||
|
continue;
|
||||||
|
Condenser *c = (Condenser *)s;
|
||||||
|
Scalar flow = 0.0f; // _DAT_0049f850 (no-condenser fallback)
|
||||||
|
if (total > 0)
|
||||||
|
flow = (Scalar)c->valveState / (Scalar)total;
|
||||||
|
c->condenserAlarm.SetLevel(flow <= c->coolantFlowScale ? 2 : 1); // @0x1dc change pulse
|
||||||
|
c->coolantFlowScale = flow; // *(float*)(iVar1+0x15c) = local_8
|
||||||
|
c->condenserAlarm.SetLevel(0);
|
||||||
|
if (getenv("BT_VALVE_LOG"))
|
||||||
|
DEBUG_STREAM << "[valve] condenser#" << c->condenserNumber
|
||||||
|
<< " valveState=" << c->valveState << " flow=" << c->coolantFlowScale
|
||||||
|
<< " (total=" << total << ")\n" << std::flush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ struct MechControlsMapper : ReconSubsystemStub { template<class...A> MechContro
|
|||||||
extern Subsystem *CreateCondenserSubsystem(Mech *, int, void *); // 0xBBD
|
extern Subsystem *CreateCondenserSubsystem(Mech *, int, void *); // 0xBBD
|
||||||
extern Subsystem *CreateHeatSinkBankSubsystem(Mech *, int, void *); // 0xBBE
|
extern Subsystem *CreateHeatSinkBankSubsystem(Mech *, int, void *); // 0xBBE
|
||||||
extern Subsystem *CreateReservoirSubsystem(Mech *, int, void *); // 0xBC0
|
extern Subsystem *CreateReservoirSubsystem(Mech *, int, void *); // 0xBC0
|
||||||
|
extern void BTRecomputeCondenserValves(Entity *); // @0049f788 (post-init valve distribution)
|
||||||
extern Subsystem *CreateHUDSubsystem(Mech *, int, void *); // 0xBD6
|
extern Subsystem *CreateHUDSubsystem(Mech *, int, void *); // 0xBD6
|
||||||
extern Subsystem *CreateMechTechSubsystem(Mech *, int, void *); // 0xBDC
|
extern Subsystem *CreateMechTechSubsystem(Mech *, int, void *); // 0xBDC
|
||||||
extern Subsystem *CreateGeneratorSubsystem(Mech *, int, void *); // 0xBC1 (WAVE 3a)
|
extern Subsystem *CreateGeneratorSubsystem(Mech *, int, void *); // 0xBC1 (WAVE 3a)
|
||||||
@@ -1237,7 +1238,11 @@ Mech::Mech(
|
|||||||
}
|
}
|
||||||
dir->Add(this); // (**(dir[4]+4))(dir+4,this)
|
dir->Add(this); // (**(dir[4]+4))(dir+4,this)
|
||||||
|
|
||||||
FinishConstruction(); // FUN_0049f788 (post-init pass)
|
// @0049f788 -- distribute coolant flow across the condensers (post-init pass).
|
||||||
|
// The real RecomputeCondenserValves; sets each condenser's coolantFlowScale to
|
||||||
|
// valveState/sum(valveState) so the ValveSetting gauge reads the authentic 1/N
|
||||||
|
// (was a no-op stub -> the valve gauge showed 0).
|
||||||
|
BTRecomputeCondenserValves(this);
|
||||||
Check_Fpu();
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import struct,sys
|
||||||
|
va=int(sys.argv[1],16)
|
||||||
|
f=open('content/BTL4OPT.EXE','rb').read()
|
||||||
|
# PE header
|
||||||
|
pe=struct.unpack_from('<I',f,0x3c)[0]
|
||||||
|
assert f[pe:pe+4]==b'PE\0\0'
|
||||||
|
numsec=struct.unpack_from('<H',f,pe+6)[0]
|
||||||
|
optsz=struct.unpack_from('<H',f,pe+20)[0]
|
||||||
|
imgbase=struct.unpack_from('<I',f,pe+0x34)[0]
|
||||||
|
sectab=pe+24+optsz
|
||||||
|
for i in range(numsec):
|
||||||
|
off=sectab+i*40
|
||||||
|
name=f[off:off+8].rstrip(b'\0').decode(errors='replace')
|
||||||
|
vsz,vaddr,rsz,raddr=struct.unpack_from('<IIII',f,off+8)
|
||||||
|
if imgbase+vaddr <= va < imgbase+vaddr+max(vsz,rsz):
|
||||||
|
fo=raddr+(va-imgbase-vaddr)
|
||||||
|
raw=f[fo:fo+4]
|
||||||
|
print(f"VA {va:#x} sec={name} bytes={raw.hex()} float={struct.unpack('<f',raw)[0]} int={struct.unpack('<i',raw)[0]}")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print("not found; imgbase",hex(imgbase))
|
||||||
Reference in New Issue
Block a user