//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Alien_Attack2.cpp
//
// Copyright (C) 2008 Rebellion. All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

// core
#include "Core/Asura.h"

// header
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Attack2.h"

// asura
#include "Entity/PhysicalObject/Moves/Asura_Moves.h"
#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Debugging/Asura_DebugLineRender.h"
#include "Rendering/Asura_Font_System.h"
#endif

// avp
#include "Animation/AVP_AnimFlags.h"
#include "Actor/AVP_Actor_AIAnimStateMachine.h"
#include "Actor/Vertebrates/Alien/AVP_Alien_MeleeCounter_System.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Controller/AVP_ServerEntity_Squad_Alien.h"
#include "Entity/Controller/AVP_ServerEntity_Team_Alien.h"
#include "Entity/Damage/AVP_EntityDamage_Request.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_Vertebrate_Messages.h"

// axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Wander.h"
#include "Core/AVP_Navigation_Types.h"
#include "Core/AVP_Waypoint_Manager.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Alien.h"
#include "Knowledge/AVP_Knowledge_System_AlienTeam.h"

DEBUGVAR float dbg_fHysteresisDistance = 3.0f;
DEBUGVAR float dbg_fSprintHysteresisDistance = 8.0f;
DEBUGVAR float dbg_fChargeDistance = 12.0f;
DEBUGVAR float dbg_fMinRoamDistance = 5.0f;
DEBUGVAR float dbg_fReplanStep = 15.0f;

DEBUGVAR float dbg_fMinAttackRangeFakeJump = 2.0f;// must be smaller then any attack range
DEBUGVAR float dbg_fMaxAttackRangeFakeJump = 7.0f;
DEBUGVAR float dbg_fMinAttackRangeJump = 2.0f;
DEBUGVAR float dbg_fMaxAttackRangeJump = 7.0f;

DEBUGVAR float dbg_fMinAtackRangeStand = 3.0f;
DEBUGVAR float dbg_fMinAtackLongRangeStandSq = 4.5f * 4.5f;
DEBUGVAR float dbg_fMaxAtackRangeStand = 5.5f;
DEBUGVAR float dbg_fMinAtackLoopRange = 2.0f;
DEBUGVAR float dbg_fMaxAtackLoopRange = 4.0f;

DEBUGVAR float dbg_fMaxAttackRangePounceGrapple = 7.0f;
DEBUGVAR float dbg_fMinAttackRangePounceGrapple = 4.0f;

DEBUGVAR float dbg_fMaxAttackRange = 7.0f; // must be larger then any attack range

DEBUGVAR float dbg_fIgnoreTargetTimeMin = 1.0f;
DEBUGVAR float dbg_fIgnoreTargetTimeMax = 4.0f;

DEBUGVAR float dbg_fMinMoveoutRangeBeforeStopSq = 5.0f * 5.0f;
DEBUGVAR float dbg_fStabilizationDistance = 1.5f;
DEBUGVAR float dbg_fMinAnyRealAttakRange = Asura_Maths::Min(dbg_fMinAttackRangeJump, Asura_Maths::Min(dbg_fMinAtackRangeStand, dbg_fMinAttackRangePounceGrapple )) + dbg_fStabilizationDistance;

//////////////////////////////////////////////////////////////////////////////
//
// AVP_AwayFromTargetFunctor - used to find a point away from a target not for actual moving
//
//////////////////////////////////////////////////////////////////////////////
class AVP_AwayFromTargetInDirectionFunctor : public AVP_Navigation::AStarFunctorBase
{
public:
	virtual float EstimateCostToGoal( const int iWaypointIndex );
	virtual float EstimateTraversalCost( const int iWaypointIndex, const int iLinkIndex, const u_int uPathfinderFlags );
	virtual bool IsGoalAchieved( const int iWaypointIndex );
	virtual bool LinkFlagsTest( const Asura_AI_NavConnection* pxLink, const u_int uPathfinderFlags );

	AVP_AwayFromTargetInDirectionFunctor( const Asura_Vector_3& xCenter, const float fDistance, const Asura_Vector_3& xDirection, u_int uPathfinderFlags  ) 
	: m_xCenter( xCenter )
	, m_fDistance( fDistance )
	, m_xDirection( xDirection )
	, m_bPreferFloor(false)
	, m_uPathfinderFlags(uPathfinderFlags)
	{}

	void PreferFloor() { m_bPreferFloor = true; }

protected:
	Asura_Vector_3 m_xCenter;
	float m_fDistance;
	Asura_Vector_3 m_xDirection;		  
	bool m_bPreferFloor;
	u_int m_uPathfinderFlags;
	
};

float AVP_AwayFromTargetInDirectionFunctor::EstimateCostToGoal( const int iWaypointIndex )
{
	const Asura_AI_NavVertex* pxWP = Asura_AI_Navigation_System::GetWaypoint( iWaypointIndex );

	Asura_Vector_3 xToWP = pxWP->GetPosition() - m_xCenter;
	if (xToWP.ApproximatelyEquals(Asura_Maths::ZeroVector3))
	{
		return 2.0f;
	}

	xToWP.Normalise();

	float fDirectionCost =  (1.0f - xToWP * m_xDirection);


	float fMismatchedAgilityCost;
	if (m_bPreferFloor)
	{
		if (pxWP->IsCeilingWaypoint() || pxWP->IsWallWaypoint())
		{
			fMismatchedAgilityCost = 0.5f;
		}
		else
		{
			fMismatchedAgilityCost = 0.0f;
		}
	}
	else
	{

		fMismatchedAgilityCost = AVP_Navigation::AlienCalculateCostMultiplierDueToMobility(pxWP, m_uPathfinderFlags, false) * 0.5f;
	}

	return fDirectionCost + fMismatchedAgilityCost; 
}

float AVP_AwayFromTargetInDirectionFunctor::EstimateTraversalCost( const int iWaypointIndex, const int iLinkIndex, const u_int uPathfinderFlags )
{
	const Asura_AI_NavVertex* pxFromWP = Asura_AI_Navigation_System::GetWaypoint( iWaypointIndex );
	const Asura_AI_NavVertex* pxToWP   = Asura_AI_Navigation_System::GetWaypoint( pxFromWP->m_pxConnections[ iLinkIndex ].m_usIndex );

	if (pxToWP->GetFlags() & AVP_Navigation::AVP_AI_NAVVERTEX_FLAG_GS2 ) // GS2 are not valid candidate
	{
		return -1.0f;
	}

	return 0.0f; 
}

bool AVP_AwayFromTargetInDirectionFunctor::IsGoalAchieved( const int iWaypointIndex )
{
	const Asura_AI_NavVertex* pxWP = Asura_AI_Navigation_System::GetWaypoint( iWaypointIndex );

	float fDistanceSq = (pxWP->GetPosition() - m_xCenter).MagnitudeSquared();

	return ( fDistanceSq > m_fDistance * m_fDistance);
}
bool AVP_AwayFromTargetInDirectionFunctor::LinkFlagsTest( const Asura_AI_NavConnection* pxLink, const u_int uPathfinderFlags )
{
	return AVP_Navigation::AlienLinkFlagsTest( pxLink, uPathfinderFlags );
}

//////////////////////////////////////////////////////////////////////////////
//
// AVP_CloseInFunctor
//
//////////////////////////////////////////////////////////////////////////////
class AVP_CloseInFunctor : public AVP_Navigation::AlienAStarFunctor
{
public:
	virtual float EstimateTraversalCost( const int iWaypointIndex, const int iLinkIndex, const u_int uPathfinderFlags );

	AVP_CloseInFunctor(const Asura_Vector_3& xTargetPosition) 
		: PARENT()
		, m_xTargetPosition(xTargetPosition)
	{}

protected:
	Asura_Vector_3 m_xTargetPosition;

	typedef AVP_Navigation::AlienAStarFunctor PARENT;

};


DEBUGVAR float dbg_fTransitionLinkCostFactor = 10.0f;
DEBUGVAR float dbg_fMismatchedMobilityAdditionalCostFactor   = 2.0;


float AVP_CloseInFunctor::EstimateTraversalCost( const int iWaypointIndex, const int iLinkIndex, const u_int uPathfinderFlags )
{
	const float fBaseCost = Asura_AI_Navigation_System::DefaultTraversalCostFunction( iWaypointIndex, iLinkIndex, uPathfinderFlags, NULL );

	const Asura_AI_NavVertex* pxFromWP = Asura_AI_Navigation_System::GetWaypoint( iWaypointIndex );
	const Asura_AI_NavVertex* pxToWP   = Asura_AI_Navigation_System::GetWaypoint( pxFromWP->m_pxConnections[ iLinkIndex ].m_usIndex );

	// Add a cost based on the waypoint type and the pathfinder's mobility (high mobility guys should try and stick to ceilings, etc).
	// If this guy is high mobility then add a cost if either of the waypoints is not ceiling.
	// If this guy is med mobility then add a cost if either of the waypoints is not wall.
	// No need to add a cost for low mobility guys as links to wall and ceiling should have been ignored.
	bool bForceLowMobility = m_eMovementLimitation == LIMIT_LOW_MOBILITY && (pxToWP->GetPosition() - m_xStartPosition).MagnitudeSquared() < m_fDistanceSq;
	float fMismatchedAgilityCost = fBaseCost * AVP_Navigation::AlienCalculateCostMultiplierDueToMobility(pxToWP, uPathfinderFlags, bForceLowMobility) * dbg_fMismatchedMobilityAdditionalCostFactor;;

	// Add a cost for transition links to stop unnecessary hopping on and off walls, etc.
	float fTransitionCost = fBaseCost * dbg_fTransitionLinkCostFactor;

	const u_short usLinkFlags = Asura_AI_Navigation_System::GetWaypoint( iWaypointIndex )->m_pxConnections[ iLinkIndex ].m_usFlags;
	if ( AVP_Navigation::IsTransitionLink( usLinkFlags ) )
	{
		if ( m_eMovementLimitation == LIMIT_AVOID_JUMPING && (pxToWP->GetPosition() - m_xStartPosition).MagnitudeSquared() < m_fDistanceSq )
		{
			fTransitionCost = fBaseCost * dbg_fTransitionLinkCostFactor * 100.0f;
		}
		else if (AVP_Navigation::IsEnvHookLink((static_cast<u_short>(usLinkFlags))))
		{
			fTransitionCost = 0.0f;
		}
		else
		{
			fTransitionCost = fBaseCost * dbg_fTransitionLinkCostFactor;
		}
	}

	float fPrevLuminace = AVP_Waypoint_Manager::GetWPLuminance( iWaypointIndex );
	float fLuminace = AVP_Waypoint_Manager::GetWPLuminance( pxFromWP->m_pxConnections[ iLinkIndex ].m_usIndex );
	float fLuminaceFactor = 1.0f;

	if (fLuminace < AVP_Waypoint_Manager::GetDefaultMaxHideLuminance()) // moving into shadow
	{
		if (fPrevLuminace < AVP_Waypoint_Manager::GetDefaultMaxHideLuminance()) // was already in shadow
		{
			fLuminaceFactor = fPrevLuminace > fLuminace ? 0.5f : 1.0f;
		}
		else // moving away from the light
		{
			fLuminaceFactor = 0.25f;
		}
	}
	else // moving into the light
	{
		if (fPrevLuminace < AVP_Waypoint_Manager::GetDefaultMaxHideLuminance()) // was already in shadow
		{
			fLuminaceFactor = 5.0f;
		}
		else // was already in the light 
		{
			fLuminaceFactor = fPrevLuminace > fLuminace ? 2.0f : 4.0f;
		}

	}

	float fVentFactor = 1.0f;
	if (AVP_Navigation::IsGS2Link(static_cast<u_short>(usLinkFlags)))
	{
		fVentFactor = 0.1f;
		fLuminaceFactor = 1.0f;
	}


	return fVentFactor * fLuminaceFactor * ( fBaseCost + fMismatchedAgilityCost + fTransitionCost );
}



