//////////////////////////////////////////////////////////////////////////////
//
// Asura_ClientEntity_ServerCutsceneActor.cpp
//
// Created by Mike Healey.
//
// Copyright (c) 2002 Rebellion. All rights reserved.
//
// Entities to receive updates from ServerCutscene and render the actor 
// accordingly. 
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_SUPPORT_SERVERCUTSCENES	// the Gunfighter-style cutscenes with entities

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

// Engine independent.
#include "Animation/Asura_Animation.h"
#include "Animation/Asura_Hierarchy.h"
#include "Camera/Asura_Camera.h"
#include "Core/Asura_Timers.h"
#include "Core/Asura_TextureManagement.h"
#include "Cutscene/Asura_Cutscene.h"
#include "Cutscene/Asura_Cutscene_Attachment.h"
#include "Cutscene/Asura_Cutscene_System.h"
#include "Entity/Cutscene/Asura_ServerEntity_ServerCutsceneActor.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/Asura_PosAndOriInterpolator.h"
#include "Network/Asura_Message.h"

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

Asura_ClientEntity_ServerCutsceneActor::Asura_ClientEntity_ServerCutsceneActor()
{
	m_bAttached = false;
    
#ifndef ASURA_NO_NETWORK_LAG_COMPENSATION
    // See note at the top of Asura_ServerEntity_ServerCutsceneActor.h.
    delete m_pxPosAndOriInterpolator;
    m_pxPosAndOriInterpolator = NULL;
#endif
}

bool Asura_ClientEntity_ServerCutsceneActor::HandleMessage(const Asura_Message* pxMessage)
{
	Asura_Assert(pxMessage,"");
	if (!pxMessage) return false;

	switch(pxMessage->GetMsgID())
    {
        default:
	    {
		    break;
	    }

        case ASURA_MSG_CLIENTUPDATE:
        {
            const Asura_Message_ServerCutsceneActor_ClientUpdateBasic* pxData = 
                reinterpret_cast<const Asura_Message_ServerCutsceneActor_ClientUpdateBasic*>(pxMessage->GetData());
            
			ProcessNewPositionAndOrientationData(pxData->xPosition, pxData->xOrientation, pxMessage->GetSentTime());
            return true;
        }
	    
    }

	// Pass to parent.
	return Asura_Entity_Renderable::HandleMessage(pxMessage);
}

Asura_ClientEntity_ServerCutsceneActorHierarchy::
Asura_ClientEntity_ServerCutsceneActorHierarchy(Asura_Hash_ID uCutsceneHash, short _SkinID, short _InitialAnimationID, int iAnimationFlags, float fAnimationDelay, u_int _uFlags, u_int uTextureHash,bool bCreatePosableAnim /*= false*/)
{
    Classification = AsuraEntityClass_ServerCutsceneActorHierarchy;

    m_pxCutscene = Asura_Cutscene_System::GetCutsceneByHash(uCutsceneHash);
    Asura_Assert(m_pxCutscene, "failed to find cutscene");

	
	#if 0 //RAR 29/06/04 - Can't create base class Asura_Animation_Posabe any more
	if (bCreatePosableAnim)
	{
		m_pxAnimation = new Asura_Animation_Posable(m_pxCutscene->SkinNames.GetName(_SkinID).GetString());
	}
	else
	#endif
	{
		m_pxAnimation = new Asura_Animation(m_pxCutscene->SkinNames.GetName(_SkinID).GetString());
	}
    Asura_Assert(m_pxAnimation, "failed to create hierarchy for server cutscene");
    if (m_pxAnimation)
    {
       	m_pxAnimation->TimersUpdatedBeforeRender = true;
		m_pxAnimation->StartAnim2(m_pxCutscene->AnimationNames.GetName(_InitialAnimationID).GetString(), iAnimationFlags, 0, fAnimationDelay);
    }
    m_uFlags = _uFlags;

	if(uTextureHash != ASURA_HASH_ID_UNSET)
	{
		m_pxAnimation->SetAnimTexID(Asura_Texture_Management::GetIndexFromHashID(uTextureHash));
	}
	
    m_fCutsceneTimeAfterLastRender = 0.0f;
    m_fCutsceneTimeAtLastUpdate = 0.0f;
	m_pxFirstAttachment = 0;
}

