//////////////////////////////////////////////////////////////////////////////
//
// AVP_Moves.h
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef AVP_MOVES_H___
#define AVP_MOVES_H___ 1

// E N D   F I L E   W R A P P E R ///////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

// Engine independent.
#include "Entity/PhysicalObject/Moves/Asura_Moves.h"

// Project independent.
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Moves/AVP_MoveData_Animation.h"
#include "Moves/AVP_Movement_System.h"

// C L A S S E S /////////////////////////////////////////////////////////////
class Asura_AnimationController_Animation;
class Asura_Blueprint;
class AVP_MoveData_Animation;

// Enumeration for grapple events, since it's the simplest way to set up the more generic blueprints for them.
// Please only add new entries to the end as the values will be saved in blueprints.
enum AVP_GRAPPLE_EVENT_TYPE
{
	AVP_GET_CAMERAPHOTON,
	AVP_GET_DISMEMBERENEMY,
	AVP_GET_HEADBITEENEMY,
	AVP_GET_PFX,

	NUM_AVP_GRAPPLE_EVENT_TYPES,
};

// Similar to the old FSFX and PFX details, it's more sensible to store the grapple events' details at this level so we don't have to copy code across multiple moves.
// This struct stores the details for each.
struct AVP_GrappleEvent_Entry
{
	Asura_Hash_ID m_uEventDataBlueprintID;
	int m_iStartFrame;
	int m_iStartAnimFlag;
	int m_iEndFrame;
	int m_iEndAnimFlag;
	u_int m_uTypeData; // Once the entry starts running it may need to store a little extra data, maybe a hash ID or an index of some kind. If more is needed this should become a void* for full type-specific data.
	bool m_bRunning; // Set to true on passing the start point, unless it ends immediately (start frame == end frame OR start anim flag == end anim flag, which ever is the valid pair for this event).
	bool m_bFinished; // Avoid retriggering.
};

class AVP_Move : public Asura_Move
{
public:
	enum MovementDirectionUsage
	{
		MOVEMENTDIRECTION_SET_FROM_VELOCITY,
		MOVEMENTDIRECTION_SET_FROM_ORIENTATION_FORWARDS_VECTOR,
		MOVEMENTDIRECTION_NOT_USED, // this isn't the default as SET_FROM_VELOCITY was what happened before this went in.

		NUM_MOVEMENTDIRECTION_USAGES // if this changes, make sure that the member m_eMovementDirectionUsage in the union below can still hold it.
	};

	AVP_Move( Asura_Movement_System& xParent, const Asura_Hash_ID uHash );
	virtual ~AVP_Move();

    AVP_Movement_System& GetAVPParentSystem( void ) { return static_cast<AVP_Movement_System&>(GetParentSystem()); }
    const AVP_Movement_System& GetAVPParentSystem( void ) const { return static_cast<const AVP_Movement_System&>(GetParentSystem()); }

	AVP_ClientEntity_Actor::AimDirectionUsage GetAimDirectionUsage() const { return static_cast< AVP_ClientEntity_Actor::AimDirectionUsage >( m_eAimDirectionUsage ); }
	const Asura_AVector3& GetAimDirection() const { return m_xAimDirection; }
	float GetAimDirectionLimitAngle() const { return m_fAimDirectionLimitAngle; }
	float GetAimDirectionAngVel() const { return m_fAimDirectionAngVel; }

	void SetAimDirectionUsage( AVP_ClientEntity_Actor::AimDirectionUsage eSet ){ m_eAimDirectionUsage = eSet; }
	void SetAimDirection( const Asura_AVector3& xDirection ){ m_xAimDirection = xDirection; }
	void SetAimDirectionLimitAngle( float fAngle ){ m_fAimDirectionLimitAngle = fAngle; }
	void SetAimDirectionAngVel( float fTimeScale ){ m_fAimDirectionAngVel = fTimeScale; }

	AVP_ClientEntity_Actor::LookAtPositionUsage GetLookAtPositionUsage() const { return static_cast< AVP_ClientEntity_Actor::LookAtPositionUsage >( m_eLookAtPositionUsage ); }
	const Asura_AVector3& GetLookAtPosition() const { return m_xLookAtPosition; }
	float GetLookDirectionLimitAngle() const { return m_fLookDirectionLimitAngle; }
	float GetLookDirectionLerpTimeScale() const { return m_fLookDirectionLerpTimeScale; }

