//
// Asura_Cutscene_Attachment.cpp
//
// Created by Richard Rice - 21/10/02
// 
// Copyright (C) 2002 Rebellion, All Rights Reserved.
//


#include "Core/Asura.h"

// This file's header.
#include "Cutscene/Asura_Cutscene_Attachment.h"

// Engine independent.
#include "Animation/Asura_Animation.h"
#include "Animation/Asura_AnimationAttachment.h"
#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_Timers.h"
#include "Cutscene/Asura_Cutscene.h"
#include "Cutscene/Asura_Cutscene_Actor.h"
#include "Debugging/Asura_Assert.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/ParticleGenerator/Asura_ClientEntity_PFX_Effect.h"
#ifdef ASURA_TARGET_SM3
#include "Entity/DynamicLight/Asura_ClientEntity_DynamicLight.h"
#else // #ifdef ASURA_TARGET_SM3
#include "Entity/PhysicalObject/Asura_ClientEntity_AdvancedLight.h"
#endif // #ifdef ASURA_TARGET_SM3
#include "Entity/Triggers/Asura_ClientEntity_SoundController.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Sound\Asura_Sound_Event_Storage.h"
#include "Sound\Asura_Sound_Event_System.h"
#include "SpecialFX/Asura_Corona_System.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"
#include "SpecialFX/PFX/Asura_PFX_Entity_Effect.h"

#if defined(ASURA_TOOLS)
#include "Particles/Tools_Mode_PFX.h"
#endif


///////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------**
** Asura_Cutscene_Attachment **
**---------------------------*/

Asura_Cutscene_Attachment* Asura_Cutscene_Attachment::AllocateAttachment(Asura_Cutscene_Actor_Hierarchy* pxActor,int iType)
{
    // First check if project code wants to handle this type of attachment.
    {
        Asura_Cutscene_Attachment* pxAttachment = Project_AllocateAttachment( pxActor, iType );
        if( pxAttachment ) { return pxAttachment; }
    }
    
	switch(iType)
	{
		case AsuraCutsceneAttachment_Hierarchy :
			return new Asura_Cutscene_Attachment_Hierarchy(pxActor);

		case AsuraCutsceneAttachment_Shape :
			return new Asura_Cutscene_Attachment_Shape(pxActor);

		case AsuraCutsceneAttachment_Actor :
			return new Asura_Cutscene_Attachment_Actor(pxActor);

		case AsuraCutsceneAttachment_Entity :
			return new Asura_Cutscene_Attachment_Entity(pxActor);
		
		case AsuraCutsceneAttachment_Corona :
			return new Asura_Cutscene_Attachment_Corona(pxActor);

		case AsuraCutsceneAttachment_PFX :
			return new Asura_Cutscene_Attachment_PFX(pxActor);

		case AsuraCutsceneAttachment_SoundEvent :
			return new Asura_Cutscene_Attachment_SoundEvent(pxActor);

	}
	Asura_Assert(0,"Unknown attachment type");
	return 0;
}


Asura_Cutscene_Attachment::Asura_Cutscene_Attachment(Asura_Cutscene_Actor_Hierarchy* pxActor)
{
	m_pxActor = pxActor;
	m_uFlags = AsuraCutsceneAttachmentFlag_InitiallyActive;
	m_bActive = false;
	m_uID = 0;
	m_pxAnimation = NULL;
}