//////////////////////////////////////////////////////////////////////////////
//
// AVP_ChargeFunctor
//
//////////////////////////////////////////////////////////////////////////////
class AVP_ChargeFunctor : public AVP_Navigation::AlienAStarFunctor
{
public:
	virtual float EstimateTraversalCost( const int iWaypointIndex, const int iLinkIndex, const u_int uPathfinderFlags );

	AVP_ChargeFunctor( const Asura_Vector_3& xTarget, float fAttackDistance ) 
		: m_xTargetPosition( xTarget )
		, m_fAttackDistance(fAttackDistance)
		, m_bPreferFloor(false)
		, m_bPreferAttackToJump(false)
	{}

	void PreferFloor() { m_bPreferFloor = true; }
	void PreferAttackToJump() { m_bPreferAttackToJump = true; }

protected:
	Asura_Vector_3 m_xTargetPosition;
	float m_fAttackDistance;
	bool m_bPreferFloor;
	bool m_bPreferAttackToJump;

	typedef AVP_Navigation::AlienAStarFunctor PARENT;

};

DEBUGVAR float dbg_fMismatchedLightingAdditionalCostFactor	 = 0.0f;
DEBUGVAR bool dbg_fUseJumpEnvHooksAtCloseRange = true;

float AVP_ChargeFunctor::EstimateTraversalCost( const int iWaypointIndex, const int iLinkIndex, const u_int uPathfinderFlags )
{
	const Asura_AI_NavVertex* pxFromWP = Asura_AI_Navigation_System::GetWaypoint( iWaypointIndex );
	const Asura_AI_NavVertex* pxToWP   = Asura_AI_Navigation_System::GetWaypoint( pxFromWP->m_pxConnections[ iLinkIndex ].m_usIndex );

	const float fBaseCost = Asura_AI_Navigation_System::DefaultTraversalCostFunction( iWaypointIndex, iLinkIndex, uPathfinderFlags, NULL );

	float fMismatchedAgilityCost = 0;
	bool bInsistOnFloor = m_bPreferFloor && ((pxToWP->GetPosition() - m_xTargetPosition).MagnitudeSquared() < m_fAttackDistance * m_fAttackDistance);
	bool bInsistNoJump = m_bPreferAttackToJump && ((pxToWP->GetPosition() - m_xTargetPosition).MagnitudeSquared() < m_fAttackDistance * dbg_fMinAnyRealAttakRange);
	if (bInsistOnFloor) 
	{
		if (pxToWP->IsCeilingWaypoint() || pxToWP->IsWallWaypoint())
		{
			fMismatchedAgilityCost = fBaseCost * dbg_fMismatchedMobilityAdditionalCostFactor;
		}
	}
	else
	{
		bool bForceLowMobility = m_eMovementLimitation == LIMIT_LOW_MOBILITY && (pxToWP->GetPosition() - m_xStartPosition).MagnitudeSquared() < m_fDistanceSq;
		fMismatchedAgilityCost = fBaseCost * AVP_Navigation::AlienCalculateCostMultiplierDueToMobility(pxToWP, uPathfinderFlags, bForceLowMobility) * dbg_fMismatchedMobilityAdditionalCostFactor;
	}


	// Add a cost for transition links to stop unnecessary hopping on and off walls, etc.
	float fTransitionCost = fBaseCost * dbg_fTransitionLinkCostFactor;
	const u_short usLinkFlags = Asura_AI_Navigation_System::GetWaypoint( iWaypointIndex )->m_pxConnections[ iLinkIndex ].m_usFlags;

	if ( AVP_Navigation::IsTransitionLink( usLinkFlags ) )
	{

		if ( m_eMovementLimitation == LIMIT_AVOID_JUMPING && (pxToWP->GetPosition() - m_xStartPosition).MagnitudeSquared() < m_fDistanceSq )
		{
			fTransitionCost = fBaseCost * dbg_fTransitionLinkCostFactor * 100.0f;
		}
		else if (bInsistNoJump)
		{
			fTransitionCost = fBaseCost * dbg_fTransitionLinkCostFactor * 100.0f;
		}
		else if (AVP_Navigation::IsEnvHookLink((static_cast<u_short>(usLinkFlags))))
		{
			fTransitionCost = 0.0f;
		}
		else
		{
			fTransitionCost = fBaseCost * dbg_fTransitionLinkCostFactor;
		}
	}

	return ( fBaseCost + fMismatchedAgilityCost + fTransitionCost );
}

//////////////////////////////////////////////////////////////////////////////
//
// AVP_MoveAvoidingTargetFunctor
//
//////////////////////////////////////////////////////////////////////////////
DEBUGVAR float dbg_fCloseMultiplier = 100.0f;
DEBUGVAR float dbg_fTransitionMultiplier = 0.5f;

class AVP_MoveAvoidingTargetFunctor : public AVP_Navigation::AlienAStarFunctor 
{
public:

	virtual float EstimateTraversalCost( const int iWaypointIndex, const int iLinkIndex, const u_int uPathfinderFlags );
	virtual bool LinkFlagsTest( const Asura_AI_NavConnection* pxLink, const u_int uPathfinderFlags );

	AVP_MoveAvoidingTargetFunctor(const Asura_Vector_3& xCenter, float fMinDistance) 
	: AVP_Navigation::AlienAStarFunctor() 
	, m_xCenter(xCenter)
	, m_fMinDistanceSq(fMinDistance * fMinDistance)
	, m_bPreferFloor(false)
	{}

	void PreferFloor() { m_bPreferFloor = true; }

private:
	Asura_Vector_3 m_xCenter;
	float m_fMinDistanceSq;
	bool m_bPreferFloor;
};

float AVP_MoveAvoidingTargetFunctor::EstimateTraversalCost( const int iWaypointIndex, const int iLinkIndex, const u_int uPathfinderFlags )
{
	const float fBaseCost = Asura_AI_Navigation_System::DefaultTraversalCostFunction( iWaypointIndex, iLinkIndex, uPathfinderFlags, NULL );

	const Asura_AI_NavVertex* pxFromWP = Asura_AI_Navigation_System::GetWaypoint( iWaypointIndex );
	const Asura_AI_NavVertex* pxToWP   = Asura_AI_Navigation_System::GetWaypoint( pxFromWP->m_pxConnections[ iLinkIndex ].m_usIndex );

	float fMismatchedAgilityCost = 0;
	if (m_bPreferFloor)
	{
		if ((pxToWP->GetPosition() - m_xCenter).MagnitudeSquared() < (dbg_fMaxAttackRangeJump + 2.0f) * (dbg_fMaxAttackRangeJump + 2.0f) ) 
		{
			if (pxToWP->IsCeilingWaypoint() || pxToWP->IsWallWaypoint())
			{
				fMismatchedAgilityCost = fBaseCost * dbg_fMismatchedMobilityAdditionalCostFactor;
			}			
		}
		else
		{
			bool bForceLowMobility = m_eMovementLimitation == LIMIT_LOW_MOBILITY && (pxToWP->GetPosition() - m_xStartPosition).MagnitudeSquared() < m_fDistanceSq;
			fMismatchedAgilityCost = fBaseCost * AVP_Navigation::AlienCalculateCostMultiplierDueToMobility(pxToWP, uPathfinderFlags, bForceLowMobility) * dbg_fMismatchedMobilityAdditionalCostFactor;
		}
	}
	else
	{
		bool bForceLowMobility = m_eMovementLimitation == LIMIT_LOW_MOBILITY && (pxToWP->GetPosition() - m_xStartPosition).MagnitudeSquared() < m_fDistanceSq;
		fMismatchedAgilityCost = fBaseCost * AVP_Navigation::AlienCalculateCostMultiplierDueToMobility(pxToWP, uPathfinderFlags, bForceLowMobility) * dbg_fMismatchedMobilityAdditionalCostFactor;
	}

	// Add a cost for transition links to stop unnecessary hopping on and off walls, etc.
	float fTransitionCost = 0.0f;
	const u_short usLinkFlags = Asura_AI_Navigation_System::GetWaypoint( iWaypointIndex )->m_pxConnections[ iLinkIndex ].m_usFlags;
	if ( AVP_Navigation::IsTransitionLink( usLinkFlags ) )
	{
		fTransitionCost = fBaseCost * dbg_fTransitionMultiplier;

		if ( m_eMovementLimitation == LIMIT_AVOID_JUMPING && (pxToWP->GetPosition() - m_xStartPosition).MagnitudeSquared() < m_fDistanceSq )
		{
			fTransitionCost *= 8.0f;
		}
	}

	float fCloseToTargetCost = 0.0f;

	Asura_Vector_3 xNearestPoint;
	Asura_Maths::GetNearestPointOnLineSegment(pxFromWP->GetPosition(), pxToWP->GetPosition(), m_xCenter, xNearestPoint);
	float fDistanceSq = (m_xCenter - xNearestPoint).MagnitudeSquared();

	if (  fDistanceSq < m_fMinDistanceSq ) 
	{
		fCloseToTargetCost = fBaseCost * dbg_fCloseMultiplier * (m_fMinDistanceSq - fDistanceSq)/m_fMinDistanceSq;
	}

	return ( fBaseCost + fMismatchedAgilityCost + fTransitionCost + fCloseToTargetCost);

}

bool AVP_MoveAvoidingTargetFunctor::LinkFlagsTest( const Asura_AI_NavConnection* pxLink, const u_int uPathfinderFlags )
{
	return AVP_Navigation::AlienLinkFlagsTest( pxLink, uPathfinderFlags );
}


//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Alien_Attack2
//
//////////////////////////////////////////////////////////////////////////////

AVP_Behaviour_Alien_Attack2::AVP_Behaviour_Alien_Attack2( AVP_Brain_Actor_Alien* pxBrain )
: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_ALIEN_ATTACK2 )
, m_fTargetPositionAtPlan( Asura_Maths::ZeroVector3 )
, m_fTargetHeadingAtPlan( Asura_Maths::ZeroVector3 )
, m_fMinDistaneToReplan(0.0f)
, m_bAttackLockedByUs(false)
, m_eCurrentAttackType(ATTACK_TYPE_UNDECIDED)
, m_ePreferedAttackType(ATTACK_TYPE_UNDECIDED)
, m_bLeftStandAttack(false)
, m_bLastAttackHit(false)
, m_eStandMeleeMode(STAND_MELEE_TO_STAND)
, m_uNumberOfStandAttacksLeft(0)
, m_xDelayedMsgList()
, m_bStopAttack(false)
, m_fTimeSinceLastChoosingTarget(0.0f)
, m_bSprintOn(false)
, m_fSprintTimer(1000.0f)
, m_fSprintTestTimer(0.0f)
, m_bTargetIsPlayerAndDidntSeenMe(true)
{
	SetCurrentState(UNSET);
}

