///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_Behaviour_AlienQueen_Attack.cpp
// Author : Andy McCabe
// Date   : 24/11/2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

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

// header
#include "Behaviour/BehaviourTypes/AlienQueen/AVP_Behaviour_AlienQueen_Attack.h"

// asura
#include "Core/Asura_Maths.h"
#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Rendering/Asura_Font_System.h"
#endif

// axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Core/BrainTypes/AVP_Brain_Actor_AlienQueen.h"

// avp
#include "Actor/Vertebrates/AlienQueen/AVP_AlienQueen_AIAnimStateMachine.h"
#include "Actor/Vertebrates/AlienQueen/AVP_AlienQueen_MeleeCounter_System.h"
#include "Animation/AVP_AnimFlags.h"
#include "Entity/AVP_Messages.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_ServerActor_Vertebrate.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_Vertebrate_Messages.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AlienQueen/AVP_AlienQueen_Messages.h"
#include "Moves/Vertebrates/AlienQueen/AVP_AlienQueen_MovesMelee.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_AlienQueen_Attack
//
//////////////////////////////////////////////////////////////////////////////

DEBUGVAR float dbg_fLowHealthPercentageGrappleKill = 30.0f;

AVP_Behaviour_AlienQueen_Attack::AVP_Behaviour_AlienQueen_Attack( AVP_Brain_Actor_AlienQueen* pxBrain )
: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_ALIENQUEEN_ATTACK )
, m_uMeleeType( AVP_ALIENQUEEN_MELEEATTACK_TYPE_PRIMARYATTACK )
, m_uInitialMeleeMoveHash( ASURA_HASH_ID_UNSET )
, m_eMeleeHandedness( NONE )
, m_uNumRemainingAttacks( 0 )
{
	SetCurrentState( UNSET );
}

bool AVP_Behaviour_AlienQueen_Attack::Activate()
{
	if ( GetAlienQueenBrain() && GetAlienQueenBrain()->GetActor() )
	{
		if ( GetAlienQueenBrain()->GetActor()->GetAIAnimStateMachine() )
		{
			//GetAlienQueenBookatrain()->GetActor()->GetAIAnimStateMachine()->RequestIdle();
		}
	}

	return PARENT::Activate();
}

void AVP_Behaviour_AlienQueen_Attack::Deactivate()
{
	// Destroy any children.
	Axon_Behaviour* pxChildBehaviour = GetChild();
	while ( pxChildBehaviour )
	{
		UnlinkChild( pxChildBehaviour );
		GetProjectBrain()->DestroyBehaviour( pxChildBehaviour );
		pxChildBehaviour = GetChild();
	}

	PARENT::Deactivate();
}


