//////////////////////////////////////////////////////////////////////////////
//
// AVP_ServerEntity_Squad_Alien.cpp
//
// Copyright (C) 2008 Rebellion. All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

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

// header
#include "Entity/Controller/AVP_ServerEntity_Squad_Alien.h"

// asura
#include "Blueprint/Asura_Blueprint_System.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_LOS.h"
#include "Core/Asura_Timers.h"
#include "Entity/Asura_Entity_Event.h"
#include "Entity/Asura_StaticMessage_System.h"

// avp
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Controller/AVP_ServerEntity_Team_Alien.h"
#include "Entity/GamePlayVolume/AVP_ServerEntity_GamePlayVolume.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"

// axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Core/BrainTypes/AVP_Brain_Team_Alien.h"
#include "Knowledge/AVP_Knowledge_System_AlienTeam.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_AlienSquadBehaviourData
//
//////////////////////////////////////////////////////////////////////////////

void AVP_AlienBehaviourData::SetParamsFromBehaviour()
{
	// Get the parameters from the blueprints.
	static const Asura_Hash_ID s_auBPHashID[ NUM_BEHAVIOURS ] =
	{
		0x5c13d641, // Asura_GetHashID( "Default" )
		0xaba36e73, // Asura_GetHashID( "Guerilla" )
		0xac1eee48, // Asura_GetHashID( "Attack" )
		0x068ac39f, // Asura_GetHashID( "Stalk" )
		0xabb46814, // Asura_GetHashID( "Ambush" )
		0xf5682fa3, // Asura_GetHashID( "Disappear" )
		0xaf968b71, // Asura_GetHashID( "Custom" )
		0x068c2178, // Asura_GetHashID( "Swarm" )
		0x0030dd42, // Asura_GetHashID( "Hide" )
		0x5cd39ffa,  // Asura_GetHashID( "Destroy" )
		0x445fdc04, // Asura_GetHashID( "Aggressive" )
		0x84a4678c // Asura_GetHashID( "HideInShadows" )
	};

	if (m_iBehaviour == AGGRESIVE || m_iBehaviour == HIDE_IN_SHADOWS || m_iBehaviour == AMBUSH) // new behaviors don't use this anymore!
	{
		return;
	}

	Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_AI_SETTINGS, s_auBPHashID[ m_iBehaviour ] );
	if ( !pxBlueprint )
	{
		Asura_Assert( false, "AVP_AlienBehaviourData::SetParamsFromBehaviour - could not get blueprint" );
		return;
	}

	enum Parameters
	{
		ATTACK_DELAY,
		IDEAL_DISTANCE_MIN,
		IDEAL_DISTANCE_MAX,
		MAX_ATTACKERS_PER_TARGET,
		MAX_ATTACKERS_TOTAL,
		MAX_BURST_PER_TARGET,
		MOVE_IN_MODE,
		POST_ATTACK_ACTION,
		RESPAWN_DELAY,
		NUM_PARAMETERS
	};
	static const Asura_Hash_ID s_auParamHashID[ NUM_PARAMETERS ] =
	{
		0xd893783b, // Asura_GetHashID( "AttackDelay" )
		0x2e70e2c8, // Asura_GetHashID( "IdealDistanceMin" )
		0x2e70e1da, // Asura_GetHashID( "IdealDistanceMax" )
		0x250c18f4, // Asura_GetHashID( "MaxAttackersPerTarget" )
		0x4533ce4a, // Asura_GetHashID( "MaxAttackersTotal" )
		0x9d26da32, // Asura_GetHashID( "MaxBurstPerTarget" )
		0xc6ff8759, // Asura_GetHashID( "MoveInMode" )
		0x64993f1e, // Asura_GetHashID( "PostAttackAction" )
		0x7b01e55b  // Asura_GetHashID( "RespawnDelay" )
	};

	Asura_Value xValue;
	if ( pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ ATTACK_DELAY ], xValue ) )
	{
		m_fAttackDelay = xValue.GetFloatValue();
	}
	if ( pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ IDEAL_DISTANCE_MIN ], xValue ) )
	{
		m_fIdealDistanceMin = xValue.GetFloatValue();
	}
	if ( pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ IDEAL_DISTANCE_MAX ], xValue ) )
	{
		m_fIdealDistanceMax = xValue.GetFloatValue();
	}
	if ( pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ MAX_ATTACKERS_PER_TARGET ], xValue ) )
	{
		m_iMaxAttackersPerTarget = xValue.GetIntValue();
	}
	if ( pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ MAX_ATTACKERS_TOTAL ], xValue ) )
	{
		m_iMaxAttackersTotal = xValue.GetIntValue();
	}
	if ( pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ MAX_BURST_PER_TARGET ], xValue ) )
	{
		m_iMaxBurstPerTarget = xValue.GetIntValue();
	}
	if ( pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ MOVE_IN_MODE ], xValue ) )
	{
		m_iMoveInMode = xValue.GetIntValue();
	}
	if ( pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ POST_ATTACK_ACTION ], xValue ) )
	{
		m_iPostAttackAction = xValue.GetIntValue();
	}
	if ( pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ RESPAWN_DELAY ], xValue ) )
	{
		m_fRespawnDelay = xValue.GetFloatValue();
	}

	Asura_Assert(
		pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ ATTACK_DELAY ], xValue ) &&
		pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ IDEAL_DISTANCE_MIN ], xValue ) &&
		pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ IDEAL_DISTANCE_MAX ], xValue ) &&
		pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ MAX_ATTACKERS_PER_TARGET ], xValue ) &&
		pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ MAX_ATTACKERS_TOTAL ], xValue ) &&
		pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ MAX_BURST_PER_TARGET ], xValue ) &&
		pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ MOVE_IN_MODE ], xValue ) &&
		pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ POST_ATTACK_ACTION ], xValue ) &&
		pxBlueprint->GetParameterValueByHashID( s_auParamHashID[ RESPAWN_DELAY ], xValue ),
		"AVP_AlienBehaviourData::SetParamsFromBehaviour - could not get all blueprint parameters" );
}

void AVP_AlienBehaviourData::SetParamsFromBlueprint()
{
	Asura_Value xValue;

	const Asura_Blueprint* xProtoBP = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_AI_SETTINGS, m_uProtoBlueprintHashID ); //"AI Settings"
	bool bAggressive = false;
	bool bHideInShadows = false;
	if (xProtoBP)
	{
		switch (m_iBehaviour)
		{
		case AGGRESIVE:
			bAggressive = true;
			break;
		case HIDE_IN_SHADOWS:
			bHideInShadows = true;
			bAggressive = true; // need all the aggressive data too
			break;
		case AMBUSH:
			bAggressive = true;
			break;
		default:
			Asura_Assert(false, "Behaviour type not supported");
		}

		if (bAggressive && xProtoBP->GetParameterValueByHashID(0xbab7a830, xValue)) //"JumpFraction"
		{
			m_fJumpFraction = xValue.GetFloatValue();
		}

		if (bAggressive && xProtoBP->GetParameterValueByHashID(0x8bba4fd8, xValue)) //"StandFraction"
		{
			m_fStandFraction = xValue.GetFloatValue();
		}

		if (bAggressive && xProtoBP->GetParameterValueByHashID(0x2bfc569f, xValue)) //"TimeBetweenGrapples"
		{
			m_fMinTimeBetweenGrapples = xValue.GetFloatValue();
		}

		if (bAggressive && xProtoBP->GetParameterValueByHashID(0xeb0ed014, xValue)) //"DamageFromBehindMultiplier"
		{
			m_fDamageFromBehindMultiplier = xValue.GetFloatValue();
		}


		if (bAggressive && xProtoBP->GetParameterValueByHashID(0x44533e30, xValue)) //"TimeBetweenAttacks"
		{
			m_fTimeBetweenAttacks = xValue.GetFloatValue();
		}

		if (bAggressive && xProtoBP->GetParameterValueByHashID(0xffa20c39, xValue)) //"MaxNumberOfChargers"
		{
			m_uMaxNumberOfChargers = xValue.GetIntValue();
		}

		if (bHideInShadows && xProtoBP->GetParameterValueByHashID(0xe1504d0a, xValue)) //"AttackIfInMarineLight"
		{
			m_bAttackIfInMarineLight = xValue.GetBoolValue();
		}

		if (bHideInShadows && xProtoBP->GetParameterValueByHashID(0x0281f02b, xValue)) //"TimeInLightBeforeAttack"
		{
			m_fTimeInLightBeforeAttack = xValue.GetFloatValue();
		}

		if (bHideInShadows && xProtoBP->GetParameterValueByHashID(0x918ac289, xValue)) //"MinHideDistance"
		{
			m_fMinHideDistance = xValue.GetFloatValue();
		}

		if (bHideInShadows && xProtoBP->GetParameterValueByHashID(0xb36ba71a, xValue)) //"ChaneToRehideIfCloseFraction"
		{
			m_fChaneToRehideIfCloseFraction = xValue.GetFloatValue();
		}

		if (bHideInShadows && xProtoBP->GetParameterValueByHashID(0x888eef33, xValue)) //"ChaneToRehideIfInLightFraction"
		{
			m_fChaneToRehideIfInLightFraction = xValue.GetFloatValue();
		}

		if (bHideInShadows && xProtoBP->GetParameterValueByHashID(0x4a0a3bc3, xValue)) //"ChaneToRehideAfterAttackFraction"
		{
			m_fChaneToRehideAfterAttackFraction = xValue.GetFloatValue();
		}
	}
	else
	{
		Asura_Assertf(false, ("Bleuprint %d not found", m_uProtoBlueprintHashID));
	}

}

