
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_Vertebrate_InputHandler.cpp
// Author : Chris Wallace
// Date   : 17-Apr-2007
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header...
#include "Actor/Vertebrates/AVP_Vertebrate_InputHandler.h"

// Asura
#include "Attractors/Asura_Attractor_System.h"
#include "Blueprint/Asura_Blueprint.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "Camera/Asura_Camera.h"
#include "Input/Asura_ControlMapping_Key.h"
#include "Interface/Asura_CommandConsole.h"

// AVP
#include "Actor/AVP_Actor_AnimationHandler.h"
#include "Actor/AVP_Actor_HUDHandler.h"
#include "Actor/AVP_Actor_PickupHandler.h"
#include "Actor/AVP_Actor_PlayerAnimStateHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_AnimationHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_CameraHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_Focus.h"
#include "Actor/Vertebrates/AVP_Vertebrate_JumpHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_MeleeCounter_System.h"
#include "Animation/AVP_Animation_BodyRig.h"
#include "Animation/AVP_AnimFlags.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Core/AVP_Game_Core.h"
#include "Core/AVP_LOS.h"
#include "Core/AVP_Settings.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_ClientActor_Vertebrate.h"
#include "Entity/AVP_Messages.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_Vertebrate_Messages.h"
#if defined(ASURA_USE_GUI_MENU_SYSTEM)
#include "GUIMenu/AVP_GUIMenu_System.h"
#endif
#include "Inventory/AVP_Inventory_HashIDs.h"
#include "Inventory/AVP_Inventory_Group.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Inventory/Items/AVP_InventoryItem_Weapon.h"
#include "Inventory/Items/AVP_InventoryItem_Stim.h"
#include "Inventory/Items/Weapons/AVP_InventoryWeapon_RangedWeapon.h"
#include "Moves/AVP_Movement_System.h"
#include "Moves/AVP_Moves.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/AVP_Font_System.h"

DEBUGVAR float dbg_fInputDeadZone		= 0.1f;

DEBUGVAR float dbg_fGimbalEpsilon		= Asura_Maths::DegToRad(2.0f);

DVSVAR float dvs_fZoomResetTime	= 1.0f;
DVSVAR float dvs_fZoomHoldTime	= 0.2f;

bool AVP_Vertebrate_InputHandler::s_bOnlyLeaveBranchByJump = false;
#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
int AVP_Vertebrate_InputHandler::s_iConsoleVarsCount	= 0;
#endif //#ifdef ASURA_SUPPORT_COMMAND_CONSOLE

AVP_Vertebrate_InputHandler::AVP_Vertebrate_InputHandler (const Asura_Guid uGuid)
: AVP_Actor_InputHandler(uGuid, TOTAL_CONTROL_MAPPINGS)
, m_fZoomTimer( 0.0f )
, m_fZoomHoldTimer( 0.0f )
, m_fSprintTime( 3.0f )
, m_fSprintTimer( 0.0f )
, m_fSprintChargeTime( 1.0f )
, m_fSprintChargeTimer( 0.0f )
, m_xFlatAirVelocity(Asura_Maths::ZeroVector2)
, m_fMaxAirSpeed(0.0f)
, m_fAirAcceleration(100.0f)
, m_bZoomed( false )
{
#ifdef ASURA_DEBUG_VARIABLE_SYSTEM
	if (s_iConsoleVarsCount == 0)
	{
		if(!Asura_CommandConsole::VarExists(L"AVP.Branch.JumpDetachOnly"))
		{
			Asura_CommandConsole::AddVar(L"AVP.Branch.JumpDetachOnly", &s_bOnlyLeaveBranchByJump);
		}
		if ( !Asura_CommandConsole::VarExists( L"Controls.Zoom.ResetTime" ) )
		{
			Asura_CommandConsole::AddVar( L"Controls.Zoom.ResetTime", &dvs_fZoomResetTime );
		}
		if ( !Asura_CommandConsole::VarExists( L"Controls.Zoom.HoldButtonTime" ) )
		{
			Asura_CommandConsole::AddVar( L"Controls.Zoom.HoldButtonTime", &dvs_fZoomHoldTime );
		}
	}
	++s_iConsoleVarsCount;
#endif //#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
}

AVP_Vertebrate_InputHandler::~AVP_Vertebrate_InputHandler()
{
#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
	--s_iConsoleVarsCount;
#endif //#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
}

void AVP_Vertebrate_InputHandler::Reset( void )
{
	const AVP_ClientActor_Vertebrate* pxVert = AVP_ClientActor_Vertebrate::FindEntity( m_uOwnerGuid );
	if ( pxVert )
	{
		const Asura_Matrix_3x3& xOrientation = pxVert->GetCurrentOrientation();
		m_xDesiredLookDirection = Asura_AVector3( xOrientation.e13, xOrientation.e23, xOrientation.e33 );
	}

	m_xCurrentRecoilOffset.Zero();
	m_xLastRecoilOffset.Zero();
	m_xCurrentRecoilVelocity.Zero();
	m_xLastRecoilVelocity.Zero();
	m_xLastRecoilForce.Zero();

	PARENT::Reset();
}

