//////////////////////////////////////////////////////////////////////////////
//
// AVP_Sense_Vision_Predator.cpp
//
// Created by NJD - 06 December 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

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

// This files header
#include "Knowledge/SenseTypes/AVP_Sense_Vision_Predator.h"

#include "Debugging/Asura_DebugLineRender.h"

// Asura
#include "Blueprint/Asura_Blueprint.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Entity/PhysicalObject/Moves/Asura_Movement_System.h"

#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Rendering/Asura_Font_System.h"
#endif

// Axon
#include "Core/AVP_Waypoint_Manager.h"
#include "Knowledge/AVP_Knowledge_System_Predator.h"
#include "Knowledge/SenseTypes/Axon_Sense_Types.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Predator.h"

// AVP
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ServerActor_Predator.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Sense_Vision_Predator
//
//////////////////////////////////////////////////////////////////////////////

#ifndef ASURA_NO_DEBUG_OUTPUT
Asura_Guid AVP_Sense_Vision_Predator::s_uRenderGuid = 999;
float AVP_Sense_Vision_Predator::s_fTotalScore = 0.0f;
float AVP_Sense_Vision_Predator::s_fSpeedScore = 0.0f;
float AVP_Sense_Vision_Predator::s_fRangeScore = 0.0f;
float AVP_Sense_Vision_Predator::s_fFOVScore = 0.0f;
float AVP_Sense_Vision_Predator::s_fThreshold = 0.0f;
#endif

DEBUGVAR float dbg_fVisionCutOffDistance = 20.0f;
DEBUGVAR float dbg_fMaxVisionRange = 200.0f;
DEBUGVAR float dbg_fFovX = 100.0f;
DEBUGVAR float dbg_fFovYUp = 70.0f;
DEBUGVAR float dbg_fFovYDown = 70.0f;


