/**
*
* AVP_Moves.cpp
*
* Created by RDM - 24 August 2006
*
* Copyright (C) 2006 Rebellion, All Rights Reserved.
*
**/

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

// Main Asura header
#include "Core/Asura.h"

// Local header
#include "Moves/AVP_Moves.h"

// Asura
#include "Animation/Asura_AnimationController_Animation.h"
#include "Cutscene/Asura_Cutscene_Track.h"
#include "Entity/PhysicalObject/Moves/Asura_Movement_System.h"
#include "Network/Asura_Network.h"
#include "Network/Asura_Message.h"
#include "Physics/Asura_Physics.h"
#include "Physics/Asura_Physics_MovementQuery.h"
#include "Rendering/Asura_Render.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"
#include "SpecialFX/PFX/Asura_PFX_Entity_Effect.h"

// AVP
#include "Actor/AVP_Actor_AttachmentHandler.h"
#include "Actor/AVP_Actor_InputHandler.h"
#include "Actor/AVP_Actor_PhysicsObjectHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_AnimationHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_HUDHandler.h"
#include "Animation/AVP_AnimFlags.h"
#include "Core/AVP_Game_Core.h"
#include "Core/AVP_Settings.h"
#include "Entity/AVP_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Alien/AVP_ClientActor_Alien.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_Vertebrate_Messages.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_ServerActor_Vertebrate.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_ClientActor_Human.h"
#include "Entity/PhysicalObject/Gib/AVP_ClientEntity_Gib.h"
#include "Moves/AVP_MoveData_Animation.h"
#include "Network/AVP_Player.h"

// axon
#include "Core/BrainTypes/AVP_Brain_Actor.h"

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

DEBUGVAR float dbg_fDefaultAimDirAngVel = Asura_Maths::Deg360; // 360 degrees per second
DEBUGVAR float dbg_fDefaultLookDirLerpTimeScale = 0.2f;

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

// AVP_Move ///////////////////////////////////////////////////////////////////////////////////

AVP_Move::AVP_Move(Asura_Movement_System& xParentSystem, const Asura_Hash_ID uMoveHash)
: PARENT(xParentSystem, uMoveHash)
, m_pxAVPAnimationData(NULL)
, m_uFirstPersonAnimHash(ASURA_HASH_ID_UNSET)
, m_uThirdPersonAnimHash(ASURA_HASH_ID_UNSET)
, m_uAVPMoveFlags(0)
, m_xAimDirection( Asura_Maths::ZeroVector3 )
, m_xLookAtPosition( Asura_Maths::ZeroVector3 )
, m_fAimDirectionLimitAngle( Asura_Maths::Deg90 )
, m_fAimDirectionAngVel( dbg_fDefaultAimDirAngVel )
, m_fLookDirectionLimitAngle( Asura_Maths::Deg90 )
, m_fLookDirectionLerpTimeScale( dbg_fDefaultLookDirLerpTimeScale )
, m_eAIPostMoveMode(AVP_Move::AI_MM_NONE)
{
}

AVP_Move::~AVP_Move()
{
	delete m_pxAVPAnimationData;
}

bool AVP_Move::HasFinished() const
{
	if (m_bSupportEndAnimFlag)
	{
		return m_bFinishEarly || PARENT::HasFinished();
	}
	else
	{
		return PARENT::HasFinished();
	}
}

bool AVP_Move::ShouldStart() const
{
	return(!Asura_Render::GetCutsceneHasCameraControl()); // Only allow moves to start if not in a cutscene
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Move::Start
// Author       : Anthony Gregson
// Date         : 10/10/07
////////////////////////////////////////////////////////////////////////////////

bool AVP_Move::Start(const Asura_Msg_Move* pxStartMsg)
{
	// If this is an external control move, set the appropriate physics object as external control.
	if(GetPositionUsage() == POSITION_SET_DIRECTLY)
	{
		ActivateExternalControlPhysics();
	}

	// Setup the correct anim.
	if (m_pxAVPAnimationData)
	{
		// Find out if we're using a HUD or a third-person skin.
		if ( !m_bUsingBlendTemplate)
		{
			if (GetAVPParentSystem().GetActorOwner().IsUsingFirstPersonSkin())
			{
				m_pxAVPAnimationData->SetAnimHash(m_uFirstPersonAnimHash);
			}
			else
			{
				m_pxAVPAnimationData->SetAnimHash(m_uThirdPersonAnimHash);
			}
		}

		if (m_pxAVPAnimationData->HasValidAnimation())
		{
			// This assumes subclasses will have set up all relevant data before this point.
			if (m_bUseSingularityAxis && GetOrientationInterpolationData())
			{
				Asura_AVector3 xSingularityAxis;
				InitialiseAnimation(*m_pxAVPAnimationData, &xSingularityAxis);

				if ( xSingularityAxis.ApproximatelyEquals( Asura_Maths::ZeroAVector3 ) )
				{
					xSingularityAxis = Asura_AVector3( 0.0f, 0.0f, 1.0f );
				}

				GetOrientationInterpolationData()->SetSingularityHintAxis(xSingularityAxis);
			}
			else
			{
				InitialiseAnimation(*m_pxAVPAnimationData);
			}
		}
	}

	if (m_bDisableSurfaceReorientation)
	{
		DisableSurfaceReorientation();
	}

	m_bCanFinishEarly = false;
	m_bFinishEarly = false;

	if (Asura_Network::IsServer() && !GetAVPParentSystem().GetActorOwner().IsPlayer() && m_eAIPostMoveMode != AI_MM_NONE)
	{
		AVP_ServerActor_Vertebrate* pxOwner = AVP_ServerActor_Vertebrate::FindEntity(GetAVPParentSystem().GetOwnerGuid());
		AVP_Vertebrate_AIAnimStateMachine* pxVertebrateAIAnimSM = pxOwner ? static_cast<AVP_Vertebrate_AIAnimStateMachine*>(pxOwner->GetAIAnimStateMachine()) : NULL;
		if (pxVertebrateAIAnimSM)
		{
			switch (m_eAIPostMoveMode)
			{
			case AI_MM_IDLE:
				pxVertebrateAIAnimSM->RequestInMove(AVP_Actor_AIAnimStateMachine::ACTOR_STATE_IDLE);
				break;
			case AI_MM_SLOW:
				pxVertebrateAIAnimSM->RequestInMove(AVP_Vertebrate_AIAnimStateMachine::VERTEBRATE_STATE_MOVE_SLOW);
				break;
			case AI_MM_MDEIUM:
				pxVertebrateAIAnimSM->RequestInMove(AVP_Vertebrate_AIAnimStateMachine::VERTEBRATE_STATE_MOVE_MEDIUM);
				break;
			case AI_MM_FAST:
				pxVertebrateAIAnimSM->RequestInMove(AVP_Vertebrate_AIAnimStateMachine::VERTEBRATE_STATE_MOVE_FAST);
				break;
			case AI_MM_NONE:
			default:
				Asura_Assert(false, "Unexpected post move mode");
			}
		}
	}

	
	return PARENT::Start(pxStartMsg);
}

Asura_Move* AVP_Move::Update()
{
	if (m_bSupportEndAnimFlag)
	{
		const u_int uAnimFlags = GetAnimFlagsForLastFrame();
		if ((uAnimFlags & AVP_ANIMFLAG_END_ANIM) != 0)
		{
			m_bCanFinishEarly = true;
		}
	}

	// Setting the standing/crouching states every frame, since the alien has timers for the standing state and potentially some controls will be trying to override it elsewhere.
	Asura_Assert(!m_bForceStanding || !m_bForceCrouching, "Can't force both standing and crouching simultaneously.");
	if (m_bForceStanding || m_bForceCrouching)
	{
		// This only works for humans or aliens, but each does it differently.
		AVP_ClientActor_Human* pxHumanOwner = AVP_ClientActor_Human::FindEntity(GetParentSystem().GetOwnerGuid());
		AVP_ClientActor_Alien* pxAlienOwner = AVP_ClientActor_Alien::FindEntity(GetParentSystem().GetOwnerGuid());
		if (pxHumanOwner)
		{
			pxHumanOwner->SetCrouching(m_bForceCrouching, true);
		}
		else if (pxAlienOwner)
		{
			pxAlienOwner->SetStanding(m_bForceStanding, true);
		}
	}

	return PARENT::Update();
}

DEBUGVAR float dbg_fMovementInputToExitEarlySquared = 0.81f;

void AVP_Move::UpdateStates(const float fTimeStep)
{
	// Slightly nastily, we have to check for the input in the update so it flips at the correct point of the frame.
	if (m_bSupportEndAnimFlag && m_bCanFinishEarly)
	{
		const AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
		if (xOwner.GetInputHandler() && (xOwner.GetInputHandler()->GetMovementInput().MagnitudeSquared() >= dbg_fMovementInputToExitEarlySquared))
		{
			m_bFinishEarly = true;
		}
	}

	PARENT::UpdateStates(fTimeStep);
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Move::Finish
// Author       : Chris Wallace
// Date         : 17-May-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_Move::Finish()
{
	AVP_ServerEntity_Actor* pxServer = AVP_ServerEntity_Actor::FindEntity(GetParentSystem().GetOwnerGuid());
	bool bAIControl = (pxServer)?pxServer->GetActorBrain()!=NULL:false;

	// Only send this message we are on the server and AI controlled
	if( bAIControl )
	{
		Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_MOVE_FINISHED, sizeof(Asura_Msg_Move) );
		if( pxMessage )
		{
			Asura_Msg_Move* pxData = static_cast<Asura_Msg_Move*>(pxMessage->GetData());
			if( pxData )
			{
				pxData->ClearFlags();
				pxData->SetMoveID(GetHash());
				pxData->SetIgnoredByOwner(true);
			}

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

			Asura_Network::Send(pxMessage);
		}
	}

	// Also, if this is an external control move we need to turn that off again.
	if(GetPositionUsage() == POSITION_SET_DIRECTLY)
	{
		DeactivateExternalControlPhysics();
	}

	if (m_bSupportEndAnimFlag)
	{
		TweenOutAnimation();
	}

	if (m_bDisableSurfaceReorientation)
	{
		ResetPhysicsObjectHandler();
		EnableSurfaceReorientation();
	}

	PARENT::Finish();
}


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Move::Abort
// Author       : Will Plano
// Date         : 18/01/2008
////////////////////////////////////////////////////////////////////////////////

void AVP_Move::Abort()
{
	AVP_ServerEntity_Actor* pxServer = AVP_ServerEntity_Actor::FindEntity(GetParentSystem().GetOwnerGuid());
	bool bAIControl = (pxServer)?pxServer->GetActorBrain()!=NULL:false;

	// Only send this message we are on the server and AI controlled
	if( bAIControl && !GetSuppressAbortMessage() )
	{
		Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_MOVE_ABORT, sizeof(Asura_Msg_Move) );
		if( pxMessage )
		{
			Asura_Msg_Move* pxData = static_cast<Asura_Msg_Move*>(pxMessage->GetData());
			if( pxData )
			{
				pxData->ClearFlags();
				pxData->SetMoveID(GetHash());
				pxData->SetIgnoredByOwner(true);
			}

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

			Asura_Network::Send(pxMessage);
		}
	}

	// Also, if this is an external control move we need to turn that off again.
	if(GetPositionUsage() == POSITION_SET_DIRECTLY)
	{
		DeactivateExternalControlPhysics();
	}

	if (m_bSupportEndAnimFlag)
	{
		TweenOutAnimation();
	}

	// If we've been aborted something's probably replaced this move. Make sure anims that don't tween out do get cleaned up.
	if (m_pxAVPAnimationData)
	{
		if (m_pxAVPAnimationData->GetLeaveAnimAtEnd() && (m_pxAVPAnimationData->GetTweenOutDuration() <= 0.0f))
		{
			EnsureAnimationFinishes(true);
		}
	}

	if (m_bDisableSurfaceReorientation)
	{
		ResetPhysicsObjectHandler();
		EnableSurfaceReorientation();
	}

	PARENT::Abort();
}


void AVP_Move::EnsureAnimationFinishes(bool bStartTweenImmediately/* = false*/)
{
	// This forces a tween out of the current animation, for situations where moves that normally run into next moves have been aborted unexpectedly.
	// Not every move will want to use this.
	if (m_pxAVPAnimationData)
	{
		// We need to make sure it tweens if we have no tween out duration and we're set to leave the animation at the end.
		const float fTweenOutDuration = m_pxAVPAnimationData->GetTweenOutDuration();

		if ((fTweenOutDuration <= 0.0f) && m_pxAVPAnimationData->GetLeaveAnimAtEnd())
		{
			const float fTweenInDuration = m_pxAVPAnimationData->GetTweenInDuration();
			const bool bAnimatedTween = m_pxAVPAnimationData->GetUseAnimatedTween();

			// Set up a tween-out, and then restore the previous settings in the move.
			m_pxAVPAnimationData->SetTweenData(fTweenInDuration, DEFAULT_TWEEN_TIME, true);
			m_pxAVPAnimationData->UpdateTweenData();
			if (bStartTweenImmediately)
			{
				TweenOutAnimation();
			}
			m_pxAVPAnimationData->SetTweenData(fTweenInDuration, fTweenOutDuration, bAnimatedTween);
		}
	}
}

void AVP_Move::TweenOutAnimation()
{
	// Different to the above, this is for moves that normally tween out automatically, allowing them to start their tween early
	if (!m_pxAVPAnimationData) { return; }

	Asura_AnimationController_Animation* pxAnim = m_pxAVPAnimationData->GetAnimationController();
	if (!pxAnim) { return; }

	// If we've not yet started tweening out, change the target time so we start tweening out immediately. Assume we have a tween duration.
	// First, make sure we haven't already started tweening out.
	const float fParametricTime = pxAnim->GetParametricTimer();
	const float fAnimDuration = pxAnim->GetDuration();
	const float fActualTime = fParametricTime * fAnimDuration;
	const float fTweenOutDuration = m_pxAVPAnimationData->GetTweenOutDuration();
	if (fActualTime >= (fAnimDuration - fTweenOutDuration)) { return; }

	// Finally, calculate a new target time.
	const float fTargetParametric = (fActualTime + fTweenOutDuration) / fAnimDuration;
	pxAnim->SetParametricTargetTime(fTargetParametric);
}


bool AVP_Move::GetShouldClearMeleeRequestQueue() const
{
	// First check the move flag
	if (!m_bClearQueuedMeleeRequests) { return false; }

	// The flag to clear gets overridden if the animation flag to allow melee requests is present
	return (GetAnimFlagsForLastFrame() & AVP_ANIMFLAG_ALLOW_NEW_MELEE) == 0;
}

