
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_RangedWeapon_ClientEffectsHandler.cpp
// Author : Will Plano
// Date   : 11/12/2007
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

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

//Asura
#include "Animation/Asura_Animation.h"
#include "Animation/Asura_AnimationAttachment.h"
#include "Animation/Asura_AnimationController_Animation.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "Core/Asura_TiledTextureManagement.h"
#include "Projectors/Asura_Projector_System.h"
#include "Sound/Asura_Sound_Event.h"
#include "Sound/Asura_Sound_Event_Storage.h"
#include "Sound/Asura_Sound_Event_System.h"
#include "SpecialFX/PFX/Asura_PFX_Entity_Effect.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"

//AVP
#include "Actor/AVP_Actor_AnimationHandler.h"
#include "Actor/AVP_Actor_AnimStateData.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Camera/AVP_CameraShake_System.h"
#include "Core/Axon_Sound_Registry.h"
#include "Core/Axon_System.h"
#include "Core/AVP_Game_Core.h"
#include "Core/AVP_Maths.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h"
#include "SpecialFX/AVP_Laser.h"

DEBUGVAR float dbg_fWeaponAnimTweenTime = 0.05f;
DEBUGVAR float dbg_fReloadTweenTime = 0.3f;

static const Asura_Hash_ID g_uSubTypeParamHash[ AVP_InventoryWeapon_RangedWeapon::NUM_SUB_TYPES ] = 
{
	uAVP_BLUEPRINT_PARAM_SUBTYPE_PRIMARY,
	uAVP_BLUEPRINT_PARAM_SUBTYPE_SECONDARY
};

Asura_TemplateHashTree< AVP_RangedWeapon_ClientEffectsHandler::AVP_RangedWeapon_ClientData >	AVP_RangedWeapon_ClientEffectsHandler::s_xClientDataTree;

AVP_RangedWeapon_ClientEffectsHandler::AVP_RangedWeapon_ClientData::AVP_RangedWeapon_ClientData()
: m_iLaserNumDots( 0 )
, m_fLaserBeamRadius( 0.1f )
, m_fLaserRadius( 0.1f )
, m_fLaserBeamSharpness( 0.1f )
, m_fLaserBeamSpread( 0.0f )
, m_fLaserMaxLength( 100.0f )
, m_fLaserFalloffLength( 50.0f )
, m_fLaserFallOffStartAngle(15.0f * Asura_Maths::DegToRadConstant)
, m_fLaserFallOffEndAngle(30.0f * Asura_Maths::DegToRadConstant)
, m_fLaserFallOffStartDistance(0.5f)
, m_fLaserFallOffEndDistance(1.0f)
, m_fLaserFallOffAngleEndIntensity(0.1f)
, m_fLaserBeamVisibilityDistance(40.0f)
, m_uLaserMarkerPoint( ASURA_HASH_ID_UNSET )
, m_uLaserFalloffTextureHash( ASURA_HASH_ID_UNSET )
, m_uLaserProjectedTextureHash( ASURA_HASH_ID_UNSET )
, m_uLaserActivationSoundHash( ASURA_HASH_ID_UNSET )
, m_uLaserDeactivationSoundHash( ASURA_HASH_ID_UNSET )
, m_uLaserLoopingSoundHash( ASURA_HASH_ID_UNSET )
, m_uBulletBySoundHash( ASURA_HASH_ID_UNSET )
, m_uSkinHash( ASURA_HASH_ID_UNSET )
, m_uPermenantAnimHash( ASURA_HASH_ID_UNSET )
, m_uAmmoCounterMarker1Hash(ASURA_HASH_ID_UNSET)
, m_uAmmoCounterMarker2Hash(ASURA_HASH_ID_UNSET)
, m_uAmmoCounterAttachment(ASURA_HASH_ID_UNSET)
{
	m_bOriginPhotonEnabled = false;
	m_fOriginPhotonRadius = 1.0f;
	m_fOriginPhotonFalloffScale = 1.0f;
	m_fOriginPhotonFalloffPower = 1.0f;
	m_bOriginPhotonEnableDistanceFade = false;
	m_fOriginPhotonDistanceFadeStart = 0.0f;
	m_fOriginPhotonDistanceFadeDist = 0.0f;

	m_bContactPhotonEnabled = false;
	m_fContactPhotonRadius = 1.0f;
	m_fContactPhotonFalloffScale = 1.0f;
	m_fContactPhotonFalloffPower = 1.0f;
	m_bContactPhotonEnableDistanceFade = false;
	m_fContactPhotonDistanceFadeStart = 0.0f;
	m_fContactPhotonDistanceFadeDist = 0.0f;
	m_fContactPhotonBackOffDistance = 0.5f;

	m_xOriginPhotonColour = Asura_RGBA(1.0f, 0.f, 0.1f, 1.0f);
	m_xContactPhotonColour =  Asura_RGBA(1.0f, 0.f, 0.1f, 1.0f);
}

