//////////////////////////////////////////////////////////////////////////////
//
// AVP_Brain_Alien.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_Alien.h"

// Asura
#include "Blueprint/Asura_Blueprint.h"
#include "Core/Asura_LOS.h"
#include "Entity/PhysicalObject/Moves/Asura_Movement_System.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Rendering/Asura_Font_System.h"

// Axon
#include "Core/Axon_System.h"

// AVP
#include "Core/AVP_LOS.h"
#include "Core/AVP_Plane.h"
#include "Entity/Controller/AVP_ServerEntity_Squad_Alien.h"
#include "Entity/Controller/AVP_ServerEntity_Team_Alien.h"
#include "Entity/GamePlayVolume/AVP_ServerEntity_GamePlayVolume.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Alien/AVP_ClientActor_Alien.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Alien/AVP_ServerActor_Alien.h"
#include "Entity/PlayerInfo/AVP_ServerEntity_PlayerInfo.h"
#include "Entity/Spawners/AVP_ServerEntity_SpawnPoint2.h"
#include "Moves/Vertebrates/Alien/AVP_Alien_MovesJump.h"

// AVP axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Ambush.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Attack.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Attack2.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_AvoidLight.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_CycleRoundDelayed.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_CycleRoundImmediate.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_GrapplePuppet.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_HideInShadows.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Injured.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_LightTest.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_MoveBetweenCover.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Root.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Stalk.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Standard.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Swarm.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_SwarmIn.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Wander.h"
#include "Core/AVP_Waypoint_Manager.h"
#include "Core/BrainTypes/AVP_Brain_Team_Alien.h"
#include "Knowledge/AVP_Knowledge_System_AlienTeam.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Brain_Alien
//
//////////////////////////////////////////////////////////////////////////////

AVP_Brain_Actor_Alien::AVP_Brain_Actor_Alien( AVP_ServerActor_Alien* pxAlien )
: PARENT( pxAlien )
, m_uCurrentTargetGuid( ASURA_GUID_UNREGISTERED )
, m_eCurrentTargetRange( AVP_AR_OK )
, m_uCurrentGPV( ASURA_GUID_UNREGISTERED )
, m_uLOSToCurrentTargetTestFrame( 0 )
, m_fTimeSinceActivate( 0.0f )
, m_uFlagsNotChunked( 0 )
, m_xToCurrentTarget( Asura_Maths::ZeroVector3 )
, m_fCurrentTargetDistance( 0.0f )
, m_uFlagsChunked( 0 )
, m_xBehaviourData()
, m_fDangerJumpFraction( 0.75f )
, m_fInjuredLifeSpan( 15.0f )
, m_uMobility(0)
, m_uMinNumStandAttack(1)
, m_uMaxNumStandAttack(3)
, m_eInLightStatus(IN_DARK)
, m_eInMarineLightStatus(MARINE_LIGHT_NO)
, m_bInLightForMarine(false)
{
	m_xMovement.SetAlienBrain( this );
}

AVP_Brain_Actor_Alien::~AVP_Brain_Actor_Alien()
{
	StoppedAttackingCurrentTarget();
	StoppedTargetingCurrentTarget();
	m_xTargetIgnoreList.Clear();
}

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

    DestroyAllBehaviours();
	if ( IsActive() )
	{
		CreateActivateAndPushBehaviour( AVP_BEHAVIOUR_TYPE_ALIEN_ROOT );
		CreateGS2BehaviourIfNecessary();
	}

	Asura_Value xValue;
	if ( xBlueprint.GetParameterValueByHashID( 0x0189dca7, xValue ) ) // Asura_GetHashID( "InjuredLifespan" )
	{
		m_fInjuredLifeSpan = xValue.GetFloatValue();
	}

	if ( xBlueprint.GetParameterValueByHashID( 0x9a13f3fb, xValue ) ) // Asura_GetHashID( "AIDangerJumpFraction" )
	{
		m_fDangerJumpFraction = xValue.GetFloatValue();
	}

	if ( xBlueprint.GetParameterValueByHashID( 0xd7e90dab, xValue ) ) // Asura_GetHashID( "Mobility" )
	{
		m_uMobility = Asura_Maths::ClampToRange(xValue.GetIntValue(), 0, 2);
	}

	if ( xBlueprint.GetParameterValueByHashID( 0xf7fb7779, xValue ) ) // Asura_GetHashID( "StandAttacks.Min" )
	{
		m_uMinNumStandAttack = xValue.GetIntValue();
	}

	if ( xBlueprint.GetParameterValueByHashID( 0xf7fb768b, xValue ) ) // Asura_GetHashID( "StandAttacks.Max" )
	{
		m_uMaxNumStandAttack = xValue.GetIntValue();
	}

	if ( xBlueprint.GetParameterValueByHashID( 0x5fff7f81, xValue ) ) // Asura_GetHashID( "PreferAttackToJump" )
	{
		m_bPreferAttackToJump = xValue.GetBoolValue();
	}

	EstablishNavData();
}

void AVP_Brain_Actor_Alien::PostLoadInitialise()
{
	RequestRegisterWithTeam();

	PARENT::PostLoadInitialise();
}

void AVP_Brain_Actor_Alien::DoPerFrameUpdate( const float fTimeslice )
{
	if ( IsPaused() || !IsActive() ) { return; }


	m_fTimeSinceActivate += fTimeslice;

	UpdateCurrentTargetData();
	UpdateTargetIgnoreList( fTimeslice );

	if ( !m_bIsInjured )
	{
		m_bIsInjured = ( GetAlienActor()->GetLimbDamage() != AVP_Alien_AIAnimStateMachine::LIMBDMG_NONE ) && GetAlienActor()->GetHealth() > 0.0f;

		if (m_bIsInjured)
		{
			GetAlienActor()->GetPhysicsObjectHandler()->SetForcedGravity(Asura_AVector3(0.0f, 1.0f, 0.0f));
			GetAlienActor()->GetPhysicsObjectHandler()->SetWalkWalkingDisabled(true);
		}
	}


	m_xMovement.PerFrameUpdate( fTimeslice );

	u_short usLastWPID = GetNavData().m_usCurrentWPID;

	PARENT::DoPerFrameUpdate( fTimeslice );

	if (usLastWPID != GetNavData().m_usCurrentWPID || m_uCurrentGPV == ASURA_GUID_UNREGISTERED)
	{
		UpdateCurrentGPV();
		m_bIsLooseAvoidance = true;

		const AVP_ServerEntity_GamePlayVolume* pxGPV = AVP_ServerEntity_GamePlayVolume::FindEntity(m_uCurrentGPV);
		if (pxGPV)
		{
			switch (pxGPV->GetGamePlayAreaType())
			{
			case AVP_ServerEntity_GamePlayVolume::TIGHT_CORRIDOR:
			case AVP_ServerEntity_GamePlayVolume::SMALL_ROOM:
			case AVP_ServerEntity_GamePlayVolume::NONE_ASSIGNED:
				m_bIsLooseAvoidance = false;
				break;
			case AVP_ServerEntity_GamePlayVolume::LARGE_CORRIDOR:
			case AVP_ServerEntity_GamePlayVolume::LARGE_ROOM:
			case AVP_ServerEntity_GamePlayVolume::OPEN_SPACE:
				m_bIsLooseAvoidance = true;
				break;
			case AVP_ServerEntity_GamePlayVolume::IGNORE_GPV:
			default:
				Asura_Assertf(false, ("Game play volume %d has invalid type", m_uCurrentGPV));
			}
		}
		else
		{
			m_bIsLooseAvoidance = false;
			Asura_Assertf(false, ("Alien %d at (%.2f %.2f %.2f) outside game play volume", GetActor()->GetGuid(), GetActor()->GetPosition().x, GetActor()->GetPosition().y, GetActor()->GetPosition().z));
		}
	}

	UpdateHiddenInShadows();
}


 void AVP_Brain_Actor_Alien::Shutdown()
 {
	 m_bLookForDestroyedSquad = true;
	 PARENT::Shutdown();
 }

