
//////////////////////////////////////////////////////////////////////////////
//
// Asura_Movement_System.h
//
// Created by CJW - 09-March-2007 (from Simp_Movement_System by Anthony Gregson / Richard May)
//
// 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 __ASURA_MOVEMENT_SYSTEM_H__
#define __ASURA_MOVEMENT_SYSTEM_H__ 1

#if defined(ASURA_USE_MOVEMENT_SYSTEM)

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

// Engine independent.
#include "Core/Asura_HashTree.h"
#include "Core/Asura_List.h"
#include "Network/Asura_Guid.h"
#include "Core/Asura_Maths.h"

// C L A S S E S /////////////////////////////////////////////////////////////

class Asura_Message;
class Asura_Move;
class Asura_ClientEntity_PhysicalObject;


//////////////////////////////////////////////////////////////////////////////
//
//	Class Name    :	Asura_Msg_Move
//
//	Author        :	Richard May
//
//	Description   :	Base class for move messages
//
//////////////////////////////////////////////////////////////////////////////

// AJG - 14/04/08 - Reworked to combine the move start extra data into here, and to allow project flags without forcing projects to add extra bitfields.

class Asura_Msg_Move
{
public:

	Asura_Hash_ID	GetMoveID(void) const					{ return(m_uID);  }
	void			SetMoveID(const Asura_Hash_ID uInput)	{ m_uID = uInput; }

	bool GetIgnoredByOwner(void) const { return (m_usAsuraFlags & ASURA_MSG_MOVE_FLAG_IGNOREDBYOWNER) != 0;  }
	void SetIgnoredByOwner(const bool bInput);

	bool GetForceAbort(void) const { return (m_usAsuraFlags & ASURA_MSG_MOVE_START_FLAG_FORCEABORT) != 0;  }
	void SetForceAbort(const bool bInput);

	// This must be called whenever this class is created (since messages rarely have the chance to call constructors, so zeroing them on construction won't work), otherwise there's a risk of some flags having random settings.
	void ClearFlags() { m_usAsuraFlags = 0; m_usProjectFlags = 0; }

	// If a project doesn't want to allocate its own message class, it can do flag checks and sets manually by getting the flags here and comparing/setting bits.
	u_short GetProjectFlags() const { return m_usProjectFlags; }
	void SetProjectFlags(u_short usNewFlags) { m_usProjectFlags = usNewFlags; }

protected:

	Asura_Hash_ID	m_uID;

	u_short m_usAsuraFlags;
	u_short m_usProjectFlags;

private:

	enum
	{
		ASURA_MSG_MOVE_FLAG_IGNOREDBYOWNER		= 0x0001,
		ASURA_MSG_MOVE_START_FLAG_FORCEABORT	= 0x0002,
	};
};

/// End of class "Asura_Msg_Move". ///




//////////////////////////////////////////////////////////////////////////////
//
//	Class Name    :	Asura_Msg_MoveChainComplete
//
//	This message is sent when we had a move running at the start of an update, but don't have any at the end of it.
//
//////////////////////////////////////////////////////////////////////////////

class Asura_Msg_MoveChainComplete
{
public:
	Asura_Hash_ID GetInitialMoveID() const { return(m_uInitialMoveID);  }
	void SetInitialMoveID(const Asura_Hash_ID uID)	{ m_uInitialMoveID = uID; }

	Asura_Hash_ID GetFinalMoveID() const { return(m_uFinalMoveID);  }
	void SetFinalMoveID(const Asura_Hash_ID uID)	{ m_uFinalMoveID = uID; }

private:
	Asura_Hash_ID m_uInitialMoveID;
	Asura_Hash_ID m_uFinalMoveID;

};

/// End of class "Asura_Msg_MoveChainComplete". ///





//////////////////////////////////////////////////////////////////////////////
//
//	Class Name    :	Asura_Movement_System
//
//	Author        :	Richard May
//
//	Description   :	The base system for actor motion control. Handles
//					both animated and position-based forced moves and
//					eventually also free-movement animation.
//
//////////////////////////////////////////////////////////////////////////////