bool AVP_Move::GetShouldClearCounterRequest() const
{
	// First check the move flag
	if (!m_bClearQueuedCounterRequest) { return false; }

	// The flag to clear gets overridden if the animation flag to allow the counter request is present
	return (GetAnimFlagsForLastFrame() & AVP_ANIMFLAG_ALLOW_NEW_COUNTER) == 0;
}

bool AVP_Move::HandleMessage(const Asura_Message* pxMessage)
{
	// Any move not overriding this returns false
	return(false);
}


void AVP_Move::SendStartMsg()
{
	if (!m_bUseServerStartMessage)
	{
		if (!m_bSuppressStartMessage)
		{
			Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_MOVE_START, sizeof(AVP_Msg_Move));

			if (pxMessage)
			{
				AVP_Msg_Move* pxData = static_cast<AVP_Msg_Move*>(pxMessage->GetData());
				const Asura_Guid uOwnerGuid	= m_xParentSystem.GetOwnerGuid();

				if(pxData != NULL)
				{
					pxData->ClearFlags();
					pxData->SetMoveID(m_uHash);
					pxData->SetIgnoredByOwner(true);
				}

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

				Asura_Network::Send(pxMessage);
			}
		}
		return;
	}

	if (AVP_Settings::GetIsServer())
	{
		Asura_Message*	pxMessage	= Asura_Network::CreateMessage(AVP_MSGID_MOVESTART_SERVER, sizeof(AVP_Msg_Move));

		if(pxMessage != NULL)
		{
			AVP_Msg_Move* pxData = static_cast<AVP_Msg_Move*>(pxMessage->GetData());
			const Asura_Guid uOwnerGuid = m_xParentSystem.GetOwnerGuid();

			if(pxData != NULL)
			{
				pxData->ClearFlags();
				pxData->SetMoveID(m_uHash);
				pxData->SetIgnoredByOwner(false);
			}

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

			Asura_Network::Send(pxMessage);
		}
	}
}


void AVP_Move::SetAnimHashes(Asura_Hash_ID uFirstPersonAnimHash, Asura_Hash_ID uThirdPersonAnimHash)
{
	m_bUsingBlendTemplate = false;
	if (uFirstPersonAnimHash == ASURA_HASH_ID_UNSET && uThirdPersonAnimHash == ASURA_HASH_ID_UNSET)
	{
		// Removing animations. Delete the data if it exists.
		if (m_pxAVPAnimationData)
		{
			// This could leave a dangling pointer in Asura_Move. Clean that up too. About the only option for that currently is to reset the whole move.
			Reset();

			delete m_pxAVPAnimationData;
			m_pxAVPAnimationData = NULL;
		}

		m_uFirstPersonAnimHash = ASURA_HASH_ID_UNSET;
		m_uThirdPersonAnimHash = ASURA_HASH_ID_UNSET;
	}
	else
	{
		// Adding animations. Construct the data if it doesn't exist.
		if (!m_pxAVPAnimationData)
		{
			m_pxAVPAnimationData = new AVP_MoveData_Animation(ASURA_HASH_ID_UNSET);
		}

		m_uFirstPersonAnimHash = uFirstPersonAnimHash;
		m_uThirdPersonAnimHash = uThirdPersonAnimHash;
	}
}

void AVP_Move::SetAnimBlendTemplateBlueprint( const Asura_Blueprint& xBP )
{
	m_bUsingBlendTemplate = true;

	// Removing animations. Delete the data if it exists.
	if (m_pxAVPAnimationData)
	{
		// This could leave a dangling pointer in Asura_Move. Clean that up too. About the only option for that currently is to reset the whole move.
		Reset();

		delete m_pxAVPAnimationData;
		m_pxAVPAnimationData = NULL;
	}

	// Adding animations. Construct the data if it doesn't exist.
	if (!m_pxAVPAnimationData)
	{
		m_pxAVPAnimationData = new AVP_MoveData_BlendedAnimation( xBP );
	}

	m_uFirstPersonAnimHash = ASURA_HASH_ID_UNSET;
	m_uThirdPersonAnimHash = ASURA_HASH_ID_UNSET;
}

void AVP_Move::ActivateExternalControlPhysics()
{
	AVP_Actor_PhysicsObjectHandler* pxPhysicsHandler = NULL;

	// AI controlled entities need to change the server physics object, while players need to change the client. For now, the following test is sufficient to detect this.
	AVP_ServerEntity_Actor* pxServer = AVP_ServerEntity_Actor::FindEntity(GetParentSystem().GetOwnerGuid());
	bool bAIControl = pxServer ? pxServer->GetActorBrain() != NULL : false;

	if (bAIControl)
	{
		pxPhysicsHandler = pxServer->GetPhysicsObjectHandler();
	}
	else
	{
		AVP_ClientEntity_Actor& xAVPOwner = GetAVPParentSystem().GetActorOwner();
		pxPhysicsHandler = xAVPOwner.GetPhysicsObjectHandler();
	}

	if (pxPhysicsHandler)
	{
		pxPhysicsHandler->EnableExternalControlPhysics( AVP_PhysicsObjectHandler::ECREQ_MOVE );
	}
}

void AVP_Move::DeactivateExternalControlPhysics()
{
	AVP_Actor_PhysicsObjectHandler* pxPhysicsHandler = NULL;

	// AI controlled entities need to change the server physics object, while players need to change the client.
	AVP_ServerEntity_Actor* pxServer = AVP_ServerEntity_Actor::FindEntity(GetParentSystem().GetOwnerGuid());
	bool bAIControl = pxServer ? pxServer->GetActorBrain() != NULL : false;

	// TODO - this actually breaks in multiplayer, since there will be no server object, but everyone except the player needs to stay on external control.
	// The correct way to fix that might be a change to the physics handler though, so this code may still be correct after this issue is fixed.
	// AJG - 27/02/08 - Is the above still true?
	if (bAIControl)
	{
		pxPhysicsHandler = pxServer->GetPhysicsObjectHandler();
	}
	else
	{
		AVP_ClientEntity_Actor& xAVPOwner = GetAVPParentSystem().GetActorOwner();
		pxPhysicsHandler = xAVPOwner.GetPhysicsObjectHandler();
	}

	if (pxPhysicsHandler)
	{
		// If this move has a final position, we should make sure that's used at this point
		Asura_Physics_Object* pxPhysObj = pxPhysicsHandler->GetPhysicsObject();
		if (pxPhysObj)
		{
			Asura_Vector_3	xTempPosition;

			m_xParentSystem.GetCurrentMovePosition(xTempPosition);
			pxPhysObj->SetPosition(xTempPosition);

			if (!pxPhysicsHandler->IsSurfaceReorientationEnabled())
			{
				// Make sure we end up upright again.
				const Asura_Matrix_3x3& xOri = pxPhysObj->GetOrientation();
				Asura_Vector_3 xFlatForward(xOri.e13, 0.0f, xOri.e33);
				Asura_Matrix_3x3 xFlatOri;
				Asura_Maths::OrientationFromDirection(xFlatOri, xFlatForward);
				pxPhysObj->SetOrientation(xFlatOri);
				m_xParentSystem.SetCurrentMoveOrientation(xFlatOri);
				pxPhysicsHandler->ResetUsingWorldSpaceOrientation(xFlatOri);
			}
		}
		pxPhysicsHandler->DisableExternalControlPhysics( AVP_PhysicsObjectHandler::ECREQ_MOVE );
	}
}

void AVP_Move::FinishOrientationInterpolation()
{
	PARENT::FinishOrientationInterpolation();

	// If our owner has a physics handler, reset that to our final orientation.
	if (GetOrientationInterpolationData() && GetAVPParentSystem().GetActorOwner().GetPhysicsObjectHandler())
	{
		GetAVPParentSystem().GetActorOwner().GetPhysicsObjectHandler()->ResetUsingWorldSpaceOrientation(GetOrientationInterpolationData()->GetFinal());
	}
}

void AVP_Move::DisableSurfaceReorientation()
{
	AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
	AVP_Actor_PhysicsObjectHandler*	pxPhysicsObjectHandler = xOwner.GetPhysicsObjectHandler();
	if ( !pxPhysicsObjectHandler )
	{
		// Try and get the server. This is for terminating AI jumps correctly.
		AVP_ServerEntity_Actor* pxServer = AVP_ServerEntity_Actor::FindEntity( xOwner.GetGuid() );
		if ( pxServer )
		{
			pxPhysicsObjectHandler = pxServer->GetPhysicsObjectHandler();
		}
	}

	if ( pxPhysicsObjectHandler )
	{
		// Actually, it works better at the moment to set the surface normal to up and allow surface reorientation to orientate the alien, rather than actually disabling the reorientation.
		pxPhysicsObjectHandler->SetForcedTargetSurfaceNormal(Asura_Vector_3(0.0f, -1.0f, 0.0f));
	}
}

void AVP_Move::EnableSurfaceReorientation()
{
	AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
	AVP_Actor_PhysicsObjectHandler*	pxPhysicsObjectHandler  = xOwner.GetPhysicsObjectHandler();
	if ( !pxPhysicsObjectHandler )
	{
		// Try and get the server. This is for terminating AI flinches correctly.
		AVP_ServerEntity_Actor* pxServer = AVP_ServerEntity_Actor::FindEntity( xOwner.GetGuid() );
		if ( pxServer )
		{
			pxPhysicsObjectHandler = pxServer->GetPhysicsObjectHandler();
		}
	}

	if ( pxPhysicsObjectHandler )
	{
		// Actually, it works better at the moment to set the surface normal to up and allow surface reorientation to orientate the alien, rather than actually disabling the reorientation.
		pxPhysicsObjectHandler->UnsetForcedTargetSurfaceNormal();
	}
}

void AVP_Move::ResetPhysicsObjectHandler()
{
	AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
	AVP_Actor_PhysicsObjectHandler*	pxPhysicsObjectHandler  = xOwner.GetPhysicsObjectHandler();
	const Asura_Matrix_3x3* pxOrientation = &xOwner.GetCurrentOrientation();

	if ( !pxPhysicsObjectHandler )
	{
		// Try and get the server. This is for terminating AI jumps correctly.
		AVP_ServerEntity_Actor* pxServer = AVP_ServerEntity_Actor::FindEntity( xOwner.GetGuid() );
		if ( pxServer )
		{
			pxPhysicsObjectHandler = pxServer->GetPhysicsObjectHandler();
			pxOrientation          = &pxServer->GetOrientation();
		}
	}

	if ( pxPhysicsObjectHandler && pxOrientation )
	{
		pxPhysicsObjectHandler->ResetUsingWorldSpaceOrientation( *pxOrientation );
	}
}

bool AVP_Move::UpdateAnimation( const float fTimeStep )
{
	bool bOK = PARENT::UpdateAnimation( fTimeStep );
	
	// We're at our destination, have we done everything we need to?
	if ( m_pxAVPAnimationData )
	{
		const Asura_MoveData::State	eCurrentState = m_pxAVPAnimationData->GetState();

		switch ( eCurrentState )
		{
			case Asura_MoveData::STARTING:
			case Asura_MoveData::UPDATING:
			{
				if( m_pxAVPAnimationData->AlwaysReCalculateFinalMovementOffset() )
				{
					Asura_AVector3	xLSMOPosition;
					Asura_Quat		xLSMOOrientation;

					Asura_AVector3*	pxLSMOPosition		= ((GetPositionInterpolationData() == NULL) || (GetPositionInterpolationData()->GetMovementOffsetOperation() == Asura_MoveData_PositionInterpolation::IGNORE_MOVEMENT_OFFSET))
														? NULL
														: &xLSMOPosition;
					Asura_Quat*		pxLSMOOrientation	= ((GetOrientationInterpolationData() == NULL) || (GetOrientationInterpolationData()->GetMovementOffsetOperation() == Asura_MoveData_OrientationInterpolation::IGNORE_MOVEMENT_OFFSET))
														? NULL
														: &xLSMOOrientation;

					// Decide whether to calculate the movement-offset data...
					if((pxLSMOOrientation != NULL) || (pxLSMOPosition != NULL))
					{
						if(m_pxAVPAnimationData->CalculateMovementOffset(1.0f, pxLSMOPosition, pxLSMOOrientation))
						{
							if(pxLSMOOrientation != NULL)
							{
								SetParentSystemLSMOFinalOrientation(xLSMOOrientation);
							}

							if(pxLSMOPosition != NULL)
							{
								SetParentSystemLSMOFinalPosition(xLSMOPosition);
							}
						}
					}
				}
			}

			default:
			{

			}
			break;
		}
	}

	return bOK;
}

static const float fANIMATION_FRAMES_PER_SECOND = 30.0f;
static const float fONE_OVER_ANIMATION_FRAMES_PER_SECOND = 1.0f / 30.0f;

void AVP_Move::UpdateGrappleEvents(AVP_GrappleEvent_Entry* pxGrappleEvents, int iNumGrappleEvents, AVP_ClientEntity_Actor& xOwner, AVP_ClientEntity_Actor* pxEnemy, Asura_AnimationController_Animation* pxCurrentAnimController)
{
	const Asura_Animation* pxAnim = xOwner.GetShapeInstance().GetAnimation();
	// Can't do grapple events without animations.
	if (!pxCurrentAnimController || !pxAnim) { return; }

	int iLastFrameFlags = pxCurrentAnimController->GetFlagsForLastFrame();
	int iCurrentFrame = Asura_Maths::FloatToInt_Chop(pxCurrentAnimController->GetDuration() * pxCurrentAnimController->GetParametricTimer() * fANIMATION_FRAMES_PER_SECOND);

	for (int i=0; i<iNumGrappleEvents; ++i)
	{
		if (!pxGrappleEvents[i].m_bFinished) // Only continue if this event hasn't already completed.
		{
			if (!pxGrappleEvents[i].m_bRunning)
			{
				// Not yet running, see if we should be starting it.
				bool bStart = false;
				if (pxGrappleEvents[i].m_iStartAnimFlag != 0)
				{
					bStart = (pxGrappleEvents[i].m_iStartAnimFlag & iLastFrameFlags) != 0;
				}
				else if (pxGrappleEvents[i].m_iStartFrame >= 0)
				{
					bStart = pxGrappleEvents[i].m_iStartFrame <= iCurrentFrame;
				}

				if (bStart)
				{
					StartGrappleEvent(pxGrappleEvents[i], xOwner, pxEnemy, pxCurrentAnimController);
				}
			}

			// Not using else, for single-frame events.
			if (pxGrappleEvents[i].m_bRunning)
			{
				// Already running, see if we should be finishing it.
				bool bFinish = false;
				if (pxGrappleEvents[i].m_iEndAnimFlag != 0)
				{
					bFinish = (pxGrappleEvents[i].m_iEndAnimFlag & iLastFrameFlags) != 0;
				}
				else if (pxGrappleEvents[i].m_iEndFrame >= 0)
				{
					bFinish = pxGrappleEvents[i].m_iEndFrame <= iCurrentFrame;
				}

				if (bFinish)
				{
					EndGrappleEvent(pxGrappleEvents[i], xOwner, pxEnemy, pxCurrentAnimController);
				}
			}
		}
	}
}

