//===========================================================================// // File: heat.hpp // // Project: BattleTech // // Contents: Implementation details for heatable subsystems // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // // //---------------------------------------------------------------------------// // Copyright (C) 1995, Virtual World Entertainment, Inc. // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// #if !defined(HEAT_HPP) # define HEAT_HPP # if !defined(MECHSUB_HPP) # include # endif # if !defined(AVERAGE_HPP) # include # endif //##################### Forward Class Declarations ####################### class Mech; //########################################################################### //################## HeatableSubsystem Model Resource ################### //########################################################################### struct HeatableSubsystem__SubsystemResource: public MechSubsystem::SubsystemResource { Scalar startingTemperature; Scalar degradationTemperature; Scalar failureTemperature; Scalar thermalConductance; Scalar thermalMass; }; //########################################################################### //######################## HeatableSubsystem ############################ //########################################################################### class HeatableSubsystem: public MechSubsystem { //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // 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 = MechSubsystem::NextAttributeID, // 3 HeatLoadAttributeID, // 3 DegradationTemperatureAttributeID, // 4 FailureTemperatureAttributeID, // 5 NextAttributeID // 6 }; static const IndexEntry AttributePointers[]; static AttributeIndexSet AttributeIndex; static Derivation ClassDerivations; static SharedData DefaultData; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Test Class Support // public: static Logical TestClass(Mech &); Logical TestInstance() const; void ResetToInitialState(); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Construction and Destruction // public: typedef HeatableSubsystem__SubsystemResource SubsystemResource; HeatableSubsystem( Mech *owner, int subsystem_ID, SubsystemResource *subsystem_resource, SharedData &shared_data = DefaultData ); ~HeatableSubsystem(); static int CreateStreamedSubsystem( NotationFile *model_file, const char *model_name, const char *subsystem_name, SubsystemResource *subsystem_resource, NotationFile *subsystem_file, const ResourceDirectories *directories, int passes = 1 ); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Local Data // protected: Scalar currentTemperature; Scalar heatLoad; Scalar degradationTemperature; Scalar failureTemperature; }; //########################################################################### //########################## SubsystemConnection ######################## //########################################################################### // // A slot linking a heat sink to its master / linked heat sink. // class SubsystemConnection { public: SubsystemConnection() { linked = NULL; } void Add(Subsystem *s) { linked = s; } Subsystem* Resolve() const { return linked; } void Clear() { linked = NULL; } protected: Subsystem *linked; int reserved[2]; }; //########################################################################### //####################### HeatSink Model Resource ####################### //########################################################################### struct HeatSink__SubsystemResource: public HeatableSubsystem__SubsystemResource { // // WIRE-VERIFIED (raw-stream dump): the roster index of the sink this // one conducts its heat into -- the weapons/equipment link to the // Condenser bank (slots 4-9), the Condensers to the central HeatSink. // This was THE missing ancestry int that shifted every descendant // resource block +1 (PoweredSubsystem voltageSourceIndex, the MechWeapon // block, ...). // int linkedSinkIndex; }; //########################################################################### //############################## HeatSink ############################### //########################################################################### //########################################################################### //################### HeatWatcher Model Resource ####################### //########################################################################### struct HeatWatcher__SubsystemResource: public MechSubsystem__SubsystemResource { int watchedSubsystem; Scalar degradationTemperature; Scalar failureTemperature; }; //########################################################################### //############################# HeatWatcher ############################# //########################################################################### // // Watches another subsystem's temperature and drives a 3-level alarm. A // sibling branch to HeatableSubsystem (derives straight from MechSubsystem); // PowerWatcher and the Torso/HUD/Gyroscope leaves descend from it. // class HeatWatcher: public MechSubsystem { public: static Derivation ClassDerivations; static SharedData DefaultData; static Logical TestClass(Mech &); Logical TestInstance() const; void ResetToInitialState(Logical powered); // // Respawn restore: base heal + the thermal state back to spawn // (temperature/coolant via ResetToInitialState). // virtual void DeathReset(Logical full_reset); void WatchSimulation(Scalar time_slice); public: typedef HeatWatcher__SubsystemResource SubsystemResource; HeatWatcher( Mech *owner, int subsystem_ID, SubsystemResource *subsystem_resource, SharedData &shared_data = DefaultData ); ~HeatWatcher(); protected: SubsystemConnection watchedLink; int watchedSubsystem; Scalar degradationTemperature; Scalar failureTemperature; AlarmIndicator heatAlarm; }; class HeatSink: public HeatableSubsystem { friend class Condenser; friend class Reservoir; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // 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 // // Authored watcher, bound on Avionics / every Condenser / // every Generator / Myomers / every weapon -- i.e. the whole // heat-bearing family, which is why it belongs on HeatSink. // Adding it here pushes PoweredSubsystem::NextAttributeID from // 0x0F to 0x10, so MechWeapon drops one bridging pad to keep // PercentDone on its binary-pinned 0x12 (see MECHWEAP.CPP). // ReportLeakAttributeID, // 0x0A NextAttributeID // 0x0A }; static const IndexEntry AttributePointers[]; static AttributeIndexSet AttributeIndex; static Derivation ClassDerivations; static SharedData DefaultData; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Test Class Support // public: static Logical TestClass(Mech &); Logical TestInstance() const; void ResetToInitialState(Logical powered); // // Respawn restore: base heal + the thermal state back to spawn. // virtual void DeathReset(Logical full_reset); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Messaging Support. The cockpit cooling toggle rides the first // subsystem-family message slot (id 3 = Receiver::NextMessageID); the // per-class id-4 slot is claimed by each subclass (Condenser MoveValve, // Reservoir InjectCoolant, ...). // public: enum { ToggleCoolingMessageID = Receiver::NextMessageID, // 3 NextMessageID // 4 }; static const HandlerEntry MessageHandlerEntries[]; static MessageHandlerSet MessageHandlers; void ToggleCoolingMessageHandler(ReceiverDataMessageOf *message); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Heat state (carried in heatAlarm, 3 levels). The thresholds are the // authored degradation/failure temperatures. // public: enum { NormalHeat = 0, DegradationHeat, FailureHeat, HeatStateCount }; unsigned GetHeatState() { Check(this); return heatAlarm.GetLevel(); } Scalar CurrentTemperatureOf() { Check(this); return currentTemperature; } Scalar StartingTemperatureOf() { Check(this); return startingTemperature; } void AddPendingHeat(Scalar heat) { Check(this); pendingHeat += heat; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Simulation Support // public: typedef void (HeatSink::*Performance)(Scalar time_slice); void SetPerformance(Performance performance) { Check(this); activePerformance = (Simulation::Performance)performance; } // // The heat-model master switch (binary FUN_004ad7d4): the owning // BTPlayer's heatModelOn experience flag, reached through the mech's // playerLink. ON only for veteran / expert; a NULL player reads ON. // Logical HeatModelActive(); void HeatSinkSimulation(Scalar time_slice); void UpdateHeatLoad(); void ConductHeat(Scalar time_slice); Scalar ComputeHeatFlow(HeatSink *other, Scalar time_slice); void UpdateCoolant(Scalar time_slice); void BalanceCoolant(Scalar time_slice); virtual Scalar DrawCoolant(Scalar requested); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Construction and Destruction // public: typedef HeatSink__SubsystemResource SubsystemResource; HeatSink( Mech *owner, int subsystem_ID, SubsystemResource *subsystem_resource, SharedData &shared_data = DefaultData ); ~HeatSink(); // // The cockpit's cooling-loop lamp needs both of these from BT_L4: // whether this sink has coolant, and which sink it is plumbed to // (the loop master, whose Condenser number IS the lamp's frame). // int GetCoolantAvailable() const { Check(this); return coolantAvailable; } Subsystem * ResolveCoolingMaster() { Check(this); return linkedSinks.Resolve(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Local Data // protected: Scalar coolantEfficiency; Scalar thermalCapacity; Scalar coolantLevel; Scalar coolantDraw; int coolantAvailable; int coolantActive; Scalar startingTemperature; Scalar thermalConductance; Scalar filterDecay; Scalar thermalMass; Scalar heatEnergy; Scalar coolantFlowScale; Scalar massScale; Scalar pendingHeat; Scalar radiatedHeat; SubsystemConnection linkedSinks; AlarmIndicator heatAlarm; AverageOf heatFilter; // // STAGED, appended last: no leak-reporting model yet. A Scalar // because the audio watcher families instantiate Motion / Hinge / // Scalar / StateIndicator, and this is not a *State name. // Scalar reportLeak; }; //########################################################################### //#################### Condenser Model Resource ######################## //########################################################################### struct Condenser__SubsystemResource: public HeatSink__SubsystemResource { Scalar refrigerationFactor; }; //########################################################################### //############################## Condenser ############################# //########################################################################### // // A HeatSink with active refrigeration (a coolant loop's condenser). // class Condenser: public HeatSink { public: //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Attribute Support (the valve slider on the Eng page). // public: enum { ValveSettingAttributeID = HeatSink::NextAttributeID, // 0x0A CondenserStateAttributeID, // authored watcher: CondenserN/CondenserState NextAttributeID }; static const IndexEntry AttributePointers[]; static AttributeIndexSet AttributeIndex; static Derivation ClassDerivations; static SharedData DefaultData; static Logical TestClass(Mech &); Logical TestInstance() const; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Messaging Support: the cockpit condenser-valve button (binary handler // table @0x50E52C, exactly one entry -- id 4, "MoveValve"). Each press // cycles this condenser's valve 1 -> 5 -> 50 -> 0 -> 1 and recomputes every // condenser's coolant flow share (valve / sum-of-valves). // public: enum { MoveValveMessageID = HeatSink::NextMessageID, // 4 NextMessageID }; static const HandlerEntry MessageHandlerEntries[]; static MessageHandlerSet MessageHandlers; void MoveValveMessageHandler(ReceiverDataMessageOf *message); // // Recompute every condenser's coolantFlowScale as its share of the // total valve opening (called by MoveValve, and once at mech spawn so // the initial all-1 valves yield equal shares). // static void RecomputeValves(Entity *owner_mech); public: typedef Condenser__SubsystemResource SubsystemResource; Condenser( Mech *owner, int subsystem_ID, SubsystemResource *subsystem_resource, SharedData &shared_data = DefaultData ); ~Condenser(); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Per-frame refrigeration (binary @4ae4d8): the condenser actively pumps // heat toward the central bank -- massScale is recomputed each frame as // (1 - own damage) * refrigerationFactor, clamped >= 1, so an undamaged // condenser behaves refrigerationFactor-times "hotter" in the conduction // exchange and chills itself below ambient (which is what cools the // weapons equalizing against it). Damage degrades it toward a plain sink. // public: typedef void (Condenser::*Performance)(Scalar time_slice); void SetPerformance(Performance performance) { Check(this); activePerformance = (Simulation::Performance)performance; } void RefrigerationSimulation(Scalar time_slice); // // The loop number (1..6) -- the cockpit lamp's image-strip frame. // int GetCondenserNumber() const { Check(this); return condenserNumber; } protected: int valveState; int condenserNumber; Scalar refrigerationFactor; // // APPENDED LAST ON PURPOSE. This class has offset-sensitive // readers -- condenserNumber is reached as master+0x1d4 -- so a new // member goes at the END of the CLASS (never into the resource // struct above, which is a wire format). // // // MUST BE A StateIndicator. The authored watcher for a *State // name is an AudioStateWatcher, which is AudioWatcherOf // and whose ctor immediately runs // Cast_Object(StateIndicator*, attributePointer)->AddAudioWatcher(this) // (AUDWTHR.CPP:891). Point it at a plain int and that call goes // through garbage -- the pod dies on the extender. // StateIndicator condenserState; // authored watcher }; //########################################################################### //##################### AggregateHeatSink Resource ###################### //########################################################################### struct AggregateHeatSink__SubsystemResource: public HeatSink__SubsystemResource { int heatSinkCount; }; //########################################################################### //######################### AggregateHeatSink ########################### //########################################################################### // // The mech's heat-sink BANK (binary classID 0x0BBE -- the value our VDATA // enum names HeatSinkClassID; there is no streamed plain-HeatSink class). // Holds the aggregate heat-sink count and the ambient-temperature setpoint, // and runs the AMBIENT RADIATOR -- the only place heat ever LEAVES the mech. // class AggregateHeatSink: public HeatSink { public: static Derivation ClassDerivations; static SharedData DefaultData; static Logical TestClass(Mech &); Logical TestInstance() const; public: typedef AggregateHeatSink__SubsystemResource SubsystemResource; AggregateHeatSink( Mech *owner, int subsystem_ID, SubsystemResource *subsystem_resource, SharedData &shared_data = DefaultData ); ~AggregateHeatSink(); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // The radiator (binary @4ae73c) -- replaces the base HeatSinkSimulation on // the bank: absorb, then relax toward the ambient setpoint (the heat EXIT), // then top the bank's coolant up from the attached store. // public: typedef void (AggregateHeatSink::*Performance)(Scalar time_slice); void SetPerformance(Performance performance) { Check(this); activePerformance = (Simulation::Performance)performance; } void RadiatorSimulation(Scalar time_slice); int GetHeatSinkCount() const { Check(this); return heatSinkCount; } protected: int heatSinkCount; Scalar ambientTemperature; }; #endif