///////////////////////////////////////////////////////////////////////////////
//
// File: Asura_ServerEntity_Container.cpp
//
// Created by Martin G.Harris (6/5/2008)
// 
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
// Server entity that manages a container instance.
//
///////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"
#include "Entity/Container/Asura_ServerEntity_Container.h"

#ifdef ASURA_USE_CONTAINER_SYSTEM

// Includes

// Asura
#include "AI/Asura_AI_Navigation.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_Timers.h"
#include "Dynamics/Asura_Collision_Object.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/Asura_Entity_Event.h"
#include "Entity/Asura_StaticMessage_System.h"
#include "Entity/Container/Asura_ClientEntity_Container.h"
#include "Entity/Container/Asura_ContainerRegistry.h"
#include "Entity/Container/Asura_Container_Messages.h"
#include "Entity/Container/Asura_Container_System.h"
#include "Entity/Container/Asura_ServerContainerInstance.h"
#include "Network/Asura_Message.h"
#include "Physics/Asura_Physics_Object.h"

// Tools
#ifdef ASURA_TOOLS
#include "EntityTemplate/Tools_EntityTemplate_System.h"
#include "Entity/Container/Tools_Entity_Container.h"
#include "Load&Save/Tools_SaveAsura.h"
#endif

// Register the server entity.
Asura_ContainerRegisterServerEntity<Asura_ServerEntity_Container> xRegisterServerEntity(false);

// Constants
const int iASURA_CONTAINER_CHUNK_VERSION = 6;

// Static data
Asura_Hashed_List<Asura_ServerEntity_Container> Asura_ServerEntity_Container::s_xList(6);


unsigned short                Asura_ServerEntity_Container::s_usNumWaypoints		 = 0;
unsigned short*               Asura_ServerEntity_Container::s_pusWaypoints			 = NULL;
unsigned char                 Asura_ServerEntity_Container::s_ucNumLinksToBlock		 = 0;
Asura_AI_NavConnection_Index* Asura_ServerEntity_Container::s_pxLinksToBlock		 = NULL;
unsigned int				  Asura_ServerEntity_Container::s_uNumWaypointsToInclude = uDEFAULT_MAX_WAYPOINTS_TO_INCLUDE;

///////////////////////////////////////////////////////////////////////////////
// Asura_ServerEntity_Container
///////////////////////////////////////////////////////////////////////////////
Asura_ServerEntity_Container::Asura_ServerEntity_Container(Asura_Guid uGuid)
: Asura_Entity_Server(uGuid)
, m_uContainerHash(ASURA_HASH_ID_UNSET)
, m_xPosition(Asura_Maths::ZeroVector3)
, m_xOriQuat(Asura_Maths::IdentityQuat)
, m_pxPendingVel(0)
, m_ucNumLinksToBlock(0)
, m_pxLinksToBlock(0)
, m_uNumDynamicPhysics(0)
, m_uNumPhysicsParam(0)
, m_uUpdatedDynamicPhysicsFrameStamp(0)
, m_pxServerInstance(0)
, m_uNumFloatParam(0)
, m_fGameTimeToDie(-1.0f)
{
	Classification = AsuraEntityClass_Container;
	s_xList.AddToList(this);

	// Set all flags to be false by default.
	m_uContainerFlags = 0;

	m_xInputState.Reset();

	// Most containers just sit around doing nothing, so there is no need to send
	// regular updates. Only send updates when the state changes (or for dynamic ones).
	SetAlwaysUpdateClient(false);
	m_bUpdateAlways = false;
}

Asura_ServerEntity_Container::~Asura_ServerEntity_Container()
{
	s_xList.RemoveFromList(this);

	DestroyInstance();

	if (m_pxLinksToBlock)
	{
		// Remove any waypoint link blocks.
		UpdateWaypointLinkBlocks(true);

		delete [] m_pxLinksToBlock;
		m_pxLinksToBlock = 0;
	}
}

/////////////////
// SetLifetime //
/////////////////
// Set a lifetime. Entity will be destroyed after the game time has advanced
// by the specified number of seconds. This is mainly useful for game-created
// containers (e.g. if we are using containers for, say, flares).
void Asura_ServerEntity_Container::SetLifetime(float fSeconds)
{
	if (fSeconds >= 0.0f)
	{
		// Set the time of death.
		m_fGameTimeToDie = Asura_Timers::GetLocalScaledTime() + fSeconds;
	}
	else
	{
		// Cancel the countdown to death.
		m_fGameTimeToDie = -1.0f;
	}
}

////////////
// Create //
////////////
void Asura_ServerEntity_Container::Create( Asura_Guid uGuid, Asura_Chunk_Stream& xStream )
{
	Asura_ServerEntity_Container* pxEntity = Asura_ContainerRegistry::CreateServerEntity(uGuid);
	if (pxEntity)
	{
		pxEntity->ReadFromChunkStream(xStream);
	}
}

// Create during the game.
// TODO extend to allow multiple input states
Asura_ServerEntity_Container* Asura_ServerEntity_Container::Create( Asura_Hash_ID uContainerHash,
																	Asura_Hash_ID uStateHash,
																	const Asura_Vector_3& xPosition,
																	const Asura_Quat& xOrientationQuat )
{
	Asura_ServerEntity_Container* pxEntity = Asura_ContainerRegistry::CreateServerEntity(Asura_Entity_System::GetCreatedByGameGuid());
	if (pxEntity)
	{
		pxEntity->m_uContainerHash	= uContainerHash;
		pxEntity->m_xInputState.SetNumInputs(1);
		pxEntity->m_xInputState.SetStateHash(0, uStateHash);
		pxEntity->m_xPosition		= xPosition;
		pxEntity->m_xOriQuat		= xOrientationQuat;

		pxEntity->CreateInstance();
	}

	return pxEntity;
}

// Create during the game, with an initial velocity.
// The position and orientation are used for the origin. The velocities are applied to any dynamic physics objects.
// TODO extend to allow multiple input states
Asura_ServerEntity_Container* Asura_ServerEntity_Container::Create( Asura_Hash_ID uContainerHash,
																	Asura_Hash_ID uStateHash,
																	const Asura_Container_PosOrientVel& xInitial )
{
	Asura_ServerEntity_Container* pxEntity = Asura_ContainerRegistry::CreateServerEntity(Asura_Entity_System::GetCreatedByGameGuid());
	if (pxEntity)
	{
		pxEntity->m_uContainerHash	= uContainerHash;
		pxEntity->m_xInputState.SetNumInputs(1);
		pxEntity->m_xInputState.SetStateHash(0, uStateHash);
		pxEntity->m_xPosition		= xInitial.m_xPosition;
		pxEntity->m_xOriQuat		= xInitial.m_xOrient;

		pxEntity->m_pxPendingVel = new Asura_Container_PosOrientVel;
		*pxEntity->m_pxPendingVel = xInitial;

		pxEntity->CreateInstance();
	}

	return pxEntity;
}

