//===========================================================================//
// File:	heat.hpp                                                            //
// Project: BattleTech           Brick: Entity Manager                        //
// Contents: Heatable subsystems -- temperature model, heat sinks, condenser  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
//  --/--/95  ??   Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//
//
// RECONSTRUCTED from the shipped binary (Ghidra pseudo-C in heat_cluster.c)
// cross-referenced with the surviving HEAT.TCP fragment and the MUNGA
// SUBSYSTM.HPP base interface.  See heat.cpp for per-method @ADDR evidence.
//
// NOTE on the engine base: the WinTesla/MUNGA `Subsystem` (subsystm.h) is a
// thin damageable-simulation base -- it does NOT carry the heat/power virtual
// surface (ResetToInitialState / GetStatusFlags / HandleMessage / PrintState /
// Simulation) that the BT game layer assumed lived on "Subsystem".  Because
// this family OWNS the subsystem base classes that the other BT families
// derive from, that virtual surface is (re)introduced on HeatableSubsystem
// here, and the per-class shared-data boilerplate follows the real engine
// GetClassDerivations()/GetMessageHandlers()/GetAttributeIndex()/StateCount
// idiom (cf. RP/VTVSUB.cpp), not the 3-arg form the raw decomp guessed.
//

#if !defined(HEAT_HPP)
#	define HEAT_HPP

#if !defined(SUBSYSTM_HPP)
#	include <subsystm.hpp>
#endif
#if !defined(MECHSUB_HPP)
#	include <mechsub.hpp>		// MechSubsystem -- HeatableSubsystem's base (re-base)
#endif
#include <affnmtrx.hpp>			// AffineMatrix
#include <average.hpp>			// AverageOf<T>
#include <scalar.hpp>			// Scalar
#include <alarm.hpp>			// GaugeAlarm

#include <string.h>
#include <stdlib.h>

//#####################    Reconstruction type aliases   #######################
#if !defined(BT_RECON_TYPE_ALIASES)
#	define BT_RECON_TYPE_ALIASES
	typedef AffineMatrix		Matrix34;		// 3x4 affine (AFFNMTRX.h)
	typedef GaugeAlarm			AlarmIndicator;	// GAUGALRM.h
	typedef AverageOf<Scalar>	FilteredScalar;	// 15-sample running average (AVERAGE.h)
#endif

// NOTE: `DebugStream` and `endl` (the ReconStream trace artifact) come from the
// shared foundation header (mechrecon.hpp, pulled in via bt.hpp/mech.hpp).

//#####################    Segment flag bits (model resource)  ###############
// The "is this a master / damaged-copy segment" test.  The decomp rendered
// these as owner->GetSegmentFlags() reads; the flags actually live in the
// streamed subsystem resource (subsystemFlags).
#if !defined(BT_SEGMENT_FLAG_BITS)
#	define BT_SEGMENT_FLAG_BITS
	enum {
		SegmentCopyMask		= 0x0C,		// (flags & 0xC): 0 == master, 4 == copy
		MasterHeatSinkFlag	= 0x100		// flags & 0x100 == participates in sim
	};
#endif

//#####################    Forward Class Declarations  #######################
class Mech;

//###########################################################################
//#################   Reconstruction helper value types   ###################
//###########################################################################
//
// The raw decomp leaned on three engine helpers whose real APIs differ from
// what the pseudo-C assumed.  Rather than bend the engine types, the family
// owns small faithful wrappers that expose exactly the surface the recovered
// bodies call.
//

