//===========================================================================//
// File:	powersub.hpp                                                        //
// Project: BattleTech           Brick: Entity Manager                        //
// Contents: Powered (electrically driven) subsystems -- voltage routing,     //
//           generators, myomer drive and the low-voltage watchdog            //
//---------------------------------------------------------------------------//
//   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, module cluster
// @0x4b02f0 .. @0x4b2678).  The single tagged function is the PoweredSubsystem
// constructor @004b0f74 (file=bt/powersub.cpp); the surrounding cluster was
// recovered by following the call graph, the class vtables and the
// "d:\tesla_bt\bt\POWERSUB.CPP" assertion strings (@0050f72d / @0050f780).
// See powersub.cpp for per-method @ADDR evidence.
//
// Method names and member names follow the embedded string pool
// (PoweredSubsystem, Generator, Myomers, PowerWatcher, the *ClassID enum,
// "VoltageSource", "ThermalResistivityCoefficient", "RatedVoltage", ...) and
// the sibling heat-subsystem source (heat.hpp / heat.cpp); the remainder is
// inferred from usage and flagged where uncertain.  Field offsets noted in
// comments are the byte offsets observed in the decompiled object
// (e.g. "@0x1dc" == this[0x77]).
//
// This file derives from the heat subsystems: a PoweredSubsystem IS-A
// HeatSink (it dissipates heat while it consumes power), so the thermal
// machinery in heat.hpp/heat.cpp is the immediate base for everything here.
//

#if !defined(POWERSUB_HPP)
#	define POWERSUB_HPP

#if !defined(HEAT_HPP)
#	include <heat.hpp>
#endif
#if !defined(HEATFAMILY_RESLICE_HPP)
#	include <heatfamily_reslice.hpp>	// HeatWatcher -- PowerWatcher's real base (RESOURCE_AUDIT.md B)
#endif

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