void AVP_Move::StartGrappleEvent(AVP_GrappleEvent_Entry& xEvent, AVP_ClientEntity_Actor& xOwner, AVP_ClientEntity_Actor* pxEnemy, Asura_AnimationController_Animation* pxCurrentAnimController)
{
	xEvent.m_bRunning = true;
	xEvent.m_bFinished = false;
	xEvent.m_uTypeData = 0;

	if (xEvent.m_uEventDataBlueprintID == ASURA_HASH_ID_UNSET) { return; }

	const Asura_Blueprint* pxEventData = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_GRAPPLEANIMATION, xEvent.m_uEventDataBlueprintID);
	if (!pxEventData) { return; }

	Asura_Value xValue;
	if (!pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_TYPE, xValue)) { return; }

	switch (xValue.GetIntValue())
	{
	default:
		break;

	case AVP_GET_CAMERAPHOTON:
		{
			AVP_Actor_HUDHandler* pxHUDHandler = xOwner.GetHUDHandler();
			if (pxHUDHandler)
			{
				Asura_Vector_3 xCameraOffset(Asura_Maths::ZeroVector3);
				float fRed = 255.0f;
				float fGreen = 255.0f;
				float fBlue = 255.0f;
				float fFadeInTime = 0.0f;
				float fFadeOutTime = 0.0f;
				float fPower = 1.0f;
				float fRange = 2.0f;

				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_CAMERAOFFSET_RT, xValue))
				{
					xCameraOffset.x = xValue.GetFloatValue();
				}
				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_CAMERAOFFSET_DN, xValue))
				{
					xCameraOffset.y = xValue.GetFloatValue();
				}
				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_CAMERAOFFSET_FW, xValue))
				{
					xCameraOffset.z = xValue.GetFloatValue();
				}

				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_COLOUR_RED, xValue))
				{
					fRed = xValue.GetFloatValue();
				}
				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_COLOUR_GREEN, xValue))
				{
					fGreen = xValue.GetFloatValue();
				}
				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_COLOUR_BLUE, xValue))
				{
					fBlue = xValue.GetFloatValue();
				}

				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_FADEIN, xValue))
				{
					fFadeInTime = xValue.GetFloatValue();
				}
				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_FADEOUT, xValue))
				{
					fFadeOutTime = xValue.GetFloatValue();
				}

				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_POWER, xValue))
				{
					fPower = xValue.GetFloatValue();
				}
				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_RANGE, xValue))
				{
					fRange = xValue.GetFloatValue();
				}

				u_int uPhotonID = pxHUDHandler->ApplyCameraPhoton(xCameraOffset, fRed, fGreen, fBlue, fFadeInTime, fFadeOutTime, fPower, fRange);
				xEvent.m_uTypeData = uPhotonID;
			}
		}
		break;

	case AVP_GET_DISMEMBERENEMY:
		{
			// Dismembering only works if we have an enemy.
			if (pxEnemy)
			{
				int iSkinElement;
				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_SKINELEMENT, xValue))
				{
					iSkinElement = xValue.GetIntValue();

					if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_MARKERPOINT, xValue))
					{
						// Attachment type.
						Asura_Hash_ID uMarkerPoint = xValue.GetHashIDValue();
						if (uMarkerPoint != ASURA_HASH_ID_UNSET)
						{
							// The gib comes from the enemy, so get the skin from them.
							const Asura_Blueprint* pxSkinElementBP = pxEnemy->GetSkinElementBlueprint(iSkinElement);
							Asura_Hash_ID uGibSkin = ASURA_HASH_ID_UNSET;
							if (pxSkinElementBP && pxSkinElementBP->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_SKINELEMENT_GIBSKIN, xValue))
							{
								uGibSkin = xValue.GetHashIDValue();
							}

							if (uGibSkin != ASURA_HASH_ID_UNSET)
							{
								Asura_Hash_ID uAnimation = ASURA_HASH_ID_UNSET;
								if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_GIBANIMATION, xValue))
								{
									uAnimation = xValue.GetHashIDValue();
								}

								// Animation is not actually required to be set.
								// Set up an attachment request.
								AVP_Actor_AttachmentHandler* pxAttachmentHandler = xOwner.GetAttachmentHandler();
								if (pxAttachmentHandler)
								{
									AVP_Actor_Attachment xAttachmentData;
									xAttachmentData.m_uSkinID = uGibSkin;
									xAttachmentData.m_uAnimID = uAnimation;
									xAttachmentData.m_uMarkerID = uMarkerPoint;
									xAttachmentData.m_bDeleteOnDeath = true;
									xAttachmentData.m_bUpdateFirstFrame = false;
									xAttachmentData.m_fAnimationStartTimeParametric = 0.0f;
									if (pxCurrentAnimController && (uAnimation != ASURA_HASH_ID_UNSET) && (xEvent.m_iStartFrame >= 0))
									{
										// We can calculate a start parametric if we have a start frame.
										const float fStartTime = static_cast<float>(xEvent.m_iStartFrame) * fONE_OVER_ANIMATION_FRAMES_PER_SECOND;
										const float fCurrentTime = pxCurrentAnimController->GetParametricTimer() * pxCurrentAnimController->GetDuration();
										const float fTimeSkipped = fCurrentTime - fStartTime;
										if (fTimeSkipped > 0.0f)
										{
											// Get the animation for the gib and work out the parametric for this delay.
											const Asura_Hierarchy_Anim* pxGibAnim = Asura_Animation_System::GetAnimByHash(uAnimation);
											if(pxGibAnim)
											{
												xAttachmentData.m_fAnimationStartTimeParametric = fTimeSkipped / pxGibAnim->GetDuration();
											}
										}
									}

									pxAttachmentHandler->AddAttachment(xAttachmentData);

									// Store the skin ID so we can unattach later.
									xEvent.m_uTypeData = uGibSkin;

									// We also want to attach the same pfx we normally do to a gib.
									Asura_Animation* pxAnim = xOwner.GetShapeInstance().GetAnimation();
									if (pxAnim)
									{
										AVP_ClientEntity_Gib::PFXAttachData axPFXAttachData[uAVP_VERTEBRATE_GIB_MAX_PFX_ATTACHMENTS];
										u_int uNumPFXAttachments = 0;
										AVP_ClientEntity_Gib::FillPFXDetailsFromBlueprint(pxSkinElementBP, axPFXAttachData, uNumPFXAttachments);
										const u_short usPFXFlags = ASURA_PFX_EFFECT_CAMERA_FLAGS;

										for (u_int u=0; u<uNumPFXAttachments; ++u)
										{
											const Asura_Hash_ID uPFXHash = axPFXAttachData[u].m_uPFXID;
											const Asura_Hash_ID uMarkerHash = axPFXAttachData[u].m_uMarkerID;
											if ((uPFXHash != ASURA_HASH_ID_UNSET) && (uMarkerHash != ASURA_HASH_ID_UNSET))
											{
												Asura_AnimationAttachment* pxAttachment = new Asura_AnimationAttachment_PFX(uPFXHash, usPFXFlags);
												if (pxAttachment)
												{
													pxAttachment->LinkAttachment(pxAnim, uMarkerHash, true);
												}
											}
										}
									}
								}
							}
						}

						// Destroy the skin element on the client.
						AVP_ClientActor_Vertebrate* pxVertEnemy = static_cast<AVP_ClientActor_Vertebrate*>(pxEnemy);
						pxVertEnemy->DestroySkinElement(iSkinElement);
					}
					else
					{
						// Either destroying a skin element or firing a gib. We'll use a dismember message for this.
						Asura_Vector_3 xRagdollDirection(0.0f, 1.0f, 0.0f);
						float fRagdollConeAngle = 0.0f;
						float fRagdollImpulse = 1.0f;
						Asura_Vector_3 xGibDirection(Asura_Maths::ZeroVector3); // Zero vector means no gib created.
						float fGibConeAngle = 0.0f;
						float fGibImpulse = 1.0f;

						if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_RAGDOLLDIR_RT, xValue))
						{
							xRagdollDirection.x = xValue.GetFloatValue();
						}
						if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_RAGDOLLDIR_DN, xValue))
						{
							xRagdollDirection.y = xValue.GetFloatValue();
						}
						if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_RAGDOLLDIR_FW, xValue))
						{
							xRagdollDirection.z = xValue.GetFloatValue();
						}
						if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_RAGDOLLDIR_CONE, xValue))
						{
							fRagdollConeAngle = Asura_Maths::DegToRad(xValue.GetFloatValue());
						}
						if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_RAGDOLLIMPULSE, xValue))
						{
							fRagdollImpulse = xValue.GetFloatValue();
						}

						if (!xRagdollDirection.ApproximatelyEquals(Asura_Maths::ZeroVector3))
						{
							xRagdollDirection = xRagdollDirection.ChooseRandomDirectionInCone(fRagdollConeAngle); // This will produce a normalised vector.
						}

						if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_GIBDIR_RT, xValue))
						{
							xGibDirection.x = xValue.GetFloatValue();
						}
						if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_GIBDIR_DN, xValue))
						{
							xGibDirection.y = xValue.GetFloatValue();
						}
						if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_GIBDIR_FW, xValue))
						{
							xGibDirection.z = xValue.GetFloatValue();
						}
						if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_GIBDIR_CONE, xValue))
						{
							fGibConeAngle = Asura_Maths::DegToRad(xValue.GetFloatValue());
						}
						if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_GIBIMPULSE, xValue))
						{
							fGibImpulse = xValue.GetFloatValue();
						}

						if (!xGibDirection.ApproximatelyEquals(Asura_Maths::ZeroVector3))
						{
							xGibDirection = xGibDirection.ChooseRandomDirectionInCone(fGibConeAngle); // This will produce a normalised vector.
						}

						const Asura_Matrix_3x3& xOwnerOri = xOwner.GetCurrentOrientation();
						xRagdollDirection = xRagdollDirection * xOwnerOri;
						xGibDirection = xGibDirection * xOwnerOri;

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

							Asura_Vector_3 xPosition;
							pxEnemy->GetHeadPosition(xPosition);

							pxData->SetElement(iSkinElement);
							pxData->SetDamagePosition(xPosition);
							pxData->SetRagdollDirection(xRagdollDirection);
							pxData->SetRagdollImpulse(fRagdollImpulse);
							pxData->SetGibDirection(xGibDirection); // The zero vector tells it not to create the head gib (the attachment is done in a different way).
							pxData->SetGibImpulse(fGibImpulse);
							pxData->SetDamage(0.0f); // Damage will be handled elsewhere.
							pxData->SetFromGuid(xOwner.GetGuid()); 

							pxMessage->SetFromGuid(xOwner.GetGuid());
							pxMessage->SetToGuid(pxEnemy->GetGuid());

							// Just handle directly.
							pxEnemy->HandleMessage(pxMessage);
							Asura_Network::DestroyMessage(pxMessage);
						}
					}

					// If we're on the server, make sure the skin element flags are correctly set up there too.
					AVP_ServerActor_Vertebrate* pxServerVert = AVP_ServerActor_Vertebrate::FindEntity(pxEnemy->GetGuid());
					if (pxServerVert)
					{
						pxServerVert->DestroySkinElement(iSkinElement);
					}
				}
			}
		}
		break;

	case AVP_GET_HEADBITEENEMY:
		{
			// Headbites only work if we have an enemy.
			if (pxEnemy)
			{
				AVP_ClientActor_Vertebrate* pxVertEnemy = static_cast<AVP_ClientActor_Vertebrate*>(pxEnemy);
				pxVertEnemy->ActivateHeadbitSkinElement();
			}
		}
		break;

	case AVP_GET_PFX:
		{
			// We need a marker point to create the PFX at.
			if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_MARKERPOINT, xValue))
			{
				Asura_Hash_ID uMarkerPoint = xValue.GetHashIDValue();
				if (uMarkerPoint != ASURA_HASH_ID_UNSET)
				{
					// A PFX effect would be handy too.
					if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_PFXEFFECT, xValue))
					{
						Asura_Hash_ID uPFXEffect = xValue.GetHashIDValue();
						if (uPFXEffect != ASURA_HASH_ID_UNSET)
						{
							// The attach parameter is optional.
							bool bAttach = false;
							if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_ATTACHTOMARKER, xValue))
							{
								bAttach = xValue.GetBoolValue();
							}

							// Store the ID so we can remove it at the end.
							u_int uPFXID = xOwner.CreatePFXEffect(uPFXEffect, uMarkerPoint, bAttach);
							xEvent.m_uTypeData = uPFXID;
						}
					}
				}
			}
		}
		break;
	}
}

void AVP_Move::EndGrappleEvent(AVP_GrappleEvent_Entry& xEvent, AVP_ClientEntity_Actor& xOwner, AVP_ClientEntity_Actor* pxEnemy, Asura_AnimationController_Animation* pxCurrentAnimController)
{
	xEvent.m_bRunning = false;
	xEvent.m_bFinished = true;

	if (xEvent.m_uEventDataBlueprintID == ASURA_HASH_ID_UNSET) { return; }

	const Asura_Blueprint* pxEventData = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_GRAPPLEANIMATION, xEvent.m_uEventDataBlueprintID);
	if (!pxEventData) { return; }

	Asura_Value xValue;
	if (!pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_TYPE, xValue)) { return; }

	switch (xValue.GetIntValue())
	{
	default:
		break;

	case AVP_GET_CAMERAPHOTON:
		{
			if (xEvent.m_uTypeData != 0)
			{
				AVP_Actor_HUDHandler* pxHUDHandler = xOwner.GetHUDHandler();
				if (pxHUDHandler)
				{
					pxHUDHandler->FinishCameraPhoton(xEvent.m_uTypeData);
					xEvent.m_uTypeData = 0;
				}
			}
		}
		break;

	case AVP_GET_DISMEMBERENEMY:
		{
			int iSkinElement;
			if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_SKINELEMENT, xValue))
			{
				iSkinElement = xValue.GetIntValue();

				if (pxEventData->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEEVENTDATA_MARKERPOINT, xValue))
				{
					// Attachment type. We need the gib skin so we can unattach it. That's stored in the type data variable.
					AVP_Actor_AttachmentHandler* pxAttachmentHandler = xOwner.GetAttachmentHandler();
					if (pxAttachmentHandler)
					{
						pxAttachmentHandler->DestroyAttachment(xEvent.m_uTypeData);
						xEvent.m_uTypeData = ASURA_HASH_ID_UNSET;
					}
				}
				// Other types have nothing to clean up.
			}
		}
		break;

	case AVP_GET_PFX:
		{
			if (xEvent.m_uTypeData != ASURA_PFX_INVALID_EFFECT_ID)
			{
				Asura_PFX_System::StopEntityEffect(xEvent.m_uTypeData, false);
				xEvent.m_uTypeData = ASURA_PFX_INVALID_EFFECT_ID;
			}
		}
		break;
	}
}

