
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_InventoryWeapon_RangedWeapon.cpp
// Author : Andy Tate
// Date   : 13-Nov-2007
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header...
#include "Inventory/Items/Weapons/AVP_InventoryWeapon_RangedWeapon.h"

// Asura independent header-files...
#include "Blueprint/Asura_Blueprint_System.h"
#include "Entity/PhysicalObject/Moves/Asura_Movement_System.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Physics/Asura_Physics.h"

// Game independent header-files...
#include "Actor/AVP_Actor_AnimationHandler.h"
#include "Actor/AVP_Actor_AnimStateData.h"
#include "Actor/AVP_Actor_DynamicStat.h"
#include "Actor/Vertebrates/AVP_Vertebrate_InputHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_Focus.h"
#include "Animation/AVP_AnimFlags.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Core/AVP_Game_Core.h"
#include "Core/AVP_Maths.h"
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Damage/AVP_EntityDamage_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_Actor_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ClientActor_Predator.h"
#include "Entity/PhysicalObject/Projectile/AVP_GenericProjectileData.h"
#include "Entity/PhysicalObject/Projectile/AVP_Projectile_Messages.h"
#include "Input/AVP_Rumble_System.h"
#include "Inventory/AVP_AmmoStore.h"
#include "Inventory/AVP_Inventory_Group.h"
#include "Inventory/AVP_Inventory_HashIDs.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Inventory/Items/AVP_InventoryItem_PredatorCloak.h"
#include "Inventory/Items/Weapons/AVP_RangedWeapon_SubType.h"
#include "Inventory/Items/Weapons/AVP_RangedWeapon_ClientEffectsHandler.h"
#include "Moves/AVP_Movement_System.h"
#include "Sound/Asura_Sound_Event_Storage.h"

////////////////////////////////////////////////////////////////////////////////
// Statics ...
////////////////////////////////////////////////////////////////////////////////

DEBUGVAR float dbg_fSpreadLerpRate = 0.2f;
//DEBUGVAR float dbg_fNoChargeBlocksToLosePowerMultiplier = 0.5f;
DEBUGVAR float dbg_fReleaseFireTime = 0.3f;

DEBUGVAR int   dbg_iErrorCountMax = 4; 
DEBUGVAR float dbg_fStartRange = 0.5f;
DEBUGVAR float dbg_fAcceptRangeVarience = dbg_fStartRange/dbg_iErrorCountMax;

////////////////////////////////////////////////////////////////////////////////
// Functions ...
////////////////////////////////////////////////////////////////////////////////

AVP_RangedWeapon_ClientEffectsHandler* AVP_InventoryWeapon_RangedWeapon::CreateClientEffectsHandler(Asura_Hash_ID uBlueprintHash, Asura_Hash_ID uOwnerGuid)
{
	return new AVP_RangedWeapon_ClientEffectsHandler(uBlueprintHash, uOwnerGuid);
}

void AVP_InventoryWeapon_RangedWeapon::OnStateChange(const u_int uOldState,
												   const u_int uNewState)
{
	const u_int	uUsageType	= GetCurrentUsageType();

	// Handle stuff for the old state...
	switch(uOldState)
	{
		case INACTIVE:
		{
			const AVP_Inventory_System&	xParentSystem	= GetParentSystem();

			if(xParentSystem.IsClientSide())
			{
				if( m_pxClientEffectsHandler == NULL )
				{
					const Asura_Guid	uOwnerGuid	= xParentSystem.GetOwnerGuid();
					m_pxClientEffectsHandler = CreateClientEffectsHandler(m_uBlueprintHash, uOwnerGuid);
				}
			}
		}
		break;

		case USING:
		{
			StopUsing(uUsageType);
		}
		break;

		default:
		{
		}
		break;
	}		// End of switch.

	if ( uNewState == INACTIVE )
	{
		HandleDeactivationEffects();
	}

	// Call the base-function...
	PARENT::OnStateChange(uOldState, uNewState);
	
	if ( uOldState == INACTIVE )
	{
		HandleActivationEffects();
	}

	// Handle stuff for the new state...
	switch(uNewState)
	{
		case INACTIVE:
		{
			delete(m_pxClientEffectsHandler);
			m_pxClientEffectsHandler	= NULL;
		}
		break;

		case USING:
		{
			StartUsing(uUsageType);
		}
		break;

		default:
		{
		}
		break;
	}		// End of switch.
}

void AVP_InventoryWeapon_RangedWeapon::UpdateClientState(AVP_ClientEntity_Actor& xClient)
{
	const State	eCurrentState				= GetCurrentState();
	const u_int					uUsageType	= GetCurrentUsageType();
	int							iSubType	= GetSubTypeIndexFromUsageType( uUsageType );
	AVP_RangedWeapon_SubType*	pxSubType	= iSubType < 0 ? NULL : static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ iSubType ] );

	// Call the base-function...
	PARENT::UpdateClientState( xClient );

	// decrement the player shot timers1
	if ( m_fPrimaryShotTimer > 0.0f )
	{
		m_fPrimaryShotTimer -= Asura_Timers::GetGameTimeSlice();
	}

	if ( m_fSecondaryShotTimer > 0.0f )
	{
		m_fSecondaryShotTimer -= Asura_Timers::GetGameTimeSlice();
	}

	// handle weapon cool down
	if ( m_fSpreadCoolDownTimer > 0.0f )
	{
		m_fSpreadCoolDownTimer -= Asura_Timers::GetGameTimeSlice();
		m_fCurrentSpreadParametric = Asura_Maths::Lerp( m_fCurrentSpreadParametric, m_fTargetSpreadParametric, dbg_fSpreadLerpRate );
	}
	else
	{
		m_fTargetSpreadParametric = m_fCurrentSpreadParametric = Asura_Maths::ClampToRange( m_fCurrentSpreadParametric - m_fSpreadCoolDown * Asura_Timers::GetGameTimeSlice(), 0.0f, 1.0f );
	}
	
	// Update lock on laser target effect
	UpdateLaser( xClient );

	if (m_pxClientEffectsHandler && m_uNumSubTypes > 0) // check for battery
	{
		const AVP_Inventory_System&	xParentSystem	= GetParentSystem();
		AVP_ClientEntity_Actor* pxClient( AVP_ClientEntity_Actor::FindEntity( xParentSystem.GetOwnerGuid() ) );
		AVP_Actor_AnimationHandler* pxAnimHandler	= pxClient != NULL ? pxClient->GetAnimationHandler() : NULL;
		Asura_Animation*			pxAnimation		= pxAnimHandler != NULL ? pxAnimHandler->GetAnimation() : NULL;

		m_pxClientEffectsHandler->UpdateAmmoCount(pxAnimation, static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ 0 ] )->GetCurrentQuantity());
	}



	// if we're moving fast then stop the actor anim
	if ( xClient.IsMovingFast() && m_pxClientEffectsHandler != NULL && uUsageType != UT_RELOAD_ALL )
	{
		m_pxClientEffectsHandler->DestroyActorAnim( xClient );
	}

	if ( eCurrentState != INACTIVE )
	{
		Asura_Hash_ID uRestAnim = ASURA_HASH_ID_UNSET;
		const AVP_Actor_AnimStateData* pxStateData = GetParentSystem().GetParentClient()->GetAnimStateDataForWeapon( GetBlueprintHash() );
		if ( pxStateData )
		{
			uRestAnim = pxStateData->GetWeaponRestWeaponAnimHash();
		}

		if ( uRestAnim != ASURA_HASH_ID_UNSET )
		{
			m_pxClientEffectsHandler->SetRestAnim( uRestAnim );
		}
	}

	switch ( eCurrentState )
	{
		case IDLING:
		{
			if ( ( GetTimeInState() > 0.0f ) && !xClient.IsMovingFast() )
			{
				CheckForAutomaticReload();
			}
		}
		break;

		case USING:
		{
			switch(uUsageType)
			{
				case UT_N_BULLET_BURST_PRIMARY:
				case UT_N_BULLET_BURST_SECONDARY: 
				{
					if (GetParentSystem().IsMaster())
					{
						// stop single shot fire mode after anim completes / no bullets left in burst
						if(m_fBurstBulletsToFire > 0.0f)
						{
							Use();
						}
						else
						{
							if ( !m_pxClientEffectsHandler->IsActorAnimPlaying( xClient ) && GetTimeInState() > 0.0f )
							{
								m_fBurstBulletsToFire = 0.0f;
								RequestIdling(false);
							}
						}
					}
					else
					{
						if(m_fBurstBulletsToFire > 0.0f)
						{
							Use();
						}
						else
						{
							if ( !m_pxClientEffectsHandler->IsActorAnimPlaying( xClient ) && GetTimeInState() > 0.0f )
							{
								m_fBurstBulletsToFire = 0.0f;
								//RequestIdling(false);
							}
						}
					}
				}
				break;
				
				case UT_FULL_AUTO_PRIMARY:
				case UT_FULL_AUTO_SECONDARY:
				{
					if ( ( xClient.IsMovingFast() || (!m_bFirstShot && !IsPlayerIsStillFiring( xClient, iSubType ) ) || (!pxSubType || (IsSubtypeEmpty(iSubType) && pxSubType->GetClickCount()>=pxSubType->GetMaxClicks()) ) ) && GetTimeInState() > 0.0f )
					{
						if ( GetParentSystem().IsMaster() )
						{
							RequestIdling(false);
						}
					}
					else
					{
						Use();
					}
				}
				break;
			
				case UT_CHARGED_SHOT_PRIMARY:
				case UT_CHARGED_SHOT_SECONDARY:
				{
					if ( IsPlayerIsStillFiring( xClient, iSubType ) )
					{
						ChargeClient(pxSubType);

						const AVP_Actor_AnimationHandler* pxAnimHandler = xClient.GetAnimationHandler();
						Asura_Hash_ID uMarkerHash = (pxAnimHandler && pxAnimHandler->IsFirstPerson()) ? pxSubType->GetMuzzleMarkerPointFirstPerson() : pxSubType->GetMuzzleMarkerPointThirdPerson();
						Asura_Vector_3 xMuzPos( Asura_Maths::ZeroVector3 );
						Asura_Matrix_3x3 xMuzOri( Asura_Maths::IdentityMatrix3x3 );
						xClient.GetMarkerLocationFromHash( uMarkerHash, &xMuzPos, &xMuzOri );
						HandleChargingEffects( static_cast< SubType >( iSubType ), xMuzPos, xMuzOri );
					}
					else
					{
						float fReleaseTime = Asura_Timers::GetServerAbsoluteTime() - m_fButtonHeldTime;
						if(fReleaseTime < dbg_fReleaseFireTime || m_fButtonHeldTime == 0.0f)// if released quickly
						{
							RequestIdling( false, TT_SHOOT );
						}
						else
						{
							RequestIdling( false, TT_ABORT_RESTORE_CHARGE );
						}
						m_fButtonHeldTime = 0.0f;
					}
				}
				break;

				// Now done server side
				case UT_RELOAD_ALL:
				{
					// If the reload anim is finished...
					if( ( !m_pxClientEffectsHandler->IsActorAnimPlaying( xClient ) || m_pxClientEffectsHandler->GetActorAnimFlagsForLastFrame( *GetParentSystem().GetParentClient() ) & AVP_ANIMFLAG_ACTION_1 ) != 0 ) // Ugh!
					{
						if(!m_bReloadHandled)
						{
							Use();
							m_bReloadHandled = true;
						}
					}
					if( !m_pxClientEffectsHandler->IsActorAnimPlaying( xClient ) )
					{
						if(GetParentSystem().IsMaster())
						{
							RequestIdling(false);
						}
						m_bReloadHandled = false;
					}
				}
				break;

				default:
				{
					// don't do anything, the derived class will probably handle it
				}
				break;
			}		// End of switch.
		}
		break;

		default:
		{
			// do nothing
		}
		break;
	}
}


// This function also check if we are sharing ammo between subtypes. If so, we will look into primary subtype, rather that into the current one.
bool AVP_InventoryWeapon_RangedWeapon::IsSubtypeEmpty( int iSubType )
{
	Asura_Assert( iSubType < static_cast< int >( GetNumSubTypes() ), "Something is wrong in Weapon's IsSubtypeEmpty. We are trying to access subtype fire, even if it doesn't exist." );
	if ( iSubType < static_cast< int >( GetNumSubTypes() ) )
	{
		if( m_bSubtypesUseSameAmmo || iSubType==ST_PRIMARY )
		{
			return GetNumSubTypes()>=1 ? m_ppxSubTypes[ ST_PRIMARY ]->IsEmpty() : true;
		}
		else
		{
			return GetNumSubTypes()>=2 ? m_ppxSubTypes[ ST_SECONDARY ]->IsEmpty() : true;
		}
	}

	// Weapon is wrong, so lets pretend that it is empty.
	return true;
}