//###########################################################################
//#########################   PoweredSubsystem   ############################
//###########################################################################
//
//	Base class for any subsystem that is driven from a voltage source (a
//	Generator segment elsewhere in the mech).  It tracks a startup timer, an
//	electrical state machine (Starting -> Ready, with NoVoltage / Shorted /
//	GeneratorOff fault states) and which generator currently powers it.
//
//	vtable @0050f9d8, ctor @004b0f74, dtor @004b115c.
//	RegisteredClass::PoweredSubsystemClassID == 0x0bc2 (resource +0x20),
//	streamed model size 0x190 (resource +0x24).
//
	class PoweredSubsystem:
		public HeatSink
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data Support
	//
	public:
		static Derivation *GetClassDerivations();		// @0050f4bc
		static SharedData DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Electrical state machine (electricalStateAlarm @0x264, level @0x278,
	// reported by PrintState @004b1224).
	//
	public:
		enum ElectricalState {
			Starting		= 0,	// powering up; startTimer counting toward startTime
			NoVoltage		= 1,	// voltage source missing / not resolvable
			Shorted			= 2,	// source state == 3 (shorted)
			GeneratorOff	= 3,	// source state == 1 or 4 (off / not ready)
			Ready			= 4		// powered and operating
		};

		// Three-level connection-mode indicator (modeAlarm @0x2b8, level @0x2cc).
		// Drives the auto-reconnect search in the simulation.
		enum ConnectMode {
			ManualConnect	= 0,
			Connected		= 1,	// ctor default
			AutoConnect		= 2		// scan all GeneratorClassID segments and attach
		};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Attribute Support  (gauge data-binding wave)
	//
	// The engineering subsystem clusters bind PoweredSubsystem/InputVoltage --
	// the power-source gate that decides whether the power-lamp / generator-
	// voltage-bar / state-lamp sub-branch is even constructed (SubsystemCluster
	// ctor skips it when InputVoltage resolves NULL).  Published on top of
	// HeatSink's dense index; Sensor/Myomers/MechWeapon chain to GetAttributeIndex()
	// below and inherit it (so InputVoltage + the HeatSink temps reach every
	// powered/weapon subsystem's cluster).
	//
	public:
		enum {
			InputVoltageAttributeID = HeatSink::NextAttributeID,
			NextAttributeID
		};
	private:
		static const IndexEntry AttributePointers[];
	public:
		static AttributeIndexSet& GetAttributeIndex();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Streamed model resource.
	//
	// Extends the HeatableSubsystem/HeatSink resource record.  The
	// power-specific fields begin at +0xFC (see CreateStreamedSubsystem
	// @004b13ac).
	//
		struct PoweredSubsystem__SubsystemResource:
			public HeatSink::SubsystemResource
		{
			int		voltageSourceIndex;			// +0xFC  "VoltageSource"  (segment index, +2 bias; -1 == unset)
			Scalar	thermalResistivityCoefficient;	// +0x100 "ThermalResistivityCoefficient"
			int		auxScreenNumber;			// +0x104 "AuxScreenNumber"
			int		auxScreenPlacement;			// +0x108 "AuxScreenPlacement" (-1 == unset)
			char	auxScreenLabel[64];			// +0x10C "AuxScreenLabel"
			char	engScreenLabel[64];			// +0x14C "EngScreenLabel"
			Scalar	startTime;					// +0x18C "StartTime"
		};
		typedef PoweredSubsystem__SubsystemResource SubsystemResource;

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

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction and Destruction
	//
	public:
		PoweredSubsystem(
			Mech				*owner,
			int					subsystem_ID,
			SubsystemResource	*subsystem_resource,
			SharedData			&shared_data = DefaultData
		);											// @004b0f74
		~PoweredSubsystem();						// @004b115c

		static int
			CreateStreamedSubsystem(				// @004b13ac
				NotationFile				*model_file,
				const char					*model_name,
				const char					*subsystem_name,
				SubsystemResource			*subsystem_resource,
				NotationFile				*subsystem_file,
				const ResourceDirectories	*directories,
				int							passes = 1
			);

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

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

		void
			PoweredSubsystemSimulation(Scalar time_slice);	// @004b0bd0 (Performance, PTR @0050f5b0)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Subsystem virtual overrides (slots on vtable @0050f9d8)
	//
	public:
		void
			ResetToInitialState(Logical powered = True);	// slot 10, @004b0e6c
		Logical
			HandleMessage(int message);						// slot  9, @004b0efc
		LWord
			GetStatusFlags();								// slot 12, @004b0f48
		void
			PrintState();									// slot 13, @004b1224

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Voltage-source linkage helpers
	//
	public:
		int
			AttachToVoltageSource(Subsystem *source);		// @004b0dd8 (returns tap index, -1 on fail)
		void
			DetachFromVoltageSource();						// @004b0e30
		Logical
			IsSourceShorted(Subsystem *source = 0);			// @004b0b5c

		// @004b11bc -- on a short event drive the live source's alarm to shorted.
		void
			ForceShortRecovery();

		// virtual "has usable voltage" query (vtable+0x40).
		virtual Logical
			HasVoltage();

		// resolve our powering source (used by PowerWatcher).
		Subsystem*
			ResolveVoltageSource()		{ return voltageSource.Resolve(); }

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Local data for the powered-subsystem class.
	// Offsets are byte offsets into the shipped object.
	//
	public:
		SubsystemConnection	voltageSource;			// @0x1D0  0xC reference to the powering Generator segment
		int				auxScreenNumber;			// @0x1DC  resource +0x104
		int				auxScreenPlacement;			// @0x1E0  resource +0x108
		char			auxScreenLabel[64];			// @0x1E4  resource +0x10C
		char			engScreenLabel[64];			// @0x224  resource +0x14C
		// The binary alarms are 0x54-byte AlarmIndicators (ctor @004b0f74:
		// FUN_0041b9ec(param_1+0x99,5) @0x264 and (param_1+0xae,3) @0x2B8; level @+0x14
		// == 0x278/0x2CC).  Now byte-exact via GaugeAlarm54 -- part of the subsystem-tree
		// alarm unification (the whole PoweredSubsystem subtree byte-exacted together).
		GaugeAlarm54	electricalStateAlarm;		// @0x264  5-level (ElectricalState); level @0x278 -> ends 0x2B8
		GaugeAlarm54	modeAlarm;					// @0x2B8  3-level (ConnectMode);     level @0x2CC -> ends 0x30C
		Scalar			thermalResistivityCoefficient;	// @0x30C  resource +0x100
		Scalar			voltageScale;				// @0x310  init 1.0f
		Scalar			startTime;					// @0x314  resource +0x18C
		Scalar			startTimer;					// @0x318  init = startTime, counts up while Starting -> ends 0x31C

		friend struct PoweredSubsystemLayoutCheck;
	};

	struct PoweredSubsystemLayoutCheck {
		static_assert(offsetof(PoweredSubsystem, voltageSource)                 == 0x1D0, "PoweredSubsystem::voltageSource @0x1D0");
		static_assert(offsetof(PoweredSubsystem, auxScreenNumber)              == 0x1DC, "PoweredSubsystem::auxScreenNumber @0x1DC (res +0x104)");
		static_assert(offsetof(PoweredSubsystem, auxScreenLabel)               == 0x1E4, "PoweredSubsystem::auxScreenLabel @0x1E4");
		static_assert(offsetof(PoweredSubsystem, engScreenLabel)               == 0x224, "PoweredSubsystem::engScreenLabel @0x224");
		static_assert(offsetof(PoweredSubsystem, electricalStateAlarm)         == 0x264, "PoweredSubsystem::electricalStateAlarm @0x264");
		static_assert(offsetof(PoweredSubsystem, modeAlarm)                    == 0x2B8, "PoweredSubsystem::modeAlarm @0x2B8");
		static_assert(offsetof(PoweredSubsystem, thermalResistivityCoefficient)== 0x30C, "PoweredSubsystem::thermalResistivityCoefficient @0x30C");
		static_assert(sizeof(PoweredSubsystem)                                 == 0x31C, "sizeof(PoweredSubsystem) 0x31C");
	};