///////////////////////////////////////////////////////////////////////
// AVP_Move_Puppet
///////////////////////////////////////////////////////////////////////

static const u_int uAVP_PUPPET_COLLISION_GROUP = 0x00000001;

AVP_Move_Puppet::AVP_Move_Puppet(Asura_Movement_System& xParent)
: PARENT(xParent, GetStaticHash())
, m_xPosition(Asura_Maths::ZeroVector3)
, m_xOrientation(Asura_Maths::IdentityQuat)
, m_uCurrentPuppeteer(ASURA_GUID_UNREGISTERED)
, m_uLastUpdateFrameStamp(0)
, m_uLastStatesUpdateFrameStamp(0)
, m_uActiveGrappleVictimBlueprint( ASURA_HASH_ID_UNSET )
, m_uGrappleSoundsVictimBlueprint( ASURA_HASH_ID_UNSET )
, m_uActiveGrappleBlueprintType(uAVP_BLUEPRINT_TYPE_GRAPPLE)
, m_iNumFirstPersonGrappleEvents(0)
, m_iNumThirdPersonGrappleEvents(0)
, m_uPuppetFlags(0)
{
	// This is a reasonable default for puppet moves.
	SetAimDirectionUsage(AVP_ClientEntity_Actor::AIM_DIRECTION_RESET);

	m_bHideReticle = true;
}

AVP_Move_Puppet::~AVP_Move_Puppet()
{
}


bool AVP_Move_Puppet::HasFinished() const
{
	// We cannot be finished if the frame stamps don't match, since that means we've been updated externally and haven't gone through our own update yet.
	if (m_uLastUpdateFrameStamp != m_uLastStatesUpdateFrameStamp) { return false; }
	// Also make sure the matching update is this frame.
	if (m_uLastUpdateFrameStamp != Asura_Timers::GetFrameNumber()) { return false; }
	// Finally, check the bool which confirms we have got past UpdateStates in our own call of Update.
	if (!m_bCanFinish) { return false; }

	// Has the puppeteer told us we're done?
	if (m_bFinishedByPuppeteer) { return true; }

	// Have we still got a puppeteer? If we do, they haven't let us finish yet.
	const AVP_ClientEntity_Actor* pxPuppeteer = AVP_ClientEntity_Actor::FindEntity(m_uCurrentPuppeteer);
	if (pxPuppeteer) { return false; }

	// Finally, if we no longer have a puppeteer, just run the usual HasFinished rules since we may just be letting an animation play to the end.
	return PARENT::HasFinished();
}

void AVP_Move_Puppet::ExternalUpdate(const float fTimeStep)
{
	// AJG - 21/02/08 - Note that in its current form it doesn't matter if this is called multiple times in a frame as it will only do something the first time.
	//					This makes it easier to have a call in the default Puppeteer move, but still allow subclasses to update this earlier in their update if they need to, so I recommend keeping this allowance.
	m_bCanFinish = false;

	// Just run the central section of the update, to get all states updated.
	CheckStatesToStart();
	UpdateStates(fTimeStep);
}

void AVP_Move_Puppet::SetPosition(const Asura_Vector_3& xPosition, const float fTimeStep)
{
	m_xPosition = xPosition;

	Asura_AVector3 xCurrentPosition;
	CalculatePosition(fTimeStep, xCurrentPosition);
	m_xParentSystem.UpdateMoveVelocityFromPosition(fTimeStep, xCurrentPosition, true);
}

void AVP_Move_Puppet::SetOrientation(const Asura_Quat& xOrientation, const float fTimeStep)
{
	m_xOrientation = xOrientation;

	Asura_Quat xCurrentOrientation;
	CalculateOrientation(fTimeStep, xCurrentOrientation);
	m_xParentSystem.SetCurrentMoveOrientation(xCurrentOrientation);
}

void AVP_Move_Puppet::SetRelativeToPuppeteer(bool bRelative)
{
	//Done as two comparisons to avoid a signed-unsigned mismatch warning
	if (bRelative && m_bRelativeToPuppeteer) { return; }
	if (!bRelative && !m_bRelativeToPuppeteer) { return; }

	m_bRelativeToPuppeteer = bRelative;

	const AVP_ClientEntity_PhysicalObject* pxPuppeteer = AVP_ClientEntity_PhysicalObject::FindEntity(m_uCurrentPuppeteer);
	if (!pxPuppeteer) { return; }

	if (m_bRelativeToPuppeteer)
	{
		// If we're becoming relative, convert the worldspace data to puppeteer-space
		Asura_Quat xInverseOri(pxPuppeteer->GetCurrentOrientation());
		xInverseOri.Invert();

		m_xPosition -= pxPuppeteer->GetCurrentPosition();
		m_xPosition = xInverseOri.RotateVector(m_xPosition);

		m_xOrientation = m_xOrientation * xInverseOri;
	}
	else
	{
		// ...and the opposite if we're not.
		Asura_Quat xOri(pxPuppeteer->GetCurrentOrientation());

		m_xPosition = xOri.RotateVector(m_xPosition);
		m_xPosition += pxPuppeteer->GetCurrentPosition();

		m_xOrientation = m_xOrientation * xOri;
	}
}

void AVP_Move_Puppet::SetCurrentPuppeteerGuid(Asura_Guid uPuppeteer)
{
	// Update a couple of flags alongside the puppeteer, as we'll be using positions set by the puppeteer for as long as we have one.
	m_uCurrentPuppeteer = uPuppeteer;

	if (m_uCurrentPuppeteer != ASURA_GUID_UNREGISTERED)
	{
		SetPositionUsage(POSITION_SET_DIRECTLY);
		SetOrientationUsage(ORIENTATION_SET_DIRECTLY);
		ActivateExternalControlPhysics();
	}
	else
	{
		SetPositionUsage(POSITION_NOT_USED);
		SetOrientationUsage(ORIENTATION_SET_DIRECTLY);
		DeactivateExternalControlPhysics();
	}
}

void AVP_Move_Puppet::GetRelativePositionData(Asura_Vector_3& xPosition, Asura_Quat& xOrientation) const
{
	Asura_Assert(m_bRelativeToPuppeteer, "Should only try to get relative position data if we are relative to a puppeteer.");

	xPosition = m_xPosition;
	xOrientation = m_xOrientation;
}

void AVP_Move_Puppet::ResetGrappleEvents()
{
	AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
	AVP_ClientEntity_Actor* pxEnemy = AVP_ClientEntity_Actor::FindEntity(m_uCurrentPuppeteer);
	Asura_AnimationController_Animation* pxAnimController = m_pxAVPAnimationData ? m_pxAVPAnimationData->GetAnimationController() : NULL;
	for (int i=0; i<m_iNumFirstPersonGrappleEvents; ++i)
	{
		if (m_axFirstPersonGrappleEvents[i].m_bRunning)
		{
			EndGrappleEvent(m_axFirstPersonGrappleEvents[i], xOwner, pxEnemy, pxAnimController);
		}
	}
	for (int i=0; i<m_iNumThirdPersonGrappleEvents; ++i)
	{
		if (m_axThirdPersonGrappleEvents[i].m_bRunning)
		{
			EndGrappleEvent(m_axThirdPersonGrappleEvents[i], xOwner, pxEnemy, pxAnimController);
		}
	}

	m_iNumFirstPersonGrappleEvents = 0;
	m_iNumThirdPersonGrappleEvents = 0;
}

bool AVP_Move_Puppet::Start(const Asura_Msg_Move* pxStartMsg)
{
	m_bRequiresRestart = false;

	// TODO - special Puppet start message?
	// Like this, it only works if the puppeteer sets up all data externally, before sending the start message.

	return PARENT::Start(pxStartMsg);
}

Asura_Move* AVP_Move_Puppet::Update()
{
	const float	fTimeStep = Asura_Timers::GetGameTimeSlice();

	// Update the puppeteer first, as that will set up any additional details we need for our own update.
	AVP_ClientEntity_PhysicalObject* pxPuppeteer = AVP_ClientEntity_PhysicalObject::FindEntity(m_uCurrentPuppeteer);
	if (pxPuppeteer)
	{
		// If they've died, but haven't released us or kicked us into a different move, make sure we disconnect.
		if (pxPuppeteer->IsDead())
		{
			if (!GetParentSystem().GetOwner().IsDead())
			{
				EnsureAnimationFinishes();
			}
			SetCurrentPuppeteerGuid(ASURA_GUID_UNREGISTERED);
		}
		else
		{
			AVP_Movement_System* pxMovementSystem = static_cast<AVP_Movement_System*>(pxPuppeteer->GetMovementSystem());
			if (pxMovementSystem)
			{
				// Make sure they still have an appropriate move.
				const AVP_Move* pxMove = static_cast<const AVP_Move*>(pxMovementSystem->GetCurrentMove());
				if (!pxMove || !pxMove->HasPuppet())
				{
					if (!GetParentSystem().GetOwner().IsDead())
					{
						EnsureAnimationFinishes();
					}
					SetCurrentPuppeteerGuid(ASURA_GUID_UNREGISTERED);
				}
				else
				{
					pxMovementSystem->ExternalUpdateCurrentMove(fTimeStep);
				}
			}
		}
	}

	m_bCanFinish = false;
	m_uLastUpdateFrameStamp = Asura_Timers::GetFrameNumber();

	return PARENT::Update();
}

void AVP_Move_Puppet::Finish()
{
	PARENT::Finish();
	ResetNextMoves(); // puppets moves are build dynamically
	// Force any subsequent puppeteers to set up all relevant data.
	m_xPosition = Asura_Maths::ZeroVector3;
	m_xOrientation = Asura_Maths::IdentityQuat;
	m_uCurrentPuppeteer = ASURA_GUID_UNREGISTERED;
	m_uLastUpdateFrameStamp = 0;
	m_uLastStatesUpdateFrameStamp = 0;
	m_uPuppetFlags = 0;
	m_uAVPMoveFlags = 0;
	m_bHideReticle = true;
	ClearFlags();
	// This is a reasonable default for puppet moves.
	SetAimDirectionUsage(AVP_ClientEntity_Actor::AIM_DIRECTION_RESET);
	ResetGrappleEvents();
}

void AVP_Move_Puppet::Abort()
{
	PARENT::Abort();
	ResetNextMoves(); // puppets moves are build dynamically
	// Force any subsequent puppeteers to set up all relevant data.
	m_xPosition = Asura_Maths::ZeroVector3;
	m_xOrientation = Asura_Maths::IdentityQuat;
	m_uCurrentPuppeteer = ASURA_GUID_UNREGISTERED;
	m_uLastUpdateFrameStamp = 0;
	m_uLastStatesUpdateFrameStamp = 0;
	m_uPuppetFlags = 0;
	m_uAVPMoveFlags = 0;
	m_bHideReticle = true;
	ClearFlags();
	// This is a reasonable default for puppet moves.
	SetAimDirectionUsage(AVP_ClientEntity_Actor::AIM_DIRECTION_RESET);
	ResetGrappleEvents();
}

DEBUGVAR bool dbg_bUseTimerUpdateAsSync = true;

void AVP_Move_Puppet::CheckStatesToStart()
{
	// If we've already updated states this frame, don't do anything here.
	if (m_uLastStatesUpdateFrameStamp == Asura_Timers::GetFrameNumber()) { return; }

	if (m_bRequiresRestart)
	{
		Reset();
		Start(NULL);
		m_bRequiresRestart = false;
	}

	// If the animation has yet to start, make sure the animation timers have already updated, so both this and the puppeteer definitely won't end up updating differently on the first frame.
	if (dbg_bUseTimerUpdateAsSync && m_pxAVPAnimationData && m_pxAVPAnimationData->IsWaiting())
	{
		Asura_ClientEntity_PhysicalObject& xOwner = m_xParentSystem.GetOwner();
		Asura_Shape_Instance& xShapeInstance = xOwner.GetShapeInstance();
		Asura_Animation* pxAnimation = xShapeInstance.GetAnimation();
		if (pxAnimation)
		{
			pxAnimation->UpdateTimers(ANIMTIME_USEGAMETIME, false);
		}
	}

	PARENT::CheckStatesToStart();
}

void AVP_Move_Puppet::UpdateStates(const float fTimeStep)
{
	// Make sure the can finish flag is updated.
	const u_int uFrameNumber = Asura_Timers::GetFrameNumber();
	m_bCanFinish = m_uLastUpdateFrameStamp == uFrameNumber;

	// If we've already updated states this frame, don't do anything more here.
	if (m_uLastStatesUpdateFrameStamp == uFrameNumber) { return; }

	if (!m_bAnimUpdatedByPuppeteer)
	{
		// We can't call the parent as we have our own positional calculations to set up.
		if(!UpdateAnimation(fTimeStep))
		{
			FinishAnimation();
		}
	}
	m_bAnimUpdatedByPuppeteer = false;

	UpdateGrappleEvents();

	// See if we still have a puppeteer, and if not we can use the regular positional calculations.
	const AVP_ClientEntity_PhysicalObject* pxPuppeteer = AVP_ClientEntity_PhysicalObject::FindEntity(m_uCurrentPuppeteer);
	if (!pxPuppeteer)
	{
		if(!UpdateOrientationInterpolation(fTimeStep))
		{
			FinishOrientationInterpolation();
		}

		if(!UpdatePositionInterpolation(fTimeStep))
		{
			FinishPositionInterpolation();
		}
	}

	m_uLastStatesUpdateFrameStamp = uFrameNumber;


	// same as for puppeteer.
	const AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
	if ( ( xOwner.GetFlagsForAllAnimsLastFrame() & AVP_ANIMFLAG_SHOW_MELEE_COUNTERBAR ) != 0 )
	{
		// SEARCH FOR HUD.
		AVP_Vertebrate_HUDHandler* pxHUDHandler = static_cast<AVP_Vertebrate_HUDHandler*>(GetAVPParentSystem().GetActorOwner().GetHUDHandler());
		if( pxHUDHandler )
		{
			pxHUDHandler->StartActiveGrapple( m_uActiveGrappleVictimBlueprint, m_uGrappleSoundsVictimBlueprint, m_uActiveGrappleBlueprintType );
		}
	}
}