//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: Constructor
//---------------------------------------------------------------------------
AVP_Sense_Vision_Predator::AVP_Sense_Vision_Predator(AVP_Knowledge_System_Predator* pxAVPKnowledgeSystem)
: PARENT( pxAVPKnowledgeSystem )
, m_fVisionCutOffDistanceSq(dbg_fVisionCutOffDistance*dbg_fVisionCutOffDistance)
, m_fVisionMemoryTime(5.0f)
, m_fAlienPlayerScore(0.0f)
, m_fAlienPlayerScoreToBeat(200.0f)
, m_uLongMemoryVisionGuid(ASURA_GUID_UNREGISTERED)
{
	m_fRange = dbg_fMaxVisionRange;
	m_fTanFovX = Asura_Maths::Tangent(Asura_Maths::DegToRad(dbg_fFovX) * 0.5f);
	m_fTanFovYUp = Asura_Maths::Tangent(Asura_Maths::DegToRad(dbg_fFovYUp));
	m_fTanFovYDown = Asura_Maths::Tangent(Asura_Maths::DegToRad(dbg_fFovYDown));
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: Deconstructor
//---------------------------------------------------------------------------
AVP_Sense_Vision_Predator::~AVP_Sense_Vision_Predator() 
{
	DestroyAllKnowledge();
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: InitialiseFromBlueprint
//---------------------------------------------------------------------------
void AVP_Sense_Vision_Predator::InitialiseFromBlueprint(const Asura_Blueprint& xBlueprint)
{
	PARENT::InitialiseFromBlueprint(xBlueprint);

	// We also need to initialise from our AI settings blueprint currently.
	InitFromSettingsBlueprint();
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: ReadFromChunkStream
//---------------------------------------------------------------------------
void AVP_Sense_Vision_Predator::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
    int iVersion;
    xStream >> iVersion;

    PARENT::ReadFromChunkStream(xStream);

    Asura_Assertf(iVersion == CURRENT_CHUNK_VERSION, ("AVP_Sense_Vision_Predator::ReadFromChunkStream - unhandled chunk version (%d)", iVersion));
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: WriteToChunkStream
//---------------------------------------------------------------------------
void AVP_Sense_Vision_Predator::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
    xStream << CURRENT_CHUNK_VERSION;

    PARENT::WriteToChunkStream(xStream);
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: Update
//---------------------------------------------------------------------------
void AVP_Sense_Vision_Predator::Update(const float fTimeslice) 
{
	// Reset these every update
	m_fAlienPlayerScore = 0.0f;
	m_fAlienPlayerScoreToBeat = GetScoreToBeat();

	AVP_ServerEntity_Actor* pxServerActor = GetActorKnowledgeSystem()->GetActorBrain()->GetActor();

	pxServerActor->GetHeadPosition(m_xPosition);
	pxServerActor->GetHeadFowardVector(m_xDirection);

	DepricateCurrentKnowledge(fTimeslice);

	AddNewKnowledge();
}


#ifndef ASURA_NO_DEBUG_OUTPUT
//---------------------------------------------------------------------------
// AVP_Sense_Vision :: RenderKnowledgeDebugInfo
//---------------------------------------------------------------------------
void AVP_Sense_Vision_Predator::RenderKnowledgeDebugInfo(Asura_Guid uGuid)
{
	if (uGuid != ASURA_GUID_UNREGISTERED)
	{
		s_uRenderGuid = uGuid;
		AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity(uGuid);

		if (pxActor)
		{
			Asura_Vector_2		xPos			= Asura_Vector_2(20.0f, 20.0f);
			u_int				uFlags			= ASURA_TEXTFORMAT_LEFTJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING;

			Asura_Font_System::Print(xPos.x, xPos.y, 0.5f, 0xffffffff,	uFlags,	"Guid Of Actor %u",	s_uRenderGuid);
			xPos.y += 20.0f;
			Asura_Font_System::Print(xPos.x, xPos.y, 0.5f, 0xffffffff,	uFlags,	"Speed Score %.2f",	s_fSpeedScore);
			xPos.y += 20.0f;
			Asura_Font_System::Print(xPos.x, xPos.y, 0.5f, 0xffffffff,	uFlags,	"Range Score %.2f",	s_fRangeScore);
			xPos.y += 20.0f;
			Asura_Font_System::Print(xPos.x, xPos.y, 0.5f, 0xffffffff,	uFlags,	"FOV Score %.2f",	s_fFOVScore);
			xPos.y += 20.0f;
			xPos.y += 20.0f;
			Asura_Font_System::Print(xPos.x, xPos.y, 0.5f, 0xffffffff,	uFlags,	"TOTAL Score %.2f",	s_fTotalScore);
			xPos.y += 20.0f;
			Asura_Font_System::Print(xPos.x, xPos.y, 0.5f, 0xffffffff,	uFlags,	"Threshold %.0f", s_fThreshold);
			xPos.y += 20.0f;
		}
	}
}
#endif

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: DepricateCurrentKnowledge
//---------------------------------------------------------------------------
void AVP_Sense_Vision_Predator::DepricateCurrentKnowledge(const float fTimeslice)
{
	// Age the vision knowledge. Cull any that are too old
	Asura_Hashed_List_Deleter_It<Axon_Knowledge> xVisionIter(&m_xVisionList);

	while (!xVisionIter.Done())
	{
		Axon_Knowledge* pxKnowledge = xVisionIter.GetCurrent();
		const AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity(pxKnowledge->GetGuid());

		bool bDeleteKnowledge = false;

		// Actor validation, pointer check, dead check is active check
		if (!pxActor || pxActor->IsDead() || !pxActor->IsEntityActive())
		{
			bDeleteKnowledge = true;
		}

		// Knowledge time out
		pxKnowledge->m_fAge += fTimeslice;

		if (pxKnowledge->m_fAge >= m_fKnowledgeLifetime && pxKnowledge->GetGuid() != m_uLongMemoryVisionGuid)
		{
			bDeleteKnowledge = true;
		}

		if (bDeleteKnowledge)
		{
			xVisionIter.DeleteCurrent();
			m_pxKnowledgeSystem->DestroyKnowledge(pxKnowledge);
		}
		else 
		{
			xVisionIter.Next();
		}
	}
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: AddNewKnowledge
//---------------------------------------------------------------------------
void AVP_Sense_Vision_Predator::AddNewKnowledge()
{
	AVP_ServerEntity_Actor* pxServerActor = GetActorKnowledgeSystem()->GetActorBrain()->GetActor();

	for (Asura_Hashed_List_ConstIt<AVP_ServerEntity_Actor> xIt(AVP_ServerEntity_Actor::GetList()); !xIt.Done(); xIt.Next())
	{
		const AVP_ServerEntity_Actor*	pxActor = xIt.GetCurrent();		

		// isActor Valid
		if (!pxActor)
			continue;

		// Ignore ourselves.
		if (pxServerActor->GetGuid() == pxActor->GetGuid())
			continue;

		// is the actor dead or inactive, don't remove from the list as he will be removed next time
		if (pxActor->IsDead() || !pxActor->IsEntityActive())
			continue;

		AVP_ClientEntity_Actor* pxClient = AVP_ClientEntity_Actor::FindEntity(pxActor->GetGuid());

		// For cases of spawned actors make sure we have started to render them before trying to use them for knowledge.
		if (pxClient && pxClient->GetShapeInstance().GetAnimation() && pxClient->GetShapeInstance().GetAnimation()->GetTweenStore())
		{
			if (!pxClient->GetShapeInstance().GetAnimation()->GetTweenStore()->IsPositionDataValid())
			{
				continue;
			}
		}
		else
		{
			continue;
		}


		// We need to store things specifically for the player alien
		bool bIsAlienPlayer = false;

		if ((pxActor->GetClassification() == AVP_ENTITYCLASS_ALIEN) && (pxActor->IsPlayer()))
		{
			bIsAlienPlayer = true;
		}

		Axon_Knowledge* pxExistingKnowledge = m_xVisionList.FindObject(pxActor->GetGuid());

		bool bRememberingActor = false;
		if (pxExistingKnowledge && pxExistingKnowledge->m_fAge < m_fVisionMemoryTime)
		{
			bRememberingActor = true;
		}

		bool bActuallySee = true;

		// If it is outside our range or FOV then move on to the next
		if (ShouldCullActorOnRangeOrFOV(pxServerActor, pxActor))
		{
#ifndef ASURA_NO_DEBUG_OUTPUT
			if (pxActor->GetGuid() == s_uRenderGuid)
			{
				s_fTotalScore = s_fSpeedScore = s_fRangeScore = s_fFOVScore = 0.0f;
			}
#endif

			if (bRememberingActor)
			{
				bActuallySee = false;
			}
			else
			{
				continue;
			}
		}

		// check if in LOS
		if (!LOSCheckOnActor(pxActor))
		{
#ifndef ASURA_NO_DEBUG_OUTPUT
			if (pxActor->GetGuid() == s_uRenderGuid)
			{
				s_fTotalScore = s_fSpeedScore = s_fRangeScore = s_fFOVScore = 0.0f;
			}
#endif
			if (bRememberingActor)
			{
				bActuallySee = false;
			}
			else
			{
				continue;
			}
		}

		if (pxExistingKnowledge)
		{
			GetActorKnowledgeSystem()->ResetAlertTimer();

			// Update the old one.
			if (bActuallySee)
			{
				pxExistingKnowledge->m_fAge       = 0.0f;
			}
			pxExistingKnowledge->m_xPosition  = pxActor->GetPosition();
			pxExistingKnowledge->m_xDirection = pxActor->GetPosition() - pxServerActor->GetPosition();
			pxExistingKnowledge->m_fDistance  = pxExistingKnowledge->m_xDirection.Normalise();

			// Alien player has been spotted so set the score to indicate as such.
			if (bIsAlienPlayer)
			{
				m_fAlienPlayerScore = 200.0f;
				m_fAlienPlayerScoreToBeat = 100.0f;
			}
		}
		else
		{
			float fActorThreatScore = 0.0f;

			//---------------------------------------------------------------------------------------------------------------
			//	Threat Score is determined by three things
			// 
			//	1.) The speed the actor is moving, returns a value between 0.0f and 100.0f.
			//	2.) The score for the range from our actor, returns a value between 0.0f and 100.0f.
			//	3.) The position in our actors FOV returns a smaller value if the actor is out to the edges of our vision -
			//		Returns a value between 0.0f and 100.0f.
			//---------------------------------------------------------------------------------------------------------------

#ifndef ASURA_NO_DEBUG_OUTPUT
			if (pxActor->GetGuid() == s_uRenderGuid)
			{
				// Get the score for the speed the actor is moving
				s_fSpeedScore = GetScoreForSpeed(pxActor);

				// Get the score for the range of the actor
				s_fRangeScore = GetScoreForRange(pxServerActor->GetPosition(), pxActor->GetPosition());

				// Get the score for the position in the FOV
				s_fFOVScore = GetScoreForFOVPosition(pxServerActor, pxActor->GetPosition());

				fActorThreatScore = s_fTotalScore = s_fSpeedScore + s_fRangeScore + s_fFOVScore;
				s_fThreshold = m_fThreshold;
			}
			else
#endif
			{
				// Get the score for the speed the actor is moving
				fActorThreatScore += GetScoreForSpeed(pxActor);

				// Get the score for the range of the actor
				fActorThreatScore += GetScoreForRange(pxServerActor->GetPosition(), pxActor->GetPosition());

				// Get the score for the position in the FOV
				fActorThreatScore += GetScoreForFOVPosition(pxServerActor, pxActor->GetPosition());
			}

			// if the actor we are checking against is the alien player update the threat scores
			if (bIsAlienPlayer)
			{
				m_fAlienPlayerScore			= fActorThreatScore;
				m_fAlienPlayerScoreToBeat	= GetScoreToBeat();
			}

			// have we beaten our current alertness levels adding knowledge score
			if (fActorThreatScore > GetScoreToBeat())
			{
				// add new knowledge.
				Axon_Knowledge* pxKnowledge = m_pxKnowledgeSystem->CreateKnowledge();
				if (pxKnowledge)
				{
					GetActorKnowledgeSystem()->ResetAlertTimer();

					pxKnowledge->m_uKnowledgeID          = m_pxKnowledgeSystem->GetNewKnowledgeID();
					pxKnowledge->m_uSourceGuid           = pxActor->GetGuid();
					pxKnowledge->m_uSourceClassification = pxActor->GetClassification();
					pxKnowledge->m_iSenseID              = AXON_SENSE_TYPE_VISION;
					pxKnowledge->m_uTypeInformation      = 0;
					pxKnowledge->m_fAge                  = 0.0f;
					pxKnowledge->m_fThreatValue          = 0.0f;
					pxKnowledge->m_xPosition             = pxActor->GetPosition();
					pxKnowledge->m_xDirection            = pxActor->GetPosition() - pxServerActor->GetPosition();
					pxKnowledge->m_fDistance             = pxKnowledge->m_xDirection.Normalise();

					// Add it to the knowledge list.
					m_xVisionList.AddToList(pxKnowledge);
				}
			}
		}
	}
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: LOSCheckOnActor
//---------------------------------------------------------------------------
bool AVP_Sense_Vision_Predator::LOSCheckOnActor(const AVP_ServerEntity_Actor* pxThreatActor, const bool bFOV)
{	
	const AVP_ServerEntity_Actor* pxOurActor = GetActorKnowledgeSystem()->GetActorBrain()->GetActor();

	// Simple LOS check
	if (pxOurActor && pxThreatActor)
	{
		// Make sure we are all alive and active
		if (!pxOurActor->IsEntityActive() || !pxThreatActor->IsEntityActive() || pxOurActor->IsDead() || pxThreatActor->IsDead())
		{
			return false;
		}

		// Get the eye position
		Asura_Vector_3 xEyePosition = pxOurActor->GetPosition();
		pxOurActor->GetHeadPosition(xEyePosition);

		// test vs the legs
		{
			// Get the Threat chest position
			Asura_Vector_3 xThreatPosition = pxThreatActor->GetPosition();
			pxThreatActor->GetHeadPosition(xThreatPosition);

			// Get the direction of the LOS
			Asura_Vector_3 xDirection = xThreatPosition - xEyePosition;
			const float    fDistance  = xDirection.Normalise();

			const Asura_LOS_InputData   xInputData(xEyePosition, xDirection, pxOurActor->GetGuid(), fDistance, true);
			const Asura_LOS_OutputData* pxOutputData = Asura_LOS::TestAgainstEverything(&xInputData);

			// if we didn't hit the environment and hit our Threat we have a sighting.
			if ((!pxOutputData->m_bHitEnvironment) && (pxOutputData->m_uHitEntity == pxThreatActor->GetGuid()))
			{
				return true;
			}
		}

		// test vs the head
		{
			// Get the Threat chest position
			Asura_Vector_3 xThreatPosition = pxThreatActor->GetPosition();
			pxThreatActor->GetHeadPosition(xThreatPosition);

			// Get the direction of the LOS
			Asura_Vector_3 xDirection = xThreatPosition - xEyePosition;
			const float    fDistance  = xDirection.Normalise();

			const Asura_LOS_InputData   xInputData(xEyePosition, xDirection, pxOurActor->GetGuid(), fDistance, true);
			const Asura_LOS_OutputData* pxOutputData = Asura_LOS::TestAgainstEverything(&xInputData);

			// if we didn't hit the environment and hit our Threat we have a sighting.
			if ((!pxOutputData->m_bHitEnvironment) && (pxOutputData->m_uHitEntity == pxThreatActor->GetGuid()))
			{
				return true;
			}
		}

		return false;
	}

	Asura_Assertf(pxOurActor, ("Invalid source actor."));
	Asura_Assertf(pxThreatActor, ("Invalid threat actor."));
	return false;
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: GetAVPKnowledgeSystem
//---------------------------------------------------------------------------
AVP_Knowledge_System_Predator* AVP_Sense_Vision_Predator::GetActorKnowledgeSystem()
{
    return static_cast< AVP_Knowledge_System_Predator* >( GetKnowledgeSystem() );
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: 
//---------------------------------------------------------------------------
void AVP_Sense_Vision_Predator::InitFromSettingsBlueprint()
{
	AVP_ServerEntity_Actor*	pxOurActor				= GetActorKnowledgeSystem()->GetActorBrain()->GetActor();
	Asura_Hash_ID uBlueprintTypeHashID	= 0x7AD7BD1B; //Asura_GetHashID("AI Settings");
	Asura_Hash_ID uBlueprintHashID		= 0xfbdd4b67; // "PredatorKnowledge"

	Asura_Assert(pxOurActor->GetClassification() == AVP_ENTITYCLASS_PREDATOR, "Actor not a predator");

	if (pxOurActor->GetClassification() == AVP_ENTITYCLASS_PREDATOR)
	{
		Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uBlueprintTypeHashID, uBlueprintHashID);
		if (pxBlueprint)
		{
			Asura_Value xValue;

			if (pxBlueprint->GetParameterValueByHashID(0x8315fc3f, xValue))	//"AgressiveSightRange"
				m_fRange = xValue.GetFloatValue();

			if (pxBlueprint->GetParameterValueByHashID(0x4672390f, xValue))	// "AgressiveThresholdToAcknowledgeThreat"
				m_fThreshold = xValue.GetFloatValue();

			if (pxBlueprint->GetParameterValueByHashID(0x18c61f5f, xValue))	// "VisionKnowledgeLifeTime"
				m_fKnowledgeLifetime = xValue.GetFloatValue();

			if (pxBlueprint->GetParameterValueByHashID(0xc5ee14f1, xValue))	//"VisionalwaysSeeDistance"
				m_fVisionCutOffDistanceSq = xValue.GetFloatValue() * xValue.GetFloatValue();

			if (pxBlueprint->GetParameterValueByHashID(0xe5464136, xValue))	//"VisionMemoryTime"
				m_fVisionMemoryTime = xValue.GetFloatValue();

		}
	}
	else
	{
		m_fRange = 10.0f;
		m_fThreshold = 10.0f;
		m_fKnowledgeLifetime = 20.0f;
		m_fVisionCutOffDistanceSq  = 8.0f;
	}

	m_fRange = dbg_fMaxVisionRange;
	m_fTanFovX = Asura_Maths::Tangent(Asura_Maths::DegToRad(dbg_fFovX) * 0.5f);
	m_fTanFovYUp = Asura_Maths::Tangent(Asura_Maths::DegToRad(dbg_fFovYUp));
	m_fTanFovYDown = Asura_Maths::Tangent(Asura_Maths::DegToRad(dbg_fFovYDown));
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: ShouldCullActorOnRangeAndFOV
//---------------------------------------------------------------------------
bool AVP_Sense_Vision_Predator::ShouldCullActorOnRangeOrFOV(const AVP_ServerEntity_Actor* pxOurActor, const AVP_ServerEntity_Actor* pxOtherActor)
{
	// Cull on range and FOV.
	const Asura_Vector_3&	xActorPosition	= pxOtherActor->GetPosition();
	Asura_Vector_3			xDirection		= xActorPosition - pxOurActor->GetPosition();
	const float				fDistance		= xDirection.Magnitude();

	if (ShouldCullOnRange(fDistance, xDirection) || ShouldCullOnFOV(fDistance, xDirection))
	{
		return true;
	}
	else
	{
		return false;
	}
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: GetScoreForFOVPosition
//---------------------------------------------------------------------------
float AVP_Sense_Vision_Predator::GetScoreForFOVPosition(const AVP_ServerEntity_Actor* pxOurActor, const Asura_Vector_3& xPoint)
{
	Asura_Vector_3				xDirection	= xPoint - pxOurActor->GetPosition();
	Asura_Vector_3				xHeadFwd	= Asura_Maths::ZeroVector3;

	pxOurActor->GetHeadFowardVector(xHeadFwd);

	// get the face marker point
	xDirection.Normalise();
	
	float fDot = xHeadFwd * xDirection;
	return fDot * 100.0f;
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: GetScoreForRange
//---------------------------------------------------------------------------
float AVP_Sense_Vision_Predator::GetScoreForRange(const Asura_Vector_3& xOurPos, const Asura_Vector_3& xOtherPos)
{
	float fDistSq = (xOtherPos - xOurPos).MagnitudeSquared();
	
	// If the threat is under the always see range
	if (fDistSq <= m_fVisionCutOffDistanceSq)
	{
		// Return a very large number as we want the threat to be detected if it is this close to the actor.
		return 1000.0f;
	}

	float	fPercentage			= (fDistSq / (m_fRange * m_fRange)) * 100.0f;
	float	fInversePercentage	= 100.0f - fPercentage;
	return	fInversePercentage;
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: GetScoreForspeed
//---------------------------------------------------------------------------
float AVP_Sense_Vision_Predator::GetScoreForSpeed(const AVP_ServerEntity_Actor* pxActor)
{
	if (!pxActor)
	{
		return 0.0f;
	}

	float fSpeed = pxActor->GetVelocity().Magnitude();

	fSpeed = fSpeed * 10.0f;

	if (fSpeed > 100.0f)
	{
		fSpeed = 100.0f;
	}

	return fSpeed;
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: GetAlertLevelScoreToBeat
//---------------------------------------------------------------------------
float AVP_Sense_Vision_Predator::GetScoreToBeat()
{
	return m_fThreshold;
}


//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: UpdatePlayerAlienThreatStatus
//---------------------------------------------------------------------------
float AVP_Sense_Vision_Predator::UpdatePlayerAlienThreatStatus(AVP_ServerActor_Alien* pxPlayerAlien)
{
	float fHighestScore	= 0.0f;

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

		if (pxVertebrate)
		{
			if (pxVertebrate->IsPlayer())
				continue;

			if (!pxVertebrate->GetActorBrain())
				continue;

			if ((pxVertebrate->GetClassification() == AVP_ENTITYCLASS_PREDATOR) && (!pxVertebrate->IsDead()) && (pxVertebrate->IsEntityActive()))
			{
				AVP_Sense_Vision_Predator*	pxVisionSense = static_cast<AVP_Sense_Vision_Predator*>(static_cast<AVP_Brain_Actor_Predator*>(pxVertebrate->GetActorBrain())->GetKnowledgeSystem()->m_pxVision);

				Asura_Assert(pxVisionSense, "Vision sense not found for predator");
				if (pxVisionSense)
				{
					float fScore = pxVisionSense->m_fAlienPlayerScore;
					float fScoreToBeat = pxVisionSense->m_fAlienPlayerScoreToBeat;

					if (fScore > fScoreToBeat)
					{
						// spotted!
						return 100.0f;
					}
					else
					{
						float fPercentage = (fScore / fScoreToBeat) * 100.0f;

						if (fPercentage > fHighestScore)
						{
							fHighestScore = fPercentage;
						}
					}
				}
			}
		}
	}

	return fHighestScore;
}


//---------------------------------------------------------------------------
// AVP_Sense_Vision_Predator :: AddToVisionList
//---------------------------------------------------------------------------
void AVP_Sense_Vision_Predator::AddToVisionList(Axon_Knowledge* pxNewKnowledge)
{
	if (pxNewKnowledge)
	{
		m_xVisionList.AddToList(pxNewKnowledge);
	}
}