void AVP_AlienBehaviourData::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	u_int uVersion;
	xStream >> uVersion;

	if ( uVersion == 1007 )
	{
		xStream >> m_iBehaviour;
		if ( m_iBehaviour == CUSTOM )
		{
			// Load the rest of the behaviour parameters in.
			xStream >> m_iMoveInMode;
			xStream >> m_fIdealDistanceMin;
			xStream >> m_fIdealDistanceMax;
			xStream >> m_fAttackDelay;
			xStream >> m_iMaxAttackersPerTarget;
			xStream >> m_iMaxAttackersTotal;
			xStream >> m_iMaxBurstPerTarget;
			xStream >> m_iPostAttackAction;
			xStream >> m_fRespawnDelay;
		}
		else if ( m_iBehaviour == AGGRESIVE )
		{			
			xStream >> m_uProtoBlueprintHashID;
			if (m_uProtoBlueprintHashID != ASURA_HASH_ID_UNSET)
			{
				SetParamsFromBlueprint();
			}
			else
			{
				xStream >> m_fJumpFraction;
				xStream >> m_fStandFraction;
				xStream >> m_fMinTimeBetweenGrapples;
				xStream >> m_fDamageFromBehindMultiplier;
				xStream >> m_fTimeBetweenAttacks;
				xStream >> m_uMaxNumberOfChargers;
			}
		}
		else if ( m_iBehaviour == HIDE_IN_SHADOWS )
		{			
			xStream >> m_uProtoBlueprintHashID;
			if (m_uProtoBlueprintHashID != ASURA_HASH_ID_UNSET)
			{
				SetParamsFromBlueprint();
			}
			else
			{
				xStream >> m_fJumpFraction;
				xStream >> m_fStandFraction;
				xStream >> m_fMinTimeBetweenGrapples;
				xStream >> m_fDamageFromBehindMultiplier;
				xStream >> m_fTimeBetweenAttacks;
				xStream >> m_uMaxNumberOfChargers;
				xStream >> m_bAttackIfInMarineLight;
				xStream >> m_fTimeInLightBeforeAttack;
				xStream >> m_fMinHideDistance;
				xStream >> m_fChaneToRehideIfCloseFraction;
				xStream >> m_fChaneToRehideIfInLightFraction;
				xStream >> m_fChaneToRehideAfterAttackFraction;
			}
		}
		else if ( m_iBehaviour == AMBUSH )
		{			
			xStream >> m_uProtoBlueprintHashID;
			if (m_uProtoBlueprintHashID != ASURA_HASH_ID_UNSET)
			{
				SetParamsFromBlueprint();
			}
			else
			{
				xStream >> m_fJumpFraction;
				xStream >> m_fStandFraction;
				xStream >> m_fMinTimeBetweenGrapples;
				xStream >> m_fDamageFromBehindMultiplier;
				xStream >> m_fTimeBetweenAttacks;
				xStream >> m_uMaxNumberOfChargers;
			}
		}
		else
		{
			SetParamsFromBehaviour();
		}		

		xStream >> m_uNumHidePoints;
		if ( m_uNumHidePoints > 0 )
		{
			m_pxHidePointGuids = new Asura_Guid[ m_uNumHidePoints ];
			for ( u_int u = 0; u < m_uNumHidePoints; ++u )
			{
				xStream >> m_pxHidePointGuids[ u ];
			}
		}
	}
	else if ( uVersion == 1006 )
	{
		xStream >> m_iBehaviour;
		if ( m_iBehaviour == CUSTOM )
		{
			// Load the rest of the behaviour parameters in.
			xStream >> m_iMoveInMode;
			xStream >> m_fIdealDistanceMin;
			xStream >> m_fIdealDistanceMax;
			xStream >> m_fAttackDelay;
			xStream >> m_iMaxAttackersPerTarget;
			xStream >> m_iMaxAttackersTotal;
			xStream >> m_iMaxBurstPerTarget;
			xStream >> m_iPostAttackAction;
			xStream >> m_fRespawnDelay;
		}
		else if ( m_iBehaviour == AGGRESIVE )
		{
			SetParamsFromBehaviour();
			xStream >> m_fTimeBetweenAttacks;
			xStream >> m_uMaxNumberOfChargers;
			m_bAttackIfInMarineLight = false;
			m_fTimeInLightBeforeAttack = 5.0f;
			m_fMinHideDistance = 5.0f;
			m_fChaneToRehideIfCloseFraction = 0.0f;
			m_fChaneToRehideIfInLightFraction = 1.0f;
			m_fChaneToRehideAfterAttackFraction = 0.0f;
		}
		else if ( m_iBehaviour == HIDE_IN_SHADOWS )
		{
			SetParamsFromBehaviour();
			xStream >> m_fTimeBetweenAttacks;
			xStream >> m_uMaxNumberOfChargers;
			xStream >> m_bAttackIfInMarineLight;
			xStream >> m_fTimeInLightBeforeAttack;
			xStream >> m_fMinHideDistance;
			xStream >> m_fChaneToRehideIfCloseFraction;
			xStream >> m_fChaneToRehideIfInLightFraction;
			xStream >> m_fChaneToRehideAfterAttackFraction;
		}
		else
		{
			SetParamsFromBehaviour();
			m_fTimeBetweenAttacks = 1.0f;
			m_uMaxNumberOfChargers = 5;
			m_bAttackIfInMarineLight = false;
			m_fTimeInLightBeforeAttack = 5.0f;
			m_fMinHideDistance = 5.0f;
			m_fChaneToRehideIfCloseFraction = 0.0f;
			m_fChaneToRehideIfInLightFraction = 1.0f;
			m_fChaneToRehideAfterAttackFraction = 0.0f;
		}		

		xStream >> m_uNumHidePoints;
		if ( m_uNumHidePoints > 0 )
		{
			m_pxHidePointGuids = new Asura_Guid[ m_uNumHidePoints ];
			for ( u_int u = 0; u < m_uNumHidePoints; ++u )
			{
				xStream >> m_pxHidePointGuids[ u ];
			}
		}
	}
	else if ( uVersion == 1005 )
	{
		xStream >> m_iBehaviour;
		if ( m_iBehaviour == CUSTOM )
		{
			// Load the rest of the behaviour parameters in.
			xStream >> m_iMoveInMode;
			xStream >> m_fIdealDistanceMin;
			xStream >> m_fIdealDistanceMax;
			xStream >> m_fAttackDelay;
			xStream >> m_iMaxAttackersPerTarget;
			xStream >> m_iMaxAttackersTotal;
			xStream >> m_iMaxBurstPerTarget;
			xStream >> m_iPostAttackAction;
			xStream >> m_fRespawnDelay;
		}
		else if ( m_iBehaviour == AGGRESIVE )
		{
			SetParamsFromBehaviour();
			xStream >> m_fTimeBetweenAttacks;
			xStream >> m_uMaxNumberOfChargers;
			m_bAttackIfInMarineLight = false;
			m_fTimeInLightBeforeAttack = 5.0f;
			m_fMinHideDistance = 5.0f;
			m_fChaneToRehideIfCloseFraction = 0.0f;
			m_fChaneToRehideIfInLightFraction = 1.0f;
		}
		else if ( m_iBehaviour == HIDE_IN_SHADOWS )
		{
			SetParamsFromBehaviour();
			xStream >> m_fTimeBetweenAttacks;
			xStream >> m_uMaxNumberOfChargers;
			xStream >> m_bAttackIfInMarineLight;
			xStream >> m_fTimeInLightBeforeAttack;
			xStream >> m_fMinHideDistance;
			xStream >> m_fChaneToRehideIfCloseFraction;
			xStream >> m_fChaneToRehideIfInLightFraction;
		}
		else
		{
			SetParamsFromBehaviour();
			m_fTimeBetweenAttacks = 1.0f;
			m_uMaxNumberOfChargers = 5;
			m_bAttackIfInMarineLight = false;
			m_fTimeInLightBeforeAttack = 5.0f;
			m_fMinHideDistance = 5.0f;
			m_fChaneToRehideIfCloseFraction = 0.0f;
			m_fChaneToRehideIfInLightFraction = 1.0f;
		}		

		m_fChaneToRehideAfterAttackFraction = 0.0f;

		xStream >> m_uNumHidePoints;
		if ( m_uNumHidePoints > 0 )
		{
			m_pxHidePointGuids = new Asura_Guid[ m_uNumHidePoints ];
			for ( u_int u = 0; u < m_uNumHidePoints; ++u )
			{
				xStream >> m_pxHidePointGuids[ u ];
			}
		}
	}
	else if ( uVersion == 1004 )
	{
		xStream >> m_iBehaviour;
		if ( m_iBehaviour == CUSTOM )
		{
			// Load the rest of the behaviour parameters in.
			xStream >> m_iMoveInMode;
			xStream >> m_fIdealDistanceMin;
			xStream >> m_fIdealDistanceMax;
			xStream >> m_fAttackDelay;
			xStream >> m_iMaxAttackersPerTarget;
			xStream >> m_iMaxAttackersTotal;
			xStream >> m_iMaxBurstPerTarget;
			xStream >> m_iPostAttackAction;
			xStream >> m_fRespawnDelay;
		}
		else if ( m_iBehaviour == AGGRESIVE )
		{
			SetParamsFromBehaviour();
			xStream >> m_fTimeBetweenAttacks;
			xStream >> m_uMaxNumberOfChargers;
			m_bAttackIfInMarineLight = false;
			m_fTimeInLightBeforeAttack = 5.0f;
			m_fMinHideDistance = 5.0f;
			
		}
		else if ( m_iBehaviour == HIDE_IN_SHADOWS )
		{
			SetParamsFromBehaviour();
			xStream >> m_fTimeBetweenAttacks;
			xStream >> m_uMaxNumberOfChargers;
			xStream >> m_bAttackIfInMarineLight;
			xStream >> m_fTimeInLightBeforeAttack;
			xStream >> m_fMinHideDistance;
		}
		else
		{
			SetParamsFromBehaviour();
			m_fTimeBetweenAttacks = 1.0f;
			m_uMaxNumberOfChargers = 5;
			m_bAttackIfInMarineLight = false;
			m_fTimeInLightBeforeAttack = 5.0f;
			m_fMinHideDistance = 5.0f;
		}	

		m_fChaneToRehideIfCloseFraction = 0.0f;
		m_fChaneToRehideIfInLightFraction = 1.0f;
		m_fChaneToRehideAfterAttackFraction = 0.0f;

		xStream >> m_uNumHidePoints;
		if ( m_uNumHidePoints > 0 )
		{
			m_pxHidePointGuids = new Asura_Guid[ m_uNumHidePoints ];
			for ( u_int u = 0; u < m_uNumHidePoints; ++u )
			{
				xStream >> m_pxHidePointGuids[ u ];
			}
		}
	}
	else if ( uVersion == 1003 )
	{
		xStream >> m_iBehaviour;
		if ( m_iBehaviour == CUSTOM )
		{
			// Load the rest of the behaviour parameters in.
			xStream >> m_iMoveInMode;
			xStream >> m_fIdealDistanceMin;
			xStream >> m_fIdealDistanceMax;
			xStream >> m_fAttackDelay;
			xStream >> m_iMaxAttackersPerTarget;
			xStream >> m_iMaxAttackersTotal;
			xStream >> m_iMaxBurstPerTarget;
			xStream >> m_iPostAttackAction;
			xStream >> m_fRespawnDelay;
		}
		else if ( m_iBehaviour == AGGRESIVE )
		{
			SetParamsFromBehaviour();
			xStream >> m_fTimeBetweenAttacks;
			xStream >> m_uMaxNumberOfChargers;
		}
		else
		{
			SetParamsFromBehaviour();
			m_fTimeBetweenAttacks = 1.0f;
			m_uMaxNumberOfChargers = 5;
		}

		m_bAttackIfInMarineLight = false;
		m_fTimeInLightBeforeAttack = 5.0f;
		m_fMinHideDistance = 5.0f;
		m_fChaneToRehideIfCloseFraction = 0.0f;
		m_fChaneToRehideIfInLightFraction = 1.0f;
		m_fChaneToRehideAfterAttackFraction = 0.0f;

		xStream >> m_uNumHidePoints;
		if ( m_uNumHidePoints > 0 )
		{
			m_pxHidePointGuids = new Asura_Guid[ m_uNumHidePoints ];
			for ( u_int u = 0; u < m_uNumHidePoints; ++u )
			{
				xStream >> m_pxHidePointGuids[ u ];
			}
		}
	}
	else if ( uVersion == 1002 )
	{
		xStream >> m_iBehaviour;
		if ( m_iBehaviour == CUSTOM )
		{
			// Load the rest of the behaviour parameters in.
			xStream >> m_iMoveInMode;
			xStream >> m_fIdealDistanceMin;
			xStream >> m_fIdealDistanceMax;
			xStream >> m_fAttackDelay;
			xStream >> m_iMaxAttackersPerTarget;
			xStream >> m_iMaxAttackersTotal;
			xStream >> m_iMaxBurstPerTarget;
			xStream >> m_iPostAttackAction;
			xStream >> m_fRespawnDelay;
		}
		else
		{
			SetParamsFromBehaviour();
		}

		m_fTimeBetweenAttacks = 1.0f;
		m_uMaxNumberOfChargers = 5;
		m_bAttackIfInMarineLight = false;
		m_fTimeInLightBeforeAttack = 5.0f;
		m_fMinHideDistance = 5.0f;
		m_fChaneToRehideIfCloseFraction = 0.0f;
		m_fChaneToRehideIfInLightFraction = 1.0f;
		m_fChaneToRehideAfterAttackFraction = 0.0f;

		xStream >> m_uNumHidePoints;
		if ( m_uNumHidePoints > 0 )
		{
			m_pxHidePointGuids = new Asura_Guid[ m_uNumHidePoints ];
			for ( u_int u = 0; u < m_uNumHidePoints; ++u )
			{
				xStream >> m_pxHidePointGuids[ u ];
			}
		}
	}
	else if ( uVersion == 1001 )
	{
		xStream >> m_iBehaviour;
		if ( m_iBehaviour == CUSTOM )
		{
			// Load the rest of the behaviour parameters in.
			xStream >> m_iMoveInMode;
			xStream >> m_fIdealDistanceMin;
			xStream >> m_fIdealDistanceMax;
			xStream >> m_fAttackDelay;
			xStream >> m_iMaxAttackersPerTarget;
			xStream >> m_iMaxAttackersTotal;
			xStream >> m_iPostAttackAction;
			xStream >> m_fRespawnDelay;
		}
		else
		{
			SetParamsFromBehaviour();
		}
		
		m_fTimeBetweenAttacks = 1.0f;
		m_uMaxNumberOfChargers = 5;
		m_bAttackIfInMarineLight = false;
		m_fTimeInLightBeforeAttack = 5.0f;
		m_fMinHideDistance = 5.0f;
		m_fChaneToRehideIfCloseFraction = 0.0f;
		m_fChaneToRehideIfInLightFraction = 1.0f;
		m_fChaneToRehideAfterAttackFraction = 0.0f;

		xStream >> m_uNumHidePoints;
		if ( m_uNumHidePoints > 0 )
		{
			m_pxHidePointGuids = new Asura_Guid[ m_uNumHidePoints ];
			for ( u_int u = 0; u < m_uNumHidePoints; ++u )
			{
				xStream >> m_pxHidePointGuids[ u ];
			}
		}
	}
	else if ( uVersion == 1000 )
	{
		xStream >> m_iBehaviour;
		if ( m_iBehaviour == CUSTOM )
		{
			// Load the rest of the behaviour parameters in.
			xStream >> m_iMoveInMode;
			xStream >> m_fIdealDistanceMin;
			xStream >> m_fIdealDistanceMax;
			xStream >> m_iMaxAttackersTotal;
			xStream >> m_iPostAttackAction;
		}
		else
		{
			SetParamsFromBehaviour();
		}

		m_fTimeBetweenAttacks = 1.0f;
		m_uMaxNumberOfChargers = 5;
		m_bAttackIfInMarineLight = false;
		m_fTimeInLightBeforeAttack = 5.0f;
		m_fMinHideDistance = 5.0f;
		m_fChaneToRehideIfCloseFraction = 0.0f;
		m_fChaneToRehideIfInLightFraction = 1.0f;
		m_fChaneToRehideAfterAttackFraction = 0.0f;

		xStream >> m_uNumHidePoints;
		if ( m_uNumHidePoints > 0 )
		{
			m_pxHidePointGuids = new Asura_Guid[ m_uNumHidePoints ];
			for ( u_int u = 0; u < m_uNumHidePoints; ++u )
			{
				xStream >> m_pxHidePointGuids[ u ];
			}
		}
	}
	else if ( uVersion < 1000 )
	{
		// Initially we weren't saving a chunk version, so anything less than 1000 is actually the behaviour ID from before we had a version.
		m_iBehaviour = uVersion;
		SetParamsFromBehaviour();

		m_fTimeBetweenAttacks = 1.0f;
		m_uMaxNumberOfChargers = 5;
		m_bAttackIfInMarineLight = false;
		m_fTimeInLightBeforeAttack = 5.0f;
		m_fMinHideDistance = 5.0f;
		m_fChaneToRehideIfCloseFraction = 0.0f;
		m_fChaneToRehideIfInLightFraction = 1.0f;
		m_fChaneToRehideAfterAttackFraction = 0.0f;

		xStream >> m_uNumHidePoints;
		if ( m_uNumHidePoints > 0 )
		{
			m_pxHidePointGuids = new Asura_Guid[ m_uNumHidePoints ];
			for ( u_int u = 0; u < m_uNumHidePoints; ++u )
			{
				xStream >> m_pxHidePointGuids[ u ];
			}
		}
	}
	else
	{
		Asura_Assertf( false, ( "AVP_AlienBehaviourData::ReadFromChunkStream - unhandled chunk version (%u)", uVersion ) );
	}
}

