//////////////////////////////////////////////////////////////////////////////
//
// AVP_ClientEntity_PhysicalObject.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header.
#include "Entity/PhysicalObject/AVP_ClientEntity_PhysicalObject.h"

// Engine independent.
#include "Animation/Asura_Animation.h"
#include "Animation/Asura_AnimationAttachment.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "Entity/PhysicalObject/Moves/Asura_Movement_System.h"
#include "Entity/PhysicalObject/Moves/Asura_Moves.h"
#include "Entity/Asura_PosAndOriInterpolator.h"
#include "Physics/Asura_Physics_Object.h"
#include "Sound/Asura_Sound_Event_System.h"
#include "Sound/Asura_Sound_Event_Storage.h"
#include "SpecialFX/pfx/Asura_PFX_Enums.h"
#include "SpecialFX/pfx/Asura_PFX_Entity_Effect.h"
#include "SpecialFX/pfx/Asura_PFX_System.h"

// Project...
#include "Core/AVP_Game_Core.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Container/AVP_ClientEntity_Container.h"
#include "Entity/Damage/AVP_EntityDamage_Messages.h"
#include "Entity/PhysicalObject/AVP_PhysicalObject_Messages.h"
#include "Mesh/AVP_PolygonFlags.h"
#include "SpecialFX/PFX/AVP_PFX_Entity_Fire.h"




#ifdef ASURA_MAX_NO_OF_STORED_POSITIONS
DEBUGVAR bool dbg_bUpdateUsingStoredPositions = true; 
#endif // #ifdef ASURA_MAX_NO_OF_STORED_POSITIONS

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

//
// AVP_ClientEntity_PhysicalObject
//

Asura_Combined_List<AVP_ClientEntity_PhysicalObject> AVP_ClientEntity_PhysicalObject::s_xList( 5 );

AVP_ClientEntity_PhysicalObject::AVP_ClientEntity_PhysicalObject( void )
: m_pxDynamicObject( NULL )
, m_uMaterialType(ASURA_HASH_ID_UNSET)
, m_fInitialHealth(100.0f)
, m_uBurningEffectID(ASURA_PFX_INVALID_EFFECT_ID)
, m_uFlags(0)
, m_fForcedBoneUpdateTimer( 0.0f )
, m_fMaxTimeBetweenBoneUpdates( 0.66f )
{
    SetBlueprintTypeHashID( 0x6B08945D );  //Asura_GetHashID("PhysicalObjects")

    s_xList.AddToList( this );
}


AVP_ClientEntity_PhysicalObject::~AVP_ClientEntity_PhysicalObject( void ) 
{
	// Destroy Fire if the object is burning.
	if( m_uBurningEffectID!=ASURA_PFX_INVALID_EFFECT_ID )
	{
		AVP_PFX_Entity_Fire* pxFire = (AVP_PFX_Entity_Fire*)Asura_PFX_System::GetEffect( m_uBurningEffectID );
		if( pxFire )
		{
			pxFire->Destroy();
		}
	}

	s_xList.RemoveFromList( this );
}


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_PhysicalObject::ProcessClientUpdate
// Author       : Andy Tate
// Date         : 23-Feb-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientEntity_PhysicalObject::ProcessClientUpdate(const AVP_Message_PhysicalObject_ClientUpdate* pxData,
														  float fServerTime,
														  bool bProcessPosAndOri /*= true*/ )
{
	if ( pxData->GetBlueprintHash() != GetBlueprintHashID() )
    {
        SetBlueprintHashID( pxData->GetBlueprintHash() );
        InitFromBlueprint();
    }
#ifdef ASURA_MAX_NO_OF_STORED_POSITIONS
	if ( dbg_bUpdateUsingStoredPositions )
	{
		u_int uNumberOfStoredPositions = pxData->GetNumberOfStoredPositions();
		if ( uNumberOfStoredPositions > 0 )
		{
			const Asura_Vector_3* pxStoredPositions = pxData->GetStoredPositions(); 
			const float* pfTimeStampsForStoredPositions = pxData->GetTimeStampsForStoredPositions(); 
			for ( u_int u = 0; u < uNumberOfStoredPositions; ++u )
			{
				ProcessNewPositionAndOrientationData( pxStoredPositions[u], pxData->m_xOrientation, pfTimeStampsForStoredPositions[u] );
			}
		}
	}
#endif // #ifdef ASURA_MAX_NO_OF_STORED_POSITIONS

    PARENT_ASURA::ProcessClientUpdate(pxData, fServerTime, bProcessPosAndOri);
}

