//////////////////////////////////////////////////////////////////////////////
//
// AVP_ClientEntity_Actor.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header.
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"

// Engine independent.
#include "Affectors/Asura_Affector.h"
#include "Affectors/Asura_Affector_System.h"
#include "Animation/Asura_Animation.h"
#include "Animation/Asura_AnimationAttachment.h"
#include "Animation/IK/Asura_Animation_IK_Chain_LOS.h"
#include "Blueprint/Asura_Blueprint.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "Core/Asura_MaterialResponse_System.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_DebugLineRender.h"
#include "Entity/Asura_PosAndOriInterpolator.h"
#include "Entity/Ragdoll/Asura_ClientEntity_Ragdoll.h"
#include "Interface/Asura_CommandConsole.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Network/Asura_Player.h"
#include "Physics/Asura_Physics.h"
#include "Physics/Asura_Physics_CCD.h"
#include "Rendering/Asura_Font_System.h"
#include "Rendering/Asura_Render.h"
#include "Shapes/Asura_Shapes.h"
#include "Sound/Asura_Sound_Event_Storage.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"
#include "SpecialFX/PFX/Asura_PFX_Entity_Effect.h"
#include "Core/AVP_Game_Core.h"
#include "Entity/PhysicalObject/Interact/AVP_ClientEntity_InteractObject.h"

// Project independent.
#include "Actor/AVP_Actor_AnimationHandler.h"
#include "Actor/AVP_Actor_AttachmentHandler.h"
#include "Actor/AVP_Actor_CameraHandler.h"
#include "Actor/AVP_Actor_HUDHandler.h"
#include "Actor/AVP_Actor_InputHandler.h"
#include "Actor/AVP_Actor_InteractionHandler.h"
#include "Actor/AVP_Actor_PhysicsObjectHandler.h"
#include "Actor/AVP_Actor_PickupHandler.h"
#include "Actor/AVP_Actor_PlayerAnimStateHandler.h"
#include "Actor/AVP_Actor_Selector.h"
#include "Actor/Vertebrates/Human/AVP_Human_Description.h"
#include "Actor/Vertebrates/AVP_Vertebrate_Focus.h"
#include "Actor/Vertebrates/Predator/AVP_Predator_Recon.h"
#include "Animation/AVP_Animation_BodyRig.h"
#include "Animation/AVP_AnimFlags.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Core/AVP_GameStatus.h"
#include "Core/AVP_Interpolator.h"
#include "Core/AVP_Settings.h"
#include "Core/AVP_SurfaceEffects.h"
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Damage/AVP_EntityDamage_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_Actor_Messages.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_ServerActor_Human.h"
#include "Entity/PhysicalObject/Pickup/AVP_Pickup_Messages.h"
#include "Entity/PhysicalObject/Pickup/AVP_ClientEntity_Pickup.h"
#include "Entity/PhysicalObject/Pickup/AVP_Pickup_Ammo.h"
#include "Entity/PhysicalObject/Pickup/AVP_Pickup_Weapon.h"
#include "Entity/PhysicalObject/Pickup/AVP_Pickup_Health.h"
#include "Entity/PlayerInfo/AVP_ClientEntity_PlayerInfo.h"
#include "Entity/PlayerInfo/AVP_Player_AudioDiary.h"
#include "GUIMenu/AVP_GUIMenu_System.h"
#include "Input/AVP_Rumble_System.h"
#include "Inventory/AVP_AmmoStore.h"
#include "Inventory/AVP_Inventory_HashIDs.h"
#include "Inventory/AVP_Inventory_Item.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Inventory/Items/AVP_InventoryItem_PredatorCloak.h"
#include "Inventory/Items/AVP_InventoryItem_Weapon.h"
#include "Inventory/Items/AVP_InventoryItem_DynamicSubType.h"
#include "Inventory/Items/AVP_InventoryItem_VisionMode.h"
#include "Inventory/Groups/AVP_InventoryGroup_VisionModes.h"
#include "Inventory/Groups/AVP_InventoryGroup_Weapons.h"
#include "Mesh/AVP_PolygonFlags.h"
#include "Moves/AVP_Movement_System.h"
#include "Moves/AVP_Moves.h"
#include "Moves/AVP_Move_Interaction.h"
#include "Multiplayer/AVP_Multiplayer_GameMessageSystem.h"
#include "Network/AVP_Client.h"
#include "Network/AVP_Player.h"
#include "Physics/AVP_Collision_Descriptor.h"
#include "SpecialFX/AVP_CloakEffect.h"
#include "SpecialFX/AVP_GlowOutlines.h"

#ifdef ASURA_TOOLS
#include "Load&Save/Tools_SaveAsura.h"
#endif

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

#ifdef ASURA_USE_AFFECTORS
DEBUGVAR float	dbg_fHackedHeadAffectorRange	= 1.0f;
DEBUGVAR float	dbg_fHackedHeadAffectorStrength	= 30.0f;

DEBUGVAR float	dbg_fHackedCompassAffectorRange		= 2.0f;
DEBUGVAR float	dbg_fHackedCompassAffectorStrength	= 30.0f;
#endif	//ASURA_USE_AFFECTORS

DEBUGVAR float dbg_fPositionOffsetMagnitude = 0.1f;
DEBUGVAR float dbg_fOrientationOffsetAngle = Asura_Maths::DegToRad( 5.0f );

//DEBUGVAR float dbg_fBoxShrinkAmount = 0.05f;
DEBUGVAR float dbg_fFloorLOSDistance = 1.0f;

DEBUGVAR float dbg_fStartNextStateAnimTime = 0.3f;

DEBUGVAR float dbg_fExternalCamInitialDistance = 5.0f; 

static bool		g_bUpdateCamera						= true;

static bool		g_bRenderPlayerPosition				= false;
static bool		g_bRenderTargetingSphereDebugInfo	= false;
#ifndef ASURA_NO_DEBUG_OUTPUT
static bool		g_bRenderPosAndOri = false;
#endif
static const Asura_Collision_Box g_xDefaultCollBox( -0.3f, 0.3f, 0.0f, -1.8f, -0.3f, 0.3f );
static const Asura_Physics_Object::COLLISION_PROXY g_eDefaultCollProxy = Asura_Physics_Object::COLLISION_PROXY_OBB;

static const u_int				s_uNumPositionOffsets = 7;
static const Asura_Vector_3		s_axPositionOffset[ s_uNumPositionOffsets ] =
{
	Asura_Maths::ZeroVector3,
	Asura_Vector_3(  dbg_fPositionOffsetMagnitude,  0.0f,  0.0f ),
	Asura_Vector_3( -dbg_fPositionOffsetMagnitude,  0.0f,  0.0f ),
	Asura_Vector_3(  0.0f,  dbg_fPositionOffsetMagnitude,  0.0f ),
	Asura_Vector_3(  0.0f, -dbg_fPositionOffsetMagnitude,  0.0f ),
	Asura_Vector_3(  0.0f,  0.0f,  dbg_fPositionOffsetMagnitude ),
	Asura_Vector_3(  0.0f,  0.0f, -dbg_fPositionOffsetMagnitude ),
};

static const u_int				s_uNumOrientationOffsets = 3;
static const float				s_fCosOrientationOffsetAngle = Asura_Maths::Cosine( dbg_fOrientationOffsetAngle );
static const float				s_fSinOrientationOffsetAngle = Asura_Maths::Sine( dbg_fOrientationOffsetAngle );
static const Asura_Matrix_3x3	s_axOrientationOffset[ s_uNumOrientationOffsets ] =
{
	Asura_Matrix_3x3(	1.0f, 0.0f, 0.0f,	// Note: Asura_Maths::IdentityMatrix3x3 may not have been set up yet!
						0.0f, 1.0f, 0.0f,
						0.0f, 0.0f, 1.0f ),
	Asura_Matrix_3x3(  s_fCosOrientationOffsetAngle,  0.0f,  s_fSinOrientationOffsetAngle,
					   0.0f,                          1.0f,  0.0f,
					  -s_fSinOrientationOffsetAngle,  0.0f,  s_fCosOrientationOffsetAngle ),
	Asura_Matrix_3x3(  s_fCosOrientationOffsetAngle,  0.0f, -s_fSinOrientationOffsetAngle,
					   0.0f,                          1.0f,  0.0f,
					   s_fSinOrientationOffsetAngle,  0.0f,  s_fCosOrientationOffsetAngle ),
};

//
// AVP_ClientEntity_Actor
//

Asura_Hashed_List<AVP_ClientEntity_Actor> AVP_ClientEntity_Actor::s_xList;

AVP_ClientEntity_Actor::AVP_ClientEntity_Actor(const u_short usFlagsToIgnore)
: m_pxAnimationHandler( NULL )
, m_pxCameraHandler( NULL )
, m_pxHUDHandler( NULL )
, m_pxInputHandler( NULL )
, m_pxInteractionHandler(NULL)
, m_pxInventorySystem(NULL)
, m_pxPhysicsObjectHandler( NULL )
, m_pxPlayerStateHandler( NULL )
, m_pxPickupHandler( NULL )
, m_pxAttachmentHandler( NULL )
, m_pxMoveDirInterpolator( NULL )
, m_xAimDirection( Asura_Maths::ZeroVector3 )
, m_xLookAtPosition( Asura_Maths::ZeroVector3 )
, m_xMovementDirection( Asura_Maths::ZeroVector3 )
, m_uGuidOfActorBeingAttackedByUs( ASURA_GUID_UNREGISTERED )
, m_uCurrentAnim( 0 )
, m_uLoopData( 0 )
, m_bShouldTween( true )
, m_uMaxHealthBlocks( 5 )
, m_uAnimStateBlueprintType( ASURA_HASH_ID_UNSET ) 
, m_fEnergyPerBlock( 20.0f )
, m_fMinEnergy( 20.0f )
, m_fMaxEnergy( 100.0f )
, m_xLaserPosition( Asura_Maths::ZeroVector3 )
, m_xLaserDirection( Asura_Maths::ZeroVector3 )
, m_fLaserRange( 0.0f )
, m_bUsingLaser( false )
, m_fAimDirectionLimitAngle( Asura_Maths::Deg180 )
, m_fAimDirectionAngVel( Asura_Maths::Deg360 )
, m_fLookDirectionLimitAngle( Asura_Maths::Deg180 )
, m_fLookDirectionStoredAngle( Asura_Maths::Deg180 )
, m_fLookDirectionLerpTimeScale( 0.2f )
, m_fAnimDurationModifier( 1.0f )
, m_fAnimDurationModifierMoving( 1.0f )
, m_fTurnAnimAngVelScale( 1.0f )
, m_fMinDistanceForJumpLandLightEffect( 1.0f )
, m_fMinDistanceForJumpLandNormalEffect( 4.0f )
, m_fMinDistanceForJumpLandHeavyEffect( 20.0f )
, m_uActorFlags(0)
, m_usFlagsToIgnore(usFlagsToIgnore)
, m_uGrappleGuid(ASURA_GUID_UNREGISTERED)
, m_uGrappleAnimationHash(ASURA_HASH_ID_UNSET)
, m_uPlayerInfoGuid(ASURA_GUID_UNREGISTERED)
, m_uDeadAnimHashID(ASURA_GUID_UNREGISTERED)
, m_bAllowWeaponActivationAnim(true)
, m_uAIAnimState( AVP_Actor_AIAnimStateMachine::ACTOR_STATE_IDLE )
, m_uNumFeet( 1 )
, m_fCorpseRemoveTime(5.0f)
, m_fCorpseFadeTime(1.0f)
, m_fInnerTargetingRadius( 0.5f )
, m_fOuterTargetingRadius( 1.0f )
{
	m_eGroundType	= AVP_Actor_PhysicsObjectHandler::ON_FLOOR;
	Classification	= AVP_ENTITYCLASS_ACTOR;

	SetEntityAlwaysMoves( true );

	SetClientControlsShapeSkinAndAnim(true);

	// Do we need our own physics-objects for remote entities?
	if(!Asura_Network::IsServer())
	{
		// The server tells us where this player is...
		m_pxPhysicsObjectHandler	= new AVP_Actor_PhysicsObjectHandler(GetGuid(), AVP_Actor_PhysicsObjectHandler::REMOTE_CLIENT, g_xDefaultCollBox, g_eDefaultCollProxy, m_usFlagsToIgnore);
		m_pxDynamicObject			= m_pxPhysicsObjectHandler->GetPhysicsObject();
		m_pxDynamicObject->SetGravityScale(2.0f); // player actors generally feel better with a higher gravity scale
		m_pxDynamicObject->SetActorsDontPushMe(true);
		m_pxDynamicObject->SetActorDontPushActors(true);
		m_pxDynamicObject->SetPolyFlagsToReturnForActorCollision( AVP_PolygonFlag_IgnoreForReorientation );
	}

	m_pxAttachmentHandler = new AVP_Actor_AttachmentHandler(this);

	m_fDeathTimer = 0.0f;

#if defined(ASURA_USE_AFFECTORS)
	// ARTTODO - Replace this with an affector attached to the animation...
	m_iHackedHeadAffector 	= Asura_Affector_System::CreateAffector();
	m_iHackedCompassAffector 	= Asura_Affector_System::CreateAffector();
#endif
	
	SetBlueprintTypeHashID( 0x766161D2 );  //Asura_GetHashID("Actor Initialisation")

	s_xList.AddToList( this );
	Asura_Entity_Client::AddToUpdateableClientList(this);
}


AVP_ClientEntity_Actor::~AVP_ClientEntity_Actor( void ) 
{
#if defined(ASURA_USE_AFFECTORS)
	// ARTTODO - Replace this with an affector attached to the animation...
	Asura_Affector_System::DestroyAffector(m_iHackedHeadAffector);
	Asura_Affector_System::DestroyAffector(m_iHackedCompassAffector);
#endif

	delete(m_pxAnimationHandler);
	delete(m_pxCameraHandler);
	delete(m_pxHUDHandler);
	delete(m_pxInputHandler);
	delete(m_pxInteractionHandler);
	delete(m_pxPhysicsObjectHandler);
	delete(m_pxInventorySystem);
	delete(m_pxPlayerStateHandler);
	delete(m_pxPickupHandler);
	if (m_pxAttachmentHandler) delete(m_pxAttachmentHandler);

	s_xList.RemoveFromList(this);

	// Using second parameter as if the entity has been deactivated it will not -
	// be in the updateable client list and therefore don't want to know about the assert that will fire.
	Asura_Entity_Client::RemoveFromUpdateableClientList(this, false);
}