void AVP_Behaviour_AlienQueen_Attack::PerFrameUpdate( const float fTimeslice )
{
	PARENT::PerFrameUpdate( fTimeslice );

	if( fTimeslice == 0.0f ) 
	{ 
		return; 
	}

	UpdateStateTimer( fTimeslice );

	if(	! ( GetAlienQueenBrain() && GetAlienQueenBrain()->GetTargetKnowledge() )													||
		! ( GetAlienQueenBrain()->HasCurrentTarget() )																				||
		( GetAlienQueenBrain() && GetAlienQueenBrain()->GetTargetKnowledge()->m_uSourceClassification == AVP_ENTITYCLASS_ALIEN )	)
	{
		// if we dont have a brain, the target is an alien or worse Not Set then we should not be attacking, finish
		if( GetCurrentState() != FINISHED )
		{
			SwitchToState( FINISHED );
		}
	}

	switch( GetCurrentState() )
	{
		////////////////////
		case UNSET:
		////////////////////
		{
			if( GetAlienQueenBrain()->GetTargetKnowledge() )
			{
				switch( GetAlienQueenBrain()->GetNextAttackType() )
				{
					////////////////////
					case AVP_AQ_ATTACK_CHARGE:
					////////////////////
					{
						SwitchToState( REQUESTED_PRIMARY_ATTACK );
						break;
					}
					////////////////////
					case AVP_AQ_ATTACK_CLAW_SWIPE:
					////////////////////
					{
						// CAH COMMENTED OUT GRAPPLE UNTIL WE HAVE ANIMS
						//if (GetTargetsHealthPercentage() < dbg_fLowHealthPercentageGrappleKill)
						//{
						//	SwitchToState( REQUESTED_CLAWSWIPEKILL_ATTACK );
						//}
						//else
						{
							SwitchToState( REQUESTED_SLASH_ATTACK );
						}
						break;
					}
					////////////////////
					case AVP_AQ_ATTACK_SPIT_ACID:
					////////////////////
					{
						SwitchToState( REQUESTED_SPIT_ACID_ATTACK );
						break;
					}
					////////////////////
					default:
					////////////////////
					{
						Asura_Assert( false, "AVP_Behaviour_AlienQueen_Attack::PerFrameUpdate - No next attack type set" );
						SwitchToState( FINISHED );
						break;
					}
				}
			}
			else
			{
				Asura_Assert( false, "AVP_Behaviour_AlienQueen_Attack::PerFrameUpdate - Cant find brain or target!!" );
				SwitchToState( FINISHED );
			}
			break;
		}
		////////////////////
		case REQUESTED_PRIMARY_ATTACK:
		case REQUESTED_SLASH_ATTACK:
		case REQUESTED_CLAWSWIPEKILL_ATTACK:
		case REQUESTED_SPIT_ACID_ATTACK:
		////////////////////
		{
			// We're waiting for the move start message to notify us we've started our melee move.
			// Hardcoded timeout in case this doesn't arrive.
			if ( GetStateTimer() > 0.5f )
			{
				SwitchToState( FINISHED );
			}
			break;
		}
		////////////////////
		case STARTED_SLASH_ATTACK:
		case STARTED_LOOP_SLASH_ATTACK:
		////////////////////
		{
			if( GetAlienQueenBrain()->GetLookAtGuid() != ASURA_GUID_UNREGISTERED )
			{
				GetAlienQueenBrain()->LookAtEntityFromMsgData();
			}
			else
			{
				UpdateLookAndAimDirections();
			}

			if( GetAlienQueenBrain() && GetAlienQueenBrain()->HasCurrentTarget() )
			{
				if( GetAlienQueenBrain()->GetTargetKnowledge()->m_fDistance > GetAlienQueenBrain()->GetSlashAttackMinRange() )
				{
					// if the target is now out of slashing range stop and finish. 
					SwitchToState( FINISHED );
					break;
				}

				// The allow new melee flag lets us know we are at the end of the melee.
				// Use this to either trigger a looped attack or exit slightly early so we can continue moving smoothly.
				const Asura_Move* pxMove = GetAlienQueenBrain()->GetClientActor()->GetMovementSystem()->GetCurrentMove();
				const u_int uMoveAnimFlags = pxMove ? pxMove->GetAnimFlagsForLastFrame() : 0;
				if ( ( uMoveAnimFlags & AVP_ANIMFLAG_ALLOW_NEW_MELEE ) != 0 )
				{
					const bool bTargetNearDeath	= ( GetTargetsHealthPercentage() < dbg_fLowHealthPercentageGrappleKill );

					const bool bCanDoMoreAttacks = ( (m_uNumRemainingAttacks > 0) && !bTargetNearDeath);

					if ( bCanDoMoreAttacks )
					{
						SwitchToState( STARTED_LOOP_SLASH_ATTACK );
					}
				}
			}
			else
			{
				SwitchToState( FINISHED );
			}

			break;
		}
		////////////////////
		case STARTED_SPIT_ACID_ATTACK:
		////////////////////
		{
			const Asura_Move* pxMove = GetAlienQueenBrain()->GetClientActor()->GetMovementSystem()->GetCurrentMove();
			const u_int uMoveAnimFlags = pxMove ? pxMove->GetAnimFlagsForLastFrame() : 0;
			if ( ( uMoveAnimFlags & AVP_ANIMFLAG_MELEE_DAMAGE ) != 0 )
			{
				// Send message to server to create and launch an acid spit projectile
				Asura_Message* pxAIMessage = Asura_Network::CreateMessage(AVP_MSGID_LAUNCH_ACID_SPIT_PROJECTILE, sizeof( AVP_Msg_AlienQueen_LaunchAcidSpit_Projectile ));
				if( pxAIMessage )
				{
					AVP_Msg_AlienQueen_LaunchAcidSpit_Projectile* pxData = static_cast<AVP_Msg_AlienQueen_LaunchAcidSpit_Projectile*>( pxAIMessage->GetData() );
					pxData->SetTargetGuid( GetAlienQueenBrain()->GetCurrentTargetGuid() );
					
					pxAIMessage->SetFromGuid( GetAlienQueenBrain()->GetOwnerGuid() );
					pxAIMessage->SetToGuid  ( GetAlienQueenBrain()->GetOwnerGuid() );
					Asura_Network::Send(pxAIMessage);
				}
				
			}

			break;
		}
		////////////////////
		case STARTED_PRIMARY_ATTACK:
		////////////////////
		{
			if( GetAlienQueenBrain()->GetLookAtGuid() != ASURA_GUID_UNREGISTERED )
			{
				// if we have a node to look at we cant assure that its in front of us so while we are charging just look forward. 
				GetAlienQueenBrain()->LookAndAimForward();
			}
			else
			{
				//update the aim direction incase the player has side stepped the queen. 
				Asura_Vector_3 xTargetDirection = GetAlienQueenBrain()->GetTargetKnowledge()->m_xPosition - GetAlienQueenBrain()->GetActor()->GetPosition();
				xTargetDirection.Normalise();

				Asura_Matrix_3x3 xQnOri = GetAlienQueenBrain()->GetActor()->GetOrientation();
				Asura_Vector_3 xQueenDirection = Asura_Vector_3( xQnOri.e13, xQnOri.e23, xQnOri.e33 );
				xQueenDirection.Normalise();

				float fDotProduct = xQueenDirection * xTargetDirection;

				if( fDotProduct > 0.95f )
				{
					// if dot product is more than cos(18) player is still in vision cone. 
					GetAlienQueenBrain()->SetMovementDirection( xTargetDirection );
				}

				UpdateLookAndAimDirections();
			}
			break;
		}
		////////////////////
		case STARTED_CLAWSWIPEKILL_ATTACK:
		case FINISHED:
		default:
		////////////////////
		{
			break;
		}
	}
}

