////////////////////////////////////////////////////////////////
//
// File:		AVP_Vertebrate_MeleeCounter_System.h
// Author:		Chris Wallace
// Date:		22-Jan-2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif   // ifdef ASURA_USE_PRAGMA_ONCE

#ifndef __AVP_VERTEBRATE_MELEECOUNTER_SYSTEM_H__
#define __AVP_VERTEBRATE_MELEECOUNTER_SYSTEM_H__

///////////////////////////////////////////////
// #includes
///////////////////////////////////////////////

#include "Blueprint/Asura_Blueprint.h"
#include "Core/Asura_Maths.h"

class Asura_LOS_InputData;
class Asura_LOS_OutputData;
class Asura_Message;

class AVP_Move_Melee_Pounce_Attack_Start;
class AVP_Move_Melee_Pounce_Attack_Return_Hit;
class AVP_Move_Melee_Pounce_Attack_Return_Countered;
class AVP_Move_Melee_Pounce_Attack_Return_Miss;
class AVP_ServerActor_Vertebrate;
class AVP_ServerEntity_Actor;
class AVP_ServerEntity_PhysicalObject;
class AVP_Vertebrate_Focus;

/////////////////////////////////////////////////////////////////////
//
// Class		:	AVP_Vertebrate_MeleeCounter_System
//
// Description	:	Stored on the server, hold a queue of requested melee moves
//				:	When a melee reaches the front of the queue the status of the 
//				:	actor is checked and a relevant move is played.
//				:	If the move fails (misses, is countered) then the queue is emptied.
//
//////////////////////////////////////////////////////////////////////

enum AVP_MELEEATTACK_TYPE
{
	AVP_MELEEATTACK_TYPE_NONE,
	AVP_MELEEATTACK_TYPE_NORMAL,
	AVP_MELEEATTACK_TYPE_COUNTER,
	AVP_MELEEATTACK_TYPE_BLOCK,
	AVP_MELEEATTACK_TYPE_UNBLOCK,
	AVP_MELEEATTACK_TYPE_GRAPPLEPOUNCE,

	NUM_AVP_MELEEATTACK_TYPES, // Subclasses should start a new enumeration from this point to add additional types. Internally, all types are treated as u_ints.
};

static const Asura_Hash_ID uMELEE_MARKER_POINT_HASH = 0x062F8D74;	//Asura_GetHashID("Melee");

class AVP_Vertebrate_MeleeCounter_System
{
public: 
	AVP_Vertebrate_MeleeCounter_System(AVP_ServerActor_Vertebrate& xOwner);
	virtual ~AVP_Vertebrate_MeleeCounter_System();

	// This uses the actor initialisation blueprint, passed through from our owner.
	virtual void InitialiseFromBlueprint(const Asura_Blueprint& xBlueprint);
	virtual void PostLoadInitialise();

	virtual bool HandleMessage(const Asura_Message* pxMessage);
	virtual void Update();

	virtual float GetCurrentMaxCounterDistance() const;

	Asura_Guid GetCurrentTarget() const { return m_uCurrentTarget; }
	bool IsTargetInDamageRange() const;
	bool GetHasAttackTarget() const; // For animation changes
	void RequestTarget(Asura_Guid uRequestedTarget);
	void RequestForceMissAndDmgMultiplier(bool bForceMiss, float fDmgMultiplier);
	float GetDamageRange( void ) const { return m_fDamageRange; }

	void ClearAllRequests() { ClearAttackRequests(); m_uQueuedCounter = AVP_MELEEATTACK_TYPE_NONE; }
	void ClearAttackRequests();

	void SetCurrentAttackCountered(bool bCountered) { m_bCurrentAttackCountered = bCountered; }

	bool TestBlock(const AVP_ServerEntity_Actor& xAttacker) const;
	void BreakBlock() { SetBlockActive(false); } // Used, for example, when an enemy causes this character to flinch normally (e.g. hits from the back, or unblockable attacks).

	inline bool IsBlockActive() const { return m_bBlockActive; }
	inline float GetBlockDamageProportion() const { return m_fBlockDamageProportion; }
	float GetAIBackAttackDamageFactor() const { return m_fDamageAIBackAttackFactor; }

	// Handy functions so the AI can do some stuff with the melee system
	bool GetCurrentAttackCanBeCountered() const;

	u_int GetCurrentAttackType() { return m_uCurrentAttackType; }

	// Helper for button-held attacks so they don't need to keep requesting all the time.
	bool GetCanStartNewAttack() const;