#if defined(ASURA_SUPPORT_COMMAND_CONSOLE)
void AVP_ClientEntity_Actor::CreateConsoleVars()
{
	Asura_CommandConsole::AddVar(L"AVP.Camera.Update", &g_bUpdateCamera, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );

	Asura_CommandConsole::AddVar(L"AVP.Player.ShowPosition", &g_bRenderPlayerPosition, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );

	Asura_CommandConsole::AddVar(L"AVP.Actor.RenderTargetingSphereDebugInfo", &g_bRenderTargetingSphereDebugInfo, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#ifndef ASURA_NO_DEBUG_OUTPUT
	Asura_CommandConsole::AddVar(L"AVP.Actor.RenderClientPosAndOri", &g_bRenderPosAndOri );
#endif
}
#endif	// defined(ASURA_SUPPORT_COMMAND_CONSOLE)


bool AVP_ClientEntity_Actor::IsPlayerMeleeTarget() const
{
	AVP_Player* pxAVPPlayer = AVP_Game_Core::GetPlayer();
	if(pxAVPPlayer)
	{
		AVP_ClientEntity_Actor* pxActor = AVP_ClientEntity_Actor::FindEntity(pxAVPPlayer->GetPuppetGuid());
		if(pxActor)
		{
			AVP_Vertebrate_Focus *pxFocus = pxActor->GetFocus();

			if (pxFocus && pxFocus->GetMeleeTarget() == GetGuid())
			{
				return true;
			}
		}
	}
	return false;
}


void AVP_ClientEntity_Actor::Update()
{
	// If the movement system is flagged for destruction, do it before it tries to be updated.
	if (m_bDestroyMovementSystem)
	{
		DestroyMovementSystem();
	}

	// Update our pheromone colour
	if (GetAnimation())
	{
		GetAnimation()->SetProjectData(&m_xAnimProjectData);
		Asura_RGBA xPheromoneColour =  m_xAnimProjectData.GetPheromoneColour();
		bool bGlowVisible = false;

		if (AVP_Settings::GetPlayerSpecies() == AVP_Settings::AVP_SPECIES_ALIEN && m_xAlienPheromoneColour.m_fA > 0.0f)
		{
			bGlowVisible = true;
			xPheromoneColour.m_fR = m_xAlienPheromoneColour.m_fR;
			xPheromoneColour.m_fG = m_xAlienPheromoneColour.m_fG;
			xPheromoneColour.m_fB = m_xAlienPheromoneColour.m_fB;
		} 
		else if (AVP_Settings::GetPlayerSpecies() == AVP_Settings::AVP_SPECIES_PREDATOR && 
			(IsPlayerMeleeTarget() || 
			((AVP_Predator_Recon::GetRecon().GetDistractionSoundPlaying() || AVP_Predator_Recon::GetRecon().GetReconIsActive()) && AVP_Predator_Recon::GetRecon().GetPreyGuid() == GetGuid())))
		{
			AVP_Player* pxAVPPlayer = AVP_Game_Core::GetPlayer();
			if(pxAVPPlayer)
			{
				AVP_ClientEntity_Actor* pxActor = AVP_ClientEntity_Actor::FindEntity(pxAVPPlayer->GetPuppetGuid());

				if (pxActor)
				{
					AVP_Inventory_System *pxSystem = pxActor->GetInventorySystem();

					if (pxSystem)
					{
						AVP_InventoryItem_VisionMode *pxItem = static_cast<AVP_InventoryItem_VisionMode*>(pxSystem->GetCurrentItem(uAVP_INVENTORY_GROUPHASH_VISIONMODE));

						if (pxItem)
						{
							xPheromoneColour.m_fR = pxItem->GetGlowColour().m_fR;
							xPheromoneColour.m_fG = pxItem->GetGlowColour().m_fG;
							xPheromoneColour.m_fB = pxItem->GetGlowColour().m_fB;
							bGlowVisible = true;
						}
					}
				}
			}
		}


		if (bGlowVisible && !AVP_Game_Core::IsLocalPlayer(Guid))
		{
			float fDelta = Asura_Timers::GetFrameTime() * Asura_Timers::GetGameTimeScale() * 2.0f;
			if (xPheromoneColour.m_fA < m_xAlienPheromoneColour.m_fA - fDelta)
			{
				xPheromoneColour.m_fA += fDelta;
			} else {
				xPheromoneColour.m_fA = m_xAlienPheromoneColour.m_fA;
			}
		}
		else {
			float fDelta = Asura_Timers::GetFrameTime() * Asura_Timers::GetGameTimeScale() * 2.0f;
			if (xPheromoneColour.m_fA > fDelta)
			{
				xPheromoneColour.m_fA -= fDelta;
			} else {
				xPheromoneColour.m_fA = 0.0f;
			}
		}
		m_xAnimProjectData.SetPheromoneColour(xPheromoneColour);
	}

	PARENT::Update();

	UpdateAimDirection();
	UpdateLookAtPosition();
	UpdateMovement();

	// CLEAN UP THE CORPSE IF IT HAS BEEN AROUND TOO LONG
	if (IsDead())
	{
		m_fDeathTimer += Asura_Timers::GetGameTimeSlice();
	}

	if(m_pxInventorySystem != NULL)
	{
		m_pxInventorySystem->Update();
	}
	// Are we on the ground?
	if( AVP_Game_Core::IsLocalPlayer( GetGuid() ) && m_pxPhysicsObjectHandler != NULL)
	{
		m_eGroundType = m_pxPhysicsObjectHandler->PerformOnGroundCheck();
	}

	// Update the handlers...
	if ( m_pxAnimationHandler )
	{
		m_pxAnimationHandler->Update();
	}

	if(m_pxInteractionHandler != NULL)
	{
		m_pxInteractionHandler->Update();
	}

	if(m_pxHUDHandler != NULL)
	{
		// AJG - 04/12/07 - temporary measure to ensure we don't reopen the HUD after death. We'll need something more complex as presumably eventually there'll be some kind of post-death HUD stuff.
		if (!IsDead())
		{
			m_pxHUDHandler->Update( *this );
		}
	}
	if ( !IsDead() && m_pxPlayerStateHandler != NULL )
	{
		m_pxPlayerStateHandler->Update();
	}

	if(m_pxPickupHandler)
	{
		m_pxPickupHandler->UpdateClient();
	}

	UpdateFloorFlags();
	UpdateActions();

#if defined(ASURA_USE_AFFECTORS)
	// ARTTODO - Replace this with an affector attached to the animation...
	Asura_Affector*	pxHeadAffector		= Asura_Affector_System::GetAffectorPointer(m_iHackedHeadAffector);
	Asura_Affector*	pxCompassAffector	= Asura_Affector_System::GetAffectorPointer(m_iHackedCompassAffector);

	if(pxHeadAffector != NULL)
	{
		Asura_Vector_3	xPosition;

		GetHeadPosition(xPosition);

		pxHeadAffector->SetAsActive(true);
		pxHeadAffector->SetRange(dbg_fHackedHeadAffectorRange);
		pxHeadAffector->SetStrength(dbg_fHackedHeadAffectorStrength);
		pxHeadAffector->SetPosition(xPosition);
	}

	if(pxCompassAffector != NULL)
	{
		const Asura_Vector_3	xPosition(GetCurrentPosition());

		pxCompassAffector->SetAsActive(true);
		pxCompassAffector->SetRange(dbg_fHackedCompassAffectorRange);
		pxCompassAffector->SetStrength(dbg_fHackedCompassAffectorStrength);
		pxCompassAffector->SetPosition(xPosition);
	}
#endif //defined(ASURA_USE_AFFECTORS)

	// Set player actor invisible during cutscenes
	if(IsPlayer())
	{
		if(Asura_Render::GetCutsceneHasCameraControl())
		{
			if(!GetInvisibleByCutscene())
			{
				SetInvisibleByCutscene( true );
			}

			if(m_pxHUDHandler != NULL)
			{
				m_pxHUDHandler->SetHUDEnabled(false);
			}

			if(m_pxMovementSystem)
			{
				m_pxMovementSystem->AbortCurrentMove();
			}
		}
		else
		{
			if(GetInvisibleByCutscene())
			{
				SetInvisibleByCutscene( false );
			}

			if( !IsDead() && m_pxHUDHandler != NULL && !AVP_GameStatus::GetLevelComplete() )
			{
				m_pxHUDHandler->SetHUDEnabled(true);
			}
		}
	}

#ifndef ASURA_NO_DEBUG_OUTPUT
	if ( g_bRenderPosAndOri )
	{
		Asura_DebugLineRender::AddAxis( GetCurrentPosition(), GetCurrentOrientation(), 0.5f, 1.0f );
	}
#endif
}

void AVP_ClientEntity_Actor::Render(void)
{
	bool bIgnoreModLinking = false;
	if ( m_xAnimProjectData.GetPheromoneColour().m_fA > 0.0f )
	{
		if ( AVP_Game_Core::GetPlayer() )
		{
			AVP_Settings::Species eLocalPlayerSpecies = AVP_Game_Core::GetPlayer()->GetPuppetSpecies();
			if ( eLocalPlayerSpecies == AVP_Settings::AVP_SPECIES_ALIEN )
			{
				if ( Asura_Camera::GetPosition() )
				{
					if ( ( GetCurrentPosition() - *Asura_Camera::GetPosition() ).Magnitude() < AVP_GlowOutlines::GetMaxRenderDistance() )
					{
						bIgnoreModLinking = true;
					}
				}
			}
		}
	}

	if ( bIgnoreModLinking && GetShapeInstance().GetAnimation() )
	{
		GetShapeInstance().AddFlags( ASURA_SHAPE_INSTANCE_IGNORE_MOD_LINKING );
		if ( Asura_Camera::IsSphereInFrustum( GetShapeInstance().GetAnimation()->GetBoundingSphereCentre(), GetShapeInstance().GetAnimation()->GetCoarseBoundingSphereIncludingEffectsRadius() ) )
		{
			ForceBoneUpdate();
		}
	}
	else
	{
		GetShapeInstance().RemoveFlags( ASURA_SHAPE_INSTANCE_IGNORE_MOD_LINKING );
	}


	if(m_pxAnimationHandler != NULL)
	{
		m_pxAnimationHandler->Render();
	}
	PARENT::Render();
#ifdef ASURA_USE_DEBUG_TEXT
	if(m_pxAnimationHandler != NULL)
	{
		m_pxAnimationHandler->RenderDebugInfo();
	}
#endif
}

bool AVP_ClientEntity_Actor::HandleMessage( const Asura_Message* pxMessage )
{
	switch ( pxMessage->GetMsgID() )
	{
		case ASURA_MSG_CLIENTCREATE:
		{
			Asura_Chunk_Memory_Stream xStream;
			xStream.Write(pxMessage->GetDataSize(), pxMessage->GetData());
#ifdef ASURA_ASSERT
			u_int uNumBytesWritten = xStream.GetPosition();
#endif
			xStream.SetPosition(0);
			ReadFromClientCreateStream(xStream, pxMessage->GetSentTime());

			Asura_Assertf( uNumBytesWritten == xStream.GetPosition(), ( "Failed to read all data in client creation chunk for entity %d, %d bytes unread, this is bad, call a coder", GetGuid(), uNumBytesWritten - xStream.GetPosition() ) );

			return(true);
		}

	    case ASURA_MSG_CLIENTUPDATE:
		{
			const AVP_Message_Actor_ClientUpdate* pxData = static_cast< const AVP_Message_Actor_ClientUpdate* >( pxMessage->GetData() );

			// If we are inactive and now receiving a client update this means we are becoming active.
			// We should go visible and teleport to this first pos/ori so we don't zip across the level.
			if(!IsEntityActive())
			{
				Asura_Matrix_3x3 xOrientMatrix;
				pxData->m_xOrientation.ToMatrix( xOrientMatrix );
				TeleportTo( pxData->m_xPosition, xOrientMatrix );
			}

			ProcessClientUpdate( pxData, pxMessage->GetSentTime() );
			return true;
		}

	    case ASURA_MSG_AXON_GS2_ANIM_SEQUENCE_DATA:
		{
			const Axon_Gamescene_AnimSequenceData* pxData = static_cast< const Axon_Gamescene_AnimSequenceData* >( pxMessage->GetData() );
			if ( m_pxAnimationHandler ) { m_pxAnimationHandler->HandleGamesceneAnimSequenceData( *pxData ); }
			return true;
		}

    	case AVP_MSGID_REQUEST_ANIMATION:
		{
			const AVP_Message_Actor_RequestAnimation* pxData = static_cast< const AVP_Message_Actor_RequestAnimation* >( pxMessage->GetData() );
			if ( m_pxAnimationHandler ) { m_pxAnimationHandler->HandleAnimationRequest( *pxData ); }
			return true;
		}

		case ASURA_MSG_MOVECHAINCOMPLETE:
		{
			return true; // nothing to do on clients for now
		}

		case AVP_MSGID_ITEM_BEGIN_IDLING:
		case AVP_MSGID_ITEM_BEGIN_USING:
		case AVP_MSGID_ITEM_REQUEST_END_USAGE:
		{
			if(m_pxInventorySystem != NULL)
			{
				m_pxInventorySystem->HandleMessage(pxMessage);
			}
			return(true);
		}

		case AVP_MSGID_DISMEMBER:
		{
			// At this level, just kill the entity
			const AVP_Msg_Dismember* pxData = static_cast<const AVP_Msg_Dismember*>(pxMessage->GetData());
			if (pxData)
			{
				const bool bAlwaysFatal = pxData->GetDamage() < 0.0f;
				if (bAlwaysFatal && !IsDead()) // Everything is handled by the normal damage code if damage is positive.
				{
					UpdateHealth(0.0f, pxMessage->GetSentTime());
					if (IsDead())
					{
						AVP_EntityDamage_ApplyToEntity xDamage(uAVP_BLUEPRINT_NAME_DAMAGE_DISMEMBER, pxData->GetFromGuid(), pxData->GetDamagePosition(), pxData->GetRagdollDirection(), -pxData->GetRagdollDirection(), Guid, 0.0f, Asura_Maths::LargeFloat, pxData->GetRagdollImpulse());
						OnDeath(xDamage);
					}
				}
			}

			return true;
		}

		case AVP_MSGID_COLLECT_PICKUP:
		{
			if(m_pxPickupHandler)
			{
				const AVP_Message_Pickup_Collect* const  pxData = static_cast<const AVP_Message_Pickup_Collect* const>(pxMessage->GetData());
				if (pxData)
				{
					Asura_Guid	  uPickupGuid;
					Asura_Hash_ID uPickupBlueprintHash;
					int           iPickupQuantity;
					int           iSecondaryPickupQuantity;
					bool          bSetAsCurrent;

					pxData->ExtractInfo(uPickupGuid, uPickupBlueprintHash, iPickupQuantity, bSetAsCurrent, &iSecondaryPickupQuantity);
					
					// Do something with the info; construct pickup, put into inventory...
					
					AVP_ClientEntity_Pickup* pxPickup = AVP_ClientEntity_Pickup::FindEntity(uPickupGuid);
					if(pxPickup)
					{
						OnPickup(pxPickup, static_cast<float>(iPickupQuantity), static_cast<float>(iSecondaryPickupQuantity), bSetAsCurrent);
					}
				}	
			}
			else
			{
				Asura_Assert(false, "Shouldn't receive a collect pickup message since this actor doesn't have a pickup handler!");
			}
			return true;
		}

		case AVP_MSGID_RESTORE_PREVIOUS_WEAPON:
		{
			RestorePreviousWeapon();
			return true;
		}

		case AVP_MSGID_INVENTORY_ITEM_RELOAD:
		{
			return(GetInventorySystem()->HandleMessage(pxMessage));
		}

		case AVP_MSGID_ATTACH_WEAPON:
		case AVP_MSGID_DETACH_WEAPON:
		{
			if(m_pxInventorySystem != NULL)
			{
				return(m_pxInventorySystem->HandleMessage(pxMessage));
			}
		}

		case AVP_MSGID_CREATE_ATTACHMENT:
		case AVP_MSGID_DESTROY_ATTACHMENT:
		case AVP_MSGID_DETACH_ATTACHMENT:
		{
			if(m_pxAttachmentHandler != NULL)
			{
				return(m_pxAttachmentHandler->HandleMessage(pxMessage));
			}
		}

		case AVP_MSGID_REQUEST_TURN:
		{
			if ( m_pxAnimationHandler && m_pxAnimationHandler->GetBodyRig() )
			{
				const AVP_Message_Actor_RequestTurn* const pxData = static_cast< const AVP_Message_Actor_RequestTurn* const >( pxMessage->GetData() );
				if ( pxData )
				{
					m_pxAnimationHandler->GetBodyRig()->RequestTurn( pxData->GetTurnAngle() );
				}
			}
		}
		return true;

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

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::OnPickup
// Author       : Chris Wallace
// Date         : 12-Sept-2008
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientEntity_Actor::OnPickup(AVP_ClientEntity_Pickup* pxPickup, float fQuantity, float fSecondaryQuantity, bool bSetAsCurrent /* defaults to true */)
{
	if(pxPickup)
	{
		const AVP_Pickup* pxPickupObject = pxPickup->GetPickupObject();
		if(pxPickupObject)
		{
			pxPickupObject->PlayPickupSound(GetCurrentPosition());

			switch(pxPickupObject->GetGroupHash())
			{
				case uAVP_INVENTORY_GROUPHASH_AMMO:
				{
					const AVP_Pickup_Ammo* pxAmmoPickup = static_cast<const AVP_Pickup_Ammo*>(pxPickupObject);

					AVP_AmmoStore* pxAmmoStore = GetInventorySystem()->GetAmmoStore();
					if(pxAmmoStore)
					{
						pxAmmoStore->AddAmmoQuantity(fQuantity, pxAmmoPickup->GetAmmoType());

						// Set ammo HUD state
						if( AVP_Game_Core::IsLocalPlayer( GetGuid() ) )
						{
							AVP_Actor_HUDHandler::SetHUDAmmoState( AVP_Actor_HUDHandler::HUD_STAT_GAIN );
						}
					}
					break;
				}	
				case uAVP_INVENTORY_GROUPHASH_HEALTH:
				{
					AVP_Inventory_System* pxInventory = GetInventorySystem();
					if(pxInventory)
					{
						AVP_Inventory_Group* pxGroup = pxInventory->GetPermanentlyActiveItemGroup();
						if(pxGroup)
						{
							AVP_Inventory_Item* pxItem = pxGroup->GetItem(uAVP_INVENTORY_ITEMHASH_STIM);
							if(pxItem)
							{
								if(pxItem->GetSubType(0)) 
								{
									pxItem->GetSubType(0)->IncreaseQuantity(1.0f);
								}
							}
						}
					}

					break;
				}	
				case uAVP_INVENTORY_GROUPHASH_WEAPON:
				{
					const AVP_Pickup_Weapon* pxWeaponPickup = static_cast<const AVP_Pickup_Weapon*>(pxPickupObject);
					const AVP_Inventory_System* pxInventory = GetInventorySystem();
					if(pxInventory)
					{
						AVP_Inventory_Group* pxGroup = pxInventory->GetGroup(uAVP_INVENTORY_GROUPHASH_WEAPON);
						if(pxGroup)
						{
							// If we don't have this weapon, pick it up, otherwise, steal the ammo
							AVP_Inventory_Item* pxItem = pxGroup->FindItemByBlueprintHash(pxWeaponPickup->GetWeaponType());
							if(pxItem)
							{
								AVP_AmmoStore* pxAmmoStore = GetInventorySystem()->GetAmmoStore();
								if(pxAmmoStore)
								{
									if(pxItem->GetNumSubTypes()>0)
									{
										pxAmmoStore->AddAmmoQuantity(fQuantity, pxItem->GetSubType(0)->GetAmmoType());
										
										if(pxItem->GetNumSubTypes()>1)
										{
											pxAmmoStore->AddAmmoQuantity(fSecondaryQuantity, pxItem->GetSubType(1)->GetAmmoType());
										}

										// Set ammo HUD state
										if( AVP_Game_Core::IsLocalPlayer( GetGuid() ) )
										{
											AVP_Actor_HUDHandler::SetHUDAmmoState( AVP_Actor_HUDHandler::HUD_STAT_GAIN );
										}
									}
								}
							}
							else
							{
								if ( !bSetAsCurrent )
								{
									// this doesn't swap the current item
									pxGroup->AddPickup(pxWeaponPickup->GetWeaponType(), fQuantity, fSecondaryQuantity);
								}
								else
								{
									pxGroup->SwapCurrentItemForPickup(pxWeaponPickup->GetWeaponType(), fQuantity, fSecondaryQuantity);
								}				
								
								// Set ammo HUD state
								if( AVP_Game_Core::IsLocalPlayer( GetGuid() ) )
								{
									AVP_Actor_HUDHandler::SetHUDAmmoState( AVP_Actor_HUDHandler::HUD_PICKUP_WEAPON );
									AVP_Actor_HUDHandler::SetHUDEnergyState( AVP_Actor_HUDHandler::HUD_PICKUP_WEAPON );
								}

								// if this is a battery, reset aim direction to look forward to hide the battery being teleported to the player's hands
								if ( pxPickup->GetPickupBlueprintHash() == uAVP_BLUEPRINT_PARAM_BATTERY )
								{
									SetAimDirectionUsage( AVP_ClientEntity_Actor::AIM_DIRECTION_RESET);
									if ( GetPickupHandler() )
									{
										GetPickupHandler()->SetOverrideAimDirectionUsage( true );
									}
								}
							}
						}
					}
					break;
				}	
				case uAVP_INVENTORY_GROUPHASH_AUDIODIARY:
				{
					// This is not used in multiplayer, no audio diary should be present in multiplayer
					if ( AVP_Settings::IsSinglePlayer() )
					{
						AVP_ClientEntity_PlayerInfo* pxPlayerInfo = AVP_ClientEntity_PlayerInfo::FindEntity( GetPlayerInfoGuid() ); 
						if ( pxPlayerInfo )
						{
							AVP_Player_AudioDiary xAudioDiary;
							xAudioDiary.SetDialogBlueprintHashID( pxPickup->GetDialogueBlueprintHashID() );
							pxPlayerInfo->GetAudioDiariesList().Push_Back( xAudioDiary );

							// Show audio diary picked on the player HUD
							AVP_Actor_HUDHandler::SetShowAudioDiaryPicked();
						}
					}
					else
					{
						Asura_Assert(false, "Audio diaries should not be placed in multiplayer levels!");
					}
					break;
				}
				default: break; // What have we picked up?!
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::RestorePreviousWeapon
// Author       : AJK
// Date         : 28-11-2008
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientEntity_Actor::RestorePreviousWeapon() const
{
	const AVP_Inventory_System* pxInventory = GetInventorySystem();
	if(pxInventory)
	{
		AVP_Inventory_Group* pxGroup = pxInventory->GetGroup(uAVP_INVENTORY_GROUPHASH_WEAPON);
		if(pxGroup)
		{
			if(pxGroup->GetPreviousItemBlueprintHash() != ASURA_HASH_ID_UNSET)
			{
				pxGroup->SwapCurrentItemForPickup(pxGroup->GetPreviousItemBlueprintHash(), pxGroup->GetPreviousItemQuantity(), pxGroup->GetPreviousItemSecondaryQuantity());
			}
			else
			{
				Asura_Assert(false, "Previous item must been set!");
			}
		}
	}
}


bool AVP_ClientEntity_Actor::IsRagdoll() const
{
	if (m_pxAnimationHandler)
	{
		return m_pxAnimationHandler->IsRagdoll();
	}

	return false;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::TeleportTo
// Author       : Andy Tate
// Date         : 2-Mar-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientEntity_Actor::TeleportTo(const Asura_Vector_3& xPosition,
										const Asura_Matrix_3x3& xOrientation)
{
	PARENT::TeleportTo(xPosition, xOrientation);
	
	if(m_pxPhysicsObjectHandler != NULL)
	{
		m_pxPhysicsObjectHandler->ResetUsingWorldSpaceOrientation(xOrientation);
	}

	// Reset the necessary handlers...
	if(m_pxCameraHandler != NULL)
	{
		m_pxCameraHandler->Reset();
	}

	if(m_pxInputHandler != NULL)
	{
		m_pxInputHandler->Reset();
	}
}

/// End of function 'AVP_ClientEntity_Actor::TeleportTo'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::RenderHUD
// Author       : Andy Tate
// Date         : 4-Oct-2006
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientEntity_Actor::RenderHUD(void) const
{
	if((m_pxHUDHandler != NULL) && m_pxHUDHandler->IsHUDEnabled())
	{
		m_pxHUDHandler->Render( *this );
	}

#if defined(AVP_ALLOW_DEBUG_MOVEMENT)
	if(m_pxInputHandler != NULL)
	{
		m_pxInputHandler->RenderDebugInfo();
	}
#endif	// defined(AVP_ALLOW_DEBUG_MOVEMENT)

	if(m_pxInventorySystem != NULL)
	{
		m_pxInventorySystem->Render();
	}

	if (g_bRenderPlayerPosition && IsPlayer() && AVP_Game_Core::IsLocalPlayer(Guid))
	{
		RenderPlayerPosition();
	}
}

/// End of function 'AVP_ClientEntity_Actor::RenderHUD'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::SetCamera
// Author       : Andy Tate
// Date         : 4-Oct-2006
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientEntity_Actor::SetCamera(void)
{
	if (m_bIsPlayer)
	{
		if(m_pxCameraHandler != NULL)
		{
			if(g_bUpdateCamera)
			{
				m_pxCameraHandler->Update(*this);
			}
			m_pxCameraHandler->SetAsuraCamera(*this);
		}
	}
}

/// End of function 'AVP_ClientEntity_Actor::SetCamera'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::UpdateInput
// Author       : Andy Tate
// Date         : 4-Oct-2006
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientEntity_Actor::UpdateInput(void)
{
	if(m_pxInputHandler != NULL)
	{
		Asura_Message*	pxMessage	= m_pxInputHandler->Update( *this );

		// Do we have a message to end to the server?
		if(pxMessage != NULL)
		{
			Send(pxMessage, Guid);
		}
	}
}

/// End of function 'AVP_ClientEntity_Actor::UpdateInput'. ///




void AVP_ClientEntity_Actor::ReadFromClientCreateStream( Asura_Chunk_Memory_Stream& xStream, float fServerTime )
{
	u_int uVersion;
	xStream >> uVersion;

	if ( uVersion == 0 )
	{
		// No parent call, use the update data...
		AVP_Message_PhysicalObject_ClientUpdate xPhysicalObjectClientUpdate;
		xStream.Read(sizeof(AVP_Message_PhysicalObject_ClientUpdate), &xPhysicalObjectClientUpdate);
		PARENT::ProcessClientUpdate(&xPhysicalObjectClientUpdate, fServerTime);
	}
	else if ( uVersion == 1 )
	{
		// No parent call, use the update data...
		AVP_Message_PhysicalObject_ClientCreate xPhysicalObjectClientCreate;
		xStream.Read(sizeof(AVP_Message_PhysicalObject_ClientCreate), &xPhysicalObjectClientCreate);
		PARENT::ProcessClientCreate(&xPhysicalObjectClientCreate, fServerTime);
	}
	else
	{
		Asura_Assert(false, "Bad creation chunk!!!!");
	}

	// Handle player specific code...
	bool bIsPlayer = false;
	Asura_Guid uInfoGuid = ASURA_GUID_SERVER;
	u_char uGroundType = 0;

	Asura_Vector_3 xAnimDirection;
	Asura_Vector_3 xLookAtPosition;
	Asura_Vector_3 xMovementDirection;

	Axon_Gamescene_AnimSequenceData xAnimData;

	Asura_Hash_ID uCurrentAnim = ASURA_HASH_ID_UNSET;
	u_int uLoopData = 0;
	bool bShouldTween = false;

	float fAIAnimTweenTime = 0.f;
	bool bPlayAnimFromStart = true;

	u_int uActorState;

	u_int uAIAnimState = 0;
	u_int uAIAnimStateAfterMove = 0;
	float fTimeInAIAnimState = 0.f;
	bool bAIAnimStateLooped = false;

	bool bIsMoving;
	bool bIsMovingSlow;
	bool bIsMovingFast;

	bool bIsDestructible;
	float fPlayerSpeed;
	float fMoveAngle;
	bool  bDeadBody;

	xStream >> xAnimDirection;
	xStream >> xLookAtPosition;
	xStream >> xMovementDirection;

	xStream.Read(sizeof(Axon_Gamescene_AnimSequenceData), &xAnimData);

	xStream >> uCurrentAnim;
	xStream >> uLoopData;
	xStream >> bShouldTween;

	xStream >> fAIAnimTweenTime;
	xStream >> bPlayAnimFromStart;

	xStream >> uActorState;

	xStream >> uAIAnimState; 
	xStream >> uAIAnimStateAfterMove; 
	xStream >> fTimeInAIAnimState; 
	xStream >> bAIAnimStateLooped; 

	// whether we are a player or not we need to store frequently who we are attacking
	xStream >> m_uGuidOfActorBeingAttackedByUs;
	
	xStream >> uInfoGuid;
	xStream >> uGroundType;
	xStream >> bIsPlayer;
	xStream >> bDeadBody;
	xStream >> m_uDeadAnimHashID;

	xStream >> bIsMoving;
	xStream >> bIsMovingSlow;
	xStream >> bIsMovingFast;
	xStream >> bIsDestructible;

	xStream >> fPlayerSpeed;
	xStream >> fMoveAngle;
	
	m_bIsDestructible = bIsDestructible;

	if (bDeadBody)
	{
		m_bDeadBody = true;
	}

	if(bIsPlayer)
	{
		if(!IsPlayer()) // If the server says we are a player but we are not, make us one
		{
			MakePlayer(uInfoGuid);
		}

		if ( !m_pxAnimationHandler )
		{
			CreateAnimationHandler();
		}

		if(!AVP_Game_Core::IsLocalPlayer(GetGuid()))
		{
			m_eGroundType = static_cast<AVP_Actor_PhysicsObjectHandler::GroundType>(uGroundType);
		}
	}
	else
	{
		if(!IsAIActor())
		{
			MakeAI();
		}

		if ( GetBlueprintHashID() != ASURA_HASH_ID_UNSET )
		{
			if ( !m_pxAnimationHandler  )
			{
				CreateAnimationHandler();
			}

 			if ( m_pxAnimationHandler )
 			{
 				m_pxAnimationHandler->HandleGamesceneAnimSequenceData( xAnimData );
 			}
		}
	}

	// Handle the anim request data.
	//## This can go if the AI are only ever going to use the animation state machine and never request anims directly.
	
	if ( m_uCurrentAnim != uCurrentAnim || m_uLoopData != uLoopData || m_bShouldTween != bShouldTween )
	{
		m_uCurrentAnim = uCurrentAnim;
		m_uLoopData    = uLoopData;
		m_bShouldTween = bShouldTween;

		//## To fit in with the old stuff we make up a request message here and handle it. This can be changed once we're culling the old stuff.
		AVP_Message_Actor_RequestAnimation xRAMsg;
		xRAMsg.m_uAnimation   = m_uCurrentAnim;
		xRAMsg.m_eLoopStatus  = static_cast< AVP_Message_Actor_RequestAnimation::LoopStatus >( m_uLoopData );
		xRAMsg.m_bShouldTween = m_bShouldTween;
		if ( m_pxAnimationHandler )
		{
			m_pxAnimationHandler->HandleAnimationRequest( xRAMsg );
		}
	}

	// Ensure that we have an inventory...
	if(m_pxInventorySystem == NULL)
	{
		InitialiseInventory();
		InitialiseMoves();
		InitialiseShapePositionAndOrientation();
		InitialiseSoundEvents();

		// Ensure that the default inventory items are active...
		if(m_pxInventorySystem->IsMaster())
		{
			AVP_Inventory_Group*	pxVisionModeGroup	= m_pxInventorySystem->GetGroup(uAVP_INVENTORY_GROUPHASH_VISIONMODE);
			AVP_Inventory_Group*	pxWeaponGroup		= m_pxInventorySystem->GetGroup(uAVP_INVENTORY_GROUPHASH_WEAPON);

			if(pxVisionModeGroup != NULL)
			{
				pxVisionModeGroup->SetCurrentItemToDefaultItem(true);
			}
			if(pxWeaponGroup != NULL)
			{
				pxWeaponGroup->SetCurrentItemToDefaultItem(true);
			}
		}
	}


	if ( !AVP_Game_Core::IsLocalPlayer( GetGuid() ) )
	{
		SetAimDirection( Asura_AVector3( xAnimDirection ), Asura_Timers::GetFrameTime() );
		SetLookAtPosition( Asura_AVector3( xLookAtPosition ), Asura_Timers::GetFrameTime() );
		SetMovementDirection( Asura_AVector3( xMovementDirection ), Asura_Timers::GetFrameTime() );

		if ( IsPlayer() ) // remote player
		{
			if ( GetPlayerStateHandler() )
			{
				GetPlayerStateHandler()->UpdateMovementParametrics( fPlayerSpeed, fMoveAngle );
			}
		}
	}

	if ( !IsPlayer() )
	{
		SetMoving( bIsMoving );
		SetMovingSlow( bIsMovingSlow );
		SetMovingFast( bIsMovingFast );

		Asura_AVector3 xMoveDirA;
		GetMovementDirection( xMoveDirA );
		Asura_Vector_3 xMoveDir( xMoveDirA );
		xMoveDir = GetCurrentOrientation().ApplyTranspose( xMoveDir );
		float fOSMoveDirAngle = Asura_Maths::ArcTangent( xMoveDir.GetX(), xMoveDir.GetZ() );

		float fTweenTime;
		bool bSyncNextAnim;
		bool bPauseNextAnimState = false;
		const AVP_Actor_AnimStateData* pxAnimData;
		if ( ( uAIAnimState == AVP_Actor_AIAnimStateMachine::ACTOR_STATE_IN_GAMESCENE || uAIAnimState == AVP_Actor_AIAnimStateMachine::ACTOR_STATE_IN_MOVE ) && ( fTimeInAIAnimState > dbg_fStartNextStateAnimTime ) )
		{
			pxAnimData = GetStateData( uAIAnimStateAfterMove, uGroundType, 0, false, 0 );
			fTweenTime = 0.2f;
			bSyncNextAnim = m_bPlayingNextAnim;
			bPauseNextAnimState = true;
			m_bPlayingNextAnim = true;
		}
		else
		{
			m_bPlayingNextAnim = false;
			pxAnimData = GetStateData( uAIAnimState,  uGroundType, 0, false, 0 );
			fTweenTime = fAIAnimTweenTime;
			bSyncNextAnim = bPlayAnimFromStart;
		}

		if ( pxAnimData )
		{
			UpdateAnimStateData( pxAnimData, NULL, pxAnimData, NULL, bAIAnimStateLooped, 0.0f, 0.0f, fOSMoveDirAngle, fTweenTime, bSyncNextAnim );
			if ( bPauseNextAnimState )
			{
				PauseAnimState();
			}
			else
			{
				PlayAnimState();
			}
		}
	}

	// If we're being updated we should be active. As long as we're not dead then we should activate our physics object (if it exists).
	// TODO - this may need more complex handling, especially if we can have death anims that don't immediately ragdoll us.
	if (m_pxDynamicObject)
	{
		// TODO - I don't think actors/physical objects handle deactivation yet (07/11/07), so only death or reset can turn us off.
		m_pxDynamicObject->SetIgnore(false);
	}

	// Inventory
	AVP_Inventory_System* pxSystem = GetInventorySystem();
	Asura_Assert( pxSystem, "We REALLY need to have an inventory system to read into as otherwise the chunk data will be wrong" );
	if(pxSystem)
	{
		pxSystem->ReadFromChunkStream(xStream);
	}
}

void AVP_ClientEntity_Actor::ProcessClientUpdate(const AVP_Message_Actor_ClientUpdate* pxData, float fServerTime, bool bProcessPosAndOri /*= true*/)
{
#if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	bool bAllowPosAndOrientationProcessing = bProcessPosAndOri && (m_pxPosAndOriInterpolator != NULL);
#else
	bool bAllowPosAndOrientationProcessing = bProcessPosAndOri;
#endif // #if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)

#if 0	//!defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	// Update the interpolator...
	if(m_pxPosAndOriInterpolator != NULL)
	{
		const float	fTimeIncrement	= 1.0f / Asura_Timers::GetSmoothedFrameRate();

		m_pxPosAndOriInterpolator->SetProcessNewDataIncrement(fTimeIncrement);
	}
#endif	// !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)


	m_uDeadAnimHashID = pxData->GetDeadAnimHash();
	m_bDeadBody       = pxData->IsADeadBody();

	if (!m_bDeadBody)
	{
		const AVP_Move* pxCurrentMove = (m_pxMovementSystem == NULL) ? NULL : static_cast< const AVP_Move*> (m_pxMovementSystem->GetCurrentMove());
		if ( pxCurrentMove && (pxCurrentMove->GetPositionUsage() == Asura_Move::POSITION_SET_DIRECTLY) )
		{
			bAllowPosAndOrientationProcessing = false;
		}
	}

	const bool bUseClientPosOriTimestamp = pxData->IsClientPosAndOriTimeValid();
	const bool bNormalPosOriProcessing = bAllowPosAndOrientationProcessing && !bUseClientPosOriTimestamp;

	// Call the base-class...
	PARENT::ProcessClientUpdate(pxData, fServerTime, bNormalPosOriProcessing);
	
	if (bUseClientPosOriTimestamp && bAllowPosAndOrientationProcessing)
	{
		// Process the client position and orientation using the client timestamp rather than the server timestamp.
		// This should prevent juddering of other remote client players on remote client machines.
		Asura_Assert(pxData->IsPlayer(), "Only expecting client pos/ori on player-controlled entities");
		ProcessNewPositionAndOrientationData(pxData->m_xPosition, pxData->m_xOrientation, pxData->GetClientPosAndOriTime());
	}

	if (!m_bDeadBody)
	{
		// Handle player specific code...
		if(pxData->IsPlayer())
		{
			if(!IsPlayer()) // If the server says we are a player but we are not, make us one
			{
				MakePlayer(pxData->GetPlayerInfoGuid());
			}

			if(!AVP_Game_Core::IsLocalPlayer(GetGuid()))
			{
				m_eGroundType = static_cast<AVP_Actor_PhysicsObjectHandler::GroundType>(pxData->GetGroundType());
			}
		}
		else
		{
			if(!IsAIActor())
			{
				MakeAI();
			}

			// Update the animation sequences...
			if ( m_pxAnimationHandler )
			{
				m_pxAnimationHandler->HandleGamesceneAnimSequenceData( pxData->GetAnimSequenceData() );
			}
		}

		// whether we are a player or not we need to store frequently who we are attacking
		m_uGuidOfActorBeingAttackedByUs = pxData->GetActorGuidImAttacking();

		// Handle the anim request data.
		//## This can go if the AI are only ever going to use the animation state machine and never request anims directly.
		if ( m_uCurrentAnim != pxData->GetCurrentAnim() || m_uLoopData != pxData->GetLoopData() || m_bShouldTween != pxData->ShouldTween() )
		{
			m_uCurrentAnim = pxData->GetCurrentAnim();
			m_uLoopData    = pxData->GetLoopData();
			m_bShouldTween = pxData->ShouldTween();

			//## To fit in with the old stuff we make up a request message here and handle it. This can be changed once we're culling the old stuff.
			AVP_Message_Actor_RequestAnimation xRAMsg;
			xRAMsg.m_uAnimation   = m_uCurrentAnim;
			xRAMsg.m_eLoopStatus  = static_cast< AVP_Message_Actor_RequestAnimation::LoopStatus >( m_uLoopData );
			xRAMsg.m_bShouldTween = m_bShouldTween;
			if ( m_pxAnimationHandler )
			{
				m_pxAnimationHandler->HandleAnimationRequest( xRAMsg );
			}
		}

		// Ensure that we have an inventory...
		if(m_pxInventorySystem == NULL)
		{
			InitialiseInventory();
			InitialiseMoves();
			InitialiseShapePositionAndOrientation();
			InitialiseSoundEvents();

			// Ensure that the default inventory items are active...
			if(m_pxInventorySystem->IsMaster())
			{
				AVP_Inventory_Group*	pxVisionModeGroup	= m_pxInventorySystem->GetGroup(uAVP_INVENTORY_GROUPHASH_VISIONMODE);
				AVP_Inventory_Group*	pxWeaponGroup		= m_pxInventorySystem->GetGroup(uAVP_INVENTORY_GROUPHASH_WEAPON);

				if(pxVisionModeGroup != NULL)
				{
					pxVisionModeGroup->SetCurrentItemToDefaultItem(true);
				}
				if(pxWeaponGroup != NULL)
				{
					pxWeaponGroup->SetCurrentItemToDefaultItem(true);
				}
			}
		}

		if ( !AVP_Game_Core::IsLocalPlayer( GetGuid() ) )
		{
			AVP_Inventory_Group*	pxWeaponGroup	= (m_pxInventorySystem == NULL) ? NULL : m_pxInventorySystem->GetGroup(uAVP_INVENTORY_GROUPHASH_WEAPON);

			if(pxWeaponGroup == NULL)
			{
				Asura_Assert(false, "Cannot find weapon group for this actor.");
			}
			else
			{
				const Asura_Hash_ID	uHash	= (pxWeaponGroup->GetNumItems() == 0)
											? ASURA_HASH_ID_UNSET
											: pxData->GetCurrentWeaponHash();

				if ( uHash != ASURA_HASH_ID_UNSET )
				{
					pxWeaponGroup->SetCurrentItem( uHash, pxWeaponGroup->GetCurrentItem() == ASURA_HASH_ID_UNSET );
				}
			}

			SetAimDirection( Asura_AVector3( pxData->GetAimDirection() ), fServerTime );
			SetLookAtPosition( Asura_AVector3( pxData->GetLookAtPosition() ), fServerTime );
			SetMovementDirection( Asura_AVector3( pxData->GetMovementDirection() ), fServerTime );

			if ( IsPlayer() ) // remote player
			{
				SetMoving( pxData->IsMoving() );
				SetMovingSlow( pxData->IsMovingSlow() );
				SetMovingFast( pxData->IsMovingFast() );

				if ( GetPlayerStateHandler() )
				{
					GetPlayerStateHandler()->UpdateMovementParametrics( pxData->GetPlayerSpeedParameter(), pxData->GetPlayerMoveAngle() );
				}
			}
		}

		if ( !IsPlayer() )
		{
			if ( !Asura_Network::IsServer() ) // don't do this on local AI client entities as it's already been done
			{
				m_uAIAnimState = pxData->GetAIAnimState();
				SetMoving( pxData->IsMoving() );
				SetMovingSlow( pxData->IsMovingSlow() );
				SetMovingFast( pxData->IsMovingFast() );

				Asura_AVector3 xMoveDirA;
				GetMovementDirection( xMoveDirA );
				Asura_Vector_3 xMoveDir( xMoveDirA );
				xMoveDir = GetCurrentOrientation().ApplyTranspose( xMoveDir );
				float fOSMoveDirAngle = Asura_Maths::ArcTangent( xMoveDir.GetX(), xMoveDir.GetZ() );

				float fTweenTime;
				bool bSyncNextAnim;
				bool bPauseNextAnimState = false;
				const AVP_Actor_AnimStateData* pxAnimData;
				if ( ( pxData->GetAIAnimState() == AVP_Actor_AIAnimStateMachine::ACTOR_STATE_IN_GAMESCENE || pxData->GetAIAnimState() == AVP_Actor_AIAnimStateMachine::ACTOR_STATE_IN_MOVE ) && ( pxData->GetTimeInAIAnimState() > dbg_fStartNextStateAnimTime ) )
				{
					pxAnimData = GetStateDataFromMesssageData( pxData->GetAIAnimStateAfterMove(), pxData );
					fTweenTime = 0.2f;
					bSyncNextAnim = m_bPlayingNextAnim;
					bPauseNextAnimState = true;
					m_bPlayingNextAnim = true;
				}
				else
				{
					m_bPlayingNextAnim = false;
					pxAnimData = GetStateDataFromMesssageData( pxData->GetAIAnimState(), pxData );
					fTweenTime = pxData->GetAIStateAnimTweenTime();
					bSyncNextAnim = pxData->IsAIStateAnimSync();
				}

				if ( pxAnimData )
				{
					UpdateAnimStateData( pxAnimData, NULL, pxAnimData, NULL, pxData->IsAIAnimStateLooped(), 0.0f, 0.0f, fOSMoveDirAngle, fTweenTime, bSyncNextAnim );
					if ( bPauseNextAnimState )
					{
						PauseAnimState();
					}
					else
					{
						PlayAnimState();
					}
				}
			}
		}

		m_bIsDestructible = pxData->IsDestructible();

		// If we're being updated we should be active. As long as we're not dead then we should activate our physics object (if it exists).
		// TODO - this may need more complex handling, especially if we can have death anims that don't immediately ragdoll us.
		if (m_pxDynamicObject && !IsDead() )
		{
			// TODO - I don't think actors/physical objects handle deactivation yet (07/11/07), so only death or reset can turn us off.
			m_pxDynamicObject->SetIgnore(false);
		}
	}
}

void AVP_ClientEntity_Actor::UpdateAimDirection()
{
	Asura_AVector3 xAimDir;
	Asura_AMatrix3x3 xClientOri( GetCurrentOrientation() );
	GetAimDirection( xAimDir );
	switch ( GetAimDirectionUsage() )
	{
		case AIM_DIRECTION_RESET:
		{
			Asura_Quat xRot;
			Asura_AVector3 xAxis;
			Asura_AVector3 xFwd = xClientOri.GetColumn2();
			xRot.RotationFromAToB( xAimDir, xFwd );
			float fAngle = xRot.ToRotation( xAxis );
			float fMaxAngle = GetAimDirectionAngVel() * Asura_Timers::GetGameTimeSlice();
			fAngle = Asura_Maths::ClampToRange( fAngle, -fMaxAngle, fMaxAngle );
			xRot.FromRotation( fAngle, xAxis );
			xAimDir = xRot.RotateVector( xAimDir );
			xAimDir.Normalise();
			SetAimDirection( xAimDir );
		}
		break;

		case AIM_DIRECTION_SET_DIRECTLY:
		{
			Asura_AVector3 xDesiredLocalVector = xClientOri.ApplyTranspose( GetTargetAimDirection() );
			xDesiredLocalVector.Normalise();
			Asura_AVector3 xCurrentLocalVector = xClientOri.ApplyTranspose( xAimDir );
			xCurrentLocalVector.Normalise();

			const float fDesPhi		= Asura_Maths::ArcTangent( xDesiredLocalVector.GetX(), xDesiredLocalVector.GetZ() );
			const float fDesTheta	= Asura_Maths::ArcSine( Asura_Maths::ClampToRange( -xDesiredLocalVector.GetY(), -1.0f, 1.0f ) );
			float fCurPhi			= Asura_Maths::ArcTangent( xCurrentLocalVector.GetX(), xCurrentLocalVector.GetZ() );
			float fCurTheta			= Asura_Maths::ArcSine( Asura_Maths::ClampToRange( -xCurrentLocalVector.GetY(), -1.0f, 1.0f ) );

			const float	fMaxAngle = GetAimDirectionAngVel() * Asura_Timers::GetGameTimeSlice();

			const float fDeltaTheta = Asura_Maths::ClampToRange( fDesTheta - fCurTheta, -fMaxAngle, fMaxAngle );
			fCurTheta += fDeltaTheta;

			const float fDeltaPhi = Asura_Maths::ClampToRange( Asura_Maths::RadiansToZeroCentredRange( fDesPhi - fCurPhi ), -fMaxAngle, fMaxAngle );
			fCurPhi += fDeltaPhi;

			Asura_Assert( fCurTheta == fCurTheta, "Bad Theta" );
			Asura_Assert( fCurPhi == fCurPhi, "Bad Phi" );

			if ( fCurPhi == fCurPhi && fCurTheta == fCurTheta )
			{
				float fAimX, fNegAimY, fAimZ, fCosTheta;
				Asura_Maths::SinCos( fCurPhi, fAimX, fAimZ );
				Asura_Maths::SinCos( fCurTheta, fNegAimY, fCosTheta );
				xCurrentLocalVector = Asura_Vector_3( fCosTheta * fAimX, -fNegAimY, fCosTheta * fAimZ );

				// Put this back into world space.
				xAimDir = xCurrentLocalVector * xClientOri;
			}

			xAimDir.Normalise();
			SetAimDirection( xAimDir );
		}
		// fall through so that we limit the target aim direction to the allowed range

		case AIM_DIRECTION_LIMIT:
		{
			Asura_AVector3 xNewAimDir( xAimDir );
			Asura_Quat xRot;
			xRot.RotationFromAToB( GetTargetAimDirection(), xNewAimDir );
			Asura_AVector3 xAxis;
			float fAngle = xRot.ToRotation( xAxis );
			if ( fAngle < 0.0f )
			{
				xAxis *= -1.0f;
				fAngle = -fAngle;
			}

			if ( fAngle > GetAimDirectionLimitAngle() )
			{
				fAngle = GetAimDirectionLimitAngle();
				xRot.FromRotation( fAngle, xAxis );
				xNewAimDir = xRot.RotateVector( GetTargetAimDirection() );

				xRot.RotationFromAToB( xAimDir, xNewAimDir );
				fAngle = xRot.ToRotation( xAxis );
				float fMaxAngle = GetAimDirectionAngVel() * Asura_Timers::GetGameTimeSlice();
				fAngle = Asura_Maths::ClampToRange( fAngle, -fMaxAngle, fMaxAngle );
				xRot.FromRotation( fAngle, xAxis );
				xAimDir = xRot.RotateVector( xAimDir );
				xAimDir.Normalise();
				SetAimDirection( xAimDir );
			}
		}
		break;

		case AIM_DIRECTION_LOCK:
		case AIM_DIRECTION_NOT_USED:
		default:
		{
			// do nothing
		}
		break;
	}
}

void AVP_ClientEntity_Actor::UpdateLookAtPosition()
{
	Asura_AVector3 xLookAtPos;
	Asura_AMatrix3x3 xClientOri( GetCurrentOrientation() );
	GetLookAtPosition( xLookAtPos );
	switch ( GetAimDirectionUsage() )
	{
		case LOOK_AT_POSITION_RESET:
		{
			xLookAtPos.Lerp( Asura_AVector3( GetCurrentPosition() ) + xClientOri.GetColumn2() * 10.0f, Asura_Maths::ClampToRange( Asura_Timers::GetGameTimeSlice() / GetLookDirectionLerpTimeScale(), 0.0f, 1.0f ) );
			SetLookAtPosition( xLookAtPos );
		}
		break;

		case LOOK_AT_POSITION_SET_DIRECTLY:
		{
			xLookAtPos.Lerp( GetTargetLookAtPosition(), Asura_Maths::ClampToRange( Asura_Timers::GetGameTimeSlice() / GetLookDirectionLerpTimeScale(), 0.0f, 1.0f ) );
			SetLookAtPosition( xLookAtPos );
		}
		// fall through so that we limit the target look at position to the allowed range

		case LOOK_AT_POSITION_LIMIT:
		{
			Asura_AVector3 xNewLookAtPos( xLookAtPos );
			float fLookDist = xNewLookAtPos.NormaliseReturningMagnitude();
			Asura_Quat xRot;
			xRot.RotationFromAToB( xClientOri.GetColumn2(), xNewLookAtPos );
			Asura_AVector3 xAxis;
			float fAngle = xRot.ToRotation( xAxis );
			if ( fAngle < 0.0f )
			{
				xAxis *= -1.0f;
				fAngle = -fAngle;
			}

			if ( fAngle > GetLookDirectionLimitAngle() )
			{
				fAngle = GetLookDirectionLimitAngle();
				xRot.FromRotation( fAngle, xAxis );
				xNewLookAtPos = xRot.RotateVector( xClientOri.GetColumn2());
				xNewLookAtPos *= fLookDist;
				xLookAtPos.Lerp( xNewLookAtPos, Asura_Maths::ClampToRange( Asura_Timers::GetGameTimeSlice() / GetLookDirectionLerpTimeScale(), 0.0f, 1.0f ) );
				SetLookAtPosition( xLookAtPos );
			}

			Asura_Quat xRot2;
			xRot2.RotationFromAToB( xLookAtPos, xNewLookAtPos );
			Asura_AVector3 xAxis2;
			float fDiffAngle = xRot2.ToRotation( xAxis2 );
			if ( fDiffAngle < 0.0f )
			{
				fDiffAngle = -fDiffAngle;
			}
			if ( fDiffAngle > Asura_Maths::Deg90 )
			{
				fDiffAngle = Asura_Maths::Deg90;
			}

			SetLookDirectionStoredAngle(fDiffAngle);
		}
		break;

		case LOOK_AT_POSITION_LOCK:
		case LOOK_AT_POSITION_NOT_USED:
		default:
		{
			// do nothing
		}
		break;
	}
}

void AVP_ClientEntity_Actor::UpdateFloorFlags(void)
{
	if(IsDead()) return; // Don't try to update a dead actors feet

	Asura_Assertf( m_uNumFeet <= MAX_NUM_FEET, ("Actor %d has %d feet!", GetGuid(), m_uNumFeet ));

	for ( u_int uFootItr = 0; uFootItr < m_uNumFeet; ++uFootItr )
	{
		const Asura_LOS_OutputData* pxOutputData = NULL;
		Asura_LOS_InputData xInputData( Asura_Maths::ZeroVector3, Asura_Maths::ZeroVector3, ASURA_GUID_UNREGISTERED );

		const Asura_Animation_IK_Chain_LOS* pxChain = GetLimbIKChainLOS( uFootItr );
		if ( pxChain && pxChain->GetLOSDataFrameStamp() + 1 >= Asura_Timers::GetFrameNumber() ) // accept LOS data that's up to 1 frame out
		{
			xInputData = (pxChain->GetLOSInputData());
			pxOutputData = &(pxChain->GetLOSOutputData());
		}
		else
		{
			Asura_Vector_3 xDirection(0.0f, 1.0f, 0.0f);
			xDirection = xDirection * GetCurrentOrientation();

			//get foot location
			Asura_Vector_3 xOrigin;
			GetFootPosition( uFootItr, xOrigin );

			//Do LOS test
			xInputData = Asura_LOS_InputData( xOrigin, xDirection, GetGuid(), dbg_fFloorLOSDistance );
			pxOutputData = Asura_LOS::TestAgainstEnvironment( &xInputData );
		}

		if ( pxOutputData->m_bHitEnvironment )
		{
			m_pxFloorPos[ uFootItr ] = xInputData.m_xOrigin + xInputData.m_xDirection * pxOutputData->m_fDistanceToIntersection;
			m_pxFloorNormal[ uFootItr ] = pxOutputData->m_xNormal;

			const Asura_MaterialResponse* pxMaterial = Asura_MaterialResponse_System::GetMaterialByHashID( pxOutputData->m_uMaterialResponseHashID );
			if(pxMaterial)
			{
				m_puFloorType[ uFootItr ] = pxMaterial->GetSurfaceTypeHash();
			}
		}
	}
}



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::CreatePFXEffect
// Author       : Andy Tate
// Date         : 31-Jan-2007
////////////////////////////////////////////////////////////////////////////////

u_int AVP_ClientEntity_Actor::CreatePFXEffect(const Asura_Hash_ID uPFXHash,
											   const Asura_Hash_ID uMarkerPointHash,
											   const bool bAttachToSkin)
{
	Asura_Vector_3		xPosition;
	Asura_Matrix_3x3	xOrientation;

	if(GetWorldSpaceMarkerPoint(uMarkerPointHash, &xPosition, &xOrientation))
	{
		const u_int	uPFXEffectID	= Asura_PFX_Entity_Effect::Create(xPosition, xOrientation, uPFXHash, 0, ASURA_PFX_EFFECT_CAMERA_FLAGS, GetGuid());

		if(uPFXEffectID == ASURA_PFX_INVALID_EFFECT_ID)
		{
			return(ASURA_PFX_INVALID_EFFECT_ID);
		}
		else
		{
			if(bAttachToSkin)
			{
				Asura_Animation*			pxAnimation		= m_pxAnimationHandler->GetAnimation();
				Asura_AnimationAttachment*	pxAttachment	= new Asura_AnimationAttachment_PFX (uPFXEffectID, true);

				pxAttachment->LinkAttachment(pxAnimation, uMarkerPointHash, true);
			}

			return(uPFXEffectID);
		}
	}
	else
	{
		return(ASURA_PFX_INVALID_EFFECT_ID);
	}
}

/// End of function 'AVP_ClientEntity_Actor::CreatePFXEffect'. ///


bool AVP_ClientEntity_Actor::GetCanPlayDialogue() const
{
	const Asura_Movement_System* pxAVPMoveSystem = GetMovementSystem();

	if ( pxAVPMoveSystem )
	{
		const Asura_Move* pxCurrentMove = pxAVPMoveSystem->GetCurrentMove();

		// TODO - this is almost certainly not the whole story.
		if ( pxCurrentMove && pxCurrentMove->GetHash() == AVP_Move_Puppet::GetStaticHash() )
		{
			return false;
		}
	}

	return PARENT::GetCanPlayDialogue();
}


Asura_Hash_ID AVP_ClientEntity_Actor::GetCurrentGamesceneAnimControllerHash() const
{
	if ( m_pxAnimationHandler != NULL )
	{
		return m_pxAnimationHandler->GetCurrentGamesceneAnimControllerHash();
	}
	else
	{
		return ASURA_HASH_ID_UNSET;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::MakePlayer
// Author       : Andy Tate
// Date         : 13-Feb-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientEntity_Actor::MakePlayer(Asura_Guid uPlayerGuid)
{
	const Asura_Vector_3&	xPosition		= GetCurrentPosition();
	const Asura_Matrix_3x3&	xOrientation	= GetCurrentOrientation();
	const Asura_Guid		uGuid			= GetGuid();
	bool					bLocalPlayer	= AVP_Game_Core::IsLocalPlayer(uGuid);

	// Initialise the player...
	if(bLocalPlayer)
	{
		// Set the skin to be first person
		if(m_pxAnimationHandler && m_pxAnimationHandler->IsThirdPerson() )
		{
			m_pxAnimationHandler->SetFirstPerson();
		}

		if(m_pxInventorySystem != NULL)
		{
			m_pxInventorySystem->SetAsMaster();
		}

		// We're in control of our own physics
		if(m_pxDynamicObject == NULL)
		{
			const AVP_Collision_Descriptor* pxCollDesc = GetDefaultCollisionDescriptor();
			if ( pxCollDesc )
			{
				const Asura_Collision_Object* pxCollObj = pxCollDesc->GetCollisionBox();
				Asura_Assert( pxCollObj != NULL, "No default collision object for actor, physics won't be set up" );
				if ( pxCollObj != NULL )
				{
					m_usFlagsToIgnore = m_usFlagsToIgnore | AVP_PolygonFlag_IgnoreForPlayerCollision;
					m_pxPhysicsObjectHandler	= new AVP_Actor_PhysicsObjectHandler (uGuid, AVP_Actor_PhysicsObjectHandler::PLAYER, *pxCollObj, AVP_Actor_PhysicsObjectHandler::GetActorCollisionProxy(Classification), m_usFlagsToIgnore);
					m_pxDynamicObject			= m_pxPhysicsObjectHandler->GetPhysicsObject();
					m_pxDynamicObject->SetGravityScale(2.0f); // player actors generally feel better with a higher gravity scale
					m_pxDynamicObject->SetActorsDontPushMe(true);
					m_pxDynamicObject->SetActorDontPushActors(true);
					m_pxDynamicObject->SetPolyFlagsToReturnForActorCollision( AVP_PolygonFlag_IgnoreForReorientation );
				}
			}
		}
		else
		{
			const AVP_Collision_Descriptor* pxCollDesc = GetDefaultCollisionDescriptor();
			if ( pxCollDesc )
			{
				const Asura_Collision_Object* pxCollObj = pxCollDesc->GetCollisionBox();
				Asura_Assert( pxCollObj != NULL, "No default collision object for actor, physics won't be set up" );
				if ( pxCollObj != NULL )
				{
					m_pxPhysicsObjectHandler->Reset(AVP_Actor_PhysicsObjectHandler::PLAYER, *pxCollObj, AVP_Actor_PhysicsObjectHandler::GetActorCollisionProxy(Classification) );
				}
			}
		}

#if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
		// Re-create the interpolators...
		if ( m_pxPosAndOriInterpolator )
		{
			delete m_pxPosAndOriInterpolator;
			m_pxPosAndOriInterpolator = NULL;
		}
#endif // #if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	}
	else
	{
		if(m_pxInventorySystem != NULL)
		{
			m_pxInventorySystem->SetAsSlave();
		}

		// Set the skin to be third person
		if(m_pxAnimationHandler && m_pxAnimationHandler->IsFirstPerson() )
		{
			m_pxAnimationHandler->SetThirdPerson();
		}

		// The server tells us where this player is...
		if(m_pxDynamicObject == NULL)
		{
			const AVP_Collision_Descriptor* pxCollDesc = GetDefaultCollisionDescriptor();
			if ( pxCollDesc )
			{
				const Asura_Collision_Object* pxCollObj = pxCollDesc->GetCollisionBox();
				Asura_Assert( pxCollObj != NULL, "No default collision object for actor, physics won't be set up" );
				if ( pxCollObj != NULL )
				{
					m_usFlagsToIgnore = m_usFlagsToIgnore | AVP_PolygonFlag_IgnoreForPlayerCollision;
					m_pxPhysicsObjectHandler	= new AVP_Actor_PhysicsObjectHandler (uGuid, AVP_Actor_PhysicsObjectHandler::REMOTE_CLIENT, *pxCollObj, AVP_Actor_PhysicsObjectHandler::GetActorCollisionProxy(Classification), m_usFlagsToIgnore);
					m_pxDynamicObject			= m_pxPhysicsObjectHandler->GetPhysicsObject();
					m_pxDynamicObject->SetGravityScale(2.0f); // player actors generally feel better with a higher gravity scale
					m_pxDynamicObject->SetActorsDontPushMe(true);
					m_pxDynamicObject->SetActorDontPushActors(true);
					m_pxDynamicObject->SetPolyFlagsToReturnForActorCollision( AVP_PolygonFlag_IgnoreForReorientation );
				}
			}
		}
		else
		{
			const AVP_Collision_Descriptor* pxCollDesc = GetDefaultCollisionDescriptor();
			if ( pxCollDesc )
			{
				const Asura_Collision_Object* pxCollObj = pxCollDesc->GetCollisionBox();
				Asura_Assert( pxCollObj != NULL, "No default collision object for actor, physics won't be set up" );
				if ( pxCollObj != NULL )
				{
					m_pxPhysicsObjectHandler->Reset(AVP_Actor_PhysicsObjectHandler::REMOTE_CLIENT, *pxCollObj, AVP_Actor_PhysicsObjectHandler::GetActorCollisionProxy(Classification) );
				}
			}
		}

#if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
		// Re-create the interpolators...
		delete(m_pxPosAndOriInterpolator);
		m_pxPosAndOriInterpolator = new Asura_PosAndOriInterpolator;
		m_pxPosAndOriInterpolator->TeleportTo(xPosition, xOrientation);
		//m_pxPosAndOriInterpolator->SetUseCubicSplineInterpolation( true ); 
#endif // #if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	}

	// Our physics object needs some blueprint parameters, so set these up now.
	InitFromBlueprint();

	// Ensure everything has the correct position/orientation...
	m_pxDynamicObject->SetPosition(xPosition);
	m_pxDynamicObject->SetOrientation(xOrientation);

	// Finally, mark us as being a player...
	m_bIsPlayer	= true;
	m_bIsAIActor = false;

	m_uPlayerInfoGuid = uPlayerGuid;

	// Create pickup handler
	if ( !m_pxPickupHandler )
	{
		m_pxPickupHandler = new AVP_Actor_PickupHandler(GetGuid());
	}
}

/// End of function 'AVP_ClientEntity_Actor::MakePlayer'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::MakeAI
// Author       : Chris Wallace
// Date         : 9-Feb-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientEntity_Actor::MakeAI()
{
	// Remove the handlers...
// 	if (m_pxAnimationHandler)
// 	{
// 		delete m_pxAnimationHandler;
// 		m_pxAnimationHandler = NULL;
// 	}

	if (m_pxCameraHandler)
	{
		delete m_pxCameraHandler;
		m_pxCameraHandler = NULL;
	}

	if (m_pxHUDHandler)
	{
		delete m_pxHUDHandler;
		m_pxHUDHandler = NULL;
	}

	if (m_pxInputHandler)
	{
		delete m_pxInputHandler;
		m_pxInputHandler = NULL;
	}

	if (m_pxInteractionHandler)
	{
		delete m_pxInteractionHandler;
		m_pxInteractionHandler = NULL;
	}

	if(m_pxInventorySystem != NULL)
	{
		m_pxInventorySystem->SetAsSlave();
	}

	if (AVP_Settings::GetIsServer())
	{
		// The server already has physics objects for the AIs, so no need for that here
		if (m_pxPhysicsObjectHandler)
		{
			delete m_pxPhysicsObjectHandler;
			m_pxPhysicsObjectHandler = NULL;
		}

		m_pxDynamicObject			= NULL;
	}
	else
	{
		// Create a remote client block
		if(m_pxDynamicObject == NULL)
		{
			const AVP_Collision_Descriptor* pxCollDesc = GetDefaultCollisionDescriptor();
			if ( pxCollDesc )
			{
				const Asura_Collision_Object* pxCollObj = pxCollDesc->GetCollisionBox();
				Asura_Assert( pxCollObj != NULL, "No default collision object for actor, physics won't be set up" );
				if ( pxCollObj != NULL )
				{
					m_pxPhysicsObjectHandler	= new AVP_Actor_PhysicsObjectHandler(Guid, AVP_Actor_PhysicsObjectHandler::REMOTE_CLIENT, *pxCollObj, AVP_Actor_PhysicsObjectHandler::GetActorCollisionProxy(Classification), m_usFlagsToIgnore);
					m_pxDynamicObject			= m_pxPhysicsObjectHandler->GetPhysicsObject();
				}
			}
		}
		else
		{
			const AVP_Collision_Descriptor* pxCollDesc = GetDefaultCollisionDescriptor();
			if ( pxCollDesc )
			{
				const Asura_Collision_Object* pxCollObj = pxCollDesc->GetCollisionBox();
				Asura_Assert( pxCollObj != NULL, "No default collision object for actor, physics won't be set up" );
				if ( pxCollObj != NULL )
				{
					m_pxPhysicsObjectHandler->Reset(AVP_Actor_PhysicsObjectHandler::REMOTE_CLIENT, *pxCollObj, AVP_Actor_PhysicsObjectHandler::GetActorCollisionProxy(Classification));
				}
			}
		}
	}

#if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	// Re-create our interpolator...
	{
		const Asura_Vector_3&	xPosition		= GetCurrentPosition();
		const Asura_Matrix_3x3&	xOrientation	= GetCurrentOrientation();

		if(m_pxPosAndOriInterpolator)
		{
			delete m_pxPosAndOriInterpolator;
		}
		m_pxPosAndOriInterpolator = new Asura_PosAndOriInterpolator;
		m_pxPosAndOriInterpolator->TeleportTo(xPosition, xOrientation);
		//m_pxPosAndOriInterpolator->SetUseCubicSplineInterpolation( true ); 
	}
#endif // #if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)

//	GetActorStateHandler().SwitchToAI();

	// Set the skin to be third person
	if(m_pxAnimationHandler && m_pxAnimationHandler->IsFirstPerson() )
	{
		m_pxAnimationHandler->SetThirdPerson();
	}

	delete m_pxPlayerStateHandler;
	m_pxPlayerStateHandler = NULL;

	// Finally, mark us as no longer being a player...
	m_bIsPlayer	= false;
	m_bIsAIActor = true;
}

/// End of function 'AVP_ClientEntity_Actor::MakeAI'. ///

void AVP_ClientEntity_Actor::DisconnectPlayer()
{
	m_uPlayerInfoGuid = ASURA_GUID_UNREGISTERED;

	if ( !Asura_Entity_Client::IsInUpdateableClientList(this) && IsEntityActive() )
	{
		Asura_Entity_Client::AddToUpdateableClientList(this);
	}

	if (m_pxCameraHandler)
	{
		delete m_pxCameraHandler;
		m_pxCameraHandler = NULL;
	}

	if (m_pxHUDHandler)
	{
		delete m_pxHUDHandler;
		m_pxHUDHandler = NULL;
	}

	if (m_pxInputHandler)
	{
		delete m_pxInputHandler;
		m_pxInputHandler = NULL;
	}

	if (m_pxInteractionHandler)
	{
		delete m_pxInteractionHandler;
		m_pxInteractionHandler = NULL;
	}
}

float AVP_ClientEntity_Actor::GetSpeedFromAnimation()
{
	AVP_Actor_AnimationHandler* pxAnimationHandler = GetAnimationHandler();
	Asura_Animation* pxAnimation = (pxAnimationHandler)?pxAnimationHandler->GetAnimation():NULL;
	
	if(pxAnimation)
	{
		Asura_Vector_3 xVelocity;
		float fSpeed;

		if(pxAnimation->GetVelocity(&xVelocity))
		{
			fSpeed = xVelocity.Magnitude();
			return(fSpeed);
		}
		else
		{
			return(0.0f);
		}
		
	}

	return(0.0f);
}

void AVP_ClientEntity_Actor::GetVelocityFromAnimation(Asura_Vector_3& xAnimVelocity)
{
	// Now returns zero anim velocity for invisible players - this is to avoid continuing player movement active at the start of a cutscene. (AN 23/10/07)
	// Note: I can't think of any situation where you want an invisible character to move around, since it's animation will never get updated until visible. Hence the warning.
	if( IsInvisible() )
	{
		if( IsPlayer() )
		{
			xAnimVelocity.Zero();
			return;
		}

		Asura_Assert( 0, "Accessing invisible actor's anim velocity - result won't be accurate!" );
	}

	AVP_Actor_AnimationHandler* pxAnimationHandler = GetAnimationHandler();
	Asura_Animation* pxAnimation = (pxAnimationHandler)?pxAnimationHandler->GetAnimation():NULL;

	if(pxAnimation && pxAnimation->GetVelocity(&xAnimVelocity))
	{
		Asura_Quat xOriOffset;
		if ( pxAnimation->GetMovementOffset( NULL, &xOriOffset ) )
		{
			xOriOffset.Invert();
			xAnimVelocity = xOriOffset.RotateVector( xAnimVelocity);
		}
		return;
	}

	xAnimVelocity.Zero();
}

void AVP_ClientEntity_Actor::GetAngularVelocityFromAnimation( Asura_Vector_3& xAxis, Asura_Angle& fAngularSpeed )
{
	AVP_Actor_AnimationHandler* pxAnimationHandler = GetAnimationHandler();
	Asura_Animation* pxAnimation = (pxAnimationHandler)?pxAnimationHandler->GetAnimation():NULL;

	if ( pxAnimation && pxAnimation->GetAngularVelocity( &xAxis, &fAngularSpeed ) )
	{
		return;
	}

	xAxis.Zero();
	fAngularSpeed = 0.0f;
}

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

    Asura_Value xValue;
    if(xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_DEADCORPSE_REMOVETIME, xValue))
    {
        m_fCorpseRemoveTime = xValue.GetFloatValue();
    }
    if(xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_DEADCORPSE_FADETIME, xValue))
    {
        m_fCorpseFadeTime = xValue.GetFloatValue();
    }
	if ( xBlueprint.GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_INNER_TARGETING_RADIUS, xValue ) )
	{
		m_fInnerTargetingRadius = xValue.GetFloatValue();
	}
	if ( xBlueprint.GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_OUTER_TARGETING_RADIUS, xValue ) )
	{
		m_fOuterTargetingRadius = xValue.GetFloatValue();
	}

	if ( xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_NUMHEALTHBLOCKS, xValue ) )
	{
		m_uMaxHealthBlocks = xValue.GetIntValue();
	}

	if ( xBlueprint.GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_ANIMSTATEBLUEPRINTTYPE, xValue ) )
	{
		m_uAnimStateBlueprintType = xValue.GetHashIDValue();
	}

	if ( xBlueprint.GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_MOVEMENTANIMDURATIONMODIFIER, xValue ) )
	{
		m_fAnimDurationModifierMoving = xValue.GetFloatValue();
	}

	if(xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_LIGHTFALLEFFECTMINDISTANCE, xValue))
	{
		m_fMinDistanceForJumpLandLightEffect = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_NORMALFALLEFFECTMINDISTANCE, xValue))
	{
		m_fMinDistanceForJumpLandNormalEffect = xValue.GetFloatValue();
	}
	if(xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_HEAVYFALLEFFECTMINDISTANCE, xValue))
	{
		m_fMinDistanceForJumpLandHeavyEffect = xValue.GetFloatValue();
	}

	if (xBlueprint.GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_PHERMONEEMITSGLOW, xValue ) && xValue.GetBoolValue())
	{
		float fR = xBlueprint.GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_PHERMONEGLOWR, xValue) ? xValue.GetFloatValue() : 0.0f;
		float fG = xBlueprint.GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_PHERMONEGLOWG, xValue) ? xValue.GetFloatValue() : 0.0f;
		float fB = xBlueprint.GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_PHERMONEGLOWB, xValue) ? xValue.GetFloatValue() : 0.0f;
		m_xAlienPheromoneColour = Asura_RGBA(fR, fG, fB, 1.0f);
	}
	else
	{
		m_xAlienPheromoneColour = Asura_RGBA(0);
	}



	if (m_pxPhysicsObjectHandler)
	{
		Asura_Physics_Object* pxPhysicsObject = m_pxPhysicsObjectHandler->GetPhysicsObject();
		if (pxPhysicsObject)
		{
			if(xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAVITYSCALE, xValue))
			{
				pxPhysicsObject->SetGravityScale(xValue.GetFloatValue());
			}
			if(xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_ELASTICITY, xValue))
			{
				pxPhysicsObject->SetElasticity(xValue.GetFloatValue());
			}
			if(xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_FRICTION, xValue))
			{
				pxPhysicsObject->SetFriction(xValue.GetFloatValue());
			}
			if(xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_DRAG, xValue))
			{
				pxPhysicsObject->SetDrag(xValue.GetFloatValue());
			}
			if(xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_ANGULARDRAG, xValue))
			{
				pxPhysicsObject->SetAngularDrag(xValue.GetFloatValue());
			}
			if(xBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_MASS, xValue))
			{
				pxPhysicsObject->SetInvMass(1.0f / xValue.GetFloatValue());
			}
		}
	}
}