/////////////////////////
// ReadFromChunkStream //
/////////////////////////
// Reads this entity from the specified chunk-stream.
void Asura_ServerEntity_Container::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	// Check our version
	int iVersion;
	xStream >> iVersion;
	if (iVersion<0 || iVersion >iASURA_CONTAINER_CHUNK_VERSION)
	{
		Asura_Assertf(0, ("Unknown chunk version %d for container entity", iVersion));
		return;
	}

	PARENT::ReadFromChunkStream(xStream);

	xStream >> m_uContainerHash;
	xStream >> m_xPosition;
	xStream >> m_xOriQuat;

	if (iVersion >= 6)
	{
		// New version has multiple state hash inputs.
		u_int uNumInputs = 1;
		xStream >> uNumInputs;
		m_xInputState.SetNumInputs(uNumInputs);
		Asura_Assertf(uNumInputs == m_xInputState.GetNumInputs(), ("Invalid number of state inputs (%u)", uNumInputs));
		
		for (u_int u=0; u < uNumInputs; ++u)
		{
			u_int uStateHash = ASURA_HASH_ID_UNSET;
			xStream >> uStateHash;
			if (u < m_xInputState.GetNumInputs())
			{
				m_xInputState.SetStateHash(u, uStateHash);
			}
		}
	}
	else if (iVersion >= 1)
	{
		// Old version has only a single state hash.
		m_xInputState.SetNumInputs(1);
		u_int uStateHash = ASURA_HASH_ID_UNSET;
		xStream >> uStateHash;
		m_xInputState.SetStateHash(0, uStateHash);
	}

	// Load any stored position/orientation data.
	// Note: this data has now been moved to the server instance.
	if (iVersion >= 2 && iVersion < 4)
	{
		int iNumDynamicPhysics;
		xStream >> iNumDynamicPhysics;

		if (iNumDynamicPhysics > 0)
		{
			for (int i=0; i<iNumDynamicPhysics; ++i)
			{
				Asura_Vector_3 xDummyVec;
				Asura_Quat xDummyQuat;
				xStream >> xDummyVec;
				xStream >> xDummyQuat;
			}
		}
	}

	if (iVersion >= 3)
	{
		// Create server-side instance data (don't bother seting up the states yet).
		CreateInstance(false);

		// Read static instance data (including health values).
		Asura_ServerContainerInstance::ReadAllData(xStream, m_pxServerInstance);
		m_uNumPhysicsParam = 0;
		
		// Make sure any damage overrides are up-to-date (must be done after loading health values).
		if (m_pxServerInstance)
		{
			m_pxServerInstance->GetNumPhysicsParam(m_uNumPhysicsParam);
			m_pxServerInstance->ChangeState(m_xInputState);
		}
	}

	m_uUpdatedDynamicPhysicsFrameStamp = Asura_Timers::GetFrameNumber();

	// From version 5 onwards, we also store waypoint link blocking information.
	if (iVersion >= 5)
	{
		DiscardWaypointLinkBlockInformation();
		
		u_int uNumLinksToBlock = 0;
		xStream >> uNumLinksToBlock;
		m_ucNumLinksToBlock = static_cast<u_char>(uNumLinksToBlock);
	
		if ( m_ucNumLinksToBlock > 0 )
		{
			m_pxLinksToBlock = new Asura_AI_NavConnection_Index[ m_ucNumLinksToBlock ];

			for ( u_char ucIndex = 0; ucIndex < m_ucNumLinksToBlock; ++ucIndex )
			{
				xStream >> m_pxLinksToBlock[ ucIndex ].m_usWaypointIndex;
				xStream >> m_pxLinksToBlock[ ucIndex ].m_usConnectionIndex;
			}
		}
	}


#ifdef ASURA_TOOLS
    // 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)
		{
			DiscardWaypointLinkBlockInformation();
		}
	}
#endif //ASURA_TOOLS
}

////////////////////////
// WriteToChunkStream //
////////////////////////
// Writes this entity to the specified chunk-stream.
void Asura_ServerEntity_Container::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	xStream << iASURA_CONTAINER_CHUNK_VERSION;
	
	PARENT::WriteToChunkStream(xStream);

	xStream << m_uContainerHash;
	xStream << m_xPosition;
	xStream << m_xOriQuat;
	
	// Write out the multiple input state hashes.
	const u_int uNumInputs = m_xInputState.GetNumInputs();
	xStream << uNumInputs;

	for (u_int u=0; u < uNumInputs; ++u)
	{
		const u_int uStateHash = m_xInputState.GetStateHash(u);
		xStream << uStateHash;	
	}

	// Write any static instance data.
	Asura_ServerContainerInstance::WriteAllData(xStream, m_pxServerInstance);

	// Write any waypoint link blocking information.
	u_int uNumLinksToBlock = m_ucNumLinksToBlock;
	
#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 ( unsigned char uc = 0; uc < m_ucNumLinksToBlock; ++uc )
		{
			Asura_AI_NavConnection_Index xConnection;
			if( !Asura_AI_Navigation_System::ConvertNavConnectionIndexForExport(m_pxLinksToBlock[uc], xConnection) )
			{
				uNumLinksToBlock--;
			}
		}
	}
#endif //ASURA_TOOLS

	xStream << uNumLinksToBlock;

#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 ( unsigned char uc = 0; uc < m_ucNumLinksToBlock; ++uc )
		{
			Asura_AI_NavConnection_Index xConnection;
			if( Asura_AI_Navigation_System::ConvertNavConnectionIndexForExport(m_pxLinksToBlock[uc], xConnection) )
			{
				xStream << xConnection.m_usWaypointIndex;
				xStream << xConnection.m_usConnectionIndex;
			}
		}
	}
	else