void AVP_InventoryWeapon_RangedWeapon::UpdateServerState(AVP_ServerEntity_Actor& xServer)
{
	const State	eCurrentState					= GetCurrentState();
	const AVP_Inventory_System&	xParentSystem	= GetParentSystem();
	const u_int	uUsageType						= GetCurrentUsageType();
	const int	iSubType						= GetSubTypeIndexFromUsageType( uUsageType );
	AVP_RangedWeapon_SubType*	pxSubType		= iSubType < 0 || iSubType >= static_cast<int>(GetNumSubTypes() ) ? NULL : static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ iSubType ] );

	// Call the base-function...
	PARENT::UpdateServerState(xServer);

	// cooldown
	if ( m_fSpreadCoolDownTimer > 0.0f )
	{
		m_fSpreadCoolDownTimer -= Asura_Timers::GetGameTimeSlice();
	}
	else
	{
		m_fCurrentSpreadParametric = Asura_Maths::ClampToRange( m_fCurrentSpreadParametric - m_fSpreadCoolDown * Asura_Timers::GetGameTimeSlice(), 0.0f, 1.0f );
	}

	if ( xParentSystem.IsMaster() ) // if we're the master system ( this will be the case for the AI )
	{
		// Decide what to do based on our current state...
		switch ( eCurrentState )
		{
			case USING:
			{
				switch(uUsageType)
				{
					case UT_CHARGED_SHOT_PRIMARY:
					case UT_CHARGED_SHOT_SECONDARY:
					{
						ChargeServer( pxSubType );
					}
					break;

					case UT_FULL_AUTO_PRIMARY:
					case UT_FULL_AUTO_SECONDARY:
					{
						Use();
						if( ( pxSubType != NULL ? (IsSubtypeEmpty(iSubType) && pxSubType->GetClickCount()>=pxSubType->GetMaxClicks()) : true ) && GetTimeInState() > 0.0f )
						{
							RequestIdling(false);
						}
					}
					break;
					
					case UT_N_BULLET_BURST_PRIMARY:
					case UT_N_BULLET_BURST_SECONDARY:
					{
						if(m_fBurstBulletsToFire > 0.0f)
						{
							Use();
						}
						else
						{
							if ( !HasPendingStateChange() && GetTimeInState() > 0.0f )
							{
								m_fBurstBulletsToFire = 0.0f;
								RequestIdling( false );
							}
						}
					}
					break;

					case UT_RELOAD_ALL:
					{
						if ( !HasPendingStateChange() && GetTimeInState() > 0.0f )
						{
							const AVP_ClientEntity_Actor* pxClient( AVP_ClientEntity_Actor::FindEntity( xParentSystem.GetOwnerGuid() ) );
							const AVP_Inventory_System* pxClientSystem( pxClient != NULL ? pxClient->GetInventorySystem() : NULL );
							const AVP_InventoryWeapon_RangedWeapon* pxClientWeapon( static_cast< const AVP_InventoryWeapon_RangedWeapon* >( pxClientSystem != NULL ? pxClientSystem->GetCurrentItem(uAVP_INVENTORY_GROUPHASH_WEAPON) : NULL ) );
							const AVP_RangedWeapon_ClientEffectsHandler* pxClientEffectsHandler( pxClientWeapon != NULL ? pxClientWeapon->m_pxClientEffectsHandler : NULL );
							if ( ( pxClientEffectsHandler == NULL || !pxClientEffectsHandler->IsActorAnimPlaying( *pxClient ) || ( ( pxClientEffectsHandler->GetActorAnimFlagsForLastFrame( *pxClient ) & AVP_ANIMFLAG_ACTION_1 ) != 0 ) ) )// Ugh!
							{
								Use();
								RequestIdling( false );
							}
						}
					}
					break;

					default:
					{
						//Asura_Assert( false, "Unhandled case" );
						// GBT : commented out assert as this usage type can belong to a derived class and thereforte can not be handled at this level. So no point asserting!
					}
					break;
				}		// End of switch.
			}
			break;

			case END_USING:
			{
				switch(uUsageType)
				{
					case UT_N_BULLET_BURST_PRIMARY:
					case UT_N_BULLET_BURST_SECONDARY:
					{
						const AVP_ClientEntity_Actor* pxClient( AVP_ClientEntity_Actor::FindEntity( xParentSystem.GetOwnerGuid() ) );
						const AVP_Inventory_System* pxClientSystem( pxClient != NULL ? pxClient->GetInventorySystem() : NULL );
						const AVP_InventoryWeapon_RangedWeapon* pxClientWeapon( static_cast< const AVP_InventoryWeapon_RangedWeapon* >( pxClientSystem != NULL ? pxClientSystem->GetCurrentItem(uAVP_INVENTORY_GROUPHASH_WEAPON) : NULL ) );
						const AVP_RangedWeapon_ClientEffectsHandler* pxClientEffectsHandler( pxClientWeapon != NULL ? pxClientWeapon->m_pxClientEffectsHandler : NULL );
						if ( ( pxClientEffectsHandler == NULL || !pxClientEffectsHandler->IsActorAnimPlaying( *pxClient ) ) && GetTimeInState() > 0.0f )// Ugh!
						{
							if ( GetDestinationState() == INACTIVE )
							{
								RequestNextInactiveState();
							}
							else
							{
								RequestNextIdlingState();
							}
						}
					}
					break;

					default:
					{
						if ( GetDestinationState() == INACTIVE )
						{
							RequestNextInactiveState();
						}
						else
						{
							RequestNextIdlingState();
						}
					}
					break;
				}
			}
			break;

			case IDLING:
			{
				if ( ( GetTimeInState() > 0.0f ) && !xServer.IsMovingFast() )
				{
					CheckForAutomaticReload();
				}
			}
			break;

			default:
			{
				// Nothing to do...
			}
			break;
		}		// End of switch.
	}
}

void AVP_InventoryWeapon_RangedWeapon::ChargeClient( AVP_RangedWeapon_SubType* pxSubType )
{
	float fChargeAmount;
	if ( m_fChargedPower > 0.0f )
	{
		fChargeAmount = Asura_Maths::Min( pxSubType->GetMaxChargePerShot() - m_fChargedPower, pxSubType->GetChargePerSecond() * Asura_Timers::GetGameTimeSlice() );
		if ( GetParentSystem().IsMaster() )
		{
			m_fChargedPower += ( pxSubType->GetCurrentQuantity() - pxSubType->DecreaseQuantity( fChargeAmount ) );
		}
		else
		{
			m_fChargedPower += fChargeAmount;
		}
	}
	else
	{
		fChargeAmount = pxSubType->GetMinChargePerShot();
		if ( pxSubType->GetCurrentQuantity() >= fChargeAmount )
		{
			m_fChargedPower += fChargeAmount;
			if ( GetParentSystem().IsMaster() )
			{
				pxSubType->DecreaseQuantity( fChargeAmount );
			}
		}
	}
}

void AVP_InventoryWeapon_RangedWeapon::ChargeServer( AVP_RangedWeapon_SubType* pxSubType )
{
	if ( GetParentSystem().IsMaster() )
	{
		float fChargeAmount;
		if ( m_fChargedPower > 0.0f )
		{
			fChargeAmount = Asura_Maths::Min( pxSubType->GetMaxChargePerShot() - m_fChargedPower, pxSubType->GetChargePerSecond() * Asura_Timers::GetGameTimeSlice() );
			m_fChargedPower += ( pxSubType->GetCurrentQuantity() - pxSubType->DecreaseQuantity( fChargeAmount ) );
		}
		else
		{
			fChargeAmount = pxSubType->GetMinChargePerShot();
			if ( pxSubType->GetCurrentQuantity() > fChargeAmount )
			{
				m_fChargedPower += ( pxSubType->GetCurrentQuantity() - pxSubType->DecreaseQuantity( fChargeAmount ) );
			}
		}
	}
}

int AVP_InventoryWeapon_RangedWeapon::GetSubTypeIndexFromUsageType(const u_int uUsageType) const
{
	switch(uUsageType)
	{
		case UT_NONE:
		case UT_RELOAD_ALL:
		case UT_MELEE:
		{
			return(-1);
		}

		case UT_FULL_AUTO_PRIMARY:
		case UT_CHARGED_SHOT_PRIMARY:
		case UT_N_BULLET_BURST_PRIMARY:
		{
			return(ST_PRIMARY);
		}

		case UT_FULL_AUTO_SECONDARY:
		case UT_CHARGED_SHOT_SECONDARY:
		case UT_N_BULLET_BURST_SECONDARY:
		{
			return(ST_SECONDARY);
		}

		default:
		{
			Asura_Assert(false, "Unexpected usage-type for this inventory-item.");
			return(-1);
		}
	}
}

AVP_InventoryWeapon_RangedWeapon::AVP_InventoryWeapon_RangedWeapon( AVP_Inventory_Group& xParentGroup, const Asura_Hash_ID uHash, const Asura_Hash_ID uBlueprintHash )
: PARENT( AVP_INVENTORYITEMTYPE_WEAPON_RANGED, xParentGroup, uHash, uBlueprintHash, "Ranged Weapon")
, m_pxClientEffectsHandler( NULL )
, m_bFirstShot( true )
, m_fCurrentSpreadParametric( 0.0f )
, m_fTargetSpreadParametric( 0.0f )
, m_fSpreadCoolDown( 1.0f )
, m_fSpreadCoolDownTimer( 0.0f )
, m_fBurstBulletsToFire( 0.0f )
, m_fMaxBurstBulletsToFire( 1.0f )
, m_fChargedPower( 0.0f )
, m_fPrimaryShotTimer( 0.0f )
, m_fSecondaryShotTimer( 0.0f )
, m_bReloadHandled( false )
, m_bCanPlayHotMuzzleFlash( false )
, m_bCanReload( true )
, m_bFirstClick( true )
, m_fButtonHeldTime( 0.0f )
, m_fStoredBulletsToFire( 0.0f )
, m_fAmmoFiredSoFar( 0.0f )
{

	if ( GetParentSystem().IsServerSide() && GetParentSystem().IsMaster() )
	{
		SetStateAsValid(END_USING);
	}

	GetStaticDataFromBlueprint();
}

/// End of function 'AVP_InventoryWeapon_RangedWeapon::AVP_InventoryWeapon_RangedWeapon'. ///

AVP_InventoryWeapon_RangedWeapon::~AVP_InventoryWeapon_RangedWeapon()
{
	delete m_pxClientEffectsHandler;
}

Asura_Animation* AVP_InventoryWeapon_RangedWeapon::CreateAnimation()
{
	const AVP_Inventory_System&	xParentSystem	= GetParentSystem();

	if(xParentSystem.IsClientSide())
	{
		if(m_pxClientEffectsHandler == NULL)
		{
			Asura_Assert(false, "Failed to find ranged-weapon client effects-handler when creating an animation.");
			return(NULL);
		}
		else
		{
			Asura_Hash_ID uRestAnim = ASURA_HASH_ID_UNSET;
			if ( xParentSystem.GetParentClient() )
			{
				const AVP_Actor_AnimStateData* pxStateData = xParentSystem.GetParentClient()->GetAnimStateDataForWeapon( GetBlueprintHash() );
				if ( pxStateData )
				{
					uRestAnim = pxStateData->GetWeaponRestWeaponAnimHash();
				}
			}

			Asura_Animation* pxAnim = m_pxClientEffectsHandler->CreateAnimation();

			if ( pxAnim )
			{
				m_pxClientEffectsHandler->SetRestAnim( uRestAnim );
			}

			return pxAnim;
		}
	}
	else
	{
		return(NULL);
	}
}

void AVP_InventoryWeapon_RangedWeapon::DestroyAnimation()
{
	if ( m_pxClientEffectsHandler != NULL )
	{
		m_pxClientEffectsHandler->DestroyAnimation();
	}
}

Asura_Animation* AVP_InventoryWeapon_RangedWeapon::GetAnimation( bool bCreateIfNecessary /*= false*/ )
{
	Asura_Animation* pxAnim = NULL;
	if ( m_pxClientEffectsHandler != NULL )
	{
		pxAnim = m_pxClientEffectsHandler->GetAnimation();
		if ( pxAnim == NULL && bCreateIfNecessary )
		{
			pxAnim = CreateAnimation();
		}
	}
	return pxAnim;
}