Asura_ClientEntity_ServerCutsceneActorHierarchy::~Asura_ClientEntity_ServerCutsceneActorHierarchy()
{
	while(m_pxFirstAttachment)
	{
		RemoveAttachment(m_pxFirstAttachment->GetID());
	}

    delete m_pxAnimation;
}

bool Asura_ClientEntity_ServerCutsceneActorHierarchy::GetMarkerPosAndOri( Asura_Hash_ID uMarkerHashID, Asura_Vector_3* pxPos, Asura_Matrix_3x3* pxOri ) const
{
    Asura_Vector_3 xMarkerPos;
    Asura_Matrix_3x3 xMarkerOri;
    if( !m_pxAnimation->GetMarkerLocationFromHash(uMarkerHashID, &xMarkerPos, &xMarkerOri) )
    {
        return false;
    }

    const Asura_Matrix_3x3& xCurOri = GetCurrentOrientation();

	if( pxPos )
	{
		*pxPos = xMarkerPos * xCurOri + GetCurrentPosition();
	}

	if( pxOri )
	{
		*pxOri = xCurOri * xMarkerOri;
	}

    return true;
}

bool Asura_ClientEntity_ServerCutsceneActorHierarchy::HandleMessage(const Asura_Message* pxMessage)
{
	Asura_Assert(pxMessage,"");
	if (!pxMessage) return false;
	
	switch(pxMessage->GetMsgID())
	{
		default:
		{
			break;
		}
        
        case ASURA_MSG_SERVERCUTSCENEACTOR_CLIENTUPDATE_STANDARD:
        {
		    const Asura_Message_ServerCutsceneActor_ClientUpdateStandard* pxData = 
                (const Asura_Message_ServerCutsceneActor_ClientUpdateStandard*)pxMessage->GetData();

            if(!m_bAttached)
			{
				ProcessNewPositionAndOrientationData(pxData->xPosition, pxData->xOrientation, pxMessage->GetSentTime());
			}
            if (m_pxAnimation)
            {
                m_pxAnimation->SetAnimDurationBySpeed(pxData->fSpeed);
            }
            m_fCutsceneTimeAtLastUpdate = pxData->fCutsceneTime;
            return true;
        }

        case ASURA_MSG_SERVERCUTSCENEACTOR_CLIENTUPDATE_PRIMARYANIMATION:
        {
            const Asura_Message_ServerCutsceneActor_ClientUpdateChangePrimaryAnim* data = 
                (const Asura_Message_ServerCutsceneActor_ClientUpdateChangePrimaryAnim*)pxMessage->GetData();
            float fTimeDelay = data->m_fCutsceneTime - m_fCutsceneTimeAfterLastRender;
            m_pxAnimation->StartAnim2(data->m_uAnimHashID, data->m_iAnimationFlags, data->m_fTweenDuration, fTimeDelay);
            
			return true;
        }

        case ASURA_MSG_SERVERCUTSCENEACTOR_CLIENTUPDATE_SECONDARYANIMATION:
        {
            const Asura_Message_ServerCutsceneActor_ClientUpdateSecondaryAnim* data = 
                (const Asura_Message_ServerCutsceneActor_ClientUpdateSecondaryAnim*)pxMessage->GetData();
            // (MH - 19.07.2002) I might be missing something, but I don't see why we can't just 
            // forget about the secondary anim straight away by setting the auto remove flag.
            int iAnimFlags = data->m_iAnimationFlags;
            iAnimFlags |= AsuraAnimationFlag_AutoRemove;
            Asura_Handle iHandle = m_pxAnimation->AddOneShotAnim2(data->m_uAnimHashID, data->m_fWeight, iAnimFlags, data->m_fTweenDuration);

            return true;
        }

        case ASURA_MSG_SERVERCUTSCENEACTOR_CLIENTUPDATE_PRIMARYANIMATION_TOPOS:
        {
            const Asura_Message_ServerCutsceneActor_ClientUpdatePrimaryAnim_ToPos* data = 
                (const Asura_Message_ServerCutsceneActor_ClientUpdatePrimaryAnim_ToPos*)pxMessage->GetData();
            const char * AnimationName = m_pxCutscene->AnimationNames.GetName(data->AnimationID).GetString();
            
            m_pxAnimation->PlayAnimToPos(AnimationName, data->m_fStartTimer, data->m_fEndTimer,  data->AnimationFlags, data->TweenDuration);

            return true;
        }

        case ASURA_MSG_SERVERCUTSCENEACTOR_CLIENTUPDATE_SECONDARYANIMATION_TOPOS:
        {
            const Asura_Message_ServerCutsceneActor_ClientUpdateSecondaryAnim_ToPos* data = 
                (const Asura_Message_ServerCutsceneActor_ClientUpdateSecondaryAnim_ToPos*)pxMessage->GetData();

            const char * AnimationName = m_pxCutscene->AnimationNames.GetName(data->AnimationID).GetString();
            int animFlags = data->AnimationFlags;
            animFlags |= AsuraAnimationFlag_AutoRemove;
            Asura_Handle iHandle = m_pxAnimation->PlayOneShotAnimToPos(AnimationName, data->Weight, data->m_fStartTimer, data->m_fEndTimer, animFlags);
			
			if(iHandle != ASURA_INVALID_HANDLE)
			{
				m_pxAnimation->SetTweenDuration(data->TweenDuration,iHandle);
			}

            return true;
        }

		case ASURA_MSG_SERVERCUTSCENEACTOR_CLIENTUPDATE_ADDATTACHMENTSHAPE :
		{
            const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentShape* pxData = 
                (const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentShape*)pxMessage->GetData();
			
			Asura_ServerCutscene_ClientAttachment* pxAttachment = new Asura_ServerCutscene_ClientAttachment_Shape( pxData, m_pxCutscene );
			InsertAttachment(pxAttachment);

			return true;
		}

		case ASURA_MSG_SERVERCUTSCENEACTOR_CLIENTUPDATE_ADDATTACHMENTHIERARCHY :
		{
            const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentHierarchy* pxData = 
                (const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentHierarchy*)pxMessage->GetData();
			
			Asura_ServerCutscene_ClientAttachment* pxAttachment = new Asura_ServerCutscene_ClientAttachment_Hierarchy( pxData, m_pxCutscene, m_pxAnimation );
			InsertAttachment(pxAttachment);

			return true;
		}

		case ASURA_MSG_SERVERCUTSCENEACTOR_CLIENTUPDATE_ADDATTACHMENTACTOR :
		{
            const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentActor* pxData = 
                (const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentActor*)pxMessage->GetData();
			
			Asura_ServerCutscene_ClientAttachment* pxAttachment = new Asura_ServerCutscene_ClientAttachment_Actor( pxData );
			InsertAttachment(pxAttachment);

			return true;
		}

		case ASURA_MSG_SERVERCUTSCENEACTOR_CLIENTUPDATE_ADDATTACHMENTENTITY :
		{
            const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentEntity* pxData = 
                (const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentEntity*)pxMessage->GetData();
			
			Asura_ServerCutscene_ClientAttachment* pxAttachment = new Asura_ServerCutscene_ClientAttachment_Entity( pxData, m_pxCutscene );
			InsertAttachment(pxAttachment);

			return true;
		}


		case ASURA_MSG_SERVERCUTSCENEACTOR_CLIENTUPDATE_REMOVEATTACHMENT :
		{
            const Asura_Message_ServerCutsceneActor_ClientUpdateRemoveAttachment* pxData = 
                (const Asura_Message_ServerCutsceneActor_ClientUpdateRemoveAttachment*)pxMessage->GetData();
			
			RemoveAttachment(pxData->m_uID);

			return true;
		}

		case ASURA_MSG_TEXTUREANIMATION :
		{
			const Asura_Message_TextureAnimation* pxData = reinterpret_cast
			    <const Asura_Message_TextureAnimation*>(pxMessage->GetData());
			Asura_Assert(pxMessage->GetDataSize() == sizeof(Asura_Message_TextureAnimation), "serious error. data size has changed");

			if(m_pxAnimation)
			{
				if(pxData->m_bChangeSequence)
				{
					m_pxAnimation->SetTextureAnimSequence(pxData->m_uSequence);
				}
				if(pxData->m_bChangeSpeed)
				{
					m_pxAnimation->SetTextureAnimSpeed(pxData->m_fSpeed);
				}
			}
			return true;
		}

    }

	// Pass to parent.
	return Asura_ClientEntity_ServerCutsceneActor::HandleMessage(pxMessage);
}

