//////////////////////////////////////////////////////////////////////////////
//
// AVP_Brain_Actor_Predator.cpp
//
// Copyright (C) 2007 Rebellion. All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

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

// header
#include "Core/BrainTypes/AVP_Brain_Actor_Predator.h"


// asura
#include "Blueprint/Asura_Blueprint.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_Scheduler.h"
#include "Rendering/Asura_Font_System.h"
#include "Physics/Asura_Physics_MovementQuery.h"

// Axon headers
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_AtVentagePoint.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Flee.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_GS2Move.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Melee.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Movement.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_PointAndShoot.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Test.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Seeking.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Sneaking.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Stalking.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_TestBehaviour.h"
#include "Core/Axon_Project_System.h"
#include "Core/Axon_System.h"
#include "Knowledge/AVP_Knowledge_System_Actor.h"
#include "Knowledge/AVP_Knowledge_System_Predator.h"

// AVP
#include "Actor/Vertebrates/Predator/AVP_Predator_AIAnimStateMachine.h"

#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Core/AVP_LOS.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Locations/ShootAt/AVP_ServerEntity_ShootAt.h"
#include "Entity/Locations/VantagePoint/AVP_ServerNode_VantagePoint_Predator.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ClientActor_Predator.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ServerActor_Predator.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_Messages_Predator.h"
#include "Entity/PlayerInfo/AVP_ServerEntity_PlayerInfo.h"
#include "Inventory/AVP_Inventory_Group.h"
#include "Inventory/AVP_Inventory_HashIDs.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Inventory/Items/AVP_InventoryItem_PredatorCloak.h"
#include "Inventory/Items/Weapons/AVP_InventoryWeapon_RangedWeapon.h"

#include "Debugging/Asura_DebugLineRender.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Predator_VentagePointEvaluator
//
//////////////////////////////////////////////////////////////////////////////

class AVP_Predator_WP_Collector_Functor : public AVP_Navigation::BreadthFirstSearch
{
public:
	AVP_Predator_WP_Collector_Functor(u_short usStartWPID , float fRadius, Asura_LinkList<const Asura_AI_NavVertex*>& xTargetWPList,
		int uPathFinderFlags);
	virtual bool VisitNode( const int iWaypointIndex );
	virtual bool LinkFlagsTest( const Asura_AI_NavConnection* pxLink, const u_int uPathfinderFlags );

private:
	Asura_LinkList<const Asura_AI_NavVertex*>& m_xTargetWPList;
	u_int m_iPathfinderFlags;
	bool m_bIgnoreCloserPoints;
};

AVP_Predator_WP_Collector_Functor::AVP_Predator_WP_Collector_Functor(u_short usStartWPID, float fRadius, Asura_LinkList<const Asura_AI_NavVertex*>& xTargetWPList, int iPathFinderFlags)
: AVP_Navigation::BreadthFirstSearch(usStartWPID, fRadius)
, m_xTargetWPList(xTargetWPList)
, m_iPathfinderFlags(iPathFinderFlags)
{
	m_xTargetWPList.Clear();
}

bool AVP_Predator_WP_Collector_Functor::VisitNode( const int iWaypointIndex )
{
	const Asura_AI_NavVertex* pxWP = Asura_AI_Navigation_System::GetWaypoint( iWaypointIndex );
	m_xTargetWPList.Push_Back(pxWP);

	return false;
}

bool AVP_Predator_WP_Collector_Functor::LinkFlagsTest( const Asura_AI_NavConnection* pxLink, const u_int uPathfinderFlags )
{
	if (AVP_FlagIsSet( uPathfinderFlags, AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID ) && !AVP_FlagIsSet( pxLink->GetFlags(), AVP_Navigation::AVP_AI_CONNECTION_FLAG_HUMANOID ))
	{
		return true;
	}

	if (AVP_FlagIsSet( uPathfinderFlags, AVP_Navigation::AVP_AI_PATHFINDER_FLAG_PREDATOR ) && !AVP_FlagIsSet( pxLink->GetFlags(), AVP_Navigation::AVP_AI_CONNECTION_FLAG_PREDATOR ))
	{
		return true;
	}

	return AVP_Navigation::BreadthFirstSearch::LinkFlagsTest(pxLink, uPathfinderFlags);
}

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Predator_VentagePointEvaluator
//
//////////////////////////////////////////////////////////////////////////////
AVP_Predator_VentagePointEvaluator::AVP_Predator_VentagePointEvaluator()
: m_pxBrain(NULL)
, m_bCalculating(false)
, m_xMyPosition(Asura_Maths::ZeroVector3)
, m_xMyPositionUsed(Asura_Maths::ZeroVector3)
, m_xVPIt(AVP_ServerNode_VantagePoint_Predator::GetList())
, m_iVPListSize(AVP_ServerNode_VantagePoint_Predator::GetList()->GetNumEntries())
, m_pxBestVP(NULL)
, m_pxCurrentPreferedVP(NULL)
, m_fDistanceToCurrentPreferedVPSqrd(Asura_Maths::LargeFloat)
, m_fBestScore(-1.0f)
, m_xTargetWPList()
, m_xTargetWPListIt(m_xTargetWPList)
, m_xIgnoredVentagePoints()
, m_iLOSTestCount(0)
#ifndef ASURA_NO_DEBUG_OUTPUT
, m_fRunTime(0.0f)
, m_iNumberOfVP(0)
, m_iNumberOfVPTested(0)
, m_iNumberOfLOSTests(0)
#endif
{}

AVP_Predator_VentagePointEvaluator::~AVP_Predator_VentagePointEvaluator()
{
	m_xTargetWPList.Clear();
}


void AVP_Predator_VentagePointEvaluator::SetBrain(const AVP_Brain_Actor_Predator* pxBrain)
{
	m_pxBrain = pxBrain;
}




void AVP_Predator_VentagePointEvaluator::UpdateMyPosition(const Asura_Vector_3 &xMyPosition)
{
	m_xMyPosition = xMyPosition;
	// if I moved too much restart ...
	// this probalby need to be limited to not restart too much
	if (m_bCalculating && (m_xMyPosition - m_xMyPositionUsed).MagnitudeSquared() > m_pxBrain->GetConfigInfo().m_xVentargePointEvalConfig.m_fMaximumMoveDistance/4.0f )
	{
		m_bCalculating = false;
		if (m_pxCurrentPreferedVP)
		{
			Start(m_pxCurrentPreferedVP->GetGuid(), m_bFastSearch);
		}
		else
		{
			Start(ASURA_GUID_UNREGISTERED, m_bFastSearch);
		}
	}
}


void AVP_Predator_VentagePointEvaluator::Start(Asura_Guid uCurrentPreferedVP, bool bFast)
{
	if (m_bCalculating && 
		(uCurrentPreferedVP == ASURA_GUID_UNREGISTERED || 
		(m_pxCurrentPreferedVP && m_pxCurrentPreferedVP->GetGuid() == uCurrentPreferedVP)) && m_bFastSearch == bFast) // if start is called while we already running no point restarting - just continue.
	{
		return;
	}

	m_bFastSearch = bFast;
	m_bCalculating = false;

	if (uCurrentPreferedVP == ASURA_GUID_UNREGISTERED )
	{
		m_pxCurrentPreferedVP = NULL;
	}
	else
	{
		m_pxCurrentPreferedVP = AVP_ServerNode_VantagePoint_Predator::FindEntity(uCurrentPreferedVP);
		Asura_Assert(m_pxCurrentPreferedVP, "prefered ventage point not found");
		if (m_pxCurrentPreferedVP)
		{
			m_fDistanceToCurrentPreferedVPSqrd = (m_pxCurrentPreferedVP->GetPosition() - m_xMyPosition).MagnitudeSquared();
		}
		else
		{
			m_fDistanceToCurrentPreferedVPSqrd  = Asura_Maths::LargeFloat;
		}
	}

	m_xMyPositionUsed= m_xMyPosition;
	m_pxBestVP = NULL;
	m_fBestScore = -Asura_Maths::LargeFloat;

	if (!m_bFastSearch && !m_pxBrain->GetKnowledgeSystem()->IsBestThreatAShootAtNode())
	{
		const Axon_Knowledge& xThreat = m_pxBrain->GetKnowledgeSystem()->GetBestThreatKnowledge();

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

		u_int uFlags = AVP_Brain_Actor_Predator::GetPathFinderFlagsFromClassification(xThreat.m_uSourceClassification);

		const AVP_Navigation::TestParams xTestParams(  uFlags, ASURA_NAVVERTEX_MAX_ZONE_ID );
		u_short usThreatWP;
		if(!AVP_Navigation::GetNearestWaypointID( xThreat.m_xPosition, usThreatWP, xTestParams ))
		{
			return;
		}

		// collect all the way point in the radius
		AVP_Predator_WP_Collector_Functor xCollector(usThreatWP, m_pxBrain->GetConfigInfo().m_xVentargePointEvalConfig.m_fRadiusAroundTarget, m_xTargetWPList, uFlags);
		AVP_Navigation::SearchWaypoints(uFlags, &xCollector);
	}
	else
	{
		m_xTargetWPList.Clear();
	}

	m_iWPListSize = m_xTargetWPList.Size();
	m_xTargetWPListIt.Restart();
	m_xVPIt.Restart();

	m_bCalculating = true;
	m_fCurrentScore = 0.0f;
	m_fCurrentDenum = 0.0f;
#ifndef ASURA_NO_DEBUG_OUTPUT
	m_fRunTime = 0.0f;
	m_iNumberOfVP = 0;
	m_iNumberOfVPTested = 0;
	m_iNumberOfLOSTests = 0;
#endif


	FindNextVentagePoint(false);
	Tick(0.0f);
}

DEBUGVAR int dbg_iMaxLOSTestsPerTick = 15;

void AVP_Predator_VentagePointEvaluator::Tick(float fTimeslice)
{
	UpdateIgnoreList();

	if (!m_bCalculating)
	{
		return;
	}

#ifndef ASURA_NO_DEBUG_OUTPUT
	m_fRunTime += fTimeslice;
#endif

	m_iLOSTestCount = dbg_iMaxLOSTestsPerTick;

	while (!m_xVPIt.Done() && m_iLOSTestCount > 0)
	{
		CheckVentagePoint();

		if (m_xTargetWPListIt.Done())
		{
			FindNextVentagePoint(true);
			m_fCurrentScore = 0.0f;
			m_fCurrentDenum = 0.0f;
			m_xTargetWPListIt.Restart();
		}
	}

	if (m_xVPIt.Done())
	{
		m_bCalculating = false;
		return;
	}

}