void AVP_Behaviour_Alien_Attack2::StopAfterAttack()
{
	m_bStopAttack = true;
}

bool AVP_Behaviour_Alien_Attack2::IsAttacking() const
{
	return GetCurrentState() != RUN_OUT;
}


bool AVP_Behaviour_Alien_Attack2::Activate()
{
	return PARENT::Activate();
}

void AVP_Behaviour_Alien_Attack2::Deactivate()
{
	SwitchToState( UNSET );
	PARENT::Deactivate();
}

DEBUGVAR float dbg_fDistanceToBiteSquared = 3.0f * 3.0f; 
DEBUGVAR float dbg_fWaitForAttack2Attack = 2.0f;
DEBUGVAR float dbg_fMaxTimeBeforeSprint = 2.0f;

void AVP_Behaviour_Alien_Attack2::PerFrameUpdate( const float fTimeslice )
{

	PARENT::PerFrameUpdate( fTimeslice );

	if (fTimeslice == 0.0f)
	{
		return;
	}

	UpdateStateTimer(fTimeslice);
	m_fTimeSinceLastChoosingTarget += fTimeslice;

	if (GetAlienBrain()->CanUpdateMelee())
	{
		LateHandleMessage(); // all the message regard the melee changes

		switch (GetCurrentState())
		{
		case UNSET:
		case RUN_IN:
		case CHARGING:
		case RUN_OUT:
		case FINISHED:
		case WANDER:
			break;
		case COUNTERED:
		case JUMP_ATTACK:
		case FAKE_JUMP_ATTACK:
		case POUNCE_GRAPPLE:
// 			if (GetAlienBrain()->IsInjured()) -- not needed this is resolved after the move is complete
// 			{
// 				//GetAlienBrain()->GetClientActor()->GetMovementSystem()->AbortCurrentMove(false);
//				SetInError(true);
//				SwitchToState(FINISHED);
//			}
// 			else if (GetStateTimer() > 5.0f) // really really shouldn't happen but it does 
// 			{
// 				GetAlienBrain()->GetClientActor()->GetMovementSystem()->AbortCurrentMove(false);
// 				SetInError(true);
// 				SwitchToState(FINISHED);
// 			}

			break;
		case STAND_ATTACK:
			UpdateStandAttack();
			break;
		default:
			Asura_Assert(false, "Unexpected state");
			break;
		}

	}


	if (!GetAlienBrain()->CanPlanMove())
	{
		switch (GetCurrentState())
		{
		case UNSET:
		case WANDER:
			break;
		case RUN_IN:
		case CHARGING:
			{
				const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
				if (pxTargetKnowledge)
				{
					float fDeltaTargetSq = (pxTargetKnowledge->m_xPosition - m_fTargetPositionAtPlan).MagnitudeSquared();

					if(GetAlienBrain()->ShouldRefreshPath(fDeltaTargetSq))
					{
						GetAlienBrain()->GetMovement().Abort();
						return;
					}
				}
			}
			break;
		case JUMP_ATTACK:
		case FAKE_JUMP_ATTACK:
		case POUNCE_GRAPPLE:
		case STAND_ATTACK:
		case RUN_OUT:
		case COUNTERED:
		case FINISHED:
			break;
		default:
			Asura_Assert(false, "Unexpected state");
			break;
		}

		return;
	}

	switch (GetCurrentState())
	{
	case UNSET:
		{
			bool bDummy;
			const AVP_AlienTeam_Knowledge* pxBestTarget = ChooseNewTarget(false, false, bDummy);
			if ( pxBestTarget )
			{
				GetAlienBrain()->StartedTargeting( pxBestTarget->GetGuid() );
				SwitchToState( RUN_IN );
			}
			else if (m_bStopAttack)
			{
				SwitchToState( FINISHED );
			}
			else
			{
				SwitchToState( WANDER );
			} 
		}
		break;
	case WANDER:
		UpdateWander();
		break;
	case RUN_IN:
		UpdateRunIn(fTimeslice);
		break;
	case CHARGING:
		UpdateCharge();
		break;
	case JUMP_ATTACK:
	case FAKE_JUMP_ATTACK:
	case POUNCE_GRAPPLE:
		break;
	case STAND_ATTACK:
		break;
	case RUN_OUT:
		UpdateRunOut();
		break;
	case COUNTERED:
	case FINISHED:
		break;
	default:
		Asura_Assert(false, "Unexpected state");
		break;
	}
}

bool AVP_Behaviour_Alien_Attack2::HandleMessage( const Asura_Message* pxMessage )
{
	switch ( GetCurrentState() )
	{
	case JUMP_ATTACK:
	case FAKE_JUMP_ATTACK:
	case STAND_ATTACK:
	case POUNCE_GRAPPLE:
	case COUNTERED:
		if ( pxMessage->GetMsgID() == ASURA_MSG_MOVECHAINCOMPLETE )
		{
			GetAlienBrain()->SetMelee(false);
			m_xDelayedMsgList.Push_Back(ASURA_MSG_MOVECHAINCOMPLETE);
			return true;
		}
		else if ( pxMessage->GetMsgID() == AVP_MSGID_COUNTER_MELEE )
		{
			m_xDelayedMsgList.Push_Back(AVP_MSGID_COUNTER_MELEE);
			return true;
		}
		else if ( ( pxMessage->GetMsgID() == AVP_MSGID_MELEE_ATTACK_HIT ) || ( pxMessage->GetMsgID() == AVP_MSGID_MELEE_ATTACK_PREPAREGRAPPLE ) )
		{
			m_bLastAttackHit = true;
			return true;
		}
		else if ( pxMessage->GetMsgID() == AVP_MSGID_MELEE_ATTACK_FAILED_TO_START )
		{
			GetAlienBrain()->SetMelee(false);
			m_xDelayedMsgList.Push_Back(AVP_MSGID_MELEE_ATTACK_FAILED_TO_START);
			return true;
		}

		break;
	default:
		break;
	}

	return PARENT::HandleMessage( pxMessage );
}

#ifndef ASURA_NO_DEBUG_OUTPUT

static const char* s_aszStateStrings[] =
{
	"UNSET",
	"RUN_IN",
	"CHARGING",
	"JUMP_ATTACK",
	"FAKE_JUMP_ATTACK",
	"STAND_ATTACK",
	"POUNCE_GRAPPLE",
	"COUNTERED",
	"RUN_OUT",
	"WANDER",
	"FINISH",
};

void AVP_Behaviour_Alien_Attack2::RenderDebugInfo()
{
	PARENT::RenderDebugInfo();	

	Asura_Font_System::Print( GetProjectBrain()->GetDebugOutputPos().x, GetProjectBrain()->GetDebugOutputPos().y, 0.5f, 0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "Attack2: %s %s", s_aszStateStrings[ GetCurrentState() ],
		(m_bAttackLockedByUs ? "LOCKED" : " ") );
	GetProjectBrain()->GetDebugOutputPos().y += 10.0f;
	

}
#endif

AVP_Brain_Actor_Alien* AVP_Behaviour_Alien_Attack2::GetAlienBrain() const
{
	return static_cast< AVP_Brain_Actor_Alien* >( GetBrain() );
}

void AVP_Behaviour_Alien_Attack2::SwitchToState( const State eState )
{

	int eLastState = GetCurrentState();
	switch ( eLastState )
	{
	case CHARGING:
		GetAlienBrain()->SetNearbyActorGuidToIgnore(ASURA_GUID_UNREGISTERED);
		GetAlienBrain()->StoppedChargingCurrentTarget();
		ChoosePreferedAttack(true);
		break;
	case POUNCE_GRAPPLE:
		if (eState != COUNTERED)
		{
			UnlockAttack();
		}
		break;
	case JUMP_ATTACK:
	case FAKE_JUMP_ATTACK:
		UnlockAttack(); // can already unlock 
		break;
	case STAND_ATTACK:
		if (eState != COUNTERED && eState != STAND_ATTACK)
		{
			UnlockAttack();
		}
		break;
	case COUNTERED:
		UnlockAttack();
		break;
	case UNSET:
		ChoosePreferedAttack(false);
		break;
	case RUN_IN:
	case RUN_OUT:
		break;
	case WANDER:
		DestroyChildWithType(AVP_BEHAVIOUR_TYPE_ALIEN_WANDER);
		ChoosePreferedAttack(false);
		break;
	case FINISHED:
		break;
	default:
		Asura_Assert(false, "Unexpected state");
		break;
	}

	SetCurrentState( eState );
	ResetStateTimer();
	switch ( GetCurrentState() )
	{
	case RUN_IN:
		m_bSprintOn = false;
		m_fSprintTimer = 0.0f;
		m_fSprintTestTimer = -1.0f;
		PlanPathIn(false);
		break;
	case CHARGING:
		m_bTargetIsPlayerAndDidntSeenMe = true;
		GetAlienBrain()->StartedChargingCurrentTarget();
		PlanCharge();
		break;
	case JUMP_ATTACK:
		GetAlienBrain()->SetMelee(true);
		RequestJumpAttack(false, IsReducedJumpAttackDamage());
		break;
	case POUNCE_GRAPPLE:
		GetAlienBrain()->SetMelee(true);
		RequestPounceGrapple();
		break;
	case FAKE_JUMP_ATTACK:
		GetAlienBrain()->SetMelee(true);
		RequestJumpAttack(true, true);
		break;
	case STAND_ATTACK:
		m_bLastAttackHit = false;
		if (eLastState != STAND_ATTACK)
		{
			GetAlienBrain()->SetMelee(true);
		}
		if (m_uNumberOfStandAttacksLeft > 0)
		{
			--m_uNumberOfStandAttacksLeft;
			RequestMeleeAttack();
		}
		else
		{
			Asura_Assert(false, "Unexpected m_uNumberOfStandAttacksLeft == 0" );
			SwitchToState(RUN_OUT);
		}
		break;
	case RUN_OUT:
		{
			if (m_bStopAttack)
			{
				PlanPathOut(eLastState == RUN_OUT, true);
			}
			else
			{
				bool bChargeImmediatly;
				const AVP_AlienTeam_Knowledge* pxBestTarget = ChooseNewTarget(true, true, bChargeImmediatly);
				if ( pxBestTarget && pxBestTarget->GetGuid() != GetAlienBrain()->GetCurrentTargetGuid() )
				{
					GetAlienBrain()->StartedTargeting( pxBestTarget->GetGuid() );
					if (bChargeImmediatly == true)
					{
						SwitchToState( CHARGING );
					}
					else
					{
						SwitchToState( RUN_IN );
					}
				}
				else
				{
					PlanPathOut(eLastState == RUN_OUT, eLastState != CHARGING);
				}
			}
		}
		break;
	case WANDER:
		PlanWander();
		break;
	case FINISHED:
		UnlockAttack();
		SetFinished(true);
		break;
	case COUNTERED:
		break;
	case UNSET:
		break;
	default:
		Asura_Assert(false, "Unexpected state");
		break;
	}
}