class Asura_Movement_System
{
	// Friend classes...
	friend class Asura_Move;

public:
	// Constructors/destructor...
	Asura_Movement_System(Asura_ClientEntity_PhysicalObject& xOwner);
	virtual	~Asura_Movement_System();

	// Public interface functions...
	virtual void	Update(void);
	virtual void	Render(void) const;
	virtual bool	HandleMessage( const Asura_Message* pxMessage );
	virtual void	Reset(void);

	// Move management...
	void AddInitialMove( Asura_Move* pxMove );
	bool AbortCurrentMove( bool bForceAbort = false, bool bSuppressAbortMessage = false );

	// Local player handling seems to be project specific at the moment...
    virtual bool IsControlledByAnyPlayer(const Asura_Guid uGuid)   const	= 0;
	virtual bool IsControlledByLocalPlayer(const Asura_Guid uGuid) const	= 0;

	// Move orientation get functions...
	void	GetCurrentMoveOrientation(Asura_Matrix_3x3& xOrientation) const	{ m_xCurrentMoveOrientation.ToMatrix(xOrientation); }
	void	GetCurrentMoveOrientation(Asura_Quat& xOrientation) const		{ xOrientation = m_xCurrentMoveOrientation; }
    void	GetPreviousMoveOrientation(Asura_Quat& xOrientation) const		{ xOrientation = m_xPreviousMoveOrientation; }

	// Move position get functions...
	void	GetCurrentMovePosition(Asura_Vector_3& xPosition) const			{ xPosition = m_xCurrentMovePosition; }
	void	GetCurrentMovePosition(Asura_AVector3_OutParam xPosition) const	{ xPosition = m_xCurrentMovePosition; }
    void	GetPreviousMovePosition(Asura_AVector3& xPosition) const	    { xPosition = m_xPreviousMovePosition; }

	// Move velocity get functions...
	void	GetCurrentMoveVelocity(Asura_Vector_3& xVelocity) const			{ xVelocity = m_xCurrentMoveVelocity; }
	void	GetCurrentMoveVelocity(Asura_AVector3_OutParam xVelocity) const	{ xVelocity = m_xCurrentMoveVelocity; }

	// Accessors...
	Asura_Guid									GetOwnerGuid(void) const	{ return(m_uOwnerGuid); }
	const Asura_ClientEntity_PhysicalObject&	GetOwner(void) const		{ return(m_xOwner); }
	Asura_ClientEntity_PhysicalObject&			GetOwner(void)				{ return(m_xOwner); }

	const Asura_Move*	GetCurrentMove(void) const	{ return(m_pxCurrentMove); }

	bool	IsMoveInProgress(void) const			{ return(m_pxCurrentMove != NULL); }
    bool    IsControlledByAnyPlayer(void) const     { return(IsControlledByAnyPlayer(m_uOwnerGuid)); }
	bool	IsControlledByLocalPlayer(void) const	{ return(IsControlledByLocalPlayer(m_uOwnerGuid)); }

	const Asura_Move*	GetMove(const Asura_Hash_ID uHash) const;
	Asura_Move*			GetMove(const Asura_Hash_ID uHash);

	// All the following functions (up to the start of the statics) are only intended to be used by moves.
	// Publicised so a project's more unusual moves can fiddle with position and orientation data in unique ways.
	// Update functions...
	void	UpdateMoveVelocityFromPosition(const float fTimeStep, const Asura_AVector3& xPosition, const bool bWorldSpace);
	void	UpdateMovePositionFromVelocity(const float fTimeStep, const Asura_AVector3& xVelocity, const bool bWorldSpace);

	void	UpdateMoveOrientationFromAngularVelocity(const float fTimerStep, const Asura_AVector3& xAxis, const Asura_Angle fAngle, const bool bWorldSpace);