bool AVP_Predator_VentagePointEvaluator::HasREsult()
{
	return !m_bCalculating;
}

const AVP_ServerNode_VantagePoint_Predator* AVP_Predator_VentagePointEvaluator::GetResult() const
{
 	return m_pxBestVP;
}

void AVP_Predator_VentagePointEvaluator::Cancel()
{
	m_pxBestVP = NULL;
	m_fBestScore = -Asura_Maths::LargeFloat;
	m_bCalculating = false;
}

#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Predator_VentagePointEvaluator::RenderDebugInfo(Asura_Vector_2 &xDebugOutputPos)
{
	Asura_Font_System::Print( xDebugOutputPos.x, xDebugOutputPos.y, 0.5f, 0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "VPEval: %f , %d, %d, %d", m_fRunTime, m_iNumberOfVP, m_iNumberOfVPTested, m_iNumberOfLOSTests );
	xDebugOutputPos.y += 10.0f;
}
#endif


void AVP_Predator_VentagePointEvaluator::IgnoreVentagePoint(Asura_Guid uGuid, float fDuration)
{
	IgnoreVPData xData;
	xData.m_uGuid = uGuid;
	xData.m_fTimeToStopIgnore = Asura_Timers::GetLocalScaledTime() + fDuration;
	m_xIgnoredVentagePoints.Push_Back(xData);
}



bool AVP_Predator_VentagePointEvaluator::IsVentagePointIgnored( Asura_Guid uGuid )
{
	for (Asura_LinkList_ConstIt<IgnoreVPData> xIt(m_xIgnoredVentagePoints); !xIt.Done(); xIt.Next())
	{
		if (xIt.GetCurrent()->m_uGuid == uGuid )
		{
			return true;
		}
	}

	return false;
}

void AVP_Predator_VentagePointEvaluator::UpdateIgnoreList()
{
	float fNow = Asura_Timers::GetLocalScaledTime();

	for (Asura_LinkList_It<IgnoreVPData> xIt(m_xIgnoredVentagePoints); !xIt.Done(); )
	{
		if (xIt.GetCurrent()->m_fTimeToStopIgnore < fNow )
		{
			xIt.DeleteCurrent();
		}
		else
		{
			xIt.Next();
		}
	}
}

DEBUGVAR float dbg_fStabilisationDistanceSq = 8.0f * 8.0f; 

void AVP_Predator_VentagePointEvaluator::FindNextVentagePoint(bool bSkipCurrent)
{
	if (bSkipCurrent)
	{
		m_xVPIt.Next();
	}

	const Axon_Knowledge& xThreat = m_pxBrain->GetKnowledgeSystem()->GetBestThreatKnowledge();

	if (xThreat.GetGuid() == ASURA_GUID_UNREGISTERED)
	{
		m_bCalculating = false;
		m_pxBestVP = NULL;
		return;
	}

	const AVP_Brain_Actor_Predator_ConfigurationData& xConfig = m_pxBrain->GetConfigInfo();
	float fMaxDistanceToTargetSq = xConfig.m_xVentargePointEvalConfig.m_fMaximumDistanceToTarget;
	fMaxDistanceToTargetSq *= fMaxDistanceToTargetSq;
	float fMinDistanceToTargetSq = xConfig.m_xVentargePointEvalConfig.m_fMinimumDistanceToTarget;
	fMinDistanceToTargetSq *= fMinDistanceToTargetSq;
	float fMaxMoveDistanceSq = xConfig.m_xVentargePointEvalConfig.m_fMaximumMoveDistance;
	fMaxMoveDistanceSq *= fMaxMoveDistanceSq;

	// skip VP that are invalid
	for ( ;!m_xVPIt.Done() ; m_xVPIt.Next() )
	{
		bool bFound = true; // assume the next on is valid

#ifndef ASURA_NO_DEBUG_OUTPUT
		++m_iNumberOfVP;
#endif


		const AVP_ServerNode_VantagePoint_Predator* pxPVP = m_xVPIt.GetCurrent();

		if (!pxPVP || !pxPVP->IsAvailable() || IsVentagePointIgnored(pxPVP->GetGuid()))
		{
			bFound = false;
			continue;
		}


		float fDistancrToThreatSqrd = (xThreat.m_xPosition - pxPVP->GetPosition()).MagnitudeSquared();
		if (fDistancrToThreatSqrd > fMaxDistanceToTargetSq || 
			fDistancrToThreatSqrd < fMinDistanceToTargetSq )
		{
			bFound = false;
			continue;
		}


		float fDistanceToMeSqrd = (m_xMyPositionUsed - pxPVP->GetPosition()).MagnitudeSquared();

#if 0
		if (fDistanceToMeSqrd > fMaxMoveDistanceSq)
		{
			bFound = false;
			continue;
		}
#endif	

		if (m_pxCurrentPreferedVP && fDistanceToMeSqrd > m_fDistanceToCurrentPreferedVPSqrd + dbg_fStabilisationDistanceSq)
		{
			bFound = false;
			continue;
		}


		if (bFound)
		{
#ifndef ASURA_NO_DEBUG_OUTPUT
			++m_iNumberOfVPTested;
#endif
			return;
		}
	}
}

void AVP_Predator_VentagePointEvaluator::CheckVentagePoint()
{

	const Axon_Knowledge& xThreat = m_pxBrain->GetKnowledgeSystem()->GetBestThreatKnowledge();

	if (xThreat.GetGuid() == ASURA_GUID_UNREGISTERED)
	{
		m_bCalculating = false;
		m_pxBestVP = NULL;
		//skip all the tests 
		while (!m_xTargetWPListIt.Done())
		{
			m_xTargetWPListIt.Next();
		}
		return;
	}


	Asura_Vector_3 xOurHeadPosition = m_xVPIt.GetCurrent()->GetPosition();	
	xOurHeadPosition.y -= 2.0f; // estimate our head position

	const AVP_ServerNode_VantagePoint_Predator* pxCurrentVP = m_xVPIt.GetCurrent();

	for (; m_iLOSTestCount >0 && !m_xTargetWPListIt.Done(); m_xTargetWPListIt.Next(), --m_iLOSTestCount)
	{
#ifndef ASURA_NO_DEBUG_OUTPUT
		++m_iNumberOfLOSTests;
#endif

		const Asura_AI_NavVertex* pxWP = *m_xTargetWPListIt.GetCurrent();
		Asura_Vector_3 xWPPosition = pxWP->GetPosition();
		xWPPosition += pxWP->GetNormal() * 1.5f; // estimate chest position 

		Asura_Vector_3 xDir = (xWPPosition - xOurHeadPosition); 
		float fMagnitute = xDir.Normalise();

		Asura_LOS_InputData xLosInput(xOurHeadPosition, xDir, ASURA_GUID_UNREGISTERED,  fMagnitute);
		const Asura_LOS_OutputData* pxHeadLosOutput = AVP_LOS::TestAgainstEnvironmentAndMoveableObjects(&xLosInput);
		float fDistanceFromThreat = (xWPPosition- xThreat.m_xPosition).Magnitude();
		float fScore = ((fDistanceFromThreat > 1.0f) ? 1.0f/fDistanceFromThreat : 1.0f);
		if (pxHeadLosOutput->m_fDistanceToIntersection > fMagnitute - 0.1)
		{
			m_fCurrentScore += fScore;
		}
		m_fCurrentDenum += fScore;
	}

	if (!m_xTargetWPListIt.Done() || m_iLOSTestCount == 0)
	{
		return;
	}

	// check also the target last known position 
	--m_iLOSTestCount;
	Asura_Vector_3 xTargetPosition = m_pxBrain->GetKnowledgeSystem()->GetBestThreatKnowledge().m_xPosition;

	Asura_Vector_3 xDir = (xTargetPosition - xOurHeadPosition); 
	float fMagnitute = xDir.Normalise();

	Asura_LOS_InputData xLosInput(xOurHeadPosition, xDir, ASURA_GUID_UNREGISTERED,  fMagnitute);
	const Asura_LOS_OutputData* pxHeadLosOutput = AVP_LOS::TestAgainstEnvironmentAndMoveableObjects(&xLosInput);

	if (pxHeadLosOutput->m_fDistanceToIntersection > fMagnitute - 0.1)
	{
		m_fCurrentScore += 2.0f;
	}
	m_fCurrentDenum += 2.0f;

	m_fCurrentScore /= m_fCurrentDenum;


	const AVP_Brain_Actor_Predator_ConfigurationData& xConfig = m_pxBrain->GetConfigInfo();

	float fDistanceToMove = (m_xVPIt.GetCurrent()->GetPosition() - m_xMyPositionUsed).Magnitude();

	if (fDistanceToMove > m_pxBrain->GetConfigInfo().m_xVentargePointEvalConfig.m_fSmallMoveDistance)
	{
		m_fCurrentScore -=  (fDistanceToMove - xConfig.m_xVentargePointEvalConfig.m_fSmallMoveDistance) / (xConfig.m_xVentargePointEvalConfig.m_fMaximumMoveDistance - xConfig.m_xVentargePointEvalConfig.m_fSmallMoveDistance) * xConfig.m_xVentargePointEvalConfig.m_fMoveFactor;
	}

	float fMaxDistanceToTarget = xConfig.m_xVentargePointEvalConfig.m_fMaximumDistanceToTarget;
	float fMinDistanceToTarget = xConfig.m_xVentargePointEvalConfig.m_fMinimumDistanceToTarget;

	float fDistanceToTarget = (m_xVPIt.GetCurrent()->GetPosition() - xTargetPosition).Magnitude();
	
	if (fDistanceToTarget > fMaxDistanceToTarget )
	{
		m_fCurrentScore -= 0.5f * xConfig.m_xVentargePointEvalConfig.m_fMoveFactor;
	}
	else if (fDistanceToTarget < fMinDistanceToTarget )
	{
		m_fCurrentScore -= xConfig.m_xVentargePointEvalConfig.m_fMoveFactor; 
	}
	else if (fDistanceToTarget < 0.5f * (fMaxDistanceToTarget + fMinDistanceToTarget )) // && fDistanceToTarget > fMinDistanceToTarget
	{
		m_fCurrentScore -= (2.0f * fDistanceToTarget - (fMaxDistanceToTarget + fMinDistanceToTarget )) / (fMinDistanceToTarget - fMaxDistanceToTarget) * xConfig.m_xVentargePointEvalConfig.m_fMoveFactor;
	}
	else // (fDistanceToTarget > 0.5f * (fMaxDistanceToTarget + fMinDistanceToTarget )) && fDistanceToTarget < fMaxDistanceToTarget
	{
		m_fCurrentScore  -=  (0.5f - (fDistanceToTarget - fMaxDistanceToTarget) / (fMinDistanceToTarget - fMaxDistanceToTarget)) * xConfig.m_xVentargePointEvalConfig.m_fMoveFactor;
	}


	if (m_fCurrentScore > m_fBestScore)
	{
		m_fBestScore = m_fCurrentScore;
		m_pxBestVP = pxCurrentVP;
	}
	else if (m_fCurrentScore == m_fBestScore && m_pxBestVP && fMagnitute < (xTargetPosition - m_pxBestVP->GetPosition()).Magnitude() )
	{
		m_fBestScore = m_fCurrentScore;
		m_pxBestVP = m_xVPIt.GetCurrent();
	}

}