void Asura_ClientEntity_ServerCutsceneActorHierarchy::Render()
{
	if(m_bAttached) return;

	Render( GetCurrentPosition(), GetCurrentOrientation() );
}

void Asura_ClientEntity_ServerCutsceneActorHierarchy::Render(const Asura_Vector_3& xPos,const Asura_Matrix_3x3& xMat)
{
    Asura_Assert(m_fCutsceneTimeAtLastUpdate >= m_fCutsceneTimeAfterLastRender, "MH sanity check");

    u_int uRenderFlags = ASURA_HIERARCHY_RENDER_UPDATEBONESALWAYS;
    if( m_pxCutscene->m_iCutsceneFlags & AsuraCutsceneFlag_IgnoreModuleLinking )
    {
        uRenderFlags |= ASURA_HIERARCHY_RENDER_NO_MODULELINK_TEST;
    }
	if(m_uFlags & Asura_SESCU_Flag_NotRendered)
	{
		uRenderFlags |= ASURA_HIERARCHY_RENDER_DONTRENDER;
	}

    // HACK
    // Turn shadows off for all of 'em
    uRenderFlags |= ASURA_HIERARCHY_RENDER_NO_SHADOW;

	if( m_pxCutscene->IsPlaying() )
	{
		m_pxAnimation->Update( xPos, xMat, m_fCutsceneTimeAtLastUpdate - m_fCutsceneTimeAfterLastRender, uRenderFlags );
	}
	else
	{
		m_pxAnimation->Update( xPos, xMat, ANIMTIME_USEGAMETIME, uRenderFlags );
	}

    m_fCutsceneTimeAfterLastRender = m_fCutsceneTimeAtLastUpdate;
	//render any attachments
	Asura_ServerCutscene_ClientAttachment* pxAttachment = m_pxFirstAttachment;
	while(pxAttachment)
	{
		pxAttachment->UpdatePosition(m_pxAnimation, xPos, xMat);
		pxAttachment->Render();
		pxAttachment = pxAttachment->m_pxNextAttachment;
	}	
}

