//////////////////////////////////////////////////////////////////////////////
//
// AVP_ClientActor_Vertebrate.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_CLIENTACTOR_VERTEBRATE_H___
#define AVP_CLIENTACTOR_VERTEBRATE_H___ 1

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

// Asura
#include "Blueprint/Asura_Blueprint.h"
#include "Sound/Asura_Sound_Event_System.h"

// AVP
#include "Actor/AVP_Actor_AnimationHandler.h"
#include "Actor/AVP_Actor_DynamicStat.h"
#include "Animation/AVP_Animation_BodyRig.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_Vertebrate_Messages.h"
#include "Rendering/Asura_Project_Skin_Elements.h"

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////

class Asura_Collision_Object;
class Asura_Message;

class AVP_Message_Vertebrate_ClientUpdate;
class AVP_Msg_Flinch;
class AVP_Movement_System;

class AVP_Actor_LockonHandler;
class AVP_Vertebrate_Focus;
class AVP_Vertebrate_JumpHandler;
class AVP_Vertebrate_PheromoneHandler;

class AVP_Vertebrate_StateHandler;

class AVP_ClientEntity_Gib;

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

class AVP_ClientActor_Vertebrate : public AVP_ClientEntity_Actor
{
public:
	// This needs to match the one in AVP_Knowledge_System_Actor
	enum AVP_AlertLevel
	{
		PASSIVE_ALERTNESS = 0,
		ACTIVE_ALERTNESS,
		AGGRESSIVE_ALERTNESS,
		MAX_ALERT_LEVELS
	};

	enum JumpLandType
	{
		JUMPLAND_NONE = 0,
		JUMPLAND_LIGHT,		// quiet jump / land type
		JUMPLAND_NORMAL,	// normal noisy jump / land type
		JUMPLAND_HEAVY,		// extremely noisy/violent jump / land type
		MAX_JUMPLAND_TYPES
	};

	// Static list functions...
	static AVP_ClientActor_Vertebrate* FindEntity( Asura_Guid uGUID )				{ return s_xList.FindEntity( uGUID ); }
	static const Asura_Hashed_List<AVP_ClientActor_Vertebrate>* GetList( void )		{ return &s_xList; }
	static const Asura_Hashed_List<AVP_ClientActor_Vertebrate>* GetRagdolledList()	{ return &s_xRagdolledList; }

	// Public functions...
	virtual	void	Update					(void);
	virtual void	Render					(void);
	virtual	bool	HandleMessage			(const Asura_Message* pxMessage);
	virtual void	MakePlayer				(Asura_Guid uPlayerGuid);
	virtual void	MakeAI					(void);
	virtual void	DisconnectPlayer();

	virtual	void RenderHUD() const;

	// Event functions...
	virtual	void	OnBeginMoving			(void);
	virtual	void	OnEndMoving				(void);
	virtual	void	OnBeginMovingFast		(void);
	virtual void	OnEndMovingFast			(void);

	//void			IsJumping();
	void			SetJumpLandType( JumpLandType eType ) { m_eJumpLandType = eType; }

	virtual void ReadFromClientCreateStream( Asura_Chunk_Memory_Stream &xStream, float fServerTime );

	virtual void TeleportTo(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);

	virtual void GetAimDirection( Asura_AVector3& xAimDir ) const;
	virtual void SetAimDirection( const Asura_AVector3& xAimDir, const float fServerTime = -1.0f );

	virtual void GetLookAtPosition( Asura_AVector3& xLookAtPos ) const;
	virtual void SetLookAtPosition( const Asura_AVector3& xLookAtPos, const float fServerTime = -1.0f );

	virtual u_int GetMaxWeapons(void) const { return 0u; }

	// Heart-rate functions...
	void	SetMinHeartRate(float fRate) { m_fMinRate = fRate; }
	float	GetMinHeartRate(void)	const { return(m_fMinRate); }
	void	SetMaxHeartRate(float fRate) { m_fMaxRate = fRate; }
	float	GetMaxHeartRate(void)	const { return(m_fMaxRate); }
	void	SetCurHeartRate(float fRate) { m_fCurRate = fRate; }
	float	GetCurHeartRate(void)	const { return(m_fCurRate); }
	
	// Zoom functions...
	AVP_Vertebrate_JumpHandler*							GetJumpHandler		()					{ return(m_pxJumpHandler); }
	const AVP_Vertebrate_JumpHandler*					GetJumpHandler		() const			{ return(m_pxJumpHandler); }

