#include "../../Scene/Base/RuEntityACT.h"

#include "../../Scene/Controller/RuController_AnimCompensator.h"
#include "../../Scene/Controller/RuController_Entity.h"
#include "../../Scene/Controller/RuController_EventTrigger.h"
#include "../../Scene/Controller/RuController_GlobalAlpha.h"
#include "../../Scene/Controller/RuController_GlobalColor.h"
#include "../../Scene/Controller/RuController_Hierarchy.h"
#include "../../Scene/Controller/RuController_Morph.h"
#include "../../Scene/Controller/RuController_ParticleSystem.h"

#include "../../Helper/RuHelper_Entity.h"

#pragma unmanaged

// ************************************************************************************************************************************************************

ruRTTI_DEFINE_SUBCLASS(CRuACTTemplateNode, IRuObject)
ruHEAP_DEFINE_SUBCLASS(CRuACTTemplateNode, "CRuACTTemplateNode", "IRuObject")
ruCLASSGUID_DEFINE(CRuACTTemplateNode, 0x0EE56AE9, 0x55B14f4e, 0xAF22A1EF, 0xC8D1FB7B)

ruRTTI_DEFINE_SUBCLASS(CRuACTTemplateModifier, IRuObject)
ruHEAP_DEFINE_SUBCLASS(CRuACTTemplateModifier, "CRuACTTemplateModifier", "IRuObject")
ruCLASSGUID_DEFINE(CRuACTTemplateModifier, 0xABE45A8B, 0xE5174429, 0x8AB9DC91, 0x7926B293)

ruRTTI_DEFINE_SUBCLASS(CRuACTMotionNode, IRuObject)
ruHEAP_DEFINE_SUBCLASS(CRuACTMotionNode, "CRuACTMotionNode", "IRuObject")
ruCLASSGUID_DEFINE(CRuACTMotionNode, 0xC6CB0FBD, 0xFE6D479c, 0x9800EFC7, 0x64894CB7)

ruRTTI_DEFINE_SUBCLASS(CRuACTMotion, IRuObject)
ruHEAP_DEFINE_SUBCLASS(CRuACTMotion, "CRuACTMotion", "IRuObject")
ruCLASSGUID_DEFINE(CRuACTMotion, 0x1AAAE4D8, 0x06A34279, 0xA0EC4755, 0xF7D79787)

ruRTTI_DEFINE_SUBCLASS(CRuACTTemplate, IRuObject)
ruHEAP_DEFINE_SUBCLASS(CRuACTTemplate, "CRuACTTemplate", "IRuObject")
ruCLASSGUID_DEFINE(CRuACTTemplate, 0xE743F431, 0x29EC4748, 0xB3A626CC, 0xF291DEB3)

ruRTTI_DEFINE_SUBCLASS(CRuACTEntity, CRuEntity_Container_Impl)
ruHEAP_DEFINE_SUBCLASS(CRuACTEntity, "CRuACTEntity", "CRuEntity_Container_Impl")
ruCLASSGUID_DEFINE(CRuACTEntity, 0xF003E140, 0xF1C24f03, 0xACCC780D, 0x01190100)

// ************************************************************************************************************************************************************

// Used for motion sorting function
static int StrCompare(const void *str0, const void *str1)
{
	return strcmp(* ( char** ) str0, * ( char** ) str1);
}

// ************************************************************************************************************************************************************

CRuACTTemplateNode_Runtime::CRuACTTemplateNode_Runtime()
:	m_sourceTemplateNode(NULL),
	m_numEntities(0),
	m_entities(NULL)
{
}

CRuACTTemplateNode_Runtime::~CRuACTTemplateNode_Runtime()
{
	ruSAFE_RELEASE(m_sourceTemplateNode);

	// Release references to the root entity (This also releases all child entities)
	if(m_numEntities > 0 && m_entities)
	{
		m_entities[0]->Release();
	}

	// Delete constructed entity array
	delete[] m_entities;

	m_numEntities = 0;
	m_entities = NULL;
}

RuACTTemplateNodeType CRuACTTemplateNode_Runtime::GetNodeType() const
{
	return m_sourceTemplateNode->GetNodeType();
}

const char *CRuACTTemplateNode_Runtime::GetName() const
{
	return m_sourceTemplateNode->GetName();
}

INT32 CRuACTTemplateNode_Runtime::GetParentIndex() const
{
	return m_sourceTemplateNode->GetParentIndex();
}

INT32 CRuACTTemplateNode_Runtime::GetConnectivityIndex() const
{
	return m_sourceTemplateNode->GetConnectivityIndex();
}

CRuACTTemplateNode *CRuACTTemplateNode_Runtime::GetTemplateNode()
{
	return m_sourceTemplateNode;
}

INT32 CRuACTTemplateNode_Runtime::IndexOf(CRuEntity *entity) const
{
	for(INT32 i = 0; i < m_numEntities; ++i)
	{
		if(m_entities[i] == entity)
		{
			return i;
		}
	}

	return -1;
}

INT32 CRuACTTemplateNode_Runtime::GetNumEntities()
{
	return m_numEntities;
}

CRuEntity *CRuACTTemplateNode_Runtime::GetRootEntity()
{
	if(m_entities)
	{
		return m_entities[0];
	}

	return NULL;
}

CRuEntity *CRuACTTemplateNode_Runtime::GetChildEntity(INT32 entityIndex)
{
	if(m_entities && entityIndex >= 0 && entityIndex < m_numEntities)
	{
		return m_entities[entityIndex];
	}

	return NULL;
}

BOOL CRuACTTemplateNode_Runtime::UpdateTemplateNode(BOOL replaceTemplateNodes)
{
	if(m_numEntities > 0 && m_entities)
	{
		if(replaceTemplateNodes)
		{
			m_sourceTemplateNode->UpdateEmbeddedEntity(m_entities[0]);
		}
		else
		{
/*
			// Decompose frame transform
			CRuVector3 scale, shear, rot, translation;
			CRuQuaternion rotation;

			m_entities[0]->GetLocalTransform().GetDecomposition(scale, shear, rot, translation);
			rotation.MakeEulerRotation(rot);
*/
			// Set orientation information into source template
			m_sourceTemplateNode->SetOrientation(m_entities[0]->GetScale(), m_entities[0]->GetRotation(), m_entities[0]->GetTranslation());
		}
	}

	return TRUE;
}

// ************************************************************************************************************************************************************

CRuACTTemplateNode::CRuACTTemplateNode()
:	m_nodeType(ruACTTEMPLATENODETYPE_DEFAULT),
	m_default_EntityName(NULL),
	m_embedded_Entity(NULL),
	m_embedded_ACT(NULL),
	m_parentIndex(-1),
	m_connectivityIndex(-1)
{
	m_frame_Scale.Set(1.0f, 1.0f, 1.0f);
	m_frame_Rotation.Set(0.0f, 0.0f, 0.0f, 1.0f);
	m_frame_Translation.Set(0.0f, 0.0f, 0.0f);
}

CRuACTTemplateNode::~CRuACTTemplateNode()
{
	// Clear old node data
	Clear();
}

IRuObject *CRuACTTemplateNode::Clone(IRuObject *clonedObject)
{
	CRuACTTemplateNode *clonedNode = ruNEW CRuACTTemplateNode();

	clonedNode->m_nodeType = m_nodeType;
	clonedNode->m_parentIndex = m_parentIndex;
	clonedNode->m_connectivityIndex = m_connectivityIndex;

	clonedNode->m_frame_Scale = m_frame_Scale;
	clonedNode->m_frame_Rotation = m_frame_Rotation;
	clonedNode->m_frame_Translation = m_frame_Translation;

	if(m_default_EntityName)
	{
		INT32 entityNameLength = strlen(m_default_EntityName) + 1;
		clonedNode->m_default_EntityName = ruNEW char [entityNameLength];
		memcpy(clonedNode->m_default_EntityName, m_default_EntityName, entityNameLength);
	}

	if(m_embedded_Entity)
	{
		clonedNode->m_embedded_Entity = static_cast<CRuEntity *>(m_embedded_Entity->Clone());
	}

	if(m_embedded_ACT)
	{
		clonedNode->m_embedded_ACT = static_cast<CRuACTTemplate *>(m_embedded_ACT->Clone());
	}

	return clonedNode;
}

BOOL CRuACTTemplateNode::SerializeFrom(IRuStream *stream)
{
	CRuGUID classGUID;

	// Read class GUID
	if(RuReadGUID(stream, classGUID) == FALSE)
	{
		return FALSE;
	}

	// Clear old node data
	Clear();

	// Read signature and version
	INT32 signature;
	INT32 version = 1;
	stream->Read(&signature, sizeof(INT32));

	if(signature == 0xFFFFFFFF)
	{
		// Signature found, read version 
		stream->Read(&version, sizeof(INT32));
	}
	else
	{
		// No signature, this is an old model stream, so rewind
		stream->Seek(-4, ruSSM_Current);
	}

	switch(version)
	{
		case 1:
			{
				// Read node type
				stream->Read(&m_nodeType, sizeof(m_nodeType));

				switch(m_nodeType)
				{
					case ruACTTEMPLATENODETYPE_FRAME:
						{
							// Read scale
							stream->Read(&m_frame_Scale.m_x, sizeof(REAL));
							stream->Read(&m_frame_Scale.m_y, sizeof(REAL));
							stream->Read(&m_frame_Scale.m_z, sizeof(REAL));

							// Read rotation
							stream->Read(&m_frame_Rotation.m_x, sizeof(REAL));
							stream->Read(&m_frame_Rotation.m_y, sizeof(REAL));
							stream->Read(&m_frame_Rotation.m_z, sizeof(REAL));
							stream->Read(&m_frame_Rotation.m_w, sizeof(REAL));

							// Read translation
							stream->Read(&m_frame_Translation.m_x, sizeof(REAL));
							stream->Read(&m_frame_Translation.m_y, sizeof(REAL));
							stream->Read(&m_frame_Translation.m_z, sizeof(REAL));
						}

						break;

					case ruACTTEMPLATENODETYPE_EMBEDDEDENTITY:
						{
							// Create object using the object factory
							IRuObject *entityObject = g_ruObjectFactory.CreateObject(stream);

							if(!entityObject || !entityObject->GetType().IsSubClassOf(CRuEntity::Type))
							{
								return FALSE;
							}

							// Assign embedded entity
							m_embedded_Entity = static_cast<CRuEntity *>(entityObject);
						}

						break;

					case ruACTTEMPLATENODETYPE_EMBEDDEDACT:
						{
							IRuObject *entityObject = g_ruObjectFactory.CreateObject(stream);

							if(!entityObject || !entityObject->GetType().IsSubClassOf(CRuACTTemplate::Type))
							{
								return FALSE;
							}

							// Assign embedded ACT template
							m_embedded_ACT = static_cast<CRuACTTemplate *>(entityObject);
						}

						break;

					case ruACTTEMPLATENODETYPE_DEFAULT:
					default:
						{
							// Read length of entity name including NULL followed by the name
							UINT32 nameLength;
							stream->Read(&nameLength, sizeof(nameLength));

							m_default_EntityName = ruNEW char [nameLength];
							stream->Read(m_default_EntityName, sizeof(char) * nameLength);

						}

						break;
				}

				// Read parent and connectivity indices
				stream->Read(&m_parentIndex, sizeof(m_parentIndex));
				stream->Read(&m_connectivityIndex, sizeof(m_connectivityIndex));
			}

			break;

		case 2:
			{
				// Read node type
				stream->Read(&m_nodeType, sizeof(m_nodeType));

				switch(m_nodeType)
				{
					case ruACTTEMPLATENODETYPE_FRAME:
						{
						}

						break;

					case ruACTTEMPLATENODETYPE_EMBEDDEDENTITY:
						{
							// Create object using the object factory
							IRuObject *entityObject = g_ruObjectFactory.CreateObject(stream);

							if(!entityObject || !entityObject->GetType().IsSubClassOf(CRuEntity::Type))
							{
								return FALSE;
							}

							// Assign embedded entity
							m_embedded_Entity = static_cast<CRuEntity *>(entityObject);
						}

						break;

					case ruACTTEMPLATENODETYPE_EMBEDDEDACT:
						{
							IRuObject *entityObject = g_ruObjectFactory.CreateObject(stream);

							if(!entityObject || !entityObject->GetType().IsSubClassOf(CRuACTTemplate::Type))
							{
								return FALSE;
							}

							// Assign embedded ACT template
							m_embedded_ACT = static_cast<CRuACTTemplate *>(entityObject);
						}

						break;

					case ruACTTEMPLATENODETYPE_DEFAULT:
					default:
						{
							// Read length of entity name including NULL followed by the name
							UINT32 nameLength;
							stream->Read(&nameLength, sizeof(nameLength));

							m_default_EntityName = ruNEW char [nameLength];
							stream->Read(m_default_EntityName, sizeof(char) * nameLength);

						}

						break;
				}

				// Read scale
				stream->Read(&m_frame_Scale.m_x, sizeof(REAL));
				stream->Read(&m_frame_Scale.m_y, sizeof(REAL));
				stream->Read(&m_frame_Scale.m_z, sizeof(REAL));

				// Read rotation
				stream->Read(&m_frame_Rotation.m_x, sizeof(REAL));
				stream->Read(&m_frame_Rotation.m_y, sizeof(REAL));
				stream->Read(&m_frame_Rotation.m_z, sizeof(REAL));
				stream->Read(&m_frame_Rotation.m_w, sizeof(REAL));

				// Read translation
				stream->Read(&m_frame_Translation.m_x, sizeof(REAL));
				stream->Read(&m_frame_Translation.m_y, sizeof(REAL));
				stream->Read(&m_frame_Translation.m_z, sizeof(REAL));

				// Read parent and connectivity indices
				stream->Read(&m_parentIndex, sizeof(m_parentIndex));
				stream->Read(&m_connectivityIndex, sizeof(m_connectivityIndex));
			}

			break;
	}

	return TRUE;
}

