//===========================================================================//
// File:	btl4vid.hpp                                                         //
// Project: BattleTech       Brick: Video Renderer Manager                    //
// Contents: BTL4VideoRenderer -- the BattleTech L4 out-the-window 3D WORLD   //
//           renderer manager.  Builds the per-entity renderable tree for the //
//           main view each time an interesting entity becomes visible:       //
//           the player mech + its weapons/effects/reticle, terrain, and      //
//           other movers, walking the jointed-mover segment table and the    //
//           subsystem roster and submitting renderables to the scene.        //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 02/13/95  CPB  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1996, Virtual World Entertainment, Inc.                //
//                      PROPRIETARY and CONFIDENTIAL                         //
//===========================================================================//
//
// RECONSTRUCTED from the shipped binary (BTL4OPT.EXE).  NO header survived.
// Class/member/method names are taken from the embedded assert path
// "d:\tesla\bt\bt_l4\BTL4VID.CPP", the embedded class name string
// "BTL4VideoRenderer::Material name ..." (@0051d6f8), and the direct
// Red Planet analogue RP_L4/RPL4VID.cpp (RPL4VideoRenderer) plus the surviving
// MUNGA headers MUNGA_L4/L4VIDEO.HPP and MUNGA_L4/L4VIDRND.HPP.  Each method
// cites its originating @ADDR.
//
// PORTING NOTES (reconstruction -> WinTesla engine)
//   * BTL4VideoRenderer is the BattleTech analogue of RPL4VideoRenderer; both
//     subclass the concrete L4 manager DPLRenderer (L4VIDEO.HPP).  Deriving
//     from DPLRenderer supplies SetFogStyle/AddDynamicRenderable, the fog
//     colour/plane fields, dplMainView/dplMainZone/dplDeathZone and the
//     vehicleReticle slot the recovered code touches.
//   * The 1996 BattleTech build drove a pre-DPL renderable hierarchy whose
//     joint renderables parent on a dpl_DCS* (rather than the WinTesla
//     HierarchicalDrawComponent* parent).  That hierarchy did NOT survive in
//     source, so the BT-specific renderables it constructs are declared here
//     (BTRootRenderable / BTHingeRenderable / ... ) with the signatures the
//     recovered code actually calls.  They are distinct from the modern
//     L4VIDRND.HPP renderables of the same role (which is why the targeting
//     reticle is BTReticleRenderable, NOT the engine's ReticleRenderable --
//     the BT object is 0x358 bytes and carries up to 10 AddWeapon pip markers).
//

#if !defined(BTL4VID_HPP)
#	define BTL4VID_HPP

//
// Forward declaration so MUNGA_L4/l4vidrnd.h (which references DPLRenderer in
// HierarchicalDrawComponent before l4video.h defines it) parses cleanly
// regardless of include order.
//
class DPLRenderer;

#if !defined(L4VIDEO_HPP)
#	include <l4video.hpp>			// DPLRenderer / VideoRenderer / INTERSECT_ALL
#endif
#if !defined(L4VIDRND_HPP)
#	include <l4vidrnd.hpp>			// VideoComponent / the *Renderable hierarchy
#endif
#if !defined(RETICLE_HPP)
#	include <reticle.hpp>
#endif
#if !defined(STATE_HPP)
#	include <state.hpp>				// StateIndicator
#endif
#if !defined(JMOVER_HPP)
#	include <jmover.hpp>			// JointedMover
#endif
#if !defined(JOINT_HPP)
#	include <joint.hpp>				// Joint / JointSubsystem / Hinge
#endif
#if !defined(SEGMENT_HPP)
#	include <segment.hpp>			// EntitySegment
#endif
#if !defined(SUBSYSTM_HPP)
#	include <subsystm.hpp>			// Subsystem
#endif
#if !defined(NAMELIST_HPP)
#	include <namelist.hpp>			// NameList / NameList::Entry
#endif
#include <map>						// per-mech render-tree bookkeeping (RemakeEntity)

	class Entity;
	class Mission;
	class ResourceDescription;
	class NotationFile;

	//
	// BT "scene" handle.  In the 1996 build the world-view renderables were
	// built against an opaque scene root (the analogue of RP's dpl_ZONE pair);
	// it is used pointer-only in this module, so a forward declaration suffices.
	//
	class Scene;