	// Move orientation set functions...
	void	SetCurrentMoveOrientation(const Asura_Matrix_3x3& xOrientation)	{ m_xPreviousMoveOrientation = m_xCurrentMoveOrientation; m_xCurrentMoveOrientation.FromMatrix(xOrientation); }
	void	SetCurrentMoveOrientation(const Asura_Quat& xOrientation)		{ m_xPreviousMoveOrientation = m_xCurrentMoveOrientation; m_xCurrentMoveOrientation = xOrientation; }

	// Move position set functions...
	void	SetCurrentMovePosition(const Asura_Vector_3& xPosition)		{ m_xPreviousMovePosition = m_xCurrentMovePosition; m_xCurrentMovePosition = xPosition; }
	void	SetCurrentMovePosition(Asura_AVector3_InParam xPosition)	{ m_xPreviousMovePosition = m_xCurrentMovePosition; m_xCurrentMovePosition = xPosition; }

	// Move velocity set functions...
	void	SetCurrentMoveVelocity(const Asura_Vector_3& xVelocity)		{ Asura_Assert((xVelocity.MagnitudeSquared() < (250.0f * 250.f)), "Suspicious velocity set for move."); m_xCurrentMoveVelocity = xVelocity; }
	void	SetCurrentMoveVelocity(Asura_AVector3_InParam xVelocity)	{ Asura_Assert((xVelocity.MagnitudeSquared() < (250.0f * 250.f)), "Suspicious velocity set for move."); m_xCurrentMoveVelocity = xVelocity; }

	static void DeferredUpdate();
	static void SetUseDeferredUpdate(bool bUseDeferred) { s_bUseDeferredUpdate = bUseDeferred; }

protected:
	// Move initialisation functions...
	void AddMove(Asura_Move* pxMove);
	bool StartMove(Asura_Move& pxMove, const Asura_Msg_Move* pxStartMsg);

	// Stationary-timer functions...
	void	ResetStationaryTimer(void)												{ m_fStationaryTimer = 0.0f; }
	bool	UpdateStationaryTimer(const float fTimeStep, const float fMaxDuration)	{ m_fStationaryTimer += fTimeStep; return(m_fStationaryTimer < fMaxDuration); }

	// Movement-offset orientation functions...
	void	SetLSMOInitialOrientation(const Asura_Quat& xOrientation)	{ m_xLSMOInitialOrientation = xOrientation; m_xLSMOPreviousOrientation = xOrientation; m_xLSMOCurrentOrientation = xOrientation; }
	void	GetLSMOInitialOrientation(Asura_Quat& xOrientation) const	{ xOrientation = m_xLSMOInitialOrientation; }

	void	SetLSMOCurrentOrientation(const Asura_Quat& xOrientation)	{ m_xLSMOPreviousOrientation = m_xLSMOCurrentOrientation; m_xLSMOCurrentOrientation = xOrientation; }
	void	GetLSMOCurrentOrientation(Asura_Quat& xOrientation) const	{ xOrientation = m_xLSMOCurrentOrientation; }
	void	GetLSMOPreviousOrientation(Asura_Quat& xOrientation) const	{ xOrientation = m_xLSMOPreviousOrientation; }

	void	SetLSMOFinalOrientation(const Asura_Quat& xOrientation)	{ m_xLSMOFinalOrientation = xOrientation; }
	void	GetLSMOFinalOrientation(Asura_Quat& xOrientation) const	{ xOrientation = m_xLSMOFinalOrientation; }

	// Movement-offset position functions...
	void	SetLSMOInitialPosition(Asura_AVector3_InParam xPosition)			{ m_xLSMOInitialPosition = xPosition; m_xLSMOPreviousPosition = xPosition; m_xLSMOCurrentPosition = xPosition; }
	void	GetLSMOInitialPosition(Asura_AVector3_OutParam xPosition) const		{ xPosition = m_xLSMOInitialPosition; }

