//
// Tools_EntityTemplate_System.cpp
//
// Created by Richard Rice - 07/04/2004
// 
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "EntityTemplate/Tools_EntityTemplate_System.h"

#include "ChunkSystem/Asura_Chunk_Entity.h"
#include "ChunkSystem/Asura_Chunk_Phonons.h"
#include "ChunkSystem/Asura_Chunk_GlobalIllumination.h"
#include "ChunkSystem/Asura_Chunk_Corona.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "ChunkSystem/Asura_Chunk_StreamingBackgroundSound.h"
#include "ChunkSystem/Asura_Chunk_Texture.h"
#ifdef ASURA_USE_ATTRACTORS
#include "ChunkSystem/Asura_Chunk_Attractors.h"
#endif
#include "ChunkSystem/Asura_Chunk_SoundEvent.h"
#include "Core/Asura_String.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/Asura_Entity.h"
#include "Entity/Asura_StaticMessage_System.h"
#include "Entity/EntityTemplate/Asura_ServerEntity_Template.h"
#include "Entity/PhysicalObject/Asura_ServerEntity_Lift.h"
#include "Entity/Triggers/Asura_ServerEntity_ProxyTrigger.h"
#ifndef ASURA_USE_AUDIO_SYSTEM
#include "Sound/Asura_ProjectSoundIDs.h"
#endif
#include "Sound/Asura_Phonon.h"
#include "Sound/Asura_StreamingBackgroundSound_System.h"
#include "SpecialFX/Asura_StaticCorona_System.h"
#if !defined(ASURA_DONT_USE_TELEPORTERS)
#include "Teleport/Asura_Teleport_Messages.h"
#endif	// !defined(ASURA_DONT_USE_TELEPORTERS)

#ifdef ASURA_USE_AXON
#include "Core\Axon_Messages.h"
#endif

#ifdef ASURA_USE_ATTRACTORS
#include "Attractors/Tools_Attractor.h"
#include "Attractors/Tools_Attractor_Node.h"
#include "Attractors/Tools_Attractor_System.h"
#include "Attractors/Tools_Object_Attractor_Node.h"
#endif
#include "Entity/Tools_Entity_Data.h"
#include "Entity/Tools_Entity_Object.h"
#include "Lighting/Tools_PhotonMapping_System.h"
#include "Lighting/Tools_PhotonMapping_LightSource.h"
#include "Lighting/Tools_PhotonMapping_LightSource_Object.h"
#include "Lighting/Tools_Corona_Object.h"
#include "Load&Save/Tools_AsuraFileContents.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Misc/list_tem.hpp"
#include "Shapes/Tools_Shapes.h"
#include "Sound/Tools_PickSound.h"
#include "Sound/Tools_Phonon_Object.h"
#include "Sound/Tools_StreamingBackgroundSound_Object.h"

////////////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------------**
** Tools_EntityTemplate_System **
**-----------------------------*/
List<Tools_EntityTemplate*> Tools_EntityTemplate_System::s_pxTemplateList;
List<Asura_Guid> Tools_EntityTemplate_System::s_xSpareEntityGuids;
List<Asura_Guid> Tools_EntityTemplate_System::s_xSpareCommonSoundGuids;
List<Asura_Guid> Tools_EntityTemplate_System::s_xSpareMissionSoundGuids;
List<u_int> Tools_EntityTemplate_System::s_xSpareLightIDs;
List<u_int> Tools_EntityTemplate_System::s_xSpareCoronaIDs;
List<u_int> Tools_EntityTemplate_System::s_xSpareStreamingSoundIDs;
Tools_EntityTemplate_System* Tools_EntityTemplate_System::s_pxThis = 0;

Tools_EntityTemplate_System::Tools_EntityTemplate_System()
{
	Asura_Assert(!s_pxThis,"Entity template system already allocated");
	s_pxThis = this;
}

Tools_EntityTemplate_System::~Tools_EntityTemplate_System()
{
	s_pxThis = 0;
}

void Tools_EntityTemplate_System::Initialise()
{
}

void Tools_EntityTemplate_System::Shutdown()
{
	while(s_pxTemplateList.size())
	{
		delete s_pxTemplateList.first_entry();
		s_pxTemplateList.delete_first_entry();
	}

	while(s_xSpareEntityGuids.size()) s_xSpareEntityGuids.delete_first_entry();
	while(s_xSpareCommonSoundGuids.size()) s_xSpareCommonSoundGuids.delete_first_entry();
	while(s_xSpareMissionSoundGuids.size()) s_xSpareMissionSoundGuids.delete_first_entry();
	while(s_xSpareLightIDs.size()) s_xSpareLightIDs.delete_first_entry();
	while(s_xSpareCoronaIDs.size()) s_xSpareCoronaIDs.delete_first_entry();
	while(s_xSpareStreamingSoundIDs.size()) s_xSpareStreamingSoundIDs.delete_first_entry();
}

Tools_EntityTemplate* Tools_EntityTemplate_System::GetTemplate(const char* pszName)
{
	//see if the template has already been loaded
	for(LIF<Tools_EntityTemplate*> xTempLIF(&s_pxTemplateList);!xTempLIF.done();xTempLIF.next())
	{
		if(!stricmp(xTempLIF()->GetName(),pszName))
		{
			//found the template , return it if it is valid
			return xTempLIF()->IsValid() ? xTempLIF() : 0;
		}
	}

	//failed to find template , so load it and add to the list
	Tools_EntityTemplate* pxTemplate = new Tools_EntityTemplate(pszName);
	s_pxTemplateList.add_entry(pxTemplate);

	//return it if it is valid
	return pxTemplate->IsValid() ? pxTemplate : 0;
}

void Tools_EntityTemplate_System::InvalidateTemplate(const char* pszName)
{
	for(LIF<Tools_EntityTemplate*> xTempLIF(&s_pxTemplateList);!xTempLIF.done();xTempLIF.next())
	{
		if(!stricmp(xTempLIF()->GetName(),pszName))
		{
			//found the template
			//delete is so that it will be loaded again when next needed
			delete xTempLIF();
			xTempLIF.delete_current();
			return;

		}
	}
}

void Tools_EntityTemplate_System::InvalidateTemplates()
{
	while(s_pxTemplateList.size())
	{
		delete s_pxTemplateList.first_entry();
		s_pxTemplateList.delete_first_entry();
	}
}

void Tools_EntityTemplate_System::RecycleEntityGuid(Asura_Guid uGuid)
{
	Asura_Assert(!s_xSpareEntityGuids.contains(uGuid),"Guid already in list");
	if(uGuid == ASURA_GUID_UNREGISTERED) return;
	if(!s_xSpareEntityGuids.contains(uGuid))
	{
		s_xSpareEntityGuids.add_entry(uGuid);
	}
}

void Tools_EntityTemplate_System::RecycleSoundGuid(Asura_Guid uGuid)
{
	//Asura_Assert(!s_xSpareSoundGuids.contains(uGuid),"Guid already in list");
	if(uGuid == ASURA_GUID_UNREGISTERED) return;

	bool bMissionFile = uGuid >= ASURA_PHONONGUID_MISSIONFILE_START ? true : false;
	List<u_int>& xSpareSoundGuids = bMissionFile ? s_xSpareMissionSoundGuids : s_xSpareCommonSoundGuids;

	if(!xSpareSoundGuids.contains(uGuid))
	{
		xSpareSoundGuids.add_entry(uGuid);
	}
}

void Tools_EntityTemplate_System::RecycleLightID(u_int uID)
{
	uID &= TOOLS_PHOTONMAPPING_IDMASK;
	
	Asura_Assert(!s_xSpareLightIDs.contains(uID),"ID already in list");
	if(uID == 0) return;
	if(!s_xSpareLightIDs.contains(uID))
	{
		s_xSpareLightIDs.add_entry(uID);
	}
}

