#include "Core/Asura.h"

#include "Animation/Asura_Animation.h"
#include "Camera/Asura_Camera.h"
#include "ChunkSystem/Asura_Chunk_Cutscene.h"
#include "Core/Asura_Graph.h"
#include "Core/Asura_Timers.h"
#include "Core/Asura_TiledTextureManagement.h"
#include "Cutscene/Asura_Cutscene_Event.h"
#include "Cutscene/Asura_Cutscene_Actor.h"
#include "Cutscene/Asura_Cutscene.h"
#include "Cutscene/Asura_Cutscene_System.h"
#include "Debugging/Asura_Assert.h"
#include "Entity/Asura_StaticMessage_System.h"
#include "GUIMenu/Asura_GUIMenu_System.h"
#include "GUIMenu/Asura_GUIMenu_Menu_Page.h"
#include "Rendering/Asura_Render.h"
#include "SpecialFX/Asura_Beam_System.h"
#include "SpecialFX/Asura_FullScreenSFX_System.h"


#ifdef ASURA_TOOLS
#if defined(ASURA_USE_FACIAL_ANIMATION_SYSTEM)
#include "FacialAnimation/Tools_FacialAnimation_System.h"
#endif
#include "Sound/Asura_Audio_System.h"
#include "Sound/Asura_Audio_Voice.h"
#include "Sound/Asura_Sound.h"
#include "Sound/Tools_PickSound.h"
#endif




///////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------**
** Asura_Cutscene_Event **
**----------------------*/