// The binary AlarmIndicator (ctor FUN_0041b9ec) is a 0x54-byte object -- base
// GaugeAlarm + three sub-indicators, level at +0x04.  Every subsystem alarm the
// binary builds via FUN_0041b9ec (heatAlarm, electricalState/mode/state/weapon/
// condenser/reservoir alarms) is this size; modeling it as 8 bytes is what slid the
// whole heat-leaf branch 0x4C short.  Access is ALWAYS through the named API
// (SetLevel/GetLevel), never a raw internal offset, so the interior padding is
// immaterial -- only sizeof must be 0x54.  This is the SAME layout as the Watcher
// branch's WatcherGaugeAlarm (heatfamily_reslice.hpp now typedefs to it), so the
// already-locked Watcher LayoutChecks stay valid.
class GaugeAlarm54
{
public:
	GaugeAlarm54(int levels = 0)	{ levelA = levelB = levels; level = 0; }
	void	Initialize(int levels)	{ levelA = levelB = levels; level = 0; }
	void	SetLevel(int n)			{ level = n; }
	int		GetLevel() const		{ return level; }
	int		Level() const			{ return level; }
	// ReconAlarm/AlarmIndicator API aliases (callers that predate the retype use these):
	void		SetState(unsigned n){ level = (int)n; }
	unsigned	GetState() const	{ return (unsigned)level; }
protected:
	// Interior mirrors FUN_0041b9ec: base GaugeAlarm header (+0x00), three count
	// words at +0x0c/+0x10/+0x14, three sub-indicators at +0x18/+0x2c/+0x40.  The
	// STATUS level the binary reads (HeatSink+0x184 == alarm+0x14, GetStatusFlags
	// @004add30 / PrintState @004ae050) is param_1[5] at +0x14, so `level` lands
	// there -- a raw `subsystem+0x184` read now returns GetLevel().
	char	_hdr[0x0c];				// +0x00  base GaugeAlarm (vtable + FUN_004178cc header)
	int		levelA;					// +0x0c  param_1[3] (level count)
	int		levelB;					// +0x10  param_1[4]
	int		level;					// +0x14  param_1[5] -- the status level (Normal/Deg/Fail)
	char	_tail[0x54 - 0x18];		// +0x18  three sub-indicators to 0x54
};

// The 8-byte modeling type kept ONLY for HUD::statusAlarm (still on the old
// HUDLayoutCheck-locked layout; retyping HUD to 0x54 is a separate follow-up F1).
// All heat/power/weapon alarms now use GaugeAlarm54 above.
class HeatAlarm
{
public:
	HeatAlarm(int levels = 0)		{ levelCount = levels; level = 0; }
	void	Initialize(int levels)	{ levelCount = levels; level = 0; }
	void	SetLevel(int n)			{ level = n; }
	int		GetLevel() const		{ return level; }
	int		Level() const			{ return level; }
protected:
	int		levelCount;
	int		level;
};

// The 15-sample running average behind heatLoad (Initialize/AddSample/Average).
class HeatFilter
{
public:
	void	Initialize(int count, Scalar value)	{ average.SetSize((size_t)count, value); }
	void	AddSample(Scalar v)					{ average.Add(v); }
	Scalar	Average()							{ return average.CalculateAverage(); }
private:
	AverageOf<Scalar>	average;
};

// A ref to a linked subsystem (heat-sink linkage / voltage source / watched
// subsystem).  The original modelled this with a small SharedData-derived
// connection object exposing Add/Resolve/Clear.
// The binary connection is a 0xC-byte SharedData-derived link node (the raw
// resolver FUN_00417ab4 two-level-derefs it: `if(*(p+8)) return *(*(p+8)+8)`),
// NOT a bare 4-byte pointer.  Modeling it as 4 bytes slid every field after an
// embedded connection low -- the direct cause of the heat-leaf 0x4C deficit
// (HeatSink::linkedSinks, PoweredSubsystem::voltageSource).  Named access
// (Add/Resolve/Clear on `linked`) is unchanged and inert in bring-up (no plug
// resolves); the interior _reserved slots reconcile with FUN_00417ab4 when link
// resolution is wired.  Same 0xC layout as heatfamily_reslice.hpp WatchedConnection.
class SubsystemConnection
{
public:
	SubsystemConnection(int = 0)	{ linked = 0; _reserved[0] = _reserved[1] = 0; }
	void		Add(Subsystem *s)	{ linked = s; }
	Subsystem*	Resolve() const		{ return linked; }
	void		Clear()				{ linked = 0; }
protected:
	Subsystem	*linked;			// +0x00
	int			_reserved[2];		// pad to the binary 0xC connection size
};
static_assert(sizeof(SubsystemConnection) == 0x0C, "subsystem connection must be 0xC (SharedData link node)");