void Tools_EntityTemplate_System::RecycleCoronaID(u_int uID)
{
	uID &= TOOLS_CORONA_IDMASK;

	Asura_Assert(!s_xSpareCoronaIDs.contains(uID),"ID already in list");
	if(uID == 0) return;
	if(!s_xSpareCoronaIDs.contains(uID))
	{
		s_xSpareCoronaIDs.add_entry(uID);
	}
}

void Tools_EntityTemplate_System::RecycleStreamingSoundID(u_int uID)
{
	Asura_Assert(!s_xSpareStreamingSoundIDs.contains(uID),"ID already in list");
	if(uID == 0) return;
	if(!s_xSpareStreamingSoundIDs.contains(uID))
	{
		s_xSpareStreamingSoundIDs.add_entry(uID);
	}
}



Asura_Guid Tools_EntityTemplate_System::GetNextEntityGuid()
{
	Asura_Guid uGuid;
	if(s_xSpareEntityGuids.size())
	{
		uGuid = s_xSpareEntityGuids.first_entry();
		s_xSpareEntityGuids.delete_first_entry();		
	}
	else
	{
		uGuid = Asura_Entity_System::GetCreatedByToolsGuid();
	}
	return uGuid;
}

Asura_Guid Tools_EntityTemplate_System::GetNextSoundGuid(bool bMissionFile)
{
	List<u_int>& xSpareSoundGuids = bMissionFile ? s_xSpareMissionSoundGuids : s_xSpareCommonSoundGuids;

	Asura_Guid uGuid;
	if(xSpareSoundGuids.size())
	{
		uGuid = xSpareSoundGuids.first_entry();
		xSpareSoundGuids.delete_first_entry();		
	}
	else
	{
		uGuid = ASURA_GUID_UNREGISTERED;
	}
	return uGuid;
}


u_int Tools_EntityTemplate_System::GetNextLightID()
{
	u_int uID;
	if(s_xSpareLightIDs.size())
	{
		uID = s_xSpareLightIDs.first_entry();
		s_xSpareLightIDs.delete_first_entry();		
	}
	else
	{
		uID = 0;
	}
	return uID;
}

u_int Tools_EntityTemplate_System::GetNextCoronaID()
{
	u_int uID;
	if(s_xSpareCoronaIDs.size())
	{
		uID = s_xSpareCoronaIDs.first_entry();
		s_xSpareCoronaIDs.delete_first_entry();		
	}
	else
	{
		uID = 0;
	}
	return uID;
}

u_int Tools_EntityTemplate_System::GetNextStreamingSoundID()
{
	u_int uID;
	if(s_xSpareStreamingSoundIDs.size())
	{
		uID = s_xSpareStreamingSoundIDs.first_entry();
		s_xSpareStreamingSoundIDs.delete_first_entry();		
	}
	else
	{
		uID = 0;
	}
	return uID;
}


void Tools_EntityTemplate_System::ConvertStaticMessage(Asura_StaticMessage* pxMessage, const Tools_EntityTemplateInstance* pxInstance) const
{
	switch(pxMessage->m_usMsgID)
	{
		case ASURA_MSG_PROXY_ADDTARGET :
		case ASURA_MSG_PROXY_REMOVETARGET :
		case ASURA_MSG_PROXY_SETTARGET :
		{
			Asura_Message_ProxyTrigger_Target* pxMessageData = (Asura_Message_ProxyTrigger_Target*) (pxMessage->m_pData);
			pxMessageData->m_uTargetGuid = pxInstance->ConvertGuid(pxMessageData->m_uTargetGuid);
			break;
		}

#if !defined(ASURA_DONT_USE_TELEPORTERS)
		case ASURA_MSGID_TELEPORT_ENTITY:
		{
			Asura_Message_TeleportEntity*	pxData		= static_cast<Asura_Message_TeleportEntity*>(pxMessage->m_pData);
			const Asura_Guid				uOldGuid	= pxData->GetGuidToTeleport();
			const Asura_Guid				uNewGuid	= pxInstance->ConvertGuid(uOldGuid);

			pxData->SetGuidToTeleport(uNewGuid);
			break;
		}
#endif	// !defined(ASURA_DONT_USE_TELEPORTERS)

		case ASURA_MSG_LIFT_GOTO_NODE :
		{
			Asura_MsgData_Lift_Goto_Node* pxMessageData = (Asura_MsgData_Lift_Goto_Node*) (pxMessage->m_pData);
			*pxMessageData->GetNodeGuidPtr() = pxInstance->ConvertGuid(pxMessageData->GetNodeGuid());
			break;
		}

#ifdef ASURA_USE_AXON
		case ASURA_MSG_AXON_GS2_DONT_STOP_AT_NODE:
		{
			Axon_Msg_GS2_Dont_Stop_At_Node* pxData = static_cast< Axon_Msg_GS2_Dont_Stop_At_Node* >( pxMessage->m_pData );
			pxData->m_uNodeGuid = pxInstance->ConvertGuid(pxData->m_uNodeGuid);
			break;
		}


		case ASURA_MSG_AXON_GS2_TAKE_LINK:
		{
			Axon_Msg_GS2_Take_Link* pxData = static_cast< Axon_Msg_GS2_Take_Link* >( pxMessage->m_pData );
			pxData->m_uLinkGuid = pxInstance->ConvertGuid(pxData->m_uLinkGuid);
			break;
		}

		case ASURA_MSG_AXON_GS2_SET_LINK_WEIGHT:
		{
			Axon_Msg_GS2_Set_Link_Weight* pxData = static_cast< Axon_Msg_GS2_Set_Link_Weight* >( pxMessage->m_pData );
			if(pxData->m_uNumEntries)
			{
				Axon_Msg_GS2_Set_Link_Weight::BoolGuidWeight* pxBGW = pxData->GetBGWArray();
				for(u_int i=0; i<pxData->m_uNumEntries; i++, pxBGW++)
				{
					pxBGW->m_uLinkGuid = pxInstance->ConvertGuid(pxBGW->m_uLinkGuid);
				}
			}
			break;
		}

		case ASURA_MSG_AXON_GS2_TELEPORT_TO_NODE:
		{
			Axon_Msg_GS2_Teleport_To_Node* pxData = static_cast< Axon_Msg_GS2_Teleport_To_Node* >( pxMessage->m_pData );
			pxData->m_uNodeGuid = pxInstance->ConvertGuid(pxData->m_uNodeGuid);
			break;
		}

		case ASURA_MSG_AXON_GS2_PATHFIND_TO_NODE:
		{
			Axon_Msg_GS2_Pathfind_To_Node* pxData = static_cast< Axon_Msg_GS2_Pathfind_To_Node* >( pxMessage->m_pData );
			pxData->m_uDestNodeGuid = pxInstance->ConvertGuid(pxData->m_uDestNodeGuid);
			break;
		}
#endif // ASURA_USE_AXON


		default :
			break;
	}
}


////////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------**
** Tools_EntityTemplate **
**----------------------*/

bool Tools_EntityTemplate::s_bUpdatingFromTemplate = false;

