/*************************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2004.
-------------------------------------------------------------------------
$Id$
$DateTime$

-------------------------------------------------------------------------
History:
- 22:8:2005   12:50 : Created by Márcio Martins

*************************************************************************/
#include "StdAfx.h"
#include "Game.h"
#include <IEntitySystem.h>
#include <IScriptSystem.h>
#include <IActionMapManager.h>
#include "WeaponSystem.h"
#include "Weapon.h"
#include "IGameObject.h"
#include "ISerialize.h"
#include "ScriptBind_Weapon.h"
#include "Actor.h"
#include "IGameObjectSystem.h"


//------------------------------------------------------------------------
CWeapon::CWeapon()
: m_fm(0),
	m_melee(0),
	m_zm(0),
	m_fmDefaults(0),
	m_zmDefaults(0),
	m_pFiringLocator(0),
	m_fire_alternation(false),
  m_destination(0,0,0),
	m_dofSpeed(0.0f),
	m_dofValue(0.0f)
{
}

//------------------------------------------------------------------------1
CWeapon::~CWeapon()
{
	// deactivate everything
	for (TFireModeVector::iterator it = m_firemodes.begin(); it != m_firemodes.end(); it++)
		(*it)->Activate(false);
	// deactivate zoommodes
	for (TZoomModeVector::iterator it = m_zoommodes.begin(); it != m_zoommodes.end(); it++)
		(*it)->Activate(false);

	// clean up firemodes
	for (TFireModeVector::iterator it = m_firemodes.begin(); it != m_firemodes.end(); it++)
		delete *it;
	// clean up zoommodes
	for (TZoomModeVector::iterator it = m_zoommodes.begin(); it != m_zoommodes.end(); it++)
		delete *it;

	if (m_pFiringLocator)
		m_pFiringLocator->WeaponReleased();

	if (m_fmDefaults)
		m_fmDefaults->Release();
	if (m_zmDefaults)
		m_zmDefaults->Release();
	if (m_params)
		m_params->Release();
}

//------------------------------------------------------------------------
bool CWeapon::ReadItemParams(const IItemParamsNode *root)
{
	if (!CItem::ReadItemParams(root))
		return false;

	// read params
	string melee_attack_firemode;
	const IItemParamsNode *params = root->GetChild("params");
	{
		CItemParamReader reader(params);
		reader.Read("melee_attack_firemode", melee_attack_firemode);
	}
		
	const IItemParamsNode *firemodes = root->GetChild("firemodes");
	InitFireModes(firemodes);

	const IItemParamsNode *zoommodes = root->GetChild("zoommodes");
	InitZoomModes(zoommodes);

	const IItemParamsNode *ammos = root->GetChild("ammos");
	InitAmmos(ammos);

	const IItemParamsNode *aiData = root->GetChild("ai_descriptor");
	InitAIData(aiData);

	m_params = root;
	m_params->AddRef();

	if (!melee_attack_firemode.empty())
	{
		m_melee = GetFireMode(melee_attack_firemode.c_str());
		if (m_melee)
			m_melee->Enable(false);
	}

	return true;
}

//------------------------------------------------------------------------
const IItemParamsNode *CWeapon::GetFireModeParams(const char *name)
{
	if (!m_params)
		return 0;

	const IItemParamsNode *firemodes = m_params->GetChild("firemodes");
	if (!firemodes)
		return 0;

	int n = firemodes->GetChildCount();
	for (int i=0; i<n; i++)
	{
		const IItemParamsNode *fm = firemodes->GetChild(i);

		const char *fmname = fm->GetAttribute("name");
		if (!fmname || !strlen(fmname) || stricmp(name, fmname))
			continue;

		return fm;
	}

	return 0;
}

//------------------------------------------------------------------------
const IItemParamsNode *CWeapon::GetZoomModeParams(const char *name)
{
	if (!m_params)
		return 0;

	const IItemParamsNode *zoommodes = m_params->GetChild("zoommodes");
	if (!zoommodes)
		return 0;

	int n = zoommodes->GetChildCount();
	for (int i=0; i<n; i++)
	{
		const IItemParamsNode *zm = zoommodes->GetChild(i);

		const char *zmname = zm->GetAttribute("name");
		if (!zmname || !strlen(zmname) || stricmp(name, zmname))
			continue;

		return zm;
	}

	return 0;
}