void AVP_AlienBehaviourData::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	xStream << uCURRENT_CHUNK_VERSION;

	xStream << m_iBehaviour;
	if ( m_iBehaviour == CUSTOM )
	{
		// Save the rest of the behaviour parameters out.
		xStream << m_iMoveInMode;
		xStream << m_fIdealDistanceMin;
		xStream << m_fIdealDistanceMax;
		xStream << m_fAttackDelay;
		xStream << m_iMaxAttackersPerTarget;
		xStream << m_iMaxAttackersTotal;
		xStream << m_iMaxBurstPerTarget;
		xStream << m_iPostAttackAction;
		xStream << m_fRespawnDelay;
	}
	else if (m_iBehaviour == AGGRESIVE)
	{
		xStream << m_uProtoBlueprintHashID;
		if (m_uProtoBlueprintHashID == ASURA_HASH_ID_UNSET)
		{
			xStream << m_fJumpFraction;
			xStream << m_fStandFraction;
			xStream << m_fMinTimeBetweenGrapples;
			xStream << m_fDamageFromBehindMultiplier;
			xStream << m_fTimeBetweenAttacks;
			xStream << m_uMaxNumberOfChargers;
		}
	}
	else if (m_iBehaviour == HIDE_IN_SHADOWS)
	{
		xStream << m_uProtoBlueprintHashID;
		if (m_uProtoBlueprintHashID == ASURA_HASH_ID_UNSET)
		{
			xStream << m_fJumpFraction;
			xStream << m_fStandFraction;
			xStream << m_fMinTimeBetweenGrapples;
			xStream << m_fDamageFromBehindMultiplier;
			xStream << m_fTimeBetweenAttacks;
			xStream << m_uMaxNumberOfChargers;
			xStream << m_bAttackIfInMarineLight;
			xStream << m_fTimeInLightBeforeAttack;
			xStream << m_fMinHideDistance;
			xStream << m_fChaneToRehideIfCloseFraction;
			xStream << m_fChaneToRehideIfInLightFraction;
			xStream << m_fChaneToRehideAfterAttackFraction;
		}
	}
	else if (m_iBehaviour == AMBUSH)
	{
		xStream << m_uProtoBlueprintHashID;
		if (m_uProtoBlueprintHashID == ASURA_HASH_ID_UNSET)
		{
			xStream << m_fJumpFraction;
			xStream << m_fStandFraction;
			xStream << m_fMinTimeBetweenGrapples;
			xStream << m_fDamageFromBehindMultiplier;
			xStream << m_fTimeBetweenAttacks;
			xStream << m_uMaxNumberOfChargers;
		}
	}

	xStream << m_uNumHidePoints;
	for ( u_int u = 0; u < m_uNumHidePoints; ++u )
	{
		xStream << m_pxHidePointGuids[ u ];
	}
}

#ifdef ASURA_TOOLS
const char* AVP_AlienBehaviourData::s_apszBehaviourStrings[ NUM_BEHAVIOURS ] =
{
	"Standard",
	"Guerilla",
	"Attack",
	"Stalk",
	"Ambush",
	"Disappear",
	"Custom",
	"Swarm",
	"Hide",
	"Destroy",
	"Aggressive",
	"Hide In Shadows"

};

