//===========================================================================//
// File:	heatfamily_reslice.cpp                                              //
// Project: BattleTech           Brick: Entity Manager                        //
// Contents: Re-slice -- Condenser completion + HeatWatcher / Reservoir /     //
//           aggregate-HeatSink bodies recovered from the heat-family gap.    //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//
//
// RECONSTRUCTED from heatfamily_gap.c (Ghidra pseudo-C, 0x4ae4d8-0x4afbe0).
// Names follow heat.cpp + the surviving DATA-section strings.  Each method
// cites its @ADDR.  Helper-function mapping is identical to heat.cpp; the
// additions used here:
//      FUN_004adda0  HeatSink base constructor            (heat.cpp @004adda0)
//      FUN_004adfd4  ~HeatSink                            (heat.cpp @004adfd4)
//      FUN_004ad748  HeatSink simulation step (vtbl slot9)
//      FUN_004ad7d4  HeatModelActive()
//      FUN_004ad7f0  HeatSink::UpdateHeatLoad
//      FUN_004ac644  HeatableSubsystem base constructor   (heat.cpp)
//      FUN_004ac0bc  Subsystem slot-9 base impl
//      FUN_004ac22c  Subsystem::ResetToInitialState
//      FUN_004ae150  HeatSink::CreateStreamedSubsystem    (heat.cpp)
//      FUN_004ac9ec  HeatableSubsystem::CreateStreamedSubsystem
//      FUN_004ae150/004040d8/00404118/00404088  NotationFile reads
//      FUN_00417ab4  SharedData::Resolve()
//      FUN_0041b9ec  AlarmIndicator(levels)   FUN_0041bbd8  AlarmIndicator::SetLevel
//      FUN_004dca38  expf()   FUN_004dcd00  fabsf()
//      FUN_0041a1a4  IsDerivedFrom(classDerivations)
//

#include <bt.hpp>
#pragma hdrstop

#if !defined(HEAT_HPP)
#	include <heat.hpp>
#endif
#if !defined(HEATFAMILY_RESLICE_HPP)
#	include <heatfamily_reslice.hpp>
#endif

#define JM_CLOSE_ENOUGH	0.0001f		// _DAT_004ae8ac / _DAT_004af3a0

//
// Recovered .data constants (best-effort; exact double bit-patterns folded to
// decimal where known).  TODO: confirm against section_dump.txt.
//
static const Scalar CoolantCapacityScale = 1.738f;	// _DAT_004af518 (~ master-sink capacity scale)

//###########################################################################
//###########################################################################
//					Condenser   (COMPLETION of heat.cpp best-effort)
//###########################################################################
//###########################################################################
//
// vtable @0050ed88   ctor @4ae568   dtor @4ae5fc   classID 0x0BBD : HeatSink
//

//
// @4ae568 -- chains the HeatSink base ctor, installs the Condenser vtable,
// captures the refrigeration factor, opens the valve, and derives the
// condenser number from the trailing digit of the instance name.
//
Condenser::Condenser(
	Mech				*owner,
	int					subsystem_ID,
	SubsystemResource	*subsystem_resource,
	SharedData			&shared_data
):
	HeatSink(owner, subsystem_ID, subsystem_resource, shared_data),	// FUN_004adda0(...,&DAT_0050e4ec,0,0)
	condenserAlarm(3)								// FUN_0041b9ec(this+0x77, 3)
{
	// *this = &PTR_FUN_0050ed88;
	valveState			= 1;					// this[0x74] @0x1D0
	coolantFlowScale	= 0.0f;					// this[0x57] @0x15C (== "word57")
	refrigerationOutput	= subsystem_resource->refrigerationFactor;	// this[0x58] = res +0xFC
	refrigerationFactor	= refrigerationOutput;	// this[0x76] @0x1D8
	simulationFlags		|= 0x8;					// this[10] |= 8  (participates in heat model)

	// condenserNumber = atoi(lastChar(name)):
	//   pcVar1 = GetName();                      this[0x35]
	//   iVar2  = strlen(pcVar1);                 FUN_004d4a78
	//   condenserNumber = atoi(pcVar1+iVar2-1);  FUN_004dd1e0  (this[0x75] @0x1D4)
	condenserNumber = NameTrailingNumber(GetName());

	Check_Fpu();
}