//------------------------------------------------------------------------
void CWeapon::InitFireModes(const IItemParamsNode *firemodes)
{
	if (!firemodes)
		return;

	int n = firemodes->GetChildCount();

	// find the default params
	m_fmDefaults = 0;
	for (int k=0; k<n; k++)
	{
		const IItemParamsNode *fm = firemodes->GetChild(k);
		const char *typ = fm->GetAttribute("type");

		if (typ && !strcmpi(typ, "default"))
		{
			m_fmDefaults = fm;
			m_fmDefaults->AddRef();
			break;
		}
	}

	for (int i=0; i<n; i++)
	{
		const IItemParamsNode *fm = firemodes->GetChild(i);

		const char *name = fm->GetAttribute("name");
		const char *typ = fm->GetAttribute("type");
		int etest = 1;
		fm->GetAttribute("enabled", etest);
		bool enabled = etest!=0;

		if (!typ || !strlen(typ))
		{
			GameWarning("Missing type for firemode in weapon '%s'! Skipping...", GetEntity()->GetName());
			continue;
		}

		if (!strcmpi(typ, "default"))
			continue;

		if (!name || !strlen(name))
		{
			GameWarning("Missing name for firemode in weapon '%s'! Skipping...", GetEntity()->GetName());
			continue;
		}

		IFireMode *pFireMode = g_pGame->GetWeaponSystem()->CreateFireMode(typ);
		if (!pFireMode)
		{
			GameWarning("Cannot create firemode '%s' in weapon '%s'! Skipping...", typ, GetEntity()->GetName());
			continue;
		}

		pFireMode->Init(this, m_fmDefaults);
		pFireMode->PatchParams(fm);
		if (!enabled)
			pFireMode->Enable(false);
		else
			pFireMode->Enable(true);

		m_firemodes.push_back(pFireMode);
		m_fmIds.insert(TFireModeIdMap::value_type(name, m_firemodes.size()-1));
	}
}

//------------------------------------------------------------------------
void CWeapon::InitZoomModes(const IItemParamsNode *zoommodes)
{
	if (!zoommodes)
	{
		m_zmId = 0;

		return;
	}

	int n = zoommodes->GetChildCount();

	// find the default params
	m_zmDefaults = 0;
	for (int k=0; k<n; k++)
	{
		const IItemParamsNode *zm = zoommodes->GetChild(k);
		const char *typ = zm->GetAttribute("type");

		if (typ && !strcmpi(typ, "default"))
		{
			m_zmDefaults = zm;
			m_zmDefaults->AddRef();
			break;
		}
	}

	for (int i=0; i<n; i++)
	{
		const IItemParamsNode *zm = zoommodes->GetChild(i);

		const char *name = zm->GetAttribute("name");
		const char *typ = zm->GetAttribute("type");

		if (!typ || !strlen(typ))
		{
			GameWarning("Missing type for zoommode in weapon '%s'! Skipping...", GetEntity()->GetName());
			continue;
		}

		if (!strcmpi(typ, "default"))
			continue;

		if (!name || !strlen(name))
		{
			GameWarning("Missing name for zoommode in weapon '%s'! Skipping...", GetEntity()->GetName());
			continue;
		}

		IZoomMode *pZoomMode = g_pGame->GetWeaponSystem()->CreateZoomMode(typ);
		if (!pZoomMode)
		{
			GameWarning("Cannot create zoommode '%s' in weapon '%s'! Skipping...", typ, GetEntity()->GetName());
			continue;
		}

		pZoomMode->Init(this, m_zmDefaults);
		pZoomMode->PatchParams(zm);

		m_zoommodes.push_back(pZoomMode);
		m_zmIds.insert(TFireModeIdMap::value_type(name, m_zoommodes.size()-1));
	}
}

//------------------------------------------------------------------------
void CWeapon::InitAmmos(const IItemParamsNode *ammos)
{
	if (!ammos)
		return;

	for (int i=0; i<ammos->GetChildCount(); i++)
	{
		const IItemParamsNode *ammo = ammos->GetChild(i);
		if (!strcmpi(ammo->GetName(), "ammo"))
		{
			const char *name=0;
			int clips=0;
			int amount=0;

			name = ammo->GetAttribute("name");
			ammo->GetAttribute("clips", clips);
			ammo->GetAttribute("amount", amount);

			if (name && g_pGame->GetWeaponSystem()->GetAmmoParams(name))
			{
				SetAmmoCount(name, amount);
				SetClipCount(name, clips);
			}
		}
	}
}

//------------------------------------------------------------------------
void CWeapon::InitAIData(const IItemParamsNode *aiDescriptor)
{
	if (!aiDescriptor)
		return;
//	<ai_descriptor	hit="instant" speed="20" damage_radius="45" charge_time="2.5" />
	
	aiDescriptor->GetAttribute("speed", m_aiWeaponDescriptor.fSpeed);
	aiDescriptor->GetAttribute("damage_radius", m_aiWeaponDescriptor.fDamageRadius);
	aiDescriptor->GetAttribute("charge_time", m_aiWeaponDescriptor.fChargeTime);
	m_aiWeaponDescriptor.firecmdHandler = aiDescriptor->GetAttribute("handler");
	int	signalOnShoot(0);
	aiDescriptor->GetAttribute("signal_on_shoot", signalOnShoot);
	m_aiWeaponDescriptor.bSignalOnShoot = signalOnShoot != 0;
}


//------------------------------------------------------------------------
bool CWeapon::Init( IGameObject * pGameObject )
{
	if (!CItem::Init(pGameObject))
		return false;

	g_pGame->GetWeaponScriptBind()->AttachTo(this);

	SetCurrentFireMode(0);
	SetCurrentZoomMode(0);

	return true;
}