#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Brain_Actor_Alien::DoRenderDebugInfo()
{
	PARENT::DoRenderDebugInfo();

	if ( GetActor()->IsDead() || !GetActor()->IsEntityActive() )
	{
		return;
	}

	Asura_Vector_3 xOutputPos = GetActor()->GetPosition();
	if (!Asura_Camera::ProjectWorldCoordsToScreenSpace( xOutputPos, true ) || xOutputPos.z <= 0.0f)
	{
		return;
	}


	char cVisible = '?';
	switch (m_eInLightStatus)
	{
	case IN_DARK:
		cVisible = 'D';
		break;
	case IN_LIGHT:
		cVisible = 'L';
		break;
	case BETWEEN_LIGHT_AND_DARK:
		cVisible = 'M';
		break;
	}

	char cInTorch = '?';
	switch (m_eInMarineLightStatus)
	{
	case MARINE_LIGHT_NO:
		cInTorch = 'N';
		break;
	case MARINE_LIGHT_YES:
		cInTorch = 'Y';
		break;
	case MARINE_LIGHT_CLOSE:
		cInTorch = 'C';
		break;
	}

	enum InMarineLightStatus { MARINE_LIGHT_NO, MARINE_LIGHT_YES, MARINE_LIGHT_CLOSE };
	Asura_Font_System::Print
		(
		m_xDebugOutputPos.x, 
		m_xDebugOutputPos.y, 
		0.5f, 
		0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, 
		"Visible: %c In Torch Light: %c", cVisible, cInTorch
		);
	m_xDebugOutputPos.y += 10.0f;

	m_xMovement.RenderDebugInfo();
}

bool AVP_Brain_Actor_Alien::Verify()
{
	// Test team, brain and knowledge system
	if ( !GetAlienTeam() )
	{
		Asura_Assert( false, "AVP_Brain_Actor_Alien::Verify - no alien team" );
		return false;
	}
	if ( !GetAlienTeam()->GetAlienTeamBrain() )
	{
		Asura_Assert( false, "AVP_Brain_Actor_Alien::Verify - no alien team brain" );
		return false;
	}
	if ( !GetAlienTeam()->GetAlienTeamBrain()->GetKnowledgeSystem() )
	{
		Asura_Assert( false, "AVP_Brain_Actor_Alien::Verify - no alien team knowledge system" );
		return false;
	}

	if ( !GetAlienSquad() )
	{
		Asura_Assert( false, "AVP_Brain_Actor_Alien::Verify - could not find squad" );
		return false;
	}

	if ( !GetClientAlienActor() )
	{
		Asura_Assert( false, "AVP_Brain_Actor_Alien::Verify - no client actor" );
		return false;
	}
	if ( !GetClientAlienActor()->GetMovementSystem() )
	{
		Asura_Assert( false, "AVP_Brain_Actor_Alien::Verify - no client actor movement system" );
		return false;
	}
	if ( !GetClientAlienActor()->GetAnimationHandler() )
	{
		Asura_Assert( false, "AVP_Brain_Actor_Alien::Verify - no client actor animation handler" );
		return false;
	}
	if ( !GetClientAlienActor()->GetAnimationHandler()->GetAnimation() )
	{
		Asura_Assert( false, "AVP_Brain_Actor_Alien::Verify - no client actor animation" );
		return false;
	}

	return PARENT::Verify();
}
#endif

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

    PARENT::ReadFromChunkStream( xStream );

    if ( iVersion == CURRENT_CHUNK_VERSION )
	{
		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xFlagsWrapper( m_uFlagsChunked );
		xStream >> xFlagsWrapper;

		if ( m_bBehaviourDataValid )
		{
			m_xBehaviourData.ReadFromChunkStream( xStream );
		}

		u_int uNumTIs;
		xStream >> uNumTIs;
		for ( u_int uTI = 0; uTI < uNumTIs; ++uTI )
		{
			TargetIgnoreData xTI;
			xStream >> xTI.m_uGuidToIgnore;
			xStream >> xTI.m_fRemoveTime;
			m_xTargetIgnoreList.Push_Back( xTI );
		}
	}
	else if ( iVersion == 3 )
	{
		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xFlagsWrapper( m_uFlagsChunked );
		xStream >> xFlagsWrapper;

		if ( m_bBehaviourDataValid )
		{
			m_xBehaviourData.ReadFromChunkStream( xStream );
		}
	}
#ifdef ASURA_TOOLS
	else if ( iVersion == 2 )
	{
		bool bDummy;
		xStream >> bDummy;

		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xFlagsWrapper( m_uFlagsChunked );
		xStream >> xFlagsWrapper;

		if ( m_bBehaviourDataValid )
		{
			m_xBehaviourData.ReadFromChunkStream( xStream );
		}
	}
	else if ( iVersion == 1 )
    {
		bool bDummy;
		xStream >> bDummy;
    }
    else if ( iVersion == 0 )
    {
    }
#endif
    else
    {
        Asura_Assertf( false, ( "AVP_Brain_NPC_Alien::ReadFromChunkStream - unhandled chunk version (%d)", iVersion ) );
    }
}

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

    PARENT::WriteToChunkStream( xStream );

	Asura_WriteToStream_Bitfield_Wrapper< u_int > xFlagsWrapper( m_uFlagsChunked );
	xFlagsWrapper.Add( m_bBehaviourDataValid, 1 );
	xFlagsWrapper.Add( m_bIsInjured,          1 );
	xStream << xFlagsWrapper;

	if ( m_bBehaviourDataValid )
	{
		m_xBehaviourData.WriteToChunkStream( xStream );
	}

	u_int uNumTIs = m_xTargetIgnoreList.Size();
	xStream << uNumTIs;
	for ( Asura_LinkList_ConstIt< TargetIgnoreData > xTIIt( m_xTargetIgnoreList ); !xTIIt.Done(); xTIIt.Next() )
	{
		xStream << xTIIt.GetCurrent()->m_uGuidToIgnore;
		xStream << xTIIt.GetCurrent()->m_fRemoveTime;
	}
}