	AVP_Vertebrate_PheromoneHandler*					GetPheromoneHandler	()					{ return(m_pxPheromoneHandler); }
	const AVP_Vertebrate_PheromoneHandler*				GetPheromoneHandler	() const			{ return(m_pxPheromoneHandler); }

	void			SetJumpVelocity	(const Asura_Vector_3& xVelocity)	{ if(m_pxDynamicObject != NULL) { m_pxDynamicObject->SetActorJumpVelocity(xVelocity); } }
	
	void			PlayFootSound			(const bool bLeftFoot, const float fMoveSpeedParametric);

	virtual void PostCreateInitialise(void);

	bool IsExhausted(void) { return(GetCurHeartRate() >= GetMaxHeartRate()-0.5f); } // define exhausted as max rate minus a small amount

	virtual u_int	GetElementFromBoneID(int iBoneID) const { return NUM_AVP_SKIN_ELEMENT_TYPES; }
	virtual int		GetAnyBoneIDFromElement(u_int uElement) const { return -1; } // Since bones->elements is a many->one mapping, this function will return one of the bones associated with the given element, but not necessarily the only one.

	bool IsElementUndamaged(const u_int uElement) const { return ( uElement >= NUM_AVP_SKIN_ELEMENT_TYPES ) ? true : ((m_uDestroyedSkinElementFlags & (1 << uElement)) == 0); }
	bool AreAnyElementsDamaged() const { return m_uDestroyedSkinElementFlags != 0; }

	void SetConstrainedMovementAttractorID(u_int uID) { m_uConstrainedMovementAttractorID = uID; }
	u_int GetConstrainedMovementAttractorID() const { return m_uConstrainedMovementAttractorID; }

	virtual const AVP_Vertebrate_Focus* GetFocus() const { return m_pxFocus; }
	virtual AVP_Vertebrate_Focus* GetFocus() { return m_pxFocus; }
	bool GetFocusActive() const;

	float GetHealthDangerFraction(void) const { return m_fHealthDangerFraction; }

	void SetFootSoundVolume( const float fFootSoundVolume ) { m_fFootSoundVolume = fFootSoundVolume; }

	void GetBlockStartAnims(Asura_Hash_ID& uFirstPersonHash, Asura_Hash_ID& uThirdPersonHash) const;
	void GetBlockLoopAnims(Asura_Hash_ID& uFirstPersonHash, Asura_Hash_ID& uThirdPersonHash) const;
	void GetBlockEndAnims(Asura_Hash_ID& uFirstPersonHash, Asura_Hash_ID& uThirdPersonHash) const;

	bool IsBlockActive() const { return m_bBlockActive; }

	virtual bool IsBoneInTorso( const int iBoneID ) { return false; }

	virtual bool MakeRagdoll();
	virtual bool GetCreateImmediateRagdoll(const AVP_EntityDamage_ApplyToEntity& xDamage, Asura_Hash_ID& uReplacementDeathAnim) const; // uReplacementDeathAnim is output-only, irrelevent if the function returns true, and may be unset if something else is controlling the animation but still doesn't want to create a ragdoll immediately.
	virtual Asura_Hash_ID GetDeathAnimation(const AVP_EntityDamage_ApplyToEntity& xDamage) const;

	Asura_Hash_ID GetBloodFromHitPFXHash() const { return m_uBloodFromHitPFXHash; }
	Asura_Hash_ID GetBloodSquirtPFXHash() const { return m_uBloodSquirtPFXHash; }

	virtual const Asura_Blueprint* GetSkinElementBlueprint(u_int uElement) const;

	virtual bool	BeginWeaponDeactivation( Asura_Hash_ID uDeactivationAnim, const bool bCalculateMovement );

	virtual	void	OnBeginJumping(void);

	virtual bool IsJumping() const { return m_bIsJumping; }
	virtual bool IsFalling() const { return m_bIsFalling; }
	virtual bool IsLanding() const;

	virtual void GetMeleeOrigin(Asura_Vector_3& xOrigin) const;
	Asura_Guid GetMeleeTarget() const;

	AVP_AlertLevel GetAlertLevel() const { return m_eAlertLevel; }

	void PlayDistractionSound( bool bOriginatesFromPlayer, Asura_Hash_ID uSoundHashId ) const;