#endif //ASURA_TOOLS
	{
		for ( unsigned char uc = 0; uc < m_ucNumLinksToBlock; ++uc )
		{
			xStream << m_pxLinksToBlock[ uc ].m_usWaypointIndex;
			xStream << m_pxLinksToBlock[ uc ].m_usConnectionIndex;
		}
	}

}

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

	switch (pxMessage->GetMsgID())
	{
	case ASURA_MSG_CLIENT_REQUESTCREATEMSG:
		{
			MakeCreateMessage(pxMessage->GetFromGuid());
			return true;
		}

	case ASURA_MSG_CONTAINER_CHANGE_STATE:
		{
			const Asura_Message_Container_ChangeState* pxData = static_cast<const Asura_Message_Container_ChangeState*>(pxMessage->GetData());
			Asura_Assert(pxData, "Null message data pointer");
			if (pxData)
			{
				// This message always changes input slot 0.
				Asura_Container_InputState xNewInput = m_xInputState;
				xNewInput.SetStateHash(0, pxData->m_uStateHash);
				ChangeState(xNewInput);
			}
			return true;
		}

	case ASURA_MSG_CONTAINER_CHANGE_STATE_EXTENDED:
		{
			const Asura_Message_Container_ChangeStateExtended* pxData = static_cast<const Asura_Message_Container_ChangeStateExtended*>(pxMessage->GetData());
			Asura_Assert(pxData, "Null message data pointer");
			if (pxData)
			{
				Asura_Container_InputState xNewInput = m_xInputState;
				xNewInput.SetStateHash(pxData->m_uInputIndex, pxData->m_uStateHash);
				ChangeState(xNewInput);
			}
			return true;
		}

	case ASURA_MSG_DESTRUCT:
		{
			// Send any destruction messages.
			SendOnDestructionMessages();
			SetThisAndClientsAsDestroyed();
			return true;
		}

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

/////////////////
// ChangeState //
/////////////////
void Asura_ServerEntity_Container::ChangeState(const Asura_Container_InputState& xNewInputState)
{
	if (m_xInputState != xNewInputState)
	{
		PreStateChange();

		if (m_uNumFloatParam > 0)
		{
			// Has health containers, so we need to recalculate the damageable status.
			m_bIsDamageableInitialised = false;
		}

		m_xInputState = xNewInputState;

		// Make sure that all the other clients are informed.
		RequestClientUpdate();

		if (m_pxServerInstance)
		{
			m_pxServerInstance->ChangeState(m_xInputState);
		}

		PostStateChange();
	}
}

/////////////////
// ApplyDamage //
/////////////////

// Apply damage.
// xOutput	  - contains useful data about the result of the function call.
// uUniqueID  - should refer to the part of the colllection that is being damaged and
//				typically refers to a mesh that has been returned as a result of a call to LOSTest.
// fDamage	  - damage to apply, can be negative to add health.
// bSendMessageToClient - inform clients of the change.

void Asura_ServerEntity_Container::ApplyDamage(Asura_Container_ApplyDamageOutput& xOutput,
											   u_int uUniqueID, float fDamage,
											   bool bSendMessageToClient /*= true*/)
{
	xOutput.Reset();

	if (fDamage == 0.0f) return; // No change needed.

	if (!m_pxServerInstance)
	{
		Asura_Assertf(0, ("No server instance for container entity %u. Can't apply damage.", GetGuid()));
		return;
	}

	// Find the relevant parent collection of the part being damaged.
	Asura_ServerContainerInstance_Collection* const pxCollection = m_pxServerInstance->FindCollectionFromUniqueID(uUniqueID);
	if (!pxCollection)
	{
		Asura_Assertf(0, ("Couldn't find relevant collection (id = 0x%02x) for entity %u. Can't apply damage.", uUniqueID, GetGuid()));
		return;
	}

	// Find the health.
	Asura_ServerContainerInstance_Health* const pxHealth = pxCollection->FindHealth();
	if (!pxHealth)
	{
		// This part of the container entity has no health container and hence isn't damageable.
		return;
	}

	// Check that the relevant mesh isn't flagged as being undamageable.
	// Note: for now we'll just peek the client; we could create mesh server container instances,
	// and then use those instead.
	const Asura_ClientEntity_Container* const pxClient = Asura_ClientEntity_Container::FindEntity(GetGuid());
	if (pxClient)
	{
		const Asura_ClientContainerInstance* const pxHitContainer = pxClient->FindFromUniqueID(uUniqueID);
		if (pxHitContainer && pxHitContainer->GetClassification() == AsuraContainerClass_Mesh)
		{
			const Asura_ClientContainerInstance_Mesh* const pxHitMesh = static_cast<const Asura_ClientContainerInstance_Mesh*>(pxHitContainer);
			if (pxHitMesh->IsIgnoreForDamage())
			{
				// Mesh is flagged as not being damageable.
				return;
			}
		}
	}

	xOutput.m_bFound = true;

	xOutput.m_fOldHealth		= pxHealth->GetHealth();
	xOutput.m_fOldHealthPercent = pxHealth->GetHealthPercent();

	// Update server instance of health container.
	const bool bChangedHealth = pxHealth->ApplyDamage(fDamage);

	xOutput.m_fNewHealth		= pxHealth->GetHealth();
	xOutput.m_fNewHealthPercent = pxHealth->GetHealthPercent();

	if (!bChangedHealth)
	{
		// Health is presumably already at a minimum (or maximum) value.
		return;
	}

	xOutput.m_bChangedHealth = true;

	// Since the health has changed, we need to check the damage overrides for the relevant collection. 
	if (pxCollection->HasDamageOverrideChanged(xOutput.m_fOldHealthPercent, xOutput.m_fNewHealthPercent))
	{
		// Refresh the child states, using the new damage override.
		PreStateChange();
		pxCollection->ChangeState(pxCollection->GetInputState());
		xOutput.m_bDamageOverrideChanged = true;

		// Damage override has changed. We need to recalculate the damageable status.
		m_bIsDamageableInitialised = false;
	}
	else if (xOutput.m_fOldHealth > 0.0f && xOutput.m_fNewHealth == 0.0f)
	{
		// Health has become zero. We need to recalculate the damageable status.
		m_bIsDamageableInitialised = false;
	}

	// By default we send a message informing all of the clients that the health value has changed.
	if (bSendMessageToClient)
	{
		const float fNewHealth = pxHealth->GetHealth();
		
		// Send message to client entities with new current health.
		Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_CONTAINER_CHANGE_HEALTH, sizeof(Asura_Message_Container_ChangeHealth) );
		if (pxMessage)
		{
			Asura_Message_Container_ChangeHealth* pxData = static_cast<Asura_Message_Container_ChangeHealth*>( pxMessage->GetData() );
			if (pxData)
			{
				pxData->m_uUniqueID = pxHealth->GetUniqueID();
				pxData->m_fHealth   = fNewHealth;
			}

			if (fNewHealth == 0.0f)
			{
				// Health has just been set to zero (it was a positive value).
				// Guarantee this, as there will be no further damage messages sent.
				pxMessage->UseGuaranteedDelivery();
			}

			Send( pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES );
		}
	}

	if (xOutput.m_bDamageOverrideChanged)
	{
		// Should be called after updating the client.
		PostStateChange();
	}

	// Generate health events
	SendOnHealthPercentMessages( xOutput.m_fOldHealthPercent, xOutput.m_fNewHealthPercent );
}