#include "Core/Asura_MemoryManagement_DebugOff.h"
Axon_Behaviour* AVP_Brain_Actor_Alien::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_ALIEN_ATTACK:
        {
            pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_Attack( this );
            break;
        }

		case AVP_BEHAVIOUR_TYPE_ALIEN_AVOIDLIGHTTEST:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_AvoidLightTest( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_CYCLEROUNDDELAYED:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_CycleRoundDelayed( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_CYCLEROUNDIMMEDIATE:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_CycleRoundImmediate( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_GRAPPLEPUPPET:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_GrapplePuppet( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_INJURED:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_Injured( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_JUMPATTACK:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_JumpAttack( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_LIGHTTEST:
        {
            pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_LightTest( this );
            break;
        }

		case AVP_BEHAVIOUR_TYPE_ALIEN_MOVEBETWEENCOVER:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_MoveBetweenCover( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_MOVEMENTWRAPPER:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_MovementWrapper( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_ROOT:
		{
			pxBehaviour	= new ( pxMemory ) AVP_Behaviour_Alien_Root( this );
			break;
		}

        case AVP_BEHAVIOUR_TYPE_ALIEN_STALK:
        {
            pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_Stalk( this );
            break;
        }

		case AVP_BEHAVIOUR_TYPE_ALIEN_STANDARD:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_Standard( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_STANDATTACK:
        {
            pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_StandAttack( this );
            break;
        }

		case AVP_BEHAVIOUR_TYPE_ALIEN_SWARM:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_Swarm( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_SWARMIN:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_SwarmIn( this );
			break;
		}

        case AVP_BEHAVIOUR_TYPE_ALIEN_WANDER:
        {
            pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_Wander( this );
            break;
        }

		case AVP_BEHAVIOUR_TYPE_ALIEN_ATTACK2:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_Attack2( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_HIDE_IN_SHADOWS:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_HideInShadows( this );
			break;
		}

		case AVP_BEHAVIOUR_TYPE_ALIEN_AMBUSH:
			{
				pxBehaviour = new ( pxMemory ) AVP_Behaviour_Alien_Ambush( 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"

void AVP_Brain_Actor_Alien::OnActivate()
{
	m_fTimeSinceActivate = 0.0f;
	PARENT::OnActivate();
	m_xMovement.Initialise();
}

void AVP_Brain_Actor_Alien::OnDeactivate()
{
	m_xMovement.Shutdown();
	PARENT::OnDeactivate();
}

void AVP_Brain_Actor_Alien::OnPause()
{
	PARENT::OnPause();
	m_xMovement.Pause();
}

void AVP_Brain_Actor_Alien::OnUnpause()
{
	m_xMovement.Unpause();
	PARENT::OnUnpause();
}

bool AVP_Brain_Actor_Alien::HandleMessage( const Asura_Message* pxMessage )
{
	return m_xMovement.HandleMessage( pxMessage ) || PARENT::HandleMessage( pxMessage );
}

bool AVP_Brain_Actor_Alien::RegisterWithTeam()
{
	if ( !PARENT::RegisterWithTeam() )
	{
		return false;
	}

	// Register with the team.
    if ( !GetAlienTeam() )
	{
		return false;
	}
    GetAlienTeam()->Register( GetOwnerGuid() );

	// Register with our squad.
	AVP_ServerEntity_Squad_Alien* pxAlienSquad = GetAlienSquad();
	if ( pxAlienSquad )
	{
		pxAlienSquad->AddMember( GetOwnerGuid() );
		return true;
	}
	else
	{
		return false;
	}
}

bool AVP_Brain_Actor_Alien::UnregisterWithTeam()
{
	if ( !PARENT::UnregisterWithTeam() )
	{
		return false;
	}

	// Unregister with the team.
    if ( !GetAlienTeam() )
	{
		return false;
	}
    GetAlienTeam()->Unregister( GetOwnerGuid() );

	// Unregister with our squad.
	AVP_ServerEntity_Squad_Alien* pxAlienSquad = GetAlienSquad();
	if ( pxAlienSquad )
	{
		pxAlienSquad->RemoveMember( GetOwnerGuid() );
		return true;
	}
	else
	{
		return false;
	}
}

void AVP_Brain_Actor_Alien::UpdateBehaviours( const float fTimeslice )
{
	PARENT::UpdateBehaviours( fTimeslice );

	if ( IsPaused() || !IsActive() ) { return; }

	m_xMovement.Update( fTimeslice );

	//## This is a temporary fix to get around our bad initialisation sequences.
	//## This should not have to remain once these have been fixed.
	if ( m_pxFirstBehaviour == NULL )
	{
		// If we end up with no top behaviour then create the default behaviour.
		CreateActivateAndPushBehaviour( AVP_BEHAVIOUR_TYPE_ALIEN_ROOT );
	}
}

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

	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_Attack );
    AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_AvoidLightTest );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_CycleRoundDelayed );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_CycleRoundImmediate );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_GrapplePuppet );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_Injured );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_LightTest );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_MoveBetweenCover );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_MovementWrapper );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_Root );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_Stalk );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_Standard );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_StandAttack );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_Swarm );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_SwarmIn );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_Wander );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_Attack2 );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Alien_HideInShadows );
    return uLargestBehaviourSize;
}

AVP_ServerActor_Alien* AVP_Brain_Actor_Alien::GetAlienActor() const
{
    return static_cast< AVP_ServerActor_Alien* >( GetActor() );
}

AVP_ClientActor_Alien* AVP_Brain_Actor_Alien::GetClientAlienActor() const
{
	return static_cast< AVP_ClientActor_Alien* >( GetClientActor() );
}

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

	u_int uMobilityFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_ALIEN_LOW_MOBILITY;
	switch (m_uMobility)
	{
	default:
	case 0:
		uMobilityFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_ALIEN_LOW_MOBILITY;
		break;
	case 1:
		uMobilityFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_ALIEN_MED_MOBILITY;
		break;
	case 2:
		uMobilityFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_ALIEN_HIGH_MOBILITY;
		break;
	}

	xNavData.m_eMovementMode    = AVP_Navigation::MM_MEDIUM;
	xNavData.m_uPathfinderFlags = uMobilityFlag | ASURA_AI_PATHFINDER_FLAGS_IGNOREZONES;
	xNavData.m_usCurrentZone    = ASURA_NAVVERTEX_MAX_ZONE_ID;
	xNavData.m_fCharacterHeight = 1.0f;
	xNavData.m_fCharacterWidth  = 1.0f;

	AVP_Navigation::TestParams xTestParams;
	xTestParams.m_uPathfinderFlags		= xNavData.m_uPathfinderFlags;
	xTestParams.m_bRejectNonalignedWPs	= true;
	xTestParams.m_xNormal				= -GetActor()->GetCurrentGravityDirection();
	AVP_Navigation::GetNearestWaypointID( GetActor()->GetPosition(), xNavData.m_usCurrentWPID, xTestParams );
}