// condenserNumber = atoi(lastChar(name)).
inline int
	NameTrailingNumber(const char *name)
	{
		if (name == 0 || *name == '\0')
			return 0;
		return atoi(name + (strlen(name) - 1));
	}

//###########################################################################
//#################   HeatableSubsystem Model Resource   ####################
//###########################################################################
//
// Extends the base damageable Subsystem resource.  The heat-specific fields
// begin at +0xE4 -- i.e. immediately after MechSubsystem__SubsystemResource
// (model size 0xE4), NOT after Subsystem::SubsystemResource (0x30).  The class
// hierarchy is HeatableSubsystem : MechSubsystem : Subsystem, so the resource
// MUST inherit MechSubsystem__SubsystemResource; inheriting Subsystem::
// SubsystemResource directly dropped the 0x30..0xE4 chunk and slid every heat
// field 0xB4 bytes low -- so thermalMass/heatSinkIndex read neighbouring floats
// (heatSinkIndex came back as 10.0f = 1/thermalMass, GetSegment -> OOB -> no link).
//
	struct HeatableSubsystem__SubsystemResource:
		public MechSubsystem__SubsystemResource
	{
		Scalar	startingTemperature;	// +0xE4  "StartingTemperature"
		Scalar	degradationTemperature;	// +0xE8  "DegradationTemperature"
		Scalar	failureTemperature;		// +0xEC  "FailureTemperature"
		Scalar	thermalConductance;		// +0xF0  "ThermalConductance"
		Scalar	thermalMass;			// +0xF4  "ThermalMass"
		int		heatSinkIndex;			// +0xF8  "HeatSink" (segment index, +2 bias)
	};

//###########################################################################
//#################   Condenser Model Resource   ############################
//###########################################################################
//
// Condenser adds the refrigeration factor (+0xFC).  Declared here because the
// Condenser class itself is declared in this header (its bodies are split
// between heat.cpp [best-effort] and heatfamily_reslice.cpp [completion]).
//
	struct Condenser__SubsystemResource:
		public HeatableSubsystem__SubsystemResource
	{
		Scalar	refrigerationFactor;	// +0xFC  "RefrigerationFactor"  (sentinel -1.0f)
	};

//###########################################################################
//#########################   HeatableSubsystem   ###########################
//###########################################################################
//
//	Abstract base for any subsystem that participates in the thermal model and
//	the shared "damageable subsystem" virtual surface the BT families expect.
//	(vtable @0050e210, destructor @004ac868.)
//
	class HeatableSubsystem:
		public MechSubsystem		// was: public Subsystem.  mechsub.hpp's MechSubsystem
									// and this are overlapping reconstructions of the SAME
									// binary cluster (vtable 0050e210); re-base + de-shadow so
									// owner/simulationState/damageZone/the virtual surface come
									// from the one real base, not uninitialised duplicates.
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data Support
	//
	public:
		static Derivation *GetClassDerivations();
		static SharedData DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Class Support
	//
	public:
		static Logical
			TestClass(Mech&);
		Logical
			TestInstance() const;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Damageable-subsystem virtual surface (engine Subsystem lacks these; the
	// heat/power families override them).
	//
	public:
		// These OVERRIDE the MechSubsystem base slots (same vtable, matching sigs) --
		// not new/parallel slots.  ResetToInitialState takes (Logical powered) to match.
		virtual void
			ResetToInitialState(Logical powered);
		virtual LWord
			GetStatusFlags();
		virtual Logical
			HandleMessage(int message);
		virtual void
			PrintState();
		virtual void
			Simulation(Scalar time_slice);		// heat per-frame (not a MechSubsystem slot)
		Logical
			IsDamaged()					{ return (simulationState != 0) ? True : False; }

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// 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
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Common subsystem state
	//
	public:
		// owner / simulationState(was "destroyed") / damageZone / hostEntity etc. are
		// INHERITED from MechSubsystem -- re-declaring them shadowed the real base
		// (engine ctor writes the base member; the duplicate is uninitialised + at a
		// wrong compiled offset).  flags/statusFlags -> engine Simulation::simulationFlags;
		// statusBits -> ForceUpdate()/dirty word.  Only the thermal fields are own.
		Scalar	currentTemperature;		// @0x114  HEAT.TCP: init 300.0f
		Scalar	degradationTemperature;	// @0x118
		Scalar	failureTemperature;		// @0x11C
		Scalar	heatLoad;				// @0x120  HEAT.TCP: init 0.0f (filtered load)
	};

