//////////////////////////////////////////////////////////////////////////////
//
// AVP_ClientActor_Vertebrate.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

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

// Engine
#include "Animation/Asura_AnimationBlend.h"
#include "Attractors/Asura_Attractor_System.h"
#include "Blueprint/Asura_Blueprint.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "Core/Asura_Maths.h"
#include "Core/Asura_StringBuffer.h"
#include "Entity/Asura_PosAndOriInterpolator.h"
#include "Entity/Ragdoll/Asura_Ragdoll_Wrapper.h"
#include "Physics/Asura_Physics.h"
#include "Physics/Asura_Physics_Ragdoll.h"
#include "Sound/Asura_Sound_Event_Storage.h"
#include "Sound/Asura_Dynamic_Music_System.h"
#include "SpecialFX/Asura_Decal_Manager.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"
#include "SpecialFX/PFX/Asura_PFX_Entity_Effect.h"
#include "SpecialFX/PFX/Asura_PFX_Source.h"

// Project
#include "Actor/AVP_Actor_AttachmentHandler.h"
#include "Actor/AVP_Actor_FidgetAnimHandler.h"
#include "Actor/AVP_Actor_HUDHandler.h"
#include "Actor/AVP_Actor_PhysicsObjectHandler.h"
#include "Actor/AVP_Actor_PlayerAnimStateHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_AnimationHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_CameraHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_Description.h"
#include "Actor/Vertebrates/AVP_Vertebrate_Focus.h"
#include "Actor/Vertebrates/AVP_Vertebrate_InputHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_HUDHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_JumpHandler.h"
#include "Actor/Vertebrates/Predator/AVP_Predator_Recon.h"
#include "Animation/AVP_Animation_BodyRig.h"
#include "Animation/AVP_AnimationAttachment_Ragdoll.h"
#include "Animation/AVP_AnimFlags.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Camera/AVP_CameraShake_System.h"
#include "Core/AVP_Core.h"
#include "Core/AVP_Game_Core.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/Vertebrates/AVP_ServerActor_Vertebrate.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_Vertebrate_Messages.h"
#include "Entity/PhysicalObject/Gib/AVP_ClientEntity_Gib.h"
#include "Entity/PhysicalObject/Projectile/AVP_ClientProjectile_GenericProjectile.h"
#include "Inventory/AVP_Inventory_HashIDs.h"
#include "Inventory/AVP_Inventory_Item.h"
#include "Inventory/AVP_Inventory_Group.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Moves/AVP_Movement_System.h"
#include "Moves/AVP_Moves.h"
#include "Moves/Vertebrates/AVP_MovesMelee.h"
#include "Moves/Vertebrates/Alien/AVP_Alien_MovesBranch.h"
#include "Moves/Vertebrates/Predator/AVP_Predator_MovesBranch.h"
#include "Network/AVP_Player.h"
#include "Rendering/Asura_Project_Skin_Elements.h"
#include "SpecialFX/PFX/AVP_PFX_Entity_Fire.h"

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

// AVP Axon
#include "Behaviour/Axon_Project_Behaviour.h" // to access AI sensitivity flags
#include "Core/AVP_Navigation.h"

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

#define AVP_MAX_BLOOD_TRAIL_PFX 3
DEBUGVAR float		dbg_fMaxMovingOrientationAngVel = Asura_Maths::DegToRad( 360.0f ); // can rotate up to 360 degrees per second
DEBUGVAR float		dbg_fMaxAimRightLeftLimit = Asura_Maths::Pi; // Setting this to zero and the above bool to false resets things to the old style of no turn anims and no left-right aiming without changing orientation.

//DVSVAR float	dvs_fDownwardMovementMultiplier		= 1.0f;

#if defined(AVP_ALLOW_DEBUG_MOVEMENT)
static const float g_fCameraMovementSpeed = 10.0f;
#endif //#if defined(AVP_ALLOW_DEBUG_MOVEMENT)


//
// AVP_ClientActor_Vertebrate
//

// this should match the enum in the client entity actor
static const u_int s_uFootstepFlags[ AVP_ClientEntity_Actor::MAX_NUM_FEET ] = 
{
	AVP_ANIMFLAG_FOOTSTEP_REAR_LEFT | AVP_ANIMFLAG_FOOTSTEP_REAR_LEFT_RUN,
	AVP_ANIMFLAG_FOOTSTEP_REAR_RIGHT | AVP_ANIMFLAG_FOOTSTEP_REAR_RIGHT_RUN,
	AVP_ANIMFLAG_FOOTSTEP_FRONT_LEFT | AVP_ANIMFLAG_FOOTSTEP_FRONT_LEFT_RUN,
	AVP_ANIMFLAG_FOOTSTEP_FRONT_RIGHT | AVP_ANIMFLAG_FOOTSTEP_FRONT_RIGHT_RUN
};

const Asura_Hash_ID AVP_ClientActor_Vertebrate::s_uBlockStartParameterID= 0x4DC8CFF5;	//Asura_GetHashID("BlockStart");
const Asura_Hash_ID AVP_ClientActor_Vertebrate::s_uBlockLoopParameterID	= 0x340B7CB1;	//Asura_GetHashID("BlockLoop");
const Asura_Hash_ID AVP_ClientActor_Vertebrate::s_uBlockEndParameterID	= 0xD8635D2E;	//Asura_GetHashID("BlockEnd");

Asura_Hashed_List<AVP_ClientActor_Vertebrate> AVP_ClientActor_Vertebrate::s_xList;
Asura_Hashed_List<AVP_ClientActor_Vertebrate> AVP_ClientActor_Vertebrate::s_xRagdolledList;


AVP_ClientActor_Vertebrate::AVP_ClientActor_Vertebrate(const u_short usFlagsToIgnore)
: PARENT(usFlagsToIgnore)
, m_fMinRate(0.0f)
, m_fMaxRate(0.0f)
, m_fCurRate(0.0f)
, m_fFootSoundVolume(1.0f)
, m_uBloodFromHitPFXHash(ASURA_HASH_ID_UNSET)
, m_uBloodFromHitDecalHash(ASURA_HASH_ID_UNSET)
, m_uBloodFromGibPFXHash(ASURA_HASH_ID_UNSET)
, m_uBloodSquirtPFXHash(ASURA_HASH_ID_UNSET)
, m_uBloodTrailBPHash( ASURA_HASH_ID_UNSET )
, m_uRightHandBloodMarker( ASURA_HASH_ID_UNSET )
, m_uLeftHandBloodMarker( ASURA_HASH_ID_UNSET )
, m_fHealthDangerFraction(0.2f)
, m_pxJumpHandler( NULL )
, m_pxFocus(NULL)
, m_pxNormalJumpSound(NULL)
, m_pxPainSoundEvent(NULL)
, m_pxDeathSoundEvent(NULL)
, m_pxDeathStingEvent(NULL)
, m_iJumpSoundHandle(ASURA_INVALID_HANDLE)
, m_iPainSoundHandle(ASURA_INVALID_HANDLE)
, m_iDeathSoundHandle(ASURA_INVALID_HANDLE)
, m_iDeathStingHandle(ASURA_INVALID_HANDLE)
, m_fTimeFalling(0.0f)
, m_fYPosAtFallStart(Asura_Maths::LargeFloat)
, m_fTimeJumping(0.0f)
, m_uVertFlags(0)
, m_eAlertLevel(PASSIVE_ALERTNESS)
, m_uConstrainedMovementAttractorID(uASURA_ATTRACTOR_ID_NONE)
, m_uDestroyedSkinElementFlags(0)
, m_fDestroyedSkinElementLastMatchTimestamp(0.0f)
, m_uPFXBurningHeadFirstPerson(ASURA_PFX_INVALID_EFFECT_ID)
, m_iNumActiveGrappleDifficulties(0)
{
	Classification = AVP_ENTITYCLASS_VERTEBRATE;

	SetNumFeet(2);

	// Initialise the sounds...

	//Grapple photon
	for (u_int u=0; u<NUM_AVP_SKIN_ELEMENT_TYPES; ++u)
	{
		m_auSkinElementBlueprints[u] = ASURA_HASH_ID_UNSET;
	}

	s_xList.AddToList( this );
}


AVP_ClientActor_Vertebrate::~AVP_ClientActor_Vertebrate( void ) 
{
	delete(m_pxJumpHandler);
	delete(m_pxFocus);

	Asura_Sound_Event_System::Stop(m_iJumpSoundHandle);
	Asura_Sound_Event_System::Stop(m_iPainSoundHandle);
	Asura_Sound_Event_System::Stop(m_iDeathSoundHandle);
	Asura_Sound_Event_System::Stop(m_iDeathStingHandle);

	s_xRagdolledList.RemoveFromListIfPresent(this);
	s_xList.RemoveFromList(this);
}


bool AVP_ClientActor_Vertebrate::InitialiseMoves(void)
{
	if(PARENT::InitialiseMoves())
	{
		Asura_Movement_System& xMovementSystem = *m_pxMovementSystem;

		new AVP_Move_Puppet(xMovementSystem);
		new AVP_Move_Flinch(xMovementSystem);

		new AVP_Move_BlockEnd(xMovementSystem);
		new AVP_Move_BlockLoop(xMovementSystem);
		new AVP_Move_BlockStart(xMovementSystem);
		new AVP_Move_BlockFlinch(xMovementSystem);

		new AVP_Move_Melee_DefaultCounter(xMovementSystem);
		new AVP_Move_Melee_DefaultAttack_Return_Hit(xMovementSystem);
		new AVP_Move_Melee_DefaultAttack_Return_Countered(xMovementSystem);
		new AVP_Move_Melee_DefaultAttack_Return_Miss(xMovementSystem);
		new AVP_Move_Melee_DefaultAttack_Start(xMovementSystem);

		new AVP_Move_Landing(xMovementSystem);

		new AVP_Move_Melee_Grapple_Finish(xMovementSystem);
		new AVP_Move_Melee_Grapple_Start(xMovementSystem);

		return(true);
	}
	else
	{
		return(false);
	}
}

bool AVP_ClientActor_Vertebrate::InitialiseSoundEvents(void)
{	
	if(PARENT::InitialiseSoundEvents())
	{
		// GetSoundEventFromBlueprints() returns NULL if it does not find the relevant sound event.
		// So it should be safe to set these here

		/////////////////////////
		// Player only sounds
		/////////////////////////
		m_pxDeathStingEvent		= GetSoundEventFromBlueprints(0x389EED8E); //PlayerDeathSting

		/////////////////////////
		// Player and AI sounds
		/////////////////////////
		m_pxNormalJumpSound		= GetSoundEventFromBlueprints(0xB5300CF4); //Jumping
		m_pxPainSoundEvent		= GetSoundEventFromBlueprints(0x003462D6); //Pain
		m_pxDeathSoundEvent		= GetSoundEventFromBlueprints(0x05B09274); //Death
	
		return(true);
	}
	else
	{
		return(false);
	}
}

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

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

	//UpdateFootSounds();
	PlayFootSounds();
	CheckForAnimFlags();

	// Tidy-up any outstanding sound-events...
	if((m_iJumpSoundHandle != ASURA_INVALID_HANDLE) && !Asura_Sound_Event_System::IsPlaying(m_iJumpSoundHandle))
	{
		Asura_Sound_Event_System::Stop(m_iJumpSoundHandle);
	}
	if((m_iPainSoundHandle != ASURA_INVALID_HANDLE) && !Asura_Sound_Event_System::IsPlaying(m_iPainSoundHandle))
	{
		Asura_Sound_Event_System::Stop(m_iPainSoundHandle);
	}
	if((m_iDeathSoundHandle != ASURA_INVALID_HANDLE) && !Asura_Sound_Event_System::IsPlaying(m_iDeathSoundHandle))
	{
		Asura_Sound_Event_System::Stop(m_iDeathSoundHandle);
	}
	if((m_iDeathStingHandle != ASURA_INVALID_HANDLE) && !Asura_Sound_Event_System::IsPlaying(m_iDeathStingHandle))
	{
		Asura_Sound_Event_System::Stop(m_iDeathStingHandle);
	}
	if (m_pxFocus)
	{
		m_pxFocus->Update();
	}

	if ( AVP_Game_Core::IsLocalPlayer( GetGuid() ) && m_pxPhysicsObjectHandler && m_pxPhysicsObjectHandler->GetPhysicsObject() )
	{
		m_pxPhysicsObjectHandler->GetPhysicsObject()->SetActorIsSuperSlidy( !m_pxPhysicsObjectHandler->IsSurfaceReorientationEnabled() && m_pxPhysicsObjectHandler->GetPhysicsObject()->IsSupported() );
	}
}


void AVP_ClientActor_Vertebrate::PlayFootSounds()
{
	// if jumping or falling, don't allow foot sounds
	bool bAllowFootsteps = true;
	if (IsPlayer())
	{
		// Only play the sounds if we are on a surface
		AVP_Actor_PhysicsObjectHandler* pxObjHandler;
		pxObjHandler = GetPhysicsObjectHandler();

		if (pxObjHandler)
		{
			Asura_Physics_Object* pxObject = pxObjHandler->GetPhysicsObject();		
			bAllowFootsteps = (!pxObject || pxObject->IsSupported());
		}
	}

	if (m_pxAnimationHandler)
	{
		if (m_pxAnimationHandler->GetAnimation())
		{
			const u_int uAnimFlags = m_pxAnimationHandler->GetAnimation()->GetFlagsForAllAnimsLastFrame();

			if (bAllowFootsteps)
			{
				u_int uFootstepAnimFlags = uAnimFlags;
				if ( IsPlayer() )
				{
					uFootstepAnimFlags = static_cast< AVP_Vertebrate_AnimationHandler* >( m_pxAnimationHandler )->GetFootStepFlags();
				}
				// the default is OK for AI as they only play one movement anim at a time (unless they're tweening)

				for ( u_int uFootItr = 0; uFootItr < GetNumFeet(); ++uFootItr )
				{
					if ( uFootstepAnimFlags & s_uFootstepFlags[ uFootItr ] )
					{
						Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_ACTORS, GetBlueprintHashID() );
						if ( pxBlueprint )
						{
							Asura_Value xValue;
							Asura_Hash_ID uActorEffectTypeBPHash = ASURA_HASH_ID_UNSET;
							Asura_Hash_ID uParamHash;
							if ( IsMovingFast() )
							{
								uParamHash = 0x6a726cb6;	// FootstepRunSurfaceType
							}
							else
							{
								uParamHash = 0x24df9c84;	// FootstepWalkSurfaceType
							}
							
							if ( pxBlueprint->GetParameterValueByHashID( uParamHash, xValue ) )
							{
								uActorEffectTypeBPHash = xValue.GetHashIDValue();
							}

							Asura_Hash_ID uFloorTypeHash = GetFloorTypeHash( uFootItr );
							const Asura_Vector_3& xFloorPos = GetFloorPosition( uFootItr );
							const Asura_Vector_3& xFloorNormal = GetFloorNormal( uFootItr );
							AVP_SurfaceEffect_System::PlayEffectTypeSurfaceEffect( uFloorTypeHash, uActorEffectTypeBPHash, xFloorPos, xFloorNormal, m_fFootSoundVolume, GetGuid() );
						}
					}
				}
			}

			// Also check the fall and landing flags.
			const bool bBodyfallLight = (uAnimFlags & AVP_ANIMFLAG_BODYFALL_LIGHT) != 0;
			const bool bBodyfallHeavy = (uAnimFlags & AVP_ANIMFLAG_BODYFALL_HEAVY) != 0;
			const bool bLanding = (uAnimFlags & AVP_ANIMFLAG_JUMP_LAND) != 0;
			if (bBodyfallLight || bBodyfallHeavy || bLanding)
			{
				Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_ACTORS, GetBlueprintHashID());
				if (pxBlueprint)
				{
					Asura_Value xValue;
					Asura_Hash_ID uActorEffectTypeBPHash = ASURA_HASH_ID_UNSET;
					Asura_Hash_ID uParamHash = ASURA_HASH_ID_UNSET;
					if (bLanding)
					{
						switch ( m_eJumpLandType )
						{
							case JUMPLAND_LIGHT:
							{
								uParamHash = 0xb7782cc5;	// SurfaceType_JumpLandLight
							}
							break;

							case JUMPLAND_NORMAL:
							{
								uParamHash = 0x3F6CB6F1;	// SurfaceType_JumpLand
							}
							break;

							case JUMPLAND_HEAVY:
							{
								uParamHash = 0xb73de876;	// SurfaceType_JumpLandHeavy
							}
							break;

							default:
							{
		
							}
							break;
						}
					}
					else if (bBodyfallHeavy)
					{
						uParamHash = 0x99C639F2;	// SurfaceType_BodyfallHeavy
					}
					else // if (bBodyfallLight)
					{
						uParamHash = 0x9A007E41;	// SurfaceType_BodyfallLight
					}

					if ( uParamHash != ASURA_HASH_ID_UNSET )
					{
						if (pxBlueprint->GetParameterValueByHashID(uParamHash, xValue))
						{
							uActorEffectTypeBPHash = xValue.GetHashIDValue();
						}

						// Just create it at one of the feet, hopefully no-one will notice.
						Asura_Hash_ID uFloorTypeHash = GetFloorTypeHash(FOOT_REAR_LEFT);
						const Asura_Vector_3& xFloorPos = GetFloorPosition(FOOT_REAR_LEFT);
						const Asura_Vector_3& xFloorNormal = GetFloorNormal(FOOT_REAR_LEFT);
						AVP_SurfaceEffect_System::PlayEffectTypeSurfaceEffect(uFloorTypeHash, uActorEffectTypeBPHash, xFloorPos, xFloorNormal, 1.0f, GetGuid() );
					}
				}
			}
		}
	}
}


