//===========================================================================// // 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 // public: Scalar CurrentTemperatureOf() { Check(this); return currentTemperature; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // The coolant-allocation trio (binary heatable +0x1d0/+0x15c/+0x1dc, // ctor @004ae568). PUBLIC: the owning Mech's redistribute walks the // heatable chain and writes all three (the duckCommand precedent). // // coolantPriority the draw-weight rung, born 1; the per-subsystem // priority button (@004ae464, id TBD) steps it 0 -> 1 -> 5 -> // 50 -> 0, and the mech's BalanceCoolant (0x16) resets every // heatable to 1 (the equal split). // coolantShare priority / total, written ONLY by the mech's // RedistributeCoolantShares (@0049f788); the reservoir draw // (@004aefa4) weights each heatable's coolant feed by it. // balanceAlarm 3 levels (binary FUN_0041b9ec(+0x1dc, 3)); the // redistribute blips it -- 2 = share shrank or held, 1 = grew, // then back to 0 -- the cockpit gauge flash. // public: int coolantPriority; Scalar coolantShare; AlarmIndicator balanceAlarm; 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); // // The class-typed Performance shim, the same shape every sibling // subsystem carries (the base takes the Subsystem-typed pointer). // typedef void (HeatWatcher::*Performance)(Scalar time_slice); void SetPerformance(Performance performance) { Check(this); activePerformance = (Simulation::Performance)performance; } 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; public: // // THE WATCH BIND (5.3.132). The streamed `watchedSubsystem` index // has been read since this class landed, but nothing ever turned it // into the link `UpdateWatch` resolves -- so every watcher in the // mech resolved NULL and reported its target dead. The Mech ctor // binds these in a post-walk pass: a watcher can legally watch a // subsystem with a HIGHER roster id, which does not exist yet while // the segment walk is still building, so the bind cannot live in // this ctor. // int WatchedSubsystemIndex() const { Check(this); return watchedSubsystem; } void BindWatchedSubsystem(Subsystem *watched) { Check(this); watchedLink.Add(watched); } protected: 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; } Scalar GetCoolantLevel() const { Check(this); return coolantLevel; } Scalar GetThermalCapacity() const { Check(this); return thermalCapacity; } 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); // // [T3 bridge] Recompute every condenser's coolantFlowScale as its // CONDENSER-relative share of the coolant priorities -- the term the // fight-verified conduction model (5.3.12-15 byte curves) consumes. // Since 5.3.124 the ladder cell is the heatable-base coolantPriority // (binary +0x1d0) and the caller is the tail of the mech's // RedistributeCoolantShares, so valve presses, BalanceCoolant and // spawn/Reset all refresh it through ONE path. Retire onto the // real binary conduction cell once the @004ad6f8/pump reads are // decoded. // 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 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