//###########################################################################
//#############################   HeatSink   ################################
//###########################################################################
//
//	Active heat sink.  Accumulates heat energy, conducts heat to a linked
//	sink, draws coolant, and raises a degradation/failure alarm.
//	(vtable @0050edc4, ctor @004adda0, dtor @004adfd4.)
//
	class HeatSink:
		public HeatableSubsystem
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data Support
	//
	public:
		static Derivation *GetClassDerivations();
		static SharedData DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Attribute Support -- the cockpit gauge bindings resolved by name through
	// Simulation::GetAttributePointer (see engine GAUGREND.cpp ParseAttribute):
	//   HeatSink/CoolantMass         -> coolantLevel        (@0x12C, live)
	//   HeatSink/CoolantCapacity     -> thermalCapacity     (@0x128)
	//   HeatSink/CurrentTemperature  -> currentTemperature  (@0x114, inherited)
	// Condenser and Reservoir derive from HeatSink and define no override, so
	// their DefaultData resolves Condenser6/CoolantMass/... to this same table.
	// (SimulationState id 1 is preserved by chaining to the parent index.)
	//
	public:
		enum {
			CoolantMassAttributeID = HeatableSubsystem::NextAttributeID,
			CoolantCapacityAttributeID,
			CurrentTemperatureAttributeID,
			// --- dense-append (gauge data-binding wave): the config binds these
			// per condenser/heat-sink (L4GAUGE.CFG GenericHeatGauges1/2 + the Eng
			// clusters).  Ids stay contiguous so AttributeIndexSet::Build has no gap.
			DegradationTemperatureAttributeID,	// @0x118 constant amber warn line
			FailureTemperatureAttributeID,		// @0x11C constant max reference
			NormalizedPressureAttributeID,		// @0x120 heatLoad (smoothed radiated heat)
			DegradationPressureAttributeID,		// @0x124 coolantEfficiency
			CoolantMassLeakRateAttributeID,		// @0x130 coolantDraw (damage-driven leak)
			HeatSinkAttributeID,				// @0x164 linkedSinks (link to master sink)
			ValveSettingAttributeID,			// @0x15C coolantFlowScale (condenser valve slider @2)
			NextAttributeID
		};
	private:
		static const IndexEntry AttributePointers[];
	public:
		static AttributeIndexSet& GetAttributeIndex();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Heat alarm state (this+0x138, reported by PrintState @004ae050)
	//
	public:
		enum HeatState {
			NormalHeat		= 0,
			DegradationHeat	= 1,
			FailureHeat		= 2
		};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Simulation Support
	//
	public:
		typedef void
			(HeatSink::*Performance)(Scalar time_slice);

		void
			SetPerformance(Performance performance)
			{
				Check(this);
				activePerformance = (Simulation::Performance)performance;
			}

		void
			HeatSinkSimulation(Scalar time_slice);		// @004ad924 (Performance)

		// FUN_004ad748 -- the base per-frame step (vtable slot 9), reused by the
		// Condenser / aggregate sink in heatfamily_reslice.cpp.
		void
			HeatSink_Step(Scalar time_slice)			{ HeatSinkSimulation(time_slice); }

		// FUN_004ad7d4 -- entity heat-model active flag.
		Logical
			HeatModelActive()							{ return True; }

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Subsystem virtual overrides (slots on vtable @0050edc4)
	//
	public:
		LWord
			GetStatusFlags();							// slot 12, @004add30
		Logical
			HandleMessage(int message);					// slot  8, @004add6c
		void
			ResetToInitialState(Logical powered);						// slot 10, @004ad760
		void
			PrintState();								// slot 13, @004ae050

		// slot 14 (vtable+0x38): asks the central cooling system for coolant
		// and returns how much was actually supplied (overridden by Reservoir).
		virtual Scalar
			DrawCoolant(Scalar requested);

		// FUN: link another sink/reservoir into this one (Reservoir ctor).
		void
			Attach(HeatSink *other)						{ linkedSinks.Add(other); }

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Class Support
	//
	public:
		static Logical
			TestClass(Mech&);
		Logical
			TestInstance() const;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction and Destruction
	//
	public:
		typedef HeatableSubsystem__SubsystemResource SubsystemResource;

		HeatSink(
			Mech				*owner,
			int					subsystem_ID,
			SubsystemResource	*subsystem_resource,
			SharedData			&shared_data = DefaultData
		);
		~HeatSink();

		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
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Internal model helpers
	//
	public:
		void
			UpdateHeatLoad();							// @004ad7f0
		void
			ClearHeatFilter();							// @004ad884
		void
			ConductHeat(Scalar time_slice);				// @004ad8ac
		Scalar
			ComputeHeatFlow(						 	// @004ad9ec
				HeatSink	*other,
				Scalar		time_slice
			);
		void
			BalanceCoolant(								// @004ada94
				Scalar		time_slice
			);
		void
			UpdateCoolant(Scalar time_slice);			// @004adbf8

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Local data for the heat sink class.
	// Offsets are byte offsets into the shipped object.
	//
	public:
		// degradationTemperature/failureTemperature are BASE fields (HeatableSubsystem
		// @0x118/0x11C, written here by the HeatSink ctor from resource +0xE8/+0xEC) --
		// re-declaring them shadowed the base + slid every own field 8 bytes low.

		// --- coolant model ---  (ctor @004adda0 param_1[0x49..0x4e])
		Scalar	coolantEfficiency;		// @0x124  init 0.5f
		Scalar	thermalCapacity;		// @0x128  init 1.0f  (coolant capacity / divisor)
		Scalar	coolantLevel;			// @0x12C  init = thermalCapacity (1.0f)
		Scalar	coolantDraw;			// @0x130  init 0      (per-frame coolant demand)
		int		coolantAvailable;		// @0x134  init 1      (coolant supply present)
		int		coolantActive;			// @0x138  init 0      (heat-model running flag; GetStatusFlags bit 0x4)

		// --- thermal parameters ---  (ctor param_1[0x4f..0x58])
		Scalar	startingTemperature;	// @0x13C  resource +0xE4 (saved initial temp)
		Scalar	thermalConductance;		// @0x140  resource +0xF0
		HeatFilter	heatFilter;			// @0x144  15-sample running average (12 bytes -> 0x150)
		Scalar	filterDecay;			// @0x150  init 0.4f
		Scalar	thermalMass;			// @0x154  resource +0xF4
		Scalar	heatEnergy;				// @0x158  init = thermalMass * startingTemperature
		Scalar	coolantFlowScale;		// @0x15C  init 1.0f  (== "word57")
		Scalar	massScale;				// @0x160  init 1.0f

		// --- linkage / display ---  (ctor param_1[0x59]=linkedSinks, [0x5c]=heatAlarm)
		SubsystemConnection	linkedSinks;	// @0x164  0xC connection to master/linked heat sink
		GaugeAlarm54		heatAlarm;		// @0x170  0x54 alarm; status level (Normal/Deg/Fail) at +0x14 == subsystem+0x184

		SubsystemResource	*resource;	// @0x1C4  saved resource pointer (ctor param_1[0x71])
		Scalar	pendingHeat;			// @0x1C8  init 0  (heat delta queued for next frame; ctor param_1[0x72])
		Scalar	radiatedHeat;			// @0x1CC  currentTemperature * coolantLevel
		// object ends @0x1D0 (Reservoir/Condenser own fields begin here)

		friend struct HeatSinkLayoutCheck;
	};

	// Byte-exact layout locks against the ctor @004adda0 (int* param_1[N] == byte N*4).
	// Compile-time proof the heat leaf now matches the shipped object; never silently regresses.
	struct HeatSinkLayoutCheck {
		static_assert(offsetof(HeatSink, coolantActive) == 0x138, "HeatSink::coolantActive @0x138 (param_1[0x4e])");
		static_assert(offsetof(HeatSink, heatEnergy)    == 0x158, "HeatSink::heatEnergy @0x158 (param_1[0x56])");
		static_assert(offsetof(HeatSink, linkedSinks)   == 0x164, "HeatSink::linkedSinks @0x164 (param_1[0x59])");
		static_assert(offsetof(HeatSink, heatAlarm)     == 0x170, "HeatSink::heatAlarm @0x170 (param_1[0x5c])");
		static_assert(offsetof(HeatSink, resource)      == 0x1C4, "HeatSink::resource @0x1C4 (param_1[0x71])");
		static_assert(offsetof(HeatSink, pendingHeat)   == 0x1C8, "HeatSink::pendingHeat @0x1C8 (param_1[0x72])");
		static_assert(offsetof(HeatSink, radiatedHeat)  == 0x1CC, "HeatSink::radiatedHeat @0x1CC");
		static_assert(sizeof(HeatSink)                  == 0x1D0, "sizeof(HeatSink) 0x1D0");
	};