void AVP_ClientActor_Vertebrate::CheckForAnimFlags()
{
	const u_int uAnimFlags = GetFlagsForAllAnimsLastFrame();

	if (IsDead() && ((uAnimFlags & AVP_ANIMFLAG_CREATE_RAGDOLL) != 0))
	{
		AVP_Vertebrate_AnimationHandler* pxVertAnimHandler = static_cast<AVP_Vertebrate_AnimationHandler*>(GetAnimationHandler());
		if (pxVertAnimHandler && !pxVertAnimHandler->IsRagdoll())
		{
			MakeRagdoll();
		}
	}
}


void AVP_ClientActor_Vertebrate::Render()
{
	PARENT::Render();

	if(m_pxJumpHandler)
	{
		m_pxJumpHandler->Render();
	}
}

bool AVP_ClientActor_Vertebrate::HandleMessage(const Asura_Message* pxMessage)
{
	// What sort of message is it?
	switch(pxMessage->GetMsgID())
	{
		case AVP_MSGID_ENABLE_IK_SOLVER:
		{
			AVP_Vertebrate_AnimationHandler* pxAnimationHandler = static_cast< AVP_Vertebrate_AnimationHandler* >( m_pxAnimationHandler );
			if ( pxAnimationHandler != NULL )
			{
				pxAnimationHandler->EnableIKSolver();
			}
			return true;
		}

		case AVP_MSGID_DISABLE_IK_SOLVER:
		{
			AVP_Vertebrate_AnimationHandler* pxAnimationHandler = static_cast< AVP_Vertebrate_AnimationHandler* >( m_pxAnimationHandler );
			if ( pxAnimationHandler != NULL )
			{
				pxAnimationHandler->DisableIKSolver();
			}
			return true;
		}

		case AVP_MSGID_FLINCH:
		{
			const AVP_Msg_Flinch* pxData = static_cast<const AVP_Msg_Flinch*>(pxMessage->GetData());
			if (pxData)
			{
				StartFlinch(*pxData);
			}

			return true;
		}

		case AVP_MSGID_DISMEMBER:
		{
			const AVP_Msg_Dismember* pxData = static_cast<const AVP_Msg_Dismember*>(pxMessage->GetData());
			if (pxData)
			{
				const bool bAlwaysFatal = pxData->GetDamage() < 0.0f;

				// First, create the gib.
				const Asura_Vector_3& xGibDirection = pxData->GetGibDirection();
				const float fGibImpulse = pxData->GetGibImpulse();
				const u_int uGibbedElement = pxData->GetElement();
				if (!xGibDirection.ApproximatelyEquals(Asura_Maths::ZeroVector3))
				{
					CreateSkinElementGib(uGibbedElement, xGibDirection, fGibImpulse);
				}
				DestroySkinElement(uGibbedElement);

				// Second, create the ragdoll if this is a fatal hit.
				if (bAlwaysFatal && !IsDead()) // Everything else is handled by the normal damage code if damage is positive.
				{
					UpdateHealth(0.0f, pxMessage->GetSentTime());
					if (IsDead())
					{
						AVP_EntityDamage_ApplyToEntity xRagdollDamage(uAVP_BLUEPRINT_NAME_DAMAGE_DISMEMBER, pxData->GetFromGuid(), pxData->GetDamagePosition(), pxData->GetRagdollDirection(), -pxData->GetRagdollDirection(), Guid, 0.0f, Asura_Maths::LargeFloat, pxData->GetRagdollImpulse());
						OnDeath(xRagdollDamage);
					}
				}
			}

			return true;
		}

		case AVP_MSGID_PLAY_DISTRACTION_SOUND:
		{
			const AVP_Msg_CreateSound* pxData = static_cast<const AVP_Msg_CreateSound*>( pxMessage->GetData() );
			if ( pxData )
			{
				PlayDistractionSound( pxData->GetOriginatesFromPlayer(), pxData->GetSoundHashId() );
			}
			return true;
		}

		case AVP_MSGID_ACTIVEGRAPPLE_RESULT:
		{
			// It's always the attacker that receives this message.
			const AVP_Message_Vertebrate_ActiveGrappleResult* pxData = static_cast<const AVP_Message_Vertebrate_ActiveGrappleResult*>(pxMessage->GetData());

			AVP_Vertebrate_HUDHandler* pxHUDHandler = static_cast<AVP_Vertebrate_HUDHandler*>(GetHUDHandler());
			if (pxHUDHandler)
			{
				AVP_VERTEBRATE_HUDHANDLER_ACTIVEGRAPPLE_RESULT eResult;
				if (pxData->GetAttackerVictorious())
				{
					eResult = pxData->GetCriticalVictory() ? AVP_VERTEBRATE_ACTIVEGRAPPLE_RESULT_CRITICALSUCCESS : AVP_VERTEBRATE_ACTIVEGRAPPLE_RESULT_SUCCESS;
				}
				else
				{
					eResult = AVP_VERTEBRATE_ACTIVEGRAPPLE_RESULT_FAILURE;
				}

				pxHUDHandler->SetActiveGrappleResult(eResult);

				if (pxData->IsFinalResult())
				{
					pxHUDHandler->FinaliseActiveGrappleResult();
				}
			}

			// Get the victim from the current move.
			if (m_pxMovementSystem && m_pxMovementSystem->IsMoveInProgress())
			{
				const AVP_Move* pxCurrentMove = static_cast<const AVP_Move*>(m_pxMovementSystem->GetCurrentMove());
				AVP_ClientActor_Vertebrate* pxVictim = AVP_ClientActor_Vertebrate::FindEntity(pxCurrentMove->GetForcedMeleeTarget());
				AVP_Vertebrate_HUDHandler* pxVictimHUDHandler = pxVictim ? static_cast<AVP_Vertebrate_HUDHandler*>(pxVictim->GetHUDHandler()) : NULL;
				if (pxVictimHUDHandler)
				{
					AVP_VERTEBRATE_HUDHANDLER_ACTIVEGRAPPLE_RESULT eResult;
					if (!pxData->GetAttackerVictorious())
					{
						eResult = pxData->GetCriticalVictory() ? AVP_VERTEBRATE_ACTIVEGRAPPLE_RESULT_CRITICALSUCCESS : AVP_VERTEBRATE_ACTIVEGRAPPLE_RESULT_SUCCESS;
					}
					else
					{
						eResult = AVP_VERTEBRATE_ACTIVEGRAPPLE_RESULT_FAILURE;
					}

					pxVictimHUDHandler->SetActiveGrappleResult(eResult);

					if (pxData->IsFinalResult())
					{
						pxVictimHUDHandler->FinaliseActiveGrappleResult();
					}
				}
			}

			// The movement system needs this too.
			if (m_pxMovementSystem)
			{
				m_pxMovementSystem->HandleMessage(pxMessage);
			}

			return true;
		}

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

			u_int uDamageType = pxData->GetDamageType();
			bool bRightHanded = pxData->GetIsRightHanded();
			Asura_Guid uHitEntity = pxData->GetHitEntity();
			
			AttachBloodTrails(uHitEntity, uDamageType, bRightHanded);

			// The movement system needs this too.
			if (m_pxMovementSystem)
			{
				m_pxMovementSystem->HandleMessage(pxMessage);
			}

			return true;
		}
		

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

void AVP_ClientActor_Vertebrate::AttachBloodTrails(Asura_Guid uHitEntity, u_int uDamageType, bool bRightHanded)
{
	AVP_ClientActor_Vertebrate* pxHitEntity = FindEntity(uHitEntity);
	if(pxHitEntity)
	{
		Asura_Vector_3 xPosition(Asura_Maths::ZeroVector3);
		Asura_Matrix_3x3 xOrientation(Asura_Maths::IdentityMatrix3x3);

		Asura_Hash_ID uMarkerHash = GetBloodTrailMarkerHash(bRightHanded);

		if(uMarkerHash != ASURA_HASH_ID_UNSET && GetAnimation()->GetMarkerLocationFromHash(uMarkerHash, &xPosition, &xOrientation, true))
		{
			Asura_Hash_ID uBPHash = pxHitEntity->GetBloodTrailBPHash();

			if(uBPHash)
			{
				Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_WEAPONEFFECTS, uBPHash );
				if(pxBlueprint)
				{
					Asura_Hash_ID auPFXHash[ AVP_MAX_BLOOD_TRAIL_PFX ];

					AVP_SurfaceEffect_PFXEnumerator xEnum( auPFXHash );
					u_int uNumPFX = pxBlueprint->EnumerateParameters( "PFX.", 2, xEnum );
					if ( uNumPFX >= AVP_MAX_BLOOD_TRAIL_PFX )
					{
						uNumPFX = AVP_MAX_BLOOD_TRAIL_PFX;
					}

					for(u_int u = 0; u<uNumPFX; ++u)
					{
						const u_int uPFXEffectID = Asura_PFX_Entity_Effect::Create(xPosition, xOrientation, auPFXHash[u], 0, ASURA_PFX_EFFECT_CAMERA_FLAGS, GetGuid());
						Asura_AnimationAttachment* pxAttachment = new Asura_AnimationAttachment_PFX(uPFXEffectID, true);

						if (pxAttachment)
						{
							pxAttachment->LinkAttachment(GetAnimation(), uMarkerHash);
						}
					}
				}
			}
		}
	}
}

DEBUGVAR float dbg_fSkinElementMatchMaxDifferenceTimeLapse = 1.0f;

void AVP_ClientActor_Vertebrate::ProcessClientUpdate(const AVP_Message_Vertebrate_ClientUpdate* pxData, float fServerTime, bool bProcessPosAndOri /*= true*/)
{
#ifndef ASURA_NO_NETWORK_LAG_COMPENSATION
	const bool bAllowPosAndOrientationProcessing = bProcessPosAndOri && (m_pxPosAndOriInterpolator != NULL);
#else // #ifndef ASURA_NO_NETWORK_LAG_COMPENSATION
	const bool bAllowPosAndOrientationProcessing = bProcessPosAndOri;
#endif

	// Call the base-class...
	PARENT::ProcessClientUpdate(pxData, fServerTime, bAllowPosAndOrientationProcessing);

	m_bEnemyInMeleeRange = pxData->GetEnemyInMeleeRange();

	// Check the state of destroyed skin elements.
	const u_int uServerDestroyedElements = pxData->GetDestroyedSkinElementFlags();
	if (uServerDestroyedElements == m_uDestroyedSkinElementFlags)
	{
		// Match, update our timer.
		m_fDestroyedSkinElementLastMatchTimestamp = Asura_Maths::Max(m_fDestroyedSkinElementLastMatchTimestamp, fServerTime);
	}
	else
	{
		// If it's been too long between accurate matches, assume we've lost a message that told us of the destruction. Silently get rid of them.
		if ((fServerTime - m_fDestroyedSkinElementLastMatchTimestamp) >= dbg_fSkinElementMatchMaxDifferenceTimeLapse)
		{
			const u_int uElementsToDestroy = uServerDestroyedElements & ~m_uDestroyedSkinElementFlags;
			const AVP_EntityDamage_ApplyToEntity xTempDamage(uAVP_BLUEPRINT_NAME_DAMAGE_MELEE, Guid, GetCurrentPosition(), Asura_Vector_3(0.0f, 1.0f, 0.0f), Asura_Vector_3(0.0f, -1.0f, 0.0f), Guid, GetHealth(), 0.0f, 0.0f, -1, uElementsToDestroy);
			DestroySkinElements(xTempDamage, true);
			m_fDestroyedSkinElementLastMatchTimestamp = fServerTime;
		}
	}
}

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

	Asura_Assert(uVersion <= AVP_ServerActor_Vertebrate::CURRENT_CLIENTCREATE_VERSION, "Bad chunk version in vertebrate client create!");

	// Call the base-class...
	PARENT::ReadFromClientCreateStream( xStream, fServerTime );
	
	bool bEnemyInMeleeRange = false;
	bool bBlockActive = false;
	
	xStream >> bEnemyInMeleeRange;
	xStream >> bBlockActive;

	// Check the state of destroyed skin elements.
	u_int uServerDestroyedElements = 0;
	xStream >> uServerDestroyedElements;
	if (uServerDestroyedElements == m_uDestroyedSkinElementFlags)
	{
		const u_int uElementsToDestroy = uServerDestroyedElements & ~m_uDestroyedSkinElementFlags;
		const AVP_EntityDamage_ApplyToEntity xTempDamage(uAVP_BLUEPRINT_NAME_DAMAGE_MELEE, Guid, GetCurrentPosition(), Asura_Vector_3(0.0f, 1.0f, 0.0f), Asura_Vector_3(0.0f, -1.0f, 0.0f), Guid, GetHealth(), 0.0f, -1, uElementsToDestroy);
		DestroySkinElements(xTempDamage, true);
	}
}


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::ApplyImpulseToRagdoll
// Author       : Andy Tate
// Date         : 25-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientActor_Vertebrate::ApplyImpulseToRagdoll(const AVP_EntityDamage_ApplyToEntity& xDamage)
{
	const float	fImpulseStrength	= xDamage.GetImpulseStrength();

	// Do we need to apply an impulse to the ragdoll?
	if(fImpulseStrength != 0.0f)
	{
		const Asura_Handle iRagdollHandle = static_cast<AVP_Vertebrate_AnimationHandler*>( m_pxAnimationHandler )->GetRagdollHandle();
		Asura_Ragdoll_Wrapper* pxRagdollWrapper = Asura_Ragdoll_Wrapper::GetRagdollWrapper( iRagdollHandle );
		Asura_Physics_Ragdoll* pxRagdoll = NULL;
		if ( pxRagdollWrapper )
		{
			pxRagdoll = pxRagdollWrapper->GetPhysicsRagdollPointer();
		}
		if ( pxRagdoll )
		{
			Asura_Vector_3			xImpulse;
			Asura_Vector_3			xPosition;
			Asura_Vector_3			xNormal;

			xDamage.GetDirection(xImpulse);
			xDamage.GetPointOfImpact(xPosition);
			xDamage.GetNormal(xNormal);

			xImpulse	-= xNormal;
			xImpulse	*= fImpulseStrength / xImpulse.Magnitude();

			pxRagdoll->ApplyImpulseToNearbyNodes(xPosition, xImpulse);
		}
	}
}

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

void AVP_ClientActor_Vertebrate::OnDeath(const AVP_EntityDamage_ApplyToEntity& xDamage)
{
	m_xDeathCamPosition = *Asura_Camera::GetPosition();

	// Play the death sound
	// Skip it for grapple damage, as we'll be playing sounds designed for the grapple anims themselves.
	if (xDamage.GetTypeHash() != uAVP_BLUEPRINT_NAME_DAMAGE_GRAPPLE)
	{
		Asura_Sound_Event_System::Stop(m_iDeathSoundHandle);
		Asura_Sound_Event_System::PlayEvent(m_pxDeathSoundEvent, &m_iDeathSoundHandle, &GetCurrentPosition(), 1.0f, GetGuid());
	}
	
	if(IsPlayer() && AVP_Game_Core::IsLocalPlayer(GetGuid())) // When the player dies...
	{
		Asura_Sound_Event_System::Stop(m_iDeathStingHandle);
		Asura_Sound_Event_System::PlayEvent(m_pxDeathStingEvent, &m_iDeathStingHandle);

		Asura_Dynamic_Music_System::Stop();

		AVP_CameraShake_System::StopAllShakes();
	}


	m_xAnimProjectData.DisablePheromoneGlow();

	if(m_pxHUDHandler)
	{
		m_pxHUDHandler->OnDeath(xDamage);
	}

	CreateHitEffects(xDamage);
	CreateSkinElementGibs(xDamage);
	DestroySkinElements(xDamage);

	// Apply ragdoll on death ONLY if not caused by a grapple kill,
	// In a grapple we ragdoll at the anim flag, not on the aliens death
	if ( m_pxAnimationHandler != NULL )
	{
		if ( GetParentGuid() == ASURA_GUID_UNREGISTERED )
		{
			// if we were killed by a generic projectile which collects corpses then stick us to it
			AVP_ClientProjectile_GenericProjectile* pxSpear = AVP_ClientProjectile_GenericProjectile::FindEntity( xDamage.GetFromGuid() );
			if ( pxSpear != NULL && pxSpear->CollectsCorpses() && MakeRagdoll() )
			{	
				const int iBoneID = xDamage.GetBoneID();
				//if ( IsBoneInTorso( iBoneID ) )
				{
					AVP_Vertebrate_AnimationHandler* pxAnimHandler = static_cast< AVP_Vertebrate_AnimationHandler* >( m_pxAnimationHandler );
					pxSpear->AddRagdollCorpse( pxAnimHandler, iBoneID );
				}
				//else
				//{
					// Create trophy head gib or arm / leg ragdoll gib.
					// pxSpear->AddCorpse( whatever );
				//}
			}
			else
			{
				Asura_Hash_ID uDeathAnim;
				if (GetCreateImmediateRagdoll(xDamage, uDeathAnim) || !m_pxAnimationHandler->PlayDeathAnimation(uDeathAnim) )
				{
					if (MakeRagdoll())
					{
						ApplyImpulseToRagdoll(xDamage);
					}
				}
			}
		}
	}

	PARENT::OnDeath(xDamage);
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_Actor::OnApplyDamage
// Author       : Andy Tate
// Date         : 25-Jan-2007
////////////////////////////////////////////////////////////////////////////////

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

	AVP_Vertebrate_AnimationHandler* pxVertAnimHandler = static_cast<AVP_Vertebrate_AnimationHandler*>(m_pxAnimationHandler);
	
	if( pxVertAnimHandler != NULL && pxVertAnimHandler->IsRagdoll() && !pxVertAnimHandler->IsRagdollFadingOut() )
	{
		ApplyImpulseToRagdoll(xDamage);
	}

	if ( xDamage.GetAmount() > 0.0f )
	{
		if(!IsDead())
		{
			if((m_pxPainSoundEvent != NULL) && (m_iPainSoundHandle == ASURA_INVALID_HANDLE))
			{
				// This stops us playing multiple pain sounds if we get hit on numerous consecutive frames...
				Asura_Sound_Event_System::PlayEvent(m_pxPainSoundEvent, &m_iPainSoundHandle, &GetCurrentPosition(), 1.0f, GetGuid());
			}
		}

		if( pxVertAnimHandler && pxVertAnimHandler->GetFidgetAnimHandler() )
		{
			pxVertAnimHandler->GetFidgetAnimHandler()->ResetFidgetTimer();
		}

		CreateHitEffects(xDamage);
		CreateSkinElementGibs(xDamage);
		DestroySkinElements(xDamage);
	}
}

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

