//===========================================================================//
// File:	dmgtable.hpp                                                        //
// Project: BattleTech           Brick: Entity Manager                        //
// Contents: Damage lookup table -- layered / pie-slice hit-location selector //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
//  --/--/95  ??   Initial coding.                                            //
//  07/08/26  RE   Completed for RUNTIME: real vector storage (was no-op      //
//                 ReconTable shims), stream name-string read, PieSlice flag  //
//                 fix, ResolveHit->zone chain, named accessors.  Byte-format //
//                 verified against BTL4.RES type-29 (see combat-damage.md).  //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//
//
// RECONSTRUCTED from the shipped binary.  Behaviour follows the Ghidra
// pseudo-C for the cluster @0x49de14..@0x49f5fb.  The three-level resolver is:
//
//   DamageLookupTable  -- stack of armour LAYERS by penetration depth (height)
//     -> PieSlice      -- an angular sector wheel (optionally torso-relative)
//        -> DamageZonePercentTable
//                       -- a weighted list of {cumulative %, zone segment index}
//                          whose percentages rise to 1.0; a uniform random roll
//                          then selects the final damage-zone index.
//
// Stream format (type-29 DamageLookupTableStream), byte-verified against the
// shipped BTL4.RES (18 tables, EXACT consumption):
//   Table { i32 layerCount;  Layer[layerCount] }
//   Layer { i32 rotateWithTorso; i32 sliceCount;  Slice[sliceCount] }
//   Slice { i32 nameLen; char name[nameLen]; u8 0x00; i32 count; Entry[count] }
//   Entry { f32 cumulativeThreshold; i32 zoneIndex }
//
// Runtime lookup (FUN_0049eb54 + FUN_0049e678 + FUN_0049de14 + glue FUN_0049ed0c):
//   local  = owner->WorldToLocal(impact)                 (owner+0xd0 xform)
//   layer  = floor(layerCount * local.y / heightRef)     (heightRef=owner+0x2ec[+0xc])
//   theta  = atan2(local.z, local.x) [+ torso twist]     (degenerate -> random*2PI)
//   slice  = floor(sliceCount * theta / 2PI)
//   zone   = first entry whose cumulative threshold > RandomUnit()
//
// vtables: 0x50bd84 (DamageLookupTable), 0x50bd90 (PieSlice),
//          0x50bd9c (DamageZonePercentTable / leaf).
//

#if !defined(DMGTABLE_HPP)
#	define DMGTABLE_HPP

#include <vector>

#if !defined(PLUG_HPP)
#	include <plug.hpp>
#endif

#include "mechrecon.hpp"		// reconstruction shim (Scalar, DebugStream, ...)

//#####################    Forward Class Declarations  #######################
class Mech;
class NotationFile;
class MemoryStream;
class Point3D;

//###########################################################################
//#####################   DamageZonePercentTable   ##########################
//###########################################################################
//
//	Leaf node.  Holds a sorted, cumulative-weighted list of damage-zone segment
//	indices; a uniform random roll turns a [0,1) draw into one of those zones.
//	(vtable @0x50bd9c, ctor @0x49deb0, dtor @0x49df80.)
//
	class DamageZonePercentTable:
		public Plug
	{
	public:
		struct Entry						// binary: zoneIndex@+0xc, cumulative@+0x14
		{
			Scalar	cumulative;			// ascending sort key (rises to 1.0)
			int		zoneIndex;			// damageZones[] / skeleton segment index
		};

		DamageZonePercentTable() {}						// stack-temp / offline builder
		DamageZonePercentTable(							// @0x49deb0 (ReadEntries @0x49e5e4)
			Mech					*owner,
			MemoryStream			*stream
		);
		~DamageZonePercentTable();						// @0x49df80

		Logical	TestInstance() const { return True; }	// @0x49e000

		//
		// @0x49de14 -- uniform weighted roll: draw a [0,1) sample (RandomUnit,
		// FUN_00408050) and return the zone index of the first entry whose
		// cumulative threshold exceeds the draw; -1 if the list is empty.
		//
		int		SelectZone() const;

		//
		// @0x49e5e4 -- read the leading cell NAME string (i32 len + len+1 bytes
		// incl. NUL, per FUN_00402948), then the entry count and that many
		// {cumulative, zoneIndex} pairs.
		//
		void	ReadEntries(MemoryStream *stream);

		// (offline @0x49e00c BuildFromNotation / @0x49e524 WriteEntries -- the
		//  content-build authoring path -- is not part of the runtime port; the
		//  shipped .RES is pre-built.  Omitted here.)

	protected:
		Mech				*owner;			// @0x0c
		std::vector<Entry>	entries;		// @0x14 (was ReconTable) -- sorted ascending
	};

//###########################################################################
//#############################   PieSlice   ################################
//###########################################################################
//
//	An angular sector "wheel".  N equal-angle slices (each 2*PI/N rad); the slice
//	containing the incoming hit direction is chosen and its leaf table rolled.
//	When rotateWithTorso is set the incoming angle is measured relative to the
//	live torso twist (owner->TorsoHeading, == torso+0x1d8).
//	(vtable @0x50bd90, ctor @0x49e740.)
//
	class PieSlice:
		public Plug
	{
	public:
		PieSlice() : owner(0), rotateWithTorso(0), sliceCount(0) {}
		PieSlice(										// @0x49e740
			Mech					*owner,
			MemoryStream			*stream
		);
		~PieSlice();

		//
		// @0x49e678 -- pick the slice for incoming angle 'theta' (radians).  When
		// rotateWithTorso, theta is shifted by the live torso twist and wrapped
		// into [0, 2*PI); slice index = floor(theta * sliceCount / 2*PI).
		//
		DamageZonePercentTable *SelectSlice(Scalar theta) const;

	protected:
		Mech			*owner;					// @0x0c
		std::vector<DamageZonePercentTable*>
						slices;					// @0x14 (was ReconTable)
		int				rotateWithTorso;		// @0x0c-flag in binary (this[3])
		int				sliceCount;				// @0x2c (this[0xb])
	};

//###########################################################################
//#########################   DamageLookupTable   ###########################
//###########################################################################
//
//	Top-level table streamed from the mech's ".tbl".  A stack of armour LAYERS:
//	the higher the local-frame impact, the higher the layer index.  Each layer
//	is a PieSlice wheel.  (vtable @0x50bd84, ctor @0x49ea48, dtor @0x49eadc.)
//
	class DamageLookupTable:
		public Plug
	{
	public:
		DamageLookupTable(								// @0x49ea48
			Mech					*owner,
			MemoryStream			*stream
		);
		~DamageLookupTable();							// @0x49eadc

		Logical	TestInstance() const { return True; }	// @0x49eb48

		//
		// @0x49eb54 (+ @0x49e678 slice + @0x49de14 roll, chained by glue @0x49ed0c)
		// -- resolve a world impact point to a damage-zone index.  Returns -1 when
		// the table is empty or the roll falls through (treated as a miss).
		//
		int		ResolveHit(const Point3D &impact) const;

		int		LayerCount() const { return layerCount; }	// bring-up verify

	protected:
		Mech			*owner;					// @0x0c
		std::vector<PieSlice*>
						layers;					// @0x10 (was ReconTable)
		int				layerCount;				// @0x28 (this[10])
	};

#endif
