//---------------------------------------------------------------------------
// File			: AVP_Behaviour_Marine_Attack.cpp
// Created		: 
// Created By	: Gareth Thomas
// Copyright (C) 2007 Rebellion. All Rights Reserved.
//---------------------------------------------------------------------------

// INCLUDES ///////////////////////////////////////////////////////////

// core
#include "Core/Asura.h"
// header
#include "Behaviour/BehaviourTypes/Human/Marine/AVP_Behaviour_Marine_Attack.h"
// asura
#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Rendering/Asura_Font_System.h"
#include "Debugging/Asura_DebugLineRender.h"
#endif

// AVP
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_ServerActor_Human.h"
#include "Entity/GamePlayVolume/AVP_ServerEntity_GamePlayVolume.h"
#include "Entity/Locations/OrientedNode/CoverpointNode/AVP_ServerNode_CoverpointNode.h"

// AVP axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Behaviour/BehaviourTypes/Human/Marine/AVP_Behaviour_Marine_InCover.h"
#include "Core/AVP_Waypoint_Manager.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Marine.h"
#include "Knowledge/AVP_Knowledge_System_Actor.h"
#include "Tactical/AVP_Tactical_System.h"

DEBUGVAR float fDbg_ShortTimout = 9.0f;
DEBUGVAR float fDbg_LongTimout = 10.0f;

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: Constructor
//---------------------------------------------------------------------------
AVP_Behaviour_Marine_Attack::AVP_Behaviour_Marine_Attack( AVP_Brain_Actor_Marine* pxBrain )
: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_MARINE_ATTACK )
, m_eUpdateState( POSITION_EVALUATION )
, m_pxMarineBrain( pxBrain )
, m_fPosEvalUpdateTimer( Asura_Random::GetFloat( 4.2f, 4.7f ) )
, m_uPosEvalCounter( 0 )
, m_bBeingDefensive( false )
, m_bDefenseDamageReset( false )
, m_bAttackDamageReset( false )
, m_pxBestCoverPoint( NULL )
, m_uBestCoverPoint( ASURA_GUID_UNREGISTERED )
, m_uRouteTimeOutState( RD_HUGE )
, m_fDefensiveRouteDistance( 0.0f )
{

}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: Destructor
//---------------------------------------------------------------------------
AVP_Behaviour_Marine_Attack::~AVP_Behaviour_Marine_Attack( void )
{

}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: Activate
//---------------------------------------------------------------------------
bool AVP_Behaviour_Marine_Attack::Activate( void )
{
	const bool bParentOk = PARENT::Activate();
	// Put Code Below This Comment

	AVP_ServerEntity_Actor* pxThreat = AVP_ServerEntity_Actor::FindEntity( GetKnowledgeSystem()->GetBestThreatKnowledge().GetGuid() );

	if ( pxThreat && pxThreat->IsCloaked( true ) )
	{
		float fDistanceSq = ( pxThreat->GetPosition() - GetMarineBrain()->GetHumanActor()->GetPosition() ).MagnitudeSquared();

		if (fDistanceSq < 100.0f)
		{
			GetMarineBrain()->GetHumanActor()->PlayDialogue( AVP_ServerActor_Human::AVP_DLG_HUMAN_SCARED_REACTION );
		}
	}

	m_fDefensiveStanceTimeout = Asura_Random::GetFloat(fDbg_ShortTimout, fDbg_LongTimout);

	// Put Code Above This Comment
	return bParentOk;
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: Deactivate
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Attack::Deactivate( void )
{
	// Put Code Above This Comment
	DestroyAllChildren();
	PARENT::Deactivate();
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: PerFrameUpdate
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Attack::PerFrameUpdate( const float fTimeslice )
{
	Asura_Vector_3 xThreatPos = Asura_Maths::ZeroVector3;

	if ( !GetMarineBrain()->GetKnowledgeSystem()->ShootAtScriptedTarget() || ( GetMarineBrain()->GetKnowledgeSystem()->ShootAtScriptedTarget() && GetMarineBrain()->GetKnowledgeSystem()->ScriptedTargetIsShootAtPoint() ) )
	{
		xThreatPos = GetKnowledgeSystem()->GetBestThreatKnowledge().m_xPosition;
	}
	else
	{
		Asura_Guid uThreatGuid = GetKnowledgeSystem()->GetBestThreatKnowledge().GetGuid();
		if ( uThreatGuid == ASURA_GUID_UNREGISTERED ) { SetFinished(); return; }

		AVP_ServerEntity_Actor* pxThreatActor = AVP_ServerEntity_Actor::FindEntity( uThreatGuid );
		if ( !pxThreatActor ) { SetFinished(); return; }

		xThreatPos = pxThreatActor->GetPosition();
	}

	switch ( GetUpdateState() )
	{
		//---------------------------------------------------------------------------
		case POSITION_EVALUATION:
		{
			if ( m_bBeingDefensive )
			{
				ModifyDefensiveTimeout();
			}

			PositionEvaluation( fTimeslice );

			// Always Look At The Threat
			GetMarineBrain()->AimAtThreat();
			GetMarineBrain()->LookAtThreat();

		}break;
		//---------------------------------------------------------------------------
		case MOVE_TO_COVER:
		{
			AVP_ServerActor_Human*			pxOurActor		= GetMarineBrain()->GetHumanActor();
			Asura_ServerNode_Coverpoint*	pxCoverPoint	= Asura_ServerNode_Coverpoint::FindEntity( m_uBestCoverPoint );

			if ( !pxCoverPoint || !pxCoverPoint->ProvidesCoverFrom( xThreatPos ) )
			{
				SwitchToState( POSITION_EVALUATION );
				return;
			}

			float fDistanceToCoverSq = ( pxCoverPoint->GetPosition() - pxOurActor->GetPosition() ).MagnitudeSquared();

			if ( fDistanceToCoverSq <= 3.0f )
			{
				SwitchToState( COVER );
			}

			if ( GetTacticalSystem()->DidRouteFindFail() )
			{
				SwitchToState( POSITION_EVALUATION );
			}

			// Always Look At The Threat
			GetMarineBrain()->AimAtThreat();
			GetMarineBrain()->LookAtThreat();

		}break;
		//---------------------------------------------------------------------------
		case COVER:
		{
			// If our cover is no longer valid then switch to position evaluation
			if (m_uBestCoverPoint == ASURA_GUID_UNREGISTERED)
			{
				SwitchToState( POSITION_EVALUATION );
				return;
			}

			// Check that the cover behaviour hasn't finished
			Axon_Behaviour* pxCoverBehaviour = GetChildWithType( this, AVP_BEHAVIOUR_TYPE_MARINE_INCOVER );
			if ( ( pxCoverBehaviour && pxCoverBehaviour->IsFinished() ) || ( !pxCoverBehaviour ) )
			{
				SwitchToState( POSITION_EVALUATION );
				return;
			}

			Asura_ServerNode_Coverpoint* pxCoverPoint = Asura_ServerNode_Coverpoint::FindEntity( m_uBestCoverPoint );

			// If our Cover no longer provides cover then switch to position evaluation
			if ( pxCoverPoint )
			{
				if ( !pxCoverPoint->ProvidesCoverFrom( xThreatPos ) )
				{
					SwitchToState( POSITION_EVALUATION );
					return;
				}
			}

		}break;
		//---------------------------------------------------------------------------
		default:
		{

		}break;
	}
}

DEBUGVAR float dbg_fLongTimeOut = 10.0f;
DEBUGVAR float dbg_fShortTimeOut = 5.0f;

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: SwitchToState
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Attack::SwitchToState( UpdateState eState )
{
	// Do anything needed to leave the old state
	switch ( GetUpdateState() )
	{
		//---------------------------------------------------------------------------
		case POSITION_EVALUATION:
		{

		}break;
		//---------------------------------------------------------------------------
		case MOVE_TO_COVER:
		{
			if (eState != COVER)
			{
				Asura_ServerNode_Coverpoint* pxCoverPoint = Asura_ServerNode_Coverpoint::FindEntity( m_uBestCoverPoint );
				if ( pxCoverPoint )
				{
					pxCoverPoint->SetIsOccupied( false );
				}
			}

		}break;
		//---------------------------------------------------------------------------
		case COVER:
		{
			DestroyChildWithType(AVP_BEHAVIOUR_TYPE_MARINE_INCOVER);
			
			m_uBestCoverPoint = ASURA_GUID_UNREGISTERED;

			Asura_ServerNode_Coverpoint* pxCoverPoint = Asura_ServerNode_Coverpoint::FindEntity( m_uBestCoverPoint );
			if ( pxCoverPoint )
			{
				pxCoverPoint->SetIsOccupied( false );
			}

		}break;
		//---------------------------------------------------------------------------
		default:
		{

		}break;
		//---------------------------------------------------------------------------
	}

	SetUpdateState( eState );

	// Do anything needed to enter the new state here
	switch ( GetUpdateState() )
	{
		//---------------------------------------------------------------------------
		case POSITION_EVALUATION:
		{
			// Reset stuff for position evaluation
			m_bDefenseDamageReset = true;
			m_bAttackDamageReset = true;
			m_uPosEvalCounter = 0;
			m_fPosEvalUpdateTimer = dbg_fLongTimeOut;

		}break;
		//---------------------------------------------------------------------------
		case MOVE_TO_COVER:
		{
			// Create the cover behaviour and set the cover point
			Asura_ServerNode_Coverpoint* pxCoverPoint = Asura_ServerNode_Coverpoint::FindEntity( m_uBestCoverPoint );
			if ( pxCoverPoint )
			{
				pxCoverPoint->SetIsOccupied( true );
			}

		}break;
		//---------------------------------------------------------------------------
		case COVER:
		{
			// Create the cover behaviour and set the cover point
			AVP_Behaviour_Marine_InCover* pxCoverBehaviour = ( AVP_Behaviour_Marine_InCover* )CreateChildWithType( AVP_BEHAVIOUR_TYPE_MARINE_INCOVER );

			if ( pxCoverBehaviour )
			{
				pxCoverBehaviour->SetCoverPoint( m_uBestCoverPoint );
			}

		}break;
		//---------------------------------------------------------------------------
		default:
		{

		}break;
		//---------------------------------------------------------------------------
	}
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: ModifyDefensiveTimeout
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Attack::ModifyDefensiveTimeout()
{
	AVP_Navigation::NavData& xNavData = GetMarineBrain()->GetNavData();

	if (xNavData.m_pusRoute)
	{
		m_fDefensiveRouteDistance = AVP_Navigation::CalculateRouteDistance( xNavData, GetOurActor()->GetPosition(), xNavData.m_usRouteSegmentIndex );

		float fRouteDistanceSq = m_fDefensiveRouteDistance * m_fDefensiveRouteDistance;

		if ( fRouteDistanceSq <= 9.0f && m_uRouteTimeOutState != RD_TINY )
		{
			m_uRouteTimeOutState = RD_TINY;
			m_fDefensiveStanceTimeout = Asura_Random::GetFloat(1.0f, 2.0f);
		}
		else if ( fRouteDistanceSq > 9.0f && fRouteDistanceSq <= 25.0f && m_uRouteTimeOutState != RD_SHORT )
		{
			m_uRouteTimeOutState = RD_SHORT;
			m_fDefensiveStanceTimeout = Asura_Random::GetFloat(3.0f, 4.0f);
		}
		else if ( fRouteDistanceSq > 25.0f && fRouteDistanceSq <= 64.0f && m_uRouteTimeOutState != RD_MEDIUM_SHORT )
		{
			m_uRouteTimeOutState = RD_MEDIUM_SHORT;
			m_fDefensiveStanceTimeout = Asura_Random::GetFloat(5.0f, 6.0f);
		}
		else if ( fRouteDistanceSq > 64.0f && fRouteDistanceSq <= 100.0f  && m_uRouteTimeOutState != RD_MEDIUM )
		{
			m_uRouteTimeOutState = RD_MEDIUM;
			m_fDefensiveStanceTimeout = Asura_Random::GetFloat(7.0f, 8.0f);
		}
		else if ( fRouteDistanceSq > 100.0f && fRouteDistanceSq <= 225.0f && m_uRouteTimeOutState != RD_MEDIUM_LONG )
		{
			m_uRouteTimeOutState = RD_MEDIUM_LONG;
			m_fDefensiveStanceTimeout = Asura_Random::GetFloat(9.0f, 10.0f);
		}
		else if ( fRouteDistanceSq > 100.0f && fRouteDistanceSq <= 225.0f && m_uRouteTimeOutState != RD_LONG )
		{
			m_uRouteTimeOutState = RD_LONG; 
			m_fDefensiveStanceTimeout = Asura_Random::GetFloat(11.0f, 12.0f);
		}
		else if ( fRouteDistanceSq > 225.0f && m_uRouteTimeOutState != RD_HUGE)
		{
			m_uRouteTimeOutState = RD_HUGE; 
			m_fDefensiveStanceTimeout = Asura_Random::GetFloat(13.0f, 14.0f);
		}
	}
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: FindBestWaypoint
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Attack::FindBestWaypoint( AVP_Navigation::PosEvalFunctor* pxPosEval )
{
	u_int uBestWaypointID = pxPosEval->GetBestWaypointID();

	if ( uBestWaypointID != ASURA_INVALID_WAYPOINT_ID )
	{
		Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uBestWaypointID );
		AVP_Tactical_System* pxTactical	= GetMarineBrain()->GetTacticalSystem();

		if ( pxWaypoint )
		{
			if ( DoesWaypointContainCoverAndItsNotOccupied( uBestWaypointID ) )
			{
				if ( m_uBestCoverPoint != ASURA_GUID_UNREGISTERED )
				{
					Asura_ServerNode_Coverpoint* pxCoverPoint = Asura_ServerNode_Coverpoint::FindEntity( m_uBestCoverPoint );
					if ( pxCoverPoint )
					{
						pxTactical->SetDestination( pxCoverPoint->GetPosition() );
						pxTactical->SetAtDestinationRange( ONE_METER );
						pxTactical->SetMovementSpeed( AVP_Navigation::MM_MEDIUM );
						pxTactical->LetRouteSetLookAt( false );
						SwitchToState( MOVE_TO_COVER );
						return;
					}
				}
			}

			pxTactical->SetDestination( pxWaypoint->m_xPosition );
			pxTactical->SetAtDestinationRange( ONE_METER );
			pxTactical->LetRouteSetLookAt( false );
			pxTactical->SetMovementSpeed( AVP_Navigation::MM_MEDIUM );
		}
	}
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: DoesWaypointContainCoverAndItsNotOccupied
//---------------------------------------------------------------------------
bool AVP_Behaviour_Marine_Attack::DoesWaypointContainCoverAndItsNotOccupied( u_int uWaypointID )
{
	// Ensure we have a threat
	if ( !GetMarineBrain()->DoesHaveThreat() ) { return false; }

	// Ensure Our Waypoint Is Valid
	Asura_AI_NavVertex*					pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );
	if ( !pxWaypoint ) { return false; }

	// Ensure Our Volume Is Valid
	AVP_ServerEntity_GamePlayVolume*	pxVolume	= AVP_ServerEntity_GamePlayVolume::GetVolumeThatContainsPosition( pxWaypoint->GetPosition() );
	if ( ( !pxVolume ) || ( pxVolume->GetNumCoverPointGuids() == 0 ) ) { return false; }

	Asura_Vector_3 xThreatPos = Asura_Maths::ZeroVector3;

	if ( !GetMarineBrain()->GetKnowledgeSystem()->ShootAtScriptedTarget() || ( GetMarineBrain()->GetKnowledgeSystem()->ShootAtScriptedTarget() && GetMarineBrain()->GetKnowledgeSystem()->ScriptedTargetIsShootAtPoint() ) )
	{
		xThreatPos = GetKnowledgeSystem()->GetBestThreatKnowledge().m_xPosition;
	}
	else
	{
		// Ensure our threat is valid
		Asura_Guid							uThreatGuid	= GetMarineBrain()->GetKnowledgeSystem()->GetBestThreatKnowledge().GetGuid();
		AVP_ServerEntity_Actor*				pxThreat	= AVP_ServerEntity_Actor::FindEntity( uThreatGuid );
		if ( !pxThreat ) { return false; }

		xThreatPos = pxThreat->GetPosition();
	}

	// Reset the best cover point
	m_uBestCoverPoint = ASURA_GUID_UNREGISTERED;

	// Iterate around all cover points in the waypoints gameplay volume
	for ( u_int u = 0; u < pxVolume->GetNumCoverPointGuids(); ++u )
	{
		Asura_ServerNode_Coverpoint* pxCoverPoint = Asura_ServerNode_Coverpoint::FindEntity( pxVolume->GetCoverPointGuid( u ) );
		if ( !pxCoverPoint || !pxCoverPoint->IsEntityActive() || pxCoverPoint->IsOccupied() ) { continue; }

		// is the cover point in our waypoint
		if ( Asura_AI_Navigation_System::IsPositionInWaypoint( pxCoverPoint->GetPosition(), uWaypointID ) )
		{
			// Does it provide cover from our threat?
			if ( pxCoverPoint->ProvidesCoverFrom( xThreatPos ) )
			{
				// wow we have one!
				m_uBestCoverPoint = pxCoverPoint->GetGuid();
				break;
			}
		}
	}

	if ( m_uBestCoverPoint != ASURA_GUID_UNREGISTERED )
	{
		return true;
	}
	else
	{
		return false;
	}
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: CalculateEngagementRange
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Attack::CalculateEngagementRange(float* fMinEngagementRange, float* fMaxEngagementRange)
{
	*fMinEngagementRange = 15.0f;
	*fMaxEngagementRange = 20.0f;

	Asura_Vector_3 xThreatPos = Asura_Maths::ZeroVector3;
	float fThreatHealth = 0.0f;

	if ( !GetMarineBrain()->GetKnowledgeSystem()->ShootAtScriptedTarget() || ( GetMarineBrain()->GetKnowledgeSystem()->ShootAtScriptedTarget() && GetMarineBrain()->GetKnowledgeSystem()->ScriptedTargetIsShootAtPoint() ) )
	{
		xThreatPos = GetKnowledgeSystem()->GetBestThreatKnowledge().m_xPosition;
		fThreatHealth = 100.0f;
	}
	else
	{
		Asura_Guid uThreatGuid = GetKnowledgeSystem()->GetBestThreatKnowledge().GetGuid();
		if ( uThreatGuid == ASURA_GUID_UNREGISTERED ) { SetFinished(); return; }

		AVP_ServerEntity_Actor* pxThreatActor = AVP_ServerEntity_Actor::FindEntity( uThreatGuid );
		if ( !pxThreatActor ) { SetFinished(); return; }

		xThreatPos = pxThreatActor->GetPosition();
		fThreatHealth = pxThreatActor->GetHealth();
	}

	u_int uNumThreats		= GetKnowledgeSystem()->GetNumSecondaryThreats() + 1; // +1 for the primary threat.

	AVP_ServerEntity_Squad* pxSquad = AVP_ServerEntity_Squad::FindEntity( GetOurActor()->GetInitialSquadGuid() );

	if ( !pxSquad )
	{
		if (uNumThreats > 1)
		{
			*fMinEngagementRange = 15.0f;
			*fMaxEngagementRange = 20.0f;
		}
		else
		{
			float fDistanceToThreatSq = ( xThreatPos - GetOurActor()->GetPosition() ).MagnitudeSquared();

			if ( fDistanceToThreatSq > 625.0f )
			{
				*fMinEngagementRange = 15.0f;
				*fMaxEngagementRange = 20.0f;
			}
			else if  ( fDistanceToThreatSq > 400.0f )
			{
				*fMinEngagementRange = 10.0f;
				*fMaxEngagementRange = 15.0f;
			}
			else if ( fDistanceToThreatSq > 225.0f )
			{
				*fMinEngagementRange = 5.0f;
				*fMaxEngagementRange = 10.0f;
			}
			else
			{
				*fMinEngagementRange = 2.0f;
				*fMaxEngagementRange = 7.0f;
			}
		}
	}
	else
	{
		u_int uNumSquadMembers	= pxSquad->GetNumSquadMembers();

		if (uNumSquadMembers == uNumThreats)
		{
			if ( fThreatHealth < GetOurActor()->GetHealth() )
			{
				*fMinEngagementRange = 5.0f;
				*fMaxEngagementRange = 10.0f;
			}
			else
			{
				*fMinEngagementRange = 10.0f;
				*fMaxEngagementRange = 15.0f;
			}
		}
		else if ( uNumSquadMembers > uNumThreats )
		{
			*fMinEngagementRange = 5.0f;
			*fMaxEngagementRange = 10.0f;
		}
		else
		{
			*fMinEngagementRange = 15.0f;
			*fMaxEngagementRange = 20.0f;
		}
	}
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: PositionEvaluation
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Attack::PositionEvaluation( const float fTimeslice )
{
	float	fTimeSinceLastDamage	= GetOurActor()->GetLastDamageInterval();
	bool	bShouldBeDefensive		= false;

	if (fTimeSinceLastDamage < 0.06f)
	{
		m_bDefenseDamageReset = true;
		m_bAttackDamageReset = true;
	}

	// If we have taken damage in the last 3 seconds or we are reloading be defensive
	if ( ( ( fTimeSinceLastDamage <= m_fDefensiveStanceTimeout ) && ( fTimeSinceLastDamage != 0.0f ) ) || ( GetTacticalSystem()->IsReloadingWeapon() && !m_bBeingDefensive ) )
	{
		if (m_bDefenseDamageReset)
		{
			m_uPosEvalCounter = 0;
			m_fPosEvalUpdateTimer = dbg_fLongTimeOut;
			m_bDefenseDamageReset = false;
		}

		bShouldBeDefensive = true;
	}
	else if ( fTimeSinceLastDamage > m_fDefensiveStanceTimeout )
	{
		if (m_bAttackDamageReset)
		{
			m_uRouteTimeOutState = RD_LONG;
			m_fDefensiveStanceTimeout = Asura_Random::GetFloat(fDbg_ShortTimout, fDbg_LongTimout);
			m_uPosEvalCounter = 0;
			m_fPosEvalUpdateTimer = dbg_fLongTimeOut;
			m_bAttackDamageReset = false;
		}

		bShouldBeDefensive = false;
	}

	// Update out position evaluation timer.
	m_fPosEvalUpdateTimer += fTimeslice;

	if ( ( ( m_fPosEvalUpdateTimer > dbg_fLongTimeOut ) && ( m_uPosEvalCounter == 0 ) ) || 
		( ( m_fPosEvalUpdateTimer > dbg_fShortTimeOut ) && ( m_uPosEvalCounter != 0 ) ) )
	{
		AVP_Navigation::NavData&	xNavData		= GetMarineBrain()->GetNavData();
		Asura_Guid					uCombatZoneGuid	= GetMarineBrain()->GetCombatZoneGuid();

		if ( !bShouldBeDefensive )
		{
			float fMinEngagementRange = 0.0f;
			float fMaxEngagementRange = 0.0f;

			CalculateEngagementRange(&fMinEngagementRange, &fMaxEngagementRange);

			if ( m_uPosEvalCounter == 0 )
			{
				AVP_Navigation::AVP_PosEvalParams	xPosEvalParams( GetMarineBrain()->GetOwnerGuid(), xNavData.m_usCurrentWPID, 25.0f, fMinEngagementRange, fMaxEngagementRange, uCombatZoneGuid );
				AVP_Navigation::PosEvalFunctor		pxPosEval( xNavData.m_usCurrentWPID, 25.0f, xPosEvalParams, GetAttackingScoreForWaypoint);
				AVP_Navigation::SearchWaypoints( AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID, &pxPosEval );
				FindBestWaypoint(&pxPosEval);
			}
			else
			{
				AVP_Navigation::AVP_PosEvalParams	xPosEvalParams( GetMarineBrain()->GetOwnerGuid(), xNavData.m_usCurrentWPID, 20.0f, fMinEngagementRange, fMaxEngagementRange, uCombatZoneGuid );
				AVP_Navigation::PosEvalFunctor		pxPosEval( xNavData.m_usCurrentWPID, 15.0f, xPosEvalParams, GetAttackingScoreForWaypoint);
				AVP_Navigation::SearchWaypoints( AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID, &pxPosEval );
				FindBestWaypoint(&pxPosEval);
			}

			m_bBeingDefensive = false;
		}
		else
		{
			AVP_Navigation::AVP_PosEvalParams	xPosEvalParams( GetMarineBrain()->GetOwnerGuid(), xNavData.m_usCurrentWPID, 20.0f, 18.0f, 20.0f, uCombatZoneGuid );
			AVP_Navigation::PosEvalFunctor		pxPosEval( xNavData.m_usCurrentWPID, 25.0f, xPosEvalParams, GetDefendingScoreForWaypoint);
			AVP_Navigation::SearchWaypoints( AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID, &pxPosEval );
			FindBestWaypoint(&pxPosEval);
			m_bBeingDefensive = true;
		}

		++m_uPosEvalCounter;
		m_fPosEvalUpdateTimer = 0.0f;

		if ( m_uPosEvalCounter >= 4 )
		{
			m_uPosEvalCounter = 0;
		}
	}
}

//-----------------------------------------------------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: GetAttackingScoreForWaypoint
//-----------------------------------------------------------------------------------------------------------------------
u_int AVP_Behaviour_Marine_Attack::GetAttackingScoreForWaypoint( AVP_Navigation::AVP_PosEvalParams xPosEvalParams )
{
	AVP_ServerActor_Human* pxHuman = AVP_ServerActor_Human::FindEntity( xPosEvalParams.m_uActorGuid );

	u_int uScore = 0;

	// this should always be valid as he has just requested a position evaluation
	if ( !pxHuman ) { return uScore; }

	// Waypoint Proximity
	uScore += AVP_PositionEvaluation::GetProximityScore( pxHuman, xPosEvalParams.m_uTestWPID, xPosEvalParams.m_fEvaluationRange );

	AVP_Brain_Actor_Marine* pxBrain = (AVP_Brain_Actor_Marine*)pxHuman->GetActorBrain();

	if ( pxBrain )
	{
		if ( !pxBrain->GetKnowledgeSystem()->ShootAtScriptedTarget() || ( pxBrain->GetKnowledgeSystem()->ShootAtScriptedTarget() && !pxBrain->GetKnowledgeSystem()->ScriptedTargetIsShootAtPoint() ) )
		{
			// Line Of Fire To The Primary Threat
			uScore += AVP_PositionEvaluation::GetWaypointLOFtoPrimaryThreatScore( pxHuman, xPosEvalParams.m_uTestWPID, 40 );

			// Don't favour locations beyond the enemy
			uScore += AVP_PositionEvaluation::GetWaypointIsBeyondTargetScore( pxHuman, xPosEvalParams.m_uTestWPID, 40 );
		}
		else
		{
			uScore += AVP_PositionEvaluation::GetWaypointLOFToScriptedTarget( pxHuman, xPosEvalParams.m_uTestWPID, 40 );
		}
	}

	// Preferred Fighting Range
	uScore += AVP_PositionEvaluation::GetPreferredFightingRangeScore( pxHuman, xPosEvalParams.m_uTestWPID, xPosEvalParams.m_fMinFightingRange, xPosEvalParams.m_fMaxFightingRange, 20 );

	// Waypoint Occupied by ally
	uScore += AVP_PositionEvaluation::GetHumanoidPositionOccupiedByAllyScore( pxHuman, xPosEvalParams.m_uTestWPID, 30 );

	// Waypoint Gives no LOF from Secondary Threats
	uScore += AVP_PositionEvaluation::GetHumanoidPositionGivesCoverFromSecondaryThreatsScore( pxHuman, xPosEvalParams.m_uTestWPID, 30 );

	// Waypoint Has a cover point that provides cover from primary threat
	uScore += AVP_PositionEvaluation::GetPositionProvidesCoverFromPrimaryThreat( pxHuman, xPosEvalParams.m_uTestWPID, 60 );

	if ( pxHuman->GetInitialSquadGuid() != ASURA_GUID_UNREGISTERED )
	{
		AVP_ServerEntity_Squad_Marine* pxSquad = AVP_ServerEntity_Squad_Marine::FindEntity( pxHuman->GetInitialSquadGuid() );

		if ( ( !pxSquad ) || ( pxSquad->GetSquadLeaderGuid() == pxHuman->GetGuid() ) ) { return uScore; }

		// Waypoint Has a cover point that provides cover from primary threat
		uScore += AVP_PositionEvaluation::GetPositionCloseToSquadLeaderScore( pxHuman, xPosEvalParams.m_uTestWPID, 60);
	}

	return uScore;
}

//-----------------------------------------------------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: GetDefendingScoreForWaypoint
//-----------------------------------------------------------------------------------------------------------------------
u_int AVP_Behaviour_Marine_Attack::GetDefendingScoreForWaypoint( AVP_Navigation::AVP_PosEvalParams xPosEvalParams )
{
	AVP_ServerActor_Human* pxHuman = AVP_ServerActor_Human::FindEntity( xPosEvalParams.m_uActorGuid );

	u_int uScore = 0;

	// this should always be valid as he has just requested a position evaluation
	if ( !pxHuman ) { return uScore; }

	// Waypoint Proximity
	uScore += AVP_PositionEvaluation::GetProximityScore( pxHuman, xPosEvalParams.m_uTestWPID, xPosEvalParams.m_fEvaluationRange );

	AVP_Brain_Actor_Marine* pxBrain = (AVP_Brain_Actor_Marine*)pxHuman->GetActorBrain();

	if ( pxBrain )
	{
		if ( !pxBrain->GetKnowledgeSystem()->ShootAtScriptedTarget() || ( pxBrain->GetKnowledgeSystem()->ShootAtScriptedTarget() && !pxBrain->GetKnowledgeSystem()->ScriptedTargetIsShootAtPoint() ) )
		{
			// We don't want LOF to the primary threat because we don't want the primary threat to be able to hit us
			uScore += AVP_PositionEvaluation::GetWaypointNoLOFToPrimaryThreatScore( pxHuman, xPosEvalParams.m_uTestWPID, 40);

			// Don't favour locations beyond the enemy
			uScore += AVP_PositionEvaluation::GetWaypointIsBeyondTargetScore( pxHuman, xPosEvalParams.m_uTestWPID, 40 );
		}
		else
		{
			uScore += AVP_PositionEvaluation::GetWaypointNoLOFToScriptedTarget( pxHuman, xPosEvalParams.m_uTestWPID, 40 );
		}
	}

	// Waypoint Occupied by ally
	uScore += AVP_PositionEvaluation::GetHumanoidPositionOccupiedByAllyScore( pxHuman, xPosEvalParams.m_uTestWPID, 40 );

	// Waypoint Gives no LOF from Secondary Threats
	uScore += AVP_PositionEvaluation::GetHumanoidPositionGivesCoverFromSecondaryThreatsScore( pxHuman, xPosEvalParams.m_uTestWPID, 30 );

	// Waypoint Has a cover point that provides cover from primary threat
	uScore += AVP_PositionEvaluation::GetPositionProvidesCoverFromPrimaryThreat( pxHuman, xPosEvalParams.m_uTestWPID, 60 );

	if ( pxHuman->GetInitialSquadGuid() != ASURA_GUID_UNREGISTERED )
	{
		AVP_ServerEntity_Squad_Marine* pxSquad = AVP_ServerEntity_Squad_Marine::FindEntity( pxHuman->GetInitialSquadGuid() );

		if ( ( !pxSquad ) || ( pxSquad->GetSquadLeaderGuid() == pxHuman->GetGuid() ) ) { return uScore; }

		// Waypoint Has a cover point that provides cover from primary threat
		uScore += AVP_PositionEvaluation::GetPositionCloseToSquadLeaderScore( pxHuman, xPosEvalParams.m_uTestWPID, 60);
	}

	return uScore;
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack :: RenderDebugInfo
//---------------------------------------------------------------------------
#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Behaviour_Marine_Attack::RenderDebugInfo( void )
{

	PARENT::RenderDebugInfo();

	if (m_bBeingDefensive)
	{
		Asura_Vector_2 xOutputPos(GetProjectBrain()->GetDebugOutputPos().x, GetProjectBrain()->GetDebugOutputPos().y);
		Asura_Font_System::Print( xOutputPos.x, xOutputPos.y, 0.5f, 0xffffff00, ASURA_TEXTFORMAT_CENTREJUSTIFIED, "DEFENSIVE" );
		xOutputPos.y += 10.0f;
		GetProjectBrain()->GetDebugOutputPos().y += 10.0f;
		Asura_Font_System::Print( xOutputPos.x, xOutputPos.y, 0.5f, 0xffffffff, ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "Def Time Out %0.2f", m_fDefensiveStanceTimeout );
		xOutputPos.y += 10.0f;
		GetProjectBrain()->GetDebugOutputPos().y += 10.0f;
		Asura_Font_System::Print( xOutputPos.x, xOutputPos.y, 0.5f, 0xffffffff, ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "Def Timer %0.2f", GetOurActor()->GetLastDamageInterval() );
		xOutputPos.y += 10.0f;
		GetProjectBrain()->GetDebugOutputPos().y += 10.0f;
		Asura_Font_System::Print( xOutputPos.x, xOutputPos.y, 0.5f, 0xffffffff, ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "Route Dist %0.2f", m_fDefensiveRouteDistance );
	}
	else
	{
		Asura_Vector_2 xOutputPos(GetProjectBrain()->GetDebugOutputPos().x, GetProjectBrain()->GetDebugOutputPos().y);
		Asura_Font_System::Print( xOutputPos.x, xOutputPos.y, 0.5f, 0xffff0000, ASURA_TEXTFORMAT_CENTREJUSTIFIED, "AGGRESIVE" );
	}

	GetProjectBrain()->GetDebugOutputPos().y += 10.0f;


}
#endif

/// End Of File 'AVP_behaviour_Marine_Attack.cpp'
