//
// AVP_CameraShake_System.cpp
//
// Created by RDM - 02 November 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

/************/
/* Includes */
/************/

// main asura header
#include "Core/Asura.h"

// This file's header.
#include "Camera/AVP_CameraShake_System.h"

// asura
#include "Animation/Asura_Animation.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "Camera/Asura_Camera.h"
#include "Core/Asura_Timers.h"
#include "Cutscene/Asura_Cutscene.h"
#include "Cutscene/Asura_Cutscene_Actor.h"
#include "Cutscene/Asura_Cutscene_System.h"
#include "Entity/Asura_Entity_Renderable.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "Interface/Asura_CommandConsole.h"

#ifdef SN_TARGET_PS2
#include "Input/Asura_PS2_ControlPortDevice.h"
#endif

// AVP
#include "Actor/AVP_Actor_CameraHandler.h"
#include "Core/AVP_Game_Core.h"
#include "Entity/PlayerInfo/AVP_ClientEntity_PlayerInfo.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"

/********/
/* Data */
/********/

#ifdef AVP_SHAKE_DEBUG
static AVP_CameraShake_Parameters g_xTestParameters;
static bool g_bShowDebug = false;
#endif

static const Asura_Hash_ID uAVP_BP_TYPE_CAMERASHAKE = Asura_GetHashID("Players");

static const Asura_Hash_ID uAVP_BP_PARAM_AMPLITUDE_HORIZONTAL	= 0x87257BAE; //Asura_GetHashID("Amplitude-Horizontal")
static const Asura_Hash_ID uAVP_BP_PARAM_AMPLITUDE_VERTICAL		= 0xCB96B180; //Asura_GetHashID("Amplitude-Vertical")
static const Asura_Hash_ID uAVP_BP_PARAM_AMPLITUDE_TWIST		= 0x1C61595D; //Asura_GetHashID("Amplitude-Twist")
static const Asura_Hash_ID uAVP_BP_PARAM_FREQUENCY_HORIZONTAL	= 0xFFF7C135; //Asura_GetHashID("Frequency-Horizontal")
static const Asura_Hash_ID uAVP_BP_PARAM_FREQUENCY_VERTICAL		= 0x2D7AACC7; //Asura_GetHashID("Frequency-Vertical")
static const Asura_Hash_ID uAVP_BP_PARAM_FREQUENCY_TWIST		= 0x847D7B76; //Asura_GetHashID("Frequency-Twist")
static const Asura_Hash_ID uAVP_BP_PARAM_DURATION				= 0x89444D94; //Asura_GetHashID("Duration")
static const Asura_Hash_ID uAVP_BP_PARAM_RANGE					= 0x0674393D; //Asura_GetHashID("Range")
static const Asura_Hash_ID uAVP_BP_PARAM_CURVE					= 0x05AA00EF; //Asura_GetHashID("Curve")

static const Asura_Hash_ID uAVP_BP_PARAM_FLAG_SOURCE_ONLY				= 0x8DC7BCFA; //Asura_GetHashID("Flag_SourceOnly")
static const Asura_Hash_ID uAVP_BP_PARAM_FLAG_SOURCE_IGNORE_DESTANCE	= 0x62356B95; //Asura_GetHashID("Flag_SourceIgnoreDistance")

// static members 
AVP_CameraShake AVP_CameraShake_System::s_axCameraShakes[uAVP_MAX_CAMERA_SHAKES];

const float AVP_CameraShake::s_fNLog = 2.7182818284590f;
const float AVP_CameraShake::s_fInvNLog = 1.0f / AVP_CameraShake::s_fNLog;
float (*AVP_CameraShake::s_apfnCurveFns[AVP_CAMERASHAKE_CURVE_MAX])(float) =
{
	NULL,
	Curve_Const_Inc,
	Curve_Const_Dec,
	Curve_Inc_Inc,
	Curve_Inc_Dec,
	Curve_Dec_Inc,
	Curve_Dec_Dec,
	Curve_Pulse,
};

/*************/
/* Functions */
/*************/