const Asura_Animation* AVP_InventoryWeapon_RangedWeapon::GetAnimation() const
{
	return m_pxClientEffectsHandler != NULL ? m_pxClientEffectsHandler->GetAnimation() : NULL;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_InventoryWeapon_RangedWeapon::StartUsing
// Author       : Andy Tate
// Date         : 13-Nov-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_InventoryWeapon_RangedWeapon::StartUsing(const u_int uUsageType)
{
	m_bFirstShot = true;
	m_fStoredBulletsToFire = 0.0f;
	m_fAmmoFiredSoFar = 0.0f;
	m_bCanPlayHotMuzzleFlash = true;

	switch(uUsageType)
	{
		case UT_N_BULLET_BURST_PRIMARY:
		case UT_N_BULLET_BURST_SECONDARY:
		{
			if (GetNumSubTypes() > 0)
			{
				AVP_RangedWeapon_SubType* pxSubType = static_cast< AVP_RangedWeapon_SubType* >( GetSubTypeFromUsageType( uUsageType ) );

				if ( pxSubType )
				{
					m_fMaxBurstBulletsToFire = (float)Asura_Random::GetIntU( pxSubType->GetBurstFireMinShots(), pxSubType->GetBurstFireMaxShots() );
					m_fBurstBulletsToFire = m_fMaxBurstBulletsToFire;
				}
			}
			
			// Now Use() now called in using state so bursts will work
		}
		break;

		case UT_FULL_AUTO_PRIMARY:
		case UT_CHARGED_SHOT_PRIMARY:
		case UT_FULL_AUTO_SECONDARY:
		case UT_CHARGED_SHOT_SECONDARY:
		{
			m_fBurstBulletsToFire = 0.0f; // Reset as different usage type
			HandleStartFiringEffects( uUsageType );
		}
		break;

		case UT_RELOAD_ALL:
		{
			m_fBurstBulletsToFire = 0.0f; // Reset as different usage type
			HandleStartReloadEffects();
		}
		break;

		default:
		{
			// do nothing
		}
		break;
	}
}

/// End of function 'AVP_InventoryWeapon_RangedWeapon::StartUsing'. ///

void AVP_InventoryWeapon_RangedWeapon::HandleReload( void )
{
	// Get our ammo store
	AVP_AmmoStore* pxAmmoStore = GetParentSystem().GetAmmoStore();

	AVP_AmmoStore::ReloadSubTypeFromAmmoStore(m_ppxSubTypes[ST_PRIMARY], pxAmmoStore);
	static_cast<AVP_RangedWeapon_SubType*>(m_ppxSubTypes[ST_PRIMARY])->SetClickCount(0.0f);

	m_bFirstClick = true;

	if ( GetNumSubTypes() == 2 && (GetItemType() != AVP_INVENTORYITEMTYPE_SMARTGUN))
	{
		AVP_AmmoStore::ReloadSubTypeFromAmmoStore(m_ppxSubTypes[ST_SECONDARY], pxAmmoStore);
		static_cast<AVP_RangedWeapon_SubType*>(m_ppxSubTypes[ST_SECONDARY])->SetClickCount(0.0f);
	}
}

void AVP_InventoryWeapon_RangedWeapon::DoReload( void )
{
	if(GetParentSystem().IsMaster())
	{
		HandleReload();

		Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSGID_INVENTORY_ITEM_RELOAD, sizeof( AVP_Message_Actor_Reload ) );
		if ( pxMessage )
		{
			AVP_Message_Actor_Reload* pxData = static_cast< AVP_Message_Actor_Reload* >( pxMessage->GetData() );

			pxData->m_uWeaponBlueprint = GetBlueprintHash();

			pxMessage->SetToGuid( GetParentSystem().GetOwnerGuid() );
			pxMessage->SetFromGuid( GetParentSystem().GetOwnerGuid() );
			pxMessage->IsUsingGuaranteedDelivery();

			Asura_Network::Send(pxMessage);
		}
	}	
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_InventoryWeapon_RangedWeapon::IsRoomToFire
// Author       : Chris Wallace
// Date         : 03-Feb-2009
// Description	: Takes a list of vectors and a vector, and returns true if
//				: the new vector is not too close to any others in the list.
//				: fMinRange defines how far it should be, error count is 
//				: incremented if false is returned.
////////////////////////////////////////////////////////////////////////////////

bool AVP_InventoryWeapon_RangedWeapon::IsRoomToFire(Asura_LinkList<Asura_Vector_2>& xList, Asura_Vector_2& xVec, float& fMinRange, int& iErrorCount, bool bIgnoreFirstShot)
{
	Asura_ListElt<Asura_Vector_2>* pxCur = xList.Front(); // get first
	u_int uFirstToCheck = 0;
	if(bIgnoreFirstShot)
	{
		if(pxCur && pxCur->Next())
		{
			// Don't avoid first bullet, its position will probably be ignored (so fires down reticle),
			// avoiding this position will leave a blank area
			pxCur = pxCur->Next(); 
			uFirstToCheck ++;
		}
	}

	for(u_int u=uFirstToCheck;u<xList.Size();++u) // we've already skipped first, so start from 1
	{
		if(pxCur)
		{
			Asura_Vector_2 xCurVec = pxCur->Data();
			
			Asura_Vector_2 xTemp = (xVec - xCurVec);
			
			if(xTemp.Magnitude() < fMinRange)
			{
				iErrorCount ++; // Add one to the provided error counter
				return false;
			}
			
			if(pxCur->Next())
			{
				pxCur = pxCur->Next(); // Move to next
			}
			else
			{
				break;
			}
		}
	}

	return true;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_InventoryWeapon_RangedWeapon::Use
// Author       : Andy Tate
// Date         : 13-Nov-2007
////////////////////////////////////////////////////////////////////////////////

DEBUGVAR float dbg_fFudgeEpsilon = 0.001f;

void AVP_InventoryWeapon_RangedWeapon::Use(void)
{
	u_int						uUsageType	= GetCurrentUsageType();
	int							iSubType	= GetSubTypeIndexFromUsageType( uUsageType );
	AVP_RangedWeapon_SubType*	pxSubType	= iSubType < 0 ? NULL : static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ iSubType ] );

	// Reload
	if ( uUsageType == UT_RELOAD_ALL && GetParentSystem().IsMaster()) // only on master
	{
		DoReload();
	}

	if(m_bSubtypesUseSameAmmo)
	{
		MoveAmmoToCurrentSubtype();
	}

	if ( pxSubType != NULL )
	{
		// How much are we shooting this frame
		float fAmount = CalculateFireAmount( pxSubType, uUsageType );
		if ( fAmount > 0.0f )
		{
			m_fStoredBulletsToFire += fAmount;

			const float fIntegerBulletsToFireThisFrame = static_cast< float >( GetBulletsAvailableToFire( m_fStoredBulletsToFire, pxSubType, uUsageType ) );

			m_fStoredBulletsToFire -= fIntegerBulletsToFireThisFrame;

			// Is this enough to fire a bullet?
			bool bHasEnoughAmmo = (pxSubType->GetCurrentQuantity() >= ( fIntegerBulletsToFireThisFrame - dbg_fFudgeEpsilon ) && !pxSubType->IsEmpty() );

			int   iFullShotsToFire		= Asura_Maths::FloatToInt(fIntegerBulletsToFireThisFrame/pxSubType->GetAmmoUsePerShot());

			// We have bullets available, but not as many as we wanted 
			// We might want to do something special in this case 
			// (Used for shotgun)
			if(!bHasEnoughAmmo)
			{
				if ( HandleHalfShot(iFullShotsToFire) )
				{
					// Get these again, as may have changed...
					uUsageType	= GetCurrentUsageType();
					iSubType	= GetSubTypeIndexFromUsageType( uUsageType );
					pxSubType	= iSubType < 0 ? NULL : static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ iSubType ] );
					if(m_bSubtypesUseSameAmmo)
					{
						MoveAmmoToCurrentSubtype();
					}
					bHasEnoughAmmo = true; // for handling of remaining bullets
				}
			}

			if(bHasEnoughAmmo || (pxSubType->IsChargedShot() && (m_fChargedPower >= pxSubType->GetMinChargePerShot())))
			{
				if(!pxSubType->IsChargedShot())
				{
					const float fAmmoQuantityToDecrease = iFullShotsToFire * pxSubType->GetAmmoUsePerShot();
					pxSubType->DecreaseQuantity( fAmmoQuantityToDecrease );
					m_fAmmoFiredSoFar += fAmmoQuantityToDecrease;
				}

				for(int i=0; i<iFullShotsToFire; ++i)
				{
					if(m_fBurstBulletsToFire > 0.0f)// Doing another burst single shot...
					{
						HandleStartFiringEffects( uUsageType );
					}

					AVP_InventoryItem_Cloak* pxCloak = static_cast< AVP_InventoryItem_Cloak* >( GetParentSystem().GetItem( uAVP_INVENTORY_ITEMHASH_CLOAK ) );
					if ( pxCloak != NULL && pxCloak->IsUsing( false ) )
					{
						if ( pxSubType->GetCloakedEnergyCost() < 0.0f )
						{
							pxCloak->RequestEndUsage( AVP_InventoryItem_Cloak::TT_AUTOMATIC );
						}
						else if ( pxSubType->GetCloakedEnergyCost() > 0.0f )
						{
							AVP_ClientEntity_Actor* pxClient = AVP_ClientEntity_Actor::FindEntity( GetParentSystem().GetOwnerGuid() );
							AVP_Actor_DynamicStat* pxDynamicAmmo = pxClient!= NULL ? pxClient->GetDynamicEnergy() : NULL;
							if ( pxDynamicAmmo != NULL )
							{
								pxDynamicAmmo->Remove( pxSubType->GetCloakedEnergyCost(), true );
							}
						}
					}

					const AVP_GenericProjectileData* pxProjectileData = pxSubType->GetProjectile() == ASURA_HASH_ID_UNSET ? NULL : AVP_GenericProjectileData::GetDataByHash( pxSubType->GetProjectile() );
					Asura_Assert( pxProjectileData != NULL, "unable to get projectile data" );
					if ( pxProjectileData != NULL )
					{
						Asura_Guid uOwnerGuid = GetParentSystem().GetOwnerGuid();
						AVP_ClientEntity_Actor* pxClient = AVP_ClientEntity_Actor::FindEntity( uOwnerGuid );
						Asura_Assert( pxClient != NULL, "No client vertebrate found" );
						if ( pxClient != NULL )
						{
							const AVP_Actor_AnimationHandler* pxAnimHandler = pxClient->GetAnimationHandler();
							const Asura_Hash_ID	uMuzMP		= (pxAnimHandler && pxAnimHandler->IsFirstPerson()) ? pxSubType->GetMuzzleMarkerPointFirstPerson() : pxSubType->GetMuzzleMarkerPointThirdPerson();
							const float			fMaxRange	= pxSubType->GetMaxRange();
							Asura_Vector_3		xFirePosition;
							Asura_Vector_3		xFireDirection;
							Asura_Matrix_3x3	xMuzzleOri;
							float				fRange;

							Asura_Guid uTargetGuid = ASURA_GUID_UNREGISTERED;
							CalculateFiringDataForSubType( *pxClient, uMuzMP, pxProjectileData->GetMuzzleVelocity(), fMaxRange, xFirePosition, xFireDirection, xMuzzleOri, fRange, &uTargetGuid );

							float fClosenessMin = dbg_fStartRange;
							int iErrorCount = 0;
							Asura_LinkList<Asura_Vector_2> xShotList;

							while(static_cast<u_int>(xShotList.Size()) < pxSubType->GetNumProjectilesPerShot()) // while we haven't calculated all shots
							{
								Asura_Vector_2 xRandomDir;
								do
								{
									if(iErrorCount>dbg_iErrorCountMax)// After a few fails, make it easier to place.								
									{
										iErrorCount = 0;
										fClosenessMin -= dbg_fAcceptRangeVarience;
									}
									xRandomDir.RandomVector();
								}
								while(!IsRoomToFire(xShotList, xRandomDir, fClosenessMin, iErrorCount, m_bFirstShot));

								xShotList.Push_Back(xRandomDir);
							}

							u_int uShotItr = 0; // count our shots, only used to see if we are on the first loop
							while ( xShotList.Size() > 0 )
							{
								Asura_Vector_3 xShotDirection( xFireDirection );
								if(xShotList.Front())
								{
									if ( !m_bFirstShot || m_fCurrentSpreadParametric > 0.0f)
									{
										xShotDirection = AVP_Maths::OffsetDirectionInCone(xShotDirection, xShotList.Front()->Data(), Asura_Maths::Lerp( pxSubType->GetMinSpread(), pxSubType->GetMaxSpread(), m_fCurrentSpreadParametric ));
									}
									xShotList.PopFront();
								}
								else
								{
									// Something has gone wrong, our list is corrupted!
									xShotList.Clear();
									break;
								}

								m_bFirstShot = false;

								Asura_Matrix_3x3 xShotOrientation;
								Asura_Maths::OrientationFromDirection( xShotOrientation, xShotDirection );

								HandleFireEffects( uUsageType, xFirePosition, xShotOrientation, fRange, uShotItr == 0 );

								// only the master system should send the damage messages
								if( GetParentSystem().IsMaster() )
								{

									if ( pxProjectileData->GetCollisionProxy() == Asura_Physics_Object::NUM_COLLISION_PROXIES ) // equivalent to none, use this as a flag for no physics object
									{
										DoBulletDamage( pxProjectileData, uMuzMP, fRange, fMaxRange, xShotDirection );
									}
									else
									{
										Asura_Vector_3 xVel;
										pxClient->GetCurrentVelocity( xVel );
										SendCreateProjectileMessage( pxProjectileData, pxSubType, uUsageType, uMuzMP, fRange, fMaxRange, xVel, xFirePosition, xShotDirection, xShotOrientation, &uTargetGuid );
									}
								}
								uShotItr ++;
							} // for ( uShotItr )

							// deal with heat up/cool down
							m_fTargetSpreadParametric = Asura_Maths::ClampToRange( m_fCurrentSpreadParametric + pxSubType->GetSpreadHeatUp(), 0.0f, 1.0f );
							m_fSpreadCoolDownTimer = pxSubType->GetSpreadCoolDownDelayTime();

						} // if ( pxVert != NULL )
					} // if ( pxProjectileData != NULL )
				} // for( iFullBulletsToFire...
			}
			else
			{
				// Do dead-mans click
				if ( m_bFirstClick )
				{
					m_bFirstClick = false;
					HandleStopFiringEffects(uUsageType, false);
				}

				if(iFullShotsToFire > 0)
				{
					pxSubType->SetClickCount(pxSubType->GetClickCount()+iFullShotsToFire);

					const Asura_Sound_Event* pxSoundEvent = Asura_Sound_Event_Storage::FindEvent( pxSubType->GetClickHash() );
					if(pxSoundEvent)
					{
						if (GetParentSystem().IsClientSide())
						{
							Asura_Guid uOwnerGuid = GetParentSystem().GetOwnerGuid();
							AVP_ClientEntity_Actor* pxClient = AVP_ClientEntity_Actor::FindEntity( uOwnerGuid );
							Asura_Vector_3 xFirePosition( Asura_Maths::ZeroVector3 );

							if ( pxClient->GetWorldSpaceMarkerPoint( 0x0001a7d2 /*"muz"*/, &xFirePosition, NULL ) )
							{
								Asura_Sound_Event_System::PlayEvent( pxSoundEvent, NULL, &xFirePosition, 1.0f, GetParentSystem().GetOwnerGuid() );
							}
						}
					}
				}
			}

			if(m_fBurstBulletsToFire > 0.0f)
			{
				m_fBurstBulletsToFire -= fIntegerBulletsToFireThisFrame / pxSubType->GetAmmoUsePerShot(); // Do this after, so single shot weapons play their starting effects
			}
		} // if ( fAmount > 0.0f )
	} // if ( pxSubType != NULL )

	if(m_bSubtypesUseSameAmmo)
	{
		MoveAmmoToPrimarySubtype();
	}

} // AVP_Inventory_RangedWeapon::Use

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_InventoryWeapon_RangedWeapon::HandleMessage
// Author       : Chris Wallace
// Date         : 13-Feb-2009
////////////////////////////////////////////////////////////////////////////////