//###########################################################################
//#############################   Myomers   #################################
//###########################################################################
//
//	The mech's artificial-muscle bundle: a PoweredSubsystem whose output
//	voltage (and hence the actuator drive available to the locomotion model)
//	tracks the powering generator and is throttled back by heat damage.
//
//	vtable @0050fb0c, ctor @004b1d18, dtor @004b1d90, shared data @0050fa1c.
//	RegisteredClass::MyomersClassID == 0x0bc3.
//
	class Myomers:
		public PoweredSubsystem
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data Support
	//
	public:
		static Derivation *GetClassDerivations();		// @0050fa2c
		static SharedData DefaultData;			// @0050fa1c

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

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

		Myomers(
			Mech				*owner,
			int					subsystem_ID,
			SubsystemResource	*subsystem_resource,
			SharedData			&shared_data = DefaultData
		);											// @004b1d18
		~Myomers();								// @004b1d90

		static int
			CreateStreamedSubsystem(				// @004b1dcc
				NotationFile				*model_file,
				const char					*model_name,
				const char					*subsystem_name,
				SubsystemResource			*subsystem_resource,
				NotationFile				*subsystem_file,
				const ResourceDirectories	*directories,
				int							passes = 1
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Simulation / overrides
	//
	public:
		void
			MyomersSimulation(Scalar time_slice);			// @004b1c4c (Performance, PTR @0050fa94)
		void
			ResetToInitialState(Logical powered = True);	// slot 10, @004b1c18
		Logical
			HandleMessage(int message);						// slot  9, @004b1bfc (-> PoweredSubsystem)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Local data.
	//
	protected:
		Scalar	outputVoltage;		// @0x31C  init 1.0f -- drive available to the locomotion model
		int		powered;			// @0x320  init 0    -- True while the generator is on-line
		int		voltageAvailable;	// @0x324  init 0    -- True while electricalState == Ready
	};