Tools_EntityTemplate::Tools_EntityTemplate(const char* pszName)
{
	m_pszName = new char[strlen(pszName)+1];
	strcpy(m_pszName,pszName);
	m_bValid = false;

	m_pxTemplateEntity = 0;
	m_pxTemplateToolsData = 0;
	m_puGuids = 0;
	m_pxTemplateInfo = 0;
	m_pxPhononChunk = 0;
	m_pxLightChunk = 0;
	m_pxCoronaChunk = 0;
	m_pxCoronaTextureChunk = 0;
	m_pxEntityTextureChunk = 0;
	#ifdef ASURA_USE_ATTRACTORS
	m_pxAttractorChunk = 0;
	#endif
	m_pxStreamingSoundChunk = 0;

	//Try to load the template file
	//Generate the filename
	char szFilename[MAX_PATH];
	Asura_PrintfToBfr(szFilename,"Templates\\%s.AsuraTemplate",MAX_PATH,true,pszName);

	//Note , we don't need the output from get network filename , and are just using it
	//in its capacity for updating local files.
	char szNetFilename[MAX_PATH];
	Asura_FileAccess::PlatformGetNetworkFilenameOrGetLocalCopy(szFilename,szNetFilename,260);

	m_pxChunkLister = new Tools_AsuraFileChunkLister();
	if(!m_pxChunkLister->LoadAsuraFile(szFilename))
	{
		Asura_Assertf(0,("Failed to load template file '%s'",szFilename));
		CleanUp();
		return;
	}

	//Search the chunk list for the chunks we will be needing
	const List<const Asura_Chunk_Header*>* pxChunkList = m_pxChunkLister->GetChunkList();

	Tools_EntityChunkPair* m_pxLastEntity = 0;

	const Asura_Chunk_TextureNames* pxTextureNamesChunk = 0;

	for(CLIF<const Asura_Chunk_Header*> xChunkLIF(pxChunkList);!xChunkLIF.done();xChunkLIF.next())
	{
		switch(xChunkLIF()->ID)
		{
			case ASURA_CHUNK_ENTITY :
			{
				const Asura_Chunk_Entity* pxEntity = (const Asura_Chunk_Entity*) xChunkLIF();

				//see if this entity is the master template
				bool bMaster = false;
				if(pxEntity->Classification == AsuraEntityClass_Template)
				{
					const char* pcChunkBuffer = (const char*)(pxEntity+1);
					int iVersion,iMaster;
					ASURA_CHUNK_EXTRACT(pcChunkBuffer,iVersion,int);
					ASURA_CHUNK_EXTRACT(pcChunkBuffer,iMaster,int);

					bMaster = iMaster!=0;
				}

				if(bMaster)
				{
					//make sure there aren't multiple master chunks
					if(m_pxTemplateEntity)
					{
						Asura_Assertf(0,("More than one master template entity found in %s",szFilename));
						CleanUp();
						return;
					}
					m_pxTemplateEntity = pxEntity;

				}
				else
				{
					m_pxLastEntity = new Tools_EntityChunkPair;
					m_pxLastEntity->m_pxEntityChunk = pxEntity;
					m_pxLastEntity->m_pxToolsChunk = 0;
					m_xEntityList.add_entry(m_pxLastEntity);
				}

				if(pxTextureNamesChunk)
				{
					//texture name chunk before entities should belong to them
					m_pxEntityTextureChunk = pxTextureNamesChunk;
					pxTextureNamesChunk = 0;
				}


				break;
			}

			case ASURA_CHUNK_ENTITY_TOOLSDATA :
			{
				const Asura_Chunk_Entity_ToolsData* pxToolsData = (const Asura_Chunk_Entity_ToolsData*) xChunkLIF();
				if(m_pxTemplateEntity && m_pxTemplateEntity->Guid == pxToolsData->Guid)
				{
					m_pxTemplateToolsData = pxToolsData;
				}
				else
				{
					//expect the tools data to come immediately after the corresponding entity
					Asura_Assert(m_pxLastEntity,"");
					Asura_Assert(!m_pxLastEntity->m_pxToolsChunk,"");
					Asura_Assert(m_pxLastEntity->m_pxEntityChunk->Guid == pxToolsData->Guid,"");

					m_pxLastEntity->m_pxToolsChunk = pxToolsData;				
				}
				break;
			}

			case ASURA_CHUNK_STATICMESSAGES :
			{
				m_pxMessageChunk = (const Asura_Chunk_StaticMessages*) xChunkLIF();
				break;
			}

			case ASURA_CHUNK_SOUNDNAMES :
			{
				m_xSoundChunkList.add_entry((const Asura_Chunk_SoundNames*) xChunkLIF());
				break;
			}
				
			case ASURA_CHUNK_SOUNDEVENT_PHONON :
			{
				m_pxPhononChunk =	(const Asura_Chunk_SoundEvent_Phonon*) xChunkLIF();
				break;
			}

			case ASURA_CHUNK_GLOBALILLUMINATION :
			{
				m_pxLightChunk = (const Asura_Chunk_GlobalIllumination*) xChunkLIF();
				break;
			}

			case ASURA_CHUNK_CORONA :
			{
				m_pxCoronaChunk = (const Asura_Chunk_Corona*) xChunkLIF();
				if(pxTextureNamesChunk)
				{
					//texture name chunk before coronas should belong to it
					m_pxCoronaTextureChunk = pxTextureNamesChunk;
					pxTextureNamesChunk = 0;
				}
				break;
			}

			case ASURA_CHUNK_TEXTURENAMES :
			{
				//store pointer to chunk , until we find out what us using it
				pxTextureNamesChunk = (const Asura_Chunk_TextureNames*) xChunkLIF();
				break;
			}

			#ifdef ASURA_USE_ATTRACTORS
			case ASURA_CHUNK_TOOLS_ATTRACTORS :
			{
				m_pxAttractorChunk = (const Asura_Chunk_Tools_Attractors*) xChunkLIF();
				break;
			}
			#endif

			case ASURA_CHUNK_STREAMINGBACKGROUNDSOUND :
			{
				m_pxStreamingSoundChunk = (const Asura_Chunk_StreamingBackgroundSound*) xChunkLIF();
				break;
			}

			default :
				break;
		}
	}

	//make sure the template master was found
	if(!m_pxTemplateEntity || !m_pxTemplateToolsData)
	{
		CleanUp();
		return;
	}


	const char* pcChunkBuffer = (const char*)(m_pxTemplateEntity+1);
	m_pxTemplateInfo = new Tools_EntityTemplateInfo(pcChunkBuffer);
	if(!m_pxTemplateInfo->m_bValid)
	{
		CleanUp();
		return;
	}
	

	//strip out any entity chunks that don't actually belong to this template
	LIF<Tools_EntityChunkPair*> xEntityLIF(&m_xEntityList);

	const Tools_EntityTemplate_ChildIDArray* pxArray = m_pxTemplateInfo->m_xChildIDHandler.GetArray(Tools_EntityTemplate_ChildType_Entity);
	
	for(;!xEntityLIF.done();)
	{
		bool bDelete = true;

		for(int i=0;pxArray && i<pxArray->m_iNumIDs;i++)
		{
			if(xEntityLIF()->m_pxEntityChunk->Guid == pxArray->m_puIDs[i])
			{
				bDelete = false;
				break;
			}
		}
		if(bDelete)
		{
			delete xEntityLIF();
			xEntityLIF.delete_current();
		}
		else
		{
			xEntityLIF.next();
		}
	}


	//create guid array
	m_iNumGuids = m_xEntityList.size()+1;
	m_puGuids = new Asura_Guid[m_iNumGuids];
	Asura_Guid* puGuid = m_puGuids;


	//Add the guids for all the child entities
	for(xEntityLIF.restart();!xEntityLIF.done();xEntityLIF.next())
	{
		*puGuid++ = xEntityLIF()->m_pxEntityChunk->Guid;
	}

	//followed by the guid for the template master
	*puGuid++ = m_pxTemplateEntity->Guid;

	m_bValid = true;

}

Tools_EntityTemplate::~Tools_EntityTemplate()
{
	delete [] m_pszName;
	CleanUp();
}

void Tools_EntityTemplate::CleanUp()
{
	delete m_pxChunkLister;
	m_pxChunkLister = 0;

	m_iNumGuids = 0;
	delete [] m_puGuids;
	m_puGuids = 0;


	m_pxTemplateEntity = 0;
	m_pxTemplateToolsData = 0;
	m_pxMessageChunk = 0;

	while(m_xEntityList.size())
	{
		delete m_xEntityList.first_entry();
		m_xEntityList.delete_first_entry();
	}

	delete m_pxTemplateInfo;
	m_pxTemplateInfo = 0;

}

