
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_EntityDamage_Request.cpp
// Author : Andy Tate
// Date   : 21-Feb-2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header...
#include "Entity/Damage/AVP_EntityDamage_Request.h"

// Asura independent header-files...
#include "Debugging/Asura_DebugLineRender.h"
#include "Entity/Container/Asura_ClientContainerInstance.h"
#if defined(ASURA_SUPPORT_COMMAND_CONSOLE)
#include "Interface/Asura_CommandConsole.h"
#endif	//defined(ASURA_SUPPORT_COMMAND_CONSOLE)
#include "Physics/Asura_Physics_Broadphase.h"
#include "Physics/Asura_Physics_RigidBody.h"
#include "Physics/Asura_Physics_RigidBodyDefinition.h"
#include "Volume/Asura_Volume.h"


// Game independent header-files...
#include "Core/Axon_Sound_Registry.h"
#include "Core/Axon_System.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Container/AVP_ClientEntity_Container.h"
#include "Entity/Container/AVP_ServerEntity_Container.h"
#include "Entity/Damage/AVP_EntityDamage_Messages.h"
#include "Entity/PhysicalObject/AVP_ClientEntity_PhysicalObject.h"
#include "Entity/PhysicalObject/AVP_ServerEntity_PhysicalObject.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Alien/AVP_ServerActor_Alien.h"
#include "Entity/PhysicalObject/Actor/Weapons/Sentrygun/AVP_ServerWeapon_Sentrygun.h"
#ifdef AVP_FRIENDLY_FIRE_OFF
	#include "Tactical/AVP_Tactical_System.h"
#endif

////////////////////////////////////////////////////////////////////////////////
// Constant declarations...
////////////////////////////////////////////////////////////////////////////////

#if defined(AVP_USE_DAMAGE_DEBUGGING)
DVSVAR bool		dvs_bRenderDamageDebuggingLOSTests			= false;

DVSVAR bool		dvs_bRenderDamageDebuggingRequestRadial	= false;
DVSVAR bool		dvs_bRenderDamageDebuggingRequestLinear	= false;
DVSVAR bool		dvs_bRenderDamageDebuggingRequestVictim	= false;

DVSVAR float	dvs_fRenderDamageDebuggingTimeBeforeFade	= 5.0f;
DVSVAR float	dvs_fRenderDamageDebuggingFadeDuration		= 0.500f;
#endif	// defined(AVP_USE_DAMAGE_DEBUGGING)


////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////

static const u_int	g_uNumRadialDamageDirections	= 6;

static const Asura_Vector_3	g_axRadialDamageDirections[g_uNumRadialDamageDirections] =
{
	Asura_Vector_3( 1.0f,  0.0f,  0.0f),
	Asura_Vector_3(-1.0f,  0.0f,  0.0f),

	Asura_Vector_3( 0.0f,  1.0f,  0.0f),
	Asura_Vector_3( 0.0f, -1.0f,  0.0f),

	Asura_Vector_3( 0.0f,  0.0f,  1.0f),
	Asura_Vector_3( 0.0f,  0.0f, -1.0f),
};

////////////////////////////
// AVP_PhysicsQueryResult //
////////////////////////////
// Helper class used to store the result of a physics query.
class AVP_PhysicsQueryResult
{
public:
	AVP_PhysicsQueryResult()
		: m_uEntityGuid(ASURA_GUID_UNREGISTERED)
		, m_iRigidBodyHandle(ASURA_INVALID_HANDLE)
		, m_xCollMeshCentre(Asura_Maths::ZeroVector3)
		, m_fMaxRadius(1.0f)
	{}

	void Set(const Asura_Physics_RigidBodyInstance* const pxBody)
	{
		if (pxBody)
		{
			m_uEntityGuid = pxBody->GetAssociatedEntityGuid();
			m_iRigidBodyHandle = pxBody->GetHandle();
			m_xCollMeshCentre = pxBody->GetCollisionMeshCentre();

			if (pxBody->GetDefinition())
			{
				m_fMaxRadius = pxBody->GetDefinition()->GetMaximumRadius();
			}
		}
	}

	Asura_Guid m_uEntityGuid;
	Asura_Handle m_iRigidBodyHandle;
	
	Asura_Vector_3 m_xCollMeshCentre;
	float m_fMaxRadius;
};

///////////////////////////////////
// AVP_PhysicsQueryResultStorage //
///////////////////////////////////
// Helper class used to store the results of physics queries.

// Maximum number of physics results that will be returned. Increase if necessary.
const u_int uMAX_NUM_PHYSICS_RESULTS = 64;

class AVP_PhysicsQueryResultStorage
{
public:
	AVP_PhysicsQueryResultStorage()
		: m_uNumResults(0)
	{}

	void Reset()
	{
		m_uNumResults = 0;
	}

	void AddResult(const Asura_Physics_RigidBodyInstance* const pxBody)
	{
		Asura_Assert(m_uNumResults < uMAX_NUM_PHYSICS_RESULTS, "Too many physics results returned by query. Ignoring the rest.");
		if (m_uNumResults < uMAX_NUM_PHYSICS_RESULTS)
		{
			m_axResult[m_uNumResults].Set(pxBody);
			++m_uNumResults;
		}
	}

	u_int GetNumResults() const { return m_uNumResults; }

	const AVP_PhysicsQueryResult& GetResult(u_int uIndex)
	{
		Asura_Assert(uIndex < m_uNumResults, "Invalid array index");
		return m_axResult[uIndex];
	}

private:
	AVP_PhysicsQueryResult m_axResult[uMAX_NUM_PHYSICS_RESULTS];
	u_int m_uNumResults;
};

// Storage for results of physics query.
static AVP_PhysicsQueryResultStorage s_xResultStorage;

////////////////////////////////////////////////////////////////////////////////
// Private functions...
////////////////////////////////////////////////////////////////////////////////