//###########################################################################
//#############################   Generator   ##############################
//###########################################################################
//
//	A voltage SOURCE.  Sibling of PoweredSubsystem (also a HeatSink, not a
//	PoweredSubsystem): a generator spins up over StartTime, can be toggled
//	on/off, and recovers from a short over ShortRecoveryTime.  PoweredSubsystem
//	segments resolve their "VoltageSource" name to one of these.
//
//	vtable @0050fd74, ctor @004b225c, dtor @004b237c, shared data @0050fb50.
//	RegisteredClass::GeneratorClassID == 0x0bc1 (matched by the auto-connect
//	scan in PoweredSubsystemSimulation).
//
	class Generator:
		public HeatSink
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data Support
	//
	public:
		static Derivation *GetClassDerivations();		// @0050fb50 region
		static Derivation ClassDerivations;				// @0050f4bc datum (IsDerivedFrom operand)
		static SharedData DefaultData;

		void	ForceShortRecovery();					// @004b11bc (short-event recovery)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Attribute Support  (gauge data-binding wave)
	//
	// The GeneratorCluster (engineering-screen buttons 9-12) binds Generator/
	// OutputVoltage for its 0..12000 voltage bar.  The temps (CurrentTemperature/
	// DegradationTemperature/FailureTemperature/CoolantMassLeakRate) already reach
	// it via the inherited HeatSink table; OutputVoltage is a real Generator member
	// (@0x1DC, NOT the Emitter alias).  Dense from HeatSink::NextAttributeID.
	//
	public:
		enum {
			OutputVoltageAttributeID = HeatSink::NextAttributeID,
			NextAttributeID
		};
	private:
		static const IndexEntry AttributePointers[];
	public:
		static AttributeIndexSet& GetAttributeIndex();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Generator state machine (stateAlarm @0x1FC, level @0x210, 5 levels).
	// Names best-effort from the embedded "GeneratorState" / message strings.
	//
	public:
		enum GeneratorState {
			GeneratorStarting	= 0,	// spinning up; startTimer -> startTime
			GeneratorIdle		= 1,	// off / no output
			GeneratorReady		= 2,	// producing RatedVoltage
			GeneratorShorted	= 3,	// shorted; shortTimer -> shortRecoveryTime
			GeneratorRecovered	= 4		// TODO: confirm 4th/5th level names
		};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Streamed model resource (power-source fields begin at +0xFC).
	//
		struct Generator__SubsystemResource:
			public HeatSink::SubsystemResource
		{
			Scalar	ratedVoltage;		// +0xFC  "RatedVoltage"
			int		maxTapCount;		// +0x100 "MaxTapCount" (max simultaneous loads, -1 == unset)
			Scalar	startTime;			// +0x104 "StartTime"
			Scalar	shortRecoveryTime;	// +0x108 "ShortRecoveryTime"
		};
		typedef Generator__SubsystemResource SubsystemResource;

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

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction and Destruction
	//
	public:
		Generator(
			Mech				*owner,
			int					subsystem_ID,
			SubsystemResource	*subsystem_resource,
			SharedData			&shared_data = DefaultData
		);											// @004b225c
		~Generator();								// @004b237c

		static int
			CreateStreamedSubsystem(				// @004b23bc
				NotationFile				*model_file,
				const char					*model_name,
				const char					*subsystem_name,
				SubsystemResource			*subsystem_resource,
				NotationFile				*subsystem_file,
				const ResourceDirectories	*directories,
				int							passes = 1
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Simulation / overrides
	//
	public:
		typedef void
			(Generator::*Performance)(Scalar time_slice);

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

		void
			GeneratorSimulation(Scalar time_slice);			// @004b1f7c (Performance, PTR @0050fc14)
		Logical
			HandleMessage(int message);						// slot  9, @004b21d0
		void
			ResetToInitialState(Logical powered = True);							// slot 10 -- recovered verbatim from GNRATOR.TCP

		// Source-state queries used by PoweredSubsystem / PowerWatcher.
		int
			GeneratorStateOf()			{ return stateAlarm.GetLevel(); }	// source+0x210
		Scalar
			MeasuredVoltage()			{ return outputVoltage; }			// source+0x1dc
		// The generator's "short" state is its stateAlarm level (GeneratorShorted==3) +
		// outputVoltage (IsSourceShorted @004b0b5c reads 0x210/0x1dc).  There is NO
		// separate shortFlag field on the Generator (it ends at 0x250 == stateAlarm end).
		// SourceLevel == linked master sink's stored heat energy (this[0x38]+0x158).
		Scalar
			SourceLevel()
			{
				HeatSink *master = (HeatSink *)linkedSinks.Resolve();
				return (master != 0) ? master->heatEnergy : (Scalar)0.0f;
			}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Local data.
	//
	public:
		Scalar	percentVoltageAvailable;	// @0x1D0  init 1.0f -- outputVoltage / ratedVoltage (clamped 0..1)
		int		generatorOn;				// @0x1D4  init 1
		Scalar	ratedVoltage;				// @0x1D8  resource +0xFC
		Scalar	outputVoltage;				// @0x1DC  init = ratedVoltage
		int		generatorNumber;			// @0x1E0  derived from segment-name suffix ('A' -> 1 ...)
		int		maxTapCount;				// @0x1E4  resource +0x100
		int		currentTapCount;			// @0x1E8  init 0 -- attached loads
		Scalar	startTime;					// @0x1EC  resource +0x104
		Scalar	startTimer;					// @0x1F0  init = startTime
		Scalar	shortRecoveryTime;			// @0x1F4  resource +0x108
		Scalar	shortTimer;					// @0x1F8  init = shortRecoveryTime
		GaugeAlarm54	stateAlarm;			// @0x1FC  0x54 alarm, 5-level (GeneratorState); level @0x210 -> ends 0x250
		// stateAlarm is the LAST field (ctor @004b225c: FUN_0041b9ec(param_1+0x7f,5) at 0x1FC).
		// The old shortFlag@0x25C was a phantom: HandleMessage @004b0efc reads *(owner+0x190)+0x25c
		// (the message-manager's short-event flag), NOT a Generator field -- Generator ends at 0x250.

		friend struct GeneratorLayoutCheck;
	};

	struct GeneratorLayoutCheck {
		static_assert(offsetof(Generator, percentVoltageAvailable) == 0x1D0, "Generator::percentVoltageAvailable @0x1D0 (word 0x74)");
		static_assert(offsetof(Generator, outputVoltage)           == 0x1DC, "Generator::outputVoltage @0x1DC (word 0x77)");
		static_assert(offsetof(Generator, stateAlarm)              == 0x1FC, "Generator::stateAlarm @0x1FC (word 0x7f)");
		static_assert(sizeof(Generator)                            == 0x250, "sizeof(Generator) 0x250 (factory alloc, stateAlarm is last)");
	};