bool AVP_RangedWeapon_ClientEffectsHandler::AVP_RangedWeapon_ClientData::InitialiseFromBlueprint( const Asura_Hash_ID uBlueprintHash )
{
	const Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, uBlueprintHash );
	if ( pxBlueprint )
	{
		Asura_Value xValue;
		if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_SKIN, xValue ) )
		{
			m_uSkinHash = xValue.GetHashIDValue();
		}

		if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_WEAPON_PERMENANT_ANIM, xValue ) )
		{
			m_uPermenantAnimHash = xValue.GetHashIDValue();
		}

		if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_WEAPON_BULLETBY_SOUND, xValue ) )
		{
			m_uBulletBySoundHash = xValue.GetHashIDValue();
		}

		InitialiseLaserFromBlueprint( *pxBlueprint );

		//------- Ammo Counter attachments ------

		if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_AMMOCOUNTER_MARKER1, xValue ) )
		{
			m_uAmmoCounterMarker1Hash = xValue.GetHashIDValue();
		}

		if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_AMMOCOUNTER_MARKER2, xValue ) )
		{
			m_uAmmoCounterMarker2Hash = xValue.GetHashIDValue();
		}

		if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_AMMOCOUNTER_ATTACHMENT, xValue ) )
		{
			m_uAmmoCounterAttachment = xValue.GetHashIDValue();
		}


		for ( u_int uItr = 0; uItr < AVP_InventoryWeapon_RangedWeapon::NUM_SUB_TYPES; ++uItr )
		{
			if ( pxBlueprint->GetParameterValueByHashID( g_uSubTypeParamHash[ uItr ], xValue ) )
			{
				const Asura_Hash_ID uSubTypeHashID = xValue.GetHashIDValue();
				if ( uSubTypeHashID != ASURA_HASH_ID_UNSET )
				{
					m_axSubTypeData[ uItr ].InitialiseFromBlueprint( uSubTypeHashID );
				}
			}
		}
		return true;
	}
	else
	{
		return false;
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::AVP_RangedWeapon_ClientData::InitialiseLaserFromBlueprint( const Asura_Blueprint& xBlueprint )
{
	Asura_Value	xValue;

	if(xBlueprint.GetParameterValueByHashID(0x06FF70AD, xValue))	//Asura_GetHashID("Laser.NumDots"), xValue))
	{
		m_iLaserNumDots	= xValue.GetIntValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xA0CBF2F3, xValue))	//Asura_GetHashID("Laser.MarkerPoint"), xValue))
	{
		m_uLaserMarkerPoint = xValue.GetHashIDValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xA505E90E, xValue))	//Asura_GetHashID("Laser.Beam.Radius"), xValue))
	{
		m_fLaserBeamRadius = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x730538D5, xValue))	//Asura_GetHashID("Laser.Beam.GroupRadius"), xValue))
	{
		m_fLaserRadius = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xC69A3745, xValue))	//Asura_GetHashID("Laser.Beam.Sharpness"), xValue))
	{
		m_fLaserBeamSharpness = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xA7946DEF, xValue))	//Asura_GetHashID("Laser.Beam.Spread", xValue))
	{
		m_fLaserBeamSpread = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x2BAEA74E, xValue))	//Asura_GetHashID("Laser.Beam.MaxLength"), xValue))
	{
		m_fLaserMaxLength = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xA31FFBFE, xValue))	//Asura_GetHashID("Laser.Beam.FalloffLength"), xValue))
	{
		m_fLaserFalloffLength = xValue.GetFloatValue();
	}

	if(xBlueprint.GetParameterValueByHashID(0x32FBB610, xValue))	//Asura_GetHashID("Laser.Beam.Attenuation.Cylinder.InnerRadius"), xValue))
	{
		m_fLaserFallOffStartDistance = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xD7F49875, xValue))	//Asura_GetHashID("Laser.Beam.Attenuation.Cylinder.OuterRadius"), xValue))
	{
		m_fLaserFallOffEndDistance = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x8904BE28, xValue))	//Asura_GetHashID("Laser.Beam.Attenuation.Cone.InnerAngle"), xValue))
	{
		m_fLaserFallOffStartAngle = xValue.GetFloatValue() * Asura_Maths::DegToRadConstant;
	}
	if(xBlueprint.GetParameterValueByHashID(0xBFE37B23, xValue))	//Asura_GetHashID("Laser.Beam.Attenuation.Cone.OuterAngle"), xValue))
	{
		m_fLaserFallOffEndAngle = xValue.GetFloatValue() * Asura_Maths::DegToRadConstant;
	}
	if(xBlueprint.GetParameterValueByHashID(0xB209B41F, xValue))	//Asura_GetHashID("Laser.Beam.Attenuation.MinValue"), xValue))
	{
		m_fLaserFallOffAngleEndIntensity = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x7DF02DDD, xValue))	//Asura_GetHashID("Laser.Beam.MaxDistFromCamera"), xValue))
	{
		m_fLaserBeamVisibilityDistance = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xFD431C18, xValue))	//Asura_GetHashID("Laser.Sound.Activation"), xValue))
	{
		m_uLaserActivationSoundHash = xValue.GetHashIDValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x392782D9, xValue))	//Asura_GetHashID("Laser.Sound.Deactivation"), xValue))
	{
		m_uLaserDeactivationSoundHash = xValue.GetHashIDValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x3C96B2FC, xValue))	//Asura_GetHashID("Laser.Sound.Looping"), xValue))
	{
		m_uLaserLoopingSoundHash = xValue.GetHashIDValue();
	}

	if(xBlueprint.GetParameterValueByHashID(0xA7946DEF, xValue))	//Asura_GetHashID("Laser.Beam.Spread", xValue))
	{
		m_fLaserBeamSpread = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x2BAEA74E, xValue))	//Asura_GetHashID("Laser.Beam.MaxLength"), xValue))
	{
		m_fLaserMaxLength = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xA31FFBFE, xValue))	//Asura_GetHashID("Laser.Beam.FalloffLength"), xValue))
	{
		m_fLaserFalloffLength = xValue.GetFloatValue();
	}


	if(xBlueprint.GetParameterValueByHashID(0x19311C02, xValue))	//Asura_GetHashID("Laser.Texture.Projected"), xValue))
	{
		m_uLaserProjectedTextureHash	= xValue.GetHashIDValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x55B9A11E, xValue))	//Asura_GetHashID("Laser.Texture.Falloff"), xValue))
	{
		m_uLaserFalloffTextureHash	= xValue.GetHashIDValue();
	}

	if(xBlueprint.GetParameterValueByHashID(0xEF20482F, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Origin.Enabled"), xValue))
	{
		m_bOriginPhotonEnabled	= xValue.GetBoolValue();
	}

	if(xBlueprint.GetParameterValueByHashID(0xCA9B59E4, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Origin.Radius"), xValue))
	{
		m_fOriginPhotonRadius	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xEA5FBD88, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Origin.FalloffScale"), xValue))
	{
		m_fOriginPhotonFalloffScale	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xEA3B3D43, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Origin.FalloffPower"), xValue))
	{
		m_fOriginPhotonFalloffPower	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x80BBDB46, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Origin.EnableDistanceFade"), xValue))
	{
		m_bOriginPhotonEnableDistanceFade	= xValue.GetBoolValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x48019E7F, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Origin.DistanceFadeStart"), xValue))
	{
		m_fOriginPhotonDistanceFadeStart	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x3721A0B8, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Origin.DistanceFadeDistance"), xValue))
	{
		m_fOriginPhotonDistanceFadeDist	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x6D8D61A2, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Origin.Colour.R"), xValue))
	{
		m_xOriginPhotonColour.m_fR	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x6D8D6197, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Origin.Colour.G"), xValue))
	{
		m_xOriginPhotonColour.m_fG	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x6D8D6192, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Origin.Colour.B"), xValue))
	{
		m_xOriginPhotonColour.m_fB	= xValue.GetFloatValue();
	}


	if(xBlueprint.GetParameterValueByHashID(0x72A7927D, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Contact.Enabled"), xValue))
	{
		m_bContactPhotonEnabled	= xValue.GetBoolValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x52FA8DD6, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Contact.Radius"), xValue))
	{
		m_fContactPhotonRadius	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x4EF0B3FA, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Contact.FalloffScale"), xValue))
	{
		m_fContactPhotonFalloffScale	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x4ECC33B5, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Contact.FalloffPower"), xValue))
	{
		m_fContactPhotonFalloffPower	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x04C2B438, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Contact.EnableDistanceFade"), xValue))
	{
		m_bContactPhotonEnableDistanceFade	= xValue.GetBoolValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xC822DF4D, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Contact.DistanceFadeStart"), xValue))
	{
		m_fContactPhotonDistanceFadeStart	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xD4D6052A, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Contact.DistanceFadeDistance"), xValue))
	{
		m_fContactPhotonDistanceFadeDist	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xACEF84AA, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Contact.BringBackDistance"), xValue))
	{
		m_fContactPhotonBackOffDistance	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x5AEF6114, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Contact.Colour.R"), xValue))
	{
		m_xContactPhotonColour.m_fR	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x5AEF6109, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Contact.Colour.G"), xValue))
	{
		m_xContactPhotonColour.m_fG	= xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x5AEF6104, xValue))	//Asura_GetHashID("Laser.Beam.Photon.Contact.Colour.B"), xValue))
	{
		m_xContactPhotonColour.m_fB	= xValue.GetFloatValue();
	}
}

AVP_RangedWeapon_ClientEffectsHandler::AVP_RangedWeapon_ClientData::AVP_RangedWeapon_SubType_ClientData::AVP_RangedWeapon_SubType_ClientData()
: m_uMuzzleMarkerHashFirstPerson( ASURA_HASH_ID_UNSET )
, m_uMuzzleMarkerHashThirdPerson( ASURA_HASH_ID_UNSET )
, m_uChargingPFXHash( ASURA_HASH_ID_UNSET )
, m_uCameraShakeHash( ASURA_HASH_ID_UNSET )
, m_fMuzzleFlash_Hot_NumBulletsToBegin( 0.0f )
{
	for ( u_int uItr = 0; uItr < NUM_EFFECT_TYPES; ++uItr )
	{
		m_auSoundEventHash[ uItr ] = NULL;
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::AVP_RangedWeapon_ClientData::AVP_RangedWeapon_SubType_ClientData::InitialiseFromBlueprint( const Asura_Hash_ID uSubTypeBPHash )
{
	Asura_Blueprint* pxSubTypeBP = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, uSubTypeBPHash );
	if ( pxSubTypeBP )
	{
		Asura_Value xValue;

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_SOUND_FIRE_BEGIN, xValue ) )
		{
			m_auSoundEventHash[ EFFECT_FIRE_BEGIN ] = xValue.GetHashIDValue();
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_SOUND_FIRE_LOOP, xValue ) )
		{
			m_auSoundEventHash[ EFFECT_FIRE_LOOP ] = xValue.GetHashIDValue();
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_SOUND_FIRE_END, xValue ) )
		{
			m_auSoundEventHash[ EFFECT_FIRE_END ] = xValue.GetHashIDValue();
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_SOUND_CHARGING, xValue ) )
		{
			m_auSoundEventHash[ EFFECT_CHARGING ] = xValue.GetHashIDValue();
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_PFX_CHARGING, xValue ) )
		{
			m_uChargingPFXHash = xValue.GetHashIDValue();
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_CAMERASHAKE, xValue ) )
		{
			m_uCameraShakeHash = xValue.GetHashIDValue();
		}

		//------- Muzzle Flash ------

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_MARKERPOINT_MUZZLE_HUD, xValue ) )
		{
			m_uMuzzleMarkerHashFirstPerson = xValue.GetHashIDValue();
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_MARKERPOINT_MUZZLE_THIRD, xValue ) )
		{
			m_uMuzzleMarkerHashThirdPerson = xValue.GetHashIDValue();
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_MUZZLEFLASH_FIRE_1P, xValue ) )
		{
			m_xMuzzleFlash_Fire1P.InitialiseFromBlueprint( xValue.GetHashIDValue() );
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_MUZZLEFLASH_FIRE_3P, xValue ) )
		{
			m_xMuzzleFlash_Fire3P.InitialiseFromBlueprint( xValue.GetHashIDValue() );
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_MUZZLEFLASH_HOT_1P, xValue ) )
		{
			m_xMuzzleFlash_Hot1P.InitialiseFromBlueprint( xValue.GetHashIDValue() );
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_MUZZLEFLASH_HOT_3P, xValue ) )
		{
			m_xMuzzleFlash_Hot3P.InitialiseFromBlueprint( xValue.GetHashIDValue() );
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_MUZZLEFLASH_RELOAD_1P, xValue ) )
		{
			m_xMuzzleFlash_Reload1P.InitialiseFromBlueprint( xValue.GetHashIDValue() );
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_MUZZLEFLASH_RELOAD_3P, xValue ) )
		{
			m_xMuzzleFlash_Reload3P.InitialiseFromBlueprint( xValue.GetHashIDValue() );
		}

		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_MUZZLEFLASH_HOT_NBTP, xValue ) )
		{
			m_fMuzzleFlash_Hot_NumBulletsToBegin = xValue.GetFloatValue();
		}

		//------- End of Muzzle Flash ------


		Asura_Hash_ID uBeamTracerBPHash = ASURA_HASH_ID_UNSET;
		if ( pxSubTypeBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_BEAMTYPE, xValue ) )
		{
			uBeamTracerBPHash = xValue.GetHashIDValue();
		}

		if ( uBeamTracerBPHash != ASURA_HASH_ID_UNSET )
		{
			InitialiseBeamTemplateFromBlueprint( uBeamTracerBPHash );
		}
		else
		{
			m_xTracerTemplate.uTileHashID = ASURA_HASH_ID_UNSET; // Use this to store the fact that we don't want a beam with this subtype
		}

	}
	else
	{
		Asura_Assert( false, "Unable to get subtype blueprint" );
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::AVP_RangedWeapon_ClientData::AVP_RangedWeapon_SubType_ClientData::InitialiseBeamTemplateFromBlueprint( const Asura_Hash_ID pxTracerBPHash )
{
	Asura_Blueprint* pxTracerBP = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, pxTracerBPHash );
	Asura_Value xValue;
	if ( pxTracerBP )
	{
		if ( pxTracerBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_DECAYRATE, xValue ) )
		{
			m_xTracerTemplate.fDecayRate = xValue.GetFloatValue();
		}
		else
		{
			Asura_Assert( false, "Unable to get beam template parameter blueprint" );
		}

		if ( pxTracerBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_ENDFARCOLOUR, xValue ) )
		{
			SetBeamColourFromARGB( m_xTracerTemplate.xEndHeadColour, static_cast< u_int >( xValue.GetIntValue() ) );
		}	
#ifdef ASURA_ASSERT
		else
		{
			Asura_Assert( false, "Unable to get beam template parameter blueprint" );
		}
#endif

		if ( pxTracerBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_ENDNEARCOLOUR, xValue ) )
		{
			SetBeamColourFromARGB( m_xTracerTemplate.xEndTailColour, static_cast< u_int >( xValue.GetIntValue() ) );
		}
#ifdef ASURA_ASSERT
		else
		{
			Asura_Assert( false, "Unable to get beam template parameter blueprint" );
		}
#endif

		if ( pxTracerBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_ENDRADIUS, xValue ) )
		{
			m_xTracerTemplate.fEndRadius = xValue.GetFloatValue();
		}
#ifdef ASURA_ASSERT
		else
		{
			Asura_Assert( false, "Unable to get beam template parameter blueprint" );
		}
#endif

		if ( pxTracerBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_FARSPEED, xValue ) )
		{
			m_xTracerTemplate.fHeadSpeed = xValue.GetFloatValue();
		}
#ifdef ASURA_ASSERT
		else
		{
			Asura_Assert( false, "Unable to get beam template parameter blueprint" );
		}
#endif

		if ( pxTracerBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_INITFARCOLOUR, xValue ) )
		{
			SetBeamColourFromARGB( m_xTracerTemplate.xInitialHeadColour, static_cast< u_int >( xValue.GetIntValue() ) );
		}
