//===========================================================================//
// File:	myomers.hpp                                                         //
// Project: BattleTech           Brick: Entity Manager                        //
// Contents: Myomers subsystem -- the mech's artificial-muscle drive.         //
//           Converts available voltage into locomotion "speed effect" and    //
//           the mechanical work that feeds the heat model.                   //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
//  --/--/95  ??   Initial coding.                                           //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//
//
// RECONSTRUCTED from the shipped binary (BTL4OPT.EXE).  NO header survived;
// every declaration below is recovered from the decompiled cluster
// @0x4b8a48 .. @0x4b95b8 (just before MechWeapon @0x4b95ec), the object
// vtable @005117dc, the Myomers attribute IndexEntry table @00511588, and
// the embedded string pool @00511660 ("Myomers", "ToggleSeekVoltage",
// "SpeedEffect", "CurrentSeekVoltageIndex", "RecommendedSeekVoltageIndex",
// "MinSeekVoltageIndex", "MaxSeekVoltageIndex", "SeekVoltage",
// "OutputVoltage", "VelocityEfficiency", "AccelerationEfficiency",
// "SeekVoltageRecommendedIndex").  See myomers.cpp for per-method @ADDR
// evidence.  Field offsets in comments are byte offsets in the shipped
// object (runtime size 0x358, alloc'd by Mech factory case 0xBC6 @part_012).
//
// ===========================================================================
// IMPORTANT CORRECTION (supersedes the earlier powersub.cpp / powersub.hpp
// "Myomers" block):
//
//   powersub.cpp/.hpp described a class it CALLED "Myomers" at vtable
//   @0050fb0c / ctor @004b1d18 / classID 0x0BC3, with inferred members
//   outputVoltage/powered/voltageAvailable.  That was a MISLABEL.  @004b1d18
//   is **Sensor** (proven by the @0050fae0 string pool "Sensor / RadarPercent
//   / SelfTest / BadVoltage" and the surviving SENSOR.HPP); it is now in
//   sensor.cpp with classID 0x0BC3.
//
//   The REAL Myomers is THIS class:
//       vtable    @005117dc          (NB: PTR_LAB_005117dc)
//       ctor      @004b8fec
//       dtor      @004b9114
//       CSS       @004b9140  (stamps classID 0x0BC6, streamed model size 0x1B0)
//       DefaultData @0051154c
//       classID   0x0BC6  (Mech factory case 0xBC6, alloc 0x358)
//   The "outputVoltage/powered/voltageAvailable" triad invented in powersub
//   has NO string backing and must be retired.  Myomers' real members are
//   the SpeedEffect / SeekVoltage[] drive table below.
// ===========================================================================
//
// A Myomers IS-A PoweredSubsystem (powersub.hpp): it is driven from a
// Generator segment, spins up over StartTime, and tracks the same electrical
// state machine.  On top of that it owns a discrete table of "seek voltages"
// (drive gears), selectable at run time, and reports the resulting locomotion
// "speed effect" both to the Mech's mover and to the heat model.
//

#if !defined(MYOMERS_HPP)
#	define MYOMERS_HPP

#if !defined(POWERSUB_HPP)
//
// powersub.hpp STILL declares a stale class it calls "Myomers" (the mislabeled
// Sensor -- vtable 0050fb0c / ctor 4b1d18 / classID 0xBC3, resolved to Sensor in
// sensor.hpp + CLASSMAP.md, with inferred members outputVoltage/powered/
// voltageAvailable that have no string backing).  The AUTHENTIC Myomers is
// classID 0xBC6, declared below.  We may not edit powersub.hpp, so the stale
// token is neutralized for the duration of the include, then the real class is
// declared.  CROSS-FAMILY: powersub.hpp should DELETE its stale 'Myomers'
// (it duplicates the Sensor now owned by sensor.hpp).
//
#	define Myomers Myomers__powersub_stale
#	include "powersub.hpp"
#	undef Myomers
#endif

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