//
//===========================================================================//
// Legacy 2D / material-callback DPL entry points
//===========================================================================//
//
// The dpl2d_ display-list layer and the material-name-substitution callback
// hook were part of libDPL (the IG-board driver) and were NOT carried into the
// WinTesla engine.  The recovered reticle builder and material setup call them
// directly; declare them here so the translation unit compiles (resolved at
// link time against the libDPL shim).
//
dpl2d_DISPLAY*	dpl2d_NewDisplayList();
void			dpl2d_Begin(dpl2d_DISPLAY *list, int mode);
void			dpl2d_SetColor(dpl2d_DISPLAY *list, Scalar red, Scalar green, Scalar blue);
void			dpl2d_Circle(dpl2d_DISPLAY *list, Scalar x, Scalar y, Scalar radius, int fill);
void			dpl2d_PushMatrix(dpl2d_DISPLAY *list);
void			dpl2d_PopMatrix(dpl2d_DISPLAY *list);
void			dpl2d_MoveTo(dpl2d_DISPLAY *list, Scalar x, Scalar y);
void			dpl2d_End(dpl2d_DISPLAY *list);
void			dpl2d_Compile(dpl2d_DISPLAY *list);
//
// The FULL recovered API (phase-02: every recorder named by its debug string;
// opcode map in phases/phase-02-dpl2d-reticle.md).  The Push/Move/Pop trio
// above is a legacy alias of OpenPolypoint/AddPoint/ClosePolypoint.
//
void			dpl2d_OpenPolypoint(dpl2d_DISPLAY *list);				// opcode 2
void			dpl2d_ClosePolypoint(dpl2d_DISPLAY *list);				// opcode 3
void			dpl2d_OpenPolyline(dpl2d_DISPLAY *list);				// opcode 4 (closed loop)
void			dpl2d_ClosePolyline(dpl2d_DISPLAY *list);				// opcode 5
void			dpl2d_OpenLines(dpl2d_DISPLAY *list);					// opcode 6 (open strip)
void			dpl2d_CloseLines(dpl2d_DISPLAY *list);					// opcode 7
void			dpl2d_AddPoint(dpl2d_DISPLAY *list, Scalar x, Scalar y);	// opcode 8
void			dpl2d_SetLineWidth(dpl2d_DISPLAY *list, Scalar width);	// opcode 0x15
void			dpl2d_PushState(dpl2d_DISPLAY *list);					// opcode 0x12
void			dpl2d_PopState(dpl2d_DISPLAY *list);					// opcode 0x13
void			dpl2d_SetMatrix(dpl2d_DISPLAY *list, const Scalar *six);	// opcode 0x10 (2x3)
void			dpl2d_ConcatMatrix(dpl2d_DISPLAY *list, const Scalar *six);	// opcode 0x11
void			dpl2d_CallList(dpl2d_DISPLAY *list, dpl2d_DISPLAY *callee);	// nested glyphs
void			dpl2d_FullScreenClip(dpl2d_DISPLAY *list);

void			dpl_SetMaterialNameCallback(char *(*callback)(char *source));