void Tools_EntityTemplate::CreateTemplateMembers(Tools_EntityTemplateInstanceMain* pxInstance)
{
	s_bUpdatingFromTemplate = true;
	//create phonons first , since we will need the sound guid conversion array when doing the entities
	CreatePhonons(pxInstance);
	CreateStreamingSounds(pxInstance);
	#ifdef ASURA_USE_ATTRACTORS
	CreateAttractors(pxInstance);
	#endif
	CreateEntities(pxInstance);
	CreateLights(pxInstance);
	CreateCoronas(pxInstance);

	//Need to apply changes to streaming sounds after the entities are created, so do this now.
	UpdateStreamingSounds(pxInstance);
	#ifdef ASURA_USE_ATTRACTORS
	//similarly for attractors
	UpdateAttractors(pxInstance);
	#endif


	s_bUpdatingFromTemplate = false;
}

void Tools_EntityTemplate::CreateEntities(Tools_EntityTemplateInstanceMain* pxInstance)
{
	LIF<Tools_EntityChunkPair*> xEntityLIF(&m_xEntityList);

	List<Asura_Guid> xGuidList;
	List<Asura_Entity*> xEntityList;

	List<Asura_Guid> xBadEntityList;

	//process the texture chunk , so that the conversion array is setup
	if(m_pxEntityTextureChunk)
	{
		m_pxEntityTextureChunk->Process((const char*)(m_pxEntityTextureChunk+1));
	}


	//Create entities from the chunks
	for(;!xEntityLIF.done();xEntityLIF.next())
	{
		Asura_Guid uGuid;
		Asura_Entity_Server* pxEntity;

		const Asura_Chunk_Entity* pxEntityChunk = xEntityLIF()->m_pxEntityChunk;
		const Asura_Chunk_Entity_ToolsData* pxToolsChunk = xEntityLIF()->m_pxToolsChunk;

		while(true)
		{
			//get the guid to use for the new entity
			uGuid = Tools_EntityTemplate_System::GetNextEntityGuid();
			
			//make sure this doesn't already exist
			pxEntity = Asura_Entity_Server::FindEntity(uGuid);
			Asura_Assert(!pxEntity,"Guid already used??");
			if(!pxEntity)
			{
				break;	
			}
		}
		xGuidList.add_entry(uGuid);

		//make a copy of the chunk header , with the new guid
		Asura_Chunk_Entity xHeader = *pxEntityChunk;
		xHeader.Guid = uGuid;

		//create the entity
		xHeader.Process((const char*)(pxEntityChunk+1));

		//make sure the entity does exist now
		pxEntity = Asura_Entity_Server::FindEntity(uGuid);
		Asura_Assertf(pxEntity,("Failed to create entity in template '%s'",m_pszName));
		if(!pxEntity)
		{
			//return the guid that we were going to use
			Tools_EntityTemplate_System::RecycleEntityGuid(uGuid);
			xGuidList.delete_entry(uGuid);

			//also take note of the bad entry , so we can fix them in just a sec...
			xBadEntityList.add_entry(pxEntityChunk->Guid);

			continue;
		}
		
		xEntityList.add_entry(pxEntity);


		//now do the tools data
		if(pxToolsChunk)
		{
			Asura_Chunk_Entity_ToolsData xToolsHeader = *pxToolsChunk;
			xToolsHeader.Guid = uGuid;

			xToolsHeader.Process((const char*)(pxToolsChunk+1));

		}

		//make sure the entity is allowed in a template (typically this should be caught before
		//the template file is saved in the first place)
		if(!pxEntity->CanBeIncludedInTemplate())
		{
			Asura_Assertf(0,("Entity %s in template %s isn't useable in entity templates.",pxEntity->ToolsData->Name.GetString(),m_pszName));

			//delete the dud entity ,and release the guid used
			pxEntity->SetAsDestroyed();
			Tools_EntityTemplate_System::RecycleEntityGuid(uGuid);

			xGuidList.delete_entry(uGuid);
			xEntityList.delete_entry(pxEntity);

			//also take note of the bad entry , so we can fix them in just a sec...
			xBadEntityList.add_entry(pxEntityChunk->Guid);
		}
		else
		{
			if(pxEntity->ToolsData && pxInstance->m_pxEntity->ToolsData)
			{
				//add the name of the template instance to the name of this object
				char szName[80];
				Asura_PrintfToBfr(szName,"%s->%s",80,true,pxInstance->m_pxEntity->ToolsData->Name.GetString(),pxEntity->ToolsData->Name.GetString());
				pxEntity->ToolsData->Name = szName;

				//clear the entity browser path for tidiness , since this won't show up in that
				//overlay anyway. (and if it has anything it should probably be the path of the template instance)
				pxEntity->ToolsData->m_xEntityBrowserPath.MakeNull();
			}
		}

    }

	if(xBadEntityList.size())
	{
		//remove the bad entities from the various lists and arrays
		for(xEntityLIF.restart();!xEntityLIF.done();)
		{
			if(xBadEntityList.contains(xEntityLIF()->m_pxEntityChunk->Guid))
			{
				delete xEntityLIF();
				xEntityLIF.delete_current();
			}
			else
			{
				xEntityLIF.next();
			}
		}

		int iNumGuids = 0;
		for(int i=0;i<m_iNumGuids;i++)
		{
			if(!xBadEntityList.contains(m_puGuids[i]))
			{
				m_puGuids[iNumGuids++] = m_puGuids[i];
			}
		}
		m_iNumGuids = iNumGuids;
		
		//make sure all lists are the size they should be
		#if defined ASURA_ASSERT
			int iSize = m_iNumGuids-1;
		#endif

		Asura_Assert(iSize == xGuidList.size(),"");
		Asura_Assert(iSize == xEntityList.size(),"");
		Asura_Assert(iSize == m_xEntityList.size(),"");
	}

	pxInstance->SetIDList(Tools_EntityTemplate_ChildType_Entity,xGuidList);

	//adjust the positions etc for all the entities
	for(LIF<Asura_Entity*> xEntityLIF(&xEntityList);!xEntityLIF.done();xEntityLIF.next())
	{
		if(xEntityLIF()->ToolsData)
		{
			//update the position in the tools data
			xEntityLIF()->ToolsData->Position = pxInstance->TransformPosition(xEntityLIF()->ToolsData->Position);
			xEntityLIF()->ToolsData->m_xOrientation = pxInstance->TransformMatrix(xEntityLIF()->ToolsData->m_xOrientation);
			xEntityLIF()->ToolsData->m_uParentTemplate = pxInstance->GetTemplateGuid();

			//combine platform and light set settings
			xEntityLIF()->ToolsData->m_uPlatforms &= pxInstance->GetTemplatePlatforms();
			xEntityLIF()->ToolsData->m_uLightSets &= pxInstance->GetTemplateLightSets();
		}		
		//update the entity
		xEntityLIF()->ApplyChangesForTemplateInstance(pxInstance);

		if(pxInstance->GetReplacementSkinID())
		{
			//Apply the replacement skin to any physical objects or child templates
			#if 0
			if(xEntityLIF()->GetClassification() == AsuraEntityClass_Template)
			{
				Asura_ServerEntity_Template* pxTemplateEntity = (Asura_ServerEntity_Template*) xEntityLIF();
				pxTemplateEntity->SetReplacementSkinID(pxInstance->GetReplacementSkinID());
			}
			else
			#endif
			{
				Asura_ServerEntity_PhysicalObject* pxPhysEntity = Asura_ServerEntity_PhysicalObject::FindEntity(xEntityLIF()->GetGuid());
				if(pxPhysEntity && pxPhysEntity->GetSkinID())
				{
					pxPhysEntity->SetSkinAndAnimID(pxInstance->GetReplacementSkinID(), pxPhysEntity->GetAnimID());
					pxPhysEntity->SetSkinAndAnimFileID(0, pxPhysEntity->GetAnimFileID());
				}
			}
		}
		if(pxInstance->ShouldMergeWithEnvironment())
		{
			//set the merge with env flag if this is a physics object
			if(xEntityLIF()->GetClassification() == AsuraEntityClass_Template)
			{
				Asura_ServerEntity_Template* pxTemplateEntity = (Asura_ServerEntity_Template*) xEntityLIF();
				pxTemplateEntity->SetMergeWithEnvironment(true);
			}
			else if(xEntityLIF()->GetClassification() == AsuraEntityClass_PhysicsObject)
			{
				Asura_ServerEntity_PhysicalObject* pxPhysEntity = Asura_ServerEntity_PhysicalObject::FindEntity(xEntityLIF()->GetGuid());
				if(pxPhysEntity && pxPhysEntity->HasShapeOrSkin())
				{
					u_int uFlags = pxPhysEntity->GetPhysicalObjectFlags() | ASURA_PHYSICALOBJECT_FLAG_SHOULDMERGEWITHENV;
					pxPhysEntity->SetPhysicalObjectFlags(uFlags);
				}
			}

		}
		

        xEntityLIF()->InitFromBlueprint();

		//Check for a client entity. 
		//In some cases these need updating as well (particle generators)
		Asura_Entity_Client* pxClientEntity = Asura_Entity_Client::FindEntity(xEntityLIF()->GetGuid());
		if(pxClientEntity)
		{
			pxClientEntity->ApplyChangesForTemplateInstance(pxInstance);
		}
	}


	Tools_ShapeTable::BuildShapeTable();

	//create tools objects for all the entities
	for(LIF<Asura_Entity*> xEntityLIF(&xEntityList);!xEntityLIF.done();xEntityLIF.next())
	{
		Tools_Entity_Object::CreateObject(xEntityLIF());
	}

	//update inputs and outputs in the template entity
	pxInstance->UpdateInputsAndOutputs(m_pxTemplateInfo);

	//update the version in the template instance
	pxInstance->UpdateVersion(GetVersion());

}