BOOL CRuACTTemplateNode::SerializeTo(IRuStream *stream)
{
	// Write class GUID
	stream->Write(GetClassGUID().Data(), GetClassGUID().Size());

	// Write signature
	INT32 signature = 0xFFFFFFFF;
	stream->Write(&signature, sizeof(INT32));

	// Write version
	INT32 version = 2;
	stream->Write(&version, sizeof(INT32));

	// Write node type
	stream->Write(&m_nodeType, sizeof(m_nodeType));

	switch(m_nodeType)
	{
		case ruACTTEMPLATENODETYPE_FRAME:
			{
			}

			break;

		case ruACTTEMPLATENODETYPE_EMBEDDEDENTITY:
			{
				// Write entity
				if(m_embedded_Entity)
				{
					m_embedded_Entity->SerializeTo(stream);
				}
				else
				{
					return FALSE;
				}
			}

			break;

		case ruACTTEMPLATENODETYPE_EMBEDDEDACT:
			{
				// Write ACT template
				if(m_embedded_ACT)
				{
					m_embedded_ACT->SerializeTo(stream);
				}
				else
				{
					return FALSE;
				}
			}

			break;

		case ruACTTEMPLATENODETYPE_DEFAULT:
		default:
			{
				// Write length of entity name including NULL followed by the name
				if(m_default_EntityName)
				{
					UINT32 nameLength = strlen(m_default_EntityName) + 1;

					stream->Write(&nameLength, sizeof(nameLength));
					stream->Write(m_default_EntityName, sizeof(char) * nameLength);
				}
				else
				{
					UINT32 nameLength = 1;
					char *dummyName = "";

					stream->Write(&nameLength, sizeof(nameLength));
					stream->Write(dummyName, sizeof(char) * nameLength);
				}
			}

			break;
	}

	// Write scale
	stream->Write(&m_frame_Scale.m_x, sizeof(REAL));
	stream->Write(&m_frame_Scale.m_y, sizeof(REAL));
	stream->Write(&m_frame_Scale.m_z, sizeof(REAL));

	// Write rotation
	stream->Write(&m_frame_Rotation.m_x, sizeof(REAL));
	stream->Write(&m_frame_Rotation.m_y, sizeof(REAL));
	stream->Write(&m_frame_Rotation.m_z, sizeof(REAL));
	stream->Write(&m_frame_Rotation.m_w, sizeof(REAL));

	// Write translation
	stream->Write(&m_frame_Translation.m_x, sizeof(REAL));
	stream->Write(&m_frame_Translation.m_y, sizeof(REAL));
	stream->Write(&m_frame_Translation.m_z, sizeof(REAL));

	// Write parent and connectivity indices
	stream->Write(&m_parentIndex, sizeof(m_parentIndex));
	stream->Write(&m_connectivityIndex, sizeof(m_connectivityIndex));

	return TRUE;
}

void CRuACTTemplateNode::Set(INT32 parentIndex, INT32 connectivityIndex)
{
	m_parentIndex = parentIndex;
	m_connectivityIndex = connectivityIndex;
}

void CRuACTTemplateNode::Set(RuACTTemplateNodeType nodeType, INT32 parentIndex, INT32 connectivityIndex)
{
	// Clear old node data
	Clear();

	m_nodeType = nodeType;

	m_parentIndex = parentIndex;
	m_connectivityIndex = connectivityIndex;

	switch(m_nodeType)
	{
		case ruACTTEMPLATENODETYPE_FRAME:
			{
				m_frame_Scale.Set(1.0f, 1.0f, 1.0f);
				m_frame_Rotation.Set(0.0f, 0.0f, 0.0f, 1.0f);
				m_frame_Translation.Set(0.0f, 0.0f, 0.0f);
			}

			break;
		default:
			break;
	}
}

void CRuACTTemplateNode::Set(char *entityName, INT32 parentIndex, INT32 connectivityIndex)
{
	// Clear old node data
	Clear();

	// Setup template node information
	m_nodeType = ruACTTEMPLATENODETYPE_DEFAULT;
	m_parentIndex = parentIndex;
	m_connectivityIndex = connectivityIndex;

	m_default_EntityName = ruNEW char [strlen(entityName) + 1];
	strcpy(m_default_EntityName, entityName);
}

void CRuACTTemplateNode::Set(CRuEntity *entity, INT32 parentIndex, INT32 connectivityIndex)
{
	// Clear old node data
	Clear();

	// Setup template node information
	m_nodeType = ruACTTEMPLATENODETYPE_EMBEDDEDENTITY;
	m_parentIndex = parentIndex;
	m_connectivityIndex = connectivityIndex;

	m_embedded_Entity = static_cast<CRuEntity *>(entity->Clone());
}

void CRuACTTemplateNode::SetOrientation(const CRuVector3 &scale, const CRuQuaternion &rotation, const CRuVector3 &translation)
{
	// Store transform information from the constructed frame entity back into the template node
	m_frame_Scale = scale;
	m_frame_Rotation = rotation;
	m_frame_Translation = translation;
}

void CRuACTTemplateNode::SetOrientation(REAL scale, const CRuQuaternion &rotation, const CRuVector3 &translation)
{
	// Store transform information from the constructed frame entity back into the template node
	m_frame_Scale.Set(scale, scale, scale);
	m_frame_Rotation = rotation;
	m_frame_Translation = translation;
}

void CRuACTTemplateNode::SetParentIndex(INT32 parentIndex)
{
	m_parentIndex = parentIndex;
}

RuACTTemplateNodeType CRuACTTemplateNode::GetNodeType() const
{
	return m_nodeType;
}

const char *CRuACTTemplateNode::GetName() const
{
	return m_default_EntityName;
}

INT32 CRuACTTemplateNode::GetParentIndex() const
{
	return m_parentIndex;
}

INT32 CRuACTTemplateNode::GetConnectivityIndex() const
{
	return m_connectivityIndex;
}

BOOL CRuACTTemplateNode::UpdateEmbeddedEntity(CRuEntity *embeddedEntity)
{
	if(embeddedEntity && m_nodeType == ruACTTEMPLATENODETYPE_EMBEDDEDENTITY)
	{
		// Release original embedded entity and clone the specified entity for use as the new template node
		ruSAFE_RELEASE(m_embedded_Entity);
		m_embedded_Entity = static_cast<CRuEntity *>(embeddedEntity->Clone());

		return TRUE;
	}

	return FALSE;
}

CRuACTTemplateNode_Runtime *CRuACTTemplateNode::Construct()
{
	// Attempt to instance the template node
	CRuEntity *templateEntity = NULL;
	switch(m_nodeType)
	{
		case ruACTTEMPLATENODETYPE_DEFAULT:
			{
				if(m_default_EntityName)
				{
					// Load entity using entity name and parse
					templateEntity = g_ruResourceManager->LoadEntity(m_default_EntityName);
				}
			}

			break;

		case ruACTTEMPLATENODETYPE_FRAME:
			{
				templateEntity = ruNEW CRuFrame();
			}

			break;

		case ruACTTEMPLATENODETYPE_EMBEDDEDENTITY:
			{
				if(m_embedded_Entity)
				{
					// Clone the embedded entity
					templateEntity = static_cast<CRuEntity *>(m_embedded_Entity->Clone());
				}
			}

			break;

		case ruACTTEMPLATENODETYPE_EMBEDDEDACT:
			{
				if(m_embedded_ACT)
				{
					// Clone the embedded entity
					templateEntity = m_embedded_ACT->Construct();
				}
			}

			break;
	}

	// If the template node was successfully instanced, expand its structure into the entity list
	if(templateEntity)
	{
		// Disable cloning and serialization
		templateEntity->SetOption(ruENTITYOPTION_NOCLONE, TRUE);
		templateEntity->SetOption(ruENTITYOPTION_NOSERIALIZE, TRUE);

		// Set the template entity's transform
		templateEntity->Scale(m_frame_Scale);
		templateEntity->Rotate(m_frame_Rotation);
		templateEntity->Translate(m_frame_Translation);

		CRuStack<CRuEntity *> entityStack;
		CRuArrayList<CRuEntity *> entityArray;

		// Parse the entity's hierarchy into a depth-first array
		entityStack.Push(templateEntity);
		entityArray.Clear();
		while(!entityStack.IsEmpty())
		{
			CRuEntity *curEntity = entityStack.Pop();

			// Add current entity to array
			entityArray.Add(curEntity);

			if(curEntity->GetType() == CRuFrame::Type)
			{
				CRuFrame *frame = (CRuFrame *) curEntity;

				// Push entities backwards onto stack
				for(UINT32 j = frame->GetNumEntities(); j > 0; --j)
				{
					entityStack.Push(frame->GetEntity(j - 1));
				}
			}
			else if(curEntity->GetType() == CRuFrameHierarchy::Type)
			{
				CRuFrameHierarchy *frame = (CRuFrameHierarchy *) curEntity;

				// Push entities backwards onto stack
				for(UINT32 j = frame->GetNumEntities(); j > 0; --j)
				{
					entityStack.Push(frame->GetEntity(j - 1));
				}
			}
		}

		// Setup node runtime data
		this->AddRef();
		CRuACTTemplateNode_Runtime *nodeRuntime = ruNEW CRuACTTemplateNode_Runtime();
		nodeRuntime->m_sourceTemplateNode = this;
		nodeRuntime->m_numEntities = entityArray.Count();
		nodeRuntime->m_entities = ruNEW CRuEntity * [nodeRuntime->m_numEntities];
		memcpy(nodeRuntime->m_entities, entityArray.GetArray(), sizeof(CRuEntity *) * nodeRuntime->m_numEntities);

		return nodeRuntime;
	}

	return NULL;
}

void CRuACTTemplateNode::Clear()
{
	// Release template data
	delete[] m_default_EntityName;
	ruSAFE_RELEASE(m_embedded_Entity);
	ruSAFE_RELEASE(m_embedded_ACT);

	m_nodeType = ruACTTEMPLATENODETYPE_DEFAULT;
	m_default_EntityName = NULL;
	m_parentIndex = -1;
	m_connectivityIndex = -1;
}

// ************************************************************************************************************************************************************

CRuACTTemplateModifier::CRuACTTemplateModifier()
:	m_modifier(ruACTTEMPLATEMODIFIERTYPE_DEFAULT),
	m_templateIndex(-1),
	m_connectivityIndex(-1),
	m_texOverride_TextureName(NULL),
	m_shaderOverride_ShaderName(NULL)
{
}

CRuACTTemplateModifier::~CRuACTTemplateModifier()
{
	if(m_texOverride_TextureName)
	{
		delete[] m_texOverride_TextureName;
	}

	if(m_shaderOverride_ShaderName)
	{
		delete[] m_shaderOverride_ShaderName;
	}
}

IRuObject *CRuACTTemplateModifier::Clone(IRuObject *clonedObject)
{
	CRuACTTemplateModifier *clonedModifier = ruNEW CRuACTTemplateModifier();

	clonedModifier->m_modifier = m_modifier;
	clonedModifier->m_templateIndex = m_templateIndex;
	clonedModifier->m_connectivityIndex = m_connectivityIndex;

	clonedModifier->m_meshOverride_Index = m_meshOverride_Index;

	clonedModifier->m_texOverride_Channel = m_texOverride_Channel;
	if(m_texOverride_TextureName)
	{
		INT32 textureNameLength = strlen(m_texOverride_TextureName) + 1;
		clonedModifier->m_texOverride_TextureName = ruNEW char [textureNameLength];
		memcpy(clonedModifier->m_texOverride_TextureName, m_texOverride_TextureName, textureNameLength);
	}

	if(m_shaderOverride_ShaderName)
	{
		INT32 shaderNameLength = strlen(m_shaderOverride_ShaderName) + 1;
		clonedModifier->m_shaderOverride_ShaderName = ruNEW char [shaderNameLength];
		memcpy(clonedModifier->m_shaderOverride_ShaderName, m_shaderOverride_ShaderName, shaderNameLength);
	}

	clonedModifier->m_matPriorityOverride_Priority = m_matPriorityOverride_Priority;

	return clonedModifier;
}

BOOL CRuACTTemplateModifier::SerializeFrom(IRuStream *stream)
{
	CRuGUID classGUID;

	// Read class GUID
	if(RuReadGUID(stream, classGUID) == FALSE)
	{
		return FALSE;
	}

	// Read version
	INT32 version = 1;
	stream->Read(&version, sizeof(INT32));

	switch(version)
	{
		case 1:
			{
				// Read modifier type
				stream->Read(&m_modifier, sizeof(m_modifier));

				switch(m_modifier)
				{
					case ruACTTEMPLATEMODIFIERTYPE_TEXTURE:
						{
							// Read override mesh index
							stream->Read(&m_meshOverride_Index, sizeof(INT32));

							// Read override texture channel
							stream->Read(&m_texOverride_Channel, sizeof(RuTextureChannel));

							// Read override texture name length including NULL
							INT32 texNameLength = 0;
							stream->Read(&texNameLength, sizeof(INT32));

							// Read override texture name
							if(texNameLength > 0)
							{
								m_texOverride_TextureName = ruNEW char [texNameLength];
								stream->Read(m_texOverride_TextureName, sizeof(char) * texNameLength);
							}
							else
							{
								m_texOverride_TextureName = ruNEW char [1];
								m_texOverride_TextureName[0] = NULL;
							}
						}

						break;

					case ruACTTEMPLATEMODIFIERTYPE_SHADER:
						{
							// Read override mesh index
							stream->Read(&m_meshOverride_Index, sizeof(INT32));

							// Read override texture name length including NULL
							INT32 shaderNameLength = 0;
							stream->Read(&shaderNameLength, sizeof(INT32));

							// Read override texture name
							if(shaderNameLength > 0)
							{
								m_shaderOverride_ShaderName = ruNEW char [shaderNameLength];
								stream->Read(m_shaderOverride_ShaderName, sizeof(char) * shaderNameLength);
							}
							else
							{
								m_shaderOverride_ShaderName = ruNEW char [1];
								m_shaderOverride_ShaderName[0] = NULL;
							}
						}

						break;

					case ruACTTEMPLATEMODIFIERTYPE_MATPRIORITY:
						{
							// Read override mesh index
							stream->Read(&m_meshOverride_Index, sizeof(INT32));

							// Read override priority
							stream->Read(&m_matPriorityOverride_Priority, sizeof(INT32));
						}

						break;
				}

				// Read template and connectivity indices
				stream->Read(&m_templateIndex, sizeof(m_templateIndex));
				stream->Read(&m_connectivityIndex, sizeof(m_connectivityIndex));
			}

			break;
	}

	return TRUE;
}

