// ------------------------------------------------------------------------------------------------------------------------------------
// File		: AVP_PositionEvaluation.cpp
// Created	: 29 / 05 / 08
// Author	: Gareth Thomas
// Copyright (C) 2008 Rebellion. All Rights Reserved.
// ------------------------------------------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------------------------------------------
// INCLUDES
// ------------------------------------------------------------------------------------------------------------------------------------

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

// This files header
#include "Core/AVP_PositionEvaluation.h"

// AVP
#include "Entity/GamePlayVolume/AVP_ServerEntity_GamePlayVolume.h"
#include "Entity/Locations/OrientedNode/CoverpointNode/AVP_ServerNode_CoverpointNode.h"
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Alien/AVP_ServerActor_Alien.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_ServerActor_Human.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ServerActor_Predator.h"

// AVP Axon
//#include "Core/AVP_Navigation.h"
#include "Core/BrainTypes/AVP_Brain_Actor.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Alien.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Civilian.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Marine.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Predator.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Sentrygun.h"
#include "Core/AVP_Waypoint_Manager.h"
#include "Knowledge/AVP_Knowledge_System_Actor.h"
#include "Tactical/AVP_Tactical_System.h"

Asura_Guid AVP_PositionEvaluation::s_uLastGPVGuid = ASURA_GUID_UNREGISTERED;

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : GetPrimaryThreatGuid
// Last modified	: 29 / 05 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
Asura_Guid AVP_PositionEvaluation::GetPrimaryThreatGuid(const AVP_ServerEntity_Actor* pxOurActor)
{
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer())
	{
		return ASURA_GUID_UNREGISTERED;
	}

	AVP_Brain_Actor* pxBrain = pxOurActor->GetActorBrain();

	if ( pxBrain )
	{
		return pxBrain->GetPrimaryThreatGuid();
	}
	else
	{
		return ASURA_GUID_UNREGISTERED;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : GetAlertGuid
// Last modified	: 10 / 06 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
Asura_Guid AVP_PositionEvaluation::GetAlertGuid( const AVP_ServerEntity_Actor* pxOurActor )
{
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer())
	{
		return ASURA_GUID_UNREGISTERED;
	}

	AVP_Brain_Actor* pxBrain = pxOurActor->GetActorBrain();

	if ( pxBrain )
	{
		return pxBrain->GetAlertGuid();
	}
	else
	{
		return ASURA_GUID_UNREGISTERED;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : GetWaypointNoLOFToPositionScore
// Last modified	: 02 / 07 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetWaypointNoLOFToPositionScore( const AVP_ServerEntity_Actor* pxOurActor, const u_int uWaypointID, const Asura_Vector_3 xPosition, const u_int uScoreIfNotInLOF )
{
	// If not Valid, Dead, not active or is a player return a zero score
	if ( !pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer() ) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );
	if ( !pxWaypoint ) { return 0; }

	Asura_Vector_3	xWaypointToPositionDirection	= xPosition - pxWaypoint->m_xPosition;
	float			fWaypointToPositionDistance		= xWaypointToPositionDirection.MagnitudeSquared();
	float			fMaxSectorLOFDistance			= AVP_Waypoint_Manager::GetMaxLOFValue( uWaypointID, xWaypointToPositionDirection );

	// If the distance to the the threat is closer than the distance to cover then we can see him
	if ( fWaypointToPositionDistance > ( fMaxSectorLOFDistance * fMaxSectorLOFDistance ) )
	{
		return uScoreIfNotInLOF;
	}

	return 0;
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : GetProximityToPositionScore
// Last modified	: 29 / 05 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetProximityToPositionScore( const u_int uWaypointID, const float fEvaluationRange, const Asura_Vector_3 xPosition, const float fScoreModifier /*= 1.0f*/ )
{
	Asura_AI_NavVertex*		pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint(uWaypointID);

	if ( !pxWaypoint ) { return 0; }

	float fDistance = ( pxWaypoint->m_xPosition - xPosition ).Magnitude();

	float fScore = ( fDistance / fEvaluationRange ) * fEvaluationRange;

	fScore = (fEvaluationRange - fScore) * 0.5f * fScoreModifier;

	return (u_int)fScore;
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : GetProximityScore
// Last modified	: 29 / 05 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetProximityScore(const AVP_ServerEntity_Actor* pxOurActor, const u_int uWaypointID, const float fEvaluationRange, const float fScoreModifier /*= 1.0f*/)
{
	Asura_AI_NavVertex*		pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint(uWaypointID);
	const Asura_Vector_3&	xOurPos		= pxOurActor->GetPosition();

	if ( !pxWaypoint ) { return 0; }

	float fDistance = ( pxWaypoint->m_xPosition - xOurPos ).Magnitude();

	float fScore = ( fDistance / fEvaluationRange ) * fEvaluationRange;

	fScore = (fEvaluationRange - fScore) * 0.5f * fScoreModifier;

	return (u_int)fScore;
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : GetProximityScore
// Last modified	: 29 / 05 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetProximityToThreatScore( const AVP_ServerEntity_Actor* pxOurActor, const u_int uWaypointID, const float fScoreModifier )
{
	Asura_AI_NavVertex*		pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint(uWaypointID);

	if ( !pxWaypoint ) { return 0; }

	Asura_Guid uPrimaryThreatGuid = GetPrimaryThreatGuid( pxOurActor );

	if ( uPrimaryThreatGuid == ASURA_GUID_UNREGISTERED ) { return 0; }

	AVP_ServerEntity_Actor* pxPrimaryThreat = AVP_ServerEntity_Actor::FindEntity( uPrimaryThreatGuid );
	if ( !pxPrimaryThreat ) { return 0; }

	float fDistance = ( pxPrimaryThreat->GetPosition() - pxWaypoint->m_xPosition ).MagnitudeSquared();

	if ( fDistance < 25.0f )
	{
		return 0;
	}
	else
	{
		float fScore = ( 1.0f / fDistance ) * 100.0f;
		fScore *= fScoreModifier;
		return (u_int)fScore;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : CalculateWaypointLOFtoPrimaryThreatScore
// Last modified	: 29 / 05 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetWaypointLOFtoPrimaryThreatScore(const AVP_ServerEntity_Actor* pxOurActor, const u_int uWaypointID, const u_int uScoreIfInLOF)
{
	// If not Valid, Dead, not active or is a player return a zero score
	if ( !pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer() ) { return 0; }

	Asura_Guid uPrimaryThreatGuid = GetPrimaryThreatGuid( pxOurActor );

	if ( uPrimaryThreatGuid == ASURA_GUID_UNREGISTERED ) { return 0; }

	AVP_ServerEntity_Actor* pxPrimaryThreat = AVP_ServerEntity_Actor::FindEntity( uPrimaryThreatGuid );
	if ( !pxPrimaryThreat ) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );
	if ( !pxWaypoint ) { return 0; }

	Asura_Vector_3	xWaypointToThreatDirection	= pxPrimaryThreat->GetPosition() - pxWaypoint->m_xPosition;
	float			fWaypointToThreatDistance	= xWaypointToThreatDirection.MagnitudeSquared();
	float			fMinDistanceToCover			= AVP_Waypoint_Manager::GetMinLOFValue( uWaypointID, xWaypointToThreatDirection );

	// If the distance to the the threat is closer than the distance to cover then we can see him
	if ( fWaypointToThreatDistance < ( fMinDistanceToCover * fMinDistanceToCover ) )
	{
		return uScoreIfInLOF;
	}

	return 0;
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : CalculateWaypointLOFtoPrimaryThreatScore
// Last modified	: 29 / 05 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetWaypointNoLOFToPrimaryThreatScore( const AVP_ServerEntity_Actor* pxOurActor, const u_int uWaypointID, const u_int uScoreIfNotInLOF )
{
	// If not Valid, Dead, not active or is a player return a zero score
	if ( !pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer() ) { return 0; }

	Asura_Guid uPrimaryThreatGuid = GetPrimaryThreatGuid( pxOurActor );

	if ( uPrimaryThreatGuid == ASURA_GUID_UNREGISTERED ) { return 0; }

	AVP_ServerEntity_Actor* pxPrimaryThreat = AVP_ServerEntity_Actor::FindEntity( uPrimaryThreatGuid );
	if ( !pxPrimaryThreat ) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );
	if ( !pxWaypoint ) { return 0; }

	Asura_Vector_3	xWaypointToThreatDirection	= pxPrimaryThreat->GetPosition() - pxWaypoint->m_xPosition;
	float			fWaypointToThreatDistance	= xWaypointToThreatDirection.MagnitudeSquared();
	//float			fMinDistanceToCover			= AVP_Waypoint_Manager::GetMinLOFValue( uWaypointID, xWaypointToThreatDirection );
	float			fMaxDistanceToCover			= AVP_Waypoint_Manager::GetMaxLOFValue( uWaypointID, xWaypointToThreatDirection );

	//fMinDistanceToCover = fMinDistanceToCover * fMinDistanceToCover;
	fMaxDistanceToCover = fMaxDistanceToCover * fMaxDistanceToCover;

	// If the distance to the the threat is closer than the distance to cover then we can see him
	if ( fWaypointToThreatDistance > fMaxDistanceToCover )
	{
		return uScoreIfNotInLOF;
	}

	return 0;
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : GetWaypointIsBeyondTargetScore
// Last modified	: 17 / 09 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetWaypointIsBeyondTargetScore( const AVP_ServerActor_Human* pxOurActor, const u_int uWaypointID, const u_int uScore )
{
	// If not Valid, Dead, not active or is a player return a zero score
	if ( !pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer() ) { return 0; }

	Asura_Guid uPrimaryThreatGuid = GetPrimaryThreatGuid( pxOurActor );

	if ( uPrimaryThreatGuid == ASURA_GUID_UNREGISTERED ) { return 0; }

	AVP_ServerEntity_Actor* pxPrimaryThreat = AVP_ServerEntity_Actor::FindEntity( uPrimaryThreatGuid );
	if ( !pxPrimaryThreat ) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );
	if ( !pxWaypoint ) { return 0; }

	Asura_Vector_3 xDirToTarget = pxPrimaryThreat->GetPosition() - pxOurActor->GetPosition();
	float fDistanceToTarget = xDirToTarget.Normalise();

	Asura_Vector_3 xDirToWP = pxWaypoint->m_xPosition - pxOurActor->GetPosition();
	float fDistanceToWP = xDirToWP.Normalise();

	float fDot = xDirToTarget * xDirToWP;

	if ( fDot >= 0.707f && fDot <= 1.0f )
	{
		if ( fDistanceToTarget < ( fDistanceToWP - 2.0f ) )
		{
			return 0;
		}
	}

	return uScore;
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : GetTraversalCostScore
// Last modified	: 02 / 10 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
// ***** NOTE ***** : FOR THIS TO WORK WE NEED ASYNCHRONOUS A* SEARCHING WORKING WE DO NOT CURRENTLY HAVE THIS! :(
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetTraversalCostScore( const AVP_ServerActor_Human* pxOurActor, AVP_Navigation::AVP_PosEvalParams xPosEvalParams, u_int uScore )
{
	// If not Valid, Dead, not active or is a player return a zero score
	if ( !pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer() ) { return 0; }

	Asura_Guid uPrimaryThreatGuid = GetPrimaryThreatGuid( pxOurActor );

	if ( uPrimaryThreatGuid == ASURA_GUID_UNREGISTERED ) { return 0; }

	AVP_ServerEntity_Actor* pxPrimaryThreat = AVP_ServerEntity_Actor::FindEntity( uPrimaryThreatGuid );
	if ( !pxPrimaryThreat ) { return 0; }

	Asura_AI_NavVertex*	pxStartWaypoint	= Asura_AI_Navigation_System::GetWaypoint( xPosEvalParams.m_uStartWaypointID );
	if ( !pxStartWaypoint ) { return 0; }

	Asura_AI_NavVertex*	pxTestWaypoint	= Asura_AI_Navigation_System::GetWaypoint( xPosEvalParams.m_uTestWPID );
	if ( !pxTestWaypoint ) { return 0; }

	AVP_Navigation::NavData xNavData;

	// Set the human path finding flags
	xNavData.m_uPathfinderFlags		= AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID;
	xNavData.m_eMovementMode		= AVP_Navigation::MM_MEDIUM;
	xNavData.m_usCurrentWPID		= (u_short)xPosEvalParams.m_uStartWaypointID;
	xNavData.m_xStartPosition		= pxStartWaypoint->GetPosition();
	xNavData.m_usDestinationWPID	= (u_short)xPosEvalParams.m_uTestWPID;
	xNavData.m_xDestinationPosition = pxTestWaypoint->GetPosition();
	xNavData.m_usCurrentZone		= pxStartWaypoint->m_usZone;
	xNavData.m_fCharacterHeight		= 2.0f;
	xNavData.m_fCharacterWidth		= 1.0f;

	AVP_Navigation::FindRoute(xNavData);

	float fRouteDistanceSq = AVP_Navigation::CalculateRouteDistance( xNavData, pxStartWaypoint->GetPosition(), 0 );
	fRouteDistanceSq = fRouteDistanceSq * fRouteDistanceSq;

	float fStraightLineDistanceSq = ( pxStartWaypoint->GetPosition() - pxTestWaypoint->GetPosition() ).MagnitudeSquared();

	if ( fRouteDistanceSq > fStraightLineDistanceSq )
	{
		float fPercentage = fStraightLineDistanceSq / fRouteDistanceSq;

		if (fPercentage < 0.7f)
		{
			return 0;
		}
		else
		{
			return uScore;
		}
	}

	return uScore;
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : GetWaypointLOFtoScriptedTarget
// Last modified	: 17 / 09 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetWaypointLOFToScriptedTarget( const AVP_ServerActor_Human* pxOurActor, const u_int uWaypointID, const u_int uScoreIfInLOF )
{
	// If not Valid, Dead, not active or is a player return a zero score
	if ( !pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer() ) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );
	if ( !pxWaypoint ) { return 0; }

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

	if (pxBrain)
	{
		Asura_Vector_3	xWaypointToThreatDirection	= pxBrain->GetKnowledgeSystem()->GetBestThreatKnowledge().m_xPosition - pxWaypoint->m_xPosition;
		float			fWaypointToThreatDistance	= xWaypointToThreatDirection.MagnitudeSquared();
		float			fMinDistanceToCover			= AVP_Waypoint_Manager::GetMinLOFValue( uWaypointID, xWaypointToThreatDirection );

		// If the distance to the the threat is closer than the distance to cover then we can see him
		if ( fWaypointToThreatDistance < ( fMinDistanceToCover * fMinDistanceToCover ) )
		{
			return uScoreIfInLOF;
		}
	}

	return 0;
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : GetWaypointNoLOFToScriptedTarget
// Last modified	: 17 / 09 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetWaypointNoLOFToScriptedTarget( const AVP_ServerEntity_Actor* pxOurActor, const u_int uWaypointID, const u_int uScoreIfNotInLOF )
{
	// If not Valid, Dead, not active or is a player return a zero score
	if ( !pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer() ) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );
	if ( !pxWaypoint ) { return 0; }

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

	if (pxBrain)
	{
		Asura_Vector_3	xWaypointToThreatDirection	= pxBrain->GetKnowledgeSystem()->GetBestThreatKnowledge().m_xPosition - pxWaypoint->m_xPosition;
		float			fWaypointToThreatDistance	= xWaypointToThreatDirection.MagnitudeSquared();
		float			fMaxDistanceToCover			= AVP_Waypoint_Manager::GetMaxLOFValue( uWaypointID, xWaypointToThreatDirection );

		fMaxDistanceToCover = fMaxDistanceToCover * fMaxDistanceToCover;

		// If the distance to the the threat is closer than the distance to cover then we can see him
		if ( fWaypointToThreatDistance > fMaxDistanceToCover )
		{
			return uScoreIfNotInLOF;
		}
	}

	return 0;
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : GetWaypointNoLOFToAlertScore
// Last modified	: 10 / 06 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetWaypointNoLOFToAlertScore( const AVP_ServerEntity_Actor* pxOurActor, const u_int uWaypointID, const u_int uScoreIfNotInLOF )
{
	// If not Valid, Dead, not active or is a player return a zero score
	if ( !pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer() ) { return 0; }

	Asura_Guid uAlertGuid = GetAlertGuid( pxOurActor );

	if ( uAlertGuid == ASURA_GUID_UNREGISTERED ) { return 0; }

	AVP_ServerEntity_Actor* pxAlertActor = AVP_ServerEntity_Actor::FindEntity( uAlertGuid );
	if ( !pxAlertActor ) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );
	if ( !pxWaypoint ) { return 0; }

	Asura_Vector_3	xWaypointToAlertDirection	= pxAlertActor->GetPosition() - pxWaypoint->m_xPosition;
	float			fWaypointToAlertDistance	= xWaypointToAlertDirection.MagnitudeSquared();
	float			fMaxDistanceToCover			= AVP_Waypoint_Manager::GetMaxLOFValue( uWaypointID, xWaypointToAlertDirection );

	// If the distance to the the threat is closer than the distance to cover then we can see him
	if ( fWaypointToAlertDistance > ( fMaxDistanceToCover * fMaxDistanceToCover ) )
	{
		return uScoreIfNotInLOF;
	}

	return 0;
}

// ------------------------------------------------------------------------------------------------------------------------------------
// Function			: AVP_PositionEvaluation : CalculatePreferredFightingRangeScore
// Last modified	: 29 / 05 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetPreferredFightingRangeScore(const AVP_ServerEntity_Actor* pxOurActor, const u_int uWaypointID, const float fMinPrefferedFightingRange, const float fMaxPrefferedFightingRange, const u_int uScoreIfInFiringRange)
{
	// If not Valid, Dead, not active or is a player return a zero score
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer()) { return 0; }
	
	Asura_Guid uPrimaryThreatGuid = GetPrimaryThreatGuid( pxOurActor );

	if (uPrimaryThreatGuid == ASURA_GUID_UNREGISTERED) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );  
	if ( !pxWaypoint ) { return 0; }

	AVP_ServerEntity_Actor* pxPrimaryThreat = AVP_ServerEntity_Actor::FindEntity(uPrimaryThreatGuid);
	if ( !pxPrimaryThreat ) { return 0; }

	float fDistanceToThreatSq = (pxPrimaryThreat->GetPosition() - pxWaypoint->m_xPosition).MagnitudeSquared();

	if ( ( fDistanceToThreatSq >= ( fMinPrefferedFightingRange * fMinPrefferedFightingRange ) ) && 
		( fDistanceToThreatSq <= ( fMaxPrefferedFightingRange * fMaxPrefferedFightingRange ) ) )
	{
		return uScoreIfInFiringRange;
	}
	else
	{
		return 0;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// AVP_PositionEvaluation :: GetPositionIsInFrontScore
// Last modified	: 08 / 09 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetPositionIsInFrontScore( const AVP_ServerEntity_Actor* pxOurActor, const u_int uWaypointID, const u_int uScore )
{
	// If not Valid, Dead, not active or is a player return a zero score
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer()) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );  
	if ( !pxWaypoint ) { return 0; }

	const Asura_Matrix_3x3& xOri = pxOurActor->GetOrientation();
	
	Asura_Vector_3 xFwd( xOri.e13, xOri.e23, xOri.e33 );
	const Asura_Vector_3&	xOurPos = pxOurActor->GetPosition();
	Asura_Vector_3	xDirToWP( pxWaypoint->GetPosition() - xOurPos );
	xDirToWP.Normalise();

	float fDot = xDirToWP * xFwd;

	if ( fDot >= 0.707f && fDot <= 1.0f )
	{
		return uScore;
	}
	else
	{
		return 0;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// AVP_PositionEvaluation :: GetPositionIsInFrontScore
// Last modified	: 08 / 09 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetPositionIsInFrontOfSquadScore( const AVP_ServerEntity_Actor* pxOurActor, const u_int uWaypointID, const u_int uScore )
{
	// If not Valid, Dead, not active or is a player return a zero score
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer()) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );  
	if ( !pxWaypoint ) { return 0; }

	if (pxOurActor->GetInitialSquadGuid() == ASURA_GUID_UNREGISTERED) { return 0; }

	AVP_ServerEntity_Squad_Marine* pxSquad = AVP_ServerEntity_Squad_Marine::FindEntity( pxOurActor->GetInitialSquadGuid() );
	if ( !pxSquad ) { return 0; }

	Asura_Vector_3 xAvgFwd( Asura_Maths::ZeroVector3 );
	u_int uNumProcessed = 0;

	for ( u_int u = 0; u < pxSquad->GetNumSquadMembers(); ++u )
	{
		AVP_ServerActor_Human* pxMember = AVP_ServerActor_Human::FindEntity( pxSquad->GetMemberGuid( u ) );

		if ( pxMember )
		{
			const Asura_Matrix_3x3& xOri = pxMember->GetOrientation();
			Asura_Vector_3 xFwd( xOri.e13, xOri.e23, xOri.e33 );

			xAvgFwd += xFwd;
			++uNumProcessed;
		}
	}

	xAvgFwd /= (float)uNumProcessed;

	Asura_Vector_3	xDirToWP( pxWaypoint->GetPosition() - pxOurActor->GetPosition() );
	xDirToWP.Normalise();

	float fDot = xDirToWP * xAvgFwd;

	if ( fDot >= 0.707f && fDot <= 1.0f )
	{
		return uScore;
	}
	else
	{
		return 0;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// AVP_PositionEvaluation :: GetPositionIsBehindScore
// Last modified	: 08 / 09 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetPositionIsBehindScore( const AVP_ServerEntity_Actor* pxOurActor, const u_int uWaypointID, const u_int uScore )
{
	// If not Valid, Dead, not active or is a player return a zero score
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer()) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uWaypointID );  
	if ( !pxWaypoint ) { return 0; }

	const Asura_Matrix_3x3& xOri = pxOurActor->GetOrientation();

	const Asura_Vector_3	xBwd( -xOri.e13, -xOri.e23, -xOri.e33 );
	const Asura_Vector_3&	xOurPos = pxOurActor->GetPosition();
	Asura_Vector_3	xDirToWP( pxWaypoint->GetPosition() - xOurPos );
	xDirToWP.Normalise();

	float fDot = xOurPos * xBwd;

	if ( fDot < 0.707f )
	{
		return uScore;
	}
	else
	{
		return 0;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// AVP_PositionEvaluation :: CalculatePositionOccupiedByAllyScore
// Last modified	: 29 / 05 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetHumanoidPositionOccupiedByAllyScore(const AVP_ServerActor_Human* pxOurActor, const u_int uWaypointID, const u_int uScoreIfAllyDoesNotOccupyPosition)
{
	// If not Valid, Dead, not active or is a player return a zero score
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer()) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint(uWaypointID);
	if (!pxWaypoint) { return 0; }

	bool bAllyAtPos = false;

	for (Asura_Hashed_List_It<AVP_ServerActor_Human> it(AVP_ServerActor_Human::GetList()); !it.Done(); it.Next())
	{
		AVP_ServerActor_Human* pxHuman = it.GetCurrent();

		if ((pxHuman) && (pxHuman->GetGuid() != pxOurActor->GetGuid()) && (pxHuman->IsEntityActive()) && (!pxHuman->IsDead()) && (pxHuman->GetClassification() == pxOurActor->GetClassification()))
		{
			float fDistanceSq = (pxWaypoint->m_xPosition - pxHuman->GetPosition()).MagnitudeSquared();
			float fTestDistanceSq = 16.0f;

			if (fDistanceSq <= fTestDistanceSq)
			{
				bAllyAtPos = true;
				break;
			}

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

			if (pxMarineBrain)
			{
				AVP_Tactical_System* pxHumanTactical = pxMarineBrain->GetTacticalSystem();

				if (pxHumanTactical)
				{
					float fDestDistSq = (pxHumanTactical->GetDestination() - pxWaypoint->m_xPosition).MagnitudeSquared();

					if (fDestDistSq <= fTestDistanceSq)
					{
						bAllyAtPos = true;
						break;
					}
				}
			}

		}
	}

	if (bAllyAtPos)
	{
		return 0;
	}
	else
	{
		return uScoreIfAllyDoesNotOccupyPosition;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// AVP_PositionEvaluation :: GetAlienPositionOccupiedByAllyScore
// Last modified	: 29 / 05 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetAlienPositionOccupiedByAllyScore(const AVP_ServerActor_Alien* pxOurActor, const u_int uWaypointID, const u_int uScoreIfAllyDoesNotOccupyPosition)
{
	// If not Valid, Dead, not active or is a player return a zero score
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer()) { return 0; }

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint(uWaypointID);
	if (!pxWaypoint) { return 0; }

	bool bAllyAtPos = false;

	for (Asura_Hashed_List_It<AVP_ServerActor_Alien> it(AVP_ServerActor_Alien::GetList()); !it.Done(); it.Next())
	{
		AVP_ServerActor_Alien* pxAlien = it.GetCurrent();

		if ((pxAlien) && (pxAlien->GetGuid() != pxOurActor->GetGuid()) && (pxAlien->IsEntityActive()) && (!pxAlien->IsDead()) && (pxAlien->GetClassification() == pxOurActor->GetClassification()))
		{
			float fDistanceSq = (pxWaypoint->m_xPosition - pxAlien->GetPosition()).MagnitudeSquared();
			float fTestDistanceSq = 16.0f;

			if (fDistanceSq <= fTestDistanceSq)
			{
				bAllyAtPos = true;
				break;
			}

			AVP_Brain_Actor_Alien* pxAlienBrain = (AVP_Brain_Actor_Alien*)pxAlien->GetActorBrain();

			if (pxAlienBrain)
			{
				AVP_Navigation::NavData& xNavData = pxAlienBrain->GetNavData();
				
				float fDestDistSq = (xNavData.m_xDestinationPosition - pxWaypoint->m_xPosition).MagnitudeSquared();

				if (fDestDistSq <= fTestDistanceSq)
				{
					bAllyAtPos = true;
					break;
				}
			}

		}
	}

	if (bAllyAtPos)
	{
		return 0;
	}
	else
	{
		return uScoreIfAllyDoesNotOccupyPosition;
	}
}

DEBUGVAR float dbg_Multiplier = 3.0f;

// ------------------------------------------------------------------------------------------------------------------------------------
// AVP_PositionEvaluation :: CalculatePositionCloseToSquadLeaderScore
// Last modified	: 03 / 06 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetPositionCloseToSquadLeaderScore(const AVP_ServerActor_Human* pxOurActor, const u_int uWaypointID, const u_int uScoreIfInCloseProximity)
{
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer())
	{
		return 0;
	}

	if (pxOurActor->GetInitialSquadGuid() == ASURA_GUID_UNREGISTERED)
	{
		return 0;
	}

	AVP_ServerEntity_Squad_Marine* pxSquad = AVP_ServerEntity_Squad_Marine::FindEntity(pxOurActor->GetInitialSquadGuid());
	if (!pxSquad)
	{
		return 0;
	}

	AVP_ServerActor_Human* pxSquadLeader = AVP_ServerActor_Human::FindEntity(pxSquad->GetSquadLeaderGuid());
	if (!pxSquadLeader || pxSquadLeader->GetGuid() == pxOurActor->GetGuid())
	{
		return 0;
	}

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint(uWaypointID);
	if (!pxWaypoint)
	{
		return 0;
	}


	Asura_Vector_3 xLeadersFuturePos = pxSquadLeader->GetPosition() + (pxSquadLeader->GetVelocity() * dbg_Multiplier);

	float fDistanceFromLeaderToWaypointSq = (xLeadersFuturePos - pxWaypoint->m_xPosition).MagnitudeSquared();

	float fTestDistanceSq = 400.0f;

	if (fDistanceFromLeaderToWaypointSq <= fTestDistanceSq)
	{
		return uScoreIfInCloseProximity;
	}
	else
	{
		return 0;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// AVP_PositionEvaluation :: GetPositionIsWaypointNExtToWall
// Last modified	: 11 / 09 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetPositionIsWaypointNextToWall( const u_int uWaypointID, const u_int uScore )
{
	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint(uWaypointID);
	
	if (!pxWaypoint)
	{
		return 0;
	}

	bool bHasSurfaceTransitionLink = false;

	for (u_int u = 0; u < pxWaypoint->GetNumLinks(); ++u )
	{
		const Asura_AI_NavConnection& xLink	= pxWaypoint->GetLink( u );
		u_int uTestWaypointIndex			= xLink.GetIndex();

		AVP_Navigation::LinkTransitionType uLinkType = AVP_Navigation::GetLinkTransitionType( (u_short)uWaypointID, (u_short)uTestWaypointIndex );

		if ( uLinkType != AVP_Navigation::LTT_NO_TRANSITION )
		{
			bHasSurfaceTransitionLink = true;
			break;
		}
	}

	if (bHasSurfaceTransitionLink)
	{
		return 0;
	}
	else
	{
		if (pxWaypoint->GetRadius() <= 0.5f)
		{
			return 0;
		}
		else
		{
			return uScore;
		}
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// AVP_PositionEvaluation :: GetPositionCloseToSquadLeaderDestinationScore
// Last modified	: 03 / 06 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetPositionCloseToSquadLeaderDestinationScore( const AVP_ServerActor_Human* pxOurActor, const u_int uWaypointID, const u_int uScoreIfInCloseProximityToLeadersDestination )
{
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer())
	{
		return 0;
	}

	if (pxOurActor->GetInitialSquadGuid() == ASURA_GUID_UNREGISTERED)
	{
		return 0;
	}

	AVP_ServerEntity_Squad_Marine* pxSquad = AVP_ServerEntity_Squad_Marine::FindEntity(pxOurActor->GetInitialSquadGuid());
	if (!pxSquad)
	{
		return 0;
	}

	AVP_ServerActor_Human* pxSquadLeader = AVP_ServerActor_Human::FindEntity(pxSquad->GetSquadLeaderGuid());
	if (!pxSquadLeader || pxSquadLeader->GetGuid() == pxOurActor->GetGuid() || pxSquadLeader->IsPlayer())
	{
		return 0;
	}

	Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint(uWaypointID);
	if (!pxWaypoint)
	{
		return 0;
	}

	const AVP_Navigation::NavData& xNavData = pxSquadLeader->GetActorBrain()->GetNavData();

	float fDistanceFromLeaderToWaypointSq = (xNavData.m_xDestinationPosition - pxWaypoint->m_xPosition).MagnitudeSquared();

	float fTestDistanceSq = 100.0f;

	if (fDistanceFromLeaderToWaypointSq <= fTestDistanceSq)
	{
		return uScoreIfInCloseProximityToLeadersDestination;
	}
	else
	{
		return 0;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// AVP_PositionEvaluation :: GetHumanoidPositionGivesCoverFromSecondaryThreatsScore
// Last modified	: 30 / 05 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetHumanoidPositionGivesCoverFromSecondaryThreatsScore(const AVP_ServerActor_Human* pxOurActor, const u_int uWaypointID, const u_int uScoreIfProvidesGoodCover)
{
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer())
	{
		return 0;
	}

	Asura_AI_NavVertex* pxWaypoint = Asura_AI_Navigation_System::GetWaypoint( uWaypointID );

	if ( !pxWaypoint )
	{
		return 0;
	}

	// If the Knowledge system isn't valid or we don't have any secondary threats just return 0;
	AVP_Knowledge_System_Actor*	pxKnowledgeSystem = ( ( AVP_Brain_Actor_Marine* )pxOurActor->GetActorBrain() )->GetKnowledgeSystem();
	
	if ( ( !pxKnowledgeSystem ) || ( pxKnowledgeSystem->GetNumSecondaryThreats() == 0 ) )
	{
		return 0;
	}

	u_int uProvidesCoverFromThreatCount = 0;

	for ( u_int u = 0; u < pxKnowledgeSystem->GetNumSecondaryThreats(); ++u )
	{
		const Axon_Knowledge& xThreat = pxKnowledgeSystem->GetSecondaryThreatKnowledge( u );

		if ( xThreat.GetGuid() == ASURA_GUID_UNREGISTERED )
		{
			continue;
		}

		const AVP_ServerEntity_Actor* pxThreatActor = AVP_ServerEntity_Actor::FindEntity( xThreat.GetGuid() );

		if ( !pxThreatActor )
		{
			continue;
		}

		Asura_Vector_3	xWaypointToThreatDirection	= pxThreatActor->GetPosition() - pxWaypoint->m_xPosition;
		float			fWaypointToThreatDistanceSq = xWaypointToThreatDirection.MagnitudeSquared();

		float fMinDistanceToCoverSq = AVP_Waypoint_Manager::GetMinLOFValue(uWaypointID, xWaypointToThreatDirection);
		fMinDistanceToCoverSq = fMinDistanceToCoverSq * fMinDistanceToCoverSq;

		if (fWaypointToThreatDistanceSq > fMinDistanceToCoverSq)
		{
			++uProvidesCoverFromThreatCount;
		}
	}

	if ( uProvidesCoverFromThreatCount >= pxKnowledgeSystem->GetNumSecondaryThreats() )
	{
		return uScoreIfProvidesGoodCover;
	}
	else
	{
		return 0;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// AVP_PositionEvaluation :: GetPositionProvidescoverFromPrimaryThreat
// Last modified	: 30 / 05 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetPositionProvidesCoverFromPrimaryThreat(const AVP_ServerActor_Human* pxOurActor, const u_int uWaypointID, const u_int uScoreIfWaypointProvidesCover)
{
	if (!pxOurActor || pxOurActor->IsDead() || !pxOurActor->IsEntityActive() || pxOurActor->IsPlayer())
	{
		return 0;
	}

	Asura_AI_NavVertex* pxWaypoint = Asura_AI_Navigation_System::GetWaypoint( uWaypointID );

	if ( !pxWaypoint )
	{
		return 0;
	}

	AVP_ServerEntity_GamePlayVolume* pxVolume = AVP_ServerEntity_GamePlayVolume::GetVolumeThatContainsPosition( pxWaypoint->m_xPosition, s_uLastGPVGuid );

	if ( !pxVolume )
	{
		return 0;
	}

	s_uLastGPVGuid = pxVolume->GetGuid();

	Asura_Guid uPrimaryThreatGuid = GetPrimaryThreatGuid(pxOurActor);

	if (uPrimaryThreatGuid == ASURA_GUID_UNREGISTERED)
	{
		return 0;
	}

	AVP_ServerEntity_Actor* pxThreatActor = AVP_ServerEntity_Actor::FindEntity( uPrimaryThreatGuid );

	if ( !pxThreatActor )
	{
		return 0;
	}

	bool bInWaypointAndProvidesCoverFromThreat = false;

	for ( u_int u = 0; u < pxVolume->GetNumCoverPointGuids(); ++u )
	{
		Asura_ServerNode_Coverpoint* pxCoverNode = Asura_ServerNode_Coverpoint::FindEntity( pxVolume->GetCoverPointGuid( u ) );

		if ( !pxCoverNode || !pxCoverNode->IsEntityActive() || pxCoverNode->IsOccupied() )
		{
			continue;
		}

		if ( !Asura_AI_Navigation_System::IsPositionInWaypoint( pxCoverNode->GetPosition(), uWaypointID ) ) 
		{
			continue;
		}

		if ( !pxCoverNode->ProvidesCoverFrom( pxThreatActor->GetPosition() ) )
		{
			continue;
		}

		Asura_Vector_3 xDirToThreat( pxThreatActor->GetPosition() - pxCoverNode->GetPosition() );
		float fDistanceSq = xDirToThreat.MagnitudeSquared();

		if ( fDistanceSq < 64.0f )
		{
			continue;
		}

		bInWaypointAndProvidesCoverFromThreat = true;
		break;
	}

	if ( bInWaypointAndProvidesCoverFromThreat )
	{
		return uScoreIfWaypointProvidesCover;
	}
	else
	{
		return 0;
	}
}

// ------------------------------------------------------------------------------------------------------------------------------------
// AVP_PositionEvaluation :: GetPositionProvidescoverFromPrimaryThreat
// Last modified	: 02 / 07 / 08
// ------------------------------------------------------------------------------------------------------------------------------------
u_int AVP_PositionEvaluation::GetWaypointProvidesCoverFromPosition(const Asura_Vector_3 xPosition, const u_int uWaypointID, const u_int uScoreIfWaypointProvidesCover )
{
	Asura_AI_NavVertex* pxWaypoint = Asura_AI_Navigation_System::GetWaypoint( uWaypointID );

	if ( !pxWaypoint )
	{
		return 0;
	}

	AVP_ServerEntity_GamePlayVolume* pxVolume = AVP_ServerEntity_GamePlayVolume::GetVolumeThatContainsPosition( pxWaypoint->m_xPosition );

	if ( !pxVolume )
	{
		return 0;
	}

	bool bInWaypointAndProvidesCoverFromPosition = false;

	for ( u_int u = 0; u < pxVolume->GetNumCoverPointGuids(); ++u )
	{
		Asura_ServerNode_Coverpoint* pxCoverNode = Asura_ServerNode_Coverpoint::FindEntity( pxVolume->GetCoverPointGuid( u ) );

		if ( !pxCoverNode || !pxCoverNode->IsEntityActive() || pxCoverNode->IsOccupied() )
		{
			continue;
		}

		if ( !Asura_AI_Navigation_System::IsPositionInWaypoint( pxCoverNode->GetPosition(), uWaypointID ) ) 
		{
			continue;
		}

		if ( !pxCoverNode->ProvidesCoverFrom( xPosition ) )
		{
			continue;
		}

		bInWaypointAndProvidesCoverFromPosition = true;
		break;
	}

	if ( bInWaypointAndProvidesCoverFromPosition )
	{
		return uScoreIfWaypointProvidesCover;
	}
	else
	{
		return 0;
	}
}