bool AVP_Behaviour_AlienQueen_Attack::HandleMessage( const Asura_Message* pxMessage )
{
	switch ( GetCurrentState() )
	{
		////////////////////
		case REQUESTED_PRIMARY_ATTACK:
		////////////////////
		{
			// Waiting for the move start message to notify us that we have started our melee.
			if ( pxMessage->GetMsgID() == ASURA_MSG_MOVE_START )
			{
				const AVP_Msg_Move* pxData = static_cast< const AVP_Msg_Move* >( pxMessage->GetData() );
				m_uInitialMeleeMoveHash = pxData->GetMoveID();
				SwitchToState( STARTED_PRIMARY_ATTACK );
				return true;
			}
			break;
		}
		////////////////////
		case REQUESTED_SLASH_ATTACK:
		////////////////////
		{
			// Waiting for the move start message to notify us that we have started our melee.
			if ( pxMessage->GetMsgID() == ASURA_MSG_MOVE_START )
			{
				const AVP_Msg_Move* pxData = static_cast< const AVP_Msg_Move* >( pxMessage->GetData() );
				m_uInitialMeleeMoveHash = pxData->GetMoveID();
				SwitchToState( STARTED_SLASH_ATTACK );
				return true;
			}
			break;
		}
		////////////////////
		case REQUESTED_CLAWSWIPEKILL_ATTACK:
		////////////////////
		{
			// Waiting for the move start message to notify us that we have started our melee.
			if ( pxMessage->GetMsgID() == ASURA_MSG_MOVE_START )
			{
				const AVP_Msg_Move* pxData = static_cast< const AVP_Msg_Move* >( pxMessage->GetData() );
				m_uInitialMeleeMoveHash = pxData->GetMoveID();
				SwitchToState( STARTED_CLAWSWIPEKILL_ATTACK );
				return true;
			}
			break;
		}
		////////////////////
		case REQUESTED_SPIT_ACID_ATTACK:
		////////////////////
		{
			// Waiting for the move start message to notify us that we have started our melee.
			if ( pxMessage->GetMsgID() == ASURA_MSG_MOVE_START )
			{
				const AVP_Msg_Move* pxData = static_cast< const AVP_Msg_Move* >( pxMessage->GetData() );
				m_uInitialMeleeMoveHash = pxData->GetMoveID();
				SwitchToState( STARTED_SPIT_ACID_ATTACK );
				return true;
			}
			break;
		}
		////////////////////
		case STARTED_PRIMARY_ATTACK:
		case STARTED_SLASH_ATTACK:
		case STARTED_LOOP_SLASH_ATTACK:
		case STARTED_CLAWSWIPEKILL_ATTACK:
		case STARTED_SPIT_ACID_ATTACK:
		////////////////////
		{
			if ( pxMessage->GetMsgID() == ASURA_MSG_MOVECHAINCOMPLETE )
			{
				const Asura_Msg_MoveChainComplete* pxData = static_cast< const Asura_Msg_MoveChainComplete* >( pxMessage->GetData() );
				if ( pxData->GetInitialMoveID() == m_uInitialMeleeMoveHash )
				{
					SwitchToState( FINISHED );
					return true;
				}
			}
			else if ( pxMessage->GetMsgID() == AVP_MSGID_COUNTER_MELEE )
			{
				return true;
			}
			break;
		}
		////////////////////
		default:
		////////////////////
		{
			break;
		}
	}
	return PARENT::HandleMessage( pxMessage );
}