Asura_Cutscene_Attachment::~Asura_Cutscene_Attachment()
{
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Attachment::Copy(Asura_Cutscene_Attachment* pxAttachment)
{
	m_xMarkerName = pxAttachment->m_xMarkerName;
	m_uFlags = pxAttachment->m_uFlags;
	m_xName = pxAttachment->m_xName;
	m_uID = pxAttachment->m_uID;
}
#endif //ASURA_TOOLS

bool Asura_Cutscene_Attachment::GetPosition(Asura_Vector_3& xPos,Asura_Matrix_3x3& xMat)
{
	Asura_Animation* pxParentAnimation = m_pxActor->GetAnimation();
	if(!pxParentAnimation) return false;
	if(m_xMarkerName.IsBlank()) return false;


	//get the relative position
	Asura_Matrix_3x3 xMatRel;
	Asura_Vector_3 xPosRel;	

	if(!pxParentAnimation->GetMarkerLocation(m_xMarkerName.GetString(),&xPosRel,&xMatRel)) return false;

	//and convert to world space
	xPos = m_pxActor->Position + xPosRel*m_pxActor->Orientation;
	xMat = m_pxActor->Orientation* xMatRel;

	return true;
}

void Asura_Cutscene_Attachment::SetMarkerName(Asura_String& xMarkerName)
{
	m_xMarkerName = xMarkerName;
}

void Asura_Cutscene_Attachment::Initialise()
{
	if(m_uFlags & AsuraCutsceneAttachmentFlag_InitiallyActive)
	{
		Activate(true);
	}
}

void Asura_Cutscene_Attachment::Finish()
{
	Activate(false);
}

void Asura_Cutscene_Attachment::Activate(bool bActive)
{
	if(m_bActive != bActive)
	{
		m_bActive = bActive;
		HandleActiveStatusChange();
	}
    
    // Check that the marker point we're supposed to be attached to actually
    // exists.
	// DF - 26/09/07 - Don't really care if its turning off...causing problems with attachments on attachments.
    if( m_pxActor->GetAnimation() && !m_xMarkerName.IsBlank() && bActive)
    {
		#if defined ASURA_ASSERT
	        const char* szAttachmentName = ( m_xName.IsBlank() ? "<unnamed>" : m_xName.GetString() );
		#endif

        Asura_Assertf( m_pxActor->GetAnimation()->HasMarker(m_xMarkerName.GetString()),
                       ("Cutscene attachment '%s' is attached to non-existent marker point '%s' on skin '%s'.", szAttachmentName, m_xMarkerName.GetString(), m_pxActor->GetSkinName()) );
    }
}

bool Asura_Cutscene_Attachment::IsPlayingOnServer()
{
	Asura_Cutscene* pxCutscene = GetCutscene();
	#ifdef ASURA_TOOLS
	if(pxCutscene->ToolsPlayback) return false;
	#endif
	return pxCutscene->PlaysOnServer;
}


///////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------------**
** Asura_Cutscene_Attachment_Hierarchy **
**-------------------------------------*/

Asura_Cutscene_Attachment_Hierarchy::Asura_Cutscene_Attachment_Hierarchy(Asura_Cutscene_Actor_Hierarchy* pxActor)
:Asura_Cutscene_Attachment(pxActor)
{
	for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
	{
		m_aiSkinID[i] = -1;
	}
	m_iAnimID = -1;
}

Asura_Cutscene_Attachment_Hierarchy::~Asura_Cutscene_Attachment_Hierarchy()
{
	Asura_Assert(!m_pxAnimation,"Should already have been deallocated");
}

void Asura_Cutscene_Attachment_Hierarchy::Initialise()
{
    //Make sure we have a skin and animation name for this
	if(!IsPlayingOnServer())
	{
    	if(m_aiSkinID[0] != -1)
		{
			Asura_String & xSkinName = GetCutscene()->SkinNames.GetName(m_aiSkinID[0]);
    		if(xSkinName.GetLength())
    		{
				if(m_iAnimID!=-1)
				{
					Asura_String & xAnimationName = GetCutscene()->AnimationNames.GetName(m_iAnimID);
    				if(xAnimationName.GetLength())
					{
						//initialise the animation
						if( !m_pxAnimation )
						{
							m_pxAnimation = new Asura_Animation( xSkinName.GetString() );
	    					Asura_Assert(m_pxAnimation, "");
						}
						else
						{
							m_pxAnimation->SetSkin( xSkinName.GetString() );
						}

						//set additional skins
						for(int i=1; i<ASURA_ANIMATION_SKIN_COUNT; i++)
						{
							if(m_aiSkinID[i] == -1) continue;
							Asura_String& xOtherSkinName = GetCutscene()->SkinNames.GetName(m_aiSkinID[i]);
							if(xOtherSkinName.IsBlank()) continue;

							m_pxAnimation->SetSkin(xOtherSkinName.GetString(), i);
						}

    					m_pxAnimation->m_xPrimaryWrapper.StartAnim2(xAnimationName.GetHash(),AsuraAnimationFlag_Loop);
					}
				}
			}
		}
	}

	Asura_Cutscene_Attachment::Initialise();

}

void Asura_Cutscene_Attachment_Hierarchy::Finish()
{
	Asura_Cutscene_Attachment::Finish();

	delete m_pxAnimation;
	m_pxAnimation = 0;

}

void Asura_Cutscene_Attachment_Hierarchy::HandleActiveStatusChange()
{
	if(m_xMarkerName.IsBlank()) return;

	if(IsPlayingOnServer())
	{
#ifdef ASURA_SUPPORT_SERVERCUTSCENES
		Asura_ServerCutscene_Actor_Hierarchy* pxActor = (Asura_ServerCutscene_Actor_Hierarchy*)m_pxActor;
		if(m_bActive)
		{
			Asura_Hash_ID uMarkerHash = Asura_GetHashID(m_xMarkerName.GetString());
			pxActor->AddAttachment_Hierarchy(m_uID,uMarkerHash,m_aiSkinID[i],m_iAnimID);
		}
		else
		{
			pxActor->RemoveAttachment(m_uID);
		}
#else // not #if ASURA_SUPPORT_SERVERCUTSCENES
		Asura_Assert(false, "server cutscenes not supported");
#endif // #else not #if ASURA_SUPPORT_SERVERCUTSCENES
	}
	else
	{
		//get the animation to link/unlink with
		Asura_Animation* pxParentAnimation = m_pxActor->GetAnimation();
		if(!pxParentAnimation) return;
		if(!m_pxAnimation) return;

		if(m_bActive)
		{
			pxParentAnimation->LinkAnimation(m_pxAnimation,m_xMarkerName.GetString(),false);
		}
		else
		{
			pxParentAnimation->UnlinkAnimation(m_pxAnimation);
		}
	}
}




#ifdef ASURA_TOOLS
void Asura_Cutscene_Attachment_Hierarchy::Copy(Asura_Cutscene_Attachment* _pxAttachment)
{
	Asura_Cutscene_Attachment_Hierarchy* pxAttachment = (Asura_Cutscene_Attachment_Hierarchy*) _pxAttachment;
	Asura_Cutscene_Attachment::Copy(pxAttachment);

	for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
		{
		if(pxAttachment->m_aiSkinID[i]==-1)
		{
			m_aiSkinID[i] = -1;
		}
		else
		{
			Asura_String& xName = pxAttachment->m_pxActor->Cutscene->SkinNames.GetName(pxAttachment->m_aiSkinID[i]);
			Asura_String& xFileName = pxAttachment->m_pxActor->Cutscene->SkinFileNames.GetName(pxAttachment->m_aiSkinID[i]);

			m_aiSkinID[i] = m_pxActor->Cutscene->SkinNames.AddName(xName);

			#if defined ASURA_ASSERT
				int iIndex =
			#endif
				m_pxActor->Cutscene->SkinFileNames.AddName(xFileName);

			Asura_Assert(iIndex==m_aiSkinID[i],"");
		}
	}

	if(pxAttachment->m_iAnimID==-1)
	{
		m_iAnimID = -1;
	}
	else
	{
		Asura_String& xName = pxAttachment->m_pxActor->Cutscene->AnimationNames.GetName(pxAttachment->m_iAnimID);
		Asura_String& xFileName = pxAttachment->m_pxActor->Cutscene->AnimationFileNames.GetName(pxAttachment->m_iAnimID);

		m_iAnimID = m_pxActor->Cutscene->AnimationNames.AddName(xName);

		#if defined ASURA_ASSERT
			int iIndex =
		#endif
			m_pxActor->Cutscene->AnimationFileNames.AddName(xFileName);

		Asura_Assert(iIndex==m_iAnimID,"");
	}
	
	
}
#endif //ASURA_TOOLS

void Asura_Cutscene_Attachment_Hierarchy::PreDestructionCleanup()
{

	Asura_Cutscene* pxCutscene = GetCutscene();
	Asura_Assert(pxCutscene,"");

	//need to remove the anim and skins from the cutscene's name lists
	if(m_iAnimID!=-1)
	{
		pxCutscene->AnimationNames.RemoveName(m_iAnimID);
		pxCutscene->AnimationFileNames.RemoveName(m_iAnimID);
		m_iAnimID = -1;
	}
	for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
	{
		if(m_aiSkinID[i]!=-1)
		{
			pxCutscene->SkinNames.RemoveName(m_aiSkinID[i]);
			pxCutscene->SkinFileNames.RemoveName(m_aiSkinID[i]);
			m_aiSkinID[i] = -1;
		}
	}

	Asura_Cutscene_Attachment::PreDestructionCleanup();
}

#ifdef ASURA_TOOLS
const char* Asura_Cutscene_Attachment_Hierarchy::GetSkinName(int iSkinIndex) const
{
	if(m_aiSkinID[iSkinIndex]==-1) return 0;
	Asura_String& xSkinName = m_pxActor->Cutscene->SkinNames.GetName(m_aiSkinID[iSkinIndex]);
	return xSkinName.IsBlank() ? 0 : xSkinName.GetString();
}
#endif

void Asura_Cutscene_Attachment_Hierarchy::InvalidateBonesUpdateFrameStamp()
{
	if(m_pxAnimation) m_pxAnimation->InvalidateBonesUpdateFrameStamp();
}

#ifdef ASURA_TOOLS
bool Asura_Cutscene_Attachment_Hierarchy::UsesSkin(int iSkinID) const
{
	for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
	{
		if(m_aiSkinID[i] == iSkinID)
		{
			return true;	
		}
	}
	return false;
}
#endif //ASURA_TOOLS

////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------------**
** Asura_Cutscene_Attachment_Shape **
**---------------------------------*/

Asura_Cutscene_Attachment_Shape::Asura_Cutscene_Attachment_Shape(Asura_Cutscene_Actor_Hierarchy* pxActor)
:Asura_Cutscene_Attachment(pxActor)
{
}

Asura_Cutscene_Attachment_Shape::~Asura_Cutscene_Attachment_Shape()
{
}

void Asura_Cutscene_Attachment_Shape::Initialise()
{
	// (AW - 30.05.2004) - When SetShape is first called the shape table isn't set up yet.
	// Refresh the shape in the shape instance here.
	Asura_Hash_ID uShapeID = m_xShapeInstance.GetShapeHashID();
	m_xShapeInstance.SetShape(ASURA_HASH_ID_UNSET);
	m_xShapeInstance.SetShape(uShapeID);
	Asura_Cutscene_Attachment::Initialise();
}


void Asura_Cutscene_Attachment_Shape::Render()
{
	if(!m_bActive) return;

	#ifdef ASURA_TOOLS
	if(GetCutscene()->m_bRecordingWavFile) return;
	#endif


	Asura_Matrix_3x3 xMat;
	Asura_Vector_3 xPos;

	if(GetPosition(xPos,xMat))
	{
		if (GetCutscene() &&
			GetCutscene()->m_iCutsceneFlags & AsuraCutsceneFlag_IgnoreModuleLinking)
		{
			m_xShapeInstance.AddFlags(ASURA_SHAPE_INSTANCE_IGNORE_MOD_LINKING);
		}
		else
		{
			m_xShapeInstance.RemoveFlags(ASURA_SHAPE_INSTANCE_IGNORE_MOD_LINKING);
		}
		m_xShapeInstance.SetNewPosAndOri(xPos,xMat);
		m_xShapeInstance.Render();
	}
}

void Asura_Cutscene_Attachment_Shape::RenderShadow()
{
	if( !m_bActive ) return;

	#if defined ASURA_TOOLS
		if( GetCutscene()->m_bRecordingWavFile ) return;
	#endif

	m_xShapeInstance.RenderShadow();
}

void Asura_Cutscene_Attachment_Shape::SetShape(Asura_Hash_ID xShapeHashID)
{
	if(xShapeHashID != ASURA_HASH_ID_UNSET)
	{
		m_xShapeInstance.SetShape(xShapeHashID);
	}
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Attachment_Shape::Copy(Asura_Cutscene_Attachment* _pxAttachment)
{
	Asura_Cutscene_Attachment_Shape* pxAttachment = (Asura_Cutscene_Attachment_Shape*) _pxAttachment;
	Asura_Cutscene_Attachment::Copy(pxAttachment);

	m_xShapeInstance.SetShape(pxAttachment->m_xShapeInstance.GetShapeHashID());
}

void Asura_Cutscene_Attachment_Shape::ListShapesUsed(List<Asura_Hash_ID>& xShapeList)
{
	Asura_Hash_ID uShapeID = m_xShapeInstance.GetShapeHashID();
	if(uShapeID != ASURA_HASH_ID_UNSET)
	{
		xShapeList.add_entry(uShapeID);
	}
}

#endif

void Asura_Cutscene_Attachment_Shape::HandleActiveStatusChange()
{
#ifdef ASURA_SUPPORT_SERVERCUTSCENES
	if(IsPlayingOnServer())
	{
		Asura_ServerCutscene_Actor_Hierarchy* pxActor = (Asura_ServerCutscene_Actor_Hierarchy*)m_pxActor;
		if(m_bActive)
		{
			Asura_Hash_ID uMarkerHash = Asura_GetHashID(m_xMarkerName.GetString());
			pxActor->AddAttachment_Shape(m_uID,uMarkerHash,m_xShapeInstance.GetShapeHashID());
		}
		else
		{
			pxActor->RemoveAttachment(m_uID);
		}
	}
#endif // #ifdef ASURA_SUPPORT_SERVERCUTSCENES
}


///////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------------**
** Asura_Cutscene_Attachment_Actor **
**---------------------------------*/

Asura_Cutscene_Attachment_Actor::Asura_Cutscene_Attachment_Actor(Asura_Cutscene_Actor_Hierarchy* pxActor)
:Asura_Cutscene_Attachment(pxActor)
{
	m_pxAttachedActor = 0;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Attachment_Actor::Copy(Asura_Cutscene_Attachment* _pxAttachment)
{
	Asura_Cutscene_Attachment_Actor* pxAttachment = (Asura_Cutscene_Attachment_Actor*) _pxAttachment;
	Asura_Cutscene_Attachment::Copy(pxAttachment);

	m_xAttachedActorName = pxAttachment->m_xAttachedActorName;
}
#endif //ASURA_TOOLS

void Asura_Cutscene_Attachment_Actor::Initialise()
{
	//find the actor for this
	m_pxAttachedActor = 0;

	Asura_Cutscene* pxCutscene = GetCutscene();

	for(int i=0;i<pxCutscene->NumberOfActors;i++)
	{
	
		if(m_xAttachedActorName == pxCutscene->Actors[i]->ActorName)
		{
			m_pxAttachedActor = (Asura_Cutscene_Actor*) pxCutscene->Actors[i];
			break;
		}
		
	}
	

	Asura_Cutscene_Attachment::Initialise();
}

void Asura_Cutscene_Attachment_Actor::HandleActiveStatusChange()
{
	if(!m_pxAttachedActor) return;

	if(IsPlayingOnServer())
	{
#ifdef ASURA_SUPPORT_SERVERCUTSCENES
		Asura_ServerCutscene_Actor_Hierarchy* pxActor = (Asura_ServerCutscene_Actor_Hierarchy*)m_pxActor;
		if(m_bActive)
		{
			Asura_Hash_ID uMarkerHash = Asura_GetHashID(m_xMarkerName.GetString());
			pxActor->AddAttachment_Actor(m_uID,uMarkerHash,m_pxAttachedActor->m_uGuid);
		}
		else
		{
			pxActor->RemoveAttachment(m_uID);
		}
#else // not #if ASURA_SUPPORT_SERVERCUTSCENES
		Asura_Assert(false, "server cutscenes not supported");
#endif // #else not #if ASURA_SUPPORT_SERVERCUTSCENES
	}
	else
	{
		if(m_bActive && m_pxAttachedActor->m_bAttached)
		{
			Asura_Assert(0,"Actor is being used by 2 attachments at once");
			m_bActive = false;
		}
		m_pxAttachedActor->m_bAttached = m_bActive;
	}
}

void Asura_Cutscene_Attachment_Actor::Update(float fFrameTime)
{
	if(!m_bActive) return;

	if(m_pxAttachedActor)
	{
		//Update the actor's position/orientation
		Asura_Matrix_3x3 xMat;
		Asura_Vector_3 xPos;

		if(GetPosition(xPos,xMat))
		{
			m_pxAttachedActor->Position = xPos;
			m_pxAttachedActor->Orientation = xMat;
		}
	}
}

void Asura_Cutscene_Attachment_Actor::Render()
{
	if(!m_bActive) return;

	if(m_pxAttachedActor)
	{
		//Render attached actor. Temporarily disabling m_bAttached flag so that it knows not to return immediately
		//There isn't really  any reason to force it to be done here now ,but 
		//leaving it alone for the moment in case it affects derived projects. RAR 23/09/2005
		m_pxAttachedActor->m_bAttached = false;
		m_pxAttachedActor->Render();
		m_pxAttachedActor->m_bAttached = true;
	
	}
}

void Asura_Cutscene_Attachment_Actor::RenderShadow()
{
	if( !m_bActive || !m_pxAttachedActor ) return;

	m_pxAttachedActor->m_bAttached = false;
	m_pxAttachedActor->RenderShadow();
	m_pxAttachedActor->m_bAttached = true;
}

#ifdef ASURA_TOOLS
bool Asura_Cutscene_Attachment_Actor::NeedsUpdateBefore(const Asura_Cutscene_Actor* pxActor) const
{
	return m_xAttachedActorName == pxActor->ActorName;
}
#endif

///////////////////////////////////////////////////////////////////////////////////////////////

/*-------------------------------------**
** Asura_Cutscene_Attachement_Entity **
**-------------------------------------*/

Asura_Cutscene_Attachment_Entity::Asura_Cutscene_Attachment_Entity( Asura_Cutscene_Actor_Hierarchy* pxActor )
    : PARENT( pxActor )
    , m_xLastPosition( 0.0f, 0.0f, 0.0f )
	, m_uEntityGUID( ASURA_GUID_UNREGISTERED )
    , m_usEntityClassification( AsuraEntityClass_NotSet )
    , m_bHasUpdated( false )
#if _DEBUG
	, m_bEntityTypeAsserted( false )
#endif
{
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Attachment_Entity::Copy( Asura_Cutscene_Attachment* pxAttachment )
{
	PARENT::Copy( pxAttachment );

	const Asura_Cutscene_Attachment_Entity* pxEntityAttachment = static_cast<const Asura_Cutscene_Attachment_Entity*>( pxAttachment );
    
	m_uEntityGUID            = pxEntityAttachment->m_uEntityGUID;
    m_usEntityClassification = pxEntityAttachment->m_usEntityClassification;
}
#endif //ASURA_TOOLS


void Asura_Cutscene_Attachment_Entity::HandleActiveStatusChange()
{
    if( ASURA_GUID_UNREGISTERED == m_uEntityGUID ) { return; }

	if(IsPlayingOnServer())
	{
#ifdef ASURA_SUPPORT_SERVERCUTSCENES
		//Get server entity to add/remove attachment from the client entities
		Asura_ServerCutscene_Actor_Hierarchy* pxActor = (Asura_ServerCutscene_Actor_Hierarchy*)m_pxActor;
		if(m_bActive)
		{
			Asura_Hash_ID uMarkerHash = Asura_GetHashID(m_xMarkerName.GetString());
			pxActor->AddAttachment_Entity( m_uID, uMarkerHash, m_uEntityGUID );
		}
		else
		{
			pxActor->RemoveAttachment(m_uID);
		}
#else // not #if ASURA_SUPPORT_SERVERCUTSCENES
		Asura_Assert(false, "server cutscenes not supported");
#endif // #else not #if ASURA_SUPPORT_SERVERCUTSCENES
	}
	else
	{
		//find the attached particle generator , and activate/deactivate it

		bool bIgnoreModuleLinking = (GetCutscene()->m_iCutsceneFlags & AsuraCutsceneFlag_IgnoreModuleLinking) ? true : false;
		ActivateEntity( m_uEntityGUID, m_usEntityClassification, m_bActive, bIgnoreModuleLinking );

		if(m_bActive)
		{
			//make sure position is up to date , to prevent entity from being in wrong position for one frame
			m_bHasUpdated = false;
			UpdatePosition();
		}
	}
}

void Asura_Cutscene_Attachment_Entity::Initialise()
{
    // Find entity.
    Asura_Entity_Client* pxEntity = Asura_Entity_Client::FindEntity( m_uEntityGUID );
    if( !pxEntity )
    {
        m_usEntityClassification = AsuraEntityClass_NotSet;
        return;
    }

    m_usEntityClassification = pxEntity->GetClassification();   

#ifndef ASURA_TARGET_SM3
	//light entities need to be told that they are going to be positioned by the cutscene
    if( AsuraEntityClass_AdvancedLight == m_usEntityClassification )
    {
        Asura_ClientEntity_AdvancedLight::FindEntity(m_uEntityGUID)->SetCutsceneControlled( true );
    }
#endif // #ifndef ASURA_TARGET_SM3

	PARENT::Initialise();
}

void Asura_Cutscene_Attachment_Entity::Finish()
{
#ifndef ASURA_TARGET_SM3
    if( AsuraEntityClass_AdvancedLight == m_usEntityClassification )
    {
	    Asura_ClientEntity_AdvancedLight* pxLight = Asura_ClientEntity_AdvancedLight::FindEntity( m_uEntityGUID );
	    if( pxLight )
	    {
		    pxLight->SetCutsceneControlled( false );
	    }
    }
#endif // #ifndef ASURA_TARGET_SM3

	PARENT::Finish();
}


void Asura_Cutscene_Attachment_Entity::Render()
{
	UpdatePosition();
}

void Asura_Cutscene_Attachment_Entity::UpdatePosition()
{
    if( !m_bActive || ASURA_GUID_UNREGISTERED == m_uEntityGUID ) { return; }

	//Update the generator's position/orientation
	Asura_Matrix_3x3 xOri;
	Asura_Vector_3 xPos;

	if( GetPosition(xPos, xOri) )
	{
		Asura_Vector_3 xVelocity;
		xVelocity.Zero();

		if( m_bHasUpdated )
		{
			float fTime = Asura_Timers::GetGameTimeSlice();
			if( fTime >= 0.0001f )
			{
			    xVelocity = xPos - m_xLastPosition;
				xVelocity /= fTime;
			}
		}

        // Only bother moving it if it's, uh, moved.
        if( !m_bHasUpdated || !xPos.ApproximatelyEquals(m_xLastPosition) )
        {
		    bool bEntityTypeOkay = MoveEntity( m_uEntityGUID, m_usEntityClassification, xPos, xOri, &xVelocity,!m_bHasUpdated );
		    if( !bEntityTypeOkay )
            {
#if _DEBUG
                if( !m_bEntityTypeAsserted )
		        {
			        Asura_Assertf( false, ("Unexpected entity type for attachment.\nCutscene %s.\nAttachment %s",GetCutscene()->CutsceneName.GetString(), m_xName.GetString()) );
			        m_bEntityTypeAsserted = true;
		        }
#endif
            }
        }

		m_xLastPosition = xPos;
		m_bHasUpdated   = true;
	}
}

void Asura_Cutscene_Attachment_Entity::ActivateEntity( Asura_Guid uGUID, u_short usClassification, bool bActivate, bool bIgnoreModuleLinking )
{
	switch( usClassification )
	{

		case AsuraEntityClass_SoundController:
		{
            Asura_ClientEntity_SoundController* pxController = Asura_ClientEntity_SoundController::FindEntity( uGUID );
            if( pxController )
            {
			    if(bActivate)
					pxController->Trigger();//also activates it
				else
				    pxController->Activate(false);
            }
			break;
		}

		case AsuraEntityClass_PFX_Effect:
		{
            Asura_ClientEntity_PFX_Effect* pxPFXEffect = Asura_ClientEntity_PFX_Effect::FindEntity( uGUID );
            if( pxPFXEffect )
            {
			    pxPFXEffect->Activate( bActivate );
				//RAR 24/03/2005 - Only switch on ignore module linking here... leave unchanged
				//if the cutscene doesn't have that flag.
				if(bIgnoreModuleLinking)
				{
			    	pxPFXEffect->SetIgnoreModuleLinking( bIgnoreModuleLinking );
				}
            }
			break;
		}



#ifdef ASURA_TARGET_SM3
		case AsuraEntityClass_DynamicLight:
		{
			Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_ACTIVATE );
			if( pxMessage )
			{
				pxMessage->SetEntityClassification(AsuraEntityClass_DynamicLight);
				pxMessage->SetToGuid(uGUID);
				pxMessage->SetFromGuid(Asura_Network::GetClientID());

				Asura_Network::Send(pxMessage);
			}
			break;
		}
#else // #ifdef ASURA_TARGET_SM3
		case AsuraEntityClass_AdvancedLight:
		{
			Asura_ClientEntity_AdvancedLight* pxLight = Asura_ClientEntity_AdvancedLight::FindEntity( uGUID );
			if( pxLight )
			{
				pxLight->ClientSideActivate( bActivate );
			}
			break;
		}
#endif // ifdef ASURA_TARGET_SM3

		default:
			break;
	}
}

bool Asura_Cutscene_Attachment_Entity::MoveEntity( Asura_Guid uGUID,
                                                   u_short usClassification,
                                                   const Asura_Vector_3& xPos,
                                                   const Asura_Matrix_3x3& xOri,
                                                   const Asura_Vector_3* pxVelocity,
												   bool bFirstUpdate)
{
    switch( usClassification )
    {
        case AsuraEntityClass_SoundController:
        {
            Asura_ClientEntity_SoundController* pxController = Asura_ClientEntity_SoundController::FindEntity( uGUID );
            pxController->SetPosition( xPos );
            break;
        }



        case AsuraEntityClass_PFX_Effect:
        {
            Asura_ClientEntity_PFX_Effect* pxPFXEffect = Asura_ClientEntity_PFX_Effect::FindEntity( uGUID );
            if( pxPFXEffect )
            {
                pxPFXEffect->SetPositionAndOrientation( xPos, xOri, bFirstUpdate );
            }
            break;
        }
	
#ifdef ASURA_TARGET_SM3
		case AsuraEntityClass_DynamicLight:
		{
			Asura_ClientEntity_DynamicLight* pxLight = Asura_ClientEntity_DynamicLight::FindEntity(uGUID);
			if(pxLight)
			{
				pxLight->SetPositionAndOrientation(xPos, xOri);
			}
			break;
		}
#else // #ifdef ASURA_TARGET_SM3
        case AsuraEntityClass_AdvancedLight:
        {
            Asura_ClientEntity_AdvancedLight* pxLight = Asura_ClientEntity_AdvancedLight::FindEntity( uGUID );
            if( pxLight )
            {
                pxLight->SetPositionAndOrientation( xPos, xOri );
            }
            break;
        }
#endif // #ifdef ASURA_TARGET_SM3

        default:
            return false;
    }

	return true;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Attachment_Entity::ResetGuidsAndMessages()
{
	m_uEntityGUID = ASURA_GUID_UNREGISTERED;
}
#endif

///////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------------**
** Asura_Cutscene_Attachment_Corona **
**----------------------------------*/

Asura_Cutscene_Attachment_Corona::Asura_Cutscene_Attachment_Corona(Asura_Cutscene_Actor_Hierarchy* pxActor)
:Asura_Cutscene_Attachment(pxActor)
{
	m_fRed = 0.5f;
	m_fGreen = 0.5f;
	m_fBlue = 0.5f;
	m_fSize = 0.25f;
	m_uCoronaFlags = AsuraCoronaAttachmentFlag_UseOrientation;
	m_iTextureID = ASURA_TEXTURE_NONE;
	m_iTextureIDForSave = ASURA_TEXTURE_NONE;

	m_pxAnimationAttachment = 0;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Attachment_Corona::Copy(Asura_Cutscene_Attachment* _pxAttachment)
{
	Asura_Cutscene_Attachment_Corona* pxAttachment = (Asura_Cutscene_Attachment_Corona*) _pxAttachment;
	Asura_Cutscene_Attachment::Copy(pxAttachment);

	m_fRed = pxAttachment->m_fRed;
	m_fGreen = pxAttachment->m_fGreen;
	m_fBlue = pxAttachment->m_fBlue;
	m_fSize = pxAttachment->m_fSize;
	m_uCoronaFlags = pxAttachment->m_uCoronaFlags;
	m_iTextureID = pxAttachment->m_iTextureID;

}

void Asura_Cutscene_Attachment_Corona::BuildUsedTextureTree(Asura_NameTree* pxNameTree)
{
	if(m_iTextureID == ASURA_TEXTURE_NONE)
	{
		m_iTextureIDForSave = ASURA_TEXTURE_NONE;
	}
	else
	{
		//get the texture name
		const Asura_String* pxName = Asura_Texture_Management::GetTextureName(m_iTextureID);
		Asura_Assert(pxName,"");

		//Add to the name tree for saving if needed , and get the saving index
		pxNameTree->FindNodeIndexByName(pxName->GetString(),true,m_iTextureIDForSave);
	}
}

#endif //ASURA_TOOLS


void Asura_Cutscene_Attachment_Corona::HandleActiveStatusChange()
{
	if(m_bActive)
	{
		Asura_Animation* pxParentAnimation = m_pxActor->GetAnimation();
		if(!pxParentAnimation) return;
		if(m_iTextureID == ASURA_TEXTURE_NONE) return;

		//create the corona
		Asura_Handle iHandle = Asura_Corona_System::CreateCorona();
		if(iHandle==ASURA_INVALID_HANDLE) return;

		Asura_Corona_System::SetRadius(iHandle,m_fSize);
		Asura_Corona_System::SetTextureIndex(iHandle,m_iTextureID);
		Asura_Corona_System::SetColour(iHandle,m_fRed,m_fGreen,m_fBlue);
		Asura_Corona_System::SetFlag_OnlyDrawIfPositionSetPerFrame(iHandle,true);
		Asura_Corona_System::SetFlag_UseDirectionEffect(iHandle,m_uCoronaFlags & AsuraCoronaAttachmentFlag_UseOrientation);
		Asura_Corona_System::SetFlag_DontFadeWithDistance(iHandle,true);

		//create the corona attachment
		m_pxAnimationAttachment = new Asura_AnimationAttachment_Corona(iHandle,true);

		//and attach
		m_pxAnimationAttachment->LinkAttachment(pxParentAnimation,m_xMarkerName.GetString(),false);

	}
	else if(m_pxAnimationAttachment)
	{
		m_pxAnimationAttachment->UnlinkAttachment();
		Asura_Corona_System::DestroyCorona(m_pxAnimationAttachment->GetCoronaHandle());
		delete m_pxAnimationAttachment;
		m_pxAnimationAttachment = 0;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------**
** Asura_Cutscene_Attachment_PFX **
**-------------------------------*/

Asura_Cutscene_Attachment_PFX::Asura_Cutscene_Attachment_PFX(Asura_Cutscene_Actor_Hierarchy* pxActor)
:Asura_Cutscene_Attachment(pxActor)
{
	m_uEffectTemplateHash = ASURA_HASH_ID_UNSET;
	m_iEffectID = ASURA_PFX_INVALID_EFFECT_ID;
	m_bHasUpdatedPosition = false;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Attachment_PFX::Copy(Asura_Cutscene_Attachment* _pxAttachment)
{
	Asura_Cutscene_Attachment_PFX* pxAttachment = (Asura_Cutscene_Attachment_PFX*) _pxAttachment;
	Asura_Cutscene_Attachment::Copy(pxAttachment);

	m_uEffectTemplateHash = pxAttachment->m_uEffectTemplateHash;

}

void Asura_Cutscene_Attachment_PFX::AddExtraFilesToList( List<Asura_String>& xNameList ) const
{
	if(m_uEffectTemplateHash)
	{
		Tools_Mode_PFX::AddTemplateToExportListByHash(&xNameList,m_uEffectTemplateHash,0);
	}	
}
#endif //ASURA_TOOLS

void Asura_Cutscene_Attachment_PFX::Finish()
{
	//Do an immediate stop on the effect if still active
	if(m_iEffectID != ASURA_PFX_INVALID_EFFECT_ID)
	{
		Asura_PFX_Entity_Effect* pxEffect = (Asura_PFX_Entity_Effect*)Asura_PFX_System::GetEffect(m_iEffectID);
		if(pxEffect)
		{
			pxEffect->Stop(true);
		}
		m_iEffectID = ASURA_PFX_INVALID_EFFECT_ID;
	} 

	Asura_Cutscene_Attachment::Finish();
}

void Asura_Cutscene_Attachment_PFX::HandleActiveStatusChange()
{
	if(m_bActive)
	{
		//make sure any previous effect is fully stopped
		if(m_iEffectID != ASURA_PFX_INVALID_EFFECT_ID)
		{
			Asura_PFX_Entity_Effect* pxEffect = (Asura_PFX_Entity_Effect*)Asura_PFX_System::GetEffect(m_iEffectID);
			if(pxEffect)
			{
				pxEffect->Stop(true);
			}
			m_iEffectID = ASURA_PFX_INVALID_EFFECT_ID;
		} 
		
		if(m_uEffectTemplateHash)
		{
			//create the effect , lying about the position for now
			m_iEffectID = Asura_PFX_Entity_Effect::Create(Asura_Maths::ZeroVector3, Asura_Maths::IdentityMatrix3x3, m_uEffectTemplateHash);

			Asura_PFX_Entity_Effect* pxEffect = (Asura_PFX_Entity_Effect*)Asura_PFX_System::GetEffect(m_iEffectID);
			if(pxEffect)
			{
				bool bIgnoreModuleLinking = (GetCutscene()->m_iCutsceneFlags & AsuraCutsceneFlag_IgnoreModuleLinking) ? true : false;
				if(bIgnoreModuleLinking)
				{
					pxEffect->SetIgnoreModuleLinking(bIgnoreModuleLinking);
				}
			}
			//set the position
			m_bHasUpdatedPosition = false;
			UpdatePosition();
		}
	}
	else if (m_iEffectID != ASURA_PFX_INVALID_EFFECT_ID)
	{
		Asura_PFX_Entity_Effect* pxEffect = (Asura_PFX_Entity_Effect*)Asura_PFX_System::GetEffect(m_iEffectID);
		if(pxEffect)
		{
			pxEffect->Stop(false);
		}
		
	}
}

void Asura_Cutscene_Attachment_PFX::Update(float fTime)
{
	Asura_Cutscene_Attachment::Update(fTime);

	UpdatePosition();
}

void Asura_Cutscene_Attachment_PFX::UpdatePosition()
{
	if(!m_bActive) return;

	if (m_iEffectID != ASURA_PFX_INVALID_EFFECT_ID)
	{
		Asura_PFX_Entity_Effect* pxEffect = (Asura_PFX_Entity_Effect*)Asura_PFX_System::GetEffect(m_iEffectID);
		if(pxEffect)
		{
			Asura_Matrix_3x3 xMat;
			Asura_Vector_3 xPos;

			if(GetPosition(xPos,xMat))
			{
				pxEffect->SetPosition(xPos,xMat,!m_bHasUpdatedPosition);
				m_bHasUpdatedPosition = true;

			}
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////
/*--------------------------------------**
** Asura_Cutscene_Attachment_SoundEvent **
**--------------------------------------*/

Asura_Cutscene_Attachment_SoundEvent::Asura_Cutscene_Attachment_SoundEvent(Asura_Cutscene_Actor_Hierarchy* pxActor)
:Asura_Cutscene_Attachment(pxActor)
{
	m_uSoundEvent = ASURA_HASH_ID_UNSET;
	m_iSoundHandle = ASURA_INVALID_HANDLE;
	m_bStartSoundWithNextPositionUpdate = false;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Attachment_SoundEvent::Copy(Asura_Cutscene_Attachment* _pxAttachment)
{
	Asura_Cutscene_Attachment_SoundEvent* pxAttachment = (Asura_Cutscene_Attachment_SoundEvent*) _pxAttachment;
	Asura_Cutscene_Attachment::Copy(pxAttachment);

	m_uSoundEvent = pxAttachment->m_uSoundEvent;

}

void Asura_Cutscene_Attachment_SoundEvent::AddExtraFilesToList( List<Asura_String>& xNameList ) const
{
	if(m_uSoundEvent)
	{
		Tools_Level_Export::AddSoundEventToExportList(m_uSoundEvent, "Cutscene Attachment");
	}	
}
#endif //ASURA_TOOLS

void Asura_Cutscene_Attachment_SoundEvent::Finish()
{
	//stop the sound if still active
	if(m_iSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Sound_Event_System::Stop(m_iSoundHandle);
		m_iSoundHandle = ASURA_INVALID_HANDLE;
	} 
	m_bStartSoundWithNextPositionUpdate = false;

	Asura_Cutscene_Attachment::Finish();
}

void Asura_Cutscene_Attachment_SoundEvent::HandleActiveStatusChange()
{
	if(m_bActive)
	{
		if(m_uSoundEvent)
		{
			//Not certain that the position will always be immediately available , so flagging the sound
			//to start during the first update where it is valid.
			m_bStartSoundWithNextPositionUpdate = true;
			UpdatePosition();
		}
	}
	else
	{
		if(m_iSoundHandle != ASURA_INVALID_HANDLE)
		{
			Asura_Sound_Event_System::Stop(m_iSoundHandle);
			m_iSoundHandle = ASURA_INVALID_HANDLE;
		} 
		m_bStartSoundWithNextPositionUpdate = false;
	}
}

void Asura_Cutscene_Attachment_SoundEvent::Update(float fTime)
{
	Asura_Cutscene_Attachment::Update(fTime);

	UpdatePosition();
}

void Asura_Cutscene_Attachment_SoundEvent::UpdatePosition()
{
	if(!m_bActive) return;

	if (m_bStartSoundWithNextPositionUpdate || m_iSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Matrix_3x3 xMat;
		Asura_Vector_3 xPos;
		if(!GetPosition(xPos,xMat)) return;

		if(m_iSoundHandle == ASURA_INVALID_HANDLE && m_uSoundEvent)
		{
			//Start the sound
			const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::FindEvent(m_uSoundEvent);
			if(pxEvent)
			{
				Asura_Sound_Event_System::PlayEvent(pxEvent, &m_iSoundHandle, &xPos);
			}
		}
		else
		{
			Asura_Sound_Event_System::UpdatePosition(m_iSoundHandle, xPos);
		}

		m_bStartSoundWithNextPositionUpdate = false;
	}
}


