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

#pragma once

#include "MW4.hpp"
#include "Subsystem.hpp"
#include "vehicleInterface.hpp"

namespace Adept
{
	class Site;
}

namespace MechWarrior4
{
	class Vehicle;
	class Engine;
	class MWMover;
	class Vehicle;
	class GUIWeapon;
	class HeatManager;


	void WeaponSecurityCheckStart();
	void WeaponSecurityCheckStop();


	//##########################################################################
	//##################    Weapon::CreateMessage    ###########################
	//##########################################################################

	class Weapon__CreateMessage:
		public Subsystem__CreateMessage
	{
	public:
		typedef Subsystem__CreateMessage BaseClass;
	
		char
			siteName[128];
		char
			ejectSiteName[128];
		int
			groupIndex;
		int
			ammoCount;
		int
			initialAmmoCount;
// MSL 5.04 Rear Firing Weapons				
		int
			m_weaponFacing;

		Weapon__CreateMessage(
			size_t length,
			int priority,
			int message_flags,
			Stuff::RegisteredClass::ClassID class_id,
			int replicator_flags,
			const ResourceID& instance_id,
			const Stuff::LinearMatrix4D &creation_matrix,
			Stuff::Scalar age,
			int execution_state,
			int name_id,
			int entity_alignment,
			int subsystem_index,
			BYTE location_id,
			int critical_hits,
			const char *site_name,
			int group_index,
			int ammo_count,
			int initial_ammo,
			const char *eject_site,
// MSL 5.04 Rear Firing Weapons				
			int weapon_facing
		):
			Subsystem__CreateMessage(
				length,
				priority,
				message_flags,
				class_id,
				replicator_flags,
				instance_id,
				creation_matrix,
				age,
				execution_state,
				name_id,
				entity_alignment,
				subsystem_index,
				location_id,
				critical_hits
			),
				groupIndex(group_index),
				ammoCount(ammo_count),
// MSL 5.04 Rear Firing Weapons				
				m_weaponFacing(weapon_facing),
				initialAmmoCount(initial_ammo)
			{
				Check_Pointer(site_name);
				Str_Copy(siteName, site_name, sizeof(siteName));
				Str_Copy(ejectSiteName, eject_site, sizeof(ejectSiteName));
			}

		static void
			ConstructCreateMessage(Script *script);
	};

	//##########################################################################
	//###########################   Weapon GameModel    ########################
	//##########################################################################

	class Weapon__GameModel;

	typedef Subsystem__ClassData							Weapon__ClassData;
	typedef Subsystem__Message								Weapon__Message;
	typedef Subsystem__ExecutionStateEngine					Weapon__ExecutionStateEngine;
	
	class Weapon__GameModel:
		public Subsystem::GameModel
	{
	public:
		typedef Subsystem::GameModel BaseClass;

		Adept::ResourceID
			muzzleFlashResource;
		Adept::ResourceID
			ejectEffectResource;
		Stuff::Scalar
			heat;
		Stuff::Scalar
			heatSpreadTime;
		int
			maxAmmoCount;
		int
			numFire;
		int
			ammoPerShot;
		Stuff::Scalar
			heatToDeal;
		Stuff::Scalar
			damageAmount;
		Stuff::Scalar
			maxDistance;
// MSL 5.03 Min Distance
//		Stuff::Scalar
//			minDistance;
		Scalar
			splashRadius;
		Stuff::Scalar
			percentageOfDamageToDirectHit;
		Stuff::Scalar
			minPercentageOfDamageToSphereHit;
		Stuff::Scalar
			maxPercentageOfDamageToSphereHit;
		Stuff::Scalar
			reloadTime;
		Stuff::Scalar
			m_lightAmpFlareOut;	

        Stuff::Scalar
            m_HudEffect; // jcem

		enum {
			MuzzleFlashResourceAttributeID = Subsystem__GameModel::NextAttributeID,
			EjectEffectResourceAttributeID,
			HeatAttributeID,
			HeatSpreadTimeAttributeID,
			MaxAmmoAttributeID,
			AmmoPerShotAttributeID,
			HeatToDealAttributeID,
			SplashRadiusAttributeID,
			PercentageOfDamageToDirectHitAttributeID,
			MinPercentageOfDamageToSphereHitAttributeID,
			MaxPercentageOfDamageToSphereHitAttributeID,
			DamageAmountAttributeID,
			MaxDistanceAttributeID,
// MSL 5.03 Min Distance
//			MinDistanceAttributeID,
			ReloadTimeAttributeID,
			LightAmpFlareOutAttributeID,
			NumFireAttributeID,
            HudEffectAttributeID,
			NextAttributeID
		};

		static bool
			ReadAndVerify(
				Weapon__GameModel *model,
				ModelAttributeEntry *attribute_entry,
				const char *data,
				char **error,
				int error_buffer = 128
			);

		static void
			ConstructGameModel(Script *script);
	};