#ifndef ASURA_NO_DEBUG_OUTPUT
const char* AVP_Behaviour_AlienQueen_Attack::s_aszStateStrings[NUM_STATES] =
{
	"UNSET",
	"REQUESTED_PRIMARY_ATTACK",
	"STARTED_PRIMARY_ATTACK",
	"REQUESTED_SLASH_ATTACK",
	"STARTED_SLASH_ATTACK",
	"STARTED_LOOP_SLASH_ATTACK",
	"REQUESTED_CLAWSWIPEKILL_ATTACK",
	"STARTED_CLAWSWIPEKILL_ATTACK",
	"REQUESTED_SPIT_ACID_ATTACK"
	"STARTED_SPIT_ACID_ATTACK"
	"FINISHED"
};

void AVP_Behaviour_AlienQueen_Attack::RenderDebugInfo()
{
	PARENT::RenderDebugInfo();

	Asura_Font_System::Print( GetProjectBrain()->GetDebugOutputPos().x, GetProjectBrain()->GetDebugOutputPos().y, 0.5f, 0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "AlienQueen Attack: %s", s_aszStateStrings[ GetCurrentState() ] );
	GetProjectBrain()->GetDebugOutputPos().y += 10.0f;
}
#endif


void AVP_Behaviour_AlienQueen_Attack::SwitchToState( const State eNextState )
{
	// Do any work we need to exit our current state.
	switch ( GetCurrentState() )
	{
		////////////////////
		case UNSET:
		case REQUESTED_PRIMARY_ATTACK:
		case STARTED_PRIMARY_ATTACK:
		case REQUESTED_SLASH_ATTACK:
		case REQUESTED_CLAWSWIPEKILL_ATTACK:
		case STARTED_SLASH_ATTACK:
		case STARTED_LOOP_SLASH_ATTACK:
		case STARTED_CLAWSWIPEKILL_ATTACK:
		case REQUESTED_SPIT_ACID_ATTACK:
		case STARTED_SPIT_ACID_ATTACK:
		case FINISHED:
		////////////////////
		{
			break;
		}
		////////////////////
		default:
		////////////////////
		{
			Asura_Assert(false, "unexpected state" );
			break;
		}
	}

	// Do any work we need to change into our next state.
	SetCurrentState( eNextState );
	ResetStateTimer();

	switch ( GetCurrentState() )
	{
		////////////////////
		case REQUESTED_PRIMARY_ATTACK:
		////////////////////
		{
			m_uMeleeType = AVP_ALIENQUEEN_MELEEATTACK_TYPE_PRIMARYATTACK;
			RequestMelee(m_uMeleeType);
			
			break;
		}
		////////////////////
		case REQUESTED_SLASH_ATTACK:
		////////////////////
		{
			SetupSlashAttack( true );
			RequestMelee(m_uMeleeType);
			
			break;
		}
		////////////////////
		case REQUESTED_CLAWSWIPEKILL_ATTACK:
		////////////////////
		{
			m_uMeleeType = AVP_ALIENQUEEN_MELEEATTACK_TYPE_CLAWSWIPEKILL_GRAPPLE;
			RequestMelee(m_uMeleeType);
			
			break;
		}
		////////////////////
		case REQUESTED_SPIT_ACID_ATTACK:
		////////////////////
		{
			RequestSpitAcidMove();
			break;
		}
		////////////////////
		case STARTED_LOOP_SLASH_ATTACK:
		////////////////////
		{
			--m_uNumRemainingAttacks;

			// find which arm to use for the attack. 
			SetupSlashAttack( false );

			RequestMelee( m_uMeleeType );

			break;
		}
		////////////////////
		case UNSET:
		case STARTED_SLASH_ATTACK:
		case STARTED_PRIMARY_ATTACK:
		case STARTED_CLAWSWIPEKILL_ATTACK:
		case STARTED_SPIT_ACID_ATTACK:
		////////////////////
		{
			break;
		}
		////////////////////
		case FINISHED:
		////////////////////
		{
			SetFinished(true);
			break;
		}
		////////////////////
		default:
		////////////////////
		{
			Asura_Assert(false, "unexpected state" );
			break;
		}
	}
}