//------------------------------------------------------------------------
void CWeapon::PostInit(IGameObject * pGameObject )
{
	CItem::PostInit(pGameObject);

	// TODO: remove these and only update when necessary...
	pGameObject->EnableUpdateSlot( this, eIUS_FireMode );
	pGameObject->EnableUpdateSlot( this, eIUS_Zooming );
}

//------------------------------------------------------------------------
void CWeapon::Release()
{
	delete this;
}

//------------------------------------------------------------------------
void CWeapon::Serialize( TSerialize ser, unsigned aspects )
{
	CItem::Serialize(ser, aspects);

	if (ser.GetSerializationTarget() == eST_Network)
	{
		if (aspects & eEA_GameServerDynamic)
		{
			ser.Value("firemode", this, &CWeapon::NetGetCurrentFireMode, &CWeapon::NetSetCurrentFireMode, NSerPolicy::AS_RangedInt32(0, 7));
		}
	}
}

//------------------------------------------------------------------------
void CWeapon::Update( SEntityUpdateContext& ctx, int update)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);
	
	if (m_frozen)
		return;

	switch (update)
	{
	case eIUS_FireMode:
		if (m_fm)
			m_fm->Update(ctx.fFrameTime, ctx.nFrameID);
		if (m_melee)
			m_melee->Update(ctx.fFrameTime, ctx.nFrameID);
		break;

	case eIUS_Zooming:
		if (m_zm)
			m_zm->Update(ctx.fFrameTime, ctx.nFrameID);
		break;
	}
  
	CItem::Update(ctx, update);


	if (fabsf(m_dofSpeed)>0.001f)
	{
		m_dofValue += m_dofSpeed*ctx.fFrameTime;
		m_dofValue = CLAMP(m_dofValue, 0, 1);

		GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_BlurAmount", m_dofValue);
	}
}

//------------------------------------------------------------------------
void CWeapon::HandleEvent( const SGameObjectEvent& event)
{
	CItem::HandleEvent(event);
}

//------------------------------------------------------------------------
void CWeapon::ProcessEvent(SEntityEvent& event)
{
	CItem::ProcessEvent(event);

	switch(event.event)
	{
	case ENTITY_EVENT_TIMER:
		{
			switch(event.nParam[0])
			{
			case eWT_Shoot:
				EnableUpdate(false);
				break;
			}
		}
	}
}

//------------------------------------------------------------------------
void CWeapon::SetAuthority(bool auth)
{
	CItem::SetAuthority(auth);
}