//
//===========================================================================//
// BattleTech renderable hierarchy (pre-DPL)
//===========================================================================//
//
// Common base: every BT world renderable carries the dpl_DCS / dpl_INSTANCE it
// creates and offers the graphical (Add) / control (Connect) hook-up the world
// container uses.  Modelled on VideoComponent so a BTRootRenderable can be
// returned as the VideoComponent* mech root.
//
class BTRenderableBase:
	public VideoComponent
{
	public:
		BTRenderableBase(
			Entity				*entity,
			VideoExecutionType	execution_type = DynamicVideoExecutionType)
				: VideoComponent(entity, execution_type), myDCS(0), myInstance(0) {}

		dpl_DCS*		GetDCS()		{ return myDCS; }
		dpl_INSTANCE*	GetInstance()	{ return myInstance; }

		//
		// Graphical / control hook-up.  These hide VideoComponent::Add/Connect
		// (which take VideoComponent*) so the world container can accept any
		// renderable in the BT hierarchy by its common HierarchicalDrawComponent
		// base.
		//
		void	Add(HierarchicalDrawComponent *child)		{ if (child) addChild(child); }
		void	Connect(HierarchicalDrawComponent *child)	{ if (child) addChild(child); }

	protected:
		dpl_DCS			*myDCS;
		dpl_INSTANCE	*myInstance;
};

//
// World container renderable (FUN_00455de4, alloc 0x60).  Holds the death /
// world DCS roots for one entity's tree.
//
class BTWorldContainerRenderable:
	public BTRenderableBase
{
	public:
		BTWorldContainerRenderable(
			Entity	*entity,
			int		execution_type,
			int		in_death_zone,
			Scene	*scene);
};

//
// Root DCS object renderable (FUN_00453578, alloc 0x64).
//
class BTRootRenderable:
	public BTRenderableBase
{
	public:
		BTRootRenderable(
			Entity			*entity,
			int				execution_type,
			d3d_OBJECT		*graphical_object,
			Scene			*scene,
			int				intersect_mode,
			uint32			intersect_mask);
};

//
// Static / jointed child DCS renderables.
//
class BTDCSObjectRenderable:
	public BTRenderableBase
{
	public:
		BTDCSObjectRenderable(			// FUN_0045848c, alloc 0x50
			Entity			*entity,
			Scene			*scene,
			d3d_OBJECT		*graphical_object,
			int				execution_type,
			uint32			intersect_mask,
			LinearMatrix	*offset_matrix,
			dpl_DCS			*parent_DCS);
};

class BTHingeRenderable:
	public BTRenderableBase
{
	public:
		BTHingeRenderable(				// FUN_004537e8, alloc 0x78
			Entity			*entity,
			int				execution_type,
			d3d_OBJECT		*graphical_object,
			Scene			*scene,
			int				exec2,
			uint32			intersect_mask,
			dpl_DCS			*parent_DCS,
			LinearMatrix	*offset_matrix,
			const Hinge		*my_hinge);
};

class BTBallJointRenderable:
	public BTRenderableBase
{
	public:
		BTBallJointRenderable(			// FUN_004539b4, alloc 0x7c
			Entity				*entity,
			int					execution_type,
			d3d_OBJECT			*graphical_object,
			Scene				*scene,
			int					exec2,
			uint32				intersect_mask,
			dpl_DCS				*parent_DCS,
			LinearMatrix		*offset_matrix,
			const EulerAngles	*my_euler);
};

class BTBallTranslateJointRenderable:
	public BTRenderableBase
{
	public:
		BTBallTranslateJointRenderable(	// FUN_00453ac4, alloc 0x8c
			Entity				*entity,
			int					execution_type,
			d3d_OBJECT			*graphical_object,
			Scene				*scene,
			int					exec2,
			uint32				intersect_mask,
			dpl_DCS				*parent_DCS,
			LinearMatrix		*offset_matrix,
			const EulerAngles	*my_euler,
			const Point3D		*my_translation);
};

//
// Inside-view eyepoint (POV/eye) renderable (FUN_004579a8, alloc 0x5c).
//
class BTEyeRenderable:
	public BTRenderableBase
{
	public:
		BTEyeRenderable(
			Entity			*entity,
			Scene			*scene,
			LinearMatrix	*offset_matrix,
			dpl_DCS			*parent_DCS,
			dpl_VIEW		*this_view,
			EulerAngles		*eyepoint_rotation);
};