void Tools_EntityTemplate::CreatePhonons(class Tools_EntityTemplateInstanceMain* pxInstance)
{
	//make sure there are some phonons that need creating
	const Tools_EntityTemplate_ChildIDArray* pxArray = m_pxTemplateInfo->m_xChildIDHandler.GetArray(Tools_EntityTemplate_ChildType_Phonon);
	if(!m_pxPhononChunk) return;
	if(!pxArray || !pxArray->m_iNumIDs) return;

	List<Asura_Guid> xGuidList;
	
	const bool bMissionFile = Tools_SaveAsura::IsEditingMission();

	for(int i=0;i<pxArray->m_iNumIDs;i++)
	{
		//get the next guid to allocate
		Asura_Guid uGuid = Tools_EntityTemplate_System::GetNextSoundGuid(bMissionFile);

		//attempt to create the phonon
		Asura_Phonon* pxPhonon = m_pxPhononChunk->ProcessSinglePhonon((const char*)(m_pxPhononChunk+1),pxArray->m_puIDs[i],uGuid,bMissionFile);
		if(!pxPhonon)
		{
			//failed to create phonon , so return the guid
			Tools_EntityTemplate_System::RecycleSoundGuid(uGuid);
			xGuidList.add_entry(ASURA_GUID_UNREGISTERED);
			continue;
		}

		//Reposition the phonon , and update the sound IDs
		pxPhonon->SetTemplateGuid(pxInstance->m_pxEntity->GetGuid());
		pxPhonon->ApplyChangesForTemplateInstance(pxInstance);

		//combine light set settings
		pxPhonon->SetLightSets(pxPhonon->GetLightSets() & pxInstance->GetTemplateLightSets());


		xGuidList.add_entry(pxPhonon->GetGuid());

		//create a tools object for the new phonon
		new Tools_Phonon_Object(pxPhonon);

		if(bMissionFile)
			Tools_SaveAsura::SetMissionSoundsModified();
		else
			Tools_SaveAsura::SetSoundsModified();
	}

	//update the store of sound guids in the instance
	pxInstance->SetIDList(Tools_EntityTemplate_ChildType_Phonon,xGuidList);
}

void Tools_EntityTemplate::CreateLights(class Tools_EntityTemplateInstanceMain* pxInstance)
{
	//make sure there are some lights that need creating
	const Tools_EntityTemplate_ChildIDArray* pxArray = m_pxTemplateInfo->m_xChildIDHandler.GetArray(Tools_EntityTemplate_ChildType_Light);
	if(!m_pxLightChunk) return;
	if(!m_pxLightChunk->GetNumberOfLights()) return;
	if(!pxArray || !pxArray->m_iNumIDs) return;

	if(Tools_SaveAsura::IsEditingMission() && !Tools_SaveAsura::IsUsingMissionGlobalIllumination())
	{
		Asura_Assertf(0, ("Template '%s' contains lights.\nThis doesn't work when not using the mission .GI file.",m_pszName));
		return;
	}


	List<u_int> xIDList;

	for(int i=0;i<pxArray->m_iNumIDs;i++)
	{
		//get the next ID to allocate
		u_int uID = Tools_EntityTemplate_System::GetNextLightID();

		//attempt to create the light
		Asura_Handle iHandle = m_pxLightChunk->ImportFromChunkBuffer((const char*)(m_pxLightChunk+1),pxArray->m_puIDs[i],uID);
		if(iHandle == ASURA_INVALID_HANDLE)
		{
			//failed to create light , so return the guid
			Tools_EntityTemplate_System::RecycleLightID(uID);
			continue;
		}

		Tools_PhotonMapping_TemplateData* pxLightTemplate = Tools_PhotonMapping_System::GetTemplateData(iHandle);
		pxLightTemplate->m_uTemplateGuid = pxInstance->m_pxEntity->GetGuid();
		pxLightTemplate->m_iTemplateVersion = GetVersion();
		if(Tools_SaveAsura::IsEditingMission())
		{

			pxLightTemplate->m_uID |= TOOLS_PHOTONMAPPING_MISSIONLIGHT;
		}


		//Reposition the light
		Tools_PhotonMapping_LightSource* pxLightSource = Tools_PhotonMapping_System::GetLightSource(iHandle);
		pxLightSource->ApplyChangesForTemplateInstance(pxInstance);

		//combine light set settings
		u_int uLightSets = pxLightSource->GetLightSets() & pxInstance->GetTemplateLightSets();
		pxLightSource->SetLightSets(uLightSets);


		//create a tools object for the new light , if needed
		if(Tools_PhotonMapping_LightSource_Object::HaveLightSourceObjectsBeenCreated())
		{
			new Tools_PhotonMapping_LightSource_Object(iHandle);
		}

		if(Tools_SaveAsura::IsEditingMission())
			Tools_SaveAsura::SetMissionGlobalIlluminationModified();
		else
			Tools_SaveAsura::SetGlobalIlluminationModified();

		xIDList.add_entry(pxLightTemplate->m_uID);
	}

	//update the store of light ids in the instance
	pxInstance->SetIDList(Tools_EntityTemplate_ChildType_Light,xIDList);

}