const char* AVP_AlienBehaviourData::s_apszMoveInModeStrings[ NUM_MOVE_STYLES ] =
{
	"Creep",
	"Direct Creep",
	"Charge"
};

const char* AVP_AlienBehaviourData::s_apszPostAttackActionStrings[ NUM_POST_ATTACK_ACTIONS ] =
{
	"Cycle Round Immediate",
	"Cycle Round Delayed",
	"Disappear"
};
#endif

void AVP_AlienSpawnToken::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	u_int uVersion;
	xStream >> uVersion;

	if ( uVersion == uCURRENT_CHUNK_VERSION )
	{
		xStream >> m_uBPTypeHashID;
		xStream >> m_uBPHashID;
		xStream >> m_uSpawnPointGuid;
		xStream >> m_uOldActorGuid;
		xStream >> m_fRespawnTimer;
	}
	else
	{
		Asura_Assertf( false, ( "AVP_AlienSpawnToken::ReadFromChunkStream - unhandled chunk version (%u)", uVersion ) );
	}
}

void AVP_AlienSpawnToken::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	xStream << uCURRENT_CHUNK_VERSION;
	xStream << m_uBPTypeHashID;
	xStream << m_uBPHashID;
	xStream << m_uSpawnPointGuid;
	xStream << m_uOldActorGuid;
	xStream	<< m_fRespawnTimer;
}

//////////////////////////////////////////////////////////////////////////////
//
// AVP_ServerEntity_Squad_Alien
//
//////////////////////////////////////////////////////////////////////////////

Asura_Hashed_List< AVP_ServerEntity_Squad_Alien > AVP_ServerEntity_Squad_Alien::s_xList;
Asura_Mempool* AVP_ServerEntity_Squad_Alien::s_pxSpawnTokenMempool = NULL;
Asura_Mempool* AVP_ServerEntity_Squad_Alien::s_pxMemberDataMempool = NULL;
AVP_ServerEntity_Squad_Alien* AVP_ServerEntity_Squad_Alien::s_pxDefaultSquad = NULL;
u_int AVP_ServerEntity_Squad_Alien::s_uLOSTestFrame = 0;
u_int AVP_ServerEntity_Squad_Alien::s_uLOSTestsThisFrame = 0;

AVP_ServerEntity_Squad_Alien::AVP_ServerEntity_Squad_Alien( Asura_Guid uGuid )
: PARENT( uGuid )
, m_xBehaviourData()
, m_uNumAttackers( 0 )
, m_fInjureOnHealthPercentage(50.0f)
, m_fChanceToGetInjoredPercentage(50.0)
, m_fPreviousMinHealthFractionSoFar(1.0f)
, m_fMinHealthFractionSoFar(1.0f)
, m_bNeedToSendHealthPrecentMessages(false)
, m_bAmbushHissLock(false)
{
	Classification = AVP_ENTITYCLASS_ALIENSQUAD;

	s_xList.AddToList( this );
}

AVP_ServerEntity_Squad_Alien::~AVP_ServerEntity_Squad_Alien()
{
	if ( IsDefaultSquad() )
	{
		s_pxDefaultSquad = NULL;
	}

	s_xList.RemoveFromList( this );
}

void AVP_ServerEntity_Squad_Alien::InitialiseFromBlueprint( const Asura_Blueprint& xBlueprint )
{
	PARENT::InitialiseFromBlueprint( xBlueprint );
	m_xBehaviourData.SetParamsFromBehaviour();	
	m_xBehaviourData.SetParamsFromBlueprint();

	Asura_Value xValue;

	if(xBlueprint.GetParameterValueByHashID(0x61bae2c1, xValue)) // Asura_GetHashID( "Injured.ChanceToGetInjuredPercentage" )
	{
		m_fChanceToGetInjoredPercentage = xValue.GetFloatValue();
	}

	if(xBlueprint.GetParameterValueByHashID(0x35c6fea9, xValue)) // Asura_GetHashID( "Injured.InjuredOnHealthPercentage" )
	{
		m_fInjureOnHealthPercentage = xValue.GetFloatValue();
	}
}

void AVP_ServerEntity_Squad_Alien::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	u_int uVersion;
	xStream	>> uVersion;

	PARENT::ReadFromChunkStream( xStream );

	if ( uVersion == 10 )
	{
		for ( u_int u = 0; u < GetNumSquadMembers(); ++u )
		{
			const Asura_Guid uMemberGuid = GetMemberGuid( u );
			MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uMemberGuid ) );
			if ( pxMemberData )
			{
				xStream >> pxMemberData->m_uAttackeeGuid;
				int iHasLOS;
				xStream >> iHasLOS;
				pxMemberData->m_bHasLOSToTarget = iHasLOS ? true : false;
			}
			else
			{
				Asura_Assert( false, "AVP_ServerEntity_Squad_Alien::ReadFromChunkStream - couldn't find member data" );
				// Must read something in so that the chunks don't get messed up.
				Asura_Guid uDummy;
				xStream >> uDummy;
				int iDummy;
				xStream >> iDummy;
			}
		}

		m_xBehaviourData.ReadFromChunkStream( xStream );

		xStream >> m_uNumAttackers;

		u_int uNumSpawnTokens;
		xStream >> uNumSpawnTokens;
		for ( u_int u = 0; u < uNumSpawnTokens; ++u )
		{
			AVP_AlienSpawnToken* pxSpawnToken = static_cast< AVP_AlienSpawnToken* >( s_pxSpawnTokenMempool->Allocate() );
			pxSpawnToken->ReadFromChunkStream( xStream );
			m_xSpawnTokenList.AddToList( pxSpawnToken );
		}
	}
	else if ( uVersion == 9 )
	{
		for ( u_int u = 0; u < GetNumSquadMembers(); ++u )
		{
			const Asura_Guid uMemberGuid = GetMemberGuid( u );
			MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uMemberGuid ) );
			if ( pxMemberData )
			{
				xStream >> pxMemberData->m_uAttackeeGuid;
				int iHasLOS;
				xStream >> iHasLOS;
				pxMemberData->m_bHasLOSToTarget = iHasLOS ? true : false;
			}
			else
			{
				Asura_Assert( false, "AVP_ServerEntity_Squad_Alien::ReadFromChunkStream - couldn't find member data" );
				// Must read something in so that the chunks don't get messed up.
				Asura_Guid uDummy;
				xStream >> uDummy;
				int iDummy;
				xStream >> iDummy;
			}
		}

		m_xBehaviourData.ReadFromChunkStream( xStream );

		xStream >> m_uNumAttackers;

		u_int uNumSpawnTokens;
		xStream >> uNumSpawnTokens;
		for ( u_int u = 0; u < uNumSpawnTokens; ++u )
		{
			AVP_AlienSpawnToken* pxSpawnToken = static_cast< AVP_AlienSpawnToken* >( s_pxSpawnTokenMempool->Allocate() );
			pxSpawnToken->ReadFromChunkStream( xStream );
			m_xSpawnTokenList.AddToList( pxSpawnToken );
		}

		xStream >> m_fChanceToGetInjoredPercentage;
		xStream >> m_fInjureOnHealthPercentage;
	}
	else if ( uVersion == 8 )
	{
		for ( u_int u = 0; u < GetNumSquadMembers(); ++u )
		{
			const Asura_Guid uMemberGuid = GetMemberGuid( u );
			MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uMemberGuid ) );
			if ( pxMemberData )
			{
				xStream >> pxMemberData->m_uAttackeeGuid;
				int iHasLOS;
				xStream >> iHasLOS;
				pxMemberData->m_bHasLOSToTarget = iHasLOS ? true : false;
			}
			else
			{
				Asura_Assert( false, "AVP_ServerEntity_Squad_Alien::WriteToChunkStream - couldn't find member data" );
				// Must read something in so that the chunks don't get messed up.
				Asura_Guid uDummy;
				xStream >> uDummy;
				int iDummy;
				xStream >> iDummy;
			}
		}

		m_xBehaviourData.ReadFromChunkStream( xStream );

		xStream >> m_uNumAttackers;

		u_int uNumSpawnTokens;
		xStream >> uNumSpawnTokens;
		for ( u_int u = 0; u < uNumSpawnTokens; ++u )
		{
			AVP_AlienSpawnToken* pxSpawnToken = static_cast< AVP_AlienSpawnToken* >( s_pxSpawnTokenMempool->Allocate() );
			pxSpawnToken->ReadFromChunkStream( xStream );
			m_xSpawnTokenList.AddToList( pxSpawnToken );
		}

		float fDummy;
		xStream >> fDummy; // used to be m_fJumpFraction

		xStream >> m_fChanceToGetInjoredPercentage;
		xStream >> m_fInjureOnHealthPercentage;
	}
	else if ( uVersion == 7 )
	{
		for ( u_int u = 0; u < GetNumSquadMembers(); ++u )
		{
			const Asura_Guid uMemberGuid = GetMemberGuid( u );
			MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uMemberGuid ) );
			if ( pxMemberData )
			{
				xStream >> pxMemberData->m_uAttackeeGuid;
				int iHasLOS;
				xStream >> iHasLOS;
				pxMemberData->m_bHasLOSToTarget = iHasLOS ? true : false;
			}
			else
			{
				Asura_Assert( false, "AVP_ServerEntity_Squad_Alien::WriteToChunkStream - couldn't find member data" );
				// Must read something in so that the chunks don't get messed up.
				Asura_Guid uDummy;
				xStream >> uDummy;
				int iDummy;
				xStream >> iDummy;
			}
		}

		m_xBehaviourData.ReadFromChunkStream( xStream );

		xStream >> m_uNumAttackers;

		u_int uNumSpawnTokens;
		xStream >> uNumSpawnTokens;
		for ( u_int u = 0; u < uNumSpawnTokens; ++u )
		{
			AVP_AlienSpawnToken* pxSpawnToken = static_cast< AVP_AlienSpawnToken* >( s_pxSpawnTokenMempool->Allocate() );
			pxSpawnToken->ReadFromChunkStream( xStream );
			m_xSpawnTokenList.AddToList( pxSpawnToken );
		}

		float fDummy;
		xStream >> fDummy; // used to be m_fJumpFraction

	}
	else if ( uVersion == 6 )
	{
		for ( u_int u = 0; u < GetNumSquadMembers(); ++u )
		{
			const Asura_Guid uMemberGuid = GetMemberGuid( u );
			MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uMemberGuid ) );
			if ( pxMemberData )
			{
				xStream >> pxMemberData->m_uAttackeeGuid;
				int iHasLOS;
				xStream >> iHasLOS;
				pxMemberData->m_bHasLOSToTarget = iHasLOS ? true : false;
			}
			else
			{
				Asura_Assert( false, "AVP_ServerEntity_Squad_Alien::WriteToChunkStream - couldn't find member data" );
				// Must read something in so that the chunks don't get messed up.
				Asura_Guid uDummy;
				xStream >> uDummy;
				int iDummy;
				xStream >> iDummy;
			}
		}

		m_xBehaviourData.ReadFromChunkStream( xStream );

		xStream >> m_uNumAttackers;

		u_int uNumSpawnTokens;
		xStream >> uNumSpawnTokens;
		for ( u_int u = 0; u < uNumSpawnTokens; ++u )
		{
			AVP_AlienSpawnToken* pxSpawnToken = static_cast< AVP_AlienSpawnToken* >( s_pxSpawnTokenMempool->Allocate() );
			pxSpawnToken->ReadFromChunkStream( xStream );
			m_xSpawnTokenList.AddToList( pxSpawnToken );
		}

		bool bIsJumpAttackSquad, bIsStandAttackSquad;
		xStream >> bIsJumpAttackSquad;
		xStream >> bIsStandAttackSquad;

	}