//
// Death / explosion effect renderable (FUN_00453f18, alloc 0x48).
//
class BTDeathEffectRenderable:
	public BTRenderableBase
{
	public:
		BTDeathEffectRenderable(
			Entity			*entity,
			int				execution_type,
			dpl_VIEW		*this_view,
			Scene			*scene,
			dpl_DCS			*parent_DCS,
			StateIndicator	*death_state,
			int				death_control_state);
};

//
// Marker (timestamp/beacon) watcher renderable (FUN_00458c58, alloc 0x120).
//
class BTMarkerWatcherRenderable:
	public BTRenderableBase
{
	public:
		BTMarkerWatcherRenderable(
			Entity		*entity,
			int			execution_type,
			dpl_VIEW	*this_view,
			dpl_DCS		*parent_DCS);
};

//
// Drop-zone translocation effect renderable (FUN_00458d2c, alloc 0x40).
//
class BTTranslocationRenderable:
	public BTRenderableBase
{
	public:
		BTTranslocationRenderable(
			Entity			*entity,
			int				execution_type,
			dpl_VIEW		*this_view,
			StateIndicator	*effect_trigger,
			Point3D			*drop_zone,
			int				effect_control_state);
};

//
// Player POV mission start/end fade renderable (FUN_00454394, alloc 0x50).
// (BT-local twin of the engine's POVStartEndRenderable; the BT ctor takes the
// main-view handle plus the world/death zones explicitly.)
//
class BTPOVStartEndRenderable:
	public BTRenderableBase
{
	public:
		BTPOVStartEndRenderable(
			Entity			*entity,
			int				execution_type,
			dpl_VIEW		*this_view,
			dpl_ZONE		*main_zone,
			dpl_ZONE		*death_zone,
			StateIndicator	*effect_trigger,
			float			red_fog,
			float			green_fog,
			float			blue_fog,
			float			near_fog,
			float			far_fog,
			int				start_mission_state,
			int				end_mission_state);
};

//
// Coolant / reservoir / tracer reservoir effect renderable (FUN_00456a68,
// alloc 0x138).
//
class BTTracerEffectRenderable:
	public BTRenderableBase
{
	public:
		BTTracerEffectRenderable(
			Entity			*entity,
			int				execution_type,
			void			*state_attr,
			int				mode,
			dpl_ZONE		*zone,
			dpl_DCS			*parent_DCS,
			LinearMatrix	*offset_matrix);
};

//
// Emitter beam renderable (FUN_004593c0, alloc 0x80).
//
class BTEmitterBeamRenderable:
	public BTRenderableBase
{
	public:
		BTEmitterBeamRenderable(
			Entity			*entity,
			int				execution_type,
			d3d_OBJECT		*graphical_object,
			Scene			*scene,
			int				exec2,
			uint32			intersect_mask,
			dpl_DCS			*parent_DCS,
			LinearMatrix	*offset_matrix,
			void			*sim_state,
			int				flag,
			void			*beam_scale,
			void			*beam_orient);
};

//
// PPC beam renderable (FUN_004590d8, alloc 0x88).
//
class BTPPCBeamRenderable:
	public BTRenderableBase
{
	public:
		BTPPCBeamRenderable(
			Entity			*entity,
			int				execution_type,
			d3d_OBJECT		*graphical_object,
			Scene			*scene,
			int				exec2,
			uint32			intersect_mask,
			dpl_DCS			*parent_DCS,
			LinearMatrix	*offset_matrix,
			void			*sim_state,
			int				flag,
			void			*beam_scale,
			void			*beam_orient,
			float			beam_width);
};

//
// Searchlight LightOn -> spotlight connector (FUN_0045612c, alloc 0x2c).
//
class BTLightConnection:
	public BTRenderableBase
{
	public:
		BTLightConnection(
			Entity			*entity,
			int				execution_type,
			dpl_INSTANCE	*spot_instance,
			int				flag,
			int				*light_on);
};

