//============================================================================//
// File:	projtile.cpp                                                         //
// Project: BattleTech																			//
// Contents: In-flight projectile entity base (Projectile : Entity)            //
//----------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved  //
//                   	PROPRIETARY AND CONFIDENTIAL                          //
//============================================================================//
//
// RECONSTRUCTED from the shipped binary.  There is NO surviving projtile.cpp --
// only PROJTILE.TCP (Projectile::TestClass).  Behaviour follows the Ghidra
// pseudo-C in part_013.c (@004be1bc ctor, @004be358 dtor, @004be384 allocator,
// @004be3b4 TestInstance).  Each non-trivial method cites the originating @ADDR.
//
// Coverage:
//   confident : ctor @004be1bc (Entity base chain, world/segment registration,
//               launch-kinematics snapshot, Performance install), dtor thunk
//               @004be358, allocator @004be384, TestInstance @004be3b4
//   best-effort: the names of the descriptor-sourced members (+0x300..+0x33C)
//               and the modelUpdate binding (+0x250/+0x254); MoveAndCollide
//               (the base Performance variants PTR_LAB_005129e8/f4 -- bodies sit
//               past the captured window; the live integrator that ships is the
//               Missile override @004bef78)
//   excluded   : the 0x41xxxx/0x42xxxx Entity engine vtable slots and the base
//               Entity ctor @004234f0
//
// Helper-function name mapping (engine internals referenced by the decomp):
//      FUN_004234f0  Entity base constructor (ctx, default record)
//      FUN_0041db7c  init embedded connection/descriptor sub-object
//      FUN_0041bbd8  AlarmIndicator::SetLevel       FUN_0041a1a4  IsDerivedFrom
//      FUN_00408440  Point3D / Vector3D copy        FUN_00433ed4  motion-source lookup
//      FUN_00402298  operator new                   FUN_004022d0  operator delete
//      DAT_004efc94  global game/world context block
//

#include <bt.hpp>
#pragma hdrstop

#if !defined(PROJTILE_HPP)
#	include <projtile.hpp>
#endif
#if !defined(TESTBT_HPP)
#	include <testbt.hpp>
#endif

//###########################################################################
//###########################################################################
//					Projectile
//###########################################################################
//###########################################################################

//#############################################################################
// Decompiler-artifact stand-ins (label/record operands).
//
static char	DAT_005129b0[0x340] = { 0 };	// default streamed-entity record

//#############################################################################
// Shared Data Support
//
Derivation
	Projectile::ClassDerivations(
		Entity::GetClassDerivations(),
		"Projectile"
	);

Receiver::MessageHandlerSet		Projectile::MessageHandlers;
Simulation::AttributeIndexSet	Projectile::AttributeIndex;

Projectile::SharedData
	Projectile::DefaultData(
		&Projectile::ClassDerivations,
		Projectile::MessageHandlers,
		Projectile::AttributeIndex,
		Projectile::StateCount,
		&Projectile::Make
	);

//
// Entity factory hook (Entity::SharedData requires a MakeHandler).  Projectiles
// are spawned through New(), not the generic Make() path, so this is a stub.
//
Entity *Projectile::Make(MakeMessage * /*creation_message*/)
{
	return 0;
}

//#############################################################################
// Allocation
//
// @004be384  Projectile::New(spawn_context) -- allocate 0x340 bytes and
// construct with the default streamed-entity record &DAT_005129b0.
//
Projectile *Projectile::New(int spawn_context)
{
	Projectile *p = (Projectile *)::operator new(0x340);		// FUN_00402298
	if (p != 0)
		p = new(p) Projectile(spawn_context, DAT_005129b0);		// @004be1bc
	return p;
}