void AVP_ClientActor_Vertebrate::OnBeginMoving( void )
{
	PARENT::OnBeginMoving();
	if ( m_pxAnimationHandler )
	{
		m_fAnimDurationModifier = m_fAnimDurationModifierMoving;
	}
}

void AVP_ClientActor_Vertebrate::OnEndMoving( void )
{
	if ( m_pxAnimationHandler )
	{
		m_fAnimDurationModifier = 1.0f;
	}
	PARENT::OnEndMoving();
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::OnBeginMovingFast
// Author       : Chris Wallace
// Date         : 5-MArch-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientActor_Vertebrate::OnBeginMovingFast(void)
{
	AVP_Vertebrate_HUDHandler*	pxHUDHandler	= static_cast<AVP_Vertebrate_HUDHandler*>(m_pxHUDHandler);

	if(pxHUDHandler != NULL)
	{
		pxHUDHandler->OnBeginMovingFast();
	}

	PARENT::OnBeginMovingFast();
}

/// End of function 'AVP_ClientActor_Vertebrate::OnBeginMovingFast'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::OnEndMovingFast
// Author       : Chris Wallace
// Date         : 5-MArch-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientActor_Vertebrate::OnEndMovingFast(void)
{
	AVP_Vertebrate_HUDHandler*	pxHUDHandler	= static_cast<AVP_Vertebrate_HUDHandler*>(m_pxHUDHandler);

	if(pxHUDHandler != NULL)
	{
		pxHUDHandler->OnEndMovingFast();
	}

	PARENT::OnEndMovingFast();
}

/// End of function 'AVP_ClientActor_Vertebrate::OnEndMovingFast'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::OnTapJump
// Author       : Andy Tate
// Date         : 14-Dec-2006
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientActor_Vertebrate::OnBeginJumping(void)
{
	AVP_Vertebrate_CameraHandler* pxCameraHandler = static_cast< AVP_Vertebrate_CameraHandler* >( GetCameraHandler() );
	if ( pxCameraHandler )
	{
		pxCameraHandler->SetZoomLevel( *this, 0 );
	}

	if (m_pxNormalJumpSound)
	{
		Asura_Sound_Event_System::Stop(m_iJumpSoundHandle);
		Asura_Sound_Event_System::PlayEvent(m_pxNormalJumpSound, &m_iJumpSoundHandle, &GetCurrentPosition(), 1.0f, GetGuid());
	}

	m_bIsJumping = true;
	m_fTimeJumping = 0.0f;
	SetConstrainedMovementAttractorID(uASURA_ATTRACTOR_ID_NONE);
}

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

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::OnBeginFalling
// Author       : Anthony Gregson
// Date         : 03/07/08
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientActor_Vertebrate::OnBeginFalling(void)
{
	m_fYPosAtFallStart = GetCurrentPosition().y;

	if (m_pxHUDHandler)
	{
		m_pxHUDHandler->OnBeginFalling();
	}

	m_bIsJumping = false;
}

/// End of function 'AVP_ClientActor_Vertebrate::OnBeginFalling'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::OnEndFalling
// Author       : Anthony Gregson
// Date         : 03/07/08
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientActor_Vertebrate::OnEndFalling()
{
	// Slightly hacky approach to landing moves, since it only works for players. But then, for the time being they're the only ones who can use this.
	// Potentially, the server should trigger this, but this quicker feedback may be preferable.
	if (AVP_Game_Core::IsLocalPlayer(GetPlayerInfoGuid()) && m_pxMovementSystem && !m_pxMovementSystem->IsMoveInProgress())
	{
		Asura_Hash_ID uFirstPersonHash;
		Asura_Hash_ID uThirdPersonHash;

		GetLandingAnimsAndApplyDamage(uFirstPersonHash, uThirdPersonHash);

		if ((uFirstPersonHash != ASURA_HASH_ID_UNSET) || (uThirdPersonHash != ASURA_HASH_ID_UNSET))
		{
			Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_MOVE_START, sizeof(AVP_Msg_Move_Start_AnimationData));
			if (pxMessage)
			{
				AVP_Msg_Move_Start_AnimationData* pxData = static_cast<AVP_Msg_Move_Start_AnimationData*>(pxMessage->GetData());

				pxData->ClearFlags();
				pxData->SetMoveID(AVP_Move_Landing::GetStaticHash());
				pxData->SetIgnoredByOwner(false);
				pxData->SetForceAbort(false);
				pxData->SetAnimHashes(uFirstPersonHash, uThirdPersonHash);

				// Handle it locally immediately, then forward to all other machines.
				HandleMessage(pxMessage);

				pxData->SetIgnoredByOwner(true);

				Send(pxMessage, Guid);
			}
		}
	}

	// Call the landing begin, since that's potentially shared with other types of landing.
	OnBeginLanding();
}

/// End of function 'AVP_ClientActor_Vertebrate::OnEndFalling'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::OnBeginLanding
// Author       : Andy Tate
// Date         : 14-Dec-2006
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientActor_Vertebrate::OnBeginLanding(void)
{
	if(!IsDead())
	{
		Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_ACTORS, GetBlueprintHashID());
		if (pxBlueprint)
		{
			Asura_Value xValue;
			Asura_Hash_ID uActorEffectTypeBPHash = ASURA_HASH_ID_UNSET;
			Asura_Hash_ID uParamHash = ASURA_HASH_ID_UNSET;

			const float fFallDistance = GetCurrentPosition().y - m_fYPosAtFallStart;

			if ( fFallDistance < m_fMinDistanceForJumpLandNormalEffect )
			{
				uParamHash = 0xb7782cc5;	// SurfaceType_JumpLandLight
			}
			else if ( fFallDistance < m_fMinDistanceForJumpLandHeavyEffect )
			{
				uParamHash = 0x3F6CB6F1;	// SurfaceType_JumpLand
			}
			else
			{
				uParamHash = 0xb73de876;	// SurfaceType_JumpLandHeavy
			}

			if ( uParamHash != ASURA_HASH_ID_UNSET )
			{
				if (pxBlueprint->GetParameterValueByHashID(uParamHash, xValue))
				{
					uActorEffectTypeBPHash = xValue.GetHashIDValue();
				}

				// Just create it at one of the feet, hopefully no-one will notice.
				Asura_Hash_ID uFloorTypeHash = GetFloorTypeHash(FOOT_REAR_LEFT);
				const Asura_Vector_3& xFloorPos = GetFloorPosition(FOOT_REAR_LEFT);
				const Asura_Vector_3& xFloorNormal = GetFloorNormal(FOOT_REAR_LEFT);
				AVP_SurfaceEffect_System::PlayEffectTypeSurfaceEffect(uFloorTypeHash, uActorEffectTypeBPHash, xFloorPos, xFloorNormal);
			}
		}
	}

	if (m_pxHUDHandler)
	{
		m_pxHUDHandler->OnBeginLanding();
	}

	m_bIsJumping = false;
}

void AVP_ClientActor_Vertebrate::OnBeginBurning()
{	
	Asura_Hash_ID uBurningBodyFirstPersonPFXHash = ASURA_HASH_ID_UNSET;
	Asura_Hash_ID uBurningBodyThirdPersonPFXHash = ASURA_HASH_ID_UNSET;

	Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_ACTORS, GetBlueprintHashID() );
	if ( pxBlueprint )
	{
		Asura_Value xValue;

		if ( pxBlueprint->GetParameterValueByHashID( 0x1E40A02E, xValue ) )	// "Burning.Body.FirstPersonPFX"
		{
			uBurningBodyFirstPersonPFXHash = xValue.GetHashIDValue();
		}

		if ( pxBlueprint->GetParameterValueByHashID( 0x6B240B57, xValue ) )	// "Burning.Body.ThirdPersonPFX"
		{
			uBurningBodyThirdPersonPFXHash = xValue.GetHashIDValue();
		}
	}

	// put fire to eyes.
	if( this->IsUsingFirstPersonSkin() )
	{

		// If we are unable to find proper blueprint for the flame, lets use default fire, so we will know when the guy is burning.
		if( uBurningBodyFirstPersonPFXHash==ASURA_HASH_ID_UNSET )
		{
			PARENT::OnBeginBurning();
			return;
		}

		Asura_Hash_ID uCameraHashID = AVP_Vertebrate_Description::GetMarkerPointHashID( AVP_Vertebrate_Description::MPID_EYES ); 

		// create particle generator at this point.
		m_uPFXBurningHeadFirstPerson = CreatePFXEffect(uBurningBodyFirstPersonPFXHash, uCameraHashID, true );
	}
	else	// Otherwise set body on fire
	{
		u_int uNumOfSources = 0;

		Asura_PFX_Effect_Template* pxTemplate = Asura_PFX_System::GetEffectTempByHash( uBurningBodyThirdPersonPFXHash );
		if( pxTemplate )
		{
			uNumOfSources = pxTemplate->GetNumOfSources();
		}

		// we can't find source or source is empty. In both case, create default fire. (or we are unable to find proper blueprint for the flame)
		if( uNumOfSources==0 || uBurningBodyThirdPersonPFXHash==ASURA_HASH_ID_UNSET )
		{
			PARENT::OnBeginBurning();
			return;
		}
		else
		{
			// Create proper fire for person.
			m_uBurningEffectID = AVP_PFX_Entity_Fire::Create();

			AVP_PFX_Entity_Fire* pxFire = (AVP_PFX_Entity_Fire*)Asura_PFX_System::GetEffect( m_uBurningEffectID );
			if( pxFire )
			{
				const Asura_Shape_Instance& xShapeInstance = GetShapeInstance();
				float fBiggestBoneVolume = pxFire->GetBiggestBoneVolume( &xShapeInstance );

				for( u_int u=0; u<uNumOfSources; u++ )
				{
					pxFire->AddSourceTemplate( pxTemplate->GetSourceHash(u), fBiggestBoneVolume*0.25f );
				}

				// Update it first time.
				pxFire->UpdatePosition( &xShapeInstance );
			}
		}
	}
}


void AVP_ClientActor_Vertebrate::OnEndBurning()
{
	if (m_uPFXBurningHeadFirstPerson != ASURA_PFX_INVALID_EFFECT_ID)
	{
		Asura_PFX_System::StopEntityEffect(m_uPFXBurningHeadFirstPerson, false);
		m_uPFXBurningHeadFirstPerson = ASURA_PFX_INVALID_EFFECT_ID;
	}


	PARENT::OnEndBurning();
}

void AVP_ClientActor_Vertebrate::GetMeleeOrigin(Asura_Vector_3& xOrigin) const
{
	if (!GetMarkerLocationFromHash(AVP_Vertebrate_Description::GetMarkerPointHashID(AVP_Vertebrate_Description::MPID_MELEEORIGIN), &xOrigin, NULL))
	{
		xOrigin = GetCurrentPosition();
	}
}

Asura_Guid AVP_ClientActor_Vertebrate::GetMeleeTarget() const
{
	if (m_pxMovementSystem && m_pxMovementSystem->IsMoveInProgress())
	{
		const AVP_Move* pxCurrentMove = static_cast<const AVP_Move*>(m_pxMovementSystem->GetCurrentMove());
		Asura_Guid uMoveMeleeTarget = pxCurrentMove->GetForcedMeleeTarget();

		if (uMoveMeleeTarget != ASURA_GUID_UNREGISTERED)
		{
			return uMoveMeleeTarget;
		}
	}

	if (m_pxFocus && m_pxFocus->GetActive())
	{
		return m_pxFocus->GetMeleeTarget();
	}

	return ASURA_GUID_UNREGISTERED;
}


void AVP_ClientActor_Vertebrate::PlayDistractionSound( bool bOriginatesFromPlayer, Asura_Hash_ID uSoundHashId ) const
{
	const Asura_Sound_Event* pxSoundEvent = GetSoundEventFromBlueprints( uSoundHashId ); 
	if (pxSoundEvent)
	{
		if( bOriginatesFromPlayer )
		{
			Asura_Sound_Event_System::PlayEvent(pxSoundEvent, NULL, &GetCurrentPosition(), 1.0f, GetGuid());
		}
		else
		{
			// CAH at-t-mo we are assuming that if the sound does not originate form the player it is the result of the predator recon mode
			// so refer to the AVP_Predator_Recon class for all the needed data. 
			if( AVP_Predator_Recon::GetRecon().GetTargetPositionValid() )
			{
				// RECONTODO, is there any way to play a sound that only one AI entity hears??
				// if not then just play a sound that only the player can hear and just effect the single entitys AI that was supposed to hear it as if they did. 
				Asura_Handle iSoundEventHandle = ASURA_INVALID_HANDLE;
				Asura_Sound_Event_System::PlayEvent( pxSoundEvent, &iSoundEventHandle, AVP_Predator_Recon::GetRecon().GetTargetPosition() );

				if( GetGuid() == AVP_Predator_Recon::GetRecon().GetOwnerGuid() )
				{
					// if this client entity if the one that caused the sound to be played record the event handle to the recon mode data. 
					AVP_Predator_Recon::GetRecon().SetSoundHandle( iSoundEventHandle );
				}
			}
		}
	}
}


void AVP_ClientActor_Vertebrate::MakePlayer(Asura_Guid uPlayerGuid)
{
	if(m_pxJumpHandler)
	{
		Asura_Value xValue;
		Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(GetBlueprintTypeHashID(),GetBlueprintHashID());
		if(pxBlueprint && pxBlueprint->GetParameterValueByHashID(0x4248862E, xValue))	//Asura_GetHashID("TapJumpVelocity"), xValue))
		{
			const float	fSpeed	= xValue.GetFloatValue();
			m_pxJumpHandler->SetUpwardsSpeed(fSpeed);
		}
	}

	PARENT::MakePlayer(uPlayerGuid);
}

void AVP_ClientActor_Vertebrate::MakeAI(void)
{
	if(m_pxJumpHandler)
	{
		delete m_pxJumpHandler;
		m_pxJumpHandler = NULL;
	}

	if (m_pxFocus)
	{
		delete m_pxFocus;
		m_pxFocus = NULL;
	}
	
	PARENT::MakeAI();
}

void AVP_ClientActor_Vertebrate::DisconnectPlayer()
{
	if(m_pxJumpHandler)
	{
		delete m_pxJumpHandler;
		m_pxJumpHandler = NULL;
	}


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

	PARENT::DisconnectPlayer();
}

void AVP_ClientActor_Vertebrate::RenderHUD() const
{
	PARENT::RenderHUD();

	if (m_pxFocus)
	{
		m_pxFocus->Render();
	}
}

//////////////////////////////////////////////////////////////
//
// Sound FX Functions
//
//////////////////////////////////////////////////////////////

const Asura_Blueprint* AVP_ClientActor_Vertebrate::GetSkinElementBlueprint(u_int uElement) const
{
	return Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_ACTORS, m_auSkinElementBlueprints[uElement]);
}

DEBUGVAR float dbg_fMinFallSpeed = 0.0f;
DEBUGVAR float dbg_fPreFallTimeLimit = 0.2f;

void AVP_ClientActor_Vertebrate::UpdateFalling()
{
	if (m_bIsJumping)
	{
		m_fTimeJumping += Asura_Timers::GetGameTimeSlice();
	}

	if ((GetGroundType() != AVP_Actor_PhysicsObjectHandler::OFF_GROUND) || (GetConstrainedMovementAttractorID() != uASURA_ATTRACTOR_ID_NONE))
	{
		m_fTimeFalling = 0.0f;
		m_bIsFalling = false;

		if (m_bIsJumping)
		{
			m_bIsJumping = (m_fTimeJumping <= dbg_fPreFallTimeLimit);
		}
	}
	else
	{
		// Check we're falling fast enough.
		Asura_Vector_3 xVelocity;
		GetCurrentVelocity(xVelocity);
		float fSpeedInGravityDir;
		if (m_pxDynamicObject)
		{
			Asura_Vector_3 xGravityDirection(m_pxDynamicObject->GetGravityDirection());
			fSpeedInGravityDir = xVelocity * xGravityDirection;
		}
		else
		{
			fSpeedInGravityDir = xVelocity.y;
		}

		if (fSpeedInGravityDir < dbg_fMinFallSpeed)
		{
			m_fTimeFalling = 0.0f;
			m_bIsFalling = false;
		}
		else
		{
			m_fTimeFalling += Asura_Timers::GetGameTimeSlice();
			m_bIsFalling = m_fTimeFalling > dbg_fPreFallTimeLimit;
		}
	}
}