/// End of function 'AVP_ClientEntity_PhysicalObject::ProcessClientUpdate'. ///

void AVP_ClientEntity_PhysicalObject::ProcessClientCreate(	const AVP_Message_PhysicalObject_ClientCreate* pxData,
															float fServerTime,
															bool bProcessPosAndOri /*= true*/ )
{
	SetIgnoreForSurfaceReorientation( pxData->GetIgnoreForSurfaceReorientation() );
	m_uMaterialType = pxData->GetMaterialType();

	ProcessClientUpdate( pxData, fServerTime, bProcessPosAndOri );
}

bool AVP_ClientEntity_PhysicalObject::HandleMessage( const Asura_Message* pxMsg )
{
    switch( pxMsg->GetMsgID() )
    {
        case ASURA_MSG_CLIENTUPDATE:
		{
			const AVP_Message_PhysicalObject_ClientUpdate*	pxData	= static_cast<const AVP_Message_PhysicalObject_ClientUpdate*>(pxMsg->GetData());

			ProcessClientUpdate(pxData, pxMsg->GetSentTime());
			return true;
		}

		case AVP_MSGID_ENTITYDAMAGE_APPLY_TO_ENTITY:
		{
			const AVP_Message_EntityDamage_ApplyToEntity*	pxData		= static_cast<const AVP_Message_EntityDamage_ApplyToEntity*>(pxMsg->GetData());

			AVP_EntityDamage_ApplyToEntity					xDamage;

			pxData->GetDamage(xDamage);

			if(IsADeadBody())
			{
				// If ragdoll is asleep wake it up
				if ( IsRagDollAsleep() ) WakeUpRagdoll();
				OnApplyDamage(xDamage);
			}
			else
			{
				const float	fServerTime	= pxMsg->GetSentTime();
				const float	fOldHealth	= GetHealth();
				float		fNewHealth	= xDamage.GetHealth();

				UpdateHealth(fNewHealth, fServerTime);

				const float fRealHealth = GetHealth(); 
				if((fOldHealth > 0.0f) && (fRealHealth <= 0.0f))
				{
					OnDeath(xDamage);
				}
				else
				{
					OnApplyDamage(xDamage);
				}
			}
			return(true);
		}

		case AVP_MSG_CLIENT_START_BURNING:			// Tell client to start burning
		{
			SetBurningFlag( true );
			OnBeginBurning();
			return true;
		}

		case AVP_MSG_CLIENT_STOP_BURNING:			// Tell client to stop burning.
		{
			OnEndBurning();
			SetBurningFlag( false );
			return true;
		}

        default:
		{
			return(PARENT_ASURA::HandleMessage( pxMsg ));
		}
    }
}


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_PhysicalObject::SendMessageToServer
// Author       : Andy Tate
// Date         : 18-Jan-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_ClientEntity_PhysicalObject::SendMessageToServer(Asura_Message* pxMessage) const
{
	if(CanSendMessageToServer())
	{
		Send(pxMessage);
		return(true);
	}
	else
	{
		Asura_Network::DestroyMessage(pxMessage);
		return(false);
	}
}

/// End of function 'AVP_ClientEntity_PhysicalObject::SendMessageToServer'. ///