//
// @4ae5fc
//
Condenser::~Condenser()			// FUN_004ae5fc(this, byte deleteFlag)
{
	Check(this);
	// condenserAlarm (@0x77) and the HeatSink base chain are torn down
	// automatically; the decompiler's explicit member/base dtor calls are
	// compiler-generated artifacts and are intentionally not reproduced.
	Check_Fpu();
}

Logical
	Condenser::TestInstance() const			// @4ae63c
{
	return IsDerivedFrom(*GetClassDerivations());	// FUN_0041a1a4(**this[3], 0x50e4fc)
}

Logical
	Condenser::TestClass(Mech &)
{
	return True;
}

//
// @4ae4d8 -- vtable slot 9 (the per-frame step).  Computes the refrigeration
// output as a function of how much heat the master heat-sink has stored, then
// runs the base HeatSink step.
//
//   refrigerationOutput = (1.0f - master->heatEnergy) * refrigerationFactor;
//   if (refrigerationOutput < 1.0f) refrigerationOutput = 1.0f;   // clamp
//   HeatSink::Step(time_slice);                                   // FUN_004ad748
//
void
	Condenser::RefrigerationSimulation(Scalar time_slice)	// FUN_004ae4d8
{
	HeatSink *master = (HeatSink *)linkedSinks.Resolve();	// this[0x38]
	refrigerationOutput =
		(1.0f - master->heatEnergy)				// _DAT_004ae530 = 1.0f ; master +0x158
		* refrigerationFactor;					// this[0x76]
	if (refrigerationOutput < 1.0f)				// _DAT_004ae530
	{
		refrigerationOutput = 1.0f;
	}
	HeatSink_Step(time_slice);					// FUN_004ad748(this, time_slice)
}

//
// @4afbe0 -- BEST-EFFORT.  The "MoveValve" handler: cycles a 3-position valve
// (0->1->2->0), tells the model (virtual slot 18, vtable+0x48) the new
// setting, and re-points two HUD graphic gauges on the owner.  Mapped to the
// Condenser by its "MoveValve"/"ValveSetting" strings and the valveState word.
//
void
	Condenser::MoveValve(int message)			// FUN_004afbe0
{
	if (message /*param_2->count*/ > 0)
	{
		valveState = (valveState + 1) % 3;		// this[100] cycle 0..2
		SetValveSetting(valveState);			// (**(*this+0x48))(this, valveState)

		// HUD gauge re-point: owner->field_438 / owner->field_5b4 are the two
		// cockpit graphic gauges this valve drives.  Resolving them is a Mech
		// game-layer concern (CROSS-FAMILY); the branch bodies below are stubs.
		(void)this->owner;						// this[0x34]
		void *gaugeA = 0;
		void *gaugeB = 0;
		(void)gaugeA; (void)gaugeB;
		if (valveState == 0)
		{
			// gaugeA: reset + select primary read-out, signal gaugeB.
			// (offsets +0x1f0/+0x274/+0x220/+0x224/+0x228/+0x22c/+0x2a0)
		}
		else /* valveState == 1 || 2 */
		{
			// gaugeA: select alternate read-out (+0x230/+0x234).
		}
	}
}

//
// @4ae658 -- parse the Condenser resource record.  Stamps classID 0x0BBD and
// model size 0x100, defaults RefrigerationFactor to -1.0f on pass 1, then
// requires "RefrigerationFactor".
//
int
	Condenser::CreateStreamedSubsystem(
		NotationFile				*model_file,
		const char					*model_name,
		const char					*subsystem_name,
		SubsystemResource			*subsystem_resource,
		NotationFile				*subsystem_file,
		const ResourceDirectories	*directories,
		int							passes
	)
{
	if (!HeatSink::CreateStreamedSubsystem(			// FUN_004ae150
			model_file, model_name, subsystem_name,
			subsystem_resource, subsystem_file, directories, passes))
	{
		return False;
	}

	subsystem_resource->classID            = RegisteredClass::CondenserClassID;	// res +0x20
	subsystem_resource->subsystemModelSize = 0x100;		// res +0x24

	if (passes == 1)
	{
		subsystem_resource->refrigerationFactor = -1.0f;	// res +0xFC, bytes bf 80 00 00
	}

	if (!model_file->GetEntry(subsystem_name, "RefrigerationFactor",
			&subsystem_resource->refrigerationFactor)
		&& subsystem_resource->refrigerationFactor == -1.0f)	// _DAT_004ae708
	{
		DebugStream << subsystem_name << " missing RefrigerationFactor!";
		return False;
	}

	Check_Fpu();
	return True;
}