// For now, the simplest approach is whether the landing move is playing
DEBUGVAR float dbg_fIsLandingTime = 0.2f;

bool AVP_ClientActor_Vertebrate::IsLanding() const
{
	if (!m_pxMovementSystem || !m_pxMovementSystem->IsMoveInProgress()) { return false; }

	const Asura_Move* pxCurrentMove = m_pxMovementSystem->GetCurrentMove();

	if (pxCurrentMove->GetHash() != AVP_Move_Landing::GetStaticHash()) { return false; }

	// This is a hackier approach than previously, but while the anim flag for finishing early isn't in all landing anims this looks better as we'll definitely have swapped to an on-ground anim state before the landing anim begins to tween out.
	const AVP_Move_Landing* pxLandingMove = static_cast<const AVP_Move_Landing*>(pxCurrentMove);

	return pxLandingMove->GetTimeInMove() < dbg_fIsLandingTime;
}


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::CreateHitEffects
// Author       : Anthony Gregson
// Date         : 15/05/08
////////////////////////////////////////////////////////////////////////////////
void AVP_ClientActor_Vertebrate::CreateHitEffects(const AVP_EntityDamage_ApplyToEntity& xDamage)
{
	if (xDamage.GetBoneID() >= 0)
	{
		// Hit a specific bone, apply special effects to that
		DamageLimb(xDamage);
	}

	if (m_uBloodFromHitDecalHash != ASURA_HASH_ID_UNSET)
	{
		CreateBloodDecalFromDamage(m_uBloodFromHitDecalHash, xDamage);
	}
}
/// End of function 'AVP_ClientActor_Vertebrate::CreateHitEffects'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::DamageLimb
// Author       : Anthony Gregson
// Date         : 14/05/08
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientActor_Vertebrate::DamageLimb(const AVP_EntityDamage_ApplyToEntity& xDamage)
{
	const int iBoneID = xDamage.GetBoneID();

#if defined(ASURA_USE_DAMAGE_MAPS)
	if (m_pxAnimationHandler)
	{
		Asura_Animation* pxAnimation = m_pxAnimationHandler->GetAnimation();

		m_pxAnimationHandler->SetDamaged(true);
		m_pxAnimationHandler->UpdateSkin();

		// Damage the bone...
		// TODO - do we sometimes want to blend somewhere between fully damaged and undamaged?
		if (pxAnimation)
		{
			pxAnimation->SetBoneDamageMapValue(iBoneID, 1.0f);
		}
	}
#endif	// defined(ASURA_USE_DAMAGE_MAPS)

	// Create the blood-spurt...
	if (m_uBloodFromHitPFXHash != ASURA_HASH_ID_UNSET)
	{
		CreateBoneRelativeBloodspurt(xDamage, m_uBloodFromHitPFXHash);
	}

	if ((xDamage.GetTypeHash() == uAVP_BLUEPRINT_NAME_DAMAGE_BITE) && (GetElementFromBoneID(iBoneID) == AVP_SET_HEAD))
	{
		// Headbite, set up the headbit head visibility.
		ActivateHeadbitSkinElement();
	}
}
/// End of function 'AVP_ClientActor_Vertebrate::DamageLimb'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::CreateBoneRelativeBloodspurt
// Author       : Andy Tate
// Date         : 10-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientActor_Vertebrate::CreateBoneRelativeBloodspurt(const AVP_EntityDamage_ApplyToEntity& xDamage, const Asura_Hash_ID uPFXHash)
{
	const int	iBoneID	= xDamage.GetBoneID();

	if (AVP_Game_Core::IsLocalPlayer(GetGuid()) || (iBoneID < 0))
	{
		return;
	}
	else
	{
		if (m_pxAnimationHandler)
		{
			Asura_Animation*	pxAnimation	= m_pxAnimationHandler->GetAnimation();

			if (pxAnimation)
			{
				Asura_Matrix_3x3	xWorldSpaceOrientationMatrix;
				Asura_Vector_3		xWorldSpacePosition;
				Asura_Vector_3		xWorldSpaceNormal;
				Asura_Quat			xObjectSpaceOrientation;
				Asura_Vector_3		xObjectSpacePosition;
				Asura_Quat			xInvMyOri;
				Asura_Vector_3		xBoneRelativePosition;
				Asura_Quat			xBoneRelativeOrientation;

				xDamage.GetPointOfImpact(xWorldSpacePosition);
				xDamage.GetNormal(xWorldSpaceNormal);

				// Calculate the bone-relative data...
				Asura_Maths::OrientationFromDirection(xWorldSpaceOrientationMatrix, xWorldSpaceNormal);
				Asura_Vector_3 xMyPos;
				Asura_Matrix_3x3 xMyOri;
				pxAnimation->GetBoneLocation( 0, &xMyPos, &xMyOri, true );
				xInvMyOri.FromMatrix( xMyOri );
				xInvMyOri.Invert();

				xObjectSpaceOrientation = xInvMyOri * Asura_Quat(xWorldSpaceOrientationMatrix);
				xObjectSpacePosition = xInvMyOri.RotateVector( xWorldSpacePosition - xMyPos);

				if(pxAnimation->GetBoneRelativePositionAndOrientation(xObjectSpacePosition, xObjectSpaceOrientation, iBoneID, xBoneRelativePosition, xBoneRelativeOrientation))
				{
					const u_int uPFXEffectID = Asura_PFX_Entity_Effect::Create(xWorldSpacePosition, xWorldSpaceOrientationMatrix, uPFXHash, 0, ASURA_PFX_EFFECT_CAMERA_FLAGS, GetGuid());
					Asura_AnimationAttachment* pxAttachment = new Asura_AnimationAttachment_BoneRelativePFX (uPFXEffectID, true);

					if (pxAttachment)
					{
						pxAttachment->LinkAttachment_BoneRelative(pxAnimation, xBoneRelativePosition, xBoneRelativeOrientation, iBoneID);
					}
				}
			}
		}
	}
}

/// End of function 'AVP_ClientActor_Alien::CreateBoneRelativeBloodspurt'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::CreateBloodDecal
// Author       : Anthony Gregson
// Date         : 15/05/08
////////////////////////////////////////////////////////////////////////////////
DEBUGVAR float dbg_fBloodDecalLOSDistance = 5.0f;

void AVP_ClientActor_Vertebrate::CreateBloodDecal(const Asura_Hash_ID uDecalHash, const Asura_Vector_3& xLOSOrigin) const
{
	if (uDecalHash == ASURA_HASH_ID_UNSET) { return; }

	Asura_Vector_3 xPosition, xNormal;
	Asura_Vector_3 xDirection(0.0f,1.0f,0.0f);

	const Asura_LOS_InputData	xLOSInput(xLOSOrigin, xDirection, GetGuid(), dbg_fBloodDecalLOSDistance);
	const Asura_LOS_OutputData*	pxLOSOutput	= Asura_LOS::TestAgainstEnvironment(&xLOSInput);

	if(pxLOSOutput->m_fDistanceToIntersection < dbg_fBloodDecalLOSDistance)
	{
		xNormal		= pxLOSOutput->m_xNormal;
		xPosition	= (xDirection * pxLOSOutput->m_fDistanceToIntersection) + xLOSOrigin;

		Asura_Decal_Manager::AddDecal(xPosition, xNormal, uDecalHash);
	}
}
/// End of function 'AVP_ClientActor_Vertebrate::CreateBloodDecal'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::CreateBloodDecalFromMarker
// Author       : Anthony Gregson
// Date         : 15/05/08
////////////////////////////////////////////////////////////////////////////////
void AVP_ClientActor_Vertebrate::CreateBloodDecalFromMarker(const Asura_Hash_ID uDecalHash, const Asura_Hash_ID uMarkerHash) const
{
	Asura_Vector_3 xPosition;
	if (GetMarkerLocationFromHash(uMarkerHash, &xPosition, NULL))
	{
		CreateBloodDecal(uDecalHash, xPosition);
	}
}
/// End of function 'AVP_ClientActor_Vertebrate::CreateBloodDecalFromMarker'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::CreateBloodDecalFromDamage
// Author       : Anthony Gregson
// Date         : 15/05/08
////////////////////////////////////////////////////////////////////////////////
void AVP_ClientActor_Vertebrate::CreateBloodDecalFromDamage(const Asura_Hash_ID uDecalHash, const AVP_EntityDamage_ApplyToEntity& xDamage) const
{
	if ( xDamage.GetAmount() > 0.0f )
	{
		Asura_Vector_3 xPosition;
		xDamage.GetPointOfImpact(xPosition);
		CreateBloodDecal(uDecalHash, xPosition);
	}
}
/// End of function 'AVP_ClientActor_Vertebrate::CreateBloodDecalFromDamage'. ///


void AVP_ClientActor_Vertebrate::ModifySkinElementGibDirectionFromDamagePosition(const u_int uElement, Asura_Vector_3* xDirection, const AVP_EntityDamage_ApplyToEntity& xDamage)
{
	const Asura_Blueprint* pxBlueprint = GetSkinElementBlueprint(uElement);
	if (!pxBlueprint) { return; }

	Asura_Value xValue;
	if (!pxBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_SKINELEMENT_MARKERPOINT, xValue)) { return; }

	const Asura_Hash_ID uMarkerPointHash = xValue.GetHashIDValue();
	if (uMarkerPointHash == ASURA_HASH_ID_UNSET) { return; }

	Asura_Vector_3 xGibPosition;
	if(!GetWorldSpaceMarkerPoint(uMarkerPointHash, &xGibPosition, NULL)) { return; }
	Asura_Vector_3 xImpactPoint;
	xDamage.GetPointOfImpact(xImpactPoint);

	*xDirection = xGibPosition - xImpactPoint;
	xDirection->Normalise();
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::CreateSkinElementGibs
// Author       : Anthony Gregson
// Date         : 15/05/08
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientActor_Vertebrate::CreateSkinElementGibs(const AVP_EntityDamage_ApplyToEntity& xDamage)
{
	const u_int uGibbedElements = xDamage.GetGibbedSkinElementFlags();
	Asura_Assert((uGibbedElements & xDamage.GetNewDestroyedSkinElementFlags()) == uGibbedElements, "The elements to gib should be a subset of the elements destroyed.");

	if (uGibbedElements == 0) { return; }

	Asura_Vector_3 xDirection;
	xDamage.GetDirection(xDirection);
	float fImpulse = xDamage.GetImpulseStrength();

	// Use impulse values from the actors blueprint for plasma explosion limb impulse
	if (xDamage.GetRandomiseDiretion())
	{
		Asura_Blueprint* pxBlueprint = GetBlueprint();
		Asura_Assert(pxBlueprint, "no blueprint");
		if (!pxBlueprint) return;
		Asura_Value xValue;

		if(pxBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_FATAL_PLASMA_IMPULSE, xValue))
		{
			fImpulse = xValue.GetFloatValue();
		} 
		else
		{
			Asura_Assert(false, "No FatalPlasma.Impulse found in blueprint");
		}

		if(pxBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_FATAL_PLASMA_IMPULSE_VARIANCE, xValue))
		{
			float fVariance = xValue.GetFloatValue();
			fImpulse += Asura_Random::GetFloat(0.f,2*fVariance);
			fImpulse -= fVariance;
		} 
		else
		{
			Asura_Assert(false, "No FatalPlasma.Impulse.Variance found in blueprint");
		}
	}

	for (u_int u=0; u<NUM_AVP_SKIN_ELEMENT_TYPES; ++u)
	{
		if ((uGibbedElements & (1 << u)) != 0)
		{
			if (xDamage.GetRandomiseDiretion())
			{
				ModifySkinElementGibDirectionFromDamagePosition(u, &xDirection, xDamage);
			} 
			CreateSkinElementGib(u, xDirection, fImpulse);
			
		}
	}
}

/// End of function 'AVP_ClientActor_Vertebrate::CreateSkinElementGib'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::CreateSkinElementGib
// Author       : Anthony Gregson
// Date         : 15/05/08
////////////////////////////////////////////////////////////////////////////////
AVP_ClientEntity_Gib* AVP_ClientActor_Vertebrate::CreateSkinElementGib(const u_int uElement, const Asura_Vector_3& xDirection, const float fImpulse)
{
	if (!IsElementUndamaged(uElement)) { return NULL; }

	// We need to get most gib details from the skin element blueprint.
	const Asura_Blueprint* pxBlueprint = GetSkinElementBlueprint(uElement);
	if (!pxBlueprint) { return NULL; }

	Asura_Value xValue;
	if (!pxBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_SKINELEMENT_MARKERPOINT, xValue)) { return NULL; }

	const Asura_Hash_ID uMarkerPointHash = xValue.GetHashIDValue();

	if (uMarkerPointHash == ASURA_HASH_ID_UNSET) { return NULL; }

	if (!pxBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_SKINELEMENT_GIBSKIN, xValue)) { return NULL; }

	const Asura_Hash_ID uSkinHash = xValue.GetHashIDValue();

	if (uSkinHash == ASURA_HASH_ID_UNSET) { return NULL; }

	// Turn on the damaged skin, as we're about to need its marker points.
	m_pxAnimationHandler->SetDamaged(true);
	m_pxAnimationHandler->UpdateSkin();

	Asura_Vector_3 xPosition;
	Asura_Matrix_3x3 xOrientation;
	if(!GetWorldSpaceMarkerPoint(uMarkerPointHash, &xPosition, &xOrientation)) { return NULL; }

	// Use a parameter enumerator to get details of any pfx we want to attach to the gib. Done here since we already have the blueprint.
	AVP_ClientEntity_Gib::PFXAttachData axPFXAttachData[uAVP_VERTEBRATE_GIB_MAX_PFX_ATTACHMENTS];
	u_int uNumPFXAttachments = 0;
	AVP_ClientEntity_Gib::FillPFXDetailsFromBlueprint(pxBlueprint, axPFXAttachData, uNumPFXAttachments);

	AVP_ClientEntity_Gib* pxGib = AVP_ClientEntity_Gib::Create(GetGuid(), xPosition, xOrientation, Asura_Maths::ZeroVector3, uSkinHash, xDirection * fImpulse, m_uBloodFromHitPFXHash, m_uBloodFromHitDecalHash, axPFXAttachData, uNumPFXAttachments);
	return pxGib;
}
/// End of function 'AVP_ClientActor_Vertebrate::CreateSkinElementGib'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::DestroySkinElements
// Author       : Anthony Gregson
// Date         : 15/05/08
////////////////////////////////////////////////////////////////////////////////
void AVP_ClientActor_Vertebrate::DestroySkinElements(const AVP_EntityDamage_ApplyToEntity& xDamage, const bool bQuietDestroy/* = false*/)
{
	if (!m_pxAnimationHandler) { return; }

	// Run through the destroyed elements flags, and get rid of them.
	// The best way should be to only destroy the topmost parents marked as destroyed, as they'll make all their children invisible in the process.
	const u_int uElementsToDestroy = xDamage.GetNewDestroyedSkinElementFlags();
	if (uElementsToDestroy == 0) { return; }

//#ifdef ASURA_ASSERT	// EP. I blocked this assert, because when there is explosion of the alien, I am creating all body parts.
//	u_int uPreviousFlags = m_uDestroyedSkinElementFlags;
//#endif //#ifdef ASURA_ASSERT

	for (u_int u=0; u<NUM_AVP_SKIN_ELEMENT_TYPES; ++u)
	{
		if (IsElementUndamaged(u) && ((uElementsToDestroy & (1 << u)) != 0))
		{
			// Find the topmost element destroyed.
			u_int uToDestroy = u;
			u_int uParent = Asura_Project_Skin_Elements::GetParentElementType(u);
			while ((uParent != NUM_AVP_SKIN_ELEMENT_TYPES) && IsElementUndamaged(uParent)  && ((uElementsToDestroy & (1 << uParent)) != 0))
			{
				uToDestroy = uParent;
				uParent = Asura_Project_Skin_Elements::GetParentElementType(uToDestroy);
			}

			DestroySkinElement(uToDestroy, bQuietDestroy);
		}
	}

//	Asura_Assert(m_uDestroyedSkinElementFlags == (uPreviousFlags | uElementsToDestroy), "Failed to destroy all requested skin elements"); // EP. I blocked this assert, because when there is explosion of the alien, I am creating all body parts.
}
/// End of function 'AVP_ClientActor_Vertebrate::CreateBloodDecalFromDamage'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::DestroySkinElement
// Author       : Anthony Gregson
// Date         : 14/05/08
////////////////////////////////////////////////////////////////////////////////
void AVP_ClientActor_Vertebrate::DestroySkinElement(const u_int uElement, const bool bQuietDestroy/* = false*/)
{
	if (!m_pxAnimationHandler) { return; }

	if (IsElementUndamaged(uElement))
	{
		// First, run through all child elements and mark them as invisible.
		for (u_int u=0; u<NUM_AVP_SKIN_ELEMENT_TYPES; ++u)
		{
			if (Asura_Project_Skin_Elements::IsChildOfElement(u, uElement, true))
			{
				MakeSkinElementInvisible(u);
			}
		}

#if defined(ASURA_SUPPORT_SKIN_ELEMENTS)
		Asura_Animation*	pxAnimation			= m_pxAnimationHandler->GetAnimation();

		if ( pxAnimation )
		{
			const u_int			uDamagedElement		= Asura_Project_Skin_Elements::GetDamagedElementIndex(uElement);
			const u_int			uUndamagedElement	= Asura_Project_Skin_Elements::GetUndamagedElementIndex(uElement);
			
			pxAnimation->SetSkinElementVisibility(uUndamagedElement, false);
			pxAnimation->SetSkinElementVisibility(uDamagedElement, true);

	#endif	// defined(ASURA_SUPPORT_SKIN_ELEMENTS)

			// Turn on the damaged skin, as we're about to need its marker points.
			m_pxAnimationHandler->SetDamaged(true);
			m_pxAnimationHandler->UpdateSkin();
			SetSkinElementDamaged(uElement);

			if (!bQuietDestroy)
			{
				// Also create the special effects that go with this destruction.
				const Asura_Blueprint* pxBlueprint = GetSkinElementBlueprint(uElement);
				if (pxBlueprint)
				{
					Asura_Value xValue;
					Asura_Hash_ID uMarkerHash = ASURA_HASH_ID_UNSET;
					if (pxBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_SKINELEMENT_MARKERPOINT, xValue))
					{
						uMarkerHash = xValue.GetHashIDValue();
					}

					if (uMarkerHash != ASURA_HASH_ID_UNSET)
					{
						// Blood PFX
						if (pxBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_SKINELEMENT_DESTRUCTIONBLOODPFX, xValue))
						{
							CreatePFXEffect(xValue.GetHashIDValue(), uMarkerHash, true);
						}

						// Blood decal
						if (pxBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_SKINELEMENT_DESTRUCTIONDECAL, xValue))
						{
							CreateBloodDecalFromMarker(xValue.GetHashIDValue(), uMarkerHash);
						}
					}
				}
			}
		}
	}
}
/// End of function 'AVP_ClientActor_Vertebrate::DestroySkinElement'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::MakeSkinElementInvisible
// Author       : Anthony Gregson
// Date         : 14/05/08
////////////////////////////////////////////////////////////////////////////////
void AVP_ClientActor_Vertebrate::MakeSkinElementInvisible(const u_int uElement)
{
	if (!m_pxAnimationHandler) { return; }

#if defined(ASURA_SUPPORT_SKIN_ELEMENTS)
	Asura_Animation*	pxAnimation			= m_pxAnimationHandler->GetAnimation();

	if (pxAnimation)
	{
		const u_int			uDamagedElement		= Asura_Project_Skin_Elements::GetDamagedElementIndex(uElement);
		const u_int			uUndamagedElement	= Asura_Project_Skin_Elements::GetUndamagedElementIndex(uElement);

		pxAnimation->SetSkinElementVisibility(uUndamagedElement, false);
		pxAnimation->SetSkinElementVisibility(uDamagedElement, false);
	#endif	// defined(ASURA_SUPPORT_SKIN_ELEMENTS)

		if (IsElementUndamaged(uElement))
		{
			SetSkinElementDamaged(uElement);
		}
	}
}
/// End of function 'AVP_ClientActor_Vertebrate::MakeSkinElementInvisible'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::SetSkinElementDamaged
// Author       : Anthony Gregson
// Date         : 15/05/08
////////////////////////////////////////////////////////////////////////////////
void AVP_ClientActor_Vertebrate::SetSkinElementDamaged(const u_int uElement)
{
	// Inform the attachment system the head has been lost so we can drop attachmets (helmets / masks).
	if ( (uElement == AVP_SET_HEAD) || (uElement == AVP_SET_ALIEN_FACE) )
	{
		if ((m_uDestroyedSkinElementFlags & (1<<uElement)) == 0)
		{
			if (GetAttachmentHandler()) GetAttachmentHandler()->OnDecapitation();
			OnDecapitation();
		}
	}


	// Mark it as destroyed.
	m_uDestroyedSkinElementFlags |= 1 << uElement;

	// And update our LOS details for newly-unhittable bones.
	if (!m_pxAnimationHandler) { return; }

	Asura_Animation* pxAnimation = m_pxAnimationHandler->GetAnimation();
	if (!pxAnimation) { return; }

	const Asura_Hierarchy_Skin* pxSkin = pxAnimation->GetSkin();
	if (!pxSkin) { return; }

	for (int iBoneID=0; iBoneID<pxSkin->NumberOfBones; ++iBoneID)
	{
		const u_int uBoneElement = GetElementFromBoneID(iBoneID);
		if (uBoneElement == uElement)
		{
			pxAnimation->SetIgnoreBoneForLOS(iBoneID, true);
		}
	}
}
/// End of function 'AVP_ClientActor_Vertebrate::SetSkinElementDamaged'. ///