//###########################################################################
//############################   PowerWatcher   #############################
//###########################################################################
//
//	A lightweight monitor subsystem that watches its voltage source and raises
//	a "BadVoltage" / self-test alarm when the available voltage drops below
//	MinVoltagePercent of rated.  Used to drive a cockpit warning gauge.
//	BEST-EFFORT: its base lies just below the captured window (base ctor
//	@004aeb40, in the heat/sensor cluster); only the power-specific override
//	bodies were recovered.
//
//	vtable @0050f99c, ctor @004b18a4, dtor @004b1930.
//	RegisteredClass classID 0x0bd5 (resource +0x20), model size 0xF4.
//
	class PowerWatcher:
		public HeatWatcher			// RESOURCE-LAYOUT FIX (RESOURCE_AUDIT.md root cause B): real
									// base is HeatWatcher (ctor chains @004aeb40, parse @004aec54);
									// was approximated as HeatableSubsystem, which sized the resource
									// at 0xFC and slid minVoltagePercent to 0xFC instead of 0xF0.
	{
	public:
		static Derivation *GetClassDerivations();		// @0050f5cc
		static SharedData DefaultData;

		struct PowerWatcher__SubsystemResource:
			public HeatWatcher::SubsystemResource	// HeatWatcher record ends 0xF0
		{
			Scalar	minVoltagePercent;	// +0xF0  "MinVoltagePercent"
		};									// ends 0xF4
		typedef PowerWatcher__SubsystemResource SubsystemResource;
		static_assert(offsetof(PowerWatcher__SubsystemResource, minVoltagePercent) == 0xF0, "PowerWatcher minVoltagePercent must be at 0xF0");
		static_assert(sizeof(PowerWatcher__SubsystemResource) == 0xF4, "PowerWatcher record must end at 0xF4");

		typedef void
			(PowerWatcher::*Performance)(Scalar time_slice);
		void
			SetPerformance(Performance performance)
			{
				Check(this);
				activePerformance = (Simulation::Performance)performance;
			}

		static Logical	TestClass(Mech&);
		Logical			TestInstance() const;	// @004b1970

		PowerWatcher(
			Mech				*owner,
			int					subsystem_ID,
			SubsystemResource	*subsystem_resource,
			SharedData			&shared_data = DefaultData
		);											// @004b18a4
		~PowerWatcher();							// @004b1930

		static int
			CreateStreamedSubsystem(				// @004b198c
				NotationFile				*model_file,
				const char					*model_name,
				const char					*subsystem_name,
				SubsystemResource			*subsystem_resource,
				NotationFile				*subsystem_file,
				const ResourceDirectories	*directories,
				int							passes = 1
			);

		Logical
			HandleMessage(int message);			// @004b179c (chains to base @004aea84)
		void
			Simulation(Scalar time_slice);		// @004b1804 (chains to base @004aea9c)

	public:
		// BASE-CHAIN RE-BASE: the shadow watchedLink was DELETED -- the binary has a
		// SINGLE watchedLink, inherited from HeatWatcher@0x114.  PowerWatcher own
		// fields now follow HeatWatcher (which ends 0x180): minVoltage@0x180, then a
		// 0x54-byte WatcherGaugeAlarm@0x184 that ends exactly at 0x1D8 = Torso start.
		Scalar	minVoltage;		// @0x180  init = MinVoltageScale * resource->minVoltagePercent
		WatcherGaugeAlarm	watchdogAlarm;	// @0x184 (0x54) 5-level self-test  -> ends 0x1D8
	};
	// BASE-CHAIN RE-BASE locks: PowerWatcher own = minVoltage@0x180 + watchdogAlarm@0x184;
	// sizeof==0x1D8 proves the shared Watcher base ends exactly where Torso own-fields
	// (currentTwist@0x1D8, the gyro cross-link target) begin.
	static_assert(offsetof(PowerWatcher, minVoltage)   == 0x180, "PowerWatcher minVoltage must be at 0x180");
	static_assert(offsetof(PowerWatcher, watchdogAlarm)== 0x184, "PowerWatcher watchdogAlarm must be at 0x184");
	static_assert(sizeof(PowerWatcher)                 == 0x1D8, "PowerWatcher must end at 0x1D8 (== Torso currentTwist)");