Asura_Cutscene_Event::Asura_Cutscene_Event(int iEventType, Asura_Cutscene* pxCutscene)
{
	m_iType = iEventType;
	m_fTriggerTime = 0;
	m_pxTargetActor = 0;
	m_xTargetActorName = "";
	m_iParentEventIndex = -1;
	m_pxCutscene = pxCutscene;
	m_iAnimationFlag = 1;
	m_pxAnimationFlagActor = NULL;
	m_xAnimationFlagActorName = "";
	m_uEventFlags = 0;
	m_iEnableID = 0;

	#ifdef ASURA_TOOLS
	m_xName = "";
	m_xComment = "";
	m_xPosition.Zero();
	m_xNameOfSourceTemplate = "";
	m_xNameOfEventWrapper = "";
	m_xRelativePosition.Zero();
	#endif
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event::Copy(Asura_Cutscene_Event* pxEvent)
{
	Asura_Assert(m_iType == pxEvent->m_iType,"");//can only copy between the same event type

	m_fTriggerTime = pxEvent->m_fTriggerTime;
	m_xTargetActorName = pxEvent->m_xTargetActorName;
	m_iParentEventIndex = pxEvent->m_iParentEventIndex;
	m_iAnimationFlag = pxEvent->m_iAnimationFlag;
	m_xAnimationFlagActorName = pxEvent->m_xAnimationFlagActorName;
	m_uEventFlags = pxEvent->m_uEventFlags;
	m_iEnableID = pxEvent->m_iEnableID;

	if(m_xTargetActorName.GetLength())
	{
		//find the named actor
		for(int i=0;i<m_pxCutscene->NumberOfActors;i++)
		{
			if(m_xTargetActorName == m_pxCutscene->Actors[i]->ActorName)
			{
				m_pxTargetActor = m_pxCutscene->Actors[i];
				break;
			}
		}
	}
	if(m_xAnimationFlagActorName.GetLength())
	{
		//find the named actor
		for(int i=0;i<m_pxCutscene->NumberOfActors;i++)
		{
			if(m_xAnimationFlagActorName == m_pxCutscene->Actors[i]->ActorName)
			{
				m_pxAnimationFlagActor = m_pxCutscene->Actors[i];
				break;
			}
		}
	}

	#ifdef ASURA_TOOLS
	m_xName = pxEvent->m_xName;
	m_xComment = pxEvent->m_xComment;
	m_xPosition = pxEvent->m_xPosition;
	m_xNameOfSourceTemplate = pxEvent->m_xNameOfSourceTemplate;
	m_xNameOfEventWrapper = pxEvent->m_xNameOfEventWrapper;
	#endif
}

void Asura_Cutscene_Event::GetEventIndexPointers(List<int*>& xIndexList)
{
	xIndexList.add_entry(&m_iParentEventIndex);
}

void Asura_Cutscene_Event::CalculateRelativePosition(const Asura_Matrix_3x3& xCutsceneOrientInverse,const Asura_Vector_3 xCutscenePosition)
{

	m_xRelativePosition = (m_xPosition-xCutscenePosition)*xCutsceneOrientInverse;
}

void Asura_Cutscene_Event::UpdateRelativeToPosition(const Asura_Matrix_3x3& xCutsceneOrient,const Asura_Vector_3 xCutscenePosition)
{

	if(m_xPosition != Asura_Maths::ZeroVector3)
	{
		m_xPosition = xCutscenePosition + m_xRelativePosition*xCutsceneOrient;
	}
}
#endif //ASURA_TOOLS

Asura_Cutscene_Event* Asura_Cutscene_Event::AllocateEvent(int iEventType,Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event* pxEvent = Project_AllocateEvent(iEventType,pxCutscene);
	if(!pxEvent)
	{
		switch(iEventType)
		{
			case ACET_AnimationChange :
				pxEvent = new Asura_Cutscene_Event_AnimationChange(pxCutscene);
				break;
			case ACET_SecondaryAnimation :
				pxEvent = new Asura_Cutscene_Event_SecondaryAnimation(pxCutscene);
				break;
			case ACET_TimeScale :
				pxEvent = new Asura_Cutscene_Event_TimeScale(pxCutscene);
				break;
			case ACET_FieldOfView :
				pxEvent = new Asura_Cutscene_Event_FieldOfView(pxCutscene);
				break;
			case ACET_Trigger :
				pxEvent = new Asura_Cutscene_Event_Trigger(pxCutscene);
				break;
        	case ACET_LoopBack :
        	    pxEvent = new Asura_Cutscene_Event_LoopBack(pxCutscene);
        	    break;
			case ACET_ActivateAttachment :
				pxEvent = new Asura_Cutscene_Event_Attachment(ACET_ActivateAttachment,pxCutscene);
				break;
			case ACET_DeactivateAttachment :
				pxEvent = new Asura_Cutscene_Event_Attachment(ACET_DeactivateAttachment,pxCutscene);
				break;
			case ACET_AttachmentControl :
				pxEvent = new Asura_Cutscene_Event_AttachmentControl(pxCutscene);
				break;
			case ACET_Beam :
				pxEvent = new Asura_Cutscene_Event_Beam(pxCutscene);
				break;
			case ACET_Fade :
				pxEvent = new Asura_Cutscene_Event_Fade(pxCutscene);
				break;
			case ACET_ReactivateAttachment:
				pxEvent = new Asura_Cutscene_Event_Attachment(ACET_ReactivateAttachment, pxCutscene);
				break;

			case ACET_TemplateWrapper:
				pxEvent = new Asura_Cutscene_Event_TemplateWrapper(pxCutscene);
				break;

			case ACET_TextureAnimation :
				pxEvent = new Asura_Cutscene_Event_TextureAnimation(pxCutscene);
				break;

			case ACET_Subtitle :
				pxEvent = new Asura_Cutscene_Event_Subtitle(pxCutscene);
				break;

			case ACET_Sound :
				pxEvent = new Asura_Cutscene_Event_Sound(pxCutscene);
				break;

			case ACET_ChangeCamera :
				pxEvent = new Asura_Cutscene_Event_ChangeCamera(pxCutscene);
				break;

			case ACET_ChangeCameraTarget :
				pxEvent = new Asura_Cutscene_Event_ChangeCameraTarget(pxCutscene);
				break;

			case ACET_Hide:
				pxEvent = new Asura_Cutscene_Event_Hide(pxCutscene);
				break;

			case ACET_Unhide:
				pxEvent = new Asura_Cutscene_Event_Unhide(pxCutscene);
				break;

			case ACET_ChangeCameraShake:
				pxEvent = new Asura_Cutscene_Event_ChangeCameraShake(pxCutscene);
				break;

			case ACET_SetAlpha:
				pxEvent = new Asura_Cutscene_Event_SetAlpha(pxCutscene);
				break;

			case ACET_ChangePlayerCameraControl:
				pxEvent = new Asura_Cutscene_Event_ChangePlayerCameraControl(pxCutscene);
				break;

#if defined(ASURA_USE_GUI_MENU_SYSTEM)
			case ACET_GUIMenu:
				pxEvent = new Asura_Cutscene_Event_GUIMenu(pxCutscene);
				break;

			case ACET_ConsoleVar:
				pxEvent = new Asura_Cutscene_Event_ConsoleVar(pxCutscene);
				break;
#endif	//defined(ASURA_USE_GUI_MENU_SYSTEM)

#if defined ASURA_USE_HDR
			case ACET_HDRSettings:
				pxEvent = new Asura_Cutscene_Event_HDRSettings(pxCutscene);
				break;
#endif	// ASURA_USE_HDR

#ifdef ASURA_USE_FACIAL_ANIMATION_SYSTEM
			case ACET_FacialAnimation :
				pxEvent = new Asura_Cutscene_Event_FacialAnimation(pxCutscene);
				break;
#endif
			case ACET_MixerPreset :
				pxEvent = new Asura_Cutscene_Event_MixerPreset(pxCutscene);
				break;

			default :
				Asura_Assert(0,"Unknown event type");
				break;
		}
	}
	return pxEvent;
}

void Asura_Cutscene_Event::Initialise()
{
	m_bTriggered = false;
    m_fNextTriggerTime = m_fTriggerTime;
}

void Asura_Cutscene_Event::Trigger(Asura_Cutscene* /*cutscene*/)
{
	if (m_pxAnimationFlagActor &&
		m_pxCutscene) // Animation flag triggers keep on triggering.
	{
		m_fNextTriggerTime = m_pxCutscene->Timer + m_fTriggerTime;
	}
	m_bTriggered = true;
}

bool Asura_Cutscene_Event::HasBeenTriggered() const
{
    return m_bTriggered;
}

void Asura_Cutscene_Event::UpdateAfterTrigger(Asura_Cutscene*)
{
	if (m_pxAnimationFlagActor &&
		m_pxCutscene) // Animation flag triggers keep on triggering.
	{
		if (m_pxCutscene->Timer > m_fNextTriggerTime)
		{
			m_bTriggered = false;
		}
	}
}

bool Asura_Cutscene_Event::IsTimeToTrigger(float fCurCutsceneTime)
{   
	if (m_pxAnimationFlagActor)
	{
		int iFlags = m_pxAnimationFlagActor->GetAnimationFlagsForLastFrame();
		if (iFlags & m_iAnimationFlag)
		{
			return true;
		}
		return false;
	}
    if (!m_bTriggered)
    {
        if (m_fNextTriggerTime <= fCurCutsceneTime)
        {
            m_bTriggered = true;
            return true;
        }
        else
        {
            return false;
        }
    }

    return false;
}

void Asura_Cutscene_Event::HandleCutsceneLoopBack(float fLoopBackTriggerTime, float fTimeToLoopBackTo)
{
    // (MH - 12.11.2002) we may allow loopbacks to be disabled, so we need to update any 
    // events which happen after the loopback.
//    if (m_fTriggerTime > fLoopBackTriggerTime)
//    {
//        
//        Asura_Assert(!m_bTriggered, "event in future has already been triggered");
//        return;
//    }

    if (m_fTriggerTime >= fTimeToLoopBackTo)
    {
        m_bTriggered = false;
        m_fNextTriggerTime += fLoopBackTriggerTime-fTimeToLoopBackTo;
    }
}


void Asura_Cutscene_Event::Update(Asura_Cutscene* pxCutscene)
{
	if(HasBeenTriggered())
	{
		UpdateAfterTrigger(pxCutscene);
	}
	else if(IsTimeToTrigger(pxCutscene->Timer))
	{
		
		if((m_bIgnoreWhenAdvancingToTime && pxCutscene->IsAdvancingToTime()) || !IsEnabled())
		{
			//this event should't be trigger when advancing time... just call the base
			//class trigger to update flags appropriately
			Asura_Cutscene_Event::Trigger(pxCutscene);	
		}
		else
		{
			//trigger the event
			Trigger(pxCutscene);
		}
	}
}

void Asura_Cutscene_Event::Render(Asura_Cutscene* pxCutscene)
{
	if(HasBeenTriggered())
	{
		RenderAfterTrigger(pxCutscene);
	}
}

bool Asura_Cutscene_Event::IsEnabled() const
{
	if(m_bDisabled) return false;

	if(!m_iEnableID) return true;

	u_int uBit = 1<<(m_iEnableID-1);
	return (m_pxCutscene->m_uEnabledEvents & uBit) ? true  : false;
}

//////////////////////////////////////////////////////////////////////////////////////////////
/*--------------------------------------**
** Asura_Cutscene_Event_AnimationChange **
**--------------------------------------*/

Asura_Cutscene_Event_AnimationChange::Asura_Cutscene_Event_AnimationChange(Asura_Cutscene* _Cutscene)
:Asura_Cutscene_Event(ACET_AnimationChange,_Cutscene)
{
	//AnimationName = "";
    m_iAnimationIndex = -1;
	m_iAnimationFlags = AnimationChangeFlag_Loop;
	m_fTweenDuration = 0.2f;
	m_fStartTimer = 0.0f;
	m_fEndTimer = 0.0f;
	m_fAnimDelay = 0.0f;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_AnimationChange::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_AnimationChange* pxEvent = (Asura_Cutscene_Event_AnimationChange*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_iAnimationFlags = pxEvent->m_iAnimationFlags;
	m_fTweenDuration = pxEvent->m_fTweenDuration;
	m_fStartTimer = pxEvent->m_fStartTimer;
	m_fEndTimer = pxEvent->m_fEndTimer;
	m_fAnimDelay = pxEvent->m_fAnimDelay;

	if(pxEvent->m_iAnimationIndex==-1)
	{
		m_iAnimationIndex = -1;
	}
	else
	{
		//get strings from the other cutscene's name lists and add to this
		Asura_String& xName = pxEvent->m_pxCutscene->AnimationNames.GetName(pxEvent->m_iAnimationIndex);
		Asura_String& xFileName = pxEvent->m_pxCutscene->AnimationFileNames.GetName(pxEvent->m_iAnimationIndex);

		m_iAnimationIndex = m_pxCutscene->AnimationNames.AddName(xName);

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

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

void Asura_Cutscene_Event_AnimationChange::Trigger(Asura_Cutscene* cutscene)
{
	Asura_Cutscene_Event::Trigger(cutscene);

	if(!m_pxTargetActor) return;

	
	if(m_pxTargetActor->IsHierarchyActor())
	{
		if(m_iAnimationIndex == -1) return;
		Asura_Cutscene_Actor_Hierarchy* actor = (Asura_Cutscene_Actor_Hierarchy*) m_pxTargetActor;

		//tell the target to change its animation
    	Asura_Assert(cutscene == m_pxCutscene, "");
		//actor->ChangeAnimation(AnimationName,TriggerTime,AnimationFlags,TweenDuration);
		if(m_iAnimationFlags & AnimationChangeFlag_ToPosition)
		{
    		int iFlags = m_iAnimationFlags  &~(AnimationChangeFlag_EventSpecificFlags);
			actor->PlayAnimationToPos(m_iAnimationIndex, m_fNextTriggerTime, m_fStartTimer, m_fEndTimer, iFlags, m_fTweenDuration, m_fAnimDelay);
		}
		else
		{
    		actor->ChangeAnimation(m_iAnimationIndex, m_fNextTriggerTime, m_iAnimationFlags&~AnimationChangeFlag_EventSpecificFlags, m_fTweenDuration, m_fAnimDelay);
		}

		actor->SetIgnoreAnimMovement((m_iAnimationFlags & AnimationChangeFlag_IgnoreMovement) ? true : false);
	}
	else if(m_pxTargetActor->Type == ACAT_Camera)
	{
		Asura_Cutscene_Actor_Camera* pxCamera = (Asura_Cutscene_Actor_Camera*) m_pxTargetActor;

		pxCamera->ChangeAnimation(m_iAnimationIndex,m_fNextTriggerTime,m_iAnimationFlags);
	}
}


void Asura_Cutscene_Event_AnimationChange::LoadFiles()
{
	//(target should be a hierarchy actor)
	if(!m_pxTargetActor) return;

	if(m_iAnimationIndex != -1)
	{
		const Asura_String & AnimationName = m_pxCutscene->AnimationNames.GetName(m_iAnimationIndex);
    	Asura_String & AnimationFileName = m_pxCutscene->AnimationFileNames.GetName(m_iAnimationIndex);
    	m_pxTargetActor->LoadAnimFile(AnimationName,AnimationFileName);
	}
}

void Asura_Cutscene_Event_AnimationChange::PreDestructionCleanup(Asura_Cutscene* cutscene)
{
	Asura_Cutscene_Event::PreDestructionCleanup(cutscene);

	if(m_iAnimationIndex != -1)
	{
	   Asura_Assert(cutscene,"");
	   cutscene->AnimationNames.RemoveName(m_iAnimationIndex);
	   cutscene->AnimationFileNames.RemoveName(m_iAnimationIndex);
	   m_iAnimationIndex = -1;
	}
}
//////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------------------------**
** Asura_Cutscene_Event_SecondaryAnimation **
**-----------------------------------------*/
Asura_Cutscene_Event_SecondaryAnimation::Asura_Cutscene_Event_SecondaryAnimation(Asura_Cutscene* _Cutscene)
: Asura_Cutscene_Event_AnimationChange(_Cutscene)
{
	m_iType = ACET_SecondaryAnimation;
	m_fWeight = 1;
	m_fDuration = 0;
	m_iAnimHandle = ASURA_INVALID_HANDLE;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_SecondaryAnimation::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_SecondaryAnimation* pxEvent = (Asura_Cutscene_Event_SecondaryAnimation*) _pxEvent;
	Asura_Cutscene_Event_AnimationChange::Copy(pxEvent);

	m_fWeight = pxEvent->m_fWeight;
	m_fDuration = pxEvent->m_fDuration;

}
#endif // ASURA_TOOLS

void Asura_Cutscene_Event_SecondaryAnimation::Initialise()
{
	Asura_Cutscene_Event_AnimationChange::Initialise();

	m_iAnimHandle = ASURA_INVALID_HANDLE;
}

void Asura_Cutscene_Event_SecondaryAnimation::Finish()
{
	Asura_Cutscene_Event_AnimationChange::Finish();

	if(m_iAnimHandle != ASURA_INVALID_HANDLE)
	{
		//remove the anim
		//(target should be a hierarchy actor)
		if(!m_pxTargetActor) return;
		Asura_Assert(m_pxTargetActor->IsHierarchyActor(),"");

		Asura_Cutscene_Actor_Hierarchy* actor = (Asura_Cutscene_Actor_Hierarchy*) m_pxTargetActor;

		actor->RemoveSecondaryAnimation(m_iAnimHandle);
		m_iAnimHandle = ASURA_INVALID_HANDLE;
	}
}

void Asura_Cutscene_Event_SecondaryAnimation::Trigger(Asura_Cutscene* cutscene)
{
	Asura_Cutscene_Event::Trigger(cutscene);//(not calling immediate parent , since a different animation function is needed)

	//(target should be a hierarchy actor)
	if(!m_pxTargetActor) return;
	if(m_iAnimationIndex == -1) return;
	Asura_Assert(m_pxTargetActor->IsHierarchyActor(),"");

	Asura_Cutscene_Actor_Hierarchy* actor = (Asura_Cutscene_Actor_Hierarchy*) m_pxTargetActor;

	bool bAutoRemove = (m_fDuration == 0.0f) && !(m_iAnimationFlags & AnimationChangeFlag_Loop);

	//play the anim
	//AnimHandle = actor->PlaySecondaryAnimation(AnimationName,TriggerTime,AnimationFlags,Weight,Duration);
	if(m_iAnimationFlags & AnimationChangeFlag_ToPosition)
	{
    	int iFlags = m_iAnimationFlags  &~(AnimationChangeFlag_EventSpecificFlags|AnimationChangeFlag_Loop);
		m_iAnimHandle = actor->PlaySecondaryAnimationToPos(m_iAnimationIndex, m_fNextTriggerTime, m_fStartTimer, m_fEndTimer, iFlags, m_fWeight, m_fTweenDuration,bAutoRemove, m_fAnimDelay);
	}
	else
	{
    	m_iAnimHandle = actor->PlaySecondaryAnimation(m_iAnimationIndex, m_fNextTriggerTime, m_iAnimationFlags&~AnimationChangeFlag_EventSpecificFlags, m_fWeight, m_fTweenDuration,bAutoRemove, m_fAnimDelay);
	}
	if(m_fDuration == 0)
	{
		//don't care about the handle in this case
		m_iAnimHandle = ASURA_INVALID_HANDLE;
	}
}

void Asura_Cutscene_Event_SecondaryAnimation::UpdateAfterTrigger(Asura_Cutscene* cutscene)
{
	if(m_iAnimHandle == ASURA_INVALID_HANDLE) return;
	Asura_Assert(m_fDuration > 0,"");

	if(cutscene->Timer>=(m_fDuration + m_fNextTriggerTime))
	{
		//remove the anim
		//(target should be a hierarchy actor)
		if(!m_pxTargetActor) return;
		Asura_Assert(m_pxTargetActor->IsHierarchyActor(),"");

		Asura_Cutscene_Actor_Hierarchy* actor = (Asura_Cutscene_Actor_Hierarchy*) m_pxTargetActor;

		actor->RemoveSecondaryAnimation(m_iAnimHandle);
		m_iAnimHandle = ASURA_INVALID_HANDLE;

	}
}

//////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------------------**
** Asura_Cutscene_Event_TextureAnimation **
**---------------------------------------*/
Asura_Cutscene_Event_TextureAnimation::Asura_Cutscene_Event_TextureAnimation(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_TextureAnimation,pxCutscene)
{
	m_iSequence = 0;
	m_fSpeed = 1.0f;
	m_uTextureAnimFlags = 0;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_TextureAnimation::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_TextureAnimation* pxEvent = (Asura_Cutscene_Event_TextureAnimation*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_iSequence = pxEvent->m_iSequence;
	m_fSpeed = pxEvent->m_fSpeed;
	m_uTextureAnimFlags = pxEvent->m_uTextureAnimFlags;

}
#endif // ASURA_TOOLS

void Asura_Cutscene_Event_TextureAnimation::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event::Trigger(pxCutscene);

	//(target should be a hierarchy actor)
	if(!m_pxTargetActor) return;
	Asura_Assert(m_pxTargetActor->IsHierarchyActor(),"");

	Asura_Cutscene_Actor_Hierarchy* pxActor = (Asura_Cutscene_Actor_Hierarchy*) m_pxTargetActor;

	pxActor->ChangeTextureAnimation(m_iSequence,m_fSpeed,m_uTextureAnimFlags);
}


//////////////////////////////////////////////////////////////////////////////////////////////
/*--------------------------------**
** Asura_Cutscene_Event_TimeScale **
**--------------------------------*/

Asura_Cutscene_Event_TimeScale::Asura_Cutscene_Event_TimeScale(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_TimeScale,pxCutscene)
{
	m_fTimeScale = 1.0f;
	m_fPreCutsceneTimeScale = 1.0f;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_TimeScale::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_TimeScale* pxEvent = (Asura_Cutscene_Event_TimeScale*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_fTimeScale = pxEvent->m_fTimeScale;
}
#endif //ASURA_TOOLS

void Asura_Cutscene_Event_TimeScale::Initialise()
{
	Asura_Cutscene_Event::Initialise();

	//store the initial timescale
	m_fPreCutsceneTimeScale = Asura_Timers::GetGameTimeScale();
}

void Asura_Cutscene_Event_TimeScale::Finish()
{
	//reset timescale
	Asura_Timers::SetGameTimeScale(m_fPreCutsceneTimeScale);

	Asura_Cutscene_Event::Finish();
}

void Asura_Cutscene_Event_TimeScale::Trigger(Asura_Cutscene* cutscene)
{
	Asura_Cutscene_Event::Trigger(cutscene);
	Asura_Timers::SetGameTimeScale(m_fTimeScale);
}



//////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------------**
** Asura_Cutscene_Event_FieldOfView **
**----------------------------------*/

Asura_Cutscene_Event_FieldOfView::Asura_Cutscene_Event_FieldOfView(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_FieldOfView,pxCutscene)
{
	m_fDuration = 0;
	m_fFinalFOV = 75.0f;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_FieldOfView::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_FieldOfView* pxEvent = (Asura_Cutscene_Event_FieldOfView*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_fDuration = pxEvent->m_fDuration;
	m_fFinalFOV = pxEvent->m_fFinalFOV;
	
}
#endif //ASURA_TOOLS

void Asura_Cutscene_Event_FieldOfView::Trigger(Asura_Cutscene* cutscene)
{
	Asura_Cutscene_Event::Trigger(cutscene);
	
	if(m_xTargetActorName.IsBlank())
	{
		//No target , so grab the initiall FOV from the current settings
		m_fInitialFOV = Asura_Maths::RadToDeg(Asura_Camera::GetHorizontalFieldOfView());
	}
	else
	{
		//have got a target ,so take initial FOV from target camera

		if(m_pxTargetActor && m_pxTargetActor->IsCamera())
		{
			m_fInitialFOV = Asura_Maths::RadToDeg(((Asura_Cutscene_Actor_Camera*)m_pxTargetActor)->GetFOV());
		}
		else
		{
			m_fInitialFOV = m_fFinalFOV;
		}
	}
	m_bFinished = false;
	UpdateAfterTrigger(cutscene);
}

void Asura_Cutscene_Event_FieldOfView::UpdateAfterTrigger(Asura_Cutscene* cutscene)
{
	if (m_bFinished || Asura_ClientCutscene_Actor_Camera::Disable) return;
	
	float newFOV;
	if(cutscene->Timer>=(m_fDuration + m_fNextTriggerTime))
	{
		newFOV = m_fFinalFOV;
		m_bFinished = true;
	}
	else
	{
		Asura_Assert(m_fDuration, "");
		newFOV = m_fInitialFOV + (m_fFinalFOV - m_fInitialFOV)*((cutscene->Timer - m_fNextTriggerTime) / m_fDuration);
	}

	newFOV = Asura_Maths::DegToRad(newFOV);

	if(m_xTargetActorName.IsBlank())
	{
		//event has no target , so effect all camera actors (to leave old cutscenes working as before , mainly)
		for(int i=0;i<m_pxCutscene->NumberOfActors;i++)
		{
			if(m_pxCutscene->Actors[i]->IsCamera())
			{
				((Asura_Cutscene_Actor_Camera*)m_pxCutscene->Actors[i])->SetFOV(newFOV);
			}
		}

	}
	else if(m_pxTargetActor)
	{
		//Set the FOV on the target actor
		Asura_Assert(m_pxTargetActor->IsCamera(),"Target of FOV event must be a camera actor");
		if(m_pxTargetActor->IsCamera())
		{
			((Asura_Cutscene_Actor_Camera*) m_pxTargetActor)->SetFOV(newFOV);
		}
	}

}

//////////////////////////////////////////////////////////////////////////////////////////////
/*------------------------------**
** Asura_Cutscene_Event_Trigger **
**------------------------------*/

Asura_Cutscene_Event_Trigger::Asura_Cutscene_Event_Trigger(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_Trigger,pxCutscene)
{
	m_sMessageBlock = ASURA_STATICMESSAGE_EMPTYBLOCK;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_Trigger::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_Trigger* pxEvent = (Asura_Cutscene_Event_Trigger*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_sMessageBlock = m_pxCutscene->CopyMessageBlock(pxEvent->m_pxCutscene,pxEvent->m_sMessageBlock);
}
#endif //ASURA_TOOLS

void Asura_Cutscene_Event_Trigger::PreDestructionCleanup(Asura_Cutscene* cutscene)
{
	Asura_Cutscene_Event::PreDestructionCleanup(cutscene);

	Asura_StaticMessage_System::DeleteMessageBlock(m_sMessageBlock);
	m_sMessageBlock = ASURA_STATICMESSAGE_EMPTYBLOCK;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_Trigger::ResetGuidsAndMessages()
{
	Asura_Cutscene_Event::ResetGuidsAndMessages();

	Asura_StaticMessage_System::DeleteMessageBlock(m_sMessageBlock);
	m_sMessageBlock = ASURA_STATICMESSAGE_EMPTYBLOCK;
}
#endif // ASURA_TOOLS

void Asura_Cutscene_Event_Trigger::Trigger(Asura_Cutscene* pxCutscene)
{
	if(m_sMessageBlock != ASURA_STATICMESSAGE_EMPTYBLOCK)
	{
		{
			// MGH 4/1/2006: Only send messages if this is the server machine.
			// In multiplayer games the server was receiving multiple copies of the messages
			// (one from each machine). This test should eliminate the problem.
			// MGH 15/1/2007: Don't send messages if the m_bSkipMessagesAfterLoad is set; this is
			// to help with the reloading of playing cutscenes in streaming zones.
			if (Asura_Network::IsServer() && !pxCutscene->m_bSkipMessagesAfterLoad)
			{
				float fDelay = 0.0f;
				if(pxCutscene->m_bAdvancingToTime)
				{
					//If we are skipping ahead through the cutscene , then apply a negative delay on any messages sent
					//RAR 30/11/2007
					fDelay = m_fTriggerTime-pxCutscene->Timer;
				}

				#if defined(ASURA_TOOLS) || defined(ASURA_USE_STREAMING)
				Asura_StaticMessage_System::SendMessages(m_sMessageBlock, pxCutscene->m_uGuid, fDelay);
				#else
				Asura_StaticMessage_System::SendMessages(m_sMessageBlock, ASURA_GUID_UNREGISTERED, fDelay);
				#endif
			}
		}
	}
    Asura_Cutscene_Event::Trigger(pxCutscene);	
}


//////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------**
** Asura_Cutscene_Event_LoopBack **
**-------------------------------*/
Asura_Cutscene_Event_LoopBack::Asura_Cutscene_Event_LoopBack(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_LoopBack,pxCutscene)
{
    m_fNumSecondsToLoopBack = 0.0f;
	m_iEventToLoopBackTo = -1;
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_LoopBack::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_LoopBack* pxEvent = (Asura_Cutscene_Event_LoopBack*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_fNumSecondsToLoopBack = pxEvent->m_fNumSecondsToLoopBack;
	m_iEventToLoopBackTo = pxEvent->m_iEventToLoopBackTo;
}

void Asura_Cutscene_Event_LoopBack::GetEventIndexPointers(List<int*>& xIndexList)
{
	Asura_Cutscene_Event::GetEventIndexPointers(xIndexList);
	xIndexList.add_entry(&m_iEventToLoopBackTo);
}
#endif // ASURA_TOOLS

void Asura_Cutscene_Event_LoopBack::Trigger(Asura_Cutscene* pCutscene)
{
	float fTimeToLoopBackTo;
	if(m_iEventToLoopBackTo>=0)
	{
		//work out how much time needs to be looped back to to get to the start of the event
        Asura_Cutscene_Event * pEvent = pCutscene->Events[m_iEventToLoopBackTo];
		m_fNumSecondsToLoopBack = m_fTriggerTime - pEvent->m_fTriggerTime;
		if(m_fNumSecondsToLoopBack<0) m_fNumSecondsToLoopBack = 0;
		fTimeToLoopBackTo = pEvent->m_fTriggerTime;

	}
	else
	{
		fTimeToLoopBackTo = m_fTriggerTime-m_fNumSecondsToLoopBack;
	}

	float fTriggerTime = m_fTriggerTime;

	//Check to see if the cutscene timer has advanced enough for more than one loop to have taken
	//place. If it has , then we advance the trigger time enough , so that the next trigger time
	//will be in the future. RAR 24/02/2005
	float fLoopDuration = m_fTriggerTime - fTimeToLoopBackTo;
	if(fLoopDuration)
	{
		int iLoops = Asura_Maths::FloatToInt((pCutscene->Timer-m_fTriggerTime)/fLoopDuration);

		//add on time of number of complete loops
		fTriggerTime += (float)iLoops*fLoopDuration;

		//in case we are off by one from rounding errors
		if(fTriggerTime + fLoopDuration <= pCutscene->Timer)
		{
			fTriggerTime += fLoopDuration;
		}

		Asura_Assert(m_fTriggerTime+fTriggerTime-fTimeToLoopBackTo > pCutscene->Timer,"Error calculating new trigger time for loop back event");
	}


    // loop through all other events affecting this actor.
    // if the trigger time falls within the range being looped back over,
    // handle the event.
    for (int i = 0; i < pCutscene->NumberOfEvents; i++)
    {
        Asura_Cutscene_Event * pEvent = pCutscene->Events[i];
        if ((m_pxTargetActor == 0) || pEvent->AffectsActor(m_pxTargetActor))
        {
			//pass the stored value of the trigger time for this event instead of the current cutscene time , 
			//so that the playback isn't affected by frame rate. - RAR 7/8/02
            pEvent->HandleCutsceneLoopBack(fTriggerTime, fTimeToLoopBackTo);
        }
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------------**
** Asura_Cutscene_Event_Attachment **
**---------------------------------*/
Asura_Cutscene_Event_Attachment::Asura_Cutscene_Event_Attachment(int iEventType,Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(iEventType,pxCutscene)
{
	m_uAttachmentID = 0;	
}


#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_Attachment::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_Attachment* pxEvent = (Asura_Cutscene_Event_Attachment*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_uAttachmentID = pxEvent->m_uAttachmentID;
}
#endif // ASURA_TOOLS

void Asura_Cutscene_Event_Attachment::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event::Trigger(pxCutscene);

	//make sure all the event data has been set
	if(!m_pxTargetActor) return;
	if(!m_pxTargetActor->IsHierarchyActor()) return;
	if(!m_uAttachmentID) return;
	
	Asura_Cutscene_Actor_Hierarchy* pxActor = (Asura_Cutscene_Actor_Hierarchy*) m_pxTargetActor;

	//activate/deactivate the attachment
	if(m_iType == ACET_ActivateAttachment)
	{
		pxActor->ActivateAttachment(m_uAttachmentID,true);
	}
	else if (m_iType == ACET_ReactivateAttachment)
	{
		pxActor->ActivateAttachment(m_uAttachmentID, false);
		pxActor->ActivateAttachment(m_uAttachmentID, true);
	}
	else
	{
		Asura_Assert(m_iType == ACET_DeactivateAttachment,"");
		pxActor->ActivateAttachment(m_uAttachmentID,false);
	}
	
}

//////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------------------**
** Asura_Cutscene_Event_AttachmentControl **
**----------------------------------------*/

Asura_Cutscene_Event_AttachmentControl::Asura_Cutscene_Event_AttachmentControl(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_AttachmentControl,pxCutscene)
{
	m_uNumAttachments = 0;
	m_pxAttachments = 0;
}

Asura_Cutscene_Event_AttachmentControl::~Asura_Cutscene_Event_AttachmentControl()
{
	delete [] m_pxAttachments;
}


#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_AttachmentControl::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_AttachmentControl* pxEvent = (Asura_Cutscene_Event_AttachmentControl*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);
	
	m_uNumAttachments = pxEvent->m_uNumAttachments;
	m_pxAttachments = m_uNumAttachments ? new Asura_Cutscene_Event_AttachmentControl_Attachment[m_uNumAttachments] : 0;

	for(u_int i=0;i<m_uNumAttachments;i++)
	{
		m_pxAttachments[i] = pxEvent->m_pxAttachments[i];
	}
}
#endif // ASURA_TOOLS

void Asura_Cutscene_Event_AttachmentControl::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event::Trigger(pxCutscene);

	//make sure all the event data has been set
	if(!m_pxTargetActor) return;
	if(!m_pxTargetActor->IsHierarchyActor()) return;
	
	Asura_Cutscene_Actor_Hierarchy* pxActor = (Asura_Cutscene_Actor_Hierarchy*) m_pxTargetActor;

	//activate/deactivate each attachment
	for(u_int i=0;i<m_uNumAttachments;i++)
	{
		switch(m_pxAttachments[i].m_uType)
		{
			case Asura_Cutscene_Event_AttachmentControl_Attachment::ACTIVATE :
				pxActor->ActivateAttachment(m_pxAttachments[i].m_uID,true);
				break;
			case Asura_Cutscene_Event_AttachmentControl_Attachment::DEACTIVATE :
				pxActor->ActivateAttachment(m_pxAttachments[i].m_uID,false);
				break;
			case Asura_Cutscene_Event_AttachmentControl_Attachment::REACTIVATE :
				pxActor->ActivateAttachment(m_pxAttachments[i].m_uID, false);
				pxActor->ActivateAttachment(m_pxAttachments[i].m_uID, true);
				break;
			default :
				Asura_Assert(m_pxAttachments[i].m_uType==Asura_Cutscene_Event_AttachmentControl_Attachment::NONE,"Unexpeceted type");
				break;
		}
	}
}



//////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------**
** Asura_Cutscene_Event_Beam **
**---------------------------*/

Asura_Cutscene_Event_Beam::Asura_Cutscene_Event_Beam(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_Beam,pxCutscene)
{
	m_xParam.m_xInitialPosition.Zero();
	m_xParam.m_xEndPosition.Zero();

	m_xParam.m_fDuration = 1.0f;

	m_xParam.m_xTemplate.fInitialRadius = 0.1f;
	m_xParam.m_xTemplate.fEndRadius = 0.1f;

	m_xParam.m_xTemplate.fTailSpeed = 0.0f;
	m_xParam.m_xTemplate.fHeadSpeed = 0.0f;

	m_xParam.m_xTemplate.fDecayRate = 1.0f;
	m_xParam.m_xTemplate.uTileHashID = ASURA_HASH_ID_UNSET;

	m_xParam.m_xTemplate.xInitialTailColour.SetColours(1.0f,1.0f,1.0f,1.0f);
	m_xParam.m_xTemplate.xInitialHeadColour.SetColours(1.0f,1.0f,1.0f,1.0f);
	m_xParam.m_xTemplate.xEndTailColour.SetColours(1.0f,1.0f,1.0f,1.0f);
	m_xParam.m_xTemplate.xEndHeadColour.SetColours(1.0f,1.0f,1.0f,1.0f);



	m_uBeamID = 0;
}

void Asura_Cutscene_Event_Beam::Finish()
{
	if(m_uBeamID)
	{
		Asura_Beam_System::DeallocateBeam(m_uBeamID);
		m_uBeamID = 0;
	}
}

void Asura_Cutscene_Event_Beam::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event::Trigger(pxCutscene);

	if(!m_xParam.m_fDuration) return;

	//update decay rate in beam template
	m_xParam.m_xTemplate.fDecayRate = 1.0f/m_xParam.m_fDuration;

	//fill in the texture tile handle
	m_xParam.m_xTemplate.iTileHandle = Asura_TiledTextureManagement::GetHandleOfTile( m_xParam.m_xTemplate.uTileHashID );

    //and then create the beam
	Asura_Beam* pxBeam = Asura_Beam_System::CreateBeam(m_xParam.m_xInitialPosition,m_xParam.m_xEndPosition,m_xParam.m_xTemplate);

	//store the beam's ID
	if(pxBeam) m_uBeamID = pxBeam->GetID();

	//set the timeslice for the first fram
	m_fLastTimer = m_fTriggerTime;
	UpdateAfterTrigger(pxCutscene);

}

void Asura_Cutscene_Event_Beam::UpdateAfterTrigger(Asura_Cutscene* pxCutscene)
{
	if(!m_uBeamID) return;
	if(m_fLastTimer >= m_fTriggerTime + m_xParam.m_fDuration) return;

	
	//determine how far the timer should advance on the beam this frame
	float fTime = pxCutscene->Timer - m_fLastTimer;
	m_fLastTimer = pxCutscene->Timer;

	Asura_Beam_System::SetTimesliceForBeam(m_uBeamID,fTime);
	
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_Beam::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_Beam* pxEvent = (Asura_Cutscene_Event_Beam*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_xParam = pxEvent->m_xParam;

}

void Asura_Cutscene_Event_Beam::CalculateRelativePosition(const Asura_Matrix_3x3& xCutsceneOrientInverse,const Asura_Vector_3 xCutscenePosition)
{
	Asura_Cutscene_Event::CalculateRelativePosition(xCutsceneOrientInverse,xCutscenePosition);

	m_xRelativeInitialPosition = (m_xParam.m_xInitialPosition-xCutscenePosition)*xCutsceneOrientInverse;
	m_xRelativeEndPosition = (m_xParam.m_xEndPosition-xCutscenePosition)*xCutsceneOrientInverse;
}

void Asura_Cutscene_Event_Beam::UpdateRelativeToPosition(const Asura_Matrix_3x3& xCutsceneOrient,const Asura_Vector_3 xCutscenePosition)
{
	Asura_Cutscene_Event::UpdateRelativeToPosition(xCutsceneOrient,xCutscenePosition);

	m_xParam.m_xInitialPosition = xCutscenePosition + m_xRelativeInitialPosition*xCutsceneOrient;
	m_xParam.m_xEndPosition = xCutscenePosition + m_xRelativeEndPosition*xCutsceneOrient;
}
#endif // ASURA_TOOLS


//////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------**
** Asura_Cutscene_Event_Fade **
**---------------------------*/

Asura_Cutscene_Event_Fade::Asura_Cutscene_Event_Fade(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_Fade,pxCutscene)
{
	m_fRed = 0.0f;
	m_fBlue = 0.0f;
	m_fGreen = 0.0f;
	m_fDuration = 1.0f;
	m_uFlags = ASURACUTSCENEFADEFLAG_FADETO|ASURACUTSCENEFADEFLAG_CANCELAFTERCUTSCENE;
}

void Asura_Cutscene_Event_Fade::Finish()
{
	if(m_uFlags & ASURACUTSCENEFADEFLAG_CANCELAFTERCUTSCENE)
	{
		Asura_FullScreenSFX_System::CancelFade();
	}
}


void Asura_Cutscene_Event_Fade::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event::Trigger(pxCutscene);

	int iRed = Asura_Maths::FloatToInt(m_fRed*255.0f);
	int iGreen = Asura_Maths::FloatToInt(m_fGreen*255.0f);
	int iBlue = Asura_Maths::FloatToInt(m_fBlue*255.0f);

	u_int uColour = 0xff000000|(iRed<<16)|(iGreen<<8)|iBlue;

	Asura_FullScreenSFX_System::FADE eFadeType = (m_uFlags & ASURACUTSCENEFADEFLAG_FADETO) ?
													Asura_FullScreenSFX_System::FADE_TO_COLOUR :
													Asura_FullScreenSFX_System::FADE_FROM_COLOUR;

	float fDuration = m_fDuration;
	if(pxCutscene->m_bAdvancingToTime)
	{
		//If we are skipping ahead through the cutscene , then shorten the length of the fade time as needed
		fDuration -= pxCutscene->Timer-m_fTriggerTime;
		if(fDuration<0) fDuration = 0;
	}

	// MGH 19/4/2007: If we are jumping ahead in to a cutscene after a load, then we want to ignore
	// old fade events that happened some time ago. There may be several such events and replaying
	// them in a random order is unlikely to reproduce the correct result. If you care about the
	// overall fade state at save time, then write some code to save and restore it globally.
	if (pxCutscene->m_bAdvancingToTime && pxCutscene->m_bSkipMessagesAfterLoad && fDuration <= 0.0f)
	{
		return;
	}

	Asura_FullScreenSFX_System::CancelFade();
	Asura_FullScreenSFX_System::BeginFade(eFadeType,fDuration,uColour);
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_Fade::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_Fade* pxEvent = (Asura_Cutscene_Event_Fade*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_fRed = pxEvent->m_fRed;
	m_fGreen = pxEvent->m_fGreen;
	m_fBlue = pxEvent->m_fBlue;
	m_fDuration = pxEvent->m_fDuration;
	m_uFlags = pxEvent->m_uFlags;
}
#endif // ASURA_TOOLS


//////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------**
** Asura_Cutscene_Event_Subtitle **
**-------------------------------*/

Asura_Cutscene_Event_Subtitle::Asura_Cutscene_Event_Subtitle(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_Subtitle,pxCutscene)
{
	m_uTextHash = ASURA_HASH_ID_UNSET;
	m_fDuration = 0.0f;
}



void Asura_Cutscene_Event_Subtitle::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event::Trigger(pxCutscene);

	//search for the next subtitle event to determine the maximum end time
	float fMaxEndTime = pxCutscene->CutsceneEndTime;
	for(int i=0;i<pxCutscene->NumberOfEvents;i++)
	{
		if(pxCutscene->Events[i]->GetType() != ACET_Subtitle) continue;

		if(pxCutscene->Events[i]->m_fTriggerTime > m_fTriggerTime)
		{
			//this is the next subtitle event then
			fMaxEndTime = pxCutscene->Events[i]->m_fTriggerTime;
			break;
		} 
	}

	float fEndTime = fMaxEndTime;
	if(m_fDuration)
	{
		//subtitle lasts for specified duration , or until next subtitle , whichever is first
		fEndTime = Asura_Maths::Min(fMaxEndTime,m_fDuration + m_fTriggerTime);
	}

	//tell the cutscene about the new subtitle
	pxCutscene->SetSubtitle(m_uTextHash,fEndTime);
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_Subtitle::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_Subtitle* pxEvent = (Asura_Cutscene_Event_Subtitle*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_uTextHash = pxEvent->m_uTextHash;
	m_fDuration = pxEvent->m_fDuration;
}
#endif // ASURA_TOOLS


//////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------**
** Asura_Cutscene_Event_Sound **
**----------------------------*/


Asura_Cutscene_Event_Sound::Asura_Cutscene_Event_Sound(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_Sound,pxCutscene)
{
#ifdef ASURA_TOOLS
	m_bIgnoreWhenAdvancingToTime = true;
	m_iSoundHandle = ASURA_INVALID_HANDLE;
#endif
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_Sound::Initialise()
{
	Asura_Cutscene_Event::Initialise();

	#ifdef ASURA_USE_AUDIO_SYSTEM
		m_uWave = m_xSoundName.GetHash();
		Asura_Audio_System::LoadWave(m_xSoundName.GetString(),false);
	#else
		//get a sound ID for the wav file
		m_iSoundID = Tools_PickSound::GetSoundIDForFilename(m_xSoundName.GetString(),TOOLS_SID_MIN,TOOLS_SID_MAX);
	#endif
}

void Asura_Cutscene_Event_Sound::Finish()
{
	Asura_Cutscene_Event::Finish();

	#ifdef ASURA_USE_AUDIO_SYSTEM
		Asura_Audio_System::DestroyVoice(m_iSoundHandle);
	#else
		Asura_Sound_System::DestroySound(m_iSoundHandle);
	#endif
}

void Asura_Cutscene_Event_Sound::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event::Trigger(pxCutscene);

	//Only play sound if not using the sound track
	if(pxCutscene->IsUsingSoundTrack()) return;
//	if(!Asura_Cutscene::ToolsPlayback) return;
	if(pxCutscene->m_bRecordingWavFile) return;

	//valid sound ID?
	#ifdef ASURA_USE_AUDIO_SYSTEM
		if(m_uWave == ASURA_HASH_ID_UNSET) return;

		m_iSoundHandle = Asura_Audio_System::CreateVoice(m_uWave,false,false);
		Asura_Audio_Voice* const pxVoice = Asura_Audio_System::GetVoice(m_iSoundHandle);

		if(!pxVoice) return;
	#else
		if(m_iSoundID == ASURA_SID_NO_SOUND) return;
	#endif

	//create sound 3d/2d according to whether there is a target actor
	if(m_pxTargetActor)
	{
		Asura_Vector_3 xPos;
		m_pxTargetActor->GetSoundPosition(xPos);

		#ifdef ASURA_USE_AUDIO_SYSTEM
			//TY 06.10.08 - Do we actually want to use volume attenuation here?
			pxVoice->SetSpeakerMappingType( ASURA_ASMT_SPACIAL );
			pxVoice->SetHasPosition( true );
			pxVoice->SetPos(xPos);
			pxVoice->SetRadii(5.0f,40.0f);
		#else
			m_iSoundHandle = Asura_Sound_System::Create3dSound((Asura_Sound_ID)m_iSoundID,&xPos,40.0f,5.0f);
		#endif
	}
	else
	{
		#ifndef ASURA_USE_AUDIO_SYSTEM
			m_iSoundHandle = Asura_Sound_System::CreateSound((Asura_Sound_ID)m_iSoundID);
		#endif
	}

	if(m_iSoundHandle == ASURA_INVALID_HANDLE) return;

	#ifdef ASURA_USE_AUDIO_SYSTEM
		pxVoice->Start();
		pxVoice->SetVolume(pxCutscene->m_fSoundTrackVolume);

		// AUDIOTODO: handle 'pitch uses time scale'
	#else
		Asura_Sound_System::PlaySound(m_iSoundHandle,false);

		Asura_Sound_System::SetSoundVolume(m_iSoundHandle,pxCutscene->m_fSoundTrackVolume);
		//These sound events are being used to test speech , and so don't want to be timescaled.
		Asura_Sound_System::SetSoundPitchUsesTimeScale(m_iSoundHandle,false);
	#endif
}

void Asura_Cutscene_Event_Sound::UpdateAfterTrigger(Asura_Cutscene* pxCutscene)
{
	//update sound position
	if(m_pxTargetActor && m_iSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Vector_3 xPos;
		m_pxTargetActor->GetSoundPosition(xPos);

		#ifdef ASURA_USE_AUDIO_SYSTEM
			Asura_Audio_Voice* const pxVoice = Asura_Audio_System::GetVoice(m_iSoundHandle);
			if(pxVoice)
			{
				pxVoice->SetPos(xPos);
			}
		#else
			Asura_Sound_System::SetSoundPosition(m_iSoundHandle,xPos);
		#endif
	}

	Asura_Cutscene_Event::UpdateAfterTrigger(pxCutscene);
}
#endif //ASURA_TOOLS

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_Sound::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_Sound* pxEvent = (Asura_Cutscene_Event_Sound*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_xSoundName = pxEvent->m_xSoundName;
}
#endif // ASURA_TOOLS

//////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------------------**
** Asura_Cutscene_Event_ChangeCamera **
**-----------------------------------*/

Asura_Cutscene_Event_ChangeCamera::Asura_Cutscene_Event_ChangeCamera(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_ChangeCamera,pxCutscene)
{
}



void Asura_Cutscene_Event_ChangeCamera::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event::Trigger(pxCutscene);

	if(m_pxTargetActor && m_pxTargetActor->IsCamera())
	{
		m_pxCutscene->SetActiveCamera((Asura_Cutscene_Actor_Camera*)m_pxTargetActor);
	}
}





//////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------------------------**
** Asura_Cutscene_Event_ChangeCameraTarget **
**-----------------------------------------*/

Asura_Cutscene_Event_ChangeCameraTarget::Asura_Cutscene_Event_ChangeCameraTarget(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_ChangeCameraTarget,pxCutscene)
{
}



void Asura_Cutscene_Event_ChangeCameraTarget::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event::Trigger(pxCutscene);

	if(m_pxTargetActor && m_pxTargetActor->IsCamera())
	{
		((Asura_Cutscene_Actor_Camera*)m_pxTargetActor)->SetCameraTarget(m_xCameraTargetName.GetString());
	}
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_ChangeCameraTarget::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_ChangeCameraTarget* pxEvent = (Asura_Cutscene_Event_ChangeCameraTarget*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_xCameraTargetName = pxEvent->m_xCameraTargetName;
}
#endif // ASURA_TOOLS

Asura_Cutscene_Event_Hide::Asura_Cutscene_Event_Hide(Asura_Cutscene* pxCutscene)
: Asura_Cutscene_Event(ACET_Hide, pxCutscene)
{
}

void Asura_Cutscene_Event_Hide::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Assert(m_pxTargetActor->IsHierarchyActor(), "Should not be calling hide on a non-hierarchy cutscene actor");
	if(m_pxTargetActor->IsHierarchyActor())
	{
		Asura_Cutscene_Actor_Hierarchy* pxActor = static_cast<Asura_Cutscene_Actor_Hierarchy*>(m_pxTargetActor);
		pxActor->SetHidden(true);
	}
}

Asura_Cutscene_Event_Unhide::Asura_Cutscene_Event_Unhide(Asura_Cutscene* pxCutscene)
: Asura_Cutscene_Event(ACET_Unhide, pxCutscene)
{
}

void Asura_Cutscene_Event_Unhide::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Assert(m_pxTargetActor->IsHierarchyActor(), "Should not be calling unhide on a non-hierarchy cutscene actor");
	if(m_pxTargetActor->IsHierarchyActor())
	{
		Asura_Cutscene_Actor_Hierarchy* pxActor = static_cast<Asura_Cutscene_Actor_Hierarchy*>(m_pxTargetActor);
		pxActor->SetHidden(false);
	}
}

Asura_Cutscene_Event_ChangeCameraShake::Asura_Cutscene_Event_ChangeCameraShake(Asura_Cutscene* pxCutscene)
: Asura_Cutscene_Event(ACET_ChangeCameraShake, pxCutscene)
, m_fDuration(0.f)
{
}

void Asura_Cutscene_Event_ChangeCameraShake::Trigger(Asura_Cutscene*)
{
	Asura_Assert(m_pxTargetActor->IsCamera(), "Should not be calling change camera shake on a non-camera cutscene actor");
	if(m_pxTargetActor->IsCamera())
	{
		Asura_Cutscene_Actor_Camera* pxActor = static_cast<Asura_Cutscene_Actor_Camera*>(m_pxTargetActor);
		pxActor->ChangeShake(m_xShake, m_fDuration);
	}

}
Asura_Cutscene_Event_SetAlpha::Asura_Cutscene_Event_SetAlpha(Asura_Cutscene* pxCutscene)
: Asura_Cutscene_Event(ACET_SetAlpha, pxCutscene)
, m_xAlpha(8,0.0f,1.0f)
, m_fDuration(0.f)
{
}


void Asura_Cutscene_Event_SetAlpha::Trigger(Asura_Cutscene* pxCutscene)
{
	PARENT::Trigger(pxCutscene);
	
	if( m_fDuration == 0.0f )
	{
		m_pxTargetActor->SetAlpha( m_xAlpha.GetAverageValue() );
	}
}

void Asura_Cutscene_Event_SetAlpha::UpdateAfterTrigger(Asura_Cutscene* pxCutscene)
{
	PARENT::UpdateAfterTrigger(pxCutscene);

	if( m_fDuration > 0.0f )
	{	
		const float fTime = pxCutscene->Timer - m_fTriggerTime;
		if( fTime <= m_fDuration )
		{
			m_pxTargetActor->SetAlpha( m_xAlpha.GetValueAtTime( fTime / m_fDuration ) );
		}		
	}
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_SetAlpha::Copy(Asura_Cutscene_Event* pxEvent)
{
	PARENT::Copy( pxEvent );

	Asura_Cutscene_Event_SetAlpha* pxAlphaEvent = static_cast<Asura_Cutscene_Event_SetAlpha*> (pxEvent);
	
	m_xAlpha    = pxAlphaEvent->m_xAlpha;
	m_fDuration = pxAlphaEvent->m_fDuration;
}
#endif


Asura_Cutscene_Event_ChangePlayerCameraControl::Asura_Cutscene_Event_ChangePlayerCameraControl(Asura_Cutscene* pxCutscene)
: Asura_Cutscene_Event(ACET_ChangePlayerCameraControl, pxCutscene)
, m_fPitchLimit(0.f)
, m_fYawLimit(0.f)
, m_fDuration(0.f)
, m_fInitialPitchLimit(0.f)
, m_fInitialYawLimit(0.f)
{
}


void Asura_Cutscene_Event_ChangePlayerCameraControl::Trigger(Asura_Cutscene* pxCutscene)
{
	PARENT::Trigger(pxCutscene);

	Asura_Assert(m_pxTargetActor->IsCamera(), "Should not be calling change player camera control on a non-camera cutscene actor");
	if(m_pxTargetActor->IsCamera())
	{
		Asura_Cutscene_Actor_Camera* pxActor = static_cast<Asura_Cutscene_Actor_Camera*>(m_pxTargetActor);
		if( m_fDuration == 0.0f )
		{
			pxActor->SetPlayerControlPitchLimit(m_fPitchLimit);
			pxActor->SetPlayerControlYawLimit(m_fYawLimit);
		}
		else
		{
			m_fInitialPitchLimit = pxActor->GetPlayerControlPitchLimit();
			m_fInitialYawLimit = pxActor->GetPlayerControlYawLimit();
		}
	}
}

void Asura_Cutscene_Event_ChangePlayerCameraControl::UpdateAfterTrigger(Asura_Cutscene* pxCutscene)
{
	PARENT::UpdateAfterTrigger(pxCutscene);

	Asura_Assert(m_pxTargetActor->IsCamera(), "Should not be calling change player camera control on a non-camera cutscene actor");
	if(m_pxTargetActor->IsCamera())
	{
		Asura_Cutscene_Actor_Camera* pxActor = static_cast<Asura_Cutscene_Actor_Camera*>(m_pxTargetActor);
		const float fTime = pxCutscene->Timer - m_fTriggerTime;
		if( m_fDuration > 0.0f && fTime <= m_fDuration)
		{	
			const float fLerpValue = fTime / m_fDuration;
			pxActor->SetPlayerControlPitchLimit(Asura_Maths::Lerp(m_fInitialPitchLimit, m_fPitchLimit, fLerpValue));
			pxActor->SetPlayerControlYawLimit(Asura_Maths::Lerp(m_fInitialYawLimit, m_fYawLimit, fLerpValue));
		}
		else
		{
			pxActor->SetPlayerControlPitchLimit(m_fPitchLimit);
			pxActor->SetPlayerControlYawLimit(m_fYawLimit);
			m_bTriggered = false;
		}
}
}


#if defined(ASURA_USE_GUI_MENU_SYSTEM)

// Asura_Cutscene_Event_GUIMenu
//
// (Ali Jakamy - 08.07.2008) Created

Asura_Cutscene_Event_GUIMenu::Asura_Cutscene_Event_GUIMenu(Asura_Cutscene* pxCutscene)
: PARENT(ACET_GUIMenu, pxCutscene)
, m_uGUIMenuHash(ASURA_HASH_ID_UNSET)
, m_fDuration(0.0f)
, m_fLastUpdateTime(0.0f)
{
}

void Asura_Cutscene_Event_GUIMenu::UpdateAfterTrigger(Asura_Cutscene* pxCutscene)
{
	Asura_GUIMenu_Menu_Page* pxMenu = Asura_GUIMenu_System::GetMenuPageByID(m_uGUIMenuHash);
	if( pxCutscene->Timer < m_fTriggerTime + m_fDuration )
	{
		if( pxMenu )
		{
			pxMenu->Update(pxCutscene->Timer - m_fLastUpdateTime);
			m_fLastUpdateTime = pxCutscene->Timer;
		}
		else
		{
			Asura_Assert(false, "Hash return no menu");
		}
	}
}

void Asura_Cutscene_Event_GUIMenu::RenderAfterTrigger(Asura_Cutscene* pxCutscene)
{
	Asura_GUIMenu_Menu_Page* pxMenu = Asura_GUIMenu_System::GetMenuPageByID(m_uGUIMenuHash);
	if( pxCutscene->Timer < m_fTriggerTime + m_fDuration )
	{
		if( pxMenu )
		{
			pxMenu->Render(true);
		}
		else
		{
			Asura_Assert(false, "Hash return no menu");
		}
	}
}

void Asura_Cutscene_Event_GUIMenu::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_GUIMenu_Menu_Page* pxMenu = Asura_GUIMenu_System::GetMenuPageByID(m_uGUIMenuHash);
	if( pxCutscene->Timer < m_fTriggerTime + m_fDuration )
	{
		if( pxMenu )
		{
			m_fLastUpdateTime = m_fTriggerTime;
			pxMenu->Update(pxCutscene->Timer - m_fLastUpdateTime);
			pxMenu->Render(true);
		}
		else
		{
			Asura_Assert(false, "Hash return no menu");
		}
	}
}

int Asura_Cutscene_Event_GUIMenu::GetChunkSize()
{
	int iChunkSize = PARENT::GetChunkSize() 
		           + sizeof(int)            // Chunk version
				   + sizeof(Asura_Hash_ID)  // GUI Menu hash
				   + sizeof(float);         // Duration

	return iChunkSize;
}

void Asura_Cutscene_Event_GUIMenu::SaveToChunk(char*& pcChunkBuffer)
{
	int iCurrentChunkVersion = CURRENT_CHUNK_VERSION;

	Asura_Chunk_ChunkBuffer_Stream xChunkBufferStream(pcChunkBuffer);

	xChunkBufferStream << iCurrentChunkVersion;

	PARENT::WriteToChunkStream(xChunkBufferStream);

	xChunkBufferStream << m_uGUIMenuHash;
	xChunkBufferStream << m_fDuration;
}

void Asura_Cutscene_Event_GUIMenu::LoadFromChunk(Asura_Chunk_CutsceneEvent* pxChunk, Asura_Handle iFileHandle)
{
	int iCurrentChunkVersion;

	Asura_Chunk_File_Stream xFstream(iFileHandle);

    xFstream >> iCurrentChunkVersion;

	switch ( iCurrentChunkVersion )
	{
		case 0 :
		{
			PARENT::LoadFromChunk(pxChunk, iFileHandle);
			
			xFstream >> m_uGUIMenuHash;
			xFstream >> m_fDuration;
			break;
		}
		default :
		{
			Asura_Assertf(false, ("Unknown chunk version (%d) in Asura_Cutscene_Event_GUIMenu", iCurrentChunkVersion));
			break;
		}
	}
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_GUIMenu::Copy(Asura_Cutscene_Event* pxEvent)
{
	Asura_Cutscene_Event_GUIMenu* pxNewEvent = static_cast<Asura_Cutscene_Event_GUIMenu*> (pxEvent);
	PARENT::Copy(pxNewEvent);

	m_uGUIMenuHash = pxNewEvent->m_uGUIMenuHash;
    m_fDuration    = pxNewEvent->m_fDuration;
}
#endif // ASURA_TOOLS


// Asura_Cutscene_Event_ConsoleVar
//
// (Ali Jakamy - 08.07.2008) Created

Asura_Cutscene_Event_ConsoleVar::Asura_Cutscene_Event_ConsoleVar(Asura_Cutscene* pxCutscene)
: PARENT(ACET_ConsoleVar, pxCutscene)
, m_eValueType(CONSOLE_VAR_INVALID)
{
}

void Asura_Cutscene_Event_ConsoleVar::Initialise()
{
	PARENT::Initialise();
	switch (m_xConsoleVar.GetType())
	{
		case Asura_CommandConsole_VarRepository::Var::TYPE_BOOL:
		{
			bool bValue;
			m_eValueType = CONSOLE_VAR_BOOL;
            m_xConsoleVar.GetBoolValue(bValue);
			m_xOriginalValue.SetValue(bValue);
			break;
		}
		case Asura_CommandConsole_VarRepository::Var::TYPE_INT:
		{
			int iValue;
			m_eValueType = CONSOLE_VAR_INT;
            m_xConsoleVar.GetIntValue(iValue);
        	m_xOriginalValue.SetValue(iValue);
			break;
		}
		case Asura_CommandConsole_VarRepository::Var::TYPE_FLOAT:
		{
			float fValue;
			m_eValueType = CONSOLE_VAR_FLOAT;
			m_xConsoleVar.GetFloatValue(fValue);
        	m_xOriginalValue.SetValue(fValue);
			break;
		}
		default:
		{
			Asura_Assert(false, "unsupported variable type");
			break;
		}
	}
}

void Asura_Cutscene_Event_ConsoleVar::Finish()
{
	switch (m_eValueType)
	{
		case CONSOLE_VAR_BOOL:
		{
			m_xConsoleVar.SetBoolValue(m_xOriginalValue.GetBoolValue());
			break;
		}
		case CONSOLE_VAR_INT:
		{
			m_xConsoleVar.SetIntValue(m_xOriginalValue.GetIntValue());
			break;
		}
		case CONSOLE_VAR_FLOAT:
		{
			m_xConsoleVar.SetFloatValue(m_xOriginalValue.GetFloatValue());
			break;
		}
		default:
		{
			Asura_Assert(false, "unsupported variable type");
			break;
		}
	}
}

void Asura_Cutscene_Event_ConsoleVar::Trigger(Asura_Cutscene* pxCutscene)
{
	switch (m_eValueType)
	{
		case CONSOLE_VAR_BOOL:
		{
			bool bValue;
			if (Asura_StringUtil::GetBoolValue(m_xValue.GetString(), &bValue))
			{
				m_xConsoleVar.SetBoolValue(bValue);
			}
			else
			{
				Asura_Assertf(false, ("failed to get bool value from %s", m_xValue.GetString()));
			}
			break;
		}
		case CONSOLE_VAR_INT:
		{
			int iValue;
			if (Asura_StringUtil::GetIntValue(m_xValue.GetString(), &iValue))
			{
				m_xConsoleVar.SetIntValue(iValue);
			}
			else
			{
				Asura_Assertf(false, ("failed to get int value from %s", m_xValue.GetString()));
			}
			break;
		}
		case CONSOLE_VAR_FLOAT:
		{
			float fValue;
			if (Asura_StringUtil::GetFloatValue(m_xValue.GetString(), &fValue))
			{
				m_xConsoleVar.SetFloatValue(fValue);
			}
			else
			{
				Asura_Assertf(false, ("failed to get float value from %s", m_xValue.GetString()));
			}
			break;
		}
		default:
		{
			Asura_Assert(false, "unsupported variable type");
			break;
		}
	}
}

int Asura_Cutscene_Event_ConsoleVar::GetChunkSize()
{
	int iChunkSize = PARENT::GetChunkSize() 
		           + sizeof(int)                   // Chunk version
				   + m_xValue.GetSizeInInts() * 4  // Asura String size
				   + m_xConsoleVar.GetChunkSize(); // Console var chunk size       

	return iChunkSize;
}

void Asura_Cutscene_Event_ConsoleVar::SaveToChunk(char*& pcChunkBuffer)
{
	int iCurrentChunkVersion = CURRENT_CHUNK_VERSION;

	Asura_Chunk_ChunkBuffer_Stream xChunkBufferStream(pcChunkBuffer);

	xChunkBufferStream << iCurrentChunkVersion;

	PARENT::WriteToChunkStream(xChunkBufferStream);

	xChunkBufferStream << m_xValue;

	// Save the console var here
	m_xConsoleVar.WriteToChunkStream(xChunkBufferStream);
}

void Asura_Cutscene_Event_ConsoleVar::LoadFromChunk(Asura_Chunk_CutsceneEvent* pxChunk, Asura_Handle iFileHandle)
{
	int iCurrentChunkVersion;

	Asura_Chunk_File_Stream xFstream(iFileHandle);

    xFstream >> iCurrentChunkVersion;

	switch ( iCurrentChunkVersion )
	{
		case 0 :
		{
			PARENT::LoadFromChunk(pxChunk, iFileHandle);
			
			xFstream >> m_xValue;
			m_xConsoleVar.ReadFromChunkStream(xFstream);
			break;
		}
		default :
		{
			Asura_Assertf(false, ("Unknown chunk version (%d) in Asura_Cutscene_Event_ConsoleVar", iCurrentChunkVersion));
			break;
		}
	}
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_ConsoleVar::Copy(Asura_Cutscene_Event* pxEvent)
{
	Asura_Cutscene_Event_ConsoleVar* pxNewEvent = static_cast<Asura_Cutscene_Event_ConsoleVar*> (pxEvent);
	PARENT::Copy(pxNewEvent);

    m_xValue = pxNewEvent->m_xValue;
	m_xConsoleVar.Copy(&pxNewEvent->m_xConsoleVar);
}
#endif // ASURA_TOOLS

#endif	//defined(ASURA_USE_GUI_MENU_SYSTEM)


#ifdef ASURA_USE_HDR
// Asura_Cutscene_Event_HDRSettings
//
// (Ali Jakamy - 21.07.2008) Created
// Static members
Asura_CameraData::HDRSettings Asura_Cutscene_Event_HDRSettings::s_xHDRSettings;
Asura_CameraData::HDRSettings Asura_Cutscene_Event_HDRSettings::s_xOriginalHDRSettings;
bool Asura_Cutscene_Event_HDRSettings::s_bHDRSet = false;

Asura_Cutscene_Event_HDRSettings::Asura_Cutscene_Event_HDRSettings(Asura_Cutscene* pxCutscene)
: PARENT(ACET_HDRSettings, pxCutscene)
, m_fDuration(0.0f)
{
}

void Asura_Cutscene_Event_HDRSettings::Finish()
{
	if ( s_bHDRSet )
	{
		// Unset HDR 
		s_bHDRSet = false;
		// Restore Original settings
		Asura_Camera::SetHDRSettings(s_xOriginalHDRSettings);
	}
}

void Asura_Cutscene_Event_HDRSettings::UpdateAfterTrigger(Asura_Cutscene* pxCutscene)
{
	if ( s_bHDRSet )
	{
		if( pxCutscene->Timer > m_fTriggerTime + m_fDuration )
		{
			// Unset HDR
            s_bHDRSet = false;
			// Restore the orginal HDR Settings
			Asura_Camera::SetHDRSettings(s_xOriginalHDRSettings);
		}
	}
}

void Asura_Cutscene_Event_HDRSettings::Trigger(Asura_Cutscene* pxCutscene)
{
	if( pxCutscene->Timer < m_fTriggerTime + m_fDuration )
	{
		// HDR is not set
		if(!s_bHDRSet)
		{
			// Save original HDR settings
			Asura_Camera::GetHDRSettings(s_xOriginalHDRSettings);
			s_bHDRSet = true;
		}
		Asura_Camera::SetHDRSettings(s_xHDRSettings);
	}
}

int Asura_Cutscene_Event_HDRSettings::GetChunkSize()
{
	Asura_Chunk_Dummy_Stream xStream;
	WriteToChunkStream(xStream);
	return sizeof(Asura_Chunk_CutsceneEvent) + xStream.GetPosition();
}

void Asura_Cutscene_Event_HDRSettings::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	int iCurrentChunkVersion = CURRENT_CHUNK_VERSION;

	xStream << iCurrentChunkVersion;

	PARENT::WriteToChunkStream(xStream);

	xStream << m_fDuration;
	
	// Save the HDR Settings here
	xStream << s_xHDRSettings.m_bDynamicExposure;
	xStream << s_xHDRSettings.m_fAdaptationRate;
	xStream << s_xHDRSettings.m_fFixedLuminance;
	xStream << s_xHDRSettings.m_fKeyValue;
	xStream << s_xHDRSettings.m_fMaxLuminance;
	xStream << s_xHDRSettings.m_fMinLuminance;
	xStream << s_xHDRSettings.m_fWhitePoint;
	xStream << s_xHDRSettings.m_fBlackPoint;
}

void Asura_Cutscene_Event_HDRSettings::ReadFromChunkStream(Asura_Chunk_CutsceneEvent* pxChunk,Asura_Chunk_Stream& xStream)
{
	int iCurrentChunkVersion;

    xStream >> iCurrentChunkVersion;

	if(iCurrentChunkVersion > CURRENT_CHUNK_VERSION)
	{
		Asura_Assertf(false, ("Unknown chunk version (%d) in Asura_Cutscene_Event_HDRSettings",
			iCurrentChunkVersion));

		return;
	}

	PARENT::ReadFromChunkStream(pxChunk, xStream);

	xStream >> m_fDuration;

	// Load HDR Settings
	xStream >> s_xHDRSettings.m_bDynamicExposure;
	xStream >> s_xHDRSettings.m_fAdaptationRate;
	xStream >> s_xHDRSettings.m_fFixedLuminance;
	xStream >> s_xHDRSettings.m_fKeyValue;
	xStream >> s_xHDRSettings.m_fMaxLuminance;
	xStream >> s_xHDRSettings.m_fMinLuminance;
	xStream >> s_xHDRSettings.m_fWhitePoint;

	if(iCurrentChunkVersion >= 1)
	{
		xStream >> s_xHDRSettings.m_fBlackPoint;
	}
}

void Asura_Cutscene_Event_HDRSettings::SaveToChunk(char*& pcChunkBuffer)
{
	Asura_Chunk_ChunkBuffer_Stream xStream(pcChunkBuffer);
    WriteToChunkStream(xStream);
}


void Asura_Cutscene_Event_HDRSettings::LoadFromChunk(Asura_Chunk_CutsceneEvent* pxChunk, Asura_Handle iFileHandle)
{
	Asura_Chunk_File_Stream xStream(iFileHandle);
	ReadFromChunkStream(pxChunk, xStream);
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_HDRSettings::Copy(Asura_Cutscene_Event* pxEvent)
{
	Asura_Cutscene_Event_HDRSettings* pxNewEvent = static_cast<Asura_Cutscene_Event_HDRSettings*> (pxEvent);
	PARENT::Copy(pxNewEvent);

	s_xHDRSettings = pxNewEvent->s_xHDRSettings;
    m_fDuration    = pxNewEvent->m_fDuration;
}
#endif	//defined ASURA_TOOLS

#endif	//defined ASURA_USE_HDR

//////////////////////////////////////////////////////////////////////////////////////////
/*--------------------------------------**
** Asura_Cutscene_Event_FacialAnimation **
**--------------------------------------*/

#ifdef ASURA_USE_FACIAL_ANIMATION_SYSTEM

Asura_Cutscene_Event_FacialAnimation::Asura_Cutscene_Event_FacialAnimation(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_FacialAnimation,pxCutscene)
{
	m_uAnimID = ASURA_HASH_ID_UNSET;
	m_uAnimationFlags = 0;
}



void Asura_Cutscene_Event_FacialAnimation::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event::Trigger(pxCutscene);

	if(m_pxTargetActor && m_pxTargetActor->IsHierarchyActor())
	{
		bool bLooped = (m_uAnimationFlags & ASURA_CUTSCENE_EVENT_FACIALANIMATION_LOOPED) ? true : false;

		Asura_Cutscene_Actor_Hierarchy* pxActor = (Asura_Cutscene_Actor_Hierarchy*) m_pxTargetActor;
		pxActor->PlayFacialAnimation(m_uAnimID, m_fNextTriggerTime, bLooped);
	}
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_FacialAnimation::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_FacialAnimation* pxEvent = (Asura_Cutscene_Event_FacialAnimation*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_uAnimID = pxEvent->m_uAnimID;
	m_uAnimationFlags = pxEvent->m_uAnimationFlags;
}

void Asura_Cutscene_Event_FacialAnimation::LoadFiles()
{
	if(m_uAnimID)
	{
		Tools_FacialAnimation_System::FindOrLoadAnimation(m_uAnimID);
	}
}

void Asura_Cutscene_Event_FacialAnimation::AddExtraFilesToList( List<Asura_String>& xNameList) const
{
	if(m_uAnimID)
	{
		Tools_Level_Export::AddFaceAnimationToExportList(NULL, m_uAnimID);
	}
}
#endif // ASURA_TOOLS

#endif //ASURA_USE_FACIAL_ANIMATION_SYSTEM


//////////////////////////////////////////////////////////////////////////////////////////
/*--------------------------------------**
** Asura_Cutscene_Event_MixerPreset **
**--------------------------------------*/


Asura_Cutscene_Event_MixerPreset::Asura_Cutscene_Event_MixerPreset(Asura_Cutscene* pxCutscene)
:Asura_Cutscene_Event(ACET_MixerPreset,pxCutscene)
{
	m_uMixerPreset = ASURA_HASH_ID_UNSET;
}



void Asura_Cutscene_Event_MixerPreset::Trigger(Asura_Cutscene* pxCutscene)
{
	Asura_Cutscene_Event::Trigger(pxCutscene);

	pxCutscene->SetMixerPreset(m_uMixerPreset);
}

#ifdef ASURA_TOOLS
void Asura_Cutscene_Event_MixerPreset::Copy(Asura_Cutscene_Event* _pxEvent)
{
	Asura_Cutscene_Event_MixerPreset* pxEvent = (Asura_Cutscene_Event_MixerPreset*) _pxEvent;
	Asura_Cutscene_Event::Copy(pxEvent);

	m_uMixerPreset = pxEvent->m_uMixerPreset;
}
#endif // ASURA_TOOLS


/// End of file. ///