AVP_Brain_Actor_AlienQueen* AVP_Behaviour_AlienQueen_Attack::GetAlienQueenBrain() const
{
	return static_cast< AVP_Brain_Actor_AlienQueen* >( GetProjectBrain() );
}

void AVP_Behaviour_AlienQueen_Attack::RequestMelee( const u_int uMeleeType )
{
	if ( !(GetAlienQueenBrain() && GetAlienQueenBrain()->HasCurrentTarget()) )
	{
		Asura_Assert( false, "AVP_Behaviour_AlienQueen_Attack::RequestMelee - Cant find AlienQueen brain or no current target" );
		return;
	}

	// Send a request melee message.
	Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSGID_REQUEST_MELEE, sizeof( AVP_Message_Vertebrate_RequestMelee ) );
	if ( pxMessage )
	{
		AVP_Message_Vertebrate_RequestMelee* pxData = static_cast< AVP_Message_Vertebrate_RequestMelee* >( pxMessage->GetData() );

		pxData->SetType( uMeleeType );
		pxData->SetTargetGuid( GetAlienQueenBrain()->GetCurrentTargetGuid() );

		pxMessage->SetToGuid( GetAlienQueenBrain()->GetOwnerGuid() );
		pxMessage->SetFromGuid( GetAlienQueenBrain()->GetOwnerGuid() );
		Asura_Network::Send( pxMessage );
	}
}

void AVP_Behaviour_AlienQueen_Attack::RequestSpitAcidMove()
{
	Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_MOVE_START, sizeof( AVP_Msg_Move ) );
	if ( pxMessage != NULL )
	{
		AVP_Msg_Move* pxData = static_cast< AVP_Msg_Move* >( pxMessage->GetData() );

		pxMessage->SetFromGuid( GetAlienQueenBrain()->GetOwnerGuid() );
		pxMessage->SetToGuid( GetAlienQueenBrain()->GetOwnerGuid() );

		pxData->ClearFlags();
		pxData->SetIgnoredByOwner( false );
		pxData->SetMoveID( AVP_AlienQueen_Move_Spit_Acid::GetStaticHash() );

		Asura_Network::Send( pxMessage );
	}
}

DEBUGVAR float dbg_fCentreRange = 0.2f;

void AVP_Behaviour_AlienQueen_Attack::SetupSlashAttack( bool bSetNumberOfAttacks )
{
	if ( GetAlienQueenBrain() && GetAlienQueenBrain()->HasCurrentTarget() )
	{
		Asura_Vector_3 xQueenDirection;
		Asura_Matrix_3x3 xOrientMatrix;

		//first we need to find the position of the target in relation to the queen. 
		GetAlienQueenBrain()->GetActor()->GetHeadFowardVector( xQueenDirection );
		Asura_Maths::OrientationFromDirection( xOrientMatrix, xQueenDirection );
		const Asura_Vector_3 xLocalSpaceTargetPos = xOrientMatrix.ApplyTranspose( GetAlienQueenBrain()->GetTargetKnowledge()->m_xPosition - GetAlienQueenBrain()->GetActor()->GetPosition() );

		// now the arm used depends on the local space position just found. 
		if( xLocalSpaceTargetPos.GetX() < - dbg_fCentreRange )
		{
			// if the target is to the left of the queen use the right arm as she'll be turning left at the time. 
			m_uMeleeType = AVP_ALIENQUEEN_MELEEATTACK_TYPE_SLASH_R;
			m_eMeleeHandedness = RIGHT;
		}
		else if( xLocalSpaceTargetPos.GetX() > dbg_fCentreRange )
		{
			// if the target is to the right of the queen use the laft arm as she'll be turning right at the time. 
			m_uMeleeType = AVP_ALIENQUEEN_MELEEATTACK_TYPE_SLASH_L;
			m_eMeleeHandedness = LEFT;
		}
		else
		{
			// else the target is right in front of us, so it can be random. 
			const float fRand = Asura_Random::GetFloat();
			if ( fRand < 0.5f )
			{
				m_uMeleeType = AVP_ALIENQUEEN_MELEEATTACK_TYPE_SLASH_R;
				m_eMeleeHandedness = RIGHT;
			}
			else
			{
				m_uMeleeType = AVP_ALIENQUEEN_MELEEATTACK_TYPE_SLASH_L;
				m_eMeleeHandedness = LEFT;
			}
		}
	}
	else
	{
		// if somehow we dont have a brain or target assert and just assign right for stability. 
		Asura_Assert( false, "AVP_Behaviour_AlienQueen_Attack::SetupSlashAttack\n We are trying to attack but dont have a brain or a target!" );
		m_uMeleeType = AVP_ALIENQUEEN_MELEEATTACK_TYPE_SLASH_R;
		m_eMeleeHandedness = RIGHT;
	}

	if( bSetNumberOfAttacks )
	{
		// lastly set up the number of attacks to try and dish out. At the mo - random. 
		m_uNumRemainingAttacks = Asura_Random::GetIntU( 1, 2 );
	}
}

