//////////////////////////////////////////////////////////////////////////////////////////
//
// FILE : Asura_ServerEntity_PhysicalObject.cpp
//
// Inherits from Asura_Entity_Server. Abstract base class for physical
// objects in the game that have shape, dynamics block and health.
//
// *NOTE* Asura_ServerEntity_PhysicalObject is special in that it passes some
// messages/updates up to Asura_Entity_Server. Other classes in the hierarchy don't
// cascade messages up to their parents.
//
//
// CREATED		: Richard M. Smith (3 July 2002)
// AUTHORS		: Martin G. Harris, Richard M. Smith
//
// Copyright (C) 2002 Rebellion, All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////////////////

//////////////
// INCLUDES //
//////////////

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

// header
#include "Entity/PhysicalObject/Asura_ServerEntity_PhysicalObject.h"

// asura
#include "AI/Asura_AI_Navigation.h"
#include "Blueprint/Asura_Blueprint_HashIDs.h"
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_LOS.h"
#include "Core/Asura_Timers.h"
#include "Core/Memory/Asura_Mem.h"
#include "Debugging/Asura_Assert.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/Asura_StaticMessage_System.h"
#include "Entity/Asura_Entity_Event.h"
#include "Entity/PhysicalObject/Asura_ApplyForce_Messages.h"
#include "Entity/PhysicalObject/Asura_ClientEntity_PhysicalObject.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Shapes/Asura_Shape_System.h"
#include "SpecialFX/Asura_Fragment_System.h"
#include "Teleport/Asura_Teleport_Messages.h"

// axon
#ifdef ASURA_USE_AXON
#include "Core/Axon_Brain.h"
#endif

// tools
#ifdef ASURA_TOOLS
#include "Entity/Tools_SecondaryEntityFile_System.h"
#include "EntityTemplate/Tools_EntityTemplate_System.h"
#include "Load&Save/LevelExport/Tools_Level_Export.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Tools_System.h"
#endif

////////////////////
// Static members //
////////////////////

u_short*                      Asura_ServerEntity_PhysicalObject::s_pusWaypoints           = NULL;
u_int                         Asura_ServerEntity_PhysicalObject::s_uWaypointArrayCapacity = 0;
u_int                         Asura_ServerEntity_PhysicalObject::s_uNumWaypoints          = 0;
Asura_AI_NavConnection_Index* Asura_ServerEntity_PhysicalObject::s_pxLinksToBlock         = NULL;
u_int                         Asura_ServerEntity_PhysicalObject::s_uLinkArrayCapacity     = 0;
u_int                         Asura_ServerEntity_PhysicalObject::s_uNumLinksToBlock       = 0;

Asura_Hashed_List<Asura_ServerEntity_PhysicalObject> Asura_ServerEntity_PhysicalObject::s_xList(5);

#ifdef ASURA_USE_STREAMING
bool         Asura_ServerEntity_PhysicalObject::s_bLinkBlockPassPending    = false;
unsigned int Asura_ServerEntity_PhysicalObject::s_uLinkBlockPassFrameStart = UINT_MAX;
#endif

//////////////////////////////////////////////////////////////////////////////////////////
// Asura_ServerEntity_PhysicalObject                                                    //
//////////////////////////////////////////////////////////////////////////////////////////

Asura_ServerEntity_PhysicalObject::Asura_ServerEntity_PhysicalObject(int iBBIndex /*= ASURA_DYNAMIC_BB_DEFAULT*/)
: Asura_Entity_Server(ASURA_GUID_UNREGISTERED) // ignored
, m_pxDynamicObject(0)
, m_uPhysicalObjectFlags(0)
, m_uFileID(ASURA_HASH_ID_UNSET)
, m_uSkinID(ASURA_HASH_ID_UNSET)
, m_uAnimID(ASURA_HASH_ID_UNSET)
, m_uAnimFileID(ASURA_HASH_ID_UNSET)
, m_iAnimFlags(AsuraAnimationFlag_Loop)
, m_fAnimTimer(0)
, m_fAnimEndTime(1.0f)
, m_fHealth(0.0f)
, m_fMaxHealth(0.0f)
, m_iBBIndex(iBBIndex)
, m_uParentGuid(ASURA_GUID_UNREGISTERED)
, m_uParentMarkerHashID(ASURA_HASH_ID_UNSET)
, m_ePhysicsTypeBeforeAttach(ASURA_PHYSICS_OBJECT_DYNAMIC)
, m_sTextureAnimationSpeed(100)
, m_ucTextureAnimationSequence(0)
, m_uNumLinksToBlock(0)
, m_pxLinksToBlock(NULL)
, m_uFlags(0)
#ifdef ASURA_USE_AXON
, m_pxBrain(NULL)
#endif
#ifdef ASURA_TOOLS
, m_xSavedQuat(Asura_Maths::IdentityQuat)
, m_xSavedPosition(Asura_Maths::ZeroVector3)
, m_bDiscardWaypointInfo(false)
#endif
{
	s_xList.AddToList(this);
}

Asura_ServerEntity_PhysicalObject::~Asura_ServerEntity_PhysicalObject()
{
	s_xList.RemoveFromList(this);
}

bool Asura_ServerEntity_PhysicalObject::HandleMessage(const Asura_Message* pxMessage)
{
	if (!pxMessage) return false;

	switch(pxMessage->GetMsgID())
	{
		case ASURA_MSG_DESTRUCT:
		{
			//Send any destruction messages. RAR 08/04/03
			SendOnDestructionMessages();
			SetThisAndClientsAsDestroyed();
			return true;
		}
        
        case ASURA_MSG_CHANGE_ANIMATION:
        {
            const Asura_Message_PhysicalObject_ChangeAnimation* pxData = reinterpret_cast
                <const Asura_Message_PhysicalObject_ChangeAnimation*>(pxMessage->GetData());
            Asura_Assert(pxMessage->GetDataSize() == sizeof(Asura_Message_PhysicalObject_ChangeAnimation), "MH: serious error. data size has changed");
            Asura_Assert(m_uSkinID != ASURA_HASH_ID_UNSET, "can't change anim without a skin");
            ChangeAnim(pxData->m_uAnimID,pxData->m_uAnimFileID, pxData->m_iFlags,pxData->m_fTweenDuration,pxData->m_fStartTime,pxData->m_fEndTime);
            return true;
        }

		case ASURA_MSG_TEXTUREANIMATION :
		{
            const Asura_Message_TextureAnimation* pxData = reinterpret_cast
                <const Asura_Message_TextureAnimation*>(pxMessage->GetData());
            Asura_Assert(pxMessage->GetDataSize() == sizeof(Asura_Message_TextureAnimation), "serious error. data size has changed");

			if(pxData->m_bChangeSequence)
			{
				m_ucTextureAnimationSequence = (unsigned char) pxData->m_uSequence;
			}
			if(pxData->m_bChangeSpeed)
			{
				m_sTextureAnimationSpeed = (short)Asura_Maths::FloatToInt(pxData->m_fSpeed*100.0f);
			}

			//forward this message to the client entities
			//Doing this rather than relying on client updates to ensure it works for static objects
	        Asura_Network::Forward(pxMessage,ASURA_GUID_MY_CLIENT_ENTITIES,GetGuid());
			return true;
		}

		case ASURA_MSG_HIDE :
		case ASURA_MSG_UNHIDE :
        {
			//update invisible flag
			if(pxMessage->GetMsgID() == ASURA_MSG_HIDE)
				SetPhysicalObjectFlags(m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_INVISIBLE);
			else
				SetPhysicalObjectFlags(m_uPhysicalObjectFlags &~ ASURA_PHYSICALOBJECT_FLAG_INVISIBLE);

			//forward to client entities
	        Asura_Network::Forward(pxMessage,ASURA_GUID_MY_CLIENT_ENTITIES,GetGuid());
			return true;
        }

		case ASURA_MSG_COLLISIONSON :
        {
			SetNoCollisionsFlag(false);
            UpdateWaypointLinkBlocks();
			return true;
        }

		case ASURA_MSG_COLLISIONSOFF :
        {
			SetNoCollisionsFlag(true);
            UpdateWaypointLinkBlocks();
            return true;
        }

		case ASURA_MSG_HIDEFORCUTSCENE :
		{
            const unsigned char* pucHide = reinterpret_cast
                <const unsigned char*>(pxMessage->GetData());
			HandleHideForCutscene((*pucHide) ? true : false);
			return true;
		}

        case ASURA_MSG_SET_DESTRUCTIBLE:
        {
            const bool* pbDestructible = reinterpret_cast< const bool* >( pxMessage->GetData() );
            if ( pbDestructible )
            {
                SetDestructible( *pbDestructible );
            }
            return true;
        }

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

			LinkToParent(pxData->GetParentGuid(), pxData->GetMarkerHash());
			return(true);
		}

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

			LinkToParent(pxData->GetParentGuid(), pxData->GetMarkerHash(), true);
			return(true);
		}

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

			LinkToParent(pxData->GetParentGuid(), pxData->GetPosition(), pxData->GetOrientation(), pxData->GetBoneID());
			return(true);
		}

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

			LinkToParent(pxData->GetParentGuid(), pxData->GetMarkerHash(), pxData->GetBoneID(), pxData->GetPosition());
			return(true);
		}

		case ASURA_MSG_UNLINK_FROM_PARENT:
		{
			UnlinkFromParent();
			return(true);
		}

		#ifdef ASURA_USE_DIALOGUE_SYSTEM
		case ASURA_MSG_DIALOGUE_PLAYLINEONENTITY :
		{
			//just forward this to the clients. RAR 28/11/2006
			Asura_Network::Forward(pxMessage,ASURA_GUID_ALL_CLIENTMACHINES,GetGuid());
			return true;
		}
		#endif //ASURA_USE_DIALOGUE_SYSTEM

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

			if(pxData != NULL)
			{
				Asura_Vector_3		xPosition;
				Asura_Matrix_3x3	xOrientation;

				pxData->GetPosition(xPosition);
				pxData->GetOrientation(xOrientation);
				TeleportTo(xPosition, xOrientation, true);
			}
			return(true);
		}

		case ASURA_MSG_MOVECHAINCOMPLETE:
		{
			// By default, just ignore this message. Projects may decide to make use of it though.
			return true;
		}

		case ASURA_MSG_PLAY_PFX_ON_ENTITY:
		{
			// Ignore the message if the entity isn't active.
			if (IsEntityActive())
			{
				Asura_Network::Forward( pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES, GetGuid(), false );
			}
			return true;
		}

		case ASURA_MSG_RESTORE_HEALTH:
		{
			SetHealth( GetMaxHealth() );  
			return true;
		}

		#ifdef ASURA_USE_AXON
		case ASURA_MSG_AXON_GS2_ATTRACTOR_PULSE:
		case ASURA_MSG_AXON_GS2_ATTRACTOR_CANCEL:
		case ASURA_MSG_AXON_GS2_LEAVE_NODE:
		case ASURA_MSG_AXON_GS2_EXIT_GAMESCENE:
		case ASURA_MSG_AXON_GS2_ANIM_CHANGE:
		case ASURA_MSG_AXON_GS2_TELEPORT_TO_NODE:
		case ASURA_MSG_AXON_GS2_TAKE_LINK:
		case ASURA_MSG_AXON_GS2_DONT_STOP_AT_NODE:
		case ASURA_MSG_AXON_GS2_PATHFIND_TO_NODE:
		{
			if ( GetBrain() )
			{
				GetBrain()->DoHandleMessage( pxMessage );
			}
			return true;
		}
		#endif

		case ASURA_MSG_APPLY_LINEAR_FORCE:
		{
			ProcessMessage_ApplyLinearImpuse( pxMessage );
			return true;
		}

		default: break;
	}

	return Asura_Entity_Server::HandleMessage(pxMessage);
}