/////////////////////////////////
// SendOnHealthPercentMessages //
/////////////////////////////////
// Note: if the entity has multiple health containers active, then there is currently no way of determining
// which one generated the event. All of the health containers will generate events.
void Asura_ServerEntity_Container::SendOnHealthPercentMessages(float fOldHealthPercent, float fNewHealthPercent)
{
	// 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 )
		{
			const float fThresholdPercent = static_cast<float>(usEventID - ASURA_ENTITY_EVENT_ONHEALTHPERCENT_0);
			if ( (fOldHealthPercent > fThresholdPercent) && (fNewHealthPercent <= fThresholdPercent) )
			{
				Asura_StaticMessage_System::SendMessages( usMsgBlockID, GetGuid() );
			}
		}
	}
}


//////////////////////////////////////////
// Asura_Container_Functor_IsDamageable //
//////////////////////////////////////////
class Asura_Container_Functor_IsDamageable : public Asura_Container_ServerFunctor
{
public:
	Asura_Container_Functor_IsDamageable() : m_bDamageable(false) {}
	
	bool IsDamageable() const { return m_bDamageable; }
	
	virtual void Process(const Asura_ServerContainerInstance* pxEntry)
	{
		if (pxEntry->GetClassification() == AsuraContainerClass_Health)
		{
			ProcessHealth(static_cast<const Asura_ServerContainerInstance_Health*>(pxEntry));
		}
	}

private:
	void ProcessHealth(const Asura_ServerContainerInstance_Health* const pxHealth)
	{
		if (!pxHealth->IsOff() && pxHealth->GetHealthPercent() > 0.0f)
		{
			m_bDamageable = true;
		}
	}

private:
	bool m_bDamageable;
};

//////////////////
// IsDamageable //
//////////////////
// Are any parts of the container damageable?
bool Asura_ServerEntity_Container::IsDamageable() const
{
	if (!m_bIsDamageableInitialised)
	{
		m_bIsDamageableInitialised = true;
		m_bIsDamageable = false;

		Asura_Assert(m_pxServerInstance, "Null server instance");
		if (m_pxServerInstance)
		{
			Asura_Container_Functor_IsDamageable xFunctor;
			m_pxServerInstance->Traverse(xFunctor);
			m_bIsDamageable = xFunctor.IsDamageable();
		}
	}

	return m_bIsDamageable;
}

///////////////////////////////
// AdjustNumberOfInputStates //
///////////////////////////////
// If the number of input states has been changed since we last saved this entity, then adjust the number of states.
void Asura_ServerEntity_Container::AdjustNumberOfInputStates()
{
	const u_int uNumInputs = m_xInputState.GetNumInputs();
	const Asura_Container* pxContainer = Asura_Container_System::FindContainer(GetContainerHash());
	Asura_Assertf(pxContainer, ("Container (0x%08x) not found.", GetContainerHash()));
	if (!pxContainer) return;

	if (pxContainer->GetClassification() != AsuraContainerClass_Collection)
	{
		Asura_Assert(0, "Wrong container classification");
		return;
	}
	
	const Asura_Container_Collection* const pxCollection = static_cast<const Asura_Container_Collection*>(pxContainer);
	const u_int uNewNumInputs = pxCollection->GetNumInputs();
	
	if (uNewNumInputs != uNumInputs)
	{
		// Adjust the number of input slots. Initialising any new states.
		for (u_int u = uNumInputs; u < uNewNumInputs; ++u)
		{
			m_xInputState.SetStateHash(u, ASURA_HASH_ID_UNSET);
		}
		m_xInputState.SetNumInputs(uNewNumInputs);
	}
}

////////////////////////
// PostLoadInitialise //
////////////////////////
void Asura_ServerEntity_Container::PostLoadInitialise()
{
	PARENT::PostLoadInitialise();

	CreateInstance();

	// Make sure that the waypoint link blocks are up-to-date.
	UpdateWaypointLinkBlocks();
}

///////////////////////////////
// SendOnDestructionMessages //
///////////////////////////////
void Asura_ServerEntity_Container::SendOnDestructionMessages()
{
	SendMessagesForEvent(ASURA_ENTITY_EVENT_ONDESTRUCTION);

	// Remove the waypoint link blocks.
	UpdateWaypointLinkBlocks(true);
}

/////////////////////////////
// MakeClientUpdateMessage //
/////////////////////////////
void Asura_ServerEntity_Container::MakeClientUpdateMessage()
{
	UpdateDynamicPhysicsData();

	Asura_Message* pxMessage = 0;

	if (m_uNumPhysicsParam == 0)
	{
		// Basic version with no physics data.
		pxMessage = Asura_Network::CreateMessage( ASURA_MSG_CLIENTUPDATE, sizeof(Asura_Message_Container_ClientUpdate) );
		if (pxMessage)
		{
			Asura_Message_Container_ClientUpdate* pxData = static_cast<Asura_Message_Container_ClientUpdate*>( pxMessage->GetData() );
			if (pxData)
			{
				pxData->FillIn(this);
			}
		}
	}
	else if (m_uNumPhysicsParam == 1)
	{
		// Extended version with one dynamic physics object.
		pxMessage = Asura_Network::CreateMessage( ASURA_MSG_CONTAINER_CLIENTUPDATE_EXTENDED1, sizeof(Asura_Message_Container_ClientUpdate_Extended1) );
		if (pxMessage)
		{
			Asura_Message_Container_ClientUpdate_Extended1* pxData = static_cast<Asura_Message_Container_ClientUpdate_Extended1*>( pxMessage->GetData() );
			if (pxData)
			{
				pxData->FillIn(this);
			}
		}
	}
	else
	{
		// Extended version with more than one dynamic physics object.
		pxMessage = Asura_Network::CreateMessage( ASURA_MSG_CONTAINER_CLIENTUPDATE_EXTENDED, sizeof(Asura_Message_Container_ClientUpdate_Extended) );
		if (pxMessage)
		{
			Asura_Message_Container_ClientUpdate_Extended* pxData = static_cast<Asura_Message_Container_ClientUpdate_Extended*>( pxMessage->GetData() );
			if (pxData)
			{
				pxData->FillIn(this);
			}
		}
	}

	if (pxMessage)
	{
		Send( pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES );
	}
}