//###########################################################################
//###########################################################################
//					HeatWatcher      (base of powersub's PowerWatcher)
//###########################################################################
//###########################################################################
//
// vtable @0050ed10  ctor @4aeb40  dtor @4aebe8  classID HeatWatcherClassID
//   : HeatableSubsystem      (PowerWatcher @4b18a4 chains to this ctor)
//

//#############################################################################
// Shared Data Support
//
HeatWatcher::SharedData
	HeatWatcher::DefaultData(
		HeatWatcher::GetClassDerivations(),
		HeatWatcher::GetMessageHandlers(),
		HeatWatcher::GetAttributeIndex(),
		HeatWatcher::StateCount
	);

Derivation*
	HeatWatcher::GetClassDerivations()
	{
		static Derivation classDerivations(HeatableSubsystem::GetClassDerivations(), "HeatWatcher");
		return &classDerivations;
	}

//
// @4aeb40 -- "PowerWatcher base ctor".  Builds on the HeatableSubsystem base,
// installs the HeatWatcher vtable, captures the degradation/failure setpoints
// and the watched-subsystem index, builds the 3-level alarm, and (for a master
// instance) registers the WatchSimulation performance.
//
HeatWatcher::HeatWatcher(
	Mech				*owner,
	int					subsystem_ID,
	SubsystemResource	*subsystem_resource,
	SharedData			&shared_data
):
	MechSubsystem(owner, subsystem_ID, subsystem_resource, shared_data),	// FUN_004ac644(...,0,0) -- MechSubsystem base
	watchedLink()							// FUN_004af9cf(this+0x45, 0) (vtbl 0050eccc)
{
	// *this = &PTR_FUN_0050ed10;
	degradationTemperature	= subsystem_resource->degradationTemperature;	// this[0x48] = res +0xE8
	failureTemperature		= subsystem_resource->failureTemperature;		// this[0x49] = res +0xEC
	watchedSubsystem		= subsystem_resource->watchedSubsystem;			// this[0x4a] = res +0xE4
	heatAlarm.Initialize(3);				// FUN_0041b9ec(this+0x4b, 3)

	if ((subsystem_resource->subsystemFlags & SegmentCopyMask) == 0			// (flags & 0xC) == 0
		&& (subsystem_resource->subsystemFlags & MasterHeatSinkFlag) != 0)	// flags & 0x100
	{
		SetPerformance(&HeatWatcher::WatchSimulation);	// this[7..9] = PTR 0050e634 -> @4aeac4
	}
	Check_Fpu();
}

//
// @4aebe8
//
HeatWatcher::~HeatWatcher()			// FUN_004aebe8
{
	Check(this);
	// heatAlarm (@0x4b) / watchedLink (@0x45) and the HeatableSubsystem base
	// chain destruct automatically; the decompiler's explicit dtor calls are
	// compiler-generated artifacts and are not reproduced.
	Check_Fpu();
}

Logical	HeatWatcher::TestInstance() const	// @4aec38 -> IsDerivedFrom 0x50e604
{
	return IsDerivedFrom(*GetClassDerivations());
}
Logical	HeatWatcher::TestClass(Mech &)	{ return True; }

//
// @4aea84 -- slot 9 (base passthrough).   @4aea9c -- slot 10 ResetToInitialState.
//
void	HeatWatcher::ResetToInitialState(Logical /*powered*/)	// @4aea9c
{
	MechSubsystem::ResetToInitialState(True);	// FUN_004ac22c (base is MechSubsystem)
	heatAlarm.SetLevel(0);					// FUN_0041bbd8(this+0x4b, 0)
}

