//===========================================================================//
// File:	HeatManager.cpp								
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 12/26/01  MSL  Added code to Restart Mech only if < 90% Heat              //
// 12/26/01  MSL  Disabled LOW_HEAT betty                                    //
// 12/26/01  MSL  Disabled MED_HEAT betty                                    //
// 01/09/02  MSL  Adjusted Coolant loss and Heat dissapated by Coolant       //
//---------------------------------------------------------------------------//
//===========================================================================//
#include "MW4Headers.hpp"

#include "HeatManager.hpp"
#include "Mech.hpp"
#include "AI.hpp"
#include "VehicleInterface.hpp"
#include "MWApplication.hpp"

// MSL 5.03 Lava
#include "DamageDispatch.hpp"

#include <Adept\Mission.hpp>
#include <Adept\Effect.hpp>

//#############################################################################
//###########################    HeatManager    ###############################
//#############################################################################

HeatManager::ClassData*
	HeatManager::DefaultData = NULL;

bool HeatManager::g_OverrideHeatOption = false;





void MechWarrior4::HeatSecurityCheckStart()
{
	_asm
	{
		nop
	}	
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HeatManager::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			HeatManagerClassID,
			"MechWarrior4::HeatManager",
			Receiver::DefaultData,
			0,
			NULL
		);
	Check_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HeatManager::TerminateClass()
{
	Check_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
	bool HeatOn(NetMissionParameters::MWNetMissionParameters& net_params)
	{
		if (InputTrainer::GetInstance()->GetFlags() != InputTrainer::ALL)
		{
			return (InputTrainer::GetInstance()->GetEnabled(InputTrainer::HEAT));
		}

		return (Network::GetInstance()->AmIServer() && net_params.m_heatOn);
	}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
HeatManager::HeatManager(
	Stuff::Scalar max_heat,
	Stuff::Scalar current_heat,
	Stuff::Scalar max_coolant,
	Stuff::Scalar current_coolant,
	Mech *parent,
	Adept::ResourceID stream_id
	) :
		Adept::Receiver(DefaultData),
		heatObjects(NULL),
		coolantEffect(NULL),
		overHeatEffect(NULL)
{
	maxHeat = max_heat;
	heatValue = current_heat;
	Clamp(heatValue, 0.0f, maxHeat);
	maxCoolant = max_coolant;
	coolantValue = current_coolant;
	lastTime = gos_GetElapsedTime();
	isCooling = 0;
	lastCoolTime = gos_GetElapsedTime();
	SetCoolantPercentage();
	SetHeatPercentage();
	isHeatDirty = 1;
	isCoolantDirty = 1;
	m_parentMech = parent;
	shutdown = false;
	m_shuttingDown = false;
	m_startingUp = false;
	m_shutDownTimer = gos_GetElapsedTime();
	m_startUpTimer = gos_GetElapsedTime();
	m_overHeatCheckTime = gos_GetElapsedTime();
	m_overHeatTimer = gos_GetElapsedTime();
	over90 = false;
// MSL 5.03 Lava
	m_maxHeatTimer = gos_GetElapsedTime();
	atMaxHeat = false;

	Resource stream(stream_id);
	Verify(stream.DoesResourceExist());
	stream >> m_movementHeat;
	stream >> m_doubleMovementHeat;
	stream >> m_heatEffectsSpeed;
	stream >> m_shutDownTime;

	m_movementHeatObject = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
HeatManager::~HeatManager()
{
	heatObjects.DeletePlugs();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HeatManager::Reuse()
{
	Check_Object(this);

	heatValue = 0.0f;
	lastTime = gos_GetElapsedTime();
	coolantValue = maxCoolant;
	lastTime = gos_GetElapsedTime();
	isCooling = 0;
	lastCoolTime = gos_GetElapsedTime();
	SetCoolantPercentage();
	SetHeatPercentage();
	shutdown = false;
	m_shuttingDown = false;
	m_shutDownTimer = gos_GetElapsedTime();
	m_startUpTimer = gos_GetElapsedTime();
	m_overHeatCheckTime = gos_GetElapsedTime();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HeatManager::AddHeat(Stuff::Scalar added_heat)
{
	Check_Object(this);

	NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();

	if (HeatOn(*params) == true)
	{
		if(added_heat < 0.0f)
		{
			Check_Object(Mission::GetInstance());
			const Mission::GameModel *mission_model = Mission::GetInstance()->GetGameModel();
			Check_Object(mission_model);
			added_heat *= mission_model->m_heatSinkEfficiency;
			added_heat *= m_parentMech->m_heatMaterialMultiplier;
		}
		heatValue += added_heat;
		Clamp(heatValue, 0.0f, maxHeat);
	}

	isHeatDirty = 1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void HeatManager::SetCooling(int new_int)
{
	Check_Object(this);
	
	if (new_int)
	{
		if (coolantValue > 0.0f)
		{
			if(m_parentMech->vehicleInterface)
				m_parentMech->vehicleInterface->ReactToEvent (VehicleInterface::COOLANT_FLUSH_START);
			isCooling = new_int;
		}
	}
	else
	{
		isCooling = new_int;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HeatManager::ApplyMovementHeat(Scalar current_rate, Scalar max_rate)
{
	Check_Object(this);

	NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();

	if (HeatOn(*params) == true)
	{

		current_rate = Abs(current_rate);
		Scalar ratio = current_rate / max_rate;

	
		if(m_movementHeatObject == NULL)
		{
			m_movementHeatObject = AddHeatObject(1.0, true);	
		}
		Check_Object(m_movementHeatObject);
		if(ratio == 0)
		{
			m_movementHeatObject->heatApplied = 0.0f;
		}
		else if(ratio < m_doubleMovementHeat)
		{
			m_movementHeatObject->heatApplied = m_movementHeat;
		}
		else
		{
			m_movementHeatObject->heatApplied = m_movementHeat * 2;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
HeatObject*
	HeatManager::AddHeatObject(Stuff::Scalar times_applied, Scalar heat_applied, bool is_permanent)
{
	Check_Object(this);

	NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();

	if ((HeatOn(*params) == true) ||
		(HeatManager::g_OverrideHeatOption == true))
	{

		HeatObject *heat_object = new HeatObject(times_applied, heat_applied, is_permanent);
		Check_Object(heat_object);

		heatObjects.Add(heat_object);

		return heat_object;
	}

	return NULL;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
HeatObject*
	HeatManager::AddHeatObject(Scalar heat_applied, bool is_permanent)
{
	Check_Object(this);

	NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();

	if ((HeatOn(*params) == true) ||
		(HeatManager::g_OverrideHeatOption == true))
	{
		HeatObject *heat_object = new HeatObject(heat_applied, is_permanent);
		Check_Object(heat_object);

		heatObjects.Add(heat_object);

		return heat_object;
	}
	
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Scalar
	HeatManager::EstimateMaxCurrentHeat()
{
	Stuff::Scalar heat = heatValue;

	ChainIteratorOf<HeatObject *> iterator(&heatObjects);
	HeatObject *heat_object;
	while ((heat_object = iterator.ReadAndNext()) !=NULL)
	{
		heat += heat_object->heatApplied;
	}

	Clamp(heat,0.0f,maxHeat);

	if (heatValue > heat)
	{
		return (heatValue);
	}

	return (heat);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	HeatManager::Execute()
{
	Check_Object(this);

	bool return_value = false; //mark not overheating to default;
	NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();
	if ((Network::GetInstance()->AmIServer() && params->m_heatOn) ||
		(HeatManager::g_OverrideHeatOption == true))
	{
		if(isCooling)
		{
// MSL 5.06
//#define HEAT_AND_COOLANT  10.0f // jcem Gold Version Value : -8.0f..., below 3 lines should use same value....
#define HEAT_AND_COOLANT  5.0f // Changed from 10.0f to 5.0f
			if((coolantValue - HEAT_AND_COOLANT) >= 0.0f)
			{
				if(gos_GetElapsedTime() - lastCoolTime >= 0.3f)
				{
					lastCoolTime = gos_GetElapsedTime();
// Changed	AddHeat(-3.6f);
// MSL 5.00
					AddHeat(-HEAT_AND_COOLANT);
// Changed    AddCoolant(-10.0f);
// MSL 5.00
					AddCoolant(-HEAT_AND_COOLANT); 
				}
			}
			else
			{
				if(m_parentMech->vehicleInterface)
					m_parentMech->vehicleInterface->ReactToEvent (VehicleInterface::COOLANT_FLUSH_STOP);
				coolantValue = 0.0f;
				isCooling = false;
			}
#undef HEAT_AND_COOLANT
		}

		if(!heatObjects.IsEmpty())
		{
			if(gos_GetElapsedTime() - lastTime >= 0.5f)
			{
				lastTime = gos_GetElapsedTime();
				ChainIteratorOf<HeatObject *> iterator(&heatObjects);
				HeatObject *heat_object;
				while((heat_object = iterator.GetCurrent()) !=NULL)
				{
					Check_Object(heat_object);
					AddHeat(heat_object->heatApplied);
					heat_object->currentCount++;
					if(!heat_object->isPermanent)
					{
						if(heat_object->currentCount >= heat_object->timesApplied)
						{
							iterator.Remove();
							delete heat_object;
						}
						else
						{
							iterator.ReadAndNext();
						}
					}
					else
					{
						iterator.ReadAndNext();
					}
				}
			}
		}

		//Ok here you want to do your time check.  If you are over 90 for so long then send a die
		//to the player
		//Jerry because this means we are the server do we have to some how post this command or can
		//we process it directly.  Not checking in until I have your ok.
		if(m_parentMech->vehicleShutDown)
		{
			over90 = false;
		}
		if((GetHeatPercentage() >= 0.85f) && (!shutdown))
		{
			if(!over90)
			{
				m_overHeatTimer = gos_GetElapsedTime();
				over90 = true;
			}
			else
			{
				if(gos_GetElapsedTime() - m_overHeatTimer > 10.0f)
				{
// MSL 5.03 Lava
//					m_parentMech->SelfDestruct();
					if (!DamageDispatch::GetGlobalInvulnerability() || (MWApplication::GetInstance()->networkingFlag))
					{
					m_parentMech->SelfDestruct();
							goto skipShutdownHeatCheck;
					}
				}
			}
		}
		else
		{
			over90 = false;
		}

// MSL 5.03 Lava
		if ((GetHeatPercentage () >= 0.85f) && (m_parentMech->vehicleShutDown) &&
		((m_parentMech->materialHit ==OpenLavaMaterial ) || (m_parentMech->materialHit == CrackedLavaMaterial) ))
		{
			if (!atMaxHeat)
			{
				m_maxHeatTimer = gos_GetElapsedTime();
				atMaxHeat = true;
			}
			else
			{
				if(gos_GetElapsedTime() - m_maxHeatTimer > 15.0f)
				{
					m_parentMech->SelfDestruct();
				}
			}
		}
		else
		{
			atMaxHeat = false;
		}
skipShutdownHeatCheck:
	;

	}
	else
	{
		ChainIteratorOf<HeatObject *> iterator(&heatObjects);
		HeatObject *heat_object;
		while((heat_object = iterator.GetCurrent()) !=NULL)
		{
			iterator.Remove();
			delete heat_object;
		}
	}

	SetCoolantPercentage();
	SetHeatPercentage();


	if (m_parentMech->GetReplicatorMode() == Replicator::ClientMasterMode || m_parentMech->GetReplicatorMode() == Replicator::MasterMode)
	{
		Scalar percent = GetHeatPercentage ();
		if(m_parentMech->vehicleInterface)
		{
			MWGUIManager *gui = MWGUIManager::GetInstance ();
			Verify (gui);
			gui->HeatLevel (percent);
		}
// Changing range from 0.7 to 0.8 to prevent constant "heat" betty
// MSL 5.00
		if (percent < 0.8f)
		{
			if((m_parentMech->vehicleInterface) && (!shutdown))
				m_parentMech->vehicleInterface->ReactToEvent (VehicleInterface::NO_HEAT);
		}
		if (percent >= 0.8f)
		{
		if((m_parentMech->vehicleInterface) && (!shutdown))
				m_parentMech->vehicleInterface->ReactToEvent (VehicleInterface::HIGH_HEAT);
		}
// MSL 5.00
//		if ((percent >= 0.7f) && (percent < 0.8f))
//		{
//			if((m_parentMech->vehicleInterface) && (!shutdown))
//				m_parentMech->vehicleInterface->ReactToEvent (VehicleInterface::LOW_HEAT);
//		}
//		else if ((percent >= 0.8f) && (percent < 0.9f))
//		{
//			if((m_parentMech->vehicleInterface) && (!shutdown))
//				m_parentMech->vehicleInterface->ReactToEvent (VehicleInterface::MED_HEAT);
//		}

		if(GetHeatPercentage() > 0.90f)
		{
			return_value = true;
// MSL 5.00
//			if((m_parentMech->vehicleInterface) && (!shutdown))
//				m_parentMech->vehicleInterface->ReactToEvent (VehicleInterface::HIGH_HEAT);
			if(!m_shuttingDown)
			{
				if(m_overHeatCheckTime <= gos_GetElapsedTime())
				{
					m_overHeatCheckTime = gos_GetElapsedTime() + 0.5f;
//					Scalar random_roll = Stuff::Random::GetFraction();
//					if((GetHeatPercentage() - 0.2f >= random_roll) && (!shutdown))
					{
						m_shutDownTimer = gos_GetElapsedTime() + m_shutDownTime;
						m_shuttingDown = true;
					}
				}
			}
			else
			{
				if(gos_GetElapsedTime() >= m_shutDownTimer)
				{
					m_shuttingDown = false;
					if ((m_parentMech->GetAI() != 0) && (!m_parentMech->IsPlayerVehicle()))
					{
						m_parentMech->GetAI()->NotifyHeatShutdownImminent();

						if (m_parentMech->ShutDownRequest(true))
						{
							over90 = false;
							if((m_parentMech->vehicleInterface) && (!shutdown))
								m_parentMech->vehicleInterface->ReactToEvent (VehicleInterface::SHUTDOWN);
							shutdown = true;
						}
					}
					else
					{
						if (m_parentMech->ShutDownRequest(true))
						{
							over90 = false;
							if((m_parentMech->vehicleInterface) && (!shutdown))
								m_parentMech->vehicleInterface->ReactToEvent (VehicleInterface::SHUTDOWN);
							shutdown = true;
						}
					}
				}
			}
		}

		else
		{
// Added Recover from Shutdown only if Heat below 66%
// MSL 5.00
		if(GetHeatPercentage() < 0.66f)
			{
			if(overHeatEffect.GetCurrent())
				overHeatEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState);

			if(m_shuttingDown)
				m_shuttingDown = false;

			if(shutdown)
			{
				if(!m_startingUp)
				{
// Reduced startup timer to elapsed time
// Original Code - m_startUpTimer = gos_GetElapsedTime() + 5.0f;
// MSL 5.00
					m_startUpTimer = gos_GetElapsedTime();
					m_startingUp = true;
				}
				if(m_startUpTimer <= gos_GetElapsedTime())
				{
					m_shuttingDown = false;
					shutdown = false;
					m_startingUp = false;
					if(m_parentMech->vehicleShutDown)
					{
						m_parentMech->ShutDownRequest(false);
						if(m_parentMech->vehicleShutDown)
						{
							m_startingUp = true;
							shutdown = true;
						}
						else
						{
							if(m_parentMech->vehicleInterface)
								m_parentMech->vehicleInterface->ReactToEvent (VehicleInterface::POWER_UP);
						}
					}
				}
			}
            }
		}
	}
	return return_value;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	HeatManager::GetSpeedMultiplier()
{
	Check_Object(this);

	Scalar value = 1.0f;
	Scalar heat_percentage = GetHeatPercentage();

	if(heat_percentage > m_heatEffectsSpeed)
	{
		value = 1.0f - (heat_percentage - m_heatEffectsSpeed);
	}
	Clamp(value, 0.5f, 1.0f);
	return value;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	HeatManager::GetTorsoTwistMultiplier()
{
	Check_Object(this);

	Scalar value = 1.0f;
	Scalar heat_percentage = GetHeatPercentage();

	if(heat_percentage > m_heatEffectsSpeed)
	{
		value = 1.0f - (heat_percentage - m_heatEffectsSpeed);
	}
	Clamp(value, 0.5f, 1.0f);
	return value;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HeatManager::SetCoolantPercentage()
{
	Check_Object(this);
	if(maxCoolant > 0.0f)
		coolantPercentage = (int)((coolantValue / maxCoolant) * 100);
	else
		coolantPercentage = 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HeatManager::SetHeatPercentage()
{
	Check_Object(this);
	if(maxHeat > 0.0f)
		heatPercentage = (int)((heatValue / maxHeat) * 100);
	else
		heatPercentage = 0;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void
	HeatManager::SetCoolantPercentage(Scalar percentage)
{
	Check_Object(this);

	Scalar old_coolant = coolantValue;
	coolantValue = maxCoolant * percentage;

	if (old_coolant != heatValue)
		isCoolantDirty = 1;

	if(maxCoolant > 0.0f)
		coolantPercentage = (int)((coolantValue / maxCoolant) * 100);
	else
		coolantPercentage = 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HeatManager::SetHeatPercentage(Scalar percentage)
{
	Check_Object(this);

	Scalar old_heat = heatValue;
	heatValue = maxHeat * percentage;

	if (old_heat != heatValue)
		isHeatDirty = 1;

	if(maxHeat > 0.0f)
		heatPercentage = (int)((heatValue / maxHeat) * 100);
	else
		heatPercentage = 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	HeatManager::OverrideShutDown()
{
	Check_Object(this);

	if(m_shuttingDown)
	{
		if((m_parentMech->vehicleInterface) && (!shutdown))
			m_parentMech->vehicleInterface->ReactToEvent (VehicleInterface::AUTO_SHUTDOWN_OVERRIDE);
		m_shutDownTimer += 3.0f;
		return true;
	}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MechWarrior4::HeatSecurityCheckStop()
{
	_asm
	{
		nop
	}	
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HeatManager::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}