// bool AVP_ClientEntity_Actor::IsAbleToSetCollisionObject( const Asura_Collision_Object* pxCollObj ) const
// {
// 	Asura_Assert( pxCollObj != NULL, "No Collision Object provided" );
// 	if ( pxCollObj != NULL )
// 	{
// 		Asura_Bounding_Box xSmallerBox( pxCollObj->GetBoundingBox() );
// 		xSmallerBox.MinX += dbg_fBoxShrinkAmount;
// 		xSmallerBox.MinY += dbg_fBoxShrinkAmount;
// 		xSmallerBox.MinZ += dbg_fBoxShrinkAmount;
// 
// 		xSmallerBox.MaxX -= dbg_fBoxShrinkAmount;
// 		xSmallerBox.MaxY -= dbg_fBoxShrinkAmount;
// 		xSmallerBox.MaxZ -= dbg_fBoxShrinkAmount;
// 
// 		if ( xSmallerBox.MaxX < xSmallerBox.MinX )
// 		{
// 			xSmallerBox.MinX = xSmallerBox.MaxX = ( xSmallerBox.MinX + xSmallerBox.MaxX ) / 2.0f;
// 		}
// 
// 		if ( xSmallerBox.MaxY < xSmallerBox.MinY )
// 		{
// 			xSmallerBox.MinY = xSmallerBox.MaxY = ( xSmallerBox.MinY + xSmallerBox.MaxY ) / 2.0f;
// 		}
// 
// 		if ( xSmallerBox.MaxZ < xSmallerBox.MinZ )
// 		{
// 			xSmallerBox.MinZ = xSmallerBox.MaxZ = ( xSmallerBox.MinZ + xSmallerBox.MaxZ ) / 2.0f;
// 		}
// 
// 		Asura_Collision_Box xSmallerCollObj( xSmallerBox );
// 		u_short usIgnoreFlags = AsuraPolygonFlag_NoCollisions;
// 		switch ( Classification )
// 		{
// 			case AVP_ENTITYCLASS_ALIEN:
// 			{
// 				usIgnoreFlags |= AVP_PolygonFlag_IgnoreForAlienCollision;
// 			}
// 			break;
// 
// 			case AVP_ENTITYCLASS_ALIENFACEHUGGER:
// 			{
// 				usIgnoreFlags |= AVP_PolygonFlag_IgnoreForFaceHuggerCollision;
// 			}
// 			break;
// 
// 			case AVP_ENTITYCLASS_PREDATOR:
// 			{
// 				usIgnoreFlags |= AVP_PolygonFlag_IgnoreForPredatorCollision;
// 			}
// 			break;
// 
// 			case AVP_ENTITYCLASS_ANDROID:
// 			case AVP_ENTITYCLASS_CIVILIAN:
// 			case AVP_ENTITYCLASS_HUMAN:
// 			case AVP_ENTITYCLASS_MARINE:
// 			{
// 				usIgnoreFlags |= AVP_PolygonFlag_IgnoreForHumanCollision;
// 			}
// 			break;
// 		
// 			default:
// 			{
// 			}
// 			break;
// 		}
// 		return Asura_Physics_System::CanPlaceObjectAt( GetCurrentPosition(), GetCurrentOrientation(), xSmallerCollObj, usIgnoreFlags );
// 	}
// 	else
// 	{
// 		return true;
// 	}
// }