//
// @4aeac4 -- the registered Performance.  Resolve the watched subsystem, read
// its currentTemperature (+0x114) and drive the 3-level alarm.
//
void
	HeatWatcher::WatchSimulation(Scalar /*time_slice*/)	// FUN_004aeac4
{
	HeatableSubsystem *watched = (HeatableSubsystem *)watchedLink.Resolve();	// FUN_00417ab4(this+0x114)

	// BRING-UP GUARD (marked deviation): in the shipped game watchedLink ALWAYS
	// resolves -- the HeatWatcher resource's "WatchedSubsystem" name binds to a
	// roster segment at load -- so the binary (FUN_004aeac4) derefs `watched`
	// unconditionally.  Our port does not yet resolve the watch link, so Resolve()
	// returns null; the deref AVs the moment a HeatWatcher-derived subsystem's
	// Performance actually runs (exposed by the WAVE 4 un-stub: AmmoBinSimulation
	// calls this directly; PowerWatcher::Simulation for Searchlight/ThermalSight).
	// With no resolved source, hold the heat alarm at Normal (same established
	// pattern as Emitter::TrackSeekVoltage's null-source guard).  FAITHFUL
	// FOLLOW-UP = resolve watchedLink so the alarm tracks the real temperature.
	if (watched == 0)
	{
		heatAlarm.SetLevel(0);						// NormalHeat (no watched source yet)
		return;
	}

	Scalar temp = watched->currentTemperature;		// *(watched + 0x114)

	if (temp > failureTemperature)					// > this+0x124
	{
		heatAlarm.SetLevel(2);						// FailureHeat
	}
	else if (temp > degradationTemperature)			// > this+0x120
	{
		heatAlarm.SetLevel(1);						// DegradationHeat
	}
	else
	{
		heatAlarm.SetLevel(0);						// NormalHeat
	}
}

//
// @4aec54 (468 bytes) -- parse the HeatWatcher resource.  Stamps classID 0x0BBF
// / size 0xF0; defaults the watched index to -1; requires DegradationTemperature,
// FailureTemperature and WatchedSubsystem (resolved name -> segment index +2).
//
int
	HeatWatcher::CreateStreamedSubsystem(
		NotationFile				*model_file,
		const char					*model_name,
		const char					*subsystem_name,
		SubsystemResource			*subsystem_resource,
		NotationFile				*subsystem_file,
		const ResourceDirectories	*directories,
		int							passes
	)
{
	if (!MechSubsystem::CreateStreamedSubsystem(	// FUN_004ac9ec -- MechSubsystem parse (0x30..0xE4)
			model_file, model_name, subsystem_name,
			subsystem_resource, subsystem_file, directories, passes))
	{
		return False;
	}

	subsystem_resource->classID            = RegisteredClass::HeatWatcherClassID;	// res +0x20
	subsystem_resource->subsystemModelSize = 0xF0;		// res +0x24  (bytes f0)

	if (passes == 1)
	{
		subsystem_resource->watchedSubsystem = -1;		// res +0xE4
	}

	if (!model_file->GetEntry(subsystem_name, "DegradationTemperature",
			&subsystem_resource->degradationTemperature)	// res +0xE8
		&& subsystem_resource->degradationTemperature == -1.0f)	// _DAT_004aee28
	{
		DebugStream << subsystem_name << " missing DegradationTemperature!";
		return False;
	}

	if (!model_file->GetEntry(subsystem_name, "FailureTemperature",
			&subsystem_resource->failureTemperature)		// res +0xEC
		&& subsystem_resource->failureTemperature == -1.0f)
	{
		DebugStream << subsystem_name << " missing FailureTemperature!";
		return False;
	}

	const char *watched = "Unspecified";
	int found = model_file->GetEntry(subsystem_name, "WatchedSubsystem", &watched);
	if (!found && subsystem_resource->watchedSubsystem == -1)
	{
		DebugStream << subsystem_name << " missing WatchedSubsystem!";
		return False;
	}
	if (strcmp(watched, "Unspecified") != 0)
	{
		subsystem_resource->watchedSubsystem =
			Get_Segment_Index(model_file, model_name, directories, watched);			// FUN_004215b0
	}
	if (subsystem_resource->watchedSubsystem < 0)
	{
		DebugStream << subsystem_name << " has an invalid watched subsystem!";
		return False;
	}
	subsystem_resource->watchedSubsystem += 2;				// +2 bias
	Check_Fpu();
	return True;
}