	void SetLookAtPositionUsage( AVP_ClientEntity_Actor::LookAtPositionUsage eSet ){ m_eLookAtPositionUsage = eSet; }
	void SetLookAtPosition( const Asura_AVector3& xPos ){ m_xLookAtPosition = xPos; }
	void SetLookDirectionLimitAngle( float fAngle ){ m_fLookDirectionLimitAngle = fAngle; }
	void SetLookDirectionLerpTimeScale( float fTimeScale ){ m_fLookDirectionLerpTimeScale = fTimeScale; }

	MovementDirectionUsage GetMovementDirectionUsage() const { return static_cast< MovementDirectionUsage >( m_eMovementDirectionUsage ); }
	void SetMovementDirectionUsage( MovementDirectionUsage eMovementDirectionUsage ) { m_eMovementDirectionUsage = eMovementDirectionUsage; }

    bool IsDisableMovement(void) const	{ return m_bDisableMovement; }
	bool IsRestrictedMovement() const { return m_bRestrictedMovement; }
	bool GetShouldClearMeleeRequestQueue() const;
	bool GetShouldClearCounterRequest() const;
	bool GetAllowMeleeTargetUpdate() const { return m_bAllowMeleeTargetUpdate; }

	bool GetAllowsBlock() const { return m_bAllowsBlock; }
	bool GetPreventsFlinch() const { return m_bPreventsFlinch; }

	Asura_Hash_ID GetThirdPersonAnimHash() const { return m_uThirdPersonAnimHash; }

	// Death handling.
	bool GetAlwaysCreateRagdollOnDeath() const { return m_bAlwaysCreateRagdollOnDeath; }
	bool GetControlDeathInternally() const { return m_bControlDeathInternally; }

	virtual Asura_Hash_ID GetDeathAnim() const { return ASURA_HASH_ID_UNSET; }

	void SetDisableMovement(bool bDisableMovement)						{ m_bDisableMovement = bDisableMovement; }
	void SetUseSingularityAxis(bool bUseSingularityAxis)				{ m_bUseSingularityAxis = bUseSingularityAxis; }
	void SetClearQueuedMeleeRequests(bool bClearQueuedMeleeRequests)	{ m_bClearQueuedMeleeRequests = bClearQueuedMeleeRequests; }
	void SetClearQueuedCounterRequest(bool bClearQueuedCounterRequest)	{ m_bClearQueuedCounterRequest = bClearQueuedCounterRequest; }
	void SetPreventsFlinch(bool bPreventsFlinch)						{ m_bPreventsFlinch = bPreventsFlinch; }
	void SetAlwaysCreateRagdollOnDeath(bool bAlwaysCreate)				{ m_bAlwaysCreateRagdollOnDeath = bAlwaysCreate; }
	void SetControlDeathInternally(bool bControlInternally)				{ m_bControlDeathInternally = bControlInternally; }
	void SetDisableSurfaceReorientation(bool bDisable)					{ m_bDisableSurfaceReorientation = bDisable; }
	void SetForceStanding(bool bForce)									{ m_bForceStanding = bForce; }
	void SetForceCrouching(bool bForce)									{ m_bForceCrouching = bForce; }

	virtual bool HandleMessage(const Asura_Message* pxMessage);

	// Virtual update function to be called from other moves, designed for the puppet move functionality. Included at this level for safety, since not all moves are a puppet or puppeteer move, but all are AVP moves.
	virtual void ExternalUpdate(const float fTimeStep) {}

	AVP_MoveData_Animation* GetAVPAnimationData() { return m_pxAVPAnimationData; }
	const AVP_MoveData_Animation* GetAVPAnimationData() const { return m_pxAVPAnimationData; }

	virtual Asura_Guid GetTargetGuid() const { return ASURA_GUID_UNREGISTERED; }

	virtual u_int GetNumVariations() const { return 0; }

	virtual bool HasFinished() const;

	bool GetCanFinishEarly() const { return m_bCanFinishEarly; }

	bool GetPreventsFocus() const { return m_bPreventsFocus; }