DEBUGVAR float dbg_fIgnorePriorityScore = -Asura_Maths::LargeFloat;
DEBUGVAR float dbg_fLowPriorityScore    = 0.5f;
DEBUGVAR float dbg_fNormalPriorityScore = 1.0f;
DEBUGVAR float dbg_fHighPriorityScore   = 2.0f;

DEBUGVAR float dbg_fAttackDistanceScore = 1.0f;
DEBUGVAR float dbg_fChargeDistanceScore = 0.75f;
DEBUGVAR float dbg_fLongDistanceScore = 0.0f;
DEBUGVAR float dbg_fTooCloseDistanceScore = 0.0f;
DEBUGVAR float dbg_fImmediateChargeScore = 2.0f;

DEBUGVAR float dbg_fStablisationScore = 0.5f;

DEBUGVAR float dbg_fTargetedScore = -1.5f;
DEBUGVAR float dbg_fTooManyTargetScore = -5.0f;
DEBUGVAR u_int dbg_uMaxTargeters = 2;
DEBUGVAR float dbg_fChargeCloseDirectionCos = Asura_Maths::Cosine(Asura_Maths::DegToRad(90.0f));
DEBUGVAR float dbg_fJumpCloseDirectionCos = Asura_Maths::Cosine(Asura_Maths::DegToRad(45.0f));
DEBUGVAR float dbg_fTimeBetweenChoosingNewTarget = 0.1f;
const AVP_AlienTeam_Knowledge* AVP_Behaviour_Alien_Attack2::ChooseNewTarget(bool bForceChooseNow, bool bTryToChargeImmediately, bool &bChargeImmediatly) const
{
	if (!bForceChooseNow && m_fTimeSinceLastChoosingTarget < dbg_fTimeBetweenChoosingNewTarget)
	{
		return NULL;
	}

	m_fTimeSinceLastChoosingTarget = 0.0f;

	bChargeImmediatly = false;
	const AVP_ServerEntity_Team_Alien* pxAlienTeam = GetAlienBrain()->GetAlienTeam();

	const AVP_AlienTeam_Knowledge* pxBestTarget = NULL;
	float fBestThreatValue = -Asura_Maths::LargeFloat;

	const Asura_Vector_3& xActorPosition = GetAlienBrain()->GetActor()->GetPosition();
	const Asura_Matrix_3x3& xActorOrientation = GetAlienBrain()->GetActor()->GetOrientation();
	const Asura_Vector_3 xActorForward(xActorOrientation.e13, xActorOrientation.e23, xActorOrientation.e33 );

	// Score all threats and pick the highest scoring.
	for ( Asura_Hashed_List_ConstIt< AVP_AlienTeam_Knowledge > xKnowIter( &pxAlienTeam->GetKnowledgeSystem()->GetAllKnowledge() );
		!xKnowIter.Done();
		xKnowIter.Next())
	{
		const AVP_AlienTeam_Knowledge* pxKnowledge = xKnowIter.GetCurrent();
		if ( !pxKnowledge )
		{
			continue;
		}

		if (GetAlienBrain()->ShouldIgnoreTarget( pxKnowledge->GetGuid() ))
		{
			continue;
		}

		const AVP_ServerEntity_Actor* pxTarget = AVP_ServerEntity_Actor::FindEntity( pxKnowledge->GetGuid() );
		if (!pxTarget || pxTarget->IsDead() || pxTarget->IsDestroyed())
		{
			continue;
		}

		switch (pxTarget->GetClassification()) // targets that are enemies but not attackable by alien AI
		{
		case AVP_ENTITYCLASS_SENTRYGUN: 
			continue;
		default:
			break;
		}


		bool bIsCurrentTarget = GetAlienBrain()->GetCurrentTargetGuid() == pxKnowledge->GetGuid();

		bool bIsPlayer = pxTarget->IsPlayer();

		float fThreatValue = 0.0f;

		// Score based on priority.
		int iTargetPriority;
		if ( !GetAlienBrain()->CalcTargetingPriority( pxKnowledge->m_iThreatClassification, pxKnowledge->GetGuid(), iTargetPriority ) )
		{
			iTargetPriority = AVP_AI_TargetPriorityData::NORMAL;
		}
 
		if (bIsPlayer && iTargetPriority == AVP_AI_TargetPriorityData::NORMAL)
		{
			iTargetPriority = AVP_AI_TargetPriorityData::HIGH;
		}

		if ( iTargetPriority == AVP_AI_TargetPriorityData::IGNORE_P )
		{
			continue;
		}

		float fPriorityFraction = dbg_fIgnorePriorityScore;
		switch (iTargetPriority)
		{
		default:
		case AVP_AI_TargetPriorityData::IGNORE_P:
			fPriorityFraction = dbg_fIgnorePriorityScore;
			break;
		case AVP_AI_TargetPriorityData::LOW:
			fPriorityFraction = dbg_fLowPriorityScore;
			break;
		case AVP_AI_TargetPriorityData::NORMAL:
			fPriorityFraction = dbg_fNormalPriorityScore;
			break;
		case AVP_AI_TargetPriorityData::HIGH:
			fPriorityFraction = dbg_fHighPriorityScore;
			break;
		}

		Asura_Vector_3 xTargetDirection = (pxKnowledge->m_xPosition - xActorPosition);
		const float fDistance = xTargetDirection.Normalise();

		float fDistanceFraction = 0.0f;
		bool bChargeImmediatlyForCurrentTarget = false;

		if (fDistance < dbg_fMinAttackRangeFakeJump) // too close 
		{
			fDistanceFraction = dbg_fTooCloseDistanceScore;
		}
		else if (fDistance < dbg_fMaxAttackRangeJump)
		{
			bool bInForwardDirection = xTargetDirection * xActorForward > dbg_fJumpCloseDirectionCos;
			if ( bTryToChargeImmediately && bInForwardDirection && GetAlienBrain()->CanCharge(pxKnowledge->GetGuid()))
			{
				fDistanceFraction = dbg_fAttackDistanceScore + dbg_fImmediateChargeScore;
				bChargeImmediatlyForCurrentTarget = true;
			}
			else
			{
				fDistanceFraction = dbg_fAttackDistanceScore;
			}
		}
		else if (fDistance < dbg_fChargeDistance)
		{
			bool bInForwardDirection = xTargetDirection * xActorForward > dbg_fChargeCloseDirectionCos;
			if ( bTryToChargeImmediately && bInForwardDirection && GetAlienBrain()->CanCharge(pxKnowledge->GetGuid()))
			{
				fDistanceFraction = dbg_fChargeDistanceScore + dbg_fImmediateChargeScore;
				bChargeImmediatlyForCurrentTarget = true;
			}
			else
			{
				fDistanceFraction = dbg_fChargeDistanceScore;
			}
		}
		else 
		{
			fDistanceFraction = dbg_fLongDistanceScore;
		}

		const float fThreatStabilisationFraction = bIsCurrentTarget ? dbg_fStablisationScore : 0.0f;
		
		u_int uNumberTargeters = bIsCurrentTarget ? pxKnowledge->m_uNumTargeters - 1 : pxKnowledge->m_uNumTargeters; // ignore us as targeters 



		const u_int uMaxTargeters = dbg_uMaxTargeters  * ( bIsPlayer ? 2 : 1 );
		const bool bTooManyTargeters = uNumberTargeters >= uMaxTargeters ;
		const float fTooManyTargetersFraction = bTooManyTargeters ? dbg_fTooManyTargetScore : 0.0f;

		const float fTargetersFraction = bTooManyTargeters ? 0.0f : uNumberTargeters * dbg_fTargetedScore / uMaxTargeters;


		fThreatValue = fPriorityFraction + fDistanceFraction + fThreatStabilisationFraction + fTargetersFraction + fTooManyTargetersFraction;

		// Is this a better target?
		if ( fThreatValue > fBestThreatValue )
		{
			fBestThreatValue = fThreatValue;
			pxBestTarget = pxKnowledge;
		}
	}

	return pxBestTarget;
}


DEBUGVAR float dbg_fMaxAngleFromTargetDirection = Asura_Maths::DegToRad(60.0f);
DEBUGVAR float dbg_fMaxAngleFromMyDirection = Asura_Maths::DegToRad(30.0f);


void AVP_Behaviour_Alien_Attack2::UpdateRunIn(float fTimeslice)
{
	const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
	if (!pxTargetKnowledge)
	{
		SwitchToState(UNSET);
		return;
	}

	AVP_Alien_Movement& xMovement = GetAlienBrain()->GetMovement();

	const Asura_Vector_3& xOurPosition = GetAlienBrain()->GetActor()->GetPosition();
	const Asura_Vector_3& xTargetPosition = pxTargetKnowledge->GetPosition();
	float fTargetDistanceSq = (xOurPosition-xTargetPosition).MagnitudeSquared();
	bool bCanStartSprint = fTargetDistanceSq > (dbg_fSprintHysteresisDistance + dbg_fChargeDistance) * (dbg_fSprintHysteresisDistance + dbg_fChargeDistance);


	if ( fTargetDistanceSq < (dbg_fHysteresisDistance + dbg_fChargeDistance) * (dbg_fHysteresisDistance + dbg_fChargeDistance))
	{
		if (GetAlienBrain()->CanChargeCurrentTarget())
		{
			SwitchToState(CHARGING);
		}
		else
		{
			SwitchToState(RUN_OUT);
		}
		return;
	}

	if (xMovement.IsInError() || xMovement.IsFinished())
	{
		PlanPathIn(true);
		UpdateSprint(fTimeslice, bCanStartSprint);
		return;
	}

	float fDeltaTargetSq = (xTargetPosition - m_fTargetPositionAtPlan).MagnitudeSquared();
	const Asura_Matrix_3x3& xTargetOrient = pxTargetKnowledge->GetOrientation();
	const Asura_Vector_3 xTargetForward(xTargetOrient.e13, xTargetOrient.e23, xTargetOrient.e33);
	float fDeltaHeadingCos = m_fTargetHeadingAtPlan * xTargetForward;


	float fDistanceToDestination = GetAlienBrain()->GetMovement().GetLengthLeftOnPath();

	if (fDistanceToDestination < m_fMinDistaneToReplan)
	{
		PlanPathIn(true);
		UpdateSprint(fTimeslice, bCanStartSprint);
		return;
	}

	if(GetAlienBrain()->ShouldRefreshPath(fDeltaTargetSq, fDeltaHeadingCos))
	{
		PlanPathIn(true);
		UpdateSprint(fTimeslice, bCanStartSprint);
		return;
	}

	UpdateSprint(fTimeslice, bCanStartSprint);

}