	void	SetLSMOCurrentPosition(Asura_AVector3_InParam xPosition)			{ m_xLSMOPreviousPosition = m_xLSMOCurrentPosition; m_xLSMOCurrentPosition = xPosition; }
	void	GetLSMOCurrentPosition(Asura_AVector3_OutParam xPosition) const		{ xPosition = m_xLSMOCurrentPosition; }
	void	GetLSMOPreviousPosition(Asura_AVector3_OutParam xPosition) const	{ xPosition = m_xLSMOPreviousPosition; }

	void	SetLSMOFinalPosition(Asura_AVector3_InParam xPosition)			{ m_xLSMOFinalPosition = xPosition; }
	void	GetLSMOFinalPosition(Asura_AVector3_OutParam xPosition) const	{ xPosition = m_xLSMOFinalPosition; }

	void SetSendChainCompleteMessages(bool bSendMessages) { m_bSendChainCompleteMessages = bSendMessages; }
    virtual void SendChainCompleteMessage(Asura_Hash_ID uFinalMoveID) const;

    Asura_Hash_ID GetFirstMoveInChain() const { return m_uFirstMoveInChain; }

	Asura_ClientEntity_PhysicalObject&	m_xOwner;
	Asura_Guid							m_uOwnerGuid;

	Asura_Message*	m_pxPendingMoveMessage;	// temporary message storage for deferred message handling

	// Current-move data...
	Asura_Move*	m_pxCurrentMove;

private:
	// Private variables...
	Asura_List<Asura_Move>              m_xInitialMoveList;
	Asura_TemplateHashTree<Asura_Move*> m_xMasterMoveTree;

	Asura_Vector_3		m_xCurrentMovePosition;
	Asura_Vector_3		m_xCurrentMoveVelocity;
	Asura_Quat			m_xCurrentMoveOrientation;

	Asura_AVector3	m_xPreviousMovePosition;
	Asura_Quat		m_xPreviousMoveOrientation;

	Asura_AVector3	m_xLSMOInitialPosition;		// The local-space position from the start of the animation's movement-offset.
	Asura_Quat		m_xLSMOInitialOrientation;	// The local-space orientation from the start of the animation's movement-offset.
	Asura_AVector3	m_xLSMOPreviousPosition;	// The previous local-space position from the animation's movement-offset.
	Asura_Quat		m_xLSMOPreviousOrientation;	// The previous local-space orientation from the animation's movement-offset.
	Asura_AVector3	m_xLSMOCurrentPosition;		// The current local-space position from the animation's movement-offset.
	Asura_Quat		m_xLSMOCurrentOrientation;	// The current local-space orientation from the animation's movement-offset.
	Asura_AVector3	m_xLSMOFinalPosition;		// The local-space position from the end of the animation's movement-offset.
	Asura_Quat		m_xLSMOFinalOrientation;	// The local-space orientation from the end of the animation's movement-offset.

	float	m_fStationaryTimer;		// The number of seconds for which this move has been stationary.

	Asura_Hash_ID m_uFirstMoveInChain;

	// Flags...
	union
	{
		u_int		m_uFlags;						// Single access point for flags.

		struct
		{
			u_int	m_bUpdatingCurrentMove			: 1,	// Flag to say that we're updating the current move.
					m_bSendChainCompleteMessages	: 1,	// Flag to decide if the project wants to use the chain complete message, since it's a guaranteed message that some projects may not have a use for.
													: 0;	// 30 spare-bits
		};
	};

	static Asura_TemplateHashTree<Asura_Movement_System*> s_xDeferredUpdateTree;
	static bool s_bUseDeferredUpdate; // If set, the project must call Asura_Movement_System::DeferredUpdate some time before the next entity update, to update all movement systems and clear the tree.
	static bool s_bRunningDeferredUpdate;
};

/// End of class "Asura_Movement_System". ///

#endif	// defined(ASURA_USE_MOVEMENT_SYSTEM)

#endif // #ifndef __ASURA_MOVEMENT_SYSTEM_H__

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