	// This one uses our current attack type and the enemy's species to work out a move for the countering enemy to perform - our attack will work out its countered animation anyway.
	// The new attack type allows us to override any damage amount that the resulting grapple may cause to the original attacker.
	// The final bool allows us to ensure the enemy gets told if we're going to force them to start the puppet move as a result of the counter starting.
	bool GetCanCounter(const AVP_ServerEntity_Actor& xEnemy, const u_int uQueuedCounterType, Asura_Hash_ID& uMoveHash, u_int& uNewAttackType, bool& bMakeEnemyIntoPuppet, Asura_Hash_ID& uGrappleSetBlueprintParam) const;

	void SetPounceAttackVariationData(u_int uNumVariations, float* pfMaxDistances, float* pfMinDrops, float* pfDamageValues, float* pfAttackImpulses);

	// This unsets any requested target and calls the target update. Allows AIs to use the melee target search as and when they want, since they no longer call it automatically.
	void SearchForNewTarget();

	void MakePlayer();
	void MakeAI();


	float GetMinorDamageAmount() const { return m_fMinorDamageAmount; }
	float GetModerateDamageAmount() const { return m_fModerateDamageAmount; }
	float GetMajorDamageAmount() const  { return m_fMajorDamageAmount; }

	static const char* GetPounceAttackBaseParameterName() { return s_szPounceAttackBaseParameterName; }
	
	virtual bool IsCurrentTargetInSuckRange() const;

	static bool IsMeleeDamageType(const Asura_Hash_ID uTypeHash);

protected:
	virtual bool StartMeleeMove(u_int uType); // This sets the current attack type and automatically unsets the block active flag if it succeeds.
	virtual bool PerformCounter(); // Uses the current target only. Automatically unsets the block active flag if it succeeds.

	virtual void CheckForAndDealMeleeDamage();
	virtual void ApplyMeleeDamage(Asura_Guid uDamagedGuid);
	virtual void ApplyGrappleDamage() const;
	void ApplyGrappleDamage(const Asura_Hash_ID uGrappleDescriptionID) const;

	virtual bool GetAttackTypeCanCounter(u_int uType) const; // Returns true if a particular melee request can also be used to counter an incoming attack.

	// Go off and get a move hash relevant to the species and weapon.
	// This one gets a melee move hash using our current attack type and the passed-in new attack type. Returning unset effectively skips the new attack.
	virtual Asura_Hash_ID GetMeleeMoveHash(u_int uType) const;
	// This one newly-current attack type to pick a relevant variation.
	virtual u_int PickMeleeMoveVariation(Asura_Hash_ID uMoveHash) const;
	// One alternative instead of the variation will be setting up a grapple directly. Allow a way of getting the grapple set parameter here.
	virtual Asura_Hash_ID GetMeleeMoveGrappleSetBlueprintParam(u_int uType) const;
	// This is for deciding whether to set the puppet flag in the melee move message.
	virtual bool GetMeleeMoveStartsPuppet(Asura_Hash_ID uMoveHash) const;
	// Similar to above, but for blocks.
	virtual bool GetCanBlock() const;
	virtual bool GetCanUnblock() const { return IsBlockActive(); }
	void SetBlockActive(bool bActive);

	// Functions for counters by each species, split up to keep the code clearer. Uses the move we're currently doing to pick the correct counter for that species.
	virtual bool GetCanCounter_AlienCounter(const AVP_ServerEntity_Actor& xEnemy, const u_int uQueuedCounterType, Asura_Hash_ID& uMoveHash, u_int& uNewAttackType, bool& bMakeEnemyIntoPuppet, Asura_Hash_ID& uGrappleSetBlueprintParam) const;
	virtual bool GetCanCounter_HumanCounter(const AVP_ServerEntity_Actor& xEnemy, const u_int uQueuedCounterType, Asura_Hash_ID& uMoveHash, u_int& uNewAttackType, bool& bMakeEnemyIntoPuppet, Asura_Hash_ID& uGrappleSetBlueprintParam) const;
	virtual bool GetCanCounter_PredatorCounter(const AVP_ServerEntity_Actor& xEnemy, const u_int uQueuedCounterType, Asura_Hash_ID& uMoveHash, u_int& uNewAttackType, bool& bMakeEnemyIntoPuppet, Asura_Hash_ID& uGrappleSetBlueprintParam) const;