void AVP_Brain_Actor_Alien::StartGS2()
{
	m_xMovement.SetGS2Mode(true);
	PARENT::StartGS2();
}

void AVP_Brain_Actor_Alien::EndGS2()
{
	m_xMovement.SetGS2Mode(false);
	PARENT::EndGS2();
}


Asura_Guid AVP_Brain_Actor_Alien::GetPrimaryThreatGuid()
{
	return m_uCurrentTargetGuid;
}

Asura_Guid AVP_Brain_Actor_Alien::GetAlertGuid()
{
	return m_uCurrentTargetGuid;
}

const AVP_AlienTeam_Knowledge* AVP_Brain_Actor_Alien::ChooseNewTarget()
{
	const AVP_ServerEntity_Team_Alien* pxAlienTeam = GetAlienTeam();

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

	const Asura_Vector_3& xActorPosition = GetActor()->GetPosition();

	// Work out the maximum threat distance so we can normalise during the scoring.
	float fMaxDistanceSq = 0.0f;
	for ( Asura_Hashed_List_ConstIt< AVP_AlienTeam_Knowledge > xKnowIter( &pxAlienTeam->GetKnowledgeSystem()->GetAllKnowledge() );
		!xKnowIter.Done();
		xKnowIter.Next())
	{
		const AVP_AlienTeam_Knowledge* pxKnowledge = xKnowIter.GetCurrent();
		if ( !pxKnowledge )
		{
			continue;
		}

		const float fDistanceSq = ( pxKnowledge->m_xPosition - xActorPosition ).MagnitudeSquared();
		if ( fDistanceSq > fMaxDistanceSq )
		{
			fMaxDistanceSq = fDistanceSq;
		}
	}

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

		// We keep a local ignore list for targets we've not been able to get to, etc.
		if ( ShouldIgnoreTarget( pxKnowledge->GetGuid() ) )
		{
			continue;
		}

		float fThreatValue = 0.0f;

		// Score based on priority.
		static float s_fPriorityWeighting = 5.0f;
		int iTargetPriority;
		if ( !CalcTargetingPriority( pxKnowledge->m_iThreatClassification, pxKnowledge->GetGuid(), iTargetPriority ) )
		{
			iTargetPriority = AVP_AI_TargetPriorityData::NORMAL;
		}
		if ( iTargetPriority == AVP_AI_TargetPriorityData::IGNORE_P )
		{
			continue;
		}
		const float fPriorityFraction = GetScoreFromPriority( iTargetPriority );
		fThreatValue += fPriorityFraction * s_fPriorityWeighting;

		// Mark down threats with too many targeters.
		static float s_fTargeterWeighting = 1.0f;
		const float fTargetersPerThreat = GetAlienTeam()->GetNumMembers() / static_cast< float >( uNumThreats );
		const float fTargeterFraction = ( 1.0f - ( pxKnowledge->m_uNumTargeters / fTargetersPerThreat ) );
		fThreatValue += fTargeterFraction * s_fTargeterWeighting;

		// Nearer is better.
		static float s_fDistanceWeighting = 1.0f;
		const float fDistanceSq = ( pxKnowledge->m_xPosition - xActorPosition ).MagnitudeSquared();
		const float fDistanceFraction = ( 1.0f - ( fDistanceSq / fMaxDistanceSq ) );
		fThreatValue += fDistanceFraction * s_fDistanceWeighting;

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

	return pxBestTarget;
}

float AVP_Brain_Actor_Alien::GetScoreFromPriority( const int iPriority )
{
	static const float s_fIgnorePriorityScore = -Asura_Maths::LargeFloat;
	static const float s_fLowPriorityScore    = 0.2f;
	static const float s_fNormalPriorityScore = 1.0f;
	static const float s_fHighPriorityScore   = 1.8f;

	switch ( iPriority )
	{
		case AVP_AI_TargetPriorityData::IGNORE_P:	return s_fIgnorePriorityScore;
		case AVP_AI_TargetPriorityData::LOW:		return s_fLowPriorityScore;
		default:
		case AVP_AI_TargetPriorityData::NORMAL:		return s_fNormalPriorityScore;
		case AVP_AI_TargetPriorityData::HIGH:		return s_fHighPriorityScore;
	}
}

bool AVP_Brain_Actor_Alien::ShouldIgnoreTarget( const Asura_Guid uTargetGuid )
{
	return ( m_xTargetIgnoreList.FindByGuid( uTargetGuid ) != NULL );
}

void AVP_Brain_Actor_Alien::UpdateTargetIgnoreList( const float fTimeslice )
{
	const float fGameTime = Asura_Timers::GetLocalScaledTime();

	for ( Asura_LinkList_It< TargetIgnoreData > xTPIter( m_xTargetIgnoreList ); !xTPIter.Done();   )
	{
		TargetIgnoreData* pxTP = xTPIter.GetCurrent();
		if ( pxTP->m_fRemoveTime <= fGameTime )
		{
			xTPIter.DeleteCurrent();
		}
		else
		{
			xTPIter.Next();
		}
	}
}


