//==========================================================================//
// File:        mechdmg.hpp                                                  //
// Project: BattleTech                                                       //
// Contents: Mech::DamageZone													           //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 06/05/95  JM  Initial coding.                                             //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc.                     //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
//
// RECONSTRUCTED from the shipped binary (Ghidra pseudo-C in part_012.c)
// cross-referenced with the SURVIVING header MECHDMG.HPP (ground truth --
// declarations below are reproduced verbatim from it).  The only additions
// over the surviving header are the byte-offset annotations ("@0x..") and
// the per-method @ADDR evidence, which live in comments.  See mechdmg.cpp.
//
// Object layout (size 0x1b8, ctor @0049ce50):
//   [0x000]  DamageZone base (vtable @0050bb90, base ctor @0041df5c):
//            owner Mech @0x00c, graphic alarms @0x010 / @0x064, flags @0x0b8,
//            5 armor/structure scalars @0x144..0x154, structureLevel @0x158,
//            ref-counted name @0x15c.
//   [0x160]  Mech::DamageZone members (see field comments below).
//

#if !defined(MECHDMG_HPP)
#	define MECHDMG_HPP

#include <vector>

#	if !defined(DAMAGE_HPP)
#		include <damage.hpp>
#	endif

#	if !defined(SLOT_HPP)
#		include <slot.hpp>
#	endif

#	if !defined(TABLE_HPP)
#		include <table.hpp>
#	endif

#	if !defined(ENTITYID_HPP)
#		include <entityid.hpp>
#	endif