	bool GetHideReticle() const { return m_bHideReticle; }

	virtual Asura_Guid GetForcedMeleeTarget() const { return ASURA_GUID_UNREGISTERED; }

	virtual bool HasPuppet() const { return false; } // by default moves dont use puppets

	void EnsureAnimationFinishes(bool bStartTweenImmediately = false);

protected:
	virtual bool ShouldStart() const;
	virtual bool Start(const Asura_Msg_Move* pxStartMsg);
	virtual Asura_Move* Update();
	virtual void UpdateStates(const float fTimeStep);
    virtual void Finish();
	virtual void Abort();

	virtual void SendStartMsg();

	virtual void FinishOrientationInterpolation();

	void DisableSurfaceReorientation();
	void EnableSurfaceReorientation();
	void ResetPhysicsObjectHandler();


	enum AIPostMoveMode 
	{
		AI_MM_NONE,
		AI_MM_IDLE,
		AI_MM_SLOW,
		AI_MM_MDEIUM,
		AI_MM_FAST
	};

	void SetAIPostMoveMode(AIPostMoveMode eMode ) { m_eAIPostMoveMode = eMode; }

	// If this move will only be used by an AI, set the first person parameter as ASURA_HASH_ID_UNSET.
	// For now we'll also allow the third person parameter to be ASURA_HASH_ID_UNSET, but that's only valid if there's a move that can only be used by the player
	// in the single player campaign (if a move works in multiplayer, remote machines will need the third person anim).
	// For now, this function will construct m_pxAnimationData if it doesn't exist. A later version may leave construction until Start is called (and destruct in
	// Finish/Abort), but that would require more changes as a lot of moves set up constant animation data in their constructors.
	// Note that since I'm expecting this to be called from constructors, it cannot be virtual.
	void SetAnimHashes(Asura_Hash_ID uFirstPersonAnimHash, Asura_Hash_ID uThirdPersonAnimHash);
	void SetAnimBlendTemplateBlueprint( const Asura_Blueprint& xBP );

	void ActivateExternalControlPhysics();
	void DeactivateExternalControlPhysics();

	virtual bool	UpdateAnimation(const float fTimeStep);

	void TweenOutAnimation();

	// Grapple events are shared by puppets and puppeteers, so to share code these static functions can go here.
	static void UpdateGrappleEvents(AVP_GrappleEvent_Entry* pxGrappleEvents, int iNumGrappleEvents, AVP_ClientEntity_Actor& xOwner, AVP_ClientEntity_Actor* pxEnemy, Asura_AnimationController_Animation* pxCurrentAnimController);
	static void StartGrappleEvent(AVP_GrappleEvent_Entry& xEvent, AVP_ClientEntity_Actor& xOwner, AVP_ClientEntity_Actor* pxEnemy, Asura_AnimationController_Animation* pxCurrentAnimController);
	static void EndGrappleEvent(AVP_GrappleEvent_Entry& xEvent, AVP_ClientEntity_Actor& xOwner, AVP_ClientEntity_Actor* pxEnemy, Asura_AnimationController_Animation* pxCurrentAnimController);

	AVP_MoveData_Animation* m_pxAVPAnimationData; // Only initialised if the animation hash ids are set up.
	Asura_Hash_ID m_uFirstPersonAnimHash;
	Asura_Hash_ID m_uThirdPersonAnimHash;

	// flags
    union
    {
        u_int		m_uAVPMoveFlags;