	virtual bool IsRagDoll()   const;
	void		 SetRagDollAsleep() const;
	virtual bool IsRagDollAsleep() const;
	virtual void WakeUpRagDoll() const;

	virtual bool  IsIdleForFidget() const;

	bool IsShortRouteAvailable(const Asura_Vector_3& xTarget, const float fMinDist, const float fMaxDist) const;

	Asura_Hash_ID GetActiveGrappleBlueprintID(int iGrappleDifficulty) const;

	void DestroySkinElement(const u_int uElement, const bool bQuietDestroy = false); // Will cause MakeSkinElementInvisible to be called on all child elements. If the bool is true, no special effects will be created, we just change to the damaged skin element.
	void ActivateHeadbitSkinElement();

	Asura_Hash_ID GetBloodTrailBPHash(void) { return m_uBloodTrailBPHash; }
	Asura_Hash_ID GetBloodTrailMarkerHash(bool bRightHanded) { return (bRightHanded)?m_uRightHandBloodMarker:m_uLeftHandBloodMarker;}

	bool IsAffectedBy(Asura_Hash_ID uDamageType) const;

protected:
	// Protected functions...
	AVP_ClientActor_Vertebrate(const u_short usFlagsToIgnore);
	virtual ~AVP_ClientActor_Vertebrate();

	virtual void UpdateMovement();
	virtual void UpdateActions() {}

	void CreateHitEffects(const AVP_EntityDamage_ApplyToEntity& xDamage);
	void DamageLimb(const AVP_EntityDamage_ApplyToEntity& xDamage);
	void CreateBoneRelativeBloodspurt(const AVP_EntityDamage_ApplyToEntity& xDamage, const Asura_Hash_ID uPFXHash);
	void CreateBloodDecal(const Asura_Hash_ID uDecalHash, const Asura_Vector_3& xLOSOrigin) const; // TODO - Do we need more details? Should we even be creating decals in this way, or using the requested create-decal-from-PFX feature?
	void CreateBloodDecalFromMarker(const Asura_Hash_ID uDecalHash, const Asura_Hash_ID uMarkerHash) const;
	void CreateBloodDecalFromDamage(const Asura_Hash_ID uDecalHash, const AVP_EntityDamage_ApplyToEntity& xDamage) const;
	void AttachBloodTrails(Asura_Guid uHitEntity, u_int uDamageType, bool bRightHanded);


	void ModifySkinElementGibDirectionFromDamagePosition(const u_int uElement, Asura_Vector_3* xDirection, const AVP_EntityDamage_ApplyToEntity& xDamage); // Set 'xDirection' to be away from the center of impact towards where this skin element is (so limbs can fly away from the centre of impact).
	void CreateSkinElementGibs(const AVP_EntityDamage_ApplyToEntity& xDamage); // Multiple create, using the flags in the AVP_EntityDamage_ApplyToEntity struct. For now, this will not destroy any elements internally, leaving that to other calls.
	AVP_ClientEntity_Gib* CreateSkinElementGib(const u_int uElement, const Asura_Vector_3& xDirection, const float fImpulse); // Single create, using the passed-in element value and some details that probably came from a damage message. For now, this will not destroy any elements internally, leaving that to other calls.
	void DestroySkinElements(const AVP_EntityDamage_ApplyToEntity& xDamage, const bool bQuietDestroy = false); // Multiple destroy, using the flags in the AVP_EntityDamage_ApplyToEntity struct.
	void MakeSkinElementInvisible(const u_int uElement); // Just makes the passed-in element invisible, ignoring all other elements.
	void SetSkinElementDamaged(const u_int uElement); // Used by both of the above function and DestroySkinElement (which is now public) to flag the element as destroyed, and to set up the LOS ignore flags in the animation.

    virtual void	InitialiseFromBlueprint( const Asura_Blueprint& xBlueprint );
	virtual void	OnEntityActivate();
	virtual void    OnEntityDeactivate();
	// Overridden initialisation functions...
	virtual	bool	InitialiseMoves(void);
	virtual bool	InitialiseSoundEvents(void);

    void	ProcessClientUpdate		(const AVP_Message_Vertebrate_ClientUpdate* pxData, float fServerTime, bool bProcessPosAndOri = true);

	void	UpdateFootSounds		(void);	
	
