//===========================================================================//
// File:	projweap.hpp                                                        //
// Project: BattleTech           Brick: Entity Manager                        //
// Contents: ProjectileWeapon -- MechWeapon that spawns physical projectiles  //
//           (tracers / missiles) and consumes ammunition from an AmmoBin.    //
//---------------------------------------------------------------------------//
//   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
// @004bbae0-@004bcff0, file=bt/projweap.cpp).  Cross-referenced against the
// SURVIVING sources PROJWEAP.TCP (TestClass/ResetToInitialState stubs),
// MISLANCH.HPP (MissileLauncher : ProjectileWeapon -- pins this interface and
// the resource layout), and the reconstructed base mechweap.hpp/.cpp.
//
// Inheritance chain established from the decomp:
//      ... -> PoweredSubsystem -> MechWeapon (mechweap.cpp, ctor @004b99a8)
//          -> ProjectileWeapon (this file, vtable @0051242c, ctor @004bc3fc)
//          -> MissileLauncher  (mislanch.cpp, vtable @00512570, ctor @004bcff0)
//
// EVIDENCE that this class is ProjectileWeapon (not Emitter):
//   * The class-name string "ProjectileWeapon" lives at DAT_00512280, immediately
//     adjacent to this class's vtable group (@00512424/@0051242c).
//   * MechWeapon has exactly TWO direct children whose ctors chain to the
//     MechWeapon ctor @004b99a8: the energy weapon Emitter (ctor @004bb120,
//     vtable @00512078, parses SeekVoltage/DischargeTime) and THIS class
//     (ctor @004bc3fc, vtable @0051242c, parses AmmoBin/TracerInterval).
//   * MissileLauncher's ctor (@004bcff0) chains to THIS ctor (@004bc3fc) and adds
//     exactly { int missileCount; Vector3D muzzleVelocity; } at resource +0x1D0 /
//     +0x1D4 -- precisely the MissileLauncher__SubsystemResource of MISLANCH.HPP.
//     By definition MissileLauncher's direct base IS ProjectileWeapon, so the
//     class at @004bc3fc is ProjectileWeapon.
//
// NOTE on PROJWEAP.TCP: the surviving .TCP is an early test-scaffold (no copyright
// banner) in which TestClass() just returns True and ResetToInitialState() is
// empty.  The shipped binary implements a real ResetToInitialState (@004bbaf8);
// the empty body was filled in after the .TCP snapshot.  TestClass()==True still
// holds.
//
// Field offsets in comments are byte offsets into the shipped object; the
// MechWeapon base occupies everything below +0x3F0.  ProjectileWeapon members
// span +0x3F0..+0x447 (object size 0x448).  Several member NAMES are taken from
// the binary's own attribute strings ("TotalTimeToEject"/"TimeToEject"/
// "PercentOfEject"/"EjectAmmo" @DAT_00512290) and resource tag strings
// ("TracerInterval","AmmoBin","MinTimeOfFlight","MinVoltagePercentToFire",
// "MinJamChance"); the remainder are inferred from usage and flagged best-effort.
//

#if !defined(PROJWEAP_HPP)
#	define PROJWEAP_HPP

#if !defined(MECHWEAP_HPP)
#	include <mechweap.hpp>
#endif

//#####################    Forward Class Declarations  #######################
class Mech;
class Entity;
class AmmoBin;
class ResourceFile;
class NotationFile;

//###########################################################################
//##################   ProjectileWeapon Model Resource   ###################
//###########################################################################
//
// Extends the MechWeapon resource record.  The ProjectileWeapon-specific fields
// begin at +0x1BC in the parsed record (see CreateStreamedSubsystem @004bc7cc).
// Total record size is 0x1D0 -- the parser stamps subsystemModelSize = 0x1D0.
//
	struct ProjectileWeapon__SubsystemResource:
		public MechWeapon::SubsystemResource
	{
		int			tracerInterval;				// +0x1BC  "TracerInterval"  (read as int/bool)
		int			ammoBinIndex;				// +0x1C0  "AmmoBin"  (subsystem-name -> index, +2 bias)
		Scalar		minTimeOfFlight;			// +0x1C4  "MinTimeOfFlight"
		Scalar		minVoltagePercentToFire;	// +0x1C8  "MinVoltagePercentToFire"
		Scalar		minJamChance;				// +0x1CC  "MinJamChance"
	};											// sizeof == 0x1D0