#ifdef ASURA_TOOLS
	else if ( uVersion == 5 )
	{
		for ( u_int u = 0; u < GetNumSquadMembers(); ++u )
		{
			const Asura_Guid uMemberGuid = GetMemberGuid( u );
			MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uMemberGuid ) );
			if ( pxMemberData )
			{
				xStream >> pxMemberData->m_uAttackeeGuid;
				int iHasLOS;
				xStream >> iHasLOS;
				pxMemberData->m_bHasLOSToTarget = iHasLOS ? true : false;
			}
			else
			{
				Asura_Assert( false, "AVP_ServerEntity_Squad_Alien::WriteToChunkStream - couldn't find member data" );
				// Must read something in so that the chunks don't get messed up.
				Asura_Guid uDummy;
				xStream >> uDummy;
				int iDummy;
				xStream >> iDummy;
			}
		}

		m_xBehaviourData.ReadFromChunkStream( xStream );

		xStream >> m_uNumAttackers;

		u_int uNumSpawnTokens;
		xStream >> uNumSpawnTokens;
		for ( u_int u = 0; u < uNumSpawnTokens; ++u )
		{
			AVP_AlienSpawnToken* pxSpawnToken = static_cast< AVP_AlienSpawnToken* >( s_pxSpawnTokenMempool->Allocate() );
			pxSpawnToken->ReadFromChunkStream( xStream );
			m_xSpawnTokenList.AddToList( pxSpawnToken );
		}

		bool bIsJumpAttackSquad;
		xStream >> bIsJumpAttackSquad;

	}
	else if ( uVersion == 4 )
	{
		for ( u_int u = 0; u < GetNumSquadMembers(); ++u )
		{
			const Asura_Guid uMemberGuid = GetMemberGuid( u );
			MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uMemberGuid ) );
			if ( pxMemberData )
			{
				xStream >> pxMemberData->m_uAttackeeGuid;
				int iHasLOS;
				xStream >> iHasLOS;
				pxMemberData->m_bHasLOSToTarget = iHasLOS ? true : false;
			}
			else
			{
				Asura_Assert( false, "AVP_ServerEntity_Squad_Alien::WriteToChunkStream - couldn't find member data" );
				// Must read something in so that the chunks don't get messed up.
				Asura_Guid uDummy;
				xStream >> uDummy;
				int iDummy;
				xStream >> iDummy;
			}
		}

		m_xBehaviourData.ReadFromChunkStream( xStream );

		xStream >> m_uNumAttackers;

		u_int uNumSpawnTokens;
		xStream >> uNumSpawnTokens;
		for ( u_int u = 0; u < uNumSpawnTokens; ++u )
		{
			AVP_AlienSpawnToken* pxSpawnToken = static_cast< AVP_AlienSpawnToken* >( s_pxSpawnTokenMempool->Allocate() );
			pxSpawnToken->ReadFromChunkStream( xStream );
			m_xSpawnTokenList.AddToList( pxSpawnToken );
		}
	}
	else if ( uVersion == 3 )
	{
		m_xBehaviourData.ReadFromChunkStream( xStream );

		xStream >> m_uNumAttackers;

		u_int uNumSpawnTokens;
		xStream >> uNumSpawnTokens;
		for ( u_int u = 0; u < uNumSpawnTokens; ++u )
		{
			AVP_AlienSpawnToken* pxSpawnToken = static_cast< AVP_AlienSpawnToken* >( s_pxSpawnTokenMempool->Allocate() );
			pxSpawnToken->ReadFromChunkStream( xStream );
			m_xSpawnTokenList.AddToList( pxSpawnToken );
		}
	}
	else if ( uVersion == 2 )
	{
		m_xBehaviourData.ReadFromChunkStream( xStream );

		xStream >> m_uNumAttackers;
	}
	else if ( uVersion == 1 )
	{
		m_xBehaviourData.ReadFromChunkStream( xStream );
	}
	else if ( uVersion == 0 )
	{
	}
#endif
	else
	{
		Asura_Assertf( false, ( "AVP_ServerEntity_Squad_Alien::ReadFromChunkStream - unhandled chunk version (%u)", uVersion ) );
	}

	if ( IsDefaultSquad() )
	{
		Asura_Assert( !s_pxDefaultSquad, "AVP_ServerEntity_Squad_Alien::ReadFromChunkStream - already got a default squad" );
		if (!s_pxDefaultSquad)
		{
			s_pxDefaultSquad = this;
		}
		else
		{
			m_bIsDefaultPetSquad = false;
		}
	}

#ifdef ASURA_TOOLS
	// Assert deprecated behaviors and change to aggresive behaviour
	switch (m_xBehaviourData.m_iBehaviour)
	{
	case AVP_AlienBehaviourData::STANDARD:
	case AVP_AlienBehaviourData::GUERILLA:
	case AVP_AlienBehaviourData::ATTACK:
	case AVP_AlienBehaviourData::STALK:
	case AVP_AlienBehaviourData::CUSTOM:
	case AVP_AlienBehaviourData::SWARM:
	case AVP_AlienBehaviourData::HIDE:
	default:
		Asura_Assertf(false,( "Alien squad %d is set to deprecated behaviour %s", GetGuid(), AVP_AlienBehaviourData::s_apszBehaviourStrings[m_xBehaviourData.m_iBehaviour]));
		break;
	case AVP_AlienBehaviourData::AMBUSH:
	case AVP_AlienBehaviourData::DISAPPEAR:
	case AVP_AlienBehaviourData::DESTROY:
	case AVP_AlienBehaviourData::AGGRESIVE:
	case AVP_AlienBehaviourData::HIDE_IN_SHADOWS:
		break;
	}
#endif // ASURA_TOOLS

}

void AVP_ServerEntity_Squad_Alien::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	xStream << uCURRENT_CHUNK_VERSION;

	PARENT::WriteToChunkStream( xStream );

	// Member data.
	for ( u_int u = 0; u < GetNumSquadMembers(); ++u )
	{
		const Asura_Guid uMemberGuid = GetMemberGuid( u );
		const MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uMemberGuid ) );
		if ( pxMemberData )
		{
			xStream << pxMemberData->m_uAttackeeGuid;
			xStream << ( pxMemberData->m_bHasLOSToTarget ? 1 : 0 );
		}
		else
		{
			Asura_Assert( false, "AVP_ServerEntity_Squad_Alien::WriteToChunkStream - couldn't find member data" );
			// Must write something out so that the chunks don't get messed up.
			xStream << ASURA_GUID_UNREGISTERED;
			xStream << 0;
		}
	}

	m_xBehaviourData.WriteToChunkStream( xStream );

	xStream << m_uNumAttackers;

	xStream << static_cast< u_int >( m_xSpawnTokenList.GetNumEntries() );
	for ( Asura_List_ConstIt< AVP_AlienSpawnToken > xSTIter( m_xSpawnTokenList ); !xSTIter.Done(); xSTIter.Next() )
	{
		xSTIter.GetCurrent()->WriteToChunkStream( xStream );
	}
}