//------------------------------------------------------------------------
void CWeapon::OnAction(EntityId actorId, const char *actionName, int activationMode, float value)
{
	CItem::OnAction(actorId, actionName, activationMode, value);

	bool isDualWield = IsDualWieldMaster();
	CWeapon *dualWield = 0;
	
	if (isDualWield)
	{
		IItem *slave = GetDualWieldSlave();
		if (slave && slave->GetIWeapon())
			dualWield = static_cast<CWeapon *>(slave);
		else
			isDualWield = false;
	}

	// attack
	if (!strcmp(actionName, "attack1") && !m_bModifying)
	{
		if (activationMode == eAAM_OnPress)
		{
			if (isDualWield)
			{
				m_fire_alternation = !m_fire_alternation;

				if ((m_fire_alternation && CanFire()) || (!dualWield->CanFire()))
					StartFire(actorId);
				else
					dualWield->StartFire(actorId);
			}
			else
				StartFire(actorId);
		}
		if (activationMode == eAAM_OnRelease)
		{
			if (isDualWield)
			{
				StopFire(actorId);
				dualWield->StopFire(actorId);
			}
			else
				StopFire(actorId);
		}
	}

	// reload
	if (!strcmp(actionName, "reload") && !m_bModifying)
	{
		Reload();
	
		if (isDualWield)
			dualWield->Reload();
	}

	// zoom
	if (!strcmp(actionName, "zoom") && !m_bModifying)
	{
		if (!isDualWield)
		{
			if (activationMode == eAAM_OnPress && m_fm && !m_fm->IsReloading())
				StartZoom(actorId);		
			if (activationMode == eAAM_OnRelease)
				StopZoom(actorId);
		}
	}

	// fire mode
	if (!strcmp(actionName, "firemode") && !m_bModifying)
	{
		if (isDualWield)
		{
			ChangeFireMode();
			dualWield->ChangeFireMode();
		}
		else
			ChangeFireMode();
	}

	if (!strcmp(actionName, "special"))
	{
		if (activationMode == eAAM_OnPress)
		{
			if (CanMeleeAttack())
				MeleeAttack();
		}
	}

	if (!strcmp(actionName, "modify") /*&& !strcmp(GetEntity()->GetClass()->GetName(), "SCAR")*/ && !IsBusy() && (!m_zm || (m_zm && !m_zm->IsZoomed())))
	{
		if (m_fm)
			m_fm->StopFire(GetEntity()->GetId());
		SmartScriptTable scriptTable = GetEntity()->GetScriptTable();
		SmartScriptTable modifyTable;
		SmartScriptTable modifyTableSCAR;
		bool bModifyTableSCAR = false;
		if (GetISystem()->GetIScriptSystem()->GetGlobalValue("WeaponModify", modifyTable))
		{
			bModifyTableSCAR = modifyTable->GetValue("SCARModify", modifyTableSCAR);
		}
		SmartScriptTable itemSystemUITable;
		bool itemSystemUITableTest = GetISystem()->GetIScriptSystem()->GetGlobalValue("ItemSystemUI", itemSystemUITable);

		HSCRIPTFUNCTION uiActivateFunc(NULL);
		if (itemSystemUITableTest)
		{
			itemSystemUITable->GetValue("Activate", uiActivateFunc);
			if (uiActivateFunc)
			{
				//Script::Call(GetISystem()->GetIScriptSystem(), uiActivateFunc, 1);
			}
		}

		SmartScriptTable modifyScreen;
		bool modifyScreenTest = GetISystem()->GetIScriptSystem()->GetGlobalValue("modifyScreen", modifyScreen);

		HSCRIPTFUNCTION screenActivateFunc(NULL);
		if (modifyScreenTest)
		{
			modifyScreen->GetValue("ActivateProxy", screenActivateFunc);
		}

		HSCRIPTFUNCTION resetFunc(NULL);
		if(bModifyTableSCAR)
		{
			modifyTableSCAR->GetValue("Reset", resetFunc);
		}

		if (m_bModifying && !m_bTransitioning)
		{
			class ScheduleLayer : public ISchedulerAction {
			public:
				ScheduleLayer(CWeapon *wep)
				{
					_pWeapon = wep;
				}
				virtual void execute(CItem *item) {
					_pWeapon->m_bTransitioning = false;

					GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_Active", 0.0f);
					_pWeapon->m_dofSpeed=0.0f;
					//_pWeapon->m_dofValue=0.0f;
					//delete this;
				}
			private:
				CWeapon *_pWeapon;
			};

			m_dofSpeed = -1.0f/((float)GetCurrentAnimationTime(eIGS_FirstPerson)/1000.0f);
			m_dofValue = 1.0f;
			if (!strcmp(GetEntity()->GetClass()->GetName(), "SCAR"))
			{
				StopLayer("modify_layer", eIPAF_Default, false);
				PlayAction("leave_modify", 0);
				GetScheduler()->TimerAction(GetCurrentAnimationTime(eIGS_FirstPerson), new ScheduleLayer(this), false);
				m_bTransitioning = true;
				SGameObjectEvent evt("HUD_SwitchWeaponSelectorInterfaceOff",eGOEF_ToAll);
				GetISystem()->GetIGame()->GetIGameFramework()->GetIGameObjectSystem()->BroadcastEvent(evt);
			}
			else
			{
				m_bTransitioning = false;
			}
			m_bModifying = false;
			//CryLogAlways("leave_modify");
			if (screenActivateFunc)
			{
				//CryLogAlways("calling activate");
				//Script::Call(GetISystem()->GetIScriptSystem(), screenActivateFunc, false);
			}
			if (resetFunc)
			{
				//CryLogAlways("calling reset");
				//Script::Call(GetISystem()->GetIScriptSystem(), resetFunc, modifyTableSCAR, scriptTable);
			}
		}
		else if (!m_bModifying && !m_bTransitioning)
		{

			class ScheduleLayer : public ISchedulerAction {
			public:
				ScheduleLayer(CWeapon *wep)
				{
					_pWeapon = wep;
				}
				virtual void execute(CItem *item) {
					SGameObjectEvent evt("HUD_SwitchWeaponSelectorInterfaceOn",eGOEF_ToAll);
					GetISystem()->GetIGame()->GetIGameFramework()->GetIGameObjectSystem()->BroadcastEvent(evt);
					_pWeapon->PlayLayer("modify_layer", eIPAF_Default|eIPAF_NoBlend, false);
					_pWeapon->m_bTransitioning = false;

					GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_BlurAmount", 1.0f);
					_pWeapon->m_dofSpeed=0.0f;
					//_pWeapon->m_dofValue=0.0f;

					//delete this;
				}
			private:
				CWeapon *_pWeapon;
			};

			GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_Active", 1.0f);
			GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_FocusMin", 0.0f);
			GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_FocusMax", 5.0f);
			GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_FocusLimit", 20.0f);
			GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_UseMask", 0.0f);

			m_dofSpeed = 1.0f/((float)GetCurrentAnimationTime(eIGS_FirstPerson)/1000.0f);
			m_dofValue = 0.0f;
			if (!strcmp(GetEntity()->GetClass()->GetName(), "SCAR"))
			{
				PlayAction("enter_modify", 0, false, eIPAF_Default | eIPAF_RepeatLastFrame);
				m_bTransitioning = true;
				GetScheduler()->TimerAction(GetCurrentAnimationTime(eIGS_FirstPerson), new ScheduleLayer(this), false);
			}
			else
			{
				m_bTransitioning = false;
			}
			//PlayLayer("modify_layer");
			//PlayAction("modify_layered", 0, true);
			//CryLogAlways("enter_modify");
			if (resetFunc)
			{
				//CryLogAlways("calling reset");
				//Script::Call(GetISystem()->GetIScriptSystem(), resetFunc, modifyTableSCAR, scriptTable);
			}
			if (screenActivateFunc)
			{
				//CryLogAlways("calling activate");
				//Script::Call(GetISystem()->GetIScriptSystem(), screenActivateFunc, true);
			}
			m_bModifying = true;
		}
	}
}