#ifdef ASURA_ASSERT
		else
		{
			Asura_Assert( false, "Unable to get beam template parameter blueprint" );
		}
#endif

		if ( pxTracerBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_INITNEARCOLOUR, xValue ) )
		{
			SetBeamColourFromARGB( m_xTracerTemplate.xInitialTailColour, static_cast< u_int >( xValue.GetIntValue() ) );
		}
#ifdef ASURA_ASSERT
		else
		{
			Asura_Assert( false, "Unable to get beam template parameter blueprint" );
		}
#endif

		if ( pxTracerBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_INITRADIUS, xValue ) )
		{
			m_xTracerTemplate.fInitialRadius = xValue.GetFloatValue();
		}
#ifdef ASURA_ASSERT
		else
		{
			Asura_Assert( false, "Unable to get beam template parameter blueprint" );
		}
#endif

		if ( pxTracerBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_NEARSPEED, xValue ) )
		{
			m_xTracerTemplate.fTailSpeed = xValue.GetFloatValue();
		}
#ifdef ASURA_ASSERT
		else
		{
			Asura_Assert( false, "Unable to get beam template parameter blueprint" );
		}
#endif

		if ( pxTracerBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_TEXTURETILE, xValue ) )
		{
			m_xTracerTemplate.uTileHashID = xValue.GetHashIDValue();
		}
#ifdef ASURA_ASSERT
		else
		{
			m_xTracerTemplate.uTileHashID = ASURA_HASH_ID_UNSET;
			Asura_Assert( false, "Unable to get beam template parameter blueprint" );
		}
#endif

		if ( pxTracerBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_HACKEDALPHA, xValue ) )
		{
			float fAlpha = xValue.GetFloatValue();
			m_xTracerTemplate.xInitialTailColour.m_afChannels[3] = fAlpha;
			m_xTracerTemplate.xInitialHeadColour.m_afChannels[3] = fAlpha;
			m_xTracerTemplate.xEndTailColour.m_afChannels[3] = fAlpha;
			m_xTracerTemplate.xEndHeadColour.m_afChannels[3] = fAlpha;
		}
#ifdef ASURA_ASSERT
		else
		{
			Asura_Assert( false, "Unable to get beam template parameter blueprint" );
		}