DEBUGVAR float dbg_fTourchMinMultiplier = 1.0f;
DEBUGVAR float dbg_fTourchMaxMultiplier = 1.2f;
DEBUGVAR float dbg_fTourchDelta = 1.0f;
void AVP_Brain_Actor_Alien::UpdateHiddenInShadows()
{
	const float fLuminaceFactor = AVP_Waypoint_Manager::GetWPLuminance( GetNavData().m_usCurrentWPID );

	bool bTotallyHidden = true;
	m_eInLightStatus = IN_DARK;
	m_eInMarineLightStatus = MARINE_LIGHT_NO;
	m_bInLightForMarine = false;

	const Asura_Vector_3 & xOurPosition = GetActor()->GetPosition();
	const Asura_Vector_3 xOurNormal( -GetActor()->GetCurrentGravityDirection() );


	const u_int uNumLights = AVP_ServerEntity_Team_Alien::GetInstance()->GetNumLights();
	for ( u_int uLight = 0; uLight < uNumLights; ++uLight )
	{
		const AVP_ServerEntity_Team_Alien::LightInfo& xLightInfo = AVP_ServerEntity_Team_Alien::GetInstance()->GetLightInfo( uLight );

		if (xLightInfo.m_eType == AVP_ServerEntity_Team_Alien::LightInfo::IDLE_FLARE ) // ignore idle flares - they already taken into account in the way points
		{
			continue;
		}

		const float fDistanceFromSourceSq = (xOurPosition - xLightInfo.m_xSourcePosition).MagnitudeSquared();
		if (fDistanceFromSourceSq > xLightInfo.m_fCutoffDistance * xLightInfo.m_fCutoffDistance)
		{
			continue;
		}

		Asura_Vector_3 xLightPlaneUp = xLightInfo.m_xPosition - xLightInfo.m_xSourcePosition;
		xLightPlaneUp.Normalise();
		AVP_Plane xLightPlane(xLightInfo.m_xPosition, xLightPlaneUp);
		Asura_Vector_3 xOurPositionInLightPlane;
		xLightPlane.CalculateClosestPoint(xOurPosition, xOurPositionInLightPlane);

		Asura_Vector_3 xFromVector = xOurPositionInLightPlane - xLightInfo.m_xPosition;
		float fDistanceFromLightSq = xFromVector.MagnitudeSquared();
		float fMinDistanceSq = ((xLightInfo.m_fRadius * dbg_fTourchMinMultiplier) * (xLightInfo.m_fRadius * dbg_fTourchMinMultiplier));
		if (fDistanceFromLightSq <  fMinDistanceSq) // if we totally in a marine light - we totally visible and in marine light
		{
			m_eInLightStatus = IN_LIGHT;
			m_bInLightForMarine = true;
			m_eInMarineLightStatus = MARINE_LIGHT_YES;
			return;
		}
		else if (fDistanceFromLightSq < (xLightInfo.m_fRadius * dbg_fTourchMaxMultiplier + dbg_fTourchDelta) * (xLightInfo.m_fRadius * dbg_fTourchMaxMultiplier + dbg_fTourchDelta) )
		{ // not in marine light but pretty close to it ...
			bTotallyHidden = false;
		}
	}

	// not in marine light 
	if (!bTotallyHidden) // but one light is close - mark as if we in marine light...
	{
		m_eInMarineLightStatus = MARINE_LIGHT_CLOSE;
	}
	else
	{
		m_eInMarineLightStatus = MARINE_LIGHT_NO;
	}

	
	// ignoring the marine update light status

	if (fLuminaceFactor > AVP_Waypoint_Manager::GetDefaultMaxHideLuminanceForMarineAI() )
	{
		m_eInLightStatus = IN_LIGHT;
		m_bInLightForMarine = true;
		return;
	}

	if (fLuminaceFactor > AVP_Waypoint_Manager::GetDefaultMaxHideLuminance() )
	{
		m_eInLightStatus = IN_LIGHT;
		return;
	}

	if (fLuminaceFactor > AVP_Waypoint_Manager::GetDefaultMinHideLuminance()) // && fLuminaceFactor < AVP_Waypoint_Manager::GetDefaultMaxHideLuminance()
	{
		m_eInLightStatus = BETWEEN_LIGHT_AND_DARK;
		return;
	}

	// fLuminaceFactor < AVP_Waypoint_Manager::GetDefaultMinHideLuminance() - so only marine light left
	if (!bTotallyHidden)
	{
		m_eInLightStatus = BETWEEN_LIGHT_AND_DARK;
	}
	else
	{
		m_eInLightStatus = IN_DARK;
	}
}

AVP_ServerEntity_SpawnPoint2* AVP_Brain_Actor_Alien::GetBestExit()
{
	AVP_ServerEntity_SpawnPoint2* pxBestSpawnPoint = NULL;
	float fSmallestDistSq = Asura_Maths::LargeFloat;

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

	UpdateCurrentGPV();

	AVP_ServerEntity_GamePlayVolume* pxGPV = AVP_ServerEntity_GamePlayVolume::FindEntity( m_uCurrentGPV );
	Asura_Assertf( pxGPV, ( "AVP_Brain_Actor_Alien::GetBestExit - (%d) not in a gpv", GetOwnerGuid() ) );
	if ( pxGPV )
	{
		const u_int uNumSpawnPoints = pxGPV->GetNumSpawnPointGuids();
		for ( u_int uSpawnPoint = 0; uSpawnPoint < uNumSpawnPoints; ++uSpawnPoint )
		{
			AVP_ServerEntity_SpawnPoint2* pxSpawnPoint = AVP_ServerEntity_SpawnPoint2::FindEntity( pxGPV->GetSpawnPointGuid( uSpawnPoint ) );
			if ( !pxSpawnPoint || !pxSpawnPoint->IsEntityActive() || !pxSpawnPoint->GetUseForDespawning() || pxSpawnPoint->IsOccupied() )
			{
				continue;
			}

			// Have to use straight line distance currently.
			const Asura_Vector_3 xToSpawnPoint = pxSpawnPoint->GetPosition() - xOurPosition;
			const float			 fToSpawnPointSq = xToSpawnPoint.MagnitudeSquared();
			if ( fToSpawnPointSq < fSmallestDistSq )
			{
                fSmallestDistSq  = fToSpawnPointSq;
				pxBestSpawnPoint = pxSpawnPoint;
			}
		}
	}

	return pxBestSpawnPoint;
}

bool AVP_Brain_Actor_Alien::CurrentTargetWithinRange( const float fRangeMin, const float fRangeMax ) const
{
	return ( fRangeMin <= m_fCurrentTargetDistance ) && ( m_fCurrentTargetDistance <= fRangeMax );
}


DEBUGVAR float dbg_fVerySmallDeltaSquared = 1.0f*1.0f;
DEBUGVAR float dbg_fSmallDeltaSquared = 2.0f*2.0f;
DEBUGVAR float dbg_fMediumDeltaSquared = 4.0f*4.0f;
DEBUGVAR float dbg_fLargeDeltaSquared = 8.0f*8.0f;
DEBUGVAR float dbg_fVeryLargeDeltaSquared = 15.0f*15.0f;

DEBUGVAR float dbg_fVerySmallPathDistance = 7.0f;
DEBUGVAR float dbg_fSmallPathDistance = 12.0f;
DEBUGVAR float dbg_fMediumPathDistance = 20.0f;
DEBUGVAR float dbg_fLargePathDistance = 40.0f;

DEBUGVAR float dbg_fVerySmallTurnCosDelta = Asura_Maths::Cosine(Asura_Maths::DegToRad(15.0f));
DEBUGVAR float dbg_fSmallTurnCosDelta = Asura_Maths::Cosine(Asura_Maths::DegToRad(30.0f));
DEBUGVAR float dbg_fMediumTurnCosDelta = Asura_Maths::Cosine(Asura_Maths::DegToRad(45.0f));