// static
bool AVP_ClientEntity_Actor::TestRayAgainstTargetingSpheres( const Asura_Vector_3& xRayOrigin, const Asura_Vector_3& xRayDirection, const Asura_Guid uGuidToIgnore /*= ASURA_GUID_UNREGISTERED*/, Asura_Guid* const puFirstHitGuid /*= NULL*/, float* const pfFirstHitDistance /*= NULL*/, float* const pfHitRadiusParametric /*= NULL */ )
{
	float fMinHitDistance = Asura_Maths::LargeFloat;
	float fCurrentHitDistance( 0.0f );
	Asura_Vector_3 xSphereCentre;

	if ( NULL != puFirstHitGuid )
	{
		*puFirstHitGuid = ASURA_GUID_UNREGISTERED;
	}

	Asura_Hashed_List_ConstIt< AVP_ClientEntity_Actor > xListItr( &s_xList );
	for ( ; !xListItr.Done(); xListItr.Next() )
	{
		const AVP_ClientEntity_Actor* pxCurrent = xListItr.GetCurrent();
		
		// TODO @ WGP : cull on entities that should not be tested, maybe have a set of ignore flags or pass in a entity classification to ignore?

		if ( pxCurrent->GetGuid() != uGuidToIgnore && !pxCurrent->IsDead() )
		{
			pxCurrent->GetChestPosition( xSphereCentre );
			float fOuterSphereRadius = pxCurrent->GetOuterTargetingRadius();
			float fInnerSphereRadius = pxCurrent->GetInnerTargetingRadius();

			Asura_Vector_3 xOffset;
			AVP_Maths::RayOffsetFromPoint( xRayOrigin, xRayDirection, xSphereCentre, xOffset );

			fCurrentHitDistance = (xSphereCentre + xOffset - xRayOrigin).Magnitude();
			float fRadius = xOffset.Magnitude();

			if ( fRadius < fOuterSphereRadius )
			{
				if ( fCurrentHitDistance < fMinHitDistance )
				{
					fMinHitDistance = fCurrentHitDistance;
					if ( NULL != puFirstHitGuid )
					{
						*puFirstHitGuid = pxCurrent->GetGuid();
					}

					if ( NULL != pfHitRadiusParametric )
					{
						*pfHitRadiusParametric = Asura_Maths::ClampToRange( ( fRadius - fInnerSphereRadius ) / ( fOuterSphereRadius - fInnerSphereRadius ), 0.0f, 1.0f );
					}

					if ( NULL != pfFirstHitDistance )
					{
						*pfFirstHitDistance = fCurrentHitDistance;
					}
				}
			}

#ifndef ASURA_NO_DEBUG_OUTPUT
			if ( g_bRenderTargetingSphereDebugInfo )
			{
				float fHitRad = pfHitRadiusParametric != NULL ? *pfHitRadiusParametric : Asura_Maths::ClampToRange( ( fRadius - fInnerSphereRadius ) / ( fOuterSphereRadius - fInnerSphereRadius ), 0.0f, 1.0f );
				const u_int uColour = fRadius < fOuterSphereRadius ? ( 0xFF000000 + ( u_int( 0xFF * fHitRad ) << 16 ) + ( u_int( 0xFF * ( 1.0f - fHitRad ) ) << 8 ) ) : 0x800000FF;
				Asura_DebugLineRender::AddSphere( xSphereCentre, fInnerSphereRadius, uColour );
				Asura_DebugLineRender::AddSphere( xSphereCentre, fOuterSphereRadius, uColour );
				Asura_DebugLineRender::AddArrow( xRayOrigin, xSphereCentre + xOffset, uColour );
			}
#endif
		}
	}
	return fMinHitDistance < Asura_Maths::LargeFloat;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::IsCloaked
// Author       : Andy Tate
// Date         : 29-Nov-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_ClientEntity_Actor::IsCloaked(const bool bFullyCloaked) const
{
	if (!m_pxInventorySystem) { return false; }

	const AVP_Inventory_Item*		pxItem			= m_pxInventorySystem->GetItem(uAVP_INVENTORY_ITEMHASH_CLOAK);
	const AVP_InventoryItem_Cloak*	pxCloakItem		= (pxItem)?static_cast<const AVP_InventoryItem_Cloak*>(pxItem):NULL;
	const AVP_CloakEffect*			pxCloakEffect	= (pxCloakItem)?pxCloakItem->GetCloakEffect():NULL;

	if(pxCloakEffect == NULL)
	{
		return(false);
	}
	else
	{
		const AVP_CloakEffect::State	eCloakState	= pxCloakEffect->GetState();

		if(bFullyCloaked)
		{
			return(eCloakState == AVP_CloakEffect::CLOAKED);
		}
		else
		{
			return(eCloakState != AVP_CloakEffect::UNCLOAKED);
		}
	}
}

/// End of function 'AVP_ClientEntity_Actor::IsCloaked'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::InitialiseInventory
// Author       : Andy Tate
// Date         : 10-Dec-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_ClientEntity_Actor::InitialiseInventory(void)
{
	// Do we already have an inventory-system?
	if(m_pxInventorySystem == NULL)
	{
		m_pxInventorySystem	= new AVP_Inventory_System (*this);

		if(m_pxInventorySystem == NULL)
		{
			Asura_Assert(false, "Failed to create inventory-system for this client-side actor.");
			return(false);
		}
		else
		{
			const Asura_Hash_ID	uBlueprintTypeHash	= GetBlueprintTypeHashID();
			const Asura_Hash_ID	uBlueprintHash		= GetBlueprintHashID();
			const Asura_Guid	uGuid				= GetGuid();
			const bool			bLocalPlayer		= AVP_Game_Core::IsLocalPlayer(uGuid);

			if(bLocalPlayer)
			{
				m_pxInventorySystem->SetAsMaster();
			}

			Asura_Blueprint*	pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uBlueprintTypeHash, uBlueprintHash);
			if(pxBlueprint)
			{
				AVP_Inventory_Group*	pxVisionModeGroup = m_pxInventorySystem->GetGroup(uAVP_INVENTORY_GROUPHASH_VISIONMODE);
				if(pxVisionModeGroup == NULL)
				{
					pxVisionModeGroup = new AVP_InventoryGroup_VisionModes(*m_pxInventorySystem);
				}

				AVP_Inventory_Group*	pxWeaponGroup = m_pxInventorySystem->GetGroup(uAVP_INVENTORY_GROUPHASH_WEAPON);
				if(pxWeaponGroup == NULL)
				{
					pxWeaponGroup = new AVP_InventoryGroup_Weapons(*m_pxInventorySystem);
				}

				// Initialise the vision-modes...
				if(pxVisionModeGroup == NULL)
				{
					Asura_Assert(false, "Cannot get inventory vision-mode group to add to.");
					return(false);
				}
				else
				{
					if(pxVisionModeGroup->GetNumItems() == 0)
					{
						AVP_Inventory_Group_Enumerator	xVisionModeEnumerator(*pxVisionModeGroup);

						// Enumerate the vision-modes...
						pxBlueprint->EnumerateParameters(szAVP_BLUEPRINT_ENUMERATIONNAME_VISIONMODE, uAVP_BLUEPRINT_ENUMERATIONDIGITS_VISIONMODE, xVisionModeEnumerator);
					}
				}

				// Initialise the weapons...
				if(pxWeaponGroup == NULL)
				{
					Asura_Assert(false, "Cannot get inventory weapon group to add to.");
					return(false);
				}
				else
				{
					if(pxWeaponGroup->GetNumItems() == 0)
					{
						if ( IsPlayer() )
						{
							// Enumerate the weapons...
							AVP_Settings::Species eSpecies = GetSpeciesByClassification();
							AVP_Game_Core::CharacterType eCharType;
							if ( AVP_Settings::IsMultiPlayer() )
							{
								switch( eSpecies )
								{
								case AVP_Settings::AVP_SPECIES_ALIEN:		eCharType = AVP_Game_Core::MULTIPLAYER_ALIEN; break;
								default:	Asura_Assert( false, "Unable to determine species" ); // fall through to human
								case AVP_Settings::AVP_SPECIES_HUMAN:		eCharType = AVP_Game_Core::MULTIPLAYER_MARINE; break;
								case AVP_Settings::AVP_SPECIES_PREDATOR:	eCharType = AVP_Game_Core::MULTIPLAYER_PREDATOR; break;
								}
							}
							else
							{
								switch( eSpecies )
								{
								case AVP_Settings::AVP_SPECIES_ALIEN:		eCharType = AVP_Game_Core::SINGLE_PLAYER_ALIEN; break;
								default:	Asura_Assert( false, "Unable to determine species" ); // fall through to human
								case AVP_Settings::AVP_SPECIES_HUMAN:		eCharType = AVP_Game_Core::SINGLE_PLAYER_MARINE; break;
								case AVP_Settings::AVP_SPECIES_PREDATOR:	eCharType = AVP_Game_Core::SINGLE_PLAYER_PREDATOR; break;
								}
							}

							const char* szLevelName = 
#ifdef ASURA_TOOLS
								Tools_SaveAsura::IsEditingMission() ? Tools_SaveAsura::GetMissionFilenameWithoutExtension() : Tools_SaveAsura::GetMainFilename();
#else
								AVP_Settings::GetLevelName();
#endif

							const Asura_Blueprint* pxLevelBlueprint = AVP_Game_Core::GetLevelBlueprint( szLevelName, eCharType );
							if( pxLevelBlueprint )
							{
								AVP_Inventory_Group_Enumerator	xWeaponEnumerator( *pxWeaponGroup, false );
								pxLevelBlueprint->EnumerateParameters( szAVP_BLUEPRINT_ENUMERATIONNAME_WEAPON, uAVP_BLUEPRINT_ENUMERATIONDIGITS_WEAPON, xWeaponEnumerator );

								u_int uIndex = pxLevelBlueprint->GetParameterAsUInt( 0x33799660, 0 ); // InitialWeapon
								if(uIndex > 0) // if they don't have a default weapon, don't try and set it.
								{
									Asura_Hash_ID uHash = uAVP_INVENTORY_WEAPONHASH_WEAPON00+uIndex;
									pxWeaponGroup->SetDefaultItem(uHash);
									pxWeaponGroup->SetCurrentItemToDefaultItem(true);
								}
							}
						}
						else
						{
							AVP_Inventory_Group_Enumerator	xWeaponEnumerator(*pxWeaponGroup, false);

							// Enumerate the weapons...
							pxBlueprint->EnumerateParameters(szAVP_BLUEPRINT_ENUMERATIONNAME_WEAPON, uAVP_BLUEPRINT_ENUMERATIONDIGITS_WEAPON, xWeaponEnumerator);

							u_int uIndex = pxBlueprint->GetParameterAsUInt(Asura_GetHashID("Weapon"), 0u);
							if(uIndex > 0) // if they don't have a default weapon, don't try and set it.
							{
								Asura_Hash_ID uHash = uAVP_INVENTORY_WEAPONHASH_WEAPON00+uIndex;
								pxWeaponGroup->SetDefaultItem(uHash);
								pxWeaponGroup->SetCurrentItemToDefaultItem(true);
							}
						}
					}
				}

				return(true);
			}
			else
			{
				Asura_Assert(false, "Failed to find blueprint for this client-side actor.");
				return(false);
			}
		}
	}
	else
	{
		Asura_Assert(false, "Inventory-system already exists for this client-side actor.");
		return(false);
	}
}