BOOL CRuACTTemplateModifier::SerializeTo(IRuStream *stream)
{
	// Write class GUID
	stream->Write(GetClassGUID().Data(), GetClassGUID().Size());

	// Write version
	INT32 version = 1;
	stream->Write(&version, sizeof(INT32));

	// Write node type
	stream->Write(&m_modifier, sizeof(m_modifier));

	switch(m_modifier)
	{
		case ruACTTEMPLATEMODIFIERTYPE_TEXTURE:
			{
				// Write override mesh index
				stream->Write(&m_meshOverride_Index, sizeof(INT32));

				// Write override texture channel
				stream->Write(&m_texOverride_Channel, sizeof(RuTextureChannel));

				if(m_texOverride_TextureName)
				{
					// Write override texture name length including NULL
					INT32 texNameLength = strlen(m_texOverride_TextureName) + 1;
					stream->Write(&texNameLength, sizeof(INT32));

					// Write override texture name
					stream->Write(m_texOverride_TextureName, sizeof(char) * texNameLength);
				}
				else
				{
					// Write override texture name length including NULL
					INT32 texNameLength = 1;
					stream->Write(&texNameLength, sizeof(INT32));

					// Write override texture name
					stream->Write("", sizeof(char) * texNameLength);
				}
			}

			break;

		case ruACTTEMPLATEMODIFIERTYPE_SHADER:
			{
				// Write override mesh index
				stream->Write(&m_meshOverride_Index, sizeof(INT32));

				if(m_shaderOverride_ShaderName)
				{
					// Write override texture name length including NULL
					INT32 shaderNameLength = strlen(m_shaderOverride_ShaderName) + 1;
					stream->Write(&shaderNameLength, sizeof(INT32));

					// Write override texture name
					stream->Write(m_shaderOverride_ShaderName, sizeof(char) * shaderNameLength);
				}
				else
				{
					// Write override texture name length including NULL
					INT32 shaderNameLength = 1;
					stream->Write(&shaderNameLength, sizeof(INT32));

					// Write override texture name
					stream->Write("", sizeof(char) * shaderNameLength);
				}
			}

			break;

		case ruACTTEMPLATEMODIFIERTYPE_MATPRIORITY:
			{
				// Write override mesh index
				stream->Write(&m_meshOverride_Index, sizeof(INT32));

				// Write override priority
				stream->Write(&m_matPriorityOverride_Priority, sizeof(INT32));
			}

			break;
	}

	// Write parent and connectivity indices
	stream->Write(&m_templateIndex, sizeof(m_templateIndex));
	stream->Write(&m_connectivityIndex, sizeof(m_connectivityIndex));

	return TRUE;
}

RuACTTemplateModifierType CRuACTTemplateModifier::GetModifierType() const
{
	return m_modifier;
}

INT32 CRuACTTemplateModifier::GetTemplateIndex() const
{
	return m_templateIndex;
}

INT32 CRuACTTemplateModifier::GetConnectivityIndex() const
{
	return m_connectivityIndex;
}

void CRuACTTemplateModifier::Set(INT32 templateIndex, INT32 connectivityIndex)
{
	m_templateIndex = templateIndex;
	m_connectivityIndex = connectivityIndex;
}

void CRuACTTemplateModifier::Set(RuACTTemplateModifierType modifierType, INT32 templateIndex, INT32 connectivityIndex)
{
	m_modifier = modifierType;
	m_templateIndex = templateIndex;
	m_connectivityIndex = connectivityIndex;
}

void CRuACTTemplateModifier::SetMeshIndex(INT32 meshIndex)
{
	m_meshOverride_Index = meshIndex;
}

const char *CRuACTTemplateModifier::Texture_GetName() const
{
	if(m_texOverride_TextureName)
	{
		return m_texOverride_TextureName;
	}

	return "";
}

void CRuACTTemplateModifier::Texture_SetMeshIndex(INT32 meshIndex)
{
	m_meshOverride_Index = meshIndex;
}

void CRuACTTemplateModifier::Texture_SetChannel(RuTextureChannel channel)
{
	m_texOverride_Channel = channel;
}

void CRuACTTemplateModifier::Texture_SetName(const char *textureName)
{
	delete[] m_texOverride_TextureName;
	m_texOverride_TextureName = ruNEW char [strlen(textureName) + 1];
	memcpy(m_texOverride_TextureName, textureName, strlen(textureName) + 1);
}

const char *CRuACTTemplateModifier::Shader_GetName() const
{
	if(m_shaderOverride_ShaderName)
	{
		return m_shaderOverride_ShaderName;
	}

	return "";
}

void CRuACTTemplateModifier::Shader_SetMeshIndex(INT32 meshIndex)
{
	m_meshOverride_Index = meshIndex;
}

void CRuACTTemplateModifier::Shader_SetName(const char *shaderName)
{
	delete[] m_shaderOverride_ShaderName;
	m_shaderOverride_ShaderName = ruNEW char [strlen(shaderName) + 1];
	memcpy(m_shaderOverride_ShaderName, shaderName, strlen(shaderName) + 1);
}

INT32 CRuACTTemplateModifier::MatPriority_GetPriority() const
{
	return m_matPriorityOverride_Priority;
}

void CRuACTTemplateModifier::MatPriority_SetPriority(INT32 priority)
{
	m_matPriorityOverride_Priority = priority;
}

BOOL CRuACTTemplateModifier::Construct(CRuACTTemplateNode_Runtime *target)
{
	switch(m_modifier)
	{
		case ruACTTEMPLATEMODIFIERTYPE_TEXTURE:
			{
				// Locate the entity to operate upon
				CRuEntity *targetEntity = target->GetChildEntity(m_connectivityIndex);

				if(targetEntity)
				{
					// Enumerate list of meshes and materials
					CRuArrayList<IRuMaterial *> materialList;
					RuEntity_EnumerateMaterials(targetEntity, materialList);

					// Override the texture channel in the target material
					if(m_meshOverride_Index >= 0 && m_meshOverride_Index < materialList.Count())
					{
						IRuTexture *texture = g_ruResourceManager->LoadTexture(m_texOverride_TextureName);
						materialList[m_meshOverride_Index]->SetTexture(m_texOverride_Channel, texture);
					}
				}
				else
				{
					OutputDebugString("ACT template modifier cannot find target");
				}
			}

			break;

		case ruACTTEMPLATEMODIFIERTYPE_SHADER:
			{
				// Locate the entity to operate upon
				CRuEntity *targetEntity = target->GetChildEntity(m_connectivityIndex);

				if(targetEntity)
				{
					// Enumerate list of meshes and materials
					CRuArrayList<IRuMaterial *> materialList;
					RuEntity_EnumerateMaterials(targetEntity, materialList);

					// Override the texture channel in the target material
					if(m_meshOverride_Index >= 0 && m_meshOverride_Index < materialList.Count())
					{
						IRuShader *shader = g_ruResourceManager->GetShader(m_shaderOverride_ShaderName);
						materialList[m_meshOverride_Index]->SetShader(shader);
					}
				}
				else
				{
					OutputDebugString("ACT template modifier cannot find target");
				}
			}

			break;

		case ruACTTEMPLATEMODIFIERTYPE_MATPRIORITY:
			{
				// Locate the entity to operate upon
				CRuEntity *targetEntity = target->GetChildEntity(m_connectivityIndex);

				if(targetEntity)
				{
					// Enumerate list of meshes and materials
					CRuArrayList<IRuMaterial *> materialList;
					RuEntity_EnumerateMaterials(targetEntity, materialList);

					// Override the priority in the target material
					if(m_meshOverride_Index >= 0 && m_meshOverride_Index < materialList.Count())
					{
						materialList[m_meshOverride_Index]->SetMaterialPriority(m_matPriorityOverride_Priority);
					}
				}
				else
				{
					OutputDebugString("ACT template modifier cannot find target");
				}
			}

			break;
	}

	return TRUE;
}

// ************************************************************************************************************************************************************

CRuACTMotionNode::CRuACTMotionNode()
:	m_templateIndex(-1),
	m_nodeIndex(-1),
	m_controller(NULL)
{
}

CRuACTMotionNode::~CRuACTMotionNode()
{
	// Release controller template
	ruSAFE_RELEASE(m_controller);
}

IRuObject *CRuACTMotionNode::Clone(IRuObject *clonedObject)
{
	CRuACTMotionNode *clonedMotionNode = ruNEW CRuACTMotionNode();

	clonedMotionNode->m_templateIndex = m_templateIndex;
	clonedMotionNode->m_nodeIndex = m_nodeIndex;

	if(m_controller)
	{
		clonedMotionNode->m_controller = static_cast<IRuEntity_Controller *>(m_controller->Clone());
	}

	return clonedMotionNode;
}