bool AVP_Brain_Actor_Alien::ShouldRefreshPath(float fDeltaDestinationSquared, float fDeltaTargetForwardCos) const
{
	
	const AVP_Navigation::NavData& xNavData = GetNavData();
	if (xNavData.m_usRouteLength == 0)
	{
		return true;
	}

	float fPathLength = GetMovement().GetLengthLeftOnPath();

	if (fPathLength < dbg_fVerySmallPathDistance)
	{
		if (fDeltaDestinationSquared > dbg_fVerySmallDeltaSquared || fDeltaTargetForwardCos < dbg_fVerySmallTurnCosDelta)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else if (fPathLength < dbg_fSmallPathDistance)
	{
		if (fDeltaDestinationSquared > dbg_fSmallDeltaSquared || fDeltaTargetForwardCos < dbg_fSmallTurnCosDelta)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else if (fPathLength < dbg_fMediumPathDistance || fDeltaTargetForwardCos < dbg_fMediumTurnCosDelta)
	{
		if (fDeltaDestinationSquared > dbg_fMediumDeltaSquared)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else if (fPathLength < dbg_fLargePathDistance)
	{
		if (fDeltaDestinationSquared > dbg_fLargeDeltaSquared)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else
	{
		if (fDeltaDestinationSquared > dbg_fVeryLargeDeltaSquared)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

}



const AVP_AlienBehaviourData& AVP_Brain_Actor_Alien::GetBehaviourData() const
{
	if ( m_bBehaviourDataValid )
	{
		return m_xBehaviourData;
	}
	else
	{
		const AVP_ServerEntity_Squad_Alien* pxSquad = GetAlienSquad();
		if ( pxSquad )
		{
			return pxSquad->GetBehaviourData();
		}
		else
		{
			return m_xBehaviourData;
		}
	}
}

void AVP_Brain_Actor_Alien::IgnoreTarget( const Asura_Guid uTargetGuid, const float fIgnorePeriod )
{
#ifdef ASURA_ASSERT
	// Make sure we haven't got this target already.
	for ( Asura_LinkList_ConstIt< TargetIgnoreData > xTIDIt( m_xTargetIgnoreList ); !xTIDIt.Done(); xTIDIt.Next() )
	{
		Asura_Assert( xTIDIt.GetCurrent()->m_uGuidToIgnore != uTargetGuid, "AVP_Brain_Actor_Alien::IgnoreTarget - already ignoring this guid" );
	}
#endif

	TargetIgnoreData xTID;
	xTID.m_uGuidToIgnore = uTargetGuid;
	xTID.m_fRemoveTime	 = Asura_Timers::GetLocalScaledTime() + fIgnorePeriod;
	m_xTargetIgnoreList.Push_Front( xTID );
}

AVP_ServerEntity_Squad_Alien* AVP_Brain_Actor_Alien::GetAlienSquad() const
{
	Asura_Guid uGuid = GetActor()->GetInitialSquadGuid();
	AVP_ServerEntity_Squad_Alien* pxSquad = NULL;
	
	if ( uGuid == ASURA_GUID_UNREGISTERED )
	{
		pxSquad = AVP_ServerEntity_Squad_Alien::GetDefaultSquad();
		Asura_Assert(pxSquad , "Default squad not found");
	}
	else
	{
		pxSquad = AVP_ServerEntity_Squad_Alien::FindEntity( uGuid, !m_bLookForDestroyedSquad );
		Asura_Assertf(pxSquad, ("Squad %d not found",uGuid) );
		if (!pxSquad)
		{
			pxSquad = AVP_ServerEntity_Squad_Alien::GetDefaultSquad();
			Asura_Assert(pxSquad , "Default squad not found");
		}
	}

	return pxSquad;
}

AVP_ServerEntity_Team_Alien* AVP_Brain_Actor_Alien::GetAlienTeam() const
{
	return AVP_ServerEntity_Team_Alien::GetInstance();
}

const AVP_AlienTeam_Knowledge* AVP_Brain_Actor_Alien::GetCurrentTargetKnowledge() const
{
	if ( CurrentTargetValid() )
	{
		return GetAlienTeam()->GetKnowledgeSystem()->GetKnowledge( GetCurrentTargetGuid() );
	}
	else
	{
		return NULL;
	}
}

u_int AVP_Brain_Actor_Alien::GetCurrentTargetWPID() const
{
	if ( CurrentTargetValid() )
	{
		AVP_ServerEntity_Actor* pxTargetActor = AVP_ServerEntity_Actor::FindEntity( GetCurrentTargetGuid() );
		if ( pxTargetActor )
		{
			return pxTargetActor->GetCurrentWPID();
		}
		else
		{
			Asura_Assert( false, "AVP_Brain_Actor_Alien::GetCurrentTargetWPID - could not find target actor" );
			return ASURA_INVALID_WAYPOINT_ID;
		}
	}
	else
	{
		Asura_Assert( false, "AVP_Brain_Actor_Alien::GetCurrentTargetWPID - no current target" );
		return ASURA_INVALID_WAYPOINT_ID;
	}
}

const Asura_Vector_3& AVP_Brain_Actor_Alien::GetCurrentTargetPosition() const
{
	if ( CurrentTargetValid() )
	{
		const AVP_AlienTeam_Knowledge* pxKnowledge = GetAlienTeam()->GetKnowledgeSystem()->GetKnowledge( GetCurrentTargetGuid() );
		if ( pxKnowledge )
		{
			return pxKnowledge->GetPosition();
		}
		else
		{
			Asura_Assert( false, "AVP_Brain_Actor_Alien::GetCurrentTargetPosition - could not find target knowledge" );
			return Asura_Maths::ZeroVector3;
		}
	}
	else
	{
		Asura_Assert( false, "AVP_Brain_Actor_Alien::GetCurrentTargetPosition - no current target" );
		return Asura_Maths::ZeroVector3;
	}
}

bool AVP_Brain_Actor_Alien::CanAttack( const Asura_Guid uTargetGuid ) const
{
	return GetAlienSquad()->CanAttack( uTargetGuid, GetActor()->GetPosition(), GetBehaviourData().m_fTimeBetweenAttacks );
}

bool AVP_Brain_Actor_Alien::CanAttackCurrentTarget() const
{
	if ( CurrentTargetValid() )
	{
		return CanAttack( GetCurrentTargetGuid() );
	}
	else
	{
		return false;
	}
}

bool AVP_Brain_Actor_Alien::CanCharge( const Asura_Guid uTargetGuid ) const
{
	return GetAlienSquad()->CanCharge( uTargetGuid, GetBehaviourData().m_uMaxNumberOfChargers );
}

bool AVP_Brain_Actor_Alien::CanChargeCurrentTarget() const
{
	if ( CurrentTargetValid() )
	{
		return CanCharge( GetCurrentTargetGuid() );
	}
	else
	{
		return false;
	}
}

void AVP_Brain_Actor_Alien::StartedTargeting( const Asura_Guid uTargetGuid )
{
	StoppedAttackingCurrentTarget();
	StoppedTargetingCurrentTarget();

	m_uCurrentTargetGuid = uTargetGuid;
	GetAlienSquad()->AddTargeter( GetOwnerGuid(), uTargetGuid );
	UpdateCurrentTargetData();
}

void AVP_Brain_Actor_Alien::StoppedTargetingCurrentTarget()
{
	StoppedAttackingCurrentTarget();

	if ( CurrentTargetValid() )
	{
		// we need to include destroyed squads here cos this gets called from PreDestructionShutdown
		GetAlienSquad()->RemoveTargeter( GetOwnerGuid(), GetCurrentTargetGuid() );
		m_uCurrentTargetGuid = ASURA_GUID_UNREGISTERED;
	}
}

void AVP_Brain_Actor_Alien::StartedAttackingCurrentTarget()
{
	if ( CurrentTargetValid() && !m_bAttackingCurrentTarget )
	{
		GetAlienSquad()->AddAttacker( GetOwnerGuid(), GetCurrentTargetGuid() );
		m_bAttackingCurrentTarget = true;
	}
}

void AVP_Brain_Actor_Alien::StoppedAttackingCurrentTarget()
{
	if ( CurrentTargetValid() && m_bAttackingCurrentTarget )
	{
		GetAlienSquad()->RemoveAttacker( GetOwnerGuid(), GetCurrentTargetGuid() );
		m_bAttackingCurrentTarget = false;
	}
}

void AVP_Brain_Actor_Alien::StartedChargingCurrentTarget()
{
	if ( CurrentTargetValid() && !m_bChargingCurrentTarget )
	{
		GetAlienSquad()->AddCharger( GetCurrentTargetGuid() );
		m_bChargingCurrentTarget = true;
	}
}

void AVP_Brain_Actor_Alien::StoppedChargingCurrentTarget()
{
	if ( CurrentTargetValid() && m_bChargingCurrentTarget )
	{
		GetAlienSquad()->RemoveCharger( GetCurrentTargetGuid() );
		m_bChargingCurrentTarget = false;
	}

}

bool AVP_Brain_Actor_Alien::IsInCoverFromTarget()
{
	if ( CurrentTargetValid() )
	{
		return AVP_Waypoint_Manager::HasCoverFrom( GetNavData().m_usCurrentWPID, GetCurrentTargetWPID() );
	}
	else
	{
		return false;
	}
}

bool AVP_Brain_Actor_Alien::WaypointIsInCoverFromTarget( const u_int uWPID )
{
	if ( CurrentTargetValid() )
	{
		return AVP_Waypoint_Manager::HasCoverFrom( uWPID, GetCurrentTargetWPID() );
	}
	else
	{
		return false;
	}
}

bool AVP_Brain_Actor_Alien::CurrentTargetWithinRange() const
{
	if ( CurrentTargetValid() )
	{
		const AVP_AlienBehaviourData& xBehaviourData = GetBehaviourData();
		return ( xBehaviourData.m_fIdealDistanceMin < m_fCurrentTargetDistance && m_fCurrentTargetDistance < xBehaviourData.m_fIdealDistanceMin );
	}
	else
	{
		return false;
	}
}

bool AVP_Brain_Actor_Alien::HaveLOSToCurrentTarget()
{
	if ( !CurrentTargetValid() )
	{
		return false;
	}

	const u_int uFrame = Asura_Timers::GetFrameNumber();
	if ( uFrame == m_uLOSToCurrentTargetTestFrame )
	{
		return m_bHaveLOSToCurrentTarget;
	}

	// Calculate it afresh.
	m_uLOSToCurrentTargetTestFrame = uFrame;

	Asura_Vector_3 xEyePosition = GetActor()->GetPosition();
	// GetActor()->GetHeadPosition( xEyePosition ); on purpose take the chest position instead of the head - 
	GetActor()->GetChestPosition( xEyePosition );
	const AVP_ServerEntity_Actor* pxTargetActor = AVP_ServerEntity_Actor::FindEntity( GetCurrentTargetGuid() );
	if ( !pxTargetActor )
	{
		m_bHaveLOSToCurrentTarget = false;
		return m_bHaveLOSToCurrentTarget;
	}

	Asura_Vector_3 xTargetPosition = pxTargetActor->GetPosition();
	pxTargetActor->GetChestPosition( xTargetPosition );

	Asura_Vector_3 xDirection = xTargetPosition - xEyePosition;
	const float    fDistance  = xDirection.Normalise();

	const Asura_LOS_InputData   xInputData( xEyePosition, xDirection, GetOwnerGuid(), fDistance );
	const u_short usAlienClasification = AVP_ENTITYCLASS_ALIEN;
	const Asura_LOS_OutputData* pxOutputData = AVP_LOS::TestAgainstAlmostEverything( &xInputData, 1, &usAlienClasification );
	m_bHaveLOSToCurrentTarget = ( !pxOutputData->m_bHitEnvironment && ( pxOutputData->m_uHitEntity == GetCurrentTargetGuid() ) );

	return m_bHaveLOSToCurrentTarget;
}

bool AVP_Brain_Actor_Alien::IsAimedAtByCurrentTarget()
{
	if ( !CurrentTargetValid() )
	{
		return false;
	}
	const AVP_ServerEntity_Actor* pxTargetActor = AVP_ServerEntity_Actor::FindEntity( GetCurrentTargetGuid() );
	if ( !pxTargetActor )
	{
		return false;
	}

	Asura_Vector_3 xTargetPosition;
	pxTargetActor->GetChestPosition( xTargetPosition );

	Asura_Vector_3 xOurPosition;
	GetActor()->GetChestPosition( xOurPosition );

	Asura_Vector_3 xTargetToUs = xOurPosition - xTargetPosition;
	xTargetToUs.Normalise();

	const Asura_Vector_3& xTargetAimDirection = pxTargetActor->GetAimDirection();

	return ( xTargetAimDirection * xTargetToUs > 0.984808f ); // ~10°
}

const AVP_ServerEntity_Actor* AVP_Brain_Actor_Alien::GetCurrentTargetActor() const
{
	if ( CurrentTargetValid() )
	{
		return AVP_ServerEntity_Actor::FindEntity( GetCurrentTargetGuid() );
	} 
	else
	{
		return NULL;
	}
}

DEBUGVAR float dbg_fLightRadiusInnerSq = 4.0f;
DEBUGVAR float dbg_fLightRadiusOuterSq = 16.0f;

bool AVP_Brain_Actor_Alien::NeedToAvoidLight() const
{
	const Asura_Vector_3 xOurPosition = GetActor()->GetPosition();

	const u_int uNumLights = AVP_ServerEntity_Team_Alien::GetInstance()->GetNumLights();
	for ( u_int uLight = 0; uLight < uNumLights; ++uLight )
	{
		const float fToLightSq = ( AVP_ServerEntity_Team_Alien::GetInstance()->GetLightInfo( uLight ).m_xPosition - xOurPosition ).MagnitudeSquared();
		if ( fToLightSq < dbg_fLightRadiusInnerSq )
		{
			return true;
		}
	}

	return false;
}

bool AVP_Brain_Actor_Alien::CalcLightCentroid( Asura_Vector_3& xCentroid ) const
{
	const Asura_Vector_3 xOurPosition = GetActor()->GetPosition();

	xCentroid = Asura_Maths::ZeroVector3;
	u_int uPositionCount = 0;

	const u_int uNumLights = AVP_ServerEntity_Team_Alien::GetInstance()->GetNumLights();
	for ( u_int uLight = 0; uLight < uNumLights; ++uLight )
	{
		const Asura_Vector_3& xLightPosition = AVP_ServerEntity_Team_Alien::GetInstance()->GetLightInfo( uLight ).m_xPosition;
		const float fToLightSq = ( xLightPosition - xOurPosition ).MagnitudeSquared();
		if ( fToLightSq < dbg_fLightRadiusOuterSq )
		{
			xCentroid += xLightPosition;
			++uPositionCount;
		}
	}

	if ( uPositionCount > 1 )
	{
		xCentroid /= static_cast< float >( uPositionCount );
	}

	return ( uPositionCount > 0 );
}

bool AVP_Brain_Actor_Alien::CanPlanMove() const
{
	if (m_fTimeSinceActivate < 0.5F )
	{
		return false;
	}

	return !(m_bIsJumping || m_bIsGS2 || m_bIsFalling || m_bIsSharpTurn || m_bIsMelee);
}

bool AVP_Brain_Actor_Alien::CanUpdateMelee() const
{
	if (m_fTimeSinceActivate < 0.5f )
	{
		return false;
	}

	return !(m_bIsJumping || m_bIsGS2 || m_bIsFalling || m_bIsSharpTurn );
}


void AVP_Brain_Actor_Alien::SetFalling(bool bIsFalling)
{
	Asura_Assert(bool(m_bIsFalling) != bIsFalling, "Setting Jump to existing value - mismatch");
	m_bIsFalling = bIsFalling;	
}

void AVP_Brain_Actor_Alien::SetSharpTurn(bool bIsSharpTurn)
{
	Asura_Assert(bool(m_bIsSharpTurn) != bIsSharpTurn, "Setting SharpTurn to existing value - mismatch");
	m_bIsSharpTurn = bIsSharpTurn;
}

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

void AVP_Brain_Actor_Alien::SetGameScene(bool bIsGameScene)
{
	Asura_Assert(bool(m_bIsGS2) != bIsGameScene, "Setting GS2 to existing value - mismatch");
	m_bIsGS2 = bIsGameScene;
}

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


void AVP_Brain_Actor_Alien::ResolveToOurPlane( const Asura_Vector_3& xVectorIn, Asura_Vector_3& xVectorOut ) const
{
	const Asura_Vector_3 xOurNormal( -GetActor()->GetCurrentGravityDirection() );

	// Get this now in case this function is called with the same vector as both arguments.
	const float fVectorInLength = xVectorIn.Magnitude();

	xVectorOut = xVectorIn ^ xOurNormal;
	xVectorOut = xOurNormal ^ xVectorOut;
	xVectorOut.Normalise();
	xVectorOut *= fVectorInLength;
}

void AVP_Brain_Actor_Alien::UpdateCurrentGPV()
{
	const AVP_ServerEntity_GamePlayVolume* pxGPV = AVP_ServerEntity_GamePlayVolume::GetVolumeThatContainsPosition( GetActor()->GetPosition(), m_uCurrentGPV );
	m_uCurrentGPV = pxGPV ? pxGPV->GetGuid() : ASURA_GUID_UNREGISTERED;
}

void AVP_Brain_Actor_Alien::UpdateCurrentTargetData()
{
	// Note - can't rely on verified members here as we may not have verified yet.
	const AVP_AlienTeam_Knowledge* pxCurrentTargetKnowledge = GetCurrentTargetKnowledge();
	if ( pxCurrentTargetKnowledge )
	{
		m_xToCurrentTarget = pxCurrentTargetKnowledge->GetPosition() - GetAlienActor()->GetPosition();
		m_fCurrentTargetDistance = m_xToCurrentTarget.Normalise();

		const AVP_AlienBehaviourData& xBehaviourData = GetBehaviourData();
		const float fTotalRangeFraction = ( xBehaviourData.m_fIdealDistanceMax - xBehaviourData.m_fIdealDistanceMin ) * 0.15f;
		float fNearDistance = xBehaviourData.m_fIdealDistanceMin;
		float fNearishDistance = xBehaviourData.m_fIdealDistanceMin + fTotalRangeFraction;
		float fFarishDistance = xBehaviourData.m_fIdealDistanceMax - fTotalRangeFraction;
		float fFarDistance = xBehaviourData.m_fIdealDistanceMax;

		// Use the previous range to add hysteresis.
		static const float s_fHysteresisAmount = 2.5f;
		switch ( m_eCurrentTargetRange )
		{
			case AVP_AR_NEAR:
			{
                fNearDistance += s_fHysteresisAmount;
				fNearishDistance += s_fHysteresisAmount;
				fFarishDistance += s_fHysteresisAmount;
				fFarDistance += s_fHysteresisAmount;
				break;
			}

			case AVP_AR_NEARISH:
			{
				fNearDistance -= s_fHysteresisAmount;
				fNearishDistance += s_fHysteresisAmount;
				fFarishDistance += s_fHysteresisAmount;
				fFarDistance += s_fHysteresisAmount;
				break;
			}

			default:
			case AVP_AR_OK:
			{
				fNearDistance -= s_fHysteresisAmount;
				fNearishDistance -= s_fHysteresisAmount;
				fFarishDistance += s_fHysteresisAmount;
				fFarDistance += s_fHysteresisAmount;
				break;
			}

			case AVP_AR_FARISH:
			{
				fNearDistance -= s_fHysteresisAmount;
				fNearishDistance -= s_fHysteresisAmount;
				fFarishDistance -= s_fHysteresisAmount;
				fFarDistance += s_fHysteresisAmount;
				break;
			}

			case AVP_AR_FAR:
			{
				fNearDistance -= s_fHysteresisAmount;
				fNearishDistance -= s_fHysteresisAmount;
				fFarishDistance -= s_fHysteresisAmount;
				fFarDistance -= s_fHysteresisAmount;
				break;
			}
		}

		if ( m_fCurrentTargetDistance < fNearDistance )
		{
			m_eCurrentTargetRange = AVP_AR_NEAR;
		}
		else if ( m_fCurrentTargetDistance < fNearishDistance )
		{
			m_eCurrentTargetRange = AVP_AR_NEARISH;
		}
		else if ( m_fCurrentTargetDistance < fFarishDistance )
		{
			m_eCurrentTargetRange = AVP_AR_OK;
		}
		else if ( m_fCurrentTargetDistance < fFarDistance )
		{
			m_eCurrentTargetRange = AVP_AR_FARISH;
		} 
		else
		{
			m_eCurrentTargetRange = AVP_AR_FAR;
		}

		const Asura_Matrix_3x3& xOrient = GetAlienActor()->GetOrientation();
		const Asura_Vector_3 xForward( xOrient.e13, xOrient.e23, xOrient.e33 );
		const float fDot = xForward * m_xToCurrentTarget;
		// ~75°
		m_bWithinAttackFOV = ( fDot > 0.25f );
	}
	else
	{
		m_eCurrentTargetRange	 = AVP_AR_OK;
		m_xToCurrentTarget		 = Asura_Maths::ZeroVector3;
		m_fCurrentTargetDistance = Asura_Maths::LargeFloat;
		m_bWithinAttackFOV		 = false;
	}

	const AVP_ClientActor_Alien* pxClientAlien = AVP_ClientActor_Alien::FindEntity( GetOwnerGuid() );
	if ( pxClientAlien && pxClientAlien->GetMovementSystem() )
	{
		m_bPlayingMove = pxClientAlien->GetMovementSystem()->IsMoveInProgress();
	}
	else
	{
		m_bPlayingMove = false;
	}
}

DEBUGVAR float dbg_fMediumRange = 12.0f;

float AVP_Brain_Actor_Alien::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;
// 	}

	return dbg_fMediumRange;
}