/// End of function 'AVP_ClientEntity_Actor::InitialiseInventory'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::InitialiseMoves
// Author       : Andy Tate
// Date         : 10-Dec-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_ClientEntity_Actor::InitialiseMoves(void)
{
	if(m_pxMovementSystem == NULL)
	{
		m_pxMovementSystem	= new AVP_Movement_System (*this);

		if(m_pxMovementSystem == NULL)
		{
			Asura_Assert(false, "Failed to create movement-system for this client-side actor.");
			return(false);
		}
		else
		{
			new AVP_Move_Interaction_EndWeaponBack( *m_pxMovementSystem );
			new AVP_Move_Interaction( *m_pxMovementSystem );
			new AVP_Move_Interaction_BeginWeaponAway( *m_pxMovementSystem );

			new AVP_Move_GS2RigidAnim( *m_pxMovementSystem );
			return(true);
		}
	}
	else
	{
		Asura_Assert(false, "Movement-system already exists for this client-side actor.");
		return(false);
	}
}

/// End of function 'AVP_ClientEntity_Actor::InitialiseMoves'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::InitialiseSoundEvents
// Author       : Andy Tate
// Date         : 10-Dec-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_ClientEntity_Actor::InitialiseSoundEvents(void)
{
	return(true);
}

/// End of function 'AVP_ClientEntity_Actor::InitialiseSoundEvents'. ///