BOOL CRuACTMotionNode::SerializeFrom(IRuStream *stream)
{
	CRuGUID classGUID;

	// Read class GUID
	if(RuReadGUID(stream, classGUID) == FALSE)
	{
		return FALSE;
	}

	// Read signature and version
	INT32 signature;
	INT32 version = 1;
	stream->Read(&signature, sizeof(INT32));

	if(signature == 0xFFFFFFFF)
	{
		// Signature found, read version 
		stream->Read(&version, sizeof(INT32));
	}
	else
	{
		// No signature, this is an old model stream, so rewind
		stream->Seek(-4, ruSSM_Current);
	}

	switch(version)
	{
		case 1:
			{
				// Read template index
				stream->Read(&m_templateIndex, sizeof(m_templateIndex));

				// Read node index
				stream->Read(&m_nodeIndex, sizeof(m_nodeIndex));

				// Read controller type
				RuACTMotionControllerType controllerType;
				stream->Read(&controllerType, sizeof(RuACTMotionControllerType));

				// Read controller data
				switch(controllerType)
				{
					case ruACTMOTIONCONTROLLER_HIERARCHY:
						{
							UINT32								nameLength;
							char*								animationName;
							REAL								startTime;
							REAL								blendInTime;
							REAL								blendOutTime;
							REAL								loopBlendTime;

							// Read controller data
							stream->Read(&nameLength, sizeof(nameLength));
							animationName = ruNEW char [nameLength];
							stream->Read(animationName, nameLength);
							stream->Read(&startTime, sizeof(startTime));
							stream->Read(&blendInTime, sizeof(blendInTime));
							stream->Read(&blendOutTime, sizeof(blendOutTime));
							stream->Read(&loopBlendTime, sizeof(loopBlendTime));

							// Create controller and set its basic parameters
							m_controller = ruNEW CRuController_Hierarchy();
							m_controller->SetStartTime(startTime);
							m_controller->SetLoopTime(loopBlendTime);

							// Set hierarchy controller parameters
							static_cast<CRuController_Hierarchy *>(m_controller)->SetAnimation(animationName);
							static_cast<CRuController_Hierarchy *>(m_controller)->SetBlendInTime(blendInTime);
							static_cast<CRuController_Hierarchy *>(m_controller)->SetBlendOutTime(blendOutTime);

							// Release
							delete[] animationName;
						}

						break;

					case ruACTMOTIONCONTROLLER_ALPHA:
						{
							RuAlphaMode							alphaMode;
							REAL								alpha_Start;
							REAL								alpha_End;
							REAL								startTime;
							REAL								duration;
							REAL								loopTime;

							// Read controller data
							stream->Read(&alphaMode, sizeof(alphaMode));
							stream->Read(&alpha_Start, sizeof(alpha_Start));
							stream->Read(&alpha_End, sizeof(alpha_End));
							stream->Read(&startTime, sizeof(startTime));
							stream->Read(&duration, sizeof(duration));
							stream->Read(&loopTime, sizeof(loopTime));

							// Create controller and set its basic parameters
							m_controller = ruNEW CRuController_GlobalAlpha();
							m_controller->SetStartTime(startTime);
							m_controller->SetDuration(duration);
							m_controller->SetLoopTime(loopTime);
/*
							// Set alpha mode into alpha mode track
							CRuAnimTrack<RuAlphaMode> &alphaModeTrack = static_cast<CRuController_GlobalAlpha *>(m_controller)->GetTrack_AlphaMode();
							alphaModeTrack.SetKeyByIndex(0, alphaMode);
							alphaModeTrack.SetKeyByIndex(1, alphaMode);
							alphaModeTrack.MoveKeyByIndex(1, duration);

							// Set start and end alphas into alpha track
							CRuAnimTrack<REAL> &alphaTrack = static_cast<CRuController_GlobalAlpha *>(m_controller)->GetTrack_Alpha();
							alphaTrack.SetKeyByIndex(0, alpha_Start);
							alphaTrack.SetKeyByIndex(1, alpha_End);
							alphaTrack.MoveKeyByIndex(1, duration);
*/
						}

						break;

					case ruACTMOTIONCONTROLLER_COLOR:
						{
							RuAlphaMode							alphaMode;
							DWORD								startColor;
							DWORD								endColor;
							REAL								startTime;
							REAL								duration;
							BOOL								loop;

							// Read controller data
							stream->Read(&alphaMode, sizeof(alphaMode));
							stream->Read(&startColor, sizeof(startColor));
							stream->Read(&endColor, sizeof(endColor));
							stream->Read(&startTime, sizeof(startTime));
							stream->Read(&duration, sizeof(duration));
							stream->Read(&loop, sizeof(loop));

							// Create controller and set its basic parameters
							m_controller = ruNEW CRuController_GlobalColor();
							m_controller->SetStartTime(startTime);
							m_controller->SetDuration(duration);
							m_controller->SetLoopTime(loop ? 0.0f : -1.0f);
/*
							// Set start and end colors into color track
							CRuAnimTrack<CRuColor4> &colorTrack = static_cast<CRuController_GlobalColor *>(m_controller)->GetTrack_Color();
							colorTrack.SetKeyByIndex(0, CRuColor4(startColor));
							colorTrack.SetKeyByIndex(1, CRuColor4(endColor));
							colorTrack.MoveKeyByIndex(1, duration);
*/
						}

						break;

					case ruACTMOTIONCONTROLLER_EMBEDDEDHIERARCHY:
						{
							REAL								startTime;
							REAL								blendInTime;
							REAL								blendOutTime;
							REAL								loopBlendTime;
							CRuAnimation*						animation;

							// Read controller data
							stream->Read(&startTime, sizeof(startTime));
							stream->Read(&blendInTime, sizeof(blendInTime));
							stream->Read(&blendOutTime, sizeof(blendOutTime));
							stream->Read(&loopBlendTime, sizeof(loopBlendTime));
							animation = LoadRuAnimation(stream);

							// Create controller and set its basic parameters
							m_controller = ruNEW CRuController_Hierarchy();
							m_controller->SetStartTime(startTime);
							m_controller->SetLoopTime(loopBlendTime);

							static_cast<CRuController_Hierarchy *>(m_controller)->SetAnimation(animation);
							static_cast<CRuController_Hierarchy *>(m_controller)->SetBlendInTime(blendInTime);
							static_cast<CRuController_Hierarchy *>(m_controller)->SetBlendOutTime(blendOutTime);
						}

						break;

					case ruACTMOTIONCONTROLLER_MORPH:
						{
							REAL								frameRate;
							BOOL								loop;
							INT32								startFrame;
							INT32								endFrame;

							// Read controller data
							stream->Read(&frameRate, sizeof(frameRate));
							stream->Read(&loop, sizeof(INT32));
							stream->Read(&startFrame, sizeof(INT32));
							stream->Read(&endFrame, sizeof(INT32));

							// Create controller and set its basic parameters
							m_controller = ruNEW CRuController_Morph();
							m_controller->SetLoopTime(loop ? 0.0f : -1.0f);

							static_cast<CRuController_Morph *>(m_controller)->SetFrameRate(frameRate);
							static_cast<CRuController_Morph *>(m_controller)->SetFrameRange(startFrame, endFrame);
						}

						break;

					case ruACTMOTIONCONTROLLER_EVENT:
						{
							REAL								startTime;
							REAL								loopTime;
							INT32								eventID;
							INT32								eventParameter0;
							INT32								eventParameter1;
							REAL								eventDuration;

							// Read controller data
							stream->Read(&startTime, sizeof(startTime));
							stream->Read(&loopTime, sizeof(loopTime));
							stream->Read(&eventID, sizeof(eventID));
							stream->Read(&eventParameter0, sizeof(eventParameter0));
							stream->Read(&eventParameter1, sizeof(eventParameter1));
							stream->Read(&eventDuration, sizeof(eventDuration));

							// Create controller and set its basic parameters
							m_controller = ruNEW CRuController_EventTrigger();
							m_controller->SetStartTime(startTime);
							m_controller->SetLoopTime(loopTime);

							static_cast<CRuController_EventTrigger *>(m_controller)->SetEventID(eventID);
							static_cast<CRuController_EventTrigger *>(m_controller)->SetEventParameter0(eventParameter0);
							static_cast<CRuController_EventTrigger *>(m_controller)->SetEventParameter1(eventParameter1);
							static_cast<CRuController_EventTrigger *>(m_controller)->SetEventDuration(eventDuration);
						}

						break;

					case ruACTMOTIONCONTROLLER_PARTICLESYSTEM:
						{
							UINT32								nameLength;
							char*								particleSystemName;
							REAL								startTime;
							REAL								duration;
							REAL								loopTime;

							// Read controller data
							stream->Read(&nameLength, sizeof(nameLength));
							particleSystemName = ruNEW char [nameLength];
							stream->Read(particleSystemName, nameLength);
							stream->Read(&startTime, sizeof(startTime));
							stream->Read(&duration, sizeof(duration));
							stream->Read(&loopTime, sizeof(loopTime));

							// Create controller and set its basic parameters
							m_controller = ruNEW CRuController_ParticleSystem();
							m_controller->SetStartTime(startTime);
							m_controller->SetDuration(duration);
							m_controller->SetLoopTime(loopTime);

							static_cast<CRuController_ParticleSystem *>(m_controller)->SetParticleSystem(particleSystemName);

							// Release
							delete[] particleSystemName;
						}

						break;

					case ruACTMOTIONCONTROLLER_ENTITY:
						{
							UINT32								nameLength;
							char*								entityName;
							char*								motionName;
							REAL								fadeInTime;
							REAL								fadeOutTime;
							REAL								startTime;
							REAL								duration;
							REAL								loopTime;

							// Read controller data
							stream->Read(&nameLength, sizeof(nameLength));
							entityName = ruNEW char [nameLength];
							stream->Read(entityName, nameLength);
							stream->Read(&nameLength, sizeof(nameLength));
							motionName = ruNEW char [nameLength];
							stream->Read(motionName, nameLength);
							stream->Read(&fadeInTime, sizeof(REAL));
							stream->Read(&fadeOutTime, sizeof(REAL));
							stream->Read(&startTime, sizeof(startTime));
							stream->Read(&duration, sizeof(duration));
							stream->Read(&loopTime, sizeof(loopTime));

							// Create controller and set its basic parameters
							m_controller = ruNEW CRuController_Entity();
							m_controller->SetStartTime(startTime);
							m_controller->SetDuration(duration);
							m_controller->SetLoopTime(loopTime);

							static_cast<CRuController_Entity *>(m_controller)->SetSpawnedEntityName(entityName);
							static_cast<CRuController_Entity *>(m_controller)->SetSpawnedMotionName(motionName);
							static_cast<CRuController_Entity *>(m_controller)->SetFadeInTime(fadeInTime);
							static_cast<CRuController_Entity *>(m_controller)->SetFadeOutTime(fadeOutTime);

							// Release
							delete[] entityName;
							delete[] motionName;
						}

						break;
				}
			}

			break;

		case 2:
			{
				// Read template index
				stream->Read(&m_templateIndex, sizeof(m_templateIndex));

				// Read node index
				stream->Read(&m_nodeIndex, sizeof(m_nodeIndex));

				// Construct controller from object factory
				IRuObject *controllerObject = g_ruObjectFactory.CreateObject(stream);

				if(controllerObject && controllerObject->GetType().IsSubClassOf(IRuEntity_Controller::Type))
				{
					m_controller = static_cast<IRuEntity_Controller *>(controllerObject);
					return TRUE;
				}
			}

			return FALSE;
	}

	return TRUE;
}

BOOL CRuACTMotionNode::SerializeTo(IRuStream *stream)
{
	// Write class GUID
	stream->Write(GetClassGUID().Data(), GetClassGUID().Size());

	// Write signature
	INT32 signature = 0xFFFFFFFF;
	stream->Write(&signature, sizeof(INT32));

	// Write version
	INT32 version = 2;
	stream->Write(&version, sizeof(INT32));

	// Write template index
	stream->Write(&m_templateIndex, sizeof(m_templateIndex));

	// Write node index
	stream->Write(&m_nodeIndex, sizeof(m_nodeIndex));

	// Serialize controller
	m_controller->SerializeTo(stream);

	return TRUE;
}

void CRuACTMotionNode::Set(INT32 templateIndex, INT32 nodeIndex)
{
	m_templateIndex = templateIndex;
	m_nodeIndex = nodeIndex;
}

void CRuACTMotionNode::Set(RuACTMotionControllerType controllerType, INT32 templateIndex, INT32 nodeIndex)
{
	m_templateIndex = templateIndex;
	m_nodeIndex = nodeIndex;

	// Set controller type
	switch(controllerType)
	{
		case ruACTMOTIONCONTROLLER_HIERARCHY:
			{
				m_controller = ruNEW CRuController_Hierarchy();
			}

			break;

		case ruACTMOTIONCONTROLLER_ALPHA:
			{
				m_controller = ruNEW CRuController_GlobalAlpha();
			}

			break;

		case ruACTMOTIONCONTROLLER_COLOR:
			{
				m_controller = ruNEW CRuController_GlobalColor();
				m_controller->SetStartTime(0.0f);
				m_controller->SetDuration(5.0f);
				m_controller->SetLoopTime(-1.0f);

//				static_cast<CRuController_GlobalColor *>(m_controller)->GetTrack_Color().MoveKeyByIndex(1, 5.0f);
			}

			break;

		case ruACTMOTIONCONTROLLER_EMBEDDEDHIERARCHY:
			{
				// Well.. do nothing
			}

		case ruACTMOTIONCONTROLLER_MORPH:
			{
				m_controller = ruNEW CRuController_Morph();

				m_controller->SetLoopTime(0.0f);
				static_cast<CRuController_Morph *>(m_controller)->SetFrameRate(30.0f);
			}

			break;

		case ruACTMOTIONCONTROLLER_EVENT:
			{
				m_controller = ruNEW CRuController_EventTrigger();
				m_controller->SetStartTime(0.0f);
				m_controller->SetLoopTime(-1.0f);

				static_cast<CRuController_EventTrigger *>(m_controller)->SetEventID(0);
				static_cast<CRuController_EventTrigger *>(m_controller)->SetEventParameter0(0);
				static_cast<CRuController_EventTrigger *>(m_controller)->SetEventParameter1(0);
				static_cast<CRuController_EventTrigger *>(m_controller)->SetEventDuration(0.0f);
			}

			break;

		case ruACTMOTIONCONTROLLER_PARTICLESYSTEM:
			{
				m_controller = ruNEW CRuController_ParticleSystem();
				m_controller->SetStartTime(0.0f);
				m_controller->SetDuration(0.0f);
				m_controller->SetLoopTime(-1.0f);
			}

			break;

		case ruACTMOTIONCONTROLLER_ENTITY:
			{
				m_controller = ruNEW CRuController_Entity();
				m_controller->SetStartTime(0.0f);
				m_controller->SetDuration(0.0f);
				m_controller->SetLoopTime(-1.0f);
			}

			break;
	}
}

void CRuACTMotionNode::Set(IRuEntity_Controller *controller, INT32 templateIndex, INT32 nodeIndex)
{
	m_templateIndex = templateIndex;
	m_nodeIndex = nodeIndex;

	m_controller = static_cast<IRuEntity_Controller *>(controller->Clone());
}

void CRuACTMotionNode::Set(INT32 templateIndex, INT32 nodeIndex, const char *animationName)
{
	m_templateIndex = templateIndex;
	m_nodeIndex = nodeIndex;

	m_controller = ruNEW CRuController_Hierarchy();
	static_cast<CRuController_Hierarchy *>(m_controller)->SetAnimation(animationName);
	static_cast<CRuController_Hierarchy *>(m_controller)->SetBlendInTime(0.0f);
	static_cast<CRuController_Hierarchy *>(m_controller)->SetBlendOutTime(0.0f);
}

INT32 CRuACTMotionNode::GetTemplateIndex() const
{
	return m_templateIndex;
}

INT32 CRuACTMotionNode::GetNodeIndex() const
{
	return m_nodeIndex;
}

IRuEntity_Controller *CRuACTMotionNode::Template_GetController()
{
	return m_controller;
}

IRuEntity_Controller *CRuACTMotionNode::GetController()
{
	if(m_controller)
	{
		// Create new color controller from the template controller
		return static_cast<IRuEntity_Controller *>(m_controller->Clone());
	}

	return NULL;
}

// ************************************************************************************************************************************************************

CRuACTMotion::CRuACTMotion()
:	m_motionID(-1),
	m_motionName(NULL),
	m_motionScale(0.0f)
{
}

CRuACTMotion::~CRuACTMotion()
{
	for(INT32 i = 0; i < m_motionNodes.Count(); ++i)
	{
		m_motionNodes[i]->Release();
	}
}

IRuObject *CRuACTMotion::Clone(IRuObject *clonedObject)
{
	CRuACTMotion *clonedMotion = ruNEW CRuACTMotion();

	clonedMotion->m_motionID = m_motionID;
	clonedMotion->m_motionName = m_motionName;
	clonedMotion->m_motionScale = m_motionScale;

	for(INT32 i = 0; i < m_motionNodes.Count(); ++i)
	{
		clonedMotion->m_motionNodes.Add(static_cast<CRuACTMotionNode *>(m_motionNodes[i]->Clone()));
	}

	return clonedMotion;
}

BOOL CRuACTMotion::SerializeFrom(IRuStream *stream)
{
	CRuGUID classGUID;

	// Read class GUID
	if(RuReadGUID(stream, classGUID) == FALSE)
	{
		return FALSE;
	}

	// Read signature and version
	INT32 signature;
	INT32 version = 1;
	stream->Read(&signature, sizeof(INT32));

	if(signature == 0xFFFFFFFF)
	{
		// Signature found, read version 
		stream->Read(&version, sizeof(INT32));
	}
	else
	{
		// No signature, this is an old model stream, so rewind
		stream->Seek(-4, ruSSM_Current);
	}

	// Read motion ID
	stream->Read(&m_motionID, sizeof(m_motionID));

	// Read motion name length including NULL
	UINT32 nameLength;
	stream->Read(&nameLength, sizeof(nameLength));

	char *name = ruNEW char [nameLength];
	stream->Read(name, nameLength);
	m_motionName = name;
	delete[] name;

	switch(version)
	{
		case 1:
			{
			}

			break;

		case 2:
			{
				stream->Read(&m_motionScale, sizeof(REAL));
			}

			break;
	}

	// Read number of motion nodes
	INT32 motionNodeCount = m_motionNodes.Count();
	stream->Read(&motionNodeCount, sizeof(motionNodeCount));

	// Read motion nodes
	for(INT32 i = 0; i < motionNodeCount; ++i)
	{
		IRuObject *object = g_ruObjectFactory.CreateObject(stream);

		if(object && object->GetType() == CRuACTMotionNode::Type)
		{
			m_motionNodes.Add((CRuACTMotionNode *) object);
		}
		else
		{
			return FALSE;
		}
	}

	return TRUE;
}