void Tools_EntityTemplate::CreateCoronas(class Tools_EntityTemplateInstanceMain* pxInstance)
{
	//make sure there are some coronas that need creating
	const Tools_EntityTemplate_ChildIDArray* pxArray = m_pxTemplateInfo->m_xChildIDHandler.GetArray(Tools_EntityTemplate_ChildType_Corona);
	if(!m_pxCoronaChunk || !m_pxCoronaTextureChunk) return;
	if(!pxArray || !pxArray->m_iNumIDs) return;

	if(Tools_SaveAsura::IsEditingMission() && !Tools_SaveAsura::IsUsingMissionLighting())
	{
		Asura_Assertf(0, ("Template '%s' contains coronas.\nThis doesn't work when not using the mission .LIT file.",m_pszName));
		return;
	}


	//process the texture chunk , so that the conversion array is setup
	m_pxCoronaTextureChunk->Process((const char*)(m_pxCoronaTextureChunk+1));


	List<u_int> xIDList;

	for(int i=0;i<pxArray->m_iNumIDs;i++)
	{
		//get the next ID to allocate
		u_int uID = Tools_EntityTemplate_System::GetNextCoronaID();

		//attempt to create the light
		int iIndex = m_pxCoronaChunk->ImportFromChunkBuffer((const char*)(m_pxCoronaChunk+1),pxArray->m_puIDs[i],uID);
		if(iIndex < 0)
		{
			//failed to create light , so return the guid
			Tools_EntityTemplate_System::RecycleCoronaID(uID);
			continue;
		}

		Asura_StaticCorona* pxCorona = Asura_StaticCorona_System::GetCorona(iIndex);
		pxCorona->m_uTemplateGuid = pxInstance->m_pxEntity->GetGuid();
		pxCorona->m_iTemplateVersion = GetVersion();
		if(Tools_SaveAsura::IsEditingMission())
		{
			pxCorona->m_uID |= TOOLS_CORONA_MISSIONCORONA;
		}

		//Reposition the corona
		pxCorona->ApplyChangesForTemplateInstance(pxInstance);

		//combine light set settings
		pxCorona->m_uLightSets = pxCorona->m_uLightSets & pxInstance->GetTemplateLightSets();



		new Tools_Corona_Object(iIndex);

		if(Tools_SaveAsura::IsEditingMission())
			Tools_SaveAsura::SetMissionLightingModified();
		else
			Tools_SaveAsura::SetLightingModified();

		xIDList.add_entry(pxCorona->m_uID);
	}

	//update the store of corona ids in the instance
	pxInstance->SetIDList(Tools_EntityTemplate_ChildType_Corona,xIDList);

}

#ifdef ASURA_USE_ATTRACTORS
void Tools_EntityTemplate::CreateAttractors(class Tools_EntityTemplateInstanceMain* pxInstance)
{
	//make sure there are some attractors that need creating
	Tools_EntityTemplate_ChildIDArray* pxArray = m_pxTemplateInfo->m_xChildIDHandler.GetArray(Tools_EntityTemplate_ChildType_Attractor);
	if(!m_pxAttractorChunk) return;
	if(!pxArray || !pxArray->m_iNumIDs) return;

	List<u_int> xIDList;

	for(int i=0;i<pxArray->m_iNumIDs;i++)
	{
		//attempt to create the attractor
		Tools_Attractor* pxAttractor = m_pxAttractorChunk->ImportFromChunkBuffer((const char*)(m_pxAttractorChunk+1),pxArray->m_puIDs[i]);
		if(!pxAttractor)
		{
			//not really expecting to fail on these
			Asura_Assert(0,"Failed to import attractor from template.");
			//Remove the ID from the template , otherwise the conversion process will be messed up
			pxArray->m_iNumIDs--;
			pxArray->m_puIDs[i] = pxArray->m_puIDs[pxArray->m_iNumIDs];
			i--;
			continue;
		}
        		
		pxAttractor->SetTemplateGuid(pxInstance->m_pxEntity->GetGuid());
		pxAttractor->SetTemplateVersion(GetVersion());
		pxAttractor->SetModified();

		pxInstance->m_xAttractorList.add_entry(pxAttractor);
		xIDList.add_entry(pxAttractor->GetID());

	}

	//update the store of attractor ids in the instance
	pxInstance->SetIDList(Tools_EntityTemplate_ChildType_Attractor,xIDList);

}

void Tools_EntityTemplate::UpdateAttractors(class Tools_EntityTemplateInstanceMain* pxInstance)
{
	
	//Repostition and convert IDs etc
	for(LIF<Tools_Attractor*>  xAttractorLIF(&pxInstance->m_xAttractorList); !xAttractorLIF.done(); xAttractorLIF.next())
	{
		xAttractorLIF()->ApplyChangesForTemplateInstance(pxInstance);
	}

	//create tools objects if they are supposed to be existing
	if(Tools_Object_Attractor_Node::GetToolsObjectsShouldExist())
	{
		for(LIF<Tools_Attractor*>  xAttractorLIF(&pxInstance->m_xAttractorList); !xAttractorLIF.done(); xAttractorLIF.next())
		{
			Tools_Attractor_Node* pxNode = xAttractorLIF()->GetFirstNode();

			while(pxNode)
			{
				new Tools_Object_Attractor_Node(pxNode);
				pxNode = pxNode->GetNext();
			}
		}
	}
}
#endif //ASURA_USE_ATTRACTORS

void Tools_EntityTemplate::CreateStreamingSounds(class Tools_EntityTemplateInstanceMain* pxInstance)
{
	//make sure there are some sounds that need creating
	const Tools_EntityTemplate_ChildIDArray* pxArray = m_pxTemplateInfo->m_xChildIDHandler.GetArray(Tools_EntityTemplate_ChildType_StreamingSound);
	if(!m_pxStreamingSoundChunk) return;
	if(!pxArray || !pxArray->m_iNumIDs) return;


	List<u_int> xIDList;

	for(int i=0;i<pxArray->m_iNumIDs;i++)
	{
		//get the next ID to allocate
		u_int uID = Tools_EntityTemplate_System::GetNextStreamingSoundID();

		//attempt to create the sound
		int iIndex = m_pxStreamingSoundChunk->ImportFromChunkBuffer((const char*)(m_pxStreamingSoundChunk+1),pxArray->m_puIDs[i],uID);
		if(iIndex == -1)
		{
			//failed to create sound , so return the guid
			Tools_EntityTemplate_System::RecycleStreamingSoundID(uID);
			continue;
		}

		Asura_StreamingBackgroundSound* pxSound = Asura_StreamingBackgroundSound_System::GetSound(iIndex);
		pxSound->m_uTemplateGuid = pxInstance->m_pxEntity->GetGuid();
		pxSound->m_iTemplateVersion = GetVersion();


		Tools_SaveAsura::SetSoundsModified();

		xIDList.add_entry(pxSound->m_uID);
	}

	//update the store of streaming sound ids in the instance
	pxInstance->SetIDList(Tools_EntityTemplate_ChildType_StreamingSound,xIDList);

}

void Tools_EntityTemplate::UpdateStreamingSounds(class Tools_EntityTemplateInstanceMain* pxInstance)
{
	const Tools_EntityTemplate_ChildIDArray* pxArray = pxInstance->GetChildIDArray(Tools_EntityTemplate_ChildType_StreamingSound);
	if(!pxArray || !pxArray->m_iNumIDs) return;

	for(int i=0;i<pxArray->m_iNumIDs;i++)
	{
		int iIndex = Asura_StreamingBackgroundSound_System::GetSoundIndexFromID(pxArray->m_puIDs[i]);
		if(iIndex>=0)
		{
			Asura_StreamingBackgroundSound* pxSound = Asura_StreamingBackgroundSound_System::GetSound(iIndex);
			Asura_Assert(pxSound, "");

			//Reposition the sound
			pxSound->ApplyChangesForTemplateInstance(pxInstance);

			//combine light set settings
			u_int uLightSets = pxSound->m_uLightSets & pxInstance->GetTemplateLightSets();
			pxSound->m_uLightSets = uLightSets;


			//create a tools object for the new sound , if needed
			if(Tools_StreamingBackgroundSound_Object::DoToolsObjectsExist())
			{
				new Tools_StreamingBackgroundSound_Object(iIndex);
			}

		}
	}
}