//###########################################################################
//#############################   Condenser   ###############################
//###########################################################################
//
//	A HeatSink subclass that models a refrigeration output fighting the master
//	heat sink's stored heat, carries a 3-position valve and a condenser number.
//	(vtable @0050ed88, ctor @4ae568, classID CondenserClassID.)  The full body
//	set lives in heatfamily_reslice.cpp; heat.cpp carries a best-effort stub.
//
	class Condenser:
		public HeatSink
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data Support
	//
	public:
		static Derivation *GetClassDerivations();
		static SharedData DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Class Support
	//
	public:
		static Logical
			TestClass(Mech&);
		Logical
			TestInstance() const;
		void
			ResetToInitialState(Logical powered);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Simulation / overrides
	//
	public:
		void
			RefrigerationSimulation(Scalar time_slice);	// @4ae4d8 (vtable slot 9)
		void
			MoveValve(int message);						// @4ae464 (valve cycle 1->5->50->0; dormant until 0xBD3)
		virtual void
			SetValveSetting(int setting)				{ valveState = setting; }

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction and Destruction
	//
	public:
		typedef Condenser__SubsystemResource SubsystemResource;

		Condenser(
			Mech				*owner,
			int					subsystem_ID,
			SubsystemResource	*subsystem_resource,
			SharedData			&shared_data = DefaultData
		);
		~Condenser();

		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:
		// The per-frame refrigeration output reuses the inherited HeatSink massScale
		// slot (word 0x58 @0x160) -- ctor @004ae568 writes param_1[0x58]=res+0xFC.  It
		// is NOT an own Condenser field (declaring it as one slid valveState off 0x1D0).
		int			valveState;				// @0x1D0 (word 0x74) init 1 (MoveValve 0..2)
		int			condenserNumber;		// @0x1D4 (word 0x75) last digit of name
		Scalar		refrigerationFactor;	// @0x1D8 (word 0x76) from resource +0xFC
		GaugeAlarm54	condenserAlarm;		// @0x1DC (word 0x77) 0x54 alarm, 3 levels -> ends 0x230

		friend struct CondenserLayoutCheck;
	};

	struct CondenserLayoutCheck {
		static_assert(offsetof(Condenser, valveState)    == 0x1D0, "Condenser::valveState @0x1D0 (word 0x74)");
		static_assert(offsetof(Condenser, condenserAlarm)== 0x1DC, "Condenser::condenserAlarm @0x1DC (word 0x77)");
	};

#endif