BOOL CRuACTMotion::SerializeTo(IRuStream *stream)
{
	// Write class GUID
	stream->Write(GetClassGUID().Data(), GetClassGUID().Size());

	// Write signature
	INT32 signature = 0xFFFFFFFF;
	stream->Write(&signature, sizeof(INT32));

	// Write version
	INT32 version = 2;
	stream->Write(&version, sizeof(INT32));

	// Write motion ID
	stream->Write(&m_motionID, sizeof(m_motionID));

	// Write motion name length including NULL
	UINT32 nameLength = m_motionName.GetLength() + 1;
	stream->Write(&nameLength, sizeof(nameLength));
	stream->Write((char *) m_motionName, nameLength);

	// Write motion scale
	stream->Write(&m_motionScale, sizeof(REAL));

	// Write number of motion nodes
	INT32 motionNodeCount = m_motionNodes.Count();
	stream->Write(&motionNodeCount, sizeof(motionNodeCount));

	// Write motion nodes
	for(INT32 i = 0; i < motionNodeCount; ++i)
	{
		m_motionNodes[i]->SerializeTo(stream);
	}

	return TRUE;
}

void CRuACTMotion::Set(INT32 motionID, const char *motionName)
{
	m_motionID = motionID;
	m_motionName = motionName;
}

BOOL CRuACTMotion::AdjustTemplateIndex(INT32 offset)
{
	for(INT32 i = 0; i < m_motionNodes.Count(); ++i)
	{
		if(m_motionNodes[i]->GetTemplateIndex() >= 0)
		{
			m_motionNodes[i]->Set(m_motionNodes[i]->GetTemplateIndex() + offset, m_motionNodes[i]->GetNodeIndex());
		}
	}

	return TRUE;
}

BOOL CRuACTMotion::DecrementTemplateIndexAfter(INT32 index)
{
	for(INT32 i = 0; i < m_motionNodes.Count(); ++i)
	{
		if(m_motionNodes[i]->GetTemplateIndex() >= index)
		{
			m_motionNodes[i]->Set(m_motionNodes[i]->GetTemplateIndex() - 1, m_motionNodes[i]->GetNodeIndex());
		}
	}

	return TRUE;
}

BOOL CRuACTMotion::AddMotionNode(RuACTMotionControllerType controllerType, INT32 templateIndex, INT32 nodeIndex)
{
	// Setup motion node
	CRuACTMotionNode *motionNode = ruNEW CRuACTMotionNode();
	motionNode->Set(controllerType, templateIndex, nodeIndex);

	// Add motion node to motion node list
	m_motionNodes.Add(motionNode);

	return TRUE;
}

BOOL CRuACTMotion::AddMotionNode(IRuEntity_Controller *controller, INT32 templateIndex, INT32 nodeIndex)
{
	// Setup motion node
	CRuACTMotionNode *motionNode = ruNEW CRuACTMotionNode();
	motionNode->Set(controller, templateIndex, nodeIndex);

	// Add motion node to motion node list
	m_motionNodes.Add(motionNode);

	return TRUE;
}

BOOL CRuACTMotion::AddMotionNode(INT32 templateIndex, INT32 nodeIndex, const char *animationName)
{
	// Setup motion node
	CRuACTMotionNode *motionNode = ruNEW CRuACTMotionNode();
	motionNode->Set(templateIndex, nodeIndex, animationName);

	// Add motion node to motion node list
	m_motionNodes.Add(motionNode);

	return TRUE;
}

BOOL CRuACTMotion::RemoveMotionNodeAt(INT32 motionNodeIndex)
{
	if(motionNodeIndex >= 0 && motionNodeIndex < m_motionNodes.Count())
	{
		// Release motion node
		m_motionNodes[motionNodeIndex]->Release();

		// Remove motion node from the array list
		m_motionNodes.RemoveAt(motionNodeIndex);

		return TRUE;
	}

	return FALSE;
}

BOOL CRuACTMotion::RemoveMotionNodesAffecting(INT32 templateIndex)
{
	for(INT32 i = 0; i < m_motionNodes.Count(); ++i)
	{
		if(m_motionNodes[i]->GetTemplateIndex() == templateIndex)
		{
			// Release motion node
			m_motionNodes[i]->Release();

			// Remove motion node from the array list
			m_motionNodes.RemoveAt(i);

			--i;
		}
	}

	return TRUE;
}

BOOL CRuACTMotion::Merge(CRuACTMotion *srcMotion, INT32 templateIndexOffset)
{
	for(INT32 i = 0; i < srcMotion->GetNumMotionNodes(); ++i)
	{
		CRuACTMotionNode *clonedMotionNode = static_cast<CRuACTMotionNode *>(srcMotion->GetMotionNode(i)->Clone());
		m_motionNodes.Add(clonedMotionNode);

		if(clonedMotionNode->GetTemplateIndex() >= 0)
		{
			clonedMotionNode->Set(clonedMotionNode->GetTemplateIndex() + templateIndexOffset, clonedMotionNode->GetNodeIndex());
		}
	}

	return TRUE;
}

INT32 CRuACTMotion::GetMotionID() const
{
	return m_motionID;
}

const CRuString &CRuACTMotion::GetMotionName() const
{
	return m_motionName;
}

REAL CRuACTMotion::GetMotionScale() const
{
	return m_motionScale;
}

void CRuACTMotion::SetMotionID(INT32 motionID)
{
	m_motionID = motionID;
}

void CRuACTMotion::SetMotionName(const char *motionName)
{
	if(motionName)
	{
		m_motionName = motionName;
	}
}

void CRuACTMotion::SetMotionScale(REAL motionScale)
{
	if(motionScale >= 0.0f)
	{
		m_motionScale = motionScale;
	}
}

INT32 CRuACTMotion::GetNumMotionNodes() const
{
	return m_motionNodes.Count();
}

CRuACTMotionNode *CRuACTMotion::GetMotionNode(INT32 index)
{
	return m_motionNodes[index];
}

// ************************************************************************************************************************************************************

CRuACTTemplate::CRuACTTemplate()
{
}

CRuACTTemplate::~CRuACTTemplate()
{
	// Release references to all template nodes
	for(INT32 i = 0; i < m_templateNodes.Count(); ++i)
	{
		m_templateNodes[i]->Release();
	}

	// Release references to all motions
	for(INT32 i = 0; i < m_motions.Count(); ++i)
	{
		m_motions[i]->Release();
	}

	// Clear template node list
	m_templateNodes.Clear();

	// Clear motion list
	m_motions.Clear();
}

IRuObject *CRuACTTemplate::Clone(IRuObject *clonedObject)
{
	CRuACTTemplate *clonedTemplate = ruNEW CRuACTTemplate();

	for(INT32 i = 0; i < m_templateNodes.Count(); ++i)
	{
		CRuACTTemplateNode *clonedNode = static_cast<CRuACTTemplateNode *>(m_templateNodes[i]->Clone());
		clonedTemplate->m_templateNodes.Add(clonedNode);
	}

	for(INT32 i = 0; i < m_templateModifiers.Count(); ++i)
	{
		CRuACTTemplateModifier *clonedModifier = static_cast<CRuACTTemplateModifier *>(m_templateModifiers[i]->Clone());
		clonedTemplate->m_templateModifiers.Add(clonedModifier);
	}

	for(INT32 i = 0; i < m_motions.Count(); ++i)
	{
		CRuACTMotion *clonedMotion = static_cast<CRuACTMotion *>(m_motions[i]->Clone());
		clonedTemplate->m_motions.Add(clonedMotion);
	}

	return clonedTemplate;
}

BOOL CRuACTTemplate::SerializeFrom(IRuStream *stream)
{
	CRuGUID classGUID;

	// Read class GUID
	if(RuReadGUID(stream, classGUID) == FALSE)
	{
		return FALSE;
	}

	// Read signature and version
	INT32 signature;
	INT32 version = 1;
	stream->Read(&signature, sizeof(INT32));

	if(signature == 0xFFFFFFFF)
	{
		// Signature found, read version 
		stream->Read(&version, sizeof(INT32));
	}
	else
	{
		// No signature, this is an old model stream, so rewind
		stream->Seek(-4, ruSSM_Current);
	}

	switch(version)
	{
		case 1:
			{
				INT32 templateNodeCount;
				INT32 motionCount;

				stream->Read(&templateNodeCount, sizeof(INT32));
				stream->Read(&motionCount, sizeof(INT32));

				// Read each template node
				for(INT32 i = 0; i < templateNodeCount; ++i)
				{
					IRuObject *object = g_ruObjectFactory.CreateObject(stream);

					if(object && object->GetType() == CRuACTTemplateNode::Type)
					{
						m_templateNodes.Add((CRuACTTemplateNode *) object);
					}
					else
					{
						return FALSE;
					}
				}

				// Read each motion
				for(INT32 i = 0; i < motionCount; ++i)
				{
					IRuObject *object = g_ruObjectFactory.CreateObject(stream);

					if(object && object->GetType() == CRuACTMotion::Type)
					{
						m_motions.Add((CRuACTMotion *) object);
					}
					else
					{
						return FALSE;
					}
				}
			}

			break;

		case 2:
			{
				INT32 templateNodeCount;
				INT32 modifierCount;
				INT32 motionCount;

				stream->Read(&templateNodeCount, sizeof(INT32));
				stream->Read(&modifierCount, sizeof(INT32));
				stream->Read(&motionCount, sizeof(INT32));

				// Read each template node
				for(INT32 i = 0; i < templateNodeCount; ++i)
				{
					IRuObject *object = g_ruObjectFactory.CreateObject(stream);

					if(object && object->GetType() == CRuACTTemplateNode::Type)
					{
						m_templateNodes.Add((CRuACTTemplateNode *) object);
					}
					else
					{
						return FALSE;
					}
				}

				// Read each modifier
				for(INT32 i = 0; i < modifierCount; ++i)
				{
					IRuObject *object = g_ruObjectFactory.CreateObject(stream);

					if(object && object->GetType() == CRuACTTemplateModifier::Type)
					{
						m_templateModifiers.Add((CRuACTTemplateModifier *) object);
					}
					else
					{
						return FALSE;
					}
				}

				// Read each motion
				for(INT32 i = 0; i < motionCount; ++i)
				{
					IRuObject *object = g_ruObjectFactory.CreateObject(stream);

					if(object && object->GetType() == CRuACTMotion::Type)
					{
						m_motions.Add((CRuACTMotion *) object);
					}
					else
					{
						return FALSE;
					}
				}
			}

			break;
	}

	return TRUE;
}

BOOL CRuACTTemplate::SerializeTo(IRuStream *stream)
{
	// Write class GUID
	stream->Write(GetClassGUID().Data(), GetClassGUID().Size());

	// Write signature
	INT32 signature = 0xFFFFFFFF;
	stream->Write(&signature, sizeof(INT32));

	// Write version
	INT32 version = 2;
	stream->Write(&version, sizeof(INT32));

	INT32 templateNodeCount = m_templateNodes.Count();
	INT32 modifierCount = m_templateModifiers.Count();
	INT32 motionCount = m_motions.Count();

	stream->Write(&templateNodeCount, sizeof(INT32));
	stream->Write(&modifierCount, sizeof(INT32));
	stream->Write(&motionCount, sizeof(INT32));

	// Write out each template node
	for(INT32 i = 0; i < templateNodeCount; ++i)
	{
		m_templateNodes[i]->SerializeTo(stream);
	}

	// Write out each template node
	for(INT32 i = 0; i < modifierCount; ++i)
	{
		m_templateModifiers[i]->SerializeTo(stream);
	}

	// Write out each motion
	for(INT32 i = 0; i < motionCount; ++i)
	{
		m_motions[i]->SerializeTo(stream);
	}

	return TRUE;
}

BOOL CRuACTTemplate::AddTemplateNode(INT32 parentIndex, INT32 connectivityIndex)
{
	CRuACTTemplateNode *node = ruNEW CRuACTTemplateNode();

	// An unnamed node is always a CRuFrame
	node->Set(ruACTTEMPLATENODETYPE_FRAME, parentIndex, connectivityIndex);

	m_templateNodes.Add(node);

	return TRUE;
}

BOOL CRuACTTemplate::AddTemplateNode(char *entityName, INT32 parentIndex, INT32 connectivityIndex)
{
	CRuACTTemplateNode *node = ruNEW CRuACTTemplateNode();

	node->Set(entityName, parentIndex, connectivityIndex);

	m_templateNodes.Add(node);

	return TRUE;
}

BOOL CRuACTTemplate::AddTemplateNode(CRuEntity *entity, INT32 parentIndex, INT32 connectivityIndex)
{
	CRuACTTemplateNode *node = ruNEW CRuACTTemplateNode();

	node->Set(entity, parentIndex, connectivityIndex);

	m_templateNodes.Add(node);

	return TRUE;
}