bool AVP_Move_Puppet::CalculatePosition(const float fTimeStep, Asura_AVector3_OutParam xPosition) const
{
	// If we still have a puppeteer, use our stored data.
	// Otherwise, assume we're being allowed to finish whatever's left set up, so update normally.
	const AVP_ClientEntity_PhysicalObject* pxPuppeteer = AVP_ClientEntity_PhysicalObject::FindEntity(m_uCurrentPuppeteer);
	if (!pxPuppeteer)
	{
		if (GetPositionInterpolationData())
		{
			return PARENT::CalculatePosition(fTimeStep, xPosition);
		}
		else
		{
			xPosition = m_xPosition;
			return false;
		}
	}

	// If we're using worldspace, things are simple.
	if (!m_bRelativeToPuppeteer)
	{
		xPosition = m_xPosition;
		return true;
	}

	// Otherwise, we need to calculate what our worldspace position is from the puppeteer.
	xPosition = (m_xPosition * pxPuppeteer->GetCurrentOrientation()) + pxPuppeteer->GetCurrentPosition();
	return true;
}

bool AVP_Move_Puppet::CalculateOrientation(const float fTimeStep, Asura_Quat& xOrientation) const
{
	// If we still have a puppeteer, use our stored data.
	// Otherwise, assume we're being allowed to finish whatever's left set up, so update normally.
	const AVP_ClientEntity_PhysicalObject* pxPuppeteer = AVP_ClientEntity_PhysicalObject::FindEntity(m_uCurrentPuppeteer);
	if (!pxPuppeteer)
	{
		if (GetOrientationInterpolationData())
		{
			return PARENT::CalculateOrientation(fTimeStep, xOrientation);
		}
		else
		{
			xOrientation = m_xOrientation;
			return false;
		}
	}

	// If we're using worldspace, things are simple.
	if (!m_bRelativeToPuppeteer)
	{
		xOrientation = m_xOrientation;
		return true;
	}

	// Otherwise, we need to calculate what our worldspace orientation is from the puppeteer.
	Asura_Quat xPuppeteerOri(pxPuppeteer->GetCurrentOrientation());
	xOrientation = m_xOrientation * xPuppeteerOri;
	return true;
}

bool AVP_Move_Puppet::PuppeteerUpdateAnimation(const float fTimeStep)
{
	m_bAnimUpdatedByPuppeteer = true;
	if (dbg_bUseTimerUpdateAsSync && m_pxAVPAnimationData && m_pxAVPAnimationData->IsWaiting())
	{
		Asura_ClientEntity_PhysicalObject& xOwner = m_xParentSystem.GetOwner();
		Asura_Shape_Instance& xShapeInstance = xOwner.GetShapeInstance();
		Asura_Animation* pxAnimation = xShapeInstance.GetAnimation();
		if (pxAnimation)
		{
			pxAnimation->UpdateTimers(ANIMTIME_USEGAMETIME, false);
		}
	}
	StartAnimation();
	return UpdateAnimation(fTimeStep);
}

void AVP_Move_Puppet::UpdateGrappleEvents()
{
	if (m_pxAVPAnimationData)
	{
		AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
		AVP_ClientEntity_Actor* pxEnemy = AVP_ClientEntity_Actor::FindEntity(m_uCurrentPuppeteer);
		if (xOwner.IsUsingFirstPersonSkin())
		{
			AVP_Move::UpdateGrappleEvents(m_axFirstPersonGrappleEvents, m_iNumFirstPersonGrappleEvents, xOwner, pxEnemy, m_pxAVPAnimationData->GetAnimationController());
		}
		else
		{
			AVP_Move::UpdateGrappleEvents(m_axThirdPersonGrappleEvents, m_iNumThirdPersonGrappleEvents, xOwner, pxEnemy, m_pxAVPAnimationData->GetAnimationController());
		}
	}
}


///////////////////////////////////////////////////////////////////////
// AVP_Move_Puppeteer
///////////////////////////////////////////////////////////////////////

AVP_Move_Puppeteer::AVP_Move_Puppeteer(Asura_Movement_System& xParent, const Asura_Hash_ID uHash)
: PARENT(xParent, uHash)
, m_uCurrentPuppet(ASURA_GUID_UNREGISTERED)
, m_uLastUpdateFrameStamp(0)
, m_uLastStatesUpdateFrameStamp(0)
, m_xInitialPuppetPosition(Asura_Maths::ZeroVector3)
, m_xInitialPuppetOrientation(Asura_Maths::IdentityQuat)
, m_uPuppeteerFSFX(ASURA_HASH_ID_UNSET)
, m_uPuppetFSFX(ASURA_HASH_ID_UNSET)
, m_uActiveGrappleAttackerBlueprint(ASURA_HASH_ID_UNSET)
, m_uActiveGrappleVictimBlueprint(ASURA_HASH_ID_UNSET)
, m_uGrappleSoundsAttackerBlueprint(ASURA_HASH_ID_UNSET)
, m_uGrappleSoundsVictimBlueprint(ASURA_HASH_ID_UNSET)
, m_uActiveGrappleBlueprintType(uAVP_BLUEPRINT_TYPE_GRAPPLE)
, m_xRelativeModeOwnerBasePosition(Asura_Maths::ZeroAVector3)
, m_xRelativeModeOwnerBaseOrientation(Asura_Maths::IdentityQuat)
, m_fRelativeModeOwnerFallingSpeed(0.0f)
, m_xRelativeModeOwnerPosition(Asura_Maths::ZeroAVector3)
, m_xRelativeModeOwnerOrientation(Asura_Maths::IdentityQuat)
, m_xRelativeModePuppetPosition(Asura_Maths::ZeroAVector3)
, m_xRelativeModePuppetOrientation(Asura_Maths::IdentityQuat)
, m_iNumFirstPersonGrappleEvents(0)
, m_iNumThirdPersonGrappleEvents(0)
, m_uPuppeteerFlags(0)
{
	SetAimDirectionUsage( AVP_ClientEntity_Actor::AIM_DIRECTION_RESET );

	m_bHideReticle = true;
}

AVP_Move_Puppeteer::~AVP_Move_Puppeteer()
{
}

bool AVP_Move_Puppeteer::HasFinished() const
{
	// We cannot be finished if the frame stamps don't match, since that means we've been updated externally and haven't gone through our own update yet.
	if (m_uLastUpdateFrameStamp != m_uLastStatesUpdateFrameStamp) { return false; }
	// Also make sure the matching update is this frame.
	if (m_uLastUpdateFrameStamp != Asura_Timers::GetFrameNumber()) { return false; }
	// Finally, check the bool which confirms we have got past UpdateStates in our own call of Update.
	if (!m_bCanFinish) { return false; }

	// Having checked all that, we can check the normal HasFinished rules.
	return PARENT::HasFinished();
}

void AVP_Move_Puppeteer::ExternalUpdate(const float fTimeStep)
{
	m_bCanFinish = false;

	// Just run the central section of the update, to get all states updated.
	CheckStatesToStart();
	UpdateStates(fTimeStep);
}

void AVP_Move_Puppeteer::ResetGrappleEvents()
{
	AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
	AVP_ClientEntity_Actor* pxEnemy = AVP_ClientEntity_Actor::FindEntity(m_uCurrentPuppet);
	Asura_AnimationController_Animation* pxAnimController = m_pxAVPAnimationData ? m_pxAVPAnimationData->GetAnimationController() : NULL;
	for (int i=0; i<m_iNumFirstPersonGrappleEvents; ++i)
	{
		if (m_axFirstPersonGrappleEvents[i].m_bRunning)
		{
			EndGrappleEvent(m_axFirstPersonGrappleEvents[i], xOwner, pxEnemy, pxAnimController);
		}
	}
	for (int i=0; i<m_iNumThirdPersonGrappleEvents; ++i)
	{
		if (m_axThirdPersonGrappleEvents[i].m_bRunning)
		{
			EndGrappleEvent(m_axThirdPersonGrappleEvents[i], xOwner, pxEnemy, pxAnimController);
		}
	}

	m_iNumFirstPersonGrappleEvents = 0;
	m_iNumThirdPersonGrappleEvents = 0;
}

void AVP_Move_Puppeteer::Reset()
{
	m_xPuppetPositionInterpolation.Reset();
	m_xPuppetOrientationInterpolation.Reset();

	ResetGrappleEvents();

	PARENT::Reset();
}

DEBUGVAR float dbg_fPuppeteerFSFXTime = 0.5f;

bool AVP_Move_Puppeteer::Start(const Asura_Msg_Move* pxStartMsg)
{
	// TODO - special Puppeteer start message?
	// Like this, it only works if the subclasses set everything up, which may well be something we can largely share code for.

	Asura_ClientEntity_PhysicalObject* pxPuppet = Asura_ClientEntity_PhysicalObject::FindEntity(m_uCurrentPuppet);
	if (pxPuppet)
	{
		const AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();

		m_xRelativeModeOwnerBasePosition = Asura_AVector3(xOwner.GetCurrentPosition());
		m_xRelativeModeOwnerBaseOrientation.FromMatrix(xOwner.GetCurrentOrientation());
		m_fRelativeModeOwnerFallingSpeed = 0.0f;

		m_xRelativeModeOwnerPosition = m_xRelativeModeOwnerBasePosition;
		m_xRelativeModeOwnerOrientation = m_xRelativeModeOwnerBaseOrientation;
		m_xRelativeModePuppetPosition = Asura_AVector3(pxPuppet->GetCurrentPosition());
		m_xRelativeModePuppetOrientation.FromMatrix(pxPuppet->GetCurrentOrientation());

		m_xInitialPuppetPosition = pxPuppet->GetCurrentPosition();
		m_xInitialPuppetOrientation = m_xRelativeModePuppetOrientation;
		SetPositionUsage(POSITION_SET_DIRECTLY);

		if (m_bPuppetRelativeToOwner)
		{
			Asura_Quat xInverseOwnerOri(m_xRelativeModeOwnerBaseOrientation);
			xInverseOwnerOri.Invert();

			m_xInitialPuppetPosition -= xOwner.GetCurrentPosition();
			m_xInitialPuppetPosition = xInverseOwnerOri.RotateVector(m_xInitialPuppetPosition);

			m_xInitialPuppetOrientation = m_xInitialPuppetOrientation * xInverseOwnerOri;
		}

		if (m_bInterpolateInitialPuppetPos)
		{
			m_xPuppetPositionInterpolation.Start(Asura_AVector3(m_xInitialPuppetPosition));
		}

		if (m_bInterpolateInitialPuppetOri)
		{
			m_xPuppetOrientationInterpolation.Start(m_xInitialPuppetOrientation, *this);
		}

		AVP_Move_Puppet* pxPuppetMove = static_cast<AVP_Move_Puppet*>(pxPuppet->GetMovementSystem() ? pxPuppet->GetMovementSystem()->GetMove(AVP_Move_Puppet::GetStaticHash()) : NULL);
		if (pxPuppetMove)
		{
			pxPuppetMove->SetRestartMove(true); // Ensures that if this is a continuation, the data gets updated.

			pxPuppetMove->SetActiveGrappleVictimBlueprint( m_uActiveGrappleVictimBlueprint );
			pxPuppetMove->SetGrappleSoundsVictimBlueprint( m_uGrappleSoundsVictimBlueprint );
			pxPuppetMove->SetActiveGrappleBlueprintType( m_uActiveGrappleBlueprintType );
		}

		// Start off the puppet move. Use the server start message so we don't abort a puppet move has already started.
		Asura_Message* pxStartMessage = Asura_Network::CreateMessage(AVP_MSGID_MOVESTART_SERVER, sizeof(Asura_Msg_Move));
		if (pxStartMessage)
		{
			Asura_Msg_Move* pxData = static_cast<Asura_Msg_Move*>(pxStartMessage->GetData());
			if (pxData)
			{
				pxData->ClearFlags();
				pxData->SetMoveID(AVP_Move_Puppet::GetStaticHash());
				pxData->SetIgnoredByOwner(false);
				pxData->SetForceAbort(true);

				pxPuppet->HandleMessage(pxStartMessage);

				// If we're on the server, make sure the AI is aware of this.
				// We need to do this as we aren't sending the message by normal means in order to ensure each machine starts the puppet and puppeteer on the same frame.
				AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity( m_uCurrentPuppet );

				if ( pxActor && pxActor->GetActorBrain() )
				{
					pxActor->GetActorBrain()->DoHandleMessage( pxStartMessage );
				}
			}

			Asura_Network::DestroyMessage(pxStartMessage);
		}

		// See if the local player is involved in this, and set up FSFX if they are.
		const AVP_Player* pxLocalPlayer = AVP_Game_Core::GetPlayer();
		if (pxLocalPlayer)
		{
			const Asura_Guid uLocalPlayerGuid = pxLocalPlayer->GetPuppetGuid();
			const Asura_Guid uPuppeteerGuid = GetParentSystem().GetOwnerGuid();

			if (uLocalPlayerGuid == uPuppeteerGuid)
			{
				AVP_Game_Core::SetFSFXValue(m_uPuppeteerFSFX, 1.0f, dbg_fPuppeteerFSFXTime);
			}
			else if (uLocalPlayerGuid == m_uCurrentPuppet)
			{
				AVP_Game_Core::SetFSFXValue(m_uPuppetFSFX, 1.0f, dbg_fPuppeteerFSFXTime);
			}
		}
	}

	bool bStarted = PARENT::Start(pxStartMsg);

	if (bStarted)
	{
		EnablePhysicsSettings();
	}

	return bStarted;
}

Asura_Move* AVP_Move_Puppeteer::Update()
{
	m_bCanFinish = false;
	m_uLastUpdateFrameStamp = Asura_Timers::GetFrameNumber();

	return PARENT::Update();
}

void AVP_Move_Puppeteer::Finish()
{
	// See if the local player is involved in this, and set up FSFX if they are.
	const AVP_Player* pxLocalPlayer = AVP_Game_Core::GetPlayer();
	if (pxLocalPlayer)
	{
		const Asura_Guid uLocalPlayerGuid = pxLocalPlayer->GetPuppetGuid();
		const Asura_Guid uPuppeteerGuid = GetParentSystem().GetOwnerGuid();

		if (uLocalPlayerGuid == uPuppeteerGuid)
		{
			AVP_Game_Core::SetFSFXValue(m_uPuppeteerFSFX, 0.0f, dbg_fPuppeteerFSFXTime);
		}
		else if (uLocalPlayerGuid == m_uCurrentPuppet)
		{
			AVP_Game_Core::SetFSFXValue(m_uPuppetFSFX, 0.0f, dbg_fPuppeteerFSFXTime);
		}
	}

	PARENT::Finish();

	if (m_bPuppetRelativeToOwner)
	{
		DisablePhysicsSettings();
	}
}