//
//#############################################################################
// BTReticleRenderable  (BattleTech targeting reticle)
//#############################################################################
//
// File-private to btl4vid.cpp.  Parallels the engine ReticleRenderable
// (L4VIDRND.HPP) but the BT object is 0x358 bytes and carries up to 10 weapon
// pip / range markers built by AddWeapon.  Constructed @004cc40c for the inside
// view; weapon markers are appended by MakeMechRenderables via AddWeapon.
//
//   header layout actually touched (this+...):
//     +0x38   weaponCount (cap 10 -- "Tried to display too many weapons")
//     ...     parallel per-weapon arrays (see members below)
//     reticle geometry: minRange@0x230, maxRange@0x22c, rangeScale@0x234,
//                        originX@0x1fc, originY@0x200, scaleY@0x204, biasX@0x208
//
class BTReticleRenderable:
	public VideoRenderable
{
	public:
		//
		// PORT ctor.  The binary ctor (@004cc40c, 15 args) received the mech's
		// TargetReticle attribute + 8 HUD-subsystem attributes; its CALIBRATION
		// however is its own hardcoded constants (0.35/0.25/0.5/0.016/0.008 --
		// the pip-row origin/scale and the two tick-ladder frames), which this
		// port ctor reproduces directly.  The one live input the glyphs consume
		// is the target RANGE (drives the range-scale caret); it binds to a
		// Scalar the targeting step updates.  The 3D marker chain + the
		// PNAME1-8.bgf pip meshes (the floating 3D target designator) are a
		// separate deferred piece -- see phases/phase-02-dpl2d-reticle.md.
		//
		BTReticleRenderable(					// @004cc40c (port signature)
			Entity			*entity,
			Scalar			*range_attr);
		~BTReticleRenderable();

		//
		// Per-frame draw (the binary Execute @004cdcf0 is in an UN-EXPORTED
		// gap; the dynamics here are [T3]: draw the master reticle, then each
		// weapon's lit pip when its within-range flag is up, else the dark
		// ring).  Called from the render loop, cockpit view only.
		//
		void
			Draw(struct IDirect3DDevice9 *device);

		int		WeaponCount() const		{ return weaponCount; }

		//
		// Append a weapon range/pip marker to the reticle.  @004cdac0
		// Parameter order/count is the binary's.  The RECOVERED Execute
		// (@004cdcf0, task #37) fixed the two state attrs' semantics:
		//   param_9  = weapon attr 0x1c, the FIRE-CYCLE state: == const2 (2)
		//              -> LOADED (lit pip A); == const3 (3) -> charging (dark
		//              ring B).  Port source: rechargeLevel (>= 1.0 == loaded).
		//   param_12 = weapon attr 1, the DAMAGE state: == const1 (1)
		//              -> Destroyed (the pip is HIDDEN entirely).
		// weapon_mode is a GROUP BIT (Front=1/Rear=2/Left=4/Right=8) tested
		// against the Reticle element mask's low nibble.
		//
		void
			AddWeapon(
				Scalar			weapon_range,			// param_2
				int				pip_position,			// param_3
				int				*within_range_value,	// param_4  TargetWithinRange
				int				extended_range,			// param_5  PipExtendedRange
				Scalar			pip_red,				// param_6..8  PipColor
				Scalar			pip_green,
				Scalar			pip_blue,
				Scalar			*cycle_ready,			// param_9  (port: rechargeLevel)
				int				const2,					// param_10 (2 = loaded)
				int				const3,					// param_11 (3 = charging)
				int				*sim_state_value,		// param_12 weapon attr 1
				int				const1,					// param_13 (1 = destroyed)
				int				weapon_mode);			// param_14 (group bit)

	protected:
		//
		// Per-weapon parallel arrays (cap 10).  Slot mapping is the verified
		// store order of AddWeapon @004cdac0 (part_014.c:4827-4837) [T1];
		// semantics RECOVERED from the disassembled Execute @004cdcf0 (task
		// #37 [T1]): the caches are the change-detect state driving the pip
		// list rebuild; the constants are the fire-cycle/damage enum values.
		//
		int				weaponCount;					// +0x38
		int				weaponMode[10];					// +0x3c   group bit (F/R/L/R)
		int				stateConst3[10];				// +0x64   3 = charging (dark)
		int				stateConst2[10];				// +0x8c   2 = loaded (lit)
		int				stateConst1[10];				// +0xb4   1 = destroyed (hidden)
		int				alarmCache[10];					// +0xdc   port: LOADED flag cache
		int				simStateCache[10];				// +0x104  port: DESTROYED flag cache
		Scalar			*cycleReady[10];				// +0x130  port: rechargeLevel source
		int				*simStateAttr[10];				// +0x158  damage state (attr 1)
		int				*withinRangePtr[10];			// +0x18c  param_4 (stored; Execute
		int				withinRangeCache[10];			// +0x1b4    never reads it -- T1)
		dpl2d_DISPLAY	*pipDisplayListB[10];			// +0x288
		dpl2d_DISPLAY	*pipDisplayListA[10];			// +0x2b0

		//
		// Calibrated reticle geometry (the ctor's authentic constants).
		//
		Scalar			originX;						// +0x1fc  [0x7f] = 0.35
		Scalar			originY;						// +0x200  [0x80] = 0.25
		Scalar			scaleY;							// +0x204  [0x81] = 0.5
		Scalar			biasX;							// +0x208  [0x82] = 0.016
		Scalar			maxRange;						// +0x22c  [0x8b] = 1200
		Scalar			minRange;						// +0x230  [0x8c] = 0
		Scalar			rangeScale;						// +0x234  [0x8f]

		//
		// The reticle's own display lists (the ctor's [0x97..0xa1, 0xb6..0xba]
		// set, as transcribed) + the live range binding.
		//
		Scalar			*rangeAttr2;					// [0x89] the target-range source
		dpl2d_DISPLAY	*masterList;					// [0x98] the composed reticle
		dpl2d_DISPLAY	*simpleXList;					// [0x99] the minimal cross (PrimaryHudOn off)
		dpl2d_DISPLAY	*aimDotList;					// [0x9a] centre aim dot
		dpl2d_DISPLAY	*rangeCaretR;					// [0x9b] right-ladder caret
		dpl2d_DISPLAY	*rangeCaretB;					// [0x9c] bottom-ladder carets
		dpl2d_DISPLAY	*headingList;					// [0x9d] heading sub-list
		dpl2d_DISPLAY	*bottomAnchor;					// [0x9e] bottom-ladder anchor
		dpl2d_DISPLAY	*leftArrow;						// [0x9f] left range arrow
		dpl2d_DISPLAY	*rightArrow;					// [0xa0] right range arrow
		dpl2d_DISPLAY	*crossList;						// [0xa1] the dotted cross
		dpl2d_DISPLAY	*subB6;							// [0xb6] the composed weapon pips
		dpl2d_DISPLAY	*subB7;							// [0xb7] the lock-ring slot
		dpl2d_DISPLAY	*subB8;							// [0xb8] plain ring (simple-X mode)
		dpl2d_DISPLAY	*subB9;							// [0xb9] ring + cross (the LOCK ring)
		dpl2d_DISPLAY	*subBA;							// [0xba] the threat-direction trail

		// Recovered-Execute dynamic state (task #37)
		int				pipsBuilt;						// subB6 composed at least once
		int				lockShown;						// subB7 holds the ring (this[0x1dc])
		float			lockSpinDeg;					// the ring spin angle (this[0x240])
};