bool AVP_InventoryWeapon_RangedWeapon::HandleMessage(const Asura_Message* pxMessage)
{
	const u_int					uMessageID		= pxMessage->GetMsgID();

	if (pxMessage->GetSentTime() < m_fLastMessageTimestamp)
	{
		// Old message, ignore!
		Asura_Assertf( false, ("Ignoring old message %s", Asura_Network::GetMsgIDDesc( pxMessage->GetMsgID() ))); 
		return true; 
	}

	switch(uMessageID)
	{
		case AVP_MSGID_INVENTORY_ITEM_RELOAD:
		{
			if(pxMessage)
			{
				const AVP_Message_Actor_Reload* pxData = static_cast<const AVP_Message_Actor_Reload*>(pxMessage->GetData());
				if(GetBlueprintHash() == pxData->m_uWeaponBlueprint)
				{
					HandleReload();

					if(GetParentSystem().IsServerSide()) // forward back to client machines
					{
						Asura_Network::Forward( pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES, ASURA_GUID_UNREGISTERED, true );
					}
					return true;
				}
			}
	
		}
	}

	return PARENT::HandleMessage(pxMessage);
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_InventoryWeapon_RangedWeapon::StopUsing
// Author       : Andy Tate
// Date         : 13-Nov-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_InventoryWeapon_RangedWeapon::StopUsing(const u_int uUsageType)
{
	m_fStoredBulletsToFire = 0.0f;
	m_fAmmoFiredSoFar = 0.0f;
	switch(uUsageType)
	{
		case UT_CHARGED_SHOT_PRIMARY:
		case UT_CHARGED_SHOT_SECONDARY:
		{
			if ( m_fChargedPower > 0.0f )
			{
				switch ( GetCurrentTransitionType() )
				{
					case TT_SHOOT:
					{
						RemoveEnergyForChargedShot( uUsageType );
						Use();
						m_fChargedPower = 0.0f;
					}
					break;

					case TT_ABORT_RESTORE_CHARGE:
					{
						AVP_RangedWeapon_SubType* pxSubType = static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ UT_CHARGED_SHOT_PRIMARY == uUsageType ? ST_PRIMARY : ST_SECONDARY ] );
						pxSubType->IncreaseQuantity( m_fChargedPower );
						m_fChargedPower = 0.0f;
					}
					break;

					case TT_ABORT_DESTROY_CHARGE:
					{
						m_fChargedPower = 0.0f;
					}
					break;

					case TT_ABORT_KEEP_CHARGE:
					case TT_NONE:
					{
						// do nothing
					}
					break;

					default:
					{
						Asura_Assert( false, "Unhandled case" );
					}
					break;
				}
			}
		}
		// Fall through
	
		case UT_FULL_AUTO_PRIMARY:
		case UT_FULL_AUTO_SECONDARY:
		case UT_N_BULLET_BURST_PRIMARY:
		case UT_N_BULLET_BURST_SECONDARY:
		{
			HandleStopFiringEffects( uUsageType, true );
		}
		break;

		case UT_RELOAD_ALL:
		{
			HandleStopReloadEffects();
		}
		break;

		default:
		{
			// do nothing
		}
		break;
	}
}

/// End of function 'AVP_InventoryWeapon_RangedWeapon::StopUsing'. ///

void AVP_InventoryWeapon_RangedWeapon::RemoveEnergyForChargedShot( const u_int uUsageType )
{
	AVP_RangedWeapon_SubType* pxSubType = static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ UT_CHARGED_SHOT_PRIMARY == uUsageType ? ST_PRIMARY : ST_SECONDARY ] );
	if ( pxSubType->IsPermanentlyLoseCharge() )
	{
		const AVP_ClientEntity_Actor* pxClient = AVP_ClientEntity_Actor::FindEntity( GetParentSystem().GetOwnerGuid() );
		if ( pxClient != NULL )
		{
			float fEnergyToLose = m_fChargedPower;
			if ( pxSubType->IsDynamic() )
			{
				//fEnergyToLose = Asura_Maths::FloatToInt_Chop( fEnergyToLose / pxClient->GetEnergyPerBlock() ) * pxClient->GetEnergyPerBlock(); // lose max energy as per energy in weapon
				fEnergyToLose = Asura_Maths::FloatToInt_Chop( ( pxSubType->GetMaxQuantity() - pxSubType->GetCurrentQuantity() ) / pxClient->GetEnergyPerBlock() ) * pxClient->GetEnergyPerBlock(); // lose max energy as per energy left
			}
			float fNewEnergy = pxSubType->GetMaxQuantity() - fEnergyToLose;
			if ( fNewEnergy < pxClient->GetMinEnergy() )
			{
				fNewEnergy = pxClient->GetMinEnergy();
				//m_fChargedPower *= dbg_fNoChargeBlocksToLosePowerMultiplier;
			}
			pxSubType->SetMaxQuantity( fNewEnergy );
		}
	}
}

void AVP_InventoryWeapon_RangedWeapon::HandleStartReloadEffects()
{
	if ( m_pxClientEffectsHandler != NULL )
	{
		AVP_ClientEntity_Actor* pxOwner = GetParentSystem().GetParentClient();
		Asura_Assert( pxOwner != NULL, "No client owner" );
		if ( pxOwner != NULL )
		{
			m_pxClientEffectsHandler->PlaySound( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_RELOAD );
			m_pxClientEffectsHandler->PlayActorAnim( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_RELOAD );
			m_pxClientEffectsHandler->PlayWeaponAnim( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_RELOAD );
		}
	}
}

void AVP_InventoryWeapon_RangedWeapon::HandleStopReloadEffects()
{
	if ( m_pxClientEffectsHandler != NULL )
	{
		AVP_ClientEntity_Actor* pxOwner = GetParentSystem().GetParentClient();
		Asura_Assert( pxOwner != NULL, "No client owner" );
		if ( pxOwner != NULL )
		{
			m_pxClientEffectsHandler->StopSound( AVP_RangedWeapon_ClientEffectsHandler::EFFECT_RELOAD );
			m_pxClientEffectsHandler->StopActorAnim( *pxOwner, false );
			m_pxClientEffectsHandler->StopWeaponAnim( *pxOwner, false );
		}
	}
}

void AVP_InventoryWeapon_RangedWeapon::HandleStartFiringEffects( const u_int uUsageType )
{
	if ( m_pxClientEffectsHandler != NULL )
	{
		m_pxClientEffectsHandler->SetTracerCount( 0 );

		int iSubType = GetSubTypeIndexFromUsageType( uUsageType );
		if ( iSubType >= 0 )
		{
			const SubType eSubType = static_cast< SubType >( iSubType );
			AVP_ClientEntity_Actor* pxOwner = GetParentSystem().GetParentClient();
			Asura_Assert( pxOwner != NULL, "No client owner" );
			if ( pxOwner != NULL )
			{
				bool bHasAmmo = false;
				if(m_bSubtypesUseSameAmmo && GetNumSubTypes() >= 2 && GetSubType(ST_PRIMARY) && GetSubType(ST_SECONDARY))
				{
					bHasAmmo = (GetSubType(ST_PRIMARY)->GetCurrentQuantity()+GetSubType(ST_SECONDARY)->GetCurrentQuantity() > 0.0f)?true:false;
				}
				else
				{
					AVP_RangedWeapon_SubType* pxSubType = static_cast<AVP_RangedWeapon_SubType*>(GetSubType(iSubType));
					bHasAmmo = (pxSubType && (!pxSubType->IsEmpty()));
				}
				
				if((GetCurrentUsageType() != UT_FULL_AUTO_PRIMARY && GetCurrentUsageType() != UT_FULL_AUTO_SECONDARY) || bHasAmmo)
				{
					m_pxClientEffectsHandler->PlaySound( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_BEGIN, eSubType );
					m_pxClientEffectsHandler->PlayActorAnim( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_BEGIN, eSubType );
				}
				m_pxClientEffectsHandler->PlayWeaponAnim( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_BEGIN, eSubType );
			}
		}
	}
}
/// End of function 'AVP_ClientWeapon_RangedWeapon::HandleStartFiringEffects'. ///



void AVP_InventoryWeapon_RangedWeapon::DoReloadMuzzleSmoke( int iSubType )
{
	AVP_RangedWeapon_SubType*	pxSubType		= iSubType < 0 ? NULL : static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ iSubType ] );
	AVP_ClientEntity_Actor*		pxOwner			= GetParentSystem().GetParentClient();
	AVP_Actor_AnimationHandler* pxAnimHandler	= pxOwner != NULL ? pxOwner->GetAnimationHandler() : NULL;
	Asura_Animation*			pxAnimation		= pxAnimHandler != NULL ? pxAnimHandler->GetAnimation() : NULL;
	Asura_Assert ( pxAnimation != NULL, "No owner client animation found" );
	if ( pxAnimation && pxSubType )
	{
		const AVP_GenericProjectileData* pxProjectileData = pxSubType->GetProjectile() == ASURA_HASH_ID_UNSET ? NULL : AVP_GenericProjectileData::GetDataByHash( pxSubType->GetProjectile() );
		Asura_Assert( pxProjectileData, "unable to get projectile data" );
		if ( pxProjectileData )
		{
			const Asura_Hash_ID	uMuzMP		= (pxAnimHandler && pxAnimHandler->IsFirstPerson()) ? pxSubType->GetMuzzleMarkerPointFirstPerson() : pxSubType->GetMuzzleMarkerPointThirdPerson();
			const float			fMaxRange	= pxSubType->GetMaxRange();
			Asura_Vector_3		xFirePosition;
			Asura_Vector_3		xFireDirection;
			Asura_Matrix_3x3	xMuzzleOri;
			float				fRange;
			CalculateFiringDataForSubType( *pxOwner, uMuzMP, pxProjectileData->GetMuzzleVelocity(), fMaxRange, xFirePosition, xFireDirection, xMuzzleOri, fRange );

			Asura_Matrix_3x3 xFireOrientation;
			Asura_Maths::OrientationFromDirection( xFireOrientation, xFireDirection );

			// Muzzle flash PFX
			m_pxClientEffectsHandler->DoMuzzleFlash( AVP_RangedWeapon_ClientEffectsHandler::FLASH_RELOAD, *pxOwner, *pxAnimation, xFirePosition, xFireOrientation, static_cast< SubType >(iSubType) );
		}
	}
}



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_InventoryWeapon_RangedWeapon::HandleFireEffects
// Author       : Andy Tate
// Date         : 20-Nov-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_InventoryWeapon_RangedWeapon::HandleFireEffects( const u_int uUsageType, const Asura_Vector_3& xFirePosition, const Asura_Matrix_3x3& xFireOrientation, const float fRange, const bool bFirstShot )
{
	if ( m_pxClientEffectsHandler != NULL )
	{
		const SubType eSubType = static_cast< SubType >( GetSubTypeIndexFromUsageType( uUsageType ) );

		if ( bFirstShot )
		{
			AVP_ClientEntity_Actor*		pxOwner			= GetParentSystem().GetParentClient();
			AVP_Actor_InputHandler*		pxInputHandler	= pxOwner ? pxOwner->GetInputHandler() : NULL;
			AVP_Actor_AnimationHandler* pxAnimHandler	= pxOwner != NULL ? pxOwner->GetAnimationHandler() : NULL;
			Asura_Animation*			pxAnimation		= pxAnimHandler != NULL ? pxAnimHandler->GetAnimation() : NULL;
			Asura_Assert ( pxAnimation != NULL, "No owner client animation found" );
			if ( pxAnimation != NULL )
			{
				// Muzzle flash PFX
				m_pxClientEffectsHandler->DoMuzzleFlash( AVP_RangedWeapon_ClientEffectsHandler::FLASH_FIRE, *pxOwner, *pxAnimation, xFirePosition, xFireOrientation, eSubType );

				// Also play muzzle flash "Hot" when certain number of bullets left the barrel already.
				if( m_bCanPlayHotMuzzleFlash )
				{
					float fMuzzleFlash_Hot_NumBulletsToBegin = m_pxClientEffectsHandler->GetWeaponHotMuzzleFlash_NumBulletsToBegin(eSubType);
					if( GetAmmoFiredSoFar()>=fMuzzleFlash_Hot_NumBulletsToBegin && fMuzzleFlash_Hot_NumBulletsToBegin!=0.0f )
					{
						m_bCanPlayHotMuzzleFlash = false;
						m_pxClientEffectsHandler->DoMuzzleFlash( AVP_RangedWeapon_ClientEffectsHandler::FLASH_HOT, *pxOwner, *pxAnimation, xFirePosition, xFireOrientation, eSubType );
					}
				}
			}

			AVP_RangedWeapon_SubType* pxSubType = static_cast<AVP_RangedWeapon_SubType*>(GetSubType(eSubType));
			if(pxSubType)
			{
				// Sound
				if ( !m_pxClientEffectsHandler->IsSoundPlaying( AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_BEGIN, eSubType ) )
				{
					if ( m_pxClientEffectsHandler->IsSoundPlaying( AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_LOOP, eSubType ) )
					{
						m_pxClientEffectsHandler->UpdateSoundPosition( xFirePosition, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_LOOP, eSubType );
					}
					else
					{
						m_pxClientEffectsHandler->PlaySound( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_LOOP, eSubType );
					}
				}

				if ( pxInputHandler )
				{
					pxInputHandler->AddRecoilVelocity( pxSubType->GetWeaponRecoil() );
				}
			}
	
			// Actor Anim
			if(uUsageType == UT_FULL_AUTO_PRIMARY || uUsageType == UT_FULL_AUTO_SECONDARY)
			{
				if(!m_pxClientEffectsHandler->IsActorAnimPlaying(*pxOwner,AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_BEGIN, eSubType ))
				{
					m_pxClientEffectsHandler->PlayActorAnim( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_LOOP, eSubType );
				}
				
				// Weapon Anim
				if(!m_pxClientEffectsHandler->IsWeaponAnimPlaying(*pxOwner,AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_BEGIN, eSubType ))
				{
					m_pxClientEffectsHandler->PlayWeaponAnim( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_LOOP, eSubType );
				}
			}

			m_pxClientEffectsHandler->DoRecoilCameraShake(GetParentSystem().GetOwnerGuid(), eSubType);
		}

		if ( ((AVP_RangedWeapon_SubType*)GetSubType( eSubType ))->GetRateOfFire() >= 1.0f )
		{
			// Beam effect
			m_pxClientEffectsHandler->DoTracerBeam( xFirePosition, xFireOrientation, fRange, eSubType, 1 );
		}
		else
		{
			// Beam effect
			m_pxClientEffectsHandler->DoTracerBeam( xFirePosition, xFireOrientation, fRange, eSubType, 2 );
		}

		// Vibrate
		if ( AVP_Settings::GetCurrentProfile()->GetControls().GetVibrate() )
		{
			AVP_ClientEntity_Actor* pxOwner	= GetParentSystem().GetParentClient();
			if ( pxOwner && AVP_Game_Core::IsLocalPlayer( pxOwner->GetGuid()) )
			{
				// AJK Vibrate
				AVP_InventoryItem_SubType* pxSubType = GetSubTypeFromUsageType( uUsageType );
				if( pxSubType->GetVibrationHash() != ASURA_HASH_ID_UNSET )
				{
					pxSubType->InitRumble();
					AVP_Rumble_System::AddHUDRumble( pxSubType->GetVibrationHash() );
				}
			}
		}
	}
}