void AVP_Move_Puppeteer::Abort()
{
	// See if the local player is involved in this, and set up FSFX if they are.
	const AVP_Player* pxLocalPlayer = AVP_Game_Core::GetPlayer();
	if (pxLocalPlayer)
	{
		const Asura_Guid uLocalPlayerGuid = pxLocalPlayer->GetPuppetGuid();
		const Asura_Guid uPuppeteerGuid = GetParentSystem().GetOwnerGuid();

		if (uLocalPlayerGuid == uPuppeteerGuid)
		{
			AVP_Game_Core::SetFSFXValue(m_uPuppeteerFSFX, 0.0f, dbg_fPuppeteerFSFXTime);
		}
		else if (uLocalPlayerGuid == m_uCurrentPuppet)
		{
			AVP_Game_Core::SetFSFXValue(m_uPuppetFSFX, 0.0f, dbg_fPuppeteerFSFXTime);
		}
	}

	PARENT::Abort();

	if (m_bPuppetRelativeToOwner)
	{
		DisablePhysicsSettings();
	}
}

void AVP_Move_Puppeteer::CheckStatesToStart()
{
	// If we've already updated states this frame, don't do anything here.
	if (m_uLastStatesUpdateFrameStamp == Asura_Timers::GetFrameNumber()) { return; }

	// If the animation has yet to start, make sure the animation timers have already updated, so both this and the puppet definitely won't end up updating differently on the first frame.
	if (dbg_bUseTimerUpdateAsSync && m_pxAVPAnimationData && m_pxAVPAnimationData->IsWaiting())
	{
		Asura_ClientEntity_PhysicalObject& xOwner = m_xParentSystem.GetOwner();
		Asura_Shape_Instance& xShapeInstance = xOwner.GetShapeInstance();
		Asura_Animation* pxAnimation = xShapeInstance.GetAnimation();
		if (pxAnimation)
		{
			pxAnimation->UpdateTimers(ANIMTIME_USEGAMETIME, false);
		}
	}

	PARENT::CheckStatesToStart();
}

void AVP_Move_Puppeteer::UpdateStates(const float fTimeStep)
{
	// Make sure the can finish flag is updated.
	const u_int uFrameNumber = Asura_Timers::GetFrameNumber();
	m_bCanFinish = m_uLastUpdateFrameStamp == uFrameNumber;

	// If we've already updated states this frame, don't do anything more here.
	if (m_uLastStatesUpdateFrameStamp == uFrameNumber) { return; }

	// Update our interpolations.
	if (m_bInterpolateInitialPuppetOri)
	{
		if (!m_xPuppetOrientationInterpolation.Update(fTimeStep))
		{
			m_xPuppetOrientationInterpolation.Finish();
		}
		m_xInitialPuppetOrientation = m_xPuppetOrientationInterpolation.GetActual();
	}

	if (m_bInterpolateInitialPuppetPos)
	{
		if (!m_xPuppetPositionInterpolation.Update(fTimeStep))
		{
			m_xPuppetPositionInterpolation.Finish();
		}
		m_xInitialPuppetPosition = m_xPuppetPositionInterpolation.GetActual();
	}

	AVP_ClientEntity_PhysicalObject* pxPuppet = AVP_ClientEntity_PhysicalObject::FindEntity(m_uCurrentPuppet);

	// The puppet-relative update is now far more interdependent, so has to run in completely its own way.
	if (m_bPuppetRelativeToOwner)
	{
		UpdateStatesRelativePuppet(fTimeStep, pxPuppet);
	}
	else
	{
		PARENT::UpdateStates(fTimeStep);
	}

	UpdateGrappleEvents();

	// Check if we should be disconnecting from the character at this point
	m_uLastStatesUpdateFrameStamp = uFrameNumber;

	// Update the puppet after us, as we'll be setting up any additional details for it in our move.
	// Also, see the note in the Puppet move ExternalUpdate relating to it being fine to call that multiple times in a frame (if subclasses need to update it earlier).
	// We just have to be a little careful that the frame stamps are updated early enough, to make sure this doesn't become circular. I believe things are fine at the moment (21/02/08).
	if (pxPuppet)
	{
		AVP_Movement_System* pxMovementSystem = static_cast<AVP_Movement_System*>(pxPuppet->GetMovementSystem());
		if (pxMovementSystem)
		{
			// We need to get the puppet move in order to update data on it, but we'll use the normal movement system external update function just in case something has stopped the puppet move being the current move.
			AVP_Move_Puppet* pxPuppetMove = static_cast<AVP_Move_Puppet*>(pxMovementSystem->GetMove(AVP_Move_Puppet::GetStaticHash()));

			if (pxPuppetMove)
			{
				// See if we should now be disconnecting.
				const u_int uAnimFlags = GetAnimFlagsForLastFrame();
				if ((uAnimFlags & AVP_ANIMFLAG_SEPARATE_CHARACTERS) != 0)
				{
					SeparateCharacters(pxPuppetMove);
				}
				else
				{
					AVP_Move_Puppet& xPuppetMove = *pxPuppetMove;
					xPuppetMove.SetRelativeToPuppeteer(m_bPuppetRelativeToOwner);
					UpdatePuppetAnimationData(xPuppetMove);

					pxMovementSystem->ExternalUpdateCurrentMove(fTimeStep);

					UpdatePuppetOrientation(xPuppetMove, fTimeStep);
					UpdatePuppetPosition(xPuppetMove, fTimeStep);
				}
			}
		}
	}


	// Check for anim state for ActiveGrapple.  (same as for puppet)
	const AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
	if ( ( xOwner.GetFlagsForAllAnimsLastFrame() & AVP_ANIMFLAG_SHOW_MELEE_COUNTERBAR ) != 0 )
	{
		// SEARCH FOR HUD.
		AVP_Vertebrate_HUDHandler* pxHUDHandler = static_cast<AVP_Vertebrate_HUDHandler*>(GetAVPParentSystem().GetActorOwner().GetHUDHandler());
		if( pxHUDHandler )
		{
			pxHUDHandler->StartActiveGrapple( m_uActiveGrappleAttackerBlueprint, m_uGrappleSoundsAttackerBlueprint, m_uActiveGrappleBlueprintType );
		}
	}
}

void AVP_Move_Puppeteer::UpdateStatesRelativePuppet(const float fTimeStep, AVP_ClientEntity_PhysicalObject* pxPuppet)
{
	// Disappointingly, we can't call the parent update as the position interpolation code doesn't work with this.
	if (m_bSupportEndAnimFlag && m_bCanFinishEarly)
	{
		const AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
		if (xOwner.GetInputHandler() && (xOwner.GetInputHandler()->GetMovementInput().MagnitudeSquared() >= dbg_fMovementInputToExitEarlySquared))
		{
			m_bFinishEarly = true;
		}
	}

	if (!UpdateAnimation(fTimeStep))
	{
		FinishAnimation();
	}

	if (m_bPuppetRelativeToOwner && pxPuppet)
	{
		AVP_Movement_System* pxMovementSystem = static_cast<AVP_Movement_System*>(pxPuppet->GetMovementSystem());
		if (pxMovementSystem)
		{
			AVP_Move_Puppet* pxPuppetMove = static_cast<AVP_Move_Puppet*>(pxMovementSystem->GetMove(AVP_Move_Puppet::GetStaticHash()));

			if (pxPuppetMove)
			{
				AVP_Move_Puppet& xPuppetMove = *pxPuppetMove;
				xPuppetMove.SetRelativeToPuppeteer(m_bPuppetRelativeToOwner);
				UpdatePuppetAnimationData(xPuppetMove);
				if (!xPuppetMove.PuppeteerUpdateAnimation(fTimeStep))
				{
					xPuppetMove.FinishAnimation();
				}

				CalculateRelativeModePositions(xPuppetMove, fTimeStep);
			}
		}
	}

	if (!UpdateOrientationInterpolation(fTimeStep))
	{
		FinishOrientationInterpolation();
	}

	if (m_bPuppetRelativeToOwner)
	{
		Asura_AVector3 xCurrentPosition;

		const bool bUpdating = CalculatePosition(fTimeStep, xCurrentPosition);

		m_xParentSystem.UpdateMoveVelocityFromPosition(fTimeStep, xCurrentPosition, true);
		
		if (!bUpdating)
		{
			FinishPositionInterpolation();
		}
	}
	else
	{
		if (!UpdatePositionInterpolation(fTimeStep))
		{
			FinishPositionInterpolation();
		}
	}
}

bool AVP_Move_Puppeteer::CalculatePosition(const float fTimeStep, Asura_AVector3& xPosition) const
{
	if (m_bPuppetRelativeToOwner)
	{
		xPosition = m_xRelativeModeOwnerPosition;
		return false;
	}
	else
	{
		return PARENT::CalculatePosition(fTimeStep, xPosition);
	}
}

bool AVP_Move_Puppeteer::CalculateOrientation(const float fTimeStep, Asura_Quat& xOrientation) const
{
	if (m_bPuppetRelativeToOwner)
	{
		xOrientation = m_xRelativeModeOwnerOrientation;
		return false;
	}
	else
	{
		return PARENT::CalculateOrientation(fTimeStep, xOrientation);
	}
}

void AVP_Move_Puppeteer::SeparateCharacters(AVP_Move_Puppet* pxPuppetMove)
{
	// See if the local player is involved in this, and set up FSFX if they are.
	const AVP_Player* pxLocalPlayer = AVP_Game_Core::GetPlayer();
	if (pxLocalPlayer)
	{
		const Asura_Guid uLocalPlayerGuid = pxLocalPlayer->GetPuppetGuid();
		const Asura_Guid uPuppeteerGuid = GetParentSystem().GetOwnerGuid();

		if (uLocalPlayerGuid == uPuppeteerGuid)
		{
			AVP_Game_Core::SetFSFXValue(m_uPuppeteerFSFX, 0.0f, dbg_fPuppeteerFSFXTime);
		}
		else if (uLocalPlayerGuid == m_uCurrentPuppet)
		{
			AVP_Game_Core::SetFSFXValue(m_uPuppetFSFX, 0.0f, dbg_fPuppeteerFSFXTime);
		}
	}

	m_bPuppetRelativeToOwner = false;
	pxPuppetMove->SetRelativeToPuppeteer(false);
	pxPuppetMove->SetCurrentPuppeteerGuid(ASURA_GUID_UNREGISTERED);
	m_uCurrentPuppet = ASURA_GUID_UNREGISTERED;
	// It'd probably be sensible to start colliding with things again now too.
	SetPositionUsage(POSITION_NOT_USED);
	DeactivateExternalControlPhysics();
	DisablePhysicsSettings();
}

AVP_Actor_PhysicsObjectHandler* AVP_Move_Puppeteer::GetOwnerPhysicsHandler()
{
	// AI controlled entities need to change the server physics object, while players need to change the client. For now, the following test is sufficient to detect this.
	AVP_ServerEntity_Actor* pxServer = AVP_ServerEntity_Actor::FindEntity(GetParentSystem().GetOwnerGuid());
	bool bAIControl = pxServer ? pxServer->GetActorBrain() != NULL : false;

	if (bAIControl)
	{
		return pxServer->GetPhysicsObjectHandler();
	}
	else
	{
		AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
		return xOwner.GetPhysicsObjectHandler();
	}
}

AVP_Actor_PhysicsObjectHandler* AVP_Move_Puppeteer::GetPuppetPhysicsHandler()
{
	// AI controlled entities need to change the server physics object, while players need to change the client. For now, the following test is sufficient to detect this.
	AVP_ServerEntity_Actor* pxServer = AVP_ServerEntity_Actor::FindEntity(m_uCurrentPuppet);
	bool bAIControl = pxServer ? pxServer->GetActorBrain() != NULL : false;

	if (bAIControl)
	{
		return pxServer->GetPhysicsObjectHandler();
	}
	else
	{
		AVP_ClientEntity_Actor* pxPuppet = AVP_ClientEntity_Actor::FindEntity(m_uCurrentPuppet);
		return pxPuppet ? pxPuppet->GetPhysicsObjectHandler() : NULL;
	}
}

void AVP_Move_Puppeteer::EnablePhysicsSettings()
{
	AVP_Actor_PhysicsObjectHandler* pxPhysicsHandler = GetOwnerPhysicsHandler();

	if (pxPhysicsHandler)
	{
		Asura_Physics_Object* pxPhysicsObj = pxPhysicsHandler->GetPhysicsObject();
		if (pxPhysicsObj)
		{
			pxPhysicsObj->SetCollisionGroups(uAVP_PUPPET_COLLISION_GROUP);
			pxPhysicsObj->SetCollisionGroupsToCollideWith(~uAVP_PUPPET_COLLISION_GROUP);
		}
	}

	if (m_uCurrentPuppet != ASURA_GUID_UNREGISTERED)
	{
		pxPhysicsHandler = GetPuppetPhysicsHandler();

		if (pxPhysicsHandler)
		{
			Asura_Physics_Object* pxPhysicsObj = pxPhysicsHandler->GetPhysicsObject();
			if (pxPhysicsObj)
			{
				pxPhysicsObj->SetCollisionGroups(uAVP_PUPPET_COLLISION_GROUP);
				pxPhysicsObj->SetCollisionGroupsToCollideWith(~uAVP_PUPPET_COLLISION_GROUP);
			}
		}
	}
}

void AVP_Move_Puppeteer::DisablePhysicsSettings()
{
	AVP_Actor_PhysicsObjectHandler* pxPhysicsHandler = GetOwnerPhysicsHandler();

	if (pxPhysicsHandler)
	{
		Asura_Physics_Object* pxPhysicsObj = pxPhysicsHandler->GetPhysicsObject();
		if (pxPhysicsObj)
		{
			pxPhysicsObj->SetCollisionGroups(0xFFFFFFFF);
			pxPhysicsObj->SetCollisionGroupsToCollideWith(0xFFFFFFFF);
			if (pxPhysicsObj->GetType() == ASURA_PHYSICS_OBJECT_ACTOR)
			{
				pxPhysicsObj->SetActorJumpVelocity(Asura_Vector_3(0.0f, m_fRelativeModeOwnerFallingSpeed, 0.0f));
			}
		}
	}

	if (m_uCurrentPuppet != ASURA_GUID_UNREGISTERED)
	{
		pxPhysicsHandler = GetPuppetPhysicsHandler();

		if (pxPhysicsHandler)
		{
			Asura_Physics_Object* pxPhysicsObj = pxPhysicsHandler->GetPhysicsObject();
			if (pxPhysicsObj)
			{
				pxPhysicsObj->SetCollisionGroups(0xFFFFFFFF);
				pxPhysicsObj->SetCollisionGroupsToCollideWith(0xFFFFFFFF);
				if (pxPhysicsObj->GetType() == ASURA_PHYSICS_OBJECT_ACTOR)
				{
					pxPhysicsObj->SetActorJumpVelocity(Asura_Vector_3(0.0f, m_fRelativeModeOwnerFallingSpeed, 0.0f));
				}
			}
		}
	}
}