bool AVP_ServerEntity_Squad_Alien::HandleMessage( const Asura_Message* pxMessage )
{
	if ( pxMessage->GetMsgID() == AVP_MSGID_SET_ALIEN_SQUAD_BEHAVIOUR )
	{
		const AVP_Msg_SetAlienSquadBehaviour* pxData = static_cast< const AVP_Msg_SetAlienSquadBehaviour* >( pxMessage->GetData() );
		m_xBehaviourData.m_iBehaviour = pxData->m_iBehaviour;
		m_xBehaviourData.SetParamsFromBehaviour();

		if ( m_xBehaviourData.m_iBehaviour == AVP_AlienBehaviourData::CUSTOM )
		{
			m_xBehaviourData.m_iMoveInMode				= pxData->m_iMoveInMode;
			m_xBehaviourData.m_fIdealDistanceMin		= pxData->m_fIdealDistanceMin;
			m_xBehaviourData.m_fIdealDistanceMax		= pxData->m_fIdealDistanceMax;
			m_xBehaviourData.m_fAttackDelay				= pxData->m_fAttackDelay;
			m_xBehaviourData.m_iMaxAttackersPerTarget	= pxData->m_iMaxAttackersPerTarget;
			m_xBehaviourData.m_iMaxAttackersTotal		= pxData->m_iMaxAttackersTotal;
			m_xBehaviourData.m_iMaxBurstPerTarget		= pxData->m_iMaxBurstPerTarget;
			m_xBehaviourData.m_iPostAttackAction		= pxData->m_iPostAttackAction;
			m_xBehaviourData.m_fRespawnDelay			= pxData->m_fRespawnDelay;
		}
		else if ( m_xBehaviourData.m_iBehaviour == AVP_AlienBehaviourData::AGGRESIVE || m_xBehaviourData.m_iBehaviour == AVP_AlienBehaviourData::HIDE_IN_SHADOWS || 
				  m_xBehaviourData.m_iBehaviour == AVP_AlienBehaviourData::AMBUSH )
		{
			m_xBehaviourData.m_uProtoBlueprintHashID = pxData->m_uProtoBlueprintHashID;
			if (m_xBehaviourData.m_uProtoBlueprintHashID != ASURA_HASH_ID_UNSET)
			{
				m_xBehaviourData.SetParamsFromBlueprint();
			}
			else
			{
				m_xBehaviourData.m_fJumpFraction			= pxData->m_fJumpFraction;
				m_xBehaviourData.m_fStandFraction			= pxData->m_fStandFraction;
				m_xBehaviourData.m_fMinTimeBetweenGrapples	= pxData->m_fMinTimeBetweenGrapples;
				m_xBehaviourData.m_fDamageFromBehindMultiplier	= pxData->m_fDamageFromBehindMultiplier;
				m_xBehaviourData.m_iMoveInMode				= pxData->m_iMoveInMode;
				m_xBehaviourData.m_fIdealDistanceMin		= pxData->m_fIdealDistanceMin;
				m_xBehaviourData.m_fIdealDistanceMax		= pxData->m_fIdealDistanceMax;
				m_xBehaviourData.m_fAttackDelay				= pxData->m_fAttackDelay;
				m_xBehaviourData.m_iMaxAttackersPerTarget	= pxData->m_iMaxAttackersPerTarget;
				m_xBehaviourData.m_iMaxAttackersTotal		= pxData->m_iMaxAttackersTotal;
				m_xBehaviourData.m_iMaxBurstPerTarget		= pxData->m_iMaxBurstPerTarget;
				m_xBehaviourData.m_iPostAttackAction		= pxData->m_iPostAttackAction;
				m_xBehaviourData.m_fRespawnDelay			= pxData->m_fRespawnDelay;
				m_xBehaviourData.m_fTimeBetweenAttacks		= pxData->m_fTimeBetweenAttacks;
				m_xBehaviourData.m_uMaxNumberOfChargers		= pxData->m_uMaxNumberOfChargers;
				m_xBehaviourData.m_bAttackIfInMarineLight	= pxData->m_bAttackIfInMarineLight;
				m_xBehaviourData.m_fTimeInLightBeforeAttack = pxData->m_fTimeInLightBeforeAttack;
				m_xBehaviourData.m_fMinHideDistance			= pxData->m_fMinHideDistance;
				m_xBehaviourData.m_fChaneToRehideIfCloseFraction = pxData->m_fChaneToRehideIfCloseFraction;
				m_xBehaviourData.m_fChaneToRehideIfInLightFraction = pxData->m_fChaneToRehideIfInLightFraction;
				m_xBehaviourData.m_fChaneToRehideAfterAttackFraction = pxData->m_fChaneToRehideAfterAttackFraction;
			}
		}

		return true;
	}
	else
	{
		return PARENT::HandleMessage( pxMessage );
	}
}

void AVP_ServerEntity_Squad_Alien::Update()
{
	PARENT::Update();

	// Update the timers on any spawn tokens around.
	for ( Asura_List_It< AVP_AlienSpawnToken > xSTIter( m_xSpawnTokenList ); !xSTIter.Done(); xSTIter.Next() )
	{
		AVP_AlienSpawnToken* pxSpawnToken = xSTIter.GetCurrent();
		pxSpawnToken->m_fRespawnTimer -= Asura_Timers::GetGameTimeSlice();
	}

	if ( m_xBehaviourData.m_iBehaviour != AVP_AlienBehaviourData::DISAPPEAR )
	{
		// See if it's time to spawn any Aliens.
		for ( Asura_List_It< AVP_AlienSpawnToken > xSTIter( m_xSpawnTokenList ); !xSTIter.Done();   )
		{
			AVP_AlienSpawnToken* pxSpawnToken = xSTIter.GetCurrent();
			if ( pxSpawnToken->m_fRespawnTimer <= 0.0f )
			{
				SpawnAlien( *pxSpawnToken );
				s_pxSpawnTokenMempool->Deallocate( pxSpawnToken );
				xSTIter.DeleteCurrent();
			}
			else
			{
				xSTIter.Next();
			}
		}
	}

	// Update anything necessary in our member data.
	for ( Asura_LinkList_ConstIt< SquadMember > xMemberIt( &GetMemberList() ); !xMemberIt.Done(); xMemberIt.Next() )
	{
		MemberData* pxMemberData = static_cast< MemberData* >( xMemberIt.GetCurrent()->m_pxMemberData );
		if ( pxMemberData->m_bIsSwarmLeader )
		{
			pxMemberData->m_fSwarmRouteAge += Asura_Timers::GetGameTimeSlice();
		}
	}	

	// send event messages
	SendOnHealthPercentMessages();
}

void AVP_ServerEntity_Squad_Alien::PreDestructionShutdown()
{
	PARENT::PreDestructionShutdown();

	AVP_AlienSpawnToken* pxSpawnToken = m_xSpawnTokenList.PopTailObject();
	while ( pxSpawnToken )
	{
		s_pxSpawnTokenMempool->Deallocate( pxSpawnToken );
		pxSpawnToken = m_xSpawnTokenList.PopTailObject();
	}

}


void AVP_ServerEntity_Squad_Alien::AddAttacker( const Asura_Guid uAttackerGuid, const Asura_Guid uTargetGuid )
{
	MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uAttackerGuid ) );
	if ( pxMemberData )
	{
		Asura_Assert( pxMemberData->m_uAttackeeGuid == ASURA_GUID_UNREGISTERED, "AVP_ServerEntity_Squad_Alien::AddAttacker - already attacking something" );
		pxMemberData->m_uAttackeeGuid = uTargetGuid;
	}
	++m_uNumAttackers;

	// Also, register this guy as attacking the threat in the Alien team knowledge.
	AVP_ServerEntity_Team_Alien::GetInstance()->AddAttacker( uAttackerGuid, uTargetGuid );
}

void AVP_ServerEntity_Squad_Alien::RemoveAttacker( const Asura_Guid uAttackerGuid, const Asura_Guid uTargetGuid )
{
	MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uAttackerGuid ) );
	if ( pxMemberData )
	{
		Asura_Assert( pxMemberData->m_uAttackeeGuid != ASURA_GUID_UNREGISTERED, "AVP_ServerEntity_Squad_Alien::RemoveAttacker - not attacking anything" );
		pxMemberData->m_uAttackeeGuid = ASURA_GUID_UNREGISTERED;
	}
	--m_uNumAttackers;

	// Also, unregister this guy as attacking the threat in the Alien team knowledge.
	AVP_ServerEntity_Team_Alien::GetInstance()->RemoveAttacker( uAttackerGuid, uTargetGuid );
}

void AVP_ServerEntity_Squad_Alien::AddTargeter( const Asura_Guid uTargeterGuid, const Asura_Guid uTargetGuid )
{
	MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uTargeterGuid ) );
	if ( pxMemberData )
	{
		Asura_Assert( pxMemberData->m_uTargetGuid == ASURA_GUID_UNREGISTERED, "AVP_ServerEntity_Squad_Alien::AddTargeter - already targeting something" );
		pxMemberData->m_uTargetGuid = uTargetGuid;
	}

	// Also, register this guy as targeting the threat in the Alien team knowledge.
	AVP_ServerEntity_Team_Alien::GetInstance()->AddTargeter( uTargeterGuid, uTargetGuid );
}

void AVP_ServerEntity_Squad_Alien::RemoveTargeter( const Asura_Guid uTargeterGuid, const Asura_Guid uTargetGuid )
{
	MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uTargeterGuid ) );
	if ( pxMemberData )
	{
		Asura_Assert( pxMemberData->m_uTargetGuid != ASURA_GUID_UNREGISTERED, "AVP_ServerEntity_Squad_Alien::RemoveTargeter - not targeting anything" );
		pxMemberData->m_uTargetGuid = ASURA_GUID_UNREGISTERED;
	}

	// Also, unregister this guy as targeting the threat in the Alien team knowledge.
	AVP_ServerEntity_Team_Alien::GetInstance()->RemoveTargeter( uTargeterGuid, uTargetGuid );
}

void AVP_ServerEntity_Squad_Alien::UnspawnAlien( AVP_ServerActor_Alien& xServerAlien, const Asura_Guid uSpawnPointGuid, float fRespawnDelay /*= -1.0f*/ )
{
	if ( fRespawnDelay == -1.0f )
	{
		fRespawnDelay = m_xBehaviourData.m_fRespawnDelay;
	}

	// Create a spawn token and add it to the list.
	AVP_AlienSpawnToken* pxSpawnToken = static_cast< AVP_AlienSpawnToken* >( s_pxSpawnTokenMempool->Allocate() );
	Asura_Assert( pxSpawnToken, "AVP_ServerEntity_Squad_Alien::UnspawnAlien - could not create a spawn token" );
	if ( pxSpawnToken )
	{
		pxSpawnToken->m_uBPTypeHashID	= xServerAlien.GetBlueprintTypeHashID();
		pxSpawnToken->m_uBPHashID		= xServerAlien.GetBlueprintHashID();
		pxSpawnToken->m_uSpawnPointGuid = uSpawnPointGuid;
		pxSpawnToken->m_uOldActorGuid	= xServerAlien.GetGuid();
		pxSpawnToken->m_fRespawnTimer   = fRespawnDelay;

		m_xSpawnTokenList.AddToList( pxSpawnToken );
	}

	xServerAlien.SetMarkOutOfPlayOnDeactivate( false );
	xServerAlien.SetEntityActive( false );
	xServerAlien.SetMarkOutOfPlayOnDeactivate( true );
}