//////////////////////////////////////////////////////////////////////////////
//
// AVP_Predator_FindATargetToAimAt
//
//////////////////////////////////////////////////////////////////////////////

AVP_Predator_FindATargetToAimAt::AVP_Predator_FindATargetToAimAt()
: m_eState(IDLE)
, m_xMuzzlePosition(Asura_Maths::ZeroVector3)
, m_pxBrain(NULL)
, m_xTargetWPList()
, m_fWPListSize(0)
, m_xTargetWPListIt(m_xTargetWPList)
, m_iAimTestCount(0)
, m_xLastResults()
, m_xStateList()
, m_bTargetFound(false)
, m_uTemporaryIndex(0)
, m_fRunTime(0.0f)
#ifndef ASURA_NO_DEBUG_OUTPUT
, m_iWPCounter(0)
#endif
{
	m_xLastResults.m_eType = TT_NO_TARGET;
}

void AVP_Predator_FindATargetToAimAt::SetBrain(const AVP_Brain_Actor_Predator* pxBrain)
{
	m_pxBrain = pxBrain;

}

void AVP_Predator_FindATargetToAimAt::Start(bool bForgetLastTarget)
{
	if (bForgetLastTarget)
	{
		m_xLastResults.m_eType = TT_NO_TARGET;
	}

	BuildCheckList();

	Asura_ListElt<State> *xStateIt = m_xStateList.Front();
	State eFirstState = xStateIt->Data();
	Asura_Assert(xStateIt, "States list empty");
	m_xStateList.Remove(xStateIt);
	SwitchToState(eFirstState);
	m_bTargetFound = false;
	m_fRunTime = 0.0f;
	Tick(0.0f);
}

DEBUGVAR int dbg_iMaxAimTestsPerTick = 10;

void AVP_Predator_FindATargetToAimAt::Tick(float fTimeslice)
{
	if (m_eState != IDLE)
	{
		m_fRunTime += fTimeslice;
	}

	m_iAimTestCount = dbg_iMaxAimTestsPerTick;

	while (m_eState != IDLE && m_iAimTestCount > 0)
	{
		switch (m_eState)
		{
		case CHECK_MAIN_TARGET_ACTUAL_POSITION:
			ConditionalSwitchToState(CheckMainTargetActualPosition() );
			break;
		case CHECK_SECONDARY_TARGETS_ACTUAL_POSITION:
			ConditionalSwitchToState(CheckAllSecondaryTargetsActualPosition() );
			break;
		case CHECK_MAIN_TARGET_LAST_KNOWN_POSITION:
			ConditionalSwitchToState(CheckMainTargetLastPosition() );
			break;
		case CHECK_SECONDARY_TARGETS_LAST_KNOWN_POSITION:
			ConditionalSwitchToState(CheckAllSecondaryTargetsLastPosition() );
			break;
		case CHECK_AROUND_MAIN_TARGET:
			ConditionalSwitchToState(CheckAroundMainTargetLastPosition() );
			break;
		case IDLE:
		default:
			Asura_Assert(false, "Unexpected state");
			return;
		}
	}

}




bool AVP_Predator_FindATargetToAimAt::Finished() const
{
	return m_eState == IDLE;
}

bool AVP_Predator_FindATargetToAimAt::GetResult(Asura_Vector_3 &xTarget) const
{
	xTarget = m_xLastResults.m_xPosition;
	return m_bTargetFound;
}

void AVP_Predator_FindATargetToAimAt::Cancel()
{
	m_bTargetFound = false;
	m_eState = IDLE;
}



#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Predator_FindATargetToAimAt::RenderDebugInfo(Asura_Vector_2 &xDebugOutputPos)
{
	int i = 0;
	for (Asura_LinkList_ConstIt<const Asura_AI_NavVertex*> it(m_xTargetWPList); !it.Done(); it.Next())
	{
		Asura_Vector_3 xRenderPosition = (*it.GetCurrent())->GetPosition();
// 		if(Asura_Camera::ProjectWorldCoordsToScreenSpace(xRenderPosition, true))
// 		{
// 			Asura_Font_System::Print( xRenderPosition.x, xRenderPosition.y, 0.5f, 0xffffffff,
// 				ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "%d", i );
// 		}
		u_int uColur = 0xff00ffff;
		if (i < m_iWPCounter )
		{
			uColur = 0xff00ff00;
		}
		Asura_DebugLineRender::AddCross((*it.GetCurrent())->GetPosition(), uColur, 0.2 );
		i++;
	}

	const Axon_Knowledge xKnowledge = m_pxBrain->GetKnowledgeSystem()->GetBestThreatKnowledge();
	// if we have a threat to avoid 
	if (xKnowledge.GetGuid() != ASURA_GUID_UNREGISTERED)
	{
		Asura_DebugLineRender::AddCross(xKnowledge.m_xPosition, 0xffff0000, 1.0 );
		AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity(xKnowledge.GetGuid());
		if ( pxActor )
		{
			Asura_DebugLineRender::AddCross(pxActor->GetPosition(), 0xff00ff00, 1.0 );
		}

	}

	for (u_int u =0; u < m_pxBrain->GetKnowledgeSystem()->GetNumSecondaryThreats(); ++u)
	{
		const Axon_Knowledge xKnowledge = m_pxBrain->GetKnowledgeSystem()->GetSecondaryThreatKnowledge(u);
		if (xKnowledge.GetGuid() != ASURA_GUID_UNREGISTERED)
		{
			Asura_DebugLineRender::AddCross(xKnowledge.m_xPosition, 0xffff0000, 1.0 );
			AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity(xKnowledge.GetGuid());
			if ( pxActor )
			{
				Asura_DebugLineRender::AddCross(pxActor->GetPosition(), 0xff00ff00, 1.0 );
			}
		}
	}
}
#endif


AVP_Predator_FindATargetToAimAt::Status AVP_Predator_FindATargetToAimAt::CheckMainTargetActualPosition()
{

	AVP_Knowledge_System_Predator* pxKnowledgeSystem = m_pxBrain->GetKnowledgeSystem();
	const Axon_Knowledge& xThreat = pxKnowledgeSystem->GetBestThreatKnowledge();

	if (xThreat.GetGuid() == ASURA_GUID_UNREGISTERED)
	{
		return STATUS_FAIL;
	}

	if (pxKnowledgeSystem->IsBestThreatAShootAtNode()) // for a shoot node - just check exact position.
	{
		if (CanFireAt(xThreat.m_xPosition))
		{
			m_xLastResults.m_eType = TT_MAIN_TARGET;
			m_xLastResults.m_uSecondaryIndex = static_cast<u_int>(-1);
			m_xLastResults.m_xPosition = xThreat.m_xPosition;			
			return STATUS_SUCCESS;
		}
		
		return STATUS_FAIL;
	}

	AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity(xThreat.GetGuid());
	if (!pxActor || pxActor->IsDead())
	{
		return STATUS_FAIL;
	}

	// first check the chest 
	// if the chest can not be seen check the head.
	// if the chest can be seen check the feet.
	Asura_Vector_3 xTarget;
	pxActor->GetChestPosition(xTarget);
 	if (CanFireAt(xTarget))
	{
		m_xLastResults.m_eType = TT_MAIN_TARGET;
		m_xLastResults.m_uSecondaryIndex = static_cast<u_int>(-1);
		m_xLastResults.m_xPosition = xTarget;
		xTarget = pxActor->GetPosition();
		if (CanFireAt(xTarget))
		{
			m_xLastResults.m_eType = TT_MAIN_TARGET;
			m_xLastResults.m_uSecondaryIndex = static_cast<u_int>(-1);
			m_xLastResults.m_xPosition = xTarget;
		}
		return STATUS_SUCCESS;
	}
	else
	{
		pxActor->GetHeadPosition(xTarget);
		if (CanFireAt(xTarget))
		{
			m_xLastResults.m_eType = TT_MAIN_TARGET;
			m_xLastResults.m_uSecondaryIndex = static_cast<u_int>(-1);
			m_xLastResults.m_xPosition = xTarget;
			return STATUS_SUCCESS;
		}
	}
	return STATUS_FAIL;
}

AVP_Predator_FindATargetToAimAt::Status AVP_Predator_FindATargetToAimAt::CheckSecondaryTargetsActualPosition(u_int uIndex)
{
	const AVP_Knowledge_System_Predator* pxKnowledgeSystem = m_pxBrain->GetKnowledgeSystem();

	if (uIndex >= pxKnowledgeSystem->GetNumSecondaryThreats())
	{
		return STATUS_FAIL;
	}

	const Axon_Knowledge& xKnowledge = pxKnowledgeSystem->GetSecondaryThreatKnowledge(uIndex);

	Asura_Assert(xKnowledge.GetGuid() != ASURA_GUID_UNREGISTERED, "knowledge system reported an invalid guid");
	if (xKnowledge.GetGuid() == ASURA_GUID_UNREGISTERED)
	{
		return STATUS_FAIL;
	}

	AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity(xKnowledge.m_uSourceGuid);
	if (!pxActor || pxActor->IsDead())
	{
		return STATUS_FAIL;
	}

	// first check the chest 
	// if the chest can not be seen check the head.
	// if the chest can be seen check the feet.
	Asura_Vector_3 xTarget;
	pxActor->GetChestPosition(xTarget);
	if (CanFireAt(xTarget))
	{
		m_xLastResults.m_eType = TT_SECONDARY_TARGET;
		m_xLastResults.m_uSecondaryIndex = uIndex;
		m_xLastResults.m_xPosition = xTarget;

		xTarget = pxActor->GetPosition();
		if (CanFireAt(xTarget))
		{
			m_xLastResults.m_eType = TT_SECONDARY_TARGET;
			m_xLastResults.m_uSecondaryIndex = uIndex;
			m_xLastResults.m_xPosition = xTarget;
		}
		return STATUS_SUCCESS;
	}
	else
	{
		pxActor->GetHeadPosition(xTarget);
		if (CanFireAt(xTarget))
		{
			m_xLastResults.m_eType = TT_SECONDARY_TARGET;
			m_xLastResults.m_uSecondaryIndex = uIndex;
			m_xLastResults.m_xPosition = xTarget;
			return STATUS_SUCCESS;
		}
	}

	return STATUS_FAIL;

}