void AVP_Move_Puppeteer::UpdatePuppetPosition(AVP_Move_Puppet& xPuppetMove, const float fTimeStep)
{
	if (m_bPuppetRelativeToOwner)
	{
		// We have a worldspace position, put it into puppeteer space.
		Asura_AVector3 xRelativePos = m_xRelativeModePuppetPosition;

		Asura_Quat xInverseOri(m_xRelativeModeOwnerOrientation);
		xInverseOri.Invert();

		xRelativePos -= m_xRelativeModeOwnerPosition;
		xRelativePos = xInverseOri.RotateVector(xRelativePos);

		xPuppetMove.SetPosition(Asura_Vector_3(xRelativePos), fTimeStep);
	}
	else
	{
		Asura_AVector3 xPositionOffset;
		Asura_Vector_3 xBasePuppetPosition(m_xInitialPuppetPosition);
		if (xPuppetMove.GetRequiresRestart() || !xPuppetMove.GetAVPAnimationData() || !xPuppetMove.GetAVPAnimationData()->CalculateMovementOffset(&xPositionOffset, NULL))
		{
			xPositionOffset.Zero();
		}

		Asura_Quat xBaseOrientation;
		GetBasePuppetOrientation(xBaseOrientation);

		xPositionOffset = xBaseOrientation.RotateVector(xPositionOffset);

		Asura_Vector_3 xFinalPosition = xBasePuppetPosition + Asura_Vector_3(xPositionOffset);

		xPuppetMove.SetPosition(xFinalPosition, fTimeStep);
	}
}

void AVP_Move_Puppeteer::UpdatePuppetOrientation(AVP_Move_Puppet& xPuppetMove, const float fTimeStep)
{
	if (m_bPuppetRelativeToOwner)
	{
		// We have a worldspace orientation, put it into puppeteer space.
		Asura_Quat xRelativeOri = m_xRelativeModePuppetOrientation;

		Asura_Quat xInverseOri(m_xRelativeModeOwnerOrientation);
		xInverseOri.Invert();

		xRelativeOri = xRelativeOri * xInverseOri;

		xPuppetMove.SetOrientation(xRelativeOri, fTimeStep);
	}
	else
	{
		Asura_Quat xOrientationOffset;
		Asura_Quat xBaseOrientation;
		GetBasePuppetOrientation(xBaseOrientation);
		if (xPuppetMove.GetRequiresRestart() || !xPuppetMove.GetAVPAnimationData() || !xPuppetMove.GetAVPAnimationData()->CalculateMovementOffset(NULL, &xOrientationOffset))
		{
			xOrientationOffset.Identity();
		}

		xPuppetMove.SetOrientation(xOrientationOffset * xBaseOrientation, fTimeStep);
	}
}

void AVP_Move_Puppeteer::GetBasePuppetOrientation(Asura_Quat& xOrientation) const
{
	if (m_bForceUprightPuppet)
	{
		Asura_Matrix_3x3 xOrientationMatrix;
		m_xInitialPuppetOrientation.ToMatrix(xOrientationMatrix);
		Asura_Maths::OrientationFromDirection(xOrientationMatrix, Asura_Vector_3(xOrientationMatrix.e13, 0.0f, xOrientationMatrix.e33));
		xOrientation.FromMatrix(xOrientationMatrix);
	}
	else
	{
		xOrientation = m_xInitialPuppetOrientation;
	}
}

DEBUGVAR float dbg_fUseGravityDistanceCheck = 0.05f;

void AVP_Move_Puppeteer::CalculateRelativeModePositions(const AVP_Move_Puppet& xPuppetMove, const float fTimeStep)
{
	if (fTimeStep <= 0.0f)
	{
		// Nothing to update if time isn't progressing.
		return;
	}

	Asura_Assert(m_bPuppetRelativeToOwner, "Shouldn't be calculating relative mode positions when our puppet isn't relative.");

	const AVP_Actor_PhysicsObjectHandler* pxOwnerPhysHandler = GetOwnerPhysicsHandler();
	const AVP_Actor_PhysicsObjectHandler* pxPuppetPhysHandler = GetPuppetPhysicsHandler();
	if (!pxOwnerPhysHandler || !pxPuppetPhysHandler) { return; }

	const Asura_Physics_Object* pxOwnerPhysObj = pxOwnerPhysHandler->GetPhysicsObject();
	const Asura_Physics_Object* pxPuppetPhysObj = pxPuppetPhysHandler->GetPhysicsObject();
	if (!pxOwnerPhysObj || !pxPuppetPhysObj) { return; }

	const float fOneOverTimeStep = 1.0f / fTimeStep;

	// First test - get a new position for the puppeteer and see if it can be reached.
	Asura_AVector3 xOwnerPositionOffset;
	Asura_Quat xOwnerOrientationOffset;
	if (!GetAVPAnimationData() || !GetAVPAnimationData()->CalculateMovementOffset(&xOwnerPositionOffset, &xOwnerOrientationOffset))
	{
		xOwnerPositionOffset.Zero();
		xOwnerOrientationOffset.Identity();
	}
	if (!IsGravityIgnored())
	{
		// First we need to work out if we should try to apply gravity. If either character is supported by something, don't bother.
		const Asura_AVector3 xDisplacement(0.0f, dbg_fUseGravityDistanceCheck, 0.0f);
		Asura_AMatrix3x3 xAMat(pxOwnerPhysObj->GetOrientation());
		Asura_OBB xOBB(pxOwnerPhysObj->GetCollisionObject()->GetBoundingBox(), xAMat);
		Asura_Physics_MovementQuery::ASURA_PMQ_COLLISION_PROXY eProxy = (pxOwnerPhysObj->GetCollisionProxy() == Asura_Physics_Object::COLLISION_PROXY_SPHERESTACK) ? Asura_Physics_MovementQuery::ASURA_PMQCP_SPHERESTACK : Asura_Physics_MovementQuery::ASURA_PMQCP_OBB;

		Asura_Physics_MovementQuery xQuery1(m_xRelativeModeOwnerPosition, xDisplacement, xOBB, eProxy, pxOwnerPhysObj->GetFlagsOfPolysToIgnore(), true, true, true, false, true, false, pxOwnerPhysObj->GetCollisionGroups(), pxOwnerPhysObj->GetCollisionGroupsToCollideWith());
		Asura_Physics_MovementQuery_Results xResults1(xQuery1);

		if (xResults1.GetAllowedDisplacement().ApproximatelyEquals(xDisplacement))
		{
			// Owner can fall. Check puppet
			xAMat.FromMat3(pxPuppetPhysObj->GetOrientation());
			xOBB = Asura_OBB(pxPuppetPhysObj->GetCollisionObject()->GetBoundingBox(), xAMat);

			eProxy = (pxPuppetPhysObj->GetCollisionProxy() == Asura_Physics_Object::COLLISION_PROXY_SPHERESTACK) ? Asura_Physics_MovementQuery::ASURA_PMQCP_SPHERESTACK : Asura_Physics_MovementQuery::ASURA_PMQCP_OBB;

			Asura_Physics_MovementQuery xQuery2(m_xRelativeModePuppetPosition, xDisplacement, xOBB, eProxy, pxPuppetPhysObj->GetFlagsOfPolysToIgnore(), true, true, true, false, true, false, pxPuppetPhysObj->GetCollisionGroups(), pxPuppetPhysObj->GetCollisionGroupsToCollideWith());
			Asura_Physics_MovementQuery_Results xResults2(xQuery2);

			if (xResults2.GetAllowedDisplacement().ApproximatelyEquals(xDisplacement))
			{
				// Puppet can fall too.
				m_fRelativeModeOwnerFallingSpeed += Asura_Physics_System::GetGravityScalar() * fTimeStep;
			}
			else
			{
				// Puppet can't fall.
				m_fRelativeModeOwnerFallingSpeed = 0.0f;
			}
		}
		else
		{
			// Owner can't fall.
			m_fRelativeModeOwnerFallingSpeed = 0.0f;
		}
	}
	Asura_AVector3 xTargetPosition = m_xRelativeModeOwnerBaseOrientation.RotateVector(xOwnerPositionOffset) + m_xRelativeModeOwnerBasePosition;
	Asura_Quat xTargetOrientation = m_xRelativeModeOwnerBaseOrientation * xOwnerOrientationOffset;

	Asura_AVector3 xDisplacement = xTargetPosition - m_xRelativeModeOwnerPosition;
	xDisplacement.SetY(xDisplacement.GetY() + (m_fRelativeModeOwnerFallingSpeed * fTimeStep));

	Asura_AMatrix3x3 xAMat;
	xTargetOrientation.ToMatrix(xAMat);
	Asura_OBB xOBB(pxOwnerPhysObj->GetCollisionObject()->GetBoundingBox(), xAMat);

	Asura_Physics_MovementQuery::ASURA_PMQ_COLLISION_PROXY eProxy = (pxOwnerPhysObj->GetCollisionProxy() == Asura_Physics_Object::COLLISION_PROXY_SPHERESTACK) ? Asura_Physics_MovementQuery::ASURA_PMQCP_SPHERESTACK : Asura_Physics_MovementQuery::ASURA_PMQCP_OBB;

	Asura_Physics_MovementQuery xQuery1(m_xRelativeModeOwnerPosition, xDisplacement, xOBB, eProxy, pxOwnerPhysObj->GetFlagsOfPolysToIgnore(), false, true, true, false, true, false, pxOwnerPhysObj->GetCollisionGroups(), pxOwnerPhysObj->GetCollisionGroupsToCollideWith());
	Asura_Physics_MovementQuery_Results xResults1(xQuery1);

	Asura_AVector3 xFinalPos = xResults1.GetEndPosition();

	// Update values - set the position to the query result, and update the base position so that if the movement offset was applied to it the result would be this final position.
	Asura_AVector3 xDifference = xFinalPos - xTargetPosition;
	m_xRelativeModeOwnerBasePosition += xDifference;
	m_xRelativeModeOwnerPosition = xFinalPos;
	m_xRelativeModeOwnerOrientation = xTargetOrientation;
	m_fRelativeModeOwnerFallingSpeed = Asura_Maths::Max(0.0f, xDifference.GetY() * fOneOverTimeStep);

	// Second test - get a new position for the puppet based on the above result and see if it can be reached.
	Asura_AVector3 xPuppetPositionOffset;
	Asura_Quat xPuppetOrientationOffset;
	if (xPuppetMove.GetRequiresRestart() || !xPuppetMove.GetAVPAnimationData() || !xPuppetMove.GetAVPAnimationData()->CalculateMovementOffset(&xPuppetPositionOffset, &xPuppetOrientationOffset))
	{
		xPuppetPositionOffset.Zero();
		xPuppetOrientationOffset.Identity();
	}

	Asura_AVector3 xBasePuppetPosition(m_xInitialPuppetPosition);
	Asura_Quat xBasePuppetOrientation;
	GetBasePuppetOrientation(xBasePuppetOrientation);

	xPuppetPositionOffset = xBasePuppetOrientation.RotateVector(xPuppetPositionOffset);

	xTargetPosition = xBasePuppetPosition + xPuppetPositionOffset;
	// As we're relative, we also need to take off our owner's movement to keep the offset correct.
	xTargetPosition -= xOwnerPositionOffset;

	Asura_Quat xInvertOwnerOrientationOffset(xOwnerOrientationOffset);
	xInvertOwnerOrientationOffset.Invert();
	xTargetPosition = xInvertOwnerOrientationOffset.RotateVector(xTargetPosition);

	xTargetOrientation = xInvertOwnerOrientationOffset * xBasePuppetOrientation;

	// Save this position off, as it needs re-adding to the puppeteer's position after the third test.
	Asura_AVector3 xRelativePosition = xTargetPosition;

	// Finally, we need this target position in worldspace.
	xTargetPosition = m_xRelativeModeOwnerOrientation.RotateVector(xTargetPosition);
	xTargetPosition += m_xRelativeModeOwnerPosition;

	xTargetOrientation = m_xRelativeModeOwnerOrientation * xTargetOrientation;

	xDisplacement = xTargetPosition - m_xRelativeModePuppetPosition;

	xTargetOrientation.ToMatrix(xAMat);
	xOBB = Asura_OBB(pxPuppetPhysObj->GetCollisionObject()->GetBoundingBox(), xAMat);

	eProxy = (pxPuppetPhysObj->GetCollisionProxy() == Asura_Physics_Object::COLLISION_PROXY_SPHERESTACK) ? Asura_Physics_MovementQuery::ASURA_PMQCP_SPHERESTACK : Asura_Physics_MovementQuery::ASURA_PMQCP_OBB;

	Asura_Physics_MovementQuery xQuery2(m_xRelativeModePuppetPosition, xDisplacement, xOBB, eProxy, pxPuppetPhysObj->GetFlagsOfPolysToIgnore(), false, true, true, false, true, false, pxPuppetPhysObj->GetCollisionGroups(), pxPuppetPhysObj->GetCollisionGroupsToCollideWith());
	Asura_Physics_MovementQuery_Results xResults2(xQuery2);

	xFinalPos = xResults2.GetEndPosition();

	// Update values - just set the orientation as the position will be set after the final test. However, we need to work out the movement for the final test.
	xDifference = xFinalPos - xTargetPosition;
	m_xRelativeModePuppetOrientation = xTargetOrientation;
	if (m_bForceUprightPuppet)
	{
		Asura_Matrix_3x3 xUprightMatrix;
		m_xRelativeModePuppetOrientation.ToMatrix(xUprightMatrix);
		Asura_Maths::OrientationFromDirection(xUprightMatrix, Asura_Vector_3(xUprightMatrix.e13, 0.0f, xUprightMatrix.e33));
		m_xRelativeModePuppetOrientation.FromMatrix(xUprightMatrix);
	}

	// Third test - use this difference we just got back on the owner again and adjust all positions accordingly
	m_xRelativeModeOwnerOrientation.ToMatrix(xAMat);
	xOBB = Asura_OBB(pxOwnerPhysObj->GetCollisionObject()->GetBoundingBox(), xAMat);

	eProxy = (pxOwnerPhysObj->GetCollisionProxy() == Asura_Physics_Object::COLLISION_PROXY_SPHERESTACK) ? Asura_Physics_MovementQuery::ASURA_PMQCP_SPHERESTACK : Asura_Physics_MovementQuery::ASURA_PMQCP_OBB;

	Asura_Physics_MovementQuery xQuery3(m_xRelativeModeOwnerPosition, xDifference, xOBB, eProxy, pxOwnerPhysObj->GetFlagsOfPolysToIgnore(), false, true, true, false, true, false, pxOwnerPhysObj->GetCollisionGroups(), pxOwnerPhysObj->GetCollisionGroupsToCollideWith());
	Asura_Physics_MovementQuery_Results xResults3(xQuery3);

	// Just use this final position, no matter what.
	xDifference = xResults3.GetEndPosition() - m_xRelativeModeOwnerPosition;
	m_fRelativeModeOwnerFallingSpeed = Asura_Maths::Max(0.0f, m_fRelativeModeOwnerFallingSpeed + (xDifference.GetY() * fOneOverTimeStep));
	m_xRelativeModeOwnerBasePosition += xDifference;
	m_xRelativeModeOwnerPosition = xResults3.GetEndPosition();
	m_xRelativeModePuppetPosition = m_xRelativeModeOwnerOrientation.RotateVector(xRelativePosition) + m_xRelativeModeOwnerPosition;
}