/////////////////////////////
// OnEntityActivateMessage //
/////////////////////////////
void Asura_ServerEntity_Container::OnEntityActivateMessage()
{
	// Send create messages first time this entity is activated.
	if (!m_bSentCreateMessages)
	{
		MakeCreateMessage(ASURA_GUID_UNREGISTERED);
		m_bSentCreateMessages = true;
	}

	PARENT::OnEntityActivateMessage();

	// Enable the waypoint link blocks.
	UpdateWaypointLinkBlocks();
}

///////////////////////////////
// OnEntityDeactivateMessage //
///////////////////////////////
void Asura_ServerEntity_Container::OnEntityDeactivateMessage()
{
	PARENT::OnEntityDeactivateMessage();

	// Remove the waypoint link blocks.
	UpdateWaypointLinkBlocks(true);
}

///////////////////////
// MakeCreateMessage //
///////////////////////
void Asura_ServerEntity_Container::MakeCreateMessage(Asura_Guid uRequestingClient, bool bSendGuaranteed /*= false*/)
{
	// Don't create clients until the server entity is active.
	if (!IsEntityActive())
	{
		return;
	}

	const u_int uNumProjectBytes = GetNumBytesForProjectCreateMessageData();
	const u_int uSize = Asura_Message_Container_Create::GetSize(GetNumInputs(), GetNumPhysicsParam(), GetNumFloatParam(), uNumProjectBytes);
	Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_CLIENTCREATE, uSize, GetClassification());
	Asura_Assert(pxMessage, "Null message pointer");
	if (!pxMessage) return;

	Asura_Message_Container_Create* pxData = static_cast<Asura_Message_Container_Create*>(pxMessage->GetData());
	Asura_Assert(pxData, "Null message data pointer");
	if (pxData)
	{
		pxData->FillIn(this);

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

		if (bSendGuaranteed)
		{
			pxMessage->UseGuaranteedDelivery();
		}

		Asura_Network::SendToSpecificMachine(pxMessage, uRequestingClient);
	}
	else
	{
		Asura_Network::DestroyMessage(pxMessage);
	}
}

////////////
// Update //
////////////
void Asura_ServerEntity_Container::Update()
{
	PARENT::Update();

	UpdateDynamicPhysicsData();

	if (m_bDeletePendingVelocities)
	{
		m_bDeletePendingVelocities = false;

		delete m_pxPendingVel;
		m_pxPendingVel = 0;
	}

	// Change the update frequency as needed.
	if (m_bDynamicPhysicsAwake)
	{
		if (!m_bUpdateAlways)
		{
			// Physics is awake so that we need to send regular client updates to
			// remote machines. Local machine doesn't need the pos/orient updates.
			SetAlwaysUpdateClient(true);
			m_bUpdateAlways = true;
		}
	}
	else if (m_bUpdateAlways)
	{
		// Physics is not awake so there is no need to send regular client updates.
		SetAlwaysUpdateClient(false);
		m_bUpdateAlways = false;
	}

	if (m_fGameTimeToDie > 0.0f &&
		Asura_Timers::GetLocalScaledTime() > m_fGameTimeToDie)
	{
		// Send any destruction messages. Then destroy the server and client entities.
		SendOnDestructionMessages();
		SetThisAndClientsAsDestroyed();
	}
}

//////////////////////////////
// UpdateDynamicPhysicsData //
//////////////////////////////
void Asura_ServerEntity_Container::UpdateDynamicPhysicsData()
{
	// Only update this data once a frame.
	if (m_uUpdatedDynamicPhysicsFrameStamp == Asura_Timers::GetFrameNumber()) return;
	
	m_uUpdatedDynamicPhysicsFrameStamp = Asura_Timers::GetFrameNumber();

	m_bDynamicPhysicsAwake = false;
	m_uNumDynamicPhysics = 0;

	const Asura_ClientEntity_Container* pxClient = Asura_ClientEntity_Container::FindEntity(GetGuid());
	if (!pxClient) return;


	for (const Asura_Container_PhysicsBlock* pxCurrent = pxClient->GetFirstPhysicsBlock();
		 pxCurrent;
		 pxCurrent = pxCurrent->GetNext())
	{
		const Asura_Physics_Object* pxPhysics = pxCurrent->m_pxPhysics;
		if (pxPhysics)
		{
			++m_uNumDynamicPhysics;

			// Copy pos/orient data from the client dynamic physics blocks to the server.
			Asura_Assert(m_pxServerInstance, "Couldn't find container server instance to store physics data.");
			if (m_pxServerInstance)
			{
				Asura_ServerContainerInstance* pxInstance = m_pxServerInstance->FindFromUniqueID(pxCurrent->m_uUniqueID);
				Asura_Assert(pxInstance, "Couldn't find container server instance to store physics data.");
				if (pxInstance)
				{
					const bool bAdded = pxInstance->StorePhysicsParam(pxPhysics);
					if (bAdded)
					{
						++m_uNumPhysicsParam;
					}
				}
			}

			if (!pxPhysics->IsAsleep() && !pxPhysics->IsIgnore())
			{
				m_bDynamicPhysicsAwake = true;
			}
		}
	}
}

////////////////////////
// GetPhysicsVelocity //
////////////////////////
// Look up the stored physics velocities for the given sub-container.
// Return true if found, in which case xLinVelocity and xAngVelocity are set with the output data.
bool Asura_ServerEntity_Container::GetPhysicsVelocity(u_int uUniqueID, Asura_Vector_3& xLinVelocity, Asura_Vector_3& xAngVelocity)
{
	// Initialise physics velocities for game created containers that have an intial velocity.
	if (m_pxPendingVel)
	{
		xLinVelocity = m_pxPendingVel->m_xLinVelocity;
		xAngVelocity = m_pxPendingVel->m_xAngVelocity;

		// Flag for deletion, so that the data is only used the first time we initialise the container.
		m_bDeletePendingVelocities = true;
		return true;
	}

	if (m_pxServerInstance)
	{
		const Asura_ServerContainerInstance* pxFound = m_pxServerInstance->FindFromUniqueID(uUniqueID);
		if (pxFound)
		{
			return pxFound->GetPhysicsVelocity(xLinVelocity, xAngVelocity);
		}
	}
	return false;
}

