//============================================================================//
// File:	ammobin.hpp                                                          //
// Project: BattleTech																			//
// Contents: Ammunition bin -- holds rounds, feeds a ProjectileWeapon, and      //
//			cooks off when overheated.                                           //
//----------------------------------------------------------------------------//
//   Date    Who  Modification                                                //
// --------  ---  ------------------------------------------------------------//
// 04/13/95  JM  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
// @004bd2c0-@004bdb94 + CSS @004bd6f0 in part_013.c).  Only the test fragment
// AMMOBIN.TCP survives (AmmoBin::TestClass -> True; AmmoBin::ResetToInitialState
// -> empty).  Member/method names are inferred from the streamed resource keys
// ("AmmoCount"/"FeedRate"/"AmmoClassID"/"ProjectileClassID"/"MissileClassID"/
// "AmmoModelFile"/"ExplosionModelFile") and from usage; see ammobin.cpp for the
// per-method @ADDR evidence.  Names past the HeatWatcher base are best-effort.
//
// Inheritance chain established from the decomp:
//      Subsystem -> HeatableSubsystem -> HeatWatcher (ctor @4aeb40, vtable
//      @0050ed10) -> AmmoBin (ctor @4bd5c4, vtable @0051286c, classID 0x0BCB).
// AmmoBin IS a HeatWatcher: it watches a temperature against the resource's
// degradation/failure thresholds, and when the watch alarm (this+0x140) reaches
// FAILURE it arms a randomised cook-off timer that dumps all stored rounds'
// heat into the mech and empties the bin.  A ProjectileWeapon links to it as a
// SharedData connection (launcher this[0x10F]@0x43C, connection ctor @4bcbb0,
// vtable @00512424) and pulls a round via FeedAmmo() each time it fires.
//
// Object byte offsets (this is an int* in the decomp, word index in parens).
// Everything below +0x180 is the HeatWatcher / HeatableSubsystem / Subsystem
// base (watch heatAlarm level @0x140 = word 0x50; base status alarm @0x2C).
//

#if !defined (AMMOBIN_HPP)
#	define	AMMOBIN_HPP

#	if !defined(HEATFAMILY_RESLICE_HPP)
#		include <heatfamily_reslice.hpp>	// HeatWatcher base (heat family)
#	endif

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

//###########################################################################
//#################   AmmoBin Model Resource   ##############################
//###########################################################################
//
//	Streamed by AmmoBin::CreateStreamedSubsystem @004bd6f0 (record size 0x104,
//	classID 0x0BCB).  The ammo-specific block sits at +0xF0 past the heat-watcher
//	resource header.  "AmmoClassID" accepts the symbolic forms "ProjectileClassID"
//	(stamps 0x0BD1) and "MissileClassID" (stamps 0x0BBA), naming the entity class
//	the bin spawns when a round is fired.
//
	struct AmmoBin__SubsystemResource:
		public HeatWatcher::SubsystemResource
	{
		int		ammoClassID;			// +0xF0  "AmmoClassID"/"ProjectileClassID"/"MissileClassID"
		int		ammoModelFile;			// +0xF4  "AmmoModelFile"      (resolved model index)
		int		explosionModelFile;		// +0xF8  "ExplosionModelFile" (resolved model index)
		int		ammoCount;				// +0xFC  "AmmoCount"
		Scalar	feedRate;				// +0x100 "FeedRate"  (seconds per round feed)
	};									// ends 0x104
	static_assert(offsetof(AmmoBin__SubsystemResource, ammoClassID) == 0xF0,  "AmmoBin ammoClassID must be at 0xF0");
	static_assert(offsetof(AmmoBin__SubsystemResource, feedRate)    == 0x100, "AmmoBin feedRate must be at 0x100");
	static_assert(sizeof(AmmoBin__SubsystemResource)                == 0x104, "AmmoBin record must be 0x104");