void AVP_ClientEntity_Actor::RenderPlayerPosition() const
{
	const float	fIncrement	= Asura_Font_System::GetFontHeight();
	const float	fX			= 80.0f;
	float		fY			= 104.0f;
	const float	fScale		= 1.0f;
	const u_int	uColour		= 0xFFFFFFFF;
	const u_int	uFormat		= ASURA_TEXTFORMAT_LEFTJUSTIFIED | ASURA_TEXTFORMAT_VERTICALBOTTOMJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING;

	const Asura_Vector_3& xPosition = GetCurrentPosition();
	Asura_Font_System::Print(fX, fY, fScale, uColour, uFormat, "Position: %.3f, %.3f, %.3f", xPosition.x, xPosition.y, xPosition.z);

	fY += fIncrement;
	Asura_Euler_3 xOrientation;
	xOrientation.FromMatrix(GetCurrentOrientation());
	Asura_Font_System::Print(fX, fY, fScale, uColour, uFormat, "Orientation: %.3f, %.3f, %.3f", Asura_Maths::RadToDeg(xOrientation.x), Asura_Maths::RadToDeg(xOrientation.y), Asura_Maths::RadToDeg(xOrientation.z));
}

/////////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::GetGuidAndMarkerHashForTarget()
// Originally	: AVP_Predator_PlasmaCasterHandler::UpdateAim()
// Author       : Chris Wallace / Ste Butcher
// Date         : 21-March-2007
// Description	: Function to workout what we are aiming at with the predators laser,
//				: Allows for two levels of accuracy, in the area of will report
//				: The body of the target, if aiming more accurately will target the 
//				: body part aimed at.
/////////////////////////////////////////////////////////////////////////////////////

bool AVP_ClientEntity_Actor::GetGuidAndMarkerHashForTarget(Asura_Vector_3& xCameraLocation, Asura_Matrix_3x3& xCameraOrientation, Asura_Guid& uHitEntity, Asura_Hash_ID& uHitMarker)
{
	uHitEntity = ASURA_GUID_UNREGISTERED;
	uHitMarker = ASURA_HASH_ID_UNSET;

	bool bAccurate = false;
	const Asura_LOS_OutputData* pxOutputData1 = NULL;
	const Asura_LOS_OutputData* pxOutputData2 = NULL;

	Asura_Vector_3 xCameraDirection(xCameraOrientation.e13, xCameraOrientation.e23, xCameraOrientation.e33);

	// Do LOS test
	Asura_LOS_InputData xInputData1 = Asura_LOS_InputData(xCameraLocation, xCameraDirection, GetGuid(), Asura_Maths::LargeFloat, false);
	xInputData1.m_bBoundingSphereOnly = true;

	// Set and clear LOS target pointers
	pxOutputData1 = Asura_LOS::TestAgainstEntities(&xInputData1);
	pxOutputData2 = NULL;

	// Have we hit the sphere?
	if(pxOutputData1->m_uHitEntity != 0)		// m_uHitEntity is reset to 0 in Asura, not ASURA_HASH_ID_UNSET
	{
		uHitEntity = pxOutputData1->m_uHitEntity;
		bAccurate = false;

		//Yes, so check if we have hit the model itself
		//Do second LOS test
		Asura_LOS_InputData xInputData2 = Asura_LOS_InputData(xCameraLocation,xCameraDirection,GetGuid(),Asura_Maths::LargeFloat,false);
		xInputData2.m_bBoundingSphereOnly = false;
		pxOutputData2 = Asura_LOS::TestAgainstEntities(&xInputData2);

		if(pxOutputData2->m_uHitEntity != 0)		// m_uHitEntity is reset to 0 in Asura, not ASURA_HASH_ID_UNSET
		{
			uHitEntity = pxOutputData2->m_uHitEntity;
			bAccurate = true;
		}
	}


	if(uHitEntity != ASURA_GUID_UNREGISTERED)
	{
		// Get exact target intersection position
		float fDistanceToIntersection = (bAccurate && pxOutputData2) ? pxOutputData2->m_fDistanceToIntersection : pxOutputData1->m_fDistanceToIntersection;
		Asura_Vector_3 xExactTargetPosition = xCameraLocation + (xCameraDirection * fDistanceToIntersection);

		//Get the entity
		AVP_ClientEntity_Actor* pxTargetEntity = AVP_ClientEntity_Actor::FindEntity(uHitEntity);
		if(pxTargetEntity != NULL)
		{
			// Get LOS Collision location

			// On accurate collision - find closest marker point
			if(bAccurate)
			{
				AVP_Actor_AnimationHandler* pxTargetAnimation = pxTargetEntity->GetAnimationHandler();

				float fLeastDistance = 1000.0f;
				Asura_Vector_3 xMarkerPosition;
				Asura_Matrix_3x3 xMatrix;

				// Hunt through all the target animations marker positions to find the closest
				for(int iTargetMarkerPoint = 0; iTargetMarkerPoint < pxTargetAnimation->GetTargetMarkerCount(); ++iTargetMarkerPoint)
				{
					const Asura_Hash_ID	uHash = AVP_Actor_AnimationHandler::GetTargetMarkerPointHash(iTargetMarkerPoint);
					if(pxTargetEntity->GetWorldSpaceMarkerPoint(uHash, &xMarkerPosition, &xMatrix))
					{
						// Get distance from this point to our collision point.
						float fDistance = (xExactTargetPosition - xMarkerPosition).Magnitude();
						if(fDistance < fLeastDistance)
						{
							fLeastDistance = fDistance;
							uHitMarker	= uHash;
						}
					}
				}
			}
			else
			{
				// On sphere-only collision - just take first marker point
				uHitMarker = AVP_Actor_AnimationHandler::GetTargetMarkerPointHash(0);
			}
		}
	}

	return(uHitEntity != ASURA_GUID_UNREGISTERED);
}