////////////////////
// CreateInstance //
////////////////////
void Asura_ServerEntity_Container::CreateInstance(bool bCallChangeState /*= true*/)
{
#ifdef ASURA_TOOLS
	// When Tools is active we don't need the server instance. It is mainly used
	// for checkpoint saves and client/server messaging.
	const bool bToolsIsActive = Tools_System::IsActive();
	if (bToolsIsActive)
	{
		DestroyInstance();
		return;
	}
#endif //ASURA_TOOLS

	if (m_pxServerInstance && m_pxServerInstance->GetContainerHash() != m_uContainerHash)
	{
		DestroyInstance();
	}

	if (!m_pxServerInstance)
	{
		// Assert if the server instance isn't created due to a loading order issue.
		// Basically we need the containers (in the common file) to load before the entities. 
		Asura_Assert(Asura_Container_System::GetTree().GetNumberOfNodes() > 0, "Failed to create server instance");

		m_pxServerInstance = Asura_Container_System::CreateServerInstance(m_uContainerHash);
		if (m_pxServerInstance)
		{
			Asura_ServerContainerInstance_Properties xData;
			m_pxServerInstance->SetUniqueID(0);
			m_pxServerInstance->Initialise(xData);

			// Flag set if any of the meshes blocks waypoint links, whether they are currently enabled or not.
			m_bShouldBlockWaypointLinks = xData.m_bBlockWaypointLinks;
			
			// Make sure that the number of input states is correct.
			AdjustNumberOfInputStates();

			// Usually we want to set up the states immediately, but when doing a load we need to
			// delay this until after loading the health values (in this case we don't want to
			// call this function twice).
			if (bCallChangeState)
			{
				m_pxServerInstance->ChangeState(m_xInputState);
			}

			m_uNumFloatParam = xData.m_uNumFloatParam;
			
			if (m_uNumFloatParam == 0)
			{
				// No health containers, so it can't be damageable.
				m_bIsDamageable = false;
				m_bIsDamageableInitialised = true;
			}
		}
	}
}

/////////////////////
// DestroyInstance //
/////////////////////
void Asura_ServerEntity_Container::DestroyInstance()
{
	Asura_Container_System::DestroyServerInstance(m_pxServerInstance);
	m_pxServerInstance = 0;
	m_uNumPhysicsParam = 0;

	delete m_pxPendingVel;
	m_pxPendingVel = 0;
}

//////////////////////
// FillPhysicsParam //
//////////////////////
// Fill the physics parameter data. Copy the physics parameters to the given data pointer.
void Asura_ServerEntity_Container::FillPhysicsParam(Asura_Container_PhysicsParam*& pxPhysicsData, u_int uNumToWrite) const
{
	Asura_Assert(m_pxServerInstance, "Null server instance");
	if (m_pxServerInstance)
	{
		Asura_Assert(uNumToWrite <= m_uNumPhysicsParam, "Requested too many physics param to write!");
		m_pxServerInstance->FillPhysicsParam(pxPhysicsData, uNumToWrite);
		Asura_Assert(uNumToWrite == 0, "Failed to write all of the container physics parameters");
	}
}


////////////////////
// FillFloatParam //
////////////////////
// Fill the float parameter data. Copy the float parameters to the given data pointer.
void Asura_ServerEntity_Container::FillFloatParam(Asura_Container_FloatParam*& pxFloatData) const
{
	Asura_Assert(m_pxServerInstance, "Null server instance");
	if (m_pxServerInstance)
	{
		u_int uNumToWrite = m_uNumFloatParam;
		m_pxServerInstance->FillFloatParam(pxFloatData, uNumToWrite);
		Asura_Assert(uNumToWrite == 0, "Failed to write all of the container float parameters");
	}
}

///////////////////
// FindBlueprint //
///////////////////
// Find the blueprint in the root collection.
// Set bIncludeDisabled to include both enabled and disabled blueprint containers.
// If uBlueprintTypeHash != ASURA_HASH_ID_UNSET, then only return blueprints of the specified type.
// Returns NULL if no relevant blueprint found.
const Asura_Container_Blueprint* Asura_ServerEntity_Container::FindBlueprint(Asura_Hash_ID uBlueprintTypeHash,
																			 bool bIncludeDisabled) const
{
	if (bIncludeDisabled)
	{
		// Just look it up directly from the container.
		const Asura_Container* pxContainer = Asura_Container_System::FindContainer(m_uContainerHash);
		if (pxContainer && pxContainer->GetClassification() == AsuraContainerClass_Collection)
		{
			const Asura_Container_Collection* const pxCollection = static_cast<const Asura_Container_Collection*>(pxContainer);
			return pxCollection->FindBlueprintContainer(uBlueprintTypeHash);
		}
	}
	else
	{
		// Peek the client as that has all of the enabled/disabled status set up.
		const Asura_ClientContainerInstance* const pxClientInstance = GetClientInstance();
		if (pxClientInstance && pxClientInstance->GetClassification() == AsuraContainerClass_Collection)
		{
			const Asura_ClientContainerInstance_Collection* const pxClientCollection = static_cast<const Asura_ClientContainerInstance_Collection*>(pxClientInstance);
			return pxClientCollection->FindBlueprint(uBlueprintTypeHash, bIncludeDisabled);
		}
	}

	return 0;
}

///////////////////////////////////////////////////////////////////////////////
// Tools functions
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_TOOLS
void Asura_ServerEntity_Container::SetContainerHash(Asura_Hash_ID uContainerHash)
{
	m_uContainerHash = uContainerHash;
	DestroyInstance();
}

void Asura_ServerEntity_Container::SetPosition(const Asura_Vector_3& xPosition)
{
	m_xPosition = xPosition;
}

void Asura_ServerEntity_Container::SetOrientationQuat(const Asura_Quat& xQuat)
{
	m_xOriQuat = xQuat;
}

/////////////////////////////////////
// ApplyChangesForTemplateInstance //
/////////////////////////////////////
void Asura_ServerEntity_Container::ApplyChangesForTemplateInstance(class Tools_EntityTemplateInstance* pxInstance)
{
	PARENT::ApplyChangesForTemplateInstance(pxInstance);

	m_xPosition = pxInstance->TransformPosition(m_xPosition);
	m_xOriQuat  = pxInstance->TransformQuat(m_xOriQuat);

	// Any blocked waypoints read from template file will be inappropriate, so delete them.
	DiscardWaypointLinkBlockInformation();
}

/////////////////////////////
// CanBeIncludedInTemplate //
/////////////////////////////
bool Asura_ServerEntity_Container::CanBeIncludedInTemplate() const
{
	return Classification == AsuraEntityClass_Container;
}

#endif //#ifdef ASURA_TOOLS