//------------------------------------------------------------------------
void CWeapon::UpdateFPView(float frameTime)
{
	CItem::UpdateFPView(frameTime);

	UpdateCrosshair(frameTime);
}

//------------------------------------------------------------------------
void CWeapon::MeleeAttack()
{
	if (!CanMeleeAttack())
		return;

	if (m_melee)
	{
		m_melee->Activate(true);
		m_melee->StartFire(m_ownerId);
		m_melee->StopFire(m_ownerId);
	}
}

//------------------------------------------------------------------------
bool CWeapon::CanMeleeAttack() const
{
	return m_melee && m_melee->CanFire();
}

//------------------------------------------------------------------------
void CWeapon::Select(bool select)
{
	CItem::Select(select);

	FadeCrosshair(0, 1.0f, WEAPON_FADECROSSHAIR_SELECT);

	if (m_fm)
		m_fm->Activate(select);
	if (m_zm)
		m_zm->Activate(select);
}

//------------------------------------------------------------------------
void CWeapon::SetFiringLocator(IWeaponFiringLocator *pLocator)
{
	if (m_pFiringLocator && m_pFiringLocator != pLocator)
		m_pFiringLocator->WeaponReleased();
	m_pFiringLocator = pLocator;
};

//------------------------------------------------------------------------
IWeaponFiringLocator *CWeapon::GetFiringLocator() const
{
	return m_pFiringLocator;
};