void AVP_ClientEntity_Actor::GetHeadPosition( Asura_Vector_3& xPosition ) const
{
	static const Asura_Hash_ID uHeadTargetMarkerHashID = 0xe5a85d04; // Asura_GetHashID("target_2")

	if (!GetWorldSpaceMarkerPoint(uHeadTargetMarkerHashID, &xPosition, NULL))
	{
		xPosition = GetCurrentPosition();
	}
}


bool AVP_ClientEntity_Actor::GetPositionForDialogue(Asura_Vector_3& xPosition) const
{
	if( IsDead() )
	{
		return false;
	}
	// Play dialogue from the head
	GetHeadPosition( xPosition );
	return true;
}


void AVP_ClientEntity_Actor::GetChestPosition( Asura_Vector_3& xPosition ) const
{
	static const Asura_Hash_ID uChestTargetMarkerHashID = 0xe5a85d03; // Asura_GetHashID("target_1")

	if (!GetWorldSpaceMarkerPoint(uChestTargetMarkerHashID, &xPosition, NULL))
	{
		xPosition = GetCurrentPosition();
	}
}

bool AVP_ClientEntity_Actor::IsUsingFirstPersonSkin() const
{
	// First version - switch on our skin ID. A bit nasty, but works while remote players are still using the HUD skin.
	if (m_pxAnimationHandler && m_pxAnimationHandler->GetAnimation())
	{
		switch (m_pxAnimationHandler->GetAnimation()->GetSkinHashID())
		{
		default:
			return false;

		case 0x65EF9F7F: // Alien_hud
		case 0xE758A0FA: // Marine_hud
		case 0x0FC6F3AF: // Predator_hud
			return true;
		}
	}

	return false;
}

u_int AVP_ClientEntity_Actor::GetFlagsForAllAnimsLastFrame() const
{
	if (m_pxAnimationHandler && m_pxAnimationHandler->GetAnimation())
	{
		return m_pxAnimationHandler->GetAnimation()->GetFlagsForAllAnimsLastFrame();
	}

	return 0;
}

bool AVP_ClientEntity_Actor::IsEnemyTo( const Asura_Guid uGuid ) const
{
	AVP_ClientEntity_Actor* pxClient = AVP_ClientEntity_Actor::FindEntity( uGuid );
	Asura_Assertf( pxClient != NULL, ( "Can't find entity %d", uGuid ) );
	const u_short eTheirClassification = pxClient != NULL ? pxClient->GetClassification() : 0;
	switch ( GetClassification() )
	{
		case AVP_ENTITYCLASS_ALIEN:
		case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
		case AVP_ENTITYCLASS_ALIENFACEHUGGER:
		case AVP_ENTITYCLASS_ALIENQUEEN:
		case AVP_ENTITYCLASS_ALIEN_HYBRID:
		{
			switch ( eTheirClassification )
			{
				case AVP_ENTITYCLASS_ALIEN:
				case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
				case AVP_ENTITYCLASS_ALIENFACEHUGGER:
				case AVP_ENTITYCLASS_ALIENQUEEN:
				case AVP_ENTITYCLASS_ALIEN_HYBRID:
				{
					return false;
				}

				case AVP_ENTITYCLASS_MARINE:
				case AVP_ENTITYCLASS_ANDROID:
				case AVP_ENTITYCLASS_CIVILIAN:
				case AVP_ENTITYCLASS_PREDATOR:		 
				case AVP_ENTITYCLASS_SENTRYGUN:
				case AVP_ENTITYCLASS_DROPSHIP:
				case AVP_ENTITYCLASS_PREDALIEN:
				{
					return true;
				}

				default:
				{
					Asura_Assert( false, "Not sure whether we're a goody or a baddy" );
					return false;
				}
			}
		}
		break;

		case AVP_ENTITYCLASS_MARINE:
		case AVP_ENTITYCLASS_SENTRYGUN:
		case AVP_ENTITYCLASS_DROPSHIP:
		{
			switch ( eTheirClassification )
			{
				case AVP_ENTITYCLASS_ALIEN:
				case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
				case AVP_ENTITYCLASS_ALIENFACEHUGGER:
				case AVP_ENTITYCLASS_ALIENQUEEN:
				case AVP_ENTITYCLASS_ALIEN_HYBRID:
				case AVP_ENTITYCLASS_ANDROID:
				case AVP_ENTITYCLASS_PREDATOR:
				case AVP_ENTITYCLASS_PREDALIEN:
				{
					return true;
				}

				case AVP_ENTITYCLASS_MARINE:
				case AVP_ENTITYCLASS_CIVILIAN:
				case AVP_ENTITYCLASS_SENTRYGUN:
				case AVP_ENTITYCLASS_DROPSHIP:
				{
					return false;
				}

				default:
				{
					Asura_Assert( false, "Not sure whether we're a goody or a baddy" );
					return false;
				}
			}
		}
		break;
		
		case AVP_ENTITYCLASS_ANDROID:
		{
			switch ( eTheirClassification )
			{
				case AVP_ENTITYCLASS_ALIEN:
				case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
				case AVP_ENTITYCLASS_ALIENFACEHUGGER:
				case AVP_ENTITYCLASS_ALIENQUEEN:
				case AVP_ENTITYCLASS_ALIEN_HYBRID:
				case AVP_ENTITYCLASS_MARINE:
				case AVP_ENTITYCLASS_CIVILIAN:
				case AVP_ENTITYCLASS_PREDATOR:	
				case AVP_ENTITYCLASS_PREDALIEN:
				case AVP_ENTITYCLASS_SENTRYGUN:
				case AVP_ENTITYCLASS_DROPSHIP:
				{
					return true;
				}

				case AVP_ENTITYCLASS_ANDROID:
				{
					return false;
				}

				default:
				{
					Asura_Assert( false, "Not sure whether we're a goody or a baddy" );
					return false;
				}
			}
		}
		break;

		case AVP_ENTITYCLASS_PREDATOR:		 
		{
			switch ( eTheirClassification )
			{
				case AVP_ENTITYCLASS_ALIEN:
				case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
				case AVP_ENTITYCLASS_ALIENFACEHUGGER:
				case AVP_ENTITYCLASS_ALIENQUEEN:
				case AVP_ENTITYCLASS_ALIEN_HYBRID:
				case AVP_ENTITYCLASS_MARINE:
				case AVP_ENTITYCLASS_CIVILIAN:
				case AVP_ENTITYCLASS_ANDROID:
				case AVP_ENTITYCLASS_SENTRYGUN:
				case AVP_ENTITYCLASS_DROPSHIP:
				case AVP_ENTITYCLASS_PREDALIEN:
				{
					return true;
				}

				case AVP_ENTITYCLASS_PREDATOR:		 
				{
					return false;
				}

				default:
				{
					Asura_Assert( false, "Not sure whether we're a goody or a baddy" );
					return false;
				}
			}
		}
		break;

		case AVP_ENTITYCLASS_CIVILIAN:
		{
			switch ( eTheirClassification )
			{
				case AVP_ENTITYCLASS_ALIEN:
				case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
				case AVP_ENTITYCLASS_ALIENFACEHUGGER:
				case AVP_ENTITYCLASS_ALIENQUEEN:
				case AVP_ENTITYCLASS_ALIEN_HYBRID:
				case AVP_ENTITYCLASS_PREDATOR:		 
				case AVP_ENTITYCLASS_ANDROID:
				{
					return true;
				}

				case AVP_ENTITYCLASS_MARINE:
				case AVP_ENTITYCLASS_CIVILIAN:
				case AVP_ENTITYCLASS_SENTRYGUN:
				case AVP_ENTITYCLASS_DROPSHIP:
				{
					return false;
				}

				default:
				{
					Asura_Assert( false, "Not sure whether we're a goody or a baddy" );
					return false;
				}
			}
		}
		break;

		case AVP_ENTITYCLASS_PREDALIEN: // everyone are this guy enemies
		{
			return true;
		}

		default:
		{	
			Asura_Assert( false, "Not sure whether we're a goody or a baddy" );
			return false;
		}
	}
}

bool AVP_ClientEntity_Actor::IsInvulnerable() const
{
	// Is the entity indestructible?
	if (!m_bIsDestructible) { return true; }

	// Additional check for the player: is God Mode on?
	if (!IsPlayer()) { return false; }

	// God mode doesn't exist in multiplayer.
	if (AVP_Settings::IsMultiPlayer()) { return false; }

	return AVP_Settings::GetGodMode();
}


void AVP_ClientEntity_Actor::GetCurrentMeleeBlueprintIDs(Asura_Hash_ID& uFirstPersonID, Asura_Hash_ID& uThirdPersonID) const
{
	uFirstPersonID = uThirdPersonID = ASURA_HASH_ID_UNSET;

	if (!m_pxInventorySystem) { return; }

	// By default, we'll assume we have a weapon and use a piece of data from that. Other entities (e.g. aliens) can override that behaviour if they need to.
	const AVP_InventoryItem_Weapon* pxCurrentWeapon = static_cast<const AVP_InventoryItem_Weapon*>(m_pxInventorySystem->GetCurrentItem(uAVP_INVENTORY_GROUPHASH_WEAPON));
	if (!pxCurrentWeapon) { return; }

	pxCurrentWeapon->GetMeleeBlueprintIDs(uFirstPersonID, uThirdPersonID);
}

bool AVP_ClientEntity_Actor::ChangeToNewWeapon(const bool bInstant, Asura_Hash_ID uExtendAnimHash, const bool bCalculateMovement)
{
	// Set ammo HUD state
	AVP_Actor_HUDHandler::SetHUDAmmoState( AVP_Actor_HUDHandler::HUD_CHANGE_WEAPON );
	const AVP_InventoryItem_Weapon* pxWeapon = GetCurrentWeapon();

	if( pxWeapon )
	{
		if( pxWeapon->GetNumSubTypes() >= 1 )
		{
			if( pxWeapon->GetSubType( 0 )->IsDynamic() )
			{
				AVP_Actor_HUDHandler::SetHUDEnergyState( AVP_Actor_HUDHandler::HUD_CHANGE_WEAPON );
			}
		}
	}

	Asura_Hash_ID uAnimHash = m_bAllowWeaponActivationAnim ? uExtendAnimHash : ASURA_HASH_ID_UNSET;
	m_bAllowWeaponActivationAnim = true;
	return m_pxAnimationHandler ? m_pxAnimationHandler->OnWeaponTweening( uAnimHash, bInstant, bCalculateMovement ) : false;
}

bool AVP_ClientEntity_Actor::BeginWeaponActivation(void)
{
	return m_pxAnimationHandler ? m_pxAnimationHandler->OnWeaponExtending() : false;
}

bool AVP_ClientEntity_Actor::EndWeaponActivation(void)
{
	return m_pxAnimationHandler ? m_pxAnimationHandler->OnWeaponExtended(ASURA_HASH_ID_UNSET) : false;
}

bool AVP_ClientEntity_Actor::BeginWeaponDeactivation( Asura_Hash_ID uDeactivationAnim, const bool bCalculateMovement )
{
	return m_pxAnimationHandler ? m_pxAnimationHandler->OnWeaponRetracting(uDeactivationAnim, bCalculateMovement) : false;
}

bool AVP_ClientEntity_Actor::EndWeaponDeactivation(void)
{
	return(true);
}

void AVP_ClientEntity_Actor::SetCollisionDescriptor( const Asura_Hash_ID uCollDescHash )
{
	const AVP_Collision_Descriptor* pxCollDesc = AVP_Collision_Descriptor::GetDataByHash( uCollDescHash );
	Asura_Assertf( pxCollDesc != NULL, ( "No Collision Descriptor found with hash 0x%08x", uCollDescHash ) );
	if ( pxCollDesc != NULL && m_pxDynamicObject != NULL && m_pxDynamicObject->GetCollisionObject() != pxCollDesc->GetCollisionBox() )
	{
		m_pxDynamicObject->SetCollisionObject( pxCollDesc->GetCollisionBox() );
	}
}

void AVP_ClientEntity_Actor::GetMovementDirection( Asura_AVector3& xMoveDir ) const
{
	if ( m_pxMoveDirInterpolator )
	{
		m_pxMoveDirInterpolator->GetCurrent( xMoveDir );
	}
	else
	{
		xMoveDir = m_xMovementDirection;
	}
}

void AVP_ClientEntity_Actor::SetMovementDirection( const Asura_AVector3& xMoveDir, const float fServerTime /*= -1.0f*/ )
{
	if ( m_pxMoveDirInterpolator )
	{
		m_pxMoveDirInterpolator->Update( xMoveDir, fServerTime );
	}
	else
	{
		m_xMovementDirection = xMoveDir;
	}
}

void AVP_ClientEntity_Actor::OnEntityActivate()
{
	PARENT::OnEntityActivate();
	AVP_Inventory_Item* pxCurrentWeapon = m_pxInventorySystem != NULL ? m_pxInventorySystem->GetCurrentItem( uAVP_INVENTORY_GROUPHASH_WEAPON ) : NULL;
	if ( pxCurrentWeapon != NULL )
	{
		pxCurrentWeapon->RequestActivation( true );
	}
}

void AVP_ClientEntity_Actor::OnEntityDeactivate()
{
	AVP_Inventory_Item* pxCurrentWeapon = m_pxInventorySystem != NULL ? m_pxInventorySystem->GetCurrentItem( uAVP_INVENTORY_GROUPHASH_WEAPON ) : NULL;
	if ( pxCurrentWeapon != NULL )
	{
		pxCurrentWeapon->RequestDeactivation( true );
	}
	PARENT::OnEntityDeactivate();
}

void AVP_ClientEntity_Actor::HealthChanged(float fHealth)
{
	const float	fHealthDifference	= fHealth - GetHealth();

	if(fHealthDifference > 0.0f)
	{
		OnApplyHealth(fHealthDifference);
	}
}

void AVP_ClientEntity_Actor::OnApplyHealth(const float fHealthIncrement)
{
	if(m_pxHUDHandler != NULL)
	{
		m_pxHUDHandler->OnApplyHealth(fHealthIncrement);
	}
}

void AVP_ClientEntity_Actor::OnApplyDamage( const AVP_EntityDamage_ApplyToEntity& xDamage )
{
	PARENT::OnApplyDamage( xDamage );

	if (GetAttachmentHandler()) GetAttachmentHandler()->OnDamage(xDamage);

	if ( xDamage.GetAmount() > 0.0f )
	{
		Asura_Hash_ID uSurfaceHash = GetSurfaceType(xDamage.GetBoneID(), false);
		AVP_SurfaceEffect_System::PlayDamageSurfaceEffect(uSurfaceHash, xDamage, GetGuid());

		if(!IsDead())
		{
			if(m_pxHUDHandler)
			{
				m_pxHUDHandler->OnApplyDamage(xDamage);	
			}
		}
	}
}

void AVP_ClientEntity_Actor::OnDeath( const AVP_EntityDamage_ApplyToEntity& xDamage )
{
	Asura_Vector_3 xPos;
	Asura_Vector_3 xNorm;
	xDamage.GetPointOfImpact( xPos );
	xDamage.GetNormal( xNorm );


	Asura_Hash_ID uSurfaceHash = GetSurfaceType(xDamage.GetBoneID(), true);

	// E3 Hack for Mr Wallace, Death effets played in the world rather than 'on' the entity as its proably going to be removed any sec..
	// AVP_SurfaceEffect_System::PlayDamageSurfaceEffect(uSurfaceHash, xDamage, GetGuid());
	AVP_SurfaceEffect_System::PlayDamageSurfaceEffect(uSurfaceHash, xDamage, ASURA_GUID_UNREGISTERED);

	// Inform the attachment handler that the actor has died so it may drop or delete attachments thats should be lost on death
	if (GetAttachmentHandler()) GetAttachmentHandler()->OnDeath();

	AVP_Inventory_Item* pxItem = GetInventorySystem()->GetCurrentItem( uAVP_INVENTORY_GROUPHASH_WEAPON );
	if ( pxItem != NULL )
	{
		pxItem->RequestDeactivation( true );
	}

	// Multiplayer only
	if (AVP_Settings::IsMultiPlayer())
	{
		// Who killed this actor	
		Asura_Guid uExecutorGuid = xDamage.GetFirerGuid();
		Asura_Guid uVictimGuid = GetGuid(); 
		Asura_Hash_ID uDamageType = xDamage.GetTypeHash();
		bool bStealth = xDamage.GetStealth();

		// Spam messages
		AVP_Multiplayer_GameMessageSystem::OnActorGetsKilled( uExecutorGuid, uVictimGuid, uDamageType, bStealth ); 

		// Death cam
		if (AVP_Game_Core::IsLocalPlayer( uVictimGuid ))
		{
			AVP_Player* pxLocalPlayer = AVP_Game_Core::GetPlayer(); 
			if (pxLocalPlayer)
			{
				// If we don't have an executor, make the camera follow the victim 
				if (uExecutorGuid != uVictimGuid)
				{
					pxLocalPlayer->SetExternalCameraTarget( uVictimGuid, uExecutorGuid, dbg_fExternalCamInitialDistance ); 
				}
				else
				{
					pxLocalPlayer->SetExternalCameraTarget( uVictimGuid, ASURA_GUID_UNREGISTERED, dbg_fExternalCamInitialDistance ); 
				}
			}
		}
		else if (AVP_Game_Core::IsLocalPlayer( uExecutorGuid ))
		{
			AVP_Player* pxLocalPlayer = AVP_Game_Core::GetPlayer(); 
			if (pxLocalPlayer)
			{
				// For now, follow the victim if a respawn is triggered from the server
				pxLocalPlayer->SetExternalCameraTarget( uVictimGuid, ASURA_GUID_UNREGISTERED, dbg_fExternalCamInitialDistance ); 
			}
		}
	}

	PARENT::OnDeath( xDamage );
}

bool AVP_ClientEntity_Actor::IsIdleForAutoCenter() const
{
	return m_pxInputHandler != NULL && m_pxInputHandler->IsIdle();
}

bool AVP_ClientEntity_Actor::IsIdleForFidget() const
{
	bool bIdle = false;
	if ( m_pxMovementSystem != NULL )
	{
		 bIdle = ( m_pxInputHandler == NULL || m_pxInputHandler->IsIdle() ) && ( m_pxMovementSystem->GetCurrentMove() == NULL );
	}
	return bIdle;
}