//#############################################################################
// Construction
//
// @004be1bc  Projectile::Projectile(int owner, const char *spawn_descriptor)
//
//   1. Chains the Entity base ctor and stamps the Projectile vtable.
//   2. Initialises the embedded segment/connection sub-object (this+0x30C),
//      flags itself as a transient/guided entity (flags |= 0x10) and raises its
//      status alarm to level 2 (in-flight).
//   3. Registers itself into the owning world's segment table (the segment is
//      resolved from descriptor+0x84 indexing world+0x128) and snapshots the
//      launch kinematics out of the spawn descriptor:
//         segmentIndex   <- descriptor +0x84
//         sourceEntity   <- descriptor +0x88
//         sourceWeapon   <- descriptor +0x8C
//         launchPosition <- descriptor +0x90   (Point3D)
//         launchVelocity <- descriptor +0x9C   (Vector3D)
//         aimDirection   <- descriptor +0xA8   (Vector3D)
//         modelIndex     <- descriptor +0xB4
//         damageRecord   <- descriptor +0xB8
//   4. Binds the per-frame model-update callback (this[0x94..0x97],
//      PTR_FUN_005129dc) and installs the active Performance: the authoritative
//      integrator (PTR_LAB_005129e8) when flags&0xC==4, else the ghost variant
//      (PTR_LAB_005129f4).  Finally fires vtable slot 14 (initial placement).
//
Projectile::Projectile(int owner, const char *spawn_descriptor)
:
	Entity((Entity::MakeMessage *)0, DefaultData)			// @004234f0
{
	(void)owner;
	// vtable installed by the compiler (PTR_FUN_00512a5c)

	InitSegmentLink(&this->sourceEntity);					// @0041db7c (this+0x30C)
	simulationFlags |= 0x10;								// transient / projectile
	SetStatusLevel(2);										// FUN_0041bbd8(this+0xb, 2) -- in flight

	// register into the world segment table
	world          = ResolveWorld(DAT_004efc94);			// this[0xC0] @0x300
	segmentIndex   = *(int *)(spawn_descriptor + 0x84);		// this[0xC2] @0x308
	currentSegment = *(Segment **)(*(int *)((int)world + 0x128) + segmentIndex * 4);	// this[0xC1] @0x304

	sourceEntity   = *(int *)(spawn_descriptor + 0x88);		// this[0xC3] @0x30C
	sourceWeapon   = *(int *)(spawn_descriptor + 0x8C);		// this[0xC4] @0x310
	modelIndex     = *(int *)(spawn_descriptor + 0xB4);		// this[0xCE] @0x338
	launchPosition = *(Point3D  *)(spawn_descriptor + 0x90);	// this[0xC5] @0x314  FUN_00408440
	launchVelocity = *(Vector3D *)(spawn_descriptor + 0x9C);	// this[0xC8] @0x320  FUN_00408440
	aimDirection   = *(Vector3D *)(spawn_descriptor + 0xA8);	// this[0xCB] @0x32C  FUN_00408440
	damageRecord   = *(int *)(spawn_descriptor + 0xB8);		// this[0xCF] @0x33C

	// model-update callback context + bound member function pointer triple
	modelUpdateContext = ResolveMotionSource(DAT_004efc94);	// this[0x94] @0x250 (FUN_00433ed4)
	BindModelUpdate(0);										// this[0x95..0x97] @0x254 (PTR_FUN_005129dc)

	// select the per-frame integrator
	if ((simulationFlags & 0xC) == 4)
		SetPerformance(/*authoritative*/ &Projectile::MoveAndCollide);	// PTR_LAB_005129e8
	else
		SetPerformance(/*ghost*/         &Projectile::MoveAndCollide);	// PTR_LAB_005129f4

	// slot 14 (initial placement): a virtual call in the shipped ctor; the
	// concrete placement runs through the installed vtable at run time.
}

//#############################################################################
// Destruction
//
// @004be358  ~Projectile() -- restores the Projectile vtable then chains the
// Entity base destructor (which un-registers from the segment table).
//
Projectile::~Projectile()
{
	// vtable reset + base ~Entity chaining handled by the compiler.
}

//#############################################################################
// MoveAndCollide -- default per-frame integrator (best-effort)
//
//   The base Projectile integrate-and-collide bodies (PTR_LAB_005129e8 /
//   005129f4) lie past the captured decomp window.  The concrete behaviour that
//   ships for the only instantiated subclass is the Missile override
//   (Missile::MoveAndCollide @004bef78): age the projectile, integrate motion
//   (FUN_00421b2c) and advance the model (FUN_00421bac), run the world collision
//   query (FUN_0042291c) and -- on a hit -- spawn the explosion/Damage entity and
//   retire (FUN_0042061c).  Declared here to anchor the vtable slot; the body is
//   reconstructed structurally.
//
void Projectile::MoveAndCollide(Scalar time_slice)
{
	IntegrateMotion();			// FUN_00421b2c
	AdvanceModel(time_slice);	// FUN_00421bac
	Entity *hit = CollisionQuery(time_slice);	// FUN_0042291c
	if (hit != 0)
		Retire();				// FUN_0042061c
}

//#############################################################################
// TestInstance
//
// @004be3b4  IsDerivedFrom(ClassDerivations tag 0x512980).
//
Logical Projectile::TestInstance() const
{
	return IsDerivedFrom(Projectile::ClassDerivations);
}

//#############################################################################
// TestClass  (PROJTILE.TCP)
//
//   Surviving fragment: takes no argument and returns True.
//
Logical Projectile::TestClass()
{
	return True;
}

//#############################################################################
// Recovered internal helpers (engine-internal world/segment/motion plumbing).
// These wrap Entity-engine operations the decompiler rendered as raw FUN_*
// calls; reconstructed as structural stubs.
//
void	Projectile::InitSegmentLink(void * /*link*/)			{}
void	Projectile::SetStatusLevel(int /*level*/)				{}
void   *Projectile::ResolveWorld(void * /*app_context*/)		{ return 0; }
void   *Projectile::ResolveMotionSource(void * /*app_context*/)	{ return 0; }
void	Projectile::BindModelUpdate(void * /*callback*/)		{}
void	Projectile::IntegrateMotion()							{}
void	Projectile::AdvanceModel(Scalar /*time_slice*/)			{}
Entity *Projectile::CollisionQuery(Scalar /*time_slice*/)		{ return 0; }
void	Projectile::Retire()									{}