	virtual float GetAttackDamage() const;
	virtual Asura_Hash_ID GetAttackDamageType() const;
	virtual float GetAttackImpulseStrength() const;
	virtual void GetAttackDirection(const AVP_ServerEntity_PhysicalObject* pxTarget, Asura_Vector_3& xDirection) const;
	virtual int GetAttackBoneHit(Asura_Guid uDamagedGuid) const { return -1; } // Default to no specific bone hit.
	virtual float GetGrappleDamage() const;
	virtual bool IsAttackUnblockable() const { return false; }
	virtual bool GetAttackOnlyHitsCurrentTarget() const { return m_uCurrentAttackType != AVP_MELEEATTACK_TYPE_NORMAL; }
	virtual bool GetAttackIsRightHanded() const { return true; }

	// Grapple-specific gets.
	virtual Asura_Hash_ID GetAttackGrappleSetBlueprintParamID() const { return ASURA_HASH_ID_UNSET; }
	Asura_Hash_ID GetAttackBlueprintIDForGrapple(Asura_Guid uHitEntity) const;

	virtual bool GetSendIncomingMeleeMessage(u_int uAttackType) const;

	virtual void SendMeleeAttackHitMessage();
	virtual void SendMeleeFailedToStartMessage(u_int uAttackType);


	void SwitchQuickAttackSide() { m_eNextQuickAttackSide = (m_eNextQuickAttackSide == QUICK_ATTACK_LEFT) ? QUICK_ATTACK_RIGHT : QUICK_ATTACK_LEFT; }

	u_int GetPounceVariation() const;
	bool GetCanUseShortestPounceVariation(Asura_Guid uTarget) const;

	virtual const Asura_Blueprint* GetExtraMeleeDataBlueprint() const { return NULL; }
	virtual bool GetQuickAttackBaseParameterNames(const char*& szLeftAttackName, const char*& szRightAttackName) const { return false; }
	virtual Asura_Hash_ID GetPounceBlueprintType() const { return ASURA_HASH_ID_UNSET; }

	void InitialiseActiveGrapple();
	void ApplyActiveGrappleResult();

	static const Asura_LOS_OutputData* DoMeleeLOSTest(const Asura_LOS_InputData* pxInput);

	static Asura_Hash_ID GetGrappleBlueprintFromGrappleSetParameter(const AVP_ServerEntity_Actor* pxVictim, const Asura_Hash_ID uGrappleSetBlueprintParam);

	AVP_ServerActor_Vertebrate& m_xOwner;

	// Attack queue data.
	enum { MAX_QUEUED_MELEE_ATTACKS = 2, };
	struct QueuedMeleeAttack
	{
		u_int m_uType;
		Asura_Guid m_uTarget;
		bool m_bForceMiss;
		float m_fDmgMultiplier;
	};
	QueuedMeleeAttack m_axQueuedMeleeAttacks[MAX_QUEUED_MELEE_ATTACKS];
	u_int m_auQueuedMeleeAttacks[MAX_QUEUED_MELEE_ATTACKS];
	u_int m_uMeleeQueueSize;
	u_int m_uQueuedCounter;

	// Detection details.
	float m_fDamageRange; // This is shorter than the attack detection radius, so we can do a mini-charge at the enemy when we attack.
	float m_fDamageAIBackAttackFactor; // Factor to multiply the damage by if this is an AI attacking from behind.
	float m_fAttackDetectionRange; // This is shorter than the distance an attack can be countered at, so is not used as part of the target detection code.
	float m_fTargetDetectionRange;
	float m_fTargetDetectionCosAngle;
	float m_fTargetDetectionOriginBackwardsOffset;
	float m_fBlockDetectionCosAngle;
	float m_fBlockDamageProportion;

	float m_fMinorDamageAmount;
	float m_fModerateDamageAmount;
	float m_fMajorDamageAmount;
	float m_fMaxSuckDistance;

	Asura_Guid m_uCurrentTarget; // Many attacks will only try to hit this one target. This is also used for the target to detect counters.
	Asura_Guid m_uRequestedTarget; // Effectively this defines whether we should be searching for a target or not, although it could also be used to detect attempts to cheat the lock-on if the target is consistently a long way from what the player could plausibly see.
	// TODO - the requested target may need timestamping so it doesn't get wrongly overwritten by reordered messages.
	// Note that the requested target is no longer used for queued attacks, but is still required for counters to work properly.

	Asura_Vector_3 m_xStoredTargetPosition; // Slightly nastily, we have to use this for container targeting at present. Can be used for other attacks where the position is picked at the start though.