void Asura_ClientEntity_ServerCutsceneActorHierarchy::InsertAttachment(Asura_ServerCutscene_ClientAttachment* pxAttachment)
{
	if(!pxAttachment) return;
	//add the new attachment to the start of the list
	pxAttachment->m_pxNextAttachment = m_pxFirstAttachment;
	m_pxFirstAttachment = pxAttachment;
}

void Asura_ClientEntity_ServerCutsceneActorHierarchy::RemoveAttachment(u_int uID)
{
	Asura_ServerCutscene_ClientAttachment** ppxAttachment = &m_pxFirstAttachment;

	while(*ppxAttachment)
	{
		if((*ppxAttachment)->GetID()==uID)
		{
			//This is the one to delete.. store the pointer
			Asura_ServerCutscene_ClientAttachment* pxAttachmentToDelete = *ppxAttachment;
			//fix up the linked list
			*ppxAttachment = pxAttachmentToDelete->m_pxNextAttachment;
			//and kill
			delete pxAttachmentToDelete;
		}
		else
		{
			//move on to the next
			ppxAttachment = &(*ppxAttachment)->m_pxNextAttachment;
		}
	}
}


const Asura_Vector_3& Asura_ClientEntity_ServerCutsceneActorHierarchy::GetCurrentPosition( void ) const
{
#ifdef ASURA_NO_NETWORK_LAG_COMPENSATION
    if( (m_uFlags & Asura_SESCU_Flag_UseAnimMovementData) && m_pxAnimation )
    {
        Asura_Vector_3 xOffset;
        if( m_pxAnimation->GetMovementOffset(&xOffset) )
        {
            m_xCurrentPosition += xOffset * GetCurrentOrientation();
        }
    }
    return m_xCurrentPosition;
#else
    // Assume these don't bother with interpolators.
    Asura_Assert( !m_pxPosAndOriInterpolator, "" );

	if( m_uCurrentPositionFrameStamp != Asura_Timers::GetFrameNumber() )
    {
        if( (m_uFlags & Asura_SESCU_Flag_UseAnimMovementData) && m_pxAnimation )
        {
            Asura_Vector_3 xOffset;
            if( m_pxAnimation->GetMovementOffset(&xOffset) )
            {
                m_xCurrentPosition += xOffset * GetCurrentOrientation();
            }
        }

        m_uCurrentPositionFrameStamp = Asura_Timers::GetFrameNumber();
    }

    return m_xCurrentPosition;
#endif
}