///////////////////////
// GetClientInstance //
///////////////////////
// Peek the client container instance, get it from the client or Tools.
const Asura_ClientContainerInstance* Asura_ServerEntity_Container::GetClientInstance() const
{
	if (m_xInputState.IsOff())
	{
		// Everything is disabled. So there is no need to parse the client instance tree.
		return 0;
	}

	const Asura_ClientEntity_Container* const pxClient = Asura_ClientEntity_Container::FindEntity(GetGuid());
	if (pxClient)
	{
		return pxClient->GetClientInstanceRoot();
	}

#ifdef ASURA_TOOLS
	const Tools_Entity_Container* const pxToolsEntity = Tools_Entity_Container::FindEntity(GetGuid());
	if (pxToolsEntity)
	{
		return pxToolsEntity->GetClientInstanceRoot();
	}
#endif //ASURA_TOOLS

	return 0;
}

/////////////////////////////////////////////
// InitialiseWaypointLinkBlockCalculations //
/////////////////////////////////////////////
// Initialise storage for waypoint link blocking related calculations.
void Asura_ServerEntity_Container::InitialiseWaypointLinkBlockCalculations(const u_int uMaxWaypointsToInclude /* = uDEFAULT_MAX_WAYPOINTS_TO_INCLUDE */)
{
	if ( s_pxLinksToBlock == 0)
	{
		s_pusWaypoints		= new unsigned short[ uMaxWaypointsToInclude ];
		s_pxLinksToBlock	= new Asura_AI_NavConnection_Index[ uMAX_LINKS_BLOCKED_PER_OBJECT ];

		s_uNumWaypointsToInclude = uMaxWaypointsToInclude;
		s_usNumWaypoints	= 0;
		s_ucNumLinksToBlock	= 0;
	}
}

///////////////////////////////////////////
// ShutdownWaypointLinkBlockCalculations //
///////////////////////////////////////////
// Shutdown storage for waypoint link blocking related calculations.
void Asura_ServerEntity_Container::ShutdownWaypointLinkBlockCalculations()
{
	if ( s_pxLinksToBlock )
	{
		delete [] s_pusWaypoints;
		s_pusWaypoints = NULL;

		delete [] s_pxLinksToBlock;
		s_pxLinksToBlock = NULL;
	}
}

////////////////////////
// BlockWaypointLinks //
////////////////////////
void Asura_ServerEntity_Container::BlockWaypointLinks(bool bBlockLinks)
{
	if ( HasBlockedLinks() != bBlockLinks )
	{
		for ( unsigned char uc = 0; uc < m_ucNumLinksToBlock; ++uc )
		{
			Asura_AI_Navigation_System::MarkConnectionAsBlocked( m_pxLinksToBlock[ uc ], bBlockLinks );
		}
		m_bBlockedLinks = bBlockLinks;
	}
}

//////////////////////////////
// UpdateWaypointLinkBlocks //
//////////////////////////////
// Update waypoint links marked for blocking.
void Asura_ServerEntity_Container::UpdateWaypointLinkBlocks(const bool bForceRemoval /*= false*/)
{
	const bool bShouldBlockLinks = ((m_ucNumLinksToBlock > 0) && !bForceRemoval && IsEntityActive());

#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 );
        BlockWaypointLinks( bShouldBlockLinks );
        Asura_AI_Navigation_System::SetCurrentHandler( &xOldHandler );
    }
#else
	BlockWaypointLinks( bShouldBlockLinks );
#endif
}

/////////////////////////////////////////
// DiscardWaypointLinkBlockInformation //
/////////////////////////////////////////
// Throw away any link blocking information. Doesn't try to unmark links, as they haven't been marked yet.
// Return true if the link blocks have changed.
bool Asura_ServerEntity_Container::DiscardWaypointLinkBlockInformation()
{
	const bool bHadLinks = ( m_ucNumLinksToBlock != 0 );

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

	return bHadLinks;
}


///////////////////////////////////////////////////////////////////////////////
// Asura_Container_Functor_BlockWaypointLinks
///////////////////////////////////////////////////////////////////////////////
// Functor to find waypoint links that are blocked by container mesh instances.
class Asura_Container_Functor_BlockWaypointLinks: public Asura_Container_Functor
{
public:
	Asura_Container_Functor_BlockWaypointLinks(Asura_Guid uGuid)
		: m_uGuid(uGuid)
	{
	}

	virtual void Process(const Asura_ClientContainerInstance* pxEntry)
	{
		if (pxEntry->GetClassification() == AsuraContainerClass_Mesh)
		{
			ProcessMesh(static_cast<const Asura_ClientContainerInstance_Mesh*>(pxEntry));
		}
	}

private:
	void ProcessMesh(const Asura_ClientContainerInstance_Mesh* const pxMesh)
	{
		const Asura_Container_Mesh* const pxContainerMesh = pxMesh->GetContainer();
		if (!pxContainerMesh) return;

		if (!pxContainerMesh->ShouldBlockWaypointLinks())
		{
			return; // Not flagged to block waypoint links.
		}
		if (pxContainerMesh->HasNoCollisions())
		{
			return; // No collisions.
		}

		const Asura_Animation* const pxAnimation = pxMesh->GetAnimation();
		if (!pxAnimation) return;

		const Asura_Physics_Object* const pxPhysicsObject = pxMesh->GetPhysicsObject();
		if (!pxPhysicsObject) return;

		const Asura_Collision_Object* const pxCollObj = pxPhysicsObject->GetCollisionObject();
		if (!pxCollObj) return;
		
		Asura_Bounding_Box xEnlargedBB;
		xEnlargedBB = pxPhysicsObject->GetBoundingBox();
		xEnlargedBB.Grow( Asura_AI_Navigation_System::GetMaxWaypointRadius() * 1.5f );
		
		// Make list of relevant waypoints.
		Asura_ServerEntity_Container::s_usNumWaypoints = 0;
		Asura_AI_Navigation_System::SearchWaypoints( xEnlargedBB, Asura_ServerEntity_Container::WaypointCallback );

		// Find any blocked links.
		Asura_ServerEntity_Container::TestForPhysicsObjectBlockedLinks( pxPhysicsObject, m_uGuid );
	}

protected:
	Asura_Guid m_uGuid;
};

//////////////////////
// WaypointCallback //
//////////////////////
bool Asura_ServerEntity_Container::WaypointCallback( u_short usWaypointIndex )
{
    if ( s_usNumWaypoints < s_uNumWaypointsToInclude )
    {
        s_pusWaypoints[ s_usNumWaypoints++ ] = usWaypointIndex;
        return true;
    }
    return false;
}

