#include "Core/Asura.h"
#include "Cutscene/Tools_CutsceneActor_Object.h"

#include "Animation/Asura_Animation.h"
#include "Animation/Asura_Hierarchy.h"
#include "Camera/Asura_Camera.h"
#include "Core/Asura_TextureManagement.h"
#include "ChunkSystem/Asura_Chunk_Loaders.h"
#include "Cutscene/Asura_Cutscene_Actor.h"
#include "Cutscene/Asura_Cutscene_Track.h"
#include "Cutscene/Asura_Cutscene_System.h"
#include "Cutscene/Asura_Cutscene.h"
#include "Cutscene/Asura_Cutscene_Attachment.h"
#include "Entity/Asura_Entity.h"
#include "Entity/Asura_StaticMessage_System.h"
#include "Entity/PhysicalObject/Asura_ServerEntity_PhysicalObject.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Dialog.h"
#include "Mesh/Asura_Mesh_System.h"


#include "Tools_Overlays.h"
#ifdef ASURA_USE_ANIMATION_IK_SOLVER
#include "Animation/IK/Tools_Animation_IK_Template_System.h"
#endif
#include "Cutscene/Tools_Cutscene_Object.h"
#include "Cutscene/Tools_Cutscene_Track.h"
#include "Cutscene/Tools_Cutscene_Timeline.h"
#include "Cutscene/Tools_Cutscene_Attachment.h"
#include "Cutscene/Tools_Mode_Cutscene.h"
#include "Cutscene/Tools_Cutscene_Dialog.h"
#include "Cutscene/Tools_Cutscene_RecordAnimation.h"
#include "Entity/Tools_SecondaryEntityFile_System.h"
#ifdef ASURA_USE_FACIAL_ANIMATION_SYSTEM
#include "FacialAnimation/Dialog/Tools_Dialog_FacialAnimation_PickFace.h"
#endif
#include "Hierarchy/Tools_Animation_Files.h"
#include "Interface/Tools_ContextMenu_System.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Misc/Tools_Timed_Messages.h"
#include "Misc/Tools_Modal_Overlay.h"
#include "Misc/Tools_Dialog.h"
#include "Objects/Tools_Object_System.h"
#include "PC/FileSystem/Tools_FileSystem.h"
#include "Shapes/Tools_Shapes.h"
#include "Texturing/Tools_PickTexture.h"


#include "Debugging/Asura_Assert.h"

//static member variables
Asura_Vector_2					Tools_CutsceneActor_Object::OverlayPosition = Asura_Vector_2(3.0f,20.0f);
Tools_CutsceneActor_Object*     Tools_CutsceneActor_Object::ThisActor = NULL;
bool							Tools_CutsceneActor_Object::OverlayOpen = true;


Tools_CutsceneActor_Object::Tools_CutsceneActor_Object(Asura_Cutscene_Actor* actor,Tools_Cutscene_Object* cutsceneObject)
:Tools_Object(TOT_CutsceneActor)
{
	Actor = actor;
	CutsceneObject = cutsceneObject;
	Orientation.Identity();
	m_bChanged = false;
	m_iAnimationFlagsBackup = 0;
	m_fAnimationDelayBackup = 0;
	m_uActorFlagsBackup = 0;
	m_uShapeFlagsBackup = 0;
	m_uHierarchyFlagsBackup = 0;
	m_uTextureHashBackup = ASURA_HASH_ID_UNSET;
	m_uIKTemplateIDBackup = ASURA_HASH_ID_UNSET;
	#ifdef ASURA_USE_FACIAL_ANIMATION_SYSTEM
	m_uFaceIDBackup = ASURA_HASH_ID_UNSET;
	#endif

	m_uCameraFlagsBackup = 0;
	m_fInitialFOVBackup = 0.0f;
	m_fNearClipBackup = 0.0f;
	m_fNearClipBackup = 0.0f;
	m_uGuidToReplaceBackup = ASURA_GUID_UNREGISTERED;
	m_bHandheldCameraEnabled = false;

	m_iSelectedAttachmentIndex = -1;
	m_pxSelectedAttachment = 0;
	m_bSelectedAttachmentIndexChanged = false;
	m_uHighestAttachmentID = 0;

	m_bHiddenInTimeline = false;
	m_pxChooseObject = 0;


	Asura_String mesh_name;
	
	switch(actor->Type)
	{
		case ACAT_Camera :
			mesh_name = "ToolsShape_CutsceneCamera";
			break;
		case ACAT_CameraTarget :
			mesh_name = "ToolsShape_CutsceneCameraTarget";
			break;
		default :
			mesh_name = "ToolsShape_CutsceneActor";
			break;

	}

	Mesh = Asura_Mesh_System::GetMesh(mesh_name);
	if(!Mesh)
	{
		mesh_name = "ToolsShape_CutsceneActor";
		Mesh = Asura_Mesh_System::GetMesh(mesh_name);
	}

	//add to the parent's list of objects
	CutsceneObject->ActorObjects.add_entry(this);

}

Tools_CutsceneActor_Object::~Tools_CutsceneActor_Object()
{
	if(m_pxChooseObject)
	{
		Tools_Object_System::ChooseObject_Finish();
		Asura_Assert(!m_pxChooseObject,"");
	}

	//delete any tracks
	while(Tracks.size())
	{
		delete Tracks.first_entry();
	}

	//and attachments
	while(m_xAttachmentList.size())
	{
		delete m_xAttachmentList.first_entry();
	}

	if(Tools_Cutscene_Timeline::ActorObject == this)
	{
		Tools_Cutscene_Timeline::SetActor(0);
	}

	CutsceneObject->ActorObjects.delete_entry(this);
}

void Tools_CutsceneActor_Object::Initialise()
{
	//create all the tracks for this actor
	for(int i=0;i<Actor->NumberOfTracks;i++)
	{
        CutsceneObject->AllocateToolsTrack( Actor->Tracks[i], this, 0 );
	}

	if(Tracks.size() && Tracks.first_entry()->m_xPointObjects.size())
	{
		Tools_Object* pointObject = Tracks.first_entry()->m_xPointObjects.first_entry();
		
		//if the cutscene has some tracks , put the actor object at the start of the first track
		Position = pointObject->Position;
		Orientation = pointObject->Orientation;
	}
	else
	{
		//no tracks yet , put the actor object at the cutscene object
		Position = CutsceneObject->Position;
	}

	m_uActorFlagsBackup = Actor->m_uActorFlags;
	m_uGuidToReplaceBackup = Actor->m_uGuidToReplace;


	if(IsHierarchyActor())
	{
		Asura_Cutscene_Actor_Hierarchy* pxHierarchyActor = (Asura_Cutscene_Actor_Hierarchy*)Actor;
		m_iAnimationFlagsBackup = pxHierarchyActor->m_iAnimationFlags;
		m_fAnimationDelayBackup = pxHierarchyActor->m_fAnimationDelay;
		m_uHierarchyFlagsBackup = pxHierarchyActor->m_uHierarchyFlags;
		m_uTextureHashBackup = pxHierarchyActor->m_uTextureHash;
		m_uIKTemplateIDBackup = pxHierarchyActor->m_uIKTemplateID;
		#ifdef ASURA_USE_FACIAL_ANIMATION_SYSTEM
		m_uFaceIDBackup = pxHierarchyActor->m_uFaceID;
		#endif


		//determine next attachment ID
		for(int i=0;i<pxHierarchyActor->m_iNumberOfAttachments;i++)
		{
			if(pxHierarchyActor->m_ppxAttachments[i]->GetID()>m_uHighestAttachmentID)
			{
				m_uHighestAttachmentID = pxHierarchyActor->m_ppxAttachments[i]->GetID();
			}
		}


		//create tools attachments
		for(int i=0;i<pxHierarchyActor->m_iNumberOfAttachments;i++)
		{
			Tools_Cutscene_Attachment::AllocateToolsAttachment(pxHierarchyActor->m_ppxAttachments[i],this);
		}
	}
	else if(IsShapeActor())
	{
		Asura_Cutscene_Actor_Shape* pxShapeActor = (Asura_Cutscene_Actor_Shape*)Actor;

		m_uShapeFlagsBackup = pxShapeActor->m_uShapeFlags;
	}
	else if(IsCameraActor())
	{
		Asura_Cutscene_Actor_Camera* pxCameraActor = (Asura_Cutscene_Actor_Camera*)Actor;

		m_uCameraFlagsBackup = pxCameraActor->GetCameraFlags();
		m_fInitialFOVBackup = pxCameraActor->GetInitialFOV();
		m_fNearClipBackup = pxCameraActor->m_fNearClip;
		m_fFarClipBackup = pxCameraActor->m_fFarClip;
		m_bHandheldCameraEnabled = pxCameraActor->m_xInitialShake.IsEnabled();
		m_xCameraShakeBackup = pxCameraActor->m_xInitialShake;
	}

	

    //allocate a guid for this actor if we haven't already got one
	//(or if it isn't in the cutscene guid range)
	if (NeedsGuid())
	{
		if(!CutsceneObject->Cutscene->IsValidActorGuid(Actor->m_uGuid))
		{
			Actor->m_uGuid = CutsceneObject->Cutscene->AssignActorGuid();
		}
	}

}


const char* Tools_CutsceneActor_Object::GetName()
{
	return Actor->ActorName.GetString();
}

void Tools_CutsceneActor_Object::Destroy()
{
	Tools_Mode_Cutscene::RefreshPlayback();

	//destroy any remaining tracks
	while(Tracks.size())
	{
		Tracks.first_entry()->Destroy();				
	}

	//destroy attachments
	while(m_xAttachmentList.size())
	{
		m_xAttachmentList.first_entry()->Destroy();
	}

	CutsceneObject->RemoveActorFromCutscene(Actor);
	if(CutsceneUndo) CutsceneUndo->Log_Actor_Destroyed(this);
	
	
	Actor->PreDestructionCleanup();

	delete Actor;
	Actor = NULL;

	Tools_Object::Destroy();

	Tools_SaveAsura::SetCutscenesModified();
}

void Tools_CutsceneActor_Object::OnSelection()
{
	//show the overlay for this object
	ThisActor = this;
	CreateOverlay();
}

void Tools_CutsceneActor_Object::OnDeselection()
{
	//get rid of the overlay
	DeleteOverlay();
	ThisActor = NULL;
}

bool Tools_CutsceneActor_Object::IsHidden()
{
	//if(Actor->Type == ACAT_Camera || Actor->Type == ACAT_CameraTarget)
    if(Actor->IsCamera() || Actor->IsCameraTarget())
	{
		//if a cutscene is being played , and the camera is disabled , then draw the actor objects
		//for the camera and camera targets regardless
		if(Tools_Mode_Cutscene::s_pxThis->CutsceneIsPlaying() && Asura_ClientCutscene_Actor_Camera::Disable)		
		{
			return false;
		}
	}


	return Tools_Object::IsHidden();
}

bool Tools_CutsceneActor_Object::ObjectIsInLOS(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDir, float& fLowest)
{
	//If this is a hierarchy actor  and the cutscene is currently playing ,then allow the
	//LOS tests to hit the cutscene anim
	if(IsHierarchyActor())
	{
		Asura_Cutscene_Actor_Hierarchy* pxHierarchy = (Asura_Cutscene_Actor_Hierarchy*) Actor;
		Asura_Animation* pxAnim = pxHierarchy->GetAnimation();
		if(pxAnim && pxAnim->GetSkin())
		{
	        Asura_Vector_3 xCentre;
	        if (pxAnim->GetOffsetFromCompassPoint(xCentre))
	        {
	            Asura_Bounding_Box xBB =pxAnim->GetSkin()->GetBB();
				float fRadius = xBB.CalculateExtents().Magnitude();
	            
				xCentre = xCentre*Actor->Orientation + Actor->Position;
	            if (Asura_Maths::RayIntersectsSphere(xOrigin, xDir, xCentre, fRadius))
	            {
					Asura_LOS_InputData xLOSInput(xOrigin, xDir, ASURA_GUID_UNREGISTERED, fLowest);
					
	                Asura_LOS_OutputData xLOSOutput;
					xLOSOutput.m_fDistanceToIntersection = fLowest;

					if (pxAnim->LOSTest( Actor->Position, 
												Actor->Orientation,
												& xLOSInput,
												& xLOSOutput) )
					{
						// A hit, write back the distance.
						fLowest = xLOSOutput.m_fDistanceToIntersection;
						return true;
					}
					else
					{
						return false;
					}
	            }
	        }
		}
	}
	//los test against tools object
	if(ShouldShowToolsObjectMesh() && Tools_Object::ShouldTestForLOS())
	{
		return Tools_Object::ObjectIsInLOS(xOrigin,xDir,fLowest);
	}
	return false;
}

bool Tools_CutsceneActor_Object::ShouldTestForLOS()
{
	//Doesn't have the range test, since while playing cutscene the animation may be in range , 
	//when the actor object isn'y
	if(IsHidden()) return false;

	return true;
}


bool Tools_CutsceneActor_Object::ShouldShowToolsObjectMesh()
{
	if(Tools_Mode_Cutscene::s_pxThis->CutsceneIsPlaying() && Tools_Mode_Cutscene::s_pxThis->CutscenePlayback_HideObjects)
	{
    	bool bShow = false;
    	if(Actor->IsCamera() || Actor->IsCameraTarget())
		{
			//camera/camera target objects need to be drawn if the camera is disabled
			bShow = Asura_ClientCutscene_Actor_Camera::Disable;
		}
		if(!bShow) return false;
	
	}
	return true;
}