//------------------------------------------------------------------------
void CWeapon::AddEventListener(IWeaponEventListener *pListener)
{
	for (TEventListenerVector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
	{
		if (*it == pListener)
			return;
	}

	m_listeners.push_back(pListener);
}

//------------------------------------------------------------------------
void CWeapon::RemoveEventListener(IWeaponEventListener *pListener)
{
	for (TEventListenerVector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
	{
		if (*it == pListener)
		{
			m_listeners.erase(it);
			return;
		}
	}
}

//------------------------------------------------------------------------
void CWeapon::StartFire(EntityId actorId)
{
	if (m_fm)
	{
		GetEntity()->KillTimer(eWT_Shoot);
		GetEntity()->SetTimer(eWT_Shoot, WEAPON_SHOOT_TIMER);
		EnableUpdate(true);

		m_fm->StartFire(actorId);
	}
}

//------------------------------------------------------------------------
void CWeapon::StopFire(EntityId actorId)
{
	if (m_fm)
		m_fm->StopFire(actorId);
}

//------------------------------------------------------------------------
bool CWeapon::CanFire() const
{
	return m_fm && m_fm->CanFire();
}

//------------------------------------------------------------------------
void CWeapon::StartZoom(EntityId actorId)
{
	if (m_zm)
		m_zm->StartZoom();
}

//------------------------------------------------------------------------
void CWeapon::StopZoom(EntityId actorId)
{
	if (m_zm)
		m_zm->StopZoom();
}

//------------------------------------------------------------------------
bool CWeapon::CanZoom() const
{
	return m_zm && m_zm->CanZoom();
}

//------------------------------------------------------------------------
void CWeapon::ExitZoom()
{
	if (m_zm)
		m_zm->ExitZoom();
}

//------------------------------------------------------------------------
void CWeapon::MountAt(const Vec3 &pos)
{
	CItem::MountAt(pos);

	AIObjectParameters params;
	GetEntity()->RegisterInAISystem(AIOBJECT_MOUNTEDWEAPON, params);
}

//------------------------------------------------------------------------
void CWeapon::Reload(bool force)
{
	if (m_fm && (m_fm->CanReload() || force))
	{
		if (m_zm)
			m_fm->Reload(m_zm->IsZoomed());
		else
			m_fm->Reload(false);
	}
}

//------------------------------------------------------------------------
bool CWeapon::CanReload() const
{
	if (m_fm)
		return m_fm->CanReload();
	return true;
}

//------------------------------------------------------------------------
bool CWeapon::OutOfAmmo(bool allFireModes) const
{
	if (!allFireModes)
		return m_fm && m_fm->OutOfAmmo();

	for (int i=0; i<m_firemodes.size(); i++)
		if (!m_firemodes[i]->OutOfAmmo())
			return false;

	return true;
}

//------------------------------------------------------------------------
int CWeapon::GetAmmoCount(const char *ammoType) const
{
	TAmmoMap::const_iterator it = m_ammo.find(CONST_TEMP_STRING(ammoType));
	if (it != m_ammo.end())
		return it->second;
	return 0;
}

//------------------------------------------------------------------------
void CWeapon::SetAmmoCount(const char *ammoType, int count)
{
	m_ammo[ammoType] = count;
}

//------------------------------------------------------------------------
int CWeapon::GetClipCount(const char *ammoType) const
{
	TAmmoMap::const_iterator it = m_clips.find(CONST_TEMP_STRING(ammoType));
	if (it != m_clips.end())
		return it->second;
	return 0;
}

//------------------------------------------------------------------------
void CWeapon::SetClipCount(const char *ammoType, int count)
{
	m_clips[ammoType] = count;
}

//------------------------------------------------------------------------
IFireMode *CWeapon::GetFireMode(int idx) const
{
	if (idx >= 0 && idx < m_firemodes.size())
		return m_firemodes[idx];
	return 0;
}

//------------------------------------------------------------------------
IFireMode *CWeapon::GetFireMode(const char *name) const
{
	TFireModeIdMap::const_iterator it = m_fmIds.find(name);
	if (it == m_fmIds.end())
		return 0;

	return GetFireMode(it->second);
}

//------------------------------------------------------------------------
int CWeapon::GetFireModeIdx(const char *name) const
{
	TFireModeIdMap::const_iterator it = m_fmIds.find(name);
	if (it != m_fmIds.end())
		return it->second;
	return -1;
}

//------------------------------------------------------------------------
int CWeapon::GetCurrentFireMode() const
{
	return m_fmId;
}

//------------------------------------------------------------------------
void CWeapon::SetCurrentFireMode(int idx)
{
	if (m_firemodes.empty())
		return;

	if (m_fmId == idx && m_fm)
		return;

	if (m_fm)
		m_fm->Activate(false);

	m_fm = m_firemodes[idx];
	m_fmId = idx;
	m_fm->Activate(true);

	GetGameObject()->ChangedNetworkState(eEA_GameServerDynamic);
}

//------------------------------------------------------------------------
void CWeapon::SetCurrentFireMode(const char *name)
{
	TFireModeIdMap::iterator it = m_fmIds.find(name);
	if (it == m_fmIds.end())
		return;

	SetCurrentFireMode(it->second);
}

//------------------------------------------------------------------------
void CWeapon::ChangeFireMode()
{
	if (m_firemodes.empty())
		return;

	int t = m_fmId;
	do {
		t++;
		if (t == m_firemodes.size())
			t = 0;
		if (GetFireMode(t)->IsEnabled())
		{
			if (IsServer())
				SetCurrentFireMode(t);
			else
				GetGameObject()->InvokeRMI(SvRequestFireMode(), RequestFireModeParams(t), eRMI_ToServer);
			break;
		}
	} while(t!=m_fmId);
}

//------------------------------------------------------------------------
void CWeapon::EnableFireMode(int idx, bool enable)
{
	IFireMode *pFireMode = GetFireMode(idx);
	if (pFireMode)
		pFireMode->Enable(enable);
}

//------------------------------------------------------------------------
IZoomMode *CWeapon::GetZoomMode(int idx) const
{
	if (idx >= 0 && idx < m_zoommodes.size())
		return m_zoommodes[idx];
	return 0;
}

//------------------------------------------------------------------------
IZoomMode *CWeapon::GetZoomMode(const char *name) const
{
	TZoomModeIdMap::const_iterator it = m_zmIds.find(name);
	if (it == m_zmIds.end())
		return 0;

	return GetZoomMode(it->second);
}

//------------------------------------------------------------------------
int CWeapon::GetZoomModeIdx(const char *name) const
{
	TZoomModeIdMap::const_iterator it = m_zmIds.find(name);
	if (it != m_zmIds.end())
		return it->second;
	return -1;
}

//------------------------------------------------------------------------
int CWeapon::GetCurrentZoomMode() const
{
	return m_zmId;
}

//------------------------------------------------------------------------
void CWeapon::SetCurrentZoomMode(int idx)
{
	if (m_zoommodes.empty())
		return;

	m_zm = m_zoommodes[idx];
	m_zmId = idx;
	m_zm->Activate(true);
}

//------------------------------------------------------------------------
void CWeapon::SetCurrentZoomMode(const char *name)
{
	TZoomModeIdMap::iterator it = m_zmIds.find(name);
	if (it == m_zmIds.end())
		return;

	SetCurrentZoomMode(it->second);
}

//------------------------------------------------------------------------
void CWeapon::ChangeZoomMode()
{
	if (m_zoommodes.empty())
		return;
/*
	if (m_zmId+1<m_zoommodes.size())
		SetCurrentZoomMode(m_zmId+1);
	else if (m_zoommodes.size()>1)
		SetCurrentZoomMode(0);
		*/
	int t = m_zmId;
	do {
		t++;
		if (t == m_zoommodes.size())
			t = 0;
		if (GetZoomMode(t)->IsEnabled())
			SetCurrentZoomMode(t);
	} while(t!=m_zmId);
}

//------------------------------------------------------------------------
void CWeapon::EnableZoomMode(int idx, bool enable)
{
	IZoomMode *pZoomMode = GetZoomMode(idx);
	if (pZoomMode)
		pZoomMode->Enable(enable);
}

//------------------------------------------------------------------------
CProjectile *CWeapon::SpawnAmmo(const char *ammoName)
{
	return g_pGame->GetWeaponSystem()->SpawnAmmo(ammoName);
}

//------------------------------------------------------------------------
void CWeapon::SetCrosshairVisibility(bool visible)
{
	m_crosshairstats.visible = visible;
}

//------------------------------------------------------------------------
bool CWeapon::GetCrosshairVisibility() const
{
	return m_crosshairstats.visible;
}

//------------------------------------------------------------------------
void CWeapon::SetCrosshairOpacity(float opacity)
{
	m_crosshairstats.opacity = opacity; 
}

//------------------------------------------------------------------------
float CWeapon::GetCrosshairOpacity() const
{
	return m_crosshairstats.opacity;
}

//------------------------------------------------------------------------
void CWeapon::FadeCrosshair(float from, float to, float time)
{
	m_crosshairstats.fading = true;
	m_crosshairstats.fadefrom = from;
	m_crosshairstats.fadeto = to;
	m_crosshairstats.fadetime = MAX(0, time);
	m_crosshairstats.fadetimer = m_crosshairstats.fadetime;

	SetCrosshairOpacity(from);
}

//------------------------------------------------------------------------
void CWeapon::UpdateCrosshair(float frameTime)
{
	if (m_crosshairstats.fading)
	{
		if (m_crosshairstats.fadetimer>0.0f)
		{
			m_crosshairstats.fadetimer -= frameTime;
			if (m_crosshairstats.fadetimer<0.0f)
				m_crosshairstats.fadetimer=0.0f;

			float t = (m_crosshairstats.fadetime-m_crosshairstats.fadetimer)/m_crosshairstats.fadetime;
			float d = (m_crosshairstats.fadeto-m_crosshairstats.fadefrom);

			if (t >= 1.0f)
				m_crosshairstats.fading = false;

			if (d < 0.0f)
				t = 1.0f-t;

			m_crosshairstats.opacity = fabsf(t*d);
		}
		else
		{
			m_crosshairstats.opacity = m_crosshairstats.fadeto;
			m_crosshairstats.fading = false;
		}
	}
}

//------------------------------------------------------------------------
void CWeapon::AccessoriesChanged()
{
	int i=0;
	for (TFireModeVector::iterator it=m_firemodes.begin(); it!=m_firemodes.end(); ++it)
	{
		for (TFireModeIdMap::iterator iit=m_fmIds.begin(); iit!=m_fmIds.end(); ++iit)
		{
			if (iit->second == i)
			{
				IFireMode *pFireMode = *it;
				pFireMode->ResetParams(0);
				if (m_fmDefaults)
					pFireMode->PatchParams(m_fmDefaults);
				PatchFireModeWithAccessory(pFireMode, "default");

				const IItemParamsNode *fmparams = GetFireModeParams(iit->first.c_str());
				if (fmparams)
					pFireMode->PatchParams(fmparams);

				PatchFireModeWithAccessory(pFireMode, iit->first.c_str());
				break;
			}
		}

		++i;
	}

	i=0;
	for (TZoomModeVector::iterator it=m_zoommodes.begin(); it!=m_zoommodes.end(); ++it)
	{
		for (TZoomModeIdMap::iterator iit=m_zmIds.begin(); iit!=m_zmIds.end(); ++iit)
		{
			if (iit->second == i)
			{
				IZoomMode *pZoomMode = *it;
				pZoomMode->ResetParams(0);
				if (m_zmDefaults)
					pZoomMode->PatchParams(m_zmDefaults);
				PatchZoomModeWithAccessory(pZoomMode, "default");

				const IItemParamsNode *zmparams = GetZoomModeParams(iit->first.c_str());
				if (zmparams)
					pZoomMode->PatchParams(zmparams);

				PatchZoomModeWithAccessory(pZoomMode, iit->first.c_str());
				break;
			}
		}
		++i;
	}
}

//------------------------------------------------------------------------
void CWeapon::PatchFireModeWithAccessory(IFireMode *pFireMode, const char *firemodeName)
{
	// patch defaults with accessory defaults
	for (TAccessoryMap::iterator ait=m_accessories.begin(); ait!=m_accessories.end(); ++ait)
	{
		SAccessoryParams *params=GetAccessoryParams(ait->first);
		if (params && params->params)
		{
			const IItemParamsNode *firemodes = params->params->GetChild("firemodes");
			if (!firemodes)
				continue;

			int n=firemodes->GetChildCount();
			for (int i=0; i<n; i++)
			{
				const IItemParamsNode *firemode = firemodes->GetChild(i);
				const char *name=firemode->GetAttribute("name");
				if (name && !stricmp(name, firemodeName))
				{
					pFireMode->PatchParams(firemode);
					break;
				}
				const char *typ=firemode->GetAttribute("type");
				if (typ && !stricmp(typ, firemodeName))
				{
					pFireMode->PatchParams(firemode);
					break;
				}
			}
		}
	}
}

//------------------------------------------------------------------------
void CWeapon::PatchZoomModeWithAccessory(IZoomMode *pZoomMode, const char *zoommodeName)
{
	// patch defaults with accessory defaults
	for (TAccessoryMap::iterator ait=m_accessories.begin(); ait!=m_accessories.end(); ++ait)
	{
		SAccessoryParams *params=GetAccessoryParams(ait->first);
		if (params && params->params)
		{
			const IItemParamsNode *zoommodes = params->params->GetChild("zoommodes");
			if (!zoommodes)
				continue;

			int n=zoommodes->GetChildCount();
			for (int i=0; i<n; i++)
			{
				const IItemParamsNode *zoommode = zoommodes->GetChild(i);
				const char *name=zoommode->GetAttribute("name");
				if (name && !stricmp(name, zoommodeName))
				{
					pZoomMode->PatchParams(zoommode);
					break;
				}
				const char *typ=zoommode->GetAttribute("type");
				if (typ && !stricmp(typ, zoommodeName))
				{
					pZoomMode->PatchParams(zoommode);
					break;
				}
			}
		}
	}
}

//------------------------------------------------------------------------
float CWeapon::GetSpinUpTime() const
{
	if (m_fm)
		return m_fm->GetSpinUpTime();
	return 0.0f;
}

//------------------------------------------------------------------------
float CWeapon::GetSpinDownTime() const
{
	if (m_fm)
		return m_fm->GetSpinDownTime();
	return 0.0f;
}

void CWeapon::SwitchAccessory(const char *newAcc)
{
	SAccessoryParams *params = GetAccessoryParams(newAcc);
	bool attached = false;
	if (params)
	{
		string replacing = "";
		bool exclusive = false;
		for (TAccessoryMap::iterator it = m_accessories.begin(); it != m_accessories.end(); it++)
		{
			SAccessoryParams *p = GetAccessoryParams(it->first.c_str());

			if (!strcmp(p->attach_helper.c_str(), params->attach_helper.c_str()))
			{
				replacing = it->first;
				attached = true;
				exclusive = p->exclusive;
			}
		}
		if (attached)
		{
			ScheduleAttachAccessory(replacing.c_str(), false);
			if (!strcmp(newAcc, replacing.c_str()))
			{

			}
			else
			{
				ScheduleAttachAccessory(newAcc, true);
			}
			
		}
	}
	if (!attached)
	{
		ScheduleAttachAccessory(newAcc, true);
	}
}

void CWeapon::ScheduleAttachAccessory(const char *accessoryName, bool attach)
{
	class ScheduleAttachClass : public ISchedulerAction {
	public:
		ScheduleAttachClass(CWeapon *wep, const char *cname, bool attch)
		{
			_pWeapon = wep;
			_className = cname;
			_attach = attch;
		}
		virtual void execute(CItem *item) {
			_pWeapon->AttachAccessory(_className, _attach, true);
		}
	private:
		CWeapon *_pWeapon;
		const char *_className;
		bool _attach;
	};
	ScheduleAttachClass *action = new ScheduleAttachClass(this, accessoryName, attach);
	GetScheduler()->ScheduleAction(action);
}

string CWeapon::CurrentAttachment(const char *attachmentPoint)
{
	for (TAccessoryMap::iterator it = m_accessories.begin(); it != m_accessories.end(); it++)
	{
		SAccessoryParams *p = GetAccessoryParams(it->first.c_str());

		if (!strcmp(p->attach_helper.c_str(), attachmentPoint))
		{
			return (it->first);
		}
	}
	return 0;
}
void CWeapon::FixAccessories(SAccessoryParams *params, bool attach)
{
	if (!attach)
	{
		if (params)
		{
			if (params->exclusive && GetFireModeIdx(params->firemode) != -1)
			{
				EnableFireMode(GetFireModeIdx(params->firemode), false);
				if (GetCurrentFireMode() == GetFireModeIdx(params->firemode))
					ChangeFireMode();
			}
			if (GetZoomModeIdx(params->zoommode) != -1)
			{
				EnableZoomMode(GetZoomModeIdx(params->zoommode), false);
				ChangeZoomMode();
			}
		}
	}
	else
	{
		if (params && GetFireModeIdx(params->firemode) != -1)
		{
			GetFireMode(params->firemode.c_str())->Enable(true);
			//if (GetFireMode(GetCurrentFireMode())->IsEnabled() == false)
			SetCurrentFireMode(params->firemode.c_str());
		}
		if (GetZoomModeIdx(params->zoommode) != -1)
		{
			EnableZoomMode(GetZoomModeIdx(params->zoommode), true);
			SetCurrentZoomMode(GetZoomModeIdx(params->zoommode));
		}
	}	
}


//------------------------------------------------------------------------
const AIWeaponDescriptor& CWeapon::GetAIWeaponDescriptor( ) const
{
	return m_aiWeaponDescriptor;
}
//------------------------------------------------------------------------