void AVP_ClientActor_Vertebrate::ActivateHeadbitSkinElement()
{
	// Inform the attachment handler the head has been bitten so appropraite attachments may be removed (helmet / mask?)
	if (GetAttachmentHandler()) GetAttachmentHandler()->OnHeadBit();
#if defined(ASURA_SUPPORT_SKIN_ELEMENTS)
	if (!m_pxAnimationHandler) { return; }
	Asura_Animation* pxAnimation = m_pxAnimationHandler->GetAnimation();
	if (!pxAnimation) { return; }

	// Only activate the skin element if the head is not already invisible, as that means either we've lost the head, or we've already got the headbit element active.
	const u_int uUndamagedHeadElement = Asura_Project_Skin_Elements::GetUndamagedElementIndex(AVP_SET_HEAD);

	if (!pxAnimation->GetSkinElementVisibility(uUndamagedHeadElement)) { return; }

	const u_int uUndamagedHeadbitElement = Asura_Project_Skin_Elements::GetUndamagedElementIndex(AVP_SET_HEAD_AFTER_HEADBITE);
	const u_int uDamagedHeadbitElement = Asura_Project_Skin_Elements::GetDamagedElementIndex(AVP_SET_HEAD_AFTER_HEADBITE);

	pxAnimation->SetSkinElementVisibility(uUndamagedHeadbitElement, false);
	pxAnimation->SetSkinElementVisibility(uDamagedHeadbitElement, true); // Probably never gets made visible.
#endif //#if defined(ASURA_SUPPORT_SKIN_ELEMENTS)
}


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

	Asura_Value xValue;

	if(xBlueprint.GetParameterValueByHashID(0x6889D06F, xValue))	//Asura_GetHashID("BloodFromHit"), xValue))
	{
		m_uBloodFromHitPFXHash = xValue.GetHashIDValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x3092DE56, xValue))	//Asura_GetHashID("BloodDecalFromHit"), xValue))
	{
		m_uBloodFromHitDecalHash = xValue.GetHashIDValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x6889CC9C, xValue))	//Asura_GetHashID("BloodFromGib"), xValue))
	{
		m_uBloodFromGibPFXHash = xValue.GetHashIDValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xD77259AE, xValue))	//Asura_GetHashID("BloodSquirt"), xValue))
	{
		m_uBloodSquirtPFXHash = xValue.GetHashIDValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xBCAED84C, xValue))	//Asura_GetHashID("BloodTrail"), xValue))
	{
		m_uBloodTrailBPHash = xValue.GetHashIDValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x4DA5B681, xValue))	//Asura_GetHashID("HealthDangerFraction"), xValue))
	{
		m_fHealthDangerFraction = xValue.GetFloatValue();
	}

	if(xBlueprint.GetParameterValueByHashID(0x0B66DBFE, xValue))	//Asura_GetHashID("LeftHandBloodMarker"), xValue))
	{
		m_uLeftHandBloodMarker = xValue.GetHashIDValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0xF2F0AD49, xValue))	//Asura_GetHashID("RightHandBloodMarker"), xValue))
	{
		m_uRightHandBloodMarker = xValue.GetHashIDValue();
	}

	for (u_int u=0; u<NUM_AVP_SKIN_ELEMENT_TYPES; ++u )
	{
		if (xBlueprint.GetParameterValueByHashID(Asura_Project_Skin_Elements::GetElementBlueprintParameter(u), xValue))
		{
			m_auSkinElementBlueprints[u] = xValue.GetHashIDValue();
		}
		else
		{
			m_auSkinElementBlueprints[u] = ASURA_HASH_ID_UNSET;
		}
	}

	InitialiseGrappleDifficulties(xBlueprint);
}


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientActor_Vertebrate::UpdateMovement
// Author       : Andy Tate
// Date         : 12-Feb-2007
////////////////////////////////////////////////////////////////////////////////
DEBUGVAR float dbg_fConstrainedMovementMaxCorrectionSpeed = 0.3f;
DEBUGVAR float dbg_fConstrainedMovementMaxCorrectionSpeedSquared = dbg_fConstrainedMovementMaxCorrectionSpeed * dbg_fConstrainedMovementMaxCorrectionSpeed;

void AVP_ClientActor_Vertebrate::UpdateMovement(void)
{
	// SG - linked objects should not be moving
	if (GetParentGuid() != ASURA_GUID_UNREGISTERED)
	{
		return;
	}

	// JG - 30 July 2008 - No point trying to update the movement if there is no movement system
	// WGP - 23/04/09 - not true, we have to continue to pass in the velocity to the physics otherwise we maintain our previous velocity when we die.
	//if (!m_pxMovementSystem) return; 

	// Are we using an interpolator OR are we in control?
	if(AVP_Game_Core::IsLocalPlayer(GetGuid()))
	{
		Asura_Assert(m_pxPhysicsObjectHandler, "Local player needs a physics object handler.");
		if (!m_pxPhysicsObjectHandler) { return; }

		const AVP_Move*	pxCurrentMove	= m_pxMovementSystem ? static_cast<const AVP_Move*>(m_pxMovementSystem->GetCurrentMove()) : NULL;
		const float		fTimeStep		= Asura_Timers::GetGameTimeSlice();

		Asura_Vector_3	xVelocity_3(Asura_Maths::ZeroVector3);

		// Are we moving to an external position?
		if( (pxCurrentMove == NULL) ||
			(pxCurrentMove->GetPositionUsage() == Asura_Move::POSITION_NOT_USED) )
		{
			Asura_AVector3	xOSVelocity;

			Asura_Quat		xWSCharacterOrient = UpdateOrientation();	// World space

			Asura_Quat		xLSCharacterOrient;		// Local space
			Asura_AVector3	xLSCharacterVelocity;	// Local space

			m_pxPhysicsObjectHandler->RotateWorldSpaceOrientationIntoLocalSpace( xWSCharacterOrient, xLSCharacterOrient );

			// Time to update our falling state, then make use of its flag.
			const bool bWasFalling = IsFalling();
			UpdateFalling();
			const bool bIsFalling = IsFalling();
			const bool bIsJumping = IsJumping();
			if ((bIsJumping || bIsFalling) && m_pxInputHandler)
			{
				// Get the air velocity from our input handler.
				const AVP_Vertebrate_InputHandler* pxVertInput = static_cast<AVP_Vertebrate_InputHandler*>(m_pxInputHandler);
				pxVertInput->GetAirVelocity(xVelocity_3);
				m_pxPhysicsObjectHandler->RotateWorldSpaceVectorIntoLocalSpace(xVelocity_3, xVelocity_3);

				xLSCharacterVelocity = Asura_AVector3(xVelocity_3);
				xLSCharacterOrient.Invert();
				xOSVelocity = xLSCharacterOrient.RotateVector(xLSCharacterVelocity);
				xLSCharacterOrient.Invert();

				if (bIsFalling && !bWasFalling)
				{
					// Just started falling.
					OnBeginFalling();
				}
			}
			else
			{
				// get velocity from animation and rotate it into worldspace
				GetVelocityFromAnimation( xVelocity_3 );
				xOSVelocity = Asura_AVector3( xVelocity_3 );
				xLSCharacterVelocity = xLSCharacterOrient.RotateVector( xOSVelocity);

				if (bWasFalling)
				{
					// Just finished falling.
					OnEndFalling();
				}
			}

#if defined(AVP_ALLOW_DEBUG_MOVEMENT)
			Asura_Quat		xWSCameraOrient;
			Asura_AVector3	xWSCameraVelocity;
			Asura_Matrix_3x3 xOriMat;
			GetAimDirection( xWSCameraVelocity );
			Asura_Maths::OrientationFromDirection( xOriMat, Asura_Vector_3( xWSCameraVelocity ) );
			xWSCameraOrient.FromMatrix( xOriMat );
			xWSCameraVelocity = xWSCameraOrient.RotateVector( xOSVelocity);

			// What sort of movement are we to perform?
			switch (AVP_Vertebrate_InputHandler::GetMovementType())
			{
				case AVP_Vertebrate_InputHandler::MT_PLAYER_FIRST_PERSON:
				case AVP_Vertebrate_InputHandler::MT_PLAYER_THIRD_PERSON:
#endif
				{
					// See if we're moving along an attractor, as that moves us very differently
					if ( m_uConstrainedMovementAttractorID != uASURA_ATTRACTOR_ID_NONE && ( pxCurrentMove == NULL || ( pxCurrentMove->GetHash() != AVP_Move_Predator_DismountBranch::GetStaticHash() && pxCurrentMove->GetHash() != AVP_Move_Alien_DismountBranch::GetStaticHash() ) ) )
					{
						const Asura_Attractor_Compiled* pxAttractor = Asura_Attractor_System::GetInstance()->GetAttractorByID(m_uConstrainedMovementAttractorID);
						if (pxAttractor)
						{
							// For now, we'll assume whatever's setting up the attractor will also handle unsetting it, so no bounds checking here.
							Asura_Vector_3 xAttractorDir;
							pxAttractor->GetAxisDirection(xAttractorDir);
							Asura_AVector3 xAttDirAVec( xAttractorDir );

							// Work out which way we're trying to go.
							Asura_Vector_3 xWSCharacterVelocity;
							m_pxPhysicsObjectHandler->RotateLocalSpaceVectorIntoWorldSpace( Asura_Vector_3( xLSCharacterVelocity ), xWSCharacterVelocity );
							float fMultiplier = Asura_Maths::Sign(xWSCharacterVelocity * xAttractorDir);

							xWSCharacterVelocity = xAttractorDir * fMultiplier * xWSCharacterVelocity.Magnitude();

							// Do a little (asymptotic, as long as the frame rate is never 1 or lower) position correction to keep us where we should be.
							if (!xWSCharacterVelocity.ApproximatelyEquals(Asura_Maths::ZeroVector3))
							{
								Asura_Vector_3 xAxisOffset;
								pxAttractor->GetRelativePosition(GetCurrentPosition(), 0.0f, NULL, NULL, NULL, &xAxisOffset, false);

								if (xAxisOffset.MagnitudeSquared() > dbg_fConstrainedMovementMaxCorrectionSpeedSquared)
								{
									xAxisOffset.Normalise();
									xAxisOffset *= dbg_fConstrainedMovementMaxCorrectionSpeed;
								}
								xWSCharacterVelocity -= xAxisOffset;
							}

							// And finally apply this (currently worldspace, so we'll rotate it back) velocity to the physics object.
							m_pxPhysicsObjectHandler->RotateWorldSpaceVectorIntoLocalSpace( xWSCharacterVelocity, xWSCharacterVelocity );
							xLSCharacterVelocity = Asura_AVector3( xWSCharacterVelocity );
							m_pxPhysicsObjectHandler->CalculateMovementUsingOrientationAndLinVelocity(fTimeStep, xLSCharacterOrient, xLSCharacterVelocity, false, AVP_PhysicsObjectHandler::LOCAL_SPACE);
						}
						else
						{
							Asura_Assert(false, "Failed to find attractor for constrained movement");
							// Fallback to the usual behaviour
						}
					}
					else
					{
						m_pxPhysicsObjectHandler->CalculateMovementUsingOrientationAndLinVelocity( fTimeStep, xLSCharacterOrient, xLSCharacterVelocity, true, AVP_PhysicsObjectHandler::LOCAL_SPACE );
					}
				}
#if defined(AVP_ALLOW_DEBUG_MOVEMENT)
				break;

				case AVP_Vertebrate_InputHandler::MT_FLYING_PLAYER_COLLISIONS:
				{
					Asura_AVector3	xRotatedVelocity;

					m_pxPhysicsObjectHandler->CalculateMovementUsingOrientationAndLinVelocity(fTimeStep, xWSCharacterOrient, xWSCameraVelocity, false, AVP_PhysicsObjectHandler::WORLD_SPACE);
				}
				break;

				case AVP_Vertebrate_InputHandler::MT_FLYING_PLAYER_NO_COLLISIONS:
				{
					const float		fTimeStep	= Asura_Timers::GetGameTimeSlice();

					Asura_Vector_3	xPosition = GetCurrentPosition();
					xWSCameraVelocity	*= fTimeStep;
					xPosition		+= Asura_Vector_3( xWSCameraVelocity );

					m_pxDynamicObject->SetIgnore(true);
					m_pxDynamicObject->SetPosition(xPosition);
					m_pxDynamicObject->SetOrientation(xWSCharacterOrient); 
				}
				break;

				case AVP_Vertebrate_InputHandler::MT_FLYING_CAMERA:
				{
					if ( xWSCameraVelocity.NormaliseReturningMagnitude() > 0.0f )
					{
						xWSCameraVelocity *= g_fCameraMovementSpeed;

						const float		fTimeStep	= Asura_Timers::GetActualFrameTime();
						Asura_Vector_3	xCameraPosition( *Asura_Camera::GetPosition() );

						xWSCameraVelocity	*= fTimeStep;
						xCameraPosition	+= Asura_Vector_3( xWSCameraVelocity );

						Asura_Camera::SetPosition( xCameraPosition );
					}
					Asura_Matrix_3x3 xOriMat;
					xWSCameraOrient.ToMatrix( xOriMat );
					Asura_Camera::SetOrientation( xOriMat );
				}
				break;

				default:
				{
					Asura_Assert(false, "Unexpected movement type.");
				}
				break;
			}		// End of switch.
#endif
		}
		else // we are in a move with position usage
		{
			const bool bGravityUsed = !pxCurrentMove->IsGravityIgnored();

			Asura_Quat xWSCharacterOrient;		// World space
			// Get the world-space orientation...
			if (pxCurrentMove->GetOrientationUsage() == Asura_Move::ORIENTATION_SET_DIRECTLY)
			{
				m_pxMovementSystem->GetCurrentMoveOrientation( xWSCharacterOrient );
			}
			else
			{
				xWSCharacterOrient.FromMatrix( GetCurrentOrientation() );
			}

			// Update the physics object...
			if(pxCurrentMove->GetPositionUsage() == Asura_Move::POSITION_SET_FROM_VELOCITY)
			{
				Asura_AVector3 xAVelocity;
				m_pxMovementSystem->GetCurrentMoveVelocity( xAVelocity );
				// If the move doesn't control the orientation, use local space so the surface reorientation can still work.
				if (pxCurrentMove->GetOrientationUsage() != Asura_Move::ORIENTATION_SET_DIRECTLY)
				{
					Asura_Vector_3	xVelocity(xAVelocity);
					Asura_Quat		xLSCharacterOrient;

					m_pxPhysicsObjectHandler->RotateWorldSpaceOrientationIntoLocalSpace( xWSCharacterOrient, xLSCharacterOrient );

					m_pxPhysicsObjectHandler->RotateWorldSpaceVectorIntoLocalSpace( xVelocity, xVelocity );
					xAVelocity = xVelocity;
					m_pxPhysicsObjectHandler->CalculateMovementUsingOrientationAndLinVelocity( fTimeStep, xLSCharacterOrient, xAVelocity, bGravityUsed, AVP_PhysicsObjectHandler::LOCAL_SPACE );
				}
				else
				{
					m_pxPhysicsObjectHandler->CalculateMovementUsingOrientationAndLinVelocity( fTimeStep, xWSCharacterOrient, xAVelocity, bGravityUsed, AVP_PhysicsObjectHandler::WORLD_SPACE );
				}
			}
			else if (pxCurrentMove->GetOrientationUsage() == Asura_Move::ORIENTATION_NOT_USED)
			{
				// We have to move by velocity in this technique, to allow surface reorientation to work still
				Asura_AVector3 xAVelocity;
				Asura_AVector3 xPreviousPosition;
				m_pxMovementSystem->GetPreviousMovePosition(xPreviousPosition);
				m_pxMovementSystem->GetCurrentMovePosition(xAVelocity);

				xAVelocity	-= xPreviousPosition;
				xAVelocity	/= fTimeStep;
				// If the move doesn't control the orientation, use local space so the surface reorientation can still work.
				Asura_Vector_3	xVelocity(xAVelocity);
				Asura_Quat		xLSCharacterOrient;

				m_pxPhysicsObjectHandler->RotateWorldSpaceOrientationIntoLocalSpace( xWSCharacterOrient, xLSCharacterOrient );

				m_pxPhysicsObjectHandler->RotateWorldSpaceVectorIntoLocalSpace( xVelocity, xVelocity );
				xAVelocity = xVelocity;
				m_pxPhysicsObjectHandler->CalculateMovementUsingOrientationAndLinVelocity( fTimeStep, xLSCharacterOrient, xAVelocity, bGravityUsed, AVP_PhysicsObjectHandler::LOCAL_SPACE );
			}
			else
			{
				Asura_AVector3 xPosition;
				m_pxMovementSystem->GetCurrentMovePosition( xPosition );
				m_pxPhysicsObjectHandler->CalculateMovementUsingOrientationAndPosition(fTimeStep, xWSCharacterOrient, xPosition, bGravityUsed, AVP_PhysicsObjectHandler::WORLD_SPACE );
			}
		}

#if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
		// Is an interpolator available?
		if( m_pxPosAndOriInterpolator != NULL )
		{
			m_pxPhysicsObjectHandler->UpdateInterpolator( *m_pxPosAndOriInterpolator );
		}
#endif // #if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	}
#if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	else if(m_pxPosAndOriInterpolator == NULL)
	{
		// AJG - 22/09/08 - This is now a valid case: we were a local player, but he's now been disconnected through death. No need to assert any more.
	}
	else // AI actor or remote player (i.e. anything not handled directly on this client).
	{
		// If we are running a move we take our pos and ori from the move.
		// When the move is finished the server takes over again and we'll interpolate to the latest position.
		const Asura_Move* pxCurrentMove = m_pxMovementSystem ? m_pxMovementSystem->GetCurrentMove() : NULL;
		const Asura_Move::PositionUsage ePosUsage = pxCurrentMove ? pxCurrentMove->GetPositionUsage() : Asura_Move::POSITION_NOT_USED;

		// We only want to teleport around if we're directly setting positions.
		if (ePosUsage == Asura_Move::POSITION_SET_DIRECTLY)
		{
			Asura_Matrix_3x3 xMoveOrientationMatrix;
			if (pxCurrentMove->GetOrientationUsage() == Asura_Move::ORIENTATION_SET_DIRECTLY)
			{
				Asura_Quat xMoveOrientation;
				m_pxMovementSystem->GetCurrentMoveOrientation( xMoveOrientation );
        		xMoveOrientation.ToMatrix( xMoveOrientationMatrix );
			}
			else
			{
				// Use character orientation if the move doesn't control it
				xMoveOrientationMatrix = GetCurrentOrientation();
			}

			Asura_Vector_3 xMovePosition;
			m_pxMovementSystem->GetCurrentMovePosition( xMovePosition );

			m_pxPosAndOriInterpolator->TeleportTo( xMovePosition, xMoveOrientationMatrix );
		}
		else if (m_pxPhysicsObjectHandler)
		{
			// Need to make sure the phys object handler (if we have one, which AIs on the server machine don't) follows the interpolator.
			m_pxPhysicsObjectHandler->UpdateFromInterpolator( *m_pxPosAndOriInterpolator );
		}
	}
#endif // #if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
}

/// End of function 'AVP_ClientActor_Vertebrate::UpdateMovement'. ///

void AVP_ClientActor_Vertebrate::SetupDefaultVisionMode()
{
	AVP_Inventory_Group*	pxVisionGroup	= (m_pxInventorySystem == NULL) ? NULL : m_pxInventorySystem->GetGroup(uAVP_INVENTORY_GROUPHASH_VISIONMODE);

	if(pxVisionGroup != NULL)
	{
		pxVisionGroup->SetCurrentItemToDefaultItem();
	}
}

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

	SetupDefaultVisionMode();

	// Play Dead animation now that the animation handler is created
	if(IsEntityActive())
	{
		MakeRagdollForStartDead();
	}

	if (m_pxFocus)
	{
		m_pxFocus->PostCreateInitialise();
	}

	AVP_Animation_BodyRig* pxBodyRig = m_pxAnimationHandler ? m_pxAnimationHandler->GetBodyRig() : NULL;
	if ( pxBodyRig )
	{
		const Asura_Matrix_3x3& xOri = GetCurrentOrientation();
		pxBodyRig->ResetAimDirection( Asura_AVector3( xOri.e13, xOri.e23, xOri.e33 ) );
	}

	UpdateFalling();
}

