BT410 Phase 5.3.27: the attribute wave -- 48/50 cockpit bindings go LIVE
The gauges now read the real simulation: heat/power/sensor/mech attribute tables published under the 1995 L4GAUGE.CFG spellings the widgets already bind by name. Temperatures, coolant mass/capacity/leak rate, condenser valve settings, generator voltages and numbers, radar percent, and the mech's radar/speed rows all resolve to live members. THE NUMBERING IS PINNED: the chain publishes 2..0x0E so PoweredSubsystem::NextAttributeID lands on the authentic 0x0F -- the surviving SENSOR.HPP numbers RadarPercent off it and MechWeapon's binary-pinned table starts at 0x12, so its pads shrank 16 -> 3 (0x0F..0x11) exactly as the 5.3.x header comment predicted. Sensor's authentic enum finally has its table defined; MechWeapon/Sensor rechain to PoweredSubsystem and Reservoir/AggregateHeatSink to HeatSink so the whole family is visible where the cfg expects it. Verified with BT_GAUGE_ATTR_LOG: 48 OK / 2 NULL (was 33/17, and 0/50 at the block's birth). The two remaining have no member to bind -- HeatSink/AmbientTemperature (a sim constant) and Searchlight/LightOn (a memberless subclass) -- and fall to the documented zero cell. Gauge fight (15/15 rounds, 127 zone hits), smoke and novice all clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -51,11 +51,34 @@ Derivation
|
||||
"HeatableSubsystem"
|
||||
);
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// Attribute tables -- the cockpit binds these by NAME (1995 L4GAUGE.CFG
|
||||
// spellings). Contiguous IDs; each set chains its parent so a weapon or
|
||||
// sensor sees the whole heat/power family.
|
||||
//#############################################################################
|
||||
//
|
||||
const HeatableSubsystem::IndexEntry
|
||||
HeatableSubsystem::AttributePointers[]=
|
||||
{
|
||||
ATTRIBUTE_ENTRY(HeatableSubsystem, CurrentTemperature, currentTemperature),
|
||||
ATTRIBUTE_ENTRY(HeatableSubsystem, HeatLoad, heatLoad),
|
||||
ATTRIBUTE_ENTRY(HeatableSubsystem, DegradationTemperature, degradationTemperature),
|
||||
ATTRIBUTE_ENTRY(HeatableSubsystem, FailureTemperature, failureTemperature)
|
||||
};
|
||||
|
||||
HeatableSubsystem::AttributeIndexSet
|
||||
HeatableSubsystem::AttributeIndex(
|
||||
ELEMENTS(HeatableSubsystem::AttributePointers),
|
||||
HeatableSubsystem::AttributePointers,
|
||||
Subsystem::AttributeIndex
|
||||
);
|
||||
|
||||
HeatableSubsystem::SharedData
|
||||
HeatableSubsystem::DefaultData(
|
||||
HeatableSubsystem::ClassDerivations,
|
||||
Subsystem::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
HeatableSubsystem::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
@@ -170,11 +193,27 @@ HeatSink::MessageHandlerSet
|
||||
Subsystem::MessageHandlers
|
||||
);
|
||||
|
||||
const HeatSink::IndexEntry
|
||||
HeatSink::AttributePointers[]=
|
||||
{
|
||||
ATTRIBUTE_ENTRY(HeatSink, CoolantMass, coolantLevel),
|
||||
ATTRIBUTE_ENTRY(HeatSink, CoolantCapacity, thermalCapacity),
|
||||
ATTRIBUTE_ENTRY(HeatSink, CoolantMassLeakRate, coolantDraw),
|
||||
ATTRIBUTE_ENTRY(HeatSink, CoolantAvailable, coolantAvailable)
|
||||
};
|
||||
|
||||
HeatSink::AttributeIndexSet
|
||||
HeatSink::AttributeIndex(
|
||||
ELEMENTS(HeatSink::AttributePointers),
|
||||
HeatSink::AttributePointers,
|
||||
HeatableSubsystem::AttributeIndex
|
||||
);
|
||||
|
||||
HeatSink::SharedData
|
||||
HeatSink::DefaultData(
|
||||
HeatSink::ClassDerivations,
|
||||
HeatSink::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
HeatSink::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
@@ -800,11 +839,24 @@ Condenser::MessageHandlerSet
|
||||
HeatSink::MessageHandlers
|
||||
);
|
||||
|
||||
const Condenser::IndexEntry
|
||||
Condenser::AttributePointers[]=
|
||||
{
|
||||
ATTRIBUTE_ENTRY(Condenser, ValveSetting, coolantFlowScale)
|
||||
};
|
||||
|
||||
Condenser::AttributeIndexSet
|
||||
Condenser::AttributeIndex(
|
||||
ELEMENTS(Condenser::AttributePointers),
|
||||
Condenser::AttributePointers,
|
||||
HeatSink::AttributeIndex
|
||||
);
|
||||
|
||||
Condenser::SharedData
|
||||
Condenser::DefaultData(
|
||||
Condenser::ClassDerivations,
|
||||
Condenser::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
Condenser::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
@@ -993,7 +1045,7 @@ AggregateHeatSink::SharedData
|
||||
AggregateHeatSink::DefaultData(
|
||||
AggregateHeatSink::ClassDerivations,
|
||||
HeatSink::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
HeatSink::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
|
||||
@@ -51,6 +51,29 @@
|
||||
// Shared Data Support
|
||||
//
|
||||
public:
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Attribute Support. The cockpit gauges bind these by NAME through the
|
||||
// interpreter (ParseAttribute -> GetAttributePointer): the 1995
|
||||
// L4GAUGE.CFG spellings are the enum names. The whole heat/power chain
|
||||
// publishes IDs 2..0x0E so PoweredSubsystem::NextAttributeID lands on the
|
||||
// authentic 0x0F -- Sensor's table (surviving SENSOR.HPP) and
|
||||
// MechWeapon's pinned 0x12 pads are both numbered off it. CONTIGUITY IS
|
||||
// LOAD-BEARING: AttributeIndexSet::Build leaves uncovered gap slots
|
||||
// uninitialized.
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
CurrentTemperatureAttributeID = Subsystem::NextAttributeID, // 2
|
||||
HeatLoadAttributeID, // 3
|
||||
DegradationTemperatureAttributeID, // 4
|
||||
FailureTemperatureAttributeID, // 5
|
||||
NextAttributeID // 6
|
||||
};
|
||||
|
||||
static const IndexEntry AttributePointers[];
|
||||
static AttributeIndexSet AttributeIndex;
|
||||
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
@@ -212,6 +235,22 @@
|
||||
// Shared Data Support
|
||||
//
|
||||
public:
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Attribute Support (the coolant family the cockpit bars read).
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
CoolantMassAttributeID = HeatableSubsystem::NextAttributeID, // 6
|
||||
CoolantCapacityAttributeID, // 7
|
||||
CoolantMassLeakRateAttributeID, // 8
|
||||
CoolantAvailableAttributeID, // 9
|
||||
NextAttributeID // 0x0A
|
||||
};
|
||||
|
||||
static const IndexEntry AttributePointers[];
|
||||
static AttributeIndexSet AttributeIndex;
|
||||
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
@@ -367,6 +406,19 @@
|
||||
public HeatSink
|
||||
{
|
||||
public:
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Attribute Support (the valve slider on the Eng page).
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
ValveSettingAttributeID = HeatSink::NextAttributeID, // 0x0A
|
||||
NextAttributeID
|
||||
};
|
||||
|
||||
static const IndexEntry AttributePointers[];
|
||||
static AttributeIndexSet AttributeIndex;
|
||||
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
|
||||
@@ -87,11 +87,34 @@ Mech::MessageHandlerSet
|
||||
JointedMover::MessageHandlers
|
||||
);
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// The entity-level attribute table (cockpit binding by name).
|
||||
//#############################################################################
|
||||
//
|
||||
const Mech::IndexEntry
|
||||
Mech::AttributePointers[]=
|
||||
{
|
||||
ATTRIBUTE_ENTRY(Mech, RadarRange, radarRange),
|
||||
ATTRIBUTE_ENTRY(Mech, RadarLinearPosition, radarLinearPosition),
|
||||
ATTRIBUTE_ENTRY(Mech, RadarAngularPosition, radarAngularPosition),
|
||||
ATTRIBUTE_ENTRY(Mech, LinearSpeed, currentBodySpeed),
|
||||
ATTRIBUTE_ENTRY(Mech, MaxRunSpeed, reverseStrideLength),
|
||||
ATTRIBUTE_ENTRY(Mech, DuckState, duckState)
|
||||
};
|
||||
|
||||
Mech::AttributeIndexSet
|
||||
Mech::AttributeIndex(
|
||||
ELEMENTS(Mech::AttributePointers),
|
||||
Mech::AttributePointers,
|
||||
JointedMover::AttributeIndex
|
||||
);
|
||||
|
||||
Mech::SharedData
|
||||
Mech::DefaultData(
|
||||
Mech::ClassDerivations,
|
||||
Mech::MessageHandlers,
|
||||
JointedMover::AttributeIndex,
|
||||
Mech::AttributeIndex,
|
||||
33,
|
||||
(Entity::MakeHandler)Mech::Make
|
||||
);
|
||||
@@ -193,6 +216,13 @@ Mech::Mech(
|
||||
lastInflictingID = EntityID::Null;
|
||||
damageLookupTable = NULL;
|
||||
deathTransitionDone = 0;
|
||||
//
|
||||
// Cockpit-published state: the radar follows our own live transform.
|
||||
//
|
||||
radarRange = 4000.0f; // the authored map() max range
|
||||
radarLinearPosition = &localOrigin.linearPosition;
|
||||
radarAngularPosition = &localOrigin.angularPosition;
|
||||
duckState = 0;
|
||||
lastInflictingDamage = 0.0f;
|
||||
|
||||
//
|
||||
|
||||
@@ -166,6 +166,26 @@
|
||||
static MessageHandlerSet
|
||||
MessageHandlers;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Attribute Support -- the ENTITY-level rows the cockpit binds without a
|
||||
// subsystem prefix (the radar trio the map() primitive reads, the speed
|
||||
// pair the speedometer arc reads, and the duck/crouch state selector).
|
||||
// Chained onto JointedMover's index.
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
RadarRangeAttributeID = JointedMover::NextAttributeID,
|
||||
RadarLinearPositionAttributeID,
|
||||
RadarAngularPositionAttributeID,
|
||||
LinearSpeedAttributeID,
|
||||
MaxRunSpeedAttributeID,
|
||||
DuckStateAttributeID,
|
||||
NextAttributeID
|
||||
};
|
||||
|
||||
static const IndexEntry AttributePointers[];
|
||||
static AttributeIndexSet AttributeIndex;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Test Class Support
|
||||
//
|
||||
@@ -462,6 +482,17 @@
|
||||
DamageLookupTable *damageLookupTable; // binary mech+0x444 -- the
|
||||
// cylinder hit-location table
|
||||
|
||||
//
|
||||
// Cockpit-published state (bound by name from L4GAUGE.CFG): the radar
|
||||
// scale + the pointers the map gauge follows, and the duck selector.
|
||||
// The position/angle pointers are the 1995 shape -- the gauge holds a
|
||||
// pointer TO the pointer and re-reads the live transform every frame.
|
||||
//
|
||||
Scalar radarRange;
|
||||
Point3D *radarLinearPosition;
|
||||
Quaternion *radarAngularPosition;
|
||||
int duckState;
|
||||
|
||||
//
|
||||
// The model's authored look-view angles (rad; deg in the resource).
|
||||
//
|
||||
@@ -475,7 +506,7 @@
|
||||
// mech2/mech3/mech4 simulation. Reserved until that path is
|
||||
// reconstructed with named fields.
|
||||
//
|
||||
int reservedState[196];
|
||||
int reservedState[191];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -83,22 +83,9 @@ MechWeapon::MessageHandlerSet
|
||||
const MechWeapon::IndexEntry
|
||||
MechWeapon::AttributePointers[]=
|
||||
{
|
||||
MECHWEAP_PAD( 0, "MechWeaponPad02"),
|
||||
MECHWEAP_PAD( 1, "MechWeaponPad03"),
|
||||
MECHWEAP_PAD( 2, "MechWeaponPad04"),
|
||||
MECHWEAP_PAD( 3, "MechWeaponPad05"),
|
||||
MECHWEAP_PAD( 4, "MechWeaponPad06"),
|
||||
MECHWEAP_PAD( 5, "MechWeaponPad07"),
|
||||
MECHWEAP_PAD( 6, "MechWeaponPad08"),
|
||||
MECHWEAP_PAD( 7, "MechWeaponPad09"),
|
||||
MECHWEAP_PAD( 8, "MechWeaponPad0A"),
|
||||
MECHWEAP_PAD( 9, "MechWeaponPad0B"),
|
||||
MECHWEAP_PAD(10, "MechWeaponPad0C"),
|
||||
MECHWEAP_PAD(11, "MechWeaponPad0D"),
|
||||
MECHWEAP_PAD(12, "MechWeaponPad0E"),
|
||||
MECHWEAP_PAD(13, "MechWeaponPad0F"),
|
||||
MECHWEAP_PAD(14, "MechWeaponPad10"),
|
||||
MECHWEAP_PAD(15, "MechWeaponPad11"),
|
||||
MECHWEAP_PAD( 0, "MechWeaponPad0F"),
|
||||
MECHWEAP_PAD( 1, "MechWeaponPad10"),
|
||||
MECHWEAP_PAD( 2, "MechWeaponPad11"),
|
||||
ATTRIBUTE_ENTRY(MechWeapon, PercentDone, rechargeLevel), // 0x12
|
||||
ATTRIBUTE_ENTRY(MechWeapon, TriggerState, fireImpulse), // 0x13
|
||||
ATTRIBUTE_ENTRY(MechWeapon, DistanceToTarget, rangeToTarget), // 0x14
|
||||
@@ -119,7 +106,7 @@ MechWeapon::AttributeIndexSet
|
||||
MechWeapon::AttributeIndex(
|
||||
ELEMENTS(MechWeapon::AttributePointers),
|
||||
MechWeapon::AttributePointers,
|
||||
Subsystem::AttributeIndex
|
||||
PoweredSubsystem::AttributeIndex
|
||||
);
|
||||
|
||||
MechWeapon::SharedData
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
MechWeaponPadFirstAttributeID = Subsystem::NextAttributeID, // 2
|
||||
MechWeaponPadFirstAttributeID = PoweredSubsystem::NextAttributeID, // 0x0F
|
||||
PercentDoneAttributeID = 0x12,
|
||||
TriggerStateAttributeID, // 0x13 -- the streamed fire-button binding
|
||||
DistanceToTargetAttributeID, // 0x14
|
||||
|
||||
@@ -55,11 +55,33 @@ PoweredSubsystem::MessageHandlerSet
|
||||
HeatSink::MessageHandlers
|
||||
);
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// Attribute tables (cockpit binding by name).
|
||||
//#############################################################################
|
||||
//
|
||||
const PoweredSubsystem::IndexEntry
|
||||
PoweredSubsystem::AttributePointers[]=
|
||||
{
|
||||
ATTRIBUTE_ENTRY(PoweredSubsystem, InputVoltage, inputVoltage),
|
||||
ATTRIBUTE_ENTRY(PoweredSubsystem, OutputVoltage, outputVoltage),
|
||||
ATTRIBUTE_ENTRY(PoweredSubsystem, RatedVoltage, ratedVoltage),
|
||||
ATTRIBUTE_ENTRY(PoweredSubsystem, VoltageState, electricalStateAlarm),
|
||||
ATTRIBUTE_ENTRY(PoweredSubsystem, ConnectMode, modeAlarm)
|
||||
};
|
||||
|
||||
PoweredSubsystem::AttributeIndexSet
|
||||
PoweredSubsystem::AttributeIndex(
|
||||
ELEMENTS(PoweredSubsystem::AttributePointers),
|
||||
PoweredSubsystem::AttributePointers,
|
||||
HeatSink::AttributeIndex
|
||||
);
|
||||
|
||||
PoweredSubsystem::SharedData
|
||||
PoweredSubsystem::DefaultData(
|
||||
PoweredSubsystem::ClassDerivations,
|
||||
PoweredSubsystem::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
PoweredSubsystem::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
@@ -557,11 +579,26 @@ Derivation
|
||||
"Generator"
|
||||
);
|
||||
|
||||
const Generator::IndexEntry
|
||||
Generator::AttributePointers[]=
|
||||
{
|
||||
ATTRIBUTE_ENTRY(Generator, OutputVoltage, outputVoltage),
|
||||
ATTRIBUTE_ENTRY(Generator, RatedVoltage, ratedVoltage),
|
||||
ATTRIBUTE_ENTRY(Generator, GeneratorNumber, generatorNumber)
|
||||
};
|
||||
|
||||
Generator::AttributeIndexSet
|
||||
Generator::AttributeIndex(
|
||||
ELEMENTS(Generator::AttributePointers),
|
||||
Generator::AttributePointers,
|
||||
HeatSink::AttributeIndex
|
||||
);
|
||||
|
||||
Generator::SharedData
|
||||
Generator::DefaultData(
|
||||
Generator::ClassDerivations,
|
||||
Subsystem::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
Generator::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
|
||||
@@ -49,6 +49,27 @@
|
||||
// Shared Data Support
|
||||
//
|
||||
public:
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Attribute Support. The electrical family; NextAttributeID lands on
|
||||
// the AUTHENTIC 0x0F -- the surviving SENSOR.HPP numbers RadarPercent
|
||||
// off it and MechWeapon's pinned table starts its real IDs at 0x12
|
||||
// (three pads bridge 0x0F..0x11). Do not renumber without re-pinning
|
||||
// both.
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
InputVoltageAttributeID = HeatSink::NextAttributeID, // 0x0A
|
||||
OutputVoltageAttributeID, // 0x0B
|
||||
RatedVoltageAttributeID, // 0x0C
|
||||
VoltageStateAttributeID, // 0x0D
|
||||
ConnectModeAttributeID, // 0x0E
|
||||
NextAttributeID // 0x0F
|
||||
};
|
||||
|
||||
static const IndexEntry AttributePointers[];
|
||||
static AttributeIndexSet AttributeIndex;
|
||||
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
@@ -301,6 +322,23 @@
|
||||
// Shared Data Support
|
||||
//
|
||||
public:
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Attribute Support (the generator panel reads these by name). A
|
||||
// SIBLING branch of PoweredSubsystem off HeatSink -- the shared 0x0A
|
||||
// base is correct, each branch owns its own index set.
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
OutputVoltageAttributeID = HeatSink::NextAttributeID, // 0x0A
|
||||
RatedVoltageAttributeID, // 0x0B
|
||||
GeneratorNumberAttributeID, // 0x0C
|
||||
NextAttributeID
|
||||
};
|
||||
|
||||
static const IndexEntry AttributePointers[];
|
||||
static AttributeIndexSet AttributeIndex;
|
||||
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
|
||||
@@ -1,379 +1,379 @@
|
||||
//===========================================================================//
|
||||
// File: reservr.cpp //
|
||||
// Project: BattleTech Brick: Mech subsystems //
|
||||
// Contents: Reservoir -- the coolant store //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
||||
// All Rights reserved worldwide //
|
||||
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
||||
//===========================================================================//
|
||||
|
||||
#include <bt.hpp>
|
||||
#pragma hdrstop
|
||||
|
||||
#if !defined(RESERVR_HPP)
|
||||
# include <reservr.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(MECH_HPP)
|
||||
# include <mech.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(MECHWEAP_HPP)
|
||||
# include <mechweap.hpp>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
Derivation
|
||||
Reservoir::ClassDerivations(
|
||||
HeatSink::ClassDerivations,
|
||||
"Reservoir"
|
||||
);
|
||||
|
||||
//
|
||||
// The cockpit coolant-flush button (id 4, "InjectCoolant"); ToggleCooling
|
||||
// (id 3) is inherited from the HeatSink chain.
|
||||
//
|
||||
const Reservoir::HandlerEntry
|
||||
Reservoir::MessageHandlerEntries[]=
|
||||
{
|
||||
MESSAGE_ENTRY(Reservoir, InjectCoolant)
|
||||
};
|
||||
|
||||
Reservoir::MessageHandlerSet
|
||||
Reservoir::MessageHandlers(
|
||||
ELEMENTS(Reservoir::MessageHandlerEntries),
|
||||
Reservoir::MessageHandlerEntries,
|
||||
HeatSink::MessageHandlers
|
||||
);
|
||||
|
||||
Reservoir::SharedData
|
||||
Reservoir::DefaultData(
|
||||
Reservoir::ClassDerivations,
|
||||
Reservoir::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// The coolant store (binary ctor @4af408): CoolantCapacity overlays the
|
||||
// inherited HeatSink thermalCapacity slot; the charge starts full; the flush
|
||||
// flow scale is zero (a reservoir never conducts like an ordinary sink). A
|
||||
// master reservoir registers the CoolantSimulation performance.
|
||||
//
|
||||
// Deferred with the AggregateHeatSink wave: the authentic master path also
|
||||
// attaches the reservoir into the central bank's radiator loop and rescales
|
||||
// the capacity by 0.05 x the bank's heat-sink count (the central sink here is
|
||||
// still a plain HeatSink, which has no count) -- until then the streamed
|
||||
// capacity is used as-is (a larger tank than authentic; noted).
|
||||
//#############################################################################
|
||||
//
|
||||
Reservoir::Reservoir(
|
||||
Mech *owner,
|
||||
int subsystem_ID,
|
||||
SubsystemResource *r,
|
||||
SharedData &shared_data
|
||||
):
|
||||
HeatSink(owner, subsystem_ID, r, shared_data)
|
||||
{
|
||||
Check(owner);
|
||||
Check_Pointer(r);
|
||||
|
||||
reservoirAlarm.Initialize(2);
|
||||
|
||||
squirtEfficiency = 0.5f;
|
||||
thermalCapacity = r->coolantCapacity;
|
||||
coolantSquirtMass = r->coolantSquirtMass;
|
||||
coolantLevel = thermalCapacity;
|
||||
coolantFlowScale = 0.0f;
|
||||
injectAccumulator = 0.0f;
|
||||
reservoirAlarm.SetLevel(0);
|
||||
|
||||
if (getenv("BT_POWER_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[resv] '" << GetName()
|
||||
<< "' capacity=" << thermalCapacity
|
||||
<< " squirtMass=" << coolantSquirtMass << endl << flush;
|
||||
}
|
||||
|
||||
if (owner->GetInstance() != Entity::ReplicantInstance)
|
||||
{
|
||||
SetPerformance(&Reservoir::CoolantSimulation);
|
||||
|
||||
//
|
||||
// The authentic master path scales the tank by the bank's aggregate
|
||||
// heat-sink count: capacity = 0.05 x count x streamed (binary
|
||||
// @4af408, CoolantCapacityScale byte-verified 0.05). The linked sink
|
||||
// (from the resource) IS the bank. (The bank-side Attach that routes
|
||||
// its DrawCoolant top-ups here joins the attach wave.)
|
||||
//
|
||||
HeatSink *link = (HeatSink *)linkedSinks.Resolve();
|
||||
if (link != NULL
|
||||
&& link->IsDerivedFrom(AggregateHeatSink::ClassDerivations))
|
||||
{
|
||||
Scalar masterScale =
|
||||
(Scalar)((AggregateHeatSink *)link)->GetHeatSinkCount();
|
||||
thermalCapacity = 0.05f * masterScale * thermalCapacity;
|
||||
coolantLevel = thermalCapacity;
|
||||
if (getenv("BT_POWER_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[resv] capacity rescaled by bank count -> "
|
||||
<< thermalCapacity << endl << flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
Reservoir::~Reservoir()
|
||||
{
|
||||
}
|
||||
|
||||
Logical
|
||||
Reservoir::TestClass(Mech &)
|
||||
{
|
||||
return True;
|
||||
}
|
||||
|
||||
Logical
|
||||
Reservoir::TestInstance() const
|
||||
{
|
||||
return IsDerivedFrom(ClassDerivations);
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// DrawCoolant -- the SOURCE (binary @4af3b0): hand out up to coolantLevel of
|
||||
// the requested amount and deduct it from the charge.
|
||||
//#############################################################################
|
||||
//
|
||||
Scalar
|
||||
Reservoir::DrawCoolant(Scalar requested)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
Scalar supplied = 0.0f;
|
||||
if (requested >= 0.0f)
|
||||
{
|
||||
supplied = requested;
|
||||
if (coolantLevel < requested)
|
||||
{
|
||||
supplied = coolantLevel;
|
||||
}
|
||||
}
|
||||
coolantLevel -= supplied;
|
||||
return supplied;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// InjectCoolantMessageHandler -- the cockpit coolant-flush button (binary
|
||||
// @4aee70, id 4): novice-locked. Release drops the inject alarm (flush OFF);
|
||||
// press raises it when the tank holds charge (the flush-cloud effect joins
|
||||
// the psfx wave) and zeroes the elapsed accumulator.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
Reservoir::InjectCoolantMessageHandler(ReceiverDataMessageOf<int> *message)
|
||||
{
|
||||
Check(this);
|
||||
Check(message);
|
||||
|
||||
if (NoviceLockout())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (message->dataContents < 1)
|
||||
{
|
||||
reservoirAlarm.SetLevel(0); // release -> flush OFF
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reservoirAlarm.GetLevel() != 1 && coolantLevel > 0.0f)
|
||||
{
|
||||
reservoirAlarm.SetLevel(1); // flush ON
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[resv] FLUSH ON via button (charge="
|
||||
<< coolantLevel << ")" << endl << flush;
|
||||
}
|
||||
}
|
||||
injectAccumulator = 0.0f;
|
||||
}
|
||||
ForceUpdate();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// CoolantSimulation -- the registered Performance (binary @4aef78): while
|
||||
// injection is active, accumulate elapsed time and run the coolant
|
||||
// distribution. The InjectCoolant COCKPIT BUTTON (the id-4 message handler
|
||||
// that raises the alarm) joins with the cockpit-button message wave; the DEV
|
||||
// hook BT_FORCE_FLUSH=1 raises it here so the flush machinery is exercisable
|
||||
// headlessly (one press ~4 s after the sim starts ticking).
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
Reservoir::CoolantSimulation(Scalar time_slice)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
{
|
||||
static int forceFlush = -1;
|
||||
if (forceFlush < 0)
|
||||
{
|
||||
forceFlush = (getenv("BT_FORCE_FLUSH") != NULL) ? 1 : 0;
|
||||
}
|
||||
if (forceFlush == 1)
|
||||
{
|
||||
static Scalar armAccum = 0.0f;
|
||||
armAccum += time_slice;
|
||||
if (armAccum >= 4.0f && reservoirAlarm.GetLevel() != 1
|
||||
&& coolantLevel > 0.0f)
|
||||
{
|
||||
forceFlush = 2;
|
||||
reservoirAlarm.SetLevel(1);
|
||||
injectAccumulator = 0.0f;
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[resv] FLUSH ON (charge="
|
||||
<< coolantLevel << ")" << endl << flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reservoirAlarm.GetLevel() == 1)
|
||||
{
|
||||
injectAccumulator += time_slice;
|
||||
InjectCoolant(time_slice);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// InjectCoolant -- the coolant-flush distribution (binary @4aefa4). Walk the
|
||||
// roster gathering the flush targets in the authentic pass order -- the
|
||||
// condensers, then the weapons, then every heat sink, then the linked master
|
||||
// (the duplicate visits are intentional weighting) -- and squirt
|
||||
// (coolantSquirtMass x the target's coolantFlowScale x dt) into each,
|
||||
// crediting a negative pending-heat chill for the moved mass. Squirts only
|
||||
// ever leave the tank; the chill only ever cools.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
Reservoir::InjectCoolant(Scalar time_slice)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
if (fabs(coolantLevel) <= 1.0e-4f) // the tank is empty
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
enum { kMaxWork = 96 };
|
||||
HeatSink *work[kMaxWork];
|
||||
int workCount = 0;
|
||||
Entity *own = (Entity *)owner;
|
||||
int count = own->GetSubsystemCount();
|
||||
int i;
|
||||
|
||||
for (i = 2; i < count && workCount < kMaxWork; ++i)
|
||||
{
|
||||
Subsystem *s = own->GetSubsystem(i);
|
||||
if (s != NULL && s->IsDerivedFrom(Condenser::ClassDerivations))
|
||||
{
|
||||
work[workCount++] = (HeatSink *)s;
|
||||
}
|
||||
}
|
||||
for (i = 2; i < count && workCount < kMaxWork; ++i)
|
||||
{
|
||||
Subsystem *s = own->GetSubsystem(i);
|
||||
if (s != NULL && s->IsDerivedFrom(MechWeapon::ClassDerivations))
|
||||
{
|
||||
work[workCount++] = (HeatSink *)s;
|
||||
}
|
||||
}
|
||||
for (i = 2; i < count && workCount < kMaxWork; ++i)
|
||||
{
|
||||
Subsystem *s = own->GetSubsystem(i);
|
||||
if (s != NULL && s->IsDerivedFrom(HeatSink::ClassDerivations))
|
||||
{
|
||||
work[workCount++] = (HeatSink *)s;
|
||||
}
|
||||
}
|
||||
{
|
||||
HeatSink *link = (HeatSink *)linkedSinks.Resolve();
|
||||
if (link != NULL && workCount < kMaxWork)
|
||||
{
|
||||
work[workCount++] = link;
|
||||
}
|
||||
}
|
||||
|
||||
for (int w = 0; w < workCount; ++w)
|
||||
{
|
||||
if (fabs(coolantLevel) <= 1.0e-4f) // ran dry mid-pass
|
||||
{
|
||||
return;
|
||||
}
|
||||
HeatSink *sink = work[w];
|
||||
if (sink->coolantFlowScale == 0.0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// squirt = -(squirtMass x flowScale x dt), clamped to [-coolantLevel, 0].
|
||||
//
|
||||
Scalar move = -coolantSquirtMass
|
||||
* sink->coolantFlowScale
|
||||
* time_slice;
|
||||
Scalar lo = -coolantLevel;
|
||||
if (move < lo) move = lo;
|
||||
if (move > 0.0f) move = 0.0f;
|
||||
|
||||
//
|
||||
// The heat delta riding the moved mass (computed BEFORE the level
|
||||
// updates, as in the binary).
|
||||
//
|
||||
Scalar den = (fabs(sink->coolantLevel) > 1.0e-4f)
|
||||
? sink->coolantLevel
|
||||
: sink->thermalCapacity;
|
||||
Scalar moved = (move < 0.0f) ? -move : move;
|
||||
Scalar fracSink = moved / den;
|
||||
Scalar fracRes = moved / coolantLevel;
|
||||
Scalar heatDelta =
|
||||
sink->heatEnergy * fracSink
|
||||
- heatEnergy * fracRes;
|
||||
|
||||
coolantLevel += move; // the reservoir drains (move <= 0)
|
||||
sink->coolantLevel -= move; // the sink gains
|
||||
if (sink->coolantLevel >= 0.0f)
|
||||
{
|
||||
if (sink->coolantLevel > sink->thermalCapacity)
|
||||
{
|
||||
sink->coolantLevel = sink->thermalCapacity;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sink->coolantLevel = 0.0f;
|
||||
}
|
||||
|
||||
Scalar cap = sink->thermalMass * startingTemperature;
|
||||
if (heatDelta > cap)
|
||||
{
|
||||
heatDelta = cap;
|
||||
}
|
||||
Scalar chill = -heatDelta;
|
||||
if (chill > 0.0f)
|
||||
{
|
||||
chill = 0.0f; // the flush only ever COOLS
|
||||
}
|
||||
sink->pendingHeat += chill;
|
||||
}
|
||||
}
|
||||
//===========================================================================//
|
||||
// File: reservr.cpp //
|
||||
// Project: BattleTech Brick: Mech subsystems //
|
||||
// Contents: Reservoir -- the coolant store //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
||||
// All Rights reserved worldwide //
|
||||
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
||||
//===========================================================================//
|
||||
|
||||
#include <bt.hpp>
|
||||
#pragma hdrstop
|
||||
|
||||
#if !defined(RESERVR_HPP)
|
||||
# include <reservr.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(MECH_HPP)
|
||||
# include <mech.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(MECHWEAP_HPP)
|
||||
# include <mechweap.hpp>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
Derivation
|
||||
Reservoir::ClassDerivations(
|
||||
HeatSink::ClassDerivations,
|
||||
"Reservoir"
|
||||
);
|
||||
|
||||
//
|
||||
// The cockpit coolant-flush button (id 4, "InjectCoolant"); ToggleCooling
|
||||
// (id 3) is inherited from the HeatSink chain.
|
||||
//
|
||||
const Reservoir::HandlerEntry
|
||||
Reservoir::MessageHandlerEntries[]=
|
||||
{
|
||||
MESSAGE_ENTRY(Reservoir, InjectCoolant)
|
||||
};
|
||||
|
||||
Reservoir::MessageHandlerSet
|
||||
Reservoir::MessageHandlers(
|
||||
ELEMENTS(Reservoir::MessageHandlerEntries),
|
||||
Reservoir::MessageHandlerEntries,
|
||||
HeatSink::MessageHandlers
|
||||
);
|
||||
|
||||
Reservoir::SharedData
|
||||
Reservoir::DefaultData(
|
||||
Reservoir::ClassDerivations,
|
||||
Reservoir::MessageHandlers,
|
||||
HeatSink::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// The coolant store (binary ctor @4af408): CoolantCapacity overlays the
|
||||
// inherited HeatSink thermalCapacity slot; the charge starts full; the flush
|
||||
// flow scale is zero (a reservoir never conducts like an ordinary sink). A
|
||||
// master reservoir registers the CoolantSimulation performance.
|
||||
//
|
||||
// Deferred with the AggregateHeatSink wave: the authentic master path also
|
||||
// attaches the reservoir into the central bank's radiator loop and rescales
|
||||
// the capacity by 0.05 x the bank's heat-sink count (the central sink here is
|
||||
// still a plain HeatSink, which has no count) -- until then the streamed
|
||||
// capacity is used as-is (a larger tank than authentic; noted).
|
||||
//#############################################################################
|
||||
//
|
||||
Reservoir::Reservoir(
|
||||
Mech *owner,
|
||||
int subsystem_ID,
|
||||
SubsystemResource *r,
|
||||
SharedData &shared_data
|
||||
):
|
||||
HeatSink(owner, subsystem_ID, r, shared_data)
|
||||
{
|
||||
Check(owner);
|
||||
Check_Pointer(r);
|
||||
|
||||
reservoirAlarm.Initialize(2);
|
||||
|
||||
squirtEfficiency = 0.5f;
|
||||
thermalCapacity = r->coolantCapacity;
|
||||
coolantSquirtMass = r->coolantSquirtMass;
|
||||
coolantLevel = thermalCapacity;
|
||||
coolantFlowScale = 0.0f;
|
||||
injectAccumulator = 0.0f;
|
||||
reservoirAlarm.SetLevel(0);
|
||||
|
||||
if (getenv("BT_POWER_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[resv] '" << GetName()
|
||||
<< "' capacity=" << thermalCapacity
|
||||
<< " squirtMass=" << coolantSquirtMass << endl << flush;
|
||||
}
|
||||
|
||||
if (owner->GetInstance() != Entity::ReplicantInstance)
|
||||
{
|
||||
SetPerformance(&Reservoir::CoolantSimulation);
|
||||
|
||||
//
|
||||
// The authentic master path scales the tank by the bank's aggregate
|
||||
// heat-sink count: capacity = 0.05 x count x streamed (binary
|
||||
// @4af408, CoolantCapacityScale byte-verified 0.05). The linked sink
|
||||
// (from the resource) IS the bank. (The bank-side Attach that routes
|
||||
// its DrawCoolant top-ups here joins the attach wave.)
|
||||
//
|
||||
HeatSink *link = (HeatSink *)linkedSinks.Resolve();
|
||||
if (link != NULL
|
||||
&& link->IsDerivedFrom(AggregateHeatSink::ClassDerivations))
|
||||
{
|
||||
Scalar masterScale =
|
||||
(Scalar)((AggregateHeatSink *)link)->GetHeatSinkCount();
|
||||
thermalCapacity = 0.05f * masterScale * thermalCapacity;
|
||||
coolantLevel = thermalCapacity;
|
||||
if (getenv("BT_POWER_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[resv] capacity rescaled by bank count -> "
|
||||
<< thermalCapacity << endl << flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
Reservoir::~Reservoir()
|
||||
{
|
||||
}
|
||||
|
||||
Logical
|
||||
Reservoir::TestClass(Mech &)
|
||||
{
|
||||
return True;
|
||||
}
|
||||
|
||||
Logical
|
||||
Reservoir::TestInstance() const
|
||||
{
|
||||
return IsDerivedFrom(ClassDerivations);
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// DrawCoolant -- the SOURCE (binary @4af3b0): hand out up to coolantLevel of
|
||||
// the requested amount and deduct it from the charge.
|
||||
//#############################################################################
|
||||
//
|
||||
Scalar
|
||||
Reservoir::DrawCoolant(Scalar requested)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
Scalar supplied = 0.0f;
|
||||
if (requested >= 0.0f)
|
||||
{
|
||||
supplied = requested;
|
||||
if (coolantLevel < requested)
|
||||
{
|
||||
supplied = coolantLevel;
|
||||
}
|
||||
}
|
||||
coolantLevel -= supplied;
|
||||
return supplied;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// InjectCoolantMessageHandler -- the cockpit coolant-flush button (binary
|
||||
// @4aee70, id 4): novice-locked. Release drops the inject alarm (flush OFF);
|
||||
// press raises it when the tank holds charge (the flush-cloud effect joins
|
||||
// the psfx wave) and zeroes the elapsed accumulator.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
Reservoir::InjectCoolantMessageHandler(ReceiverDataMessageOf<int> *message)
|
||||
{
|
||||
Check(this);
|
||||
Check(message);
|
||||
|
||||
if (NoviceLockout())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (message->dataContents < 1)
|
||||
{
|
||||
reservoirAlarm.SetLevel(0); // release -> flush OFF
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reservoirAlarm.GetLevel() != 1 && coolantLevel > 0.0f)
|
||||
{
|
||||
reservoirAlarm.SetLevel(1); // flush ON
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[resv] FLUSH ON via button (charge="
|
||||
<< coolantLevel << ")" << endl << flush;
|
||||
}
|
||||
}
|
||||
injectAccumulator = 0.0f;
|
||||
}
|
||||
ForceUpdate();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// CoolantSimulation -- the registered Performance (binary @4aef78): while
|
||||
// injection is active, accumulate elapsed time and run the coolant
|
||||
// distribution. The InjectCoolant COCKPIT BUTTON (the id-4 message handler
|
||||
// that raises the alarm) joins with the cockpit-button message wave; the DEV
|
||||
// hook BT_FORCE_FLUSH=1 raises it here so the flush machinery is exercisable
|
||||
// headlessly (one press ~4 s after the sim starts ticking).
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
Reservoir::CoolantSimulation(Scalar time_slice)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
{
|
||||
static int forceFlush = -1;
|
||||
if (forceFlush < 0)
|
||||
{
|
||||
forceFlush = (getenv("BT_FORCE_FLUSH") != NULL) ? 1 : 0;
|
||||
}
|
||||
if (forceFlush == 1)
|
||||
{
|
||||
static Scalar armAccum = 0.0f;
|
||||
armAccum += time_slice;
|
||||
if (armAccum >= 4.0f && reservoirAlarm.GetLevel() != 1
|
||||
&& coolantLevel > 0.0f)
|
||||
{
|
||||
forceFlush = 2;
|
||||
reservoirAlarm.SetLevel(1);
|
||||
injectAccumulator = 0.0f;
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[resv] FLUSH ON (charge="
|
||||
<< coolantLevel << ")" << endl << flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reservoirAlarm.GetLevel() == 1)
|
||||
{
|
||||
injectAccumulator += time_slice;
|
||||
InjectCoolant(time_slice);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// InjectCoolant -- the coolant-flush distribution (binary @4aefa4). Walk the
|
||||
// roster gathering the flush targets in the authentic pass order -- the
|
||||
// condensers, then the weapons, then every heat sink, then the linked master
|
||||
// (the duplicate visits are intentional weighting) -- and squirt
|
||||
// (coolantSquirtMass x the target's coolantFlowScale x dt) into each,
|
||||
// crediting a negative pending-heat chill for the moved mass. Squirts only
|
||||
// ever leave the tank; the chill only ever cools.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
Reservoir::InjectCoolant(Scalar time_slice)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
if (fabs(coolantLevel) <= 1.0e-4f) // the tank is empty
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
enum { kMaxWork = 96 };
|
||||
HeatSink *work[kMaxWork];
|
||||
int workCount = 0;
|
||||
Entity *own = (Entity *)owner;
|
||||
int count = own->GetSubsystemCount();
|
||||
int i;
|
||||
|
||||
for (i = 2; i < count && workCount < kMaxWork; ++i)
|
||||
{
|
||||
Subsystem *s = own->GetSubsystem(i);
|
||||
if (s != NULL && s->IsDerivedFrom(Condenser::ClassDerivations))
|
||||
{
|
||||
work[workCount++] = (HeatSink *)s;
|
||||
}
|
||||
}
|
||||
for (i = 2; i < count && workCount < kMaxWork; ++i)
|
||||
{
|
||||
Subsystem *s = own->GetSubsystem(i);
|
||||
if (s != NULL && s->IsDerivedFrom(MechWeapon::ClassDerivations))
|
||||
{
|
||||
work[workCount++] = (HeatSink *)s;
|
||||
}
|
||||
}
|
||||
for (i = 2; i < count && workCount < kMaxWork; ++i)
|
||||
{
|
||||
Subsystem *s = own->GetSubsystem(i);
|
||||
if (s != NULL && s->IsDerivedFrom(HeatSink::ClassDerivations))
|
||||
{
|
||||
work[workCount++] = (HeatSink *)s;
|
||||
}
|
||||
}
|
||||
{
|
||||
HeatSink *link = (HeatSink *)linkedSinks.Resolve();
|
||||
if (link != NULL && workCount < kMaxWork)
|
||||
{
|
||||
work[workCount++] = link;
|
||||
}
|
||||
}
|
||||
|
||||
for (int w = 0; w < workCount; ++w)
|
||||
{
|
||||
if (fabs(coolantLevel) <= 1.0e-4f) // ran dry mid-pass
|
||||
{
|
||||
return;
|
||||
}
|
||||
HeatSink *sink = work[w];
|
||||
if (sink->coolantFlowScale == 0.0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// squirt = -(squirtMass x flowScale x dt), clamped to [-coolantLevel, 0].
|
||||
//
|
||||
Scalar move = -coolantSquirtMass
|
||||
* sink->coolantFlowScale
|
||||
* time_slice;
|
||||
Scalar lo = -coolantLevel;
|
||||
if (move < lo) move = lo;
|
||||
if (move > 0.0f) move = 0.0f;
|
||||
|
||||
//
|
||||
// The heat delta riding the moved mass (computed BEFORE the level
|
||||
// updates, as in the binary).
|
||||
//
|
||||
Scalar den = (fabs(sink->coolantLevel) > 1.0e-4f)
|
||||
? sink->coolantLevel
|
||||
: sink->thermalCapacity;
|
||||
Scalar moved = (move < 0.0f) ? -move : move;
|
||||
Scalar fracSink = moved / den;
|
||||
Scalar fracRes = moved / coolantLevel;
|
||||
Scalar heatDelta =
|
||||
sink->heatEnergy * fracSink
|
||||
- heatEnergy * fracRes;
|
||||
|
||||
coolantLevel += move; // the reservoir drains (move <= 0)
|
||||
sink->coolantLevel -= move; // the sink gains
|
||||
if (sink->coolantLevel >= 0.0f)
|
||||
{
|
||||
if (sink->coolantLevel > sink->thermalCapacity)
|
||||
{
|
||||
sink->coolantLevel = sink->thermalCapacity;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sink->coolantLevel = 0.0f;
|
||||
}
|
||||
|
||||
Scalar cap = sink->thermalMass * startingTemperature;
|
||||
if (heatDelta > cap)
|
||||
{
|
||||
heatDelta = cap;
|
||||
}
|
||||
Scalar chill = -heatDelta;
|
||||
if (chill > 0.0f)
|
||||
{
|
||||
chill = 0.0f; // the flush only ever COOLS
|
||||
}
|
||||
sink->pendingHeat += chill;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,33 @@ Derivation
|
||||
"Sensor"
|
||||
);
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// Attribute table -- the AUTHENTIC surviving SENSOR.HPP enum
|
||||
// (RadarPercent/SelfTest/BadVoltage off PoweredSubsystem::NextAttributeID =
|
||||
// 0x0F). The cockpit map() binds "Avionics/RadarPercent".
|
||||
//#############################################################################
|
||||
//
|
||||
const Sensor::IndexEntry
|
||||
Sensor::AttributePointers[]=
|
||||
{
|
||||
ATTRIBUTE_ENTRY(Sensor, RadarPercent, radarPercent),
|
||||
ATTRIBUTE_ENTRY(Sensor, SelfTest, selfTest),
|
||||
ATTRIBUTE_ENTRY(Sensor, BadVoltage, badVoltage)
|
||||
};
|
||||
|
||||
Sensor::AttributeIndexSet
|
||||
Sensor::AttributeIndex(
|
||||
ELEMENTS(Sensor::AttributePointers),
|
||||
Sensor::AttributePointers,
|
||||
PoweredSubsystem::AttributeIndex
|
||||
);
|
||||
|
||||
Sensor::SharedData
|
||||
Sensor::DefaultData(
|
||||
Sensor::ClassDerivations,
|
||||
Subsystem::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
Sensor::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
|
||||
@@ -439,3 +439,39 @@ tables -- lights half the panels), add BTPlayer::GetKillCount() and the
|
||||
ammoBinLink accessor, then the VISUAL pass on the rig (emulator VPXLOG=1 +
|
||||
VDB head windows / pod-launch --layout explode) diffing against the shipped
|
||||
exe on the same GAUGE content.
|
||||
|
||||
## 5.3.27 -- THE ATTRIBUTE WAVE (48/50 cockpit bindings LIVE)
|
||||
|
||||
The heat/power/sensor/mech attribute rows are published, so the gauges read
|
||||
the real simulation instead of the zero cell. THE NUMBERING IS PINNED:
|
||||
the chain publishes IDs 2..0x0E so `PoweredSubsystem::NextAttributeID`
|
||||
lands on the AUTHENTIC 0x0F -- the surviving SENSOR.HPP numbers
|
||||
RadarPercent off it, and MechWeapon's binary-pinned table starts its real
|
||||
IDs at 0x12 (its pads therefore shrank 16 -> 3, bridging 0x0F..0x11 exactly
|
||||
as the header comment predicted). Do not renumber without re-pinning both.
|
||||
|
||||
HeatableSubsystem 2..5 CurrentTemperature / HeatLoad /
|
||||
DegradationTemperature / FailureTemperature
|
||||
HeatSink 6..9 CoolantMass / CoolantCapacity /
|
||||
CoolantMassLeakRate / CoolantAvailable
|
||||
Condenser 0x0A ValveSetting (sibling branch)
|
||||
PoweredSubsystem 0x0A-0E InputVoltage / OutputVoltage / RatedVoltage /
|
||||
VoltageState / ConnectMode -> Next = 0x0F
|
||||
Generator 0x0A-0C OutputVoltage / RatedVoltage / GeneratorNumber
|
||||
(sibling branch off HeatSink)
|
||||
Sensor 0x0F-11 RadarPercent / SelfTest / BadVoltage
|
||||
(the AUTHENTIC surviving enum, now defined)
|
||||
Mech (entity) Radar{Range,LinearPosition,AngularPosition} /
|
||||
LinearSpeed / MaxRunSpeed / DuckState
|
||||
|
||||
Also rechained so the whole family is visible where the cfg expects it:
|
||||
MechWeapon -> PoweredSubsystem::AttributeIndex (weapons expose temps),
|
||||
Sensor -> PoweredSubsystem, Reservoir + AggregateHeatSink -> HeatSink.
|
||||
|
||||
VERIFIED (BT_GAUGE_ATTR_LOG=1 on gaugeattr.conf): 48 OK / 2 NULL, up from
|
||||
33/17 and 0/50 at the block's birth. The two remaining have NO member to
|
||||
bind: HeatSink/AmbientTemperature (the radiator's ambient is a sim constant,
|
||||
not state) and Searchlight/LightOn (Searchlight is a memberless
|
||||
PowerWatcher subclass) -- both fall to the zero cell and draw static, which
|
||||
is correct until those bricks exist. Gauge fight + smoke + novice all
|
||||
clean.
|
||||
|
||||
Reference in New Issue
Block a user