//============================================================================//
// File:	misthrst.cpp                                                         //
// Project: BattleTech																			//
// Contents: Missile Thruster -- provides thrust/burn to move a Missile        //
//----------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved  //
//                   	PROPRIETARY AND CONFIDENTIAL                          //
//============================================================================//
//
// RECONSTRUCTED from the shipped binary.  The class declaration survives intact
// (MISTHRST.HPP); behaviour follows the Ghidra pseudo-C in part_013.c
// (@004be7c4 ctor, @004be8bc dtor, @004be8e8 TestInstance, @004bf8ec streamed
// resource parser).  Each non-trivial method cites the originating @ADDR.
//
// Coverage:
//   confident : ctor @004be7c4, dtor thunk @004be8bc, TestInstance @004be8e8,
//               CreateStreamedSubsystem @004bf8ec
//   best-effort: MissileThrusterSimulation -- the Performance method pointer is
//               installed by the ctor (PTR_LAB_00512b20) but its standalone body
//               is folded into the host Missile::MoveAndCollide @004bef78 (which
//               samples the thruster acceleration buffer at missile+0x234/+0x250).
//   excluded   : the 0x41xxxx engine vtable slots (pure Subsystem base behaviour;
//               vtable @00512bbc overrides only slot0 = destructor)
//
// Decoded constant:
//      _DAT_004be8b8 = 0x3c8efa35 = 0.0174533f   (PI/180, Degree -> Radian)
//
// Helper-function name mapping (engine internals referenced by the decomp):
//      FUN_0041c52c  Subsystem base constructor (owner, id, name, sharedData)
//      FUN_00408440  Vector3D copy
//      FUN_0041bbd8  AlarmIndicator::SetLevel
//      FUN_0041a1a4  IsDerivedFrom(classDerivations)
//      FUN_004238bc  Entity model-record base parser
//      FUN_00404118  read "<section>"/"<key>" Scalar from notation file
//      FUN_00406db4  register parsed model record (type 0xf)
//      FUN_004dbb24/FUN_004d9c38  error-string accumulate / flush
//

#include <bt.hpp>
#pragma hdrstop

#if !defined(MISTHRST_HPP)
#	include <misthrst.hpp>
#endif
#if !defined(MISSILE_HPP)
#	include <missile.hpp>
#endif
#if !defined(TESTBT_HPP)
#	include <testbt.hpp>
#endif

//
// Degree -> Radian scale factor, read as a read-only global in the decomp.
//
static const Scalar DegreesToRadians = 0.0174533f;	// _DAT_004be8b8 (0x3c8efa35)

//
// Unrecovered streamed-resource helper stand-ins (engine model-record parser).
//
static int  ParseEntityModel(NotationFile*, const char*, NotationFile*, const ResourceDirectories*, float*) { return 0; }	// FUN_004238bc
static int  ReadScalar(NotationFile*, const char*, const char*, float*) { return 1; }	// FUN_00404118
static void ReportMissing(const char*, const char*) {}									// FUN_004dbb24
static void SetStatusLevel(int) {}														// FUN_0041bbd8 (status alarm)
static int  RegisterModelRecord(NotationFile*, const char*, int, int, int, float*, int, int) { return 0; }	// FUN_00406db4

//###########################################################################
//###########################################################################
//					MissileThruster
//###########################################################################
//###########################################################################

//#############################################################################
// Shared Data Support
//
Derivation
	MissileThruster::ClassDerivations(
		Subsystem::GetClassDerivations(),
		"MissileThruster"
	);

Receiver::MessageHandlerSet		MissileThruster::MessageHandlers;

const MissileThruster::IndexEntry
	MissileThruster::AttributePointers[] = { { 0, 0, 0 } };	// IndexEntry table @00512ad0

MissileThruster::AttributeIndexSet	MissileThruster::AttributeIndex;

MissileThruster::SharedData
	MissileThruster::DefaultData(				// resolved as &DAT_00512a9c
		&MissileThruster::ClassDerivations,
		MissileThruster::MessageHandlers,
		MissileThruster::AttributeIndex,
		MissileThruster::StateCount
	);

//#############################################################################
// Construction
//
// @004be7c4  MissileThruster::MissileThruster(Missile *owner, int id,
//                                             SubsystemResource *resource)
//
//   Chains the Subsystem base ctor, stamps the MissileThruster vtable, copies the
//   four streamed tuning scalars (converting the rotation rate from degrees to
//   radians), then primes the base thrust acceleration vector to point "down the
//   tail" by thrusterAcceleration.  The active Performance (the per-frame burn /
//   steer simulation) is installed unconditionally to PTR_LAB_00512b20; the
//   authoritative-vs-ghost guard (owner flags & 0xC) selects an alternate entry
//   that the final write overwrites in the shipped code.
//
MissileThruster::MissileThruster(
	Missile				*owner,
	int					subsystem_ID,
	SubsystemResource	*subsystem_resource)