bool Tools_CutsceneActor_Object::ShouldBeRendered()
{
	//If playing a cutscene  , then we may not want to render the tools object
	if(!ShouldShowToolsObjectMesh()) return false;
	return Tools_Object::ShouldBeRendered();
}

void Tools_CutsceneActor_Object::UpdatePositionInAsuraData(bool updateLinkedObjects /*=true*/)
{
	if(!updateLinkedObjects) return;
	
	//move the first point of the first track here
	//except possibly during cutscene playback
	if(Tools_Mode_Cutscene::s_pxThis->CutsceneIsPlaying())
	{
		if(!IsHierarchyActor() && !IsShapeActor()) return;
	}
	if(!Tracks.size()) return;
	Tools_Cutscene_Track* track = Tracks.first_entry();
	if(!track->m_xPointObjects.size()) return;
	Tools_CutsceneTrackPoint_Object* point = track->m_xPointObjects.first_entry();
	
	point->Position = Position;
	point->Orientation = Orientation;
	point->UpdatePositionInAsuraData(false);

	if((track->m_pxTrack->m_uTrackFlags & TrackFlag_Circular) && track->m_xPointObjects.size()>=2)
	{
		//update the last point as well
		point = track->m_xPointObjects.first_entry();
	
		point->Position = Position;
		point->Orientation = Orientation;
		point->UpdatePositionInAsuraData(false);
	}

}

void Tools_CutsceneActor_Object::UpdatePositionFromTrack()
{
	if(!Tracks.size()) return;
	Tools_Cutscene_Track* track = Tracks.first_entry();
	if(!track->m_xPointObjects.size()) return;
	Tools_CutsceneTrackPoint_Object* point = track->m_xPointObjects.first_entry();
	
	Position = point->Position;
	Orientation = point->Orientation;
}

Asura_Guid Tools_CutsceneActor_Object::GetGuid() const
{
	return Actor->m_uGuid;
}


void Tools_CutsceneActor_Object::ToggleOverlay(void)
{
	OverlayOpen = !OverlayOpen;

	//only need to create/destoy overlay if we have an actor selected
	if(ThisActor)
	{
		if (OverlayOpen)
		{
			ThisActor->CreateOverlay();
		}
		else
		{
			ThisActor->DeleteOverlay();
		}
	}
}
bool Tools_CutsceneActor_Object::OverlayIsOn(void)
{
	return OverlayOpen;
}

void Tools_CutsceneActor_Object::CreateOverlay()
{
	if(!OverlayOpen) return;
	
	Asura_Overlay* newOverlay;
	newOverlay = new Asura_Overlay(GetName(),false,&OverlayPosition);
	
	Asura_Vector_2 offset = Asura_Vector_2(1.0f,3.0f);

	AddOverlayControls(newOverlay,offset);

	newOverlay->m_uID = TOOLS_OVERLAY_SELECTEDOBJECT;

	Asura_Overlay_System::SubmitNewOverlay(newOverlay,false);

	UpdateOverlay();
}