void Asura_ServerEntity_PhysicalObject::ApplyLinearImpuseFromMsg( const Asura_Vector_3& xSourcePos, const Asura_Vector_3& xOffsetFromCentreOfMass, ApplyForceMsg_ImpulseData& xImpulseData )
{
	if( HasPhysicsObject() )
	{
		const Asura_Vector_3& xDestPos = GetPosition();

		Asura_Vector_3 xDirection = xDestPos - xSourcePos;
		
		float fLength = xDirection.Normalise();	// Get distance from Source to Dest position.

		float fImpulsePower = xImpulseData.CalculateImplusePower( fLength );

		if( fImpulsePower!=0.0f && xDirection.ApproximatelyEquals( Asura_Maths::ZeroVector3 )==false )
		{
			m_pxDynamicObject->ApplyImpulse( (xDirection * fImpulsePower), xOffsetFromCentreOfMass + xDestPos );
		}
	}
}


void Asura_ServerEntity_PhysicalObject::ProcessMessage_ApplyLinearImpuse( const Asura_Message* pxMessage )
{
	const Asura_Msg_ApplyForce_Linear*	pxData  = static_cast<const Asura_Msg_ApplyForce_Linear*>(pxMessage->GetData());

	if( !pxData || !HasPhysicsObject() )
	{
		return;
	}

	Asura_ServerEntity_PhysicalObject* pxSourceEntity  = Asura_ServerEntity_PhysicalObject::FindEntity( pxData->m_uSourcePosObjectGuid );
	if( !pxSourceEntity )
	{
		Asura_Assertf(false, ("Can't find Source pos object in Apply Linear Impulse message! (Guid %d). Set it in Message Editor.", GetGuid() ));

		pxSourceEntity = Asura_ServerEntity_PhysicalObject::FindEntity( pxMessage->GetFromGuid() );
		if( !pxSourceEntity )
		{
			Asura_Assertf(false, ("Can't even find the sender of Apply Linear Impulse message! (Guid %d). Apply Linear Impulse will have no effect!", GetGuid() ));
			return;
		}
	}

	ApplyForceMsg_ImpulseData xImpulseData;
	if( xImpulseData.FillFromBlueprint( pxData->m_uExtraInfoBPHash )==false )
	{
		// We couldn't find any info from this blueprint.
		return;
	}

	const Asura_Vector_3& xSourcePos = pxSourceEntity->GetPosition();

	ApplyLinearImpuseFromMsg( xSourcePos, pxData->m_xOffsetFromCentreOfMass, xImpulseData );
}

void Asura_ServerEntity_PhysicalObject::Update()
{
	if (m_uAnimID != ASURA_HASH_ID_UNSET)
	{
		m_fAnimTimer += Asura_Timers::GetGameTimeSlice();
	}
	if (m_uParentGuid != ASURA_GUID_UNREGISTERED && !HasAttachedWithPhysicsFlag())
	{
        // (NY - 07.03.2007) only do this if we're ignoring physics
		// keep the dynamics object in the right place, even though it's set to be ignored.
		const Asura_ServerEntity_PhysicalObject* pxParent = GetList()->FindObject( m_uParentGuid, false );
		Asura_Assert( pxParent, "parent entity for linked object not found");
		if (pxParent)
		{
			SetPosition( pxParent->GetPosition() );
		}
	}
	Asura_Entity_Server::Update();
}

///////////////////////
// Chunk buffer code //
///////////////////////
void Asura_ServerEntity_PhysicalObject::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	/*
	This entity type can be used in template files , so any changes to WriteToChunkStream may
	require ApplyChangesForTemplateInstance() to be updated as well.
	Please see the comment at the top of Tools/EntityTemplate/Tools_EntityTemplate_System.h before 
	making any changes.
	RAR 29/04/2004
	*/
	const u_int uVersion = uCURRENT_CHUNK_VERSION;

	// Write the version.
	xStream << uVersion;

	// Write the parent data
	Asura_Entity_Server::WriteToChunkStream(xStream);

	// Write the data for this class
#ifdef ASURA_TOOLS
	if(m_xSavedPosition.ApproximatelyEquals(GetPosition()))
	{
		//Approximately the same as the position from load ,so assume any differences were
		//unintentional. Go with the value from load to prevent changes to the checksum for the object.
		//RAR 23/01/2006
		xStream << m_xSavedPosition;
	}
	else
#endif
	{
		xStream << GetPosition();
	}

	Asura_Quat xOrientation;
	xOrientation.FromMatrix(GetOrientation());
	//Need to renormalise the quaternion , since otherwise it will gradually drift away
	//as the entity is loaded and saved. RAR 20/05/03
	xOrientation.Normalise();

#ifdef ASURA_TOOLS
	if(m_xSavedQuat.ApproximatelyEquals(xOrientation))
	{
		//Approximately the same as the quat from load ,so assume any differences were
		//unintentional. Go with the value from load to prevent changes to the checksum for the object.
		//RAR 23/01/2006
		xStream << m_xSavedQuat;
	}
	else
#endif
	{
		xStream << xOrientation;
	}

	xStream << GetVelocity();

	Asura_Vector_3 xAngVelocity;
	GetAngVelocity(xAngVelocity);
	xStream << xAngVelocity;

	bool bIsAsleep = (m_pxDynamicObject!=NULL)?m_pxDynamicObject->IsAsleep():true;
	xStream << bIsAsleep;

	xStream << GetHealth();
	xStream << GetMaxHealth();

#ifdef ASURA_TOOLS
	if(Tools_Level_Export::IsExport() && IsMergedWithEnvironment())
	{
		//Mesh has been merged to the environment , so set the shape hash ID to null
		//for level exports (since the shape won't be needed , and may not ba available)
		//RAR 10/05/04
		xStream << static_cast<Asura_Hash_ID>(ASURA_HASH_ID_UNSET);
	}
	else
#endif
	{
		xStream << m_uFileID;
	}

	xStream << m_uSkinID;
	xStream << m_uAnimID;
	xStream << m_uAnimFileID;
	xStream << m_iAnimFlags;
	xStream << GetBBIndex();
    {
    // Create the bitfield...
    Asura_WriteToStream_Bitfield_Wrapper< u_int > xFlagsBitfield( m_uFlags );
    xFlagsBitfield.Add( m_bIsDestructible, 1 );
    xFlagsBitfield.Add( m_bBlockedLinks, 1 );
	xFlagsBitfield.Add( m_bPhysicsObjectIgnoredByDeactivate, 1 );
	xFlagsBitfield.Add( m_bHasBrain, 1 );
	xStream << xFlagsBitfield;
    }
    xStream << m_uPhysicalObjectFlags;
	xStream << m_fAnimTimer;
	xStream << m_sTextureAnimationSpeed;
	xStream << m_ucTextureAnimationSequence;
    
	u_int uNumLinksToBlock = m_uNumLinksToBlock;
	#ifdef ASURA_TOOLS
	//If not all waypoints are being exported, then scan through the list of blocked connections and see how
	//many are still valid. RAR 09/04/2008
	if(Tools_Level_Export::IsExport() && Asura_AI_Navigation_System::HasSubsetForExport())
	{
		for ( u_int u = 0; u < m_uNumLinksToBlock; ++u )
    	{
    	    Asura_AI_NavConnection_Index xConnection;
			if(!Asura_AI_Navigation_System::ConvertNavConnectionIndexForExport(m_pxLinksToBlock[u], xConnection))
    	    {
				--uNumLinksToBlock;
			}
    	}
	}
	#endif
    xStream << uNumLinksToBlock;
	xStream << m_fAnimEndTime;

	xStream.Write4BytePadding();

	#ifdef ASURA_TOOLS
	if(Tools_Level_Export::IsExport() && Asura_AI_Navigation_System::HasSubsetForExport())
	{
		//Write out converted waypoint connections that are valid for the subset of waypoints being exported.
		//RAR 09/04/2008
		for ( u_int u = 0; u < m_uNumLinksToBlock; ++u )
    	{
    	    Asura_AI_NavConnection_Index xConnection;
			if(Asura_AI_Navigation_System::ConvertNavConnectionIndexForExport(m_pxLinksToBlock[u], xConnection))
    	    {
    	    	xStream << xConnection.m_usWaypointIndex;
    	    	xStream << xConnection.m_usConnectionIndex;
			}
    	}
	}
	else
	#endif
	{
		for ( u_int u = 0; u < m_uNumLinksToBlock; ++u )
    	{
    	    xStream << m_pxLinksToBlock[ u ].m_usWaypointIndex;
    	    xStream << m_pxLinksToBlock[ u ].m_usConnectionIndex;
    	}
	}

	#ifdef ASURA_USE_AXON
	if ( m_bHasBrain )
	{
		Asura_Assert( GetBrain(), "Asura_ServerEntity_PhysicalObject::WriteToChunkStream - we must have a brain at this point" );
		if ( GetBrain() )
		{
			GetBrain()->WriteToChunkStream( xStream );
		}
	}
	#endif

	/*
	This entity type can be used in template files , so any changes to WriteToChunkStream may
	require ApplyChangesForTemplateInstance() to be updated as well.
	Please see the comment at the top of Tools/EntityTemplate/Tools_EntityTemplate_System.h before 
	making any changes.
	RAR 29/04/2004
	*/
}