:
	// Subsystem base ctor: owner, id, resource, shared default record.
	Subsystem(owner, subsystem_ID, subsystem_resource, DefaultData)
{
	// vtable installed by the compiler (PTR_LAB_00512bbc)

	burnTimeRemaining		= subsystem_resource->burnTime;				// @0xE4  resource +0x30
	thrusterAcceleration	= subsystem_resource->thrusterAcceleration;	// @0xEC  resource +0x38
	thrusterClimb			= subsystem_resource->thrusterClimb;		// @0xF0  resource +0x3C

	// install the burn/steer Performance (only the conditional guard differs for
	// remote ghosts; the unconditional store @004be7c4 is what actually runs).
	if ((owner->simulationFlags & 0xC) != 4)				// owner+0x28 & 0xC
		SetPerformance(/*ghost*/ &MissileThruster::MissileThrusterSimulation);
	SetPerformance(&MissileThruster::MissileThrusterSimulation);	// PTR_LAB_00512b20

	// Degree -> Radian  (Radian::operator=(const Degree&) performs the scaling)
	maxThrusterRotationRate = subsystem_resource->maxThrusterRotationRate;	// @0xE8

	SetStatusLevel(1);										// FUN_0041bbd8(this+0xb, 1)

	// base acceleration = (0,0,-thrusterAcceleration), angular = (0,0,0)
	acceleration.linearMotion  = Vector3D(0.0f, 0.0f, 0.0f);	// @0xF4  FUN_00408440(.., &DAT_004e0f74)
	acceleration.linearMotion.z -= thrusterAcceleration;	// @0xFC  this[0x3f] -= this[0x3b]
	acceleration.angularMotion = Vector3D(0.0f, 0.0f, 0.0f);	// @0x100 FUN_00408440(.., &DAT_004e0f74)
}

//#############################################################################
// Destruction
//
// @004be8bc  ~MissileThruster() -- restores the vtable then chains the Subsystem
// base destructor.  (Reached through the scalar-deleting thunk.)
//
MissileThruster::~MissileThruster()
{
	// vtable reset + base ~Subsystem chaining handled by the compiler.
}

//#############################################################################
// MissileThrusterSimulation -- the registered Performance (best-effort)
//
//   The thruster's per-frame work (bleed burnTimeRemaining by the time slice and
//   contribute its acceleration to the missile's velocity) is, in the shipped
//   binary, executed from inside Missile::MoveAndCollide @004bef78 which reads
//   this->acceleration directly out of the roster.  No distinct @ADDR body
//   survives for the standalone Performance; the reconstruction below mirrors
//   that folded behaviour and the MISTHRST.HPP signature.
//
void MissileThruster::MissileThrusterSimulation(Scalar time_slice)
{
	if (burnTimeRemaining <= 0.0f)
		return;									// fuel exhausted -- coast

	burnTimeRemaining -= time_slice;
	if (burnTimeRemaining < 0.0f)
		burnTimeRemaining = 0.0f;

	// thrust is applied to the host Missile's motion by the integrator
	// (Missile::MoveAndCollide), which scales this->acceleration by the slice.
}

//#############################################################################
// Streamed resource parser
//
// @004bf8ec  MissileThruster::CreateStreamedSubsystem(...)
//
//   Parses the thruster tuning block out of the "gamedata" section of the
//   notation file into the (0x54-byte) shared model record and registers it as
//   a type-0xF entity-model resource.  Field order in the record:
//      +0x40 MaxThrusterRotationRate   +0x44 BurnTime   +0x48 ThrusterAcceleration
//      +0x4C ThrusterClimb             +0x50 SplashRadius
//   The Missile ctor later copies +0x40..+0x4C into the thruster's
//   SubsystemResource (+0x34/+0x30/+0x38/+0x3C).  Any missing key is fatal.
//
int MissileThruster::CreateStreamedSubsystem(
	NotationFile				*model_file,
	const char					*model_name,
	NotationFile				*subsystem_file,
	const ResourceDirectories	*directories,
	SubsystemResource			*subsystem_resource)
{
	Logical		owned_record = (subsystem_resource == 0);
	float		*record = (float *)subsystem_resource;
	if (owned_record)
		record = (float *)::operator new(0x54);				// FUN_00402298(0x54)

	// Entity model-record base parse.
	if (ParseEntityModel(model_file, model_name, subsystem_file, directories, record) == -1)
		goto fail;

	if (!ReadScalar(subsystem_file, "gamedata", "BurnTime", record + 0x11))		// +0x44
		{ ReportMissing(model_name, "Missing BurnTime"); goto fail; }
	if (!ReadScalar(subsystem_file, "gamedata", "MaxThrusterRotationRate", record + 0x10))	// +0x40
		{ ReportMissing(model_name, "MaxThrusterRotationRate Missing"); goto fail; }
	if (!ReadScalar(subsystem_file, "gamedata", "ThrusterAcceleration", record + 0x12))	// +0x48
		{ ReportMissing(model_name, "ThrusterAcceleration Missing"); goto fail; }
	if (!ReadScalar(subsystem_file, "gamedata", "ThrusterClimb", record + 0x13))		// +0x4C
		{ ReportMissing(model_name, "ThrusterClimb missing"); goto fail; }
	if (!ReadScalar(subsystem_file, "gamedata", "SplashRadius", record + 0x14))		// +0x50
		{ ReportMissing(model_name, "Splash Radius missing"); goto fail; }

	if (owned_record)
	{
		int handle = RegisterModelRecord(model_file, model_name, 0xF, 1, 0, record, 0x54, -1);
		::operator delete(record);
		return handle;
	}
	return 1;

fail:
	if (owned_record)
		::operator delete(record);
	return -1;
}

//#############################################################################
// TestInstance
//
// @004be8e8  IsDerivedFrom(ClassDerivations tag 0x512aac).
//
Logical MissileThruster::TestInstance() const
{
	return IsDerivedFrom(MissileThruster::ClassDerivations);
}

//#############################################################################
// TestClass  (MISTHRST.HPP / no surviving .TCP)
//
Logical MissileThruster::TestClass(Missile &)
{
	return True;
}