void Tools_CutsceneActor_Object::AddOverlayControls(class Asura_Overlay* pxOverlay,Asura_Vector_2& xOffset)
{
	Asura_Dialog* pxDialog;

	pxDialog = new Asura_Function_Dialog("Create Track",xOffset,CreateTrack);
	pxDialog->SetHelpText("Create a new track for this actor , placed at the end of the list of tracks.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Copy Tracks",xOffset,CopyTracks);
	pxDialog->SetHelpText("Copies the tracks from another actor in this or another cutscene.\nThis will delete the existing tracks.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Rename",xOffset,_Rename);
	pxDialog->SetHelpText("Rename this actor.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Edit Events",xOffset,_EditEvents);
	pxDialog->SetHelpText("Opens the event editing overlay at the first event that targets this actor.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	if(IsHierarchyActor())
	{
		pxDialog = new Asura_Function_Dialog("Record Animation",xOffset,RecordAnimation);
		pxDialog->SetHelpText("Records this actor's movements to an animation .asr file.\nMainly added to allow the resultant animation to be imported into Max for editing.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}

	pxDialog = new Asura_Function_Dialog("Replace",xOffset,ChooseEntityToReplace);
	pxDialog->SetHelpText("Select an entity that is replaced by this actor.\nUsed for hiding the entity that corresponds to this actor while the cutscene is playing.");
	pxDialog->m_iControlID = DialogID_GuidToReplace;
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_FlagTickbox_Dialog("External Control",xOffset,&Actor->m_uActorFlags,ASURA_ACTORFLAG_EXTERNALTRACKCONTROL,&m_bChanged);
	pxDialog->SetHelpText("If ticked then the actor won't move along its track of its own accord, and is instead moved by some project specific mechanism.\nDon't tick this unless its needed for yur project.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();


	if(IsHierarchyActor())
	{
		pxDialog = new Asura_FlagTickbox_Dialog("Collisions",xOffset,&Actor->m_uActorFlags,ASURA_ACTORFLAG_COLLISIONS,&m_bChanged);
		pxDialog->SetHelpText("If ticked then the actor will have a collision mesh/box applied to it, for doing things like pushing other objects out of the way.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}


	//add controls specific to hierarchy actors
	if(IsHierarchyActor())
	{
 		
		pxDialog = new Asura_Dialog("Attachment:",xOffset);
		pxOverlay->AddDialog(pxDialog);
		xOffset.x += 12.0f;
	
        pxDialog = new Asura_Function_Dialog( "Create", xOffset, CreateAttachmentForCurActorObj );
        pxDialog->SetHelpText("Add a new attachment to this actor , and start editing it.");
        pxOverlay->AddDialog(pxDialog);
        xOffset.x += 10.0f;

		pxDialog = new Asura_Function_Dialog("Edit",xOffset,EditAttachment);
		pxDialog->SetHelpText("Brings up overlay for editing existing attachments (if any).");
		pxOverlay->AddDialog(pxDialog);
		xOffset.x -= 22.0f;
		xOffset.y+=Asura_Dialog::DefaultSpacing();
		
		Asura_Cutscene_Actor_Hierarchy* hierarchyActor = (Asura_Cutscene_Actor_Hierarchy*)Actor;


		for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
		{
			pxDialog = new Asura_Function_Dialog("Skin",xOffset,ChooseSkin,i);
			pxDialog->SetIsEnabledFunction(IsEnabled_ChooseSkin, i);
			pxDialog->SetHelpText("Choose skin for this actor.");
			pxDialog->m_iControlID = DialogID_Skin0+i;
			pxOverlay->AddDialog(pxDialog);
			xOffset.y+=Asura_Dialog::DefaultSpacing();
		}

		pxDialog = new Asura_Function_Dialog("Edit/Create Initial Anim",xOffset,EditInitialAnim);
		pxDialog->SetHelpText("Edits the first animation change event for this actor , or creates an edits such an event at timer zero.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		Asura_Icon_Dialog* pxIcon = new Asura_Icon_Dialog(xOffset,Tools_Dialog::GetIconTexture(),TOOLS_ICON_DELETE);
		pxIcon->SetClickedFunction(ClearTexture,(int)this);
		pxIcon->m_iControlID = DialogID_ClearTexture;
		pxIcon->SetHelpText("Resets texture back to default");
		pxOverlay->AddDialog(pxIcon);
		
		pxDialog = new Asura_Function_Dialog("Texture",xOffset,ChooseTexture,(int)this);
		pxDialog->SetHelpText("Allows a replacement texture to be selected for the skin");
		pxDialog->m_iControlID = DialogID_Texture;
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();


		#ifdef ASURA_USE_FACIAL_ANIMATION_SYSTEM
		pxDialog = new Tools_Dialog_FacialAnimation_PickFace("Face: ", xOffset, &hierarchyActor->m_uFaceID, &m_bChanged);
		pxDialog->SetHelpText("Sets the Face to use for this actor , if using facial animation");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::LargeSpacing();
		#endif

		
		#ifdef ASURA_USE_ANIMATION_IK_SOLVER
		pxDialog = new Tools_PickIKTemplate_Dialog(xOffset, 12, &hierarchyActor->m_uIKTemplateID, &m_bChanged, "IK");
		pxDialog->SetHelpText("Sets the IK template to use on this actor");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::LargeSpacing();
		#endif




//		float fX = xOffset.x;
		//add tickboxes for the flags
//		pxDialog = new Asura_FlagTickbox_Dialog("Loop",xOffset,&hierarchyActor->m_iAnimationFlags,AsuraAnimationFlag_Loop,&m_bChanged);
//		pxDialog->SetHelpText("Initial animation should be looped.");
//  		pxOverlay->AddDialog(pxDialog);

//		xOffset.x += pxDialog->m_xMaxCoord.x;
// 		pxDialog = new Asura_FlagTickbox_Dialog("Reversed",xOffset,&hierarchyActor->m_iAnimationFlags,AsuraAnimationFlag_Reversed,&m_bChanged);
//		pxDialog->SetHelpText("Initial animation should be reversed.");
//		pxOverlay->AddDialog(pxDialog);
//		xOffset.x = fX;
//		xOffset.y+=Asura_Dialog::DefaultSpacing();

//		pxDialog = new Asura_FlagTickbox_Dialog("Ignore Movement",xOffset,&hierarchyActor->m_iAnimationFlags,AnimationChangeFlag_IgnoreMovement,&m_bChanged);
//		pxDialog->SetHelpText("Disables the use of any movement data stored in the animation.\nBy default animation movement gets used if there is no movement from the tracks.");
//		pxOverlay->AddDialog(pxDialog);
//		xOffset.y+=Asura_Dialog::DefaultSpacing();

//		pxDialog = new Asura_FlagTickbox_Dialog("Mirror",xOffset,&hierarchyActor->m_iAnimationFlags,AsuraAnimationFlag_Mirror,&m_bChanged);
//		pxDialog->SetHelpText("Mirrors the initial animation. This requires project-specific implementation to work correctly.");
//		pxOverlay->AddDialog(pxDialog);
//		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_FlagTickbox_Dialog("No Shadow",xOffset,&hierarchyActor->m_uHierarchyFlags,ASURA_HIERARCHYACTORFLAG_NOSHADOW,&m_bChanged);
		pxDialog->SetHelpText("Don't draw the blob shadow beneath this actor.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_FlagTickbox_Dialog("Not Rendered",xOffset,&hierarchyActor->m_uHierarchyFlags,ASURA_HIERARCHYACTORFLAG_INITIALLY_HIDDEN,&m_bChanged);
		pxDialog->SetHelpText("This actor is never rendered (such as particle bloke).\nUsed for optimisation purposes.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_FlagTickbox_Dialog("Ambient Only",xOffset,&hierarchyActor->m_uHierarchyFlags,ASURA_HIERARCHYACTORFLAG_AMBIENTONLY,&m_bChanged);
		pxDialog->SetHelpText("This actor is never rendered (such as particle bloke).\nUsed for optimisation purposes.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_FlagTickbox_Dialog("Don't Export Resources",xOffset,&hierarchyActor->m_uHierarchyFlags,ASURA_HIERARCHYACTORFLAG_DONT_EXPORT_RESOURCES,&m_bChanged);
		pxDialog->SetHelpText("Prevents this actors cutscene resources being exported to level files, unless a non-flagged actor requires them.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

//		pxDialog = new Tools_CutsceneTime_Dialog("Anim Delay",xOffset,&hierarchyActor->m_fAnimationDelay,0,999.0f,0.1f,&m_bChanged);
//		pxDialog->SetHelpText("Time delay before the animation should start playing.");
//		pxOverlay->AddDialog(pxDialog);
//		xOffset.y+=Asura_Dialog::DefaultSpacing();


	}
	if(IsShapeActor())
	{
		Asura_Cutscene_Actor_Shape* pxShapeActor = (Asura_Cutscene_Actor_Shape*)Actor;


		pxDialog = new Asura_Function_Dialog("Shape",xOffset,ChooseShape);
		pxDialog->SetHelpText("Choose shape for this actor.");
		pxDialog->m_iControlID = DialogID_Shape;
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_FlagTickbox_Dialog("No Shadow",xOffset,&pxShapeActor->m_uShapeFlags,ASURA_SHAPEACTORFLAG_NOSHADOW,&m_bChanged);
		pxDialog->SetHelpText("Don't draw the blob shadow beneath this actor.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
		
	}
	if(IsCameraActor())
	{
		Asura_Cutscene_Actor_Camera* pxCameraActor = (Asura_Cutscene_Actor_Camera*) Actor;


		pxDialog = new Asura_AngleNumerical_Dialog("FOV",xOffset,&pxCameraActor->m_fInitialFOV,2.0f,179.0f,0.1f,"%.2f",&m_bChanged);
		pxDialog->SetHelpText("Initial horizontal field of view in degrees.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
		
		pxDialog = new Asura_FlagTickbox_Dialog("", xOffset, &pxCameraActor->m_uCameraFlags, ASURA_CAMERAACTORFLAG_USENEARCLIP, &m_bChanged);
		pxDialog->SetHelpText("Enables control of the near clip plane by this camera");
		pxOverlay->AddDialog(pxDialog);
		xOffset.x = pxDialog->GetMaxCoords().x;

		pxDialog = new Asura_Numerical_Dialog("Near Clip",xOffset,&pxCameraActor->m_fNearClip,0.001f,99999.0f,1.0f,"%.3f",&m_bChanged);
		pxDialog->SetHelpText("The near clip plane to use for this camera");
		pxDialog->SetIsEnabledFunction(_CameraUsesNearClipPlane);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
		xOffset.x = 1.0f;

		pxDialog = new Asura_FlagTickbox_Dialog("", xOffset, &pxCameraActor->m_uCameraFlags, ASURA_CAMERAACTORFLAG_USEFARCLIP, &m_bChanged);
		pxDialog->SetHelpText("Enables control of the far clip plane by this camera");
		pxOverlay->AddDialog(pxDialog);
		xOffset.x = pxDialog->GetMaxCoords().x;

		pxDialog = new Asura_Numerical_Dialog("Far Clip",xOffset,&pxCameraActor->m_fFarClip,0.001f,99999.0f,1.0f,"%.3f",&m_bChanged);
		pxDialog->SetHelpText("The far clip plane to use for this camera");
		pxDialog->SetIsEnabledFunction(_CameraUsesFarClipPlane);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
		xOffset.x = 1.0f;


		
		pxDialog = new Asura_Tickbox_Dialog("Camera shake",xOffset,&m_bHandheldCameraEnabled,&m_bChanged);
		pxDialog->SetHelpText("If ticked a camera shake effect will be applied to this camera as it moves.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Frequency",xOffset,&pxCameraActor->m_xInitialShake.m_fFrequency,0.1f,20.0f,0.1f,"%.2f",&m_bChanged);
		pxDialog->SetHelpText("How quickly the camera jitters around.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Pitch",xOffset,&pxCameraActor->m_xInitialShake.m_fPitch,0.0f,4.0f,0.05f,"%.2f",&m_bChanged);
		pxDialog->SetHelpText("How much the camera pitches.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Yaw",xOffset,&pxCameraActor->m_xInitialShake.m_fYaw,0.0f,4.0f,0.05f,"%.2f",&m_bChanged);
		pxDialog->SetHelpText("How much the camera yaws.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Roll",xOffset,&pxCameraActor->m_xInitialShake.m_fRoll,0.0f,4.0f,0.05f,"%.2f",&m_bChanged);
		pxDialog->SetHelpText("How much the camera rolls.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Speed Scale",xOffset,&pxCameraActor->m_xInitialShake.m_fSpeedScale,0.0f,50.0f,0.1f,"%.2f",&m_bChanged);
		pxDialog->SetHelpText("How much the camera's movement is affected by the speed of the camera.\nSmaller numbers mean the camera needs to move faster before the camera will reach full wobbliness.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Min Jitter",xOffset,&pxCameraActor->m_xInitialShake.m_fMinScale,0.0f,1.0f,0.05f,"%.2f",&m_bChanged);
		pxDialog->SetHelpText("The minimum amount of jitter that will be applied to the camera.\nIf 0, the camera will be motionless when stationary.\nWhen 1, the camera will always be at full wobbliness, regardless of how slow it's moving.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		//determine whether this is the initial camera
		if(Actor == Actor->Cutscene->GetCameraByIndex(0))
		{
			pxDialog = new Asura_Dialog("Initial Camera",xOffset);
			pxDialog->SetHelpText("This is the initial camera.\nIf you want to change that... select another camera");
			pxOverlay->AddDialog(pxDialog);
			xOffset.y+=Asura_Dialog::DefaultSpacing();
		}
		else
		{
			pxDialog = new Asura_Function_Dialog("Set Initial Camera",xOffset,SetInitialCamera,(int)this);
			pxDialog->SetHelpText("Makes this the initial camera");
			pxOverlay->AddDialog(pxDialog);
			xOffset.y+=Asura_Dialog::DefaultSpacing();

		}

		pxDialog = new Asura_Function_Dialog("Target",xOffset,ChooseCameraTarget);
		pxDialog->SetHelpText("Choose the initial target actor for this camera");
		pxDialog->m_iControlID = DialogID_CameraTarget;
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_FlagTickbox_Dialog("Allow player control", xOffset, &pxCameraActor->m_uCameraFlags, ASURA_CAMERAACTORFLAG_ALLOWPLAYERCONTROL, &m_bChanged);
		pxDialog->SetHelpText("Whether or not the player may rotate the camera");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Player control yaw limit", xOffset, &pxCameraActor->m_fInitialPlayerControlYawLimit, 0.f, 180.f, 1.f, "%1.0f", &m_bChanged);
		pxDialog->SetHelpText("The maximum amount by which the player may affect the yaw of the camera");
		pxDialog->SetIsEnabledFunction(_CameraAllowsPlayerControl);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Player control pitch limit", xOffset, &pxCameraActor->m_fInitialPlayerControlPitchLimit, 0.f, 180.f, 1.f, "%1.0f", &m_bChanged);
		pxDialog->SetHelpText("The maximum amount by which the player may affect the pitch of the camera");
		pxDialog->SetIsEnabledFunction(_CameraAllowsPlayerControl);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}

	//set things up so that right clicking on the overlay will be equivalent to right clicking on the object
	//RAR 06/11/2007
	pxOverlay->SetContextMenuCallback(Tools_Object_System::AddContextMenuItemsFromOverlay, this);

}

void Tools_CutsceneActor_Object::DeleteOverlay()
{
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_SELECTEDOBJECT);
}

void Tools_CutsceneActor_Object::UpdateOverlay()
{
	if(!IsSelected()) return;
	char szText[200];

	Asura_Overlay* pxOverlay = Asura_Overlay_System::GetOverlay(TOOLS_OVERLAY_SELECTEDOBJECT);
	if(!pxOverlay) return;


	if(IsHierarchyActor())
	{
		Asura_Cutscene_Actor_Hierarchy* pxHierarchyActor = (Asura_Cutscene_Actor_Hierarchy*)Actor;

		//update skin label
		for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
		{
        	if (pxHierarchyActor->m_aiSkinID[i]!=-1)
        	{
	    	    Asura_String & xSkinName = CutsceneObject->Cutscene->SkinNames.GetName(pxHierarchyActor->m_aiSkinID[i]);
        	    #if ASURA_ANIMATION_SKIN_COUNT > 1
        	    Asura_PrintfToBfr(szText,"Skin %d : %s",200,true,i,xSkinName.GetString());
				#else
        	    Asura_PrintfToBfr(szText,"Skin : %s",200,true,xSkinName.GetString());
				#endif
        	}
			else
        	{
        	    #if ASURA_ANIMATION_SKIN_COUNT > 1
        	    Asura_PrintfToBfr(szText,"Skin %d : *None*",200,true,i);
				#else
				strcpy(szText,"Skin : *None*");
				#endif
        	}
			pxOverlay->UpdateDialogLabel(DialogID_Skin0+i,szText);
		}

		//update anim label
        if (pxHierarchyActor->AnimID!=-1)
        {
	        Asura_String & xAnimName = CutsceneObject->Cutscene->AnimationNames.GetName(pxHierarchyActor->AnimID);
            Asura_PrintfToBfr(szText,"Anim : %s",200,true,xAnimName.GetString());
        }
        else
        {
            strcpy(szText,"Anim : *None*");
        }
		pxOverlay->UpdateDialogLabel(DialogID_Anim,szText);
		
		//update texture control
		int iTextureID = Asura_Texture_Management::GetIndexFromHashID(pxHierarchyActor->m_uTextureHash);
		if(iTextureID != ASURA_TEXTURE_NONE)
		{
			const Asura_String* pxName = Asura_Texture_Management::GetTextureName(iTextureID);
			Asura_PrintfToBfr(szText,"Texture : %s",200,true,Tools_FileSystem::StripPath(pxName->GetString()));
		}
		else
		{
			strcpy(szText,"Texture : *Default*");
		}
		pxOverlay->UpdateDialogLabel(DialogID_Texture,szText);


		//re-position the clear texture icon
		Asura_Dialog* pxTextureDialog = pxOverlay->GetDialog(DialogID_Texture);
		Asura_Dialog* pxClearTextureDialog = pxOverlay->GetDialog(DialogID_ClearTexture);

		if(pxTextureDialog && pxClearTextureDialog)
		{
			Asura_Vector_2 xPos = pxClearTextureDialog->m_xOffset;
			xPos.x = pxTextureDialog->m_xMaxCoord.x+1.0f;
			pxClearTextureDialog->MoveDialog(xPos);
			pxOverlay->ResizeOverlay();
		}
		
	}

	if(IsShapeActor())
	{
		Asura_Cutscene_Actor_Shape* pxShapeActor = (Asura_Cutscene_Actor_Shape*)Actor;

		//update shape label
		if(pxShapeActor->m_uShapeID)
		{
			const char* pszShapeName = Tools_ShapeTable::LookupObjectMeshName(pxShapeActor->m_uShapeID);
			if(pszShapeName)
			{
				Asura_PrintfToBfr(szText,"Shape : %s",200,true,pszShapeName);
			}
			else
			{
				strcpy(szText,"Shape : *Unknown*");
			}
		}
		else
		{
			strcpy(szText,"Shape : *Not Set*");
		}
		pxOverlay->UpdateDialogLabel(DialogID_Shape,szText);
	}

	if(IsCameraActor())
	{
		Asura_Cutscene_Actor_Camera* pxCameraActor = (Asura_Cutscene_Actor_Camera*)Actor;

		//update target label
		if(pxCameraActor->GetInitialTargetName())
		{
			
			Asura_PrintfToBfr(szText,"Target : %s",200,true,pxCameraActor->GetInitialTargetName());
		}
		else
		{
			strcpy(szText,"Target : *None*");
		}
		pxOverlay->UpdateDialogLabel(DialogID_CameraTarget,szText);
	}

	//Guid to replace
	if(Actor->m_uGuidToReplace == ASURA_GUID_UNREGISTERED)
	{
		strcpy(szText,"Replaces : *None*");
	}
	else
	{
		Tools_Object* pxEntityObject = Tools_Object_System::GetObjectFromGuid(Actor->m_uGuidToReplace);
		if(pxEntityObject)
		{
			Asura_PrintfToBfr(szText,"Replaces : %s",200,true,pxEntityObject->GetName());
		}
		else
		{
			strcpy(szText,"Replaces : *Not Found*");
		}
	}
	pxOverlay->UpdateDialogLabel(DialogID_GuidToReplace,szText);

}



void Tools_CutsceneActor_Object::CreateTrack()
{
	Asura_Assert(ThisActor,"");

	Asura_String name;

	//if this is the first track , just get the name from the actor
	if(!ThisActor->Actor->NumberOfTracks)
	{
		name = ThisActor->Actor->ActorName;
	}
	else
	{
		//get a name for the new track
		GetString_Overlay overlay("Enter name for the new track","");
		if(!overlay.DoModal()) return;
		name = overlay.GetString() ? overlay.GetString() : "";
	}
	
	Tools_Mode_Cutscene::RefreshPlayback();

	float time = 0;
	unsigned uTrackFlags = 0;
	if(ThisActor->Tracks.size())
	{
		time = ThisActor->Tracks.last_entry()->m_xPointObjects.last_entry()->Time;

		//copy some flags from the previous track
		uTrackFlags = ThisActor->Tracks.last_entry()->m_pxTrack->m_uTrackFlags;
		uTrackFlags &= (TrackFlag_OrientationFromPoints|TrackFlag_RollFromPoints|TrackFlag_PitchAlongTrack);
	}
	else
	{
		uTrackFlags = ThisActor->Actor->GetDefaultTrackFlags();
	}

	
	
	//create the track
	Tools_Cutscene_Track* toolsTrack = ThisActor->CreateTrack(0);
	
	Asura_Cutscene_Track* track = toolsTrack->m_pxTrack;
	track->TrackName = name;
	track->m_uTrackFlags = uTrackFlags;

	//create the first track point
	Tools_CutsceneTrackPoint_Object* pointObject = toolsTrack->AddPoint(0,false);
	
	pointObject->Position = *Asura_Camera::GetPosition();
	pointObject->Orientation = *Asura_Camera::GetOrientation();
	pointObject->Time = time;
	pointObject->SnapToLevel();
	pointObject->UpdateBackupVariables();


	Tools_Timed_Message_System::AddMessage(0xffffff,"Track created");

	Tools_SaveAsura::SetCutscenesModified();
}

Tools_Cutscene_Track* Tools_CutsceneActor_Object::CreateTrack(int trackID)
{
	//create the new track
	Asura_Cutscene_Track* track = new Asura_Cutscene_Track();

	Asura_ResourcePushType(ASURA_RESOURCE_CUTSCENE);
	{
		//enlarge the array of tracks
		Actor->NumberOfTracks++;
		Asura_Cutscene_Track** NewTracks;
		Asura_ResourceAllocateArray(NewTracks, Actor->NumberOfTracks);
		for(int i=0;i<Actor->NumberOfTracks-1;i++)
		{
			NewTracks[i] = Actor->Tracks[i];
		}

		//add on the new track
		NewTracks[Actor->NumberOfTracks-1] = track;

		//replace the old array
		Asura_ResourceDeallocateArray(Actor->Tracks);
		Actor->Tracks = NewTracks;
	}
	Asura_ResourcePopType();

	//create the tools track
	Tools_Cutscene_Track* toolsTrack = CutsceneObject->AllocateToolsTrack( track, this, trackID );


	//log the creation of the track
	if(CutsceneUndo) CutsceneUndo->Log_Track_Created(toolsTrack);
	return toolsTrack;
}

void Tools_CutsceneActor_Object::SortTracks()
{
	//sort tracks by the time for the first point
	bool bLoopAgain = false;
	bool bOrderChanged = false;
	int iLoops = 0;
	//sort the array of tracks by time
	do
	{
		bLoopAgain = false;
		for(int i=0;i<Actor->NumberOfTracks-iLoops-1;i++)
		{
			if(Actor->Tracks[i]->GetStartTime() > Actor->Tracks[i+1]->GetStartTime())
			{
				Asura_Cutscene_Track* pxTemp = Actor->Tracks[i];
				Actor->Tracks[i] = Actor->Tracks[i+1];
				Actor->Tracks[i+1] = pxTemp;

				bLoopAgain = true;
				bOrderChanged = true;
			}
		}	
		iLoops++;
	}while(bLoopAgain);

	if(bOrderChanged)
	{
		//need to get the list to match the array
		//a pretty rubbish method of doing this really , but with some luck it shouldn't matter
		List<Tools_Cutscene_Track*>	xListCopy = Tracks;
		while(Tracks.size()) Tracks.delete_first_entry();

		for(int i=0;i<Actor->NumberOfTracks;i++)
		{
			for(LIF<Tools_Cutscene_Track*> xTrackLIF(&xListCopy);!xTrackLIF.done();xTrackLIF.next())
			{
				if(xTrackLIF()->m_pxTrack == Actor->Tracks[i])
				{
					Tracks.add_entry(xTrackLIF());
					xTrackLIF.delete_current();
					break;
				}
			}
		}
		Asura_Assert(!xListCopy.size(),"");
	}
}

void Tools_CutsceneActor_Object::CreateAttachmentForCurActorObj( void )
{
	Asura_Assert( ThisActor, "" );
    ThisActor->CreateAttachment();
}

Tools_Cutscene_Attachment* Tools_CutsceneActor_Object::CreateAttachment( void )
{
	//choose the type of attachment
	ListSelection_Overlay xOverlay("Attachment Type :");
	xOverlay.AddItem("Hierarchy",AsuraCutsceneAttachment_Hierarchy);
	xOverlay.AddItem("Shape",AsuraCutsceneAttachment_Shape);
	xOverlay.AddItem("Actor",AsuraCutsceneAttachment_Actor);
	xOverlay.AddItem("Particle/Sound/Light",AsuraCutsceneAttachment_Entity);
	xOverlay.AddItem("Corona",AsuraCutsceneAttachment_Corona);
	if(!CutsceneObject->IsServerCutscene())
	{
		xOverlay.AddItem("PFX",AsuraCutsceneAttachment_PFX);
		xOverlay.AddItem("Sound Event",AsuraCutsceneAttachment_SoundEvent);
	}

	if(!xOverlay.DoModal()) return NULL;
	
	return CreateAttachmentOfType( xOverlay.GetSelectionData() );
}

Tools_Cutscene_Attachment* Tools_CutsceneActor_Object::CreateAttachmentOfType( int iType )
{
	Asura_Assert(Actor->IsHierarchyActor(),"");
	Asura_Cutscene_Actor_Hierarchy* pxHierarchyActor = (Asura_Cutscene_Actor_Hierarchy*)Actor;

	Tools_SaveAsura::SetCutscenesModified();
	Tools_Mode_Cutscene::RefreshPlayback();//cutscene needs to be restarted after creating attachments

	///create the asura attachment
	Asura_Cutscene_Attachment* pxAttachment = Asura_Cutscene_Attachment::AllocateAttachment(pxHierarchyActor,iType);	
	Asura_Assert(pxAttachment,"");

	Asura_ResourcePushType(ASURA_RESOURCE_CUTSCENE);
	{
		//enlarge the array of attachments
		pxHierarchyActor->m_iNumberOfAttachments++;
		Asura_Cutscene_Attachment** ppxNewAttachments;
		Asura_ResourceAllocateArray(ppxNewAttachments, pxHierarchyActor->m_iNumberOfAttachments);
		for(int i=0;i<pxHierarchyActor->m_iNumberOfAttachments-1;i++)
		{
			ppxNewAttachments[i] = pxHierarchyActor->m_ppxAttachments[i];
		}

		//add on the new attachment
		ppxNewAttachments[pxHierarchyActor->m_iNumberOfAttachments-1] = pxAttachment;

		//replace the old array
		Asura_ResourceDeallocateArray(pxHierarchyActor->m_ppxAttachments);
		pxHierarchyActor->m_ppxAttachments = ppxNewAttachments;
	}
	Asura_ResourcePopType();

	//create the tools attachment
	Tools_Cutscene_Attachment* pxToolsAttachment =  Tools_Cutscene_Attachment::AllocateToolsAttachment(pxAttachment,this);

	SelectAttachment(m_xAttachmentList.size()-1);
	Tools_Timed_Message_System::AddMessage(0xffffff,"Attachment created");

	return pxToolsAttachment;
}

void Tools_CutsceneActor_Object::EditAttachment()
{
	Asura_Assert(ThisActor,"");
	if(!ThisActor->m_xAttachmentList.size())
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"This actor doesn't have any attachments yet.");
		return;
	}
	if(ThisActor->m_iSelectedAttachmentIndex<0) ThisActor->m_iSelectedAttachmentIndex = 0;
	ThisActor->SelectAttachment(ThisActor->m_iSelectedAttachmentIndex);

}

bool Tools_CutsceneActor_Object::IsEnabled_ChooseSkin(int iSkinIndex)
{
	if(iSkinIndex == 0) return true;
	//only allow secondary skins to be edited if a main skin has been selected , or if they
	//already have a skin(so as not to intefere with removing them)
	Asura_Assert(ThisActor,"");
	Asura_Assert(ThisActor->IsHierarchyActor(),"");
	Asura_Cutscene_Actor_Hierarchy* pxHierarchyActor = (Asura_Cutscene_Actor_Hierarchy*) ThisActor->Actor;

	return (pxHierarchyActor->m_aiSkinID[0] != -1) || (pxHierarchyActor->m_aiSkinID[iSkinIndex] != -1);
}

void Tools_CutsceneActor_Object::ChooseSkin(int iSkinIndex)
{
	Asura_Assert(ThisActor,"");



	Asura_String xSkinName;
	Asura_String xSkinFilename;
	if(!Tools_Animation_Files::ChooseSkin(&xSkinName,&xSkinFilename))
	{
		return;
	}

	

	Tools_Mode_Cutscene::RefreshPlayback();
	
	if(CutsceneUndo) CutsceneUndo->Log_Actor_Changed(ThisActor);
	
	//update the skin for the actor
	Asura_Assert(ThisActor->IsHierarchyActor(),"");
	Asura_Cutscene_Actor_Hierarchy* hierarchyActor = (Asura_Cutscene_Actor_Hierarchy*) ThisActor->Actor;

    Asura_Cutscene * cur_cutscene = Tools_Mode_Cutscene::CurrentCutscene->Cutscene;
	//get an entry in the name lists , if we don't already have one
	if(hierarchyActor->m_aiSkinID[iSkinIndex]==-1)
	{
		Asura_String temp = "";
		hierarchyActor->m_aiSkinID[iSkinIndex] = cur_cutscene->SkinNames.AddName(temp);

		#if defined ASURA_ASSERT
			int filenameIndex =
		#endif
			cur_cutscene->SkinFileNames.AddName(temp);

		Asura_Assert(filenameIndex == hierarchyActor->m_aiSkinID[iSkinIndex],"");
	}
    Asura_String & SkinName = cur_cutscene->SkinNames.GetName(hierarchyActor->m_aiSkinID[iSkinIndex]);
    Asura_String & SkinFileName = cur_cutscene->SkinFileNames.GetName(hierarchyActor->m_aiSkinID[iSkinIndex]);
    SkinName = xSkinName;
	SkinFileName = xSkinFilename;

	ThisActor->AnimDirectoryName = "";
	
	hierarchyActor->LoadFiles();

	//see if skin was loaded successfully
	//if(!Asura_Animation_System::GetSkin(hierarchyActor->SkinName))
    if(!Asura_Animation_System::GetSkin(SkinName.GetString()))
	{
		//Tools_Timed_Message_System::AddMessage(0xff0000,"Failed to get skin '%s'",hierarchyActor->SkinName.GetString());
        Tools_Timed_Message_System::AddMessage(0xff0000,"Failed to get skin '%s'",SkinName.GetString());
		Tools_Timed_Message_System::AddMessage(0xff0000,"Skin name doesn't match filename?");
	}
	Asura_Texture_Management::LoadTextures();

	//update the overlay
	ThisActor->UpdateOverlay();

	Tools_SaveAsura::SetCutscenesModified();

	//allow derived classes to do stuff as a resuly of the changed skin
	ThisActor->OnSkinChange();
		
}

void Tools_CutsceneActor_Object::ChooseAnim()
{
	Asura_Assert(ThisActor,"");
	Asura_Assert(ThisActor->IsHierarchyActor(),"");
	Asura_Cutscene_Actor_Hierarchy* hierarchyActor = (Asura_Cutscene_Actor_Hierarchy*) ThisActor->Actor;

	if(CutsceneUndo) CutsceneUndo->Log_Actor_Changed(ThisActor);
	//if(!ThisActor->ChooseAnim(hierarchyActor->InitialAnimationName,hierarchyActor->AnimationFileName)) return;
    if(!ThisActor->ChooseAnim(hierarchyActor->AnimID)) return;

	//update the overlay
	ThisActor->UpdateOverlay();
}


bool Tools_CutsceneActor_Object::ChooseAnim(int& animIndex)
{
	//Asura_Assert(IsHierarchyActor(),"");

	Asura_String szAnimName;
	Asura_String szFileName;
	if(!ChooseAnim("Select Anim",szAnimName,szFileName)) return false; 


	Tools_Mode_Cutscene::RefreshPlayback();

	Asura_Cutscene * cur_cutscene = Tools_Mode_Cutscene::CurrentCutscene->Cutscene;
	if(szAnimName.IsBlank())
	{
		if(animIndex!=-1)
		{
			cur_cutscene->AnimationNames.RemoveName(animIndex);
			cur_cutscene->AnimationFileNames.RemoveName(animIndex);
			animIndex = -1;
		}
	}
	else
	{
		if(animIndex==-1)
		{
			//add entries to the lists
			Asura_String temp="";
			animIndex = cur_cutscene->AnimationNames.AddName(temp);

			#if defined ASURA_ASSERT
				int filenameIndex =
			#endif
				cur_cutscene->AnimationFileNames.AddName(temp);

			Asura_Assert(animIndex == filenameIndex,"");
		}

		cur_cutscene->AnimationNames.GetName(animIndex) = szAnimName;
		cur_cutscene->AnimationFileNames.GetName(animIndex) = szFileName;

		CutsceneObject->Cutscene->LoadFiles();

		//see if anim was loaded successfully
		if(!Asura_Animation_System::GetAnim(szAnimName.GetString()))
		{
			Tools_Timed_Message_System::AddMessage(0xff0000,"Failed to get anim '%s'",szAnimName.GetString());
			Tools_Timed_Message_System::AddMessage(0xff0000,"Anim name doesn't match filename?");
		}
	}

	Tools_SaveAsura::SetCutscenesModified();

	return true;
}

bool Tools_CutsceneActor_Object::ChooseAnim(const char* message,Asura_String& szAnimName,Asura_String& szFileName)
{
		
	const char* pszSkinName = 0;
	if(IsHierarchyActor())
	{
		Asura_Cutscene_Actor_Hierarchy* hierarchyActor = (Asura_Cutscene_Actor_Hierarchy*) Actor;	
		if(hierarchyActor->m_aiSkinID[0]!=-1)
		{
			pszSkinName = Tools_Mode_Cutscene::CurrentCutscene->Cutscene->SkinNames.GetName(hierarchyActor->m_aiSkinID[0]).GetString();
		}
	}
		   
	bool bCancelled = false;
	Tools_Animation_Files::ChooseAnimation(pszSkinName,&AnimDirectoryName,&szAnimName,&szFileName,0,true,&bCancelled);
	return !bCancelled;
}

Asura_Hierarchy_Anim* Tools_CutsceneActor_Object::ChooseAnim(const char* message)
{
	Asura_Assert(IsHierarchyActor(),"");
	
	Asura_String szAnimName;
	Asura_String szFileName;
	if(!ChooseAnim(message,szAnimName,szFileName)) return false; 

	Asura_Hierarchy_Anim* pxAnim = Asura_Animation_System::GetAnim(szAnimName.GetString());
	if(!pxAnim)
	{
		//try loading the animation
		LoadAsuraFile(szFileName.GetString());
		pxAnim = Asura_Animation_System::GetAnim(szAnimName.GetString());
		if(pxAnim)
		{
			Tools_Timed_Message_System::AddMessage(0xff0000,"Failed to get anim '%s'",szAnimName.GetString());
			Tools_Timed_Message_System::AddMessage(0xff0000,"Anim name doesn't match filename?");
		}
	}
	return pxAnim;
}



void Tools_CutsceneActor_Object::ChooseShape()
{
	Asura_Assert(ThisActor,"");
	Asura_Assert(ThisActor->IsShapeActor(),"");
	Asura_Cutscene_Actor_Shape* pxShapeActor = (Asura_Cutscene_Actor_Shape*) ThisActor->Actor;


	//ask for the new shape
	Asura_Hash_ID uShape = Tools_ShapeTable::SelectShape();
	if(uShape == ASURA_HASH_ID_UNSET || uShape == pxShapeActor->m_uShapeID) return;

	if(CutsceneUndo) CutsceneUndo->Log_Actor_Changed(ThisActor);
	pxShapeActor->m_uShapeID = uShape;

	//update the overlay
	ThisActor->UpdateOverlay();
}

void Tools_CutsceneActor_Object::ChooseCameraTarget()
{
	Asura_Assert(ThisActor,"");
	Asura_Assert(ThisActor->IsCameraActor(),"");

	Asura_Cutscene_Actor_Camera* pxCameraActor = (Asura_Cutscene_Actor_Camera*) ThisActor->Actor;

	//build a list of valid targets.. limit to camera tarets for the mo
	ListSelection_Overlay xOverlay("Select Target : ");
	for(LIF<Tools_CutsceneActor_Object*> alif(&ThisActor->CutsceneObject->ActorObjects);!alif.done();alif.next())
	{
		if(!alif()->IsCameraTargetActor()) continue;
		const char* pszName = alif()->Actor->ActorName.GetString();
		bool bSelected = false;
		if(pxCameraActor->GetInitialTargetName())
		{
			bSelected = !strcmp(pszName,pxCameraActor->GetInitialTargetName());
		}

		xOverlay.AddItem(pszName,(int)alif()->Actor,bSelected);
	}

	//add no target option
	xOverlay.AddItem("*None*",0,!pxCameraActor->GetInitialTargetName());

	//show the list overlay
	if(!xOverlay.DoModal()) return;

	if(CutsceneUndo) CutsceneUndo->Log_Actor_Changed(ThisActor);
	if(xOverlay.GetSelectionData())
	{
		pxCameraActor->SetInitialTargetName(xOverlay.GetSelectionString());
	}
	else
	{
		pxCameraActor->SetInitialTargetName(0);
	}
	
	Tools_SaveAsura::SetCutscenesModified();

	//update the overlay
	ThisActor->UpdateOverlay();

}

void Tools_CutsceneActor_Object::ChooseEntityToReplace()
{
	Asura_Assert(ThisActor,"");

	//Find current object
	Tools_Object* pxObject = Tools_Object_System::GetObjectFromGuid(ThisActor->Actor->m_uGuidToReplace);
	
	//start the object selection process
	ThisActor->m_pxChooseObject = new Tools_ChooseEntityToReplace(ThisActor);
	Tools_Object_System::ChooseObject("Select entity that this actor replaces",ThisActor->m_pxChooseObject,pxObject);
}

void Tools_CutsceneActor_Object::SetEntityToReplace(Asura_Guid uGuid)
{
	//make sure the previous object doesn't stay hidden
	if(Actor->m_uGuidToReplace != ASURA_GUID_UNREGISTERED)
	{
		Tools_Object* pxObject = Tools_Object_System::GetObjectFromGuid(Actor->m_uGuidToReplace);
		if(pxObject) pxObject->SetHiddenFlag(ObjectHideFlag_ReplacedByCutscene,false);
	}

	Actor->m_uGuidToReplace = uGuid;
		
	if(CutsceneUndo) CutsceneUndo->Log_Actor_Changed(ThisActor);
	Tools_SaveAsura::SetCutscenesModified();
	//update the overlay
	if(ThisActor)
	{
		ThisActor->UpdateOverlay();
	}
}

void Tools_CutsceneActor_Object::SetInitialCamera()
{
	Asura_Assert(IsCameraActor(),"");

	Asura_Cutscene* pxCutscene = Actor->Cutscene;
	Asura_Cutscene_Actor* pxOtherCamera = pxCutscene->GetCameraByIndex(0);
	if(pxOtherCamera == Actor) return;

	//swap positions with the previous initial camera
	int iThisIndex = -1;
	int iOtherIndex = -1;

	for(int i=0;i<pxCutscene->NumberOfActors;i++)
	{
		if(pxCutscene->Actors[i] == Actor) iThisIndex = i;
		if(pxCutscene->Actors[i] == pxOtherCamera) iOtherIndex = i;
	}
	Asura_Assert(iThisIndex!=-1 && iOtherIndex!=-1,"");

	pxCutscene->Actors[iThisIndex] = pxOtherCamera;
	pxCutscene->Actors[iOtherIndex] = Actor;

	
	Tools_SaveAsura::SetCutscenesModified();

	//rebuild the overlay
	DeleteOverlay();
	CreateOverlay();

}


void Tools_CutsceneActor_Object::ChooseTexture()
{
	if(!IsHierarchyActor()) return;

	Asura_Cutscene_Actor_Hierarchy* pxHierarchyActor = (Asura_Cutscene_Actor_Hierarchy*) Actor;

	//ask for new texture
	int iOldTextureID = Asura_Texture_Management::GetIndexFromHashID(pxHierarchyActor->m_uTextureHash);
	int iNewTextureID = Tools_PickTexture::PickTexture("Select Replacement Texture",iOldTextureID,PICKTEXTURE_ALLTEXTURES);
	if(iNewTextureID == ASURA_TEXTURE_NONE) return;
	if(iOldTextureID == iNewTextureID) return;

	pxHierarchyActor->m_uTextureHash = Asura_Texture_Management::GetHashIDFromIndex(iNewTextureID);

	//log for undo
	Tools_Mode_Cutscene::RefreshPlayback();
	if(CutsceneUndo) CutsceneUndo->Log_Actor_Changed(ThisActor);

	UpdateOverlay();
	Tools_SaveAsura::SetCutscenesModified();
}

void Tools_CutsceneActor_Object::ClearTexture()
{
	if(!IsHierarchyActor()) return;

	Asura_Cutscene_Actor_Hierarchy* pxHierarchyActor = (Asura_Cutscene_Actor_Hierarchy*) Actor;
	if(pxHierarchyActor->m_uTextureHash == ASURA_HASH_ID_UNSET) return;

	pxHierarchyActor->m_uTextureHash = ASURA_HASH_ID_UNSET;
	
	//log for undo
	Tools_Mode_Cutscene::RefreshPlayback();
	if(CutsceneUndo) CutsceneUndo->Log_Actor_Changed(ThisActor);

	UpdateOverlay();
	Tools_SaveAsura::SetCutscenesModified();
}


void Tools_CutsceneActor_Object::_Rename()
{
	Asura_Assert(ThisActor,"");
	ThisActor->Rename();

}

void Tools_CutsceneActor_Object::Rename(const char* name /*= NULL*/)
{
	Asura_String actorName;
	if(!name)
	{
		GetString_Overlay overlay("Rename actor :",Actor->ActorName.GetString());
		if(!overlay.DoModal()) return;
		actorName = overlay.GetString();
	}
	else
	{
		actorName = name;
	}

	//needs to have some length to it
	if(!actorName.GetLength()) return;
	//ignore if the name is unchanges
	if(!actorName.Compare(Actor->ActorName)) return;

	//need to check for this name being used elsewhere
	if(CutsceneObject->GetActor(actorName.GetString()))
	{
		Tools_Timed_Message_System::AddMessage(0xff0000,"That name is already used by another actor");
		return;
	}

	if(CutsceneUndo) CutsceneUndo->Log_Actor_Rename(this,actorName);
	
	//go ahead and change the name
	Actor->ActorName = actorName;

	//tell the cutscene about it , so it can correct the events
	CutsceneObject->HandleActorNameChange(Actor);

	if(ThisActor==this)
	{
		//update the overlay
		DeleteOverlay();
		CreateOverlay();
	}

	Tools_SaveAsura::SetCutscenesModified();

	if(Tracks.size() && !name)
	{
		//ask if the track should be renamed as well
		YesNo_Overlay overlay("Would you like to give the track the same name?");
		if(overlay.DoModal())
		{
			Tracks.first_entry()->Rename(actorName.GetString());
		}
	}
}

void Tools_CutsceneActor_Object::_EditEvents()
{
	Asura_Assert(ThisActor,"");
	ThisActor->CutsceneObject->EditFirstEventForActor(ThisActor);
}

void Tools_CutsceneActor_Object::EditInitialAnim()
{
	Asura_Assert(ThisActor,"");
	if(!ThisActor->IsHierarchyActor()) return;
	//check for an existing change animation event
	if(ThisActor->CutsceneObject->EditFirstEventForActor(ThisActor, ACET_AnimationChange))
	{
		return;		
	}
	if(ShowYesNoOverlay("This actor has no animation change events yet.\nCreate one now?"))
	{
		//create a new event for the actor
		ThisActor->CutsceneObject->CreateCutsceneEvent(true,ACET_AnimationChange, -1, NULL, ThisActor->Actor, 0.0f);
	}
	
}

void Tools_CutsceneActor_Object::RecordAnimation()
{
	Asura_Assert(ThisActor,"");
	if(!ThisActor->IsHierarchyActor()) return;

	Tools_Cutscene_RecordAnimation::StartRecording(Tools_Mode_Cutscene::GetCurrentCutscene(), ThisActor->Actor);

}


void Tools_CutsceneActor_Object::RemoveTrackFromActor(Asura_Cutscene_Track* track)
{
	//find the track in the array
	int i;
	for(i=0;i<Actor->NumberOfTracks;i++)
	{
		if(Actor->Tracks[i] == track)
		{
			break;
		}
	}
	Asura_Assert(i<Actor->NumberOfTracks,"");

	//shuffle remaining down
	for(;i<Actor->NumberOfTracks-1;i++)
	{
		Actor->Tracks[i] = Actor->Tracks[i+1];
	}

	//and reduce the number of tracks
	//(won't bother resizing the array... it wil get sorted out when the actor is deleted)
	Actor->NumberOfTracks--;

	Tools_SaveAsura::SetCutscenesModified();
}


void Tools_CutsceneActor_Object::NotifyToolsTrackDeleted(Tools_Cutscene_Track* pxToolsTrack)
{
    Tracks.delete_entry(pxToolsTrack);
}

void Tools_CutsceneActor_Object::RemoveAttachmentFromActor(Asura_Cutscene_Attachment* pxAttachment)
{
	Asura_Assert(Actor->IsHierarchyActor(),"");
	Asura_Cutscene_Actor_Hierarchy* pxHierarchyActor = (Asura_Cutscene_Actor_Hierarchy*)Actor;



	//find the attachment in the array
	int i;
	for(i=0;i<pxHierarchyActor->m_iNumberOfAttachments;i++)
	{
		if(pxHierarchyActor->m_ppxAttachments[i] == pxAttachment)
		{
			break;
		}
	}
	Asura_Assert(i<pxHierarchyActor->m_iNumberOfAttachments,"");

	//shuffle remaining down
	for(;i<pxHierarchyActor->m_iNumberOfAttachments-1;i++)
	{
		pxHierarchyActor->m_ppxAttachments[i] = pxHierarchyActor->m_ppxAttachments[i+1];
	}

	//and reduce the number of attachments
	//(won't bother resizing the array... it wil get sorted out when the actor is deleted)
	pxHierarchyActor->m_iNumberOfAttachments--;

	Tools_SaveAsura::SetCutscenesModified();
}


void Tools_CutsceneActor_Object::UpdateTracks()
{
	for(LIF<Tools_Cutscene_Track*> tlif(&Tracks);!tlif.done();tlif.next())
	{
		tlif()->UpdateAsuraTrack();
	}
	SortTracks();
}

void Tools_CutsceneActor_Object::GetPositionAtTime(float time,Asura_Vector_3* outputPosition)
{
	UpdateTracks();

	if(!Tracks.size())
	{
		*outputPosition = Position;
		return;
	}

	//find the track for the time
	Tools_Cutscene_Track* track = Tracks.first_entry();

	for(LIF<Tools_Cutscene_Track*> tlif(&Tracks);!tlif.done();tlif.next())
	{
		if(time>tlif()->m_xPointObjects.first_entry()->Time)
		{
			track = tlif();
		}
	}

	track->m_pxTrack->GetPositionFromTime(time,outputPosition);

}

void Tools_CutsceneActor_Object::AddContextMenuItems(class Tools_ContextMenu& xMenu, const class Tools_ContextMenu_Context& xContext)
{
	Tools_Object::AddContextMenuItems(xMenu, xContext);

	Tools_ContextMenu_Item* pxItem;

	Tools_ContextMenu_Section* pxSection = xMenu.GetSection(TOOLS_CONTEXTMENU_SECTION_SELECTION);
	pxSection->AddSeparator();

	//Add option for selecting/deselecting this actor
	u_int uMode;
	const char* szText;
	if(xContext.m_bShiftHeld)
	{
		szText = "Deselect actor";
		uMode = 0;
	}
	else if(xContext.m_bControlHeld)
	{
		szText = "Select actor";
		uMode = 1;
	}
	else
	{
		szText = "Select actor";
		uMode = 2;
	}

	pxItem = new Tools_ContextMenu_Item_Function(szText, ContextMenu_SelectActor, this, uMode);
	pxItem->SetID("Select actor");
	pxItem->SetHelpText("Selects/deselects the actor and all of its tracks");
	pxSection->AddItem(pxItem);
}


void Tools_CutsceneActor_Object::ListObjectsForActor(List<Tools_Object*>& xObjectList)
{
	//and all the points in the tracks;
	for(LIF<Tools_Cutscene_Track*> tlif(&Tracks);!tlif.done();tlif.next())
	{
		if(tlif()->m_xPointObjects.size())
		{
			tlif()->m_xPointObjects.first_entry()->ListObjectsForTrack(xObjectList);
		}
	}
	//add the actor object itself
	xObjectList.add_entry(this);
}

void Tools_CutsceneActor_Object::ContextMenu_SelectActor(void* pObject, u_int uMode)
{
	Tools_Object* pxObject = (Tools_Object*) pObject;
	
	Asura_Assert(pxObject && pxObject->Type==TOT_CutsceneActor,"");
	Tools_CutsceneActor_Object* pxActorObject = (Tools_CutsceneActor_Object*) pxObject;

	List<Tools_Object*> xSelectionList;
	
	//get all objects used by this actors
	pxActorObject->ListObjectsForActor(xSelectionList);

	//And (de)select them
	if(uMode == 0)
	{
		Tools_Object_System::DeselectObjects(xSelectionList);
	}
	else
	{
		Tools_Object_System::SelectObjects(xSelectionList, uMode == 1);
	}
}



bool Tools_CutsceneActor_Object::IsHierarchyActor()
{
	return Actor->Type==ACAT_Hierarchy;
}

bool Tools_CutsceneActor_Object::IsShapeActor()
{
	return Actor->Type==ACAT_Shape;
}

bool Tools_CutsceneActor_Object::IsCameraActor()
{
	return Actor->IsCamera();
}

bool Tools_CutsceneActor_Object::IsCameraTargetActor()
{
	return Actor->IsCameraTarget();
}

void Tools_CutsceneActor_Object::UpdateCutsceneEdit()
{
	if(m_bChanged)
	{
		Tools_SaveAsura::SetCutscenesModified();
		Tools_Mode_Cutscene::RefreshPlayback();
		m_bChanged = false;
		if(CutsceneUndo) CutsceneUndo->Log_Actor_Changed(ThisActor);

		if(IsCameraActor())
		{
			Asura_Cutscene_Actor_Camera* pxCameraActor = (Asura_Cutscene_Actor_Camera*) Actor;
			pxCameraActor->m_xInitialShake.SetEnabled(m_bHandheldCameraEnabled);
		}
	}

	if(m_bSelectedAttachmentIndexChanged)
	{
		SelectAttachment(m_iSelectedAttachmentIndex);
		m_bSelectedAttachmentIndexChanged = false;
	}

	if(m_pxSelectedAttachment)
	{
		m_pxSelectedAttachment->UpdateCutsceneEdit();	
	}
}

void Tools_CutsceneActor_Object::SelectAttachment(int iIndex)
{
	Asura_Assert(iIndex>=-1 && iIndex<m_xAttachmentList.size(),"");

	//deselect current one
	if(m_pxSelectedAttachment)
	{
		m_pxSelectedAttachment->OnDeselection();
		m_pxSelectedAttachment = 0;
		m_iSelectedAttachmentIndex = -1;
	}

	//and select the attachment
	if(iIndex!=-1)
	{
		m_pxSelectedAttachment = m_xAttachmentList[iIndex];
		m_iSelectedAttachmentIndex = iIndex;
		m_pxSelectedAttachment->OnSelection();
	}
}

void Tools_CutsceneActor_Object::UpdateAttachmentSelection()
{
	if(!m_pxSelectedAttachment) return;

	//get the new index for the currently selected attachment
	for(int i=0;i<m_xAttachmentList.size();i++)
	{
		if(m_xAttachmentList[i] == m_pxSelectedAttachment)
		{
			m_iSelectedAttachmentIndex = i;
			return;
		}
	}
	
	//previously selected attachment no longer exists
	m_pxSelectedAttachment = 0;

	//select a new one
	if(m_iSelectedAttachmentIndex>=m_xAttachmentList.size())
	{
		m_iSelectedAttachmentIndex = m_xAttachmentList.size()-1;
	}
	SelectAttachment(m_iSelectedAttachmentIndex);
}

void Tools_CutsceneActor_Object::CopyTracks()
{
	Asura_Assert(ThisActor,"");

	//First need to select the cutscene to copy from
	//Get a list of all cutscenes
	ListSelection_Overlay xCutsceneListOverlay("Select cutscene to copy track from");
	xCutsceneListOverlay.AddItem("*This Cutscene*",(int)ThisActor->CutsceneObject->Cutscene);
	
	for(int i=0;i<Asura_Cutscene_System::GetNumberOfCutscenes();i++)
	{
		Asura_Cutscene* pxCutscene = Asura_Cutscene_System::GetCutsceneByIndex(i);
		if(pxCutscene == ThisActor->CutsceneObject->Cutscene) continue;

		xCutsceneListOverlay.AddItem(pxCutscene->CutsceneName.GetString(),(int)pxCutscene);
	}

	if(!xCutsceneListOverlay.DoModal()) return;

	Asura_Cutscene* pxCutscene = (Asura_Cutscene*) xCutsceneListOverlay.GetSelectionData();
	if(!pxCutscene->NumberOfActors)
	{
		Tools_Timed_Message_System::AddMessage(0xff0000,"%s doesn't contain any actors",pxCutscene->CutsceneName.GetString());
		return;
	}
	
	

	//Next select the actor from the cutscene
	ListSelection_Overlay xActorListOverlay("Select actor to copy");
	for(int i=0;i<pxCutscene->NumberOfActors;i++)
	{
		if(pxCutscene->Actors[i] == ThisActor->Actor) continue;
		xActorListOverlay.AddItem(pxCutscene->Actors[i]->ActorName.GetString(),(int)pxCutscene->Actors[i]);
	}

	if(!xActorListOverlay.DoModal()) return;

	Asura_Cutscene_Actor* pxActorToCopy =  (Asura_Cutscene_Actor*) xActorListOverlay.GetSelectionData();
	
	//go ahead and do the copying
	ThisActor->CopyTracks(pxActorToCopy);
}

void Tools_CutsceneActor_Object::CopyTracks(Asura_Cutscene_Actor* pxAsuraActor)
{
	Asura_Assert(pxAsuraActor,"");

	Tools_Mode_Cutscene::RefreshPlayback();

	//kill the existing tracks
	while(Tracks.size())
	{
		Tracks.first_entry()->Destroy();
	}

	Asura_ResourcePushType(ASURA_RESOURCE_CUTSCENE);
	{
		Asura_ResourceDeallocateArray(Actor->Tracks);
		Actor->Tracks = 0;
		Actor->NumberOfTracks = 0;
	}
	Asura_ResourcePopType();

	//copy the asura tracks
	Actor->CopyTracks(pxAsuraActor);

	//give the copied tracks default names based on the name of the actor
	for(int i=0;i<Actor->NumberOfTracks;i++)
	{
		Asura_Cutscene_Track* pxTrack = Actor->Tracks[i];
		if(i)
		{
			char szName[200];
			sprintf(szName,"%s %d",Actor->ActorName.GetString(), i);
			pxTrack->TrackName = szName;
		}
		else
		{
			pxTrack->TrackName = Actor->ActorName;
		}
	}

	//create all the new tools tracks
	for(int i=0;i<Actor->NumberOfTracks;i++)
	{
		Tools_Cutscene_Track* pxTrack = CutsceneObject->AllocateToolsTrack( Actor->Tracks[i], this, i );

		//log track and point creation for undo
		if(CutsceneUndo)
		{
			CutsceneUndo->Log_Track_Created(pxTrack);
			for(LIF<Tools_CutsceneTrackPoint_Object*> xPointLIF(&pxTrack->m_xPointObjects);!xPointLIF.done();xPointLIF.next())
			{
				CutsceneUndo->Log_TrackPoint_Created(xPointLIF());
			}
		}
		
	}
}

void Tools_CutsceneActor_Object::UpdateSoundPosition()
{
	Actor->GetSoundPosition(m_xSoundPosition);	
}

const char* Tools_CutsceneActor_Object::GetNameOfChangedVariable()
{
	if(Actor->m_uGuidToReplace != m_uGuidToReplaceBackup)
	{
		return "Entity to Replace";
	}

	if(IsHierarchyActor())
	{
		//get hierarchy actor specific data
		const Asura_Cutscene_Actor_Hierarchy* pxHierarchyActor = (const Asura_Cutscene_Actor_Hierarchy*)Actor;

		if(pxHierarchyActor->m_iAnimationFlags != m_iAnimationFlagsBackup)
		{
			int iChangedFlags = pxHierarchyActor->m_iAnimationFlags ^ m_iAnimationFlagsBackup;
			
			if(iChangedFlags & AsuraAnimationFlag_Loop) return "Looped";
			if(iChangedFlags & AsuraAnimationFlag_Reversed) return "Reversed";
			if(iChangedFlags & AnimationChangeFlag_IgnoreMovement) return "Ignore Movement";
			return "Animation Flags";
		}

		if(pxHierarchyActor->m_uHierarchyFlags != m_uHierarchyFlagsBackup)
		{
			u_int uChangedFlags = pxHierarchyActor->m_uHierarchyFlags ^ m_uHierarchyFlagsBackup;
			if(uChangedFlags & ASURA_HIERARCHYACTORFLAG_NOSHADOW) return "No Shadow";
			if(uChangedFlags & ASURA_HIERARCHYACTORFLAG_INITIALLY_HIDDEN) return "Not Rendered";
			return "Flags";
		}

		if(pxHierarchyActor->m_fAnimationDelay != m_fAnimationDelayBackup)
		{
			return "Animation Delay";
		}
		if(pxHierarchyActor->m_uTextureHash != m_uTextureHashBackup)
		{
			return "Texture";
		}
		if(pxHierarchyActor->m_uIKTemplateID != m_uIKTemplateIDBackup)
		{
			return "IK";
		}
		#ifdef ASURA_USE_FACIAL_ANIMATION_SYSTEM
		if(pxHierarchyActor->m_uFaceID != m_uFaceIDBackup)
		{
			return "Face";
		}
		#endif


	}

	if(IsShapeActor())
	{
		const Asura_Cutscene_Actor_Shape* pxShapeActor = (const Asura_Cutscene_Actor_Shape*)Actor;
		
		if(pxShapeActor->m_uShapeFlags != m_uShapeFlagsBackup)
		{
			u_int uChangedFlags = pxShapeActor->m_uShapeFlags ^ m_uShapeFlagsBackup;
			if(uChangedFlags & ASURA_HIERARCHYACTORFLAG_NOSHADOW) return "No Shadow";
			return "Flags";
		}
	}
	if(IsCameraActor())
	{
		const Asura_Cutscene_Actor_Camera* pxCameraActor = (const Asura_Cutscene_Actor_Camera*)Actor;

		if(pxCameraActor->GetInitialFOV() != m_fInitialFOVBackup) return "FOV";
		if(pxCameraActor->m_fNearClip != m_fNearClipBackup) return "Near Clip";
		if(pxCameraActor->m_fFarClip != m_fFarClipBackup) return "Far Clip";
		const Asura_Cutscene_Camera_Shake& xShakeParams = pxCameraActor->GetInitialShake();
		if(m_xCameraShakeBackup.IsEnabled() != xShakeParams.IsEnabled()) return "Handheld Camera";
		if(m_xCameraShakeBackup.GetFrequency() != xShakeParams.GetFrequency()) return "Handheld Frequency";
		if(m_xCameraShakeBackup.GetPitch() != xShakeParams.GetPitch()) return "Handheld Pitch";
		if(m_xCameraShakeBackup.GetYaw() != xShakeParams.GetYaw()) return "Handheld Yaw";
		if(m_xCameraShakeBackup.GetRoll() != xShakeParams.GetRoll()) return "Handheld Roll";
		if(m_xCameraShakeBackup.GetSpeedScale() != xShakeParams.GetSpeedScale()) return "Handheld Speed Scale";
		if(m_xCameraShakeBackup.GetMinScale() != xShakeParams.GetMinScale()) return "Handheld Min Jitter";
	}

	if(Actor->m_uActorFlags != m_uActorFlagsBackup)
	{
		u_int uChangedFlags = Actor->m_uActorFlags ^ m_uActorFlagsBackup;
		if(uChangedFlags & ASURA_ACTORFLAG_EXTERNALTRACKCONTROL) return "External Control";
		return "Flags";
	}

	return 0;
}

bool Tools_CutsceneActor_Object::CameraAllowsPlayerControl()
{
	if(IsCameraActor())
	{
		const Asura_Cutscene_Actor_Camera* pxCameraActor = (const Asura_Cutscene_Actor_Camera*)Actor;
		return (pxCameraActor->m_uCameraFlags & ASURA_CAMERAACTORFLAG_ALLOWPLAYERCONTROL) != 0;
	}
	return false;
}

bool Tools_CutsceneActor_Object::CameraUsesNearClipPlane()
{
	if(IsCameraActor())
	{
		const Asura_Cutscene_Actor_Camera* pxCameraActor = (const Asura_Cutscene_Actor_Camera*)Actor;
		return (pxCameraActor->m_uCameraFlags & ASURA_CAMERAACTORFLAG_USENEARCLIP) != 0;
	}
	return false;
}

bool Tools_CutsceneActor_Object::CameraUsesFarClipPlane()
{
	if(IsCameraActor())
	{
		const Asura_Cutscene_Actor_Camera* pxCameraActor = (const Asura_Cutscene_Actor_Camera*)Actor;
		return (pxCameraActor->m_uCameraFlags & ASURA_CAMERAACTORFLAG_USEFARCLIP) != 0;
	}
	return false;
}



////////////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------------**
** Tools_ChooseEntityToReplace **
**-----------------------------*/
bool Tools_ChooseEntityToReplace::CanBeSelected(Tools_Object* pxObject)
{
	Asura_Assert(pxObject, "Null tools object");
	if (!pxObject) return false;

	//allow object if its derived physical object , or if it's the player
	const Asura_Guid uGuid = pxObject->GetGuid();
	if(uGuid == ASURA_GUID_UNREGISTERED) return false;

	// Add all the special guids except for a few that obviously make no sense.
	if (pxObject && pxObject->Type == TOT_SpecialGuid)
	{
		if (uGuid == ASURA_GUID_SERVER ||
			uGuid == ASURA_GUID_ALL_CLIENTMACHINES ||
			uGuid == ASURA_GUID_DIALOGUE_SPEAKER)
		{
			return false;
		}
		return true;
	}

	if(Asura_ServerEntity_PhysicalObject::FindEntity(uGuid))
	{
		return true;
	}

	if(pxObject->CanBeReplacedByCutsceneActor())
	{
		return true;
	}

	return false;
}

void Tools_ChooseEntityToReplace::ObjectSelected(Tools_Object* pxObject)
{
	if(Tools_SecondaryEntityFile_System::IsAllowableTarget(0,true,pxObject))
	{
		m_pxActorObject->SetEntityToReplace(pxObject ? pxObject->GetGUIDToReplaceByCutsceneActor() : ASURA_GUID_UNREGISTERED);
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------**
** Cutscene Undo **
**---------------*/
/////////Actor_Created
void CutsceneModeUndo::Log_Actor_Created(Tools_CutsceneActor_Object* actorObject)
{
	RootUndoData** undo = AllocateNextUndoData();
	if(!undo) return;
	
	*undo = CreateUndo_Actor_Created(actorObject);
}

RootUndoData* CutsceneModeUndo::CreateUndo_Actor_Created(Tools_CutsceneActor_Object* actorObject)
{
	return new CutsceneUndoData_Actor_Created(actorObject);
}

CutsceneUndoData_Actor_Created::CutsceneUndoData_Actor_Created(Tools_CutsceneActor_Object* actorObject)
{
	ActorName = actorObject->GetName();

	SetDescription("Actor Created '%s'",actorObject->GetName());
}

RootUndoData* CutsceneUndoData_Actor_Created::ProcessUndoData()
{
	//find the actor object for this
	Tools_CutsceneActor_Object* actorObject = GetActorObject(ActorName);
	Asura_Assert(actorObject,"");
	Asura_Assert(!actorObject->Tracks.size(),"");//all the tracks should be killed off by other undo actions

	//create the reverse undo data
	RootUndoData* undo = CutsceneUndo->CreateUndo_Actor_Destroyed(actorObject);
	undo->SetDescription("%s",GetDescription());

	//do the undo deeds
	actorObject->Destroy();
	
	return undo;
}

int CutsceneUndoData_Actor_Created::GetPriority() const
{
	return CutsceneModeUndo::PRIORITY_ACTOR_CREATED;
}


/////////Actor_Destroyed
void CutsceneModeUndo::Log_Actor_Destroyed(Tools_CutsceneActor_Object* actorObject)
{
	RootUndoData** undo = AllocateNextUndoData();
	if(!undo) return;
	
	*undo = CreateUndo_Actor_Destroyed(actorObject);
}

RootUndoData* CutsceneModeUndo::CreateUndo_Actor_Destroyed(Tools_CutsceneActor_Object* actorObject)
{
	return new CutsceneUndoData_Actor_Destroyed(actorObject);
}

CutsceneUndoData_Actor_Destroyed::CutsceneUndoData_Actor_Destroyed(Tools_CutsceneActor_Object* actorObject)
{
	ActorName = actorObject->GetName();
	Type = actorObject->Actor->Type;
	Position = actorObject->Position;
	Orientation = actorObject->Orientation;
	m_uGuid = actorObject->Actor->m_uGuid;
	m_uActorFlags = actorObject->Actor->m_uActorFlags;

	if(actorObject->IsHierarchyActor())
	{
		//get hierarchy actor specific data
		Asura_Cutscene_Actor_Hierarchy* hierarchyActor = (Asura_Cutscene_Actor_Hierarchy*)actorObject->Actor;
        Asura_Cutscene * cur_cutscene = Tools_Mode_Cutscene::CurrentCutscene->Cutscene;
		for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
		{
			if(hierarchyActor->m_aiSkinID[i]!=-1)
			{
	   			m_axSkinName[i] = cur_cutscene->SkinNames.GetName(hierarchyActor->m_aiSkinID[i]);
				m_axSkinFileName[i] = cur_cutscene->SkinFileNames.GetName(hierarchyActor->m_aiSkinID[i]);
			}
		}
		if(hierarchyActor->AnimID!=-1)
		{
			InitialAnimationName = cur_cutscene->AnimationNames.GetName(hierarchyActor->AnimID);
			AnimationFileName = cur_cutscene->AnimationFileNames.GetName(hierarchyActor->AnimID);
		}

		m_iAnimationFlags = hierarchyActor->m_iAnimationFlags;
		m_fAnimationDelay = hierarchyActor->m_fAnimationDelay;
		m_uHierarchyFlags = hierarchyActor->m_uHierarchyFlags;
		m_uTextureHash = hierarchyActor->m_uTextureHash;
		m_uIKTemplateID = hierarchyActor->m_uIKTemplateID;
		#ifdef ASURA_USE_FACIAL_ANIMATION_SYSTEM
		m_uFaceID = hierarchyActor->m_uFaceID;
		#endif

	}
	if(actorObject->IsShapeActor())
	{
		Asura_Cutscene_Actor_Shape* pxShapeActor = (Asura_Cutscene_Actor_Shape*)actorObject->Actor;
		m_uShapeID = pxShapeActor->m_uShapeID;
		m_uShapeFlags = pxShapeActor->m_uShapeFlags;
	}
	if(actorObject->IsCameraActor())
	{
		Asura_Cutscene_Actor_Camera* pxCameraActor = (Asura_Cutscene_Actor_Camera*)actorObject->Actor;
		m_xTargetName = pxCameraActor->GetInitialTargetName();

		m_uCameraFlags = pxCameraActor->GetCameraFlags();
		m_fInitialFOV = pxCameraActor->GetInitialFOV();
		m_fNearClip = pxCameraActor->GetInitialNearClip();
		m_fFarClip = pxCameraActor->GetInitialFarClip();
		m_xCameraShake = pxCameraActor->GetInitialShake();
	}

	SetDescription("Actor Destroyed '%s'",ActorName.GetString());
}

RootUndoData* CutsceneUndoData_Actor_Destroyed::ProcessUndoData()
{
	Asura_Assert(Tools_Mode_Cutscene::CurrentCutscene,"");

	//create the new actor
	Tools_CutsceneActor_Object* actorObject = Tools_Mode_Cutscene::CurrentCutscene->CreateActor(Type,ActorName.GetString(),m_uGuid,false);
	Asura_Assert(actorObject,"");

	//fill in the details from the undo data
	CopyToActor(actorObject);

	//return the reverse undo data
	RootUndoData* pxUndo = CutsceneUndo->CreateUndo_Actor_Created(actorObject);
	pxUndo->SetDescription("%s",GetDescription());
	return pxUndo;
}

void CutsceneUndoData_Actor_Destroyed::CopyToActor(Tools_CutsceneActor_Object* actorObject)
{
	//fill in the details from the undo data
	actorObject->Position = Position;
	actorObject->Orientation = Orientation;

	actorObject->Actor->m_uActorFlags = m_uActorFlags;
	actorObject->m_uActorFlagsBackup = m_uActorFlags;

	actorObject->Actor->m_uGuidToReplace = m_uGuidToReplace;
	actorObject->m_uGuidToReplaceBackup = m_uGuidToReplace;
	

	if(actorObject->IsHierarchyActor())
	{
		//set hierarchy actor specific data
		Asura_Cutscene_Actor_Hierarchy* hierarchyActor = (Asura_Cutscene_Actor_Hierarchy*)actorObject->Actor;
        Asura_Cutscene * cur_cutscene = Tools_Mode_Cutscene::CurrentCutscene->Cutscene;

		//remove old strings
		for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
		{
			if(hierarchyActor->m_aiSkinID[i]!=-1)
			{
				cur_cutscene->SkinNames.RemoveName(hierarchyActor->m_aiSkinID[i]);
				cur_cutscene->SkinFileNames.RemoveName(hierarchyActor->m_aiSkinID[i]);
				hierarchyActor->m_aiSkinID[i] = -1;
			}
		}
		if(hierarchyActor->AnimID!=-1)
		{
			cur_cutscene->AnimationNames.RemoveName(hierarchyActor->AnimID);
			cur_cutscene->AnimationFileNames.RemoveName(hierarchyActor->AnimID);
			hierarchyActor->AnimID = -1;
		}
		
		//and add the new names to the lists
		for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
		{
			if(!m_axSkinName[i].IsBlank())
			{
				hierarchyActor->m_aiSkinID[i] = cur_cutscene->SkinNames.AddName(m_axSkinName[i]);

				#if defined ASURA_ASSERT
					int filenameIndex =
				#endif
					cur_cutscene->SkinFileNames.AddName(m_axSkinFileName[i]);

				Asura_Assert(filenameIndex == hierarchyActor->m_aiSkinID[i],"");
			}
		}
		if(!InitialAnimationName.IsBlank())
		{
			hierarchyActor->AnimID = cur_cutscene->AnimationNames.AddName(InitialAnimationName);

			#if defined ASURA_ASSERT
				int filenameIndex =
			#endif
				cur_cutscene->AnimationFileNames.AddName(AnimationFileName);

			Asura_Assert(filenameIndex == hierarchyActor->AnimID,"");
		}

		hierarchyActor->m_iAnimationFlags = m_iAnimationFlags;
		actorObject->m_iAnimationFlagsBackup = m_iAnimationFlags;

		hierarchyActor->m_uHierarchyFlags = m_uHierarchyFlags;
		actorObject->m_uHierarchyFlagsBackup = m_uHierarchyFlags;

		hierarchyActor->m_uTextureHash = m_uTextureHash;
		actorObject->m_uTextureHashBackup = m_uTextureHash;

		hierarchyActor->m_uIKTemplateID = m_uIKTemplateID;
		actorObject->m_uIKTemplateIDBackup = m_uIKTemplateID;

		#ifdef ASURA_USE_FACIAL_ANIMATION_SYSTEM
		hierarchyActor->m_uFaceID = m_uFaceID;
		actorObject->m_uFaceIDBackup = m_uFaceID;
		#endif



		hierarchyActor->m_fAnimationDelay = m_fAnimationDelay;
		actorObject->m_fAnimationDelayBackup = m_fAnimationDelay;
		
	}
	if(actorObject->IsShapeActor())
	{
		Asura_Cutscene_Actor_Shape* pxShapeActor = (Asura_Cutscene_Actor_Shape*) actorObject->Actor;

		Asura_Hash_ID uTemp = m_uShapeID;
		m_uShapeID = pxShapeActor->m_uShapeID;
		pxShapeActor->m_uShapeID = uTemp;

		pxShapeActor->m_uShapeFlags = m_uShapeFlags;
		actorObject->m_uShapeFlagsBackup = m_uShapeFlags;

	}
	if(actorObject->IsCameraActor())
	{
		Asura_Cutscene_Actor_Camera* pxCameraActor = (Asura_Cutscene_Actor_Camera*) actorObject->Actor;
		Asura_String xTemp = m_xTargetName;
		m_xTargetName = pxCameraActor->GetInitialTargetName();
		pxCameraActor->SetInitialTargetName(xTemp.GetString());

		pxCameraActor->SetCameraFlags(m_uCameraFlags);
		actorObject->m_uCameraFlagsBackup = m_uCameraFlags;

		pxCameraActor->SetInitialFOV(m_fInitialFOV);
		actorObject->m_fInitialFOVBackup = m_fInitialFOV;
		actorObject->m_xCameraShakeBackup = m_xCameraShake;

		pxCameraActor->SetInitialNearClip(m_fNearClip);
		actorObject->m_fNearClipBackup = m_fNearClip;

		pxCameraActor->SetInitialFarClip(m_fFarClip);
		actorObject->m_fFarClipBackup = m_fFarClip;

	}
}

int CutsceneUndoData_Actor_Destroyed::GetPriority() const
{
	return CutsceneModeUndo::PRIORITY_ACTOR_DESTROYED;
}

/////////Actor_Changed
void CutsceneModeUndo::Log_Actor_Changed(Tools_CutsceneActor_Object* actorObject)
{
	RootUndoData** undo = AllocateNextUndoData();
	if(!undo) return;
	
	*undo = CutsceneUndo->CreateUndo_Actor_Changed(actorObject);
}

RootUndoData* CutsceneModeUndo::CreateUndo_Actor_Changed(Tools_CutsceneActor_Object* actorObject)
{
	return new CutsceneUndoData_Actor_Changed(actorObject);
}

CutsceneUndoData_Actor_Changed::CutsceneUndoData_Actor_Changed(Tools_CutsceneActor_Object* actorObject)
{
	ActorName = actorObject->GetName();

	
	//fill in desc of change
	const char* pszVarName = actorObject->GetNameOfChangedVariable();
	if(pszVarName)
		SetDescription("Actor Changed (%s) '%s'",pszVarName,actorObject->GetName());
	else
		SetDescription("Actor Changed  '%s'",actorObject->GetName());

	m_uActorFlags = actorObject->m_uActorFlagsBackup;
	actorObject->m_uActorFlagsBackup = actorObject->Actor->m_uActorFlags;

	m_uGuidToReplace = actorObject->m_uGuidToReplaceBackup;
	actorObject->m_uGuidToReplaceBackup = actorObject->Actor->m_uGuidToReplace;
	

	if(actorObject->IsHierarchyActor())
	{
		//get hierarchy actor specific data
		Asura_Cutscene_Actor_Hierarchy* hierarchyActor = (Asura_Cutscene_Actor_Hierarchy*)actorObject->Actor;

        Asura_Cutscene * cur_cutscene = Tools_Mode_Cutscene::CurrentCutscene->Cutscene;
		for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
		{
			if(hierarchyActor->m_aiSkinID[i]!=-1)
			{
	   			m_axSkinName[i] = cur_cutscene->SkinNames.GetName(hierarchyActor->m_aiSkinID[i]);
				m_axSkinFileName[i] = cur_cutscene->SkinFileNames.GetName(hierarchyActor->m_aiSkinID[i]);
			}
		}
		if(hierarchyActor->AnimID!=-1)
		{
			InitialAnimationName = cur_cutscene->AnimationNames.GetName(hierarchyActor->AnimID);
			AnimationFileName = cur_cutscene->AnimationFileNames.GetName(hierarchyActor->AnimID);
		}

		m_iAnimationFlags = actorObject->m_iAnimationFlagsBackup;
		actorObject->m_iAnimationFlagsBackup = hierarchyActor->m_iAnimationFlags;

		m_uHierarchyFlags = actorObject->m_uHierarchyFlagsBackup;
		actorObject->m_uHierarchyFlagsBackup = hierarchyActor->m_uHierarchyFlags;

		m_fAnimationDelay = actorObject->m_fAnimationDelayBackup;
		actorObject->m_fAnimationDelayBackup = hierarchyActor->m_fAnimationDelay;

		m_uTextureHash = actorObject->m_uTextureHashBackup;
		actorObject->m_uTextureHashBackup = hierarchyActor->m_uTextureHash;

		m_uIKTemplateID = actorObject->m_uIKTemplateIDBackup;
		actorObject->m_uIKTemplateIDBackup = hierarchyActor->m_uIKTemplateID;

		#ifdef ASURA_USE_FACIAL_ANIMATION_SYSTEM
		m_uFaceID = actorObject->m_uFaceIDBackup;
		actorObject->m_uFaceIDBackup = hierarchyActor->m_uFaceID;
		#endif



	}
	if(actorObject->IsShapeActor())
	{
		Asura_Cutscene_Actor_Shape* pxShapeActor = (Asura_Cutscene_Actor_Shape*)actorObject->Actor;
		m_uShapeID = pxShapeActor->m_uShapeID;

		m_uShapeFlags = actorObject->m_uShapeFlagsBackup;
		actorObject->m_uShapeFlagsBackup = pxShapeActor->m_uShapeFlags;

	}
	if(actorObject->IsCameraActor())
	{
		Asura_Cutscene_Actor_Camera* pxCameraActor = (Asura_Cutscene_Actor_Camera*)actorObject->Actor;
		m_xTargetName = pxCameraActor->GetInitialTargetName();

		m_uCameraFlags = actorObject->m_uCameraFlagsBackup;
		actorObject->m_uCameraFlagsBackup = pxCameraActor->GetCameraFlags();
	
		m_fInitialFOV = actorObject->m_fInitialFOVBackup;
		actorObject->m_fInitialFOVBackup = pxCameraActor->GetInitialFOV();

		m_xCameraShake = actorObject->m_xCameraShakeBackup;
		actorObject->m_xCameraShakeBackup = pxCameraActor->GetInitialShake();

		m_fNearClip = actorObject->m_fNearClipBackup;
		actorObject->m_fNearClipBackup = pxCameraActor->GetInitialNearClip();

		m_fFarClip = actorObject->m_fFarClipBackup;
		actorObject->m_fFarClipBackup = pxCameraActor->GetInitialFarClip();

	}

}

RootUndoData* CutsceneUndoData_Actor_Changed::ProcessUndoData()
{
	Asura_Assert(Tools_Mode_Cutscene::CurrentCutscene,"");

	//find the actor object
	Tools_CutsceneActor_Object* actorObject = GetActorObject(ActorName);
	Asura_Assert(actorObject,"");

	SwapWithActor(actorObject);

	if(actorObject->IsSelected())
	{
		//update the overlay
		actorObject->DeleteOverlay();
		actorObject->CreateOverlay();
	}

	
 	return this;
}

void CutsceneUndoData_Actor_Changed::SwapWithActor(Tools_CutsceneActor_Object* actorObject)
{
	if(actorObject->IsHierarchyActor())
	{
		Asura_String tempString;
		//set hierarchy actor specific data
		Asura_Cutscene_Actor_Hierarchy* hierarchyActor = (Asura_Cutscene_Actor_Hierarchy*)actorObject->Actor;
        
        Asura_Cutscene * cur_cutscene = Tools_Mode_Cutscene::CurrentCutscene->Cutscene;

		Asura_String axTempSkinName[ASURA_ANIMATION_SKIN_COUNT];
		Asura_String axTempSkinFileName[ASURA_ANIMATION_SKIN_COUNT];
		Asura_String tempInitialAnimationName;
		Asura_String tempAnimationFileName;

		//get the current strings , and remove from the lists
		for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
		{
			if(hierarchyActor->m_aiSkinID[i]!=-1)
			{
	   			axTempSkinName[i] = cur_cutscene->SkinNames.GetName(hierarchyActor->m_aiSkinID[i]);
				axTempSkinFileName[i] = cur_cutscene->SkinFileNames.GetName(hierarchyActor->m_aiSkinID[i]);
				cur_cutscene->SkinNames.RemoveName(hierarchyActor->m_aiSkinID[i]);
				cur_cutscene->SkinFileNames.RemoveName(hierarchyActor->m_aiSkinID[i]);
				hierarchyActor->m_aiSkinID[i] = -1;
			}
		}
		if(hierarchyActor->AnimID!=-1)
		{
			tempInitialAnimationName = cur_cutscene->AnimationNames.GetName(hierarchyActor->AnimID);
			tempAnimationFileName = cur_cutscene->AnimationFileNames.GetName(hierarchyActor->AnimID);
			cur_cutscene->AnimationNames.RemoveName(hierarchyActor->AnimID);
			cur_cutscene->AnimationFileNames.RemoveName(hierarchyActor->AnimID);
			hierarchyActor->AnimID = -1;
		}

		//add the names from the undo data
		for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
		{
			if(!m_axSkinName[i].IsBlank())
			{
				hierarchyActor->m_aiSkinID[i] = cur_cutscene->SkinNames.AddName(m_axSkinName[i]);

				#if defined ASURA_ASSERT
					int filenameIndex =
				#endif
					cur_cutscene->SkinFileNames.AddName(m_axSkinFileName[i]);

				Asura_Assert(filenameIndex == hierarchyActor->m_aiSkinID[i],"");
			}
		}
		if(!InitialAnimationName.IsBlank())
		{
			hierarchyActor->AnimID = cur_cutscene->AnimationNames.AddName(InitialAnimationName);

			#if defined ASURA_ASSERT
				int filenameIndex =
			#endif
				cur_cutscene->AnimationFileNames.AddName(AnimationFileName);

			Asura_Assert(filenameIndex == hierarchyActor->AnimID,"");
		}
        
        if(axTempSkinName[0]!=m_axSkinName[0])
		{
			//set the default directory to get anims from
			//actorObject->AnimDirectoryName = hierarchyActor->SkinName;
            actorObject->AnimDirectoryName = m_axSkinName[0];
			actorObject->AnimDirectoryName += "\\Anims";
		}

		int iTemp = hierarchyActor->m_iAnimationFlags;
		hierarchyActor->m_iAnimationFlags = m_iAnimationFlags;
		actorObject->m_iAnimationFlagsBackup = m_iAnimationFlags = iTemp;

		u_int uTemp = hierarchyActor->m_uHierarchyFlags;
		hierarchyActor->m_uHierarchyFlags = m_uHierarchyFlags;
		actorObject->m_uHierarchyFlagsBackup = m_uHierarchyFlags = uTemp;

		float fTemp = hierarchyActor->m_fAnimationDelay;
		hierarchyActor->m_fAnimationDelay = m_fAnimationDelay;
		actorObject->m_fAnimationDelayBackup = m_fAnimationDelay = fTemp;

		Asura_Hash_ID uHashTemp = hierarchyActor->m_uTextureHash;
		hierarchyActor->m_uTextureHash = m_uTextureHash;
		actorObject->m_uTextureHashBackup = m_uTextureHash = uHashTemp;

		uHashTemp = hierarchyActor->m_uIKTemplateID;
		hierarchyActor->m_uIKTemplateID = m_uIKTemplateID;
		actorObject->m_uIKTemplateIDBackup = m_uIKTemplateID = uHashTemp;

		#ifdef ASURA_USE_FACIAL_ANIMATION_SYSTEM
		uHashTemp = hierarchyActor->m_uFaceID;
		hierarchyActor->m_uFaceID = m_uFaceID;
		actorObject->m_uFaceIDBackup = m_uFaceID = uHashTemp;
		#endif



		//update undo data
		for(int i=0; i<ASURA_ANIMATION_SKIN_COUNT; i++)
		{
			m_axSkinName[i] = axTempSkinName[i];
			m_axSkinFileName[i] = axTempSkinFileName[i];
		}
		InitialAnimationName = tempInitialAnimationName;
		AnimationFileName = tempAnimationFileName;
				
	
	}
	if(actorObject->IsShapeActor())
	{
		Asura_Cutscene_Actor_Shape* pxShapeActor = (Asura_Cutscene_Actor_Shape*) actorObject->Actor;

		Asura_Hash_ID uTemp = m_uShapeID;
		m_uShapeID = pxShapeActor->m_uShapeID;
		pxShapeActor->m_uShapeID = uTemp;

		u_int uTemp2 = pxShapeActor->m_uShapeFlags;
		pxShapeActor->m_uShapeFlags = m_uShapeFlags;
		actorObject->m_uShapeFlagsBackup = m_uShapeFlags = uTemp2;

	}
	if(actorObject->IsCameraActor())
	{
		Asura_Cutscene_Actor_Camera* pxCameraActor = (Asura_Cutscene_Actor_Camera*) actorObject->Actor;
		Asura_String xTemp = m_xTargetName;
		m_xTargetName = pxCameraActor->GetInitialTargetName();
		pxCameraActor->SetInitialTargetName(xTemp.GetString());

		u_int uTemp = pxCameraActor->GetCameraFlags();
		pxCameraActor->SetCameraFlags(m_uCameraFlags);
		actorObject->m_uCameraFlagsBackup = m_uCameraFlags = uTemp;
	
		float fTemp = pxCameraActor->GetInitialFOV();
		pxCameraActor->SetInitialFOV(m_fInitialFOV);
		actorObject->m_fInitialFOVBackup = m_fInitialFOV = fTemp;
		
		Asura_Cutscene_Camera_Shake xTempShakeParams = pxCameraActor->GetInitialShake();
		pxCameraActor->SetInitialShake(m_xCameraShake);
		actorObject->m_xCameraShakeBackup = m_xCameraShake = xTempShakeParams;

		fTemp = pxCameraActor->GetInitialNearClip();
		pxCameraActor->SetInitialNearClip(m_fNearClip);
		actorObject->m_fNearClipBackup = m_fNearClip = fTemp;

		fTemp = pxCameraActor->GetInitialFarClip();
		pxCameraActor->SetInitialFarClip(m_fFarClip);
		actorObject->m_fFarClipBackup = m_fFarClip = fTemp;

	}

	{
		u_int uTemp = actorObject->Actor->m_uActorFlags;
		actorObject->Actor->m_uActorFlags = m_uActorFlags;
		actorObject->m_uActorFlagsBackup = m_uActorFlags = uTemp;
	}

	{
		u_int uTemp = actorObject->Actor->m_uGuidToReplace;
		actorObject->Actor->m_uGuidToReplace = m_uGuidToReplace;
		actorObject->m_uGuidToReplaceBackup = m_uGuidToReplace = uTemp;
	}

}

int CutsceneUndoData_Actor_Changed::GetPriority() const
{
	return CutsceneModeUndo::PRIORITY_ACTOR_CHANGED;
}


/////////Actor_Rename
void CutsceneModeUndo::Log_Actor_Rename(Tools_CutsceneActor_Object* actorObject,const Asura_String& newName)
{
	RootUndoData** undo = AllocateNextUndoData();
	if(!undo) return;
	
	*undo = CreateUndo_Actor_Rename(actorObject,newName);
}

RootUndoData* CutsceneModeUndo::CreateUndo_Actor_Rename(Tools_CutsceneActor_Object* actorObject,const Asura_String& newName)
{
	return new CutsceneUndoData_Actor_Rename(actorObject,newName);
}

CutsceneUndoData_Actor_Rename::CutsceneUndoData_Actor_Rename(Tools_CutsceneActor_Object* actorObject,const Asura_String& newName)
{
	ActorName = newName;
	OldName = actorObject->GetName();
	
	SetDescription("Actor Renamed '%s'",OldName.GetString());
}

RootUndoData* CutsceneUndoData_Actor_Rename::ProcessUndoData()
{
	Asura_Assert(Tools_Mode_Cutscene::CurrentCutscene,"");

	//find the actor object
	Tools_CutsceneActor_Object* actorObject = GetActorObject(ActorName);
	Asura_Assert(actorObject,"");

	actorObject->Rename(OldName.GetString());

	Asura_String tempName = ActorName;
	ActorName = OldName;
	OldName = tempName;


 	return this;
}

int CutsceneUndoData_Actor_Rename::GetPriority() const
{
	return CutsceneModeUndo::PRIORITY_ACTOR_RENAMED;
}

const char* Tools_CutsceneActor_Object::GetTypeDescription(void)
{
	switch (Actor->Type)
	{
		case ACAT_Camera:			return "Cutscene Actor - Camera";
		case ACAT_CameraTarget:		return "Cutscene Actor - Camera Target";
		case ACAT_Hierarchy:		return "Cutscene Actor - Hierarchy";
		case ACAT_Shape:			return "Cutscene Actor - Shape";
		default:					return "Cutscene Actor";
	}
}