AVP_Predator_FindATargetToAimAt::Status AVP_Predator_FindATargetToAimAt::CheckAllSecondaryTargetsActualPosition()
{
	Status eStatus = CheckSecondaryTargetsActualPosition(m_uTemporaryIndex);
	if (eStatus == STATUS_FAIL)
	{
		if (m_uTemporaryIndex >= m_pxBrain->GetKnowledgeSystem()->GetNumSecondaryThreats())
		{
			return STATUS_FAIL;
		}
		else
		{
			++m_uTemporaryIndex;
			return STATUS_CONTINUE;
		}
	}
	else
	{
		return STATUS_SUCCESS;
	}

}

AVP_Predator_FindATargetToAimAt::Status AVP_Predator_FindATargetToAimAt::CheckMainTargetLastPosition()
{

	const AVP_Knowledge_System_Predator* pxKnowledgeSystem = m_pxBrain->GetKnowledgeSystem();
	const Axon_Knowledge& xKnowledge = pxKnowledgeSystem->GetBestThreatKnowledge();

	if (xKnowledge.GetGuid() == ASURA_GUID_UNREGISTERED)
	{
		return STATUS_FAIL;
	}

	if (pxKnowledgeSystem->IsBestThreatAShootAtNode()) // for a shoot node - the last position is the same as current position - 
												   // if we got here then we must have failed current position so retrun fail
	{
		return STATUS_FAIL;
	}

	AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity(xKnowledge.GetGuid());
	if (!pxActor || pxActor->IsDead())
	{
		return STATUS_FAIL;
	}

	Asura_Vector_3 xTarget = xKnowledge.m_xPosition;

	if (CanFireAt(xTarget))
	{
		m_xLastResults.m_eType = TT_MAIN_TARGET_LAST_POSITION;
		m_xLastResults.m_uSecondaryIndex = static_cast<u_int>(-1);
		m_xLastResults.m_xPosition = xTarget;
		return STATUS_SUCCESS;
	}

	return STATUS_FAIL;
}

AVP_Predator_FindATargetToAimAt::Status AVP_Predator_FindATargetToAimAt::CheckSecondaryTargetsLastPosition(u_int uIndex)
{
	const AVP_Knowledge_System_Predator* pxKnowledgeSystem = m_pxBrain->GetKnowledgeSystem();

	if (uIndex >= pxKnowledgeSystem->GetNumSecondaryThreats())
	{
		return STATUS_FAIL;
	}
	const Axon_Knowledge& xKnowledge = pxKnowledgeSystem->GetSecondaryThreatKnowledge(uIndex);

	Asura_Assert(xKnowledge.GetGuid() != ASURA_GUID_UNREGISTERED, "knowledge system reported an invalid guid");
	if (xKnowledge.GetGuid() == ASURA_GUID_UNREGISTERED)
	{
		return STATUS_FAIL;
	}

	AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity(xKnowledge.GetGuid());
	if (!pxActor || pxActor->IsDead())
	{
		return STATUS_FAIL;
	}

	Asura_Vector_3 xTarget = xKnowledge.m_xPosition;

	if (CanFireAt(xTarget))
	{
		m_xLastResults.m_eType = TT_MAIN_TARGET_LAST_POSITION;
		m_xLastResults.m_uSecondaryIndex = static_cast<u_int>(-1); // we wont be using this so set it to invali value
		m_xLastResults.m_xPosition = xTarget;
		return STATUS_SUCCESS;
	}

	return STATUS_FAIL;
}

AVP_Predator_FindATargetToAimAt::Status AVP_Predator_FindATargetToAimAt::CheckAllSecondaryTargetsLastPosition()
{
	Status eStatus = CheckSecondaryTargetsLastPosition(m_uTemporaryIndex);
	if (eStatus == STATUS_FAIL)
	{
		if (m_uTemporaryIndex >= m_pxBrain->GetKnowledgeSystem()->GetNumSecondaryThreats())
		{
			return STATUS_FAIL;
		}
		else
		{
			++m_uTemporaryIndex;
			return STATUS_CONTINUE;
		}
	}
	else
	{
		return STATUS_SUCCESS;
	}
}


AVP_Predator_FindATargetToAimAt::Status AVP_Predator_FindATargetToAimAt::CheckAroundMainTargetLastPosition()
{
	if (m_xTargetWPListIt.Done())
	{
		return STATUS_FAIL;
	}

	Asura_Vector_3 xTarget = (*m_xTargetWPListIt.GetCurrent())->GetPosition();

#ifndef ASURA_NO_DEBUG_OUTPUT
	++m_iWPCounter;
#endif

	if (CanFireAt(xTarget))
	{
		m_xLastResults.m_eType = TT_MAIN_TARGET_LAST_POSITION_AREA;
		m_xLastResults.m_uSecondaryIndex = static_cast<u_int>(-1); 
		m_xLastResults.m_xPosition = xTarget;
		return STATUS_SUCCESS;
	}

	m_xTargetWPListIt.Next();

	return STATUS_CONTINUE;

}

bool AVP_Predator_FindATargetToAimAt::GenerateWaypoints()
{

	const AVP_Knowledge_System_Predator* pxKnowledgeSystem = m_pxBrain->GetKnowledgeSystem();
	const Axon_Knowledge xKnowledge = pxKnowledgeSystem->GetBestThreatKnowledge();

	if ( xKnowledge.GetGuid() == ASURA_GUID_UNREGISTERED )
	{
		return false;
	}

	if (pxKnowledgeSystem->IsBestThreatAShootAtNode())
	{
		m_xTargetWPList.Clear();
		return false;
	}

	u_int uFlags = AVP_Brain_Actor_Predator::GetPathFinderFlagsFromClassification(xKnowledge.m_uSourceClassification);

	uFlags |= ASURA_AI_PATHFINDER_FLAGS_IGNOREZONES;

	const AVP_Navigation::TestParams xTestParams(  uFlags, ASURA_NAVVERTEX_MAX_ZONE_ID );
	u_short usThreatWP;
	if(!AVP_Navigation::GetNearestWaypointID( xKnowledge.m_xPosition, usThreatWP, xTestParams ))
	{
		return false;
	}

	AVP_Predator_WP_Collector_Functor xCollector(usThreatWP, m_pxBrain->GetConfigInfo().m_xWeaponConfig.m_fAimSearchRadius , m_xTargetWPList, uFlags);
	AVP_Navigation::SearchWaypoints(uFlags, &xCollector);

	m_fWPListSize = static_cast<float>(m_xTargetWPList.Size());
	m_xTargetWPListIt.Restart();
	return true;
}


void AVP_Predator_FindATargetToAimAt::ConditionalSwitchToState(AVP_Predator_FindATargetToAimAt::Status eStatus)
{

	switch (eStatus)
	{
	case STATUS_CONTINUE:
		break;
	case STATUS_FAIL:
		{
			Asura_ListElt<State> *xStateIt = m_xStateList.Front();
			Asura_Assert(xStateIt, "States list empty");
			State eState = xStateIt->Data();
			m_xStateList.Remove(xStateIt);
			SwitchToState(eState);
		}
		break;
	case STATUS_SUCCESS: // we are done
		m_bTargetFound = true;
		SwitchToState(IDLE);
		break;
	default:
		Asura_Assert(false, "Unexpected status");
	}
}

void AVP_Predator_FindATargetToAimAt::SwitchToState(AVP_Predator_FindATargetToAimAt::State eState)
{
	switch (m_eState)
	{		
	case IDLE:
		m_bTargetFound = false;
		break;
	case CHECK_MAIN_TARGET_ACTUAL_POSITION:
	case CHECK_SECONDARY_TARGETS_ACTUAL_POSITION:
	case CHECK_MAIN_TARGET_LAST_KNOWN_POSITION:
	case CHECK_SECONDARY_TARGETS_LAST_KNOWN_POSITION:
	case CHECK_AROUND_MAIN_TARGET:
		break;
	default:
		Asura_Assert(false, "Unexpected state");
	}

	m_eState = eState;

	switch (m_eState)
	{		
	case IDLE:
		if (m_bTargetFound == false && m_xLastResults.m_eType != TT_NO_TARGET) // reuse last target if it exist
		{
			m_bTargetFound = true;
		}
		break;
	case CHECK_MAIN_TARGET_ACTUAL_POSITION:
		break;
	case CHECK_SECONDARY_TARGETS_ACTUAL_POSITION:
		m_uTemporaryIndex = 0;
		break;
	case CHECK_MAIN_TARGET_LAST_KNOWN_POSITION:
		break;
	case CHECK_SECONDARY_TARGETS_LAST_KNOWN_POSITION:
		m_uTemporaryIndex = 0;
		break;
	case CHECK_AROUND_MAIN_TARGET:
		m_uTemporaryIndex = 0;
#ifndef ASURA_NO_DEBUG_OUTPUT
		m_iWPCounter = 0;
#endif
		if (!GenerateWaypoints()) 
		{
			SwitchToState(IDLE);
		}
		break;
	default:
		Asura_Assert(false, "Unexpected state");
	}
}

