//===========================================================================//
// File:	Weapon.cpp										
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 09/09/98  JSE  Inital base class based off of Shadowrun/Adept			 //
// 09/24/98  BDB  Inital Weapon subsytem class based off of Subsystem		 //
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "MW4Headers.hpp"

#include "Weapon.hpp"
#include "GUIWeaponManager.hpp"
#include "HeatManager.hpp"
#include "SubsystemClassData.hpp"
#include "MWApplication.hpp"
#include "Torso.hpp"
#include "MechLabHeaders.h"

#include <Adept\Map.hpp>
#include <Adept\Effect.hpp>
#include <Adept\Site.hpp>
#include <Adept\DamageObject.hpp>

//#############################################################################
//###############################    Weapon    ################################
//#############################################################################

Weapon::ClassData*
	Weapon::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			WeaponClassID,
			"MechWarrior4::Weapon",
			Subsystem::DefaultData,
			0, NULL,
			(Entity::Factory)Make,
			(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
			ExecutionStateEngine::DefaultData,
			(Entity::GameModel::Factory)GameModel::ConstructGameModel,
			NULL,
			(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
			(Entity::GameModel::ModelWrite)GameModel::WriteToText,
			(Entity::GameModel::ModelSave)GameModel::SaveGameModel,
			(Subsystem::StreamCreate) CreateStream
		);
	Register_Object(DefaultData);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		MuzzleFlashResource,
		muzzleFlashResource,
		ResourceID
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		EjectEffectResource,
		ejectEffectResource,
		ResourceID
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		Heat,
		heat,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		HeatSpreadTime,
		heatSpreadTime,
		Scalar
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		MaxAmmo,
		maxAmmoCount,
		int,
		IntClassID
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		AmmoPerShot,
		ammoPerShot,
		int,
		IntClassID
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		HeatToDeal,
		heatToDeal,
		Scalar
	);

	
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		SplashRadius,
		splashRadius,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		PercentageOfDamageToDirectHit,
		percentageOfDamageToDirectHit,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		MinPercentageOfDamageToSphereHit,
		minPercentageOfDamageToSphereHit,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		MaxPercentageOfDamageToSphereHit,
		maxPercentageOfDamageToSphereHit,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		DamageAmount,
		damageAmount,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		MaxDistance,
		maxDistance,
		Scalar
	);