void AVP_Behaviour_Alien_Attack2::UpdateCharge()
{
	const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
	if (!pxTargetKnowledge)
	{
		SwitchToState(UNSET);
		return;
	}

	AVP_Alien_Movement& xMovement = GetAlienBrain()->GetMovement();

	const float fDistanceToTarget = GetAlienBrain()->GetCurrentTargetDistance();

	if (m_bTargetIsPlayerAndDidntSeenMe)
	{
		m_bTargetIsPlayerAndDidntSeenMe = TargetIsPlayerAndDidntSeeMe();
	}

	if (xMovement.IsInError())
	{
		PlanCharge();
		return;
	}

	if ( fDistanceToTarget < dbg_fMinAttackRangeFakeJump )
	{
		SwitchToState(RUN_OUT);
		return;
	}

	if ( fDistanceToTarget < dbg_fMaxAttackRange )
	{
		xMovement.ForceJumpOnWaypoint();
		if (m_ePreferedAttackType == ATTACK_TYPE_POUNCE_GRAPPLE)
		{
			if (!CanLockAttack(m_ePreferedAttackType)) 
			{
				if (fDistanceToTarget < dbg_fMinAttackRangePounceGrapple)
				{
					m_ePreferedAttackType = ATTACK_TYPE_STAND;
				}
			}
			else if (PounceGrappleAllowed())
			{
				LockAttack(m_ePreferedAttackType);
				SwitchToState(POUNCE_GRAPPLE);
				return;
			}
			else if (fDistanceToTarget < dbg_fMinAttackRangePounceGrapple)
			{
				m_ePreferedAttackType = ATTACK_TYPE_STAND;
			}
		}

		if (m_ePreferedAttackType == ATTACK_TYPE_STAND)
		{
			if (!CanLockAttack(m_ePreferedAttackType) )
			{
				if (fDistanceToTarget < dbg_fMinAtackRangeStand)
				{
					m_ePreferedAttackType = ATTACK_TYPE_JUMP;
				}
			}
			else if (!StandAttackWillBeAllowed()) // stand attack is not likely to be possible
			{
				m_ePreferedAttackType = ATTACK_TYPE_JUMP;
			}
			else if (StandAttackAllowed())
			{
				m_uNumberOfStandAttacksLeft = Asura_Random::GetInt(GetAlienBrain()->GetStandAttackMin(), GetAlienBrain()->GetStandAttackMax());
				LockAttack(m_ePreferedAttackType);
				m_eStandMeleeMode = STAND_MELEE_TO_STAND;
				SwitchToState(STAND_ATTACK);
				return;
			}
		}

		if (m_ePreferedAttackType == ATTACK_TYPE_JUMP)
		{
			if (!CanLockAttack(m_ePreferedAttackType)  )
			{		
				if (fDistanceToTarget < dbg_fMinAttackRangeJump && JumpAttackAllowed(true))
				{
					SwitchToState(FAKE_JUMP_ATTACK);
					return;
				}
			}
			else if (JumpAttackAllowed(false))
			{
				LockAttack(m_ePreferedAttackType);
				SwitchToState(JUMP_ATTACK);
				return;
			}
			else if (fDistanceToTarget < dbg_fMinAttackRangeJump && JumpAttackAllowed(true))
			{
				SwitchToState(FAKE_JUMP_ATTACK);
				return;
			}
		}
	}

	if (xMovement.IsFinished())
	{
		SwitchToState(RUN_OUT);
		return;
	}

	float fDeltaTargetSq = (pxTargetKnowledge->m_xPosition - m_fTargetPositionAtPlan).MagnitudeSquared();

	if(GetAlienBrain()->ShouldRefreshPath(fDeltaTargetSq))
	{
		PlanCharge();
		return;
	}
}


void AVP_Behaviour_Alien_Attack2::UpdateStandAttack()
{

	if (GetAlienBrain()->IsInjured()) // no need to do anything special - just dont allow to start a continuation melee move
	{
//		GetAlienBrain()->GetClientActor()->GetMovementSystem()->AbortCurrentMove(false); 
//		SetInError(true);
//		SwitchToState(FINISHED);
 		return;
	}


	if (m_uNumberOfStandAttacksLeft == 0 || !GetAlienBrain()->GetCurrentTargetKnowledge())
	{
		return;
	}

	const Asura_Move* pxMove = GetAlienBrain()->GetClientActor()->GetMovementSystem()->GetCurrentMove();
	const u_int uMoveAnimFlags = pxMove ? pxMove->GetAnimFlagsForLastFrame() : 0;
	if ( ( uMoveAnimFlags & AVP_ANIMFLAG_ALLOW_NEW_MELEE ) != 0 ) 
	{
		const float fDistanceToTarget = GetAlienBrain()->GetCurrentTargetDistance();
		if ( fDistanceToTarget > dbg_fMaxAtackLoopRange)
		{
			// nothing target is too close let cycle finish
		}
		else if ( fDistanceToTarget > dbg_fMinAtackLoopRange ) 
		{
			m_eStandMeleeMode = STAND_MELEE_WALK;		
			SwitchToState(STAND_ATTACK);
		}
		else
		{
			m_eStandMeleeMode = STAND_MELEE_STAND;		
			SwitchToState(STAND_ATTACK);
		}
	}
// 	else if (GetStateTimer() > 5.0f) // really really shouldn't happen but it does 
// 	{
// 		GetAlienBrain()->GetClientActor()->GetMovementSystem()->AbortCurrentMove(false);
// 		SetInError(true);
// 		SwitchToState(FINISHED);
// 	}
}


void AVP_Behaviour_Alien_Attack2::UpdateRunOut()
{
	const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
	if (!pxTargetKnowledge)
	{
		if (!m_bStopAttack)
		{
			SwitchToState(UNSET);
		}
		else
		{
			SwitchToState(FINISHED);
		}
		return;
	}

	AVP_Alien_Movement& xMovement = GetAlienBrain()->GetMovement();

	const Asura_Vector_3& xTargetPosition = pxTargetKnowledge->GetPosition();
	const float fDistanceSq = (GetAlienBrain()->GetActor()->GetPosition() - xTargetPosition).MagnitudeSquared();

	if (m_bStopAttack && fDistanceSq > dbg_fMinMoveoutRangeBeforeStopSq )
	{
		SwitchToState(FINISHED);
		return;
	}

	if (!m_bStopAttack && fDistanceSq > dbg_fMaxAttackRangeJump * dbg_fMaxAttackRangeJump )
	{
		if (GetAlienBrain()->CanChargeCurrentTarget())
		{
			SwitchToState(CHARGING);
		}
		else
		{
			SwitchToState(RUN_IN);
		}
		return;
 	}

	if (xMovement.IsInError() || xMovement.IsFinished())
	{
		SwitchToState(RUN_OUT);
		return;
	}

	float fDeltaTargetSq = (xTargetPosition - m_fTargetPositionAtPlan).MagnitudeSquared();
	const Asura_Matrix_3x3& xTargetOrient = pxTargetKnowledge->GetOrientation();
	const Asura_Vector_3 xTargetForward(xTargetOrient.e13, xTargetOrient.e23, xTargetOrient.e33);
	float fDeltaHeadingCos = m_fTargetHeadingAtPlan * xTargetForward;

	float fDistanceToDestination = GetAlienBrain()->GetMovement().GetLengthLeftOnPath();

	if (fDistanceToDestination < m_fMinDistaneToReplan)
	{
		PlanPathOut(true, true);
	}
	else if(GetAlienBrain()->ShouldRefreshPath(fDeltaTargetSq, fDeltaHeadingCos))
	{
		PlanPathOut(true, true);
	}

	if (GetStateTimer() > dbg_fMaxTimeBeforeSprint)
	{
		GetAlienBrain()->GetMovement().SetMovementMode(AVP_Alien_Movement::EMM_FAST);
	}
	else
	{
		GetAlienBrain()->GetMovement().SetMovementMode(AVP_Alien_Movement::EMM_MEDIUM);
	}
}


void AVP_Behaviour_Alien_Attack2::UpdateWander()
{
	bool bDummy;
	const AVP_AlienTeam_Knowledge* pxBestTarget = ChooseNewTarget(false, false, bDummy);
	if ( pxBestTarget )
	{
		GetAlienBrain()->StartedTargeting( pxBestTarget->GetGuid() );
		SwitchToState( RUN_IN );
		return;
	}

	AVP_Behaviour_Alien_Wander* pxBehaviour = static_cast< AVP_Behaviour_Alien_Wander* >( GetChildWithType( this, AVP_BEHAVIOUR_TYPE_ALIEN_WANDER ) );
	if ( !pxBehaviour || pxBehaviour->IsInError() || pxBehaviour->IsFinished())
	{
		PlanWander();
		return;
	}
}

void AVP_Behaviour_Alien_Attack2::PlanPathIn(bool bIsReplan)
{
	const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
	if (!pxTargetKnowledge)
	{
		SwitchToState(UNSET);
		return;
	}

	const Asura_Vector_3& xOurPosition = GetAlienBrain()->GetActor()->GetPosition();
	const Asura_Vector_3& xTargetPosition = pxTargetKnowledge->GetPosition();

	if ( (xOurPosition-xTargetPosition).MagnitudeSquared() < (dbg_fHysteresisDistance + dbg_fChargeDistance) * (dbg_fHysteresisDistance + dbg_fChargeDistance))
	{
		SwitchToState(CHARGING);
		return;
	}

	const Asura_Matrix_3x3& xTargetOrient = pxTargetKnowledge->GetOrientation();
	
	const Asura_Vector_3 xTargetForward(xTargetOrient.e13, xTargetOrient.e23, xTargetOrient.e33);
	const Asura_Vector_3 xTargetUp(xTargetOrient.e12, xTargetOrient.e22, xTargetOrient.e32);
 
// 	Asura_Quat xTurn;
// 	float fTurnAngle = dbg_fMaxAngleFromTargetDirection * Asura_Random::GetFloat(-1.0f,1.0f);
// 	xTurn.FromRotation(fTurnAngle, xTargetUp);
// 
// 	const Asura_Vector_3 xDirectionFromTarget = xTurn.RotateVector(xTargetForward);
// 
// 	AVP_AwayFromTargetInDirectionFunctor xAwayFunctor(xTargetPosition, dbg_fChargeDistance, xDirectionFromTarget, GetAlienBrain()->GetNavData().m_uPathfinderFlags);
// 
// 	AVP_Navigation::NavData xNavData;
// 	GetAlienBrain()->GetNavData().CopyConfigTo(xNavData);
// 
// 	xNavData.m_xStartPosition = xTargetPosition;
// 	const AVP_Navigation::TestParams xTestParams( xNavData.m_uPathfinderFlags, ASURA_NAVVERTEX_MAX_ZONE_ID );
// 
// 	if ( !AVP_Navigation::GetNearestWaypointID( xTargetPosition, xNavData.m_usCurrentWPID, xTestParams ) )
// 	{
// 		GetAlienBrain()->IgnoreTarget(pxTargetKnowledge->GetGuid(), dbg_fIgnoreTargetTime);
// 		SetInError(true);
// 		SwitchToState(WANDER);
// 		return;
// 	}
// 
// 
// 	if(!AVP_Navigation::FindRouteAwayFrom( xNavData, &xAwayFunctor, false ) || xNavData.m_usRouteLength == 0)
// 	{
// 		GetAlienBrain()->IgnoreTarget(pxTargetKnowledge->GetGuid(), dbg_fIgnoreTargetTime);
// 		SetInError(true);
// 		SwitchToState(WANDER);
// 		return;
// 	}
// 
// 	const Asura_Vector_3 xDestination = 
// 		Asura_AI_Navigation_System::GetWaypoint( xNavData.m_pusRoute[xNavData.m_usRouteLength-1] )->GetPosition();

	const Asura_Vector_3 xDestination = xTargetPosition;

	AVP_CloseInFunctor xCloseInFunctor(xTargetPosition);
	if ( !GetAlienBrain()->GetMovement().GetToPosition( xDestination, AVP_Alien_Movement::GetMaxArrivalRange(), xCloseInFunctor, m_bSprintOn ? AVP_Alien_Movement::EMM_FAST : AVP_Alien_Movement::EMM_MEDIUM, false ) )
	{
		GetAlienBrain()->IgnoreTarget(pxTargetKnowledge->GetGuid(), Asura_Random::GetFloat(dbg_fIgnoreTargetTimeMin,dbg_fIgnoreTargetTimeMax) );
		SetInError();
		SwitchToState(UNSET);
		return;
	}

	m_fTargetPositionAtPlan = xTargetPosition;
	m_fTargetHeadingAtPlan = xTargetForward;

	float fDistanceToReplan = GetAlienBrain()->GetMovement().GetLengthLeftOnPath() / 2.0f;
	if (fDistanceToReplan < dbg_fReplanStep )
	{
		fDistanceToReplan = 0.0f;
	}
	if (bIsReplan)
	{
		m_fMinDistaneToReplan = Asura_Maths::Min(fDistanceToReplan, m_fMinDistaneToReplan);
	}
	else
	{
		m_fMinDistaneToReplan = fDistanceToReplan;
	}
}