void AVP_ClientActor_Vertebrate::OnEntityActivate()
{
	PARENT::OnEntityActivate();

	MakeRagdollForStartDead();
}

void AVP_ClientActor_Vertebrate::OnEntityDeactivate()
{
	PARENT::OnEntityDeactivate();

	DestroyRagdollForStartDead();
}

void AVP_ClientActor_Vertebrate::DestroyRagdollForStartDead()
{
	if(m_bDeadBody)
	{
		if(m_pxAnimationHandler)
		{
			(static_cast<AVP_Vertebrate_AnimationHandler*> (m_pxAnimationHandler))->DestroyRagdoll(true);
		}
	}
}

void AVP_ClientActor_Vertebrate::MakeRagdollForStartDead()
{
	if(m_bDeadBody)
	{
		if(m_uDeadAnimHashID != ASURA_HASH_ID_UNSET)
		{
			if(m_pxAnimationHandler && !m_pxAnimationHandler->IsRagdoll())
			{
				Asura_AnimationBlend*	pxBlend = new Asura_AnimationBlend( 1.0f );
				m_pxAnimationHandler->PlayAnimation(AAC_FLAG_ACTIVE, 0, m_uDeadAnimHashID, pxBlend);
				if(m_pxAnimationHandler->GetAnimation())
				{
					if ( !m_pxAnimationHandler->GetAnimation()->UpdateTimers( GetShapeInstance().GetAnimTimestep() ) )
					{
						m_pxAnimationHandler->GetAnimation()->UpdateTimers( 0.0f, true );
					}
					m_pxAnimationHandler->GetAnimation()->Update(GetShapeInstance().GetPosition(), GetShapeInstance().GetOrientation(), ANIMTIME_USEGAMETIME, ASURA_HIERARCHY_RENDER_DONTRENDER | ASURA_HIERARCHY_RENDER_UPDATEBONESALWAYS);
				}
				RemoveFromUpdateableClientList(this, false);
				MakeRagdoll();
				(static_cast<AVP_Vertebrate_AnimationHandler*> (m_pxAnimationHandler))->SetRagDollAsleep();
			}
		}
		else
		{
			Asura_Assert( false, " Dead body must have an dead anim." );
		}
	}
}

void AVP_ClientActor_Vertebrate::StartFlinch(const AVP_Msg_Flinch& xFlinchData)
{
	if (xFlinchData.GetUseFlinchMove())
	{
		// Set up a flinch move start message, and handle it locally only.
		if (!m_pxMovementSystem) { return; }

		Asura_Message* pxMoveMessage = Asura_Network::CreateMessage(ASURA_MSG_MOVE_START, sizeof(AVP_Msg_Flinch_Move_Start));
		if (!pxMoveMessage) { return; }

		AVP_Msg_Flinch_Move_Start* pxData = static_cast<AVP_Msg_Flinch_Move_Start*>(pxMoveMessage->GetData());
		if (pxData)
		{
			const bool bOrientateToImpactDir = xFlinchData.GetOrientateToImpactDirection();
			const bool bDisableSurfaceReorientation = xFlinchData.GetDisableSurfaceReorientation();
			Asura_Matrix_3x3 xTargetOri;
			if (bOrientateToImpactDir)
			{
				// Use the target heading.
				Asura_Euler_3 xEuler(0.0f, xFlinchData.GetTargetHeading(), 0.0f);
				Asura_Matrix_3x3 xMat;
				xEuler.ToMatrix(xMat);
				pxData->SetTargetOrientation(xMat);
			}
			else if (bDisableSurfaceReorientation)
			{
				// Base it on our own orientation.
				Asura_Euler_3 xEuler;
				Asura_Matrix_3x3 xMat;
				xEuler.FromMatrix(GetCurrentOrientation());
				xEuler.x = xEuler.z = 0.0f;
				xEuler.ToMatrix(xMat);
				pxData->SetTargetOrientation(xMat);
			}
			else
			{
				// Just use our current orientation
				pxData->SetTargetOrientation(GetCurrentOrientation());
			}

			pxData->ClearFlags();
			pxData->SetAnimHashes(xFlinchData.GetFirstPersonAnim(), xFlinchData.GetThirdPersonAnim());
			pxData->SetDisableSurfaceReorientation(bDisableSurfaceReorientation || bOrientateToImpactDir);
			pxData->SetResetAimDirection(xFlinchData.GetResetAimDirection());
			pxData->SetForceAbort(true);
			pxData->SetIgnoredByOwner(false);
			pxData->SetMoveID(xFlinchData.GetAttackBlocked() ? AVP_Move_BlockFlinch::GetStaticHash() : AVP_Move_Flinch::GetStaticHash());
		}

		m_pxMovementSystem->HandleMessage(pxMoveMessage);

		Asura_Network::DestroyMessage(pxMoveMessage);
	}
	else
	{
		// Play this on top of everything else, but have it not affect anything to do with movement.
		if (m_pxAnimationHandler)
		{
			const bool bFirstPerson = m_pxAnimationHandler->IsFirstPerson();
			u_int uFlags = AAC_FLAG_ACTIVE | AAC_FLAG_ALLOW_DESTROY_FROM_MESSAGE | AAC_FLAG_ENSURE_UNIQUE_HASH;
			u_int uPriority = bFirstPerson ? AVP_AAPC_FLINCH_ANIM_HUD : AVP_AAPC_FLINCH_ANIM_TP;
			Asura_Hash_ID uAnimHash = bFirstPerson ? xFlinchData.GetFirstPersonAnim() : xFlinchData.GetThirdPersonAnim();
			Asura_AnimationBlend_AnimatedTween* pxBlend = new Asura_AnimationBlend_AnimatedTween(DEFAULT_TWEEN_TIME, DEFAULT_TWEEN_TIME, false);
			m_pxAnimationHandler->PlayAnimation(uFlags, uPriority, uAnimHash, pxBlend);
		}
	}
}

bool AVP_ClientActor_Vertebrate::GetFocusActive() const
{
	if (m_pxFocus)
	{
		return m_pxFocus->GetActive();
	}

	return false;
}

void AVP_ClientActor_Vertebrate::GetBlockStartAnims(Asura_Hash_ID& uFirstPersonHash, Asura_Hash_ID& uThirdPersonHash) const
{
	uFirstPersonHash = uThirdPersonHash = ASURA_HASH_ID_UNSET;

	Asura_Hash_ID uBlueprintTypeHash, uFirstPersonBlueprintHash, uThirdPersonBlueprintHash;
	GetCurrentBlockBlueprintHashes(uBlueprintTypeHash, uFirstPersonBlueprintHash, uThirdPersonBlueprintHash);

	const Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uBlueprintTypeHash, uFirstPersonBlueprintHash);
	if (pxBlueprint)
	{
		Asura_Value xValue;
		if (pxBlueprint->GetParameterValueByHashID(s_uBlockStartParameterID, xValue))
		{
			uFirstPersonHash = xValue.GetHashIDValue();
		}
	}

	pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uBlueprintTypeHash, uThirdPersonBlueprintHash);
	if (pxBlueprint)
	{
		Asura_Value xValue;
		if (pxBlueprint->GetParameterValueByHashID(s_uBlockStartParameterID, xValue))
		{
			uThirdPersonHash = xValue.GetHashIDValue();
		}
	}
}

void AVP_ClientActor_Vertebrate::GetBlockLoopAnims(Asura_Hash_ID& uFirstPersonHash, Asura_Hash_ID& uThirdPersonHash) const
{
	uFirstPersonHash = uThirdPersonHash = ASURA_HASH_ID_UNSET;

	Asura_Hash_ID uBlueprintTypeHash, uFirstPersonBlueprintHash, uThirdPersonBlueprintHash;
	GetCurrentBlockBlueprintHashes(uBlueprintTypeHash, uFirstPersonBlueprintHash, uThirdPersonBlueprintHash);

	const Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uBlueprintTypeHash, uFirstPersonBlueprintHash);
	if (pxBlueprint)
	{
		Asura_Value xValue;
		if (pxBlueprint->GetParameterValueByHashID(s_uBlockLoopParameterID, xValue))
		{
			uFirstPersonHash = xValue.GetHashIDValue();
		}
	}

	pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uBlueprintTypeHash, uThirdPersonBlueprintHash);
	if (pxBlueprint)
	{
		Asura_Value xValue;
		if (pxBlueprint->GetParameterValueByHashID(s_uBlockLoopParameterID, xValue))
		{
			uThirdPersonHash = xValue.GetHashIDValue();
		}
	}
}

void AVP_ClientActor_Vertebrate::GetBlockEndAnims(Asura_Hash_ID& uFirstPersonHash, Asura_Hash_ID& uThirdPersonHash) const
{
	uFirstPersonHash = uThirdPersonHash = ASURA_HASH_ID_UNSET;

	Asura_Hash_ID uBlueprintTypeHash, uFirstPersonBlueprintHash, uThirdPersonBlueprintHash;
	GetCurrentBlockBlueprintHashes(uBlueprintTypeHash, uFirstPersonBlueprintHash, uThirdPersonBlueprintHash);

	const Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uBlueprintTypeHash, uFirstPersonBlueprintHash);
	if (pxBlueprint)
	{
		Asura_Value xValue;
		if (pxBlueprint->GetParameterValueByHashID(s_uBlockEndParameterID, xValue))
		{
			uFirstPersonHash = xValue.GetHashIDValue();
		}
	}

	pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uBlueprintTypeHash, uThirdPersonBlueprintHash);
	if (pxBlueprint)
	{
		Asura_Value xValue;
		if (pxBlueprint->GetParameterValueByHashID(s_uBlockEndParameterID, xValue))
		{
			uThirdPersonHash = xValue.GetHashIDValue();
		}
	}
}