        struct
        {
            u_int   m_bDisableMovement              : 1,	// This flag will disable player movement input
					m_bUseSingularityAxis			: 1,	// If set, and we have both animation data and orientation data to use, the InitialiseAnimation call will calculate the hint axis and set it in the orientation data.
					m_bClearQueuedMeleeRequests		: 1,
					m_bClearQueuedCounterRequest	: 1,
					m_bUseServerStartMessage		: 1,	// If set, we send a special start message from the server (even if this is a remote player-controlled system) on a ShouldStart succeeding, that will start a move on the client only if the same one isn't already playing.
					m_eAimDirectionUsage			: 3,	// if enum AVP_ClientEntity_Actor::AimDirectionUsage changed make sure to update this 
					m_bAllowsBlock					: 1,	// Most moves will force you out of blocking, so set this flag if blocking can continue through the move.
					m_bPreventsFlinch				: 1,	// Some moves will want to push through attacks that normally cause flinching. Flag those moves with this bool.
					m_bRestrictedMovement			: 1,	// Some moves don't force you to stop, but limit how fast you can move.
					m_bAllowMeleeTargetUpdate		: 1,
					m_bAlwaysCreateRagdollOnDeath	: 1,
					m_bControlDeathInternally		: 1,
				    m_eLookAtPositionUsage			: 3,
					m_bSupportEndAnimFlag			: 1,	// If true, the "end_anim" anim flag is checked, and the move can then be exited early once that flag is passed.
					m_bCanFinishEarly				: 1,	// Additional flag to handle the states of m_bSupportEndAnimFlag.
					m_bFinishEarly					: 1,	// Additional flag to handle the states of m_bSupportEndAnimFlag.
					m_bPreventsFocus				: 1,	// If true, a player's focus will deactivate (if on) and be prevented from activating.
					m_bDisableSurfaceReorientation	: 1,
					m_bForceStanding				: 1,	// These two are mutually exclusive, and irrelevant to everything but humans and aliens.
					m_bForceCrouching				: 1,
					m_bUsingBlendTemplate			: 1,
					m_eMovementDirectionUsage		: 3,
					m_bHideReticle					: 1,
													: 0;
        };
    };

	// this is used when m_eAimDirectionUsage is AVP_ClientEntity_Actor::AIM_DIRECTION_SET_DIRECTLY and look stuff ditto
	Asura_AVector3 m_xAimDirection;
	Asura_AVector3 m_xLookAtPosition;

	// this is for AVP_ClientEntity_Actor::AIM_DIRECTION_LIMIT, LOOK_DIRECTION_LIMIT
	float	m_fAimDirectionLimitAngle;
	float	m_fAimDirectionAngVel;

	float	m_fLookDirectionLimitAngle;
	float	m_fLookDirectionLerpTimeScale;

	AIPostMoveMode m_eAIPostMoveMode;

private:
    typedef Asura_Move PARENT;

	friend class AVP_Movement_System;
};

static const int iAVP_MAX_GRAPPLE_EVENTS = 8;

///////////////////////////////////////////////////////////////////////
// AVP_Move_Puppet
// Special one-off move that is controlled by a move in another entity's movement system.
// It's expected the controlling move will be a subclass of AVP_Move_Puppeteer, but this 
// move won't make any assumptions of that (at least, not initially).
///////////////////////////////////////////////////////////////////////

class AVP_Move_Puppet : public AVP_Move
{
	typedef AVP_Move PARENT;
	friend class AVP_Move_Puppeteer;

public:
	AVP_Move_Puppet(Asura_Movement_System& xParent);
	virtual ~AVP_Move_Puppet();

	virtual bool HasFinished() const;

	virtual void ExternalUpdate(const float fTimeStep);
	virtual bool CanBeAborted(void) const { return false; }


	void SetPosition(const Asura_Vector_3& xPosition, const float fTimeStep);
	void SetOrientation(const Asura_Quat& xOrientation, const float fTimeStep);

	// This function will automatically convert the position data if the flag changes. The puppeteer can obviously immediately replace anything this calculates.
	void SetRelativeToPuppeteer(bool bRelative);
	void SetFinished(bool bFinished) { m_bFinishedByPuppeteer = bFinished; }
	void SetRestartMove(bool bRestart) { m_bRequiresRestart = bRestart; }

	void SetCurrentPuppeteerGuid(Asura_Guid uPuppeteer); // If set to unregistered, we'll run the normal move update and HasFinished code, instead of waiting for the puppeteer to tell us what to do.
	Asura_Guid GetCurrentPuppeteerGuid() const { return m_uCurrentPuppeteer; }

	bool GetRequiresRestart() const { return m_bRequiresRestart; }

	virtual Asura_Guid GetForcedMeleeTarget() const { return m_uCurrentPuppeteer; }

	static Asura_Hash_ID GetStaticHash() { return 0x686B8AFE; } //Asura_GetHashID("AVP_Move_Puppet");