void AVP_InventoryWeapon_RangedWeapon::HandleChargingEffects( const SubType eSubType, const Asura_Vector_3& xFirePosition, const Asura_Matrix_3x3& xFireOrientation )
{
	if ( m_pxClientEffectsHandler != NULL )
	{
		AVP_ClientEntity_Actor*		pxOwner			= GetParentSystem().GetParentClient();
		AVP_Actor_AnimationHandler* pxAnimHandler	= pxOwner != NULL ? pxOwner->GetAnimationHandler() : NULL;
		Asura_Animation*			pxAnimation		= pxAnimHandler != NULL ? pxAnimHandler->GetAnimation() : NULL;
		Asura_Assert ( pxAnimation != NULL, "No owner client animation found" );
		if ( pxAnimation != NULL )
		{
			// PFX
			if ( !m_pxClientEffectsHandler->IsChargePFXPlaying( eSubType ) )
			{
				m_pxClientEffectsHandler->DoChargePFX( *pxOwner, *pxAnimation, xFirePosition, xFireOrientation, eSubType );
			}
		}

		// Sound
		if ( m_pxClientEffectsHandler->IsSoundPlaying( AVP_RangedWeapon_ClientEffectsHandler::EFFECT_CHARGING, eSubType ) )
		{
			m_pxClientEffectsHandler->UpdateSoundPosition( xFirePosition, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_CHARGING, eSubType );
		}
		else
		{
			m_pxClientEffectsHandler->PlaySound( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_CHARGING, eSubType );
		}
	}
}

/// End of function 'AVP_ClientWeapon_RangedWeapon::CreateBulletFireEffects'. ///


 
////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_InventoryWeapon_RangedWeapon::HandleStopFiringEffects
// Author       : Andy Tate
// Date         : 27-Nov-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_InventoryWeapon_RangedWeapon::HandleStopFiringEffects( const u_int uUsageType, const bool bStopWeaponAnims )
{
	if ( m_pxClientEffectsHandler != NULL )
	{
		AVP_ClientEntity_Actor*		pxOwner			= GetParentSystem().GetParentClient();
		Asura_Assert( pxOwner != NULL, "No client owner" );
		if ( pxOwner != NULL )
		{
			const SubType eSubType = static_cast< SubType >( GetSubTypeIndexFromUsageType( uUsageType ) );

			m_pxClientEffectsHandler->StopChargePFX( eSubType );
			if ( m_pxClientEffectsHandler->IsActorAnimPlaying( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_LOOP, eSubType ) ||
				 m_pxClientEffectsHandler->IsActorAnimPlaying( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_BEGIN, eSubType ))
			{
				m_pxClientEffectsHandler->StopActorAnim( *pxOwner, true, eSubType );
			}

			if ( bStopWeaponAnims )
			{
				if ( m_pxClientEffectsHandler->IsWeaponAnimPlaying( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_LOOP, eSubType ) ||
					m_pxClientEffectsHandler->IsWeaponAnimPlaying( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_BEGIN, eSubType ))
				{
					m_pxClientEffectsHandler->StopWeaponAnim( *pxOwner, true, eSubType );
				}
			}

			if(!m_bFirstShot) // Don't play these if we never got off the first frame.
			{
				if(m_pxClientEffectsHandler->IsSoundPlaying(AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_LOOP, eSubType))
				{
					m_pxClientEffectsHandler->PlaySound( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_END, eSubType );
				}
		
				if ( !m_pxClientEffectsHandler->IsActorAnimPlaying( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_END, eSubType ) )
				{
					m_pxClientEffectsHandler->PlayActorAnim( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_END, eSubType );
				}
				if ( bStopWeaponAnims )
				{
					if ( !m_pxClientEffectsHandler->IsWeaponAnimPlaying( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_END, eSubType ) )
					{
						m_pxClientEffectsHandler->PlayWeaponAnim( *pxOwner, AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_END, eSubType );
					}
				}
			}

			/// Stop loop effects
			m_pxClientEffectsHandler->StopSound( AVP_RangedWeapon_ClientEffectsHandler::EFFECT_FIRE_LOOP, eSubType );
			m_pxClientEffectsHandler->StopSound( AVP_RangedWeapon_ClientEffectsHandler::EFFECT_CHARGING, eSubType );
		}
	}
}

void AVP_InventoryWeapon_RangedWeapon::HandleActivationEffects()
{
	if ( m_pxClientEffectsHandler != NULL )
	{
		AVP_ClientEntity_Actor*		pxOwner			= GetParentSystem().GetParentClient();
		AVP_Actor_AnimationHandler* pxAnimHandler	= pxOwner != NULL ? pxOwner->GetAnimationHandler() : NULL;
		Asura_Animation*			pxAnimation		= pxAnimHandler != NULL ? pxAnimHandler->GetAnimation() : NULL;
		Asura_Assert ( pxAnimation != NULL, "No owner client animation found" );
		if ( pxAnimation != NULL )
		{
			// Show laser in all circumstances, except when the Disc is selected as a weapon. Because for disc, we handle Activation of Laser
			// differently. Laser can be turn on/off anytime when disc is equipped. But it starts as turned off.
			if( GetItemType()!=AVP_INVENTORYITEMTYPE_PREDATORDISC )
			{
				m_pxClientEffectsHandler->ActivateLaser( *pxAnimation );
			}
		}
	}
}

void AVP_InventoryWeapon_RangedWeapon::HandleDeactivationEffects()
{
	if ( m_pxClientEffectsHandler != NULL )
	{
		m_pxClientEffectsHandler->DeactivateLaser();
	}
}

void AVP_InventoryWeapon_RangedWeapon::SetServerItemToUseDynamicAmmo( AVP_Actor_DynamicStat* pxDynamicAmmo )
{
	AVP_ServerEntity_Actor* pxServer = AVP_ServerEntity_Actor::FindEntity( GetParentSystem().GetOwnerGuid() );
	AVP_Inventory_System* pxSystem = pxServer != NULL ? pxServer->GetInventorySystem() : NULL;
	AVP_Inventory_Group* pxGroup = pxSystem != NULL ? pxSystem->GetGroup( uAVP_INVENTORY_GROUPHASH_WEAPON ) : NULL;
	AVP_InventoryWeapon_RangedWeapon* pxWeapon = pxGroup != NULL ? static_cast< AVP_InventoryWeapon_RangedWeapon* >( pxGroup->GetItem( m_uHashInParentGroup ) ) : NULL;
	if ( pxWeapon != NULL )
	{
		pxWeapon->SetDynamicAmmo( pxDynamicAmmo );
	}
}

void AVP_InventoryWeapon_RangedWeapon::SetDynamicAmmo( AVP_Actor_DynamicStat* pxDynamicAmmo )
{
	const u_int uEnd = GetNumSubTypes();
	for ( u_int uItr = 0; uItr < uEnd; ++uItr )
	{
		static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ uItr ] )->SetDynamicAmmo( pxDynamicAmmo );
	}
}

float AVP_InventoryWeapon_RangedWeapon::CalculateFireAmount( const AVP_RangedWeapon_SubType* pxSubType, u_int uUsageType )
{
	if(pxSubType)
	{
		float fAmount = pxSubType->GetAmmoUsePerShot();
		if(!m_bFirstShot)
		{
			if ( uUsageType == UT_FULL_AUTO_PRIMARY || uUsageType == UT_FULL_AUTO_SECONDARY )
			{
				fAmount	= pxSubType->GetRateOfFire() * Asura_Timers::GetGameTimeSlice();
			}
			else if( uUsageType == UT_N_BULLET_BURST_PRIMARY || uUsageType == UT_N_BULLET_BURST_SECONDARY )
			{
				if(m_fMaxBurstBulletsToFire > 1.0f) // If we are firing a burst, use the rate of fire
				{
					fAmount	= pxSubType->GetRateOfFire() * Asura_Timers::GetGameTimeSlice();
				}
			}
		}
		return fAmount;
	}
	return 0.0f;
}

u_int AVP_InventoryWeapon_RangedWeapon::GetBulletsAvailableToFire( float fAmount, AVP_RangedWeapon_SubType* pxSubType, u_int uUsageType )
{
	u_int uBulletsToFire = 0;
	if ( uUsageType == UT_CHARGED_SHOT_PRIMARY || uUsageType == UT_CHARGED_SHOT_SECONDARY )
	{
		if(m_fChargedPower > 0.0f) // charged shots decrease their ammo before using
		{
			uBulletsToFire = 1;
		}
	}
	else
	{
		uBulletsToFire = Asura_Maths::FloatToInt_Chop( fAmount / pxSubType->GetAmmoUsePerShot() ) * Asura_Maths::FloatToInt_Near( pxSubType->GetAmmoUsePerShot() );
	}
	return uBulletsToFire;
}