#if defined(AVP_SHAKE_DEBUG)
static bool ConsoleCmd_StartTest( u_int uSourceGuid , bool bHelp, bool bInterrogate )
{
	if (bHelp || bInterrogate)
	{
		Asura_CommandConsole::Printf("Usage: Shake.StartTest <SourceGuid>" );
		Asura_CommandConsole::Printf("Starts a camera shake set up in Shake.Test.XXX with the source" );
		Asura_CommandConsole::Printf("guid supplied (or the local player if no guid is supplied)" );
	}
	else
	{
		Asura_Guid uPlayerGuid = AVP_Game_Core::GetPlayerGuid();
		if(uSourceGuid)
		{
			AVP_CameraShake_System::StartShake( g_xTestParameters, uSourceGuid );
		}
		else
		{
			AVP_ClientEntity_PlayerInfo* pxPlayerInfo = AVP_ClientEntity_PlayerInfo::FindEntity(uPlayerGuid);
			if(pxPlayerInfo)
			{
				Asura_Guid uPuppetGuid = pxPlayerInfo->GetPuppetGuid();
				AVP_CameraShake_System::StartShake( g_xTestParameters, uPuppetGuid );
			}
		}		
	}

	return true;
}

static bool ConsoleCmd_Start( const Asura_Char* szShake, bool bHelp, bool bInterrogate )
{
	if (bHelp || bInterrogate)
	{
		Asura_CommandConsole::Printf("Usage: Shake.Start <Shake Blueprint Name>" );
		Asura_CommandConsole::Printf("Starts a camera shake with Player 1 as the source" );
	}
	else
	{
		Asura_Hash_ID uBlueprint = Asura_GetHashID( szShake );
		Asura_Guid uPlayerGuid = AVP_Game_Core::GetPlayerGuid();
		AVP_ClientEntity_PlayerInfo* pxPlayerInfo = AVP_ClientEntity_PlayerInfo::FindEntity(uPlayerGuid);
		if(pxPlayerInfo)
		{
			Asura_Guid uPuppetGuid = pxPlayerInfo->GetPuppetGuid();
			AVP_CameraShake_System::StartShake( uBlueprint, uPuppetGuid );
		}
	}
	
	return true;
}

static bool ConsoleCmd_Stop( bool bHelp )
{
	if (bHelp)
	{
		Asura_CommandConsole::Printf("Usage: Shake.Stop" );
		Asura_CommandConsole::Printf("Stops all camera shakes started by Shake.Start" );
	}
	else
	{
		Asura_Guid uPlayerGuid = AVP_Game_Core::GetPlayerGuid();
		AVP_ClientEntity_PlayerInfo* pxPlayerInfo = AVP_ClientEntity_PlayerInfo::FindEntity(uPlayerGuid);
		if(pxPlayerInfo)
		{
			Asura_Guid uPuppetGuid = pxPlayerInfo->GetPuppetGuid();
			AVP_CameraShake_System::StopShake( uPuppetGuid );
		}
	}

	return true;
}
#endif


void AVP_CameraShake::SetCommonParameters( AVP_CameraShake_Parameters& xParameters )
{
	m_xAmplitude.x	= m_xInitialAmplitude.x = xParameters.m_xAmplitude.x;
	m_xAmplitude.y	= m_xInitialAmplitude.y = xParameters.m_xAmplitude.y;
	m_xAmplitude.z	= m_xInitialAmplitude.z = xParameters.m_xAmplitude.z;
	m_xFrequency.x							= xParameters.m_xFrequency.x;
	m_xFrequency.y							= xParameters.m_xFrequency.y;
	m_xFrequency.z							= xParameters.m_xFrequency.z;
	m_fTimer								= 0.0f;
	m_fScale								= 1.0f;
	m_fDuration								= xParameters.m_fDuration;	
	m_fMaxDistSqrd							= xParameters.m_fMaxDist * xParameters.m_fMaxDist;

	m_uCurve								= xParameters.m_uCurve;

	m_bActive								= true;

	m_uFlags								= xParameters.m_uFlags;

	m_xRandom.SetX( 1.0f );
	m_xRandom.SetY( Asura_Random::GetIntU(0, 1) ? 1.0f : -1.0f );
	m_xRandom.SetZ( Asura_Random::GetIntU(0, 1) ? 1.0f : -1.0f );
}


/*************************************************************
 *
 * Name          : AVP_CameraShake::Start
 *
 * Author        : Andrew McDonald
 *
 * Description   : Start a camera shake off
 *
 *************************************************************/