bool AVP_ClientActor_Vertebrate::MakeRagdoll()
{
	bool bOK = false;
	if (AVP_Settings::IsSinglePlayer() && AVP_Game_Core::IsLocalPlayer(GetGuid()))
	{
		// Just do nothing if we're the local player in single player.
		bOK = true;

		// AJG - 09/11/07 - For now, don't go to game over in multiplayer.
		AVP_Core::RequestGameOver();
	}
	else
	{
		if ( m_pxAnimationHandler )
		{ 
			AVP_Vertebrate_AnimationHandler* pxVertebrateAnimHandler =  static_cast< AVP_Vertebrate_AnimationHandler* >( m_pxAnimationHandler );
			if (!pxVertebrateAnimHandler->IsRagdoll())
			{
				if (m_pxAnimationHandler->IsFirstPerson())
				{
					m_pxAnimationHandler->SetThirdPerson();
					// Ensure that the animation gets an update with these new settings
					Asura_Animation* pxAnim = m_pxAnimationHandler->GetAnimation();
					if (pxAnim)
					{
						const u_int uFlags = ASURA_HIERARCHY_RENDER_NO_FRUSTUM_TEST | ASURA_HIERARCHY_RENDER_NO_MODULELINK_TEST | ASURA_HIERARCHY_RENDER_DONTRENDER | ASURA_HIERARCHY_RENDER_UPDATEBONESALWAYS;
						pxAnim->Update(GetCurrentPosition(), GetCurrentOrientation(), ANIMTIME_USEGAMETIME, uFlags);
					}
				}

				// We need the render orientation for this.
				if (pxVertebrateAnimHandler->MakeRagdoll(GetCurrentPosition(), GetCurrentOrientation()))
				{
					// Add ourselves to the ragdolled entity list.
					s_xRagdolledList.AddToList(this);
					bOK = true;

					if(m_pxDynamicObject)
					{
						m_pxDynamicObject->SetIgnore(true);
					}
				}
			}
			else
			{
				Asura_Assert(false, "Calling MakeRagdoll on an actor who is already a ragdoll."); 
				bOK = true; 
			}
		}
	}

	if ( !bOK )
	{
		SetInvisibleFlag( true ); 
	}

	return bOK;
}

// uReplacementDeathAnim is output-only, irrelevent if the function returns true, and may be unset if something else is controlling the animation but still doesn't want to create a ragdoll immediately.
bool AVP_ClientActor_Vertebrate::GetCreateImmediateRagdoll(const AVP_EntityDamage_ApplyToEntity& xDamage, Asura_Hash_ID& uReplacementDeathAnim) const
{
	// The current move (if there is one) gets first veto on death control.
	if (m_pxMovementSystem)
	{
		const AVP_Move* pxCurrentMove = static_cast<const AVP_Move*>(m_pxMovementSystem->GetCurrentMove());
		if (pxCurrentMove)
		{
			// no insta-death on the local player (always play a death anim)
			if ( !AVP_Game_Core::IsLocalPlayer( GetGuid() ) )
			{
				// Do we always want to ragdoll from this move?
				if (pxCurrentMove->GetAlwaysCreateRagdollOnDeath()) { return true; }
			}

			if (pxCurrentMove->GetControlDeathInternally())
			{
				// For if a move is expecting to play its own internal death anim - for instance, melee kill grapples.
				uReplacementDeathAnim = ASURA_HASH_ID_UNSET;
				return false;
			}
			// Finally, see if this move has its own intended death anim.
			const Asura_Hash_ID uAnim = pxCurrentMove->GetDeathAnim();
			if (uAnim != ASURA_HASH_ID_UNSET)
			{
				uReplacementDeathAnim = uAnim;
				return false;
			}
		}
	}

	// Next, see if we have a particular death anim we want to play.
	const Asura_Hash_ID uAnim = GetDeathAnimation(xDamage);
	if (uAnim != ASURA_HASH_ID_UNSET)
	{
		uReplacementDeathAnim = uAnim;
		return false;
	}

	// If nothing else, just create a ragdoll.
	return true;
}

static Asura_StringBuffer<32> g_xDeathAnimParameterName;
DEBUGVAR float dbg_fDefaultStrongDeathImpactStrength = 2.5f;

Asura_Hash_ID AVP_ClientActor_Vertebrate::GetDeathAnimation(const AVP_EntityDamage_ApplyToEntity& xDamage) const
{
	if ( !AVP_Game_Core::IsLocalPlayer( GetGuid() ) )
	{
		// Always ragdoll from explosions unless we're the local player
		bool bAlwaysRagdoll = false;
		Asura_Blueprint* pxBP = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_DAMAGE, xDamage.GetTypeHash() );
		if ( pxBP )
		{
			Asura_Value xVal;
			if ( pxBP->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_ALWAYSRAGDOLL, xVal ) )
			{
				bAlwaysRagdoll = xVal.GetBoolValue();
			}
		}
		if ( bAlwaysRagdoll ) { return ASURA_HASH_ID_UNSET; }
	}

	// We need to get the death anim set from the weapon.
	if (!m_pxInventorySystem) { return ASURA_HASH_ID_UNSET; }
	if (!m_pxAnimationHandler) { return ASURA_HASH_ID_UNSET; }

	const AVP_Inventory_Item* pxCurrentWeapon = m_pxInventorySystem->GetCurrentItem(uAVP_INVENTORY_GROUPHASH_WEAPON);
	if (!pxCurrentWeapon) { return ASURA_HASH_ID_UNSET; }

	const Asura_Blueprint* pxWeaponBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, pxCurrentWeapon->GetBlueprintHash());
	if (!pxWeaponBlueprint) { return ASURA_HASH_ID_UNSET; }

	Asura_Value xValue;
	if (!pxWeaponBlueprint->GetParameterValueByHashID((m_pxAnimationHandler->IsFirstPerson() ? uAVP_BLUEPRINT_PARAM_FIRSTPERSONDEATH : uAVP_BLUEPRINT_PARAM_THIRDPERSONDEATH), xValue)) { return ASURA_HASH_ID_UNSET; }

	const Asura_Blueprint* pxDeathBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_ACTORS, xValue.GetHashIDValue());
	if (!pxDeathBlueprint) { return ASURA_HASH_ID_UNSET; }

	// First version - check if the head has been removed, else check if the impact strength is above a certain threshold.
	if (!IsElementUndamaged(AVP_SET_HEAD) || ((xDamage.GetNewDestroyedSkinElementFlags() & (1 << AVP_SET_HEAD)) != 0))
	{
		// We won't assume all characters have head loss deaths, so continue to the power selection if this fails to provide an anim.
		AVP_Blueprint_ParameterEnumerator_DeathAnimSelector xSelector;
		pxDeathBlueprint->EnumerateParameters(szAVP_BLUEPRINT_ENUMERATIONNAME_DEATHANIM_NAME_HEADLOSS, 2, xSelector);

		const Asura_Hash_ID uSelectedAnim = xSelector.SelectAnim();
		if (uSelectedAnim != ASURA_HASH_ID_UNSET)
		{
			return uSelectedAnim;
		}
	}

	// Build up the string for the parameter enumeration based on the details in the damage struct.
	Asura_Vector_3 xHitDirection;
	xDamage.GetDirection(xHitDirection);

	const Asura_Matrix_3x3& xOri = GetCurrentOrientation();
	const Asura_Vector_3 xForwards(xOri.e13, xOri.e23, xOri.e33);
	const Asura_Vector_3 xRight(xOri.e11, xOri.e21, xOri.e31);

	const float fForwardsDP = xForwards * xHitDirection;
	if (fForwardsDP > 0.0f)
	{
		// Impact applying forwards movement
		g_xDeathAnimParameterName = szAVP_BLUEPRINT_ENUMERATIONNAMEFRAGMENT_DEATHANIM_FORWARD;
	}
	else
	{
		// Impact applying backwards movement
		g_xDeathAnimParameterName = szAVP_BLUEPRINT_ENUMERATIONNAMEFRAGMENT_DEATHANIM_BACKWARD;
	}

	const float fRightDP = xRight * xHitDirection;
	if (fRightDP > 0.0f)
	{
		// Impact applying right movement
		g_xDeathAnimParameterName += szAVP_BLUEPRINT_ENUMERATIONNAMEFRAGMENT_DEATHANIM_RIGHT;
	}
	else
	{
		// Impact applying left movement
		g_xDeathAnimParameterName += szAVP_BLUEPRINT_ENUMERATIONNAMEFRAGMENT_DEATHANIM_LEFT;
	}

	float fStrongImpactStrength = dbg_fDefaultStrongDeathImpactStrength;
	if (pxDeathBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_DEATHANIM_STRONGTHRESHOLD, xValue))
	{
		fStrongImpactStrength = xValue.GetFloatValue();
	}

	if (xDamage.GetImpulseStrength() >= fStrongImpactStrength)
	{
		// Powerful impact
		g_xDeathAnimParameterName += szAVP_BLUEPRINT_ENUMERATIONNAMEFRAGMENT_DEATHANIM_STRONG;
	}
	else
	{
		// Weak impact
		g_xDeathAnimParameterName += szAVP_BLUEPRINT_ENUMERATIONNAMEFRAGMENT_DEATHANIM_WEAK;
	}

	AVP_Blueprint_ParameterEnumerator_DeathAnimSelector xSelector;
	pxDeathBlueprint->EnumerateParameters(g_xDeathAnimParameterName.GetString(), 2, xSelector);

	return xSelector.SelectAnim();
}

void AVP_ClientActor_Vertebrate::TeleportTo( const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation )
{
	PARENT::TeleportTo( xPosition, xOrientation );
	
	AVP_Animation_BodyRig* pxBodyRig = m_pxAnimationHandler ? m_pxAnimationHandler->GetBodyRig() : NULL;
	if ( pxBodyRig )
	{
		Asura_AMatrix3x3 xAOri( xOrientation );
		pxBodyRig->ResetAimDirection( xAOri.GetColumn2() );
		pxBodyRig->ResetLookAtPosition( Asura_AVector3( xPosition ) + xAOri.GetColumn2() * 10.0f );
	}
}
void AVP_ClientActor_Vertebrate::GetAimDirection( Asura_AVector3& xAimDir ) const
{
	AVP_Animation_BodyRig* pxBodyRig = m_pxAnimationHandler ? m_pxAnimationHandler->GetBodyRig() : NULL;
	if ( pxBodyRig )
	{
		pxBodyRig->GetAimDirection( xAimDir );
	}
	else
	{
		PARENT::GetAimDirection( xAimDir );
	}
}

void AVP_ClientActor_Vertebrate::SetAimDirection( const Asura_AVector3& xAimDir, const float fServerTime /*= -1.0f*/ )
{
	AVP_Animation_BodyRig* pxBodyRig = m_pxAnimationHandler ? m_pxAnimationHandler->GetBodyRig() : NULL;
	if ( pxBodyRig )
	{
		pxBodyRig->SetAimDirection( xAimDir, fServerTime );
	}
	else
	{
		PARENT::SetAimDirection( xAimDir, fServerTime );
	}
}

void AVP_ClientActor_Vertebrate::GetLookAtPosition( Asura_AVector3& xLookDir ) const
{
	AVP_Animation_BodyRig* pxBodyRig = m_pxAnimationHandler ? m_pxAnimationHandler->GetBodyRig() : NULL;
	if ( pxBodyRig )
	{
		pxBodyRig->GetLookAtPosition( xLookDir );
	}
	else
	{
		PARENT::GetLookAtPosition( xLookDir );
	}
}

void AVP_ClientActor_Vertebrate::SetLookAtPosition( const Asura_AVector3& xLookDir, const float fServerTime /*= -1.0f*/ )
{
	AVP_Animation_BodyRig* pxBodyRig = m_pxAnimationHandler ? m_pxAnimationHandler->GetBodyRig() : NULL;
	if ( pxBodyRig )
	{
		pxBodyRig->SetLookAtPosition( xLookDir, fServerTime );
	}
	else
	{
		PARENT::SetLookAtPosition( xLookDir, fServerTime );
	}
}

bool AVP_ClientActor_Vertebrate::BeginWeaponDeactivation( Asura_Hash_ID uDeactivationAnim, const bool bCalculateMovement )
{
	AVP_Vertebrate_CameraHandler* pxCameraHandler = static_cast< AVP_Vertebrate_CameraHandler* >( GetCameraHandler() );
	if ( pxCameraHandler )
	{
		pxCameraHandler->SetZoomLevel( *this, 0 );
	}

	return PARENT::BeginWeaponDeactivation( uDeactivationAnim, bCalculateMovement );
}