void AVP_Predator_FindATargetToAimAt::BuildCheckList()
{
	m_xStateList.Clear();

	switch (m_xLastResults.m_eType)
	{
	case TT_MAIN_TARGET: // check new main real position then last main real position  (which should usually succeed)then secondary positions
		m_xStateList.Push_Back(CHECK_MAIN_TARGET_ACTUAL_POSITION);
		m_xStateList.Push_Back(CHECK_SECONDARY_TARGETS_ACTUAL_POSITION);
		break;
	case TT_MAIN_TARGET_LAST_POSITION: // check main new real position then secondary real position otherwise leave main last seen position
		m_xStateList.Push_Back(CHECK_MAIN_TARGET_ACTUAL_POSITION);
		m_xStateList.Push_Back(CHECK_SECONDARY_TARGETS_ACTUAL_POSITION);
		break;
	case TT_SECONDARY_TARGET: // check main new real position then secondary real position otherwise leave secondary last seen position
		m_xStateList.Push_Back(CHECK_MAIN_TARGET_ACTUAL_POSITION);
		m_xStateList.Push_Back(CHECK_SECONDARY_TARGETS_ACTUAL_POSITION);
		break;
	case TT_SECONDARY_TARGET_LAST_POSITION: // check main new real position then secondary real position otherwise leave secondary last position
		m_xStateList.Push_Back(CHECK_MAIN_TARGET_ACTUAL_POSITION);
		m_xStateList.Push_Back(CHECK_SECONDARY_TARGETS_ACTUAL_POSITION);
		break;
	case TT_MAIN_TARGET_LAST_POSITION_AREA: // check main new real position then secondary real position otherwise leave main area 
		m_xStateList.Push_Back(CHECK_MAIN_TARGET_ACTUAL_POSITION);
		m_xStateList.Push_Back(CHECK_SECONDARY_TARGETS_ACTUAL_POSITION);
		break;
	case TT_NO_TARGET:
		m_xStateList.Push_Back(CHECK_MAIN_TARGET_ACTUAL_POSITION);
		m_xStateList.Push_Back(CHECK_SECONDARY_TARGETS_ACTUAL_POSITION);
		m_xStateList.Push_Back(CHECK_MAIN_TARGET_LAST_KNOWN_POSITION);
		m_xStateList.Push_Back(CHECK_SECONDARY_TARGETS_LAST_KNOWN_POSITION);
		m_xStateList.Push_Back(CHECK_AROUND_MAIN_TARGET);
		break;
	}
	m_xStateList.Push_Back(IDLE);
}

DEBUGVAR float gbd_fPlasmaBallRadius = 0.15f;

bool AVP_Predator_FindATargetToAimAt::CanFireAt(const Asura_Vector_3& xTarget)
{
	--m_iAimTestCount;
	return m_pxBrain->CanFireAt(xTarget, false);
}

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Brain_Actor_Predator
//
//////////////////////////////////////////////////////////////////////////////

AVP_Brain_Actor_Predator::AVP_Brain_Actor_Predator( AVP_ServerActor_Predator* pxPredator )
: PARENT( pxPredator )
, m_uFlags( 0 )
, m_iKnowledgeUpdateHandle( ASURA_INVALID_HANDLE )
, m_pxKnowledgeSystem( NULL )
, m_xVentagePointEvaluator()
, m_xFindTargetToAimAt()
, m_xSpawnPoint(pxPredator->GetPosition())
, m_bIsFalling(false)
, m_bIsJumping(false)
, m_bJustFinishedFalling(false)
, m_bIsMelee(false)
, m_fNotSupportedTime(0.0)
, m_xConfig()
{
	// defaults that should be overwritten by the blueprint
	m_xConfig.m_xWeaponConfig.m_fAimSearchRadius = 8.0f;
	m_xConfig.m_xWeaponConfig.m_fPlasmaCasterDamage = 3.0f;
	m_xConfig.m_xWeaponConfig.m_fPlasmaCasterDelayBetweenShots = 1.0f;
	m_xConfig.m_xWeaponConfig.m_iPlasmaCasterMaximumNumberOfShots = 5;
	m_xConfig.m_xWeaponConfig.m_iPlasmaCasterMiniumNumberOfShots = 1;
	m_xConfig.m_xVentargePointEvalConfig.m_fMaximumDistanceToTarget = 50.0f;
	m_xConfig.m_xVentargePointEvalConfig.m_fMaximumMoveDistance = 75.0f;
	m_xConfig.m_xVentargePointEvalConfig.m_fMinimumDistanceToTarget = 15.0f;
	m_xConfig.m_xVentargePointEvalConfig.m_fMoveFactor = 0.1f;
	m_xConfig.m_xVentargePointEvalConfig.m_fRadiusAroundTarget = 8.0f;
	m_xConfig.m_xVentargePointEvalConfig.m_fSmallMoveDistance = 20.0f;
	m_xConfig.m_xAtVPBehaviour.m_fAimDelay = 3.0f;
	m_xConfig.m_xFleeBehaviour.m_fFleeMinDistance = 10.0f;
	m_xConfig.m_xFleeBehaviour.m_fFleeSearchRadius = 20.0f;
	m_xConfig.m_xFleeBehaviour.m_fFleeTime = 5.0f;
	m_xConfig.m_xHighLevelBehaviour.m_iNumberOfDamagePercentPoint = 3;
	m_xConfig.m_xHighLevelBehaviour.m_fDamagePercentPoint[0] = 0.25f;
	m_xConfig.m_xHighLevelBehaviour.m_fDamagePercentPoint[1] = 0.50f;
	m_xConfig.m_xHighLevelBehaviour.m_fDamagePercentPoint[2] = 0.75f;
	m_xConfig.m_xHighLevelBehaviour.m_fAvoidDistance = 15.0f;
	m_xConfig.m_xHighLevelBehaviour.m_fSeenRecentlyTime = 1.0f;
	m_xConfig.m_xHighLevelBehaviour.m_fNotSeenRecentlyTime = 10.0f;
	m_xConfig.m_xSeekBehaviour.m_fLookAroundMaxTime = 5.0f;
	m_xConfig.m_xSeekBehaviour.m_fSeekRadiusNoTarget = 20.0f;
	m_xConfig.m_xSeekBehaviour.m_fSeekRadiusAroundTarget = 40.0f;

	SetNormalTurnSpeed();
	SetNormalAimSpeed();

	m_xVentagePointEvaluator.SetBrain(this);
	m_xFindTargetToAimAt.SetBrain(this);
}

void AVP_Brain_Actor_Predator::Initialise()
{
	PARENT::Initialise();

	m_pxKnowledgeSystem = new AVP_Knowledge_System_Predator( this );
	if ( m_pxKnowledgeSystem )
	{
		m_pxKnowledgeSystem->Initialise();
	}
}

void AVP_Brain_Actor_Predator::Shutdown()
{
	if ( m_pxKnowledgeSystem )
	{
		m_pxKnowledgeSystem->Shutdown();
		delete m_pxKnowledgeSystem;
		m_pxKnowledgeSystem = NULL;
	}

	PARENT::Shutdown();
}