#if defined(AVP_USE_DAMAGE_DEBUGGING) && defined(ASURA_SUPPORT_COMMAND_CONSOLE)

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_Request::RegisterWithCommandConsole
// Author       : Andy Tate
// Date         : 28-Feb-2008
////////////////////////////////////////////////////////////////////////////////

void AVP_EntityDamage_Request::RegisterWithCommandConsole(void)
{
#ifdef ASURA_DEBUG_VARIABLE_SYSTEM
	if(!Asura_CommandConsole::VarExists(L"AVP.DamageDebugging.LOSTests"))
	{
		Asura_CommandConsole::AddVar(L"AVP.DamageDebugging.LOSTests", &dvs_bRenderDamageDebuggingLOSTests);

		Asura_CommandConsole::AddVar(L"AVP.DamageDebugging.RequestRadial", &dvs_bRenderDamageDebuggingRequestRadial);
		Asura_CommandConsole::AddVar(L"AVP.DamageDebugging.RequestLinear", &dvs_bRenderDamageDebuggingRequestLinear);
		Asura_CommandConsole::AddVar(L"AVP.DamageDebugging.RequestVictim", &dvs_bRenderDamageDebuggingRequestVictim);

		Asura_CommandConsole::AddVar(L"AVP.DamageDebugging.TimeBeforeFade", &dvs_fRenderDamageDebuggingTimeBeforeFade);
		Asura_CommandConsole::AddVar(L"AVP.DamageDebugging.FadeDuration", &dvs_fRenderDamageDebuggingFadeDuration);
	}
#endif //ASURA_DEBUG_VARIABLE_SYSTEM
}

/// End of function 'AVP_EntityDamage_Request::RegisterWithCommandConsole'. ///

#endif	//defined(AVP_USE_DAMAGE_DEBUGGING) && defined(ASURA_SUPPORT_COMMAND_CONSOLE)


////////////////////////////////////////////////////////////////////////////////
// Protected functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestSpatial::LOSTest
// Author       : Andy Tate
// Date         : 30-Jan-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_EntityDamage_RequestSpatial::LOSTest(AVP_ServerEntity_PhysicalObject& xFrom,
											  const Asura_Vector_3& xPosition,
											  const Asura_Vector_3& xDirection,
											  const float fMaxRange,
											  const float fParametric) const
{
	const Asura_LOS_InputData		xLOSInput(xPosition, xDirection, m_uFromGuid, fMaxRange);

	ApplyToBotanicals( xLOSInput );

	bool bHitEnv = false;
	float fDistance = Asura_Maths::LargeFloat;
	const Asura_LOS_OutputData		xEntLOSOutput			= *(Asura_LOS::TestAgainstEntities(&xLOSInput));
	if ( xEntLOSOutput.m_uHitEntity != 0 && xEntLOSOutput.m_uHitEntity != m_uIgnoreGuid )
	{
		fDistance = xEntLOSOutput.m_fDistanceToIntersection;
	}

	Asura_LOS_OutputData xEnvLOSOutput = *(Asura_LOS::TestAgainstEnvironment(&xLOSInput));
	if ( xEnvLOSOutput.m_bHitEnvironment && xEnvLOSOutput.m_fDistanceToIntersection < fDistance )
	{
		fDistance = xEnvLOSOutput.m_fDistanceToIntersection;
		bHitEnv = true;
	}

	// Did we hit anything?
	if(fDistance < fMaxRange)
	{
		Asura_Vector_3	xHitPosition(xLOSInput.m_xDirection);

		// Calculate the actual point-of-impact...
		xHitPosition	*= fDistance;
		xHitPosition	+= xPosition;

		// What did we hit?
		if( bHitEnv )
		{
			// Environment - apply the damage here...
			if(ApplyToEnvironment(xLOSInput, xEnvLOSOutput))
			{
#if defined(AVP_USE_DAMAGE_DEBUGGING)
				if(dvs_bRenderDamageDebuggingLOSTests)
				{
					const Asura_Vector_3&			xNormal		= xEnvLOSOutput.m_xNormal;
					Asura_DebugLineRender_LineSet*	pxLineSet	= Asura_DebugLineRender::CreateTimedLineSet(16, dvs_fRenderDamageDebuggingTimeBeforeFade, dvs_fRenderDamageDebuggingFadeDuration);

					const Asura_Vector_3			xNormalEndPosition(xHitPosition + xNormal);

					pxLineSet->AddArrow(xPosition, xHitPosition, 0x80FFFF00, 0.125f, 0.125f);
					pxLineSet->AddArrow(xHitPosition, xNormalEndPosition, 0x80FFFF00, 0.125f, 0.125f);
				}
#endif	// defined(AVP_USE_DAMAGE_DEBUGGING)

				return(true);
			}
			else
			{
				return(false);
			}
		}
		else
		{
			if(ApplyToEntity(xFrom, xLOSInput, xEntLOSOutput, fParametric))
			{
#if defined(AVP_USE_DAMAGE_DEBUGGING)
				if(dvs_bRenderDamageDebuggingLOSTests)
				{
					const Asura_Vector_3&			xNormal		= xEntLOSOutput.m_xNormal;
					Asura_DebugLineRender_LineSet*	pxLineSet	= Asura_DebugLineRender::CreateTimedLineSet(16, dvs_fRenderDamageDebuggingTimeBeforeFade, dvs_fRenderDamageDebuggingFadeDuration);

					const Asura_Vector_3			xNormalEndPosition(xHitPosition + xNormal);

					pxLineSet->AddArrow(xPosition, xHitPosition, 0x8000FFFF, 0.125f, 0.125f);
					pxLineSet->AddArrow(xHitPosition, xNormalEndPosition, 0x8000FFFF, 0.125f, 0.125f);
				}
#endif	// defined(AVP_USE_DAMAGE_DEBUGGING)
				return(true);
			}
			else
			{
				return(false);
			}
		}
	}

#if defined(AVP_USE_DAMAGE_DEBUGGING)
	if(dvs_bRenderDamageDebuggingLOSTests)
	{
		const Asura_Vector_3			xEndPosition(xPosition + (xDirection * fDistance));
		Asura_DebugLineRender_LineSet*	pxLineSet	= Asura_DebugLineRender::CreateTimedLineSet(8, dvs_fRenderDamageDebuggingTimeBeforeFade, dvs_fRenderDamageDebuggingFadeDuration);

		pxLineSet->AddArrow(xPosition, xEndPosition, 0x80FF0000, 0.125f, 0.125f);
	}
#endif	// defined(AVP_USE_DAMAGE_DEBUGGING)
	return(false);
}