	void PlayFootSounds();
	virtual void CheckForAnimFlags(); // For any special vertebrate actions from anim flags not handled by the footsteps (or elsewhere such as in moves).

	// Called from constructor, so do not make virtual.
	void SetupDefaultVisionMode();

	// Either play an animation directly as a secondary, or start a move using the provided animations.
	void StartFlinch(const AVP_Msg_Flinch& xFlinchData);

	virtual void GetCurrentBlockBlueprintHashes(Asura_Hash_ID& uBlueprintTypeHash, Asura_Hash_ID& uFirstPersonBlueprintHash, Asura_Hash_ID& uThirdPersonBlueprintHash) const { uBlueprintTypeHash = uFirstPersonBlueprintHash = uThirdPersonBlueprintHash = ASURA_HASH_ID_UNSET; }

	virtual void UpdateFalling();

	// Damage functions...
	void	ApplyImpulseToRagdoll(const AVP_EntityDamage_ApplyToEntity& xDamage);

	// Event functions...
	virtual void	OnBeginFalling(void);
	virtual void	OnEndFalling(void); // This will be used to trigger landing moves, whereas more than just landing moves may want to call OnBeginLanding (e.g. jumping moves which include the landing phase)
	virtual	void	OnBeginLanding(void);

	virtual void	OnApplyDamage(const AVP_EntityDamage_ApplyToEntity& xDamage);
	virtual	void	OnDeath(const AVP_EntityDamage_ApplyToEntity& xDamage);

	virtual void	OnBeginBurning();
	virtual void	OnEndBurning();

	virtual void GetLandingAnimsAndApplyDamage(Asura_Hash_ID& uFirstPersonHash, Asura_Hash_ID& uThirdPersonHash) const;
	Asura_Quat	 UpdateOrientation();

	virtual void GetDesiredForwardMovementDirection(Asura_AVector3& xDirection, const Asura_AMatrix3x3& xOri) const;

	void		 MakeRagdollForStartDead();
	void		 DestroyRagdollForStartDead();

	void InitialiseGrappleDifficulties(const Asura_Blueprint& xActorBlueprint);

	// Protected variables...
	float m_fMinRate;
	float m_fMaxRate;
	float m_fCurRate;

	float m_fFootSoundVolume;

	// Blood hashes
	Asura_Hash_ID m_uBloodFromHitPFXHash;
	Asura_Hash_ID m_uBloodFromHitDecalHash;
	Asura_Hash_ID m_uBloodFromGibPFXHash;
	Asura_Hash_ID m_uBloodSquirtPFXHash;

	Asura_Hash_ID m_uBloodTrailBPHash;
	Asura_Hash_ID m_uRightHandBloodMarker;
	Asura_Hash_ID m_uLeftHandBloodMarker;

	float m_fHealthDangerFraction;

	AVP_Vertebrate_JumpHandler*			m_pxJumpHandler;		// The Jump-handler to use for this actor.
	AVP_Vertebrate_PheromoneHandler*	m_pxPheromoneHandler;		// The Pheromone-handler to use for this actor.

	AVP_Vertebrate_Focus* m_pxFocus;

	const Asura_Sound_Event*	m_pxNormalJumpSound;		// Sound-event for jumping.
	const Asura_Sound_Event*	m_pxPainSoundEvent;
	const Asura_Sound_Event*	m_pxDeathSoundEvent;
	const Asura_Sound_Event*	m_pxDeathStingEvent;

	Asura_Handle				m_iJumpSoundHandle;
	Asura_Handle				m_iPainSoundHandle;
	Asura_Handle				m_iDeathSoundHandle;
	Asura_Handle				m_iDeathStingHandle;

	float m_fTimeFalling; // Timer to prevent activating the falling state prematurely, since changing animations only to land immediately looks rubbish.
	float m_fYPosAtFallStart; // Storage of initial falling position, since the speed we get back from velocity calculations appears too inconsistent to use for choosing landing types.

	float m_fTimeJumping; // Timer to prevent exiting the jumping state prematurely, since we have to at least have a chance to leave the ground.

	union
	{
		u_int		m_uVertFlags;				// Single access point for flags.