u_short Tools_EntityTemplate::ConvertMessageBlock(u_short usBlockID) const
{
	if(!m_pxMessageChunk) return ASURA_STATICMESSAGE_EMPTYBLOCK;
	return m_pxMessageChunk->ProcessSingleMessageBlock((const char*)(m_pxMessageChunk+1),usBlockID);
}

int Tools_EntityTemplate::GetIndexFromGuid(Asura_Guid uGuid)
{
	for(int i=0;i<m_iNumGuids;i++)
	{
		if(m_puGuids[i] == uGuid)
		{
			return i;
		}
	}
	return -1;
}

int Tools_EntityTemplate::GetIndexFromID(enum Tools_EntityTemplate_ChildType eType,u_int uID)
{
	const Tools_EntityTemplate_ChildIDArray* pxArray = m_pxTemplateInfo->m_xChildIDHandler.GetArray(eType);
	if(!pxArray) return -1;

	for(int i=0;i<pxArray->m_iNumIDs;i++)
	{
		if(pxArray->m_puIDs[i] == uID)
		{
			return i;
		}
	}
	return -1;
}


int Tools_EntityTemplate::ConvertSoundID(int iSoundID)
{
#if defined ASURA_USE_AUDIO_SYSTEM

	// AUDIOTODO: this function simply doesn't need to exist with the new audio system
	return iSoundID;

#else

	if(!m_xSoundChunkList.size()) return ASURA_SID_NO_SOUND;

	//if not an entity sound ID , then no conversion is needed
	bool bEntity = iSoundID >= ASURA_SID_ENTITY_MIN && iSoundID <= ASURA_SID_ENTITY_MAX;
	bool bEnv = iSoundID >= ASURA_SID_ENVIRONMENT_00 && iSoundID <= ASURA_SID_ENVIRONMENT_MAX;
	if(!bEntity && !bEnv) return iSoundID;

	Asura_Sound_ID eMin = bEntity ? ASURA_SID_ENTITY_MIN : ASURA_SID_ENVIRONMENT_00;
	Asura_Sound_ID eMax = bEntity ? ASURA_SID_ENTITY_MAX : ASURA_SID_ENVIRONMENT_MAX;
	
	for(CLIF<const Asura_Chunk_SoundNames*> xChunkLIF(&m_xSoundChunkList);!xChunkLIF.done();xChunkLIF.next())
	{
		const char* pszFilename = xChunkLIF()->GetFilename((const char*)(xChunkLIF()+1),iSoundID);

		if(pszFilename)
		{
			return Tools_PickSound::GetSoundIDForFilename(pszFilename,eMin,eMax);
		}
		
	}
	return ASURA_SID_NO_SOUND;

#endif
}


bool Tools_EntityTemplate::GetDependencies(List<const char*>& xNameList)
{
	//scan through the child entity list for any templates
	for(CLIF<Tools_EntityChunkPair*> xEntityLIF(&m_xEntityList);!xEntityLIF.done();xEntityLIF.next())
	{
		const Asura_Chunk_Entity* pxEntity = xEntityLIF()->m_pxEntityChunk;
		if(pxEntity->Classification != AsuraEntityClass_Template) continue;

		//get the template instance's name
		Tools_EntityTemplateInfo xInfo((const char*)(pxEntity+1));
		if(!xInfo.m_bValid)
		{
			return false;
		}

		//add to the list , if it hasn't already been done
		bool bAdd =true;
		for(LIF<const char*> xNameLIF(&xNameList);!xNameLIF.done();xNameLIF.next())
		{
			if(!stricmp(xNameLIF(),xInfo.m_xName.GetString()))
			{
				bAdd = false;
				break;
			}
		}
		if(bAdd)
		{
			xNameList.add_entry(xInfo.m_xName.GetString());
			//need to check this template's dependencies as well
			Tools_EntityTemplate* pxTemplate = Tools_EntityTemplate_System::GetTemplate(xInfo.m_xName.GetString());
			if(!pxTemplate) return false;

			pxTemplate->GetDependencies(xNameList);
		}
	}
	return true;
}

const Asura_Vector_3& Tools_EntityTemplate::GetPosition() const
{
	return m_pxTemplateInfo->m_xPosition;
}

const Asura_Matrix_3x3& Tools_EntityTemplate::GetOrient() const
{
	return m_pxTemplateInfo->m_xOrient;
}

int Tools_EntityTemplate::GetVersion() const 
{
	return m_pxTemplateInfo->m_iTemplateVersion;
}


///////////////////////////////////////////////////////////////////////////////////////////////
/*------------------------------**
** Tools_EntityTemplateInstance **
**------------------------------*/

Asura_Vector_3 Tools_EntityTemplateInstance::TransformPosition(const Asura_Vector_3& xPosition) const
{
	if(ShouldUseRelativeMatrix())
		return (xPosition * GetRelativeMatrix()) + GetRelativePosition();
	else
		return xPosition + GetRelativePosition();
}

Asura_Matrix_3x3 Tools_EntityTemplateInstance::TransformMatrix(const Asura_Matrix_3x3& xMat) const
{
	if(!ShouldUseRelativeMatrix()) return xMat;

	return (GetRelativeMatrix()*xMat);
}

Asura_Quat Tools_EntityTemplateInstance::TransformQuat(const Asura_Quat& xQuat) const
{
	if(!ShouldUseRelativeMatrix()) return xQuat;
	
	Asura_Quat xRelQuat;
	xRelQuat.FromMatrix(GetRelativeMatrix());

	return xRelQuat*xQuat;
}

Asura_Euler_3 Tools_EntityTemplateInstance::TransformEuler(const Asura_Euler_3 xEuler) const
{
	if(!ShouldUseRelativeMatrix()) return xEuler;

	Asura_Matrix_3x3 xTempMatrix;
	xEuler.ToMatrix(xTempMatrix);
	xTempMatrix = TransformMatrix(xTempMatrix);
	Asura_Euler_3 xRetEuler;
	xRetEuler.FromMatrix(xTempMatrix);
	return xRetEuler;
}


Asura_Bounding_Box Tools_EntityTemplateInstance::TransformBoundingBox(const Asura_Bounding_Box& xBox) const
{
	Asura_Bounding_Box xRetBox;
	if(ShouldUseRelativeMatrix())
	{
		Asura_Vector_3 xMin(xBox.MinX,xBox.MinY,xBox.MinZ);
		Asura_Vector_3 xMax(xBox.MaxX,xBox.MaxY,xBox.MaxZ);

		Asura_Vector_3 xCentre = (xMin+xMax) / 2.0f;
		xMin -= xCentre;
		xMax -= xCentre;

		//box has to be axially aligned , so get rotation matrix snapped to nearest axes
		Asura_Matrix_3x3 xMat = GetRelativeMatrix().GetAxiallyAlignedMatrix();

		xMin = (xMin * xMat);
		xMax = (xMax * xMat);

		xCentre = (xCentre * GetRelativeMatrix()) + GetRelativePosition();

		xMin += xCentre;
		xMax += xCentre;

		xRetBox.SetToPoint(xMin);
		xRetBox.Set(xMax);
	}
	else
	{
		xRetBox = xBox;
		xRetBox.Displace(GetRelativePosition())	;
	}

	return xRetBox;
}

Asura_OrientedVolume Tools_EntityTemplateInstance::TransformOrientedVolume( const Asura_OrientedVolume& xVolume ) const
{
    Asura_OrientedVolume xTransformedVolume;

    Asura_Vector_3 xCentre = xVolume.m_xBox.CalculateCentre();
    Asura_Vector_3 xNewCentre = TransformPosition( xCentre );
    xTransformedVolume.m_xBox = xVolume.m_xBox;
    xTransformedVolume.m_xBox.Displace( xNewCentre - xCentre );

    xTransformedVolume.m_xOrient = TransformQuat( xVolume.m_xOrient );

    return xTransformedVolume;
}