/// End of function 'AVP_EntityDamage_RequestSpatial::LOSTest'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestSpatial::ApplyToBotanicals
// Author       : Andy Tate
// Date         : 3-Mar-2008
////////////////////////////////////////////////////////////////////////////////

bool AVP_EntityDamage_RequestRadial::ApplyToBotanicals( const Asura_Vector_3& xPosition ) const
{
	Asura_Message*	pxMessage	= Asura_Network::CreateMessage(AVP_MSGID_ENTITYDAMAGE_APPLYRADIAL_TO_BOTANICALS, sizeof(AVP_Message_EntityDamage_ApplyRadialToBotanicals));

	if(pxMessage == NULL)
	{
		return(false);
	}
	else
	{
		const AVP_EntityDamage_ApplyRadialToBotanicals	xDamage(m_uTypeHash,
																m_uFromGuid,
																xPosition,
																m_fRadiusFor100PercentDamage,
																m_fRadiusFor0PercentDamage,
																m_fMaxAmount,
																m_fMaxImpulseStrength,
																m_uParentGuid);

		AVP_Message_EntityDamage_ApplyRadialToBotanicals*	pxData	= static_cast<AVP_Message_EntityDamage_ApplyRadialToBotanicals*>(pxMessage->GetData());


		pxData->SetDamage(xDamage);
		pxMessage->SetFromGuid(m_uFromGuid);
		pxMessage->SetToGuid(ASURA_GUID_ALL_CLIENTMACHINES);
		Asura_Network::Send(pxMessage);
		return(true);
	}
}

/// End of function 'AVP_EntityDamage_RequestSpatial::ApplyToBotanicals'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestSpatial::ApplyToEntity
// Author       : Andy Tate
// Date         : 3-Mar-2008
////////////////////////////////////////////////////////////////////////////////

bool AVP_EntityDamage_RequestSpatial::ApplyToEntity(AVP_ServerEntity_PhysicalObject& xFrom,
													const Asura_LOS_InputData& xLOSInput,
													const Asura_LOS_OutputData& xLOSOutput,
													const float fParametric) const
{
	const Asura_Guid	uVictimGuid	= xLOSOutput.m_uHitEntity;

	// Notes on the LOS-test: this is a part of the game which needs to be quite fast. As it stands, the LOS-test above finds the entity that has been hit but following
	// the usual Asura philosophy, it will only return the Guid. To access the entity again, one needs to do a FindEntity on this Guid, which is the kind of minor time waste
	// that will quickly accumulate to noticeable proportions. Not that I could recommend any feasible solution, but if you want to make it faster, you might want to
	// consider some kind of alternative approach. (AN 04/02/08)
	if(uVictimGuid == ASURA_GUID_UNREGISTERED)
	{
		return(false);
	}
	else
	{
		// Hit an entity - is it a new one?
		if(uVictimGuid == m_uFromGuid)
		{
			return(false);
		}
		else
		{
			bool bHitContainer = false;
			Asura_Entity_Server* pxVictim = AVP_ServerEntity_PhysicalObject::FindEntity(uVictimGuid);
			
#ifdef ASURA_USE_CONTAINER_SYSTEM
			if (!pxVictim)
			{
				// Not a physical object, perhaps it is a container.
				pxVictim = AVP_ServerEntity_Container::FindEntity(uVictimGuid);
				if (pxVictim)
				{
					bHitContainer = true;
				}
			}
#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM

			if(pxVictim == NULL)
			{
				return(false);
			}
			else
			{
				bool	bIsFriend	= false;

#ifdef AVP_FRIENDLY_FIRE_OFF
				if (AVP_Tactical_System::GetAllegiance( *pxVictim, xFrom ) == AVP_ALLEGIANCE_FRIEND)
				{
					bIsFriend = true;
				}
				else
#endif
				{
					// Alien AI can not damage each other (or the player)
					if((pxVictim->GetClassification() == AVP_ENTITYCLASS_ALIEN) && (xFrom.GetClassification() == AVP_ENTITYCLASS_ALIEN))
					{
						const AVP_ServerActor_Alien*	pxFromAlien	= AVP_ServerActor_Alien::FindEntity(m_uFromGuid);

						bIsFriend	= (pxFromAlien != NULL) && !pxFromAlien->IsPlayer();
					}
				}

				// Check this first
				if(bIsFriend)
				{
					return(false);
				}
				else
				{
					const int	iHitAttachmentID	= xLOSOutput.m_iHitAttachmentID;
					const int	iHitBoneID			= xLOSOutput.m_iHitBoneID;

					// First hit on a new entity - do we have a bone?
					// Note: For containers we don't need a bone.
					if ( bHitContainer || (iHitBoneID >= 0) )
					{
						float									fAmount				= m_fMaxAmount * fParametric;
						const float								fImpulseStrength	= m_fMaxImpulseStrength * fParametric;

						const Asura_Vector_3&					xDirection			= xLOSInput.m_xDirection;
						const Asura_Vector_3&					xNormal				= xLOSOutput.m_xNormal;
						const Asura_Vector_3&					xPosition			= xLOSInput.m_xOrigin;

						const Asura_Vector_3					xHitPosition(xPosition + (xDirection * xLOSOutput.m_fDistanceToIntersection));

						if( xFrom.GetClassification() == AVP_ENTITYCLASS_SENTRYGUN )
						{
							AVP_ServerWeapon_Sentrygun* pxSentryTemp = AVP_ServerWeapon_Sentrygun::FindEntity( m_uFromGuid );
							if( pxSentryTemp )
							{
								if( pxSentryTemp->IsWithinLethalRange( xHitPosition ) )
								{
									// if the owner is a sentry gun and the victim is within lethal range bump the damage to a lethal amount. 
									fAmount = 999.99f;
								}
							}
						}

						const AVP_EntityDamage_RequestVictim	xDamage(m_uTypeHash,
																		m_uFromGuid,
																		m_uFromMarkerPointHash,
																		uVictimGuid,
																		fAmount,
																		xHitPosition,
																		xDirection,
																		xNormal,
																		fImpulseStrength,
																		iHitBoneID,
																		false,
																	    ASURA_GUID_UNREGISTERED,
																	    iHitAttachmentID,
																		xLOSOutput.m_uMaterialResponseHashID);

						// Don't need to go via messages here since we're guaranteed to be server-side...
						xDamage.Apply();
						return(true);
					}
					else
					{
						return(false);
					}
				}
			}
		}
	}
}