//###########################################################################
//###########################################################################
//					Reservoir
//###########################################################################
//###########################################################################
//
// vtable @0050ecd4/ecd8  ctor @4af408  classID ReservoirClassID  : HeatSink
//

//#############################################################################
// Shared Data Support
//
Reservoir::SharedData
	Reservoir::DefaultData(
		Reservoir::GetClassDerivations(),
		Reservoir::GetMessageHandlers(),
		Reservoir::GetAttributeIndex(),
		Reservoir::StateCount
	);

Derivation*
	Reservoir::GetClassDerivations()
	{
		static Derivation classDerivations(HeatSink::GetClassDerivations(), "Reservoir");
		return &classDerivations;
	}

//
// @4af408 -- HeatSink base + reservoir charge.  CoolantCapacity overlays the
// HeatSink thermalCapacity slot; coolantLevel starts full.  A master reservoir
// scales its capacity by the linked sink's +0x1D0 field and registers the
// CoolantSimulation performance; a copy just flags itself inactive (|2).
//
Reservoir::Reservoir(
	Mech				*owner,
	int					subsystem_ID,
	SubsystemResource	*subsystem_resource,
	SharedData			&shared_data
):
	HeatSink(owner, subsystem_ID, subsystem_resource, shared_data),	// FUN_004adda0(...,param_5,0,0)
	reservoirAlarm(2)								// FUN_0041b9ec(this+0x74, 2)
{
	// *this = &PTR_LAB_0050ecd4;
	squirtEfficiency	= 0.5f;					// this[0x8b] @0x22C
	coolantCapacity		= subsystem_resource->coolantCapacity;		// this[0x4a] @0x128 = res +0xFC
	coolantSquirtMass	= subsystem_resource->coolantSquirtMass;	// this[0x89] @0x224 = res +0x100
	coolantLevel		= coolantCapacity;		// this[0x4b] @0x12C
	coolantFlowScale	= 0.0f;					// this[0x57] @0x15C (== "word57")
	reservoirAlarm.SetLevel(0);					// FUN_0041bbd8(this+0x74, 0)
	injectActive		= 0;					// this[0x8a] @0x228

	if ((subsystem_resource->subsystemFlags & SegmentCopyMask) == 0
		&& (subsystem_resource->subsystemFlags & MasterHeatSinkFlag) != 0)
	{
		SetPerformance(&Reservoir::CoolantSimulation);	// this[7..9] = PTR 0050e6b4 -> @4aef78
		HeatSink *link = (HeatSink *)linkedSinks.Resolve();	// FUN_00417ab4(this+0x59)
		link->Attach(this);								// (**(link+0x1d8+4))(link+0x1d8, this)
		coolantCapacity = CoolantCapacityScale * link->field_1d0 * coolantCapacity;	// scale by master
		coolantLevel    = coolantCapacity;
	}
	else
	{
		simulationFlags |= 2;									// this[10] |= 2 (inactive copy)
	}
	Check_Fpu();
}

Logical	Reservoir::TestInstance() const	// @4af564 -> IsDerivedFrom 0x50e650
{
	return IsDerivedFrom(*GetClassDerivations());
}
Logical	Reservoir::TestClass(Mech &)	{ return True; }

//
// @4aee70 -- "InjectCoolant" message / per-state update.  When the watched
// segment has positive count and the reservoir is active above 0 temperature,
// raise the inject alarm and zero the inject accumulator; otherwise clear it.
//
Logical
	Reservoir::HandleMessage(int message)		// FUN_004aee70
{
	if (!HeatableSubsystem::HandleMessage(message))		// FUN_004ac9c8
	{
		if (message /*->count*/ < 1)
		{
			reservoirAlarm.SetLevel(0);			// this+0x1d0
		}
		else
		{
			if (injectActive != 1 && currentTemperature > 0.0f)	// _DAT_004aeedc, this+0x12c
			{
				reservoirAlarm.SetLevel(1);
			}
			injectAccumulator = 0;				// this+0x228
		}
		ForceUpdate();						// this+0x18
	}
	return True;
}

//
// @4aef78 -- registered Performance.  While injection is active, accumulate
// elapsed time and run the coolant distribution.
//
void
	Reservoir::CoolantSimulation(Scalar time_slice)	// FUN_004aef78
{
	if (injectActive == 1)						// this+0x1e4
	{
		injectAccumulator += time_slice;		// this+0x228
		InjectCoolant(time_slice);				// FUN_004aefa4
	}
}