//###########################################################################
//#########################     CLASS -- AmmoBin    #########################
//###########################################################################
//
//	(vtable @0051286c, ctor @004bd5c4, dtor @004bd6b0, CSS @004bd6f0.)
//
	class AmmoBin:
		public HeatWatcher
	{
		friend struct AmmoBinLayoutCheck;	// compile-time offset locks (ammobin.cpp)
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data Support
	//
	public:
		static Derivation ClassDerivations;		// IsDerivedFrom tag 0x512488
		static Receiver::MessageHandlerSet MessageHandlers;
		static AttributeIndexSet AttributeIndex;
		static SharedData DefaultData;			// resolved as &DAT_005125bc

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Ammo feed state machine (this->ammoAlarm @0x194, level @0x1A8 = word 0x6A)
	//
	//	The 6-level AlarmIndicator carries the feed cycle.  Levels confirmed from
	//	usage; 3 and 4 are unobserved.
	//
	public:
		enum AmmoState {
			Feeding		= 0,	// a round is in transit (feedTimer counting down)
			Loaded		= 1,	// a round is chambered and ready to fire
			Empty		= 2,	// no rounds remain
			Dumped		= 5		// transient pulse raised by DumpAmmo() before Empty
		};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Model Support
	//
	public:
		enum {
			CookingOff = HeatWatcher::StateCount,
			StateCount
		};

		typedef void
			(AmmoBin::*Performance)(Scalar time_slice);

		// @004bd394 -- the registered Performance.  Drives the watch alarm, ticks
		// the feed timer (Feeding -> Loaded), arms/fires the cook-off, and trips
		// the Empty alarm when exhausted.
		void
			AmmoBinSimulation(Scalar time_slice);

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

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Ammo feed interface (called by the linked ProjectileWeapon)
	//
	public:
		// @004bd4f4 -- request one round.  When Loaded: chamber the next round,
		// reset the feed timer to feedRate, decrement ammoCount, and return the
		// remaining count (Empty alarm raised at 0).  Returns 0 if not ready /
		// already feeding / empty / destroyed.
		int
			FeedAmmo();

		// @004bd588 -- forcibly empty the bin (eject / strip): zero ammoCount and
		// pulse the alarm Dumped(5) -> Empty(2).
		void
			DumpAmmo();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Subsystem virtual overrides (vtable @0051286c)
	//
	public:
		Logical
			HandleMessage(int message);				// slot 8, @004bdb94
		void
			ReadUpdateRecord(UpdateRecord *update);			// slot 9, @004bd2c0
		void
			ResetToInitialState();						// slot 10 (AMMOBIN.TCP: empty)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Local helpers
	//
	protected:
		// @004bd300 -- cook-off: inject (ammoCount * heatPerRound) heat into the
		// mech, then zero ammoCount and raise the Empty alarm.
		void
			CookOff();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// 	TestClass Support  (AMMOBIN.TCP)
	//
	public:
		static Logical
			TestClass(Mech &);						// AMMOBIN.TCP: returns True
		Logical
			TestInstance() const;					// @004bd1e0

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

		AmmoBin(									// @004bd5c4
			Mech				*owner,
			int					subsystem_ID,
			SubsystemResource	*subsystem_resource
		);
		~AmmoBin();									// @004bd6b0

		static int
			CreateStreamedSubsystem(				// @004bd6f0 (record size 0x104)
				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 appended past the HeatWatcher base (offsets best-effort).
	//
	protected:
		int			ammoCount;			// @0x180 (word 0x60) rounds remaining
		Scalar		feedTimer;			// @0x184 (word 0x61) seconds left in current feed
		int			ammoClassID;		// @0x188 (word 0x62) entity class spawned per round
		int			cookOffArmed;		// @0x18C (word 0x63) cook-off timer running
		int			cookOffTime;		// @0x190 (word 0x64) game clock at which it detonates
		WatcherGaugeAlarm	ammoAlarm;	// @0x194 (0x54) 6-level feed alarm (level @0x1A8)
		// (WAVE 4) `int statusState` DELETED -- it was a this+0x40 SHADOW of the
		// inherited MechSubsystem::simulationState; the reads now use that base
		// member.  The 0x54 ammoAlarm (was an 8-byte HeatAlarm) lands ammoModelFile
		// at 0x1E8 -- exact binary layout, locked by AmmoBinLayoutCheck.
		int			ammoModelFile;		// @0x1E8 (word 0x7A) round model index
		int			explosionModelFile;	// @0x1EC (word 0x7B) cook-off explosion model index
		// @0x1F0 (word 0x7C) cook-off heat-injection descriptor, inited by
		// FUN_0041db7c; CookOff() fills word 0x7D (@0x1F4) below with the total
		// heat (ammoCount * heatPerRound) before injecting via HeatableSubsystem.
		int			cookOffHeatSource[1];	// @0x1F0 (word 0x7C)
		Scalar		heatPerRound;			// @0x1F4 (word 0x7D)
		int			cookOffHeatRest[10];	// @0x1F8..0x21C (words 0x7E..0x87)
		int			initialAmmoCount;	// @0x220 (word 0x88) ammoCount captured at ctor
		Scalar		feedRate;			// @0x224 (word 0x89) resource FeedRate
		int			reserved;			// @0x228 (word 0x8A) init 0 (unused)
	};
#endif