/// End of function 'AVP_EntityDamage_RequestSpatial::ApplyToEntity'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestSpatial::ApplyToEnvironment
// Author       : Andy Tate
// Date         : 3-Mar-2008
////////////////////////////////////////////////////////////////////////////////

bool AVP_EntityDamage_RequestSpatial::ApplyToEnvironment(const Asura_LOS_InputData& xLOSInput,
														 const Asura_LOS_OutputData& xLOSOutput) const
{
	// What did we hit?
	if(xLOSOutput.m_bHitEnvironment)
	{
		Asura_Message*			pxMessage		= Asura_Network::CreateMessage(AVP_MSGID_ENTITYDAMAGE_APPLY_TO_ENVIRONMENT, sizeof(AVP_Message_EntityDamage_ApplyToEnvironment));
		const Asura_Vector_3&	xNormal			= xLOSOutput.m_xNormal;
		const Asura_Vector_3&	xStartPosition	= xLOSInput.m_xOrigin;
		const Asura_Vector_3&	xDirection		= xLOSInput.m_xDirection;

		const Asura_Vector_3	xPointOfImpact(xStartPosition + (xDirection * xLOSOutput.m_fDistanceToIntersection));

		// Hit the environment - pass this onto the client...
		if(pxMessage != NULL)
		{
			const Asura_Hash_ID								uMaterialResponseHash	= xLOSOutput.m_uMaterialResponseHashID;
			const u_short									usPolyFlags				= xLOSOutput.m_usPolyFlags;
			const u_int										uFaceIndex				= xLOSOutput.m_uEnvironmentFaceIndex;

			const AVP_EntityDamage_ApplyToEnvironment		xDamage(m_uTypeHash,
																	m_uFromGuid,
																	xPointOfImpact,
																	xDirection,
																	xNormal,
																	uMaterialResponseHash,
																	usPolyFlags,
																	uFaceIndex);

			AVP_Message_EntityDamage_ApplyToEnvironment*	pxData	= static_cast<AVP_Message_EntityDamage_ApplyToEnvironment*>(pxMessage->GetData());

			pxData->SetDamage(xDamage);

			pxMessage->SetFromGuid(m_uFromGuid);
			pxMessage->SetToGuid(ASURA_GUID_ALL_CLIENTMACHINES);
			Asura_Network::Send(pxMessage);
		}

		return(true);
	}
	else
	{
		Asura_Assert(false, "The LOS-test didn't hit the environment - cannot apply to it.");
		return(false);
	}
}

bool AVP_EntityDamage_RequestSpatial::ApplyToBotanicals( const Asura_LOS_InputData& xLOSInput ) const
{
	Asura_Message*	pxMessage	= Asura_Network::CreateMessage(AVP_MSGID_ENTITYDAMAGE_APPLYLINEAR_TO_BOTANICALS, sizeof(AVP_Message_EntityDamage_ApplyLinearToBotanicals));

	if(pxMessage == NULL)
	{
		return(false);
	}
	else
	{
		const AVP_EntityDamage_ApplyLinearToBotanicals	xDamage(m_uTypeHash,
																m_uFromGuid,
																xLOSInput.m_xOrigin,
																xLOSInput.m_xDirection * xLOSInput.m_fInitialMaximumRange,
																m_fMaxAmount,
																m_fMaxImpulseStrength,
																m_uParentGuid);

		AVP_Message_EntityDamage_ApplyLinearToBotanicals*	pxData	= static_cast<AVP_Message_EntityDamage_ApplyLinearToBotanicals*>(pxMessage->GetData());


		pxData->SetDamage(xDamage);
		pxMessage->SetFromGuid(m_uFromGuid);
		pxMessage->SetToGuid(ASURA_GUID_ALL_CLIENTMACHINES);
		Asura_Network::Send(pxMessage);
		return(true);
	}
}
/// End of function 'AVP_EntityDamage_RequestSpatial::ApplyToEnvironment'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestRadial::ApplyToEntity
// Author       : Andy Tate
// Date         : 3-Mar-2008
////////////////////////////////////////////////////////////////////////////////