	void SetActiveGrappleVictimBlueprint(Asura_Hash_ID uActiveGrappleVictimBlueprint)	{ m_uActiveGrappleVictimBlueprint = uActiveGrappleVictimBlueprint; }
	void SetGrappleSoundsVictimBlueprint(Asura_Hash_ID uGrappleSoundsVictimBlueprint)	{ m_uGrappleSoundsVictimBlueprint = uGrappleSoundsVictimBlueprint; }
	void SetActiveGrappleBlueprintType(Asura_Hash_ID uActiveGrappleBlueprintType)		{ m_uActiveGrappleBlueprintType = uActiveGrappleBlueprintType; }

	void GetRelativePositionData(Asura_Vector_3& xPosition, Asura_Quat& xOrientation) const;

	AVP_GrappleEvent_Entry* GetFirstPersonGrappleEventArray() { return m_axFirstPersonGrappleEvents; }
	int& GetNumFirstPersonGrappleEventsReference() { return m_iNumFirstPersonGrappleEvents; }
	AVP_GrappleEvent_Entry* GetThirdPersonGrappleEventArray() { return m_axThirdPersonGrappleEvents; }
	int& GetNumThirdPersonGrappleEventsReference() { return m_iNumThirdPersonGrappleEvents; }
	void ResetGrappleEvents();

protected:
	virtual bool Start(const Asura_Msg_Move* pxStartMsg);
	virtual Asura_Move* Update();
	virtual void Finish();
	virtual void Abort();

	virtual void CheckStatesToStart();
	virtual void UpdateStates(const float fTimeStep);

	virtual bool CalculatePosition(const float fTimeStep, Asura_AVector3_OutParam xPosition) const;
	virtual bool CalculateOrientation(const float fTimeStep, Asura_Quat& xOrientation) const;

	bool PuppeteerUpdateAnimation(const float fTimeStep);

	void UpdateGrappleEvents();

	Asura_Vector_3 m_xPosition; // The flag below dictates whether this is in worldspace or relative to the puppeteer.
	Asura_Quat m_xOrientation; // As above.

	Asura_Guid m_uCurrentPuppeteer;

	// Framestamps to handle the difficulties of updating both this and the puppeteer move within the update of whichever gets updated first.
	u_int m_uLastUpdateFrameStamp; // The last time Update() was called.
	u_int m_uLastStatesUpdateFrameStamp; // The last time UpdateStates() was called (can be different, as it's that function which gets called from the puppeteer move if it happens to update first this frame).

	Asura_Hash_ID m_uActiveGrappleVictimBlueprint;
	Asura_Hash_ID m_uGrappleSoundsVictimBlueprint;

	// Temporary (hopefully) variable while we support both the old and new style of grapple sequence and blueprints.
	Asura_Hash_ID m_uActiveGrappleBlueprintType;

	AVP_GrappleEvent_Entry m_axFirstPersonGrappleEvents[iAVP_MAX_GRAPPLE_EVENTS];
	int m_iNumFirstPersonGrappleEvents;
	AVP_GrappleEvent_Entry m_axThirdPersonGrappleEvents[iAVP_MAX_GRAPPLE_EVENTS];
	int m_iNumThirdPersonGrappleEvents;

	union
	{
		u_int m_uPuppetFlags;
		struct
		{
			u_int	m_bRelativeToPuppeteer		: 1,
					m_bFinishedByPuppeteer		: 1, // Flagged by the puppeteer to force us to finish this frame
					m_bRequiresRestart			: 1, // Used to force the move to effectively start again, e.g. if we want to tween to a new animation.
					m_bCanFinish				: 1, // Not really a descriptive enough name, but anything better would be about four times as long. This gets set when we enter UpdateStates after Update has been entered, allowing the correct detection of WasFinished and HasFinished in Asura_Move::Update.
					m_bAnimUpdatedByPuppeteer	: 1,
												: 0;
		};
	};
};

///////////////////////////////////////////////////////////////////////
// AVP_Move_Puppeteer
// The expected move that anything set up to work with AVP_Move_Puppet will
// be a subclass of. Has some extra functionality to interface and stay in
// sync with a AVP_Move_Puppet more easily.
// Unlike AVP_Move_Puppet, this move is never intended to be used without
// creating a subclass of it.
///////////////////////////////////////////////////////////////////////