bool Asura_ClientEntity_ServerCutsceneActorCamera::HandleMessage(const Asura_Message* pxMessage)
{
	Asura_Assert(pxMessage,"");
	if (!pxMessage) return false;
	
	switch(pxMessage->GetMsgID())
	{
		default:
		{
			break;
		}
        
        case ASURA_MSG_CLIENTUPDATE:
        {
		    const Asura_Message_ServerCutsceneActor_ClientUpdateCamera* pxData = 
                (const Asura_Message_ServerCutsceneActor_ClientUpdateCamera*)pxMessage->GetData();

			
			if(!m_bAttached)
			{
				ProcessNewPositionAndOrientationData(pxData->xPosition, pxData->xOrientation, pxMessage->GetSentTime());
			}
			m_fFOV = pxData->m_fFOV;
			m_bWidescreen = pxData->m_ucWidescreen ? true : false;
			
            return true;
        }

    }

	// Pass to parent.
	return Asura_ClientEntity_ServerCutsceneActor::HandleMessage(pxMessage);
}


void Asura_ClientEntity_ServerCutsceneActorCamera::Render()
{
	if( !IsDestroyed() )
    {
        Asura_Camera::SetCameraIndex(0);
		if(m_fFOV)
		{
			float fWidescreenRatioModifier = 1.0f;
			if (m_bWidescreen)
			{
				fWidescreenRatioModifier = Asura_Cutscene_System::GetWidescreenDisplayedXOverYRatio();
			}
			Asura_Camera::SetFieldOfView(m_fFOV, fWidescreenRatioModifier);
		}

		Asura_Camera::SetPosition(GetCurrentPosition());
	    Asura_Camera::SetOrientation(GetCurrentOrientation());
    }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------------------------**
** Asura_ClientEntity_ServerCutsceneActorShape **
**---------------------------------------------*/

Asura_ClientEntity_ServerCutsceneActorShape::
Asura_ClientEntity_ServerCutsceneActorShape(Asura_Hash_ID uCutsceneHash, Asura_Hash_ID uShapeID)
{
    Classification = AsuraEntityClass_ServerCutsceneActorShape;

    m_pxCutscene = Asura_Cutscene_System::GetCutsceneByHash(uCutsceneHash);
    Asura_Assert(m_pxCutscene, "failed to find cutscene");

	m_xShapeInstance.SetShape(uShapeID);

    if( m_pxCutscene->m_iCutsceneFlags & AsuraCutsceneFlag_IgnoreModuleLinking )
    {
        m_xShapeInstance.AddFlags( ASURA_SHAPE_INSTANCE_IGNORE_MOD_LINKING );
    }

    m_fCutsceneTimeAfterLastRender = 0.0f;
    m_fCutsceneTimeAtLastUpdate = 0.0f;
}

bool Asura_ClientEntity_ServerCutsceneActorShape::HandleMessage(const Asura_Message* pxMessage)
{
	Asura_Assert(pxMessage,"");
	if (!pxMessage) return false;
	
	switch(pxMessage->GetMsgID())
	{
		default:
		{
			break;
		}
        
        case ASURA_MSG_SERVERCUTSCENEACTORSHAPE_CLIENTUPDATE_STANDARD:
        {
		    const Asura_Message_ServerCutsceneActorShape_ClientUpdateStandard* pxData = 
                (const Asura_Message_ServerCutsceneActorShape_ClientUpdateStandard*)pxMessage->GetData();

			
			if(!m_bAttached)
			{
				ProcessNewPositionAndOrientationData(pxData->m_xPosition, pxData->m_xOrientation, pxMessage->GetSentTime());
			}
			
            m_fCutsceneTimeAtLastUpdate = pxData->m_fCutsceneTime;
            return true;
        }

    }

	// Pass to parent.
	return Asura_ClientEntity_ServerCutsceneActor::HandleMessage(pxMessage);
}

void Asura_ClientEntity_ServerCutsceneActorShape::Render()
{
	if(m_bAttached) return;

	Render( GetCurrentPosition(), GetCurrentOrientation() );
}

void Asura_ClientEntity_ServerCutsceneActorShape::Render(const Asura_Vector_3& xPos,const Asura_Matrix_3x3& xMat)
{
    Asura_Assert(m_fCutsceneTimeAtLastUpdate >= m_fCutsceneTimeAfterLastRender, "MH sanity check");

	m_xShapeInstance.SetNewPosAndOri(xPos,xMat);
	m_xShapeInstance.Render();
}

//
// Asura_ServerCutscene_ClientAttachment.
//

Asura_ServerCutscene_ClientAttachment::Asura_ServerCutscene_ClientAttachment(const struct Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachment* pxMessage)
{
	m_uID = pxMessage->m_uID;
	m_uMarkerHash = pxMessage->m_uMarkerHash;
	m_pxNextAttachment = 0;
	m_bPositionValid = false;
}

void Asura_ServerCutscene_ClientAttachment::UpdatePosition(const Asura_Animation* pxParentAnimation, const Asura_Vector_3 &xParentPosition, const Asura_Matrix_3x3 &xParentMatrix)
{
	m_bPositionValid = false;
	if(!pxParentAnimation) return;
	if(!pxParentAnimation->GetMarkerLocationFromHash(m_uMarkerHash,&m_xPos,&m_xMat))
	{
		return;
	}
	//convert to world space
	m_xPos = xParentPosition + m_xPos * xParentMatrix;
	m_xMat = xParentMatrix * m_xMat;

	m_bPositionValid = true;
}

//
// Asura_ServerCutscene_ClientAttachment_Shape.
//

Asura_ServerCutscene_ClientAttachment_Shape::Asura_ServerCutscene_ClientAttachment_Shape( const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentShape* pxMessage,
                                                                                          Asura_Cutscene* pxParentCutscene )
    : PARENT( pxMessage )
{
	m_xShapeInstance.SetShape( pxMessage->m_uShapeHash );

    if( pxParentCutscene->m_iCutsceneFlags & AsuraCutsceneFlag_IgnoreModuleLinking )
    {
        m_xShapeInstance.AddFlags( ASURA_SHAPE_INSTANCE_IGNORE_MOD_LINKING );
    }
}

void Asura_ServerCutscene_ClientAttachment_Shape::Render()
{
	if(!m_bPositionValid) return;
	m_xShapeInstance.SetNewPosAndOri(m_xPos,m_xMat);
	m_xShapeInstance.Render();

}

//
// Asura_ServerCutscene_ClientAttachment_Hierarchy.
//

Asura_ServerCutscene_ClientAttachment_Hierarchy::Asura_ServerCutscene_ClientAttachment_Hierarchy( const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentHierarchy* pxMessage,
                                                                                                  Asura_Cutscene* pxParentCutscene,
                                                                                                  Asura_Animation* pxParentAnimation )
    : PARENT( pxMessage )
{
	m_pxParentAnimation = pxParentAnimation;

	m_pxAnimation = 0;
	if(pxMessage->m_iSkinID ==-1 || pxMessage->m_iAnimID==-1) return;

	m_pxAnimation = new Asura_Animation( pxParentCutscene->SkinNames.GetName(pxMessage->m_iSkinID).GetString() );
	if(!m_pxAnimation) return;

	m_pxAnimation->StartAnim2( pxParentCutscene->AnimationNames.GetName(pxMessage->m_iAnimID).GetString(), AsuraAnimationFlag_Loop );

	m_pxParentAnimation->LinkAnimation(m_pxAnimation,m_uMarkerHash,false);

}

Asura_ServerCutscene_ClientAttachment_Hierarchy::~Asura_ServerCutscene_ClientAttachment_Hierarchy()
{
	m_pxParentAnimation->UnlinkAnimation(m_pxAnimation);

	delete m_pxAnimation;
	m_pxAnimation = 0;
}


//
// Asura_ServerCutscene_ClientAttachment_Actor.
//

Asura_ServerCutscene_ClientAttachment_Actor::Asura_ServerCutscene_ClientAttachment_Actor( const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentActor* pxMessage )
    : PARENT( pxMessage )
{
	m_uActorID = pxMessage->m_uActorID;

	Asura_ClientEntity_ServerCutsceneActor* pxEntity = GetAttachedActor();
	if(pxEntity)
	{
		if( pxEntity->m_bAttached )
		{
			Asura_Assert(0,"Actor is being used by 2 attachments at once");
			m_uActorID = ASURA_GUID_UNREGISTERED;
		}
		else
		{
			pxEntity->m_bAttached = true;
		}
	}
}

Asura_ServerCutscene_ClientAttachment_Actor::~Asura_ServerCutscene_ClientAttachment_Actor()
{
	Asura_ClientEntity_ServerCutsceneActor* pxEntity = GetAttachedActor();
	if(pxEntity) pxEntity->m_bAttached = false;
}

Asura_ClientEntity_ServerCutsceneActor* Asura_ServerCutscene_ClientAttachment_Actor::GetAttachedActor( void ) const
{
	return static_cast<Asura_ClientEntity_ServerCutsceneActor*>( Asura_Entity_Renderable::FindEntity(m_uActorID) );
}

void Asura_ServerCutscene_ClientAttachment_Actor::Render()
{
	if(!m_bPositionValid) return;
	Asura_ClientEntity_ServerCutsceneActor* pxEntity = GetAttachedActor();

	// (MH - 06.12.2002) Rich assures me this should be OK. 
    //Asura_Assert(pxEntity, "failed to find attached actor");
    if (pxEntity)
    {
        Asura_Quat xQuat;
	    xQuat.FromMatrix(m_xMat);
	    
	    pxEntity->ProcessNewPositionAndOrientationData(m_xPos,xQuat,Asura_Timers::GetServerAbsoluteTime());
	    pxEntity->Render(m_xPos,m_xMat);	
    }
}

//
// Asura_ServerCutscene_ClientAttachment_Entity.
//

Asura_ServerCutscene_ClientAttachment_Entity::Asura_ServerCutscene_ClientAttachment_Entity( const Asura_Message_ServerCutsceneActor_ClientUpdateAddAttachmentEntity* pxMessage,
                                                                                            Asura_Cutscene* pxParentCutscene )
    : PARENT( pxMessage )
{
	m_bHasBeenUpdated = false;
	m_uEntityGUID = pxMessage->m_uEntityGUID;

    const Asura_Entity_Client* pxEntity = Asura_Entity_Client::FindEntity( m_uEntityGUID );
    if( pxEntity )
    {
        m_usEntityClassification = pxEntity->GetClassification();
        bool bIgnoreModLinking = ( 0 != (pxParentCutscene->m_iCutsceneFlags & AsuraCutsceneFlag_IgnoreModuleLinking) );
	    Asura_Cutscene_Attachment_Entity::ActivateEntity( m_uEntityGUID, m_usEntityClassification, true, bIgnoreModLinking );
    }
    else
    {
        m_usEntityClassification = AsuraEntityClass_NotSet;
    }
}

Asura_ServerCutscene_ClientAttachment_Entity::~Asura_ServerCutscene_ClientAttachment_Entity()
{
    Asura_Cutscene_Attachment_Entity::ActivateEntity( m_uEntityGUID, m_usEntityClassification, false );
}

void Asura_ServerCutscene_ClientAttachment_Entity::Render()
{
	if(!m_bPositionValid) return;

	Asura_Cutscene_Attachment_Entity::MoveEntity( m_uEntityGUID, m_usEntityClassification, m_xPos, m_xMat, NULL, !m_bHasBeenUpdated );
	m_bHasBeenUpdated = true;
}

#endif // #ifdef ASURA_SUPPORT_SERVERCUTSCENES	// the Gunfighter-style cutscenes with entities
