gauge wave P1b: Searchlight/LightOn + fix coolant/refrigeration source misread
- Searchlight publishes "LightOn" -> lightState@0x1D8 (the button-5 searchlight lamp; the empty default-constructed index resolved NOTHING). The config binds the name "LightOn", so the enum id is renamed LightOnAttributeID and the table is chained to PowerWatcher's dense index. Verified: LightOn resolves OK. - FIX a genuine reconstruction bug the heatmodel decode caught (heat.cpp UpdateCoolant + heatfamily_reslice RefrigerationSimulation): both read linkedSinks.Resolve()->heatEnergy where the binary reads *(this[0x38]+0x158) = the subsystem's OWN DamageZone.damageLevel (the engine base zone @0xE0). Consequences of the misread: coolantDraw = master.heatEnergy(~1.3e7) * heatLoad would SLAM the live CoolantMass bars to empty whenever the link resolved; and RefrigerationSimulation clamped massScale permanently to 1.0 (minimum) instead of the authentic 3.0, plus null-deref'd a Condenser whose linkedSinks is skipped. Now both read this->Subsystem::damageZone->damageLevel (qualified past the MechSubsystem shadow, per mechweap.cpp:252): undamaged 0 -> coolantDraw 0 (no leak, coolant bars stay full = authentic pristine) / massScale = 3.0, rising only as the sink/condenser itself takes battle damage. After this only HeatSink/AmbientTemperature remains NULL (the #if 0'd aggregate HeatSink bank -> P3). Verified DBASE+dev gauges: no crash, combat un-regressed (TARGET DESTROYED). (Also noted: the Condenser dup-ctor links to the REAL heatfamily_reslice definition -- LNK4006 keeps the first/real one; the heat.cpp stub is ignored -- so no bug today; #if 0 cleanup deferred to the Condenser table.) 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
070af409f7
commit
64975ec22f
@@ -753,11 +753,19 @@ void
|
|||||||
void
|
void
|
||||||
HeatSink::UpdateCoolant(Scalar time_slice)
|
HeatSink::UpdateCoolant(Scalar time_slice)
|
||||||
{
|
{
|
||||||
HeatSink *master = (HeatSink *)linkedSinks.Resolve(); // this[0x38] linked master
|
// AUTHENTIC (heatmodel decode, FUN_004adbf8): the binary reads *(this[0x38]+0x158)
|
||||||
// NULL-master guard (reconstruction omission -- the sibling ConductHeat/BalanceCoolant
|
// = this subsystem's OWN DamageZone.damageLevel (the engine base zone at @0xE0,
|
||||||
// guard this same Resolve(); UpdateCoolant didn't, so a Condenser/Reservoir with no
|
// word 0x38), NOT linkedSinks->heatEnergy. The earlier reconstruction confused
|
||||||
// linked master null-deref'd here on its first tick). No master -> no coolant draw.
|
// word 0x38/@0xE0 (the DamageZone) with linkedSinks@0x164, so when the link
|
||||||
coolantDraw = master ? master->heatEnergy * heatLoad : 0.0f; // *(this[0x38]+0x158) * this[0x48]
|
// resolved (heatEnergy ~1.3e7) coolantDraw became ~2e6 and would SLAM coolantLevel
|
||||||
|
// 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.)
|
||||||
|
::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]
|
||||||
if (coolantDraw < CoolantDrawEpsilon)
|
if (coolantDraw < CoolantDrawEpsilon)
|
||||||
{
|
{
|
||||||
coolantDraw = 0.0f;
|
coolantDraw = 0.0f;
|
||||||
|
|||||||
@@ -122,9 +122,17 @@ Logical
|
|||||||
void
|
void
|
||||||
Condenser::RefrigerationSimulation(Scalar time_slice) // FUN_004ae4d8
|
Condenser::RefrigerationSimulation(Scalar time_slice) // FUN_004ae4d8
|
||||||
{
|
{
|
||||||
HeatSink *master = (HeatSink *)linkedSinks.Resolve(); // this[0x38]
|
// AUTHENTIC (heatmodel decode): massScale = clamp>=1( (1 - ownDamageZone.damageLevel)
|
||||||
|
// * refrigerationFactor ). The binary reads *(this[0x38]+0x158) = this condenser's
|
||||||
|
// OWN DamageZone.damageLevel (the engine base zone @0xE0, word 0x38): undamaged 0 ->
|
||||||
|
// massScale = refrigerationFactor (3.0), degrading toward 1.0 as the condenser is
|
||||||
|
// damaged. The earlier recon read linkedSinks->heatEnergy (~1.3e7) -> (1 - 1.3e7)
|
||||||
|
// clamps massScale permanently to 1.0 (minimum refrigeration) AND null-derefs a
|
||||||
|
// Condenser whose linkedSinks is skipped (the ctor guard).
|
||||||
|
::DamageZone *ownZone = this->Subsystem::damageZone; // @0xE0 (word 0x38)
|
||||||
|
Scalar zoneDamage = (ownZone != 0) ? ownZone->damageLevel : 0.0f; // +0x158
|
||||||
massScale = // refrigeration output reuses massScale @0x160 (word 0x58)
|
massScale = // refrigeration output reuses massScale @0x160 (word 0x58)
|
||||||
(1.0f - master->heatEnergy) // _DAT_004ae530 = 1.0f ; master +0x158
|
(1.0f - zoneDamage) // _DAT_004ae530 = 1.0f ; own DamageZone +0x158
|
||||||
* refrigerationFactor; // this[0x76]
|
* refrigerationFactor; // this[0x76]
|
||||||
if (massScale < 1.0f) // _DAT_004ae530
|
if (massScale < 1.0f) // _DAT_004ae530
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,8 +50,21 @@ Derivation
|
|||||||
Receiver::MessageHandlerSet
|
Receiver::MessageHandlerSet
|
||||||
Searchlight::MessageHandlers; // includes { "ToggleLamp", &Searchlight::ToggleLamp } @00511210
|
Searchlight::MessageHandlers; // includes { "ToggleLamp", &Searchlight::ToggleLamp } @00511210
|
||||||
|
|
||||||
|
// gauge data-binding wave: publish "LightOn" -> lightState@0x1D8 (the button-5
|
||||||
|
// searchlight lamp; the empty default-constructed index resolved NOTHING).
|
||||||
|
// Chained to PowerWatcher's dense index.
|
||||||
|
const Searchlight::IndexEntry
|
||||||
|
Searchlight::AttributePointers[]= // @005111e0
|
||||||
|
{
|
||||||
|
ATTRIBUTE_ENTRY(Searchlight, LightOn, lightState) // @0x1D8 (reported on/off)
|
||||||
|
};
|
||||||
|
|
||||||
Searchlight::AttributeIndexSet
|
Searchlight::AttributeIndexSet
|
||||||
Searchlight::AttributeIndex;
|
Searchlight::AttributeIndex(
|
||||||
|
ELEMENTS(Searchlight::AttributePointers),
|
||||||
|
Searchlight::AttributePointers,
|
||||||
|
PowerWatcher::GetAttributeIndex()
|
||||||
|
);
|
||||||
|
|
||||||
Searchlight::SharedData
|
Searchlight::SharedData
|
||||||
Searchlight::DefaultData( // @00511198
|
Searchlight::DefaultData( // @00511198
|
||||||
|
|||||||
@@ -104,7 +104,10 @@
|
|||||||
//
|
//
|
||||||
public:
|
public:
|
||||||
enum {
|
enum {
|
||||||
LightStateAttributeID = PowerWatcher::NextAttributeID, // 5
|
// The cockpit button-5 lamp binds "Searchlight/LightOn" (L4GAUGE.CFG:5017);
|
||||||
|
// the published NAME must be "LightOn" for Find() to hit, bound to the
|
||||||
|
// reported on/off member lightState@0x1D8.
|
||||||
|
LightOnAttributeID = PowerWatcher::NextAttributeID, // 5
|
||||||
NextAttributeID
|
NextAttributeID
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user