void AVP_Behaviour_Alien_Attack2::PlanCharge()
{
	const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
	if (!pxTargetKnowledge)
	{
		SwitchToState(UNSET);
		return;
	}

	const Asura_Vector_3& xTargetPosition = pxTargetKnowledge->GetPosition();
	float fAttackDistanceFromTarget = 0.0f;
	switch (m_ePreferedAttackType)
	{
	case ATTACK_TYPE_STAND:
		fAttackDistanceFromTarget = dbg_fMinAtackRangeStand + dbg_fStabilizationDistance;
		break;
	case ATTACK_TYPE_JUMP:
		fAttackDistanceFromTarget = dbg_fMinAttackRangeJump + dbg_fStabilizationDistance;
		break;
	case ATTACK_TYPE_POUNCE_GRAPPLE:
		fAttackDistanceFromTarget = dbg_fMinAttackRangePounceGrapple + dbg_fStabilizationDistance;		
		break;
	default:
	case ATTACK_TYPE_UNDECIDED:
		fAttackDistanceFromTarget = dbg_fMinAttackRangeFakeJump + dbg_fStabilizationDistance;
		Asura_Assert(false, "unknown attack type");
	}

	AVP_ChargeFunctor xFunctor(xTargetPosition, fAttackDistanceFromTarget);
	if (m_ePreferedAttackType == ATTACK_TYPE_STAND)
	{
		xFunctor.PreferFloor();
 	}
	if (GetAlienBrain()->GetPreferAttackToJump())
	{
		xFunctor.PreferAttackToJump();
	}

// 	AVP_CloseInFunctor xFunctor(xTargetPosition);

	if ( !GetAlienBrain()->GetMovement().GetToPosition( xTargetPosition, AVP_Alien_Movement::GetMinArrivalRange(), xFunctor, AVP_Alien_Movement::EMM_MEDIUM, false ) )
	{
		GetAlienBrain()->IgnoreTarget(pxTargetKnowledge->GetGuid(), Asura_Random::GetFloat(dbg_fIgnoreTargetTimeMin, dbg_fIgnoreTargetTimeMax));
		SetInError(true);
		SwitchToState(UNSET);
		return;
	}

	GetAlienBrain()->SetNearbyActorGuidToIgnore(pxTargetKnowledge->GetGuid());

	m_fTargetPositionAtPlan = xTargetPosition;

}

void AVP_Behaviour_Alien_Attack2::PlanPathOut(bool bIsReplan, bool bUseMyOrientation)
{
	const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
	if (!pxTargetKnowledge)
	{
		SwitchToState(UNSET);
		return;
	}

	const Asura_Vector_3& xTargetPosition = pxTargetKnowledge->GetPosition();

	const Asura_Matrix_3x3& xTargetOrient = pxTargetKnowledge->GetOrientation();
	const Asura_Vector_3 xTargetUp(xTargetOrient.e12, xTargetOrient.e22, xTargetOrient.e32);
	const Asura_Vector_3 xTargetForward(xTargetOrient.e13, xTargetOrient.e23, xTargetOrient.e33);
	const Asura_Vector_3 xTargetRight(xTargetOrient.e11, xTargetOrient.e21, xTargetOrient.e31);


	Asura_Vector_3 xMoveDirection;
	Asura_Quat xTurn;
	if (bUseMyOrientation)
	{
		const Asura_Matrix_3x3& xMyOrient = GetAlienBrain()->GetActor()->GetOrientation();

		const Asura_Vector_3 xMyForward(xMyOrient.e13, xMyOrient.e23, xMyOrient.e33);
		Asura_Vector_3 xToTarget = GetAlienBrain()->GetActor()->GetPosition() - xTargetPosition;
		xToTarget.Normalise();
		bool bFront = xToTarget * xTargetForward > 0;
		bool bRight = xToTarget * xTargetRight > 0;
		float fTurnAngle = m_bStopAttack ? 0.0f : dbg_fMaxAngleFromMyDirection * Asura_Random::GetFloat(0.0f, 1.0f ); // if stopping just move in my current direction

		Asura_Vector_3 xStartDirection;

		if (bFront)
		{
			if (bRight)
			{
				fTurnAngle = -fTurnAngle;
			}
			else
			{				
			}
		}
		else
		{
			if (bRight)
			{
				/*fTurnAngle = Asura_Maths::Pi - fTurnAngle;*/
			}
			else
			{
				/*fTurnAngle = Asura_Maths::Pi + fTurnAngle;*/
				fTurnAngle = -fTurnAngle;			
			}
		}

		xTurn.FromRotation(fTurnAngle, xTargetUp);
		xMoveDirection = xTurn.RotateVector(xMyForward);

	}
	else
	{
		float fTurnAngle = dbg_fMaxAngleFromTargetDirection * Asura_Random::GetFloat(-1.0f, 1.0f );
		xTurn.FromRotation(fTurnAngle, xTargetUp);
		xMoveDirection = xTurn.RotateVector(xTargetForward);
	}


	AVP_AwayFromTargetInDirectionFunctor xAwayFunctor(xTargetPosition, dbg_fChargeDistance + dbg_fHysteresisDistance, xMoveDirection, GetAlienBrain()->GetNavData().m_uPathfinderFlags);
	AVP_Navigation::NavData xNavData;
	GetAlienBrain()->GetNavData().CopyConfigTo(xNavData);

	xNavData.m_xStartPosition = xTargetPosition;
	const AVP_Navigation::TestParams xTestParams( xNavData.m_uPathfinderFlags, ASURA_NAVVERTEX_MAX_ZONE_ID );

	if ( !AVP_Navigation::GetNearestWaypointID( xTargetPosition, xNavData.m_usCurrentWPID, xTestParams ) )
	{
		GetAlienBrain()->IgnoreTarget(pxTargetKnowledge->GetGuid(), Asura_Random::GetFloat(dbg_fIgnoreTargetTimeMin, dbg_fIgnoreTargetTimeMax));
		SwitchToState(UNSET);
		return;
	}

	if(!AVP_Navigation::FindRouteAwayFrom( xNavData, &xAwayFunctor, false ) || xNavData.m_usRouteLength == 0)
	{
		GetAlienBrain()->IgnoreTarget(pxTargetKnowledge->GetGuid(), Asura_Random::GetFloat(dbg_fIgnoreTargetTimeMin, dbg_fIgnoreTargetTimeMax));		
		SwitchToState(UNSET);
		return;
	}

	const Asura_Vector_3 xDestination = 
		Asura_AI_Navigation_System::GetWaypoint( xNavData.m_pusRoute[xNavData.m_usRouteLength-1] )->GetPosition();

	AVP_MoveAvoidingTargetFunctor xFunctor(xTargetPosition, dbg_fMinRoamDistance);
	if (m_ePreferedAttackType == ATTACK_TYPE_STAND)
	{
		xFunctor.PreferFloor();
	}

	if ( !GetAlienBrain()->GetMovement().GetToPosition( xDestination, AVP_Alien_Movement::GetMaxArrivalRange(), xFunctor, AVP_Alien_Movement::EMM_MEDIUM, false ) )
	{
		GetAlienBrain()->IgnoreTarget( GetAlienBrain()->GetCurrentTargetGuid(), Asura_Random::GetFloat(dbg_fIgnoreTargetTimeMin, dbg_fIgnoreTargetTimeMax));
		SwitchToState(UNSET);
		return;
	}

	m_fTargetPositionAtPlan = xTargetPosition;
	m_fTargetHeadingAtPlan = xTargetForward;


	float fDistanceToReplan = GetAlienBrain()->GetMovement().GetLengthLeftOnPath() / 2.0f;
	if (fDistanceToReplan < dbg_fReplanStep )
	{
		fDistanceToReplan = 0.0f;
	}
	if (bIsReplan)
	{
		m_fMinDistaneToReplan = Asura_Maths::Min(fDistanceToReplan, m_fMinDistaneToReplan);
	}
	else
	{
		m_fMinDistaneToReplan = fDistanceToReplan;
	}

}


void AVP_Behaviour_Alien_Attack2::PlanWander()
{
	DestroyChildWithType(AVP_BEHAVIOUR_TYPE_ALIEN_WANDER);

	if (m_bStopAttack)
	{
		SwitchToState(FINISHED);
	}
	else
	{
		bool bDummy;
		const AVP_AlienTeam_Knowledge* pxBestTarget = ChooseNewTarget(true, false, bDummy);
		if ( pxBestTarget )
		{
			GetAlienBrain()->StartedTargeting( pxBestTarget->GetGuid() );
			SwitchToState( RUN_IN );
		}
		else
		{
			CreateChildWithType(AVP_BEHAVIOUR_TYPE_ALIEN_WANDER);
		}
	}
}

bool AVP_Behaviour_Alien_Attack2::EndOfRouteAllowsStandAttack() const
{
	// Work backwards along the route and determine whether we are on the floor until at least fMIN_ATTACK_RANGE_STAND away.
	const float fDistanceRequiredSq = ( dbg_fMinAtackRangeStand * dbg_fMinAtackRangeStand );

	const Asura_Vector_3& xTargetPosition = GetAlienBrain()->GetCurrentTargetPosition();

	const AVP_Navigation::NavData& xNavData = GetAlienBrain()->GetNavData();
	if ( xNavData.m_usRouteLength == 0 )
	{
		return false;
	}
	else if ( xNavData.m_usRouteLength == 1 )
	{		
		const Asura_AI_NavVertex* pxWP = Asura_AI_Navigation_System::GetWaypoint( xNavData.m_pusRoute[ 0 ] );

		if ( pxWP->IsCeilingWaypoint() || pxWP->IsWallWaypoint() )
		{
			return false;
		}

		return true;
	}
	else
	{
		float fDistanceSq = 0.0f;
		for ( u_short usWPIndex = xNavData.m_usRouteLength - 1; usWPIndex > 0; --usWPIndex )
		{
			const Asura_AI_NavVertex* pxToWP = Asura_AI_Navigation_System::GetWaypoint( xNavData.m_pusRoute[ usWPIndex ] );
			const Asura_AI_NavVertex* pxFromWP   = Asura_AI_Navigation_System::GetWaypoint( xNavData.m_pusRoute[ usWPIndex - 1 ] );

			if ( pxFromWP->IsCeilingWaypoint() || pxFromWP->IsWallWaypoint() )
			{
				return false;
			}
			if ( pxToWP->IsCeilingWaypoint() || pxToWP->IsWallWaypoint() )
			{
				return false;
			}

			const Asura_AI_NavConnection* pxLink = pxFromWP->GetLinkTo( xNavData.m_pusRoute[ usWPIndex ] );
			if (pxLink)
			{
				u_short usLinkFlags = static_cast<u_short>(pxLink->GetFlags());

				if (AVP_Navigation::IsEnvHookLink(usLinkFlags) || AVP_Navigation::IsGS2Link(usLinkFlags) )
				{
					return false;
				}
			}

			fDistanceSq = ( pxToWP->GetPosition() - xTargetPosition ).MagnitudeSquared();
			if ( fDistanceSq > fDistanceRequiredSq )
			{
				return true;
			}
		}

		return false;
	}
}