BOOL CRuACTTemplate::DeleteTemplateNode(INT32 templateNodeIndex)
{
	CRuStack<INT32> deleteStack;
	CRuArrayList<INT32> deleteList;

	// Push the deletion index onto the stack as a starting point
	deleteStack.Push(templateNodeIndex);

	while(!deleteStack.IsEmpty())
	{
		INT32 curIndex = deleteStack.Pop();

		deleteList.Add(curIndex);

		// Look for nodes which are attached to this deleted node
		for(INT32 i = 0; i < m_templateNodes.Count(); ++i)
		{
			// If the node is attached to the deleted node, push it onto the delete stack
			if(m_templateNodes[i]->GetParentIndex() == curIndex)
			{
				deleteStack.Push(i);
			}
		}

		// Look for modifiers which affect this deleted node
		for(INT32 i = 0; i < m_templateModifiers.Count(); ++i)
		{
			// If the modifier affects this deleted node, delete the modifier
			if(m_templateModifiers[i]->GetTemplateIndex() == curIndex)
			{
				ruSAFE_RELEASE(m_templateModifiers[i]);
				m_templateModifiers.RemoveAt(i);
				--i;
			}
		}

		// Remove all motion nodes which require this deleted node
		for(INT32 j = 0; j < m_motions.Count(); ++j)
		{
			m_motions[j]->RemoveMotionNodesAffecting(curIndex);
		}
	}

	// Sort the delete list
	g_radixSort.Sort(deleteList.GetArray(), deleteList.Count());

	// Iterate through delete list and delete all nodes
	const UINT32 *sortedIndices = g_radixSort.GetSortedIndices();
	for(INT32 i = deleteList.Count(); i > 0; --i)
	{
		INT32 deleteIndex = deleteList[sortedIndices[i - 1]];

		// Delete template node
		m_templateNodes[deleteIndex]->Release();

		// Remove template node from node list
		m_templateNodes.RemoveAt(deleteIndex);

		// Adjust remaining template node index references
		for(INT32 j = 0; j < m_templateNodes.Count(); ++j)
		{
			if(m_templateNodes[j]->GetParentIndex() >= deleteIndex)
			{
				m_templateNodes[j]->SetParentIndex(m_templateNodes[j]->GetParentIndex() - 1);
			}
		}

		// Adjust motion node index references
		for(INT32 j = 0; j < m_motions.Count(); ++j)
		{
			m_motions[j]->DecrementTemplateIndexAfter(deleteIndex);
		}
	}

	return TRUE;
}

BOOL CRuACTTemplate::MoveTemplateNode(INT32 templateNodeIndex, INT32 parentIndex, INT32 connectivityIndex)
{
/*	// If the template node to be moved appears before its parent, swap them
	if(templateNodeIndex < parentIndex)
	{
		CRuACTTemplateNode *parentNode = m_templateNodes[parentIndex];
		CRuACTTemplateNode *childNode = m_templateNodes[templateNodeIndex];

		m_templateNodes[parentIndex] = parentNode;
		m_templateNodes[templateNodeIndex] = childNode;

		// Adjust remaining parent template node index references
		for(INT32 j = 0; j < m_templateNodes.Count(); ++j)
		{
			if(m_templateNodes[j]->GetParentIndex() == parentIndex)
			{
				m_templateNodes[j]->SetParentIndex(templateNodeIndex);
			}
			else if(m_templateNodes[j]->GetParentIndex() == templateNodeIndex)
			{
				m_templateNodes[j]->SetParentIndex(parentIndex);
			}
		}
	}

//	CRuACTTemplateNode *templateNode = m_templateNodes[templateNodeIndex];
//	templateNode->AddRef();
*/
	m_templateNodes[templateNodeIndex]->Set(parentIndex, connectivityIndex);

	return TRUE;
}

INT32 CRuACTTemplate::AddTemplateModifier(CRuACTTemplateModifier *modifier)
{
	m_templateModifiers.Add(modifier);

	return m_templateModifiers.Count() - 1;
}

BOOL CRuACTTemplate::DeleteTemplateModifier(INT32 modifierIndex)
{
	if(modifierIndex >= 0 && modifierIndex < m_templateModifiers.Count())
	{
		// Release the modifier
		ruSAFE_RELEASE(m_templateModifiers[modifierIndex]);

		// Remove the pointer
		m_templateModifiers.RemoveAt(modifierIndex);

		return TRUE;
	}

	return FALSE;
}

BOOL CRuACTTemplate::Merge(CRuACTTemplate *srcTemplate)
{
	INT32 templateIndexOffset = this->GetNumTemplateNodes();

	for(INT32 i = 0; i < srcTemplate->GetNumTemplateNodes(); ++i)
	{
		CRuACTTemplateNode *clonedNode = static_cast<CRuACTTemplateNode *>(srcTemplate->GetTemplateNode(i)->Clone());
		m_templateNodes.Add(clonedNode);

		// Adjust parent index for the node if necessary
		if(clonedNode->GetParentIndex() >= 0)
		{
			clonedNode->Set(clonedNode->GetParentIndex() + templateIndexOffset, clonedNode->GetConnectivityIndex());
		}
	}

	for(INT32 i = 0; i < srcTemplate->GetNumTemplateModifiers(); ++i)
	{
		CRuACTTemplateModifier *clonedModifier = static_cast<CRuACTTemplateModifier *>(srcTemplate->GetTemplateModifier(i)->Clone());
		m_templateModifiers.Add(clonedModifier);

		// Adjust template index for the modifier if necessary
		if(clonedModifier->GetTemplateIndex() >= 0)
		{
			clonedModifier->Set(clonedModifier->GetTemplateIndex() + templateIndexOffset, clonedModifier->GetConnectivityIndex());
		}
	}

	for(INT32 i = 0; i < srcTemplate->GetNumMotions(); ++i)
	{
		// Find appropriate motion to merge into
		BOOL motionMerged = FALSE;
		for(INT32 j = 0; j < m_motions.Count(); ++j)
		{
			if(m_motions[j]->GetMotionName() == srcTemplate->GetMotion(i)->GetMotionName())
			{
				m_motions[j]->Merge(srcTemplate->GetMotion(i), templateIndexOffset);
				motionMerged = TRUE;
				break;
			}
		}

		if(motionMerged == FALSE)
		{
			CRuACTMotion *clonedMotion = static_cast<CRuACTMotion *>(srcTemplate->GetMotion(i)->Clone());
			m_motions.Add(clonedMotion);
			clonedMotion->AdjustTemplateIndex(templateIndexOffset);
		}
	}

	return TRUE;
}

BOOL CRuACTTemplate::Sort()
{
	char **motionNames = ruNEW char * [m_motions.Count()];

	for(INT32 i = 0; i < m_motions.Count(); ++i)
	{
		motionNames[i] = m_motions[i]->GetMotionName();
	}

	qsort(motionNames, m_motions.Count(), sizeof(char *), StrCompare);

	CRuArrayList<CRuACTMotion *> sortedMotions;
	for(INT32 i = 0; i < m_motions.Count(); ++i)
	{
		for(INT32 j = 0; j < m_motions.Count(); ++j)
		{
			if(m_motions[j]->GetMotionID() == 0)
			{
				sortedMotions.Insert(0, m_motions[j]);
			}
			else if(strcmp(m_motions[j]->GetMotionName(), motionNames[i]) == 0)
			{
				sortedMotions.Add(m_motions[j]);
				break;
			}
		}
	}

	m_motions.CloneFrom(sortedMotions);

	// Renumber
	for(INT32 i = 0, j = 1; i < m_motions.Count(); ++i)
	{
		if(m_motions[i]->GetMotionID() != 0)
		{
			m_motions[i]->SetMotionID(j);
			++j;
		}
	}

	delete[] motionNames;

	return TRUE;
}

INT32 CRuACTTemplate::GetNumTemplateNodes() const
{
	return m_templateNodes.Count();
}

CRuACTTemplateNode *CRuACTTemplate::GetTemplateNode(INT32 index)
{
	return m_templateNodes[index];
}

INT32 CRuACTTemplate::GetTemplateNodeIndex(CRuACTTemplateNode *templateNode)
{
	for(INT32 i = 0; i < m_templateNodes.Count(); ++i)
	{
		if(templateNode == m_templateNodes[i])
		{
			return i;
		}
	}

	return -1;
}

INT32 CRuACTTemplate::GetNumTemplateModifiers() const
{
	return m_templateModifiers.Count();
}

CRuACTTemplateModifier *CRuACTTemplate::GetTemplateModifier(INT32 index)
{
	return m_templateModifiers[index];
}

BOOL CRuACTTemplate::AddMotion(INT32 motionID, const char *motionName)
{
	// Create new motion
	CRuACTMotion *newMotion = ruNEW CRuACTMotion();
	newMotion->Set(motionID, motionName);

	// Add motion to motion list
	m_motions.Add(newMotion);

	return TRUE;
}

BOOL CRuACTTemplate::DeleteMotion(INT32 index)
{
	if(index >= 0 && index < (INT32) m_motions.Count())
	{
		m_motions[index]->Release();
		m_motions.RemoveAt(index);

		return TRUE;
	}

	return FALSE;
}

INT32 CRuACTTemplate::GetNumMotions() const
{
	return m_motions.Count();
}

CRuACTMotion *CRuACTTemplate::GetMotion(INT32 index)
{
	if(index >= 0 && index < (INT32) m_motions.Count())
		return m_motions[index];

	return NULL;
}

CRuACTMotion *CRuACTTemplate::GetMotionByID(INT32 id)
{
	for(INT32 i = 0; i < (INT32) m_motions.Count(); i++)
	{
		if(m_motions[i]->GetMotionID() == id)
		{
			return m_motions[i];
		}
	}

	return NULL;
}

CRuACTMotion *CRuACTTemplate::GetMotionByName(const char *motionName)
{
	for(INT32 i = 0; i < (INT32) m_motions.Count(); i++)
	{
		if(m_motions[i]->GetMotionName() == motionName)
		{
			return m_motions[i];
		}
	}

	return NULL;
}

INT32 CRuACTTemplate::GetNextMotionID() const
{
	INT32 largestMotionID = 0;

	for(INT32 i = 0; i < (INT32) m_motions.Count(); i++)
	{
		if(m_motions[i]->GetMotionID() > largestMotionID)
		{
			largestMotionID = m_motions[i]->GetMotionID();
		}
	}

	return largestMotionID + 1;
}

INT32 CRuACTTemplate::GetMotionIDByName(const char *motionName) const
{
	for(INT32 i = 0; i < (INT32) m_motions.Count(); i++)
	{
		if(m_motions[i]->GetMotionName() == motionName)
		{
			return m_motions[i]->GetMotionID();
		}
	}

	return -1;
}

const char *CRuACTTemplate::GetMotionNameByID(INT32 id) const
{
	for(INT32 i = 0; i < (INT32) m_motions.Count(); i++)
	{
		if(m_motions[i]->GetMotionID() == id)
		{
			return m_motions[i]->GetMotionName();
		}
	}

	return "";
}

void CRuACTTemplate::SetMotion(INT32 index, CRuACTMotion *motion)
{
	if(index >= 0 && index < (INT32) m_motions.Count() && motion)
	{
		ruSAFE_RELEASE(m_motions[index]);
		m_motions[index] = motion;
		m_motions[index]->AddRef();
	}
}

CRuACTEntity *CRuACTTemplate::Construct()
{
	CRuArrayList<CRuACTTemplateNode_Runtime *> nodeRuntimeArray;
	CRuStack<CRuEntity *> entityStack;
	CRuArrayList<CRuEntity *> entityArray;

	// Construct ACT entity
	CRuACTEntity *actEntity = ruNEW CRuACTEntity();
	actEntity->m_template = this;
	actEntity->SetResourceName(this->GetResourceName());
	this->AddRef();										// Since the entity holds a reference to this template, increment reference count

	// Iterate through template nodes and construct each entity
	for(INT32 i = 0; i < m_templateNodes.Count(); ++i)
	{
		nodeRuntimeArray.Add(m_templateNodes[i]->Construct());
	}

	// Modifiers
	for(INT32 i = 0; i < m_templateModifiers.Count(); ++i)
	{
		for(INT32 j = 0; j < nodeRuntimeArray.Count(); ++j)
		{
			if(nodeRuntimeArray[j] == NULL)
				continue;

			if(m_templateModifiers[i]->GetTemplateIndex() == j)
			{
				if(m_templateModifiers[i]->Construct(nodeRuntimeArray[j]) == FALSE)
				{
					// TODO: Some appropriate error response here
				}
			}
		}
	}

	// Construct entity tree from the template nodes
	for(INT32 i = 0; i < nodeRuntimeArray.Count(); ++i)
	{
		if(nodeRuntimeArray[i] == NULL)
			continue;

		CRuEntity *rootEntity = nodeRuntimeArray[i]->GetRootEntity();
		INT32 parentIndex = nodeRuntimeArray[i]->GetParentIndex();
		INT32 connectivityIndex = nodeRuntimeArray[i]->GetConnectivityIndex();

		if(rootEntity)
		{
			if(parentIndex < 0)
			{
				// If the template node is a top-level node, add its entity to the root level
				actEntity->AddChild(rootEntity);
			}
			else
			{
				if(nodeRuntimeArray[parentIndex])
				{
					// Search
					CRuEntity *parentEntity = nodeRuntimeArray[parentIndex]->GetChildEntity(connectivityIndex);

					if(parentEntity)
					{
						if(parentEntity->GetType().IsSubClassOf(CRuEntity_Container_Impl::Type))
						{
							static_cast<CRuEntity_Container_Impl *>(parentEntity)->AddChild(rootEntity);
						}
					}
				}
			}

			if(i == 0)
			{
				// Inherit option flags from the template node, always inheriting the least restrictive option
				actEntity->SetOption(ruENTITYOPTION_DISTANCECLASS, rootEntity->GetOption(ruENTITYOPTION_DISTANCECLASS));
				actEntity->SetOption(ruENTITYOPTION_REFLECTCLASS, rootEntity->GetOption(ruENTITYOPTION_REFLECTCLASS));
				actEntity->SetOption(ruENTITYOPTION_CASTERCLASS, rootEntity->GetOption(ruENTITYOPTION_CASTERCLASS));
				actEntity->SetOption(ruENTITYOPTION_RECEIVERCLASS, rootEntity->GetOption(ruENTITYOPTION_RECEIVERCLASS));
			}
			else
			{
				// Inherit option flags from the template node, always inheriting the least restrictive option
				actEntity->SetOption(ruENTITYOPTION_DISTANCECLASS, min(actEntity->GetOption(ruENTITYOPTION_DISTANCECLASS), rootEntity->GetOption(ruENTITYOPTION_DISTANCECLASS)));
				actEntity->SetOption(ruENTITYOPTION_REFLECTCLASS, min(actEntity->GetOption(ruENTITYOPTION_REFLECTCLASS), rootEntity->GetOption(ruENTITYOPTION_REFLECTCLASS)));
				actEntity->SetOption(ruENTITYOPTION_CASTERCLASS, min(actEntity->GetOption(ruENTITYOPTION_CASTERCLASS), rootEntity->GetOption(ruENTITYOPTION_CASTERCLASS)));
				actEntity->SetOption(ruENTITYOPTION_RECEIVERCLASS, min(actEntity->GetOption(ruENTITYOPTION_RECEIVERCLASS), rootEntity->GetOption(ruENTITYOPTION_RECEIVERCLASS)));
			}
		}
	}

	// Save runtime nodes into ACT entity
	actEntity->m_numRuntimeNodes = nodeRuntimeArray.Count();
	actEntity->m_runtimeNodes = ruNEW CRuACTTemplateNode_Runtime * [actEntity->m_numRuntimeNodes];
	for(INT32 i = 0; i < nodeRuntimeArray.Count(); ++i)
	{
		actEntity->m_runtimeNodes[i] = nodeRuntimeArray[i];
	}

	// Force update so we get the correct world transforms at start
	actEntity->Update(0.0f);

	return actEntity;
}

