diff --git a/context/gauges-hud.md b/context/gauges-hud.md index ba58ab0..f20683d 100644 --- a/context/gauges-hud.md +++ b/context/gauges-hud.md @@ -162,10 +162,15 @@ independent bugs stacked (all fixed, commit e634709): now passes `subsystem_in` (not the `InputVoltage` attribute slot). **Lesson: gauge value feeds must read named members through a complete-type bridge, NOT `AttributePointerOf`+`ResolveLink` — the attribute table's chained ids are unstable under the audio rows.** The eng-page - `GeneratorVoltageConnection` (evolt voltage bar, btl4gau2.cpp:1737) still uses the same - `AttributePointerOf("InputVoltage")` path and is the remaining sibling to convert. + generator-voltage bar + MyomerCluster seek-voltage graph (`GeneratorVoltageConnection`, + evolt.pcc, btl4gau2.cpp:806/1750) were on the same dead path and are now converted the same + way -> `BTGeneratorVoltage(subsystem)` reads `Generator::MeasuredVoltage()` + (outputVoltage@0x1DC); `[voltfeed]`-verified (Myomers -> 10000V from GeneratorD). Still on the + OLD path but NOT yet converted (different attribute, direct read, not confirmed broken): the + GeneratorCluster + EnergyWeaponCluster bars that read `AttributePointerOf(subsystem, + "OutputVoltage")` directly (btl4gau2.cpp:968, ~1920). Diagnostics retained (env `BT_LOOP_LOG`): `[loopfeed]` (BTCoolingLoopFrame) + `[busfeed]` -(BTPowerSourceFrame) print each lamp's resolved frame + source. +(BTPowerSourceFrame) + `[voltfeed]` (BTGeneratorVoltage) print each widget's resolved value + source. **HUMAN-VERIFIED live 2026-07-21** (pod build, solo ARENA1 cockpit): the loop number + generator letter boxes render correctly on the weapon panels. diff --git a/game/reconstructed/btl4gau2.cpp b/game/reconstructed/btl4gau2.cpp index 5531e85..cc9bddb 100644 --- a/game/reconstructed/btl4gau2.cpp +++ b/game/reconstructed/btl4gau2.cpp @@ -233,9 +233,18 @@ protected: // // GeneratorVoltageConnection -- @004c3288 ctor / @004c32f4 sampler (vt 0x518e90). -// Resolves the voltage source and copies its output voltage (+0x1dc); 0 if -// unresolved. Drives ScalarBarGauge / SeekVoltageGraph value slots. +// The binary resolves the subsystem's InputVoltage link and copies its output +// voltage (resolved+0x1DC == Generator::outputVoltage); 0 if unresolved. Drives +// the eng-page generator-voltage bar (ScalarBarGauge) + the MyomerCluster +// seek-voltage graph. +// DATABINDING FIX 2026-07-21 (gotcha 8, sibling of the PowerSource/CoolingLoop +// fixes): the ResolveLink(AttributePointerOf(subsystem,"InputVoltage")) path is +// dead after the BT_DEV_GAUGES audio attribute rows shifted the chained ids, so +// the bar always read 0. 'source' now carries the SUBSYSTEM and +// BTGeneratorVoltage (powersub.cpp) reads the NAMED member via +// ResolveVoltageSource()->MeasuredVoltage(), bypassing the attribute table. // +extern Scalar BTGeneratorVoltage(void *subsystem); class GeneratorVoltageConnection : public GaugeConnection { public: @@ -243,9 +252,7 @@ public: : GaugeConnection(0), source(source), destination(destination) {} void Update() // @004c32f4 { - void *resolved = ResolveLink(source); - *destination = (resolved == 0) ? 0.0f - : *(Scalar *)((char *)resolved + 0x1dc); + *destination = BTGeneratorVoltage(source); } protected: void *source; // @0x10 @@ -1466,8 +1473,8 @@ SubsystemCluster::SubsystemCluster( generatorVoltageBar = new ScalarBarGauge(ChildRate(), eng_mode, renderer_in, // @004c721c engPort, 0x89, 0x82, 0x93, 0x13b, "evolt.pcc", 0xff, 0, - WipeGaugeScalar::wipeUp, 0.0f, GeneratorMaxVoltage, inputVoltage, - "GeneratorVoltage(slotOf)"); + WipeGaugeScalar::wipeUp, 0.0f, GeneratorMaxVoltage, subsystem_in, // pass the SUBSYSTEM + "GeneratorVoltage(slotOf)"); // -> BTGeneratorVoltage reads voltage by name (attr-shift fix) // stateLamp only when the subsystem is a Generator (IsDerivedFrom 0x50f4bc). if (IsGeneratorDerived(subsystem_in)) @@ -1738,8 +1745,9 @@ MyomerCluster::MyomerCluster( // INFERRED (the MyomerCluster ctor @004c8df4 isn't in the assert-anchored export). (int *)((char *)subsystem_in + 0x320), "OneOfSeveralInt"); - void *inputVoltage = AttributePointerOf(subsystem_in, "InputVoltage"); - AddConnection(new GeneratorVoltageConnection(&seekValue, inputVoltage)); // @004c3288 + // Pass the SUBSYSTEM -- BTGeneratorVoltage resolves voltageSource by name + // (the attribute-table shift makes AttributePointerOf("InputVoltage") dead). + AddConnection(new GeneratorVoltageConnection(&seekValue, subsystem_in)); // @004c3288 } MyomerCluster::~MyomerCluster() diff --git a/game/reconstructed/powersub.cpp b/game/reconstructed/powersub.cpp index 5ebd75e..3238254 100644 --- a/game/reconstructed/powersub.cpp +++ b/game/reconstructed/powersub.cpp @@ -716,6 +716,44 @@ int BTPowerSourceFrame(void *subsystem_v) return frame; } +// +// BTGeneratorVoltage -- complete-type bridge for the GeneratorVoltageConnection +// (@004c3288), which drives the eng-page generator-voltage bar (evolt.pcc, +// ScalarBarGauge @004c721c) and the MyomerCluster seek-voltage graph. The +// binary resolved the subsystem's InputVoltage link and read resolved+0x1DC == +// Generator::outputVoltage (0..12000, == MeasuredVoltage()). Same databinding +// trap as BTPowerSourceFrame: the port's ResolveLink(AttributePointerOf(..., +// "InputVoltage")) path is dead after the BT_DEV_GAUGES audio attribute rows +// shifted the chained ids, so the bar read 0. This reads the NAMED member via +// PoweredSubsystem::ResolveVoltageSource(), bypassing the attribute table. +// +Scalar BTGeneratorVoltage(void *subsystem_v) +{ + static int s_log = -1; + if (s_log < 0) s_log = getenv("BT_LOOP_LOG") ? 1 : 0; + static int s_n = 0; + int trace = s_log && (s_n++ < 40); + + Subsystem *sub = (Subsystem *)subsystem_v; + if (sub == 0 || !sub->IsDerivedFrom(*PoweredSubsystem::GetClassDerivations())) + { + if (trace) DEBUG_STREAM << "[voltfeed] " << (sub ? sub->GetName() : "(null)") + << " -> 0 (not a PoweredSubsystem)\n" << std::flush; + return 0.0f; + } + Generator *gen = (Generator *)((PoweredSubsystem *)sub)->ResolveVoltageSource(); + if (gen == 0 || gen->GetClassID() != RegisteredClass::GeneratorClassID) + { + if (trace) DEBUG_STREAM << "[voltfeed] " << sub->GetName() + << " -> 0 (no Generator)\n" << std::flush; + return 0.0f; + } + Scalar volts = gen->MeasuredVoltage(); // outputVoltage @0x1DC + if (trace) DEBUG_STREAM << "[voltfeed] " << sub->GetName() << " -> " << volts + << "V (generator '" << gen->GetName() << "')\n" << std::flush; + return volts; +} + //############################################################################# // Voltage-source linkage helpers