bool AVP_Behaviour_Alien_Attack2::OnFloorRouteTillEnd() const
{
	const AVP_Navigation::NavData& xNavData = GetAlienBrain()->GetNavData();
	if ( xNavData.m_usRouteLength == 0 )
	{
		return false;
	}
	else if ( xNavData.m_usRouteLength == 1 )
	{		
		const Asura_AI_NavVertex* pxWP = Asura_AI_Navigation_System::GetWaypoint( xNavData.m_pusRoute[ 0 ] );

		if ( pxWP->IsCeilingWaypoint() || pxWP->IsWallWaypoint() )
		{
			return false;
		}

		return true;
	}
	else
	{
		for ( u_short usWPIndex = xNavData.m_usRouteSegmentIndex; usWPIndex < xNavData.m_usRouteLength -1; ++usWPIndex )
		{			
			const Asura_AI_NavVertex* pxFromWP = Asura_AI_Navigation_System::GetWaypoint( xNavData.m_pusRoute[ usWPIndex ] );
			const Asura_AI_NavVertex* pxToWP   = Asura_AI_Navigation_System::GetWaypoint( xNavData.m_pusRoute[ usWPIndex + 1 ] );
			if ( pxFromWP->IsCeilingWaypoint() || pxFromWP->IsWallWaypoint() )
			{
				return false;
			}
			if ( pxToWP->IsCeilingWaypoint() || pxToWP->IsWallWaypoint() )
			{
				return false;
			}

			const Asura_AI_NavConnection* pxLink = pxFromWP->GetLinkTo( xNavData.m_pusRoute[ usWPIndex ] );
			if (pxLink)
			{
				u_short usLinkFlags = static_cast<u_short>(pxLink->GetFlags());

				if (AVP_Navigation::IsEnvHookLink(usLinkFlags) || AVP_Navigation::IsGS2Link(usLinkFlags) )
				{
					return false;
				}
			}
		}
	}

	return true;
}

bool AVP_Behaviour_Alien_Attack2::StandAttackWillBeAllowed() const
{
	const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
	if (!pxTargetKnowledge)
	{
		return false;
	}

	const float fDistanceOnPathToTarget = GetAlienBrain()->GetMovement().GetLengthLeftOnPath();
	const float fDistanceToTarget = GetAlienBrain()->GetCurrentTargetDistance();

	if (fDistanceToTarget < dbg_fMinAtackRangeStand && fDistanceOnPathToTarget < dbg_fMinAtackRangeStand)
	{		
		return false;	
	}


	if (!EndOfRouteAllowsStandAttack())
	{
		return false;
	}

	return true;
}


bool AVP_Behaviour_Alien_Attack2::StandAttackAllowed() const
{
	const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
	if (!pxTargetKnowledge)
	{
		return false;
	}

	const float fDistanceToTarget = GetAlienBrain()->GetCurrentTargetDistance();

	if (fDistanceToTarget > dbg_fMaxAtackRangeStand || fDistanceToTarget < dbg_fMinAtackRangeStand)
	{
		return false;
	}

	if (!OnFloorRouteTillEnd())
	{
		return false;
	}


	if (GetAlienBrain()->GetActor()->GetGroundType() != AVP_Actor_PhysicsObjectHandler::ON_FLOOR)
	{
		return false;
	}
	
	if (m_bTargetIsPlayerAndDidntSeenMe)
	{
		return false;
	}

	if (!GetAlienBrain()->CurrentTargetWithinAttackFOV() || !GetAlienBrain()->HaveLOSToCurrentTarget())
	{
		return false;
	}

	return true;
}

bool AVP_Behaviour_Alien_Attack2::JumpAttackAllowed(bool bFake) const
{
	const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
	if (!pxTargetKnowledge)
	{
		return false;
	}

	const float fDistanceToTarget = GetAlienBrain()->GetCurrentTargetDistance();

	if (bFake)
	{
		if (fDistanceToTarget > dbg_fMaxAttackRangeFakeJump || fDistanceToTarget < dbg_fMinAttackRangeFakeJump )
		{
			return false;
		}
	}
	else
	{
		if (fDistanceToTarget > dbg_fMaxAttackRangeJump || fDistanceToTarget < dbg_fMinAttackRangeJump )
		{
			return false;
		}
	}

	if (!GetAlienBrain()->CurrentTargetWithinAttackFOV() || !GetAlienBrain()->HaveLOSToCurrentTarget())
	{
		return false;
	}

	return true;
}

bool AVP_Behaviour_Alien_Attack2::PounceGrappleAllowed() const
{
	const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
	if (!pxTargetKnowledge)
	{
		return false;
	}

	const AVP_ServerEntity_Actor* pxTarget = AVP_ServerEntity_Actor::FindEntity(pxTargetKnowledge->GetGuid());
	if (!pxTarget)
	{
		return false;
	}

	switch (pxTarget->GetClassification()) // can only pounce grapple human
	{
	case AVP_ENTITYCLASS_CIVILIAN:
	case AVP_ENTITYCLASS_MARINE:
		break;
	default:
		return false;
	}	

	const float fDistanceToTarget = GetAlienBrain()->GetCurrentTargetDistance();

	if (fDistanceToTarget > dbg_fMaxAttackRangePounceGrapple || fDistanceToTarget < dbg_fMinAttackRangePounceGrapple)
	{
		return false;
	}

	if (m_bTargetIsPlayerAndDidntSeenMe)
	{
		return false;
	}

	if (!GetAlienBrain()->CurrentTargetWithinAttackFOV() || !GetAlienBrain()->HaveLOSToCurrentTarget())
	{
		return false;
	}

	return true;
}

void AVP_Behaviour_Alien_Attack2::RequestJumpAttack(bool bFake, bool bReducedDmg)
{
	if ( !GetAlienBrain()->GetCurrentTargetKnowledge() )
	{
		Asura_Assert( false, "AVP_Behaviour_Alien_JumpAttack::RequestMelee - we have tested this already!" );
		SwitchToState(UNSET);
		return;
	}

	Asura_Guid uTarget = GetAlienBrain()->GetCurrentTargetKnowledge()->GetGuid();

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

		pxData->SetType( AVP_ALIEN_MELEEATTACK_TYPE_JUMPSLASH );
		pxData->SetTargetGuid(uTarget);

		pxMessage->SetToGuid( GetAlienBrain()->GetOwnerGuid() );
		pxMessage->SetFromGuid( GetAlienBrain()->GetOwnerGuid() );
		if (bFake)
		{
			pxData->SetForceMiss(true);
		}
		else
		{
			pxData->SetForceMiss(false);
		}
		if (bReducedDmg)
		{
			pxData->SetDamageMultiplier(GetAlienBrain()->GetBehaviourData().m_fDamageFromBehindMultiplier);
		}
		else
		{
			pxData->SetDamageMultiplier(1.0f);
		}


		Asura_Network::Send( pxMessage );
		AVP_Actor_AIAnimStateMachine* pxVertStateHandler = GetAlienBrain()->GetActor()->GetAIAnimStateMachine();
		pxVertStateHandler->RequestInMove( AVP_Alien_Movement::GetAnimStateFromMovementMode(GetAlienBrain()->GetMovement().GetCurrentMovementMode()) );

	}

}

void AVP_Behaviour_Alien_Attack2::RequestMeleeAttack()
{

	if ( !GetAlienBrain()->GetCurrentTargetKnowledge() )
	{
		Asura_Assert( false, "AVP_Behaviour_Alien_StandAttack::RequestMelee - we have tested this already!" );
		return;
	}

	float fDistanceTotargetSq = (GetAlienBrain()->GetActor()->GetPosition()- GetAlienBrain()->GetCurrentTargetKnowledge()->GetPosition()).MagnitudeSquared();


	// Send a request melee message.
	Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSGID_REQUEST_MELEE, sizeof( AVP_Message_Vertebrate_RequestMelee ) );
	if ( pxMessage )
	{
		AVP_Message_Vertebrate_RequestMelee* pxData = static_cast< AVP_Message_Vertebrate_RequestMelee* >( pxMessage->GetData() );

		switch (m_eStandMeleeMode)
		{
		case STAND_MELEE_TO_STAND:
			if (fDistanceTotargetSq < dbg_fMinAtackLongRangeStandSq)
			{
				pxData->SetType( m_bLeftStandAttack ? AVP_ALIEN_MELEEATTACK_TYPE_AI_CRAWLTOSTANDSLASH_SHORT_L : AVP_ALIEN_MELEEATTACK_TYPE_AI_CRAWLTOSTANDSLASH_SHORT_R );
			}
			else
			{
				pxData->SetType( m_bLeftStandAttack ? AVP_ALIEN_MELEEATTACK_TYPE_AI_CRAWLTOSTANDSLASH_L : AVP_ALIEN_MELEEATTACK_TYPE_AI_CRAWLTOSTANDSLASH_R );
			}
			break;
		case STAND_MELEE_STAND:
			pxData->SetType( m_bLeftStandAttack ? AVP_ALIEN_MELEEATTACK_TYPE_AI_STANDSLASHLOOP_L : AVP_ALIEN_MELEEATTACK_TYPE_AI_STANDSLASHLOOP_R );
			break;
		case STAND_MELEE_WALK:
			pxData->SetType( m_bLeftStandAttack ? AVP_ALIEN_MELEEATTACK_TYPE_AI_WALKSLASHLOOP_L : AVP_ALIEN_MELEEATTACK_TYPE_AI_WALKSLASHLOOP_R );
			break;
		default:
			Asura_Assert( false, "AVP_Behaviour_Alien_StandAttack::RequestMelee - unexpected mode");
			pxData->SetType( m_bLeftStandAttack ? AVP_ALIEN_MELEEATTACK_TYPE_AI_STANDSLASHLOOP_L : AVP_ALIEN_MELEEATTACK_TYPE_AI_STANDSLASHLOOP_R );
			break;
		}


		pxData->SetTargetGuid( GetAlienBrain()->GetCurrentTargetKnowledge()->GetGuid() );

		pxMessage->SetToGuid( GetAlienBrain()->GetOwnerGuid() );
		pxMessage->SetFromGuid( GetAlienBrain()->GetOwnerGuid() );
		Asura_Network::Send( pxMessage );

		AVP_Actor_AIAnimStateMachine* pxVertStateHandler = GetAlienBrain()->GetActor()->GetAIAnimStateMachine();
		pxVertStateHandler->RequestInMove( AVP_Alien_Movement::GetAnimStateFromMovementMode(GetAlienBrain()->GetMovement().GetCurrentMovementMode()) );

		// GetAlienBrain()->GetMovement().Stop();

		m_bLeftStandAttack = !m_bLeftStandAttack;
	}
}

void AVP_Behaviour_Alien_Attack2::RequestPounceGrapple()
{
	if ( !GetAlienBrain()->GetCurrentTargetKnowledge() )
	{
		Asura_Assert( false, "AVP_Behaviour_Alien_JumpAttack::RequestMelee - we have tested this already!" );
		SwitchToState(UNSET);
		return;
	}

	Asura_Guid uTarget = GetAlienBrain()->GetCurrentTargetKnowledge()->GetGuid();

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

		pxData->SetType( AVP_ALIEN_MELEEATTACK_TYPE_AI_POUNCEGRAPPLE );
		pxData->SetTargetGuid(uTarget);

		pxMessage->SetToGuid( GetAlienBrain()->GetOwnerGuid() );
		pxMessage->SetFromGuid( GetAlienBrain()->GetOwnerGuid() );
		Asura_Network::Send( pxMessage );
		GetAlienBrain()->GetMovement().Stop();
	}
}


void AVP_Behaviour_Alien_Attack2::LateHandleMessage()
{
	Asura_LinkList_It< int > xIter(m_xDelayedMsgList);
	for (; !xIter.Done(); xIter.Next())
	{
		int eMsgId = *xIter.GetCurrent();
		switch (eMsgId)
		{
		case ASURA_MSG_MOVECHAINCOMPLETE:
			switch (GetCurrentState())
			{
			case FAKE_JUMP_ATTACK:
			case JUMP_ATTACK:
			case STAND_ATTACK:
			case POUNCE_GRAPPLE:
			case COUNTERED:
				SwitchToState( RUN_OUT );
				break;
			default:
				break;
			}
			break;
		case AVP_MSGID_MELEE_ATTACK_FAILED_TO_START:
			switch (GetCurrentState())
			{
			case FAKE_JUMP_ATTACK:
			case JUMP_ATTACK:
			case STAND_ATTACK:
			case POUNCE_GRAPPLE:
			case COUNTERED:
				SwitchToState( RUN_OUT );
				break;
			default:
				break;
			}
			break;
		case AVP_MSGID_COUNTER_MELEE:
			switch (GetCurrentState())
			{
			case FAKE_JUMP_ATTACK:
			case JUMP_ATTACK:
			case STAND_ATTACK:
			case POUNCE_GRAPPLE:
				SwitchToState( COUNTERED );
				break;
			default:
				break;
			}
			break;
		}
	}
	m_xDelayedMsgList.Clear();
}

bool AVP_Behaviour_Alien_Attack2::CanLockAttack(AVP_Behaviour_Alien_Attack2::AttackType eAttackType) const
{
	switch (eAttackType)
	{
	case ATTACK_TYPE_STAND:
		return GetAlienBrain()->GetAlienSquad()->CanLockAttackStand(GetAlienBrain()->GetCurrentTargetGuid(), GetAlienBrain()->GetActor()->GetPosition());	
	case ATTACK_TYPE_JUMP:
		return GetAlienBrain()->GetAlienSquad()->CanLockAttackJump(GetAlienBrain()->GetCurrentTargetGuid(), GetAlienBrain()->GetActor()->GetPosition());	
	case ATTACK_TYPE_POUNCE_GRAPPLE:
		return GetAlienBrain()->GetAlienSquad()->CanLockAttackGrapple(GetAlienBrain()->GetCurrentTargetGuid(), GetAlienBrain()->GetActor()->GetPosition());	
	default:
	case ATTACK_TYPE_UNDECIDED:
		Asura_Assert(false, "Unexpected attack type");
		return false;
	}
}

void AVP_Behaviour_Alien_Attack2::LockAttack(AVP_Behaviour_Alien_Attack2::AttackType eAttackType)
{
	switch (eAttackType)
	{
	case ATTACK_TYPE_STAND:
		GetAlienBrain()->GetAlienSquad()->LockAttackStand(GetAlienBrain()->GetCurrentTargetGuid());	
		break;
	case ATTACK_TYPE_JUMP:
		GetAlienBrain()->GetAlienSquad()->LockAttackJump(GetAlienBrain()->GetCurrentTargetGuid());
		break;
	case ATTACK_TYPE_POUNCE_GRAPPLE:
		GetAlienBrain()->GetAlienSquad()->LockAttackGrapple(GetAlienBrain()->GetCurrentTargetGuid());	
		break;
	default:
	case ATTACK_TYPE_UNDECIDED:
		Asura_Assert(false, "Unexpected attack type");
		break;
	}

	m_eCurrentAttackType = eAttackType;

}
void AVP_Behaviour_Alien_Attack2::UnlockAttack()
{
	switch (m_eCurrentAttackType)
	{
	case ATTACK_TYPE_STAND:
		GetAlienBrain()->GetAlienSquad()->UnlockAttackStand(GetAlienBrain()->GetCurrentTargetGuid());	
		break;
	case ATTACK_TYPE_JUMP:
		GetAlienBrain()->GetAlienSquad()->UnlockAttackJump(GetAlienBrain()->GetCurrentTargetGuid());
		break;
	case ATTACK_TYPE_POUNCE_GRAPPLE:
		GetAlienBrain()->GetAlienSquad()->UnlockAttackGrapple(GetAlienBrain()->GetCurrentTargetGuid());	
		break;
	default:
	case ATTACK_TYPE_UNDECIDED: // to allow multiple unlocking without problem
		break;
	}
	m_eCurrentAttackType = ATTACK_TYPE_UNDECIDED;
}

DEBUGVAR float dbg_fMinSprintTime = 3.0f;
DEBUGVAR float dbg_fMinSprintCoolTime = 2.0f;
DEBUGVAR float dbg_fChanceToStartSprint = 0.5f;
DEBUGVAR float dbg_fChanceToStopSprint = 0.8f;


void AVP_Behaviour_Alien_Attack2::UpdateSprint(float fTimeslice, bool bCanStartSprint)
{
	AVP_Alien_Movement::ExMovementMode eEMM = AVP_Alien_Movement::EMM_MEDIUM;
	m_fSprintTestTimer -= fTimeslice;
	m_fSprintTimer += fTimeslice;
	if (!m_bSprintOn)
	{
		if (m_fSprintTimer < dbg_fMinSprintCoolTime || !bCanStartSprint)
		{
			eEMM = AVP_Alien_Movement::EMM_MEDIUM;
		}
		else if (m_fSprintTestTimer < 0.0f)
		{
			m_fSprintTestTimer = 1.0f;
			if (Asura_Random::GetFloat() < dbg_fChanceToStartSprint)
			{
				m_fSprintTimer = 0.0f;
				eEMM = AVP_Alien_Movement::EMM_FAST;
			}
			else
			{
				eEMM = AVP_Alien_Movement::EMM_MEDIUM;
			}
		}
		else
		{
			eEMM = AVP_Alien_Movement::EMM_MEDIUM;
		}
	}
	else
	{
		if (m_fSprintTimer < dbg_fMinSprintTime)
		{
			eEMM = AVP_Alien_Movement::EMM_FAST;
		}
		else if (m_fSprintTestTimer < 0.0f)
		{
			m_fSprintTestTimer = 1.0f;
			if (Asura_Random::GetFloat() < dbg_fChanceToStopSprint)
			{
				m_fSprintTimer = 0.0f;
				eEMM = AVP_Alien_Movement::EMM_MEDIUM;
			}
			else
			{
				eEMM = AVP_Alien_Movement::EMM_FAST;
			}
		}
		else
		{
			eEMM = AVP_Alien_Movement::EMM_FAST;
		}
	}

	m_bSprintOn = eEMM == AVP_Alien_Movement::EMM_FAST;

	GetAlienBrain()->GetMovement().SetMovementMode(eEMM);
}

bool AVP_Behaviour_Alien_Attack2::IsReducedJumpAttackDamage() const
{
	return TargetIsPlayerAndDidntSeeMe();
}

DEBUGVAR float dbg_fFrontalAngleCosine = Asura_Maths::Cosine(Asura_Maths::DegToRad(45));

bool AVP_Behaviour_Alien_Attack2::TargetIsPlayerAndDidntSeeMe() const
{
	const AVP_AlienTeam_Knowledge* pxTargetKnowledge = GetAlienBrain()->GetCurrentTargetKnowledge();
	if (!pxTargetKnowledge)
	{
		return false;
	}
	AVP_ServerEntity_Actor* pxTarget = AVP_ServerEntity_Actor::FindEntity(pxTargetKnowledge->GetGuid());

	if (!pxTarget)
	{
		return false;
	}

	if (pxTarget->IsPlayer())
	{

		Asura_Vector_3 xAim = pxTarget->GetAimDirection();
		Asura_Vector_3 xDirection = GetAlienBrain()->GetActor()->GetPosition() - pxTarget->GetPosition();
		xDirection.Normalise();

		bool bTargetFacingMe = xAim * xDirection > dbg_fFrontalAngleCosine;

		if (!bTargetFacingMe )
		{
			return true;
		}
	}
	return false;
}

DEBUGVAR float dbg_fTimeAllowedTillGrapple = 2.0f;

void AVP_Behaviour_Alien_Attack2::ChoosePreferedAttack(bool bTakeLastGrappleTimeIntoAccount)
{	
	float fFraction = Asura_Random::GetFloat();

	const AVP_AlienTeam_Knowledge* pxTarget = GetAlienBrain()->GetCurrentTargetKnowledge();
	Asura_Guid uTargetGuid = pxTarget ? pxTarget->GetGuid() : ASURA_GUID_UNREGISTERED;


	if (fFraction < GetAlienBrain()->GetAlienSquad()->GetBehaviourData().m_fJumpFraction )
	{
		m_ePreferedAttackType = ATTACK_TYPE_JUMP;
	}
	else if (fFraction < GetAlienBrain()->GetAlienSquad()->GetBehaviourData().m_fJumpFraction +
						 GetAlienBrain()->GetAlienSquad()->GetBehaviourData().m_fStandFraction)
	{
		m_ePreferedAttackType = ATTACK_TYPE_STAND;
	}
	else if (bTakeLastGrappleTimeIntoAccount && uTargetGuid != ASURA_GUID_UNREGISTERED && GetAlienBrain()->GetAlienTeam()->TimeSinceLastGrapple(uTargetGuid) < GetAlienBrain()->GetBehaviourData().m_fMinTimeBetweenGrapples - dbg_fTimeAllowedTillGrapple)
	{ // no point choosing grapple if it going to be most likely overthrown.
		m_ePreferedAttackType = ATTACK_TYPE_STAND;
	}
	else
	{
		m_ePreferedAttackType = ATTACK_TYPE_POUNCE_GRAPPLE;
	}
}


//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////