class AVP_Move_Puppeteer : public AVP_Move
{
	typedef AVP_Move PARENT;

public:
	AVP_Move_Puppeteer(Asura_Movement_System& xParent, const Asura_Hash_ID uHash);
	virtual ~AVP_Move_Puppeteer();

	virtual bool HasFinished() const;

	virtual void ExternalUpdate(const float fTimeStep);

	Asura_Guid GetCurrentPuppet() const { return m_uCurrentPuppet; }

	virtual Asura_Guid GetForcedMeleeTarget() const { return m_uCurrentPuppet; }

	virtual bool HasPuppet() const { return true; } // all the moves inheriting from this use puppets

	void ResetGrappleEvents();

protected:
	virtual void Reset();

	virtual bool Start(const Asura_Msg_Move* pxStartMsg);
	virtual Asura_Move* Update();
	virtual void Finish();
	virtual void Abort();

	virtual void CheckStatesToStart();
	virtual void UpdateStates(const float fTimeStep);

	void UpdateStatesRelativePuppet(const float fTimeStep, AVP_ClientEntity_PhysicalObject* pxPuppet);

	virtual bool CalculatePosition(const float fTimeStep, Asura_AVector3_OutParam xPosition) const;
	virtual bool CalculateOrientation(const float fTimeStep, Asura_Quat& xOrientation) const;

	// Functions to set up the animations to use and the positional data for the puppet. The animations are unlikely to change during a single puppeteer move, but is included in case it's useful.
	virtual void UpdatePuppetAnimationData(AVP_Move_Puppet& xPuppetMove) {}
	virtual void UpdatePuppetPosition(AVP_Move_Puppet& xPuppetMove, const float fTimeStep);
	virtual void UpdatePuppetOrientation(AVP_Move_Puppet& xPuppetMove, const float fTimeStep);

	virtual void GetBasePuppetOrientation(Asura_Quat& xOrientation) const;

	void CalculateRelativeModePositions(const AVP_Move_Puppet& xPuppetMove, const float fTimeStep);

	void SetPuppetRelativeToOwner(bool bRelative);
	void SetPuppetAnimationData(Asura_Hash_ID uFirstPersonHash, Asura_Hash_ID uThirdPersonHash, float fTweenInTime, float fTweenOutTime, bool bAnimatedTween, bool bLeaveAnimAtEnd) const;

	void SeparateCharacters(AVP_Move_Puppet* pxPuppetMove);

	AVP_Actor_PhysicsObjectHandler* GetOwnerPhysicsHandler();
	AVP_Actor_PhysicsObjectHandler* GetPuppetPhysicsHandler();
	void EnablePhysicsSettings();
	void DisablePhysicsSettings();

	void UpdateGrappleEvents();

	Asura_Guid m_uCurrentPuppet;

	// Timestamps to handle the difficulties of updating both this and the puppet move within the update of whichever gets updated first.
	u_int m_uLastUpdateFrameStamp; // The last time Update() was called.
	u_int m_uLastStatesUpdateFrameStamp; // The last time UpdateStates() was called (can be different, as it's that function which gets called from the puppet move if it happens to update first this frame).

	// These are either in local space or worldspace, depending on the m_bPuppetRelativeToOwner flag. 
	Asura_Vector_3 m_xInitialPuppetPosition;
	Asura_Quat m_xInitialPuppetOrientation;

	Asura_MoveData_PositionInterpolation m_xPuppetPositionInterpolation;
	Asura_MoveData_OrientationInterpolation m_xPuppetOrientationInterpolation;

	// FSFX details, since it makes sense not to have to write this in all three of the generic puppeteer moves.
	Asura_Hash_ID m_uPuppeteerFSFX;
	Asura_Hash_ID m_uPuppetFSFX;

	Asura_Hash_ID m_uActiveGrappleAttackerBlueprint;
	Asura_Hash_ID m_uActiveGrappleVictimBlueprint;
	Asura_Hash_ID m_uGrappleSoundsAttackerBlueprint;
	Asura_Hash_ID m_uGrappleSoundsVictimBlueprint;

	// Temporary (hopefully) variable while we support both the old and new style of grapple sequence and blueprints.
	Asura_Hash_ID m_uActiveGrappleBlueprintType;