void AVP_InventoryWeapon_RangedWeapon::CalculateFiringDataForSubType( const AVP_ClientEntity_Actor& xActor, Asura_Hash_ID uMuzMP, float fMuzVel, float fMaxRange, Asura_Vector_3& xFirePosition, Asura_Vector_3& xFireDirection, Asura_Matrix_3x3& xMuzzleOri, float& fRange, Asura_Guid* puTargetGuid /*= NULL*/ )
{
	if(puTargetGuid)
	{
		*puTargetGuid = ASURA_GUID_UNREGISTERED; // reset here for weapons that cant target
	}

	if(!xActor.GetMarkerLocationFromHash( uMuzMP, &xFirePosition, &xMuzzleOri ))
	{
		xFirePosition	= xActor.GetCurrentPosition();
		xMuzzleOri		= xActor.GetCurrentOrientation();
	}

	if ( xActor.IsPlayer() )
	{
		const AVP_Vertebrate_Focus* pxFocus = xActor.GetFocus();
		if ( GetUsesLockOn() && pxFocus != NULL && pxFocus->HasValidLockOnTarget() )
		{
			Asura_Vector_3			xTargetVelocity;
			Asura_Vector_3			xTargetPosition( Asura_Maths::ZeroVector3 );
			AVP_ClientEntity_Actor*	pxTarget	= AVP_ClientEntity_Actor::FindEntity( pxFocus->GetLockOnTarget() );
			Asura_Assert( pxTarget != NULL, "Unable to get tagged actor" );
			if ( pxTarget != NULL )
			{
				pxTarget->GetCurrentVelocity(xTargetVelocity);
				if ( !pxTarget->GetMarkerLocationFromHash( 0xe5a85d03, &xTargetPosition, NULL ) ) // target_1
				{
					xTargetPosition = pxTarget->GetCurrentPosition();
					Asura_Assert(false, "Failed to find target markerpoint");
				}
			}

			if(puTargetGuid)
			{
				// If a Guid pointer was passed in set this, we assume that the velocity will be calculated server side.
				// For weapons where this isn't the case such as the Smartgun, this function should be overwritten.
				*puTargetGuid = pxFocus->GetTargetGuid();
			}
			else 
			{
				// Still set these from client data if no guid pointer was passed in.
				CalculateFiringDataForMovingTarget( xActor, uMuzMP, fMuzVel, xTargetPosition, xTargetVelocity, xFirePosition, xMuzzleOri, xFireDirection);
			}

			fRange = ( xTargetPosition - xFirePosition ).Magnitude();
		}
		else
		{
			Asura_Matrix_3x3	xFireOrientation;
			fRange = CalculateFiringDataForCamera( xActor, uMuzMP, fMaxRange, xFirePosition, xFireOrientation );
			xFireDirection = Asura_Vector_3( xFireOrientation.e13, xFireOrientation.e23, xFireOrientation.e33 );
		}
	}
	else // if AI
	{
		Asura_AVector3 xTemp;
		xActor.GetAimDirection( xTemp );
		xFireDirection = Asura_Vector_3( xTemp );
		
		Asura_AVector3 xLookAtPos;
		xActor.GetLookAtPosition( xLookAtPos );

		Asura_Matrix_3x3 xOri;
		
		Asura_Vector_3 xPosition;
		if ( !xActor.GetMarkerLocationFromHash( uMuzMP, &xPosition, &xOri ) ) // muz
		{
			
			xPosition = xActor.GetCurrentPosition();
			Asura_Assert(false, "Failed to find target markerpoint");
		}

		xFireDirection = Asura_Vector_3( xOri.e13, xOri.e23, xOri.e33 );
		fRange = (Asura_Vector_3(xLookAtPos) - xPosition).Magnitude();
	}
}

void AVP_InventoryWeapon_RangedWeapon::DoBulletDamage( const AVP_GenericProjectileData* pxProjectileData, Asura_Hash_ID uMuzMP, float fRange, float fMaxRange, const Asura_Vector_3& xShotDirection )
{
	Asura_Guid uOwnerGuid = GetParentSystem().GetOwnerGuid();
	Asura_Message*	pxMessage	= Asura_Network::CreateMessage(AVP_MSGID_ENTITYDAMAGE_REQUEST_LINEAR, sizeof(AVP_Message_EntityDamage_RequestLinear));

	// Calculate muzzle position, impact position and impact GUID
	Asura_Vector_3 xStartPosition(0.f, 0.f, 0.f);
	Asura_Vector_3 xImpactPosition(0.f, 0.f, 0.f); 
	Asura_Vector_3 xVictimPosition(0.f, 0.f, 0.f);
	Asura_Vector_3 xRelativeStartPosition(xShotDirection);
	Asura_Guid uImpactGuid(ASURA_GUID_UNREGISTERED);
	
	const AVP_ClientEntity_PhysicalObject*	pxPhysicalObject	= AVP_ClientEntity_PhysicalObject::FindEntity(uOwnerGuid);

	if(pxPhysicalObject == NULL)
	{
		Asura_Assert( uMuzMP == ASURA_HASH_ID_UNSET, "Failed to find client physical-object to request linear damage from a marker point for, using server position");
		const AVP_ServerEntity_PhysicalObject*	pxServer = AVP_ServerEntity_PhysicalObject::FindEntity(uOwnerGuid);
		if ( pxServer )
		{
			xStartPosition = pxServer->GetPosition();
		}
		else
		{
			Asura_Assert( false, "Failed to find server object to do damage from" );
		}
	}
	else
	{
		if ( uMuzMP == ASURA_HASH_ID_UNSET || !pxPhysicalObject->GetWorldSpaceMarkerPoint(uMuzMP, &xStartPosition, NULL) )
		{
			Asura_Assert( uMuzMP == ASURA_HASH_ID_UNSET, "Unable to get marker point for damage request" );
			xStartPosition = pxPhysicalObject->GetCurrentPosition();
		}
	}

	float fDistance = Asura_Maths::LargeFloat;
	const Asura_LOS_InputData		xLOSInput(xStartPosition, xShotDirection, uOwnerGuid, fMaxRange);
	const Asura_LOS_OutputData		xEntLOSOutput			= *(Asura_LOS::TestAgainstEntities(&xLOSInput));

	if ( xEntLOSOutput.m_uHitEntity != 0 && xEntLOSOutput.m_uHitEntity != uOwnerGuid )
	{
		fDistance = xEntLOSOutput.m_fDistanceToIntersection;
		uImpactGuid = xEntLOSOutput.m_uHitEntity;
	
		// Position to model space
		const AVP_ClientEntity_PhysicalObject*	pxPhysicalObject	= AVP_ClientEntity_PhysicalObject::FindEntity(uImpactGuid);
		if ( pxPhysicalObject == NULL )
		{
			Asura_Assert( false, "Failed to find client physical-object to request linear damage to, using server position");
			const AVP_ServerEntity_PhysicalObject*	pxServer = AVP_ServerEntity_PhysicalObject::FindEntity(uImpactGuid);
			if ( pxServer )
			{
				xVictimPosition = pxServer->GetPosition();
			}
			else
			{
				Asura_Assert( false, "Failed to find server object to do damage to" );
			}
		}
		else
		{
			xVictimPosition = pxPhysicalObject->GetCurrentPosition(); 
		}

		xRelativeStartPosition = xShotDirection * fDistance;
		xImpactPosition = xStartPosition + xRelativeStartPosition - xVictimPosition; 
	}

	Asura_LOS_OutputData xEnvLOSOutput = *(Asura_LOS::TestAgainstEnvironment(&xLOSInput));
	if ( xEnvLOSOutput.m_bHitEnvironment && xEnvLOSOutput.m_fDistanceToIntersection < fDistance )
	{
		fDistance = xEnvLOSOutput.m_fDistanceToIntersection;
		xRelativeStartPosition = xShotDirection * fDistance;
		xImpactPosition = xStartPosition + xRelativeStartPosition; 
		uImpactGuid = ASURA_GUID_UNREGISTERED; 
	}

	if(pxMessage != NULL)
	{
		AVP_Message_EntityDamage_RequestLinear*	pxData				= static_cast<AVP_Message_EntityDamage_RequestLinear*>(pxMessage->GetData());
		const float								fImpulseStrength	= pxProjectileData->GetImpactImpulseAtRange( fRange );
		const float								fDamageAmount		= pxProjectileData->GetImpactDamageAtRange( fRange );

		const AVP_EntityDamage_RequestLinear	xDamage(pxProjectileData->GetImpactDamageType(),
			uOwnerGuid,
			uMuzMP,
			fDamageAmount,
			fImpulseStrength,
			uOwnerGuid,
			xImpactPosition,
			xRelativeStartPosition,
			fMaxRange,
			ASURA_GUID_UNREGISTERED,
			uImpactGuid);

		pxData->SetDamage(xDamage);

		pxMessage->SetFromGuid(uOwnerGuid);
		pxMessage->SetToGuid(uOwnerGuid);
		Asura_Network::Send(pxMessage);
	}
}

void AVP_InventoryWeapon_RangedWeapon::SendCreateProjectileMessage( const AVP_GenericProjectileData* pxProjectileData, const AVP_RangedWeapon_SubType* pxSubType, u_int uUsageType, Asura_Hash_ID uMuzMP, float fRange, float fMaxRange, const Asura_Vector_3& xActorVel, const Asura_Vector_3& xFirePosition, const Asura_Vector_3& xShotDirection, const Asura_Matrix_3x3& xShotOrientation, Asura_Guid* puTargetGuid /*=NULL*/ )
{
	Asura_Guid uOwnerGuid = GetParentSystem().GetOwnerGuid();
	Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSGID_CREATE_GENERICPROJECTILE, sizeof( AVP_Message_CreateGenericProjectile ) );
	Asura_Assert( pxMessage != NULL, "Unable to create message" );
	if(pxMessage != NULL)
	{
		AVP_Message_CreateGenericProjectile*	pxData		= static_cast< AVP_Message_CreateGenericProjectile* >( pxMessage->GetData() );

		Asura_Matrix_3x3 xAdjustedShotOri = xShotOrientation;

		// In case the projectile is grenade (or other projectiles with gravity on), adjust the shooting angle little bit up, so it will really hit the target. ( Because we see the target directly (by direct line), but grenade is flying in angle. )
		if ( pxProjectileData->GetDoBallisticAdjustment() )	
		{
			const float fMuzzleVel = pxProjectileData->GetMuzzleVelocity();
			float fBallisticAngle = 0.5f * Asura_Maths::ArcSine( Asura_Maths::ClampToRange( ( pxProjectileData->GetGravityScale() * Asura_Physics_System::GetGravityScalar() * fRange ) / ( fMuzzleVel * fMuzzleVel ), 0.0f, 1.0f ) ); // zero drag, zero wind, zero elevation approximation seems to work ok in most cases, well done Galileo
			fBallisticAngle = Asura_Maths::ClampToRange( fBallisticAngle, 0.0f, Asura_Maths::DegToRad( 15.0f ) );
			float fSin;
			float fCos;
			Asura_Maths::SinCos( fBallisticAngle, fSin, fCos );
			Asura_Matrix_3x3 xBallisticAdjstment( 1.0f, 0.0f, 0.0f, 0.0f, fCos, -fSin, 0.0f, fSin, fCos );

			xAdjustedShotOri = xShotOrientation * xBallisticAdjstment;
		}

		// Fill-in the message...
		pxData->SetProjectileBlueprintHash( pxProjectileData->GetProjectileHash() );
		pxData->SetPosition( xFirePosition );
		pxData->SetOrientation( Asura_Quat(xAdjustedShotOri) );
		pxData->SetActorVelocity( xActorVel );

		float fDamageScale = 1.0f;
		if ( uUsageType == UT_CHARGED_SHOT_PRIMARY || uUsageType == UT_CHARGED_SHOT_SECONDARY )
		{
			fDamageScale = (pxSubType->GetDamageMultiplierPerCharge() * m_fChargedPower)/pxSubType->GetMaxChargePerShot();
		}
		pxData->SetDamageScale( fDamageScale );
		pxData->SetLaunch( true );

		if(puTargetGuid)
		{
			pxData->SetTargetGuid(*puTargetGuid);
		}
		else
		{
			pxData->SetTargetGuid(ASURA_GUID_UNREGISTERED);
		}

		// TODO we might need to do the local handling hack to make this more responsive on remote clients

		pxMessage->SetToGuid( uOwnerGuid );
		pxMessage->SetFromGuid( uOwnerGuid );

		Asura_Network::Send( pxMessage );
	} // if ( pxMessage != NULL )
}