//
// The live reticle draw hook (called from the engine render loop after the
// 3D scene; draws only in the cockpit view).
//
extern void BTDrawReticle(struct IDirect3DDevice9 *device);

//
//#############################################################################
// BTL4VideoRenderer
//#############################################################################
//
// @ vtable: BattleTech L4 video renderer manager subclass.  Base is the MUNGA
// L4 renderer manager (DPLRenderer, l4video.hpp).  Parallels RPL4VideoRenderer.
//
	class BTL4VideoRenderer:
		public DPLRenderer
	{
	public:
		BTL4VideoRenderer(
			RendererRate			calibration_rate,
			RendererComplexity	calibration_complexity,
			RendererPriority		calibration_priority,
			InterestType			interest_type,
			InterestDepth			depth_calibration
		);
		~BTL4VideoRenderer();

		Logical
			TestInstance() const;

		//
		// Material substitution (mirrors RPL4VideoRenderer).
		//
		void
			SetupMaterialSubstitutionList(Entity *entity);		// @004d0cc0
		void
			TearDownMaterialSubstitutionList();					// @004d11e8

		//
		// RemakeEntity (damage-model swap).  The 1996 binary drives a per-entity
		// render state machine (Make / RemakeEntity / DestroyEntity -- the state
		// name strings survive at .rdata 0x4e3f20).  Only "Make" was ported (the
		// tree is built ONCE at entity creation, with fixed d3d_OBJECTs).
		// RemakeEntity is the model REFRESH: when a damage zone's graphic state
		// changes (a segment becomes Destroyed/Gone), re-pick each segment's video-
		// object variant by its zone graphic state (EntitySegment::GetVideoObjectName
		// is keyed by {skeleton, graphic_state}) and swap it onto the already-built
		// joint renderable IN PLACE (HierarchicalDrawComponent::Execute re-reads
		// graphicalObject each frame).  No teardown: the component dtor does not
		// cascade to children (L4VIDRND.cpp:104) so a rebuild would leak -- the
		// authentic behaviour is an in-place mesh swap.
		//
		void
			RemakeEntityRenderables(Entity *entity);

	protected:
		//
		// Per-mech render-tree bookkeeping so RemakeEntityRenderables can find each
		// segment's joint renderable (the dcs_array in MakeMechRenderables is local
		// and freed).  Keyed by Entity*; one entry per built mech.  segRenderable
		// maps a segment SLOT (EntitySegment::GetIndex) to its draw component;
		// segGState is the graphic state last applied to that slot so a swap only
		// reloads geometry on an actual state change.
		//
		struct MechRenderTree
		{
			int											skeletonType;	// EntitySegment::SkeletonType used at build
			int											viewSkeleton;	// the skeleton currently DISPLAYED (view toggle)
			HierarchicalDrawComponent					*rootRenderable;// the tree root (wreck hulk parent)
			int											wrecked;		// 1 = swapped to the <mech>dbr hulk
			DPLStaticChildRenderable					*wreckHulk;		// the <mech>dbr piece (sinks)
			DPLStaticChildRenderable					*wreckDebris;	// the ldbr scatter (sinks)
			float										wreckAge;		// seconds since the swap
			std::map<int, HierarchicalDrawComponent*>	segRenderable;	// slot -> joint renderable
			std::map<int, int>							segGState;		// slot -> last applied graphic state
		};
		std::map<Entity*, MechRenderTree>	mMechRenderTrees;

	public:
		//
		// The wreck's quadratic SINK (the 1996 script's burial: FUN_00456410
		// computes offsetY = rate * t^2; the hulk's authored rate is -0.025 ->
		// the ~7-unit hulk is fully buried ~17s after the kill).  Ticked per
		// frame from the dead mech's UpdateDeathState.  Returns 0 once the
		// wreck is fully buried (the caller stops the wreck-smoke re-arm),
		// 1 while anything is still visible.
		//
		int
			TickWreck(Entity *victim, float dt);

		//
		// VIEW TOGGLE: the player's mech builds BOTH cameras -- the authentic
		// cockpit eyepoint (DPLEyeRenderable at 'siteeyepoint', the pod's only
		// view) and the port's external chase camera (a usability addition for
		// the windowed build).  SetViewInside switches the renderer's live
		// camera between them (V key via the BTSetViewInside bridge).
		//
		void
			SetViewInside(int inside);

	protected:
		DPLEyeRenderable	*mEyeCockpit;		// the authentic cockpit eyepoint
		DPLEyeRenderable	*mEyeChase;			// the external chase camera
		int					mViewInside;		// the pilot's chosen view (V/state);
												// survives renderable REBUILDS (the
												// chase-eye build used to stomp
												// mCamera back to chase every remake)

	public:
		//
		// The death-wreck swap (ExplosionScripts effect 104, reconstructed): the
		// victim's whole visual becomes its burning hulk -- hide every segment
		// mesh and hang "<prefix>dbr.bgf" (gendbr.bgf fallback) on the tree root.
		// The 1996 script hardcoded thrdbr.bgf (a dev shortcut; every mech ships
		// its own *DBR hulk).  If the victim's tree isn't built yet, the swap is
		// remembered and applied when MakeMechRenderables builds it.
		//
		void
			SwapToWreck(Entity *victim);

	protected:
		//
		// Renderer-manager overrides
		//
		void
			LoadMissionImplementation(Mission *mission);

		void
			MakeEntityRenderables(								// @004d0774
				Entity					*this_entity,
				ResourceDescription	*model_resource,
				ViewFrom					type);

		HierarchicalDrawComponent*
			MakeMechRenderables(								// @004cef28
				Entity					*entity,
				ResourceDescription	*model_resource,
				ViewFrom					type);

		//
		//--------------------------------------------------------------------
		// BT renderer helpers (recovered).
		//--------------------------------------------------------------------
		//
		Scene*
			GetScene();											// FUN_0045a724

		dpl_VIEW*
			GetMainView()	{ return dplMainView; }			// this[0x124]
		dpl_ZONE*
			GetMainZone()	{ return dplMainZone; }			// this[300]

		d3d_OBJECT*
			LoadObject(const char *object_name);			// FUN_00498448

		void
			AttachToEyeDCS(dpl_DCS *root_dcs, LinearMatrix &local_to_world);	// FUN_00489cec

		void
			AddDynamicRenderable(VideoComponent *renderable, Entity *entity);	// FUN_0045a994

	protected:
		//
		// vtvCount / vtvsExpected analogues (RPL4VideoRenderer).
		//
		int
			mechsExpected,
			mechCount;

		//
		// True once the linked (player) entity exists, so its root DCS is
		// hooked onto the eye.  ([0xb0] in the recovered layout.)
		//
		Logical
			linkedEntityPresent;

		//
		// Effect zones touched by the recovered code.
		//
		dpl_ZONE
			*scene_zone,		// [0x318]  coolant / reservoir effects
			*tracer_zone;		// [0x2e4]  projectile tracers
	};

//
// Sim-side bridge to RemakeEntityRenderables.  MechDeathHandler (sim TU) calls
// this when a mech's damage graphic state changes; defined in btl4vid.cpp, it
// reaches the live renderer via l4_application->GetVideoRenderer().  A free
// function so the sim TU needs no renderer header (just this extern).
//
extern void BTRemakeMechModel(Entity *entity);

//
// Render bridge for the death-wreck swap (effect 104): called by the engine's
// ExplosionClassID dispatch with the explosion's entityHit (the dead mech).
//
extern void BTSwapMechToWreck(Entity *victim);

//
// Per-frame wreck sink tick (sim -> render bridge; called by the dead mech's
// UpdateDeathState with the frame dt).  Returns 0 once the wreck is fully
// buried -- the caller stops re-arming the wreck smoke.
//
extern int BTWreckSinkTick(Entity *victim, float dt);

//
// View-mode bridge (the V key): 1 = the authentic cockpit eyepoint,
// 0 = the external chase camera.
//
extern void BTSetViewInside(int inside);

#endif // BTL4VID_HPP

//===========================================================================//
