
#include "MW4Headers.hpp"

#include "MWDamageObject.hpp"
#include "Subsystem.hpp"
#include "MWObject.hpp"
#include "Weapon.hpp"
#include "MWApplication.hpp"
#include "DamageDispatch.hpp"
#include "bucket.hpp"
#include "MWMission.hpp"

#include "windows.h"
#include "recscore.h" // jcem
//#############################################################################
//###########################    MWInternalDamageObject   #####################
//#############################################################################

extern int g_nMR;

MWInternalDamageObject::ClassData*
	MWInternalDamageObject::DefaultData = NULL;


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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWInternalDamageObject::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			MWInternalDamageObjectClassID,
			"MechWarrior4::MWInternalDamageObject",
			InternalDamageObject::DefaultData,
			0,
			NULL
		);
	Register_Object(DefaultData);
	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWInternalDamageObject::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MWInternalDamageObject::MWInternalDamageObject(
	ClassData *class_data,
	MemoryStream *stream
):
	InternalDamageObject(class_data, stream),
// MSL 5.02 headshot
	destroyedSubsystemChain(NULL),
	subsystemChain(NULL)
{
	Check_Pointer(this);
	Check_Object(stream);

	*stream >> m_maxMissileSlots;
	*stream >> m_maxProjectileSlots;
	*stream >> m_maxBeamSlots;
	*stream >> m_maxOmniSlots;

	m_missileSlotsAvailable = m_maxMissileSlots;
	m_projectileSlotsAvailable = m_maxProjectileSlots;
	m_beamSlotsAvailable = m_maxBeamSlots;
	m_omniSlotsAvailable = m_maxOmniSlots;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWInternalDamageObject::ConstructMWInternalDamageObjectStream(
		MemoryStream *stream,
		Page *instance_page
	)	
{
	Check_Object(instance_page);

	InternalDamageObject::ConstructInternalDamageObjectStream(
		stream,
		instance_page,
		DefaultData->GetClassID()
	);

	int slots = 0;
	instance_page->GetEntry("MissileSlots", &slots); 
	*stream << slots;
	slots = 0;
	instance_page->GetEntry("ProjectileSlots", &slots); 
	*stream << slots;
	slots = 0;
	instance_page->GetEntry("BeamSlots", &slots); 
	*stream << slots;
	slots = 0;
	instance_page->GetEntry("OmniSlots", &slots); 
	*stream << slots;
}



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

void*
	MWInternalDamageObject::operator new(size_t size)
{
	/*
	gos_PushCurrentHeap(Adept::g_DamageObjectHeap);

	void *temp = ::new(size_t);

	gos_PopCurrentHeap();

	return temp;
	*/
	return gos_Malloc(size,Adept::g_DamageObjectHeap);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWInternalDamageObject::Save(MemoryStream *stream)
{
	Check_Object(this);
	
	InternalDamageObject::Save(stream, DefaultData->GetClassID());

	*stream << m_maxMissileSlots;
	*stream << m_maxProjectileSlots;
	*stream << m_maxBeamSlots;
	*stream << m_maxOmniSlots;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MWInternalDamageObject::~MWInternalDamageObject()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MWInternalDamageObject::AddSubsystem(Subsystem *subsystem)
{
	Check_Object(this);
	Check_Object(subsystem);

	int result = VerifySubsystem(subsystem);
	if(result)
		subsystemChain.Add(subsystem);
	
	return result;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Subsystem*
	MWInternalDamageObject::GetSubsystem(int type)
{
	Check_Object(this);
	ChainIteratorOf<Subsystem *> iterator(&subsystemChain);
	Subsystem *subsystem;

	while((subsystem = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(subsystem);
		const Subsystem::GameModel *model = subsystem->GetGameModel();
		Check_Object(model);

		if(model->itemID == type)
			return subsystem;
	}

	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MWInternalDamageObject::VerifySubsystem(Subsystem *subsystem)
{
	Check_Object(this);
	Check_Object(subsystem);
	
	const Subsystem::GameModel *model = subsystem->GetGameModel();
	subsystem->containedInSlotType = DoesHaveAvailableSlots(model->slotType, model->totalSlotsTaken);

	return subsystem->containedInSlotType >= -1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MWInternalDamageObject::DoesHaveAvailableSlots(int slot_type, int slots_needed, bool check_omni, bool subtract_slot)
{
	Check_Object(this);

	switch(slot_type)
	{
	case -1:
		return -1;
		break;
	case MissileSlotType:
		if((m_missileSlotsAvailable - slots_needed) >= 0)
		{
			if(subtract_slot)
				m_missileSlotsAvailable -= slots_needed;
			return MissileSlotType;
		}
		break;
	case ProjectileSlotType:
		if((m_projectileSlotsAvailable - slots_needed) >= 0)
		{
			if(subtract_slot)
				m_projectileSlotsAvailable -= slots_needed;
			return ProjectileSlotType;
		}
		break;
	case BeamSlotType:
		if((m_beamSlotsAvailable - slots_needed) >= 0)
		{
			if(subtract_slot)
				m_beamSlotsAvailable -= slots_needed;
			return BeamSlotType;
		}
		break;
	case OmniSlotType:
		if((m_omniSlotsAvailable - slots_needed) >= 0)
		{
			if(subtract_slot)
				m_omniSlotsAvailable -= slots_needed;
			return OmniSlotType;
		}
		break;
	}

	if(check_omni)
	{
		if((m_omniSlotsAvailable - slots_needed) >= 0)
		{
			if(subtract_slot)
				m_omniSlotsAvailable -= slots_needed;
			return OmniSlotType;
		}
	}

	return -2;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWInternalDamageObject::FreeAvailableSlots(int slot_type, int num)
{
	Check_Object(this);
	switch(slot_type)
	{
	case MissileSlotType:
		m_missileSlotsAvailable += num;
		break;
	case ProjectileSlotType:
		m_projectileSlotsAvailable += num;
		break;
	case BeamSlotType:
		m_beamSlotsAvailable += num;
		break;
	case OmniSlotType:
		m_omniSlotsAvailable += num;
		break;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MWInternalDamageObject::RollCriticalHit()
{
	Check_Object(this);

	int roll = Stuff::Random::GetLessThan(100);

	if(roll <= 70)
		return 0;
	else if(roll >= 97)
		return 3;
	else if(roll >= 90)
		return 2;
	else 
		return 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWInternalDamageObject::InflictCriticalHit()
{
	Check_Object(this);
	DamageDispatch *dispatch = DamageDispatch::GetInstance();
	Check_Pointer(dispatch);
	dispatch->InflictCriticalHit(*this);
}

void
	MWInternalDamageObject::_InflictCriticalHit()
{
	Check_Object(this);
	//
	//---------------------------------
	//Get Number of Critical Hits taken
	//---------------------------------
	//
	int num_crit_hits = RollCriticalHit();
	ChainIteratorOf<Subsystem *> iterator(&subsystemChain);

	for(int i=0; i<num_crit_hits; i++)
	{
		//
		//---------------------------------
		//Apply Critical Hits to Subsystems
		//---------------------------------
		//
		int chain_size = iterator.GetSize();
		if(chain_size > 0)
		{
			int roll = Stuff::Random::GetLessThan(iterator.GetSize());
			Subsystem *subsystem = iterator.GetNth(roll);
			Check_Object(subsystem);
			bool does_destroy = subsystem->TakeCriticalHit();
			if(does_destroy)
			{
				g_RSF.RecScore(CBucketManager::COMPONENT_DEATHS, ReplicatorID::Null, ReplicatorID::Null, 0, 0, (DWORD)subsystem); // jcem
// MSL 5.02 headshot
				destroyedSubsystemChain.Add(subsystem); // So we can later completely remove the subsystem
				subsystemChain.Remove(subsystem);
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	MWInternalDamageObject::TakeDamage(Scalar damage_taken, Entity__TakeDamageMessage *message)
{
	Check_Object(this);
	DamageDispatch *dispatch = DamageDispatch::GetInstance();
	Check_Pointer(dispatch);
	return (dispatch->TakeDamage(*this,damage_taken,message));
}

Scalar
	MWInternalDamageObject::_TakeDamage(Scalar damage_taken, Entity__TakeDamageMessage *message)
{
	Check_Object(this);

    if (g_nMR == 2) {
        return InternalDamageObject::TakeDamage(damage_taken, message);
    }
	int prev_num_subsystems = subsystemChain.GetSize();


	Scalar rv = InternalDamageObject::TakeDamage(damage_taken, message);

	if (Network::GetInstance()->AmIServer())
	{
		if(currentInternalDamage <= 0.0f)
		{
			KillAllContainedSubsystems();
		}
	}


	const int num_subsystems = subsystemChain.GetSize();

	if (num_subsystems < prev_num_subsystems)
	{
		MWApplication* mwapplication = Cast_Object(MWApplication*,MWApplication::GetInstance());
		if (mwapplication->networkingFlag == false)
		{
			if (!mwapplication->IsCampCOOP())
			return (rv);
		}
		MWMission *mission = Cast_Object(MWMission*,MWMission::GetInstance());

		if ((mission == 0) ||
			(mission->m_BucketManager == 0))
		{
			return (rv);
		}

		Adept::ReplicatorID me = parentEntity->GetReplicatorID();
		const Adept::ReplicatorID& attacker = message->inflictingEntityID;

		int num_subsystems_destroyed = prev_num_subsystems - num_subsystems;

		if (num_subsystems_destroyed > 0)
		{
			MWMover *mover = Cast_Object(MWMover*, parentEntity);
			Check_Object(mover);

			if (mover->GetParentVehicle() && mover->GetParentVehicle()->IsDerivedFrom(Mech::DefaultData))
			{
				Mech *mech = Cast_Object(Mech *,mover->GetParentVehicle());

				if (mech->IsDestroyed() == true)
				{
					return (rv);
				}

				me = mech->GetReplicatorID();
			}

			if(mover->GetParentVehicle() && mover->GetParentVehicle()->GetClassID() == MechClassID)
			{
				mission->m_BucketManager->NotifyAction(CBucketManager::COMPONENT_KILLS,attacker,num_subsystems_destroyed);
                g_RSF.RecScore(CBucketManager::COMPONENT_KILLS, attacker, me, 0, 0, num_subsystems_destroyed); // jcem
				mission->m_BucketManager->NotifyAction(CBucketManager::COMPONENT_DEATHS,me,num_subsystems_destroyed);

				if (mwapplication->TeamsAreFriendly(me,attacker) == true)
				{
					mission->m_BucketManager->NotifyAction(CBucketManager::FRIENDLY_COMPONENT_KILLS,attacker,num_subsystems_destroyed,me);
					mission->m_BucketManager->NotifyAction(CBucketManager::FRIENDLY_COMPONENT_DEATHS,me,num_subsystems_destroyed);
				}
				else
				{
					mission->m_BucketManager->NotifyAction(CBucketManager::ENEMY_COMPONENT_KILLS,attacker,num_subsystems_destroyed,me);
					mission->m_BucketManager->NotifyAction(CBucketManager::ENEMY_COMPONENT_DEATHS,me,num_subsystems_destroyed);
				}
			}



#if !defined(NO_LOG)
			// ngLog addition
			int self = parentEntity->GetReplicatorID().connectionID;
			int inflictor = message->inflictingEntityID.connectionID;

			if(self >= 0 && inflictor >= 0) {
				MWMover *mobj = Cast_Object(MWMover *, parentEntity);
				Check_Object(mobj);

				if(mobj && mobj->GetParentVehicle() && mobj->GetParentVehicle()->IsDerivedFrom(Mech::DefaultData)) {
					Mech *mech = Cast_Object(Mech *, mobj->GetParentVehicle());
				
					if(mech && mech->IsLanceMate()) {
						self = mech->lancemateIndex + Maximum_Players;
					}
				}

				// Check for local bots, weeding out stuff like buildings, tanks, etc.
				Replicator *r = NULL;
				Connection *conn = NULL;

				// Check if inflictor is a bot
				if(inflictor == Connection::Server->GetID()) {
					conn = Network::GetInstance()->GetConnection(message->inflictingEntityID.connectionID);
					if(conn) {
						r = conn->FindReplicator(message->inflictingEntityID);
						if(r && r->GetClassID() == MechClassID) {
							Mech *inflict = Cast_Object(Mech *, r);

							if(inflict && inflict->IsLanceMate()) {
								inflictor = inflict->lancemateIndex + Maximum_Players;
							}
						}
					}
				}

				MWApplication *app = MWApplication::GetInstance();

				if(self >= Maximum_Players) {
					app->lancemateConnectionData[self-Maximum_Players].comp_rcv[inflictor] += num_subsystems_destroyed;
					app->lancemateConnectionData[self-Maximum_Players].stats_update = 1;
				} else {
					app->servedConnectionData[self].comp_rcv[inflictor] += num_subsystems_destroyed;
					app->servedConnectionData[self].stats_update = 1;
				}

				if(inflictor >= Maximum_Players) {
					app->lancemateConnectionData[inflictor-Maximum_Players].comp_inf[self] += num_subsystems_destroyed;
					app->lancemateConnectionData[inflictor-Maximum_Players].stats_update = 1;
				} else {
					app->servedConnectionData[inflictor].comp_inf[self] += num_subsystems_destroyed;
					app->servedConnectionData[inflictor].stats_update = 1;
				}
			}
#endif // !defined(NO_LOG)
		}
	}

	return (rv);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWInternalDamageObject::KillAllContainedSubsystems()
{
	Check_Object(this);
	DamageDispatch *dispatch = DamageDispatch::GetInstance();
	Check_Pointer(dispatch);
	dispatch->KillAllContainedSubsystems(*this);
}

void
	MWInternalDamageObject::_KillAllContainedSubsystems()
{
	ChainIteratorOf<Subsystem *> iterator(&subsystemChain);
	Subsystem *subsystem;

	while((subsystem = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(subsystem);
		bool does_destroy = subsystem->TakeCriticalHit();
		if(does_destroy) {
            g_RSF.RecScore(CBucketManager::COMPONENT_DEATHS, ReplicatorID::Null, ReplicatorID::Null, 0, 0, (DWORD)subsystem);
// MSL 5.02 headshot
			destroyedSubsystemChain.Add(subsystem); // So we can later completely remove the subsystem
			subsystemChain.Remove(subsystem);
		}
	}
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char*
	MWInternalDamageObject::SlotTypeAsciiToText(int slot_type)
{	
	switch(slot_type)
	{
	case MissileSlotType:
		return "MissileSlot";
	case ProjectileSlotType:
		return "ProjectileSlot";
	case BeamSlotType:
		return "BeamSlot";
	case OmniSlotType:
		return "OmniSlot";
	}

	PAUSE(("That is a bad slot value"));
	return "Bad";
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MWInternalDamageObject::SlotTypeTextToAscii(const char *slot_type_string)
{	
	if(!_stricmp(slot_type_string, "MissileSlot"))
		return MissileSlotType;
	if(!_stricmp(slot_type_string, "ProjectileSlot"))
		return ProjectileSlotType;
	if(!_stricmp(slot_type_string, "BeamSlot"))
		return BeamSlotType;
	if(!_stricmp(slot_type_string, "OmniSlot"))
		return OmniSlotType;

	PAUSE(("That is a bad Slot Type String: %s", slot_type_string));
	return -1;
}



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