	//##########################################################################
	//###########################   Weapon  ####################################
	//##########################################################################
	class Weapon:
		public Subsystem
	{
	public:
		static void
			InitializeClass();
		static void
			TerminateClass();

		typedef Subsystem BaseClass;

	//##########################################################################
	// Inheritance support
	//
	public:
		typedef Weapon__ClassData									ClassData;
		typedef Weapon__GameModel									GameModel;
		typedef Weapon__Message										Message;
		typedef Weapon__ExecutionStateEngine					ExecutionStateEngine;
		typedef Weapon__CreateMessage								CreateMessage;


	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Run-time Construction and Destruction Support
	//
	public:
		static Weapon*
			Make(
				CreateMessage *message,
				ReplicatorID *base_id
			);
		
		Adept::Replicator::CreateMessage*
			SaveMakeMessage(Stuff::MemoryStream *stream, Adept::ResourceFile *res_file);

		void
			SaveInstanceText(Stuff::Page *page);
		void
			Respawn(Adept::Entity::CreateMessage *message);
		void
			CommonCreation(CreateMessage *message);

		static void
			CreateStream(
				Adept::ResourceID data_list,
				Stuff::MemoryStream *stream
			);

	protected:
		Weapon(
			ClassData *class_data,
			CreateMessage *message,
			ReplicatorID *base_id,
			ElementRenderer::Element *element
		);

		~Weapon();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Class Data & Game Model Support
	//
	public:
		const GameModel*
			GetGameModel()
				{return Cast_Pointer(const GameModel*, gameModelResource.GetPointer());}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data support
	//
	public:
		static ClassData
			*DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Simulation Support
	//
		enum
		{
			WeaponGroup1FlagBit = 0,
			WeaponGroup2FlagBit,
			WeaponGroup3FlagBit,
			WeaponGroup4FlagBit,
			WeaponGroup5FlagBit,
			WeaponGroup6FlagBit

		};

		enum
		{
			WeaponGroup1Flag		= 1<<WeaponGroup1FlagBit,
			WeaponGroup2Flag		= 1<<WeaponGroup2FlagBit,
			WeaponGroup3Flag		= 1<<WeaponGroup3FlagBit,
			WeaponGroup4Flag		= 1<<WeaponGroup4FlagBit,
			WeaponGroup5Flag		= 1<<WeaponGroup5FlagBit,
			WeaponGroup6Flag		= 1<<WeaponGroup6FlagBit,
			DefaultWeaponGroupFlags			= 0
		};


		enum
		{
			FacingForwardFlagBit = 0,
			FacingBackFlagBit,
			FacingAllFlagBit,
		};

		enum
		{
			FacingForwardFlag = 1<<FacingForwardFlagBit,
			FacingBackFlag = 1<<FacingBackFlagBit,
			FacingAllFlag = 1<<FacingAllFlagBit,
		};
		
		
		VehicleInterface *vehicleInterface;
		bool m_PlayedRechargeSound;
		
		bool
			IsInWeaponGroup1()
				{Check_Object(this); return (groupIndexFlags & WeaponGroup1Flag) != 0;}
		bool
			IsInWeaponGroup2()
				{Check_Object(this); return (groupIndexFlags & WeaponGroup2Flag) != 0;}
		bool
			IsInWeaponGroup3()
				{Check_Object(this); return (groupIndexFlags & WeaponGroup3Flag) != 0;}
		bool
			IsInWeaponGroup4()
				{Check_Object(this); return (groupIndexFlags & WeaponGroup4Flag) != 0;}
		bool
			IsInWeaponGroup5()
				{Check_Object(this); return (groupIndexFlags & WeaponGroup5Flag) != 0;}
		bool
			IsInWeaponGroup6()
				{Check_Object(this); return (groupIndexFlags & WeaponGroup6Flag) != 0;}
		void
			SetGroup1()
				{Check_Object(this); groupIndexFlags |= WeaponGroup1Flag;}
		void
			SetGroup2()
				{Check_Object(this); groupIndexFlags |= WeaponGroup2Flag;}
		void
			SetGroup3()
				{Check_Object(this); groupIndexFlags |= WeaponGroup3Flag;}
		void
			SetGroup4()
				{Check_Object(this); groupIndexFlags |= WeaponGroup4Flag;}
		void
			SetGroup5()
				{Check_Object(this); groupIndexFlags |= WeaponGroup5Flag;}
		void
			SetGroup6()
				{Check_Object(this); groupIndexFlags |= WeaponGroup6Flag;}
		void
			ClearGroup1()
				{Check_Object(this); groupIndexFlags &= ~WeaponGroup1Flag;}
		void
			ClearGroup2()
				{Check_Object(this); groupIndexFlags &= ~WeaponGroup2Flag;}
		void
			ClearGroup3()
				{Check_Object(this); groupIndexFlags &= ~WeaponGroup3Flag;}
		void
			ClearGroup4()
				{Check_Object(this); groupIndexFlags &= ~WeaponGroup4Flag;}
		void
			ClearGroup5()
				{Check_Object(this); groupIndexFlags &= ~WeaponGroup5Flag;}
		void
			ClearGroup6()
				{Check_Object(this); groupIndexFlags &= ~WeaponGroup6Flag;}
		void
			SetWeaponGroup(int group);
		void
			ClearWeaponGroup(int group);

		void
			ApplyHeat();

		int GetAmmoCount()
				{Check_Object(this); return DECRYPT(encryptedAmmoCount);}

// MSL 5.05 Rear Firing Weapons
		int GetWeaponFacing()
				{Check_Object(this); return m_weaponFacing;}

		void
			SetWeaponFacing(int facing)
				{Check_Object(this); m_weaponFacing = facing;}

		void
			ClearWeaponFacing()
				{Check_Object(this); m_weaponFacing = 0;}

		void SetAmmoCount(int ammo_count)
		{Check_Object(this); encryptedAmmoCount = ENCRYPT(ammo_count);};
		
		bool DoesHaveAmmo();

		void ClearAmmo()
			{Check_Object(this); encryptedAmmoCount = ENCRYPT(0);}

		void UseAmmo();

		bool AddAmmoPack();
		bool RemoveAmmoPack();
		int GetAmmoPacks();
		int GetAmmoPerPack();

		void
			DestroySubsystem();
		bool
			TakeCriticalHit();

		int
			weaponID;
		
// MSL 5.04 Rear Firing Weapons
		int m_weaponFacing;

		Stuff::MString		
			siteName;
		Stuff::MString
			ejectSiteName;
				
		Engine*
			enginePointer;
		HeatManager*
			heatPointer;
			
		Adept::Site*
			sitePointer;
		Adept::Site*
			ejectSitePointer;
		
		int
			groupIndexFlags;

		Stuff::SlotOf<GUIWeapon*>
			guiWeapon;

		bool
			ConnectSubsystem();
		bool
			DisconnectSubsystem();
		void
			ConnectHeatManager(HeatManager *heat_manager)
				{Check_Object(this); heatPointer = heat_manager;}

		void
			FireWeapon(Adept::Entity::CollisionQuery *query, 
					   Stuff::Time time_locked, 
					   const Stuff::Point3D& lockedTargetEntityPartOffset);

		void QuickFireWeapon (Adept::Entity *target);

		virtual void
			CreateMuzzleFlash();
		void
			CreateEjectEffect();
		void
			CreateLightAmpFlareOut();
		virtual bool
			ReadyToFire()
			{
				Check_Object(this);
				return false;
			}

		Stuff::Scalar
			GetTotalHeatGenerated();

		void
			SetWeaponID(int weapon_id)
			{
				weaponID = weapon_id;
			}
		
		int
			GetWeaponID(void)
			{
				return weaponID;
			}

		Stuff::Scalar
			GetAIWaitValue() const;

		void
			SetMaxWaitValue();

		Stuff::Time
			GetLastTimeReadyToFire() const;
		bool
			HasFiredAmmo()
				{Check_Object(this); return (initialAmmoCount - DECRYPT(encryptedAmmoCount)) > 0;}
		void
			Reload()
				{Check_Object(this); encryptedAmmoCount = ENCRYPT(initialAmmoCount);}

		int encryptedAmmoCount;

		int initialAmmoCount;

		bool
			CanFireFromCurrentArm();

		bool
			CanFireForward();
		bool
			CanFireRight();
		bool
			CanFireLeft();
		bool
			CanFireBackward();

		void
			SetNonrandomWaitTimes(bool use_nonrandom);

	protected:
		virtual void
			Fire(Adept::Entity::CollisionQuery *query, 
				 Stuff::Time time_locked, 
				 const Stuff::Point3D& lockedTargetEntityPartOffset)
				{ }
		virtual void QuickFire (Adept::Entity *target)
			{  }

		void SetCount(Stuff::Scalar count);

		void GenerateAIWaitValue();

	private:
		Stuff::Scalar m_AI_WaitValue;
		Stuff::Time m_AI_LastTimeReadyToFire;
		Stuff::Scalar m_AI_LastCountValue;
		bool m_AI_NonrandomWaitTimes;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Support
	//
	public:
		void
      	TestInstance() const;
	};
}