#endif
		m_xTracerTemplate.iTileHandle = ASURA_INVALID_HANDLE; // this is set up later
	}
	else
	{
		Asura_Assert( false, "unable to get beam BP" );
		m_xTracerTemplate.uTileHashID = ASURA_HASH_ID_UNSET;
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::AVP_RangedWeapon_ClientData::AVP_RangedWeapon_SubType_ClientData::SetBeamColourFromARGB( Asura_Beam_Colour& xColour, const u_int uARGB )
{
	xColour.SetColours( ( ( uARGB & 0x00FF0000 ) >> 16 ) / 255.0f, ( ( uARGB & 0x0000FF00 ) >> 8 ) / 255.0f, ( uARGB & 0x000000FF ) / 255.0f, ( ( uARGB & 0xFF000000 ) >> 24 ) / 255.0f );
}

AVP_RangedWeapon_ClientEffectsHandler::AVP_RangedWeapon_ClientEffectsHandler( const Asura_Hash_ID uHash, const Asura_Guid uOwnerGuid )
: m_uOwnerGuid( uOwnerGuid )
, m_pxAnimation( NULL )
, m_pxLaserAttachment( NULL )
, m_uTracerCount( 0 )
, m_iLaserSoundHandle( ASURA_INVALID_HANDLE )
, m_uActorAnimControllerHash( ASURA_HASH_ID_UNSET )
, m_uWeaponAnimControllerHash( ASURA_HASH_ID_UNSET )
, m_uCurrentActorAnimHash( ASURA_HASH_ID_UNSET )
, m_uCurrentWeaponAnimHash( ASURA_HASH_ID_UNSET )
, m_uRestAnimHash( ASURA_HASH_ID_UNSET )
, m_uRestAnimControllerHash( ASURA_HASH_ID_UNSET )
{
	m_pxClientData = s_xClientDataTree.GetDataByHash( uHash, false );
	if ( m_pxClientData == NULL )
	{
		m_pxClientData = s_xClientDataTree.AddNode( uHash );
		Asura_Assert( m_pxClientData != NULL, "Unable to add new projectile data to tree" );
		if ( m_pxClientData != NULL )
		{
			if ( !m_pxClientData->InitialiseFromBlueprint( uHash ) )
			{
				Asura_Assert( false, "Unable to initilise client data" );
				s_xClientDataTree.DeleteNodeByHash( uHash );
				m_pxClientData = NULL;
			}
		}
	}

	if ( m_pxClientData->m_iLaserNumDots > 0 )
	{
		m_pxLaserAttachment = new AVP_AnimationAttachment_Laser( new AVP_Laser(), true );
		InitiliaseLaserFromClientData( m_pxLaserAttachment->GetLaser(), m_pxClientData, uOwnerGuid );
	}
	m_pxAmmoAttachments[0] = NULL;
	m_pxAmmoAttachments[1] = NULL;
	if ( m_pxClientData->m_uAmmoCounterAttachment != ASURA_HASH_ID_UNSET )
	{
		if (m_pxClientData->m_uAmmoCounterMarker1Hash != ASURA_HASH_ID_UNSET)
		{
			m_pxAmmoAttachments[0] = new Asura_AnimationAttachment_Animation(new Asura_Animation(m_pxClientData->m_uAmmoCounterAttachment), false);
			m_pxAmmoAttachments[0]->SetDeleteAnimOnUnlink(true);

		}
		if (m_pxClientData->m_uAmmoCounterMarker2Hash != ASURA_HASH_ID_UNSET)
		{
			m_pxAmmoAttachments[1] = new Asura_AnimationAttachment_Animation(new Asura_Animation(m_pxClientData->m_uAmmoCounterAttachment), false);
			m_pxAmmoAttachments[1]->SetDeleteAnimOnUnlink(true);
		}
	}

	for ( u_int uSTItr = 0; uSTItr < AVP_InventoryWeapon_RangedWeapon::NUM_SUB_TYPES; ++uSTItr )
	{
		AVP_InventoryWeapon_RangedWeapon::SubType eSTItr = static_cast< AVP_InventoryWeapon_RangedWeapon::SubType >( uSTItr );
		m_uCurrentChargePFXEffectID[ eSTItr ] = ASURA_PFX_INVALID_EFFECT_ID;
		for ( u_int uEffectItr = 0; uEffectItr < NUM_EFFECT_TYPES; ++uEffectItr )
		{
			EffectType eEffectItr = static_cast< EffectType >( uEffectItr );
			m_aaiSoundHandles[ eEffectItr ][ eSTItr ] = ASURA_INVALID_HANDLE;
		}
	}
}

AVP_RangedWeapon_ClientEffectsHandler::~AVP_RangedWeapon_ClientEffectsHandler()
{
	if ( m_pxLaserAttachment != NULL && m_pxLaserAttachment->IsLinked() )
	{
		m_pxLaserAttachment->UnlinkAttachment( true );
	}
	else
	{
		delete m_pxLaserAttachment;
	}
	
	if (m_pxAmmoAttachments[0])
	{
		if (m_pxAmmoAttachments[0]->IsLinked())
		{
			m_pxAmmoAttachments[0]->UnlinkAttachment( );
		}
		delete m_pxAmmoAttachments[0];
		m_pxAmmoAttachments[0] = NULL;
	}
	if (m_pxAmmoAttachments[1])
	{
		if (m_pxAmmoAttachments[1]->IsLinked())
		{
			m_pxAmmoAttachments[1]->UnlinkAttachment( );
		}
		delete m_pxAmmoAttachments[1];
		m_pxAmmoAttachments[1] = NULL;
	}

	if ( m_pxAnimation != NULL )
	{
		Asura_Animation* pxParentAnim = m_pxAnimation->GetParent();
		if ( pxParentAnim != NULL )
		{
			pxParentAnim->UnlinkAnimation( m_pxAnimation );
		}
	}
	delete m_pxAnimation;

	for ( u_int uEffectItr = 0; uEffectItr < NUM_EFFECT_TYPES; ++uEffectItr )
	{
		EffectType eEffectItr = static_cast< EffectType >( uEffectItr );
		for ( u_int uSTItr = 0; uSTItr < AVP_InventoryWeapon_RangedWeapon::NUM_SUB_TYPES; ++uSTItr )
		{
			AVP_InventoryWeapon_RangedWeapon::SubType eSTItr = static_cast< AVP_InventoryWeapon_RangedWeapon::SubType >( uSTItr );
			if ( m_aaiSoundHandles[ eEffectItr ][ eSTItr ] != ASURA_INVALID_HANDLE )// && IsSoundPlaying( eEffectItr, eSTItr ) )
			{
				StopSound( eEffectItr, eSTItr);
			}
		}
	}

	if(m_iLaserSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Sound_Event_System::Stop( m_iLaserSoundHandle );
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::PlaySound( AVP_ClientEntity_Actor& xClient, EffectType eEffect, AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY*/ )
{
	const Asura_Sound_Event* pxSoundEvent = NULL;
	bool bLooped = false;
	switch ( eEffect )
	{
		case EFFECT_RELOAD:
		{
			// no sound for reloading (sounds are in the anims)
		}
		break;

		case EFFECT_FIRE_LOOP:
		case EFFECT_CHARGING:
		{
			bLooped = true;
		} // fall through;
		case EFFECT_FIRE_BEGIN:
		case EFFECT_FIRE_END:
		{
			pxSoundEvent = Asura_Sound_Event_Storage::FindEvent( m_pxClientData->m_axSubTypeData[ eSubType ].m_auSoundEventHash[ eEffect ] );
		}
		break;

		default:
		{
			Asura_Assert( false, "Unhandled case" );
		}
		break;
	}

	const bool bInvalidSoundHandle = m_aaiSoundHandles[ eEffect ][ eSubType ] == ASURA_INVALID_HANDLE;
	const bool bValidSoundNotPlaying = bInvalidSoundHandle ? false : !Asura_Sound_Event_System::IsPlaying( m_aaiSoundHandles[ eEffect ][ eSubType ] );

	if ( pxSoundEvent != NULL && ( !bLooped || ( bInvalidSoundHandle || bValidSoundNotPlaying ) ) )
	{
		if (bValidSoundNotPlaying)
		{
			Asura_Sound_Event_System::Stop( m_aaiSoundHandles[ eEffect ][ eSubType ] );
		}
		Asura_Vector_3 xPosition;
		const AVP_Actor_AnimationHandler* pxAnimHandler = xClient.GetAnimationHandler();
		Asura_Hash_ID uMarkerHash = (pxAnimHandler && pxAnimHandler->IsFirstPerson()) ? m_pxClientData->m_axSubTypeData[ eSubType ].m_uMuzzleMarkerHashFirstPerson : m_pxClientData->m_axSubTypeData[ eSubType ].m_uMuzzleMarkerHashThirdPerson;
		if ( xClient.GetWorldSpaceMarkerPoint( uMarkerHash, &xPosition, NULL ) )
		{
			// CAH replaced line below with if/else clause below that so we make sure never to play an even that we already have an external handle to. 
			//Asura_Sound_Event_System::PlayEvent( pxSoundEvent, bLooped ? &m_aaiSoundHandles[ eEffect ][ eSubType ] : NULL, &xPosition, 1.0f, m_uOwnerGuid );

			if( bLooped )
			{
				if( m_aaiSoundHandles[ eEffect ][ eSubType ] != ASURA_INVALID_HANDLE )
				{
					// if we already have a handle for the sound event requested then we have to free it before we can play the sound again, otherwise we get memory leaks. 
					StopSound( eEffect, eSubType );
				}

				Asura_Sound_Event_System::PlayEvent( pxSoundEvent, &m_aaiSoundHandles[ eEffect ][ eSubType ], &xPosition, 1.0f, m_uOwnerGuid );
			}
			else
			{
				Asura_Sound_Event_System::PlayEvent( pxSoundEvent, NULL, &xPosition, 1.0f, m_uOwnerGuid );
			}
		}
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::StopSound( const EffectType eEffect, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY */ )
{
	Asura_Sound_Event_System::Stop( m_aaiSoundHandles[ eEffect ][ eSubType ] );
	m_aaiSoundHandles[ eEffect ][ eSubType ] = ASURA_INVALID_HANDLE;
}

void AVP_RangedWeapon_ClientEffectsHandler::UpdateSoundPosition( const Asura_Vector_3& xPosition, const EffectType eEffect, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY */ )
{
	Asura_Sound_Event_System::UpdatePosition( m_aaiSoundHandles[ eEffect ][ eSubType ], xPosition );
}

bool AVP_RangedWeapon_ClientEffectsHandler::IsSoundPlaying( const EffectType eEffect, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY */ )
{
	return m_aaiSoundHandles[ eEffect ][ eSubType ] != ASURA_INVALID_HANDLE && Asura_Sound_Event_System::IsPlaying( m_aaiSoundHandles[ eEffect ][ eSubType ] );
}

void AVP_RangedWeapon_ClientEffectsHandler::PlayActorAnim( AVP_ClientEntity_Actor& xClient, const EffectType eEffect, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY */ )
{
	float fTweenTime = dbg_fWeaponAnimTweenTime;
	const AVP_Actor_AnimStateData* pxStateData = xClient.GetCurrentAnimStateData();
	if ( pxStateData )
	{
		bool bLeaveAtEnd = false;

		Asura_Hash_ID uHash = GetActorAnimForEffectType( *pxStateData, eEffect, eSubType, &fTweenTime );
	
		if(eEffect == EFFECT_FIRE_BEGIN)
		{
			Asura_Hash_ID uLoopHash = GetActorAnimForEffectType( *pxStateData, EFFECT_FIRE_LOOP, eSubType, NULL );
			Asura_Hash_ID uEndHash = GetActorAnimForEffectType( *pxStateData, EFFECT_FIRE_END, eSubType, NULL );
			if(uLoopHash != ASURA_HASH_ID_UNSET || uEndHash != ASURA_HASH_ID_UNSET)
			{
				bLeaveAtEnd = true; // Keep start anims if we have something to follow on.
			}
		}

		if ( uHash != ASURA_HASH_ID_UNSET && ( eEffect != EFFECT_FIRE_LOOP || !IsActorAnimPlaying( xClient ) || m_uCurrentActorAnimHash != uHash ) )
		{
			m_uCurrentActorAnimHash = uHash;
			AVP_Actor_AnimationHandler* pxAnimHandler = xClient.GetAnimationHandler();
			Asura_Assert( pxAnimHandler != NULL, "Unable to cast to vertebrate anim handler" );
			if ( pxAnimHandler != NULL )
			{
				u_int uFlags = AAC_FLAG_ACTIVE | AAC_FLAG_ENSURE_UNIQUE_HASH;
				if(bLeaveAtEnd)
				{
					uFlags |= AAC_FLAG_ALLOW_DESTROY_FROM_MESSAGE;
				}
				else
				{
					uFlags |= AAC_FLAG_DESTROY_WHEN_FINISHED;
				}
				m_uActorAnimControllerHash = pxAnimHandler->PlayWeaponAnimation( uHash, eEffect == EFFECT_RELOAD ? 0.2f : fTweenTime, eEffect == EFFECT_FIRE_LOOP, uFlags );
			}
		}
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::StopActorAnim( AVP_ClientEntity_Actor& xClient, const bool bDestroyIfNoEndAnim, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY*/ )
{
	AVP_Actor_AnimationHandler* pxAnimHandler = xClient.GetAnimationHandler();
	Asura_Assert( pxAnimHandler != NULL, "Unable to cast to vertebrate anim handler" );
	if ( pxAnimHandler != NULL )
	{
		bool bDestroy = false;
		const AVP_Actor_AnimStateData* pxStateData = xClient.GetCurrentAnimStateData();
		if ( pxStateData )
		{
			if ( GetWeaponAnimForEffectType( *pxStateData, EFFECT_FIRE_END, eSubType, NULL ) == ASURA_HASH_ID_UNSET )
			{
				bDestroy = true;
			}
		}
		pxAnimHandler->StopWeaponAnimation( m_uActorAnimControllerHash, bDestroy );
	}
	m_uActorAnimControllerHash = ASURA_HASH_ID_UNSET;
}

void AVP_RangedWeapon_ClientEffectsHandler::DestroyActorAnim( AVP_ClientEntity_Actor& xClient )
{
	AVP_Actor_AnimationHandler* pxAnimHandler = xClient.GetAnimationHandler();
	Asura_Assert( pxAnimHandler != NULL, "Unable to cast to vertebrate anim handler" );
	if ( pxAnimHandler != NULL )
	{
		pxAnimHandler->StopWeaponAnimation( m_uActorAnimControllerHash, true );
	}
	m_uActorAnimControllerHash = ASURA_HASH_ID_UNSET;

}

bool AVP_RangedWeapon_ClientEffectsHandler::IsActorAnimPlaying( const AVP_ClientEntity_Actor& xClient, const EffectType eEffect /*= EFFECT_ANY*/, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY*/ ) const
{
	bool bPlaying = false;
	Asura_Hash_ID uActorAnim = ASURA_HASH_ID_UNSET;
	if ( eEffect != EFFECT_ANY )
	{
		const AVP_Actor_AnimStateData* pxStateData = xClient.GetCurrentAnimStateData();
		if ( pxStateData )
		{
			uActorAnim = GetActorAnimForEffectType( *pxStateData, eEffect, eSubType );
		}
	}

	if ( m_uActorAnimControllerHash != ASURA_HASH_ID_UNSET && ( uActorAnim == ASURA_HASH_ID_UNSET || uActorAnim == m_uActorAnimControllerHash ) )
	{
		const AVP_Actor_AnimationHandler* pxAnimHandler = xClient.GetAnimationHandler();
		Asura_Assert( pxAnimHandler != NULL, "Unable to cast to vertebrate anim handler" );
		if ( pxAnimHandler != NULL )
		{
			bPlaying = pxAnimHandler->IsWeaponAnimationPlaying( m_uActorAnimControllerHash );
		}
	}
	return bPlaying;
}

u_int AVP_RangedWeapon_ClientEffectsHandler::GetActorAnimFlagsForLastFrame( const AVP_ClientEntity_Actor& xClient ) const
{
	u_int uRet = 0;
	if ( m_uActorAnimControllerHash != ASURA_HASH_ID_UNSET )
	{
		const AVP_Actor_AnimationHandler* pxAnimHandler = xClient.GetAnimationHandler();
		const Asura_AnimationController_Animation* pxAACA = pxAnimHandler != NULL ? pxAnimHandler->GetAnimation()->GetControllerAnimationFromHash( m_uActorAnimControllerHash ) : NULL;
		if ( pxAACA != NULL )
		{
			uRet = pxAACA->GetFlagsForLastFrame();
		}
	}
	return uRet;
}

void AVP_RangedWeapon_ClientEffectsHandler::PlayPermenantWeaponAnim( void )
{
	if ( m_pxAnimation && m_pxClientData)
	{
		if ( m_pxClientData->m_uPermenantAnimHash != ASURA_HASH_ID_UNSET && !IsPermenantWeaponAnimPlaying())
		{
			AddPermenantAnimation(m_pxClientData->m_uPermenantAnimHash, AVP_AAPC_MINIGUN_SPIN_ANIM);
		}
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::StopPermenantWeaponAnim( void )
{
	if ( m_pxAnimation && m_pxClientData )
	{	
		if ( m_pxClientData->m_uPermenantAnimHash != ASURA_HASH_ID_UNSET && IsPermenantWeaponAnimPlaying())
		{
			RemovePermenantAnimation(m_pxClientData->m_uPermenantAnimHash);
		}	
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::PlayWeaponAnim( const AVP_ClientEntity_Actor& xClient, const EffectType eEffect, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY */ )
{
	float fTweenTime = dbg_fWeaponAnimTweenTime;
	const AVP_Actor_AnimStateData* pxStateData = xClient.GetCurrentAnimStateData();
	if ( m_pxAnimation != NULL )
	{
		if ( pxStateData )
		{
			bool bLeaveAtEnd = false;

			Asura_Hash_ID uHash = GetWeaponAnimForEffectType( *pxStateData, eEffect, eSubType, &fTweenTime );

			if(eEffect == EFFECT_FIRE_BEGIN)
			{
				Asura_Hash_ID uLoopHash = GetActorAnimForEffectType( *pxStateData, EFFECT_FIRE_LOOP, eSubType, NULL );
				Asura_Hash_ID uEndHash = GetActorAnimForEffectType( *pxStateData, EFFECT_FIRE_END, eSubType, NULL );
				if(uLoopHash != ASURA_HASH_ID_UNSET || uEndHash != ASURA_HASH_ID_UNSET)
				{
					bLeaveAtEnd = true; // Keep start anims if we have something to follow on.
				}
			}

			if ( uHash != ASURA_HASH_ID_UNSET && ( eEffect != EFFECT_FIRE_LOOP || !IsWeaponAnimPlaying( xClient ) || m_uCurrentWeaponAnimHash != uHash ) )
			{
				m_uCurrentWeaponAnimHash = uHash;

				Asura_AnimationBlend* pxBlend = new Asura_AnimationBlend_AnimatedTween( fTweenTime, fTweenTime, false );
				u_int uFlags = AAC_FLAG_ACTIVE;
				if ( eEffect == EFFECT_FIRE_LOOP )
				{
					uFlags |= AACA_FLAG_LOOPED;
				}
				else
				{
					uFlags |= AAC_FLAG_ENSURE_UNIQUE_HASH;
				}

				if ( bLeaveAtEnd )
				{
					uFlags |= AAC_FLAG_ALLOW_DESTROY_FROM_MESSAGE;
				}
				else
				{
					uFlags |= AAC_FLAG_DESTROY_WHEN_FINISHED;
				}

				Asura_AnimationController_Animation* pxAACA = m_pxAnimation->CreateControllerAnimation( uFlags, AVP_AAPC_WEAPON_ANIM, uHash, pxBlend );
				if ( pxAACA != NULL )
				{
					pxAACA->Play();
					m_uWeaponAnimControllerHash = pxAACA->GetControllerHash();
				}
				else
				{
					delete pxBlend;
				}
			}
		}
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::PlayWeaponAnim( const Asura_Hash_ID uHash )
{
	if ( m_pxAnimation != NULL )
	{
		Asura_AnimationBlend* pxBlend = new Asura_AnimationBlend_AnimatedTween( 0.0f, false );
		u_int uFlags = AAC_FLAG_ACTIVE | AAC_FLAG_DESTROY_WHEN_FINISHED | AAC_FLAG_ENSURE_UNIQUE_HASH;
		Asura_AnimationController_Animation* pxAACA = m_pxAnimation->CreateControllerAnimation( uFlags, 0, uHash, pxBlend );
		Asura_Assert( pxAACA, "Unable to create weapon anim 0x%08X" );
		if ( pxAACA != NULL )
		{
			pxAACA->Play();
		}
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::UpdateAmmoCount( Asura_Animation* pxAnimation, float fAmmo )
{
	int iAmmo = Asura_Maths::FloatToInt(fAmmo);
	if (m_pxAmmoAttachments[0])
	{
		if (!m_pxAmmoAttachments[0]->IsLinked())
		{
			m_pxAmmoAttachments[0]->LinkAttachment(pxAnimation, m_pxClientData->m_uAmmoCounterMarker1Hash, false);
		}
		Asura_Animation *pxAmmoAnimation = m_pxAmmoAttachments[0]->GetAttachedAnimation();
		if (pxAmmoAnimation)
		{
			pxAmmoAnimation->SetTextureAnimSequence(iAmmo % 10);
		}
	}
	if (m_pxAmmoAttachments[1])
	{
		if (!m_pxAmmoAttachments[1]->IsLinked())
		{
			m_pxAmmoAttachments[1]->LinkAttachment(pxAnimation, m_pxClientData->m_uAmmoCounterMarker2Hash, false);
		}
		Asura_Animation *pxAmmoAnimation = m_pxAmmoAttachments[1]->GetAttachedAnimation();
		if (pxAmmoAnimation)
		{
			pxAmmoAnimation->SetTextureAnimSequence((iAmmo / 10) % 10);
		}
	}
}


void AVP_RangedWeapon_ClientEffectsHandler::StopWeaponAnim( const AVP_ClientEntity_Actor& xClient, const bool bDestroyIfNoEndAnim, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY*/ )
{
	if ( m_pxAnimation != NULL )
	{
		Asura_AnimationController_Animation* pxAACA = m_pxAnimation->GetControllerAnimationFromHash( m_uWeaponAnimControllerHash );
		if ( pxAACA != NULL )
		{
			const AVP_Actor_AnimStateData* pxStateData = xClient.GetCurrentAnimStateData();
			if ( pxStateData )
			{
				if ( GetWeaponAnimForEffectType( *pxStateData, EFFECT_FIRE_END, eSubType, NULL ) == ASURA_HASH_ID_UNSET )
				{
					pxAACA->SetControllerFlags( pxAACA->GetControllerFlags() | AAC_FLAG_DESTROY_WHEN_FINISHED );
				}
			}
			pxAACA->Stop();
		}
		m_uWeaponAnimControllerHash = ASURA_HASH_ID_UNSET;
	}
}

bool AVP_RangedWeapon_ClientEffectsHandler::IsWeaponAnimPlaying( const AVP_ClientEntity_Actor& xClient, const EffectType eEffect /*= EFFECT_ANY*/, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY*/ ) const
{
	bool bPlaying = false;
	if ( m_pxAnimation != NULL )
	{
		Asura_Hash_ID uWeaponAnim = ASURA_HASH_ID_UNSET;
		if ( eEffect != EFFECT_ANY )
		{
			const AVP_Actor_AnimStateData* pxStateData = xClient.GetCurrentAnimStateData();
			if ( pxStateData )
			{
				uWeaponAnim  = GetWeaponAnimForEffectType( *pxStateData, eEffect, eSubType );
			}
		}

		if ( m_uWeaponAnimControllerHash != ASURA_HASH_ID_UNSET && ( uWeaponAnim == ASURA_HASH_ID_UNSET || uWeaponAnim == m_uWeaponAnimControllerHash ) )
		{
			Asura_AnimationController_Animation* pxAACA = m_pxAnimation->GetControllerAnimationFromHash( m_uWeaponAnimControllerHash );
			if ( pxAACA != NULL )
			{
				bPlaying = !pxAACA->IsFinished();
			}
		}
	}
	return bPlaying;
}

bool AVP_RangedWeapon_ClientEffectsHandler::IsPermenantWeaponAnimPlaying(void) const
{
	bool bPlaying = false;
	if ( m_pxAnimation && m_pxClientData)
	{
		if ( m_pxClientData->m_uPermenantAnimHash != ASURA_HASH_ID_UNSET )
		{
			Asura_AnimationController_Animation* pxAACA = m_pxAnimation->GetControllerAnimationFromHash( m_pxClientData->m_uPermenantAnimHash );
			if ( pxAACA != NULL )
			{
				bPlaying = true;
			}
		}
	}
	return bPlaying;
}

void AVP_RangedWeapon_ClientEffectsHandler::DoChargePFX( const AVP_ClientEntity_Actor& xClient, Asura_Animation& xAnimation, const Asura_Vector_3& xFirePosition, const Asura_Matrix_3x3& xFireOrientation, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType )
{
	const Asura_Hash_ID uEffecthash = m_pxClientData->m_axSubTypeData[ eSubType ].m_uChargingPFXHash;
	if ( uEffecthash != ASURA_HASH_ID_UNSET )
	{
		u_short uPFXFlags = ASURA_PFX_EFFECT_CAMERA_FLAGS | ASURA_PFX_EFFECT_MOVE_PARTICLES_WITH_EFFECT;
		if ( AVP_Game_Core::IsLocalPlayer( xAnimation.GetParentEntityGUID() ) )
		{
			uPFXFlags |= ASURA_PFX_EFFECT_FORCE_RENDER_ON_MAIN_THREAD;
		}
		m_uCurrentChargePFXEffectID[ eSubType ] = Asura_PFX_Entity_Effect::Create( xFirePosition, xFireOrientation, uEffecthash, 0, uPFXFlags, m_uOwnerGuid);

		// Attach the charging effect...
		const AVP_Actor_AnimationHandler* pxAnimHandler = xClient.GetAnimationHandler();
		const Asura_Hash_ID uMarkerPoint = (pxAnimHandler && pxAnimHandler->IsFirstPerson()) ? m_pxClientData->m_axSubTypeData[ eSubType ].m_uMuzzleMarkerHashFirstPerson : m_pxClientData->m_axSubTypeData[ eSubType ].m_uMuzzleMarkerHashThirdPerson;
		if ( m_uCurrentChargePFXEffectID[ eSubType ] != ASURA_PFX_INVALID_EFFECT_ID && uMarkerPoint != ASURA_HASH_ID_UNSET )
		{
			Asura_AnimationAttachment*	pxAttachment	= new Asura_AnimationAttachment_PFX( m_uCurrentChargePFXEffectID[ eSubType ], true );
			pxAttachment->LinkAttachment( &xAnimation, uMarkerPoint, true );
		}
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::StopChargePFX( const AVP_InventoryWeapon_RangedWeapon::SubType eSubType )
{
	if ( m_uCurrentChargePFXEffectID[ eSubType ] != ASURA_PFX_INVALID_EFFECT_ID )
	{
		Asura_PFX_Entity_Effect* pxPFXEffect = Asura_PFX_System::GetEntityEffect( m_uCurrentChargePFXEffectID[ eSubType ] );

		if ( pxPFXEffect != NULL )
		{
			pxPFXEffect->Stop( false );
		}
		m_uCurrentChargePFXEffectID[ eSubType ] = ASURA_PFX_INVALID_EFFECT_ID;
	}
}

bool AVP_RangedWeapon_ClientEffectsHandler::IsChargePFXPlaying( const AVP_InventoryWeapon_RangedWeapon::SubType eSubType ) const 
{
	if ( m_uCurrentChargePFXEffectID[ eSubType ] != ASURA_PFX_INVALID_EFFECT_ID )
	{
		const Asura_PFX_Entity_Effect* pxPFXEffect = Asura_PFX_System::GetEntityEffect( m_uCurrentChargePFXEffectID[ eSubType ] );
		return pxPFXEffect != NULL;
	}
	return false;
}

void AVP_RangedWeapon_ClientEffectsHandler::DoRecoilCameraShake( const Asura_Guid uPlayerGuid, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType )
{
	// CameraShake
	const Asura_Hash_ID uCameraShakeHash = m_pxClientData->m_axSubTypeData[ eSubType ].m_uCameraShakeHash;
	if ( uCameraShakeHash != ASURA_HASH_ID_UNSET)
	{
		AVP_CameraShake_System::StartShake(uCameraShakeHash, uPlayerGuid);
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::DoMuzzleFlash( MuzzleFlashTypes eMuzzleFlashType, const AVP_ClientEntity_Actor& xClient, Asura_Animation& xAnimation, const Asura_Vector_3& xFirePosition, const Asura_Matrix_3x3& xFireOrientation, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType )
{
	const AVP_Actor_AnimationHandler* pxAnimHandler = xClient.GetAnimationHandler();
	if( pxAnimHandler==NULL || m_pxClientData==NULL )
	{
		return;									// Don't continue, if we don't have relevant data.
	}

	AVP_MuzzleFlash_Data* pxMuzzleFlashData = NULL;
	switch( eMuzzleFlashType )
	{
	case FLASH_FIRE:
		{
			if( pxAnimHandler->IsFirstPerson() )
			{
				pxMuzzleFlashData = &m_pxClientData->m_axSubTypeData[ eSubType ].m_xMuzzleFlash_Fire1P;
			}
			else
			{
				pxMuzzleFlashData = &m_pxClientData->m_axSubTypeData[ eSubType ].m_xMuzzleFlash_Fire3P;
			}
			break;
		}
	case FLASH_RELOAD:
		{
			if( pxAnimHandler->IsFirstPerson() )
			{
				pxMuzzleFlashData = &m_pxClientData->m_axSubTypeData[ eSubType ].m_xMuzzleFlash_Reload1P;
			}
			else
			{
				pxMuzzleFlashData = &m_pxClientData->m_axSubTypeData[ eSubType ].m_xMuzzleFlash_Reload3P;
			}
			break;
		}
	case FLASH_HOT:
		{
			if( pxAnimHandler->IsFirstPerson() )
			{
				pxMuzzleFlashData = &m_pxClientData->m_axSubTypeData[ eSubType ].m_xMuzzleFlash_Hot1P;
			}
			else
			{
				pxMuzzleFlashData = &m_pxClientData->m_axSubTypeData[ eSubType ].m_xMuzzleFlash_Hot3P;
			}
			break;
		}
	default:
		{
			Asura_Assert( false, "Impossible to reach this code: AVP_RangedWeapon_ClientEffectsHandler::DoMuzzleFlash" );
			return;
		}
	}

	// Get the marker point from which the muzzle PFXs will be spawn
	const Asura_Hash_ID uMarkerPoint = (pxAnimHandler->IsFirstPerson()) ? m_pxClientData->m_axSubTypeData[ eSubType ].m_uMuzzleMarkerHashFirstPerson : m_pxClientData->m_axSubTypeData[ eSubType ].m_uMuzzleMarkerHashThirdPerson;
	if( uMarkerPoint!=ASURA_HASH_ID_UNSET )
	{
		DoMuzzleFlashEffect( pxMuzzleFlashData, xClient, xAnimation, xFirePosition, xFireOrientation, uMarkerPoint );
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::DoMuzzleFlashEffect( const AVP_MuzzleFlash_Data* pxMuzzleFlashData, const AVP_ClientEntity_Actor& xClient, Asura_Animation& xAnimation, const Asura_Vector_3& xFirePosition, const Asura_Matrix_3x3& xFireOrientation, const Asura_Hash_ID uMarkerPoint )
{
	u_short uPFXFlags = ASURA_PFX_EFFECT_CAMERA_FLAGS; // | ASURA_PFX_EFFECT_MOVE_PARTICLES_WITH_EFFECT;
	if ( AVP_Game_Core::IsLocalPlayer( xAnimation.GetParentEntityGUID() ) )
	{
		uPFXFlags |= ASURA_PFX_EFFECT_FORCE_RENDER_ON_MAIN_THREAD;
	}

	for( u_int u=0; u<pxMuzzleFlashData->GetNumOfPFX(); ++u )
	{
		const Asura_Hash_ID uEffectHash = pxMuzzleFlashData->GetPFXHash(u);
		if ( uEffectHash != ASURA_HASH_ID_UNSET )
		{
			const u_int	uPFXEffectID = Asura_PFX_Entity_Effect::Create( xFirePosition, xFireOrientation, uEffectHash, 0, uPFXFlags, m_uOwnerGuid );

			// Attach the muzzle-flash...
			if ( uPFXEffectID != ASURA_PFX_INVALID_EFFECT_ID )
			{
				Asura_AnimationAttachment*	pxAttachment	= new Asura_AnimationAttachment_PFX( uPFXEffectID, true );
				pxAttachment->LinkAttachment( &xAnimation, uMarkerPoint, true );
			}
		}
	}
}

/*
void AVP_RangedWeapon_ClientEffectsHandler::DoMuzzleFlash( const AVP_ClientEntity_Actor& xClient, Asura_Animation& xAnimation, const Asura_Vector_3& xFirePosition, const Asura_Matrix_3x3& xFireOrientation, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType )
{
	const AVP_Actor_AnimationHandler* pxAnimHandler = xClient.GetAnimationHandler();
	if( pxAnimHandler==NULL || m_pxClientData==NULL )
	{
		return;									// Don't continue, if we don't have relevant data.
	}

	// Get the marker point from which the muzzle PFXs will be spawn
	const Asura_Hash_ID uMarkerPoint = (pxAnimHandler->IsFirstPerson()) ? m_pxClientData->m_axSubTypeData[ eSubType ].m_uMuzzleMarkerHashFirstPerson : m_pxClientData->m_axSubTypeData[ eSubType ].m_uMuzzleMarkerHashThirdPerson;
	if( uMarkerPoint==ASURA_HASH_ID_UNSET )
	{
		return;
	}

	u_short uPFXFlags = ASURA_PFX_EFFECT_CAMERA_FLAGS | ASURA_PFX_EFFECT_MOVE_PARTICLES_WITH_EFFECT;
	if ( AVP_Game_Core::IsLocalPlayer( xAnimation.GetParentEntityGUID() ) )
	{
		uPFXFlags |= ASURA_PFX_EFFECT_FORCE_RENDER_ON_MAIN_THREAD;
	}

	// Create the PFX effects that are stored inside MuzzleFlash_Data. (This structure can contain more that one PFX effect)
	const AVP_MuzzleFlash_Data& xMuzzleFlashData = (pxAnimHandler->IsFirstPerson()) ? m_pxClientData->m_axSubTypeData[ eSubType ].m_xMuzzleFlash_Fire1P : m_pxClientData->m_axSubTypeData[ eSubType ].m_xMuzzleFlash_Fire3P;
	for( u_int u=0; u<xMuzzleFlashData.GetNumOfPFX(); ++u )
	{
		const Asura_Hash_ID uEffectHash = xMuzzleFlashData.GetPFXHash(u);
		if ( uEffectHash != ASURA_HASH_ID_UNSET )
		{
			const u_int	uPFXEffectID = Asura_PFX_Entity_Effect::Create( xFirePosition, xFireOrientation, uEffectHash, 0, uPFXFlags, m_uOwnerGuid );

			// Attach the muzzle-flash...
			if ( uPFXEffectID != ASURA_PFX_INVALID_EFFECT_ID )
			{
				Asura_AnimationAttachment*	pxAttachment	= new Asura_AnimationAttachment_PFX( uPFXEffectID, true );
				pxAttachment->LinkAttachment( &xAnimation, uMarkerPoint, true );
			}
		}
	}
}
*/

void AVP_RangedWeapon_ClientEffectsHandler::DoTracerBeam( const Asura_Vector_3& xFirePosition, const Asura_Matrix_3x3& xFireOrientation, const float fRange, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType, u_int uNumTracers )
{
	Asura_Beam_Template& xBeamTemplate = m_pxClientData->m_axSubTypeData[ eSubType ].m_xTracerTemplate;
	if ( xBeamTemplate.uTileHashID != ASURA_HASH_ID_UNSET )
	{
		if ( m_uTracerCount == 0 )
		{
			if ( xBeamTemplate.iTileHandle == ASURA_INVALID_HANDLE )
			{
				xBeamTemplate.iTileHandle = Asura_TiledTextureManagement::GetHandleOfTile( xBeamTemplate.uTileHashID );
			}

			if(fRange > 0.01f && xBeamTemplate.fTailSpeed > 0.01f) // Don't allow setting of the decay rate to 0, and definitely don't allow divide by 0.
			{
				xBeamTemplate.fDecayRate = xBeamTemplate.fTailSpeed / fRange; // Hacked decay rate, stops beams traveling through walls
			}
			
			const Asura_Vector_3	xFireDirection( xFireOrientation.e13, xFireOrientation.e23, xFireOrientation.e33 );
			const Asura_Vector_3	xEndPosition( xFirePosition + ( xFireDirection * fRange ) );

			Asura_Beam_System::CreateBeam( xFirePosition, xEndPosition, xBeamTemplate );

			// Play Bullet by sound if close to camera, but only if it wasn't fired by the local player
			if(!AVP_Game_Core::IsLocalPlayer(m_uOwnerGuid))
			{
				Asura_AVector3 xClosestPoint;
				Asura_AVector3 xCamPosition(*Asura_Camera::GetPosition());
				AVP_Maths::FindClosestPointOnEdge(xCamPosition, Asura_AVector3(xFirePosition), Asura_AVector3(xEndPosition), xClosestPoint);
				float fDistance = (xCamPosition - xClosestPoint).Magnitude();
				if(fDistance < 3.0f && m_pxClientData->m_uBulletBySoundHash != ASURA_HASH_ID_UNSET)
				{
					// Close, play sound
					const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::FindEvent(m_pxClientData->m_uBulletBySoundHash);
					if(pxEvent)
					{
						Asura_Vector_3 xPosition(xClosestPoint);
						Asura_Sound_Event_System::PlayEvent(pxEvent, NULL, &xPosition);
					}
				}
			}
		}
		m_uTracerCount = ( m_uTracerCount + 1 ) % uNumTracers; // only do a beam every 2 shots - 14/10/08 GBT  Alex wanted more beams!
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::ActivateLaser( Asura_Animation& xAnimation )
{
	if ( m_pxLaserAttachment != NULL )
	{
		if ( !m_pxLaserAttachment->IsLinked() )
		{
#ifdef ASURA_ASSERT
			bool bOK = 
#endif
			m_pxLaserAttachment->LinkAttachment( &xAnimation, m_pxClientData->m_uLaserMarkerPoint, false );
			Asura_Assertf( bOK, ( "Unable to link laser to marker point 0x%08x", m_pxClientData->m_uLaserMarkerPoint ) );
		}

		if (m_pxLaserAttachment->GetProjector() == ASURA_INVALID_HANDLE)
		{
			Asura_Projector* pxReticleLaser = Asura_Projector_System::CreateProjector();
			Asura_Assert( pxReticleLaser != NULL, "Unable to crete projector" );
			if ( pxReticleLaser != NULL )
			{
				const Asura_ShadowParameters	xShadowParams(ASURA_SHADOWFLAG_CASTS_ENVIRONMENT_SHADOWS | ASURA_SHADOWFLAG_CASTS_OWN_SHADOWS);

				const int						iFalloffTextureIndex	= Asura_Texture_Management::GetIndexFromHashID(m_pxClientData->m_uLaserFalloffTextureHash);
				const int						iProjectedTextureIndex	= Asura_Texture_Management::GetIndexFromHashID(m_pxClientData->m_uLaserProjectedTextureHash);

				pxReticleLaser->SetShadowParameters( xShadowParams );
				pxReticleLaser->SetFakeLight(false);
				pxReticleLaser->SetDynamicLight(true);
				pxReticleLaser->SetType( ASURA_PROJECTOR_TYPE_PARALLEL );
				pxReticleLaser->SetBlendMode( ASURA_PROJECTOR_BLEND_ADD );
				pxReticleLaser->SetIntensity( 20.0f ); // BP?
				pxReticleLaser->SetTextureIndex( iProjectedTextureIndex );
				pxReticleLaser->SetFalloffTextureIndex( iFalloffTextureIndex );
				pxReticleLaser->SetDetailTextureIndex( ASURA_TEXTURE_NONE ); // BP
				pxReticleLaser->SetScale( m_pxClientData->m_fLaserRadius );
				pxReticleLaser->SetRange( 0.6f, m_pxClientData->m_fLaserMaxLength ); // BP?

				m_pxLaserAttachment->SetProjector( pxReticleLaser->GetHandle(), true );

				// Play sounds
				Asura_Vector_3 xPos;
				m_pxLaserAttachment->GetWorldRelativePosition(&xPos, NULL);

				const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::FindEvent(m_pxClientData->m_uLaserActivationSoundHash);
				if(pxEvent)
				{
					Asura_Sound_Event_System::PlayEvent(pxEvent, NULL, &xPos, 1.0f, m_uOwnerGuid );
				}
				const Asura_Sound_Event* pxLoopingEvent = Asura_Sound_Event_Storage::FindEvent(m_pxClientData->m_uLaserLoopingSoundHash);
				if(pxLoopingEvent && m_iLaserSoundHandle == ASURA_INVALID_HANDLE)
				{
					Asura_Sound_Event_System::PlayEvent(pxLoopingEvent, &m_iLaserSoundHandle, &xPos, 1.0f, m_uOwnerGuid);
				}
			}
		}
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::DeactivateLaser()
{
	if ( m_pxLaserAttachment != NULL && m_pxLaserAttachment->IsLinked() )
	{
		m_pxLaserAttachment->UnlinkAttachment( false );
		m_pxLaserAttachment->SetProjector( ASURA_INVALID_HANDLE, false );

		const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::FindEvent(m_pxClientData->m_uLaserDeactivationSoundHash);
		if(pxEvent)
		{
			Asura_Sound_Event_System::PlayEvent(pxEvent, NULL);
		}
	}
}

Asura_Animation* AVP_RangedWeapon_ClientEffectsHandler::CreateAnimation()
{
	if ( m_pxClientData->m_uSkinHash != ASURA_HASH_ID_UNSET )
	{
		Asura_Assert( m_pxAnimation == NULL, "Already got an animation" );
		m_pxAnimation = new Asura_Animation( m_pxClientData->m_uSkinHash );
		return m_pxAnimation;
	}
	else
	{
		return NULL;
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::SetRestAnim( const Asura_Hash_ID uRestAnim )
{
	if ( m_pxAnimation && uRestAnim != ASURA_HASH_ID_UNSET && uRestAnim != m_uRestAnimHash )
	{
		Asura_AnimationController_Animation* pxOldAACA = m_pxAnimation->GetControllerAnimationFromHash( m_uRestAnimControllerHash );
		if ( pxOldAACA )
		{
			pxOldAACA->SetControllerFlags( pxOldAACA->GetControllerFlags() | AAC_FLAG_ALLOW_DESTROY_FROM_MESSAGE );
		}

		u_int uFlags = AAC_FLAG_ACTIVE | AACA_FLAG_LOOPED;
		u_int uPriority = AVP_AAPC_WEAPON_IDLE;
		Asura_AnimationBlend* pxBlend = new Asura_AnimationBlend_AnimatedTween( dbg_fWeaponAnimTweenTime, false );
		Asura_AnimationController_Animation* pxAACA = m_pxAnimation->CreateControllerAnimation( uFlags, uPriority, uRestAnim, pxBlend );
		if ( pxAACA == NULL )
		{
			delete pxBlend;
		}
		else
		{
			pxAACA->Play();
			m_uRestAnimHash = uRestAnim;
			m_uRestAnimControllerHash = pxAACA->GetControllerHash();
		}
	}
}

Asura_Animation* AVP_RangedWeapon_ClientEffectsHandler::AddPermenantAnimation( const Asura_Hash_ID uAnimHash, u_int uPriority )
{
	if ( m_pxClientData->m_uSkinHash != ASURA_HASH_ID_UNSET )
	{
		if(m_pxAnimation)
		{
			if ( uAnimHash != ASURA_HASH_ID_UNSET )
			{
				u_int uFlags = AAC_FLAG_ACTIVE | AACA_FLAG_LOOPED;
				Asura_AnimationBlend* pxBlend = new Asura_AnimationBlend( 1.0f );
				Asura_AnimationController_Animation* pxAACA = m_pxAnimation->CreateControllerAnimation( uFlags, uPriority, uAnimHash, pxBlend );
				if ( pxAACA == NULL )
				{
					delete pxBlend;
				}
				else
				{
					pxAACA->Play();
				}
			}
		}
	
		return m_pxAnimation;
	}
	else
	{
		return NULL;
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::RemovePermenantAnimation( const Asura_Hash_ID uAnimHash )
{
	if ( m_pxClientData->m_uSkinHash != ASURA_HASH_ID_UNSET )
	{
		if(m_pxAnimation)
		{
			if ( uAnimHash != ASURA_HASH_ID_UNSET )
			{
				Asura_AnimationController_Animation* pxAACA = m_pxAnimation->GetControllerAnimationFromHash( uAnimHash );
				if ( pxAACA )
				{
					m_pxAnimation->DestroyController( uAnimHash );
				}
			}
		}
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::DestroyAnimation()
{
	delete m_pxAnimation;
	m_pxAnimation = NULL;
	m_uRestAnimControllerHash = ASURA_HASH_ID_UNSET;
	m_uRestAnimHash = ASURA_HASH_ID_UNSET;
}

void AVP_RangedWeapon_ClientEffectsHandler::InitiliaseLaserFromClientData( AVP_Laser* pxLaser, const AVP_RangedWeapon_ClientData* const pxClientData, const Asura_Guid uOwnerGuid )
{
	pxLaser->SetBeamRadius( pxClientData->m_fLaserBeamRadius );
	pxLaser->SetBeamSharpness( pxClientData->m_fLaserBeamSharpness );
	pxLaser->SetBeamSpread( pxClientData->m_fLaserBeamSpread );
	pxLaser->SetFallOffLength( pxClientData->m_fLaserFalloffLength );
	pxLaser->SetGuidToIgnore( uOwnerGuid );
	pxLaser->SetMaximumLength( pxClientData->m_fLaserMaxLength );
	pxLaser->SetRadius( pxClientData->m_fLaserRadius );

	pxLaser->SetFallOffStartAngle( pxClientData->m_fLaserFallOffStartAngle );
	pxLaser->SetFallOffStartDistance( pxClientData->m_fLaserFallOffStartDistance);
	pxLaser->SetFallOffEndAngle( pxClientData->m_fLaserFallOffEndAngle );
	pxLaser->SetFallOffEndDistance( pxClientData->m_fLaserFallOffEndDistance );
	pxLaser->SetBeamVisibilityDistance( pxClientData->m_fLaserBeamVisibilityDistance );
	pxLaser->SetFallOffAngleEndIntensity( pxClientData->m_fLaserFallOffAngleEndIntensity );

	pxLaser->SetOriginPhotonEnabled(pxClientData->m_bOriginPhotonEnabled);
	pxLaser->SetOriginPhotonRadius(pxClientData->m_fOriginPhotonRadius);
	pxLaser->SetOriginPhotonFalloffScale(pxClientData->m_fOriginPhotonFalloffScale);
	pxLaser->SetOriginPhotonFalloffPower(pxClientData->m_fOriginPhotonFalloffPower);
	pxLaser->SetOriginPhotonDistanceFade(pxClientData->m_bOriginPhotonEnableDistanceFade, pxClientData->m_fOriginPhotonDistanceFadeStart, pxClientData->m_fOriginPhotonDistanceFadeDist);

	pxLaser->SetContactPhotonEnabled(pxClientData->m_bContactPhotonEnabled);
	pxLaser->SetContactPhotonRadius(pxClientData->m_fContactPhotonRadius);
	pxLaser->SetContactPhotonFalloffScale(pxClientData->m_fContactPhotonFalloffScale);
	pxLaser->SetContactPhotonFalloffPower(pxClientData->m_fContactPhotonFalloffPower);
	pxLaser->SetContactPhotonDistanceFade(pxClientData->m_bContactPhotonEnableDistanceFade, pxClientData->m_fContactPhotonDistanceFadeStart, pxClientData->m_fContactPhotonDistanceFadeDist);
	pxLaser->SetContactPhotonBackOffDistance(pxClientData->m_fContactPhotonBackOffDistance);

	pxLaser->SetOriginPhotonColour(pxClientData->m_xOriginPhotonColour);
	pxLaser->SetContactPhotonColour(pxClientData->m_xContactPhotonColour);

	pxLaser->SetNumBeams( pxClientData->m_iLaserNumDots );

}

void AVP_RangedWeapon_ClientEffectsHandler::SetLaserTarget( const Asura_Vector_3& xPosition ) 
{ 
	if(m_pxLaserAttachment) 
	{ 
		m_pxLaserAttachment->SetTarget(xPosition); 
		m_pxLaserAttachment->SetTargetEnabled(true);
	} 
}

void AVP_RangedWeapon_ClientEffectsHandler::SetLaserTarget( const Asura_Guid uTargetGuid, const Asura_Hash_ID uTargetMPHash ) 
{ 
	if(m_pxLaserAttachment) 
	{ 
		m_pxLaserAttachment->SetTarget( uTargetGuid, uTargetMPHash ); 
		m_pxLaserAttachment->SetTargetEnabled(true);
	} 
}

void AVP_RangedWeapon_ClientEffectsHandler::ResetLaserTarget()	
{ 
	if(m_pxLaserAttachment) 
	{ 
		m_pxLaserAttachment->SetTargetEnabled(false);
	} 
}

DEBUGVAR float dbg_fAngleParametricScale = 1.0f;

void AVP_RangedWeapon_ClientEffectsHandler::UpdateLaserLoopedSound(const Asura_Vector_3& xFirePosition)
{
	if(m_iLaserSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Sound_Event_System::UpdatePosition( m_iLaserSoundHandle, xFirePosition );
		
		AVP_ClientEntity_Actor* pxClient = AVP_ClientEntity_Actor::FindEntity(m_uOwnerGuid);
		if(pxClient)
		{
			float fAngleParametric = pxClient->GetLookDirectionStoredAngle() / pxClient->GetLookDirectionLimitAngle();

			Asura_Sound_Event_System::SetPitchScale( m_iLaserSoundHandle, 1.0f+(fAngleParametric*dbg_fAngleParametricScale) );
		}
	}
}

Asura_Hash_ID AVP_RangedWeapon_ClientEffectsHandler::GetActorAnimForEffectType( const AVP_Actor_AnimStateData& xStateData, const EffectType eEffect, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY*/, float* pfTweenTime /*= NULL*/ ) const
{
	Asura_Hash_ID uHash;
	switch ( eEffect )
	{
		case EFFECT_RELOAD:
		{
			uHash = xStateData.GetReloadActorHash();
			if ( pfTweenTime )
			{
				*pfTweenTime = dbg_fReloadTweenTime;
			}
		}
		break;

		case EFFECT_FIRE_BEGIN:
		{
			if ( eSubType == AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY )
			{
				uHash = xStateData.GetPrimaryActorShootBeginHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
			else
			{
				uHash = xStateData.GetSecondaryActorShootBeginHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
		}
		break;

		case EFFECT_FIRE_LOOP:
		{
			if ( eSubType == AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY )
			{
				uHash = xStateData.GetPrimaryActorShootLoopHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
			else
			{
				uHash = xStateData.GetSecondaryActorShootLoopHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
		}
		break;

		case EFFECT_FIRE_END:
		{
			if ( eSubType == AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY )
			{
				uHash = xStateData.GetPrimaryActorShootEndHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
			else
			{
				uHash = xStateData.GetSecondaryActorShootEndHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
		}
		break;

		default:
		{
			uHash = ASURA_HASH_ID_UNSET;
			Asura_Assert( false, "Unhandled case" );
		}
		break;
	}
	return uHash;
}

Asura_Hash_ID AVP_RangedWeapon_ClientEffectsHandler::GetWeaponAnimForEffectType( const AVP_Actor_AnimStateData& xStateData, const EffectType eEffect, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY*/, float* pfTweenTime /*= NULL*/ ) const
{
	Asura_Hash_ID uHash;
	switch ( eEffect )
	{
		case EFFECT_RELOAD:
		{
			uHash = xStateData.GetReloadWeaponHash();
			if ( pfTweenTime )
			{ 
				*pfTweenTime = dbg_fReloadTweenTime;
			}
		}
		break;

		case EFFECT_FIRE_BEGIN:
		{
			if ( eSubType == AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY )
			{
				uHash = xStateData.GetPrimaryWeaponShootBeginHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
			else
			{
				uHash = xStateData.GetSecondaryWeaponShootBeginHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
		}
		break;

		case EFFECT_FIRE_LOOP:
		{
			if ( eSubType == AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY )
			{
				uHash = xStateData.GetPrimaryWeaponShootLoopHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
			else
			{
				uHash = xStateData.GetSecondaryWeaponShootLoopHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
		}
		break;

		case EFFECT_FIRE_END:
		{
			if ( eSubType == AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY )
			{
				uHash = xStateData.GetPrimaryWeaponShootEndHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
			else
			{
				uHash = xStateData.GetSecondaryWeaponShootEndHash();
				if ( pfTweenTime )
				{ 
					*pfTweenTime = dbg_fWeaponAnimTweenTime;
				}
			}
		}
		break;

		default:
		{
			uHash = ASURA_HASH_ID_UNSET;
			Asura_Assert( false, "Unhandled case" );
		}
		break;
	}
	return uHash;
}

void AVP_RangedWeapon_ClientEffectsHandler::SetWeaponAnimParametricTimer(float fParametricTimer, AVP_ClientEntity_Actor& xClient, const EffectType eEffect, const AVP_InventoryWeapon_RangedWeapon::SubType eSubType /*= AVP_InventoryWeapon_RangedWeapon::ST_PRIMARY */)
{
	const AVP_Actor_AnimStateData* pxStateData = xClient.GetCurrentAnimStateData();
	if ( pxStateData )
	{
		Asura_Hash_ID uHash = GetWeaponAnimForEffectType( *pxStateData, eEffect, eSubType, NULL );
		if ( uHash != ASURA_HASH_ID_UNSET )
		{
			if ( m_pxAnimation != NULL )
			{
				Asura_AnimationController_Animation* pxAnimCA = m_pxAnimation->GetControllerAnimationFromHash(uHash);
				if(pxAnimCA)
				{
					if(pxAnimCA->IsPlaying())
					{					
						pxAnimCA->Pause(); // We are setting a point in the anim we want, do allow it to play
					}
					pxAnimCA->SetParametricTimer(fParametricTimer);	
				}
			}
		}
	}
}

void AVP_RangedWeapon_ClientEffectsHandler::SetWeaponPermenantAnimParametricTimer(float fParametricTimer)
{
	if(m_pxClientData)
	{
		if ( m_pxClientData->m_uPermenantAnimHash != ASURA_HASH_ID_UNSET )
		{
			if ( m_pxAnimation != NULL )
			{
				Asura_AnimationController_Animation* pxAnimCA = m_pxAnimation->GetControllerAnimationFromHash(m_pxClientData->m_uPermenantAnimHash);
				if(pxAnimCA)
				{
					if(pxAnimCA->IsPlaying())
					{					
						pxAnimCA->Pause(); // We are setting a point in the anim we want, do allow it to play
					}
					pxAnimCA->SetParametricTimer(fParametricTimer);	
				}
			}
		}
	}
}

float AVP_RangedWeapon_ClientEffectsHandler::GetWeaponHotMuzzleFlash_NumBulletsToBegin( const AVP_InventoryWeapon_RangedWeapon::SubType eSubType )
{
	return m_pxClientData ? m_pxClientData->m_axSubTypeData[eSubType].m_fMuzzleFlash_Hot_NumBulletsToBegin : 0.0f;
}




//-----------------------------------------------------------
// Local class that is made just for purpose of enumeration "PFX."

class AVP_WeaponMuzzleFlash_PFXEnumerator : public Asura_Blueprint_ParameterEnumerator
{
public:
	AVP_WeaponMuzzleFlash_PFXEnumerator( Asura_Hash_ID* puHash ) : m_puHash( puHash ) {}
	virtual ~AVP_WeaponMuzzleFlash_PFXEnumerator() {}

	virtual void BeginEnumeration() {}
	virtual bool Enumerate( const u_int uIndex, char const* const szName, const Asura_Value& xValue);
	virtual void EndEnumeration( const bool bSuccess ) {}

private:
	typedef Asura_Blueprint_ParameterEnumerator PARENT;

	Asura_Hash_ID* m_puHash;
};

bool AVP_WeaponMuzzleFlash_PFXEnumerator::Enumerate( const u_int uIndex, char const* const szName, const Asura_Value& xValue )
{
	Asura_Assertf( uIndex < AVP_MAX_MUZZLE_FLASH_EFFECT_PFX, ("Too many PFX (%d) in this bleuprint, max is %d", uIndex, AVP_MAX_MUZZLE_FLASH_EFFECT_PFX ) );
	if ( uIndex < AVP_MAX_MUZZLE_FLASH_EFFECT_PFX )
	{
		m_puHash[ uIndex ] = xValue.GetHashIDValue();
		return true;
	}
	return false;
}

bool AVP_MuzzleFlash_Data::InitialiseFromBlueprint( const Asura_Hash_ID uBlueprintHash )
{
	// First, get the blueprint.:
	Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_WEAPONEFFECTS, uBlueprintHash );
	if ( pxBlueprint )
	{
		Asura_Value xValue;

		AVP_WeaponMuzzleFlash_PFXEnumerator xEnum( m_auPFXHash );
		m_uNumPFX = pxBlueprint->EnumerateParameters( "PFX.", 2, xEnum );
		if ( m_uNumPFX >= AVP_MAX_MUZZLE_FLASH_EFFECT_PFX )
		{
			m_uNumPFX = AVP_MAX_MUZZLE_FLASH_EFFECT_PFX;
		}

		if(pxBlueprint->GetParameterValueByHashID(0x0688c90f, xValue))	// Sound
		{
			m_uSoundHash = xValue.GetHashIDValue();
		}
		else
		{
			m_uSoundHash = ASURA_HASH_ID_UNSET;
		}
		return true;
	}

	return false;
}
//-----------------------------------------------------------