Asura_Guid AVP_ServerEntity_Squad_Alien::GetSwarmLeaderGuid( const Asura_Guid uTargetGuid ) const
{
	// See if any member is leader for this target.
	for ( Asura_LinkList_ConstIt< SquadMember > xMemberIt( &GetMemberList() ); !xMemberIt.Done(); xMemberIt.Next() )
	{
		const MemberData* pxMemberData = static_cast< const MemberData* >( xMemberIt.GetCurrent()->m_pxMemberData );
		if ( pxMemberData->m_uTargetGuid == uTargetGuid && pxMemberData->m_bIsSwarmLeader )
		{
			return xMemberIt.GetCurrent()->m_uMemberGuid;
		}
	}

	return ASURA_GUID_UNREGISTERED;
}

int AVP_ServerEntity_Squad_Alien::GetBurstingCount( const Asura_Guid uTargetGuid ) const
{
	int iCount = 0;
	for ( Asura_LinkList_ConstIt< SquadMember > xMemberIt( &GetMemberList() ); !xMemberIt.Done(); xMemberIt.Next() )
	{
		const MemberData* pxMemberData = static_cast< const MemberData* >( xMemberIt.GetCurrent()->m_pxMemberData );
		if ( pxMemberData->m_uTargetGuid == uTargetGuid && pxMemberData->m_bIsBursting )
		{
			return ++iCount;
		}
	}

	return iCount;
}

bool AVP_ServerEntity_Squad_Alien::CanBurst( const Asura_Guid uTargetGuid ) const
{
	return GetBurstingCount(uTargetGuid) < m_xBehaviourData.m_iMaxBurstPerTarget;
}


void AVP_ServerEntity_Squad_Alien::SetSwarmLeaderGuid( const Asura_Guid uLeaderGuid, const Asura_Guid uTargetGuid )
{
	MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uLeaderGuid ) );
	Asura_Assert( pxMemberData, "AVP_ServerEntity_Squad_Alien::SetSwarmLeaderGuid - could not find member data" );
	if ( pxMemberData )
	{
		Asura_Assert( !pxMemberData->m_bIsSwarmLeader, "AVP_ServerEntity_Squad_Alien::SetSwarmLeaderGuid - already a leader" );
		Asura_Assert( pxMemberData->m_uTargetGuid == uTargetGuid, "AVP_ServerEntity_Squad_Alien::SetSwarmLeaderGuid - not the correct target" );
		pxMemberData->m_bIsSwarmLeader = true;
	}
}

void AVP_ServerEntity_Squad_Alien::SetBursting( const Asura_Guid uAlienGuid, bool bIsBursting)
{
	MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uAlienGuid ) );
	if ( pxMemberData )
	{
		pxMemberData->m_bIsBursting = bIsBursting;
	}
}


void AVP_ServerEntity_Squad_Alien::ResetSwarmLeaderGuid( const Asura_Guid uLeaderGuid )
{
	MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uLeaderGuid ) );
	Asura_Assert( pxMemberData, "AVP_ServerEntity_Squad_Alien::ResetSwamLeaderGuid - could not find member data" );
	if ( pxMemberData )
	{
		Asura_Assert( pxMemberData->m_bIsSwarmLeader, "AVP_ServerEntity_Squad_Alien::SetSwarmLeaderGuid - not a leader" );
		pxMemberData->m_bIsSwarmLeader = false;
	}
}

void AVP_ServerEntity_Squad_Alien::ResetSwarmRouteAge( const Asura_Guid uLeaderGuid )
{
	MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uLeaderGuid ) );
	Asura_Assert( pxMemberData, "AVP_ServerEntity_Squad_Alien::ResetSwarmRouteAge - could not find member data" );
	if ( pxMemberData )
	{
		pxMemberData->m_fSwarmRouteAge = 0.0f;
	}
}

float AVP_ServerEntity_Squad_Alien::GetSwarmRouteAge( const Asura_Guid uLeaderGuid ) const
{
	MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uLeaderGuid ) );
	Asura_Assert( pxMemberData, "AVP_ServerEntity_Squad_Alien::GetSwarmRouteAge - could not find member data" );
	if ( pxMemberData )
	{
		return pxMemberData->m_fSwarmRouteAge;
	}
	else
	{
		return Asura_Maths::LargeFloat;
	}
}

void AVP_ServerEntity_Squad_Alien::SetInjurePrecentageOfHealth(float fInjureOnHealthPercentage)
{
	m_fInjureOnHealthPercentage = fInjureOnHealthPercentage;
}

void AVP_ServerEntity_Squad_Alien::SetChanceToGetInjoredPercentage(float fInjureOnHealthPercentage)
{
	m_fChanceToGetInjoredPercentage = fInjureOnHealthPercentage;
}

float AVP_ServerEntity_Squad_Alien::GetInjurePrecentageOfHealth() const
{
	return m_fInjureOnHealthPercentage;
}

float AVP_ServerEntity_Squad_Alien::GetChanceToGetInjoredPercentage() const
{
	return m_fChanceToGetInjoredPercentage;
}

void AVP_ServerEntity_Squad_Alien::InitialiseMempools()
{
	s_pxSpawnTokenMempool = new Asura_Mempool( sizeof( AVP_AlienSpawnToken ), 128 );
	s_pxMemberDataMempool = new Asura_Mempool( sizeof( MemberData ), 128 );
}

void AVP_ServerEntity_Squad_Alien::ShutdownMempools()
{
	//Asura_Assert( s_pxSpawnTokenMempool->GetNumAllocated() == 0, "AVP_ServerEntity_Squad_Alien::ShutdownMempools - there are still spawn tokens allocated" );
	delete s_pxSpawnTokenMempool;
	s_pxSpawnTokenMempool = NULL;

	//Asura_Assert( s_pxMemberDataMempool->GetNumAllocated() == 0, "AVP_ServerEntity_Squad_Alien::ShutdownMempools - there are still member datas allocated" );
	delete s_pxMemberDataMempool;
	s_pxMemberDataMempool = NULL;
}

void* AVP_ServerEntity_Squad_Alien::AllocateMemberData( const Asura_Guid uMemberGuid )
{
	MemberData* pxMemberData = static_cast< MemberData* >( s_pxMemberDataMempool->Allocate() );
	if ( pxMemberData )
	{
		pxMemberData->Reset();
	}
	return pxMemberData;
}

void AVP_ServerEntity_Squad_Alien::ReturnMemberData( void* pData )
{
	s_pxMemberDataMempool->Deallocate( pData );
}

void AVP_ServerEntity_Squad_Alien::SpawnAlien( const AVP_AlienSpawnToken& xSpawnToken )
{
	// Pick a spawn point reasonably near the player to spawn back in at.
	Asura_Hashed_List_It< AVP_ServerEntity_PlayerInfo > xPlayerInfoIter( AVP_ServerEntity_PlayerInfo::GetList() );
	AVP_ServerEntity_PlayerInfo* pxFirstPlayer = xPlayerInfoIter.GetCurrent();
	Asura_Assert( pxFirstPlayer, "AVP_ServerEntity_Squad_Alien::SpawnAlien - could not find player info" );
	if ( !pxFirstPlayer ) { return; }

	AVP_ServerEntity_GamePlayVolume* pxPlayersGPV = AVP_ServerEntity_GamePlayVolume::FindEntity( pxFirstPlayer->GetPlayerGamePlayVolumeGuid() );
	Asura_Assert( pxPlayersGPV, "AVP_ServerEntity_Squad_Alien::SpawnAlien - could not find player's game play volume" );
	if ( !pxPlayersGPV ) { return; }

	const Asura_Vector_3& xPlayerPosition = pxFirstPlayer->GetPlayerPosition();

	static const float s_fIdealRespawnDistanceSq = 2500.0f;

    float						  fBestScore       = Asura_Maths::LargeFloat;
	AVP_ServerEntity_SpawnPoint2* pxBestSpawnPoint = NULL;

	u_int uLinkedVolumeIndex = 0;
	AVP_ServerEntity_GamePlayVolume* pxCurrentGPV = pxPlayersGPV;
	while ( pxCurrentGPV )
	{
		for ( u_int uSpawnPoint = 0; uSpawnPoint < pxCurrentGPV->GetNumSpawnPointGuids(); ++uSpawnPoint )
		{
			AVP_ServerEntity_SpawnPoint2* pxSpawnPoint = AVP_ServerEntity_SpawnPoint2::FindEntity( pxCurrentGPV->GetSpawnPointGuid( uSpawnPoint ) );
			if ( !pxSpawnPoint || !pxSpawnPoint->IsEntityActive() || pxSpawnPoint->IsOccupied() )
			{
				continue;
			}

			const float fScore = Asura_Maths::Abs( s_fIdealRespawnDistanceSq - ( pxSpawnPoint->GetPosition() - xPlayerPosition ).MagnitudeSquared() );
			if ( fScore < fBestScore )
			{
                fBestScore       = fScore;
				pxBestSpawnPoint = pxSpawnPoint;
			}
		}

		if ( uLinkedVolumeIndex < pxPlayersGPV->GetNumberOfVolumes() )
		{
			pxCurrentGPV = AVP_ServerEntity_GamePlayVolume::FindEntity( pxPlayersGPV->GetLinkedGamePlayVolumeGuid( uLinkedVolumeIndex++ ) );
		}
		else
		{
			pxCurrentGPV = NULL;
		}
	}

	Asura_Assert( pxBestSpawnPoint, "AVP_ServerEntity_Squad_Alien::SpawnAlien - could not find a spawn point to use" );
	if ( pxBestSpawnPoint )
	{
		AVP_SpawnParams xSpawnParams;
		xSpawnParams.m_uSpawneeGuid  = xSpawnToken.m_uOldActorGuid;
		xSpawnParams.m_uBPTypeHashID = xSpawnToken.m_uBPTypeHashID;
		xSpawnParams.m_uBPHashID	 = xSpawnToken.m_uBPHashID;
		xSpawnParams.m_uSquadGuid	 = GetGuid();
		xSpawnParams.m_bUpdateStats  = false;
		pxBestSpawnPoint->Spawn( pxBestSpawnPoint->GetGuid(), &xSpawnParams );
	}
}