//###########################################################################
//#######################   VoltageSourceConnection   #######################
//###########################################################################
//
//	The small ref-counted handle that links a PoweredSubsystem to its powering
//	Generator segment.  Functionally a specialised SharedData (cf. HeatSink's
//	linkedSinks).  vtable @0050f994, ctor @004b1bb1, dtor @004b1bd0.
//
	class VoltageSourceConnection:
		public SubsystemConnection
	{
	public:
		VoltageSourceConnection(int initial = 0);	// @004b1bb1
		~VoltageSourceConnection();					// @004b1bd0
	};

//###########################################################################
//#######   Cross-family helpers provided by the Mech game layer   ##########
//###########################################################################
//
// These reach into the owning Mech (subsystem message manager / power bus /
// myomer back-pointer).  They are NOT part of the heat/power family -- they
// are declared here (definitions live in the mech family) so the recovered
// power bodies link against them without this family editing mech.hpp.
// See "CROSS-FAMILY NEEDS" in the porting notes.
//
	class SubsystemMessageManager;
	SubsystemMessageManager*	BT_GetMessageManager(Mech *owner);	// *(owner+0x190)
	Logical						BT_IsBusLive(Mech *owner);			// *(*(owner+0x190)+0x274)
	void						BT_ClearMyomers(Mech *owner);		// owner+0x374 = 0

#endif