// MSL 5.03 Min Distance
//	DIRECT_GAME_MODEL_ATTRIBUTE(
//		Weapon__GameModel,
//		MinDistance,
//		minDistance,
//		Scalar
//	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		ReloadTime,
		reloadTime,
		Scalar,
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		LightAmpFlareOut,
		m_lightAmpFlareOut,
		Scalar
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		NumFire,
		numFire,
		int,
		IntClassID
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Weapon__GameModel,
		HudEffect,
		m_HudEffect,
		Scalar,
        ScalarClassID
	); // jcem

}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Weapon*
	Weapon::Make(
		CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(message);
	gos_PushCurrentHeap(Heap);
	Weapon *new_entity =
		new Weapon(DefaultData, message, base_id, NULL);
	gos_PopCurrentHeap();
	Check_Object(new_entity);
	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Replicator::CreateMessage*
	Weapon::SaveMakeMessage(MemoryStream *stream, ResourceFile *res_file)
{
	Check_Object(this);
	Check_Object(stream);

	stream->AllocateBytes(sizeof(CreateMessage));
	Subsystem::SaveMakeMessage(stream, res_file);
	CreateMessage *message =
		Cast_Pointer(CreateMessage*, stream->GetPointer());
	message->messageLength = sizeof(*message);

	Str_Copy(message->siteName, (char *)siteName, sizeof(message->siteName));
	Str_Copy(message->ejectSiteName, (char *)ejectSiteName, sizeof(message->ejectSiteName));

	message->groupIndex = groupIndexFlags;
	message->ammoCount = DECRYPT(encryptedAmmoCount);
	message->initialAmmoCount = initialAmmoCount;

// MSL 5.04 Rear Firing Weapons
	message->m_weaponFacing = m_weaponFacing;

	return message;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Weapon::Weapon(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Subsystem(class_data, message, base_id, element),
	guiWeapon(NULL),
	m_AI_WaitValue(0),
	m_AI_LastCountValue(0),
	m_AI_NonrandomWaitTimes(false)
{
	Check_Pointer(this);
	Check_Object(message);

// MSL 5.04 Rear Firing Weapons				
	m_weaponFacing = message->m_weaponFacing;

	siteName = message->siteName;
	ejectSiteName = message->ejectSiteName;
	groupIndexFlags = message->groupIndex;

	sitePointer		= NULL;
	ejectSitePointer = NULL;
	enginePointer	= NULL;
	weaponID = -1;

	CommonCreation(message);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::CommonCreation(CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);

	m_AI_LastTimeReadyToFire = gos_GetElapsedTime();
	encryptedAmmoCount = ENCRYPT(message->ammoCount);
	initialAmmoCount = message->initialAmmoCount;
	vehicleInterface = NULL;
	m_PlayedRechargeSound = false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::Respawn(Entity::CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);
	
	Subsystem::Respawn(message);
	CreateMessage *weapon_message = Cast_Pointer(CreateMessage *, message);
	CommonCreation(weapon_message);
	if(guiWeapon.GetCurrent())
	{
		guiWeapon.GetCurrent()->SetStatus(GUIWeapon::ReadyStatus);
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Weapon::~Weapon()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool Weapon::ConnectSubsystem()
{
	MWObject *mw_object = GetParentVehicle();
	Check_Object(mw_object);

	enginePointer = NULL;
	heatPointer = mw_object->m_heatManager;

	sitePointer = mw_object->FindSite(siteName);
	Check_Object(sitePointer);

	ejectSitePointer = mw_object->FindSite(ejectSiteName);

	//Must call the inherited virtual function!!!!
	bool return_value = Subsystem::ConnectSubsystem();

	//Only want to do this stuff if we did get connected.
	if(return_value)
	{
		int ammo_tonnage = 0;
		const GameModel *model = GetGameModel();
		Check_Object(model);

		if((model->maxAmmoCount > 0) && (DECRYPT(encryptedAmmoCount) > 0))
			ammo_tonnage = (DECRYPT(encryptedAmmoCount) / GetAmmoPerPack()) - 1; //subtract one because you get one free ton

		mw_object->AddTonage((Scalar)ammo_tonnage);
	}

	return return_value;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//
bool
	Weapon::DisconnectSubsystem()
{
	Check_Object(this);
	
	MWObject *parent_vehicle;
	parent_vehicle = GetParentVehicle();
	Check_Object(parent_vehicle);
	const GameModel *model = GetGameModel();
	Check_Object(model);

	int ammo_tonnage = 0;
	if((model->maxAmmoCount > 0) && (DECRYPT(encryptedAmmoCount) > 0))
		ammo_tonnage = (DECRYPT(encryptedAmmoCount) / GetAmmoPerPack()) - 1;

	parent_vehicle->SubtractTonage((Scalar) ammo_tonnage);
	
	return Subsystem::DisconnectSubsystem();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::CreateMuzzleFlash()
{
	Check_Object(this);

	const GameModel *model = GetGameModel();
	Check_Object(model);
	
	if(model->muzzleFlashResource == ResourceID::Null)
		return;

	Resource muzzle_flash_resource(model->muzzleFlashResource);
	if(muzzle_flash_resource.DoesResourceExist())
	{
		ClassID class_ID;
		class_ID = GetClassIDFromDataListID(model->muzzleFlashResource);
		Verify(class_ID != NullClassID);

		LinearMatrix4D effect_position = LinearMatrix4D::Identity;
		UnitVector3D unit_forward;
		Check_Object(sitePointer);
		LinearMatrix4D site_position = sitePointer->GetLocalToWorld();
		site_position.GetLocalForwardInWorld(&unit_forward);
		effect_position.AlignLocalAxisToWorldVector(unit_forward, Y_Axis, X_Axis, Z_Axis);
		Point3D muzzle_translation;
		muzzle_translation = sitePointer->GetLocalToWorld();
		effect_position.BuildTranslation(muzzle_translation);

		Effect::CreateMessage effect_create_message(
			sizeof(Effect::CreateMessage),
			DefaultEventPriority,
			CreateMessage::DefaultFlags,
			class_ID,
			Effect::DefaultFlags,
			model->muzzleFlashResource,
			effect_position,
			0.0f,
			ExecutionStateEngine::AlwaysExecuteState,
			NameTable::NullObjectID,
			Entity::DefaultAlignment
		);
		MemoryStream stream(&effect_create_message, effect_create_message.messageLength);

		Entity *entity;
		ReplicatorID beam_id = Connection::Hermit->GetNextReplicatorID();
		entity = CreateEntity(&stream, &beam_id, false);
		Check_Object(Map::GetInstance());
		Map::GetInstance()->AddChild(entity);
		Effect *effect;
		effect = Cast_Object(Effect *, entity);
		effect->SetFollowEntity(sitePointer);
		entity->SyncMatrices(true);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::CreateEjectEffect()
{
	Check_Object(this);

	const GameModel *model = GetGameModel();
	Check_Object(model);
	
	if(model->ejectEffectResource == ResourceID::Null)
		return;

	if(ejectSitePointer == NULL)
		return;

	Resource eject_effect_resource(model->ejectEffectResource);
	if(eject_effect_resource.DoesResourceExist())
	{
		ClassID class_ID;
		class_ID = GetClassIDFromDataListID(model->ejectEffectResource);
		Verify(class_ID != NullClassID);

		LinearMatrix4D effect_position = LinearMatrix4D::Identity;
		UnitVector3D unit_forward;
		Check_Object(ejectSitePointer);
		LinearMatrix4D site_position = ejectSitePointer->GetLocalToWorld();
		site_position.GetLocalForwardInWorld(&unit_forward);
		effect_position.AlignLocalAxisToWorldVector(unit_forward, Y_Axis, X_Axis, Z_Axis);
		Point3D muzzle_translation;
		muzzle_translation = ejectSitePointer->GetLocalToWorld();
		effect_position.BuildTranslation(muzzle_translation);

		Effect::CreateMessage effect_create_message(
			sizeof(Effect::CreateMessage),
			DefaultEventPriority,
			CreateMessage::DefaultFlags,
			class_ID,
			Effect::DefaultFlags,
			model->ejectEffectResource,
			effect_position,
			0.0f,
			ExecutionStateEngine::AlwaysExecuteState,
			NameTable::NullObjectID,
			Entity::DefaultAlignment
		);
		MemoryStream stream(&effect_create_message, effect_create_message.messageLength);

		Entity *entity;
		ReplicatorID beam_id = Connection::Hermit->GetNextReplicatorID();
		entity = CreateEntity(&stream, &beam_id, false);
		Check_Object(Map::GetInstance());
		Map::GetInstance()->AddChild(entity);
		Effect *effect;
		effect = Cast_Object(Effect *, entity);
		effect->SetFollowEntity(ejectSitePointer);
		entity->SyncMatrices(true);
	}
}

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


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

// MSL 5.04 Rear Firing Weapons
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Weapon::CanFireForward()
{
	Check_Object(this);

	Torso* torso = parentAsVehicle->GetTorso();

	if ((torso->eyeYaw < 15.0f * Stuff::Radians_Per_Degree) || (torso->eyeYaw > -15.0f * Stuff::Radians_Per_Degree))
		{
			if (m_weaponFacing != 1)
				return (true);
		}

	return (false);
}

// MSL 5.04 Rear Firing Weapons
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Weapon::CanFireRight()
{
	Check_Object(this);

	if ((parentAsVehicle != 0) &&
		(parentAsVehicle->IsDestroyed() == false) &&
		(sitePointer != 0) &&
		(sitePointer->GetParent() != 0) &&
		(sitePointer->GetParent()->damageObject != 0) &&
		(sitePointer->GetParent()->damageObject->internalDamageObject != 0))
	{
		Torso* torso = parentAsVehicle->GetTorso();

		if (torso != 0)
		{
			Adept::InternalDamageObject* ido = sitePointer->GetParent()->damageObject->internalDamageObject;
			
			if ((torso->lookDirection == Torso::LookRightDirection) &&
				(ido->damageZone == Adept::InternalDamageObject::RightArmZone))
			{
				return (true);
			}
			if (m_weaponFacing == 2)
			{
				return (true);
			}
		}
	}

	return (false);
}
// MSL 5.04 Rear Firing Weapons
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Weapon::CanFireLeft()
{
	Check_Object(this);

	if ((parentAsVehicle != 0) &&
		(parentAsVehicle->IsDestroyed() == false) &&
		(sitePointer != 0) &&
		(sitePointer->GetParent() != 0) &&
		(sitePointer->GetParent()->damageObject != 0) &&
		(sitePointer->GetParent()->damageObject->internalDamageObject != 0))
	{
		Torso* torso = parentAsVehicle->GetTorso();

		if (torso != 0)
		{
			Adept::InternalDamageObject* ido = sitePointer->GetParent()->damageObject->internalDamageObject;
			
			if ((torso->lookDirection == Torso::LookLeftDirection) &&
				(ido->damageZone == Adept::InternalDamageObject::LeftArmZone))
			{
				return (true);
			}
			if (m_weaponFacing == 2)
			{
				return (true);
			}
		}
	}

	return (false);
}
// MSL 5.04 Rear Firing Weapons
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Weapon::CanFireBackward()
{
	Check_Object(this);

	if (m_weaponFacing == 1)
			return (true);

	return (false);
}
// MSL 5.04 Rear Firing Weapons
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Weapon::CanFireFromCurrentArm()
{
	Check_Object(this);

	if ((parentAsVehicle != 0) &&
		(parentAsVehicle->IsDestroyed() == false))
	{
		Torso* torso = parentAsVehicle->GetTorso();

		if (torso != 0)
		{
			switch (torso->lookDirection)
			{
				case Torso::LookBackDirection:
						if (CanFireBackward() == false)
						{
							return (false);
						}
						break;
				case Torso::LookLeftDirection:
						if (CanFireLeft() == false)
						{
							return (false);
						}
						break;
				case Torso::LookRightDirection:
						if (CanFireRight() == false)
						{
							return (false);
						}
						break;
				case Torso::LookForwardDirection:
						if (CanFireForward() == false)
						{
							return (false);
						}
						break;
			}
		}
	}

	return (true);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Weapon::FireWeapon(Adept::Entity::CollisionQuery *query,
				   Stuff::Time time_locked,
				   const Stuff::Point3D& lockedTargetEntityPartOffset)
{
	if (CanFireFromCurrentArm() == false)
	{
		return;
	}

	if ((query != 0) &&
		(GetParentVehicle() != 0))
	{
		GetParentVehicle()->NotifyFired(*query);
	}

	Fire(query,time_locked, lockedTargetEntityPartOffset);

	GenerateAIWaitValue();
}
		
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Weapon::QuickFireWeapon (Adept::Entity *target)
{
	Verify(!Application::GetInstance()->InPreCollisionPhase());
	QuickFire(target);

	GenerateAIWaitValue();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Scalar
	Weapon::GetAIWaitValue() const
{
	return (m_AI_WaitValue);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Time
	Weapon::GetLastTimeReadyToFire() const
{
	return (m_AI_LastTimeReadyToFire);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::SetCount(Stuff::Scalar count)
{
	if ((count == 0) &&
		(m_AI_LastCountValue != 0))
	{
		m_AI_LastTimeReadyToFire = gos_GetElapsedTime();
	}

	if (guiWeapon.GetCurrent())
	{
		Check_Object(guiWeapon.GetCurrent());
		guiWeapon.GetCurrent()->SetCount(count);
	}

	m_AI_LastCountValue = count;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::ApplyHeat()
{
	Check_Object(this);

	const GameModel *model = GetGameModel();
	Check_Object(model);

	if(heatPointer)
	{
		Check_Object(heatPointer);
		heatPointer->AddHeat(model->heat);
		heatPointer->AddHeatObject(model->heatSpreadTime, model->heat);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Scalar
	Weapon::GetTotalHeatGenerated()
{
	Check_Object(this);

	const GameModel *model = GetGameModel();
	Check_Object(model);

	return (model->heat * 2);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Weapon::DoesHaveAmmo()
{
	Check_Object(this);

	const GameModel *model = GetGameModel();
	Check_Object(model);

// MSL 5.03 Bug Fix
// -1 == Beam weapon...so should return a false unless it's a PPC
	if((DECRYPT(encryptedAmmoCount) == -1) && ((model->itemID == W_ClanERPPC) || (model->itemID == W_PPC) || (model->itemID == W_ERPPC)))
		return true;

	if(DECRYPT(encryptedAmmoCount) >= model->ammoPerShot)
		return true;

	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::UseAmmo()
{
	Check_Object(this);

	const GameModel *model = GetGameModel();
	Check_Object(model);

	int ammo_count = DECRYPT(encryptedAmmoCount);

	NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();
	if(ammo_count >= 0 && !params->m_unlimitedAmmo)
	{
		ammo_count -= model->ammoPerShot;

		if (ammo_count < model->ammoPerShot)
		{
			ammo_count = 0;
		}

		Min_Clamp(ammo_count, 0);

		encryptedAmmoCount = ENCRYPT(ammo_count);

		if (ammo_count == 0)
		{
			if (GetParentVehicle ()->vehicleInterface)
			{
				GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::OUT_OF_AMMO);
			}

		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::SetMaxWaitValue()
{
	m_AI_WaitValue = 1;
	m_AI_LastTimeReadyToFire = gos_GetElapsedTime();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::DestroySubsystem()
{
	Check_Object(this);

// MSL 5.02 Weapon Destroyed Betty
	Check_Object(executionState);
	if(GetParentVehicle()->vehicleInterface)
	{
		GetParentVehicle()->vehicleInterface->ReactToEvent (VehicleInterface::CRITICAL_HIT_WEAPON);
	}

	Subsystem::DestroySubsystem();
	if(guiWeapon.GetCurrent())
	{
		guiWeapon.GetCurrent()->SetStatus(GUIWeapon::DestroyedStatus);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Weapon::TakeCriticalHit()
{
	Check_Object(this);
	Check_Object(executionState);
// MSL 5.02 Weapon Destroyed Betty	
//	if(GetParentVehicle()->vehicleInterface)
//	{
//		GetParentVehicle()->vehicleInterface->ReactToEvent (VehicleInterface::CRITICAL_HIT_WEAPON);
//	}
	return Subsystem::TakeCriticalHit();
}


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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::CreateStream(ResourceID data_list, MemoryStream *stream)
{
	Check_Pointer(stream);
	
	stream->AllocateBytes(sizeof(Weapon__CreateMessage));
	Subsystem::CreateStream(data_list, stream);

	Weapon__CreateMessage *create_message = Cast_Pointer(
		Weapon__CreateMessage *, stream->GetPointer());

	Str_Copy(create_message->siteName, "site_", sizeof(create_message->siteName));
	Str_Copy(create_message->ejectSiteName, " ", sizeof(create_message->ejectSiteName));

	create_message->groupIndex = 1;

	Resource model_resource;
	Entity::GetGameModelResourceFromDataListID(&model_resource, data_list);
	GameModel *model = Cast_Pointer(GameModel *, model_resource.GetPointer());

	if((model->itemID >= FirstProjectileID) && (model->itemID <= LastProjectileID))
		create_message->groupIndex = 2;
	if((model->itemID >= FirstMissileID) && (model->itemID <= LastMissileID))
		create_message->groupIndex = 4;

	if(model->itemID == W_Flare)
		create_message->groupIndex = 32;

	if((model->itemID == W_NarcBeacon) || (model->itemID == W_ClanNarcBeacon))
		create_message->groupIndex = 8;

	if(model->maxAmmoCount > 0)
	{																					
		Verify(model->totalSlotsTaken != 0);
		create_message->ammoCount = model->maxAmmoCount / (3 * model->totalSlotsTaken);
		create_message->initialAmmoCount = model->maxAmmoCount / (3 * model->totalSlotsTaken);
	}
	else
	{
		create_message->ammoCount = -1;
		create_message->initialAmmoCount = -1;
	}

// MSL 5.04 Rear Firing Weapons				
	create_message->m_weaponFacing = 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::SetWeaponGroup(int group)
{
	Check_Object(this);
	
	switch(group)
	{
	case 1:
		SetGroup1();
		break;
	case 2:
		SetGroup2();
		break;
	case 3:
		SetGroup3();
		break;
	case 4:
		SetGroup4();
		break;
	case 5:
		SetGroup5();
		break;
	case 6:
		SetGroup6();
		break;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::ClearWeaponGroup(int group)
{
	Check_Object(this);
	
	switch(group)
	{
	case 1:
		ClearGroup1();
		break;
	case 2:
		ClearGroup2();
		break;
	case 3:
		ClearGroup3();
		break;
	case 4:
		ClearGroup4();
		break;
	case 5:
		ClearGroup5();
		break;
	case 6:
		ClearGroup6();
		break;
	}

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Weapon::AddAmmoPack()
{
	Check_Object(this);

	const GameModel *model = GetGameModel();
	Check_Object(model);

	if(DECRYPT(encryptedAmmoCount) < model->maxAmmoCount)
	{
		if(GetParentVehicle()->DoesHaveAvailableTonage(1.0f))
		{
			int ammo_count = DECRYPT(encryptedAmmoCount);

			int added_ammo = GetAmmoPerPack();
			ammo_count += added_ammo;
			Max_Clamp(ammo_count, model->maxAmmoCount);
			initialAmmoCount = ammo_count;
			if(added_ammo > 0)
				GetParentVehicle()->AddTonage(1.0f);

			encryptedAmmoCount = ENCRYPT(ammo_count);

			return true;
		}
		return false;
	}

	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Weapon::RemoveAmmoPack()
{
	Check_Object(this);
	
	int ammo_count = DECRYPT(encryptedAmmoCount);

	int sub_ammo = GetAmmoPerPack();
	if(ammo_count > sub_ammo)
	{
		ammo_count -= sub_ammo;
		Min_Clamp(ammo_count, sub_ammo);
		initialAmmoCount = ammo_count;

		encryptedAmmoCount = ENCRYPT(ammo_count);

		if(sub_ammo > 0)
			GetParentVehicle()->SubtractTonage(1.0f);
		return true;
	}

	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	Weapon::GetAmmoPacks()
{
	Check_Object(this);

	if(DECRYPT(encryptedAmmoCount) > 0)
		return DECRYPT(encryptedAmmoCount) / GetAmmoPerPack();

	return 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	Weapon::GetAmmoPerPack()
{
	Check_Object(this);
	const GameModel *model = GetGameModel();
	Check_Object(model);
	Check_Object(model);
	Verify(model->totalSlotsTaken != 0);
	int value = model->maxAmmoCount / (3 * model->totalSlotsTaken);

	Min_Clamp(value, 0);

	return value;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::CreateLightAmpFlareOut()
{
	Check_Object(this);
	Check_Object(GetParentVehicle());

	if(GetParentVehicle()->vehicleInterface)
	{
		const GameModel *model = GetGameModel();
		Check_Object(model);
		//Here is where we need to effect the Light Amp values
		GetParentVehicle()->vehicleInterface->SetLightAmpFlareOut(model->m_lightAmpFlareOut);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::SetNonrandomWaitTimes(bool use_nonrandom)
{
	m_AI_NonrandomWaitTimes = use_nonrandom;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Weapon::GenerateAIWaitValue()
{
	if (m_AI_NonrandomWaitTimes == true)
	{
		m_AI_WaitValue = 0.5f;
	}
	else
	{
		m_AI_WaitValue = Stuff::Random::GetFraction();
	}

}