// entity-based
void AVP_CameraShake::Start(AVP_CameraShake_Parameters& xCommon, Asura_Guid uSourceGuid )
{
	Asura_Assert(xCommon.m_fDuration > 0.0f || uSourceGuid != ASURA_GUID_UNREGISTERED, "Shake must attenuate by either time or source distance.");

	SetCommonParameters( xCommon );

	m_uSourceGuid				= uSourceGuid;
	
	//This shouldn't change
	m_iSourceCamera = (AVP_CameraShake_System::IsSourceLocalPlayer(m_uSourceGuid)) ? 0 : -1;
	Asura_Assert( !(AVP_CameraShake_System::IsSourceLocalPlayer(m_uSourceGuid) && (m_iSourceCamera < 0)),"Couldn't find source camera - should have already been culled");

	m_bPositionless = false;
	m_xPosition.Zero();

	// Do an update in case the source entity is about to die
	Update( true );
}

// position-based
void AVP_CameraShake::Start(AVP_CameraShake_Parameters& xCommon, const Asura_Vector_3& xSourcePosition )
{
	SetCommonParameters( xCommon );

	m_uSourceGuid			= ASURA_GUID_UNREGISTERED;
	m_iSourceCamera			= -1;

	m_bPositionless = false;
	m_xPosition		= xSourcePosition;
}

// purely time-based (position less)
void AVP_CameraShake::Start( AVP_CameraShake_Parameters& xCommon )
{
	SetCommonParameters( xCommon );

	m_uSourceGuid			= ASURA_GUID_UNREGISTERED;
	m_iSourceCamera			= -1;

	m_fMaxDistSqrd	= 0.0f;
	m_bPositionless = true;
	m_xPosition.Zero();
}
/* end of function Start */

bool AVP_CameraShake::IsActiveForCamera( int iCamera ) const
{
#ifdef ASURA_SUPPORT_FLYCAM
	if (AVP_Actor_CameraHandler::GetFixCam() || AVP_Actor_CameraHandler::GetFlyCam())
	{
		return false;
	}
#endif //#ifdef ASURA_SUPPORT_FLYCAM

	bool bResult = m_bActive;
	
	if (m_bActive)
	{
		if (m_uFlags & AVP_CAMERASHAKE_FLAG_SOURCE_ONLY)
		{
			bResult = (m_iSourceCamera == iCamera);
		}
	}

	return bResult;
}

/*************************************************************
 *
 * Name          : AVP_CameraShake::Update
 *
 * Author        : Andrew McDonald
 * 
 * Description   : Update a camera shake; call once per frame, passing the location of the camera in
 *                 (For source-based shakes)
 *
 *************************************************************/
void AVP_CameraShake::Update( bool bIgnoreTime /*= false*/ )
{
	if( !m_bActive ) return;

	// Some reduce in intensity based on distance from their source
	if( !m_bPositionless )
	{	
		if( m_uSourceGuid != ASURA_GUID_UNREGISTERED )
		{
			// First look for the entity in the renderable list	
			const Asura_Entity_Renderable* const pxRenderableEntity = Asura_Entity_Renderable::FindEntity(m_uSourceGuid);

			if (pxRenderableEntity)
			{
				m_xPosition = pxRenderableEntity->GetCurrentPosition();
			}
			// If it's not there, look in the cutscene actor list
			else
			{
				Asura_Cutscene_Actor* const pxCutsceneActor = Asura_Cutscene_System::FindCutsceneActor(m_uSourceGuid);
				if( pxCutsceneActor )
				{
					m_xPosition = pxCutsceneActor->Position;
				}
				else
				{
					if (m_xPosition.MagnitudeSquared() == 0.0f)
					{
						Asura_Assert(false, "Could not find shake source entity.");
						m_bActive = false;
					}
					else
					{
						// We had an entity but it died - keep position
						// Also keep controller - presumably it wont die
						m_uSourceGuid = ASURA_GUID_UNREGISTERED;
					}
				}
			}
		}
	}
	
	//Curve amplitude
	m_xAmplitude.x = m_xInitialAmplitude.x * GetCurve(m_uCurve);		//TODO: m_uShakeCurve
	m_xAmplitude.y = m_xInitialAmplitude.y * GetCurve(m_uCurve);		//TODO: m_uShakeCurve
	m_xAmplitude.z = m_xInitialAmplitude.z * GetCurve(m_uCurve);		//TODO: m_uShakeCurve

	if (m_xAmplitude.x < 0.0f)
	{
		m_xAmplitude.x = 0.0f;
	}
	if (m_xAmplitude.y < 0.0f)
	{
		m_xAmplitude.y = 0.0f;
	}
	if (m_xAmplitude.z < 0.0f)
	{
		m_xAmplitude.z = 0.0f;
	}

	if (!bIgnoreTime) m_fTimer += Asura_Timers::GetGameTimeSlice();
	if ((m_fTimer > m_fDuration) && (m_fDuration > 0.0f))
	{
		m_bActive = false;
	}
}
/* end of function Update */

bool AVP_CameraShake::CalculateDistanceCurve( Asura_Vector_3& xCamPos, float& fDistanceCurve)
{
	bool bResult = false; //True if in range

	if (IsIgnoringDistanceForCamera( Asura_Camera::GetCameraIndex() ))
	{
		fDistanceCurve = 1.0f;
		bResult = true;
	}
	else
	{
		//Linear dropoff for now - could use an user defined curve if we wanted.
		float fDistanceSqrd = (xCamPos - m_xPosition).MagnitudeSquared();
		if ((fDistanceSqrd <= m_fMaxDistSqrd) && (m_fMaxDistSqrd > 0.0f))
		{
			fDistanceCurve = (1.0f - (fDistanceSqrd / m_fMaxDistSqrd));
			bResult = true;
		}
	}
	
	return bResult;
}

bool AVP_CameraShake::IsIgnoringDistanceForCamera( int iCamera ) const
{
	bool bResult = false;
	if (m_uFlags & AVP_CAMERASHAKE_FLAG_SOURCE_IGNORE_DISTANCE)
	{
		bResult = (m_iSourceCamera == iCamera);
	}

	return bResult || m_bPositionless;
}

/*************************************************************
 *
 * Name          : AVP_CameraShake::Apply
 *
 * Author        : Andrew McDonald
 *
 * Description   : Apply a camera shake by perturbing the given orientation matrix
 *
 *************************************************************/
void AVP_CameraShake::Apply( Asura_Matrix_3x3& xOrientation, float fDistCurve )
{
	if (!m_bActive) return;

	Asura_Vector_3 xAmplitude;
	xAmplitude.x = m_xAmplitude.x * fDistCurve * m_fScale;
	xAmplitude.y = m_xAmplitude.y * fDistCurve * m_fScale;
	xAmplitude.z = m_xAmplitude.z * fDistCurve * m_fScale;

	// Random vector adds a random element to the *initial* rotation of the shake
	float fX = xAmplitude.x * Asura_Maths::Sine(m_xFrequency.x * m_fTimer) * m_xRandom.x;
	float fY = xAmplitude.y * 0.3f * Asura_Maths::Sine(m_xFrequency.y * 0.7f * m_fTimer) * m_xRandom.y;
	float fZ = xAmplitude.z * 0.5f * Asura_Maths::Sine(m_xFrequency.z * 2.3f * m_fTimer) * m_xRandom.z;

	Asura_Matrix_3x3 xMatrix;
	Asura_Euler_3(fX, fY, fZ).ToMatrix(xMatrix);

	xOrientation = xOrientation * xMatrix;
}
/* end of function Apply */

#ifdef AVP_SHAKE_DEBUG
void AVP_CameraShake::DebugOutputShake( float fAmplitude )
{
// 	if (g_bShowDebug)
// 	{
// 		char acText[100];
// 
// 		//Both
// 		Asura_PrintfToBfr( acText, "Scale: %.3f", 100, true, Asura_Maths::FtoD(m_fScale) );
// 		AVP_DebugInfo_Text::Create( acText, m_xPosition - Asura_Vector_3(0.0f, 1.2f, 0.0f), AVP_DebugInfo_Text::ColourBrown(), 0.0f );
// 
// 		//Shake
// 		Asura_PrintfToBfr( acText, "Distance Curve: %.3f", 100, true, Asura_Maths::FtoD(fAmplitude/m_fAmplitude) );
// 		AVP_DebugInfo_Text::Create( acText, m_xPosition - Asura_Vector_3(0.0f, 1.0f, 0.0f), AVP_DebugInfo_Text::ColourAqua(), 0.0f );
// 
// 		Asura_PrintfToBfr( acText, "Shake Curve: %.3f", 100, true, Asura_Maths::FtoD(m_fAmplitude/m_fInitialAmplitude) );
// 		AVP_DebugInfo_Text::Create( acText, m_xPosition - Asura_Vector_3(0.0f, 0.8f, 0.0f), AVP_DebugInfo_Text::ColourAqua(), 0.0f );
// 
// 		Asura_PrintfToBfr( acText, "Final Value: %.3f", 100, true, Asura_Maths::FtoD(fAmplitude) );
// 		AVP_DebugInfo_Text::Create( acText, m_xPosition - Asura_Vector_3(0.0f, 0.6f, 0.0f), AVP_DebugInfo_Text::ColourAqua(), 0.0f );
// 	}
}
#endif

float AVP_CameraShake::GetCurve( u_int uCurve )
{
	//TODO: Pass parametric time with curve, parametric time calc'ed in updateshake -- Put line below in update to avoid it being recalculated
	Asura_Assert((uCurve == AVP_CAMERASHAKE_CURVE_NONE) || (m_fDuration > 0.0f), "Trying to use curve on camera shake without a duration");
	Asura_Assert(uCurve < AVP_CAMERASHAKE_CURVE_MAX,"Invalid Curve");

	if ((uCurve > AVP_CAMERASHAKE_CURVE_NONE) && (uCurve < AVP_CAMERASHAKE_CURVE_MAX))
	{
		float fParametricTime = (m_fDuration > 0.0f) ? Asura_Maths::ClampToRange(m_fTimer / m_fDuration, 0.0f, 1.0f) : 1.0f;
		return s_apfnCurveFns[uCurve](fParametricTime);
	}

	return 1.0f;
}

// System ///////////////////////////////////////////////////


/*************************************************************
 *
 * Name          : AVP_CameraShake_System::Initialise
 *
 * Author        : Richard May
 *
 * Description   : Setup the system
 *
 *************************************************************/
void AVP_CameraShake_System::Initialise( void )
{
	for( u_int u=0; u<uAVP_MAX_CAMERA_SHAKES; ++u )
	{
		s_axCameraShakes[u].Deactivate();
	}

#if defined(AVP_SHAKE_DEBUG)
	InitialiseDebug();
#endif
}
/* end of function Initialise */

#if defined(AVP_SHAKE_DEBUG)
void AVP_CameraShake_System::InitialiseDebug()
{
	if (!Asura_CommandConsole::CmdExists(L"Shake.Start"))
	{
		//Asura_CommandConsole::AddVar( L"Shake.RenderDebug", &g_bShowDebug );

		Asura_CommandConsole::AddVar( L"Shake.Test.Amplitude.X",	&g_xTestParameters.m_xAmplitude.x );
		Asura_CommandConsole::AddVar( L"Shake.Test.Amplitude.Y",	&g_xTestParameters.m_xAmplitude.y );
		Asura_CommandConsole::AddVar( L"Shake.Test.Amplitude.Z",	&g_xTestParameters.m_xAmplitude.z );
		Asura_CommandConsole::AddVar( L"Shake.Test.Duration",		&g_xTestParameters.m_fDuration );
		Asura_CommandConsole::AddVar( L"Shake.Test.Frequency.X",	&g_xTestParameters.m_xFrequency.x );
		Asura_CommandConsole::AddVar( L"Shake.Test.Frequency.Y",	&g_xTestParameters.m_xFrequency.y );
		Asura_CommandConsole::AddVar( L"Shake.Test.Frequency.Z",	&g_xTestParameters.m_xFrequency.z );
		Asura_CommandConsole::AddVar( L"Shake.Test.MaxDist",		&g_xTestParameters.m_fMaxDist );
		Asura_CommandConsole::AddVar( L"Shake.Test.Curve",			&g_xTestParameters.m_uCurve );
		Asura_CommandConsole::AddVar( L"Shake.Test.Flags",			&g_xTestParameters.m_uFlags );

		Asura_CommandConsole::AddCmd( L"Shake.StartTest", ConsoleCmd_StartTest );
		Asura_CommandConsole::AddCmd( L"Shake.Start", ConsoleCmd_Start );
		Asura_CommandConsole::AddCmd( L"Shake.Stop", ConsoleCmd_Stop );
	}

	g_xTestParameters.m_xAmplitude.Zero();
	g_xTestParameters.m_xFrequency.Zero();
	g_xTestParameters.m_fDuration = 0.0f;
	g_xTestParameters.m_fMaxDist = 0.0f;
	g_xTestParameters.m_uCurve = 0;
	g_xTestParameters.m_uFlags = 0;
}
#endif

/*************************************************************
 *
 * Name          : AVP_CameraShake_System::Shutdown
 *
 * Author        : Richard May
 * 
 * Description   : Setup the system
 *
 *************************************************************/
void AVP_CameraShake_System::Shutdown( void )
{
	StopAllShakes();
}
/* end of function Shutdown */

/*************************************************************
 *
 * Name          : AVP_CameraShake_System::Reset
 *
 * Author        : Marten Carlzon
 * 
 * Description   : Reset the system
 *
 *************************************************************/
void AVP_CameraShake_System::Reset( void )
{
	for( u_int u=0; u<uAVP_MAX_CAMERA_SHAKES; ++u )
	{
		s_axCameraShakes[u].Deactivate();
	}
	StopAllShakes();
}

/*************************************************************
 *
 * Name          : BF3_CameraShake_System::Update
 *
 * Author        : Richard May
 *
 * Description   : Updates all the shakes in the system
 *
 *************************************************************/
void AVP_CameraShake_System::Update( void )
{
	for( u_int u=0; u<uAVP_MAX_CAMERA_SHAKES; ++u )
	{
		if( s_axCameraShakes[u].IsActive() )
		{
			s_axCameraShakes[u].Update();
		}
	}
}
/* end of function Update */



/*************************************************************
 *
 * Name          : BF3_CameraShake_System::Update
 *
 * Author        : Richard May
 *
 * Description   : Updates all the shakes in the system
 *
 *************************************************************/
void AVP_CameraShake_System::ApplyShakes( void )
{
	for( int iCameraIndex = 0; iCameraIndex < Asura_Camera::GetNumberOfCameras(); ++iCameraIndex )
	{
		Asura_Camera::SetCameraIndex( iCameraIndex );
		Asura_Matrix_3x3 xOrientation = *Asura_Camera::GetOrientation();
		Asura_Vector_3 xPosition = *Asura_Camera::GetPosition();		

		for( u_int u=0; u<uAVP_MAX_CAMERA_SHAKES; ++u )
		{
			AVP_CameraShake& xCurrentShake = s_axCameraShakes[u];

			if( xCurrentShake.IsActive() && xCurrentShake.IsActiveForCamera( Asura_Camera::GetCameraIndex() ) )
			{
				float fDistCurve = 0.0f;
				if ( xCurrentShake.CalculateDistanceCurve( xPosition, fDistCurve ) )
				{
					xCurrentShake.Apply( xOrientation, fDistCurve );
				}
			}
		}

		Asura_Camera::SetOrientation( xOrientation );
	}
}
/* end of function Update */

/*************************************************************
 *
 * Name          : AVP_CameraShake_System::ShouldCreateShake
 *
 * Author        : Richard May
 *
 * Description   : Should a camera shake be created at the given position?			   
 *
 *************************************************************/
bool AVP_CameraShake_System::ShouldCreateShake( float fDuration, Asura_Guid uSourceGuid, const Asura_Vector_3* const pxPosition, float fMaxDist, u_int uFlags, u_int* puSlot/*=NULL*/ )
{
	// is there space for a new shake?
	u_int uSlot = 0;
	for( ; uSlot<uAVP_MAX_CAMERA_SHAKES; ++uSlot )
	{
		if( !s_axCameraShakes[uSlot].IsActive() )
		{			
			if( puSlot )
			{
				*puSlot = uSlot;
			}

			break;
		}
	}

	if( uSlot < uAVP_MAX_CAMERA_SHAKES )
	{
		if( (pxPosition == NULL) && (uSourceGuid == ASURA_GUID_UNREGISTERED) )
		{
			// positionless (and sourceless) shake, always make it
			return true;
		}
		else if (fDuration <= 0.0f)
		{
			return true;
		}
		else
		{
			// is the shake intended for our machine?
			if ((uFlags & AVP_CAMERASHAKE_FLAG_SOURCE_ONLY) || (uFlags & AVP_CAMERASHAKE_FLAG_SOURCE_IGNORE_DISTANCE))
			{
				bool bIsSourceLocalPlayer = IsSourceLocalPlayer(uSourceGuid);
				
				if ((uFlags & AVP_CAMERASHAKE_FLAG_SOURCE_ONLY) && !bIsSourceLocalPlayer)
				{
					//We definitely won't be able to play (can't find the only player which will be effected)
					return false;
				}

				if ((uFlags & AVP_CAMERASHAKE_FLAG_SOURCE_IGNORE_DISTANCE) && bIsSourceLocalPlayer)
				{
					//We'll definitely be able to play for the source player (i.e. don't worry about position)
					return true;
				}
			}

			// is the shake actually near a camera?
			Asura_Vector_3 xShakePosition;
			if( pxPosition )
			{
				xShakePosition = *pxPosition;
			}
			else
			{
				// check for renderable entities
				const Asura_Entity_Renderable* const pxRenderableEntity = Asura_Entity_Renderable::FindEntity( uSourceGuid );
				if( pxRenderableEntity )
				{
					xShakePosition = pxRenderableEntity->GetCurrentPosition();
				}
				// If it's not there, look in the cutscene actor list
				else
				{
					const Asura_Cutscene_Actor* const pxCutsceneActor = Asura_Cutscene_System::FindCutsceneActor( uSourceGuid );
					if( pxCutsceneActor )
					{
						xShakePosition = pxCutsceneActor->Position;
					}
					else
					{
						Asura_Assert( false, "No way to get Camera Shake position, using the origin" );
						xShakePosition = Asura_Maths::ZeroVector3;
					}
				}
			}
			
			// check each camera in turn
			for( int iCameraIndex = 0; iCameraIndex < Asura_Camera::GetNumberOfCameras(); ++iCameraIndex )
			{
				Asura_Camera::SetCameraIndex( iCameraIndex );
				const Asura_Vector_3* pxCameraPosition = Asura_Camera::GetPosition();		

				if( (xShakePosition - *pxCameraPosition).Magnitude() < fMaxDist )
				{
					// near enough to a camera, return true
					return true;
				}
			}
		}
	}
	else
	{
		Asura_OutputToDebugger( ( "AVP_CameraShake_System::StartShake - run out of shake space.\n" ) );
	}	
	
	return false;
}
/* end of function ShouldCreateShake */


/*************************************************************
 *
 * Name          : AVP_CameraShake_System::StartShake
 *
 * Author        : Richard May
 *
 * Description   : Various shake creation overloads. Checks whether the
 *				   shake should be created before adding it to the system
 *
 *************************************************************/
void SetParametersFromBlueprint(AVP_CameraShake_Parameters& xParameters, Asura_Blueprint* pxBlueprint)
{
	if(pxBlueprint)
	{
		xParameters.m_xAmplitude.x = pxBlueprint->GetParameterAsFloat( uAVP_BP_PARAM_AMPLITUDE_HORIZONTAL, 0.0f );
		xParameters.m_xAmplitude.y = pxBlueprint->GetParameterAsFloat( uAVP_BP_PARAM_AMPLITUDE_VERTICAL, 0.0f );
		xParameters.m_xAmplitude.z = pxBlueprint->GetParameterAsFloat( uAVP_BP_PARAM_AMPLITUDE_TWIST, 0.0f );
		xParameters.m_xFrequency.x = pxBlueprint->GetParameterAsFloat( uAVP_BP_PARAM_FREQUENCY_HORIZONTAL, 0.0f );
		xParameters.m_xFrequency.y = pxBlueprint->GetParameterAsFloat( uAVP_BP_PARAM_FREQUENCY_VERTICAL, 0.0f );
		xParameters.m_xFrequency.z = pxBlueprint->GetParameterAsFloat( uAVP_BP_PARAM_FREQUENCY_TWIST, 0.0f );
		xParameters.m_fDuration = pxBlueprint->GetParameterAsFloat( uAVP_BP_PARAM_DURATION, 0.0f );
		xParameters.m_uCurve = pxBlueprint->GetParameterAsUInt( uAVP_BP_PARAM_CURVE, static_cast<u_int>(AVP_CAMERASHAKE_CURVE_NONE) );
	
		xParameters.m_uFlags = 0;
	
		if (pxBlueprint->GetParameterAsBool( uAVP_BP_PARAM_FLAG_SOURCE_ONLY, false ))			xParameters.m_uFlags |= AVP_CAMERASHAKE_FLAG_SOURCE_ONLY;
		if (pxBlueprint->GetParameterAsBool( uAVP_BP_PARAM_FLAG_SOURCE_IGNORE_DESTANCE, false ))	xParameters.m_uFlags |= AVP_CAMERASHAKE_FLAG_SOURCE_IGNORE_DISTANCE;
	}
}

void AVP_CameraShake_System::StartShake( Asura_Hash_ID uBlueprint, Asura_Guid uSourceGuid /*= ASURA_GUID_UNREGISTERED*/ )
{
	if (uBlueprint != ASURA_HASH_ID_UNSET)
	{
		//TODO: Can we cache the blueprint values somehow? Is this too slow?
		Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BP_TYPE_CAMERASHAKE, uBlueprint );
		Asura_Assert(pxBlueprint,"Couldn't find blueprint 0x%08x for camera shake");
		if (pxBlueprint)
		{
			AVP_CameraShake_Parameters xParameters;
			
			SetParametersFromBlueprint(xParameters, pxBlueprint);
		
			if (uSourceGuid != ASURA_GUID_UNREGISTERED)
			{
				xParameters.m_fMaxDist = pxBlueprint->GetParameterAsFloat( uAVP_BP_PARAM_RANGE, 0.0f );
				StartShake( xParameters, uSourceGuid );
			}
			else
			{
				StartShake( xParameters );
			}
		}
	}
}

bool AVP_CameraShake_System::IsSourceLocalPlayer( Asura_Guid uSourceGuid )
{
	// First, find the source - if it is a player, it must be an actor
	AVP_ClientEntity_Actor* pxActor = AVP_ClientEntity_Actor::FindEntity(uSourceGuid);
	if(pxActor)
	{
		if(pxActor->GetPlayerInfoGuid() == AVP_Game_Core::GetPlayerGuid())
		{
			return true;
		}
	}
	return false;
}

void AVP_CameraShake_System::StartShake( AVP_CameraShake_Parameters& xParameters, Asura_Guid uSourceGuid )
{
	u_int uSlot = 0;
	if( ShouldCreateShake( xParameters.m_fDuration, uSourceGuid, NULL, xParameters.m_fMaxDist, xParameters.m_uFlags, &uSlot ) )
	{
		s_axCameraShakes[uSlot].Start( xParameters, uSourceGuid );
	}
}

void AVP_CameraShake_System::StartShake( AVP_CameraShake_Parameters& xParameters, const Asura_Vector_3& xSourcePosition )
{
	u_int uSlot = 0;
	if( ShouldCreateShake( xParameters.m_fDuration, ASURA_GUID_UNREGISTERED, &xSourcePosition, xParameters.m_fMaxDist, xParameters.m_uFlags, &uSlot ) )
	{
		s_axCameraShakes[uSlot].Start( xParameters, xSourcePosition );
	}
}

void AVP_CameraShake_System::StartShake( AVP_CameraShake_Parameters& xParameters )
{
	u_int uSlot = 0;
	if( ShouldCreateShake( xParameters.m_fDuration, ASURA_GUID_UNREGISTERED, NULL, 0.0f, xParameters.m_uFlags, &uSlot ) )
	{
		s_axCameraShakes[uSlot].Start( xParameters );
	}
}
/* end of function StartShake */



/*************************************************************
 *
 * Name          : AVP_CameraShake_System::StopShake
 *
 * Author        : Richard May
 *
 * Description   : Stops all shakes belonging to the given source guid
 *
 *************************************************************/
void AVP_CameraShake_System::StopShake( Asura_Guid uSourceGuid )
{
	for( u_int u=0; u<uAVP_MAX_CAMERA_SHAKES; ++u )
	{
		if( s_axCameraShakes[u].GetSourceGuid() == uSourceGuid )
		{
			s_axCameraShakes[u].Deactivate();
		}
	}
}
/* end of function StopShake */

void AVP_CameraShake_System::ScaleShake( Asura_Guid uSourceGuid, float fScale )
{
	Asura_Assert(0.0f <= fScale,"Scale should not be negative");

	if (fScale >= 0.0f)
	{
		for( u_int u=0; u<uAVP_MAX_CAMERA_SHAKES; ++u )
		{
			if( s_axCameraShakes[u].GetSourceGuid() == uSourceGuid )
			{
				s_axCameraShakes[u].SetScale( fScale );
			}
		}
	}
}

/*************************************************************
 *
 * Name          : AVP_CameraShake_System::StopAllShakes
 *
 * Author        : Richard May
 *
 * Description   : Stops all shakes
 *
 *************************************************************/
void AVP_CameraShake_System::StopAllShakes( void )
{
	for( u_int u=0; u<uAVP_MAX_CAMERA_SHAKES; ++u )
	{
		s_axCameraShakes[u].Deactivate();
	}
}

/* end of function StopAllShakes */





/***************/
/* End of File */
/***************/