// ************************************************************************************************************************************************************

CRuACTEntity::ActiveMotionDesc::ActiveMotionDesc()
:	m_motionID(-1),
	m_channel(0xFFFFFFFF),
	m_channelWeight(1.0f),
	m_paused(FALSE)
{
}

CRuACTEntity::ActiveMotionDesc::~ActiveMotionDesc()
{
	Clear();
}

CRuAnimationInterpolator *CRuACTEntity::ActiveMotionDesc::GetInterpolator(CRuFrameHierarchy *hierarchy)
{
	for(INT32 i = 0; i < m_interpolators.Count(); ++i)
	{
		if(m_interpolators[i].m_boundHierarchy == hierarchy)
		{
			return m_interpolators[i].m_interpolator;
		}
	}

	InterpolatorDesc desc;
	desc.m_boundHierarchy = hierarchy;
	desc.m_interpolator = hierarchy->GetInterpolator()->Clone();
	desc.m_interpolator->SetChannel(m_channel);
	m_interpolators.Add(desc);

	return desc.m_interpolator;
}

void CRuACTEntity::ActiveMotionDesc::Synchronize(ActiveMotionDesc *keyedMotion)
{
	// Update local motion time
	m_motionTime = keyedMotion->m_motionTime;

	m_leadIn = keyedMotion->m_leadIn;
	m_leadOut = keyedMotion->m_leadOut;

	m_motionLength = keyedMotion->m_motionLength;
	m_fadeTime = keyedMotion->m_fadeTime;
	m_fadeDuration = keyedMotion->m_fadeDuration;

	// TODO: what do we do with other controllers?
	// Update controllers
	for(INT32 i = 0; i < m_controllers.Count(); ++i)
	{
		m_controllers[i]->Enable(FALSE);
		m_controllers[i]->Update(m_motionTime);
	}

	// Update interpolators
	for(INT32 i = 0; i < m_interpolators.Count(); ++i)
	{
		// Synchronize to the keyed motion's interpolators
		m_interpolators[i].m_interpolator->Synchronize(keyedMotion->m_interpolators[i].m_interpolator);
	}
}

void CRuACTEntity::ActiveMotionDesc::Update(REAL elapsedTime, BOOL surpressControllers)
{
	// If paused, reset elapsed time to zero
	if(m_paused)
	{
		elapsedTime = 0.0f;
	}

	// Multiply elapsed time by the time multiplier
	elapsedTime *= m_timeMultiplier;

	// Update local motion time
	m_motionTime += elapsedTime;

	// Update controllers
	if(surpressControllers)
	{
		// When surpress controllers is enabled, only hierarchy controllers remain active
		for(INT32 i = 0; i < m_controllers.Count(); ++i)
		{
			if(m_controllers[i]->GetType() == CRuController_Hierarchy::Type)
			{
				m_controllers[i]->Enable(TRUE);
			}
			else
			{
				m_controllers[i]->Enable(FALSE);
			}

			m_controllers[i]->Update(elapsedTime);
		}
	}
	else
	{
		for(INT32 i = 0; i < m_controllers.Count(); ++i)
		{
			m_controllers[i]->Enable(TRUE);
			m_controllers[i]->Update(elapsedTime);
		}
	}

	// Update interpolators
	for(INT32 i = 0; i < m_interpolators.Count(); ++i)
	{
		// Update interpolator key frames
		m_interpolators[i].m_interpolator->UpdateAnimationTime(elapsedTime);
	}
}

void CRuACTEntity::ActiveMotionDesc::Mix(REAL elapsedTime)
{
	// If paused, reset elapsed time to zero
	if(m_paused)
	{
		elapsedTime = 0.0f;
	}

	if(m_motionID >= 0)
	{
		// Update interpolators
		for(INT32 i = 0; i < m_interpolators.Count(); ++i)
		{
			REAL leadInWeight = 1.0f;
			REAL leadOutWeight = 1.0f;

			if(m_leadIn > 0.0f)
			{
				if(m_motionTime < m_leadIn)
				{
					leadInWeight = max(0.0f, min(1.0f, m_motionTime / m_leadIn));
				}
			}

			if(m_leadOut > 0.0f)
			{
				REAL timeLeft = m_motionLength - m_motionTime;
				if(timeLeft < m_leadOut)
				{
					leadOutWeight = max(0.0f, min(1.0f, timeLeft / m_leadOut));
				}
			}

			// Mix interpolator into target hierarchy
			m_interpolators[i].m_boundHierarchy->GetInterpolator()->Mix(m_interpolators[i].m_interpolator, m_channel, m_channelWeight * leadInWeight * leadOutWeight);
		}
	}
	else
	{
		if(m_fadeDuration > 0.0f)
		{
			REAL weight = 1.0f - m_fadeTime / m_fadeDuration;

			// Update interpolators
			for(INT32 i = 0; i < m_interpolators.Count(); ++i)
			{
				// Mix interpolator into target hierarchy
				m_interpolators[i].m_boundHierarchy->GetInterpolator()->Mix(m_interpolators[i].m_interpolator, m_channel, weight * m_channelWeight);
			}

			// Update fade time
			m_fadeTime += elapsedTime;

			// If the motion fade is finished, reset all controllers and interpolators
			if(m_fadeTime >= m_fadeDuration)
			{
				m_fadeTime = 0.0f;
				m_fadeDuration = 0.0f;
			}
		}
	}
}

void CRuACTEntity::ActiveMotionDesc::UpdateMotionLength()
{
	// Find length of the animation, ignoring looping
	m_motionLength = 0.0f;
	for(INT32 i = 0; i < m_controllers.Count(); ++i)
	{
		m_motionLength = max(m_motionLength, m_controllers[i]->GetStartTime() + m_controllers[i]->GetDuration());
	}
}

BOOL CRuACTEntity::ActiveMotionDesc::IsMotionComplete() const
{
	REAL duration = 0.0f;

	// Find length of the animation
	for(INT32 i = 0; i < m_controllers.Count(); ++i)
	{
		// If the controller is a looping controller, the motion never completes
		if(m_controllers[i]->IsLooping())
		{
			return FALSE;
		}

		duration = max(duration, m_controllers[i]->GetStartTime() + m_controllers[i]->GetDuration());
	}

	// If local motion time exceeds duration, the motion is done
	if(m_motionTime >= duration)
	{
		return TRUE;
	}

	return FALSE;
}

void CRuACTEntity::ActiveMotionDesc::ClearMotion(REAL transitionDuration, REAL fadeDuration)
{
	if(m_motionID >= 0)
	{
		// Store fading parameters
		m_fadeTime = 0.0f;
		m_fadeDuration = fadeDuration;

		// Reset motion ID
		m_motionID = -1;

		// Reset all interpolators
		for(INT32 i = 0; i < m_interpolators.Count(); ++i)
		{
			m_interpolators[i].m_interpolator->ClearAnimations(transitionDuration);
		}

		// Remove all controllers
		for(INT32 i = 0; i < m_controllers.Count(); ++i)
		{
			ruSAFE_RELEASE(m_controllers[i]);
		}
		m_controllers.Clear();
	}
}

void CRuACTEntity::ActiveMotionDesc::Clear()
{
	m_motionID = -1;

	for(INT32 i = 0; i < m_interpolators.Count(); ++i)
	{
		ruSAFE_RELEASE(m_interpolators[i].m_interpolator);
	}

	for(INT32 i = 0; i < m_controllers.Count(); ++i)
	{
		ruSAFE_RELEASE(m_controllers[i]);
	}
}

// ************************************************************************************************************************************************************

CRuACTEntity::CRuACTEntity()
:	m_template(NULL),
	m_numRuntimeNodes(0),
	m_runtimeNodes(NULL),
	m_motionTime(0.0f)
{
	m_persistentMotion = ruNEW ActiveMotionDesc();
	m_persistentMotion->m_channel = 0xFFFFFFFF;
}

CRuACTEntity::~CRuACTEntity()
{
	// Release the ACT template
	ruSAFE_RELEASE(m_template);

	if(m_runtimeNodes)
	{
		for(INT32 i = 0; i < m_numRuntimeNodes; ++i)
		{
			delete m_runtimeNodes[i];
		}

		delete[] m_runtimeNodes;
	}

	delete m_persistentMotion;

	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		delete m_activeMotions[i];
	}
}

IRuObject *CRuACTEntity::Clone(IRuObject *clonedObject)
{
	if(m_template)
	{
		return m_template->Construct();
	}

	return NULL;
}

BOOL CRuACTEntity::SerializeFrom(IRuStream *stream)
{
	return TRUE;
}

BOOL CRuACTEntity::SerializeTo(IRuStream *stream)
{
	return TRUE;
}

BOOL CRuACTEntity::Update(REAL elapsedTime)
{
	CRuArrayList<INT32> playedMotions;

	// Update motion time marker
	m_motionTime += elapsedTime;

	// Update persistent motion
	m_persistentMotion->Update(elapsedTime, FALSE);

	// Update active motions
	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		BOOL surpressControllers = FALSE;

		for(INT32 j = 0; j < playedMotions.Count(); ++j)
		{
			if(playedMotions[j] == m_activeMotions[i]->m_motionID)
			{
				surpressControllers = TRUE;
				break;
			}
		}

		m_activeMotions[i]->Update(elapsedTime, surpressControllers);
		playedMotions.Add(m_activeMotions[i]->m_motionID);
	}

	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		m_activeMotions[i]->Mix(elapsedTime);
	}

	// Create any pending animation compensators
	CreatePendingAnimationCompensators();

	// Chain update call to parent class
	return CRuEntity_Container_Impl::Update(elapsedTime);
}

CRuACTTemplate *CRuACTEntity::GetTemplate()
{
	return m_template;
}

INT32 CRuACTEntity::GetNumRuntimeNodes() const
{
	return m_numRuntimeNodes;
}

CRuACTTemplateNode_Runtime *CRuACTEntity::GetRuntimeNode(INT32 index)
{
	if(index >= 0 && index < m_numRuntimeNodes)
	{
		return m_runtimeNodes[index];
	}

	return NULL;
}

CRuEntity *CRuACTEntity::FindEntity(INT32 parentIndex, INT32 connectivityIndex)
{
	if(parentIndex >= 0 && parentIndex < m_numRuntimeNodes && m_runtimeNodes[parentIndex])
	{
		return m_runtimeNodes[parentIndex]->GetChildEntity(connectivityIndex);
	}

	return NULL;
}

BOOL CRuACTEntity::UpdateTemplate(BOOL replaceTemplateNodes)
{
	// Iterate through template nodes and force each to update themselves
	for(INT32 i = 0; i < m_numRuntimeNodes; ++i)
	{
		if(m_runtimeNodes[i])
		{
			m_runtimeNodes[i]->UpdateTemplateNode(replaceTemplateNodes);
		}
	}

	return TRUE;
}

BOOL CRuACTEntity::PlayPersistentMotion(BOOL forceOverride)
{
	if(m_persistentMotion->m_motionID != 0 || forceOverride)
	{
		// Store new motion ID
		m_persistentMotion->m_motionID = 0;

		if(m_template)
		{
			// Get persistent motion (motion ID 0 is always the persistent motion)
			CRuACTMotion *motion = m_template->GetMotionByID(0);

			if(motion)
			{
				// Clear persistent motion
				m_persistentMotion->ClearMotion(0.0f, 0.0f);

				// Clear local motion time
				m_persistentMotion->m_motionTime = 0.0f;

				// Set channel weight, lead-in, and lead-out times
				m_persistentMotion->m_channelWeight = 1.0f;
				m_persistentMotion->m_leadIn = 0.0f;
				m_persistentMotion->m_leadOut = 0.0f;

				// Iterate through each motion node and create a controller for each
				for(INT32 i = 0; i < motion->GetNumMotionNodes(); ++i)
				{
					CRuACTMotionNode *motionNode = motion->GetMotionNode(i);
					IRuEntity_Controller *controller = motionNode->GetController();

					CRuEntity *targetEntity = FindEntity(motionNode->GetTemplateIndex(), motionNode->GetNodeIndex());

					if(targetEntity && controller)
					{
						// If the target entity is a hierarchy and the controller is a hierarchy controller, perform pose blend out
						if(targetEntity->GetType().IsSubClassOf(CRuFrameHierarchy::Type) && controller->GetType().IsSubClassOf(CRuController_Hierarchy::Type))
						{
							// Setup target
							controller->SetTarget(m_persistentMotion->GetInterpolator(static_cast<CRuFrameHierarchy *>(targetEntity)));

							// Force update with zero delta
							controller->Update(0.0f);

							// Add controller to the list of controllers
							m_persistentMotion->m_controllers.Add(controller);
						}
						else
						{
							// Setup target
							controller->SetTarget(targetEntity);

							// Force update with zero delta
							controller->Update(0.0f);

							// Add controller to the list of controllers
							m_persistentMotion->m_controllers.Add(controller);
						}
					}

				}

				// Update motion length
				m_persistentMotion->UpdateMotionLength();

				return TRUE;
			}
		}

		return FALSE;
	}

	return TRUE;
}