const Asura_Sound_Event* AVP_ClientEntity_PhysicalObject::GetSoundEventFromBlueprints(const Asura_Hash_ID uBlueprintParamHash) const
{
	Asura_Hash_ID uSEHash = ASURA_HASH_ID_UNSET;

	// Delve into the blueprints
	Asura_Blueprint* pxActorBlueprint = GetBlueprint();
	if(pxActorBlueprint)
	{
		Asura_Value xValue;
		if(pxActorBlueprint->GetParameterValueByHashID( 0x41BAA264, xValue ) ) //Asura_GetHashID("SoundBlueprint");
		{
			
			Asura_Hash_ID uBPHashID = xValue.GetHashIDValue();
			Asura_Hash_ID uTypeHashID = 0x2351E9CF; //Asura_GetHashID("Actor Sounds");

			Asura_Blueprint* pxSoundBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uTypeHashID, uBPHashID);
			if(pxSoundBlueprint)
			{
				Asura_Value xValue2;
				if(pxSoundBlueprint->GetParameterValueByHashID(uBlueprintParamHash, xValue2))
				{
					uSEHash = xValue2.GetHashIDValue();
					return(Asura_Sound_Event_Storage::FindEvent(uSEHash));
				}
			}
		}
	}
	return(NULL);	
}

/// End of function 'AVP_ClientEntity_PhysicalObject::PlaySoundEvent'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_PhysicalObject::GetBoundingSphere
// Author       : Andy Tate
// Date         : 12-Jan-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_ClientEntity_PhysicalObject::GetBoundingSphere(Asura_Vector_3& xBSCentre,
														 float& fBSRadius) const
{
	const Asura_Shape_Instance&	xShapeInstance	= GetShapeInstance();
	const Asura_Animation*		pxAnimation		= xShapeInstance.GetAnimation();

	// Take our bounding-box...
	if(pxAnimation == NULL)
	{
		return(false);
	}
	else
	{
		fBSRadius	= pxAnimation->GetCoarseBoundingSphereRadius();
		xBSCentre	= pxAnimation->GetBoundingSphereCentre();
		return(true);
	}
}

/// End of function 'AVP_ClientEntity_PhysicalObject::GetBoundingSphere'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_PhysicalObject::GetObjectSpaceMarkerPoint
// Author       : Andy Tate
// Date         : 17-Jan-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_ClientEntity_PhysicalObject::GetObjectSpaceMarkerPoint(const Asura_Hash_ID uMarkerPointHash,
																 Asura_Vector_3* pxPosition,
																 Asura_Matrix_3x3* pxOrientation) const
{
	const Asura_Shape_Instance&	xShapeInstance	= GetShapeInstance();
	const Asura_Animation*		pxAnimation		= xShapeInstance.GetAnimation();

	if(pxAnimation->GetMarkerLocationFromHash(uMarkerPointHash, pxPosition, pxOrientation, true))
	{
		const Asura_Animation*	pxRootAnimation		= pxAnimation;

		Asura_Quat				xInverseObjectOrientation;

		// Find the root animation...
		while(pxRootAnimation->GetParent() != NULL)
		{
			pxRootAnimation	= pxRootAnimation->GetParent();
		}

		// Calculate the inverse object orientation...
		xInverseObjectOrientation	= pxRootAnimation->GetOrientQuat();
		xInverseObjectOrientation.Invert();

		// Do we need to store the orientation?
		if(pxOrientation != NULL)
		{
			Asura_Quat	xObjectSpaceOrientation;
			Asura_Quat	xWorldSpaceOrientation;

			xWorldSpaceOrientation.FromMatrix(*pxOrientation);
			xObjectSpaceOrientation	= xWorldSpaceOrientation * xInverseObjectOrientation;
			xObjectSpaceOrientation.ToMatrix(*pxOrientation);
		}

		// Do we need to store the position?
		if(pxPosition == NULL)
		{
			return(true);
		}
		else
		{
			const Asura_Vector_3&	xObjectPosition	= pxRootAnimation->GetPosition();
			const Asura_Vector_3	xTempPosition(*pxPosition - xObjectPosition);

			*pxPosition = xInverseObjectOrientation.RotateVector(xTempPosition);
			return(true);
		}
	}
	else
	{
		return(false);
	}
}

/// End of function 'AVP_ClientEntity_PhysicalObject::GetObjectSpaceMarkerPoint'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_PhysicalObject::GetWorldSpaceMarkerPoint
// Author       : Andy Tate
// Date         : 17-Jan-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_ClientEntity_PhysicalObject::GetWorldSpaceMarkerPoint(const Asura_Hash_ID uMarkerPointHash,
																Asura_Vector_3* pxPosition,
																Asura_Matrix_3x3* pxOrientation) const
{
	const Asura_Shape_Instance&	xShapeInstance	= GetShapeInstance();
	const Asura_Animation*		pxAnimation		= xShapeInstance.GetAnimation();

	return pxAnimation != NULL ? (pxAnimation->GetMarkerLocationFromHash(uMarkerPointHash, pxPosition, pxOrientation, true)) : false;
}

/// End of function 'AVP_ClientEntity_PhysicalObject::GetWorldSpaceMarkerPoint'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_PhysicalObject::TeleportTo
// Author       : Andy Tate
// Date         : 1-Mar-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientEntity_PhysicalObject::TeleportTo(const Asura_Vector_3& xPosition,
												 const Asura_Matrix_3x3& xOrientation)
{
	if(m_pxDynamicObject != NULL)
	{
		m_pxDynamicObject->TeleportTo(xPosition);
		m_pxDynamicObject->SetOrientation(xOrientation);
	}

	PARENT_ASURA::TeleportTo(xPosition, xOrientation);
}

/// End of function 'AVP_ClientEntity_PhysicalObject::TeleportTo'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_PhysicalObject::GetCurrentPosition
// Author       : Andy Tate
// Date         : 12-Feb-2007
////////////////////////////////////////////////////////////////////////////////

const Asura_Vector_3& AVP_ClientEntity_PhysicalObject::GetCurrentPosition(void) const
{
#if defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	const bool	bNoInterpolator	= true;
#else
	const bool	bNoInterpolator	= (m_pxPosAndOriInterpolator == NULL);
#endif	// defined(ASURA_NO_NETWORK_LAG_COMPENSATION)

	// Do we need to take our data from the current move?
	if(CanCurrentlyUseMoves() && m_pxMovementSystem->IsMoveInProgress())
	{
		const Asura_Move*	pxCurrentMove	= m_pxMovementSystem->GetCurrentMove();

		if(pxCurrentMove->GetPositionUsage() == Asura_Move::POSITION_SET_DIRECTLY)
		{
			m_pxMovementSystem->GetCurrentMovePosition(m_xCurrentPosition);
			return(m_xCurrentPosition);
		}
	}

	// No move - what about our interpolator and physics object?
	if(m_pxDynamicObject == NULL)
	{
		m_xCurrentPosition	= PARENT_ASURA::GetCurrentPosition();
	}
	else if(bNoInterpolator)
	{
		m_xCurrentPosition	= m_pxDynamicObject->GetPosition();
	}
	else
	{
#if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
		// Check to see if the interpolated data was already computed this frame...
		if (m_uCurrentPositionFrameStamp != Asura_Timers::GetFrameNumber())
		{
			m_uCurrentPositionFrameStamp	= Asura_Timers::GetFrameNumber();

			m_pxPosAndOriInterpolator->GetCurrentPosition(m_xCurrentPosition, m_xCurrentVelocity);
		}
#endif	// defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	}

	// Return the position...
	return(m_xCurrentPosition);
}

/// End of function 'AVP_ClientEntity_PhysicalObject::GetCurrentPosition'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_PhysicalObject::GetCurrentOrientation
// Author       : Andy Tate
// Date         : 12-Feb-2007
////////////////////////////////////////////////////////////////////////////////

const Asura_Matrix_3x3& AVP_ClientEntity_PhysicalObject::GetCurrentOrientation(void) const
{
#if defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	const bool	bNoInterpolator	= true;
#else
	const bool	bNoInterpolator	= (m_pxPosAndOriInterpolator == NULL);
#endif	// defined(ASURA_NO_NETWORK_LAG_COMPENSATION)

	// Do we need to take our data from the current move?
	if(CanCurrentlyUseMoves() && m_pxMovementSystem->IsMoveInProgress())
	{
		const Asura_Move*	pxCurrentMove	= m_pxMovementSystem->GetCurrentMove();
		
		if(pxCurrentMove->GetOrientationUsage() == Asura_Move::ORIENTATION_SET_DIRECTLY)
		{
			m_pxMovementSystem->GetCurrentMoveOrientation(m_xCurrentOrientation);
			return(m_xCurrentOrientation);
		}
	}

	// No move - what about our interpolator and physics object?
	if(m_pxDynamicObject == NULL)
	{
		m_xCurrentOrientation	= PARENT_ASURA::GetCurrentOrientation();
	}
	else if(bNoInterpolator)
	{
		m_xCurrentOrientation	= m_pxDynamicObject->GetOrientation();
	}
	else
	{
#if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
		// Check to see if the interpolated data was already computed this frame...
		if (m_uCurrentOrientationFrameStamp != Asura_Timers::GetFrameNumber())
		{
			m_uCurrentOrientationFrameStamp	= Asura_Timers::GetFrameNumber();

			m_pxPosAndOriInterpolator->GetCurrentOrientation(m_xCurrentOrientation);
		}
#endif	// defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	}

	// Return the orientation...
	return(m_xCurrentOrientation);
}

/// End of function 'AVP_ClientEntity_PhysicalObject::GetCurrentOrientation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientEntity_PhysicalObject::GetCurrentVelocity
// Author       : Andy Tate
// Date         : 12-Feb-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientEntity_PhysicalObject::GetCurrentVelocity(Asura_Vector_3& xVelocity) const
{
#if defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	const bool	bNoInterpolator	= true;
#else
	const bool	bNoInterpolator	= (m_pxPosAndOriInterpolator == NULL);
#endif	// defined(ASURA_NO_NETWORK_LAG_COMPENSATION)

	// Do we need to take our data from the current move?
	if(CanCurrentlyUseMoves() && m_pxMovementSystem->IsMoveInProgress())
	{
		const Asura_Move*	pxCurrentMove	= m_pxMovementSystem->GetCurrentMove();

		if(pxCurrentMove->GetPositionUsage() == Asura_Move::POSITION_SET_DIRECTLY)
		{
			m_pxMovementSystem->GetCurrentMoveVelocity(xVelocity);
			return;
		}
	}

	// No move - what about our interpolator and physics object?
	if(m_pxDynamicObject == NULL)
	{
		PARENT_ASURA::GetCurrentVelocity(xVelocity);
	}
	else if(bNoInterpolator)
	{
		xVelocity	= m_pxDynamicObject->GetLinVelocity();
	}
	else
	{
#if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
		// Check to see if the interpolated data was already computed this frame...
		if (m_uCurrentPositionFrameStamp != Asura_Timers::GetFrameNumber())
		{
			m_uCurrentPositionFrameStamp = Asura_Timers::GetFrameNumber();

			m_pxPosAndOriInterpolator->GetCurrentPosition(m_xCurrentPosition, m_xCurrentVelocity);
		}
#endif	// !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)

		// Return the velocity...
		xVelocity = m_xCurrentVelocity;
	}
}

/// End of function 'AVP_ClientEntity_PhysicalObject::GetCurrentVelocity'. ///

void AVP_ClientEntity_PhysicalObject::Update()
{
	PARENT_ASURA::Update();
}

void AVP_ClientEntity_PhysicalObject::InitialiseFromBlueprint( const Asura_Blueprint& xBlueprint )
{
    PARENT_ASURA::InitialiseFromBlueprint( xBlueprint );

    Asura_Value xValue;
	if(xBlueprint.GetParameterValueByHashID(0x81F508E1, xValue))	//Asura_GetHashID("MaterialType"), xValue))
	{
		m_uMaterialType = xValue.GetHashIDValue();
	}
    if(xBlueprint.GetParameterValueByHashID(0x0563D267, xValue))	// Asura_GetHashID("IgnoreForSurfaceReorientation"), xValue))
    {
		SetIgnoreForSurfaceReorientation( xValue.GetBoolValue() );
    }

	if(xBlueprint.GetParameterValueByHashID(0xB735025C, xValue))	// Asura_GetHashID("Health"), xValue))
	{
		m_fInitialHealth = xValue.GetFloatValue();
	}


    if(xBlueprint.GetParameterValueByHashID(0x5CB97221, xValue))	// Asura_GetHashID("NoShadow"), xValue))
    {
        if(xValue.GetBoolValue())
        {
            GetShapeInstance().AddFlags(ASURA_SHAPE_INSTANCE_NO_SHADOW);
        }
        else
        {
            GetShapeInstance().RemoveFlags(ASURA_SHAPE_INSTANCE_NO_SHADOW);
        }
    }
}

void AVP_ClientEntity_PhysicalObject::SetIgnoreForSurfaceReorientation( bool bIgnore )
{
	m_bIgnoreForSurfaceReorientation = bIgnore;

	if ( m_pxDynamicObject )
	{
		if ( m_bIgnoreForSurfaceReorientation )
		{
			m_pxDynamicObject->SetPolyFlagsToReturnForActorCollision( m_pxDynamicObject->GetPolyFlagsToReturnForActorCollision() | AVP_PolygonFlag_IgnoreForReorientation );
		}
		else
		{
			m_pxDynamicObject->SetPolyFlagsToReturnForActorCollision( m_pxDynamicObject->GetPolyFlagsToReturnForActorCollision() & ~AVP_PolygonFlag_IgnoreForReorientation );
		}
	}
}

// Can the specified entity be used for surface reorientation?
bool AVP_ClientEntity_PhysicalObject::CanUseForSurfaceReorientation(Asura_Guid uGuid)
{
	// Check physical objects.
	const AVP_ClientEntity_PhysicalObject* const pxPhysicalObject = AVP_ClientEntity_PhysicalObject::FindEntity(uGuid);
	if (pxPhysicalObject)
	{
		return !pxPhysicalObject->IsIgnoredForSurfaceReorientation();
	}

	// Check containers.
	const AVP_ClientEntity_Container* const pxContainerObject = AVP_ClientEntity_Container::FindEntity(uGuid);
	if (pxContainerObject)
	{
		return !pxContainerObject->IsIgnoredForSurfaceReorientation();
	}

	return false;
}

void AVP_ClientEntity_PhysicalObject::Render()
{
	m_fForcedBoneUpdateTimer -= Asura_Timers::GetGameTimeSlice();
	if ( m_fForcedBoneUpdateTimer < 0.0f )
	{
		m_fForcedBoneUpdateTimer = Asura_Random::GetFloat( 0.5f * m_fMaxTimeBetweenBoneUpdates, m_fMaxTimeBetweenBoneUpdates );
		if ( GetShapeInstance().GetAnimation() && GetShapeInstance().GetAnimation()->GetSkin() )
		{
			GetShapeInstance().GetAnimation()->UpdateTimers( GetShapeInstance().GetAnimTimestep() );
			GetShapeInstance().GetAnimation()->UpdateBoneData( 0, GetShapeInstance().GetAnimation()->GetSkin()->NumberOfBones - 1 );
		}
	}

	// EP. Update the flame generator in render, so it will match correct position of bones. Well, at least according to Rich.
	// But this also can be moved to Update I think.
	if( m_uBurningEffectID!= ASURA_PFX_INVALID_EFFECT_ID )
	{
		AVP_PFX_Entity_Fire* pxFire = (AVP_PFX_Entity_Fire*)Asura_PFX_System::GetEffect(m_uBurningEffectID);
		if( pxFire )
		{
			const Asura_Shape_Instance& xShapeInstance = GetShapeInstance();
			pxFire->UpdatePosition( &xShapeInstance );
		}
	}
	// End of update flame

	PARENT_ASURA::Render();
}

void AVP_ClientEntity_PhysicalObject::PostCreateInitialise()
{
	if ( GetShapeInstance().GetAnimation() == NULL && GetShapeInstance().GetShape() == NULL )
	{
		SetInvisibleFlag( true );
	}

	PARENT_ASURA::PostCreateInitialise();
}

void AVP_ClientEntity_PhysicalObject::OnBeginBurning()
{
	// If there is already some fire that is going on, lets destroy it.
	AVP_PFX_Entity_Fire* pxFire = (AVP_PFX_Entity_Fire*)Asura_PFX_System::GetEffect( m_uBurningEffectID );
	if( pxFire )
	{
		pxFire->Destroy();
	}

	// Create new fire.
	m_uBurningEffectID = AVP_PFX_Entity_Fire::Create();

	pxFire = (AVP_PFX_Entity_Fire*)Asura_PFX_System::GetEffect( m_uBurningEffectID );
	if( pxFire )
	{
		unsigned int uBurningHandsPFXHash	= 0xe9f823f8;
		pxFire->AddSourceTemplate( uBurningHandsPFXHash, 0.00001f );

		// Update it first time.
		const Asura_Shape_Instance& xShapeInstance = GetShapeInstance();
		pxFire->UpdatePosition( &xShapeInstance );
	}
}

void AVP_ClientEntity_PhysicalObject::OnEndBurning()
{
	if( m_uBurningEffectID!=ASURA_PFX_INVALID_EFFECT_ID )
	{
		AVP_PFX_Entity_Fire* pxFire = (AVP_PFX_Entity_Fire*)Asura_PFX_System::GetEffect( m_uBurningEffectID );
		if( pxFire )
		{
			pxFire->SetDieOut( true );
			pxFire->Stop();
		}
	}
}

bool AVP_ClientEntity_PhysicalObject::AttachPFX(Asura_Hash_ID uMarkerID, Asura_Hash_ID uPFXID, bool bMoveWithEffect /*false*/ )
{
	u_short usPFXFlags = ASURA_PFX_EFFECT_CAMERA_FLAGS;
	if (bMoveWithEffect) usPFXFlags |= ASURA_PFX_EFFECT_MOVE_PARTICLES_WITH_EFFECT;

	Asura_Vector_3 xPosition;
	Asura_Matrix_3x3 xOrientation;

	if (!GetWorldSpaceMarkerPoint(uMarkerID, &xPosition, &xOrientation))
	{
		Asura_Assert(false, "AVP_ClientEntity_PhysicalObject::AttachPFX cant find marker.");
		return false;
	}

	Asura_Animation* pxAnim = GetShapeInstance().GetAnimation();
	if (!pxAnim)
	{
		Asura_Assert(false, "AVP_ClientEntity_PhysicalObject::AttachPFX cant find Animation");
		return false;
	}

	u_int uPFXHandle = Asura_PFX_Entity_Effect::Create(xPosition, xOrientation, uPFXID, 0, usPFXFlags);
	if (uPFXHandle == ASURA_PFX_INVALID_EFFECT_ID)
	{
		Asura_Assert(false, "AVP_ClientEntity_PhysicalObject::AttachPFX failed to create specified PFX.");
		return false;
	}

	Asura_AnimationAttachment_PFX* pxPFXAttach = new Asura_AnimationAttachment_PFX(uPFXHandle, true);
	if (!pxPFXAttach)
	{
		Asura_Assert(false, "AVP_ClientEntity_PhysicalObject::AttachPFX failed to create animation attachment pfx");

		Asura_PFX_Entity_Effect* pxEffect = Asura_PFX_System::GetEntityEffect(uPFXHandle);
		if (pxEffect) pxEffect->Destroy();

		return false;
	}

	if (!pxPFXAttach->LinkAttachment( pxAnim, uMarkerID ))
	{
		Asura_Assert(false, "AVP_ClientEntity_PhysicalObject::AttachPFX couldnt link attachment.");

		delete pxPFXAttach;

		Asura_PFX_Entity_Effect* pxEffect = Asura_PFX_System::GetEntityEffect(uPFXHandle);
		if (pxEffect) pxEffect->Destroy();

		return false;
	}

	return true;
}

/// End of file. ///