void AVP_Brain_Actor_Predator::InitialiseFromBlueprint( const Asura_Blueprint& xBlueprint )
{
    PARENT::InitialiseFromBlueprint( xBlueprint );

	if ( m_pxKnowledgeSystem )
	{
		m_pxKnowledgeSystem->InitialiseFromBlueprint( xBlueprint );
	}

	Asura_Value xValue;

	const Asura_Blueprint* xWeaponConfigBP = Asura_Blueprint_System::GetBlueprintByHashID(0x7ad7bd1b, 0xde93005a ); //"AI Settings", "WeaponConfiguration"
	if (xWeaponConfigBP)
	{
		if (xWeaponConfigBP->GetParameterValueByHashID(0x78e4adff, xValue)) //"AimSearchRadius"
		{
			m_xConfig.m_xWeaponConfig.m_fAimSearchRadius = xValue.GetFloatValue();
		}
		if (xWeaponConfigBP->GetParameterValueByHashID(0xf34d20dd, xValue)) //"PlasmaCasterDamage"
		{
			m_xConfig.m_xWeaponConfig.m_fPlasmaCasterDamage = xValue.GetFloatValue();
		}
		if (xWeaponConfigBP->GetParameterValueByHashID(0x96604486, xValue)) //"PlasmaCasterDelayBetweenShots"
		{
			m_xConfig.m_xWeaponConfig.m_fPlasmaCasterDelayBetweenShots = xValue.GetFloatValue();
		}
		if (xWeaponConfigBP->GetParameterValueByHashID(0x26bcd0a7, xValue)) //"PlasmaCasterMaximumNumberOfShots""
		{
			m_xConfig.m_xWeaponConfig.m_iPlasmaCasterMaximumNumberOfShots = xValue.GetIntValue();
		}
		if (xWeaponConfigBP->GetParameterValueByHashID(0xe65d0979, xValue)) //"PlasmaCasterMinimumNumberOfShots""
		{
			m_xConfig.m_xWeaponConfig.m_iPlasmaCasterMiniumNumberOfShots = xValue.GetIntValue();
		}				
	}

	const Asura_Blueprint* xVentagePointEvalBP = Asura_Blueprint_System::GetBlueprintByHashID(0x7ad7bd1b, 0xacef7b62 ); //"AI Settings", "VentagePointEvaluation"
	if (xVentagePointEvalBP)
	{
		if (xVentagePointEvalBP->GetParameterValueByHashID(0x64b8b4e1, xValue)) //"MaximumDistanceToTarget"
		{
			m_xConfig.m_xVentargePointEvalConfig.m_fMaximumDistanceToTarget = xValue.GetFloatValue();
		}
		if (xVentagePointEvalBP->GetParameterValueByHashID(0x6bb1ece6, xValue)) //"MaximumMoveDistance"
		{
			m_xConfig.m_xVentargePointEvalConfig.m_fMaximumMoveDistance = xValue.GetFloatValue();
		}
		if (xVentagePointEvalBP->GetParameterValueByHashID(0x1aecf2cf, xValue)) //"MinimumDistanceToTarget"
		{
			m_xConfig.m_xVentargePointEvalConfig.m_fMinimumDistanceToTarget = xValue.GetFloatValue();
		}
		if (xVentagePointEvalBP->GetParameterValueByHashID(0xe86e5120, xValue)) //"PercentageLostDueToMovement"
		{
			m_xConfig.m_xVentargePointEvalConfig.m_fMoveFactor = static_cast<float>(xValue.GetIntValue())/100.0f;
		}
		if (xVentagePointEvalBP->GetParameterValueByHashID(0x36ee57d3, xValue)) //"RadiusAroundTargetToEstimate"
		{
			m_xConfig.m_xVentargePointEvalConfig.m_fRadiusAroundTarget = xValue.GetFloatValue();
		}
		if (xVentagePointEvalBP->GetParameterValueByHashID(0x3c3da94d, xValue)) //"SmallMoveDistance"
		{
			m_xConfig.m_xVentargePointEvalConfig.m_fSmallMoveDistance = xValue.GetFloatValue();
		}
	}

	const Asura_Blueprint* xAtVPBP = Asura_Blueprint_System::GetBlueprintByHashID(0x7ad7bd1b, 0x70c1a359 ); //"AI Settings", "AtVentagePoint"
	if (xAtVPBP)
	{
		if (xAtVPBP->GetParameterValueByHashID(0xce71435e, xValue)) //"AimDelay"
		{
			m_xConfig.m_xAtVPBehaviour.m_fAimDelay = xValue.GetFloatValue();
		}
	}

	const Asura_Blueprint* xFleeBP = Asura_Blueprint_System::GetBlueprintByHashID(0x7ad7bd1b, 0x002fffe6 ); //"AI Settings", "Flee"
	if (xFleeBP)
	{
		if (xFleeBP->GetParameterValueByHashID(0x5e18c047, xValue)) //"MinDistance""
		{
			m_xConfig.m_xFleeBehaviour.m_fFleeMinDistance = xValue.GetFloatValue();
		}
		if (xFleeBP->GetParameterValueByHashID(0x3afff43a, xValue)) //"SearchRadius""
		{
			m_xConfig.m_xFleeBehaviour.m_fFleeSearchRadius = xValue.GetFloatValue();
		}
		if (xFleeBP->GetParameterValueByHashID(0x66f7efb3, xValue)) //"FleeTime""
		{
			m_xConfig.m_xFleeBehaviour.m_fFleeTime = xValue.GetFloatValue();
		}
	}

	const Asura_Blueprint* xHighLevelBP = Asura_Blueprint_System::GetBlueprintByHashID(0x7ad7bd1b, 0xd763f322 ); //"AI Settings", "HighLevel"
	if (xHighLevelBP)
	{
		if (xHighLevelBP->GetParameterValueByHashID(0xee5d7a17, xValue)) //"DamagePercentPoint1"
		{
			m_xConfig.m_xHighLevelBehaviour.m_fDamagePercentPoint[0] = static_cast<float>(xValue.GetIntValue()) / 100.0f;
		}
		if (xHighLevelBP->GetParameterValueByHashID(0xee5d7a18, xValue)) //"DamagePercentPoint2"
		{
			m_xConfig.m_xHighLevelBehaviour.m_fDamagePercentPoint[1] = static_cast<float>(xValue.GetIntValue()) / 100.0f;
		}
		if (xHighLevelBP->GetParameterValueByHashID(0xee5d7a19, xValue)) //"DamagePercentPoint3"
		{
			m_xConfig.m_xHighLevelBehaviour.m_fDamagePercentPoint[2] = static_cast<float>(xValue.GetIntValue()) / 100.0f;
		}
		if (xHighLevelBP->GetParameterValueByHashID(0xa8bd806a, xValue)) //"AvoidDistance"
		{
			m_xConfig.m_xHighLevelBehaviour.m_fAvoidDistance = xValue.GetFloatValue();
		}
		if (xHighLevelBP->GetParameterValueByHashID(0xdde095f0, xValue)) //"SeenRecentlyTime"
		{
			m_xConfig.m_xHighLevelBehaviour.m_fSeenRecentlyTime = xValue.GetFloatValue();
		}
		if (xHighLevelBP->GetParameterValueByHashID(0x05503ac3, xValue)) //"NotSeenRecentlyTime"
		{
			m_xConfig.m_xHighLevelBehaviour.m_fNotSeenRecentlyTime = xValue.GetFloatValue();
		}
	}

	const Asura_Blueprint* xSeekBP = Asura_Blueprint_System::GetBlueprintByHashID(0x7ad7bd1b, 0x0035ce78 ); //"AI Settings", "Seek"
	if (xSeekBP)
	{
		if (xSeekBP->GetParameterValueByHashID(0xd593b5f9, xValue)) //"LookAroundTime"
		{
			m_xConfig.m_xSeekBehaviour.m_fLookAroundMaxTime = xValue.GetFloatValue();
		}
		if (xSeekBP->GetParameterValueByHashID(0x2f772ba8, xValue)) //"SeekRadiusAroundTarget"
		{
			m_xConfig.m_xSeekBehaviour.m_fSeekRadiusAroundTarget = xValue.GetFloatValue();
		}
		if (xSeekBP->GetParameterValueByHashID(0x53cc419c, xValue)) //"SeekRadiusNoTarget"
		{
			m_xConfig.m_xSeekBehaviour.m_fSeekRadiusNoTarget = xValue.GetFloatValue();
		}
	}

    DestroyAllBehaviours();
	if ( IsActive() )
	{
		// Create our initial behaviours based on our blueprint and any relevant information in our actor.
		bool bCreatedFromBP = false;
		if ( xBlueprint.GetParameterValueByHashID( 0xae7a48c3, xValue ) ) // Asura_GetHashID( "AIStartBehaviour" )
		{
			if ( xValue.GetIntValue() >= AXON_BEHAVIOUR_TYPE_FIRST_PROJECT_SPECIFIC )
			{
				CreateActivateAndPushBehaviour( xValue.GetIntValue() );
				bCreatedFromBP = true;
			}
		}
		if ( !bCreatedFromBP )
		{
			CreateActivateAndPushBehaviour( AVP_BEHAVIOUR_TYPE_PREDATOR );
		}
		CreateGS2BehaviourIfNecessary();
	}

	EstablishNavData();
}

void AVP_Brain_Actor_Predator::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
    int iVersion;
    xStream >> iVersion;

    PARENT::ReadFromChunkStream( xStream );

    if ( iVersion == CURRENT_CHUNK_VERSION )
    {
        Asura_ReadFromStream_Bitfield_Wrapper< u_int > xBitfieldWrapper( m_uFlags );
        xStream >> xBitfieldWrapper;
    }
    else if ( iVersion == 0 )
    {
    }
    else
    {
        Asura_Assertf( false, ( "AVP_Brain_Actor_Predator::ReadFromChunkStream - unhandled chunk version (%d)", iVersion ) );
    }
}

void AVP_Brain_Actor_Predator::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
    xStream << CURRENT_CHUNK_VERSION;

    PARENT::WriteToChunkStream( xStream );

    Asura_WriteToStream_Bitfield_Wrapper< u_int > xBitfieldWrapper( m_uFlags );
    xBitfieldWrapper.Add( false, 1 );
    xStream << xBitfieldWrapper;
}


void AVP_Brain_Actor_Predator::EndGS2()
{
	// reset all the flags since they are probably invalid now
	m_bIsJumping = false;
	m_bJustFinishedFalling = false;
	m_bIsMelee = false;
	m_fNotSupportedTime = 0.0;
}

void AVP_Brain_Actor_Predator::DoPerFrameUpdate( const float fTimeslice )
{
	if (!m_bActive)
	{
		return;
	}

	UpdateFalling(fTimeslice);
	m_xVentagePointEvaluator.Tick(fTimeslice);
	m_xFindTargetToAimAt.Tick(fTimeslice);

	PARENT::DoPerFrameUpdate( fTimeslice );
}


#include "Core/Asura_MemoryManagement_DebugOff.h"
Axon_Behaviour* AVP_Brain_Actor_Predator::CreateBehaviour( const u_int uBehaviourType, const Asura_Message* pxMessage /*= NULL*/ )
{
    void* pxMemory = s_pxBehaviourMempool->Allocate();
    if ( !pxMemory )
    {
        Asura_Assert( false, "Axon_Project_Brain::CreateBehaviour - could not allocate behaviour memory" );
        return NULL;
    }

    Axon_Behaviour* pxBehaviour = NULL;
    switch ( uBehaviourType )
    {
        case AVP_BEHAVIOUR_TYPE_PREDATOR:
        {
            pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predator( this );
            break;
        }

		case AVP_BEHAVIOUR_TYPE_PREDATOR_MOVEMENT:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predator_Movement( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_PREDATOR_TEST:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predator_Test( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_PREDATOR_GS2MOVE:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predator_GS2Move( this );
			break;
		}
		case AVP_BEHAVIOUR_TYPE_PREDATOR_STALKING:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predator_Stalking( this );
			break;
		}
		case AVP_BEHAVIOUR_TYPE_PREDATOR_SEEKING:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predator_Seeking( this );
			break;
		}		
		case AVP_BEHAVIOUR_TYPE_PREDATOR_SNEAKING:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predator_Sneaking( this );
			break;
		}		
		case AVP_BEHAVIOUR_TYPE_PREDATOR_FLEE:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predator_Flee( this );
			break;
		}
		case AVP_BEHAVIOUR_TYPE_PREDATOR_AT_VENTAGE_POINT:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predator_AtVentagePoint( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_PREDATOR_MELEE:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predator_Melee( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_PREDATOR_TESTBEHAVIOUR:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predator_TestBehaviour( this );
			break;
		}

		default:
        {
            break;
        }
    }

    if ( !pxBehaviour )
    {
        // Couldn't create the behaviour - try the parent.
        s_pxBehaviourMempool->Deallocate( pxMemory );
        pxBehaviour = PARENT::CreateBehaviour( uBehaviourType, pxMessage );
    }
    else if ( pxMessage )
    {
        pxBehaviour->DoHandleMessage( pxMessage, true );
    }

    return pxBehaviour;
}
#include "Core/Asura_MemoryManagement_DebugOn.h"

Asura_Guid AVP_Brain_Actor_Predator::GetPrimaryThreatGuid()
{
	AVP_Knowledge_System_Predator*	pxKnowledgeSystem = GetKnowledgeSystem();

	if ( !pxKnowledgeSystem ) 
	{
		return ASURA_GUID_UNREGISTERED;
	}
	else
	{
		return  pxKnowledgeSystem->GetBestThreatKnowledge().GetGuid();
	}
}

Asura_Guid AVP_Brain_Actor_Predator::GetAlertGuid( void )
{
	AVP_Knowledge_System_Predator*	pxKnowledgeSystem = GetKnowledgeSystem();

	if ( !pxKnowledgeSystem ) 
	{
		return ASURA_GUID_UNREGISTERED;
	}

	const Axon_Knowledge xAlertInfo = pxKnowledgeSystem->GetBestAlertKnowledge();

	if ( xAlertInfo.m_uSourceGuid != ASURA_GUID_UNREGISTERED )
	{
		AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity( xAlertInfo.m_uSourceGuid );

		if ( pxActor )
		{
			return xAlertInfo.m_uSourceGuid;
		}
		else
		{
			pxActor = AVP_ServerEntity_Actor::FindEntity( xAlertInfo.m_uProjectData );

			if ( pxActor )
			{
				return xAlertInfo.m_uProjectData;
			}
		}
	}

	return ASURA_GUID_UNREGISTERED;
}