//
// @4af3b0 -- slot-14 DrawCoolant SOURCE.  Hand out up to coolantLevel of the
// requested amount and deduct it from the charge.
//
Scalar
	Reservoir::DrawCoolant(Scalar requested)	// FUN_004af3b0
{
	Scalar supplied = 0.0f;						// _DAT_004af404
	if (requested >= 0.0f)
	{
		supplied = requested;
		if (coolantLevel < requested)			// this+0x12c
		{
			supplied = coolantLevel;
		}
	}
	coolantLevel -= supplied;
	return supplied;
}

//
// @4aefa4 (1019 bytes) -- distribute the reservoir charge across the linked
// heat-sink network.  Iterates three engine collections off the owner
// (+0x7ac/+0x7bc/+0x7cc) plus the local sink, builds a work list, and for each
// member moves a clamped, time-scaled "squirt" of coolant -- bounded by the
// per-sink remaining capacity and by the reservoir's own coolantLevel -- while
// tracking the per-sink coolant-flow delta (+0x1c8).  Bails out as soon as the
// reservoir's currentTemperature drops to ~0 (|temp| <= 1e-4).
//
// (Uses the engine collection-iterator helpers @4af9cf..@4afb96 as locals --
//  see notes at the foot of this file.  Body reproduced structurally; the
//  per-sink arithmetic mirrors HeatSink::ComputeHeatFlow.)
//
void
	Reservoir::InjectCoolant(Scalar time_slice)	// FUN_004aefa4
{
	if (fabsf(currentTemperature) <= 1.0e-4f)	// _DAT_004af3a0
	{
		return;
	}
	// ... iterate owner collections (+0x7ac/+0x7bc/+0x7cc) and self,
	//     squirt clamped coolant to each linked sink, update +0x1c8 ...
	// (full per-sink math recovered in heatfamily_gap.c @4aefa4)
}

//
// @4aeef8 -- PrintState (ReservoirState).   @4aef40 -- slot 7 state query
// (reports state code 0x14 plus the active flag).
//
void	Reservoir::PrintState()				// FUN_004aeef8 (best-effort label)
{
	HeatableSubsystem::PrintState();
	// << GetName() << " ReservoirState " << reservoirAlarm.Level() ...
}

//
// @4af580 -- parse the Reservoir resource.  classID 0x0BC0 / size 0x104.
//
int
	Reservoir::CreateStreamedSubsystem(
		NotationFile				*model_file,
		const char					*model_name,
		const char					*subsystem_name,
		SubsystemResource			*subsystem_resource,
		NotationFile				*subsystem_file,
		const ResourceDirectories	*directories,
		int							passes
	)
{
	if (!HeatSink::CreateStreamedSubsystem(			// FUN_004ae150
			model_file, model_name, subsystem_name,
			subsystem_resource, subsystem_file, directories, passes))
	{
		return False;
	}

	subsystem_resource->classID            = RegisteredClass::ReservoirClassID;	// res +0x20
	subsystem_resource->subsystemModelSize = 0x104;		// res +0x24

	if (passes == 1)
	{
		subsystem_resource->coolantCapacity   = -1.0f;	// res +0xFC
		subsystem_resource->coolantSquirtMass = -1.0f;	// res +0x100
	}

	if (!model_file->GetEntry(subsystem_name, "CoolantCapacity",
			&subsystem_resource->coolantCapacity)
		&& subsystem_resource->coolantCapacity == -1.0f)	// _DAT_004af694
	{
		DebugStream << subsystem_name << " missing CoolantCapacity!";
		return False;
	}
	if (!model_file->GetEntry(subsystem_name, "CoolantSquirtMass",
			&subsystem_resource->coolantSquirtMass)
		&& subsystem_resource->coolantSquirtMass == -1.0f)
	{
		DebugStream << subsystem_name << " missing CoolantSquirtMass!";
		return False;
	}
	Check_Fpu();
	return True;
}