	Asura_AVector3 m_xRelativeModeOwnerBasePosition; // Starts as initial pos/ori, but gets updated if the movement queries decide we can't reach the position they're trying to get to. In the first version, this prevents position interpolation working for the puppeteer.
	Asura_Quat m_xRelativeModeOwnerBaseOrientation;
	float m_fRelativeModeOwnerFallingSpeed;

	// All of these are stored in worldspace.
	Asura_AVector3 m_xRelativeModeOwnerPosition;
	Asura_Quat m_xRelativeModeOwnerOrientation;
	Asura_AVector3 m_xRelativeModePuppetPosition;
	Asura_Quat m_xRelativeModePuppetOrientation;

	AVP_GrappleEvent_Entry m_axFirstPersonGrappleEvents[iAVP_MAX_GRAPPLE_EVENTS];
	int m_iNumFirstPersonGrappleEvents;
	AVP_GrappleEvent_Entry m_axThirdPersonGrappleEvents[iAVP_MAX_GRAPPLE_EVENTS];
	int m_iNumThirdPersonGrappleEvents;

	union
	{
		u_int m_uPuppeteerFlags;
		struct
		{
			u_int	m_bPuppetRelativeToOwner		: 1, // If the move is already in progress, only set this with the function, as that handles converting the positional data.
					m_bInterpolateInitialPuppetPos	: 1, // Initially, these two flags won't support mid-move changes to m_bPuppetRelativeToOwner. You have been warned.
					m_bInterpolateInitialPuppetOri	: 1,
					m_bCanFinish					: 1, // Not really a descriptive enough name, but anything better would be about four times as long. This gets set when we enter UpdateStates after Update has been entered, allowing the correct detection of WasFinished and HasFinished in Asura_Move::Update.
					m_bForceUprightPuppet			: 1, // Some alien grapples may cause reorientation issues for the other species. This should simplify fixing that.
													: 0;
		};
	};
};

///////////////////////////////////////////////////////////////////////
// AVP_Move_Flinch
// Generic flinch move, common to all species. Animations are sent through from the server.
///////////////////////////////////////////////////////////////////////

class AVP_Move_Flinch : public AVP_Move
{
	typedef AVP_Move PARENT;

public:
	AVP_Move_Flinch(Asura_Movement_System& xParent);
	//virtual ~AVP_Move_Flinch();

	static Asura_Hash_ID GetStaticHash() { return 0x56D90ADA; } //Asura_GetHashID("AVP_Move_Flinch");

protected:
	virtual bool Start(const Asura_Msg_Move* pxStartMsg);
	virtual void Abort();

	Asura_MoveData_OrientationInterpolation m_xOrientationInterpolationData;
};

class AVP_Move_Landing : public AVP_Move
{
	typedef AVP_Move PARENT;

public:
	AVP_Move_Landing(Asura_Movement_System& xParent);

	float GetTimeInMove() const { return m_fTimeInMove; }

	static Asura_Hash_ID GetStaticHash() { return 0xAF2A668D; } //Asura_GetHashID("AVP_Move_Landing");

protected:
	virtual bool Start(const Asura_Msg_Move* pxStartMsg); // Get the animation from our owner.
	virtual Asura_Move* Update();

	void StopAnimation();

	float m_fTimeInMove;
};

class AVP_Move_GS2RigidAnim : public AVP_Move
{
public:
	AVP_Move_GS2RigidAnim(Asura_Movement_System& xParent);

	static Asura_Hash_ID GetStaticHash() { return 0xfcf19200; } //Asura_GetHashID("AVP_Move_GS2RigidAnim");

protected:
	virtual bool Start(const Asura_Msg_Move* pxStartMsg); // Get the animation from our owner.
	virtual Asura_Move* Update();

private:
	typedef AVP_Move PARENT;

	bool m_bToldAnimHandlerAboutNewControllerHash;

	Asura_Vector_3 m_xInterpolatedCompassPosition;
	Asura_Quat m_xInterpolatedCompassOrientation;

	Asura_MoveData_PositionInterpolation m_xCompassPositionInterpolation;
	Asura_MoveData_OrientationInterpolation m_xCompassOrientationInterpolation;
};

#endif // #ifndef AVP_MOVES_H___