bool AVP_EntityDamage_RequestRadial::ApplyToEntity(AVP_ServerEntity_PhysicalObject& xFrom,
												   AVP_ServerEntity_PhysicalObject& xTo) const
{
	Asura_Vector_3	xBSCentre;
	float			fBSRadius;

	if(xTo.GetBoundingSphere(xBSCentre, fBSRadius))
	{
		const Asura_Guid		uVictimGuid			= xTo.GetGuid();
		const AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity( uVictimGuid );
		if ( pxActor )
		{
			pxActor->GetChestPosition( xBSCentre );
		}

		const Asura_Vector_3&	xPosition	= xFrom.GetPosition();
		Asura_Vector_3			xDirection(xBSCentre - xPosition);

		const float				fDistance			= xDirection.Normalise();
		Asura_Vector_3			xNormal				= -xDirection;

		const float				fClosestDistance	= fDistance - fBSRadius;

		// Are the bounding-spheres intersecting?
		if(fClosestDistance < m_fRadiusFor0PercentDamage)
		{
			float	fParametric;

			if(fClosestDistance <= m_fRadiusFor100PercentDamage)
			{
				fParametric	= 1.0f;
			}
			else
			{
				const float	fMaxDifference	= m_fRadiusFor0PercentDamage - m_fRadiusFor100PercentDamage;
				const float	fDifference		= m_fRadiusFor0PercentDamage - fClosestDistance;

				fParametric	= fDifference / fMaxDifference;
			}

			const float								fAmount				= m_fMaxAmount * fParametric;
			const float								fImpulseStrength	= m_fMaxImpulseStrength * fParametric;

			Asura_Vector_3 xImpactPosition = xBSCentre;
			int iHitBoneID = -1;

			if ( m_bPerformLOSTest )
			{
				Asura_LOS_InputData xInData( xPosition, xDirection, ASURA_GUID_UNREGISTERED, m_fRadiusFor0PercentDamage );
				Asura_LOS_OutputData xOutData;
				xOutData.Reset( m_fRadiusFor0PercentDamage );
				Asura_Entity_Renderable* pxRenderable = Asura_Entity_Renderable::FindEntity( uVictimGuid );
				if ( pxRenderable )
				{
					pxRenderable->LOSTest( &xInData, &xOutData );
					if ( xOutData.m_uHitEntity == uVictimGuid )
					{
						iHitBoneID = xOutData.m_iHitBoneID;
						xImpactPosition = xPosition + xDirection * xOutData.m_fDistanceToIntersection;
						xNormal = xOutData.m_xNormal;
					}
				}
			}

			const AVP_EntityDamage_RequestVictim	xDamage(m_uTypeHash,
															m_uFromGuid,
															m_uFromMarkerPointHash,
															uVictimGuid,
															fAmount,
															xImpactPosition,
															xDirection,
															xNormal,
															fImpulseStrength,
															iHitBoneID, 
															false,
															xFrom.GetOwnerGuid()
															);

			// Don't need to go via messages here since we're guaranteed to be server-side...
			xDamage.Apply();
			return(true);
		}
		else
		{
			return(false);
		}
	}
	else
	{
		return(false);
	}
}

/// End of function 'AVP_EntityDamage_RequestRadial::ApplyToEntity'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestRadial::ApplyToContainerEntity
// Author       : Martin Harris
//
// Returns true if we've applied damage to a container entity.
////////////////////////////////////////////////////////////////////////////////
#ifdef ASURA_USE_CONTAINER_SYSTEM
bool AVP_EntityDamage_RequestRadial::ApplyToContainerEntity(AVP_ServerEntity_PhysicalObject& xFrom,
															const AVP_ServerEntity_Container& xTo,
															const AVP_PhysicsQueryResult& xResult) const
{
	const Asura_Vector_3& xFromPosition = xFrom.GetPosition();
	const Asura_Vector_3& xBSCentre = xResult.m_xCollMeshCentre;

	Asura_Vector_3 xDirection(xBSCentre - xFromPosition);
	const float	fDistance = xDirection.Normalise();

	Asura_Vector_3 xNormal = -xDirection;

	const float fBSRadius = xResult.m_fMaxRadius;
	const float fClosestDistance = fDistance - fBSRadius;
	
	// Are the bounding-spheres intersecting?
	if (fClosestDistance < m_fRadiusFor0PercentDamage)
	{
		float fParametric = 1.0f;

		if (fClosestDistance > m_fRadiusFor100PercentDamage)
		{
			const float	fMaxDifference	= m_fRadiusFor0PercentDamage - m_fRadiusFor100PercentDamage;
			const float	fDifference		= m_fRadiusFor0PercentDamage - fClosestDistance;

			fParametric	= fDifference / fMaxDifference;
		}

		const float fAmount = m_fMaxAmount * fParametric;
		const float fImpulseStrength = m_fMaxImpulseStrength * fParametric;

		Asura_Vector_3 xImpactPosition = xBSCentre;
		int iHitBoneID = -1;
		Asura_Hash_ID uMaterialResponseHashID = ASURA_HASH_ID_UNSET;
		int iAttachmentID = static_cast<int>(uASURA_CONTAINER_UNIQUE_ID_INVALID);
		
		// Look up the unique ID for the mesh container corresponding to the given physics handle.
		const Asura_Guid uVictimGuid = xTo.GetGuid();
		AVP_ClientEntity_Container* const pxClientContainer = AVP_ClientEntity_Container::FindEntity(uVictimGuid);
		if (pxClientContainer)
		{
			const int iRigidBodyHandle = xResult.m_iRigidBodyHandle;
			iAttachmentID = pxClientContainer->FindUniqueIDFromRigidBodyHandle(iRigidBodyHandle);
		}

		if (iAttachmentID == static_cast<int>(uASURA_CONTAINER_UNIQUE_ID_INVALID))
		{
			return false;
		}

		// Perform LOS test if needed.
		if ( m_bPerformLOSTest )
		{
			Asura_LOS_InputData xInData( xFromPosition, xDirection, ASURA_GUID_UNREGISTERED, m_fRadiusFor0PercentDamage );
			Asura_LOS_OutputData xOutData;
			xOutData.Reset( m_fRadiusFor0PercentDamage );

			if ( pxClientContainer )
			{
				pxClientContainer->LOSTest( &xInData, &xOutData );
				if ( xOutData.m_uHitEntity == uVictimGuid &&
					 xOutData.m_iHitAttachmentID == iAttachmentID )
				{
					xImpactPosition	= xFromPosition + xDirection * xOutData.m_fDistanceToIntersection;
					xNormal					= xOutData.m_xNormal;
					iHitBoneID				= xOutData.m_iHitBoneID;
					uMaterialResponseHashID = xOutData.m_uMaterialResponseHashID;
				}
			}
		}

		const AVP_EntityDamage_RequestVictim	xDamage(m_uTypeHash,
														m_uFromGuid,
														m_uFromMarkerPointHash,
														uVictimGuid,
														fAmount,
														xImpactPosition,
														xDirection,
														xNormal,
														fImpulseStrength,
														iHitBoneID, 
														false,
														xFrom.GetOwnerGuid(),
														iAttachmentID,
														uMaterialResponseHashID);
														
		// Don't need to go via messages here since we're guaranteed to be server-side...
		xDamage.Apply();
		return true;
	}

	return false;
}
#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM

////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestVictim::Apply
// Author       : Andy Tate
// Date         : 27-Feb-2008
////////////////////////////////////////////////////////////////////////////////

bool AVP_EntityDamage_RequestVictim::Apply() const
{
	if(Asura_Network::IsServer())
	{
		AVP_ServerEntity_PhysicalObject*	pxTo	= AVP_ServerEntity_PhysicalObject::FindEntity(m_uEntityToDamage);

		if (pxTo == NULL)
		{
			// Not a physical object, perhaps it's a container.
#ifdef ASURA_USE_CONTAINER_SYSTEM
			AVP_ServerEntity_Container* pxToContainer = AVP_ServerEntity_Container::FindEntity(m_uEntityToDamage);
			if (pxToContainer)
			{
				pxToContainer->TakeDamageExtended(*this, false);
				return true;
			}
#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM

			// Not asserting here as the entity might not be around for various reasons, just handle it..
			return(false);
		}
		else
		{
			pxTo->TakeDamageExtended(*this, false);

#if defined(AVP_USE_DAMAGE_DEBUGGING)
			if(dvs_bRenderDamageDebuggingRequestVictim)
			{
				const Asura_Bounding_Box*	pxBB	= pxTo->GetBB();

				if(pxBB != NULL)
				{
					Asura_DebugLineRender_LineSet*	pxLineSet		= Asura_DebugLineRender::CreateTimedLineSet(12, dvs_fRenderDamageDebuggingTimeBeforeFade, dvs_fRenderDamageDebuggingFadeDuration);

					const Asura_Vector_3&			xPosition		= pxTo->GetPosition();
					const Asura_Matrix_3x3&			xOrientation	= pxTo->GetOrientation();
					const u_int						uARGB			= (pxTo->GetHealth() <= 0.0f) ? 0xFFFF0000 : 0xFFFFFF00;

					pxLineSet->AddBoundingBox(*pxBB, xPosition, xOrientation, uARGB);
				}
			}
#endif	// defined(AVP_USE_DAMAGE_DEBUGGING)

			return(true);
		}
	}
	else
	{
		Asura_Assert(false, "Cannot call damage application functions on client-machines.");
		return(false);
	}
}

/// End of function 'AVP_EntityDamage_RequestVictim::Apply'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestLinear::AVP_EntityDamage_RequestLinear
// Author       : Andy Tate
// Date         : 26-Feb-2008
////////////////////////////////////////////////////////////////////////////////

//AVP_EntityDamage_RequestLinear::AVP_EntityDamage_RequestLinear(const Asura_Hash_ID uTypeHash,
//															   const Asura_Guid uFromGuid,
//															   const Asura_Hash_ID uMarkerPointHash,
//															   const float fMaxAmount,
//															   const float fMaxImpulseStrength,
//															   const Asura_Guid uIgnoreGuid,
//															   const float fDistanceOutFromMarkerPoint,
//															   const Asura_Guid uParentGuid /*= ASURA_GUID_UNREGISTERED*/)
//: PARENT(uTypeHash, uFromGuid, uMarkerPointHash, fMaxAmount, fMaxImpulseStrength, uIgnoreGuid, uParentGuid)
//, m_fDistance( fDistanceOutFromMarkerPoint )
//{
//	const AVP_ClientEntity_PhysicalObject*	pxPhysicalObject	= AVP_ClientEntity_PhysicalObject::FindEntity(uFromGuid);
//
//	if(pxPhysicalObject == NULL)
//	{
//		Asura_Assert(false, "Failed to find physical-object to request linear damage for.");
//	}
//	else
//	{
//		Asura_Matrix_3x3	xOrientation;
//
//		if(pxPhysicalObject->GetWorldSpaceMarkerPoint(uMarkerPointHash, &m_xStartPosition, &xOrientation))
//		{
//			m_xDirection = Asura_Vector_3(xOrientation.e31, xOrientation.e32, xOrientation.e33);
//		}
//		else
//		{
//			Asura_Assert(false, "Failed to find marker-point to request linear damage from.");
//		}
//	}
//}

/// End of function 'AVP_EntityDamage_RequestLinear::AVP_EntityDamage_RequestLinear'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestLinear::AVP_EntityDamage_RequestLinear
// Author       : Andy Tate
// Date         : 26-Feb-2008
////////////////////////////////////////////////////////////////////////////////

//AVP_EntityDamage_RequestLinear::AVP_EntityDamage_RequestLinear(const Asura_Hash_ID uTypeHash,
//															   const Asura_Guid uFromGuid,
//															   const Asura_Hash_ID uMarkerPointHash,
//															   const float fMaxAmount,
//															   const float fMaxImpulseStrength,
//															   const Asura_Guid uIgnoreGuid,
//															   const Asura_Vector_3& xDirection,
//															   const float fDistance,
//															   const Asura_Guid uParentGuid /*= ASURA_GUID_UNREGISTERED*/)
//: PARENT(uTypeHash, uFromGuid, uMarkerPointHash, fMaxAmount, fMaxImpulseStrength, uIgnoreGuid, uParentGuid)
//, m_xDirection( xDirection )
//, m_fDistance( fDistance )
//{
//	const AVP_ClientEntity_PhysicalObject*	pxPhysicalObject	= AVP_ClientEntity_PhysicalObject::FindEntity(uFromGuid);
//
//	if(pxPhysicalObject == NULL)
//	{
//		Asura_Assert( uMarkerPointHash == ASURA_HASH_ID_UNSET, "Failed to find client physical-object to request linear damage from a marker point for, using server position");
//		const AVP_ServerEntity_PhysicalObject*	pxServer = AVP_ServerEntity_PhysicalObject::FindEntity(uFromGuid);
//		if ( pxServer )
//		{
//			m_xStartPosition = pxServer->GetPosition();
//		}
//		else
//		{
//			Asura_Assert( false, "Failed to find server object to do damage from" );
//		}
//	}
//	else
//	{
//		if ( uMarkerPointHash == ASURA_HASH_ID_UNSET || !pxPhysicalObject->GetWorldSpaceMarkerPoint(uMarkerPointHash, &m_xStartPosition, NULL) )
//		{
//			Asura_Assert( uMarkerPointHash == ASURA_HASH_ID_UNSET, "Unable to get marker point for damage request" );
//			m_xStartPosition = pxPhysicalObject->GetCurrentPosition();
//		}
//	}
//}

/// End of function 'AVP_EntityDamage_RequestLinear::AVP_EntityDamage_RequestLinear'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestLinear::Apply
// Author       : Andy Tate
// Date         : 3-Jan-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_EntityDamage_RequestLinear::Apply(AVP_ServerEntity_PhysicalObject& xFrom) const
{
	if(Asura_Network::IsServer())
	{
		if (m_uImpactGuid == ASURA_GUID_UNREGISTERED)
		{
			Asura_Vector_3 xNormaliseDirection( m_xDirection ); 
			xNormaliseDirection.Normalise();
			return LOSTest(xFrom, m_xImpactPosition - m_xDirection, xNormaliseDirection, m_fDistance, 1.0f);
		}
		else
		{
			Asura_Vector_3 xVictimPosition(0.f, 0.f, 0.f); 
			// Transform the impact position back into world space
			const AVP_ClientEntity_PhysicalObject*	pxPhysicalObject	= AVP_ClientEntity_PhysicalObject::FindEntity(m_uImpactGuid);
			if ( pxPhysicalObject == NULL )
			{
				Asura_Assert( false, "Failed to find client physical-object to request linear damage to, using server position");
				const AVP_ServerEntity_PhysicalObject*	pxServer = AVP_ServerEntity_PhysicalObject::FindEntity(m_uImpactGuid);
				if ( pxServer )
				{
					xVictimPosition = pxServer->GetPosition();
				}
				else
				{
					Asura_Assert( false, "Failed to find server object to do damage to" );
				}
			}
			else
			{
				xVictimPosition = pxPhysicalObject->GetCurrentPosition();
			}
			Asura_Vector_3 xNormaliseDirection( m_xDirection ); 
			xNormaliseDirection.Normalise();
			return LOSTest(xFrom, m_xImpactPosition + xVictimPosition - m_xDirection, xNormaliseDirection, m_fDistance, 1.0f);		
		}
	}
	else
	{
		Asura_Assert(false, "Cannot call damage application functions on client-machines.");
		return(false);
	}
}

/// End of function 'AVP_EntityDamage_RequestLinear::Apply'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestRadial::Apply
// Author       : Andy Tate
// Date         : 12-Jan-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_EntityDamage_RequestRadial::Apply(AVP_ServerEntity_PhysicalObject& xFrom) const
{
	if(Asura_Network::IsServer())
	{
		const float	fRadiusDifference	= m_fRadiusFor0PercentDamage - m_fRadiusFor100PercentDamage;
		bool		bSuccess			= false;

		// Validate the radius values...
		if(fRadiusDifference >= 0.0f)
		{
			const Asura_Vector_3& xPosition = xFrom.GetPosition();

			// Make a bounding box for the radial damage.
			Asura_Bounding_Box xAABB;
			xAABB.SetToPoint(xPosition);
			if (m_fRadiusFor0PercentDamage > 0.0f)
			{
				xAABB.Grow(m_fRadiusFor0PercentDamage);
			}

#if defined(AVP_USE_DAMAGE_DEBUGGING)
			if(dvs_bRenderDamageDebuggingRequestRadial)
			{
				Asura_DebugLineRender_LineSet*	pxLineSet	= Asura_DebugLineRender::CreateTimedLineSet(256, dvs_fRenderDamageDebuggingTimeBeforeFade, dvs_fRenderDamageDebuggingFadeDuration);

				pxLineSet->AddCross(xPosition, 0x80FFFFFF, 0.500f);
				pxLineSet->AddSphere(xPosition, m_fRadiusFor0PercentDamage, 0x80FF0000, 8, 4);
				pxLineSet->AddSphere(xPosition, m_fRadiusFor100PercentDamage, 0x80FFFF00, 8, 4);
			}
#endif	// defined(AVP_USE_DAMAGE_DEBUGGING)

			ApplyToBotanicals( xPosition );

			// Check the environment...
			for(u_int u = 0; u < g_uNumRadialDamageDirections; u++)
			{
				const Asura_Vector_3&	xDirection	= g_axRadialDamageDirections[u];

				float fSize = (m_fRadiusFor0PercentDamage!=0.0f)?m_fRadiusFor0PercentDamage*0.5f:0.2f; // half the damage radius?!

				const Asura_LOS_InputData	xLOSInput(xPosition - xDirection * 0.05f, xDirection, m_uIgnoreGuid, fSize );
				const Asura_LOS_OutputData	pxLOSOutput	= *Asura_LOS::TestAgainstEnvironment(&xLOSInput);

				if(pxLOSOutput.m_bHitEnvironment)
				{
					ApplyToEnvironment(xLOSInput, pxLOSOutput);
				}
			}

			// Check whether it is a physical object.
			Asura_Hashed_List_It< AVP_ServerActor_Vertebrate > xVertIt( AVP_ServerActor_Vertebrate::GetList() );
			for ( ; !xVertIt.Done(); xVertIt.Next() )
			{
				if ( xVertIt.GetCurrent()->IsEntityActive() && xVertIt.GetCurrent()->IsDead() )
				{
					bool bOK = ApplyToEntity(xFrom, *xVertIt.GetCurrent() );
					bSuccess |= bOK;
				}
			}

			// MGH 4/9/2008: The previous code just iterated over the physical objects. Using the broadphase physics query
			// should be faster (once it has been optimised), and is needed for the containers anyway.
			// Check the container and physical object entities for radial damage...
			// Do a rough AABB test to get any relevant entities.
			Asura_Physics_Broadphase_Query xQuery;
			xQuery.MakeQuery( xAABB, true, true, true, true );

			// MGH 12/9/2008: We need to make a temporary copy of the results, because the damage applied
			// to victims can cause the relevant rigid bodies to be destroyed (and reused) during the loop.
			// Accessing destroyed objects is generally not a great idea; although since these are stored in
			// a pool, it isn't all that dangerous in this case.
			s_xResultStorage.Reset();

			// Store the results.
			for ( const Asura_Physics_Broadphase_Entry* pxEntry = xQuery.GetFirstEntry(); pxEntry; pxEntry = pxEntry->GetNextEntry() )
			{
				Asura_Physics_RigidBodyInstance* pxBody = pxEntry->GetInstance();
				if (!pxBody) continue;

				const Asura_Guid uTargetGuid = pxBody->GetAssociatedEntityGuid();
				if (uTargetGuid == ASURA_GUID_UNREGISTERED ||
					uTargetGuid == GetIgnoreGuid())
				{
					continue;
				}

				s_xResultStorage.AddResult(pxBody);
			}

			// Apply the results.
			for (u_int u = 0 ; u < s_xResultStorage.GetNumResults(); ++u)
			{
				const AVP_PhysicsQueryResult& xResult = s_xResultStorage.GetResult(u);
				const Asura_Guid uTargetGuid = xResult.m_uEntityGuid;
			
				// Check whether it is a physical object.
				AVP_ServerEntity_PhysicalObject* const pxPhysicalObject = AVP_ServerEntity_PhysicalObject::FindEntity(uTargetGuid);
				if (pxPhysicalObject)
				{
					if ( pxPhysicalObject->IsEntityActive() &&
						!pxPhysicalObject->IsDestroyed() &&
						!pxPhysicalObject->IsMergedWithEnvironment() )
					{
						bool bOK = ApplyToEntity(xFrom, *pxPhysicalObject);
						bSuccess |= bOK;
					}
				}
#ifdef ASURA_USE_CONTAINER_SYSTEM
				else
				{
					// Check whether it is a container.
					AVP_ServerEntity_Container* const pxContainer = AVP_ServerEntity_Container::FindEntity(uTargetGuid);
					if ( pxContainer &&
						 pxContainer->IsEntityActive() &&
						!pxContainer->IsDestroyed() )
					{
						bool bOK = ApplyToContainerEntity(xFrom, *pxContainer, xResult);
						bSuccess |= bOK;
					}
				}
#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM
			}
		}
		else
		{
			Asura_Assertf( false, ( "Bad radius values - 100% = %.3f -> 0% = %.3f, ignoring damage.", m_fRadiusFor100PercentDamage, m_fRadiusFor0PercentDamage ) );
		}

		return( bSuccess );
	}
	else
	{
		Asura_Assert(false, "Cannot call damage application functions on client-machines.");
		return(false);
	}
}

/// End of function 'AVP_EntityDamage_RequestRadial::Apply'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestVolume::AVP_EntityDamage_RequestVolume
// Author       : Andy Tate
// Date         : 26-Feb-2008
////////////////////////////////////////////////////////////////////////////////

AVP_EntityDamage_RequestVolume::AVP_EntityDamage_RequestVolume(const AVP_EntityDamage_RequestVolume& xOther)
: PARENT(xOther)
{
	if(xOther.m_pxVolume == NULL)
	{
		m_pxVolume	= NULL;
	}
	else
	{
		m_pxVolume	= xOther.m_pxVolume->Duplicate();
	}
}

/// End of function 'AVP_EntityDamage_RequestVolume::AVP_EntityDamage_RequestVolume'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestVolume::~AVP_EntityDamage_RequestVolume
// Author       : Andy Tate
// Date         : 26-Feb-2008
////////////////////////////////////////////////////////////////////////////////

AVP_EntityDamage_RequestVolume::~AVP_EntityDamage_RequestVolume()
{
	delete(m_pxVolume);
}

/// End of function 'AVP_EntityDamage_RequestVolume::~AVP_EntityDamage_RequestVolume'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestVolume::operator=
// Author       : Andy Tate
// Date         : 27-Feb-2008
////////////////////////////////////////////////////////////////////////////////

AVP_EntityDamage_RequestVolume& AVP_EntityDamage_RequestVolume::operator=(const AVP_EntityDamage_RequestVolume& xOther)
{
	delete(m_pxVolume);
	if(xOther.m_pxVolume == NULL)
	{
		m_pxVolume	= NULL;
	}
	else
	{
		m_pxVolume	= xOther.m_pxVolume->Duplicate();
	}

	PARENT::operator=(xOther);
	return(*this);
}

/// End of function 'AVP_EntityDamage_RequestVolume::operator='. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_EntityDamage_RequestVolume::Apply
// Author       : Andy Tate
// Date         : 26-Feb-2008
////////////////////////////////////////////////////////////////////////////////

bool AVP_EntityDamage_RequestVolume::Apply(AVP_ServerEntity_PhysicalObject& xFrom) const
{
	if(Asura_Network::IsServer())
	{
		Asura_Assert(false, "Not yet implemented.");
		return(false);
	}
	else
	{
		Asura_Assert(false, "Cannot call damage application functions on client-machines.");
		return(false);
	}
}

/// End of function 'AVP_EntityDamage_RequestVolume::Apply'. ///


/// End of file. ///