void AVP_InventoryWeapon_RangedWeapon::UpdateLaser( const AVP_ClientEntity_Actor& xActor )
{
	if ( m_pxClientEffectsHandler && m_pxClientEffectsHandler->HasLaser() )
	{
		Asura_Guid uTargetGuid = ASURA_GUID_UNREGISTERED; 
		bool bIsLockedOnTarget = false;
		
		if (GetUsesLockOn())
		{
			const AVP_Vertebrate_Focus* pxFocus = xActor.GetFocus();
			if ( pxFocus ) // get guid, even if not locked onto it
			{
				if(pxFocus->GetTargetGuid() != ASURA_GUID_UNREGISTERED)
				{
					uTargetGuid = pxFocus->GetTargetGuid();
				}
				else if(pxFocus->GetBestTargetGuid() != ASURA_GUID_UNREGISTERED)
				{
					uTargetGuid = pxFocus->GetBestTargetGuid(); 
				}
			}
			else
			{
				// Remote predator player?
				if (xActor.GetClassification() == AVP_ENTITYCLASS_PREDATOR)
				{
					const AVP_ClientActor_Predator* pxPredActor = reinterpret_cast<const AVP_ClientActor_Predator*>( &xActor );
					if (pxPredActor->IsLockOn())
					{
						uTargetGuid = pxPredActor->GetTargetGuid();
					}
				}
			}

			if ( uTargetGuid != ASURA_GUID_UNREGISTERED )
			{
				m_pxClientEffectsHandler->SetLaserTarget( uTargetGuid, 0xe5a85d03 );
				bIsLockedOnTarget = true; 
			}
			else
			{
				bIsLockedOnTarget = false;
			}
		}
		
		Asura_Vector_3 xFirePosition(Asura_Maths::ZeroVector3);

		if (!bIsLockedOnTarget)
		{
			const AVP_RangedWeapon_SubType* pxSubtype = static_cast< const AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_PRIMARY ] );
			const float			fMaxRange	= pxSubtype->GetMaxRange();
			Asura_Vector_3		xFireDirection;
			Asura_Matrix_3x3	xMuzzleOri;
			float				fRange;
			const AVP_Actor_AnimationHandler* pxAnimHandler = xActor.GetAnimationHandler();
			Asura_Hash_ID uMarkerHash = (pxAnimHandler && pxAnimHandler->IsFirstPerson()) ? pxSubtype->GetMuzzleMarkerPointFirstPerson() : pxSubtype->GetMuzzleMarkerPointThirdPerson();
			CalculateFiringDataForSubType( xActor, uMarkerHash, 1000.0f, fMaxRange, xFirePosition, xFireDirection, xMuzzleOri, fRange );
			m_pxClientEffectsHandler->SetLaserTarget( xFirePosition + xFireDirection * fRange );

			// No target so we just hitting the environment use calculated position.
			AVP_ClientEntity_Actor* pxClientActor = GetParentSystem().GetParentClient();

			if ( pxClientActor )
			{
				pxClientActor->UpdateLaserData( true, xFirePosition, xFireDirection, fRange );
			}
		}
		else
		{
			// We have a laser and a target so calculate muzzle to chest direction and range.
			AVP_ClientEntity_Actor* pxClientActor = GetParentSystem().GetParentClient();
			AVP_ClientEntity_Actor* pxTargetActor = AVP_ClientEntity_Actor::FindEntity( uTargetGuid );

			if ( pxClientActor && pxTargetActor)
			{
				Asura_Vector_3 xTargetChestPos( Asura_Maths::ZeroVector3 );

				if ( pxClientActor->GetWorldSpaceMarkerPoint( 0x061FBECB /*Laser*/, &xFirePosition, NULL ) && pxTargetActor->GetWorldSpaceMarkerPoint( 0xE5A85D03 /*target_1*/, &xTargetChestPos, NULL ) )
				{
					Asura_Vector_3 xFireDirection( xTargetChestPos - xFirePosition );
					float fRange = xFireDirection.Normalise();

					pxClientActor->UpdateLaserData( true, xFirePosition, xFireDirection, fRange );
				}
			}				
		}

		m_pxClientEffectsHandler->UpdateLaserLoopedSound(xFirePosition);
	}
	else
	{
		// Not using a weapon with a laser.
		AVP_ClientEntity_Actor* pxClientActor = GetParentSystem().GetParentClient();

		if ( pxClientActor )
		{
			pxClientActor->UpdateLaserData( false );
		}
	}
}

bool AVP_InventoryWeapon_RangedWeapon::ShouldAllowReload(void)
{
	// Work out if we should reload...
	bool bAllowReload = false;
	const bool bPrimaryNotFull		= !m_ppxSubTypes[ ST_PRIMARY ]->IsFull();
	const bool bSecondaryNotFull	= (this->GetNumSubTypes() == 2) ? !m_ppxSubTypes[ ST_SECONDARY ]->IsFull() : false;

	if(m_bSubtypesUseSameAmmo)
	{
		// Only care about primary
		AVP_AmmoStore* pxAmmoStore = GetParentSystem().GetAmmoStore();
		if(pxAmmoStore)
		{
			bool bPrimaryAmmoAvailable = pxAmmoStore->GetAmmoQuantity(m_ppxSubTypes[ ST_PRIMARY ]->GetAmmoType())>=1.0f;
		
			bAllowReload = (bPrimaryNotFull && bPrimaryAmmoAvailable);
		}
		bAllowReload |= AVP_Settings::GetInfiniteAmmo() && bPrimaryNotFull;
	}
	else
	{
		AVP_AmmoStore* pxAmmoStore = GetParentSystem().GetAmmoStore();
		if(pxAmmoStore)
		{
			bool bPrimaryAmmoAvailable = pxAmmoStore->GetAmmoQuantity(m_ppxSubTypes[ ST_PRIMARY ]->GetAmmoType())>=1.0f;
			bool bSecondaryAmmoAvailable = (this->GetNumSubTypes() == 2) ? pxAmmoStore->GetAmmoQuantity(m_ppxSubTypes[ ST_SECONDARY ]->GetAmmoType())>=1.0f : false;

			bAllowReload = (bPrimaryNotFull && bPrimaryAmmoAvailable) || (bSecondaryNotFull && bSecondaryAmmoAvailable);
		}
		bAllowReload |= AVP_Settings::GetInfiniteAmmo() && (bPrimaryNotFull || bSecondaryNotFull);
	}

	// Done working out if we should reload...
	return bAllowReload;
}

void AVP_InventoryWeapon_RangedWeapon::ProcessPlayerInput( const AVP_ClientEntity_Actor& xActor )
{
	PARENT::ProcessPlayerInput( xActor );

	if( !IsEncumberment() )
	{
		const AVP_Actor_InputHandler* pxInputHandler = xActor.GetInputHandler();
		if ( GetCurrentState() == IDLING && pxInputHandler != NULL && ( GetTimeInState() > 0.0f ) )
		{
			// Work out if we should reload...
			const bool bNoPrimaryAmmo		= m_ppxSubTypes[ ST_PRIMARY ]->IsEmpty();
			const bool bNoSecondaryAmmo		= (GetNumSubTypes() >= 2) ? m_ppxSubTypes[ ST_SECONDARY ]->IsEmpty() : true;
			
			const bool bNoPrimaryAmmoAtAll = bNoPrimaryAmmo && (!GetParentSystem().GetAmmoStore() || GetParentSystem().GetAmmoStore()->GetAmmoQuantity(m_ppxSubTypes[ ST_PRIMARY ]->GetAmmoType()) <= 0.0f);

			bool bAllowReload = ShouldAllowReload();
			
			bool bFiredPrimary = false;
			// Check for the appropriate input...
			if ( !xActor.IsMovingFast() )
			{
				if ( ( pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::RELOAD ) > 0.0f ) && bAllowReload && CanReload() )
				{
					RequestUsing( UT_RELOAD_ALL );
				}
				else
				{
					if ( m_ppxSubTypes[ ST_PRIMARY ] != NULL )
					{
						if ( ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_PRIMARY ] ) )->IsFullAuto() )
						{
							if ( pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::PRIMARY_ATTACK_HELD ) > 0.0f && ( !bNoPrimaryAmmoAtAll ) )
							{
								if ( m_fPrimaryShotTimer <= 0.0f )
								{
									m_fPrimaryShotTimer = 1.0f / ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_PRIMARY ] ) )->GetRateOfFire();
									RequestUsing( UT_FULL_AUTO_PRIMARY );
									bFiredPrimary = true;
								}
							}
						}
						else if ( ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_PRIMARY ] ) )->IsChargedShot() )
						{
							if ( pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::PRIMARY_ATTACK_HELD ) > 0.0f && ( !bNoPrimaryAmmo ) )
							{
								if ( m_fPrimaryShotTimer <= 0.0f )
								{
									AVP_RangedWeapon_SubType* pxSubType = static_cast<AVP_RangedWeapon_SubType*>(GetSubTypeFromUsageType(UT_CHARGED_SHOT_PRIMARY));
									if(pxSubType)
									{
										if(pxSubType->GetCurrentQuantity() >= pxSubType->GetMinChargePerShot()) // dont start charging if we dont have enough ammo to start
										{
											m_fPrimaryShotTimer = 1.0f / ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_PRIMARY ] ) )->GetRateOfFire();
											RequestUsing( UT_CHARGED_SHOT_PRIMARY );
											bFiredPrimary = true;
										}
									}
								}
							}
						}
						else if ( pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::PRIMARY_ATTACK_TAP ) > 0.0f /*&& ( !bNoPrimaryAmmo )*/ )
						{
							if ( m_fPrimaryShotTimer <= 0.0f )
							{
								m_fPrimaryShotTimer = 1.0f / ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_PRIMARY ] ) )->GetRateOfFire();
								RequestUsing( UT_N_BULLET_BURST_PRIMARY );
								bFiredPrimary = true;
							}
						}
					}

					if ( !bFiredPrimary && this->GetNumSubTypes() == 2 )
					{
						if ( ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_SECONDARY ] ) )->IsFullAuto() )
						{
							if ( pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::SECONDARY_ATTACK_HELD ) > 0.0f/* && ( !bNoSecondaryAmmo )*/ )
							{
								if ( m_fSecondaryShotTimer <= 0.0f )
								{
									m_fSecondaryShotTimer = 1.0f / ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_SECONDARY ] ) )->GetRateOfFire();
									RequestUsing( UT_FULL_AUTO_SECONDARY );
								}
							}
						}
						else if ( ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_SECONDARY ] ) )->IsChargedShot() )
						{
							if ( pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::SECONDARY_ATTACK_HELD ) > 0.0f && ( !bNoSecondaryAmmo ) )
							{
								if ( m_fSecondaryShotTimer <= 0.0f )
								{
									m_fSecondaryShotTimer = 1.0f / ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_SECONDARY ] ) )->GetRateOfFire();
									RequestUsing( UT_CHARGED_SHOT_SECONDARY );
								}
							}
						}
						else if ( pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::SECONDARY_ATTACK_TAP ) > 0.0f /*&& ( !bNoSecondaryAmmo )*/ )
						{
							if ( m_fSecondaryShotTimer <= 0.0f )
							{
								m_fSecondaryShotTimer = 1.0f / ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_SECONDARY ] ) )->GetRateOfFire();
								RequestUsing( UT_N_BULLET_BURST_SECONDARY );
							}
						}
					}
				}
			}
		}
	}
}

void AVP_InventoryWeapon_RangedWeapon::PlayActivationWeaponAnim()
{
	const AVP_ClientEntity_Actor* pxClient = GetParentSystem().GetParentClient();
	if ( pxClient )
	{
		const AVP_Actor_AnimStateData* pxStateData = pxClient->GetAnimStateDataForWeapon( GetBlueprintHash() );
		if ( pxStateData )
		{
			const Asura_Hash_ID uAnimHash = pxStateData->GetWeaponInWeaponAnimHash();
			if ( uAnimHash != ASURA_HASH_ID_UNSET )
			{
				PlayWeaponAnim( uAnimHash );
			}
		}
	}
}

void AVP_InventoryWeapon_RangedWeapon::PlayDeactivationWeaponAnim()
{
	const AVP_ClientEntity_Actor* pxClient = GetParentSystem().GetParentClient();
	if ( pxClient )
	{
		const AVP_Actor_AnimStateData* pxStateData = pxClient->GetAnimStateDataForWeapon( GetBlueprintHash() );
		if ( pxStateData )
		{
			const Asura_Hash_ID uAnimHash = pxStateData->GetWeaponOutWeaponAnimHash();
			if ( uAnimHash != ASURA_HASH_ID_UNSET )
			{
				PlayWeaponAnim( uAnimHash );
			}
		}
	}
}

bool AVP_InventoryWeapon_RangedWeapon::IsPlayerIsStillFiring( const AVP_ClientEntity_Actor& xActor, int iSubType ) const
{
	if ( GetParentSystem().IsMaster() )
	{
		if(xActor.GetMovementSystem() && xActor.GetMovementSystem()->IsMoveInProgress())
		{
			return false; // Stop firing in a move
		}
	}

	const AVP_Actor_InputHandler* pxInputHandler = xActor.GetInputHandler();
	if ( pxInputHandler != NULL && ( GetTimeInState() > 0.0f ) )
	{
		switch ( iSubType )
		{
			case ST_PRIMARY:
			{
				if(pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::FAST ) == 0.0f)//Not running
				{
					if(pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::PRIMARY_ATTACK_HELD ) > 0.0f) // Button not fully released
					{
						if(pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::PRIMARY_ATTACK_HELD ) > 0.95f) // Pretty much fully pressed...
						{
							m_fButtonHeldTime = Asura_Timers::GetServerAbsoluteTime();
						}
						return true;
					}
				}
				return false;
			}

			case ST_SECONDARY:
			{
				return pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::SECONDARY_ATTACK_HELD ) > 0.0f && pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::FAST ) == 0.0f;
			}

			default:
			{

			}
		}
	}
	//else we're either not a player or we've not been in the state for long enough.
	return true;
}

float AVP_InventoryWeapon_RangedWeapon::GetPrimaryMinSpread() const
{
	Asura_Assert( m_uNumSubTypes > 0 && m_ppxSubTypes[0] != NULL, "No subtype" );
	if ( m_uNumSubTypes > 0 && m_ppxSubTypes[0] != NULL )
	{
		return static_cast< const AVP_RangedWeapon_SubType* >( m_ppxSubTypes[0] )->GetMinSpread();
	}
	else
	{
		return 5.0f;
	}
}

float AVP_InventoryWeapon_RangedWeapon::GetPrimaryMaxSpread() const
{
	Asura_Assert( m_uNumSubTypes > 0 && m_ppxSubTypes[0] != NULL, "No subtype" );
	if ( m_uNumSubTypes > 0 && m_ppxSubTypes[0] != NULL )
	{
		return static_cast< const AVP_RangedWeapon_SubType* >( m_ppxSubTypes[0] )->GetMaxSpread();
	}
	else 
	{
		return 5.0f;
	}
}

void AVP_InventoryWeapon_RangedWeapon::PlayWeaponAnim( Asura_Hash_ID uHash )
{
	if ( m_pxClientEffectsHandler )
	{
		m_pxClientEffectsHandler->PlayWeaponAnim( uHash );
	}
}