//###########################################################################
//#######################     ProjectileWeapon     #########################
//###########################################################################
//
//	Abstract MechWeapon specialisation for weapons that launch a physical
//	projectile (a tracer round or a Missile) and draw rounds from an AmmoBin.
//	Adds an eject/reload cycle, a heat-driven jam roll, a minimum time-of-flight
//	lead solution, and per-shot projectile spawning.
//	(vtable @0051242c, ctor @004bc3fc, dtor @004bc688.)
//
	class ProjectileWeapon:
		public MechWeapon
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data Support
	//
	public:
		static Derivation ClassDerivations;		// parent = MechWeapon::ClassDerivations,
												// name = "ProjectileWeapon" (DAT_00512280)
		static Receiver::MessageHandlerSet MessageHandlers;
		static AttributeIndexSet AttributeIndex;
		static SharedData DefaultData;

		// Embedded ref-counted connection to the AmmoBin subsystem (resource
		// AmmoBin index).  The shipped layout is a 0xC-byte SharedData::Connection
		// (ctor FUN_004bcbb0->FUN_004179d4; resolver FUN_00417ab4 two-level-derefs +8),
		// NOT an empty proxy -- modeling it as 1 byte made ProjectileWeapon 8 bytes
		// short (0x440 vs the binary 0x448) and slid MissileLauncher's missileCount off
		// 0x448.  Named access (Construct/Connect/Resolve/Destruct) is unchanged + inert
		// in bring-up (no plug resolves).  Same 0xC pattern as SubsystemConnection.
		struct AmmoBinConnection {
			void			   *linked;					// +0x00 (SharedData::Connection; inert here)
			int					_reserved[2];			// pad to the binary 0xC connection
			void				Construct()				{ linked = 0; _reserved[0] = _reserved[1] = 0; }
			template<class T> void Connect(T s)			{ linked = (void *)s; }
			void				Destruct()				{ linked = 0; }
			void			   *Resolve()				{ return linked; }
		};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Weapon firing-state alarm levels (this+0x350 weaponAlarm, reported by
	// PrintState @004bc6c8).  Levels 0..4 are inherited from the MechWeapon /
	// Emitter scheme (Firing/Loaded/Loading/TriggerDuringLoad); ProjectileWeapon
	// adds the ammo-specific states:
	//
	public:
		enum WeaponState {
			JammedState				= 5,	// HandleMessage(2) -> SetLevel(5)
			TriggerDuringJamState	= 6,
			NoAmmoState				= 7
		};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Simulation Support (Performance member-function pointer; the per-frame
	// driver lives at @004bbd04 and is installed by the ctor unless the model
	// is flagged "no simulation").
	//
	public:
		typedef void
			(ProjectileWeapon::*Performance)(Scalar time_slice);

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

		// @004bbd04 -- ProjectileWeapon per-frame update (Performance).  Body not
		// recovered by the decompiler (function prologue confirmed at 0x4bbd04);
		// drives UpdateEject / DrawFiringCharge / CheckForJam / ComputeTimeOfFlight
		// and triggers FireWeapon via the vtable.
		void
			ProjectileWeaponSimulation(Scalar time_slice);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Firing -- overrides MechWeapon's pure-virtual FireWeapon (vtable slot 18).
	//
	//	MechWeapon's slot-18 body is the pure-virtual trap @004ba45c
	//	("ERROR: Accessing Pure Virtual Method").  ProjectileWeapon provides the
	//	real spawn at @004bc104; MissileLauncher overrides again at @004bcc60.
	//
	protected:
		void
			FireWeapon();								// slot 18, @004bc104 (body not recovered)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Subsystem virtual overrides (slots on vtable @0051242c)
	//
	//	Slot indices verified by decoding the raw vtable bytes (section_dump):
	//		slot  6  TakeDamage          INHERITED MechWeapon @004b964c
	//		slot  7  WriteUpdateRecord   INHERITED MechWeapon @004b9690
	//		slot  8  HandleMessage       OVERRIDE  @004bcabc
	//		slot  9  ReadUpdateRecord    OVERRIDE  @004bbae0 (thin -> MechWeapon)
	//		slot 10  ResetToInitialState OVERRIDE  @004bbaf8
	//		slot 12  GetStatusFlags      OVERRIDE  @004bbf88 (body not recovered)
	//		slot 13  PrintState          OVERRIDE  @004bc6c8
	//		slot 16  (per-frame hook)    OVERRIDE  @004bbc20 (body not recovered)
	//		slot 18  FireWeapon          OVERRIDE  @004bc104 (body not recovered)
	//
	public:
		LWord
			GetStatusFlags();							// slot 12, @004bbf88
		Logical
			HandleMessage(int message);					// slot  8, @004bcabc
		void
			ResetToInitialState();						// slot 10, @004bbaf8
		void
			PrintState();								// slot 13, @004bc6c8

	protected:
		void
			ReadUpdateRecord(UpdateRecord *message);	// slot  9, @004bbae0

		// slot 16, @004bbc20 -- MechWeapon-family per-frame hook (overrides
		// MechWeapon @004b0b5c).  Body not recovered.  TODO: confirm role.
		virtual void
			UpdateWeaponState();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Class Support
	//
	public:
		static Logical
			TestClass(Mech&);							// @ PROJWEAP.TCP -> True
		Logical
			TestInstance() const;						// @004bd1e0 family (IsDerivedFrom)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Internal model helpers (recovered)
	//
	protected:
		// @004bbb50 -- advance the eject/reload countdown (TimeToEject), update
		// PercentOfEject, and when complete request a fresh round from the AmmoBin
		// (NoAmmo alarm if the bin is dry).
		void
			UpdateEject(Scalar time_slice);

		// @004bbc78 -- bleed the firing charge (MechWeapon recoil @0x3E8) according
		// to the available voltage / ammo-bin supply.
		void
			DrawFiringCharge(Scalar time_slice);

		// @004bbfcc -- roll a heat-scaled jam chance: returns True if the weapon
		// jams this shot.  Jam probability = clamp((0.41*currentTemperature /
		// failureTemperature), minJamChance, 1.0) compared against a uniform random.
		Logical
			CheckForJam();

		// @004bc06c -- lead solution: project the target position by launchVelocity
		// and return the time-of-flight (or minTimeOfFlight + bias when no target
		// is in range).
		Scalar
			ComputeTimeOfFlight();

		// --- cross-family isolation helpers (owning Mech subsystem roster +
		//     cockpit controls live in the mech/controls families).  Declared
		//     here so the recovered bodies compile; the mech family wires the
		//     real roster/controls query. ---
		int
			OwnerSubsystemCount(Mech *owner) const;		// Mech roster size
		Subsystem *
			OwnerSubsystem(Mech *owner, int index) const;	// Mech roster lookup
		Logical
			LiveFireEnabled() const;					// controls "live fire" flag

		// Resolve the linked AmmoBin and pull one round (AmmoBin::FeedAmmo).
		// Returns True when a round was dispensed; on a dry / not-ready / missing
		// bin it raises the NoAmmo alarm and returns False.  Shared by the
		// ProjectileWeapon and MissileLauncher FireWeapon spawn paths.
		Logical
			ConsumeRound();

		// True when the weapon has recovered its firing charge (recoil counts
		// down toward 0 in DrawFiringCharge; 0 == ready) and is not mid-eject.
		Logical
			ReadyToFire() const
			{ return (recoil <= 0.0f) && (ejectState != 0); }

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

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

		static int
			CreateStreamedSubsystem(					// @004bc7cc
				ResourceFile				*resource_file,
				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 for the projectile-weapon class.
	// Offsets are byte offsets into the shipped object (MechWeapon occupies
	// everything below +0x3F0).  Object size is 0x448.
	//
	protected:
		// --- eject / reload cycle (attribute names from DAT_00512290) ---
		Scalar	totalTimeToEject;		// @0x3F0  "TotalTimeToEject"  init 3.0
		Scalar	timeToEject;			// @0x3F4  "TimeToEject"       countdown
		Scalar	percentOfEject;			// @0x3F8  "PercentOfEject"   0..1 progress (HUD)
		Scalar	minJamChance;			// @0x3FC  resource MinJamChance (+0x1CC)
		int		ejectState;				// @0x400  -1 = idle, 0 = ejecting (init -1)
		Scalar	minVoltagePercentToFire;// @0x404  resource MinVoltagePercentToFire (+0x1C8)
		int		tracerCounter;			// @0x408  init 0   (best-effort: tracer cadence)

		// --- launch / lead solution ---
		Scalar	minTimeOfFlight;		// @0x40C  resource MinTimeOfFlight (+0x1C4)
		Vector3D	launchVelocity;		// @0x410  init {0,0,-effectiveRange/speed}
										//         (MissileLauncher overwrites w/ MuzzleVelocity)
		int		tracerModelHandle;		// @0x41C  spawned tracer/explosion model handle
		Point3D		leadPosition;		// @0x420  computed target lead point (init origin)
		Vector3D	tracerOrigin;		// @0x42C  best-effort: runtime muzzle origin (sim scratch)

		// --- ammunition ---
		int		tracerInterval;			// @0x438  resource TracerInterval (+0x1BC)
		AmmoBinConnection
				ammoBinLink;			// @0x43C  embedded 0xC connection to the AmmoBin subsystem
										//         (ctor @004bcbb0, dtor @004bcbcf, link vtable
										//          @00512424; resolved via the resource AmmoBin
										//          index at +0x1C0) -- LAST field, object ends 0x448

		friend struct ProjectileWeaponLayoutCheck;
	};											// sizeof == 0x448

	struct ProjectileWeaponLayoutCheck {
		static_assert(offsetof(ProjectileWeapon, totalTimeToEject) == 0x3F0, "ProjectileWeapon::totalTimeToEject @0x3F0 (MechWeapon must end 0x3F0)");
		static_assert(offsetof(ProjectileWeapon, launchVelocity)   == 0x410, "ProjectileWeapon::launchVelocity @0x410");
		static_assert(offsetof(ProjectileWeapon, leadPosition)     == 0x420, "ProjectileWeapon::leadPosition @0x420");
		static_assert(offsetof(ProjectileWeapon, tracerInterval)   == 0x438, "ProjectileWeapon::tracerInterval @0x438");
		static_assert(offsetof(ProjectileWeapon, ammoBinLink)      == 0x43C, "ProjectileWeapon::ammoBinLink @0x43C");
		static_assert(sizeof(ProjectileWeapon)                     == 0x448, "sizeof(ProjectileWeapon) 0x448 (== factory alloc)");
	};

#endif