DEBUGVAR float dbg_fFrontalAngleCosine = Asura_Maths::Cosine(Asura_Maths::DegToRad(45.0f));
bool AVP_ServerEntity_Squad_Alien::CanAttack( const Asura_Guid uTargetGuid, const Asura_Vector_3& xFromPosition, float fTimeBetweenAttacks  ) const
{
	if ( GetNumAttackers() >= static_cast< u_int >( m_xBehaviourData.m_iMaxAttackersTotal ) )
	{
		return false;
	}

	const AVP_AlienTeam_Knowledge*pxTargetKnowledge = AVP_ServerEntity_Team_Alien::GetInstance()->GetKnowledgeSystem()->GetKnowledge(uTargetGuid);

	if (pxTargetKnowledge == NULL)
	{
		return false;
	}

	if (pxTargetKnowledge->m_uNumAttackers >= static_cast< u_int >( m_xBehaviourData.m_iMaxAttackersPerTarget ))
	{
		return false;
	}

	bool bTargetFacingMe = false;

	AVP_ServerEntity_Actor* pxTarget = AVP_ServerEntity_Actor::FindEntity(pxTargetKnowledge->GetGuid());

	if (!pxTarget || pxTarget->IsDead() )
	{
		return false;
	}

	// player should not be attacked from behind if it has more then one targeter and if anyone attacking him now or he was attacked recently. 
	if (pxTarget->IsPlayer())
	{
		Asura_Vector_3 xAim = pxTarget->GetAimDirection();
		Asura_Vector_3 xDirection = xFromPosition - pxTarget->GetPosition();
		xDirection.Normalise();

		bTargetFacingMe = xAim * xDirection > dbg_fFrontalAngleCosine;

		if (!bTargetFacingMe )
		{
			return false;
		}
		else if ((Asura_Timers::GetLocalScaledTime() - pxTargetKnowledge->m_fLastAttackFinishTime) < fTimeBetweenAttacks)
		{
			return false;
		}

	}


	return true;
}

bool AVP_ServerEntity_Squad_Alien::HasLOSToTarget( const Asura_Guid uTargeterGuid, const Asura_Guid uTargetGuid )
{
	const u_int uFrame = Asura_Timers::GetFrameNumber();
	if ( s_uLOSTestFrame != uFrame )
	{
        s_uLOSTestFrame      = uFrame;
		s_uLOSTestsThisFrame = 0;
	}

	if ( s_uLOSTestsThisFrame < 4 )
	{
		// We can go ahead and do the LOS test.
		++s_uLOSTestsThisFrame;

		//## todo
	}
	else
	{
		MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uTargeterGuid ) );
		if ( pxMemberData && pxMemberData->m_uAttackeeGuid == uTargetGuid )
		{
			return pxMemberData->m_bHasLOSToTarget;
		}
		else
		{
			Asura_Assert( pxMemberData, "AVP_ServerEntity_Squad_Alien::IsLOSToTarget - couldn't find member" );
			Asura_Assert( !pxMemberData, "AVP_ServerEntity_Squad_Alien::IsLOSToTarget - unexpected target" );
			return false;
		}
	}

	return false;
}


bool AVP_ServerEntity_Squad_Alien::CanCharge( const Asura_Guid uTargetGuid, u_int uMaxNumberOfChargers ) const
{
	return uMaxNumberOfChargers == 0 || AVP_ServerEntity_Team_Alien::GetInstance()->GetNumberOfChargers( uTargetGuid ) < uMaxNumberOfChargers;
}

void AVP_ServerEntity_Squad_Alien::AddCharger( const Asura_Guid uTargetGuid )
{
	// Also, register this guy as charging the threat in the Alien team knowledge.
	AVP_ServerEntity_Team_Alien::GetInstance()->AddCharger( uTargetGuid );
}

void AVP_ServerEntity_Squad_Alien::RemoveCharger( const Asura_Guid uTargetGuid )
{
	// Also, register this guy as charging the threat in the Alien team knowledge.
	AVP_ServerEntity_Team_Alien::GetInstance()->RemoveCharger( uTargetGuid );
}

void AVP_ServerEntity_Squad_Alien::UpdateHealthFraction( const Asura_Guid uAlienGuid, float fHealthFraction )
{
	MemberData* pxMemberData = static_cast< MemberData* >( GetMemberData( uAlienGuid ) );
	if ( pxMemberData ) // alien might have died by this point so the member data might not be here anymore.
	{
		if (m_fMinHealthFractionSoFar > fHealthFraction)
		{
			m_fMinHealthFractionSoFar = fHealthFraction;
			m_bNeedToSendHealthPrecentMessages = true;
		}
		pxMemberData->m_fHealthFraction = fHealthFraction;
	}
}

bool AVP_ServerEntity_Squad_Alien::CanLockAttackGrapple( const Asura_Guid uTargetGuid, const Asura_Vector_3& xFromPosition )
{
	u_int uAttackGrapple, uAttackStand, uAttackJump;
	if (AVP_ServerEntity_Team_Alien::GetInstance()->GetNumAttackers(uTargetGuid, uAttackGrapple, uAttackStand, uAttackJump))
	{
		if (uAttackGrapple > 0 )
		{
			return false;
		}
		else if (AVP_ServerEntity_Team_Alien::GetInstance()->TimeSinceLastGrapple(uTargetGuid) < m_xBehaviourData.m_fMinTimeBetweenGrapples)
		{
			return false;
		}
		else
		{
			return true;
		}
	}
	else
	{
		return false;
	}
}

void AVP_ServerEntity_Squad_Alien::LockAttackGrapple( const Asura_Guid uTargetGuid )
{
	AVP_ServerEntity_Team_Alien::GetInstance()->AddAttackGrapple(uTargetGuid);
}

void AVP_ServerEntity_Squad_Alien::UnlockAttackGrapple( const Asura_Guid uTargetGuid )
{
	AVP_ServerEntity_Team_Alien::GetInstance()->RemoveAttackGrapple(uTargetGuid);
}

bool AVP_ServerEntity_Squad_Alien::CanLockAttackStand( const Asura_Guid uTargetGuid, const Asura_Vector_3& xFromPosition )
{
	u_int uAttackGrapple, uAttackStand, uAttackJump;
	if (AVP_ServerEntity_Team_Alien::GetInstance()->GetNumAttackers(uTargetGuid, uAttackGrapple, uAttackStand, uAttackJump))
	{
		if (uAttackGrapple > 0 || uAttackStand > 0)
		{
			return false;
		}
		else
		{
			return true;
		}
	}
	else
	{
		return false;
	}
}

void AVP_ServerEntity_Squad_Alien::LockAttackStand( const Asura_Guid uTargetGuid )
{
	AVP_ServerEntity_Team_Alien::GetInstance()->AddAttackStand(uTargetGuid);
}

void AVP_ServerEntity_Squad_Alien::UnlockAttackStand( const Asura_Guid uTargetGuid )
{
	AVP_ServerEntity_Team_Alien::GetInstance()->RemoveAttackStand(uTargetGuid);
}

bool AVP_ServerEntity_Squad_Alien::CanLockAttackJump( const Asura_Guid uTargetGuid, const Asura_Vector_3& xFromPosition )
{
	u_int uAttackGrapple, uAttackStand, uAttackJump;
	u_int uMaxJumpAttackers;
	if (AVP_ServerEntity_Team_Alien::GetInstance()->DidTargetMovedRecently(uTargetGuid))
	{
		uMaxJumpAttackers = 1;
	}
	else
	{
		uMaxJumpAttackers = 2;
	}
	if (AVP_ServerEntity_Team_Alien::GetInstance()->GetNumAttackers(uTargetGuid, uAttackGrapple, uAttackStand, uAttackJump))
	{
		if (uAttackGrapple > 0 || uAttackStand > 0 || uAttackJump >= uMaxJumpAttackers)
		{
			return false;
		}
// 		else if (!CanAttackFromPosition(uTargetGuid, xFromPosition, false)) - jump attacks are allowed from any direction now. attacks from behind will do reduced damage.
// 		{
// 			return false;
// 		}
		else
		{
			return true;
		}
	}
	else
	{
		return false;
	}
}

void AVP_ServerEntity_Squad_Alien::LockAttackJump( const Asura_Guid uTargetGuid )
{
	AVP_ServerEntity_Team_Alien::GetInstance()->AddAttackJump(uTargetGuid);
}

void AVP_ServerEntity_Squad_Alien::UnlockAttackJump( const Asura_Guid uTargetGuid )
{
	AVP_ServerEntity_Team_Alien::GetInstance()->RemoveAttackJump(uTargetGuid);
}

bool AVP_ServerEntity_Squad_Alien::CanLockAmbushHiss()
{
	return !m_bAmbushHissLock;
}
void AVP_ServerEntity_Squad_Alien::LockAmbushHiss()
{
	Asura_Assert(!m_bAmbushHissLock, "Lock Ambush hiss lock while it is locked.");
	m_bAmbushHissLock = true;
}

void AVP_ServerEntity_Squad_Alien::ReleaseAmbushHiss()
{
	Asura_Assert(m_bAmbushHissLock, "Release Ambush hiss lock without locking it.");
	m_bAmbushHissLock = false;
}



void AVP_ServerEntity_Squad_Alien::SendOnHealthPercentMessages()
{

	if(!m_bNeedToSendHealthPrecentMessages)
	{
		return;
	}

	const Asura_EventAndMessage* pxNextEvent = GetEvents();
	for( int i = GetNumEvents(); i; ++pxNextEvent, --i )
	{
		const u_short usMsgBlockID = pxNextEvent->m_usMessageBlockID;
		if( usMsgBlockID == ASURA_STATICMESSAGE_EMPTYBLOCK ) continue;

		const u_short usEventID = pxNextEvent->m_usEventID;
		if( ASURA_ENTITY_EVENT_ONHEALTHPERCENT_0 <= usEventID && usEventID <= ASURA_ENTITY_EVENT_ONHEALTHPERCENT_99 )
		{
			float fThreshold = static_cast<float>(usEventID - ASURA_ENTITY_EVENT_ONHEALTHPERCENT_0) * 0.01f;
			if( (m_fPreviousMinHealthFractionSoFar > fThreshold) && (m_fMinHealthFractionSoFar <= fThreshold) )
			{
				Asura_StaticMessage_System::SendMessages( usMsgBlockID, GetGuid() );
			}
		}
	}

	m_fPreviousMinHealthFractionSoFar = m_fMinHealthFractionSoFar;
	m_bNeedToSendHealthPrecentMessages = false;
}

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