//###########################################################################
//###########################################################################
//	HeatSink aggregate class @0050ed4c/ed50   (BEST-EFFORT, name unconfirmed)
//###########################################################################
//###########################################################################
//
// vtable @0050ed4c/ed50  ctor @4ae8d0  classID 0x0BBE  : HeatSink
//
// Parses "HeatSinkCount" (+ defaults "AmbientTemperature" to 300.0f) and
// conducts stored heat toward the ambient setpoint while drawing coolant.
// The only adjacent standalone name string is the pooled "HeatSink", so the
// class name cannot be confirmed from the dump (likely an aggregate / master
// heat-sink bank).  Skeleton given for reference; FOLD INTO THE RIGHT CLASS
// once the name is known.
//
#if 0	// name unconfirmed -- do not compile as-is

// @4ae8d0
AggregateHeatSink::AggregateHeatSink(Mech *owner, int id,
		SubsystemResource *res, SharedData &shared)
:	HeatSink(owner, id, res, shared)		// FUN_004adda0(...,&DAT_0050e580,0,0)
{
	// *this = &PTR_LAB_0050ed4c;
	heatSinkCount     = res->heatSinkCount;		// this[0x74] = res +0xFC
	ambientTemperature= 300.0f;					// this[0x75]  (_DAT_004ae89c)
	helper.Init();								// FUN_004afa1a(this+0x76,0) vtbl 0050ecc4
	thermalConductance= _DAT_004ae974 * heatSinkCount * thermalConductance;	// this[0x50]
	if (master) SetPerformance(&AggregateHeatSink::Simulation);	// PTR 0050e5e8 -> @4ae73c
}

// @4ae724 -- slot 9: base HeatSink step.
void AggregateHeatSink::Step(Scalar dt) { HeatSink_Step(dt); }	// FUN_004ad748

// @4ae73c (351 bytes) -- Performance.  Equilibrate heat toward the ambient
// setpoint and draw coolant.
//   setpoint = 300.0f - (300.0f - ambientTemperature) * 3.0f;   // _DAT_004ae89c/8a0
//   heatEnergy   += pendingHeat;  currentTemp = heatEnergy/thermalMass;  UpdateHeatLoad();
//   q = expf( -dt*thermalConductance*(1.0 - master->heatEnergy)
//             *(coolantLevel/coolantCapacity)*coolantFlowScale / thermalMass );
//   pendingHeat += -(currentTemp*refOut - setpoint)*thermalMass*(1.0 - q);
//   diff = coolantCapacity - coolantLevel;
//   if (diff > 0 && fabsf(diff) > 1e-4) coolantLevel += DrawCoolant(diff);  // slot 14
void AggregateHeatSink::Simulation(Scalar dt) { /* FUN_004ae73c */ }

// @4ae9c0 TestInstance (IsDerivedFrom 0x50e590);  @4ae9dc CreateStreamedSubsystem:
//   classID 0x0BBE / size 0x100; default heatSinkCount = -1; require "HeatSinkCount".

#endif


//###########################################################################
//	Engine collection-iterator helper thunks  (NOT heat family)
//###########################################################################
//
// Tiny ctor/dtor wrappers for the engine's generic collection/iterator
// library, used only as locals inside Reservoir::InjectCoolant (@4aefa4).
// Listed for completeness; they belong to the collection module, not here.
//
//   @4af9cf / @4af9ee   vtable 0050eccc   (on base 004179d4/004179f8)
//   @4afa1a / @4afa39   vtable 0050ecc4   (on base 004179d4/004179f8)
//   @4afa65 / @4afa84   vtable 0050ecbc   (on base 00417be0/00417c0c)
//   @4afab0 @4afacf @4afaee / @4afb0d   vtable 0050ec6c (on 00417d00/d28/d54)
//   @4afb39 @4afb58 @4afb77 / @4afb96   vtable 0050ec1c (on 00417d00/d28/d54)
//

//===========================================================================//
// WAVE 2 factory bridge -- Reservoir (factory case 0xBC0, "Condenser" label).
//===========================================================================//
Subsystem *CreateReservoirSubsystem(Mech *owner, int id, void *seg)
{
	Check(sizeof(Reservoir) <= 0x230);
	return (Subsystem *) new (Memory::Allocate(0x230))
		Reservoir(owner, id, (Reservoir::SubsystemResource *)seg, Reservoir::DefaultData);
}