//###########################################################################
// SubsystemMessage -- minimal local stand-in.
//
// CROSS-FAMILY: the engine subsystem-message type is not present in the
// reconstructed/engine headers available here.  The mover connect/disconnect
// handlers only read the signed attachment index/handle at message+0xC, so a
// one-field stand-in lets this module compile.  Real need: the BT
// SubsystemMessage class.
//
#if !defined(BT_SUBSYSTEM_MESSAGE_SHIM)
#	define BT_SUBSYSTEM_MESSAGE_SHIM
	struct SubsystemMessage
	{
		int		value;		// message+0xC : signed mover-attachment index/handle
	};
#endif

//###########################################################################
//#############################   Myomers   #################################
//###########################################################################
//
//	vtable @005117dc, ctor @004b8fec, dtor @004b9114, shared data @0051154c.
//	RegisteredClass::MyomersClassID == 0x0BC6  (stamped by CreateStreamedSubsystem
//	@004b9140 into resource +0x20; streamed model size 0x1B0 into resource +0x24).
//
	class Myomers:
		public PoweredSubsystem
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data Support
	//
	public:
		static Derivation ClassDerivations;
		static SharedData DefaultData;			// @0051154c
		static Receiver::MessageHandlerSet MessageHandlers;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Streamed model resource.
	//
	// Extends the PoweredSubsystem resource (which ends at startTime @+0x18C).
	// The myomer-specific fields begin at +0x190.  Parsed by
	// CreateStreamedSubsystem @004b9140; streamed model size is 0x1B0.
	//
		struct Myomers__SubsystemResource:
			public PoweredSubsystem::SubsystemResource
		{
			Scalar	velocityEfficiency;			// +0x190 "VelocityEfficiency"      (-1.0 == missing -> error)
			Scalar	accelerationEfficiency;		// +0x194 "AccelerationEfficiency"  (-1.0 == missing -> error)
			Scalar	seekVoltage[5];				// +0x198 "SeekVoltage" (fraction of rated; list ends at -1.0 sentinel)
			int		seekVoltageRecommendedIndex;// +0x1AC "SeekVoltageRecommendedIndex"  (-1 == missing -> error)
		};
		typedef Myomers__SubsystemResource SubsystemResource;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Attribute Support
	//
	// Recovered IndexEntry table @00511588 (each record {attribID, nameptr,
	// byte_offset|1, 0}).  ToggleSeekVoltage (id 0x09) is a mappable COMMAND
	// bound to the toggle handler @004b8a48; the rest are data attributes.
	// (PoweredSubsystem::NextAttributeID == 0x12, so SpeedEffect resolves to
	//  0x12 and the enum below is contiguous.)
	//
	//     ToggleSeekVoltage           -> id 0x09 -> handler @004b8a48 (command)
	//     SpeedEffect                 -> id 0x12 -> @0x31C
	//     CurrentSeekVoltageIndex     -> id 0x13 -> @0x320
	//     RecommendedSeekVoltageIndex -> id 0x14 -> @0x324
	//     MinSeekVoltageIndex         -> id 0x15 -> @0x328
	//     MaxSeekVoltageIndex         -> id 0x16 -> @0x32C
	//     SeekVoltage                 -> id 0x17 -> @0x330  (array base)
	//     OutputVoltage               -> id 0x18 -> @0x344
	//
	public:
		enum {
			ToggleSeekVoltageCommandID		= 0x09,	// mappable command (not in the attribute id run)
			SpeedEffectAttributeID			= PoweredSubsystem::NextAttributeID,	// 0x12
			CurrentSeekVoltageIndexAttributeID,			// 0x13
			RecommendedSeekVoltageIndexAttributeID,		// 0x14
			MinSeekVoltageIndexAttributeID,				// 0x15
			MaxSeekVoltageIndexAttributeID,				// 0x16
			SeekVoltageAttributeID,						// 0x17
			OutputVoltageAttributeID,					// 0x18
			NextAttributeID								// 0x19
		};

	private:
		static const IndexEntry AttributePointers[];	// @00511588
	protected:
		static AttributeIndexSet AttributeIndex;

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

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

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

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Drive model.
	//
	// AvailableOutput() is the shared core: it converts an input voltage into
	// the locomotion drive available, scaled by the Mech's base-speed factor,
	// throttled by the heat-degradation curve, and attenuated by accumulated
	// damage.  RegisterMaxOutput() pushes the strongest myomer's full-throttle
	// output to the Mech so the cockpit can show top speed.
	//
	public:
		Scalar
			AvailableOutput(Scalar input_voltage);			// @004b8ac0 (helper, returns drive)
		void
			RegisterMaxOutput();							// @004b8ef0 (-> owner Mech max speed)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Simulation Support
	//
	// MyomersSimulation is the per-tick Performance: it integrates the work
	// done driving the Mech's velocity/acceleration and accumulates the
	// resulting heat (gated by the sim-control "advanced damage" flag).
	//
	public:
		typedef void
			(Myomers::*Performance)(Scalar time_slice);

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

		void
			MyomersSimulation(Scalar time_slice);			// @004b8d18 (Performance)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Subsystem virtual overrides (vtable @005117dc)
	//
	public:
		Logical
			HandleMessage(int message);			// slot  9, @004b8a8c (-> PoweredSubsystem @004b0efc)
		void
			ResetToInitialState(Logical powered = True);	// slot 10, @004b8aa4 (-> PoweredSubsystem @004b0e6c)
		// slot 15 (PoweredSubsystem base @004b1780) -- normalised speed/gauge readout
		//          (output km/h scaled to 350): GetSpeedReading @004b8f94
		Scalar
			GetSpeedReading(Scalar input_voltage);			// slot 15, @004b8f94  (name best-effort)
		// slot 16 (PoweredSubsystem::IsSourceShorted base @004b0b5c) -- Myomers
		//          requires the source to supply at least the selected seek voltage
		//          AND be in the Ready (==2) state.
		Logical
			HasAdequateVoltage(Subsystem *source = 0);		// slot 16, @004b8f3c  (overrides IsSourceShorted)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Mover-connection message handlers (bound via the simulation message map;
	// route the live SpeedEffect @0x31C into / out of the Mech's JointedMover
	// roster at owner Mech[0x128]).
	//
	protected:
		void
			ConnectToMover(SubsystemMessage &message);		// @004b9550  (attach SpeedEffect feed)
		void
			DisconnectFromMover(SubsystemMessage &message);	// @004b95b8  (detach SpeedEffect feed)

		// ToggleSeekVoltage command handler @004b8a48 (cycles currentSeekVoltageIndex;
		// present in the binary, not captured in the decompiled shards -- best-effort).
		void
			ToggleSeekVoltage();							// @004b8a48  (best-effort)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// CROSS-FAMILY compile shims (owning Mech / JointedMover roster / DamageZone).
	//
	// The drive and heat arithmetic below reads values that logically live on the
	// owning Mech (base-speed, top-speed, velocity/acceleration, motion gain,
	// mass, the sim-control "advanced damage" gate), on the Mech's JointedMover
	// roster (Attach/Detach/Release of the SpeedEffect feed), and on this
	// segment's DamageZone (structureLevel).  None of those are reachable from a
	// PoweredSubsystem here (owner is an incomplete Mech*, JointedMover exposes no
	// roster API, and DamageZone is not in this hierarchy), so they are backed
	// locally to keep the formulas intact and compiling.  The real accessors
	// belong on Mech / JointedMover / DamageZone -- see report CROSS-FAMILY NEEDS.
	//
	protected:
		// WAVE 6 un-stub -- CROSS-FAMILY Mech/Mover/DamageZone couplings.  These
		// logically read the owning Mech's motion state, its JointedMover roster,
		// and this segment's DamageZone -- none reachable from a PoweredSubsystem
		// here (owner is an incomplete Mech*, JointedMover exposes no roster API,
		// DamageZone is not in this hierarchy).  To let the real class FIT its 0x358
		// factory alloc, the shim BACKING FIELDS are dropped and the accessors
		// return the neutral defaults the ctor used to prime, so the subsystem
		// CONSTRUCTS + TICKS but is INERT w.r.t. locomotion/heat: the mover feed is
		// a no-op and MyomersSimulation early-returns on OwnerAdvancedDamage()==False.
		// ⚠ FAITHFUL FOLLOW-UP: redirect these to real Mech/JointedMover/DamageZone
		// accessors + the advanced-damage gate (SubsystemMessageManager 0xBD3) so the
		// mech actually slows when damaged/overheated and motion generates heat --
		// that path DOES drive the live mover, so reconcile it with the gait cutover
		// FIRST (both would feed the mover).
		Scalar	OwnerBaseSpeed() const		{ return 1.0f; }	// Mech +0x34C (neutral)
		Scalar	OwnerMaxSpeed() const		{ return 0.0f; }	// Mech +0x7A0
		void	SetOwnerMaxSpeed(Scalar)	{ }					// no-op (real: write Mech top speed)
		Scalar	OwnerVelocityMag() const	{ return 0.0f; }	// |Mech velocity|
		Scalar	OwnerDriveMag() const		{ return 0.0f; }	// |Mech drive/accel|
		Scalar	OwnerVelocityY() const		{ return 0.0f; }	// Mech velocity.y
		Scalar	OwnerMotionGain() const		{ return 0.0f; }	// Mech +0x20C
		Scalar	OwnerMass() const			{ return 0.0f; }	// *Mech +0x250
		Logical	OwnerAdvancedDamage() const	{ return False; }	// advanced-damage gate (keeps sim INERT)
		Scalar	DamageStructureLevel() const{ return 0.0f; }	// DamageZone +0x158

		void	MoverAttach(int index, Scalar *feed)	{ (void)index; (void)feed; }	// JointedMover vtable +0x38 (no-op)
		void	MoverDetach(int index)					{ (void)index; }				// JointedMover vtable +0x3C (no-op)
		void	MoverRelease(int handle, Scalar *feed)	{ (void)handle; (void)feed; }	// JointedMover vtable +0x44 (no-op)

		// Live-master gate constants (the ctor reads owner->simulationFlags).
		enum {
			SegmentCopyMask		= 0x0C,		// (flags & 0xC): 0 == master, 4 == damaged copy
			MasterHeatSinkFlag	= 0x0100	// live-master segment bit
		};
		int		moverConnection;			// @0x110 mover attachment handle (-1 == detached; set by ConnectToMover)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Local data.  Offsets are byte offsets into the shipped object.
	//
	protected:
		Scalar	speedEffect;				// @0x31C  attr 0x12; ctor 1.0f -- live drive fed to the mover
		int		currentSeekVoltageIndex;	// @0x320  attr 0x13; ctor = recommended -- selected drive "gear"
		int		recommendedSeekVoltageIndex;// @0x324  attr 0x14; ctor = resource +0x1AC
		int		minSeekVoltageIndex;		// @0x328  attr 0x15; ctor 0
		int		maxSeekVoltageIndex;		// @0x32C  attr 0x16; ctor = (seekVoltageCount - 1)
		Scalar	seekVoltage[5];				// @0x330  attr 0x17; ctor = resource fraction * source ratedVoltage
		Scalar	outputVoltage;				// @0x344  attr 0x18; runtime output voltage
		Scalar	heatRange;					// @0x348  ctor = failureTemperature - degradationTemperature
		Scalar	heatRangeSquared;			// @0x34C  ctor = heatRange*heatRange (degradation-curve denominator)
		Scalar	velocityEfficiency;			// @0x350  resource +0x190
		Scalar	accelerationEfficiency;		// @0x354  resource +0x194
	};

#endif
