diff --git a/restoration/source410/BT/HEAT.CPP b/restoration/source410/BT/HEAT.CPP index c53b3121..694ee037 100644 --- a/restoration/source410/BT/HEAT.CPP +++ b/restoration/source410/BT/HEAT.CPP @@ -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 ); diff --git a/restoration/source410/BT/HEAT.HPP b/restoration/source410/BT/HEAT.HPP index 85fcbcd8..98c88f64 100644 --- a/restoration/source410/BT/HEAT.HPP +++ b/restoration/source410/BT/HEAT.HPP @@ -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; diff --git a/restoration/source410/BT/MECH.CPP b/restoration/source410/BT/MECH.CPP index 8e559057..37808485 100644 --- a/restoration/source410/BT/MECH.CPP +++ b/restoration/source410/BT/MECH.CPP @@ -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; // diff --git a/restoration/source410/BT/MECH.HPP b/restoration/source410/BT/MECH.HPP index ade9c5d8..26eacbdd 100644 --- a/restoration/source410/BT/MECH.HPP +++ b/restoration/source410/BT/MECH.HPP @@ -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 diff --git a/restoration/source410/BT/MECHWEAP.CPP b/restoration/source410/BT/MECHWEAP.CPP index 6249fea4..d35e9d3a 100644 --- a/restoration/source410/BT/MECHWEAP.CPP +++ b/restoration/source410/BT/MECHWEAP.CPP @@ -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 diff --git a/restoration/source410/BT/MECHWEAP.HPP b/restoration/source410/BT/MECHWEAP.HPP index 203e0fdf..14c291bc 100644 --- a/restoration/source410/BT/MECHWEAP.HPP +++ b/restoration/source410/BT/MECHWEAP.HPP @@ -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 diff --git a/restoration/source410/BT/POWERSUB.CPP b/restoration/source410/BT/POWERSUB.CPP index 19b0b47b..97757a28 100644 --- a/restoration/source410/BT/POWERSUB.CPP +++ b/restoration/source410/BT/POWERSUB.CPP @@ -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 ); diff --git a/restoration/source410/BT/POWERSUB.HPP b/restoration/source410/BT/POWERSUB.HPP index 0b25c8a5..d84ddb92 100644 --- a/restoration/source410/BT/POWERSUB.HPP +++ b/restoration/source410/BT/POWERSUB.HPP @@ -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; diff --git a/restoration/source410/BT/RESERVR.CPP b/restoration/source410/BT/RESERVR.CPP index 8cea36c8..57ada596 100644 --- a/restoration/source410/BT/RESERVR.CPP +++ b/restoration/source410/BT/RESERVR.CPP @@ -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 -#pragma hdrstop - -#if !defined(RESERVR_HPP) -# include -#endif - -#if !defined(MECH_HPP) -# include -#endif - -#if !defined(MECHWEAP_HPP) -# include -#endif - -#include - -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 *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 +#pragma hdrstop + +#if !defined(RESERVR_HPP) +# include +#endif + +#if !defined(MECH_HPP) +# include +#endif + +#if !defined(MECHWEAP_HPP) +# include +#endif + +#include + +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 *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; + } +} diff --git a/restoration/source410/BT/SENSOR.CPP b/restoration/source410/BT/SENSOR.CPP index e1e94f68..0d95b17c 100644 --- a/restoration/source410/BT/SENSOR.CPP +++ b/restoration/source410/BT/SENSOR.CPP @@ -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 ); diff --git a/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md b/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md index d76a7bef..18ed62b7 100644 --- a/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md +++ b/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md @@ -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.