#	include "mechrecon.hpp"		// reconstruction shim

	class Mech;
	class Subsystem;
	class DamageZone;

	//======================================================================//
	// Reconstruction proxies for the per-zone containers/slots.
	//   The recovered code drives the redirect table, integer plugs and the
	//   parent/subsystem slots through a verb set (Construct / Add / Resolve /
	//   Next / Count / value ...) that the modern engine TableOf / PlugOf /
	//   SlotOf spell differently; these behaviour-neutral stand-ins carry that
	//   surface so the recovered bodies compile.  (Reconstruction only.)
	//======================================================================//
	struct DZIntegerPlug							// PlugOf<int> stand-in
	{
		int		value;
		DZIntegerPlug()					: value(0) {}
		DZIntegerPlug(const int *p)		: value(p ? *p : 0) {}
	};
	struct DZIndexTable								// TableOf<IntegerPlug*,int> stand-in
	{
		void	Construct(int, int)		{}
		void	Add(DZIntegerPlug *)	{}
	};
	struct DZIndexTableIterator						// TableIteratorOf<...> stand-in
	{
		DZIndexTableIterator(DZIndexTable *)	{}
		int				Count()				{ return 0; }
		DZIntegerPlug	*Next()				{ return 0; }
		DZIntegerPlug	*operator[](int)	{ return 0; }
		void			DeleteContents()	{}
	};
	template<class T> struct DZSlot					// SlotOf<T> stand-in
	{
		DZSlot()				{}
		DZSlot(const void *)	{}			// ctor(owner)
		void	Construct(int)	{}
		T		Resolve()		{ return T(); }
		DZSlot &operator=(const DZSlot &)		{ return *this; }
		template<class A> DZSlot &operator=(const A &)	{ return *this; }
	};

	//##########################################################################
	//######################    MechCriticalSubsystem          #################
	//##########################################################################
	//
	//	One critical subsystem that a damage zone can destroy.  Streamed from the
	//	"CriticalSubsystem" tokens in the .dmg file ("<name> <pct> <pct>").
	//	Object size 0x1c (28 bytes).  ctor @0049dd18, dtor @0049dd44,
	//	vtable @0050bb8c (single slot: the destructor).
	//
	class
		MechCriticalSubsystem  SIGNATURED
	{
		public:

		MechCriticalSubsystem(DamageZone *owner);				// @0049dd18

		virtual ~MechCriticalSubsystem();						// @0049dd44 (vtable slot 0)

		DZSlot<Subsystem*>
			subsystemPlug;			// @0x04  ctor @0049dd7e / dtor @0049dd9d (vtable @0050bb84)

		Scalar
			damagePercentage,		// @0x10  streamed (1st "%f")
			damagePercentageUsed;	// @0x14  streamed (2nd "%f"); init 0

		Scalar
			criticalWeight;			// @0x18  init 0; summed into owner.criticalWeightSum

		Logical
			TestInstance() const;	// @0049dd74 (returns 1)

	};


	//##########################################################################
	//####################    Mech::DamageZone       ###########################
	//##########################################################################
	//
	//	vtable @0050bb90 (7 slots).  Overridden slots vs the DamageZone base:
	//	    slot 0 (dtor)        -> ~Mech::DamageZone   @0049d23c
	//	    slot 3 (SetGraphic)  -> SetGraphicState     @0049c66c
	//	    slot 6 (TakeDamage)  -> TakeDamage          @0049c690
	//	Slots 1,2,4,5 (@004173b8/@00417938/@0041ddd8/@0041dd98) inherit DamageZone.
	//
	class Mech__DamageZone :
		public DamageZone
	{
		// The owning Mech reads zone state (structureLevel, criticalSubsystemCount,
		// vital/destroyed flags) when routing damage + reporting -- the binary's Mech
		// damage code (FUN_004a0230 / FUN_0049fb54 etc.) is a Mech method touching
		// these zone fields directly.
		friend class Mech;
		friend class MechDeathHandler;		// walks the descriptor table + applies the destroyed skin
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// LOD Support
	//
		protected:

			void
				LODDamageRouter(EntityID inflicting, Damage damage);	// @0049c40c

			int
				RandomRedirect();									// @0049c600

			typedef DZIntegerPlug IntegerPlug;								// (was PlugOf<int>)

			typedef DZIndexTable DamageZoneIndexTable;						// (was TableOf<IntegerPlug*,int>)

			typedef DZIndexTableIterator DamageZoneIndexTableIterator;		// (was TableIteratorOf<...>)


			// Zones which are artifacts have a table of their real children.

			DamageZoneIndexTable redirectTable;			// @0x160  ctor @00424767(,0,1)

			// The result of the last random redirect

			int lastHitZone;							// @0x178  init RandomRedirect() or 0
			Scalar lastDamageTime;						// @0x17c  init now() (ticks/TicksPerSec)
			EntityID lastInflicting;					// @0x180  copied from mech[+0x184]
			//
			// Zones which are real have a pointer (slot) back to their artifact parent.
			// With the current art (1/30/96) no zone has both a parent and children,
			// and many zones have neither.

			DZSlot<Mech__DamageZone*> parentArtifactZone;	// @0x188  ctor @0049ddc9 (vtable @0050bb7c)

			// --- reconstruction-named state (decomp spellings; real DamageZone
			//     base uses damageLevel/damageZoneState/etc.) ------------------
			// NOTE: structureLevel was a RE-DECLARATION of the engine DamageZone base
			// field `damageLevel` (binary offset 0x158).  Compiled as a subclass of the
			// engine DamageZone, the re-declared copy landed at a different offset and
			// stayed uninitialised (0xCDCDCDCD) while the engine ctor/TakeDamage wrote
			// the *base* damageLevel -- so the death/vital logic read garbage.  Removed;
			// the reconstruction now uses the inherited `damageLevel` (the same field the
			// binary's 0x158 IS).  Likewise damageScale[5]/defaultArmorPoints are the
			// inherited base members, not raw 0x144/0x140 offsets.
			// graphicState/graphicStateAlarm/flags/state were RE-DECLARATIONS of engine
			// DamageZone base fields (binary 0x010/0x064/0x0b8): the StateIndicators
			// damageZoneState (2 states, @0x010) and damageZoneGraphicState (3 states,
			// @0x064), and changedFlags.  The engine ctor initialises them; the
			// re-declared copies stayed uninitialised -> SetLevel on garbage crashed when
			// a vital zone was destroyed.  Removed; the reconstruction uses the inherited
			// accessors: SetDamageZoneState/GetDamageZoneState (was graphicState) and
			// SetGraphicState/GetGraphicState (was graphicStateAlarm/state).

			void
				UpdateLODDamageLevel();						// @0049c51c

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Descending Heirarchy when DamageZone Destroyed Support
	//
			void
				ShortAttachedGenerators();					// inlined into TakeDamage @0049c690

			void
				SendSubsystemDamage();						// @0049c9a8

			void
				RecurseSegmentTable(Mech *my_mech);			// @0049cad4

			int
				segmentIndex;								// @0x194  GetSegmentIndex() result

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Damage Support
	//
		public:

			void
				TakeDamage(Damage& damage);					// @0049c690 (vtable slot 6)

			Subsystem*
				CriticalHit(Damage &damage_data);			// @0049ccc4

		protected:

      	Logical
				vitalDamageZone;							// @0x198  streamed "VitalDamageZone"

			Logical
         	descendOnDestruction;						// @0x19c  streamed "DescendOnDestruction"

			Logical
			destroySiblingsOnDestruction;					// @0x1a0  streamed "DestroySiblingsOnDestruction"

			Logical
				leftLeg;									// @0x1a4  "LegDamageZone == LeftLeg"

			Logical
				rightLeg;									// @0x1a8  "LegDamageZone == RightLeg"

			int
				criticalSubsystemCount;						// @0x1ac  streamed count

			Scalar
				criticalWeightSum;							// @0x1b0  sum of criticalWeight

			MechCriticalSubsystem
				**criticalSubsystems;						// @0x1b4  array[criticalSubsystemCount]

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Damage-state descriptor table (binary this+0xd4).  Streamed from the
	// type-0x1e resource by LoadCriticalSubsystems (FUN_0041e4a8 -> FUN_0042a748);
	// as this zone's damageLevel crosses each entry's threshold, MechDeathHandler
	// fires the entry's explosion + applies its GraphicState (destroyed skin).
	// Entries ascend by damageLevel.  (Was un-built -- the loader was a stub.)
	//
	public:
		struct DamageDescriptor						// binary entry 0x1C (ctor FUN_0042a2c8)
		{
			Scalar	damageLevel;					// +0xc  threshold [0,1]
			int		graphicState;					// +0x10 GraphicState enum (Destroyed=1)
			int		effectResource;					// +0x14 ExplosionModelFile resource id
			Scalar	timeDelay;						// +0x18
		};
		// @0042a664: first entry whose threshold > level (0 if none / fully destroyed).
		const DamageDescriptor *DescriptorForLevel(Scalar level) const;
		// @0042a5f4: true iff some entry's threshold lies in (oldLevel, newLevel].
		Logical DescriptorCrossed(Scalar oldLevel, Scalar newLevel) const;
		// @0042a6c4: first entry whose GraphicState == gstate (0 if none).
		const DamageDescriptor *DescriptorForGraphicState(int gstate) const;
		Scalar  GetStructureDamageLevel() const;	// inherited damageLevel (binary +0x158)
		// Apply a descriptor's GraphicState (Destroyed=1) to this zone -- the
		// destroyed-skin swap the renderer reads (public wrapper so MechDeathHandler
		// can drive it; the base SetGraphicState is otherwise unreachable from it).
		void	ApplyDamageGraphicState(int gstate);
	protected:
		std::vector<DamageDescriptor>
				damageDescriptors;						// binary this+0xd4 collection

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Graphic State Support
	//

	public:

		// SetGraphicState (vtable slot 3) is NOT overridden -- the binary's override is
		// byte-identical to the engine base DamageZone::SetGraphicState (sets
		// damageZoneGraphicState + GraphicStateChangedFlag), so we use the base.

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction / Destruction
	//

	public:

			static const int NullDamageZone;				// = -1

			Mech__DamageZone(								// @0049ce50
				Mech *mech,
				int	damage_zone_index,
				MemoryStream *stream
			);

			void SetLODParentPointers();					// @0049d1d0

			void LoadCriticalSubsystems(MemoryStream *stream);	// @0041e4a8 (crit table)

			~Mech__DamageZone();							// @0049d23c

			static Logical
			CreateStreamedDamageZone(						// @0049d304 (one zone)
				ResourceFile   *resource_file,
				NotationFile	*model_file,
				const char		*model_name,
				NotationFile	*skl_file,
				const char     *damage_zone_name,
				NotationFile   *dmg_file,
				const ResourceDirectories  *directories,
				MemoryStream    *damage_zone_stream
			);

         static int
				GetSegmentIndex(							// @0049db20
					CString	damage_zone_name,
					NotationFile *skl_file
				);
	};

//###########################################################################
//###########################################################################
//					MechDeathHandler   (@0042a984 / @0042aa2c)
//
//	A per-frame handler built by the Mech ctor (cached at mech[0x214]) that turns
//	subsystem/zone damage into VISIBLE destruction: each tick it walks the mech's
//	damage zones and, as a zone's damageLevel crosses a descriptor threshold,
//	spawns that entry's explosion effect at the zone + applies its GraphicState
//	(the destroyed skin).  The binary registers it on the mech's Performance list
//	(mech+0xbc); the bring-up drive override does not tick that list, so
//	Mech::PerformAndWatch drives Tick() directly.  Effect spawn uses the
//	established Explosion::Make port (the authentic effect-message manager, the
//	0xBD3 SubsystemMessageManager, is unreconstructed) -- see BTSpawnDamageEffect.
//###########################################################################
class MechDeathHandler
{
	public:
		MechDeathHandler(Mech *mech);					// @0042a984
		~MechDeathHandler();							// @0042a9f4
		void	Tick();									// @0042aa2c (the Performance)

	private:
		Mech				*owner;						// @0x14
		std::vector<Scalar>	lastLevel;				// @0x10  per-zone last damageLevel cache
};

#endif