float AVP_Behaviour_AlienQueen_Attack::GetTargetsHealthPercentage() const
{
	if ( GetAlienQueenBrain() && GetAlienQueenBrain()->HasCurrentTarget() )
	{
		AVP_ServerActor_Vertebrate* pxTarget = AVP_ServerActor_Vertebrate::FindEntity( GetAlienQueenBrain()->GetCurrentTargetGuid() );
					
		if (pxTarget)
		{
			if (pxTarget->GetInitialHealth() < 0.001f)
			{
				Asura_Assert( false, "AVP_Behaviour_AlienQueen_Attack::GetTargetsHealthPercentage - Targets initial health is zero. Why?" );
				return 0.0f;
			}
			
			return ( (pxTarget->GetHealth()/pxTarget->GetInitialHealth()) * 100.0f );
		}
	}
	
	return 100.0f;
}

void AVP_Behaviour_AlienQueen_Attack::UpdateLookAndAimDirections()
{
	//update the aim direction incase the player has side stepped the queen. 
	Asura_Vector_3 xAimDirection = GetAlienQueenBrain()->GetTargetKnowledge()->m_xPosition - GetAlienQueenBrain()->GetActor()->GetPosition();
	xAimDirection.Normalise();

	Asura_Matrix_3x3 xQnOri = GetAlienQueenBrain()->GetActor()->GetOrientation();
	Asura_Vector_3 xQueenDirection = Asura_Vector_3( xQnOri.e13, xQnOri.e23, xQnOri.e33 );
	xQueenDirection.Normalise();

	float fDotProduct = xQueenDirection * xAimDirection;

	if( fDotProduct > GetAlienQueenBrain()->GetMaxHeadTurnCosine() )
	{
		// if dot product is more than cos(30)
		GetAlienQueenBrain()->SetLookAtPosition( GetAlienQueenBrain()->GetTargetKnowledge()->m_xPosition );
		GetAlienQueenBrain()->SetAimDirection( xAimDirection );
	}
	else if( fDotProduct < 0.0f )
	{
		// if dot product is less then cos(90)
		GetAlienQueenBrain()->ResetAimDirection();
		GetAlienQueenBrain()->ResetLookAtPosition();

		// if the target is behind the queen then we shouldnt be attaking anymore, so finish. 
		SwitchToState( FINISHED );
	}
	else
	{
		// anything between 45 and 90 degrees slowly force the head back to the forwards position. 
		GetAlienQueenBrain()->ForceAimDirection( GetAlienQueenBrain()->GetAimDirection() + ( ( Asura_Vector_3( xQnOri.e13, xQnOri.e23, xQnOri.e33 ) - GetAlienQueenBrain()->GetAimDirection() ) * 0.05f ) );
		GetAlienQueenBrain()->ForceLookAtPosition( GetAlienQueenBrain()->GetLookAtPosition() + ( ( Asura_Vector_3( xQnOri.e13, xQnOri.e23, xQnOri.e33 ) * 10.0f - GetAlienQueenBrain()->GetLookAtPosition() ) * 0.05f ) );
	}
}
//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////