		struct
		{
			u_int	m_uZoomLevel			: 4,	// Level of zoom
					m_uPreviousZoomLevel	: 4,	// Previous level of zoom
					m_bEnemyInMeleeRange	: 1,	// Some vertebrates may want to change animations when this is true.
					m_bBlockActive			: 1,	// Trigger some moves based on this. May be replaced by some kind of state, if that makes more sense.
					m_bIsFalling			: 1,
					m_bIsJumping			: 1,
					m_eJumpLandType			: 2,
											: 0;	
		};
	};

	AVP_AlertLevel m_eAlertLevel;


	u_int m_uConstrainedMovementAttractorID;

	// Skin element data.
	Asura_Hash_ID m_auSkinElementBlueprints[NUM_AVP_SKIN_ELEMENT_TYPES];
	u_int m_uDestroyedSkinElementFlags;
	float m_fDestroyedSkinElementLastMatchTimestamp;

	u_int m_uPFXBurningHeadFirstPerson;

	// Active grapple difficulty settings.
	enum { MAX_ACTIVEGRAPPLE_DIFFICULTIES = 8, };
	Asura_Hash_ID m_auActiveGrappleBlueprints[MAX_ACTIVEGRAPPLE_DIFFICULTIES];
	int m_iNumActiveGrappleDifficulties;

private:
	Asura_Vector_3 m_xDeathCamPosition;

	static const Asura_Hash_ID s_uBlockStartParameterID;
	static const Asura_Hash_ID s_uBlockLoopParameterID;
	static const Asura_Hash_ID s_uBlockEndParameterID;


	// Private typedefs...
	typedef AVP_ClientEntity_Actor PARENT;

	static Asura_Hashed_List<AVP_ClientActor_Vertebrate> s_xList;
	static Asura_Hashed_List<AVP_ClientActor_Vertebrate> s_xRagdolledList;
};

// Parameter enumerator for death anims.
class AVP_Blueprint_ParameterEnumerator_DeathAnimSelector : public Asura_Blueprint_ParameterEnumerator
{
	typedef Asura_Blueprint_ParameterEnumerator PARENT;

public:
	AVP_Blueprint_ParameterEnumerator_DeathAnimSelector();

	Asura_Hash_ID SelectAnim() const;

	virtual void BeginEnumeration();
	virtual bool Enumerate(const u_int uIndex, char const* const szName, const Asura_Value& xValue);
	virtual void EndEnumeration(const bool bSuccess);

protected:
	enum { MAX_DEATH_ANIMS = 8, };

	Asura_Hash_ID m_auDeathAnims[MAX_DEATH_ANIMS];
	u_int m_uNumDeathAnims;
};

// And for landing anims. In this case, we just need to enumerate the landing speeds and find which is the greatest we exceed.
class AVP_Blueprint_ParameterEnumerator_LandingSelector : public Asura_Blueprint_ParameterEnumerator
{
	typedef Asura_Blueprint_ParameterEnumerator PARENT;

public:
	AVP_Blueprint_ParameterEnumerator_LandingSelector();

	int GetLandingID(float fDistanceFallen) const;
	int GetDamageLimitID(float fDistanceFallen) const;
	float GetProportionBetweenLandings(int iLowerID, int iUpperID, float fDistanceFallen) const;

	virtual void BeginEnumeration();
	virtual bool Enumerate(const u_int uIndex, char const* const szName, const Asura_Value& xValue);
	virtual void EndEnumeration(const bool bSuccess);

protected:
	enum { MAX_LANDING_DISTANCES = 8, };

	float m_afLandingDistances[MAX_LANDING_DISTANCES];
	u_int m_uNumLandingDistances;
};

// Enumerator for grapple difficulties, called on initialising from our actor blueprint.
class AVP_Blueprint_ParameterEnumerator_GrappleDifficulties : public Asura_Blueprint_ParameterEnumerator_ByHash
{
	typedef Asura_Blueprint_ParameterEnumerator_ByHash PARENT;

public:
	AVP_Blueprint_ParameterEnumerator_GrappleDifficulties(Asura_Hash_ID* puActiveGrappleBlueprints, int& iNumDifficulties, int iMaxDifficulties);

	virtual void BeginEnumeration();
	virtual bool Enumerate(const u_int uIndex, const Asura_Value& xValue);
	virtual void EndEnumeration(const bool bSuccess) {}

protected:
	Asura_Hash_ID* m_puActiveGrappleBlueprints;
	int& m_iNumDifficulties;
	int m_iMaxDifficulties;
};

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

#endif // #ifndef AVP_CLIENTACTOR_VERTEBRATE_H___