void AVP_ClientActor_Vertebrate::GetLandingAnimsAndApplyDamage(Asura_Hash_ID& uFirstPersonHash, Asura_Hash_ID& uThirdPersonHash) const
{
	uFirstPersonHash = ASURA_HASH_ID_UNSET;
	uThirdPersonHash = ASURA_HASH_ID_UNSET;

	// Step 1 - get our actor blueprint, and find out if there are any falling speeds we exceed. Pick the greatest of those as the fall to use.
	const Asura_Blueprint* pxBlueprint = GetBlueprint();
	if (!pxBlueprint) { return; }

	AVP_Blueprint_ParameterEnumerator_LandingSelector xEnumerator;
	pxBlueprint->EnumerateParameters(szAVP_BLUEPRINT_PARAM_LANDING_MINFALLDISTANCE, 2, xEnumerator);

	const float fFallDistance = GetCurrentPosition().y - m_fYPosAtFallStart;
	int iLandingSelection = xEnumerator.GetLandingID(fFallDistance);
	if (iLandingSelection < 0) { return; }

	Asura_Value xValue;
	Asura_StringBuffer<32> xString;
	char szIDBuffer[4];

	int iDamageLimitSelection = xEnumerator.GetDamageLimitID(fFallDistance);
	const float fDamageLimitProportion = xEnumerator.GetProportionBetweenLandings(iLandingSelection, iDamageLimitSelection, fFallDistance);

	float fDamageLimit = 0.0f;

	// Form the string for the damage limit, since we need to interpolate our damage between landings, rather than having it increase in steps.

	if (iDamageLimitSelection >= 0)
	{
		Asura_PrintfToBfr(szIDBuffer, "%02d", 4, true, iDamageLimitSelection+1); // Blueprint enumerations start from 1, not 0.
		xString = szAVP_BLUEPRINT_PARAM_LANDING_DAMAGE;
		xString += szIDBuffer;
		if (pxBlueprint->GetParameterValueByHashID(xString.GetHash(), xValue))
		{
			fDamageLimit = xValue.GetFloatValue();
		}
	}

	// Form the string to append to all other strings below.
	Asura_PrintfToBfr(szIDBuffer, "%02d", 4, true, iLandingSelection+1); // Blueprint enumerations start from 1, not 0.

	// The damage for this fall comes from the initial blueprint. For now, only the local player calls this function, so no need to do additional checks to avoid multiple messages in multiplayer here.
	xString = szAVP_BLUEPRINT_PARAM_LANDING_DAMAGE;
	xString += szIDBuffer;
	if (pxBlueprint->GetParameterValueByHashID(xString.GetHash(), xValue))
	{
		float fDamage = xValue.GetFloatValue();
		if (fDamage > 0.0f)
		{
			fDamage += (fDamageLimit - fDamage) * fDamageLimitProportion;

			// Send the damage to the server for handling.
			Asura_Message* pxMessage = Asura_Network::CreateMessage(AVP_MSGID_ENTITYDAMAGE_REQUEST_VICTIM, sizeof(AVP_Message_EntityDamage_RequestVictim));
			if(pxMessage)
			{
				AVP_Message_EntityDamage_RequestVictim*	pxData = static_cast<AVP_Message_EntityDamage_RequestVictim*>(pxMessage->GetData());

				AVP_EntityDamage_RequestVictim xDamage(uAVP_BLUEPRINT_NAME_DAMAGE_FALLING, Guid, ASURA_HASH_ID_UNSET, Guid, fDamage, GetCurrentPosition(), Asura_Vector_3(0.0f, -1.0f, 0.0f), Asura_Vector_3(0.0f, -1.0f, 0.0f));
				pxData->SetDamage(xDamage);

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

	// TODO - This only works for predators and humans, an alien needs to do something different. Further virtual function perhaps?
	const Asura_Blueprint* pxLandingBlueprint = pxBlueprint;
	if (m_pxInventorySystem)
	{
		const AVP_Inventory_Item* pxWeapon = m_pxInventorySystem->GetCurrentItem(uAVP_INVENTORY_GROUPHASH_WEAPON);
		if (pxWeapon)
		{
			pxLandingBlueprint = pxWeapon->GetBlueprint();
		}
	}

	xString = szAVP_BLUEPRINT_PARAM_LANDING_ANIMFIRSTPERSON;
	xString += szIDBuffer;
	if (pxLandingBlueprint->GetParameterValueByHashID(xString.GetHash(), xValue))
	{
		uFirstPersonHash = xValue.GetHashIDValue();
	}

	xString = szAVP_BLUEPRINT_PARAM_LANDING_ANIMTHIRDPERSON;
	xString += szIDBuffer;
	if (pxLandingBlueprint->GetParameterValueByHashID(xString.GetHash(), xValue))
	{
		uThirdPersonHash = xValue.GetHashIDValue();
	}
}

Asura_Quat AVP_ClientActor_Vertebrate::UpdateOrientation()
{
	Asura_AMatrix3x3 xOri( GetCurrentOrientation() );

	// Apply any rotation that might be present in any anims we're playing ( i.e. turn anims )
	Asura_Angle	fDeltaAngle;
	Asura_Vector_3 xAxis;
	GetAngularVelocityFromAnimation( xAxis, fDeltaAngle );

	if ( m_pxAnimationHandler && m_pxAnimationHandler->GetBodyRig() && m_pxAnimationHandler->GetBodyRig()->IsTurning() )
	{
		fDeltaAngle *= m_fTurnAnimAngVelScale;
	}

	float fPhysicsTimeSlice = Asura_Maths::FloatToInt_Chop( ( Asura_Physics_System::GetTimeLeft() + Asura_Timers::GetGameTimeSlice() ) / Asura_Physics_System::GetTimeSlice() ) * Asura_Physics_System::GetTimeSlice();
	fDeltaAngle *= fPhysicsTimeSlice;

	if ( Asura_Maths::Abs( fDeltaAngle ) > 0.0f )
	{
		Asura_Quat xRot;
		xRot.FromRotation( fDeltaAngle, xAxis );
		Asura_AMatrix3x3 xRotMat;
		xRot.ToMatrix( xRotMat );
		xOri = xRotMat * xOri;
	}

	if ( IsMoving() )
	{
		// If we're moving bring our feet round to point in our desired direction
		Asura_AVector3 xForwardsWS;
		GetDesiredForwardMovementDirection( xForwardsWS, xOri );
		Asura_AVector3 xForwardsOS = Asura_AMatrix3x3( GetCurrentOrientation() ).ApplyTranspose( xForwardsWS );
		float fAimAngOS = Asura_Maths::ArcTangent( xForwardsOS.GetX(), xForwardsOS.GetZ() );
		fAimAngOS /= Asura_Maths::Max( 0.001f, Asura_Timers::GetGameTimeSlice() );

		fAimAngOS = Asura_Maths::ClampToRange( fAimAngOS, -dbg_fMaxMovingOrientationAngVel, dbg_fMaxMovingOrientationAngVel );
		
		Asura_AMatrix3x3 xRotMat;
		Asura_Quat xRot;
		xRot.FromRotation( fAimAngOS * Asura_Timers::GetGameTimeSlice(), Asura_AVector3( 0.0f, 1.0f, 0.0f ) );
		xRot.ToMatrix( xRotMat );

		xOri = xRotMat * xOri;
	}
	xOri.Orthogonalise();

	// As a last resort, slide the orientation around so we can aim where we want to.
	const AVP_Actor_AnimationHandler* pxAnimHandler = GetAnimationHandler();
	const AVP_Animation_BodyRig* pxBodyRig = pxAnimHandler ? m_pxAnimationHandler->GetBodyRig() : NULL;
	if ( pxBodyRig )
	{
		Asura_AVector3 xOSAim;
		GetAimDirection( xOSAim );
		xOSAim = xOri.ApplyTranspose( xOSAim );
		float fOSAimPhi = Asura_Maths::ArcTangent( xOSAim.GetX(), xOSAim.GetZ() );
		float fAbsPhi = Asura_Maths::Abs( fOSAimPhi );

		const float fMaxAimRightLeft = Asura_Maths::Min( pxBodyRig->GetMaxAimRightLeft(), dbg_fMaxAimRightLeftLimit );
		if ( fAbsPhi > fMaxAimRightLeft )
		{
			float fSin;
			float fCos;
			const float fPhi = Asura_Maths::Sign( fOSAimPhi ) * ( fAbsPhi - fMaxAimRightLeft );
			Asura_Maths::SinCos( fPhi, fSin, fCos );
			Asura_AMatrix3x3 xRot( fCos, 0.0f, fSin, 0.0f, 1.0f, 0.0f, -fSin, 0.0f, fCos );
			xOri = xRot * xOri;
		}
	}

	Asura_Quat xRet;
	xRet.FromMatrix( xOri );
	return xRet;
}

void AVP_ClientActor_Vertebrate::GetDesiredForwardMovementDirection(Asura_AVector3& xDirection, const Asura_AMatrix3x3& xOri) const
{
	// By default, our desired direction is our aim direction.
	const Asura_AVector3 xDown = xOri.GetColumn1();
	Asura_AVector3 xAim;
	GetAimDirection(xAim);
	xDirection = xAim - (xDown * (xAim * xDown));
	xDirection.Normalise();
}

bool AVP_ClientActor_Vertebrate::IsRagDoll() const
{
	return ( m_pxAnimationHandler ) ? m_pxAnimationHandler->IsRagdoll() : false;
}

void AVP_ClientActor_Vertebrate::SetRagDollAsleep() const
{
	if ( m_pxAnimationHandler ) ( static_cast<AVP_Vertebrate_AnimationHandler*> (m_pxAnimationHandler) )->SetRagDollAsleep();
}

void AVP_ClientActor_Vertebrate::WakeUpRagDoll()  const
{
	if ( m_pxAnimationHandler ) ( static_cast<AVP_Vertebrate_AnimationHandler*> (m_pxAnimationHandler) )->WakeUpRagDoll();
}

bool AVP_ClientActor_Vertebrate::IsRagDollAsleep()  const
{
	return ( m_pxAnimationHandler ) ? ( static_cast<AVP_Vertebrate_AnimationHandler*> (m_pxAnimationHandler) )->IsRagDollAsleep() : false;
}


DEBUGVAR float dbg_fShortRouteMaxOffsetProportion = 2.0f;

bool AVP_ClientActor_Vertebrate::IsShortRouteAvailable(const Asura_Vector_3& xTarget, const float fMinDist, const float fMaxDist) const
{
	const Asura_Vector_3& xOrigin = GetCurrentPosition();

	// Early out - if it's far enough away anyway, we won't bother doing this test.
	const float fMidDist = (fMaxDist + fMinDist) * 0.5f;
	Asura_Vector_3 xDir(xTarget - xOrigin);
	const float fOffsetDist = xDir.Normalise();
	if (fOffsetDist >= fMidDist) { return false; }

	int iRouteLength = 0;
	u_short* pusRouteBuffer = AVP_Navigation::GetWorkingRouteBuffer();

	AVP_Navigation::TestParams xTestParams = AVP_Navigation::s_xTPStandardParams;

	AVP_Navigation::AVP_AI_PATHFINDER_FLAGS eWPFlag;
	AVP_Navigation::AVP_AI_PATHFINDER_FLAGS ePathFlag;
	switch ( GetClassification() )
	{
		case AVP_ENTITYCLASS_PREDATOR:
		{
			eWPFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_PREDATOR;
			ePathFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_PREDATOR;
		}
		break;

		case AVP_ENTITYCLASS_ANDROID:
		case AVP_ENTITYCLASS_CIVILIAN:
		case AVP_ENTITYCLASS_MARINE:
		{
			eWPFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID;
			ePathFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID;
		}
		break;

		case AVP_ENTITYCLASS_ALIEN:
		{
			eWPFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_ALIEN_HIGH_MOBILITY;
			ePathFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_ALIEN_LOW_MOBILITY;
		}
		break;

		default:
		{
			Asura_Assertf( false, ( "Who am I? Unhandled case, add entityclass %d to this switch", GetClassification() ) );
			eWPFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID;
			ePathFlag = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID;
		}
		break;
	}
	xTestParams.m_uPathfinderFlags = eWPFlag;
	xTestParams.m_xNormal = -GetDownVector();
	xTestParams.m_bRejectNonalignedWPs = true;
	u_short usOriginWP, usTargetWP;
	if (!AVP_Navigation::GetNearestWaypointID(xOrigin, usOriginWP, xTestParams)) { return false; }
	if (!Asura_AI_Navigation_System::IsPositionInWaypoint(xOrigin, usOriginWP)) { return false; }
	if (!AVP_Navigation::GetNearestWaypointID(xTarget, usTargetWP, xTestParams)) { return false; }
	if (!Asura_AI_Navigation_System::IsPositionInWaypoint(xTarget, usTargetWP)) { return false; }

	Asura_AI_Navigation_System::FindRouteCustom(pusRouteBuffer,
		iRouteLength,
		AVP_Navigation::GetWorkingRouteBufferLength(),
		usOriginWP,
		usTargetWP,
		ePathFlag,
		&AVP_Navigation::s_xPredatorAStarFunctor,
		AVP_Navigation::FunctorGoalCostFunction,
		AVP_Navigation::FunctorTraversalCostFunction,
		AVP_Navigation::FunctorGoalAchievedFunction,
		AVP_Navigation::FunctorLinkFlagsTestFunction );

	// See if we failed to get a route at all.
	if (iRouteLength <= 0) { return false; }

	// Go through the route and see how far it is.
	// Just use the estimate of the waypoints only, so we aren't affected by which side of the waypoint we're on.
	float fRouteDist = 0.0f;
	const Asura_AI_NavVertex* pxCurrentWaypoint = Asura_AI_Navigation_System::GetWaypoint(pusRouteBuffer[0]);
	for (int i=1; i<iRouteLength; ++i)
	{
		const Asura_AI_NavVertex* pxNewWaypoint = Asura_AI_Navigation_System::GetWaypoint(pusRouteBuffer[i]);
		fRouteDist += (pxNewWaypoint->GetPosition() - pxCurrentWaypoint->GetPosition()).Magnitude();
		pxCurrentWaypoint = pxNewWaypoint;
	}

	return (fRouteDist / fOffsetDist) <= dbg_fShortRouteMaxOffsetProportion;
}

bool AVP_ClientActor_Vertebrate::IsIdleForFidget() const
{
	bool bParentIdle = PARENT::IsIdleForFidget();
	return bParentIdle && ( m_pxCameraHandler == NULL || static_cast< AVP_Vertebrate_CameraHandler* >( m_pxCameraHandler )->GetZoomLevel() == 0 );
}

Asura_Hash_ID AVP_ClientActor_Vertebrate::GetActiveGrappleBlueprintID(int iGrappleDifficulty) const
{
	Asura_Assertf((iGrappleDifficulty >= 0) && (iGrappleDifficulty <= m_iNumActiveGrappleDifficulties), ("Cannot get active grapple blueprint for entity %d of difficulty %d (entity has %d difficulties).", Guid, iGrappleDifficulty, m_iNumActiveGrappleDifficulties));
	// 0 means no AG, so is valid as an option (so not in the assert), but can still return here with any invalid difficulty values.
	if ((iGrappleDifficulty <= 0) || (iGrappleDifficulty > m_iNumActiveGrappleDifficulties))
	{
		return ASURA_HASH_ID_UNSET;
	}

	// Grapple difficulties start from 1, but indices obviously start from 0.
	return m_auActiveGrappleBlueprints[iGrappleDifficulty-1];
}

void AVP_ClientActor_Vertebrate::InitialiseGrappleDifficulties(const Asura_Blueprint& xActorBlueprint)
{
	m_iNumActiveGrappleDifficulties = 0;

	// Enumerate all parameters in the grapple difficulties blueprint we pull out of our actor blueprint.
	Asura_Value xValue;
	if (xActorBlueprint.GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_GRAPPLEDIFFICULTIES, xValue))
	{
		Asura_Hash_ID uDifficultiesHash = xValue.GetHashIDValue();
		if (uDifficultiesHash != ASURA_HASH_ID_UNSET)
		{
			const Asura_Blueprint* pxDifficulties = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_GRAPPLEDESIGN, xValue.GetHashIDValue());
			if (pxDifficulties)
			{
				// The variables are filled in by the enumerator itself.
				AVP_Blueprint_ParameterEnumerator_GrappleDifficulties xEnumerator(m_auActiveGrappleBlueprints, m_iNumActiveGrappleDifficulties, MAX_ACTIVEGRAPPLE_DIFFICULTIES);
				pxDifficulties->EnumerateParameters(uAVP_BLUEPRINT_PARAM_GRAPPLEDIFFICULTIES_DIFFICULTY, 2, xEnumerator);
			}
		}
	}
}

// Parameter enumerator for death anims.
AVP_Blueprint_ParameterEnumerator_DeathAnimSelector::AVP_Blueprint_ParameterEnumerator_DeathAnimSelector()
: PARENT()
, m_uNumDeathAnims(0)
{
}

Asura_Hash_ID AVP_Blueprint_ParameterEnumerator_DeathAnimSelector::SelectAnim() const
{
	if (m_uNumDeathAnims == 0) { return ASURA_HASH_ID_UNSET; }

	const u_int uIndex = Asura_Random::GetIntU(0, m_uNumDeathAnims-1);
	return m_auDeathAnims[uIndex];
}

void AVP_Blueprint_ParameterEnumerator_DeathAnimSelector::BeginEnumeration()
{
	m_uNumDeathAnims = 0;
}

bool AVP_Blueprint_ParameterEnumerator_DeathAnimSelector::Enumerate(const u_int uIndex, char const* const szName, const Asura_Value& xValue)
{
	if (m_uNumDeathAnims >= MAX_DEATH_ANIMS)
	{
		Asura_Assert(false, "Too many death anims, ignoring the rest.");
		return false;
	}

	m_auDeathAnims[m_uNumDeathAnims++] = xValue.GetHashIDValue();
	return true;
}

void AVP_Blueprint_ParameterEnumerator_DeathAnimSelector::EndEnumeration(const bool bSuccess)
{
	// Nothing to do here, as the caller needs to call the selection function.
	// Success is irrelevant, as we should still have a valid array of animations, we just happened to find more than we could buffer up.
}


// And for landing anims. In this case, we just need to enumerate the landing speeds and find which is the greatest we exceed.

AVP_Blueprint_ParameterEnumerator_LandingSelector::AVP_Blueprint_ParameterEnumerator_LandingSelector()
: PARENT()
, m_uNumLandingDistances(0)
{
}

int AVP_Blueprint_ParameterEnumerator_LandingSelector::GetLandingID(float fDistanceFallen) const
{
	int iSelectedLanding = -1;
	float fGreatestDistance = -1.0f;
	for (u_int u=0; u<m_uNumLandingDistances; ++u)
	{
		if ((fDistanceFallen >= m_afLandingDistances[u]) && (fGreatestDistance < m_afLandingDistances[u]))
		{
			fGreatestDistance = m_afLandingDistances[u];
			iSelectedLanding = static_cast<int>(u);
		}
	}

	return iSelectedLanding;
}

int AVP_Blueprint_ParameterEnumerator_LandingSelector::GetDamageLimitID(float fDistanceFallen) const
{
	int iSelectedLimit = -1;
	float fLeastDistance = Asura_Maths::LargeFloat;
	for (u_int u=0; u<m_uNumLandingDistances; ++u)
	{
		if ((fDistanceFallen < m_afLandingDistances[u]) && (fLeastDistance > m_afLandingDistances[u]))
		{
			fLeastDistance = m_afLandingDistances[u];
			iSelectedLimit = static_cast<int>(u);
		}
	}

	return iSelectedLimit;
}

float AVP_Blueprint_ParameterEnumerator_LandingSelector::GetProportionBetweenLandings(int iLowerID, int iUpperID, float fDistanceFallen) const
{
	if ((iLowerID < 0) || (iUpperID < 0))
	{
		return 0.0f;
	}
	else
	{
		const float fLowerDistance = m_afLandingDistances[iLowerID];
		const float fUpperDistance = m_afLandingDistances[iUpperID];
		Asura_Assert(fUpperDistance > fLowerDistance, "Landing parameters passed in wrong order.");
		Asura_Assert((fLowerDistance < fDistanceFallen) && (fDistanceFallen < fUpperDistance), "Distance fallen not between landings chosen.");

		return (fDistanceFallen - fLowerDistance) / (fUpperDistance - fLowerDistance);
	}
}

void AVP_Blueprint_ParameterEnumerator_LandingSelector::BeginEnumeration()
{
	m_uNumLandingDistances = 0;
}

bool AVP_Blueprint_ParameterEnumerator_LandingSelector::Enumerate(const u_int uIndex, char const* const szName, const Asura_Value& xValue)
{
	if (m_uNumLandingDistances >= MAX_LANDING_DISTANCES)
	{
		Asura_Assert(false, "Too many landings, ignoring the rest.");
		return false;
	}

	m_afLandingDistances[m_uNumLandingDistances++] = xValue.GetFloatValue();
	return true;
}

void AVP_Blueprint_ParameterEnumerator_LandingSelector::EndEnumeration(const bool bSuccess)
{
	// Nothing to do here, as the caller needs to call the selection function.
	// Success is irrelevant, as we should still have a valid array of animations, we just happened to find more than we could buffer up.
}


AVP_Blueprint_ParameterEnumerator_GrappleDifficulties::AVP_Blueprint_ParameterEnumerator_GrappleDifficulties(Asura_Hash_ID* puActiveGrappleBlueprints, int& iNumDifficulties, int iMaxDifficulties)
: m_puActiveGrappleBlueprints(puActiveGrappleBlueprints)
, m_iNumDifficulties(iNumDifficulties)
, m_iMaxDifficulties(iMaxDifficulties)
{
}

void AVP_Blueprint_ParameterEnumerator_GrappleDifficulties::BeginEnumeration()
{
	m_iNumDifficulties = 0;
}

bool AVP_Blueprint_ParameterEnumerator_GrappleDifficulties::Enumerate(const u_int uIndex, const Asura_Value &xValue)
{
	if (m_iNumDifficulties >= m_iMaxDifficulties)
	{
		Asura_Assert(false, "Too many grapple difficulties, ignoring the rest.");
		return false;
	}

	Asura_Hash_ID uBlueprint = xValue.GetHashIDValue();

	if (uBlueprint != ASURA_HASH_ID_UNSET)
	{
		m_puActiveGrappleBlueprints[m_iNumDifficulties++] = uBlueprint;
	}

	return true;
}



/// End of file. ///