void AVP_Move_Puppeteer::SetPuppetRelativeToOwner(bool bRelative)
{
	//Done as two comparisons to avoid a signed-unsigned mismatch warning
	if (bRelative && m_bPuppetRelativeToOwner) { return; }
	if (!bRelative && !m_bPuppetRelativeToOwner) { return; }

	m_bPuppetRelativeToOwner = bRelative;

	const AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();

	if (m_bPuppetRelativeToOwner)
	{
		// If we're becoming relative, convert the worldspace data to puppeteer-space
		Asura_Quat xInverseOri(xOwner.GetCurrentOrientation());
		xInverseOri.Invert();

		m_xInitialPuppetPosition -= xOwner.GetCurrentPosition();
		m_xInitialPuppetPosition = xInverseOri.RotateVector(m_xInitialPuppetPosition);

		m_xInitialPuppetOrientation = m_xInitialPuppetOrientation * xInverseOri;

		m_xRelativeModeOwnerPosition = Asura_AVector3(xOwner.GetCurrentPosition());
		m_xRelativeModeOwnerOrientation.FromMatrix(xOwner.GetCurrentOrientation());
		m_fRelativeModeOwnerFallingSpeed = 0.0f;

		const AVP_ClientEntity_Actor* pxPuppet = AVP_ClientEntity_Actor::FindEntity(GetCurrentPuppet());
		if (pxPuppet)
		{
			m_xRelativeModePuppetPosition = Asura_AVector3(pxPuppet->GetCurrentPosition());
			m_xRelativeModePuppetOrientation.FromMatrix(pxPuppet->GetCurrentOrientation());
		}
	}
	else
	{
		// ...and the opposite if we're not.
		Asura_Quat xOri(xOwner.GetCurrentOrientation());

		m_xInitialPuppetPosition = xOri.RotateVector(m_xInitialPuppetPosition);
		m_xInitialPuppetPosition += xOwner.GetCurrentPosition();

		m_xInitialPuppetOrientation = m_xInitialPuppetOrientation * xOri;
	}
}

void AVP_Move_Puppeteer::SetPuppetAnimationData(Asura_Hash_ID uFirstPersonHash, Asura_Hash_ID uThirdPersonHash, float fTweenInTime, float fTweenOutTime, bool bAnimatedTween, bool bLeaveAnimAtEnd) const
{
	AVP_ClientActor_Vertebrate* pxVictim = AVP_ClientActor_Vertebrate::FindEntity(m_uCurrentPuppet);
	if (!pxVictim) { return; }

	Asura_Movement_System* pxMovementSystem = pxVictim->GetMovementSystem();
	if (!pxMovementSystem) { return; }

	AVP_Move_Puppet* pxPuppetMove = static_cast<AVP_Move_Puppet*>(pxMovementSystem->GetMove(AVP_Move_Puppet::GetStaticHash()));
	if (!pxPuppetMove) { return; }

	pxPuppetMove->SetAnimHashes(uFirstPersonHash, uThirdPersonHash);

	Asura_MoveData_Animation* pxAnimData = pxPuppetMove->GetAVPAnimationData();
	if (pxAnimData)
	{
		pxAnimData->SetTweenData(fTweenInTime, fTweenOutTime, bAnimatedTween);
		pxAnimData->SetLeaveAnimAtEnd(bLeaveAnimAtEnd);
	}
}

void AVP_Move_Puppeteer::UpdateGrappleEvents()
{
	if (m_pxAVPAnimationData)
	{
		AVP_ClientEntity_Actor& xOwner = GetAVPParentSystem().GetActorOwner();
		AVP_ClientEntity_Actor* pxEnemy = AVP_ClientEntity_Actor::FindEntity(m_uCurrentPuppet);
		if (xOwner.IsUsingFirstPersonSkin())
		{
			AVP_Move::UpdateGrappleEvents(m_axFirstPersonGrappleEvents, m_iNumFirstPersonGrappleEvents, xOwner, pxEnemy, m_pxAVPAnimationData->GetAnimationController());
		}
		else
		{
			AVP_Move::UpdateGrappleEvents(m_axThirdPersonGrappleEvents, m_iNumThirdPersonGrappleEvents, xOwner, pxEnemy, m_pxAVPAnimationData->GetAnimationController());
		}
	}
}


///////////////////////////////////////////////////////////////////////
// AVP_Move_Flinch
///////////////////////////////////////////////////////////////////////

DEBUGVAR float dbg_fFlinchTweenInDuration = 0.1f;
DEBUGVAR float dbg_fFlinchTweenOutDuration = 0.2f;
DEBUGVAR float dbg_fFlinchOrientationInterpolationTime = 0.3f;

AVP_Move_Flinch::AVP_Move_Flinch(Asura_Movement_System& xParent)
: PARENT(xParent, GetStaticHash())
{
	// Since animations are set up from the message, setting up additional anim data will have to wait until Start is called for the first time.
	m_bClearQueuedMeleeRequests = true;
}                                                                                                                                                                                                                                                                                                                                                      

bool AVP_Move_Flinch::Start(const Asura_Msg_Move* pxStartMsg)
{
	// This move can only be started by message.
	if (!pxStartMsg) { return false; }

	// We need our own special message.
	const AVP_Msg_Flinch_Move_Start* pxFlinchMsg = static_cast<const AVP_Msg_Flinch_Move_Start*>(pxStartMsg);

	SetAnimHashes(pxFlinchMsg->GetFirstPersonAnim(), pxFlinchMsg->GetThirdPersonAnim());

	// Make sure the anim data was successfully set up.
	if (!m_pxAVPAnimationData) { return false; }

	m_pxAVPAnimationData->SetTweenData(dbg_fFlinchTweenInDuration, dbg_fFlinchTweenOutDuration, true);

	u_int uPriority = GetAVPParentSystem().GetActorOwner().IsUsingFirstPersonSkin() ? AVP_AAPC_FLINCH_ANIM_HUD : AVP_AAPC_FLINCH_ANIM_TP;
	m_pxAVPAnimationData->SetAnimPriority(uPriority);

	m_pxAVPAnimationData->SetLeaveAnimAtEnd(true);

	m_bDisableSurfaceReorientation = pxFlinchMsg->GetDisableSurfaceReorientation();

	Asura_Matrix_3x3 xTargetOrientation;
	pxFlinchMsg->GetTargetOrientation(xTargetOrientation);
	m_xOrientationInterpolationData.SetMovementOffsetOperation(Asura_MoveData_OrientationInterpolation::ADD_MOVEMENT_OFFSET);
	m_xOrientationInterpolationData.SetDuration(dbg_fFlinchOrientationInterpolationTime);
	m_xOrientationInterpolationData.SetFinal(xTargetOrientation);
	m_xOrientationInterpolationData.SetSingularityHintAxis(Asura_AVector3(0.0f,0.0f,-1.0f));
	InitialiseOrientationInterpolation(m_xOrientationInterpolationData);

	SetAimDirectionUsage(pxFlinchMsg->GetResetAimDirection() ? AVP_ClientEntity_Actor::AIM_DIRECTION_RESET : AVP_ClientEntity_Actor::AIM_DIRECTION_LIMIT);
	SetLookAtPositionUsage(pxFlinchMsg->GetResetAimDirection() ? AVP_ClientEntity_Actor::LOOK_AT_POSITION_RESET : AVP_ClientEntity_Actor::LOOK_AT_POSITION_LIMIT);

	SetPositionUsage(POSITION_SET_FROM_VELOCITY);

	return PARENT::Start(pxStartMsg);
}

void AVP_Move_Flinch::Abort()
{
	TweenOutAnimation();

	PARENT::Abort();
}


///////////////////////////////////////////////////////////////////////
// AVP_Move_Landing
///////////////////////////////////////////////////////////////////////

AVP_Move_Landing::AVP_Move_Landing(Asura_Movement_System& xParent)
: PARENT(xParent, GetStaticHash())
, m_fTimeInMove(0.0f)
{
	// We need to get landing anim data from our owner each time, so there's no point in setting up any data here.
	m_bClearQueuedMeleeRequests = true;
	m_bSupportEndAnimFlag = true;
}

DEBUGVAR float dbg_fLandingTweenInDuration = 0.1f;
DEBUGVAR float dbg_fLandingTweenOutDuration = 0.2f;

bool AVP_Move_Landing::Start(const Asura_Msg_Move* pxStartMsg)
{
	// This can only be started by message.
	if (!pxStartMsg) { return false; }

	// We have our own special message, since in the initial version this allows client entities to decide whether to start or not based on whether they have animations.
	// In the future, this may be triggered from the server instead. but if that's the case this setup still works here.
	const AVP_Msg_Move_Start_AnimationData* pxAnimStartMsg = static_cast<const AVP_Msg_Move_Start_AnimationData*>(pxStartMsg);

	SetAnimHashes(pxAnimStartMsg->GetFirstPersonAnim(), pxAnimStartMsg->GetThirdPersonAnim());

	// Make sure the anim data was successfully set up.
	if (!m_pxAVPAnimationData) { return false; }

	m_pxAVPAnimationData->SetTweenData(dbg_fLandingTweenInDuration, dbg_fLandingTweenOutDuration, true);
	m_pxAVPAnimationData->SetLeaveAnimAtEnd(true);

	SetAimDirectionUsage(AVP_ClientEntity_Actor::AIM_DIRECTION_NOT_USED);

	m_fTimeInMove = 0.0f;

	return PARENT::Start(pxStartMsg);
}

Asura_Move* AVP_Move_Landing::Update()
{
	const u_int uAnimFlags = GetAnimFlagsForLastFrame();
	SetAimDirectionUsage(((uAnimFlags & AVP_ANIMFLAG_RESET_LOOK) != 0) ? AVP_ClientEntity_Actor::AIM_DIRECTION_RESET : AVP_ClientEntity_Actor::AIM_DIRECTION_NOT_USED);

	m_fTimeInMove += Asura_Timers::GetGameTimeSlice();

	return PARENT::Update();
}

AVP_Move_GS2RigidAnim::AVP_Move_GS2RigidAnim( Asura_Movement_System& xParent )
: PARENT(xParent, GetStaticHash())
, m_bToldAnimHandlerAboutNewControllerHash( false )
{

}

bool AVP_Move_GS2RigidAnim::Start( const Asura_Msg_Move* pxStartMsg )
{
	if ( !pxStartMsg )
	{
		return false;
	}

	// We have our own special message, since in the initial version this allows client entities to decide whether to start or not based on whether they have animations.
	// In the future, this may be triggered from the server instead. but if that's the case this setup still works here.
	const AVP_Msg_Move_Start_GS2RigidAnim* pxGS2StartMsg = static_cast< const AVP_Msg_Move_Start_GS2RigidAnim* >( pxStartMsg );

	SetAnimHashes( ASURA_HASH_ID_UNSET, pxGS2StartMsg->GetAnimHash() );

	// Make sure the anim data was successfully set up.
	if ( !m_pxAVPAnimationData )
	{
		return false;
	}

	m_pxAVPAnimationData->SetAnimHash( m_uThirdPersonAnimHash );
	m_pxAVPAnimationData->SetAnimPriority( AVP_AAPC_GAMESCENE_ANIM );
	m_pxAVPAnimationData->SetTweenData( pxGS2StartMsg->GetTweenDuration(), 0.0f, true );
	m_pxAVPAnimationData->SetAnimFlags( pxGS2StartMsg->GetAnimFlags() );
	m_pxAVPAnimationData->SetLeaveAnimAtEnd( true );
	m_pxAVPAnimationData->SetParametricTimes( pxGS2StartMsg->GetParametricStartTime(), 1.0f );

	float fDuration = 0.2f;
	if ( m_pxAVPAnimationData->GetHierarchyAnim() )
	{
		fDuration = Asura_Maths::Min( m_pxAVPAnimationData->GetHierarchyAnim()->GetDuration(), fDuration );
	}

	m_xCompassOrientationInterpolation.SetMovementOffsetOperation( Asura_MoveData_OrientationInterpolation::ADD_MOVEMENT_OFFSET );
	m_xCompassPositionInterpolation.SetMovementOffsetOperation( Asura_MoveData_PositionInterpolation::ADD_MOVEMENT_OFFSET );

	m_xCompassOrientationInterpolation.SetDuration( fDuration );
	m_xCompassOrientationInterpolation.SetFinal( pxGS2StartMsg->GetStartOrientation() );
	m_xCompassPositionInterpolation.SetDuration( fDuration );
	m_xCompassPositionInterpolation.SetFinal( pxGS2StartMsg->GetStartPosition() );

	InitialiseOrientationInterpolation( m_xCompassOrientationInterpolation );
	InitialisePositionInterpolation( m_xCompassPositionInterpolation );

	m_bToldAnimHandlerAboutNewControllerHash = false;

	return PARENT::Start( pxStartMsg );
}

Asura_Move* AVP_Move_GS2RigidAnim::Update()
{
	Asura_Move* pxRet = PARENT::Update();

	if ( !m_bToldAnimHandlerAboutNewControllerHash )
	{
		AVP_ClientEntity_Actor* pxActor = AVP_ClientEntity_Actor::FindEntity( GetParentSystem().GetOwnerGuid() );
		if ( m_pxAVPAnimationData && pxActor && pxActor->GetAnimationHandler() && m_pxAVPAnimationData->GetAnimationControllerHash() != ASURA_HASH_ID_UNSET )
		{
			pxActor->GetAnimationHandler()->SetCurrentGamesceneAnimControllerHash( m_pxAVPAnimationData->GetAnimationControllerHash() );
			m_bToldAnimHandlerAboutNewControllerHash = true;
		}
	}

	return pxRet;
}

/// End of file. ///