BOOL CRuACTEntity::PlayMotion(UINT32 channel, INT32 motionID, REAL blendTime, BOOL forceOverride, REAL channelWeight, REAL leadIn, REAL leadOut, REAL expectedMotionScale)
{
	if(m_template)
	{
		// Attempt to get motion data for the specified motion ID
		CRuACTMotion *motion = m_template->GetMotionByID(motionID);

		if(motion)
		{
			INT32 motionIndex = GetMotionDescriptorIndex(channel);
			ActiveMotionDesc *motionDesc = m_activeMotions[motionIndex];

			// Set the motion time multiplier
			if(expectedMotionScale == 0.0f || motion->GetMotionScale() == 0.0f)
			{
				motionDesc->m_timeMultiplier = 1.0f;
			}
			else
			{
				motionDesc->m_timeMultiplier = expectedMotionScale * motion->GetMotionScale();
			}

			// Play motion only if the existing motion is not the same, or if an override is forced
			if(motionDesc->m_motionID != motionID || forceOverride)
			{
				// Clear global motion time
				m_motionTime = 0.0f;

				// Clear motion
				motionDesc->ClearMotion(blendTime, 0.0f);

				// Store new motion ID
				motionDesc->m_motionID = motionID;

				// Clear local motion time
				motionDesc->m_motionTime = 0.0f;

				// Set channel weight
				motionDesc->m_channelWeight = channelWeight;

				// Set lead-in and lead-out times
				motionDesc->m_leadIn = leadIn;
				motionDesc->m_leadOut = leadOut;

				// Iterate through each motion node and create a controller for each
				for(INT32 i = 0; i < motion->GetNumMotionNodes(); ++i)
				{
					CRuACTMotionNode *motionNode = motion->GetMotionNode(i);
					IRuEntity_Controller *controller = motionNode->GetController();

					CRuEntity *targetEntity = FindEntity(motionNode->GetTemplateIndex(), motionNode->GetNodeIndex());

					if(targetEntity && controller)
					{
						// If the target entity is a hierarchy and the controller is a hierarchy controller, perform pose blend out
						if(targetEntity->GetType().IsSubClassOf(CRuFrameHierarchy::Type) && controller->GetType().IsSubClassOf(CRuController_Hierarchy::Type))
						{
							// Setup target
							controller->SetTarget(motionDesc->GetInterpolator(static_cast<CRuFrameHierarchy *>(targetEntity)));

							// Force update with zero delta
							controller->Update(0.0f);

							// Add controller to the list of controllers
							motionDesc->m_controllers.Add(controller);
						}
						else
						{
							// Setup target
							controller->SetTarget(targetEntity);

							// Force update with zero delta
							controller->Update(0.0f);

							// Add controller to the list of controllers
							motionDesc->m_controllers.Add(controller);
						}
					}

				}

				// Update motion length
				motionDesc->UpdateMotionLength();

				// Perform motion synchronization with existing motions
				SynchronizeMotion(motionIndex);
			}

			return TRUE;
		}
	}

	return FALSE;
}

BOOL CRuACTEntity::SwapMotion(UINT32 srcchannel, UINT32 destchannel)
{
	if(m_template)
	{
		INT32 srcMotionIndex = GetMotionDescriptorIndex(srcchannel);
		INT32 destMotionIndex = GetMotionDescriptorIndex(destchannel);
		ActiveMotionDesc *srcMotionDesc = m_activeMotions[srcMotionIndex];
		ActiveMotionDesc *destMotionDesc = m_activeMotions[destMotionIndex];

		m_activeMotions[srcMotionIndex] = destMotionDesc;
		m_activeMotions[destMotionIndex] = srcMotionDesc;

		srcMotionDesc->m_channel = destchannel;
		destMotionDesc->m_channel = srcchannel;
	}

	return FALSE;
}

BOOL CRuACTEntity::ClearMotion(UINT32 channel, REAL fadeDuration)
{
	// Search for the specified group ID mask among currently active motions
	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		if(m_activeMotions[i]->m_channel == channel)
		{
			m_activeMotions[i]->ClearMotion(0.0f, fadeDuration);

			return TRUE;
		}
	}

	return FALSE;
}

/*!
	Synchronizes the motion at syncIndex with any compatible motion on other channels.
	A compatible motion is defined as one which has the same motion ID as the one being
	synchronized. This function synchronizes time only. Controller timing will not
	necessarily be correct. It is also assumed that the motion to be synchronized has a
	motion time of zero.

	@param syncIndex Index of the motion to synchronize.

	@return TRUE if successful, FALSE otherwise.
*/
BOOL CRuACTEntity::SynchronizeMotion(INT32 syncIndex)
{
	if(syncIndex >= 0 && syncIndex < m_activeMotions.Count())
	{
		UINT32 channel = m_activeMotions[syncIndex]->m_channel;

		// If the motion has already been started, abort synchronization
		if(m_activeMotions[syncIndex]->m_motionTime > 0.0f)
		{
			return FALSE;
		}

		// Search for a compatible motion
		for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
		{
			if(i == syncIndex)
				continue;

			if(m_activeMotions[i]->m_motionID == m_activeMotions[syncIndex]->m_motionID)
			{
				m_activeMotions[syncIndex]->Synchronize(m_activeMotions[i]);
				return TRUE;
			}
		}
	}

	return FALSE;
}

BOOL CRuACTEntity::PauseChannel(UINT32 channel)
{
	// Search for the specified channel
	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		if(m_activeMotions[i]->m_channel == channel)
		{
			m_activeMotions[i]->m_paused = TRUE;
			return TRUE;
		}
	}

	return FALSE;
}

BOOL CRuACTEntity::ResumeChannel(UINT32 channel)
{
	// Search for the specified channel
	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		if(m_activeMotions[i]->m_channel == channel)
		{
			m_activeMotions[i]->m_paused = FALSE;
			return TRUE;
		}
	}

	return FALSE;
}

void CRuACTEntity::CreatePendingAnimationCompensators()
{
	for(INT32 i = 0; i < m_pendingAnimCompensators.Count(); ++i)
	{
		// Search for the specified target channel
		for(INT32 j = 0; j < m_activeMotions.Count(); ++j)
		{
			if(m_activeMotions[j]->m_channel == m_pendingAnimCompensators[i].m_targetChannel)
			{
				// Iterate through all interpolators in the target channel and find one that has the specified node ID
				for(INT32 k = 0; k < m_activeMotions[k]->m_interpolators.Count(); ++k)
				{
					INT32 subNodeIndex = m_activeMotions[j]->m_interpolators[k].m_boundHierarchy->GetSubNodeIndex(m_pendingAnimCompensators[i].m_nodeID);

					if(subNodeIndex >= 0)
					{
						// Search for the source channel
						for(INT32 l = 0; l < m_activeMotions.Count(); ++l)
						{
							if(m_activeMotions[l]->m_channel == m_pendingAnimCompensators[i].m_sourceChannel)
							{
								// Construct animation compensator
								CRuController_AnimCompensator *compController = ruNEW CRuController_AnimCompensator();
								compController->SetTarget(m_activeMotions[j]->m_interpolators[k].m_boundHierarchy);
								compController->SetCompensatorSource(m_activeMotions[l]->m_interpolators[k].m_interpolator, m_activeMotions[j]->m_interpolators[k].m_interpolator, m_pendingAnimCompensators[i].m_nodeID, m_pendingAnimCompensators[i].m_strength);

								// Add animation compensator to entity list
								this->AddChild(compController);

								// Release local reference to animation compensator
								ruSAFE_RELEASE(compController);

								// Remove from pending list
								m_pendingAnimCompensators.RemoveAt(i);
								--i;

								break;
							}
						}

						break;
					}
				}

				break;
			}
		}
	}
}

BOOL CRuACTEntity::CreateAnimationCompensator(UINT32 sourceChannel, UINT32 targetChannel, INT32 nodeID, REAL strength)
{
	AnimCompensatorDesc cmpDesc;

	cmpDesc.m_sourceChannel = sourceChannel;
	cmpDesc.m_targetChannel = targetChannel;
	cmpDesc.m_nodeID = nodeID;
	cmpDesc.m_strength = strength;

	m_pendingAnimCompensators.Add(cmpDesc);

	return TRUE;
}

INT32 CRuACTEntity::GetActiveMotionID(UINT32 channel) const
{
	// Search for the specified group ID mask among currently active motions
	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		if(m_activeMotions[i]->m_channel == channel)
		{
			return m_activeMotions[i]->m_motionID;
		}
	}

	return -1;
}

const char *CRuACTEntity::GetActiveMotionName(UINT32 channel) const
{
	// Scan through all active motion channels
	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		if(m_activeMotions[i]->m_channel == channel)
		{
			return m_template->GetMotionNameByID(m_activeMotions[i]->m_motionID);
		}
	}

	return "";
}

REAL CRuACTEntity::GetActiveMotionTime(UINT32 channel) const
{
	// Scan through all active motion channels
	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		if(m_activeMotions[i]->m_channel == channel)
		{
			return m_activeMotions[i]->m_motionTime;
		}
	}

	return -1.0f;
}

REAL CRuACTEntity::GetMotionTime() const
{
	return m_motionTime;
}

BOOL CRuACTEntity::IsActiveMotion(UINT32 channel, const char *motionName)
{
	INT32 motionID = m_template->GetMotionIDByName(motionName);

	if(motionID >= 0)
	{
		// Search for the specified group ID mask among currently active motions
		for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
		{
			if(m_activeMotions[i]->m_channel == channel)
			{
				return m_activeMotions[i]->m_motionID == motionID;
			}
		}
	}

	return FALSE;
}

BOOL CRuACTEntity::IsMotionComplete(UINT32 channel) const
{
	if(channel == 0xFFFFFFFF)
	{
		// If the specified group ID mask is 0xFFFFFFFF, then we make sure all motions are complete
		for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
		{
			if(m_activeMotions[i]->IsMotionComplete() == FALSE)
			{
				return FALSE;
			}
		}
	}
	else
	{
		// Search for the specified group ID mask among currently active motions
		for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
		{
			if(m_activeMotions[i]->m_channel == channel)
			{
				return m_activeMotions[i]->IsMotionComplete();
			}
		}
	}

	return TRUE;
}

INT32 CRuACTEntity::GetMotionIDByName(const char *motionName)
{
	return m_template->GetMotionIDByName(motionName);
}

BOOL CRuACTEntity::CheckForController(UINT32 channel, const CRuRTTI &controllerType)
{
	// Search for the specified channel mask among currently active motions
	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		if(m_activeMotions[i]->m_channel == channel)
		{
			for(INT32 j = 0; j < m_activeMotions[i]->m_controllers.Count(); ++j)
			{
				if(m_activeMotions[i]->m_controllers[j]->GetType() == controllerType)
				{
					return TRUE;
				}
			}
		}
	}

	return FALSE;
}

BOOL CRuACTEntity::CheckForEvent(UINT32 channel, RuEntityEvent eventType, REAL *startTimeOut)
{
	// Search for the specified channel mask among currently active motions
	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		if(m_activeMotions[i]->m_channel == channel)
		{
			for(INT32 j = 0; j < m_activeMotions[i]->m_controllers.Count(); ++j)
			{
				if(m_activeMotions[i]->m_controllers[j]->GetType() == CRuController_EventTrigger::Type)
				{
					if(static_cast<CRuController_EventTrigger *>(m_activeMotions[i]->m_controllers[j])->GetEventID() == eventType)
					{
						if(startTimeOut)
						{
							*startTimeOut = static_cast<CRuController_EventTrigger *>(m_activeMotions[i]->m_controllers[j])->GetStartTime();
						}

						return TRUE;
					}
				}
			}
		}
	}

	return FALSE;
}

INT32 CRuACTEntity::GetMotionDescriptorIndex(UINT32 channel)
{
	INT32 motionIndex = -1;
	ActiveMotionDesc *motionDesc = NULL;

	// Search for the specified group ID mask among currently active motions
	for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
	{
		if(m_activeMotions[i]->m_channel == channel)
		{
			motionIndex = i;
			motionDesc = m_activeMotions[i];
			break;
		}
	}

	// If no matching mask is found, create a new active motion descriptor
	if(motionDesc == NULL)
	{
		motionIndex = m_activeMotions.Count();
		motionDesc = ruNEW ActiveMotionDesc();
		motionDesc->m_channel = channel;

		// Find appropriate location for the motion descriptor
		for(INT32 i = 0; i < m_activeMotions.Count(); ++i)
		{
			if(m_activeMotions[i]->m_channel > motionDesc->m_channel)
			{
				m_activeMotions.Insert(i, motionDesc);
				return motionIndex;
			}
		}

		m_activeMotions.Add(motionDesc);
	}

	return motionIndex;
}

// ************************************************************************************************************************************************************

#pragma managed