void AVP_Brain_Actor_Predator::Cloak()
{
	GetPredatorActor()->Cloak();
}

void AVP_Brain_Actor_Predator::Decloak()
{
	GetPredatorActor()->Decloak();
}

bool AVP_Brain_Actor_Predator::IsCloaked() const
{
	return GetPredatorActor()->IsCloaked();
}

void AVP_Brain_Actor_Predator::EquipPlasmaCaster()
{
	GetPredatorActor()->EquipPlasmaCaster();
}

void AVP_Brain_Actor_Predator::EquipWristBlades()
{
	GetPredatorActor()->EquipWristBlades();
}

bool AVP_Brain_Actor_Predator::IsHoldingPlasmaCaster() const
{
	return GetPredatorActor()->IsHoldingPlasmaCaster();
}

bool AVP_Brain_Actor_Predator::IsHoldingWristBlades() const
{
	return GetPredatorActor()->IsHoldingWristBlades();
}


bool AVP_Brain_Actor_Predator::StartPlasmaCasterCharge(float fDamage)
{
	return GetPredatorActor()->StartPlasmaCasterCharge(fDamage);
}

void AVP_Brain_Actor_Predator::ReleasePlasmaCasterCharge()
{
	GetPredatorActor()->ReleasePlasmaCasterCharge();
}

void AVP_Brain_Actor_Predator::AbortPlasmaCasterCharge()
{
	GetPredatorActor()->AbortPlasmaCasterCharge();
}

bool AVP_Brain_Actor_Predator::IsPlasmaCasterCharged()
{
	return GetPredatorActor()->IsPlasmaCasterCharged();
}

#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Brain_Actor_Predator::GetCloakDebugString(const u_int uMaxChars, char* szText) const
{
	GetPredatorActor()->GetCloakDebugString(uMaxChars, szText);
}

void AVP_Brain_Actor_Predator::GetWeaponDebugString(const u_int uMaxChars, char* szText) const
{
	GetPredatorActor()->GetWeaponDebugString(uMaxChars, szText);
}
#endif

void AVP_Brain_Actor_Predator::SetFastTurnSpeed()
{
	SetMaxMoveDirAngularVelocity( Asura_Maths::Pi * 4.0f );
}

void AVP_Brain_Actor_Predator::SetNormalTurnSpeed()
{
	SetMaxMoveDirAngularVelocity( Asura_Maths::Pi * 2.0f );
}

void AVP_Brain_Actor_Predator::SetNormalAimSpeed()
{
	SetMaxAimDirAngularVelocity( Asura_Maths::Pi * 2.0f );
	SetMaxLookDirAngularVelocity( Asura_Maths::Pi * 2.0f );
}

AVP_ServerActor_Predator* AVP_Brain_Actor_Predator::GetPredatorActor() const
{
    return static_cast< AVP_ServerActor_Predator* >( GetActor() );
}

AVP_ClientActor_Predator* AVP_Brain_Actor_Predator::GetPredatorClientActor() const
{
	return static_cast< AVP_ClientActor_Predator* >( GetClientActor() );
}

void AVP_Brain_Actor_Predator::SetCrouched( const bool bCrouched )
{
	AVP_ServerActor_Predator* pxPred = GetPredatorActor();
	AVP_Predator_AIAnimStateMachine* pxStateMachine = static_cast< AVP_Predator_AIAnimStateMachine*  >( pxPred->GetAIAnimStateMachine() );
	if ( pxPred->IsMoving() )
	{
		if ( bCrouched )
		{
			pxStateMachine->RequestCrouchMove();
		}
		else
		{
			pxStateMachine->RequestMoveSlow();
		}
	}
	else
	{
		if ( bCrouched )
		{
			pxStateMachine->RequestCrouchIdle();
		}
		else
		{
			pxStateMachine->RequestIdle();
		}
	}
}

void AVP_Brain_Actor_Predator::IgnoreVentagePoint(Asura_Guid uGuid, float fDuration)
{
	m_xVentagePointEvaluator.IgnoreVentagePoint(uGuid, fDuration);
}

void AVP_Brain_Actor_Predator::StartVPTest(Asura_Guid uCurrentPreferedVP, bool bFast)
{
	m_xVentagePointEvaluator.UpdateMyPosition(GetPredatorActor()->GetPosition());
	m_xVentagePointEvaluator.Start(uCurrentPreferedVP, bFast);
}

bool AVP_Brain_Actor_Predator::IsVPTestFinished()
{
	return m_xVentagePointEvaluator.HasREsult();
}

const AVP_ServerNode_VantagePoint_Predator* AVP_Brain_Actor_Predator::GetVPTestResult() const
{
	return m_xVentagePointEvaluator.GetResult();
}

void AVP_Brain_Actor_Predator::CancelVPTest()
{
	m_xVentagePointEvaluator.Cancel();
}

void AVP_Brain_Actor_Predator::StartAimAtTest(bool bForgetLastTarget)
{
	m_xFindTargetToAimAt.Start(bForgetLastTarget);
}

bool AVP_Brain_Actor_Predator::IsAimAtTestFinished() const
{
	return m_xFindTargetToAimAt.Finished();
}

bool AVP_Brain_Actor_Predator::GetAimAtTestResult(Asura_Vector_3& xResult) const
{
	return m_xFindTargetToAimAt.GetResult(xResult);
}

void AVP_Brain_Actor_Predator::CancelAimAtTest()
{
	m_xFindTargetToAimAt.Cancel();
}

bool AVP_Brain_Actor_Predator::CanFireAt(const Asura_Vector_3& xTarget, bool bMustBePrecise) const
{
	// Old code that use LOS test - new code use movementquery to take the bolt size into account.
	//Asura_Vector_3 xOurPosition;
	//AVP_ServerActor_Predator* pxActor = m_pxBrain->GetPredatorActor();
	//if (!pxActor->GetMuzzlePosition(xOurPosition)) // we can not get muzzle position if weapon is not equipped - get next best thing and get head
	//{
	//pxActor->GetHeadPosition(xOurPosition);
	//}
	//
	//Asura_Vector_3 xAimDir = xTarget - xOurPosition;
	//float fMagnitute = xAimDir.Normalise(); 
	//
	//Asura_LOS_InputData xLosInput(xOurPosition, xAimDir, pxActor->GetGuid(), fMagnitute);
	//const Asura_LOS_OutputData* pxLosOutput = AVP_LOS::TestAgainstEnvironmentAndMoveableObjects(&xLosInput);
	//
	//if (pxLosOutput->m_fDistanceToIntersection >=  fMagnitute - 0.1f)
	//{
	//return true;
	//}
	//else
	//{
	//return false;
	//}
	

	Asura_Vector_3 xOurPosition;
	AVP_ServerActor_Predator* pxActor = GetPredatorActor();
	if (!pxActor->GetMuzzlePosition(xOurPosition)) // we can not get muzzle position if weapon is not equipped - get next best thing and get head
	{
		Asura_Assert(!bMustBePrecise, "CanFireAt failed because of no MuzzlePosition but exact caclulation required");
		pxActor->GetHeadPosition(xOurPosition);
	}

	Asura_Vector_3 xDisplacement = xTarget - xOurPosition; 
	float fRequestedMagnitute = xDisplacement.Magnitude();
	Asura_OBB xSphere(gbd_fPlasmaBallRadius);
	Asura_Physics_MovementQuery xQuery(Asura_AVector3(xOurPosition), Asura_AVector3(xDisplacement), xSphere, 
		Asura_Physics_MovementQuery::ASURA_PMQCP_SPHERE, 
		AsuraPolygonFlag_NoCollisions, 
		true, // bool bStopAtFirstCollision
		true, //bool bCollideWithStaticObjects ! different from default!
		false, // bool bCollideWithExternalControlObjects 
		false, // bool bCollideWithActorObjects 
		true, // bool bCollideWithEnvironment 
		true // bool bCollideWithDynamicObjects ! different from default!
		);

	Asura_Physics_MovementQuery_Results xQueryResults(xQuery);
	float fActualMagnitute = xQueryResults.GetAllowedDisplacement().Magnitude();

	if (fActualMagnitute >=  fRequestedMagnitute - gbd_fPlasmaBallRadius - 1.0f)
	{
		return true;
	}
	else
	{
		return false;
	}
}


bool AVP_Brain_Actor_Predator::WasAnyTargetSeenRecently(float fRecentTime) const
{	
	const Axon_Knowledge xKnowledge = m_pxKnowledgeSystem->GetBestThreatKnowledge();
	// if we have a threat to avoid 
	if (xKnowledge.GetGuid() != ASURA_GUID_UNREGISTERED && xKnowledge.m_fAge < fRecentTime)
	{
		return true;
	}

	for (u_int u =0; u < m_pxKnowledgeSystem->GetNumSecondaryThreats(); ++u)
	{
		const Axon_Knowledge xKnowledge = m_pxKnowledgeSystem->GetSecondaryThreatKnowledge(u);
		if (xKnowledge.GetGuid() != ASURA_GUID_UNREGISTERED && xKnowledge.m_fAge < fRecentTime)
		{
			return true;
		}
	}

	return false;
}
	
bool AVP_Brain_Actor_Predator::AreAllTargetsTooOld() const
{
	float fNotRecentTime = m_xConfig.m_xHighLevelBehaviour.m_fNotSeenRecentlyTime;
	const Axon_Knowledge xKnowledge = m_pxKnowledgeSystem->GetBestThreatKnowledge();
	// if we have a threat to avoid 
	if (xKnowledge.GetGuid() != ASURA_GUID_UNREGISTERED && xKnowledge.m_fAge < fNotRecentTime)
	{
		return false;
	}

	for (u_int u =0; u < m_pxKnowledgeSystem->GetNumSecondaryThreats(); ++u)
	{
		const Axon_Knowledge xKnowledge = m_pxKnowledgeSystem->GetSecondaryThreatKnowledge(u);
		if (xKnowledge.GetGuid() != ASURA_GUID_UNREGISTERED && xKnowledge.m_fAge < fNotRecentTime)
		{
			return false;
		}
	}

	return true;
}


void AVP_Brain_Actor_Predator::SetJumping( bool bIsJumping )
{
	Asura_Assert(m_bIsJumping != bIsJumping, "Setting Jump to existing value - mismatch");
	m_bIsJumping = bIsJumping;
}