///////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------------**
** Tools_EntityTemplateInstanceMain **
**----------------------------------*/

Tools_EntityTemplateInstanceMain::Tools_EntityTemplateInstanceMain(Asura_ServerEntity_Template* pxEntity,Tools_EntityTemplate* pxTemplate)
{
	m_pxEntity = pxEntity;
	m_pxTemplate = pxTemplate;


	//calculate the relative position
	Asura_Vector_3 xTempPos = pxTemplate->GetPosition();
	Asura_Matrix_3x3 xTempMat = pxTemplate->GetOrient();
	xTempMat.Transpose();

	Asura_Vector_3 xInstancePos = pxEntity->m_xPosition;
	Asura_Matrix_3x3 xInstanceMat = pxEntity->m_xOrient;

	m_xRelOrient = xInstanceMat*xTempMat;
	m_xRelPos = xInstancePos - xTempPos*m_xRelOrient;
}


Tools_EntityTemplate_ChildIDArray* Tools_EntityTemplateInstanceMain::GetChildIDArray(Tools_EntityTemplate_ChildType eType)
{
	return m_pxEntity->m_xChildIDHandler.GetArray(eType);
}


void Tools_EntityTemplateInstanceMain::SetIDList(Tools_EntityTemplate_ChildType eType,const List<Asura_Guid>& xGuidList)
{
	m_pxEntity->m_xChildIDHandler.SetIDList(eType,xGuidList);
}



u_short Tools_EntityTemplateInstanceMain::ConvertMessageBlock(u_short usBlockID) const
{
	if(usBlockID == ASURA_STATICMESSAGE_EMPTYBLOCK) return usBlockID;//empty blocks don't need converting

	usBlockID = m_pxTemplate->ConvertMessageBlock(usBlockID);
	if(usBlockID == ASURA_STATICMESSAGE_EMPTYBLOCK) return usBlockID;//failed to find in chunk , so leaving as empty is best we can do

	//now need to check the destinations for all the messages ,and remap the guids
	int iNumMessages = Asura_StaticMessage_System::GetNumberOfMessagesInBlock(usBlockID);
	for(int i=0;i<iNumMessages;i++)
	{
		Asura_StaticMessage* pxMessage = Asura_StaticMessage_System::GetMessagePointer(usBlockID,i);
		if(!pxMessage) continue;

		pxMessage->m_uSendTo = ConvertGuid(pxMessage->m_uSendTo);

		//deal with message specific guids
		Tools_EntityTemplate_System::GetThis()->ConvertStaticMessage(pxMessage,this);
	}
	return usBlockID;
}


Asura_Guid Tools_EntityTemplateInstanceMain::ConvertGuid(Asura_Guid uGuid) const
{
	if(uGuid == ASURA_GUID_UNREGISTERED) return uGuid;

	if(uGuid<ASURA_GUID_TOOLSCREATEDENTITY_START || uGuid>ASURA_GUID_TOOLSCREATEDENTITY_END)
	{
		//non-tools guids won't need converting
		return uGuid;
	}
	 


	//get an index number for the guid from the template
	int iIndex = m_pxTemplate->GetIndexFromGuid(uGuid);

	const Tools_EntityTemplate_ChildIDArray* pxArray = m_pxEntity->m_xChildIDHandler.GetArray(Tools_EntityTemplate_ChildType_Entity);

	int iNumIDs = pxArray ? pxArray->m_iNumIDs : 0;
	if(iIndex < 0 || iIndex > iNumIDs)
	{
		return ASURA_GUID_UNREGISTERED;
	}

	if(iIndex < iNumIDs)
	{
		return pxArray->m_puIDs[iIndex];
	}

	//else we want the guid of the template entity itself
	return m_pxEntity->Guid;
}


Asura_Guid Tools_EntityTemplateInstanceMain::ConvertSoundGuid(Asura_Guid uGuid) const
{
	if(uGuid == ASURA_GUID_UNREGISTERED) return uGuid;


	//get an index number for the guid from the template
	int iIndex = m_pxTemplate->GetIndexFromID(Tools_EntityTemplate_ChildType_Phonon,uGuid);

	const Tools_EntityTemplate_ChildIDArray* pxArray = m_pxEntity->m_xChildIDHandler.GetArray(Tools_EntityTemplate_ChildType_Phonon);
	if(!pxArray) return ASURA_GUID_UNREGISTERED;

	if(iIndex < 0 || iIndex >= pxArray->m_iNumIDs)
	{
		return ASURA_GUID_UNREGISTERED;
	}

	return pxArray->m_puIDs[iIndex];
}


int Tools_EntityTemplateInstanceMain::ConvertSoundID(int iSoundID) const
{
	#if defined ASURA_USE_AUDIO_SYSTEM

	// AUDIOTODO: this function simply doesn't need to exist with the new audio system
	return iSoundID;

	#else
	
	if(iSoundID == ASURA_SID_NO_SOUND) return iSoundID;
	return m_pxTemplate->ConvertSoundID(iSoundID);

	#endif
}

u_int Tools_EntityTemplateInstanceMain::ConvertAttractorID(u_int uAttractorID) const
{
	//ID 0 isn't changed
	if(!uAttractorID) return 0;

	const Tools_EntityTemplate_ChildIDArray* pxArray = m_pxEntity->m_xChildIDHandler.GetArray(Tools_EntityTemplate_ChildType_Attractor);
	if(!pxArray) return 0;

	int iIndex = m_pxTemplate->GetIndexFromID(Tools_EntityTemplate_ChildType_Attractor,uAttractorID);
	if(iIndex < 0 || iIndex >= pxArray->m_iNumIDs) return 0;

	return pxArray->m_puIDs[iIndex];
}

u_int Tools_EntityTemplateInstanceMain::ConvertStreamingSoundID(u_int uSoundID) const
{
	//ID 0 isn't changed
	if(!uSoundID) return 0;

	const Tools_EntityTemplate_ChildIDArray* pxArray = m_pxEntity->m_xChildIDHandler.GetArray(Tools_EntityTemplate_ChildType_StreamingSound);
	if(!pxArray) return 0;

	int iIndex = m_pxTemplate->GetIndexFromID(Tools_EntityTemplate_ChildType_StreamingSound,uSoundID);
	if(iIndex < 0 || iIndex >= pxArray->m_iNumIDs) return 0;

	return pxArray->m_puIDs[iIndex];
}

void Tools_EntityTemplateInstanceMain::UpdateVersion(int iVersion)
{
	m_pxEntity->m_iTemplateVersion = iVersion;
}

void Tools_EntityTemplateInstanceMain::UpdateInputsAndOutputs(const struct Tools_EntityTemplateInfo* pxTemplateInfo)
{
	m_pxEntity->UpdateInputsAndOutputs(this,pxTemplateInfo);
}



u_int Tools_EntityTemplateInstanceMain::GetTemplatePlatforms() const
{
	return m_pxEntity->ToolsData->m_uPlatforms;
}

u_int Tools_EntityTemplateInstanceMain::GetTemplateLightSets() const 
{
	return m_pxEntity->ToolsData->m_uLightSets;
}

Asura_Guid Tools_EntityTemplateInstanceMain::GetTemplateGuid() const 
{
	return m_pxEntity->GetGuid();
}



u_short Tools_EntityTemplateInstance_MessageBlocks::ConvertMessageBlock(u_short usBlockID) const
{
	return Asura_StaticMessage_System::CopyMessageBlock(usBlockID,m_uFromGuid,m_uToGuid);
}

Asura_Hash_ID Tools_EntityTemplateInstanceMain::GetReplacementSkinID() const
{
	return m_pxEntity->m_uReplacementSkinID;
}

bool Tools_EntityTemplateInstanceMain::ShouldMergeWithEnvironment() const
{
	return m_pxEntity->m_bMergeWithEnvironment;
}