////////////////////////////
// AddWaypointLinkToBlock //
////////////////////////////
void Asura_ServerEntity_Container::AddWaypointLinkToBlock(u_short usNavVertexIndex, u_char ucConnectionIndex, u_int uEndIndex, Asura_Guid uGuid)
{
	if (IsDuplicateLinkToBlock(usNavVertexIndex, ucConnectionIndex, uEndIndex))
	{
		return;
	}

	Asura_Assertf( s_ucNumLinksToBlock < UCHAR_MAX, ( "Too many waypoints links blocked by container entity %u", uGuid ) );

	if ( s_ucNumLinksToBlock < UCHAR_MAX )
	{
		s_pxLinksToBlock[ s_ucNumLinksToBlock ].m_usWaypointIndex   = usNavVertexIndex;
		s_pxLinksToBlock[ s_ucNumLinksToBlock ].m_usConnectionIndex = ucConnectionIndex;
		++s_ucNumLinksToBlock;
	}
}

////////////////////////////
// IsDuplicateLinkToBlock //
////////////////////////////
bool Asura_ServerEntity_Container::IsDuplicateLinkToBlock(u_short usNavVertexIndex, u_char ucConnectionIndex, u_int uEndIndex)
{
	Asura_Assert(uEndIndex <= s_ucNumLinksToBlock, "Invalid waypoint link index");
	for (u_int u=0; u<uEndIndex; ++u)
	{
		if (s_pxLinksToBlock[u].m_usWaypointIndex == usNavVertexIndex &&
			s_pxLinksToBlock[u].m_usConnectionIndex == ucConnectionIndex)
		{
			return true;
		}
	}

	return false;
}

//////////////////////////////////////
// TestForPhysicsObjectBlockedLinks //
//////////////////////////////////////
void Asura_ServerEntity_Container::TestForPhysicsObjectBlockedLinks( const Asura_Physics_Object* pxPhysicsObject, Asura_Guid uGuid )
{
	// If this is the first this function has been called then there is no need to check for duplicates.
	// Otherwise we need to check for duplicates from previous calls.
	const u_int uPreviousNumLinksToBlock = s_ucNumLinksToBlock;

    for ( unsigned short usWaypoint = 0; usWaypoint < s_usNumWaypoints; ++usWaypoint )
    {
        const unsigned short      usNavVertexIndex = s_pusWaypoints[ usWaypoint ];
        const Asura_AI_NavVertex* pxNavVertex      = Asura_AI_Navigation_System::GetWaypoint( usNavVertexIndex );
        for ( unsigned char ucConnectionIndex = 0; ucConnectionIndex < pxNavVertex->m_ucNumConnections; ++ucConnectionIndex )
        {
            const unsigned short      usTargetNavVertexIndex = pxNavVertex->m_pxConnections[ ucConnectionIndex ].m_usIndex;
            const Asura_AI_NavVertex* pxTargetNavVertex      = Asura_AI_Navigation_System::GetWaypoint( usTargetNavVertexIndex );

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

				Asura_Vector_3 xBeta = pxTargetNavVertex->m_xPosition - pxNavVertex->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 )
                    {
                        AddWaypointLinkToBlock(usNavVertexIndex, ucConnectionIndex, uPreviousNumLinksToBlock, uGuid);

                        // (TI - 24.05.2007) - 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 ( unsigned short usWaypointIndex = 0; usWaypointIndex < s_usNumWaypoints && bBlockLinkComingOtherWay; ++usWaypointIndex )
						{
							if ( usTargetNavVertexIndex == s_pusWaypoints[ usWaypointIndex ] )
							{
								bBlockLinkComingOtherWay = false;
							}
						}

						if ( bBlockLinkComingOtherWay )
						{
							// Block the link coming the other way (if there is one) also.
							for ( unsigned char ucTargetConnectionIndex = 0; ucTargetConnectionIndex < pxTargetNavVertex->m_ucNumConnections; ++ucTargetConnectionIndex )
							{
								if ( pxTargetNavVertex->m_pxConnections[ ucTargetConnectionIndex ].m_usIndex == usNavVertexIndex )
								{
									AddWaypointLinkToBlock(usTargetNavVertexIndex, ucTargetConnectionIndex, uPreviousNumLinksToBlock, uGuid);
									break;
								}
							}
						}

                        break;
                    }
                }
            }
        }
    }
}

/////////////////////////////////
// CalculateWaypointLinkBlocks //
/////////////////////////////////
// Determine which waypoint links should be blocked.
// Return true if the link blocks have changed.
bool Asura_ServerEntity_Container::CalculateWaypointLinkBlocks(const bool bForceUpdate /*= true*/)
{
	if ( !s_pxLinksToBlock )
	{
		Asura_Assert( false, "Asura_ServerEntity_Container::CalculateWaypointLinkBlocks - calculations not initialised yet" );
		return false;
	}

	if (!ShouldBlockWaypointLinks())
	{
		// No mesh containers have the "block waypoint links" flag set.
		if (m_ucNumLinksToBlock)
		{
			BlockWaypointLinks(false);
			return DiscardWaypointLinkBlockInformation();
		}
		return false;
	}

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

	// Make sure that we've cleared any links that are marked as blocked.
	BlockWaypointLinks(false);

    // Clear the blocked waypoint list, keeping the old list so we can compare later on.
	const unsigned char                       ucOldCount = m_ucNumLinksToBlock;
    const Asura_AI_NavConnection_Index* const pxOldList  = m_pxLinksToBlock;
    m_ucNumLinksToBlock = 0;
    m_pxLinksToBlock    = NULL;

    // Calculate the links that we block.
    s_usNumWaypoints    = 0;
    s_ucNumLinksToBlock = 0;

	// Traverse the client instance looking for meshes which might blocked links.
	const Asura_ClientContainerInstance* const pxClientInstance = GetClientInstance();
	if (pxClientInstance)
	{
		Asura_Container_Functor_BlockWaypointLinks xFunctor(GetGuid());
		pxClientInstance->Traverse(xFunctor);
	}

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

    if ( ucOldCount != m_ucNumLinksToBlock )
    {
        delete [] pxOldList;
        return true;
    }
    
	// Compare old list with new list to see whether they are different.
    bool bDifferent = false;
    for ( unsigned char uc = 0; uc < m_ucNumLinksToBlock; ++uc )
    {
        if ( pxOldList[ uc ] != m_pxLinksToBlock[ uc ] )
        {
            bDifferent = true;
            break;
        }
    }
    delete [] pxOldList;
    return bDifferent;
}

#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM
