//////////////////////////////////////////////////////////////////////////////
//
// AVP_ServerEntity_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_ServerEntity_PhysicalObject.h"

// Engine independent.
#include "Animation/Asura_AnimationAttachment.h"
#include "Blueprint/Asura_Blueprint.h"
#include "Blueprint/Asura_Blueprint_System.h"

#ifdef ASURA_USE_BOTANICALS
	#include "Botanicals/Asura_Botanical_Game_Render_Instance.h"
	#include "Botanicals/Asura_Botanical_Game_Render_Instance_Header.h"
	#include "Botanicals/Asura_Botanical_Game_Render_Manager.h"
#endif	//#ifdef ASURA_USE_BOTANICALS

#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Network/Asura_Message.h"

// Project...
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Core/AVP_Game_Core.h"
#include "Core/AVP_Settings.h"
#include "Core/AVP_SurfaceEffects.h"
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Container/AVP_ServerEntity_Container.h"
#include "Entity/Damage/AVP_EntityDamage_Messages.h"
#include "Entity/GamePlayVolume/AVP_ServerEntity_GamePlayVolume.h"
#include "Entity/PhysicalObject/AVP_ClientEntity_PhysicalObject.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Alien/AVP_ServerActor_Alien.h"
#include "Entity/PhysicalObject/Actor/AVP_Actor_Messages.h"
#include "Mesh/AVP_PolygonFlags.h"
#include "Network/AVP_Client.h"
#include "SpecialFX/PFX/Asura_PFX_Entity_Effect.h"

#include "Tactical/AVP_Tactical_System.h"

// EP - 06 March 2009 - VERSION 9. I have added flag IsFlammable for physical objects. (So this flag can be read from the disc, but later on it can be overridden by blueprint. It depends if the blueprint exists for this physical object or not.)
static const u_int s_uCURRENT_CHUNK_VERSION = 9;

// Old version data... now obsolete
union ChunkVersion1To4Flags
{
	u_int		m_uFlags;

	struct
	{
		u_int	m_bActive							: 1,
				m_bResetNoCollFlagOnUnhide			: 1,
				m_bIgnoreForSurfaceReorientation	: 1,
													: 0;
	};
};

// Old version data... now obsolete
union ChunkVersion0Flags
{
	u_int	m_uFlags;

	struct
	{
		u_int	bResetNoCollFlagOnUnhide	: 1,
											: 0;
	};
};

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

//
// AVP_ServerEntity_PhysicalObject
//

Asura_Hashed_List<AVP_ServerEntity_PhysicalObject> AVP_ServerEntity_PhysicalObject::s_xList(5);


AVP_ServerEntity_PhysicalObject::AVP_ServerEntity_PhysicalObject(void)
: Asura_Entity_Server(ASURA_GUID_UNREGISTERED) // Ignored.
, m_xLinVelocity(Asura_Maths::ZeroVector3)
, m_xPosition(Asura_Maths::ZeroVector3)
, m_xOrientation(Asura_Maths::IdentityMatrix3x3)
, m_uOwnerGuid(ASURA_GUID_UNREGISTERED)
#ifdef ASURA_MAX_NO_OF_STORED_POSITIONS
, m_uNumberOfStoredPositions(0)
#endif // #ifdef ASURA_MAX_NO_OF_STORED_POSITIONS
, m_uFlags(0)
, m_uMaterialType(ASURA_HASH_ID_UNSET)
, m_fInitialHealth(0.0f)
, m_uNumOnHealthEvents(0)
, m_pxOnHealthEvents(NULL)
, m_uCurrentGamePlayVolumeID(ASURA_GUID_UNREGISTERED)
, m_xFlammableHandler(NULL)
{
	m_bIgnoreForSurfaceReorientation = true;
	m_bIsFlammable = false;


	SetBlueprintTypeHashID( uAVP_BLUEPRINT_TYPE_PHYSICAL_OBJECTS );

#ifdef ASURA_MAX_NO_OF_STORED_POSITIONS
	for (u_int u = 0; u < ASURA_MAX_NO_OF_STORED_POSITIONS; u++)
	{
		m_afTimeStampsForStoredPositions[u] = 0.0f; 
		m_axStoredPositions[u].Zero(); 
	}
#endif // #ifdef ASURA_MAX_NO_OF_STORED_POSITIONS

    s_xList.AddToList( this );
}


AVP_ServerEntity_PhysicalObject::~AVP_ServerEntity_PhysicalObject()
{
	delete [] m_pxOnHealthEvents;
	m_pxOnHealthEvents = NULL;

	if( m_xFlammableHandler )
	{
		delete m_xFlammableHandler;
		m_xFlammableHandler = NULL;
	}

    s_xList.RemoveFromList( this );
}

void AVP_ServerEntity_PhysicalObject::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
    xStream << s_uCURRENT_CHUNK_VERSION;

    PARENT_ASURA::WriteToChunkStream( xStream );

    // Flags.
    {
		Asura_WriteToStream_Bitfield_Wrapper<u_int>	xBitfield(m_uFlags);

		// Generate the bitfield...
		xBitfield.Add(m_bResetNoCollFlagOnUnhide,	1);
		xBitfield.Add(m_bIgnoreForSurfaceReorientation,	1);
		xBitfield.Add(m_bIsFlammable, 1);

        xStream << xBitfield;
    }

	// On health percent events.
	// this is not and never worked
	xStream << 0;
// 	xStream << m_uNumOnHealthEvents;
// 	for ( u_int u = 0; u < m_uNumOnHealthEvents; ++u )
// 	{
// 		xStream << m_pxOnHealthEvents[ u ].m_iEventID;
// 		xStream << m_pxOnHealthEvents[ u ].m_fHealthFraction;
// 	}

	xStream << m_uMaterialType;
}

void AVP_ServerEntity_PhysicalObject::ReadFromChunkStream( Asura_Chunk_Stream& xStream ) 
{
	u_int uVersion;
	xStream >> uVersion;

    PARENT_ASURA::ReadFromChunkStream( xStream );
	
	if (uVersion == 9)
	{
		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xBitfield( m_uFlags );
		xStream >> xBitfield;
		SetIgnoreForSurfaceReorientation( m_bIgnoreForSurfaceReorientation );
		SetAsFlammable( m_bIsFlammable );

		xStream >> m_uNumOnHealthEvents;
		if ( m_uNumOnHealthEvents )
		{
			m_pxOnHealthEvents = new EventIDAndHealth[ m_uNumOnHealthEvents ];
			for ( u_int u = 0; u < m_uNumOnHealthEvents; ++u )
			{
				xStream >> m_pxOnHealthEvents[ u ].m_iEventID;
				xStream >> m_pxOnHealthEvents[ u ].m_fHealthFraction;
			}
		}

		xStream >> m_uMaterialType;
	}
	else if (uVersion == 8)
	{
		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xBitfield( m_uFlags );
		xStream >> xBitfield;
		SetIgnoreForSurfaceReorientation( m_bIgnoreForSurfaceReorientation );

		xStream >> m_uNumOnHealthEvents;
		if ( m_uNumOnHealthEvents )
		{
			m_pxOnHealthEvents = new EventIDAndHealth[ m_uNumOnHealthEvents ];
			for ( u_int u = 0; u < m_uNumOnHealthEvents; ++u )
			{
				xStream >> m_pxOnHealthEvents[ u ].m_iEventID;
				xStream >> m_pxOnHealthEvents[ u ].m_fHealthFraction;
			}
		}

		xStream >> m_uMaterialType;
	}
	else if (uVersion == 7)
	{
		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xBitfield( m_uFlags );
		xStream >> xBitfield;
		SetIgnoreForSurfaceReorientation( m_bIgnoreForSurfaceReorientation );
		
		xStream >> m_uNumOnHealthEvents;
		if ( m_uNumOnHealthEvents )
		{
			m_pxOnHealthEvents = new EventIDAndHealth[ m_uNumOnHealthEvents ];
			for ( u_int u = 0; u < m_uNumOnHealthEvents; ++u )
			{
				xStream >> m_pxOnHealthEvents[ u ].m_iEventID;
				xStream >> m_pxOnHealthEvents[ u ].m_fHealthFraction;
			}
		}
	}
	else if ( uVersion == 6 )
	{
		Asura_ReadFromStream_Bitfield_Wrapper< u_int > xBitfield( m_uFlags );
		xStream >> xBitfield;
		m_bIgnoreForSurfaceReorientation = true;
		SetIgnoreForSurfaceReorientation( m_bIgnoreForSurfaceReorientation );

		xStream >> m_uNumOnHealthEvents;
		if ( m_uNumOnHealthEvents )
		{
			m_pxOnHealthEvents = new EventIDAndHealth[ m_uNumOnHealthEvents ];
			for ( u_int u = 0; u < m_uNumOnHealthEvents; ++u )
			{
				xStream >> m_pxOnHealthEvents[ u ].m_iEventID;
				xStream >> m_pxOnHealthEvents[ u ].m_fHealthFraction;
			}
		}
	}
	else if ( uVersion == 5 )
    {
        Asura_ReadFromStream_Bitfield_Wrapper< u_int > xBitfield( m_uFlags );
        xStream >> xBitfield;
    }
    else if ( uVersion == 4 )
    {
        ChunkVersion1To4Flags xChunkVersion1To4Flags;
        Asura_ReadFromStream_Bitfield_Wrapper<u_int> xBitfield( xChunkVersion1To4Flags.m_uFlags );
        xStream >> xBitfield;
        m_bResetNoCollFlagOnUnhide = xChunkVersion1To4Flags.m_bResetNoCollFlagOnUnhide;
    }
	else if(uVersion == 3)
	{
        ChunkVersion1To4Flags xChunkVersion1To4Flags;
        Asura_ReadFromStream_Bitfield_Wrapper<u_int> xBitfield( xChunkVersion1To4Flags.m_uFlags );
        xStream >> xBitfield;
        m_bResetNoCollFlagOnUnhide = xChunkVersion1To4Flags.m_bResetNoCollFlagOnUnhide;

        Asura_Hash_ID uBlueprintHashID;
		xStream >> uBlueprintHashID;
        SetBlueprintHashID( uBlueprintHashID );

		Asura_Assertf((uBlueprintHashID != ASURA_HASH_ID_UNSET), ("Uninitialised blueprint for entity %d.", GetGuid()));
	}
#ifdef ASURA_TOOLS
	else if(uVersion == 2)
	{
        ChunkVersion1To4Flags xChunkVersion1To4Flags;
        Asura_ReadFromStream_Bitfield_Wrapper<u_int> xBitfield( xChunkVersion1To4Flags.m_uFlags );
        xStream >> xBitfield;
        m_bResetNoCollFlagOnUnhide = xChunkVersion1To4Flags.m_bResetNoCollFlagOnUnhide;

        u_int uBlueprintType;
		xStream >> uBlueprintType;
	}
	else if(uVersion == 1)
	{
        ChunkVersion1To4Flags xChunkVersion1To4Flags;
		Asura_ReadFromStream_Bitfield_Wrapper<u_int> xBitfield( xChunkVersion1To4Flags.m_uFlags );
		xStream >> xBitfield;
        m_bResetNoCollFlagOnUnhide = xChunkVersion1To4Flags.m_bResetNoCollFlagOnUnhide;
	}
	else if(uVersion == 0)
	{
		ChunkVersion0Flags	xChunkVersion0Flags;

		xStream >> xChunkVersion0Flags.m_uFlags;

		m_uFlags				   = 0;
		m_bResetNoCollFlagOnUnhide = xChunkVersion0Flags.bResetNoCollFlagOnUnhide; 
	}
#endif
	else
	{
		Asura_Assert(false, "Invalid chunk version specified.");
	}

	Asura_Assertf(m_pxOnHealthEvents == 0, ("Guid %d has wrong On Health Percent event set ", GetGuid()));
}

void AVP_ServerEntity_PhysicalObject::OnEntityActivate()
{
    PARENT_ASURA::OnEntityActivate();

    // We may not want our physics object to be active under certain circumstances.
    if(m_pxDynamicObject != NULL)
    {
        const bool	bNotCollidable	= HasNoCollisionsFlag();
        const bool	bMergedWithEnv	= IsMergedWithEnvironment();

        m_pxDynamicObject->SetIgnore(bNotCollidable);

        if (!bNotCollidable)
        {
            m_pxDynamicObject->SetIgnore(bMergedWithEnv);
        }
    }
}

void AVP_ServerEntity_PhysicalObject::OnEntityDeactivate()
{
    PARENT_ASURA::OnEntityDeactivate();

    // We may not want our physics object to be active under certain circumstances.
    if(m_pxDynamicObject != NULL)
    {
        const bool	bNotCollidable	= HasNoCollisionsFlag();
        const bool	bMergedWithEnv	= IsMergedWithEnvironment();

        m_pxDynamicObject->SetIgnore(!bNotCollidable);

        if (bNotCollidable)
        {
            m_pxDynamicObject->SetIgnore(!bMergedWithEnv);
        }
    }
}

void AVP_ServerEntity_PhysicalObject::OnEntityActivateMessage()
{
    PARENT_ASURA::OnEntityActivateMessage();

    //## NJD - This is here because I assumed we don't want to send a client update message when objects
    //##       are set as active from their chunk loads, only when they receive an activate message.
    AVP_ClientEntity_PhysicalObject* pxClient = AVP_ClientEntity_PhysicalObject::FindEntity(GetGuid());
    if(!pxClient)
    {
        MakeClientUpdateMessage();
    }
}

void AVP_ServerEntity_PhysicalObject::Update()
{
	PARENT_ASURA::Update();

	// Only flammable objects have handlers. So if this handler is set to NULL, then this object can't be damaged by fire.
	if( m_xFlammableHandler )
	{
		// If the object is burning, then update handler and return the amount of damage we need to apply to this physical object.
		float fActualBurningDamage = m_xFlammableHandler->Update( GetMaxHealth(), GetGuid() );
		if( fActualBurningDamage>0.0f )
		{
			AVP_EntityDamage_RequestVictim xDamage( uAVP_BLUEPRINT_NAME_DAMAGE_BURN, GetGuid(), 0x0000000, GetGuid(), fActualBurningDamage,
				Asura_Vector_3(0.0f,0.0f,0.0f), Asura_Vector_3(0.0f,1.0f,0.0f), Asura_Vector_3(0.0f,1.0f,0.0f) ) ;
			TakeDamageExtended( xDamage, false, false );
		}
	}
}

void AVP_ServerEntity_PhysicalObject::TakeDamageExtended(const AVP_EntityDamage_RequestVictim& xDamage,
														 const bool bForceFatal, const bool bSendMessageToClient)
{
	// If the physical object is not active, do not send a damage message to the client.
	if(IsEntityActive())
	{
		const float		fImpulseStrength	= xDamage.GetImpulseStrength();

		Asura_Vector_3	xDirection;
		Asura_Vector_3	xNormal;
		Asura_Vector_3	xPointOfImpact;

		xDamage.GetDirection(xDirection);
		xDamage.GetNormal(xNormal);
		xDamage.GetPointOfImpact(xPointOfImpact);
		xDirection.Normalise();

		{
			u_int 				uDestroyedSkinElements =0;
			u_int 				uGibbedSkinElements =0;

			float				fAdjustedDamage	= CalculateAndUpdateLocationSpecificDamage(xDamage, uDestroyedSkinElements, uGibbedSkinElements, bForceFatal);
			const Asura_Hash_ID uDamageTypeHash	= xDamage.GetTypeHash();
			const Asura_Guid	uVictimGuid		= GetGuid();
			const float			fOldHealth		= GetHealth();
			float				fNewHealth		= fOldHealth;
			bool				bSendMessage	= true;
			bool				bRandomiseDirection = false;
			// Do we need to damage this entity?
			if(IsDamageable())
			{
				bool bIsFatal = (bForceFatal || (fOldHealth <= fAdjustedDamage));

				const AVP_DamageMultiplier*	pxDamageMultiplier	= m_xDamageMultiplierContainer.GetDataByHash(uDamageTypeHash);

				if (AVP_Game_Core::IsLocalPlayer(uVictimGuid) && AVP_Settings::GetGodMode())
				{
					if (bIsFatal)
					{
						// We're the player in God-mode but that doesn't mean that we can NEVER be killed, an
						// instant-death damage-volume will kill us, otherwise we set our health to the bare
						// minimum where it can be to still get the appropriate HUD effects for being damaged...
						fAdjustedDamage	= ((uDamageTypeHash == uAVP_BLUEPRINT_NAME_DAMAGE_VOLUME) && (fAdjustedDamage == Asura_Maths::LargeFloat))
										? fOldHealth
										: (fOldHealth - 0.1f);
					}
				}
				else
				{
					// We're either not the player OR we're not in God-mode, apply the damage...
					if (bIsFatal)
					{
						fAdjustedDamage	= fOldHealth;
					}
				}

				// Do we have a damage-multiplier to use?
				if(pxDamageMultiplier != NULL)
				{
					fAdjustedDamage	= pxDamageMultiplier->Apply(fAdjustedDamage);
				}
			
				// Better recalculate this, looks like it could get multiplied above..
				bIsFatal = bForceFatal || (fOldHealth <= fAdjustedDamage);
				// If we look like we are human and going to get hit by enough 'plasma' damage to kill us,
				// we want to pretend this was enough to get our legs, arms and head blown off.
				if ( ExplodesOnFatalPlasmaDamage() && bIsFatal &&  
					(xDamage.GetTypeHash() == uAVP_BLUEPRINT_NAME_DAMAGE_PLASMA
					|| xDamage.GetTypeHash() == uAVP_BLUEPRINT_NAME_DAMAGE_PLASMA_DISRUPTION
					|| xDamage.GetTypeHash() == uAVP_BLUEPRINT_NAME_DAMAGE_PLASMA_IMPACT)
					)
				{
					uDestroyedSkinElements |= 	(1 << AVP_SET_RIGHT_LEG);
					uDestroyedSkinElements |= 	(1 << AVP_SET_LEFT_LEG);
					uDestroyedSkinElements |= 	(1 << AVP_SET_RIGHT_ARM);
					uDestroyedSkinElements |= 	(1 << AVP_SET_LEFT_ARM);
					uDestroyedSkinElements |= 	(1 << AVP_SET_HEAD);

					uDestroyedSkinElements |= 	(1 << AVP_SET_ALIEN_PIPES_LEFT);
					uDestroyedSkinElements |= 	(1 << AVP_SET_ALIEN_PIPES_RIGHT);
					uDestroyedSkinElements |= 	(1 << AVP_SET_ALIEN_TAIL);

					uGibbedSkinElements |= 	(1 << AVP_SET_RIGHT_LEG);
					uGibbedSkinElements |= 	(1 << AVP_SET_LEFT_LEG);
					uGibbedSkinElements |= 	(1 << AVP_SET_RIGHT_ARM);
					uGibbedSkinElements |= 	(1 << AVP_SET_LEFT_ARM);
					uGibbedSkinElements |= 	(1 << AVP_SET_HEAD);
					uGibbedSkinElements |= 	(1 << AVP_SET_HEAD);

					uGibbedSkinElements |= 	(1 << AVP_SET_ALIEN_PIPES_LEFT);
					uGibbedSkinElements |= 	(1 << AVP_SET_ALIEN_PIPES_RIGHT);
					uGibbedSkinElements |= 	(1 << AVP_SET_ALIEN_TAIL);

					bRandomiseDirection = true;
				}

				// Update the actual health...
				if(fAdjustedDamage > 0.0f)
				{
					TakeDamage(fAdjustedDamage);
					fNewHealth		= GetHealth();
				}
				else
				{
					bSendMessage = false;

					const Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_DAMAGE, xDamage.GetTypeHash() );
					if ( pxBlueprint )
					{
						Asura_Value xVal;
						if ( pxBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_DISRUPT_EQUIPMENT, xVal ) )
						{
							if ( xVal.GetBoolValue() )
							{
								bSendMessage = true; // If we do disruption, send message anyway.
							}
						}
					}
				}
			}

			// Do we need to set it on fire?
			if( IsFlammable() && m_xFlammableHandler )
			{
				if( xDamage.GetTypeHash()==uAVP_BLUEPRINT_NAME_DAMAGE_FLAME )
				{
					m_xFlammableHandler->AddReceivedFireDamage( fAdjustedDamage );
				}
			}

			// Send the damage message and use this to handle any events...
			if(bSendMessage)
			{
				const int								iBoneID				= xDamage.GetBoneID();
				const Asura_Guid						uAttackerGuid		= xDamage.GetFromGuid();
				const Asura_Guid						uAttackerParentGuid = xDamage.GetParentGuid();
				bool									bStealth			= xDamage.GetStealth(); 

				const AVP_EntityDamage_ApplyToEntity	xNewDamage(uDamageTypeHash,
																   uAttackerGuid,
																   xPointOfImpact,
																   xDirection,
																   xNormal,
																   uVictimGuid,
																   fNewHealth,
																   xDamage.GetAmount(),
																   fImpulseStrength,
																   iBoneID,
																   uDestroyedSkinElements,
																   uGibbedSkinElements,
																   uAttackerParentGuid,
																   -1,
																   ASURA_HASH_ID_UNSET,
																   bStealth,
																   bRandomiseDirection);

				SendDamageMessageToClient(xNewDamage);

				if(!IsADeadBody())
				{
					// Call the appropriate events...
					if((fOldHealth > 0.0f) && (fNewHealth <= 0.0f))
					{
						OnDeath(xNewDamage);
					}
					else
					{
						OnApplyDamage(xNewDamage);
					}
				}
				else
				{
					if( m_pxDynamicObject && m_pxDynamicObject->IsAsleep() )
					{
						m_pxDynamicObject->WakeUp();
					}
					OnApplyDamage(xNewDamage);
				}
			}
		}

		// Apply the damage impulse
		if(m_pxDynamicObject)
		{
			const Asura_Vector_3	xImpulse(xDirection * fImpulseStrength);

			m_pxDynamicObject->ApplyImpulse(xImpulse, xPointOfImpact, false);
		}
	}
}

void AVP_ServerEntity_PhysicalObject::OnDeath(const AVP_EntityDamage_ApplyToEntity& xDamage)
{
	// Call the base-function...
	PARENT_ASURA::MakeDead();

	// Our physical object might get destroyed soon so make sure our position/ori is up to date.
	if ( m_pxDynamicObject )
	{
		m_xPosition = m_pxDynamicObject->GetPosition();
		m_xOrientation = m_pxDynamicObject->GetOrientation();
	}
}

void AVP_ServerEntity_PhysicalObject::SendDamageMessageToClient(const AVP_EntityDamage_ApplyToEntity& xDamage)
{
	// Dismember damage is handled through its own specific message, so don't forward that to the client.
	if (xDamage.GetTypeHash() == uAVP_BLUEPRINT_NAME_DAMAGE_DISMEMBER) { return; }

	Asura_Message*	pxMessage	= Asura_Network::CreateMessage(AVP_MSGID_ENTITYDAMAGE_APPLY_TO_ENTITY, sizeof(AVP_Message_EntityDamage_ApplyToEntity));

	if(pxMessage != NULL)
	{
		AVP_Message_EntityDamage_ApplyToEntity*	pxData		= static_cast<AVP_Message_EntityDamage_ApplyToEntity*>(pxMessage->GetData());

		// Inform the client regardless of whether we can be damaged...
		pxData->SetDamage(xDamage);

		pxMessage->SetFromGuid( GetGuid() );
		pxMessage->SetToGuid( ASURA_GUID_MY_CLIENT_ENTITIES );
		Asura_Network::Send( pxMessage );
	}
}

float AVP_ServerEntity_PhysicalObject::CalculateAndUpdateLocationSpecificDamage(const AVP_EntityDamage_RequestVictim& xDamage, u_int& uDestroyedSkinElements, u_int& uGibbedSkinElements, bool bForceFatal)
{
	uDestroyedSkinElements = 0;
	uGibbedSkinElements = 0;
	return xDamage.GetAmount();
}


bool AVP_ServerEntity_PhysicalObject::HandleMessage( const Asura_Message* pxMsg )
{
    switch( pxMsg->GetMsgID() )
    {
		case AVP_MSGID_DESTROY_OCCUPIER:
		{
			Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_DESTRUCT );

			Send( pxMessage, GetGuid() );

			return true;
			
		}

		case AVP_MSGID_DEACTIVATE_OCCUPIER:
		{
			Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_DEACTIVATE );

			Send( pxMessage, GetGuid() );

			return true;

		}

		case AVP_MSGID_SET_HEALTH:
		{
			if ( GetHealth() <= 0.0f )
			{
				return true;
			}

			const AVP_Message_SetHealth* pxData = static_cast< const AVP_Message_SetHealth* >( pxMsg->GetData() );

			if ( pxData )
			{
				bool	bIsDamage				= (pxData->m_bTreatAsDamage == 0) ? false : true;
				bool	bMethodIsDirect			= (pxData->m_uSetHealthAsPercentageOfMaxHealth == 0 ) ? true : false;
				float	fDirectAmount			= pxData->m_fDirectHealthValue;
				float	fPercentageOfMaxAmount	= pxData->m_fPercentageHealthValue;
				bool	bIsActor				= false;


				if ( GetClassification() == AVP_ENTITYCLASS_PREDATOR ||
					GetClassification() == AVP_ENTITYCLASS_ALIEN ||
					GetClassification() == AVP_ENTITYCLASS_HUMAN ||
					GetClassification() == AVP_ENTITYCLASS_ALIEN_HYBRID ||
					GetClassification() == AVP_ENTITYCLASS_ALIENCHESTBURSTER ||
					GetClassification() == AVP_ENTITYCLASS_ALIENFACEHUGGER ||
					GetClassification() == AVP_ENTITYCLASS_ALIENQUEEN ||
					GetClassification() == AVP_ENTITYCLASS_MARINE ||
					GetClassification() == AVP_ENTITYCLASS_ANDROID ||
					GetClassification() == AVP_ENTITYCLASS_CIVILIAN ||
					GetClassification() == AVP_ENTITYCLASS_PREDALIEN )
				{
					bIsActor = true;
				}


				//---------------------------------------------
				if ( bMethodIsDirect )
				{
					if ( !bIsDamage && fDirectAmount > 0.0f )
					{
						SetHealth( fDirectAmount );
						SetMaxHealth( fDirectAmount );

						if ( bIsActor )
						{
							// we setting a health value above our current health
							AVP_ServerEntity_Actor* pxActor = static_cast<AVP_ServerEntity_Actor*>(this);

							if ( pxActor )
							{
								pxActor->SetNumActiveHealthBlocks();
							}
						}
						return true;
					}
					else
					{
						// setting to 0 or is damage
						bIsDamage = true;
					}
				}
				//---------------------------------------------
				else
				{
					if ( !bIsDamage && fPercentageOfMaxAmount > 0.0f )
					{
						Asura_Blueprint* pxOurBp = Asura_Blueprint_System::GetBlueprintByHashID( GetBlueprintTypeHashID(), GetBlueprintHashID() );
						float fOriginalHealth = -1.0f;

						if ( pxOurBp )
						{
							Asura_Value xValue;

							if ( pxOurBp->GetParameterValueByHashID( 0xb735025c /*"Health"*/, xValue ) )
							{
								fOriginalHealth = xValue.GetFloatValue();
							}
						}

						if ( fOriginalHealth != -1.0f )
						{
							// we setting a health value above our current health
							SetHealth( fOriginalHealth * ( fPercentageOfMaxAmount * 0.01f ) );
							SetMaxHealth( fOriginalHealth * ( fPercentageOfMaxAmount * 0.01f ) );
							if ( bIsActor )
							{
								AVP_ServerEntity_Actor* pxActor = static_cast<AVP_ServerEntity_Actor*>(this);

								if ( pxActor )
								{
									pxActor->SetNumActiveHealthBlocks();
								}
							}
							return true;
						}
					}
					else
					{
						// setting to 0 or is damage
						bIsDamage = true;
					}
				}

				//---------------------------------------------
				// take set health as damage
				//---------------------------------------------
				if ( bIsDamage )
				{
					Asura_Hash_ID uDamageType = pxData->m_uDamageType;

					if ( pxData->m_bTreatAsDamage == 0 )
					{
						uDamageType = ASURA_HASH_ID_UNSET;
					}
					else
					{
						uDamageType = pxData->m_uDamageType;
					}

					float			fRemainingHealth = 0.0f;
					float			fDamageAmount = 0.0f;
					Asura_Vector_3	xNorm(0.0f, -1.0f, 0.0f);

					if ( bMethodIsDirect )
					{
						if ( fDirectAmount > GetHealth() )
						{
							// we setting a health value above our current health
							SetHealth( fDirectAmount );
							SetMaxHealth( fDirectAmount );
							if ( bIsActor )
							{
								AVP_ServerEntity_Actor* pxActor = static_cast<AVP_ServerEntity_Actor*>(this);

								if ( pxActor )
								{
									pxActor->SetNumActiveHealthBlocks();
								}
							}
							return true;
						}
						else
						{
							fRemainingHealth = fDirectAmount;
							fDamageAmount = GetHealth() - fRemainingHealth;
						}
					}
					else
					{
						// Get the blueprint to find out the original max health
						Asura_Blueprint* pxOurBp = Asura_Blueprint_System::GetBlueprintByHashID( GetBlueprintTypeHashID(), GetBlueprintHashID() );
						float fOriginalHealth = -1.0f;

						if ( pxOurBp )
						{
							Asura_Value xValue;

							// find the starting health parameter
							if ( pxOurBp->GetParameterValueByHashID( 0xb735025c /*"Health"*/, xValue ) )
							{
								fOriginalHealth = xValue.GetFloatValue();
								float fHealthAfter = fOriginalHealth * ( fPercentageOfMaxAmount * 0.01f );

								if ( fHealthAfter < GetHealth() )
								{
									fRemainingHealth = fHealthAfter;
									fDamageAmount = GetHealth() - fRemainingHealth;
								}
								else
								{
									// we setting a health value above our current health
									SetHealth( fHealthAfter );
									SetMaxHealth( fHealthAfter );
									if ( bIsActor )
									{
										AVP_ServerEntity_Actor* pxActor = static_cast<AVP_ServerEntity_Actor*>(this);

										if ( pxActor )
										{
											pxActor->SetNumActiveHealthBlocks();
										}
									}
									return true;
								}
							}
						}
					}

					
					Asura_Vector_3 xDirection( Asura_Maths::ZeroVector3 );
					Asura_Guid uFromGuid = ASURA_GUID_UNREGISTERED;

					// Does the damage come from an entity? if so get the direction from that entity
					if ( pxData->m_uDamageFromGuid != ASURA_GUID_UNREGISTERED )
					{
						AVP_ServerEntity_PhysicalObject* pxPhysicalObject = AVP_ServerEntity_PhysicalObject::FindEntity( pxData->m_uDamageFromGuid );

						if ( pxPhysicalObject )
						{
							uFromGuid = pxPhysicalObject->GetGuid();
							xDirection = GetPosition() - pxPhysicalObject->GetPosition();
						}
					}

					// Lets  grab the chest position if there is one otherwise just use the entities position
					Asura_Vector_3 xPointOfImpact( GetPosition() );
					GetWorldSpaceMarkerPoint( 0xe5a85d03 /*"target_1"*/, &xPointOfImpact, NULL );
					const AVP_EntityDamage_RequestVictim xDamage( uDamageType, uFromGuid, ASURA_HASH_ID_UNSET, GetGuid(), fDamageAmount, xPointOfImpact, xDirection, xNorm, 1.0f );
					TakeDamageExtended( xDamage, false );
				}
			}

			return true;
		}

        case ASURA_MSG_HIDE:
        {
            PARENT_ASURA::HandleMessage( pxMsg );

            // No collisions if invisible.
            m_bResetNoCollFlagOnUnhide = !IsPhysicsObjectIgnored();
            SetNoCollisionsFlag( true );

            return true;
        }

        case ASURA_MSG_UNHIDE:
        {
            PARENT_ASURA::HandleMessage( pxMsg );

            // Restore collisions, if necessary.
            if( m_bResetNoCollFlagOnUnhide )
            {
                SetNoCollisionsFlag( false );
            }               

            return true;
        }

		case AVP_MSGID_ENTITYDAMAGE_APPLYRADIAL_TO_BOTANICALS:
		case AVP_MSGID_ENTITYDAMAGE_APPLYLINEAR_TO_BOTANICALS:
		case AVP_MSGID_ENTITYDAMAGE_APPLY_TO_ENTITY:
		case AVP_MSGID_ENTITYDAMAGE_APPLY_TO_ENVIRONMENT:
		{
			Asura_Network::Forward(pxMsg, ASURA_GUID_MY_CLIENT_ENTITIES, GetGuid());
			return(true);
		}

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

			AVP_EntityDamage_RequestLinear					xDamage;

			pxData->GetDamage(xDamage);
			xDamage.Apply(*this);
			return(true);
		}

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

			AVP_EntityDamage_RequestRadial					xDamage;

			pxData->GetDamage(xDamage);
			xDamage.Apply(*this);
			return(true);
		}

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

			AVP_EntityDamage_RequestVolume					xDamage;

			pxData->GetDamage(xDamage);
			xDamage.Apply(*this);
			return(true);
		}

		case AVP_MSGID_ENTITYDAMAGE_REQUEST_VICTIM:
		{
			const AVP_Message_EntityDamage_RequestVictim*	pxData	= static_cast<const AVP_Message_EntityDamage_RequestVictim*>(pxMsg->GetData());
			AVP_EntityDamage_RequestVictim					xDamage;
			
			pxData->GetDamage(xDamage);
			xDamage.Apply();
			return(true);
		}

		// We want default handling of these messages here, as only actors will do anything with this but more than that can be a target.
		case AVP_MSGID_INCOMING_MELEE_STARTED:
		case AVP_MSGID_INCOMING_MELEE_FINISHED:
			return true;

        default:
		{
			return PARENT_ASURA::HandleMessage( pxMsg );
		}
    }		// End of switch.
}



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

bool AVP_ServerEntity_PhysicalObject::GetBoundingSphere(Asura_Vector_3& xBSCentre,
														 float& fBSRadius) const
{
	const AVP_ClientEntity_PhysicalObject*	pxClient	= AVP_ClientEntity_PhysicalObject::FindEntity(GetGuid());
	const Asura_Animation*					pxAnimation	= (pxClient == NULL)
														? NULL
														: pxClient->GetShapeInstance().GetAnimation();

	if(pxAnimation == NULL)
	{
		const Asura_Bounding_Box*	pxBB	= GetBB();

		// Take our bounding-box...
		if(pxBB == NULL)
		{
			return(false);
		}
		else
		{
			const Asura_Vector_3&	xPosition		= GetPosition();
			const Asura_Matrix_3x3&	xOrientation	= GetOrientation();

			fBSRadius	= pxBB->CalculateRadius();
			xBSCentre	= (pxBB->CalculateCentre() * xOrientation) + xPosition;
			return(true);
		}
	}
	else
	{
		xBSCentre	= pxAnimation->GetBoundingSphereCentre();
		fBSRadius	= pxAnimation->GetCoarseBoundingSphereRadius();
		return(true);
	}
}

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

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ServerEntity_PhysicalObject::GetObjectSpaceMarkerPoint
// Author       : Andy Tate
// Date         : 18-Jan-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_ServerEntity_PhysicalObject::GetObjectSpaceMarkerPoint(const Asura_Hash_ID uMarkerPointHash,
																 Asura_Vector_3 *pxPosition,
																 Asura_Matrix_3x3 *pxOrientation) const
{
	const AVP_ClientEntity_PhysicalObject*	pxClient	= AVP_ClientEntity_PhysicalObject::FindEntity(GetGuid());

	return(pxClient != NULL && pxClient->GetObjectSpaceMarkerPoint(uMarkerPointHash, pxPosition, pxOrientation));
}

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



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ServerEntity_PhysicalObject::GetWorldSpaceMarkerPoint
// Author       : Andy Tate
// Date         : 18-Jan-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_ServerEntity_PhysicalObject::GetWorldSpaceMarkerPoint(const Asura_Hash_ID uMarkerPointHash,
																Asura_Vector_3 *pxPosition,
																Asura_Matrix_3x3 *pxOrientation) const
{
	const AVP_ClientEntity_PhysicalObject*	pxClient	= AVP_ClientEntity_PhysicalObject::FindEntity(GetGuid());

	return(pxClient != NULL && pxClient->GetWorldSpaceMarkerPoint(uMarkerPointHash, pxPosition, pxOrientation));
}

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


void AVP_ServerEntity_PhysicalObject::GetTargetPosition( Asura_Vector_3& xPosition ) const
{
	xPosition = GetPosition();
}


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

void AVP_ServerEntity_PhysicalObject::TeleportTo(const Asura_Vector_3& xPosition,
												 const Asura_Matrix_3x3& xOrientation,
												 const bool bInformClients)
{
	PARENT_ASURA::TeleportTo(xPosition, xOrientation, bInformClients);

	m_xPosition		= xPosition;
	m_xOrientation	= xOrientation;
}

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




////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ServerEntity_PhysicalObject::SetPosition
// Author       : Andy Tate
// Date         : 22-Feb-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ServerEntity_PhysicalObject::SetPosition(const Asura_Vector_3& xPosition)
{
	PARENT_ASURA::SetPosition(xPosition);

	m_xPosition = xPosition;
}

/// End of function 'AVP_ServerEntity_PhysicalObject::SetPosition'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ServerEntity_PhysicalObject::GetPosition
// Author       : Andy Tate
// Date         : 22-Feb-2007
////////////////////////////////////////////////////////////////////////////////

const Asura_Vector_3& AVP_ServerEntity_PhysicalObject::GetPosition(void) const
{
	if(m_pxDynamicObject == NULL)
	{
		return(m_xPosition);
	}
	else
	{
		return(m_pxDynamicObject->GetPosition());
	}
}

/// End of function 'AVP_ServerEntity_PhysicalObject::GetPosition'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ServerEntity_PhysicalObject::SetOrientation
// Author       : Andy Tate
// Date         : 22-Feb-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ServerEntity_PhysicalObject::SetOrientation(const Asura_Matrix_3x3& xOrientation)
{
	PARENT_ASURA::SetOrientation(xOrientation);

	m_xOrientation = xOrientation;
}

/// End of function 'AVP_ServerEntity_PhysicalObject::SetOrientation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ServerEntity_PhysicalObject::GetOrientation
// Author       : Andy Tate
// Date         : 22-Feb-2007
////////////////////////////////////////////////////////////////////////////////

const Asura_Matrix_3x3& AVP_ServerEntity_PhysicalObject::GetOrientation(void) const
{
	if(m_pxDynamicObject == NULL)
	{
		return(m_xOrientation);
	}
	else
	{
		return(m_pxDynamicObject->GetOrientation());
	}
}

/// End of function 'AVP_ServerEntity_PhysicalObject::GetOrientation'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ServerEntity_PhysicalObject::SetLinVelocity
// Author       : Chris Wallace
// Date         : 01-May-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ServerEntity_PhysicalObject::SetLinVelocity(const Asura_Vector_3& xVelocity)
{
	PARENT_ASURA::SetLinVelocity(xVelocity);

	m_xLinVelocity = xVelocity;
}
/// End of function 'AVP_ServerEntity_PhysicalObject::SetLinVelocity'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ServerEntity_PhysicalObject::GetVelocity
// Author       : Andy Tate
// Date         : 22-Feb-2007
////////////////////////////////////////////////////////////////////////////////

const Asura_Vector_3& AVP_ServerEntity_PhysicalObject::GetVelocity(void) const
{
	if(m_pxDynamicObject == NULL)
	{
		return(m_xLinVelocity);
	}
	else
	{
		return(m_pxDynamicObject->GetLinVelocity());
	}
}
#ifdef ASURA_MAX_NO_OF_STORED_POSITIONS

void AVP_ServerEntity_PhysicalObject::ResetStoredPositions()
{
	m_uNumberOfStoredPositions = 0; 
}

void AVP_ServerEntity_PhysicalObject::StoreCurrentPosition( float fTimeStamp )
{
	if (m_uNumberOfStoredPositions < ASURA_MAX_NO_OF_STORED_POSITIONS )
	{
		m_axStoredPositions[ m_uNumberOfStoredPositions ] = GetPosition();
		m_afTimeStampsForStoredPositions[ m_uNumberOfStoredPositions ] = fTimeStamp; 
		++m_uNumberOfStoredPositions; 
	}
}

void AVP_ServerEntity_PhysicalObject::EndOfPhysicsUpdateCallback(float fTimeStamp)
{
	// Cache a new position for all physical objects in the server
	for ( Asura_Hashed_List_It<AVP_ServerEntity_PhysicalObject> xListIt( GetList() ); !xListIt.Done(); xListIt.Next() )
	{
		AVP_ServerEntity_PhysicalObject* pxServerEntityPhysicalObject = xListIt.GetCurrent();
		if ( pxServerEntityPhysicalObject )
		{
			pxServerEntityPhysicalObject->StoreCurrentPosition( fTimeStamp );
		}
	}
}

void AVP_ServerEntity_PhysicalObject::ResetAllStoredPositions()
{
	// Reset all stored positions for all physical object server entities
	for ( Asura_Hashed_List_It<AVP_ServerEntity_PhysicalObject> xListIt( GetList() ); !xListIt.Done(); xListIt.Next() )
	{
		AVP_ServerEntity_PhysicalObject* pxServerEntityPhysicalObject = xListIt.GetCurrent();
		if ( pxServerEntityPhysicalObject )
		{
			pxServerEntityPhysicalObject->ResetStoredPositions();
		}
	}
}
#endif // #ifdef ASURA_MAX_NO_OF_STORED_POSITIONS

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

    Asura_Value	xValue;

	Asura_Hash_ID uSkinHash = ASURA_HASH_ID_UNSET;
	Asura_Hash_ID uAnimHash = ASURA_HASH_ID_UNSET;

	if(xBlueprint.GetParameterValueByHashID(0x0035e57d, xValue)) // Asura_GetHashID( "Skin" )
	{
		uSkinHash = xValue.GetHashIDValue();
	}
	if(xBlueprint.GetParameterValueByHashID(0x002dc211, xValue)) // Asura_GetHashID( "Anim" )
	{
		uAnimHash = xValue.GetHashIDValue();
	}
	SetSkinAndAnimID(uSkinHash, uAnimHash);
#if defined(ASURA_TOOLS)
	Tools_PatchSkinAndAnimFileIDs();
#endif	// defined(ASURA_TOOLS)

	u_int iAnimFlags = GetAnimFlags();
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("Looping"), xValue))
	{
		xValue.GetBoolValue() ? (iAnimFlags |= AsuraAnimationFlag_Loop) : (iAnimFlags &= ~AsuraAnimationFlag_Loop);
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("Reversed"), xValue))
	{
		xValue.GetBoolValue() ? (iAnimFlags |= AsuraAnimationFlag_Reversed) : (iAnimFlags &= ~AsuraAnimationFlag_Reversed);
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("UseAnimMovement"), xValue))
	{
		xValue.GetBoolValue() ? (iAnimFlags |= AsuraAnimationFlag_UseMovementOffset) : (iAnimFlags &= ~AsuraAnimationFlag_UseMovementOffset);
	}
	SetAnimFlags(iAnimFlags);

	if(xBlueprint.GetParameterValueByHashID(0xA0D3F1F3, xValue))	// Asura_GetHashID("IsDamageable")
	{
		SetDestructible(xValue.GetBoolValue());
	}

	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("NoCollisions"), xValue))
	{
		SetNoCollisionsFlag(xValue.GetBoolValue());
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("CollisionProxy"), xValue))
	{
		SetCollisionProxy(static_cast<Asura_Physics_Object::COLLISION_PROXY>(xValue.GetIntValue()));
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("GravityScale"), xValue))
	{
		SetGravityScale(xValue.GetFloatValue());
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("Elasticity"), xValue))
	{
		SetElasticity(xValue.GetFloatValue());
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("Friction"), xValue))
	{
		SetFriction(xValue.GetFloatValue());
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("Drag"), xValue))
	{
		SetDrag(xValue.GetFloatValue());
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("AngularDrag"), xValue))
	{
		SetAngularDrag(xValue.GetFloatValue());
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("Mass"), xValue))
	{
		SetInvMass(1.0f / xValue.GetFloatValue());
	}

	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("NoShadow"), xValue))
	{
		if(xValue.GetBoolValue())
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_NOSHADOW);
		}
		else
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_NOSHADOW);
		}
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("Hidden"), xValue))
	{
		if(xValue.GetBoolValue())
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_INVISIBLE);
		}
		else
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_INVISIBLE);
		}
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("IgnoreForLOS"), xValue))
	{
		if(xValue.GetBoolValue())
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_IGNOREFORLOS);
		}
		else
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_IGNOREFORLOS);
		}
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("IgnoreModuleLinking"), xValue))
	{
		if(xValue.GetBoolValue())
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_IGNOREMODULELINKING);
		}
		else
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_IGNOREMODULELINKING);
		}
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("FullBright"), xValue))
	{
		if(xValue.GetBoolValue())
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_FULLBRIGHT);
		}
		else
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_FULLBRIGHT);
		}
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("BlockWaypointLinks"), xValue))
	{
		if(xValue.GetBoolValue())
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_BLOCKWAYPOINTLINKS);
		}
		else
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_BLOCKWAYPOINTLINKS);
		}
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("BlockWaypointExpansion"), xValue))
	{
		if(xValue.GetBoolValue())
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_BLOCKNAVDATAGENERATION);
		}
		else
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_BLOCKNAVDATAGENERATION);
		}
	}
	if(xBlueprint.GetParameterValueByHashID(Asura_GetHashID("MergeWithEnvironment"), xValue))
	{
		if(xValue.GetBoolValue())
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_SHOULDMERGEWITHENV);
		}
		else
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_SHOULDMERGEWITHENV);
		}
	}
	if(xBlueprint.GetParameterValueByHashID(0x2418E388, xValue))	// Asura_GetHashID("MergeWithSkybox")
	{
		if(xValue.GetBoolValue())
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags | ASURA_PHYSICALOBJECT_FLAG_SHOULDMERGEWITHSKY);
		}
		else
		{
			SetPhysicalObjectFlags(m_uPhysicalObjectFlags & ~ASURA_PHYSICALOBJECT_FLAG_SHOULDMERGEWITHSKY);
		}
	}

    if(xBlueprint.GetParameterValueByHashID(0x81F508E1, xValue))	//Asura_GetHashID("MaterialType")
    {
        m_uMaterialType = xValue.GetHashIDValue();
    }
    if(xBlueprint.GetParameterValueByHashID(0xB735025C, xValue))	//Asura_GetHashID("Health")
    {
		float fHealth;
		if ( xValue.GetValueType() == Asura_Value::VT_FLOAT )
		{	
			fHealth = xValue.GetFloatValue();
		}
		else
		{
			fHealth = float( xValue.GetIntValue() );
		}
		SetMaxHealth( fHealth );
		SetInitialHealth( fHealth );
		SetHealth( fHealth );
    }
	if(xBlueprint.GetParameterValueByHashID(0x0563D267, xValue))	// Asura_GetHashID("IgnoreForSurfaceReorientation")
    {
		SetIgnoreForSurfaceReorientation( xValue.GetBoolValue() );
    }

	// Handle IsFlammable Attribute.
	if(xBlueprint.GetParameterValueByHashID(0x8078912B, xValue))	// Asura_GetHashID("IsFlammable")
	{
		const bool bIsFlammable = xValue.GetBoolValue();

		SetAsFlammable( bIsFlammable );											// In this function we allocate/deallocate memory for flammable handler.

		if( IsFlammable() )
		{
			float fMaxTimeToStopBurning = 10.0f;								// 10 means 10 seconds
			float fMaxHealthPercentageToTake = 0.5f;							// 0.5 means 50% of total object energy
			float fStartBurningDamageTreshold = 0.5f;							// 0.5 means 50% of total object energy. So that means, We need to cause 50% of max health to start burning.

			if(xBlueprint.GetParameterValueByHashID(0x8DE04725, xValue))		// Asura_GetHashID("Burning.TimeToStop")
			{
				fMaxTimeToStopBurning = xValue.GetFloatValue();
				if( fMaxTimeToStopBurning==0.0f )
				{
					Asura_Assert( false, "Wrong value in blueprint! 'Burning.TimeToStop' must not be zero! So I am setting it by default to 1 (second)" );
					fMaxTimeToStopBurning = 1.0f;
				}
			}

			if(xBlueprint.GetParameterValueByHashID(0xEADBA093, xValue))		// Asura_GetHashID("Burning.HealthToTakePercentage")
			{
				float fValue = xValue.GetFloatValue();							// Int value from 0 to 100
				fMaxHealthPercentageToTake = fValue * 0.01f;					// Convert it to range 0.0f to 1.0f  (0% to 100%)
			}

			if(xBlueprint.GetParameterValueByHashID(0xEF8C729B, xValue))		// Asura_GetHashID("Burning.Start.DamageTresholdPercentage")
			{
				float fValue = xValue.GetFloatValue();							// Int value from 0 to 100
				fStartBurningDamageTreshold = fValue * 0.01f;					// Convert it to range 0.0f to 1.0f  (0% to 100%)
			}

			if( m_xFlammableHandler )											// This value should be ALWAYS non null here, but it is better to be 101% shure
			{
				// If the object was created meantime, don't destroy it. Instead simply overwrite all values.
				m_xFlammableHandler->Init( fMaxTimeToStopBurning, fMaxHealthPercentageToTake, fStartBurningDamageTreshold );;
			}
		}
	}

	{
		AVP_DamageMultiplier_Blueprint_ParameterEnumerator	xEnumerator(m_xDamageMultiplierContainer);

		// Time to enumerate the damage multipliers for this entity...
		xBlueprint.EnumerateParameters(szAVP_BLUEPRINT_ENUMERATIONNAME_DAMAGEMULTIPLIER, uAVP_BLUEPRINT_ENUMERATIONDIGITS_DAMAGEMULTIPLIER, xEnumerator);
	}
}

AVP_ServerEntity_GamePlayVolume* AVP_ServerEntity_PhysicalObject::GetCurrentGamePlayVolume() const
{
	AVP_ServerEntity_GamePlayVolume* pxGPVolume = AVP_ServerEntity_GamePlayVolume::GetVolumeThatContainsPosition(GetPosition(), m_uCurrentGamePlayVolumeID);
	if (pxGPVolume)
	{
		m_uCurrentGamePlayVolumeID = pxGPVolume->GetGuid();
	}
	else
	{
		m_uCurrentGamePlayVolumeID = ASURA_GUID_UNREGISTERED;
	}
	return pxGPVolume;
}

#if defined(ASURA_TOOLS)

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ServerEntity_PhysicalObject::Tools_PatchSkinAndAnimFileIDs
// Author       : Andy Tate
// Date         : 10-Aug-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_ServerEntity_PhysicalObject::Tools_PatchSkinAndAnimFileIDs(void)
{
	const Asura_Hash_ID	uAnimHash	= GetAnimID();
	const Asura_Hash_ID	uSkinHash	= GetSkinID();

	Asura_Hash_ID		uAnimFileHash;
	Asura_Hash_ID		uSkinFileHash;

	// Do we have an animation?
	if(uAnimHash == ASURA_HASH_ID_UNSET)
	{
		uAnimFileHash	= ASURA_HASH_ID_UNSET;
	}
	else
	{
		const char*	szAnimFilename	= Asura_HashedFilename_System::GetFullFilename(uAnimHash, false);

		uAnimFileHash	= Asura_GetHashID(szAnimFilename);
	}

	// Do we have a skin?
	if(uSkinHash == ASURA_HASH_ID_UNSET)
	{
		uSkinFileHash	= ASURA_HASH_ID_UNSET;
	}
	else
	{
		const char*	szSkinFilename	= Asura_HashedFilename_System::GetFullFilename(uSkinHash, false);

		uSkinFileHash	= Asura_GetHashID(szSkinFilename);
	}

	// Set the file-hashes from the filenames associated with these hashes...
	SetSkinAndAnimFileID(uSkinFileHash, uAnimFileHash);
}

/// End of function 'AVP_ServerEntity_PhysicalObject::Tools_PatchSkinAndAnimFileIDs'. ///

float AVP_ServerEntity_PhysicalObject::GetHealthValueForOnHealthEvent( const int iOnHealthEventID )
{
	for ( u_int u = 0; u < m_uNumOnHealthEvents; ++u )						
	{
		if ( m_pxOnHealthEvents[ u ].m_iEventID == iOnHealthEventID )
		{
			return m_pxOnHealthEvents[ u ].m_fHealthFraction;
		}
	}

	return -1.0f;
}