void AVP_Vertebrate_InputHandler::MapMovementAxes(Asura_ControlMap& xMap,
												  const u_int uAxisForwardsBackwards,
												  const u_int uAxisStrafeLeftRight,
												  const u_int uKeySlow,
												  const u_int uKeyFast,
												  const u_int uKeyStanceToggle)
{
	PARENT::MapMovementAxes(xMap, uAxisForwardsBackwards, uAxisStrafeLeftRight, uKeySlow, uKeyFast, uKeyStanceToggle);

	xMap.AddKeyMapping(AVP_Vertebrate_InputHandler::SLOW, uKeySlow, Asura_ControlMapping::ON_PRESS);
	xMap.AddKeyMapping(AVP_Vertebrate_InputHandler::FAST, uKeyFast, Asura_ControlMapping::ON_PRESS);
	xMap.AddKeyMapping(AVP_Vertebrate_InputHandler::STANCE_TOGGLE, uKeyStanceToggle, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
}

void AVP_Vertebrate_InputHandler::MapMovementKeys(Asura_ControlMap& xMap,
												  const u_int uKeyForwards,
												  const u_int uKeyBackwards,
												  const u_int uKeyStrafeLeft,
												  const u_int uKeyStrafeRight,
												  const u_int uKeySlow,
												  const u_int uKeyFast,
												  const u_int uKeyStanceToggle)
{
	PARENT::MapMovementKeys(xMap, uKeyForwards, uKeyBackwards, uKeyStrafeLeft, uKeyStrafeRight, uKeySlow, uKeyFast, uKeyStanceToggle);

	xMap.AddKeyMapping(AVP_Vertebrate_InputHandler::SLOW, uKeySlow, Asura_ControlMapping::ON_PRESS);
	xMap.AddKeyMapping(AVP_Vertebrate_InputHandler::FAST, uKeyFast, Asura_ControlMapping::ON_PRESS);
	xMap.AddKeyMapping(AVP_Vertebrate_InputHandler::STANCE_TOGGLE, uKeyStanceToggle, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
}

void AVP_Vertebrate_InputHandler::MapAttacks(Asura_ControlMap& xMap,
											 const u_int uKeyPrimary,
											 const u_int uKeySecondary,
											 const u_int uKeyLeft,
											 const u_int uKeyRight)
{
	// Create the attack mappings...
	xMap.AddKeyMapping(ATTACK_LEFT, uKeyLeft, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
	xMap.AddKeyMapping(ATTACK_RIGHT, uKeyRight, Asura_ControlMapping::ON_DEBOUNCED_PRESS);

	xMap.AddKeyMapping(PRIMARY_ATTACK_TAP, uKeyPrimary, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
	xMap.AddKeyMapping(SECONDARY_ATTACK_TAP, uKeySecondary, Asura_ControlMapping::ON_DEBOUNCED_PRESS);

	xMap.AddKeyMapping(PRIMARY_ATTACK_HELD, uKeyPrimary, Asura_ControlMapping::ON_PRESS);
	xMap.AddKeyMapping(SECONDARY_ATTACK_HELD, uKeySecondary, Asura_ControlMapping::ON_PRESS);

	Asura_ControlMapping* pxReleaseMapping = new Asura_ControlMapping_Key(uKeyPrimary, Asura_ControlMapping::ON_DEBOUNCED_RELEASE, 1.0f);
	pxReleaseMapping->SetPauseWhileInactive(true);
	xMap.StoreMapping(PRIMARY_ATTACK_RELEASE, pxReleaseMapping);
	pxReleaseMapping = new Asura_ControlMapping_Key(uKeySecondary, Asura_ControlMapping::ON_DEBOUNCED_RELEASE, 1.0f);
	pxReleaseMapping->SetPauseWhileInactive(true);
	xMap.StoreMapping(SECONDARY_ATTACK_RELEASE, pxReleaseMapping);
}

void AVP_Vertebrate_InputHandler::MapFocusKeys(Asura_ControlMap &xMap, const u_int uKeyFocusHeld, const u_int uKeyFocusToggle)
{
	xMap.AddKeyMapping(FOCUS_HELD, uKeyFocusHeld, Asura_ControlMapping::ON_PRESS);
	xMap.AddKeyMapping(FOCUS_TOGGLE, uKeyFocusToggle, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
}

void AVP_Vertebrate_InputHandler::MapMelee(Asura_ControlMap &xMap, const u_int uKeyMelee, const u_int uKeyCounter, const u_int uKeyActiveGrapple)
{
	xMap.AddKeyMapping(MELEE, uKeyMelee, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
	xMap.AddKeyMapping(COUNTER, uKeyCounter, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
	xMap.AddKeyMapping(GRAPPLE_INPUT, uKeyActiveGrapple, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
}

void AVP_Vertebrate_InputHandler::MapJump(Asura_ControlMap& xMap, const u_int uKey)
{
	xMap.AddKeyMapping(JUMP, uKey, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
}

void AVP_Vertebrate_InputHandler::MapReload(Asura_ControlMap& xMap, const u_int uKey)
{
	xMap.AddKeyMapping(RELOAD, uKey, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
}

DEBUGVAR float dbg_fReloadOrInteractHoldTime = 0.5f;

void AVP_Vertebrate_InputHandler::MapInteractAndReloadToSameKey(Asura_ControlMap& xMap, const u_int uKeyInteractAndReload )
{
	xMap.DeleteMapping(RELOAD);
	xMap.DeleteMapping(INTERACT);

	xMap.AddTapKeyMapping(RELOAD, uKeyInteractAndReload, dbg_fReloadOrInteractHoldTime);
	xMap.AddHoldKeyMapping(INTERACT, uKeyInteractAndReload, dbg_fReloadOrInteractHoldTime, 1.0f, Asura_ControlMapping_HoldKey::NO_RAMPING, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
}

void AVP_Vertebrate_InputHandler::MapHealth(Asura_ControlMap& xMap, const u_int uKey)
{
	xMap.AddKeyMapping(HEALTH_REGAIN, uKey, Asura_ControlMapping::ON_DEBOUNCED_PRESS);
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Actor_InputHandler::Update
// Author       : Andy Tate
// Date         : 4-Oct-2006
////////////////////////////////////////////////////////////////////////////////

Asura_Message* AVP_Vertebrate_InputHandler::Update ( AVP_ClientEntity_Actor& xClient )
{
	Asura_Message* pxCmdMsg = PARENT::Update( xClient );

	if ( !xClient.IsDead() )
	{
		const AVP_Movement_System*	pxMovementSystem	= static_cast<const AVP_Movement_System*>(xClient.GetMovementSystem());
		const AVP_Move*				pxCurrentMove		= (pxMovementSystem == NULL) ? NULL : static_cast<const AVP_Move*>(pxMovementSystem->GetCurrentMove());

		AVP_ClientActor_Vertebrate& xVert = static_cast<AVP_ClientActor_Vertebrate&>(xClient);


		// Update the rest of the Predator...
#if defined(ASURA_USE_GUI_MENU_SYSTEM)
		bool bInMenu = AVP_GUIMenu_System::IsCurrentlyInNonHudOrLoadingMenu();		
#endif //defined(ASURA_USE_GUI_MENU_SYSTEM)

#if defined(AVP_ALLOW_DEBUG_MOVEMENT)
		if(!Asura_Render::GetCutsceneHasCameraControl() && !bInMenu && s_eMovementType != MT_FLYING_CAMERA)
#else
		if(!Asura_Render::GetCutsceneHasCameraControl() && !bInMenu)
#endif
		{
			if((pxCurrentMove == NULL) || !pxCurrentMove->IsDisableMovement())
			{
				UpdateZoom( xVert );
				UpdateLockon();
				AVP_Actor_PickupHandler* pxPickupHandler = xClient.GetPickupHandler();
				if( (pxPickupHandler == NULL || !pxPickupHandler->HasRequestedPickup()) && !xClient.IsMovingFast())
				{
					UpdateMeleeRequests( xVert );
				}
			}
			else
			{
				// update the zoom camera ( this normally gets done from UpdateZoom but we don't want zoom input enabled if we're in a move )
				AVP_Vertebrate_CameraHandler* pxCameraHandler = static_cast<AVP_Vertebrate_CameraHandler*>(xClient.GetCameraHandler());
				if (pxCameraHandler)
				{
					pxCameraHandler->UpdateZoomCamera( static_cast< AVP_ClientActor_Vertebrate& > ( xClient ) );
				}
			}
		}

		// Reordered the checks so I can set HUD health state even if no stim has been used - FS 27th april 2009
		if( GetControlMapValue( AVP_Vertebrate_InputHandler::HEALTH_REGAIN, true, true ) > 0.0f )
		{
			AVP_Actor_HUDHandler::SetHUDHealthState( AVP_Actor_HUDHandler::HUD_USE_STIM );
			AVP_Actor_HUDHandler::SetHUDEnergyState( AVP_Actor_HUDHandler::HUD_USE_STIM );
			if(xVert.GetHealth() < xVert.GetInitialHealth())
			{
				AVP_InventoryItem_Stim* pxStim = xClient.GetInventorySystem() ? static_cast< AVP_InventoryItem_Stim* >( xClient.GetInventorySystem()->GetItem( uAVP_INVENTORY_ITEMHASH_STIM ) ) : NULL;
				if ( pxStim && pxStim->IsIdling( true ) && !pxStim->IsEmpty() )
				{
					pxStim->RequestBeginUsage( AVP_InventoryItem_Stim::UT_STIM );
				}
			}
		}

		if( GetControlMapValue( AVP_Vertebrate_InputHandler::RELOAD, true ) > 0.0f )
		{
			AVP_Actor_HUDHandler::SetHUDAmmoState( AVP_Actor_HUDHandler::HUD_RELOAD_WEAPON );
			AVP_Actor_HUDHandler::SetHUDHealthState( AVP_Actor_HUDHandler::HUD_RELOAD_WEAPON );
		}

		AVP_InventoryItem_Weapon* pxWeapon = xClient.GetInventorySystem() ? static_cast< AVP_InventoryItem_Weapon* >( xClient.GetInventorySystem()->GetCurrentItem( uAVP_INVENTORY_GROUPHASH_WEAPON ) ) : NULL;
		if ( pxWeapon )
		{
			pxWeapon->ProcessPlayerInput( xClient );
		}
	}

	return pxCmdMsg;
}

/// End of function 'AVP_Actor_InputHandler::Update'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Vertebrate_InputHandler::UpdateMovement
// Author       : Anthony Gregson
// Date         : 12/03/08
////////////////////////////////////////////////////////////////////////////////

void AVP_Vertebrate_InputHandler::UpdateMovement( AVP_ClientEntity_Actor& xClient )
{
	AVP_ClientActor_Vertebrate& xVert = static_cast<AVP_ClientActor_Vertebrate&>(xClient);
	AVP_Vertebrate_JumpHandler*	pxJumpHandler = xVert.GetJumpHandler();

	// Check for jumping...
	if (pxJumpHandler)
	{
		const float	fTJValue = GetControlMapValue(JUMP);

		pxJumpHandler->HandleJump(fTJValue);
	}
}

/// End of function 'AVP_Alien_InputHandler::UpdateMovement'. ///


void AVP_Vertebrate_InputHandler::UpdateMeleeRequests( AVP_ClientActor_Vertebrate& xClient )
{
	const Asura_Guid uMeleeTarget = xClient.GetMeleeTarget();
	if (GetControlMapValue(AVP_Vertebrate_InputHandler::COUNTER) > 0.0f)
	{
		// Send a message to the server
		Asura_Message* pxMessage = Asura_Network::CreateMessage(AVP_MSGID_REQUEST_MELEE, sizeof(AVP_Message_Vertebrate_RequestMelee));

		// Tell the server we want to interact...
		if(pxMessage != NULL)
		{
			AVP_Message_Vertebrate_RequestMelee* pxData = static_cast<AVP_Message_Vertebrate_RequestMelee*>(pxMessage->GetData());

			// Fill-in the message...
			pxData->SetType(AVP_MELEEATTACK_TYPE_COUNTER);
			pxData->SetTargetGuid(uMeleeTarget);

			// Send the request...
			pxMessage->SetToGuid(m_uOwnerGuid);
			pxMessage->SetFromGuid(m_uOwnerGuid);
			Asura_Network::Send(pxMessage);
		} 
	}
	if (GetControlMapValue(AVP_Vertebrate_InputHandler::MELEE) > 0.0f)
	{
		// Send a message to the server
		Asura_Message* pxMessage = Asura_Network::CreateMessage(AVP_MSGID_REQUEST_MELEE, sizeof(AVP_Message_Vertebrate_RequestMelee));

		// Tell the server we want to interact...
		if(pxMessage != NULL)
		{
			AVP_Message_Vertebrate_RequestMelee* pxData = static_cast<AVP_Message_Vertebrate_RequestMelee*>(pxMessage->GetData());

			// Fill-in the message...
			pxData->SetType(AVP_MELEEATTACK_TYPE_NORMAL);
			pxData->SetTargetGuid(uMeleeTarget);

			// Send the request...
			pxMessage->SetToGuid(m_uOwnerGuid);
			pxMessage->SetFromGuid(m_uOwnerGuid);
			Asura_Network::Send(pxMessage);
		} 
	}
}

void AVP_Vertebrate_InputHandler::UpdateRecoil( AVP_ClientEntity_Actor& xClient )
{
	float fSqrtWeaponRecoilReturnFactor = 5.0f;
	const AVP_Inventory_System* pxInvSys = xClient.GetInventorySystem();
	const AVP_InventoryItem_Weapon* pxWeapon = pxInvSys ? static_cast< const AVP_InventoryItem_Weapon* >( pxInvSys->GetCurrentItem( uAVP_INVENTORY_GROUPHASH_WEAPON ) ) : NULL;
	if ( pxWeapon )
	{
		fSqrtWeaponRecoilReturnFactor = pxWeapon->GetSqrtWeaponRecoilReturnFactor();
		if ( m_xCurrentRecoilVelocity.x > 0.0f )
		{
			fSqrtWeaponRecoilReturnFactor *= pxWeapon->GetRecoilUpDownFactor();
		}
	}

	float fTimeStep = Asura_Maths::Min( Asura_Timers::GetGameTimeSlice(), 0.015f );
	Asura_Euler_3 xForce = ( m_xCurrentRecoilOffset * fSqrtWeaponRecoilReturnFactor + m_xCurrentRecoilVelocity * 2.0f ) * fSqrtWeaponRecoilReturnFactor;
	Asura_Euler_3 xVelocity = m_xCurrentRecoilVelocity - ( xForce + m_xLastRecoilForce ) * 0.5f * fTimeStep;

	m_xCurrentRecoilOffset += ( xVelocity + m_xLastRecoilVelocity ) * 0.5f * fTimeStep;
	m_xLastRecoilForce = xForce;
	m_xLastRecoilOffset = m_xCurrentRecoilOffset;
	m_xCurrentRecoilVelocity = m_xLastRecoilVelocity = xVelocity;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Vertebrate_InputHandler::UpdateZoom
// Author       : Chris Wallace
// Date         : 22-Nov-2006
////////////////////////////////////////////////////////////////////////////////

void AVP_Vertebrate_InputHandler::UpdateZoom ( AVP_ClientActor_Vertebrate& xClient )
{
	AVP_Vertebrate_CameraHandler* pxCameraHandler = static_cast<AVP_Vertebrate_CameraHandler*>(xClient.GetCameraHandler());
	if (pxCameraHandler)
	{
		// Neither zoom update nor activation necessary unless we are in first person camera
		if (pxCameraHandler->GetCameraType() != AVP_Actor_CameraHandler::CT_FIRST_PERSON) return;

		// Update the camera zoom *every* frame
		pxCameraHandler->UpdateZoomCamera( xClient );

		m_fZoomTimer += Asura_Timers::GetGameTimeSlice();
		const float fZoomHold = GetControlMapValue(ZOOM_HOLD);
		// Check for zoom controls
		if( fZoomHold > 0.0f )
		{
			if ( m_bZoomed == false )
			{
				m_fZoomHoldTimer += Asura_Timers::GetGameTimeSlice();
			}
		}
		else
		{
			m_bZoomed = false;
		}
		
		if ( m_fZoomHoldTimer > 0.0f )
		{
			if ( pxCameraHandler->GetZoomLevel() == 0 || m_fZoomHoldTimer > dvs_fZoomHoldTime )
			{
				pxCameraHandler->BeginZoomIn( xClient, true );
				m_bZoomed = true;
				m_fZoomHoldTimer = 0.0f;
				m_fZoomTimer = 0.0f;
			}
			else if ( fZoomHold == 0.0f )
			{
				if ( m_fZoomTimer > dvs_fZoomResetTime )
				{
					pxCameraHandler->SetZoomLevel( xClient, 0 );
				}
				else
				{
					pxCameraHandler->BeginZoomIn( xClient, true );
				}
				m_fZoomHoldTimer = 0.0f;
				m_fZoomTimer = 0.0f;
			}
		}

		if(GetControlMapValue(ZOOM_IN, true, true))
		{
			pxCameraHandler->BeginZoomIn( xClient );
		}
 		else if(GetControlMapValue(ZOOM_OUT, true, true))
 		{
 			pxCameraHandler->BeginZoomOut( xClient );
 		}
	}
}

/// End of function 'AVP_Vertebrate_InputHandler::UpdateZoom'. ///


void AVP_Vertebrate_InputHandler::UpdateMovementInput ( AVP_ClientEntity_Actor& xClient )
{
	const AVP_Inventory_System* pxInvSys = xClient.GetInventorySystem();
	const AVP_Inventory_Group* pxWeapGrp = pxInvSys != NULL ? pxInvSys->GetGroup( uAVP_INVENTORY_GROUPHASH_WEAPON ) : NULL;
	const AVP_InventoryItem_Weapon* pxWeapItem = pxWeapGrp != NULL ? static_cast< const AVP_InventoryItem_Weapon* >( pxWeapGrp->GetCurrentInventoryItem() ) : NULL;

	const float fFastModifier = Asura_Maths::Abs(GetControlMapValue(FAST));
	const float fSlowModifier = Asura_Maths::Abs(GetControlMapValue(SLOW));

	Asura_Vector_3 xInput(GetControlMapValue(STRAFE_LEFT_RIGHT), 0.0f, GetControlMapValue(FORWARDS_BACKWARDS));

	AVP_Vertebrate_AnimationHandler* pxAnimHandler = static_cast< AVP_Vertebrate_AnimationHandler* >( xClient.GetAnimationHandler() );
	AVP_Animation_BodyRig* pxBR = pxAnimHandler ? pxAnimHandler->GetBodyRig() : NULL;
	if ( pxBR )
	{
		pxBR->SetTargetStrafeDragValue( xInput.x );
	}

	// Save this off, in case any code below messes with it.
	m_xMovementInput = xInput;

	float fMag;
	float fAngle;

	// See if we should be using air control or normal control
	if (xClient.IsJumping() || xClient.IsFalling())
	{
		// Treat the input as a change to the current air velocity.
		if (fSlowModifier > 0.0f)
		{
			xInput *= 0.5f;
		}

		const float fMaxDeltaV = m_fAirAcceleration * Asura_Timers::GetGameTimeSlice();
		Asura_Quat xOriQuat(xClient.GetCurrentOrientation());
		xInput = xOriQuat.RotateVector(xInput);
		xInput *= fMaxDeltaV;

		Asura_Vector_3 xNewVel(m_xFlatAirVelocity.x, 0.0f, m_xFlatAirVelocity.y);
		xNewVel += xInput;

		const float fNewMagSquared = xNewVel.MagnitudeSquared();
		const float fOldMagSquared = m_xFlatAirVelocity.MagnitudeSquared();
		if (fOldMagSquared > (m_fMaxAirSpeed * m_fMaxAirSpeed))
		{
			if (fNewMagSquared > (m_fMaxAirSpeed * m_fMaxAirSpeed))
			{
				xNewVel.Normalise();
				float fAdjustedSpeed = m_xFlatAirVelocity.Magnitude();
				fAdjustedSpeed -= fMaxDeltaV;
				fAdjustedSpeed = Asura_Maths::Max( fAdjustedSpeed, m_fMaxAirSpeed );
				xNewVel *= fAdjustedSpeed;
				fMag = 1.0f;
			}
			else
			{
				if (m_fMaxAirSpeed <= 0.0001f)
				{
					fMag = 0.0f;
				}
				else
				{
					fMag = Asura_Maths::Min(xNewVel.Magnitude() / m_fMaxAirSpeed, 1.0f);
				}
			}

		}
		else
		{
			if (fNewMagSquared > (m_fMaxAirSpeed * m_fMaxAirSpeed))
			{
				xNewVel.Normalise();
				xNewVel *= m_fMaxAirSpeed;
				fMag = 1.0f;
			}
			else
			{
				if (m_fMaxAirSpeed <= 0.0001f)
				{
					fMag = 0.0f;
				}
				else
				{
					fMag = Asura_Maths::Min(xNewVel.Magnitude() / m_fMaxAirSpeed, 1.0f);
				}
			}
		}


		// Store off this now, as we need to rotate it back into local space to get the angle.
		m_xFlatAirVelocity.x = xNewVel.x;
		m_xFlatAirVelocity.y = xNewVel.z;

		xOriQuat.Invert();
		xNewVel = xOriQuat.RotateVector(xNewVel);
		fAngle = Asura_Maths::ArcTangent(xNewVel.x, xNewVel.z);

		// No sprinting in mid air.
		xClient.SetMovingFast(false);
		if ( m_fSprintTimer > 0.0f )
		{ 
			m_fSprintTimer = 0.0f;
			m_fSprintChargeTimer = m_fSprintChargeTime;
		}
	}
	else
	{
		// If we're not falling, make sure the velocity value is saved off.
		Asura_Vector_3 xCurrentVel;
		xClient.GetCurrentVelocity(xCurrentVel);
		m_xFlatAirVelocity.x = xCurrentVel.x;
		m_xFlatAirVelocity.y = xCurrentVel.z;

		if ( fFastModifier )
		{
			// reset the zoom level
			AVP_Vertebrate_CameraHandler* pxCameraHandler = static_cast< AVP_Vertebrate_CameraHandler* >( xClient.GetCameraHandler() );
			pxCameraHandler->SetZoomLevel( static_cast< AVP_ClientActor_Vertebrate& >( xClient ), 0 );
		}

		// don't run while reloading a gun, it's dangerous.
		bool bCanMoveFast = pxWeapItem == NULL || !pxWeapItem->IsUsing(false) || (pxWeapItem->GetCurrentUsageType() != AVP_InventoryItem_Weapon::UT_RELOAD_ALL && pxWeapItem->GetCurrentUsageType() != AVP_InventoryItem_Weapon::UT_MELEE);
		if(pxWeapItem)
		{
			bCanMoveFast &= pxWeapItem->GetItemType() != AVP_INVENTORYITEMTYPE_SMARTGUN;
		}
	
		// Should be safe to assume our client is a vertebrate.
		const AVP_ClientActor_Vertebrate& xVertebrate = static_cast<AVP_ClientActor_Vertebrate&>(xClient);
		if (xVertebrate.GetPhysicsObjectHandler())
		{
			u_int uConstrainedMovementAttractorID = xVertebrate.GetConstrainedMovementAttractorID();
			if (uConstrainedMovementAttractorID != uASURA_ATTRACTOR_ID_NONE)
			{
				// We're constrained to an attractor, so try to move along it
				const Asura_Attractor_Compiled* pxAttractor = Asura_Attractor_System::GetInstance()->GetAttractorByID(uConstrainedMovementAttractorID);
				if (pxAttractor)
				{
					// For now, we'll leave bounds checking to whatever's deciding to attach us to the attractor in the first place.
					Asura_Vector_3 xAttractorDirection;
					pxAttractor->GetAxisDirection(xAttractorDirection);

					// We need to rotate this into local space, and remove the gravity component.
					xAttractorDirection = xVertebrate.GetCurrentOrientation().ApplyTranspose( xAttractorDirection );
					xAttractorDirection.y = 0.0f;

					if (!xAttractorDirection.ApproximatelyEquals(Asura_Maths::ZeroVector3))
					{
						xAttractorDirection.Normalise();
					}

					// Finally, change the input to be the appropriate amount in the direction of the rotated attractor.
					xInput = xAttractorDirection * (xAttractorDirection * xInput);
				}
			}
		}

		const float fTimeSlice = Asura_Timers::GetGameTimeSlice();

		if ( m_fSprintTimer > 0.0f )
		{
			m_fSprintTimer -= fTimeSlice;
			if ( m_fSprintTimer <= 0.0f )
			{
				m_fSprintChargeTimer = m_fSprintChargeTime;
			}
		}

		if ( m_fSprintChargeTimer > 0.0f )
		{
			m_fSprintChargeTimer -= fTimeSlice;
		}

		fMag							= Asura_Maths::ClampToRange( xInput.Magnitude(), 0.0f, 1.0f );
		fAngle							= Asura_Maths::ArcTangent( xInput.GetX(), xInput.GetZ() );
		const bool	bIsMovingForwards	= (xInput.z > 0.0f);
		const bool	bMoveSlow			= (fSlowModifier > 0.0f) || (pxWeapItem && pxWeapItem->GetItemType() == AVP_INVENTORYITEMTYPE_SMARTGUN && pxWeapItem->IsUsing(true) && pxWeapItem->GetCurrentUsageType() >= AVP_InventoryWeapon_RangedWeapon::NUM_RANGED_WEAPON_USAGE_TYPES); // Also move slowely if using a weapon weapon specific usagetype on smartgun
		const bool	bMoveFast			= !bMoveSlow && bIsMovingForwards && fFastModifier > 0.0f && bCanMoveFast && m_fSprintTimer <= 0.0f && m_fSprintChargeTimer <= 0.0f && !xClient.IsCrouching();
		const bool	bIsStationary		= fMag < dbg_fInputDeadZone;

		if ( bMoveFast )
		{
			m_fSprintTimer = m_fSprintTime;
		}

		if ( bMoveSlow )
		{
			fMag *= 0.5f;
			if ( m_fSprintTimer > 0.0f )
			{ 
				m_fSprintTimer = 0.0f;
				m_fSprintChargeTimer = m_fSprintChargeTime;
			}
		}

		if ( !bIsMovingForwards || xInput.z < Asura_Maths::Abs( xInput.x ) )
		{
			if ( m_fSprintTimer > 0.0f )
			{ 
				m_fSprintTimer = 0.0f;
				m_fSprintChargeTimer = m_fSprintChargeTime;
			}
		}

		if ( bIsStationary )
		{
			xClient.SetMoving( false );
			fMag = 0.0f;
		}
		else
		{
			xClient.SetMoving( true );
		}

		xClient.SetMovingSlow( fMag <= 0.5f );

		if ( m_fSprintTimer > 0.0f )
		{
			fMag = 2.0f;
			xClient.SetMovingFast( true );
		}
		else
		{
			xClient.SetMovingFast( false );
		}
	}

	xClient.GetPlayerStateHandler()->UpdateMovementParametrics( fMag, fAngle );
}

void AVP_Vertebrate_InputHandler::UpdateLockon(void)
{

}

DEBUGVAR float dbg_fAimDirectionLimitAngleLeewayMultiplier = 1.05f;

void AVP_Vertebrate_InputHandler::UpdateLookInput ( AVP_ClientEntity_Actor& xClient, const float fTiltMaxSpeed, const float fTurnMaxSpeed, const float fInnerAimAssistMultiplier, const float fOuterAimAssistMultiplier, const float fTanLookAssistHalfAngle, const float fLookAssistHorizontalMaxSpeed, const float fLookAssistVerticalMaxSpeed )
{
	const AVP_ClientEntity_Actor::AimDirectionUsage eAimUsage = xClient.GetAimDirectionUsage();
	if ((eAimUsage == AVP_ClientEntity_Actor::AIM_DIRECTION_NOT_USED) || (eAimUsage == AVP_ClientEntity_Actor::AIM_DIRECTION_LIMIT))
	{
		Asura_Euler_3 xPadInputOrientation( GetControlMapValue( LOOK_UP_DOWN ), GetControlMapValue( LOOK_LEFT_RIGHT ), 0.0f );
		Asura_Euler_3 xMouseInputOrientation( GetControlMapValue( LOOK_MOUSE_UP_DOWN ), GetControlMapValue( LOOK_MOUSE_LEFT_RIGHT ), 0.0f );

		const AVP_Profile_Controls& xProfileControls = AVP_Settings::GetCurrentProfile()->GetControls();
		const float fZoomScale = GetLookZoomScale( xClient );
		const float	fXAxisScale	= 1.0f;
		const float	fYAxisScale	= (xProfileControls.GetInvertY()) ? -1.0f : 1.0f;

		xPadInputOrientation.x		*= fTiltMaxSpeed * fZoomScale * fYAxisScale * xProfileControls.GetSensitivityY(); // this is correct as the x-axis input (stick left-right) controls your orientation about the y-axis (i.e. rotation)
		xPadInputOrientation.y		*= fTurnMaxSpeed * fZoomScale * fXAxisScale * xProfileControls.GetSensitivityX();

		xMouseInputOrientation.x	*= fTiltMaxSpeed * fZoomScale * fYAxisScale * xProfileControls.GetSensitivityY(); // this is correct as the x-axis input (stick left-right) controls your orientation about the y-axis (i.e. rotation)
		xMouseInputOrientation.y	*= fTurnMaxSpeed * fZoomScale * fXAxisScale * xProfileControls.GetSensitivityX();

		const bool bLooking    = Asura_Maths::Abs( xPadInputOrientation.x ) > 0.0001f || Asura_Maths::Abs( xPadInputOrientation.y ) > 0.0001f;
		const bool bMoving     = !m_xMovementInput.ApproximatelyEquals( Asura_Maths::ZeroVector3 );
		const bool bAimAssist  = xProfileControls.GetAimAssist() && bLooking;
		const bool bLookAssist = xProfileControls.GetLookAssist() && bMoving;

		bool bLookAssisted = false;

		if ( bAimAssist || bLookAssist )
		{
			Asura_Vector_3 xCameraDir;
			Asura_Vector_3 xOrigin;
			xOrigin = *Asura_Camera::GetPosition();
			Asura_Camera::GetForwardVector( xCameraDir );

			Asura_Guid uHitGuid;
			float fHitDistance;
			float fHitRadiusParametric;

			// Check against the targetting spheres
			if ( AVP_ClientEntity_Actor::TestRayAgainstTargetingSpheres( xOrigin, xCameraDir, xClient.GetGuid(), &uHitGuid, &fHitDistance, &fHitRadiusParametric ) )
			{
				const AVP_ClientEntity_Actor* pxTarget = AVP_ClientEntity_Actor::FindEntity( uHitGuid );
				Asura_Assert( NULL != pxTarget, "Unable to find target entity" );
				if ( NULL != pxTarget && pxTarget->IsEnemyTo( m_uOwnerGuid ) )
				{
					// make sure the target is in our FOV
					Asura_Vector_3 xChestPos;
					pxTarget->GetChestPosition( xChestPos );
					Asura_Vector_3 xScreenPos( xChestPos );
					if ( Asura_Camera::ProjectWorldCoordsToScreenSpace( xScreenPos ) && xScreenPos.GetZ() > 0.0f )
					{
						// Check that there's nothing in the way.
						Asura_Vector_3 xDir = (xChestPos - xOrigin);
						xDir.Normalise();
						const Asura_LOS_InputData xIn( xOrigin, xDir, xClient.GetGuid(), fHitDistance );
						const Asura_LOS_OutputData* xOut = Asura_LOS::TestAgainstEnvironment( &xIn );
						if ( !xOut->m_bHitEnvironment )
						{ 
							// Set the assist strength to a value between zero and one (full strength)
							// depending on the distance to the target, full strength when the distance is at 
							// the weapon's effective range or less, and fading out to zero as we go beyond 1.5x the weapon's range
							float fWeight = Asura_Maths::ClampToRange( 3.0f - ( 2.0f * fHitDistance / xClient.GetWeaponEffectiveRange() ), 0.0f, 1.0f );
							fWeight *= ( 1.0f - fHitRadiusParametric );

							if ( bAimAssist )
							{
								xPadInputOrientation *= Asura_Maths::Lerp( fOuterAimAssistMultiplier, fInnerAimAssistMultiplier, fWeight );
							}

							if ( bLookAssist )
							{
								const float fScale = fHitDistance + pxTarget->GetInnerTargetingRadius() / fTanLookAssistHalfAngle;

								Asura_Vector_3 xMyVelocity;
								xClient.GetCurrentVelocity( xMyVelocity );
								xMyVelocity /= fScale;

								Asura_Vector_3 xDeltaVeocity;
								pxTarget->GetCurrentVelocity( xDeltaVeocity );
								xDeltaVeocity /= fHitDistance;
								xDeltaVeocity -= xMyVelocity;

								xDeltaVeocity = xClient.GetCurrentOrientation().ApplyTranspose( xDeltaVeocity );
								if ( m_xSmoothedDeltaVelocity.ApproximatelyEquals( Asura_Maths::ZeroVector3 ) )
								{
									m_xSmoothedDeltaVelocity = xDeltaVeocity;
								}
								else
								{
									m_xSmoothedDeltaVelocity.Lerp( m_xSmoothedDeltaVelocity, xDeltaVeocity, 0.1f );
								}

								Asura_Euler_3 xLookAssist( -m_xSmoothedDeltaVelocity.GetY(), m_xSmoothedDeltaVelocity.GetX(), 0.0f );

								xLookAssist *= fWeight;

								xLookAssist.x = Asura_Maths::ClampToRange( xLookAssist.x, -fLookAssistVerticalMaxSpeed, fLookAssistVerticalMaxSpeed );
								xLookAssist.y = Asura_Maths::ClampToRange( xLookAssist.y, -fLookAssistHorizontalMaxSpeed, fLookAssistHorizontalMaxSpeed );

								xPadInputOrientation += xLookAssist;
								bLookAssisted = true;
							}
						}
					}
				}
			}
		}

		if ( bLookAssisted == false )
		{
			m_xSmoothedDeltaVelocity = Asura_Maths::ZeroVector3;
		}

		Asura_Euler_3 xInputOrientation = ( xPadInputOrientation + xMouseInputOrientation );

		AVP_Vertebrate_AnimationHandler* pxAnimHandler = static_cast< AVP_Vertebrate_AnimationHandler* >( xClient.GetAnimationHandler() );
		AVP_Animation_BodyRig* pxBR = pxAnimHandler ? pxAnimHandler->GetBodyRig() : NULL;
		if ( pxBR )
		{
			pxBR->SetTargetAimDownUpDragValue( Asura_Maths::ClampToRange( xInputOrientation.x, -1.0f, 1.0f ) );
			pxBR->SetTargetAimRightLeftDragValue( Asura_Maths::ClampToRange( xInputOrientation.y, -1.0f, 1.0f ) );
		}

		xInputOrientation *= Asura_Timers::GetGameTimeSlice();

		Asura_AVector3 xCurrentAim;
		Asura_AMatrix3x3 xOri( xClient.GetCurrentOrientation() );
		xClient.GetAimDirection( xCurrentAim );

		// Rotate by change in physics object orientation to take account of rotating lifts.
		// Only do this if a move isn't controlling the orientation, as if it is that will be having a similar effect but shouldn't affect look.
		const Asura_Movement_System* pxMovementSystem = xClient.GetMovementSystem();
		const Asura_Move* pxCurrentMove = pxMovementSystem ? pxMovementSystem->GetCurrentMove() : NULL;
		if ( xClient.GetPhysicsObjectHandler() && (!pxCurrentMove || (pxCurrentMove->GetOrientationUsage() != Asura_Move::ORIENTATION_SET_DIRECTLY)) )
		{
			Asura_Quat xRot;
			xRot.FromMatrix( xOri );
			Asura_Quat xInvOldOri = xClient.GetPhysicsObjectHandler()->GetOrientationPassedIntoPhysics();
			xInvOldOri.Invert();
			xRot = xRot * xInvOldOri;
			xCurrentAim = xRot.RotateVector( xCurrentAim );
		}

		// convert to object space
		xCurrentAim = xOri.ApplyTranspose( xCurrentAim );

		Asura_Euler_3 xOSAim;
		xOSAim.x = Asura_Maths::ArcSine( -xCurrentAim.GetY() );
		xOSAim.y = Asura_Maths::ArcTangent( xCurrentAim.GetX(), xCurrentAim.GetZ() );

		xOSAim -= m_xLastRecoilOffset;
		UpdateRecoil( xClient );
		xOSAim += m_xCurrentRecoilOffset;
		
		float fTheta = Asura_Maths::ClampToRange( xOSAim.x + xInputOrientation.x, -( Asura_Maths::Deg90 - dbg_fGimbalEpsilon ), Asura_Maths::Deg90 - dbg_fGimbalEpsilon );
		float fPhi = Asura_Maths::RadiansToZeroCentredRange( xOSAim.y + xInputOrientation.y );

		float fSinTheta;
		float fCosTheta;
		float fSinPhi;
		float fCosPhi;

		Asura_Maths::SinCos( fTheta, fSinTheta, fCosTheta );
		Asura_Maths::SinCos( fPhi, fSinPhi, fCosPhi );

		Asura_AVector3 xNewAim( fCosTheta * fSinPhi, -fSinTheta, fCosTheta * fCosPhi );

		if (eAimUsage == AVP_ClientEntity_Actor::AIM_DIRECTION_LIMIT)
		{
			Asura_AVector3 xTargetAim = xClient.GetTargetAimDirection();
			xTargetAim = xOri.ApplyTranspose(xTargetAim);
			// If we're limiting the look angle, make sure we aren't going past the limit or already outside it.
			const float fLimitAngle = xClient.GetAimDirectionLimitAngle();
			float fPrevAngle = Asura_Maths::ArcCosine(xCurrentAim * xTargetAim);
			if (fPrevAngle > (fLimitAngle * dbg_fAimDirectionLimitAngleLeewayMultiplier))
			{
				// Just take control away in this case.
				xNewAim = xCurrentAim;
			}
			else
			{
				Asura_Quat xRot;
				xRot.RotationFromAToB(xTargetAim, xNewAim);
				Asura_AVector3 xAxis;
				float fAngle = xRot.ToRotation(xAxis);
				if (fAngle < 0.0f)
				{
					xAxis *= -1.0f;
					fAngle = -fAngle;
				}

				if (fAngle > fLimitAngle)
				{
					xRot.FromRotation(fLimitAngle, xAxis);
					xNewAim = xRot.RotateVector(xTargetAim);
				}
			}
		}

		// back to worldspace
		xNewAim = xNewAim * xOri;

		if ( Asura_Maths::Abs( 1.0f - xNewAim.MagnitudeSquared() ) > 1e-5f )
		{
			xNewAim.Normalise();
		}

		SetNewLookDirection( xClient, xNewAim, true );
	}
}

float AVP_Vertebrate_InputHandler::GetLookZoomScale( AVP_ClientEntity_Actor& xClient )
{
	AVP_Vertebrate_CameraHandler*	pxCameraHandler	= static_cast< AVP_Vertebrate_CameraHandler* >( xClient.GetCameraHandler() );

	// Do we have a camera-handler?
	float fScale = 1.0f;
	Asura_Assert( pxCameraHandler != NULL, "Unable to get camera handler" );
	if ( pxCameraHandler != NULL)
	{
		// Scale down the input look orientation when zoomed in
		if (pxCameraHandler->GetCameraType() == AVP_Actor_CameraHandler::CT_FIRST_PERSON && pxCameraHandler->GetZoomLevel() > 0)
		{
			fScale = 0.5f / pxCameraHandler->GetZoomLevel();
		}
	}

	return fScale;
}

bool AVP_Vertebrate_InputHandler::IsIdle() const
{
	bool bIdle = PARENT::IsIdle();
	bIdle &= GetControlMapValue( ATTACK_LEFT ) == 0.0f;
	bIdle &= GetControlMapValue( ATTACK_RIGHT ) == 0.0f;
	bIdle &= GetControlMapValue( PRIMARY_ATTACK_TAP ) == 0.0f;
	bIdle &= GetControlMapValue( SECONDARY_ATTACK_TAP ) == 0.0f;
	bIdle &= GetControlMapValue( PRIMARY_ATTACK_HELD ) == 0.0f;
	bIdle &= GetControlMapValue( SECONDARY_ATTACK_HELD ) == 0.0f;
	return bIdle;
}

void AVP_Vertebrate_InputHandler::RefreshBlueprintData()
{
	const AVP_ClientActor_Vertebrate* pxOwner = AVP_ClientActor_Vertebrate::FindEntity(m_uOwnerGuid);

	PARENT::RefreshBlueprintData();

	if (pxOwner)
	{
		const Asura_Hash_ID		uBlueprintHash	= pxOwner->GetBlueprintHashID();
		const Asura_Hash_ID		uTypeHash		= uAVP_BLUEPRINT_TYPE_PLAYERS;

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

			if(pxBlueprint->GetParameterValueByHashID(0x4D19C6A1, xValue))	//Asura_GetHashID("MaxAirSpeed")
			{
				m_fMaxAirSpeed = xValue.GetFloatValue();
			}

			if(pxBlueprint->GetParameterValueByHashID(0xD55F94E6, xValue))	//Asura_GetHashID("MaxAirAcceleration")
			{
				m_fAirAcceleration = xValue.GetFloatValue();
			}

			if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_SPRINT_TIME, xValue ) )
			{
				m_fSprintTime = xValue.GetFloatValue();
			}

			if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_SPRINT_CHARGE_TIME, xValue ) )
			{
				m_fSprintChargeTime = xValue.GetFloatValue();
			}
		}
	}
}

void AVP_Vertebrate_InputHandler::SetNewLookDirection( AVP_ClientEntity_Actor& xClient, const Asura_AVector3& xDir, const bool bImmediate )
{
	Asura_AVector3 xPrevDirection(m_xDesiredLookDirection);
	m_xDesiredLookDirection = xDir;
	xClient.SetAimDirection( xDir );
	Asura_Vector_3 xEyesPos;
	if (!xClient.GetWorldSpaceMarkerPoint(0x002fbc62, &xEyesPos, NULL)) // eyes
	{
		xEyesPos = xClient.GetCurrentPosition() + Asura_Vector_3( 0.0f, -2.0f, 0.0f );
	}
	// Cast LOS
	float fRange = 10.0f;
	Asura_LOS_InputData xInputData(xEyesPos, Asura_Vector_3( xDir ), xClient.GetGuid(), 1000.0f);
	const int NUM_EXCEPTIONS = 7;
	static const u_short s_ausClassificationExceptionArray[NUM_EXCEPTIONS] = { 
		AVP_ENTITYCLASS_DISC, 
		AVP_ENTITYCLASS_DROPSHIP_MISSILE, 
		AVP_ENTITYCLASS_GENERICPROJECTILE, 
		AVP_ENTITYCLASS_HUMANFLARE, 
		AVP_ENTITYCLASS_PROJECTILE_FLAMETHROWER,
		AVP_ENTITYCLASS_MINE,
		AVP_ENTITYCLASS_PROJECTILE_ACIDSPIT
	};

	const Asura_LOS_OutputData* pxOutput = AVP_LOS::TestAgainstAlmostEverything( &xInputData, NUM_EXCEPTIONS, s_ausClassificationExceptionArray );
	if ( pxOutput )
	{
		fRange = pxOutput->m_fDistanceToIntersection;
	}

	xClient.SetLookAtPosition( Asura_AVector3( xEyesPos ) + xDir * fRange );

	// Calc difference angle
	Asura_Quat xRot;
	xRot.RotationFromAToB( xPrevDirection, xDir );
	Asura_AVector3 xAxis;
	float fAngle = xRot.ToRotation( xAxis );
	if ( fAngle < 0.0f )
	{
		fAngle = -fAngle;
	}
	if ( fAngle > Asura_Maths::Deg90 )
	{
		fAngle = Asura_Maths::Deg90;
	}

	xClient.SetLookDirectionStoredAngle(fAngle);

}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Vertebrate_InputHandler::GetTileIdOrDescStringForMapping (Public)
// Author       : Callum Hope
// Date         : 30-Oct-2008
// Desc			: Takes a set of IDs and finds a mapping to pass to the platform specific code, if it doesnt exist it fills the string accordingly. 
//
// Arguments	: uMapID, the id if the Map to be used, 0- Current Set 1- m_xMapFirstPerson, 2- m_xMapVisionSelector, 3- m_xMapCutscene
//				  uMapping, the enum value of the mapping to be looked up, could be a value from enums "VertebrateMappings", "ActorMappings" OR "AVP_FONTSYTEM_SPECIALCHARS"
//				  uMappingID, the mapping for the enum wanted, as some have more than one keys for the same function. 
//				  bTileId, if a hex tile id should be returned or an asura string should be filled with a textual description.
//				  pxDescString, the AsuraString to be filled with the resulting desc, NULL if tile id requested.
//
// Return		: u_int, that will be 0 in an error state or if bTileId is low, or a hex id if it is high
////////////////////////////////////////////////////////////////////////////////
u_int AVP_Vertebrate_InputHandler::GetTileIdOrDescStringForMapping( u_int uMapID, u_int uMapping, u_int uMappingID, bool bTileId, Asura_String* pxDescString, u_int uComboId )
{
	// standard saftey checks of the arguments
	if( !bTileId && !pxDescString )
	{
		Asura_Assert( false, "GetTileIdOrDescStringForMapping\n, Description string requested but no string supplied " );
		return 0;
	}

	if( uMapID > 3 )
	{
		Asura_Assert( false, "GetTileIdOrDescStringForMapping\n, Map ID passed to description string creator is not valid" );
		if( pxDescString )
		{
			Asura_PrintfToBfr( pxDescString->GetString(), "ERROR", 8, true );
		}
		return 0;
	}

	// check to see if we have to scale the mapping id into the right range. 
	// ( it might be a value from 0xE900 used from the tutorial system, so make it an id ranging from 0 and up )
	if (!ConvertSpecialCharToMapping(uMapping))
	{
		// if the Mapping ID is not in any of the correct enum values ranges we have problems. 
		Asura_Assert( false, "GetTileIdOrDescStringForMapping\n, Mapping passed for tile id of string description is not valid" );
		if( pxDescString )
		{
			Asura_PrintfToBfr( pxDescString->GetString(), "ERROR", 8, true );
		}
		return 0;
	}

	Asura_ControlMap* pxMapUnderInspection;
	const Asura_ControlMapping* pxMappingUnderInspection;
	u_int uNumOfMappings;

	//First find the correct map to look at and get a pointer to it. 
	switch( uMapID )
	{
		case 0:		// Current Map Set
		default:
		{
			pxMapUnderInspection = m_pxCurrentControlMap;
			break;
		}
		case 1:		// m_xMapFirstPerson
		{
			pxMapUnderInspection = &m_xMapFirstPerson;
			break;
		}
		case 2:		// m_xMapVisionSelector
		{
			pxMapUnderInspection = &m_xMapVisionSelector;
			break;
		}
		case 3:		// m_xMapCutscene
		{
			pxMapUnderInspection = &m_xMapCutscene;
			break;
		}
	}

	// get the number of mappings we have for this mapping type
	uNumOfMappings = pxMapUnderInspection->GetNumMappingsOfType( uMapping );

	if( ( uNumOfMappings == 0 ) || ( uMappingID > ( uNumOfMappings - 1 ) ) )
	{
		// if there are no mappings OR the id doesnt exist just return now
		if( pxDescString )
		{
			Asura_PrintfToBfr( pxDescString->GetString(), "--", 4, true );
		}
		return AVP_Buttons::AVP_FONTSYSTEM_SPECIALCHAR_NA;
	}
	else
	{
		// now get the first mapping 
		pxMappingUnderInspection = pxMapUnderInspection->GetMapping( uMapping );
	}

	// We have the first mapping and we know if the Mapping ID sent is possible. 
	// So if the id is not 0 (in which case we already have the correct mapping) get the correct mapping
	if( uMappingID != 0 )
	{
		for( u_int uCount = 0; uCount < uMappingID; uCount ++ )
		{
			pxMappingUnderInspection = pxMappingUnderInspection->GetNextMapping();
		}
	}

	// sanity check
	if( ! pxMappingUnderInspection )
	{
		Asura_Assert( false, "GetTileIdOrDescStringForMapping\n Mapping id requested can not be found." );
		if( pxDescString )
		{
			Asura_PrintfToBfr( pxDescString->GetString(), "ERROR", 8, true );
		}
		return 0;
	}

	// OK we are ready to start retrieving information, call the platform dependant functionality. 
	if( bTileId )
	{
		return Platform_GetTileIdForMapping( pxMappingUnderInspection, uComboId );
	}
	else
	{
		Platform_GetDescStringForMapping( pxDescString, pxMappingUnderInspection );
	}
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Vertebrate_InputHandler::GetTileIdOrDescStringForMapping (Public)
// Author       : Callum Hope
// Date         : 31-Oct-2008 HAPPY HALLOWEEN!!!   
// Desc			: Function Overload to skip platform independant functionality if it is not needed. 
//
// Arguments	: pxMapping, the Asura_ControlMapping Structure to be eveluated. 
//				  bTIleId, bool to say we want an id for a tile or a textual description
//				  pxDescString, the AsuraString to be filled with the resulting desc. 
//
// Return		: u_int, that will be 0 in an error state or if bTileId is low, or a hex id if it is high
////////////////////////////////////////////////////////////////////////////////
u_int AVP_Vertebrate_InputHandler::GetTileIdOrDescStringForMapping( const Asura_ControlMapping* pxMapping, bool bTileId, Asura_String* pxDescString, u_int uComboId )
{
	// standard saftey checks of the arguments
	if( !bTileId && !pxDescString )
	{
		Asura_Assert( false, "GetTileIdOrDescStringForMapping\n, Description string requested but no string supplied " );
		return 0;
	}
	else if( !pxMapping )
	{
		Asura_Assert( false, "GetTileIdOrDescStringForMapping\n No mapping was send so no information can be retrieved." );
		if( pxDescString )
		{
			Asura_PrintfToBfr( pxDescString->GetString(), "ERROR", 8, true );
		}
		return 0;
	}

	// OK we are ready to start retrieving information, call the platform dependant functionality. 
	if( bTileId )
	{
		return Platform_GetTileIdForMapping( pxMapping, uComboId );
	}
	else
	{
		Platform_GetDescStringForMapping( pxDescString, pxMapping );
	}
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Vertebrate_InputHandler::IsSpecialCharAComboMapping (Static Public)
// Author       : Callum Hope
// Date         : 18th-Nov-2008
// Desc			: Function to take an unsigned char, compair it against the enums accessible to this class 
//				  and return the char point to a mapping find if t is a combination mappping. 
//
// Arguments	: uMapping, the special char read in from a multilingual string, eg. \xE902, could be sent as u_short or u_int
//
// Return		: bool, high if a combo mapping was found, low for anything else. 
//
// Other Notes	: There are a couple assumtions about combo mappings in the function, all of which are safe for AvP3. 
//				  The any mapping with a combo mapping assigned only has one mapping for it - because combo mappings can not be changed. 
//				  That when this is called only the current map is being checked - because this is for displaying current controls in game.
////////////////////////////////////////////////////////////////////////////////
bool AVP_Vertebrate_InputHandler::IsSpecialCharAComboMapping( u_int uMapping )
{
	u_int uNumOfMappings;
	const Asura_ControlMapping* pxMappingUnderInspection;

	// check to see if we have to scale the mapping id into the right range. 
	// ( it might be a value from 0xE900 used from the tutorial system, so make it an id ranging from 0 and up as in the input handler mappings)
	if (!ConvertSpecialCharToMapping(uMapping))
	{
		// if the Mapping ID is not in either of the correct enum values ranges its definatly not a combo mapping! return false. 
		return false;
	}

	// get the number of mappings we have for this mapping type
	uNumOfMappings = m_pxCurrentControlMap->GetNumMappingsOfType( uMapping );

	if( uNumOfMappings == 0 )
	{
		// if there are no mappings for this type return false
		return false;
	}
	
	// finally get the mapping under question
	pxMappingUnderInspection = m_pxCurrentControlMap->GetMapping( uMapping );

	// sanity check, if we dont have a mapping now something has gone wrong, assert. 
	if( ! pxMappingUnderInspection )
	{
		Asura_Assert( false, "GetTileIdOrDescStringForMapping\n Mapping id requested can not be found." );
		return false;
	}

	if( pxMappingUnderInspection->GetInstanceMappingType() == Asura_ControlMapping_Combination::GetClassMappingType() )
	{
		// finally return true if it is combo mapping. 
		return true;
	}

	return false;
}


// Converts the parameter to a value from the VertebrateMappings or ActorMappings enumerations, returning true if the final parameter is within the correct range.
bool AVP_Vertebrate_InputHandler::ConvertSpecialCharToMapping(u_int& uMapping)
{
	if ((uMapping >= AVP_Buttons::AVP_FONTSYSTEM_START_OF_ACTOR_SPECIAL_CHAR_MAPPINGS) && (uMapping < AVP_Buttons::AVP_FONTSYSTEM_END_OF_ACTOR_SPECIAL_CHAR_MAPPINGS))
	{
		uMapping -= AVP_Buttons::AVP_FONTSYSTEM_START_OF_ACTOR_SPECIAL_CHAR_MAPPINGS;
		return true;
	}
	else if ((uMapping >= AVP_Buttons::AVP_FONTSYSTEM_START_OF_VERTEBRATE_SPECIAL_CHAR_MAPPINGS) && (uMapping < AVP_Buttons::AVP_FONTSYSTEM_END_OF_VERTEBRATE_SPECIAL_CHAR_MAPPINGS))
	{
		uMapping -= AVP_Buttons::AVP_FONTSYSTEM_START_OF_VERTEBRATE_SPECIAL_CHAR_MAPPINGS;
		uMapping += MIN_VERTEBRATE_MAPPINGS;
		return true;
	}
	else if ((uMapping >= AVP_Buttons::AVP_FONTSYSTEM_START_OF_ALIEN_SPECIAL_CHAR_MAPPINGS) && (uMapping < AVP_Buttons::AVP_FONTSYSTEM_END_OF_ALIEN_SPECIAL_CHAR_MAPPINGS))
	{
		uMapping -= AVP_Buttons::AVP_FONTSYSTEM_START_OF_ALIEN_SPECIAL_CHAR_MAPPINGS;
		uMapping += MIN_VERTEBRATE_MAPPINGS;
		return true;
	}
	else if ((uMapping >= AVP_Buttons::AVP_FONTSYSTEM_START_OF_HUMAN_SPECIAL_CHAR_MAPPINGS) && (uMapping < AVP_Buttons::AVP_FONTSYSTEM_END_OF_HUMAN_SPECIAL_CHAR_MAPPINGS))
	{
		uMapping -= AVP_Buttons::AVP_FONTSYSTEM_START_OF_HUMAN_SPECIAL_CHAR_MAPPINGS;
		uMapping += MIN_VERTEBRATE_MAPPINGS;
		return true;
	}
	else if ((uMapping >= AVP_Buttons::AVP_FONTSYSTEM_START_OF_PREDATOR_SPECIAL_CHAR_MAPPINGS) && (uMapping < AVP_Buttons::AVP_FONTSYSTEM_END_OF_PREDATOR_SPECIAL_CHAR_MAPPINGS))
	{
		uMapping -= AVP_Buttons::AVP_FONTSYSTEM_START_OF_PREDATOR_SPECIAL_CHAR_MAPPINGS;
		uMapping += MIN_VERTEBRATE_MAPPINGS;
		return true;
	}
	else
	{
		// return true if the mapping is already within the enumeration, otherwise return false.
		return uMapping < TOTAL_CONTROL_MAPPINGS;
	}
}