	// Details about the attack in progress.
	u_int m_uCurrentAttackType; // May need more details than this to pick damage and hit detection style, but it's a reasonable starting point.
	u_int m_uCurrentVariation; // It feels a little hacky to use this as a way to define additional details about the attack, as there's no specific list of variations for different moves. I'm yet to decide what a better solution is though, so until then...
	Asura_Hash_ID m_uCurrentGrappleBlueprint;
	bool m_bCurrentMoveForceMiss; // force a miss on the current attack
	float m_fCurrentDmgMultiplier; // force damage multiplier on the current attack - used for reduce damage when attacking from behind etc.
	enum { MAX_HIT_MELEE_ENTITIES = 8, };
	Asura_Guid m_auHitEntities[MAX_HIT_MELEE_ENTITIES]; // Built up to ensure a particular attack doesn't hit the same enemy twice. Reset the count below to allow them to be hit again.
	u_int m_uNumHitEntities;

	// Unified flags for any of the above sections
	union
	{
		u_int m_uFlags;
		struct
		{
			u_int	m_bBlockQueued								: 1,
					m_bUnblockQueued							: 1,
					m_bCurrentAttackShouldHit					: 1,
					m_bCurrentAttackCountered					: 1,
					m_bBlockActive								: 1,
					m_bCurrentAttackHasHitEnv					: 1,
					m_bCurrentAttackSentIncomingMeleeMessage	: 1,
					m_bAlwaysUpdateTarget						: 1,
					m_bActiveGrappleReceivedAttackerValue		: 1,
					m_bActiveGrappleReceivedVictimValue			: 1,
					m_bActiveGrappleVictimAdvantage				: 1, // The attacker always wins an equal result unless the attacker is a player and the victim is an AI.
					m_bActiveGrappleAttackerVictorious			: 1,
					m_bActiveGrappleCriticalVictory				: 1,
																: 0;
		};
	};

	float m_fActiveGrappleAttackerValue;
	float m_fActiveGrappleVictimValue;

	// Pounces are now shared between aliens and predators, so all set-up code has moved to the vertebrate level.
	struct PounceAttackData
	{
		float m_fMaxDistance;
		float m_fMinDrop;
		float m_fDamage;
		float m_fAttackImpulse;
	};

	float m_fMinPounceAttackDistance;
	u_int m_uNumPounceAttackVariations;
	PounceAttackData* m_pxPounceAttacks;

	// Ditto for quick attacks, although we need slightly different blueprint parameters between the two for this.
	u_int m_uNumLeftQuickAttackVariations;
	u_int m_uNumRightQuickAttackVariations;

	enum QuickAttackSides
	{
		QUICK_ATTACK_LEFT,
		QUICK_ATTACK_RIGHT,
	};
	QuickAttackSides m_eNextQuickAttackSide;

	static const char* s_szPounceAttackBaseParameterName;

private:
	void UpdateCurrentTarget();

	void SendIncomingMeleeStartedMessage(Asura_Guid uTarget);
	void SendIncomingMeleeFinishedMessage(Asura_Guid uTarget);

	enum { NUM_LOS_CLASSIFICATION_EXCEPTIONS = 3 };
	static const u_short s_ausLosClassificationExceptions[NUM_LOS_CLASSIFICATION_EXCEPTIONS];
};

// Still-abstract subclass for the pounce variations, so all classes that need some data can enumerate in the same way
class AVP_Blueprint_ParameterEnumerator_PounceVariations : public Asura_Blueprint_ParameterEnumerator
{
	typedef Asura_Blueprint_ParameterEnumerator PARENT;

public:
	AVP_Blueprint_ParameterEnumerator_PounceVariations(Asura_Hash_ID uPounceBlueprintType);
	virtual ~AVP_Blueprint_ParameterEnumerator_PounceVariations() {}

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

protected:
	enum { MAX_POUNCE_VARIATIONS = 8, };

	Asura_Hash_ID m_uPounceBlueprintType;