void AVP_Brain_Actor_Predator::SetMelee( bool bIsMelee )
{
	Asura_Assert(m_bIsMelee != bIsMelee, "Setting Melee to existing value - mismatch");
	m_bIsMelee = bIsMelee;
}


u_int AVP_Brain_Actor_Predator::GetLargestBehaviourSize()
{
    u_int uLargestBehaviourSize = 0;

	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predator );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predator_Movement );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predator_GS2Move );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predator_Seeking );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predator_Stalking );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predator_Sneaking );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predator_AtVentagePoint );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predator_Flee );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predator_Melee );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predator_Test );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predator_TestBehaviour );
	
    return uLargestBehaviourSize;
}

u_int AVP_Brain_Actor_Predator::GetPathFinderFlagsFromClassification(u_int uClassification)
{
	u_int uFlags = 0;
	switch (uClassification)
	{
	case AVP_ENTITYCLASS_ALIEN:
	case AVP_ENTITYCLASS_ALIENQUEEN:
	case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
		uFlags = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_ALIEN_HIGH_MOBILITY | AVP_Navigation::AVP_AI_PATHFINDER_FLAG_ALIEN_MED_MOBILITY | 
			AVP_Navigation::AVP_AI_PATHFINDER_FLAG_ALIEN_LOW_MOBILITY | 
			ASURA_AI_PATHFINDER_FLAGS_CANFALL | ASURA_AI_PATHFINDER_FLAGS_CANFALLFAR | 
			ASURA_AI_PATHFINDER_FLAGS_CANJUMP | ASURA_AI_PATHFINDER_FLAGS_IGNOREZONES;
		break;
	case AVP_ENTITYCLASS_ALIENFACEHUGGER:
	case AVP_ENTITYCLASS_HUMAN:	
	case AVP_ENTITYCLASS_CIVILIAN:
	case AVP_ENTITYCLASS_MARINE:
	case AVP_ENTITYCLASS_ANDROID:
	case AVP_ENTITYCLASS_SENTRYGUN:
		uFlags = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID | ASURA_AI_PATHFINDER_FLAGS_IGNOREZONES;
		break;
	case AVP_ENTITYCLASS_PREDATOR:
	case AVP_ENTITYCLASS_PREDALIEN:
		uFlags = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_PREDATOR | 
			ASURA_AI_PATHFINDER_FLAGS_CANFALL | ASURA_AI_PATHFINDER_FLAGS_CANFALLFAR | 
			ASURA_AI_PATHFINDER_FLAGS_CANJUMP | ASURA_AI_PATHFINDER_FLAGS_IGNOREZONES;
		Asura_Assert(false, "Unexpected predator as target");
		break;
	default:
		Asura_Assert(false, "Unexpected source classification as target");
	}

	return uFlags;
}


#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Brain_Actor_Predator::DoRenderDebugInfo()
{
	char szInfo[100];
	GetCloakDebugString(100, szInfo);
	Asura_Vector_2 xDebugOutputPos = GetDebugOutputPos();
	Asura_Font_System::Print( xDebugOutputPos.x, xDebugOutputPos.y, 0.5f, 0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "Cloak: %s", szInfo );
	xDebugOutputPos.y += 10.0f;
	GetWeaponDebugString(100, szInfo);
	Asura_Font_System::Print( xDebugOutputPos.x, xDebugOutputPos.y, 0.5f, 0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "Weapon: %s", szInfo );
	xDebugOutputPos.y += 10.0f;


	PARENT::DoRenderDebugInfo();

	//m_xFindTargetToAimAt.RenderDebugInfo(GetDebugOutputPos());
	//m_xVentagePointEvaluator.RenderDebugInfo(GetDebugOutputPos());

// 	Asura_Vector_3 xHeadPosition;
// 	GetActor()->GetHeadPosition(xHeadPosition);
// 	Asura_Quat xOrient;
// 	xOrient.FromMatrix(GetActor()->GetOrientation());
// 
// 	Asura_DebugLineRender::AddAxis(xHeadPosition, xOrient);

}
#endif

void AVP_Brain_Actor_Predator::RegisterCallbacks()
{
	PARENT::RegisterCallbacks();

	if ( m_iKnowledgeUpdateHandle == ASURA_INVALID_HANDLE )
	{
		Asura_Scheduler& xKnowledgeUpdateScheduler = Axon_System::GetKnowledgeUpdateScheduler();
		Asura_Callback_MemberFunction_1Arg<void, float, AVP_Brain_Actor_Predator > xKnowledgeCallback( this, &AVP_Brain_Actor_Predator::DoKnowledgeUpdate );
		m_iKnowledgeUpdateHandle = xKnowledgeUpdateScheduler.RegisterCallback( 0.1f, &xKnowledgeCallback );
	}
}

void AVP_Brain_Actor_Predator::UnregisterCallbacks()
{
	if ( m_iKnowledgeUpdateHandle != ASURA_INVALID_HANDLE )
	{
		Axon_System::GetKnowledgeUpdateScheduler().UnregisterCallback( m_iKnowledgeUpdateHandle );
		m_iKnowledgeUpdateHandle = ASURA_INVALID_HANDLE;
	}

	PARENT::UnregisterCallbacks();
}

bool AVP_Brain_Actor_Predator::HandleMessage( const Asura_Message* pxMessage )
{

	switch (pxMessage->GetMsgID())
	{
	case AVP_MSGID_ATTACK_SCRIPTED_TARGET:
		{
			const AVP_Message_AttackScriptedTarget* pxMsgData = static_cast< const AVP_Message_AttackScriptedTarget*>( pxMessage->GetData() );		
			AVP_ServerEntity_ShootAt* pxShootAt = pxMsgData ? AVP_ServerEntity_ShootAt::FindEntity( pxMsgData->m_uShootAtGuid ) : NULL;
			if (!pxShootAt)
			{
				return true;
			}

			if ( pxShootAt->GetShootAtPlayer() )
			{
				const AVP_ServerEntity_PlayerInfo* pxPlayerInfo = AVP_ServerEntity_PlayerInfo::FindEntity(ASURA_GUID_CLIENT_START);
				const AVP_ServerEntity_Actor* pxPlayerActor = pxPlayerInfo ? AVP_ServerEntity_Actor::FindEntity(pxPlayerInfo->GetPuppetGuid()) : NULL;
				if (!pxPlayerActor)
				{
					return true;
				}
				GetKnowledgeSystem()->SetShootAtTarget(pxPlayerActor->GetGuid(), false);
			}
			else if ( pxShootAt->GetShouldShootAtLinkedEntity() )
			{
				GetKnowledgeSystem()->SetShootAtTarget(pxShootAt->GetShootAtEntityGuid(), false);
			}
			else
			{
				GetKnowledgeSystem()->SetShootAtTarget(pxShootAt->GetGuid(), true);
			}
			return true;
		}

	case AVP_MSGID_STOP_ATTACKING_SCRIPTED_TARGET:
		{
			GetKnowledgeSystem()->ResetShootAtTarget();
			return true;
		}

    default:
		return PARENT::HandleMessage( pxMessage );
	}
}

DEBUGVAR float dbg_fSlowRange = 4.0f;
DEBUGVAR float dbg_fMediumRange = 8.0f;
DEBUGVAR float dbg_fFastRange = 10.0f;

float AVP_Brain_Actor_Predator::GetNearbyRange() const
{
	switch (GetActor()->GetAIAnimStateMachine()->GetTargetState())
	{

	case AVP_Vertebrate_AIAnimStateMachine::ACTOR_STATE_IDLE:
		return 0.0f;
	case AVP_Vertebrate_AIAnimStateMachine::VERTEBRATE_STATE_MOVE_SLOW:
		return dbg_fSlowRange;
	default:
	case AVP_Vertebrate_AIAnimStateMachine::VERTEBRATE_STATE_MOVE_MEDIUM:
		return dbg_fMediumRange;
	case AVP_Vertebrate_AIAnimStateMachine::VERTEBRATE_STATE_MOVE_FAST:
		return dbg_fFastRange;
	}
}

#ifndef ASURA_NO_DEBUG_OUTPUT
bool AVP_Brain_Actor_Predator::Verify()
{
	Asura_Assert( m_pxKnowledgeSystem, "AVP_Brain_Actor_Predator::Verify - no knowledge system" );
	return ( PARENT::Verify() && m_pxKnowledgeSystem && m_pxKnowledgeSystem->Verify() );
}
#endif

void AVP_Brain_Actor_Predator::EstablishNavData()
{
	AVP_Navigation::NavData& xNavData = GetNavData();

	xNavData.m_uPathfinderFlags = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_PREDATOR | ASURA_AI_PATHFINDER_FLAGS_IGNOREZONES;

	// Establish our current waypoint ID.
	AVP_Navigation::TestParams xTestParams;
	xTestParams.m_uPathfinderFlags		= xNavData.m_uPathfinderFlags;
	xTestParams.m_bRejectNonalignedWPs	= true;
	xTestParams.m_xNormal				= -GetPredatorActor()->GetCurrentGravityDirection();
	AVP_Navigation::GetNearestWaypointID( GetPredatorActor()->GetPosition(), xNavData.m_usCurrentWPID, xTestParams );

	// As a predator we don't use zones.
	xNavData.m_usCurrentZone = ASURA_NAVVERTEX_MAX_ZONE_ID;

	xNavData.m_fCharacterHeight = 2.0f;
	xNavData.m_fCharacterWidth  = 1.0f;

	xNavData.m_eMovementMode = AVP_Navigation::MM_MEDIUM;
}

void AVP_Brain_Actor_Predator::DoKnowledgeUpdate( const float fTimeslice )
{
	if ( m_pxKnowledgeSystem )
	{
		m_pxKnowledgeSystem->Update( fTimeslice );
	}
}

DEBUGVAR float dbg_fPreFallTime = 0.2f;

void AVP_Brain_Actor_Predator::UpdateFalling(float fTimeslice )
{
	m_bJustFinishedFalling = false;

	if (IsJumping())
	{
		m_bIsFalling = false;
		m_bJustFinishedFalling = false;	
		return;
	}

	bool bIsSupported = GetActor()->GetPhysicsObject()->IsSupported();

	if (!bIsSupported)
	{
		m_fNotSupportedTime += fTimeslice;
		if (m_fNotSupportedTime > dbg_fPreFallTime)
		{
			m_bIsFalling = true;
		}
	}
	else
	{
		m_fNotSupportedTime = 0.0f;
		if (m_bIsFalling)
		{
			m_bIsFalling = false;
			m_bJustFinishedFalling = true;
		}
	}
}


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