void Asura_ServerEntity_PhysicalObject::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	u_int			uVersion;
	Asura_Vector_3	xPosition;
	Asura_Quat		xOrientation;
	Asura_Vector_3	xVelocity;
	Asura_Vector_3	xAngVelocity;
	bool			bIsAsleep;

	// Read the version.
	xStream >> uVersion;

	// Read the parent data
	Asura_Entity_Server::ReadFromChunkStream( xStream );

	// Read our data.
	if ( uVersion == uCURRENT_CHUNK_VERSION )
	{
		xStream >> xPosition;
		xStream >> xOrientation;
		xStream >> xVelocity;
		xStream >> xAngVelocity;
		xStream >> bIsAsleep;

		xStream >> m_fHealth;
		xStream >> m_fMaxHealth;
		xStream >> m_uFileID;
		xStream >> m_uSkinID;
		xStream >> m_uAnimID;
		xStream >> m_uAnimFileID;
		xStream >> m_iAnimFlags;
		xStream >> m_iBBIndex;

		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xFlagsBitfield( m_uFlags );
		xStream >> xFlagsBitfield;
		// Deliberately clear the m_bBlockedLinks flag here, as the nav data doesn't remember whether it was blocked or not.
		m_bBlockedLinks = false;
		xStream >> m_uPhysicalObjectFlags;
		xStream >> m_fAnimTimer;
		xStream >> m_sTextureAnimationSpeed;
		xStream >> m_ucTextureAnimationSequence;
		xStream >> m_uNumLinksToBlock;
		xStream >> m_fAnimEndTime;

		xStream.Skip4BytePadding();

		delete [] m_pxLinksToBlock;
		m_pxLinksToBlock = new Asura_AI_NavConnection_Index[ m_uNumLinksToBlock ];
		for ( u_int u = 0; u < m_uNumLinksToBlock; ++u )
		{
			xStream >> m_pxLinksToBlock[ u ].m_usWaypointIndex;
			xStream >> m_pxLinksToBlock[ u ].m_usConnectionIndex;
		}

#ifdef ASURA_USE_AXON
		if ( m_bHasBrain )
		{
			CreateAndInitialiseBrain();
			Asura_Assert( GetBrain(), "Asura_ServerEntity_PhysicalObject::ReadFromChunkStream - no brain - we will not read all of our chunk data" );
			if ( GetBrain() )
			{
				GetBrain()->ReadFromChunkStream( xStream );
			}
		}
#endif
	}
	else if ( uVersion == 19 )
	{
		xStream >> xPosition;
		xStream >> xOrientation;
		xStream >> xVelocity;
		xStream >> xAngVelocity;
		xStream >> bIsAsleep;

		xStream >> m_fHealth;
		xStream >> m_fMaxHealth;
		xStream >> m_uFileID;
		xStream >> m_uSkinID;
		xStream >> m_uAnimID;
		xStream >> m_uAnimFileID;
		xStream >> m_iAnimFlags;
		xStream >> m_iBBIndex;

		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xFlagsBitfield( m_uFlags );
		xStream >> xFlagsBitfield;
		// Deliberately clear the m_bBlockedLinks flag here, as the nav data doesn't remember whether it was blocked or not.
		m_bBlockedLinks = false;
		xStream >> m_uPhysicalObjectFlags;
		xStream >> m_fAnimTimer;
		xStream >> m_sTextureAnimationSpeed;
		xStream >> m_ucTextureAnimationSequence;
		u_char ucNumLinksToBlock;
		xStream >> ucNumLinksToBlock;
		m_uNumLinksToBlock = ucNumLinksToBlock;
		xStream >> m_fAnimEndTime;

		xStream.Skip4BytePadding();

		delete [] m_pxLinksToBlock;
		m_pxLinksToBlock = new Asura_AI_NavConnection_Index[ m_uNumLinksToBlock ];
		for ( u_int u = 0; u < m_uNumLinksToBlock; ++u )
		{
			xStream >> m_pxLinksToBlock[ u ].m_usWaypointIndex;
			xStream >> m_pxLinksToBlock[ u ].m_usConnectionIndex;
		}

#ifdef ASURA_USE_AXON
		if ( m_bHasBrain )
		{
			CreateAndInitialiseBrain();
			Asura_Assert( GetBrain(), "Asura_ServerEntity_PhysicalObject::ReadFromChunkStream - no brain - we will not read all of our chunk data" );
			if ( GetBrain() )
			{
				GetBrain()->ReadFromChunkStream( xStream );
			}
		}
#endif
	}
	else if ( uVersion == 18 )
	{
		xStream >> xPosition;
		xStream >> xOrientation;
		xStream >> xVelocity;
		xStream >> xAngVelocity;
		xStream >> bIsAsleep;

		xStream >> m_fHealth;
		xStream >> m_fMaxHealth;
		xStream >> m_uFileID;
		xStream >> m_uSkinID;
		xStream >> m_uAnimID;
		xStream >> m_uAnimFileID;
		xStream >> m_iAnimFlags;
		xStream >> m_iBBIndex;

		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xFlagsBitfield( m_uFlags );
		xStream >> xFlagsBitfield;
		// Deliberately clear the m_bBlockedLinks flag here, as the nav data doesn't remember whether it was blocked or not.
		m_bBlockedLinks = false;
		xStream >> m_uPhysicalObjectFlags;
		xStream >> m_fAnimTimer;
		xStream >> m_sTextureAnimationSpeed;
		xStream >> m_ucTextureAnimationSequence;
		u_char ucNumLinksToBlock;
		xStream >> ucNumLinksToBlock;
		m_uNumLinksToBlock = ucNumLinksToBlock;
		xStream >> m_fAnimEndTime;

		xStream.Skip4BytePadding();

		delete [] m_pxLinksToBlock;
		m_pxLinksToBlock = new Asura_AI_NavConnection_Index[ m_uNumLinksToBlock ];
		for ( u_int u = 0; u < m_uNumLinksToBlock; ++u )
		{
			xStream >> m_pxLinksToBlock[ u ].m_usWaypointIndex;
			xStream >> m_pxLinksToBlock[ u ].m_usConnectionIndex;
		}
	}
	else if ( uVersion == 17 )
	{
		xStream >> xPosition;
		xStream >> xOrientation;
        xVelocity    = Asura_Maths::ZeroVector3;
        xAngVelocity = Asura_Maths::ZeroVector3;
        bIsAsleep    = true;

		xStream >> m_fHealth;
		xStream >> m_fMaxHealth;
		xStream >> m_uFileID;
		xStream >> m_uSkinID;
		xStream >> m_uAnimID;
		xStream >> m_uAnimFileID;
		xStream >> m_iAnimFlags;
		xStream >> m_iBBIndex;

		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xFlagsBitfield( m_uFlags );
		xStream >> xFlagsBitfield;
		// Deliberately clear the m_bBlockedLinks flag here, as the nav data doesn't remember whether it was blocked or not.
		m_bBlockedLinks = false;
		xStream >> m_uPhysicalObjectFlags;
		xStream >> m_fAnimTimer;
		xStream >> m_sTextureAnimationSpeed;
		xStream >> m_ucTextureAnimationSequence;
		u_char ucNumLinksToBlock;
		xStream >> ucNumLinksToBlock;
		m_uNumLinksToBlock = ucNumLinksToBlock;
		xStream >> m_fAnimEndTime;

		xStream.Skip4BytePadding();

		delete [] m_pxLinksToBlock;
		m_pxLinksToBlock = new Asura_AI_NavConnection_Index[ m_uNumLinksToBlock ];
		for ( u_int u = 0; u < m_uNumLinksToBlock; ++u )
		{
			xStream >> m_pxLinksToBlock[ u ].m_usWaypointIndex;
			xStream >> m_pxLinksToBlock[ u ].m_usConnectionIndex;
		}
	}
	else if ( uVersion == 16 )
	{
		xStream >> xPosition;
		xStream >> xOrientation;
		xVelocity    = Asura_Maths::ZeroVector3;
		xAngVelocity = Asura_Maths::ZeroVector3;
		bIsAsleep    = true;

		xStream >> m_fHealth;

		m_fMaxHealth = m_fHealth;

		xStream >> m_uFileID;
		xStream >> m_uSkinID;
		xStream >> m_uAnimID;
		xStream >> m_uAnimFileID;
		xStream >> m_iAnimFlags;

		xStream >> m_iBBIndex;

		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xFlagsBitfield( m_uFlags );
		xStream >> xFlagsBitfield;
		// Deliberately clear the m_bBlockedLinks flag here, as the nav data doesn't remember whether it was blocked or not.
		m_bBlockedLinks = false;
		xStream >> m_uPhysicalObjectFlags;
		xStream >> m_fAnimTimer;
		xStream >> m_sTextureAnimationSpeed;
		xStream >> m_ucTextureAnimationSequence;
		u_char ucNumLinksToBlock;
		xStream >> ucNumLinksToBlock;
		m_uNumLinksToBlock = ucNumLinksToBlock;
		xStream >> m_fAnimEndTime;

		xStream.Skip4BytePadding();

		delete [] m_pxLinksToBlock;
		m_pxLinksToBlock = new Asura_AI_NavConnection_Index[ m_uNumLinksToBlock ];
		for ( u_int u = 0; u < m_uNumLinksToBlock; ++u )
		{
			xStream >> m_pxLinksToBlock[ u ].m_usWaypointIndex;
			xStream >> m_pxLinksToBlock[ u ].m_usConnectionIndex;
		}
	}
	else if ( uVersion < 16 )
	{
		Asura_Assertf( false, ( "Asura_ServerEntity_PhysicalObject::ReadFromChunkStream - version no longer supported (%u)", uVersion ) );
		return;
	}
	else
	{
		Asura_Assertf( false, ( "Asura_ServerEntity_PhysicalObject::ReadFromChunkStream - unhandled chunk version (%u)", uVersion ) );
		return;
	}

	// Do any setup, following the data load.

	// AMC - 12/10/07 - Move the call to SetBBIndex( m_iBBIndex ) below where the m_uPhysicalObjectFlags are read in, 
	// as a function ResetCollisionObject(), called from SetBBIndex(), checks the m_uPhysicsObjectFlags via the call HasNoCollisionsFlag().
	// In the original InitialiseFromChunkBuffer(), the m_uPhysicsObjectFlags had been read before this function was called.
	SetBBIndex( m_iBBIndex );

	// RDM - 13/12/06 - Moved this section to the end to allow overrides of SetPosition/SetOrientation 
	// to have access to a fully-initialised entity rather than a partially-filled out one. 
	Asura_Matrix_3x3 xMat;
	xOrientation.ToMatrix( xMat );
	InitialisePosition( xPosition );
	SetOrientation( xMat );
	if ( !bIsAsleep )
	{
		SetLinVelocity( xVelocity );
		SetAngVelocity( xAngVelocity );
	}
	
#ifdef ASURA_TOOLS
	m_xSavedPosition = xPosition;
	m_xSavedQuat	 = xOrientation;
#endif

#ifdef ASURA_TOOLS
	m_bDiscardWaypointInfo = false;

    // When we're dealing with templates, just disregard link blocking data
    // until we instantiate them.
    if ( Tools_System::IsActive() )
	{
		if (Tools_EntityTemplate::IsUpdatingFromTemplate() || Tools_SaveAsura::GetFileType() == AsuraFile_Template)
		{
			m_bDiscardWaypointInfo = true;
		}
	}
#endif
}

void Asura_ServerEntity_PhysicalObject::FillInChunkBuffer(char*& pcChunkBuffer) 
{
	Asura_Chunk_ChunkBuffer_Stream xStream(pcChunkBuffer);
	Asura_ServerEntity_PhysicalObject::WriteToChunkStream(xStream);
}

void Asura_ServerEntity_PhysicalObject::InitialiseFromChunkBuffer(const char*& pcChunkBuffer)
{
	Asura_Chunk_ChunkBuffer_Stream	xStream(pcChunkBuffer);
	Asura_ServerEntity_PhysicalObject::ReadFromChunkStream(xStream);
}

int Asura_ServerEntity_PhysicalObject::GetChunkBufferSize() 
{
	Asura_Chunk_Dummy_Stream xStream;
	Asura_ServerEntity_PhysicalObject::WriteToChunkStream(xStream);

	return xStream.GetPosition();
}

//////////////////////////////////////////////////////
// TakeDamage: Reduce health if destructible.       
// Calls MakeDead() if health is reduced to zero.		
// Also responsible for sending 'On X% Health messages.  
//////////////////////////////////////////////////////
void Asura_ServerEntity_PhysicalObject::TakeDamage(float fDamage)
{
	if (IsDamageable())
	{
		const float fOldHealth = GetHealth();

		m_fHealth -= fDamage;
		if (m_fHealth<=0)
		{
			m_fHealth = 0;
			MakeDead();
		}
		
		SendOnHealthPercentMessages( fOldHealth, GetHealth() );
	}
}

void Asura_ServerEntity_PhysicalObject::SendOnHealthPercentMessages( const float fOldHealth, const float fNewHealth )
{
	// Deal with any events that may need to be sent.
	const Asura_EventAndMessage* pxNextEvent = GetEvents();
	for( int i = GetNumEvents(); i; ++pxNextEvent, --i )
	{
		const u_short usMsgBlockID = pxNextEvent->m_usMessageBlockID;
		if( usMsgBlockID == ASURA_STATICMESSAGE_EMPTYBLOCK ) continue;

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


//////////////////////////////////////////////////////
// PostLoadInitialise  - Update the block links here instead of after reading from chunk
//////////////////////////////////////////////////////
void Asura_ServerEntity_PhysicalObject::PostLoadInitialise()
{
	PARENT::PostLoadInitialise();

	#ifdef ASURA_USE_AXON
	if ( GetBrain() )
	{
		GetBrain()->PostLoadInitialise();
	}
	#endif

	#ifdef ASURA_TOOLS
	if ( m_bDiscardWaypointInfo )
	{
		DiscardWaypointLinkBlockInformation();
	}
	else
	#endif
	{
		#ifdef ASURA_USE_STREAMING
		// We need to set up the correct navigation handler for this entity before updating the waypoint link blocks.
		const Asura_Hash_ID uContainingZoneHashID = UpdateContainingZone( GetPosition() );
		if ( uContainingZoneHashID != ASURA_HASH_ID_UNSET )
		{
			Asura_AI_Navigation_Handler& xOldHandler = Asura_AI_Navigation_System::GetCurrentHandler();
			Asura_AI_Navigation_System::SetCurrentHandler( uContainingZoneHashID );
			UpdateWaypointLinkBlocks();
			Asura_AI_Navigation_System::SetCurrentHandler( &xOldHandler );
		}
		#else
		UpdateWaypointLinkBlocks();
		#endif
	}
}

void Asura_ServerEntity_PhysicalObject::PreDestructionShutdown()
{
	if (m_pxDynamicObject)
	{
		Asura_Physics_Object::Destroy( m_pxDynamicObject );
		m_pxDynamicObject = NULL;
	}

	// Remove any waypoint link blocks.
	#ifdef ASURA_USE_STREAMING
	// We need to set up the correct navigation handler for this entity before updating the waypoint link blocks.
	const Asura_Hash_ID uContainingZoneHashID = UpdateContainingZone( GetPosition() );
	if ( uContainingZoneHashID != ASURA_HASH_ID_UNSET )
	{
		Asura_AI_Navigation_Handler& xOldHandler = Asura_AI_Navigation_System::GetCurrentHandler();
		Asura_AI_Navigation_System::SetCurrentHandler( uContainingZoneHashID );
		BlockLinks( false );
		Asura_AI_Navigation_System::SetCurrentHandler( &xOldHandler );
	}
	#else
	BlockLinks( false );
	#endif
	delete [] m_pxLinksToBlock;
    m_pxLinksToBlock   = NULL;
	m_uNumLinksToBlock = 0;

	#ifdef ASURA_USE_AXON
	ShutdownAndDestroyBrain();
	#endif

	PARENT::PreDestructionShutdown();
}

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

	#ifdef ASURA_USE_AXON
	if ( GetBrain() )
	{
		GetBrain()->InitialiseFromBlueprint( xBlueprint );
	}
	#endif
}

///////////////////////////////////////////////////////////////////////////////////
// MakeDead: Called if health is reduced to zero by TakeDamage,
// *not* called if it receives a DESTRUCT message or is SetAsDestroyed.
//
// Override this for adding server side damage SFX or to prevent/delay deletion.
//
// By default it just sends a message to the clients that the health is zero.
///////////////////////////////////////////////////////////////////////////////////
void Asura_ServerEntity_PhysicalObject::MakeDead()
{
	m_fHealth = 0.0f; // Make sure we are dead.

	//Send any death messages.
	SendOnDeathMessages();
}

///////////////////////////////////////////////////////
// ResetCollisionObject: Reset the collision object. //
///////////////////////////////////////////////////////
void Asura_ServerEntity_PhysicalObject::ResetCollisionObject()
{
	// MGH 14/3/03: Glass panes have their own collision objects that have already been set up.
	if (AsuraEntityClass_Glass == Classification) return;

	if (m_pxDynamicObject)
	{	
		Asura_Shape *pxShape = NULL;
		if (GetShapeFileID() != ASURA_HASH_ID_UNSET)
		{
			pxShape = Asura_Shape_System::GetShape(GetShapeFileID());
		}

		if(IsMergedWithEnvironment() || IsMergedWithSkybox())
		{
			m_pxDynamicObject->SetIgnore(true);
		}
		else if (pxShape &&
			pxShape->HasNoCollisions())
		{
			m_pxDynamicObject->SetIgnore(true);
		}
		else
		{
			//In theory , if the NoCollisions flag wasn't set here then we might want to do SetIgnore(false) ,
			//so that objects would get collisions in tools if the shape was changed from one that had no collisions ,
			//or if the merged with environment flag was turned off. However , I can't do that , since some derived
			//classes will most likely disable collisions on their own. RAR 12/01/2005
			if(HasNoCollisionsFlag())
			{
				m_pxDynamicObject->SetIgnore(true);
			}
		}

		// KOR - 11/11/2005 - physics rendering still needs the collision definition even if it's set to ignore
		const Asura_Collision_Object* pxCollisionObject = Asura_Dynamic_Extents::GetCollisionObject(m_iBBIndex, GetShapeFileID(), GetGuid());
		m_pxDynamicObject->SetCollisionObject(pxCollisionObject);
	}
}

void Asura_ServerEntity_PhysicalObject::SetPhysicalObjectFlags(u_int uFlags)
{
	u_int uChangedFlags = m_uPhysicalObjectFlags^uFlags;
	m_uPhysicalObjectFlags = uFlags;
	//RAR 27/06/2006 Don't let the 'skin' flag be changed along with the other flags.
	UpdateSkinPhysicalObjectFlag();
		
	#ifdef ASURA_TOOLS
	//If the merged with environment flag has changed , then the collision object will need to be changed
	//Ditto for merge with environment
	if(uChangedFlags & (ASURA_PHYSICALOBJECT_FLAG_ISMERGEDWITHENV | ASURA_PHYSICALOBJECT_FLAG_SHOULDMERGEWITHSKY))
	{
		ResetCollisionObject();
	}
	#endif
	if(uChangedFlags & ASURA_PHYSICALOBJECT_FLAG_NOCOLLISIONS)
	{
		if (m_pxDynamicObject)
		{		
			if(HasNoCollisionsFlag())
			{
				//collisions switched off
				m_pxDynamicObject->SetIgnore(true);
			}
			else
			{
				//collisions switched on.. but may not be able to turn on in dynamics block
				Asura_Shape *pxShape = NULL;
				if (GetShapeFileID() != ASURA_HASH_ID_UNSET)
				{
					pxShape = Asura_Shape_System::GetShape(GetShapeFileID());
				}
				if(!IsMergedWithEnvironment() &&
					!(pxShape && pxShape->HasNoCollisions()) )
				{
					//Going to assume that if an object is switching the 'No Collisions' flag off , then
					//it isn't a derived object that never has collisions (and so isn't fiddling with
					//SetIgnore on its own)	RAR 12/01/2005
					m_pxDynamicObject->SetIgnore(false);
				}
			}
		}
	}
	if(uChangedFlags & ASURA_PHYSICALOBJECT_FLAG_SHOULDMERGEWITHSKY)
	{
		// send a client update
		ForceUpdateMessage();
		MakeClientUpdateMessage();
	}
}

///////////////
// SetHealth //
///////////////
void Asura_ServerEntity_PhysicalObject::SetHealth(float fHealth) 
{
	Asura_Assert(fHealth>=0.0f,"Invalid health");
	m_fHealth = fHealth; 
}

void Asura_ServerEntity_PhysicalObject::SetNoCollisionsFlag( bool bSet )
{
    if ( bSet )
    {
        SetPhysicalObjectFlags( m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_NOCOLLISIONS );
    }
    else
    {
        SetPhysicalObjectFlags( m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_NOCOLLISIONS );
    }
}

void Asura_ServerEntity_PhysicalObject::SetBlockNavDataGenerationFlag( bool bBlock )
{
	if ( bBlock )
	{
		SetPhysicalObjectFlags( m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_BLOCKNAVDATAGENERATION );
	}
	else
	{
		SetPhysicalObjectFlags( m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_BLOCKNAVDATAGENERATION );
	}
}

void Asura_ServerEntity_PhysicalObject::SetAttachedWithPhysicsFlag( bool bAttachWithPhysics )
{
	if ( bAttachWithPhysics )
	{
		SetPhysicalObjectFlags( m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_ATTACHEDWITHPHYSICS );
	}
	else
	{
		SetPhysicalObjectFlags( m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_ATTACHEDWITHPHYSICS );
	}
}

void Asura_ServerEntity_PhysicalObject::SetBlockWaypointLinksFlag( bool bBlock )
{
    if ( bBlock )
    {
        SetPhysicalObjectFlags( m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_BLOCKWAYPOINTLINKS );
    }
    else
    {
        SetPhysicalObjectFlags( m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_BLOCKWAYPOINTLINKS );
    }
}

#ifdef ASURA_USE_STREAMING
void Asura_ServerEntity_PhysicalObject::BeginLinkBlockPass()
{
    if ( !s_bLinkBlockPassPending )
    {
        s_bLinkBlockPassPending = true;
        Asura_ServerEntity_PhysicalObject::InitialiseWaypointLinkBlockCalculations();
    }
}

void Asura_ServerEntity_PhysicalObject::UpdateLinkBlockPass()
{
    // We divide the entities up by masking their guid and the frame number.
    // This ensures that we only do at most 1/32nd of them every frame.
    enum
    {
        SELECTION_MASK = 0x0000001f     // (32 - 1)
    };
    const u_int uCappedFrameNumber = Asura_Timers::GetFrameNumber() & SELECTION_MASK;

    if ( s_bLinkBlockPassPending )
    {
        // If we have a pass pending then reset the pass start frame
        s_bLinkBlockPassPending    = false;
        s_uLinkBlockPassFrameStart = uCappedFrameNumber;
    }
    else if ( s_uLinkBlockPassFrameStart == UINT_MAX )
    {
        // If we are not doing a pass then just return.
        return;
    }
    else if ( uCappedFrameNumber == s_uLinkBlockPassFrameStart )
    {
        // If we have finished our pass then reset our frame count and the calculations and return.
        s_uLinkBlockPassFrameStart = UINT_MAX;
        Asura_ServerEntity_PhysicalObject::ShutdownWaypointLinkBlockCalculations();
        return;
    }

    // Remember the current nav handler for restoration when we're done.
    Asura_AI_Navigation_Handler& xOldHandler = Asura_AI_Navigation_System::GetCurrentHandler();

    Asura_Hashed_List_It< Asura_ServerEntity_PhysicalObject > xPhyObjIter( Asura_ServerEntity_PhysicalObject::GetList() );
    while ( !xPhyObjIter.Done() )
    {
        Asura_ServerEntity_PhysicalObject* pxPhyObj = xPhyObjIter.GetCurrent();
        xPhyObjIter.Next();

        const u_int uCappedGuid = pxPhyObj->GetGuid() & SELECTION_MASK;
        if ( uCappedGuid != uCappedFrameNumber )
        {
            continue;
        }

        const Asura_Hash_ID uContainingZoneHashID = pxPhyObj->UpdateContainingZone( pxPhyObj->GetPosition() );
        if ( uContainingZoneHashID == ASURA_HASH_ID_UNSET )
        {
            continue;
        }

        // We need to set up the correct nav handler for this entity before updating the waypoint link blocks.
        Asura_AI_Navigation_System::SetCurrentHandler( uContainingZoneHashID );
        pxPhyObj->UpdateWaypointLinkBlocks( true );
        pxPhyObj->CalculateWaypointLinkBlocks( false );
        pxPhyObj->UpdateWaypointLinkBlocks();
    }

    // Restore the original nav handler.
    Asura_AI_Navigation_System::SetCurrentHandler( &xOldHandler );
}

void Asura_ServerEntity_PhysicalObject::ShutdownLinkBlockPass()
{
    if ( s_bLinkBlockPassPending || s_uLinkBlockPassFrameStart != UINT_MAX )
    {
        s_bLinkBlockPassPending    = false;
        s_uLinkBlockPassFrameStart = UINT_MAX;
        Asura_ServerEntity_PhysicalObject::ShutdownWaypointLinkBlockCalculations();
    }
}
#endif

void Asura_ServerEntity_PhysicalObject::InitialiseWaypointLinkBlockCalculations()
{
    if ( !WaypointLinkBlockCalculationsInitialised() )
    {
		static const u_int uINITIAL_ARRAY_SIZE = 512;

		s_uWaypointArrayCapacity = uINITIAL_ARRAY_SIZE;
        s_pusWaypoints           = new u_short[ s_uWaypointArrayCapacity ];
        s_uNumWaypoints          = 0;

		s_uLinkArrayCapacity = uINITIAL_ARRAY_SIZE;
        s_pxLinksToBlock     = new Asura_AI_NavConnection_Index[ s_uLinkArrayCapacity ];
		s_uNumLinksToBlock   = 0;
    }
}

void Asura_ServerEntity_PhysicalObject::ShutdownWaypointLinkBlockCalculations()
{
    // Release the memory if it has been initialised and if there is no link block pass currently being done.
    bool bShouldShutdown = WaypointLinkBlockCalculationsInitialised();
#ifdef ASURA_USE_STREAMING
    bShouldShutdown &= ( !s_bLinkBlockPassPending && s_uLinkBlockPassFrameStart == UINT_MAX );
#endif
    if ( bShouldShutdown )
    {
        delete [] s_pusWaypoints;
        s_pusWaypoints           = NULL;
		s_uWaypointArrayCapacity = 0;
        s_uNumWaypoints          = 0;

        delete [] s_pxLinksToBlock;
        s_pxLinksToBlock     = NULL;
		s_uLinkArrayCapacity = 0;
        s_uNumLinksToBlock   = 0;
    }
}

bool Asura_ServerEntity_PhysicalObject::CalculatePhysicsObjectWaypointLinkBlocks( Asura_Physics_Object* pxPhysicsObject, const bool bForceUpdate /*=true*/ )
{
    if ( !WaypointLinkBlockCalculationsInitialised() )
    {
        Asura_Assert( false, "Asura_ServerEntity_PhysicalObject::CalculateWaypointLinkBlocks - calculations not initialised yet" );
        return false;
    }

	Asura_Assert(pxPhysicsObject, "This object hasn't had its physics object initialised yet. This may leave waypoints open which shouldn't be.");
	if (!pxPhysicsObject) { return false; }

    if ( !HasBlockWaypointLinksFlag() )
    {
        if ( m_uNumLinksToBlock )
        {
            m_uNumLinksToBlock = 0;
            delete [] m_pxLinksToBlock;
            m_pxLinksToBlock = NULL;
            return true;
        }
        else
        {
            return false;
        }
    }

    // If we've already calculated them we don't do it again unless we're forced to.
    if ( m_uNumLinksToBlock > 0 && !bForceUpdate )
    {
        return false;
    }

    // Clear the blocked waypoint list, keeping the old list so we can compare later on.
    const u_int                               uOldCount = m_uNumLinksToBlock;
    const Asura_AI_NavConnection_Index* const pxOldList = m_pxLinksToBlock;
    m_uNumLinksToBlock = 0;
    m_pxLinksToBlock   = NULL;

    // Calculate the links that we block.
    s_uNumWaypoints    = 0;
    s_uNumLinksToBlock = 0;
	Asura_Bounding_Box xEnlargedBB;
	xEnlargedBB = pxPhysicsObject->GetBoundingBox();
	xEnlargedBB.Grow( Asura_AI_Navigation_System::GetMaxWaypointRadius() * 1.5f );
    Asura_AI_Navigation_System::SearchWaypoints( xEnlargedBB, AddWaypointToTest );
    TestForPhysicsObjectBlockedLinks( pxPhysicsObject );

    // Fill in the details, if any.
    if ( s_uNumLinksToBlock )
    {
        m_uNumLinksToBlock = s_uNumLinksToBlock;
        m_pxLinksToBlock   = new Asura_AI_NavConnection_Index[ m_uNumLinksToBlock ];
        Asura_Mem::Copy( m_pxLinksToBlock, s_pxLinksToBlock, m_uNumLinksToBlock * sizeof( Asura_AI_NavConnection_Index ) );
    }

    if ( uOldCount != m_uNumLinksToBlock )
    {
        delete [] pxOldList;
        return true;
    }
    else
    {
        bool bDifferent = false;
        for ( u_int u = 0; u < m_uNumLinksToBlock; ++u )
        {
            if ( pxOldList[ u ] != m_pxLinksToBlock[ u ] )
            {
                bDifferent = true;
                break;
            }
        }
        delete [] pxOldList;
        return bDifferent;
    }
}

void Asura_ServerEntity_PhysicalObject::TestForPhysicsObjectBlockedLinks( Asura_Physics_Object* pxPhysicsObject )
{
    s_uNumLinksToBlock = 0;

    for ( u_int uWP = 0; uWP < s_uNumWaypoints; ++uWP )
    {
        const u_short             usWPIndex = s_pusWaypoints[ uWP ];
        const Asura_AI_NavVertex* pxWP      = Asura_AI_Navigation_System::GetWaypoint( usWPIndex );
        for ( u_int uLink = 0; uLink < pxWP->GetNumLinks(); ++uLink )
        {
            const u_int               uTargetWPIndex = pxWP->GetLink( uLink ).GetIndex();
            const Asura_AI_NavVertex* pxTargetWP     = Asura_AI_Navigation_System::GetWaypoint( uTargetWPIndex );

            for ( float fOffset = 0.0f; fOffset < 0.51f; fOffset += 0.5f )
            {
                Asura_Vector_3 xAlpha = pxWP->m_xPosition + ( pxWP->GetNormal() * fOffset );
				xAlpha -= pxPhysicsObject->GetPosition();
				xAlpha = pxPhysicsObject->GetOrientation().ApplyTranspose( xAlpha );

				Asura_Vector_3 xBeta = pxTargetWP->m_xPosition - pxWP->m_xPosition;
                const float fMaxDist = xBeta.Magnitude();
                xBeta *= 1.0f / fMaxDist;
				xBeta = pxPhysicsObject->GetOrientation().ApplyTranspose( xBeta );

				float fCollisionDist;
                if ( Asura_Maths::RayIntersectsBoundingBox( xAlpha, xBeta, pxPhysicsObject->GetCollisionObject()->GetBoundingBox(), &fCollisionDist ) )
                {
                    if ( fCollisionDist <= fMaxDist )
                    {
						if ( !AddLinkToBlock( usWPIndex, uLink ) ) { return; }

                        // If the other waypoint is also in our array of waypoints to test then we don't need to block the link
						// going the other way as the link would only get tested again (and end up being blocked 4 times).
						bool bBlockLinkComingOtherWay = true;
						for ( u_short uWP2 = 0; uWP2 < s_uNumWaypoints && bBlockLinkComingOtherWay; ++uWP2 )
						{
							if ( uTargetWPIndex == s_pusWaypoints[ uWP2 ] )
							{
								bBlockLinkComingOtherWay = false;
							}
						}

						if ( bBlockLinkComingOtherWay )
						{
							// Block the link coming the other way (if there is one) also.
							for ( u_int uTargetLinkIndex = 0; uTargetLinkIndex < pxTargetWP->GetNumLinks(); ++uTargetLinkIndex )
							{
								if ( pxTargetWP->GetLink( uTargetLinkIndex ).GetIndex() == usWPIndex )
								{
									if ( !AddLinkToBlock( uTargetWPIndex, uTargetLinkIndex ) ) { return; }
									break;
								}
							}
						}

						// Break out of the height increment loop.
                        break;
                    }
                }
            }
        }
    }
}

bool Asura_ServerEntity_PhysicalObject::AddWaypointToTest( u_short usWaypointIndex )
{
	if ( s_uNumWaypoints == s_uWaypointArrayCapacity )
	{
		static const u_int uARRAY_SIZE_INCREMENT = 256;

		// We must enlarge the array.
		u_short* pusNewArray = new u_short[ s_uWaypointArrayCapacity + uARRAY_SIZE_INCREMENT ];
		if ( !pusNewArray )
		{
			Asura_Assert( false, "Asura_ServerEntity_PhysicalObject::AddWaypointToTest - cannot allocate any more memory" );
			return false;
		}
		// Copy over the old entries.
		Asura_Mem::Copy( pusNewArray, s_pusWaypoints, s_uWaypointArrayCapacity * sizeof( u_short ) );
		s_uWaypointArrayCapacity += uARRAY_SIZE_INCREMENT;
		// Swap the arrays over.
		delete [] s_pusWaypoints;
		s_pusWaypoints = pusNewArray;
	}

    s_pusWaypoints[ s_uNumWaypoints++ ] = usWaypointIndex;
    return true;
}

bool Asura_ServerEntity_PhysicalObject::AddLinkToBlock( const u_int uWPID, const u_int uLinkIndex )
{
	if ( s_uNumLinksToBlock == s_uLinkArrayCapacity )
	{
		static const u_int uARRAY_SIZE_INCREMENT = 256;

		// We must enlarge the array.
		Asura_AI_NavConnection_Index* pusNewArray = new Asura_AI_NavConnection_Index[ s_uLinkArrayCapacity + uARRAY_SIZE_INCREMENT ];
		if ( !pusNewArray )
		{
			Asura_Assert( false, "Asura_ServerEntity_PhysicalObject::AddLinkToBlock - cannot allocate any more memory" );
			return false;
		}
		// Copy over the old entries.
		Asura_Mem::Copy( pusNewArray, s_pxLinksToBlock, s_uLinkArrayCapacity * sizeof( Asura_AI_NavConnection_Index ) );
		s_uLinkArrayCapacity += uARRAY_SIZE_INCREMENT;
		// Swap the arrays over.
		delete [] s_pxLinksToBlock;
		s_pxLinksToBlock = pusNewArray;
	}

	s_pxLinksToBlock[ s_uNumLinksToBlock ].m_usWaypointIndex   = static_cast< u_short >( uWPID );
	s_pxLinksToBlock[ s_uNumLinksToBlock ].m_usConnectionIndex = static_cast< u_short >( uLinkIndex );
	++s_uNumLinksToBlock;
	return true;
}

void Asura_ServerEntity_PhysicalObject::UpdateWaypointLinkBlocks( const bool bForceRemoval /*=false*/ )
{
	bool bShouldBlockLinks = true;
	// (TI - 03.07.2008) - Added check for IsPhysicsObjectIgnored as you can deactivate an object and it used
	// to still mark links as blocked even though it had no collisions.  This used to be a common hack to temporarily
	// block off parts of the environment to AI but the new waypoint blocker volumes should be used for this now.
	if ( bForceRemoval || !HasBlockWaypointLinksFlag() || HasNoCollisionsFlag() || IsPhysicsObjectIgnored() )
	{
		bShouldBlockLinks = false;
	}

    BlockLinks( bShouldBlockLinks );
}

void Asura_ServerEntity_PhysicalObject::BlockLinks( const bool bBlock )
{
    if ( ( m_bBlockedLinks != 0 ) == bBlock )
    {
        return;
    }

    for ( u_int u = 0; u < m_uNumLinksToBlock; ++u )
    {
        Asura_AI_Navigation_System::MarkConnectionAsBlocked( m_pxLinksToBlock[ u ], bBlock );
    }

    m_bBlockedLinks = bBlock;
}

bool Asura_ServerEntity_PhysicalObject::DiscardWaypointLinkBlockInformation()
{
    const bool bHadLinks = ( m_uNumLinksToBlock != 0 );

    delete [] m_pxLinksToBlock;
    m_pxLinksToBlock   = NULL;
    m_uNumLinksToBlock = 0;
	m_bBlockedLinks = false;

    return bHadLinks;
}

////////////////
// ChangeAnim //
////////////////
void Asura_ServerEntity_PhysicalObject::ChangeAnim(Asura_Hash_ID uAnimID, Asura_Hash_ID uAnimFileID, int iAnimFlags, float fTweenDuration, float fStartTime,float fEndTime)
{
    Asura_Assert(m_uSkinID != ASURA_HASH_ID_UNSET, "can't change anim without a skin");
    // set up defaults so that clients who missed the initial anim will look OK.
    m_uAnimID = uAnimID;
    m_iAnimFlags = iAnimFlags;
	m_fAnimTimer = 0;
	m_fAnimEndTime = fEndTime;
    // send change message to clients.
    Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_CHANGE_ANIMATION, 
        sizeof(Asura_Message_PhysicalObject_ChangeAnimation));
    if (pxMessage)
    {
        Asura_Message_PhysicalObject_ChangeAnimation* pxChangeAnimData = static_cast
            <Asura_Message_PhysicalObject_ChangeAnimation*>(pxMessage->GetData());
        pxChangeAnimData->m_uAnimID = uAnimID;
        pxChangeAnimData->m_uAnimFileID = uAnimFileID;
        pxChangeAnimData->m_iFlags = iAnimFlags;
        pxChangeAnimData->m_fTweenDuration = fTweenDuration;
		pxChangeAnimData->m_fStartTime = fStartTime;
		pxChangeAnimData->m_fEndTime = fEndTime;
        Send(pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES);
    }
}

void Asura_ServerEntity_PhysicalObject::SendOnDestructionMessages()
{
//	if(m_usOnDestructionMessageBlock != ASURA_STATICMESSAGE_EMPTYBLOCK)
//	{
//		Asura_StaticMessage_System::SendMessages(m_usOnDestructionMessageBlock,GetGuid());
//	}
	SendMessagesForEvent(ASURA_ENTITY_EVENT_ONDESTRUCTION);
}

void Asura_ServerEntity_PhysicalObject::SendOnDeathMessages()
{
	SendMessagesForEvent(ASURA_ENTITY_EVENT_ONDEATH);
}

void Asura_ServerEntity_PhysicalObject::OnEntityActivateMessage()
{
	PARENT::OnEntityActivateMessage();

	if (m_bPhysicsObjectIgnoredByDeactivate)
	{
		m_bPhysicsObjectIgnoredByDeactivate = false;
		SetNoCollisionsFlag( false );
		UpdateWaypointLinkBlocks();
	}

	#ifdef ASURA_USE_AXON
	if ( GetBrain() )
	{
		SetBrainActive( true );
	}
	#endif
}

void Asura_ServerEntity_PhysicalObject::OnEntityDeactivateMessage()
{
	#ifdef ASURA_USE_AXON
	if ( GetBrain() )
	{
		SetBrainActive( false );
	}
	#endif

	if (!IsPhysicsObjectIgnored())
	{
		m_bPhysicsObjectIgnoredByDeactivate = true;
		SetNoCollisionsFlag( true );
		UpdateWaypointLinkBlocks();
	}

	PARENT::OnEntityDeactivateMessage();
}

#ifdef ASURA_USE_AXON
void Asura_ServerEntity_PhysicalObject::DestroyBrain( Axon_Brain* pxBrain )
{
	delete pxBrain;
}

void Asura_ServerEntity_PhysicalObject::CreateAndInitialiseBrain()
{
	Asura_Assert( !m_pxBrain, "Asura_ServerEntity_PhysicalObject::CreateAndInitialiseBrain - we already have a brain" );
	m_pxBrain = CreateBrain();
	if ( GetBrain() )
	{
		m_bHasBrain = true;
		GetBrain()->Initialise();
		SetBrainActive( IsEntityActive() );
	}
	else
	{
		m_bHasBrain = false;
	}
}

void Asura_ServerEntity_PhysicalObject::ShutdownAndDestroyBrain()
{
	if ( GetBrain() )
	{
		GetBrain()->Shutdown();
		DestroyBrain( GetBrain() );
		m_pxBrain = NULL;
	}
	m_bHasBrain = false;
}

void Asura_ServerEntity_PhysicalObject::SetBrainActive( const bool bActive )
{
	if ( GetBrain() )
	{
		// Don't go through Asura_Network since this can be called from the loading thread and usage of the message pool isn't thread safe.
		Asura_Message xTempMessage;
		Asura_Mem::Set( &xTempMessage, 0, sizeof( Asura_Message ) );
		xTempMessage.SetToGuid( GetGuid() );
		xTempMessage.SetFromGuid( GetGuid() );
		xTempMessage.SetMsgID( static_cast< u_short >( bActive ? ASURA_MSG_ACTIVATE : ASURA_MSG_DEACTIVATE ) );
		GetBrain()->DoHandleMessage( &xTempMessage );
	}
}
#endif

bool Asura_ServerEntity_PhysicalObject::ChangeToDestroyedShape()
{
	//get the current shape being used
	if (GetShapeFileID() == ASURA_HASH_ID_UNSET) return false;
	if(IsMergedWithEnvironment()) return false;

	Asura_Shape *pxShape = Asura_Shape_System::GetShape(GetShapeFileID());
	if(!pxShape) return false;

	//get the fragment type used by the shape
	Asura_Hash_ID uFragmentHash = pxShape->GetFragmentHashID();
	if(uFragmentHash == ASURA_HASH_ID_UNSET) return false;

	//and get the destroyed shape
	Asura_Hash_ID uDestroyedShapeHash = Asura_Fragment_System::GetDestroyedShapeHash(uFragmentHash);
	if(uDestroyedShapeHash == ASURA_HASH_ID_UNSET) return false;

	//set the new shape
	SetShapeFileID(uDestroyedShapeHash);
	
	// get an update sent to the client entity
	RequestClientUpdate();
	
	return true;
}

void Asura_ServerEntity_PhysicalObject::SetBBIndex( int iIndex )
{
	m_iBBIndex = iIndex;
    ResetCollisionObject();
}

// MGH 24/4/2003: Loop over all the physical objects resetting their collision objects.
// This is sometimes needed after loading the level. It does no harm to do it again even
// if it isn't needed.
void Asura_ServerEntity_PhysicalObject::ResetAllCollisionObjects()
{
	Asura_Hashed_List_It<Asura_ServerEntity_PhysicalObject>	xIterator( GetList() );
	
	for ( ; !xIterator.Done(); xIterator.Next() )
	{
		Asura_ServerEntity_PhysicalObject* pxObject = xIterator.GetCurrent();

		if ( pxObject )
		{
			pxObject->ResetCollisionObject();
		}
	}
}

bool Asura_ServerEntity_PhysicalObject::LOSTest( const Asura_LOS_InputData* pxInputData, Asura_LOS_OutputData* pxOutputData ) const
{
    if( pxInputData->m_uEntityToIgnore == Guid ) { return false; }
	if(IsMergedWithEnvironment()) return false;
	if(IsIgnoreForLOS()) return false;

	if (!m_pxDynamicObject) return false;
    const Asura_Collision_Object* pxCO = m_pxDynamicObject->GetCollisionObject();
    if( !pxCO ) { return false; }

    if( !pxCO->LOSTest(GetPosition(), GetOrientation(), pxInputData, pxOutputData) ) { return false; }

    pxOutputData->m_uHitEntity = Guid;
    return true;
}

#ifdef ASURA_SUPPORT_NETMSG_SYSTEM
#include "Network/Asura_NetMsg.h"

DEBUGVAR float dbg_fPhysicalObjectPositionChangeThreshold = 0.01f;
DEBUGVAR float dbg_fPhysicalObjectOrientationChangeThreshold = 0.01f;

u_int Asura_Message_PhysicalObject_ClientUpdate::s_uHealthBits = 8;
float Asura_Message_PhysicalObject_ClientUpdate::s_fHealthMin = 0.f;
float Asura_Message_PhysicalObject_ClientUpdate::s_fHealthMax = 255.f;
float Asura_Message_PhysicalObject_ClientUpdate::s_fHealthChangeThreshold = 1.0f;

void Asura_Message_PhysicalObject_ClientUpdate::RegisterFields(u_short usMsgID, u_short usEntityClassification)
{
	Asura_Assert(usMsgID == ASURA_MSG_CLIENTUPDATE, "invalid msg ID");
	Asura_Assert(!Asura_NetMsg::FindNetMsgObject(usMsgID, usEntityClassification), "already exists");
	
	u_char ucNumFields = GetNumFields();

	Asura_NetMsg* pxNetMsg = new Asura_NetMsg(usMsgID, usEntityClassification, ucNumFields, sizeof(Asura_Message_PhysicalObject_ClientUpdate));
	RegisterFields(pxNetMsg);
	pxNetMsg->EndRegistration();
}

void Asura_Message_PhysicalObject_ClientUpdate::SetHealthFieldAccuracy(u_int uBits, float fMin, float fMax, float fChangeThreshold )
{
	s_uHealthBits = uBits;
	s_fHealthMin  = fMin;
	s_fHealthMax  = fMax;
	s_fHealthChangeThreshold = fChangeThreshold;
}

void Asura_Message_PhysicalObject_ClientUpdate::RegisterFields(class Asura_NetMsg* pxNetMsg)
{
	const Asura_Message_PhysicalObject_ClientUpdate xDummy;
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_CompressedQuat,							m_xOrientation,		1,	Asura_Threshold_Magnitude,	dbg_fPhysicalObjectOrientationChangeThreshold);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_CompressedPosition,						m_xPosition,		1,	Asura_Threshold_Magnitude,	dbg_fPhysicalObjectPositionChangeThreshold);
	
	// SJL 17/8/2007: Now allowing the project to specify the ranges via SetHealthFieldAccuracy
	Asura_NetMsg_RegisterFloatFieldBits(pxNetMsg, xDummy, Asura_FieldType_CompressedFloat, s_uHealthBits, s_fHealthMin, s_fHealthMax,	m_fHealth,			1,	Asura_Threshold_Magnitude,	s_fHealthChangeThreshold);	

	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_uint,		m_uShapeFileID,		1,  Asura_Threshold_AnyChange,	0.0f);
    Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_uint,		m_uAnimID,			1,  Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_int,		m_iAnimFlags,		1,  Asura_Threshold_AnyChange,	0.0f);
	// (MH - 30.03.2004) hmm, this anim timer wasn't in here before, so it was only being taken notice of in single-player.
	// Therefore to maintain the old behaviour, in adding it to the netmsg I'm making it a constant value.
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_float,		m_fAnimTimer,		1,  Asura_Threshold_ConstantValue,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_float,		m_fAnimEndTime,		1,  Asura_Threshold_AnyChange,		0.0f);

	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_uint,		m_uPhysicalObjectFlags,		1,  Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_Guid,		m_uParentGuid,				1,  Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_uint,		m_uParentMarkerHashID,		1,  Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_short,		m_sTextureAnimSpeed,		1,  Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_uchar,		m_ucTextureAnimSequence,	1,  Asura_Threshold_AnyChange,	0.0f);

}
#endif//#ifdef ASURA_SUPPORT_NETMSG_SYSTEM




void Asura_ServerEntity_PhysicalObject::HideForCutscene(bool bHide)
{
	//tell client entities
	Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_HIDEFORCUTSCENE,sizeof(unsigned char));
	unsigned char* pucHide = (unsigned char*) pxMessage->GetData();
	*pucHide = bHide ? 1 : 0;
	pxMessage->To =	ASURA_GUID_MY_CLIENT_ENTITIES;
	pxMessage->From = Guid;
	Asura_Network::Send(pxMessage);

	//and deal with the hiding/unhiding
	HandleHideForCutscene(bHide);
}


#ifdef ASURA_TOOLS
void Asura_ServerEntity_PhysicalObject::ApplyChangesForTemplateInstance(Tools_EntityTemplateInstance* pxInstance)
{
	Asura_Entity_Server::ApplyChangesForTemplateInstance(pxInstance);

	//update position and orientation
	InitialisePosition( pxInstance->TransformPosition(GetPosition()) );
	SetOrientation(pxInstance->TransformMatrix(GetOrientation()));

	if(!pxInstance->IsTemporaryMove())
	{
		//reset merged to environment flag
		m_uPhysicalObjectFlags &=~ ASURA_PHYSICALOBJECT_FLAG_ISMERGEDWITHENV;
	
    	// Any blocked waypoints read from template file will be inappropriate, so delete them.
        DiscardWaypointLinkBlockInformation();
	}

	#ifdef ASURA_USE_AXON
	if ( GetBrain() )
	{
		GetBrain()->ApplyChangesForTemplateInstance( pxInstance );
	}
	#endif
}
#endif // ASURA_TOOLS

#ifdef ASURA_TOOLS
u_int Asura_ServerEntity_PhysicalObject::GetPositionAndOrientChecksum()
{
	u_int uChecksum = 0;
	
	Asura_Vector_3 xPos = GetPosition();

 	//If approximately the same as the position from loading , then assume that any
 	//changes were unintentional , and so go with the original value. RAR 23/01/2006
	if(m_xSavedPosition.ApproximatelyEquals(xPos))
	{
		xPos = m_xSavedPosition;
	} 
	else
	{
		//Update saved position , since this is the value now being used for the checksuming
		//RAR 23/01/2006
		m_xSavedPosition = xPos;
	}
	
	uChecksum += *(u_int*) &xPos.x;
	uChecksum += *(u_int*) &xPos.y;
	uChecksum += *(u_int*) &xPos.z;

	//determine the quat that would be saved
	Asura_Quat xQuat;
	xQuat.FromMatrix(GetOrientation());
	xQuat.Normalise();

 	//If approximately the same as the quaternion from loading , then assume that any
 	//changes were unintentional , and so go with the original value. RAR 23/01/2006
	if(xQuat.ApproximatelyEquals(m_xSavedQuat))
	{

		xQuat = m_xSavedQuat;
	}
	else
	{
		//Update saved quat , since this is the value now being used for the checksuming
		//RAR 23/01/2006
		m_xSavedQuat = xQuat;
	}

	uChecksum += *(u_int*) &xQuat.x;
	uChecksum += *(u_int*) &xQuat.y;
	uChecksum += *(u_int*) &xQuat.z;
	uChecksum += *(u_int*) &xQuat.w;

	return uChecksum;	
}
#endif // ASURA_TOOLS

#ifdef ASURA_TOOLS
bool Asura_ServerEntity_PhysicalObject::ShouldIncludeInLevelExport() const
{
	return Asura_Entity_Server::ShouldIncludeInLevelExport();
}
#endif

#ifdef ASURA_TOOLS
void Asura_ServerEntity_PhysicalObject::TraverseUsedShapes(void (*pfnCallback)(Asura_Hash_ID uShapeHash,void* pUserData),void* pUserData) const
{
	//Report which shape is used.
	//Note that if this is a skin , then a zero hash will be passed , but that is okay.
	pfnCallback(GetShapeFileID(),pUserData);
}
#endif

#ifdef ASURA_USE_STREAMING
Asura_Hash_ID Asura_ServerEntity_PhysicalObject::CheckForZoneChange()
{
	Asura_Hash_ID uNewHash = UpdateContainingZone(GetPosition());
	return uNewHash;
}
#endif //#ifdef ASURA_USE_STREAMING


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ServerEntity_PhysicalObject::LinkToParent
// Author       : Andy Tate
// Date         : 1-Nov-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_ServerEntity_PhysicalObject::LinkToParent(const Asura_Guid uParentGuid,
													  const Asura_Hash_ID uMarkerHash,
													  const bool bWithPhysics /*= false*/)
{
	// Do we already have a parent?
	if(m_uParentGuid == ASURA_GUID_UNREGISTERED)
	{
		Asura_Message*	pxMessage	= Asura_Network::CreateMessage(static_cast<u_short>(bWithPhysics?ASURA_MSG_LINK_TO_PARENT_WITH_PHYSICS:ASURA_MSG_LINK_TO_PARENT), sizeof(Asura_Message_PhysicalObject_LinkToParent));

		if(pxMessage == NULL)
		{
			return(false);
		}
		else
		{
			Asura_Message_PhysicalObject_LinkToParent*	pxData	= static_cast<Asura_Message_PhysicalObject_LinkToParent*>(pxMessage->GetData());

			if(pxData == NULL)
			{
				return(false);
			}
			else
			{
				// Store the connection...
				m_uParentGuid = uParentGuid;
				m_uParentMarkerHashID = uMarkerHash;
				SetAttachedWithPhysicsFlag(bWithPhysics);
				// AJG - 17/08/07 - We don't need to have a physics block for this to work, so don't assume one exists.
				//	 ... 14/01/08 - However, it seems a bit odd to not have one if we're trying to keep it active.
				Asura_Assert(m_pxDynamicObject || !bWithPhysics, "Trying to attach with physics, but we don't have a physics object.");
				if (m_pxDynamicObject)
				{
					if (bWithPhysics)
					{
						// (NY - 07.03.2007) physics object required (movement of such left to projects)
						// store old physics object type, then go external control and flag to ignore our parent (so it doesn't collide with us)
						m_ePhysicsTypeBeforeAttach = m_pxDynamicObject->GetType();
						m_pxDynamicObject->ChangeType(ASURA_PHYSICS_OBJECT_EXTERNALCONTROL);
						const Asura_ServerEntity_PhysicalObject* pxParent = Asura_ServerEntity_PhysicalObject::FindEntity(uParentGuid);
						if (pxParent)
						{
							m_pxDynamicObject->SetHandleToIgnore(pxParent->GetPhysicsObjectHandle());
						}
					}
					else
					{
						m_pxDynamicObject->SetIgnore(true);
					}
				}

				// Create and send the message...
				pxData->SetParentGuid(uParentGuid);
				pxData->SetMarkerHash(uMarkerHash);

				pxMessage->UseGuaranteedDelivery();
				Send(pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES);
				return(true);
			}
		}
	}
	else
	{
		Asura_Assert(false, "Already have a parent - cannot link to another one.");
		return(false);
	}
}

/// End of function 'Asura_ServerEntity_PhysicalObject::LinkToParent'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ServerEntity_PhysicalObject::LinkToParent
// Author       : Andy Tate
// Date         : 6-Nov-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_ServerEntity_PhysicalObject::LinkToParent(const Asura_Guid uParentGuid,
													  const Asura_Vector_3& xBoneRelativePos,
													  const Asura_Quat& xBoneRelativeOri,
													  const u_int uBoneID)
{
	// It's possible that the message can be sent and the client would be created afterwards. 
	// In a networked game this is quite likely. So the data we're sending here ought to be in 
	// the client update message, or possibly something encoded in there which triggers a 
	// request for the full data to do the linking (to avoid bloating the update message).
	Asura_Assert(!Asura_Network::IsExternalNetworkActive(), "this version of LinkToParent may break in networked games");

	// Do we already have a parent?
	if(m_uParentGuid == ASURA_GUID_UNREGISTERED)
	{
		Asura_Message*	pxMessage	= Asura_Network::CreateMessage(ASURA_MSG_LINK_TO_PARENT_BONE_RELATIVE, sizeof(Asura_Message_PhysicalObject_LinkToParentBoneRelative));

		if(pxMessage == NULL)
		{
			return(false);
		}
		else
		{
			Asura_Message_PhysicalObject_LinkToParentBoneRelative*	pxData	= static_cast<Asura_Message_PhysicalObject_LinkToParentBoneRelative*>(pxMessage->GetData());

			if(pxData == NULL)
			{
				return(false);
			}
			else
			{
				// Store the connection...
				m_uParentGuid	= uParentGuid;
				if ( m_pxDynamicObject )
				{
					m_pxDynamicObject->SetIgnore(true);
				}

				// Create and send the message...
				pxData->SetParentGuid(uParentGuid);
				pxData->SetBoneID(uBoneID);
				pxData->SetPosition(xBoneRelativePos);
				pxData->SetOrientation(xBoneRelativeOri);

				pxMessage->UseGuaranteedDelivery();
				Send(pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES);
				return(true);
			}
		}
	}
	else
	{
		Asura_Assert(false, "Already have a parent - cannot link to another one.");
		return(false);
	}
}

/// End of function 'Asura_ServerEntity_PhysicalObject::LinkToParent'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ServerEntity_PhysicalObject::LinkToParent
// Author       : Andy Tate
// Date         : 6-Nov-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_ServerEntity_PhysicalObject::LinkToParent(const Asura_Guid uParentGuid,
													  const Asura_Hash_ID uMarkerHash,
													  const u_int uBoneID,
													  const Asura_Vector_3& xPosition)
{
	// It's possible that the message can be sent and the client would be created afterwards. 
	// In a networked game this is quite likely. So the data we're sending here ought to be in 
	// the client update message, or possibly something encoded in there which triggers a 
	// request for the full data to do the linking (to avoid bloating the update message).
	Asura_Assert(!Asura_Network::IsExternalNetworkActive(), "this version of LinkToParent may break in networked games");

	// Do we already have a parent?
	if(m_uParentGuid == ASURA_GUID_UNREGISTERED)
	{
		Asura_Message*	pxMessage	= Asura_Network::CreateMessage(ASURA_MSG_LINK_TO_PARENT_STICKY, sizeof(Asura_Message_PhysicalObject_LinkToParentSticky));

		if(pxMessage == NULL)
		{
			return(false);
		}
		else
		{
			Asura_Message_PhysicalObject_LinkToParentSticky*	pxData	= static_cast<Asura_Message_PhysicalObject_LinkToParentSticky*>(pxMessage->GetData());

			if(pxData == NULL)
			{
				return(false);
			}
			else
			{
				// Store the connection...
				m_uParentGuid	= uParentGuid;
				if ( m_pxDynamicObject )
				{
					m_pxDynamicObject->SetIgnore(true);
				}

				// Create and send the message...
				pxData->SetParentGuid(uParentGuid);
				pxData->SetMarkerHash(uMarkerHash);
				pxData->SetBoneID(uBoneID);
				pxData->SetPosition(xPosition);

				pxMessage->UseGuaranteedDelivery();
				Send(pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES);
				return(true);
			}
		}
	}
	else
	{
		Asura_Assert(false, "Already have a parent - cannot link to another one.");
		return(false);
	}
}

/// End of function 'Asura_ServerEntity_PhysicalObject::LinkToParent'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ServerEntity_PhysicalObject::UnlinkFromParent
// Author       : Andy Tate
// Date         : 1-Nov-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_ServerEntity_PhysicalObject::UnlinkFromParent(void)
{
	// Do we already have a parent?
	if(m_uParentGuid == ASURA_GUID_UNREGISTERED)
	{
		Asura_Assert(false, "We don't have a parent - nothing to unlink from.");
		return(false);
	}
	else
	{
		Asura_Message*	pxMessage	= Asura_Network::CreateMessage(ASURA_MSG_UNLINK_FROM_PARENT);

		if(pxMessage == NULL)
		{
			return(false);
		}
		else
		{
			const Asura_ClientEntity_PhysicalObject*	pxClient	= Asura_ClientEntity_PhysicalObject::FindEntity(GetGuid());

			// Re-position this entity...
			if(pxClient)
			{
				const Asura_Shape_Instance&	xShapeInstance	= pxClient->GetShapeInstance();
				const Asura_Animation*		pxAnimation		= xShapeInstance.GetAnimation();
				if( pxAnimation )
				{
					const Asura_Vector_3&		xPosition = pxAnimation->GetPosition();
					Asura_Matrix_3x3			xOrientation;

					pxAnimation->GetOrientQuat().ToMatrix(xOrientation);
					SetPosition(xPosition);
					SetOrientation(xOrientation);
				}
			}

			// Remove the connection...
			m_uParentGuid = ASURA_GUID_UNREGISTERED;
			m_uParentMarkerHashID = ASURA_HASH_ID_UNSET;
			if (m_pxDynamicObject)
			{
				if (HasAttachedWithPhysicsFlag())
				{
					//we've been using physics, so reset the physics object to it's state before attachment
					m_pxDynamicObject->ChangeType(m_ePhysicsTypeBeforeAttach);
					m_pxDynamicObject->SetHandleToIgnore(ASURA_INVALID_HANDLE);
				}
				else
				{
					m_pxDynamicObject->SetIgnore(false);
				}
			}
			SetAttachedWithPhysicsFlag(false);

			// Send the message...
			pxMessage->UseGuaranteedDelivery();
			Send(pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES);
			return(true);
		}
	}
}

/// End of function 'Asura_ServerEntity_PhysicalObject::UnlinkFromParent'. ///


void Asura_ServerEntity_PhysicalObject::InitialisePosition( const Asura_Vector_3& xInitialPosition )
{
    // Teleport the physics object first.
	if( m_pxDynamicObject )
	{
		m_pxDynamicObject->TeleportTo( xInitialPosition );
	}

    // Now call through to our normal SetPosition() method to ensure that
    // subclasses know about this. This is to maintain previous behaviour.
    // Since we've already teleported the object, this will generally have
    // little effect on our internal physics object (as opposed to the
    // problems before where we were doing a SetPosition() from, eg., the
    // origin to the correct position and ending up with massive collision
    // BBs for external control objects).
    SetPosition( xInitialPosition );
}


void Asura_ServerEntity_PhysicalObject::TeleportTo(const Asura_Vector_3& xPosition,
												   const Asura_Matrix_3x3& xOrientation,
												   const bool bInformClients)
{
	// Move ourselves...
	if(m_pxDynamicObject != NULL)
	{
		m_pxDynamicObject->TeleportTo( xPosition );
		m_pxDynamicObject->SetOrientation(xOrientation);
	}

	if(bInformClients)
	{
		Asura_Message*	pxMessage	= Asura_Network::CreateMessage(ASURA_MSGID_TELEPORT, sizeof(Asura_Message_Teleport));

		if(pxMessage == NULL)
		{
			Asura_Assert(false, "Failed to create teleport message.");
		}
		else
		{
			Asura_Message_Teleport*	pxData	= static_cast<Asura_Message_Teleport*>(pxMessage->GetData());

			if(pxData == NULL)
			{
				Asura_Assert(false, "No data in teleport message.");
			}
			else
			{
				pxData->SetPosition(xPosition);
				pxData->SetOrientation(xOrientation);

				pxMessage->SetFromGuid(GetGuid());
				pxMessage->SetToGuid( ASURA_GUID_MY_CLIENT_ENTITIES );

				pxMessage->UseGuaranteedDelivery();
				Asura_Network::Send( pxMessage );
			}
		}
	}
}

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

#ifdef ASURA_TOOLS
void Asura_ServerEntity_PhysicalObject::ForceClientUpdate()
{
	ForceUpdateMessage();
	MakeClientUpdateMessage();
}
#endif 

#ifdef ASURA_NETMSG_PACK_ALL_DATA
void Asura_Message_PhysicalObject_LinkToParent::RegisterFields(Asura_NetMsg* pxNetMsg) const
{
	Asura_Assert(pxNetMsg->GetMsgID() == ASURA_MSG_LINK_TO_PARENT, "unexpected message ID");

	Asura_NetMsg_RegisterFieldSimple( Asura_FieldType_Guid, m_uParentGuid );
	Asura_NetMsg_RegisterFieldSimple( Asura_FieldType_uint, m_uMarkerHash );
}
#endif 

/// End of file. ///