	u_int m_uNumVariations;
	Asura_Hash_ID m_auFirstPersonStartAnims[MAX_POUNCE_VARIATIONS];
	Asura_Hash_ID m_auFirstPersonHitAnims[MAX_POUNCE_VARIATIONS];
	Asura_Hash_ID m_auFirstPersonCounteredAnims[MAX_POUNCE_VARIATIONS];
	Asura_Hash_ID m_auFirstPersonMissAnims[MAX_POUNCE_VARIATIONS];
	Asura_Hash_ID m_auThirdPersonStartAnims[MAX_POUNCE_VARIATIONS];
	Asura_Hash_ID m_auThirdPersonHitAnims[MAX_POUNCE_VARIATIONS];
	Asura_Hash_ID m_auThirdPersonCounteredAnims[MAX_POUNCE_VARIATIONS];
	Asura_Hash_ID m_auThirdPersonMissAnims[MAX_POUNCE_VARIATIONS];
	float m_afMaxDistances[MAX_POUNCE_VARIATIONS];
	float m_afMinDrops[MAX_POUNCE_VARIATIONS];
	float m_afDamageValues[MAX_POUNCE_VARIATIONS];
	float m_afAttackImpulses[MAX_POUNCE_VARIATIONS];
};

class AVP_Blueprint_ParameterEnumerator_PounceVariations_MeleeCounterData : public AVP_Blueprint_ParameterEnumerator_PounceVariations
{
	typedef AVP_Blueprint_ParameterEnumerator_PounceVariations PARENT;

public:
	AVP_Blueprint_ParameterEnumerator_PounceVariations_MeleeCounterData(Asura_Hash_ID uPounceBlueprintType, AVP_Vertebrate_MeleeCounter_System& xMeleeCounterSystem);
	virtual ~AVP_Blueprint_ParameterEnumerator_PounceVariations_MeleeCounterData() {}

	virtual void EndEnumeration(const bool bSuccess);

protected:
	AVP_Vertebrate_MeleeCounter_System& m_xMeleeCounterSystem;
};

class AVP_Blueprint_ParameterEnumerator_PounceVariations_MoveData : public AVP_Blueprint_ParameterEnumerator_PounceVariations
{
	typedef AVP_Blueprint_ParameterEnumerator_PounceVariations PARENT;

public:
	AVP_Blueprint_ParameterEnumerator_PounceVariations_MoveData(Asura_Hash_ID uPounceBlueprintType,
																AVP_Move_Melee_Pounce_Attack_Start& xStartMove,
																AVP_Move_Melee_Pounce_Attack_Return_Hit& xHitMove,
																AVP_Move_Melee_Pounce_Attack_Return_Countered& xCounteredMove,
																AVP_Move_Melee_Pounce_Attack_Return_Miss& xMissMove);
	virtual ~AVP_Blueprint_ParameterEnumerator_PounceVariations_MoveData() {}

	virtual void EndEnumeration(const bool bSuccess);

protected:
	AVP_Move_Melee_Pounce_Attack_Start& m_xStartMove;
	AVP_Move_Melee_Pounce_Attack_Return_Hit& m_xHitMove;
	AVP_Move_Melee_Pounce_Attack_Return_Countered& m_xCounteredMove;
	AVP_Move_Melee_Pounce_Attack_Return_Miss& m_xMissMove;
};

class AVP_Blueprint_ParameterEnumerator_PounceVariations_FocusData : public AVP_Blueprint_ParameterEnumerator_PounceVariations
{
	typedef AVP_Blueprint_ParameterEnumerator_PounceVariations PARENT;

public:
	AVP_Blueprint_ParameterEnumerator_PounceVariations_FocusData(Asura_Hash_ID uPounceBlueprintType, AVP_Vertebrate_Focus& xFocus);
	virtual ~AVP_Blueprint_ParameterEnumerator_PounceVariations_FocusData() {}

	virtual void EndEnumeration(const bool bSuccess);

protected:
	AVP_Vertebrate_Focus& m_xFocus;
};

// Enumerator for picking a grapple variation. Using the brand new by-hash enumerator.
class AVP_Blueprint_ParameterEnumerator_GrappleSelector : public Asura_Blueprint_ParameterEnumerator_ByHash
{
	typedef Asura_Blueprint_ParameterEnumerator_ByHash PARENT;

public:
	AVP_Blueprint_ParameterEnumerator_GrappleSelector();
	virtual ~AVP_Blueprint_ParameterEnumerator_GrappleSelector() {}

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

	Asura_Hash_ID GetSelectedGrappleBlueprint() const;

protected:
	enum { MAX_GRAPPLE_VARIATIONS = 8, };

	u_int m_uNumVariations;
	Asura_Hash_ID m_auGrappleBlueprints[MAX_GRAPPLE_VARIATIONS];
	u_int m_uSelectedVariation;
};


#endif //__AVP_VERTEBRATE_MELEECOUNTER_SYSTEM_H__

/// End of file. ///