void AVP_ServerEntity_PhysicalObject::AddOnHealthEventData( const int iOnHealthEventID )
{
	GetNumber_Overlay xHealthOverlay( "Select Health Percent", 50.0f, 0.0f, 100.0f, "%3.0f" );
	if ( !xHealthOverlay.DoModal() )
	{
		return;
	}

	// Add this on health event.
	EventIDAndHealth* pxOldEvents   = m_pxOnHealthEvents;		
	const u_int		  uNumOldEvents = m_uNumOnHealthEvents;

	++m_uNumOnHealthEvents;
	m_pxOnHealthEvents = new EventIDAndHealth[ m_uNumOnHealthEvents ];
	Asura_Mem::Copy( m_pxOnHealthEvents, pxOldEvents, uNumOldEvents * sizeof( EventIDAndHealth ) );

	m_pxOnHealthEvents[ uNumOldEvents ].m_iEventID		  = iOnHealthEventID;
	// Convert the health percent to a 0.0f -> 1.0f fraction.
	m_pxOnHealthEvents[ uNumOldEvents ].m_fHealthFraction = xHealthOverlay.GetNumber() * 0.01f;

	delete [] pxOldEvents; 
}

void AVP_ServerEntity_PhysicalObject::RemoveOnHealthEventData(int iEventID)
{
	bool bFound = false;
	u_int uEvent;
	for (u_int u = 0; u < m_uNumOnHealthEvents && !bFound; ++u)
	{
		if (m_pxOnHealthEvents[u].m_iEventID == iEventID )
		{
			uEvent = u;
			bFound = true;
		}
	}

	if (!bFound)
	{
		Asura_Assert(false, "Event Not found to delete");
		return;
	}

	for (u_int u = uEvent; u < m_uNumOnHealthEvents-1; ++u)
	{
		m_pxOnHealthEvents[u].m_iEventID = m_pxOnHealthEvents[u+1].m_iEventID; 
		m_pxOnHealthEvents[u].m_fHealthFraction = m_pxOnHealthEvents[u+1].m_fHealthFraction; 
	}

	--m_uNumOnHealthEvents;

}

#endif	// defined(ASURA_TOOLS)

void AVP_ServerEntity_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? Checks physical objects and containers.
bool AVP_ServerEntity_PhysicalObject::CanUseForSurfaceReorientation(Asura_Guid uGuid)
{
	// Check server physical objects.
	const AVP_ServerEntity_PhysicalObject* const pxPhysicalObject = AVP_ServerEntity_PhysicalObject::FindEntity(uGuid);
	if (pxPhysicalObject)
	{
		return !pxPhysicalObject->IsIgnoredForSurfaceReorientation();
	}

	// Check server container objects.
	const AVP_ServerEntity_Container* const pxContainerObject = AVP_ServerEntity_Container::FindEntity(uGuid);
	if (pxContainerObject)
	{
		return !pxContainerObject->IsIgnoredForSurfaceReorientation();
	}
	
	// Check the client objects.
	return AVP_ClientEntity_PhysicalObject::CanUseForSurfaceReorientation(uGuid);
}

void AVP_ServerEntity_PhysicalObject::SetAsFlammable( bool bIsFlammable )
{
	m_bIsFlammable = bIsFlammable;

	if( m_bIsFlammable && m_xFlammableHandler==NULL )
	{
		m_xFlammableHandler = new AVP_ServerEntity_PhysicalObject_FlammableHandler();

		Asura_Assert( m_xFlammableHandler, "Not enought memory to allocate AVP_ServerEntity_PhysicalObject_FlammableHandler!" );
	}
	else if( m_bIsFlammable==false && m_xFlammableHandler )
	{
		delete m_xFlammableHandler;
		m_xFlammableHandler = NULL;
	}
}

bool AVP_ServerEntity_PhysicalObject::IsCurrentlyBurning()
{
	return(m_xFlammableHandler ? m_xFlammableHandler->IsCurrentlyBurning() : false); 
}

//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------

AVP_ServerEntity_PhysicalObject_FlammableHandler::AVP_ServerEntity_PhysicalObject_FlammableHandler()
: m_bIsBurning(false)
, m_fAlreadyReceivedFireDamage( 0.0f )
, m_fCurrenTimeToStopBurning( 10.0f )	// 10 seconds for burning.
, m_fMaxTimeToStopBurning( 10.0f )		// 10 seconds for burning.
, m_fMaxHealthPercentageToTake( 0.5f )	// 0.5 means 50% of total object energy
, m_fStartBurningDamageTreshold( 0.5f )	// If enemy has 150 health, then we need to damage him with flamethrower for 75 points. After that he will starts to burn.
{
}

void AVP_ServerEntity_PhysicalObject_FlammableHandler::Init( float fMaxTimeToStopBurning, float fMaxHealthPercentageToTake, float fStartBurningDamageTreshold )
{
	 m_fMaxTimeToStopBurning		= fMaxTimeToStopBurning;
	 m_fMaxHealthPercentageToTake	= fMaxHealthPercentageToTake;	// e.g.: 0.5 means 50% of total object energy
	 m_fStartBurningDamageTreshold	= fStartBurningDamageTreshold;	// e.g.: 0.3 means 30 damage points must be dealt to person with 100 points of life so it can be set on fire.
}

// Return value is amount of damage that this FlammableHandler wants to do to object or actor.
float AVP_ServerEntity_PhysicalObject_FlammableHandler::Update( float fMaxHealth, Asura_Guid uFromGuid )
{
	if( IsCurrentlyBurning() )
	{
		float fStoredCurrentTimeToStopBurning = m_fCurrenTimeToStopBurning;

		// Check: It is time to stop burning?
		m_fCurrenTimeToStopBurning -= Asura_Timers::GetGameTimeSlice();
		if( m_fCurrenTimeToStopBurning<=0.0f )
		{
			// Yes, it is. So stop burning the object/actor.
			m_bIsBurning = false;

			ResetReceivedFireDamage();						// Make sure that next time we would like to this object on fire, it will take some time.
			SendMessageToClient_StopBurning( uFromGuid );
			m_fCurrenTimeToStopBurning = 0.0f;

			// calculate only final slice of the damage we need to deliver to object.
			return CalculateCurrentBurningDamage( fMaxHealth, fStoredCurrentTimeToStopBurning );
		}
		else
		{
			return CalculateCurrentBurningDamage( fMaxHealth, Asura_Timers::GetGameTimeSlice() );
		}
	}
	else
	{
		if( m_fAlreadyReceivedFireDamage>=fMaxHealth * m_fStartBurningDamageTreshold )
		{
			m_bIsBurning = true;

			SendMessageToClient_StartBurning( uFromGuid );

			return CalculateCurrentBurningDamage( fMaxHealth, Asura_Timers::GetGameTimeSlice() );
		}
	}

	return 0.0f;
}

void AVP_ServerEntity_PhysicalObject_FlammableHandler::SendMessageToClient_StartBurning( Asura_Guid uFromGuid )
{
	//tell client entities
	Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSG_CLIENT_START_BURNING );
	pxMessage->To =	ASURA_GUID_MY_CLIENT_ENTITIES;
	pxMessage->From = uFromGuid;
	pxMessage->UseGuaranteedDelivery();
	Asura_Network::Send(pxMessage);
}

void AVP_ServerEntity_PhysicalObject_FlammableHandler::SendMessageToClient_StopBurning( Asura_Guid uFromGuid )
{
	//tell client entities
	Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSG_CLIENT_STOP_BURNING );
	pxMessage->To =	ASURA_GUID_MY_CLIENT_ENTITIES;
	pxMessage->From = uFromGuid;
	pxMessage->UseGuaranteedDelivery();
	Asura_Network::Send(pxMessage);
}


/// End of file. ///