bool AVP_InventoryWeapon_RangedWeapon::IsDoingDeadMansClick(const AVP_RangedWeapon_SubType* pxSubType) const
{
	return(pxSubType->GetClickCount() < pxSubType->GetMaxClicks());
}

void AVP_InventoryWeapon_RangedWeapon::CheckForAutomaticReload(void)
{
	const AVP_Inventory_System&	xParentSystem	= GetParentSystem();

	if(xParentSystem.IsMaster())
	{
		const float	fTimeInState	= 1.0f;	//GetTimeInState();

		if(fTimeInState >= 0.100f)
		{
			if(m_bSubtypesUseSameAmmo)
			{
				const AVP_RangedWeapon_SubType*	pxSubType	= static_cast<const AVP_RangedWeapon_SubType*>(GetSubType(ST_PRIMARY));

				if( pxSubType && pxSubType->ReadyToAutoReloadNow() )	
				{
					if(GetParentSystem().GetAmmoStore())
					{
						bool bPrimaryAmmoAvailable = GetParentSystem().GetAmmoStore()->GetAmmoQuantity(pxSubType->GetAmmoType())>=1.0f;
						if(bPrimaryAmmoAvailable)
						{
							DoReloadMuzzleSmoke( ST_PRIMARY );
							RequestUsing(UT_RELOAD_ALL);
							return;
						}
					}
				}
			}
			else
			{
				for(u_int u = 0; u < m_uNumSubTypes; u++)
				{
					const AVP_RangedWeapon_SubType*	pxSubType	= static_cast<const AVP_RangedWeapon_SubType*>(GetSubType(u));

					if( pxSubType && pxSubType->ReadyToAutoReloadNow() )	
					{
						if(GetParentSystem().GetAmmoStore())
						{
							bool bPrimaryAmmoAvailable = GetParentSystem().GetAmmoStore()->GetAmmoQuantity(pxSubType->GetAmmoType())>=1.0f;
							if(bPrimaryAmmoAvailable)
							{
								DoReloadMuzzleSmoke(u);
								RequestUsing(UT_RELOAD_ALL);
								return;
							}
						}
					}
				}
			}
		}
	}
}

void AVP_InventoryWeapon_RangedWeapon::InitialiseFromBlueprint()
{
	PARENT::InitialiseFromBlueprint();

	const Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, GetBlueprintHash() );
	if ( pxBlueprint )
	{
		Asura_Value xValue;

		Asura_Hash_ID uPrimaryHashValue = ASURA_HASH_ID_UNSET;
		if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_SUBTYPE_PRIMARY, xValue ) )
		{
			uPrimaryHashValue = xValue.GetHashIDValue();
		}
		Asura_Hash_ID uSecondaryHashValue = ASURA_HASH_ID_UNSET;
		if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_SUBTYPE_SECONDARY, xValue ) )
		{
			uSecondaryHashValue = xValue.GetHashIDValue();
		}

		if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_SPREAD_COOL_DOWN, xValue ) )
		{
			m_fSpreadCoolDown = xValue.GetFloatValue();
		}

		if(pxBlueprint->GetParameterValueByHashID(0x3600e569, xValue))	//Asura_Hash_ID("CanReload"), xValue))
		{
			m_bCanReload = xValue.GetBoolValue();
		}

		if ( uSecondaryHashValue != ASURA_HASH_ID_UNSET )
		{
			InitialiseSubTypes(2);
			m_ppxSubTypes[ST_PRIMARY]	= new AVP_RangedWeapon_SubType( AVP_INVENTORYITEMSUBTYPE_RANGED_WEAPON, uPrimaryHashValue );
			if ( m_ppxSubTypes[ST_PRIMARY] )
			{
				m_ppxSubTypes[ST_PRIMARY]->InitialiseFromBlueprint();
			}
			m_ppxSubTypes[ST_SECONDARY]	= new AVP_RangedWeapon_SubType( AVP_INVENTORYITEMSUBTYPE_RANGED_WEAPON, uSecondaryHashValue );
			if ( m_ppxSubTypes[ST_SECONDARY] )
			{
				m_ppxSubTypes[ST_SECONDARY]->InitialiseFromBlueprint();
			}
		}
		else if ( uPrimaryHashValue != ASURA_HASH_ID_UNSET )
		{
			InitialiseSubTypes(1);
			m_ppxSubTypes[ST_PRIMARY] = new AVP_RangedWeapon_SubType( AVP_INVENTORYITEMSUBTYPE_RANGED_WEAPON, uPrimaryHashValue );
			if ( m_ppxSubTypes[ST_PRIMARY] )
			{
				m_ppxSubTypes[ST_PRIMARY]->InitialiseFromBlueprint();
			}
		}

		if(GetNumSubTypes() > 1)
		{
			if(GetSubType(ST_PRIMARY)->GetAmmoType() == GetSubType(ST_SECONDARY)->GetAmmoType())
			{
				m_bSubtypesUseSameAmmo = true;
			}
		}

		AVP_Actor_DynamicStat* pxDynamicAmmo = NULL;
		if ( GetParentSystem().IsClientSide() )
		{
			pxDynamicAmmo = GetParentSystem().GetParentClient()->GetDynamicEnergy();
			if ( !GetParentSystem().IsMaster() )
			{
				SetServerItemToUseDynamicAmmo( pxDynamicAmmo );
			}
		}

		for ( u_int u = 0; u < GetNumSubTypes(); ++u )
		{
			AVP_InventoryItem_SubType* pxSubType = GetSubType( u );
			if ( pxSubType && 
				( pxSubType->GetType() == AVP_INVENTORYITEMSUBTYPE_DYNAMIC || pxSubType->GetType() == AVP_INVENTORYITEMSUBTYPE_RANGED_WEAPON ) )
			{
				AVP_InventoryItem_DynamicSubType* pxDynamicSubType = static_cast< AVP_InventoryItem_DynamicSubType* >( pxSubType );
				pxDynamicSubType->SetDynamicAmmo( pxDynamicAmmo );
			}
		}
	}
	else
	{
		Asura_Assertf( false, ( "AVP_InventoryWeapon_RangedWeapon::InitialiseFromBlueprint - cannot find blueprint %u to initialise from.", GetBlueprintHash() ) );
	}
}

void AVP_InventoryWeapon_RangedWeapon::PostLoadInitialise()
{
	PARENT::PostLoadInitialise();

	const AVP_Inventory_System& xParentSystem = GetParentSystem();
	AVP_Actor_DynamicStat* pxDynamicAmmo = NULL;
	if ( xParentSystem.IsClientSide() )
	{
		pxDynamicAmmo = xParentSystem.GetParentClient()->GetDynamicEnergy();
		if ( !xParentSystem.IsMaster() )
		{
			SetServerItemToUseDynamicAmmo( pxDynamicAmmo );
		}
	}

	for ( u_int u = 0; u < GetNumSubTypes(); ++u )
	{
		AVP_InventoryItem_SubType* pxSubType = GetSubType( u );
		if ( pxSubType && 
			( pxSubType->GetType() == AVP_INVENTORYITEMSUBTYPE_DYNAMIC || pxSubType->GetType() == AVP_INVENTORYITEMSUBTYPE_RANGED_WEAPON ) )
		{
			AVP_InventoryItem_DynamicSubType* pxDynamicSubType = static_cast< AVP_InventoryItem_DynamicSubType* >( pxSubType );
			pxDynamicSubType->SetDynamicAmmo( pxDynamicAmmo );
		}
	}

	if(GetNumSubTypes() > 1)
	{
		if(GetSubType(ST_PRIMARY)->GetAmmoType() == GetSubType(ST_SECONDARY)->GetAmmoType())
		{
			m_bSubtypesUseSameAmmo = true;
		}
	}
}

void AVP_InventoryWeapon_RangedWeapon::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	u_int uVersion;
	xStream >> uVersion;

	if ( uVersion >= 2 )
	{
		PARENT::ReadFromChunkStream( xStream );
	}

	if ( uVersion == uCURRENT_CHUNK_VERSION )
	{
		xStream >> m_fMaxBurstBulletsToFire;
	}
	else if ( uVersion == 2 )
	{
	}
	else if ( uVersion == 1 )
	{
		u_int uNumSubTypes;
		xStream >> uNumSubTypes;

		for( u_int u = 0; u < GetNumSubTypes(); ++u )
		{
			bool bUsesDynamicAmmo;
			xStream >> bUsesDynamicAmmo;

			if( !bUsesDynamicAmmo )
			{
				float fQuantity;
				xStream >> fQuantity;

				AVP_RangedWeapon_SubType* pxSubType = static_cast<AVP_RangedWeapon_SubType*>( GetSubType( u ) );
				if( pxSubType )
				{
					pxSubType->SetCurrentQuantity( fQuantity );
				}
			}
		}
	}
	else if ( uVersion == 0 )
	{
		u_int uNumSubTypes;
		xStream >> uNumSubTypes;

		for( u_int u = 0; u < GetNumSubTypes(); ++u )
		{
			float fQuantity;
			xStream >> fQuantity;

			AVP_RangedWeapon_SubType* pxSubType = static_cast<AVP_RangedWeapon_SubType*>( GetSubType( u ) );
			if( pxSubType )
			{
				pxSubType->SetCurrentQuantity( fQuantity );
			}
		}
	}
	else
	{
		Asura_Assertf( false, ( "AVP_InventoryWeapon_RangedWeapon::ReadFromChunkStream - unhandled chunk version (%u)", uVersion ) );
	}
}

void AVP_InventoryWeapon_RangedWeapon::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	xStream << uCURRENT_CHUNK_VERSION;

	PARENT::WriteToChunkStream( xStream );

	xStream << m_fMaxBurstBulletsToFire;
}

AVP_InventoryItem_SubType* AVP_InventoryWeapon_RangedWeapon::CreateSubType( const int iSubType, const Asura_Hash_ID uBlueprintHashID )
{
	if ( iSubType == AVP_INVENTORYITEMSUBTYPE_RANGED_WEAPON )
	{
		return new AVP_RangedWeapon_SubType( iSubType, uBlueprintHashID );
	}
	else
	{
		return PARENT::CreateSubType( iSubType, uBlueprintHashID );
	}
}

void AVP_InventoryWeapon_RangedWeapon::GetStaticDataFromBlueprint()
{
	const Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, GetBlueprintHash() );
	if ( pxBlueprint )
	{
		// We don't know if we're a battery at this point, so get these values anyway.
		Asura_Value xValue;
		if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_SPREAD_COOL_DOWN, xValue ) )
		{
			m_fSpreadCoolDown = xValue.GetFloatValue();
		}

		if ( pxBlueprint->GetParameterValueByHashID( 0x3600e569, xValue ) ) //Asura_Hash_ID("CanReload"), xValue))
		{
			m_bCanReload = xValue.GetBoolValue();
		}
	}
	else
	{
		Asura_Assertf( false, ( "AVP_InventoryWeapon_RangedWeapon::GetStaticDataFromBlueprint - cannot find blueprint %u to initialise from.", GetBlueprintHash() ) );
	}
}

void AVP_InventoryWeapon_RangedWeapon::MoveAmmoToCurrentSubtype(void)
{
	// If only 1 subtype, something really bad has happened, probably in blueprints
	if(m_uNumSubTypes < 2)
	{
		Asura_Assert(false, "Shotgun trying to move ammo from one subtype to another on a weapon with %d subtypes. Are the blueprints correct?");
	}
	else
	{
		u_int uNumSubtypes = GetNumSubTypes();

		if(uNumSubtypes > 0)
		{
			AVP_RangedWeapon_SubType* pxCurrentSubtype = static_cast<AVP_RangedWeapon_SubType*>(GetSubTypeFromUsageType(GetCurrentUsageType()));
			if(pxCurrentSubtype)
			{
				float fTotalAmmo = 0.0f;
				for(u_int u=0;u<uNumSubtypes;++u)
				{
					AVP_RangedWeapon_SubType* pxSubtype = static_cast<AVP_RangedWeapon_SubType*>(GetSubType(u));
					if(pxSubtype)
					{
						fTotalAmmo += pxSubtype->GetCurrentQuantity();
						pxSubtype->SetCurrentQuantity(0);
					}
				}

				pxCurrentSubtype->SetCurrentQuantity(fTotalAmmo);
			}	
		}	
	}
}

void AVP_InventoryWeapon_RangedWeapon::MoveAmmoToPrimarySubtype(void)
{
	// If only 1 subtype, something really bad has happened, probably in blueprints
	if(m_uNumSubTypes < 2)
	{
		Asura_Assert(false, "Shotgun trying to move ammo from one subtype to another on a weapon with %d subtypes. Are the blueprints correct?");
	}
	else
	{
		u_int uNumSubtypes = GetNumSubTypes();
		
		if(uNumSubtypes>0)
		{
			float fTotalAmmo = 0.0f;
			for(u_int u=0;u<uNumSubtypes;++u)
			{
				AVP_RangedWeapon_SubType* pxSubtype = static_cast<AVP_RangedWeapon_SubType*>(GetSubType(u));
				if(pxSubtype)
				{
					fTotalAmmo += pxSubtype->GetCurrentQuantity();
					pxSubtype->SetCurrentQuantity(0);
				}
			}

			GetSubType(ST_PRIMARY)->SetCurrentQuantity(fTotalAmmo);
		}
	}
}

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////