const AVP_Collision_Descriptor* AVP_ClientEntity_Actor::GetDefaultCollisionDescriptor()
{
	const Asura_Blueprint* pxBP = GetBlueprint();
	if ( pxBP )
	{
		Asura_Value xVal;
		if ( pxBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_DEFAULT_POSTURE, xVal ) )
		{
			const AVP_Collision_Descriptor* pxCollDesc = AVP_Collision_Descriptor::GetDataByHash( xVal.GetHashIDValue() );
			return pxCollDesc;
		}
	}
	// else
	return NULL;
}

bool AVP_ClientEntity_Actor::CanChangeCollisionObject( const Asura_Collision_Box& xCollisionObject )
{
	if (!m_pxDynamicObject) { return false; }

	const Asura_Vector_3&	xMyPosition		= GetCurrentPosition();
	const Asura_Matrix_3x3&	xMyOrientation	= GetCurrentOrientation();

	bool bPlacedObject = false;
	for ( u_int uPositionOffsetItr = 0; !bPlacedObject && uPositionOffsetItr < s_uNumPositionOffsets; ++uPositionOffsetItr )
	{
		switch ( m_pxDynamicObject->GetCollisionProxy() )
		{
			case Asura_Physics_Object::COLLISION_PROXY_POLYGONAL:
			{
				Asura_Assert( false, "Polygonal collision proxy not supported in this function, using OBB" );
			}	// fall through
			case Asura_Physics_Object::COLLISION_PROXY_OBB:
			{
				for ( u_int uOrientationOffsetItr = 0; !bPlacedObject && uOrientationOffsetItr < s_uNumOrientationOffsets; ++uOrientationOffsetItr )
				{
					bPlacedObject = Asura_Physics_System::CanPlaceObjectAt( xMyPosition + s_axPositionOffset[ uPositionOffsetItr ], s_axOrientationOffset[ uOrientationOffsetItr ] * xMyOrientation, xCollisionObject );
				}
			}
			break;

			case Asura_Physics_Object::COLLISION_PROXY_SPHERE:
			case Asura_Physics_Object::COLLISION_PROXY_SPHERESTACK:
			{
				bPlacedObject = Asura_Physics_System::CanPlaceSphereStackAt( xCollisionObject.GetBoundingBox(), xMyPosition + s_axPositionOffset[ uPositionOffsetItr ] );
			}
			break;

			case Asura_Physics_Object::NUM_COLLISION_PROXIES:
			{
				Asura_Assert( false, "invalid case" );
			}
			break;
		}
	}

	return bPlacedObject;
}

void AVP_ClientEntity_Actor::PostCreateInitialise( void )
{
	PARENT::PostCreateInitialise();

	const Asura_Matrix_3x3& xOri = GetCurrentOrientation();

	SetAimDirection( Asura_AVector3( xOri.e13, xOri.e23, xOri.e33 ) );
	SetLookAtPosition( Asura_AVector3( GetCurrentPosition() ) + Asura_AVector3( xOri.e13, xOri.e23, xOri.e33 ) * 10.0f );

	if ( m_pxInventorySystem )
	{
		m_pxInventorySystem->PostLoadInitialise();
	}
}

void AVP_ClientEntity_Actor::PreDestructionShutdown()
{
	if (m_pxAnimationHandler)
	{
		m_pxAnimationHandler->Shutdown();
	}

	PARENT::PreDestructionShutdown();
}

void AVP_ClientEntity_Actor::SetMoving( bool bSet )
{
	if ( bool( m_bMoving ) != bSet )
	{
		if ( bSet )
		{
			OnBeginMoving();
		}
		else
		{
			OnEndMoving();
		}
	}
}

void AVP_ClientEntity_Actor::SetMovingSlow( bool bSet )
{
	if ( bool( m_bMovingSlow ) != bSet )
	{
		if ( bSet )
		{
			OnBeginMovingSlowly();
		}
		else
		{
			OnEndMovingSlowly();
		}
	}
}

void AVP_ClientEntity_Actor::SetMovingFast( bool bSet )
{
	if ( bool( m_bMovingFast ) != bSet )
	{
		if ( bSet )
		{
			OnBeginMovingFast();
		} 
		else
		{
			OnEndMovingFast();
		}
	}
}

Asura_Hash_ID AVP_ClientEntity_Actor::GetSurfaceType( int iBoneID, bool bFatalDamage )
{
	u_int uSkinElement = GetElementFromBoneID( iBoneID );
	Asura_Hash_ID uParamHash = bFatalDamage ? uAVP_BLUEPRINT_PARAM_SURFACETYPE_FATAL : uAVP_BLUEPRINT_PARAM_SURFACETYPE_WOUND;
	const Asura_Blueprint* pxBP = GetSkinElementBlueprint( uSkinElement );
	if ( pxBP == NULL )
	{
		pxBP = GetBlueprint();
	}
	Asura_Assertf( pxBP, ( "Unable to get surface type blueprint for actor %d bone %d", GetGuid(), iBoneID ) );
	Asura_Value xVal;
	if ( pxBP && pxBP->GetParameterValueByHashID( uParamHash, xVal ) )
	{
		return xVal.GetHashIDValue();
	}
	else
	{
		return ASURA_HASH_ID_UNSET;
	}
}

void AVP_ClientEntity_Actor::DestroyMovementSystem()
{
	if (m_pxMovementSystem)
	{
		m_pxMovementSystem->AbortCurrentMove(true, true);
	}
	delete m_pxMovementSystem;
	m_pxMovementSystem = NULL;
	m_bDestroyMovementSystem = false;
}

const AVP_Actor_AnimStateData* AVP_ClientEntity_Actor::GetCurrentAnimStateData() const
{
	if (GetAnimationHandler())
	{
		return GetAnimationHandler()->GetCurrentAnimStateData();
	}
	else
	{
		Asura_Assert(false, "AVP_ClientEntity_Actor::GetCurrentAnimStateData() returning NULL due to no animation handler.");
		return NULL;
	}
}

void AVP_ClientEntity_Actor::UpdateAnimStateData( const AVP_Actor_AnimStateData* pxAnimDataLBSlow, const AVP_Actor_AnimStateData* pxAnimDataLBFast, const AVP_Actor_AnimStateData* pxAnimDataUBSlow, const AVP_Actor_AnimStateData* pxAnimDataUBFast, const bool bLoop, const float fBlendLBSpeed, const float fBlendUBSpeed, const float fOSMoveDirAngle, const float fTweenTime, const bool bSynch )
{
	if ( m_pxAnimationHandler )
	{
		m_pxAnimationHandler->UpdateAnimStateData( pxAnimDataLBSlow, pxAnimDataLBFast, pxAnimDataUBSlow, pxAnimDataUBFast, bLoop, fBlendLBSpeed, fBlendUBSpeed, fOSMoveDirAngle, fTweenTime, bSynch );
	}
}

void AVP_ClientEntity_Actor::PauseAnimState()
{
	if ( m_pxAnimationHandler )
	{
		m_pxAnimationHandler->PauseCurrentAnimState();
	}
}

void AVP_ClientEntity_Actor::PlayAnimState()
{
	if ( m_pxAnimationHandler )
	{
		m_pxAnimationHandler->PlayCurrentAnimState();
	}
}

AVP_ClientEntity_Actor::AimDirectionUsage AVP_ClientEntity_Actor::GetAimDirectionUsage() const
{
	const Asura_Movement_System* pxMS = GetMovementSystem();
	const AVP_Move* pxMove = pxMS != NULL ? static_cast< const AVP_Move* >( pxMS->GetCurrentMove() ) : NULL;
	if ( pxMove )
	{
		return pxMove->GetAimDirectionUsage();
	}
	else
	{
		return static_cast< AVP_ClientEntity_Actor::AimDirectionUsage >( m_eAimDirectionUsage );
	}
}

const Asura_AVector3 AVP_ClientEntity_Actor::GetTargetAimDirection() const
{
	const Asura_Movement_System* pxMS = GetMovementSystem();
	const AVP_Move* pxMove = pxMS != NULL ? static_cast< const AVP_Move* >( pxMS->GetCurrentMove() ) : NULL;
	if ( pxMove )
	{
		const Asura_AVector3& xDir = pxMove->GetAimDirection();
		if (xDir.ApproximatelyEquals(Asura_Maths::ZeroAVector3))
		{
			// Zero aim vector => use actor forward vector.
			const Asura_AMatrix3x3 xOri(GetCurrentOrientation());
			return xOri.GetColumn2();
		}
		else
		{
			return xDir;
		}
	}
	else
	{
		return m_xTargetAimDirection;
	}
}

float AVP_ClientEntity_Actor::GetAimDirectionLimitAngle() const
{
	const Asura_Movement_System* pxMS = GetMovementSystem();
	const AVP_Move* pxMove = pxMS != NULL ? static_cast< const AVP_Move* >( pxMS->GetCurrentMove() ) : NULL;
	if ( pxMove )
	{
		return pxMove->GetAimDirectionLimitAngle();
	}
	else
	{
		return m_fAimDirectionLimitAngle;
	}
}

float AVP_ClientEntity_Actor::GetAimDirectionAngVel() const
{
	const Asura_Movement_System* pxMS = GetMovementSystem();
	const AVP_Move* pxMove = pxMS != NULL ? static_cast< const AVP_Move* >( pxMS->GetCurrentMove() ) : NULL;
	if ( pxMove )
	{
		return pxMove->GetAimDirectionAngVel();
	}
	else
	{
		return m_fAimDirectionAngVel;
	}
}

AVP_ClientEntity_Actor::LookAtPositionUsage AVP_ClientEntity_Actor::GetLookAtPositionUsage() const
{
	const Asura_Movement_System* pxMS = GetMovementSystem();
	const AVP_Move* pxMove = pxMS != NULL ? static_cast< const AVP_Move* >( pxMS->GetCurrentMove() ) : NULL;
	if ( pxMove )
	{
		return pxMove->GetLookAtPositionUsage();
	}
	else
	{
		return static_cast< AVP_ClientEntity_Actor::LookAtPositionUsage >( m_eLookAtPositionUsage );
	}
}

const Asura_AVector3& AVP_ClientEntity_Actor::GetTargetLookAtPosition() const
{
	const Asura_Movement_System* pxMS = GetMovementSystem();
	const AVP_Move* pxMove = pxMS != NULL ? static_cast< const AVP_Move* >( pxMS->GetCurrentMove() ) : NULL;
	if ( pxMove )
	{
		return pxMove->GetLookAtPosition();
	}
	else
	{
		return m_xTargetLookAtPosition;
	}
}

float AVP_ClientEntity_Actor::GetLookDirectionLimitAngle() const
{
	const Asura_Movement_System* pxMS = GetMovementSystem();
	const AVP_Move* pxMove = pxMS != NULL ? static_cast< const AVP_Move* >( pxMS->GetCurrentMove() ) : NULL;
	if ( pxMove )
	{
		return pxMove->GetLookDirectionLimitAngle();
	}
	else
	{
		return m_fLookDirectionLimitAngle;
	}
}

float AVP_ClientEntity_Actor::GetLookDirectionLerpTimeScale() const
{
	const Asura_Movement_System* pxMS = GetMovementSystem();
	const AVP_Move* pxMove = pxMS != NULL ? static_cast< const AVP_Move* >( pxMS->GetCurrentMove() ) : NULL;
	if ( pxMove )
	{
		return pxMove->GetLookDirectionLerpTimeScale();
	}
	else
	{
		return m_fLookDirectionLerpTimeScale;
	}
}

const AVP_InventoryItem_Weapon* AVP_ClientEntity_Actor::GetCurrentWeapon() const
{
	const AVP_Inventory_System* pxInventory = GetInventorySystem();
	if(pxInventory)
	{
		AVP_Inventory_Group* pxGroup = pxInventory->GetGroup(uAVP_INVENTORY_GROUPHASH_WEAPON);
		if(pxGroup)
		{
			return static_cast<const AVP_InventoryItem_Weapon*> (pxGroup->GetCurrentInventoryItem());
		}
	}
	return NULL;
}

void AVP_ClientEntity_Actor::ActivateCurrentWeapon()
{
	const AVP_Inventory_System* pxInventory = GetInventorySystem();
	if(pxInventory)
	{
		AVP_Inventory_Group* pxGroup = pxInventory->GetGroup(uAVP_INVENTORY_GROUPHASH_WEAPON);
		if(pxGroup)
		{
			AVP_InventoryItem_Weapon* pxWeapon = static_cast<AVP_InventoryItem_Weapon*> (pxGroup->GetCurrentInventoryItem());
			if ( pxWeapon )
			{
				pxWeapon->RequestActivation( true );
			}
		}
	}
}

void AVP_ClientEntity_Actor::DeactivateCurrentWeapon()
{
	const AVP_Inventory_System* pxInventory = GetInventorySystem();
	if(pxInventory)
	{
		AVP_Inventory_Group* pxGroup = pxInventory->GetGroup(uAVP_INVENTORY_GROUPHASH_WEAPON);
		if(pxGroup)
		{
			AVP_InventoryItem_Weapon* pxWeapon = static_cast<AVP_InventoryItem_Weapon*> (pxGroup->GetCurrentInventoryItem());
			if ( pxWeapon )
			{
				pxWeapon->RequestDeactivation( true );
			}
		}
	}
}

AVP_Settings::Team AVP_ClientEntity_Actor::GetTeam(void) const
{
	Asura_Guid uGuid = GetGuid();
	if(AVP_Settings::IsMultiPlayer())
	{
		AVP_ClientEntity_PlayerInfo* pxPlayerInfo = AVP_ClientEntity_PlayerInfo::FindEntity(uGuid);
		if(pxPlayerInfo)
		{
			return pxPlayerInfo->GetMultiplayerTeam();
		}
	}

	// Not multiplayer or failed to find player info, use species(?!) system instead.
	return static_cast<AVP_Settings::Team>(AVP_Settings::GetSpeciesByClassification(static_cast<AVP_ENTITY_CLASSIFICATION>(Classification)));
}

void AVP_ClientEntity_Actor::UpdateLaserData( bool bUsingLaser, Asura_Vector_3 xFirePosition /*= Asura_Maths::ZeroVector3*/, Asura_Vector_3 xFireDirection /*= Asura_Maths::ZeroVector3*/, float fRange /*= 0.0f*/ )
{
	m_bUsingLaser = bUsingLaser;
	m_xLaserPosition = xFirePosition;
	m_xLaserDirection = xFireDirection;
	m_fLaserRange = fRange;
}	

bool  AVP_ClientEntity_Actor::IntersectsSphere(const Asura_Vector_3& xPos, float fRadius) const
{
	// Bit nasty this, we are going to queery the sphere stack in the collision objects, assumptions made about
	// type of physics object present.
	if (!GetPhysicsObject()) return false;
	// Get pointer to rigid body.
	const Asura_Physics_RigidBodyInstance* pxRigidBodyInstace= GetPhysicsObject()->GetRigidBodyInstance();
	if (!pxRigidBodyInstace) return false;
	// Create constant reference to the instance
	const Asura_Physics_RigidBodyInstance& xInstance= *GetPhysicsObject()->GetRigidBodyInstance();
	// Construct a sphere group from the instance.
	Asura_Physics_SphereGroup* pxSphereGroup = new Asura_Physics_SphereGroup(xInstance);

	float fSphereGroupRadius = pxSphereGroup->GetSphereRadius();
	const Asura_AVector3* xSphereGroupPos = pxSphereGroup->GetSphereCentres();
	float fRad2 = fSphereGroupRadius + fRadius;
	fRad2 *= fRad2;

	for (u_int i=0 ; i < pxSphereGroup->GetNumberOfSpheres() ; i++)
	{
		Asura_Vector_3 xFromAToB = Asura_Vector_3(xSphereGroupPos[i]) - xPos;
		float fD2FromAToB = xFromAToB.MagnitudeSquared();
		if (fD2FromAToB < fRad2) 
		{	
			delete pxSphereGroup;
			return true;
		}
	}

	delete pxSphereGroup;
	return false;
}

DEBUGVAR float dbg_fCoarseIntersectionRadius = 2.0f;
DEBUGVAR float dbg_fCoarseIntersectionHeight = 1.0f;

bool AVP_ClientEntity_Actor::CoarseIntersectsSphere(const Asura_Vector_3& xPos, float fRadius) const
{
	Asura_Vector_3 xActorPos = GetCurrentPosition(); // Assumed to be at feet
	xActorPos.y -= dbg_fCoarseIntersectionHeight;	// Raise it up so sphere is touching the ground.

	Asura_Vector_3 xAToB = xActorPos - xPos;
	float fDist = fRadius + dbg_fCoarseIntersectionRadius;

	if (xAToB.MagnitudeSquared() < (fDist*fDist)) return true;
	return false;
}


bool  AVP_ClientEntity_Actor::IntersectsBoundingBox(const Asura_Bounding_Box& xBB) const
{
	// Bit nasty this, we are going to queery the sphere stack in the collision objects, assumptions made about
	// type of physics object present.
	if (!GetPhysicsObject()) return false;
	// Get pointer to rigid body.
	const Asura_Physics_RigidBodyInstance* pxRigidBodyInstace= GetPhysicsObject()->GetRigidBodyInstance();
	if (!pxRigidBodyInstace) return false;
	// Create constant reference to the instance
	const Asura_Physics_RigidBodyInstance& xInstance= *GetPhysicsObject()->GetRigidBodyInstance();
	// Construct a sphere group from the instance.
	Asura_Physics_SphereGroup* pxSphereGroup = new Asura_Physics_SphereGroup(xInstance);

	float fSphereGroupRadius = pxSphereGroup->GetSphereRadius();
	const Asura_AVector3* xSphereGroupPos = pxSphereGroup->GetSphereCentres();

	for (u_int i=0 ; i < pxSphereGroup->GetNumberOfSpheres() ; i++)
	{
		if (xBB.IntersectsSphere(Asura_Vector_3(xSphereGroupPos[i]), fSphereGroupRadius))
		{	
			delete pxSphereGroup;
			return true;
		}
	}

	delete pxSphereGroup;
	return false;
}

DEBUGVAR float dbg_fCoarseIntersectionDistance = 3.0f;

bool  AVP_ClientEntity_Actor::CoarseIntersectsBoundingBox(const Asura_Bounding_Box& xBB) const
{
	Asura_Vector_3 xActorPos = GetCurrentPosition(); // Assumed to be at feet
	xActorPos.y -= dbg_fCoarseIntersectionHeight;	// Raise it up to the 'middle'
	Asura_Vector_3 xAToB = xActorPos - xBB.CalculateCentre();
	if (xAToB.MagnitudeSquared() < (dbg_fCoarseIntersectionRadius*dbg_fCoarseIntersectionRadius)) return true;
	return false;
}


/// End of file. ///
