
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_ServerEntity_Projectile.cpp
// Author : Andy Tate
// Date   : 15-Nov-2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

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

// Asura independent header-files...
#include "Core/Asura_Timers.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Physics/Asura_Physics.h"
#include "Physics/Asura_Physics_Object.h"
// Asura platform header-files...

// Game independent header-files...
#include "Core/AVP_LOS.h"
#include "Entity/PhysicalObject/AVP_ClientEntity_PhysicalObject.h"
#include "Entity/PhysicalObject/Projectile/AVP_Projectile_Messages.h"
#include "Mesh/AVP_PolygonFlags.h"

// Game platform header-files...

// Tools header-files...


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


////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////
Asura_Hashed_List<AVP_ServerEntity_Projectile>	AVP_ServerEntity_Projectile::s_xList;

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


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

AVP_ServerEntity_Projectile::AVP_ServerEntity_Projectile(const Asura_Guid uGuid,
														 const Asura_Guid uOwnerGuid)
: Asura_Entity_Server(uGuid)
, m_fDamageMultiplier(1.0f)
, m_xPrevLOSPosition( Asura_Maths::ZeroVector3 )
, m_bIsDisarmed(false)
{
	// The actor who owns this projectile (for later classification on who killed who)
	SetOwnerGuid(uOwnerGuid);

	const AVP_ServerEntity_PhysicalObject*	pxServer	= AVP_ServerEntity_PhysicalObject::FindEntity(m_uOwnerGuid);

	// Initialise the physics-obect...
	m_pxDynamicObject	= Asura_Physics_Object::Create(ASURA_PHYSICS_OBJECT_DYNAMIC);
	m_pxDynamicObject->SetIgnore(true);
	m_pxDynamicObject->SetAssociatedEntityGuid(GetGuid());

	if(pxServer == NULL)
	{
		const AVP_ClientEntity_PhysicalObject*	pxClient	= AVP_ClientEntity_PhysicalObject::FindEntity(m_uOwnerGuid);

		if(pxClient == NULL)
		{
			Asura_Assert(false, "Cannot find owner-entity when creating projectile.");
		}
		else
		{
			const Asura_Handle	iHandle	= pxClient->GetPhysicsObjectHandle();

			m_pxDynamicObject->SetHandleToIgnore(iHandle);
		}
	}
	else
	{
		const Asura_Handle	iHandle	= pxServer->GetPhysicsObjectHandle();

		m_pxDynamicObject->SetHandleToIgnore(iHandle);
	}

	// Ensure we're in the projectile list...
	s_xList.AddToList( this );
}

AVP_ServerEntity_Projectile::~AVP_ServerEntity_Projectile()
{
	// Destroy the physics-object (not forgetting to NULL the pointer to prevent
	// parent classes from also attempting to destroy it)...
	Asura_Physics_Object::Destroy(m_pxDynamicObject);
	m_pxDynamicObject	= NULL;

	s_xList.RemoveFromList( this );
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ServerEntity_Projectile::CheckForCollisionsUsingLOS
// Author       : Andy Tate
// Date         : 15-Nov-2006
////////////////////////////////////////////////////////////////////////////////

bool AVP_ServerEntity_Projectile::CheckForCollisionsUsingLOS( const Asura_Vector_3& xPosition, const Asura_Vector_3& xVelocity )
{
	bool bHit = false;
	if ( !xPosition.ApproximatelyEquals( m_xPrevLOSPosition ) )
	{
		if ( m_xPrevLOSPosition == Asura_Maths::ZeroVector3 )
		{
			m_xPrevLOSPosition = xPosition;
		}
		const float					fTimeStep		= Asura_Maths::Max(Asura_Physics_System::GetTimeSlice(), Asura_Timers::GetGameTimeSlice());
		Asura_Vector_3				xDirection( ( xVelocity * fTimeStep ) + xPosition - m_xPrevLOSPosition);
		const float					fDistance	= xDirection.Normalise();

		const Asura_LOS_InputData	xLOSInputData( m_xPrevLOSPosition, xDirection, GetGuid(), fDistance);
		const Asura_LOS_OutputData	xLOSEnvOutputData = *Asura_LOS::TestAgainstEnvironment(&xLOSInputData);
		
		// JG - 21 April 2009 - Replaced entities LOS test with the version using list of ignore Guids, to ignore the firer and the projectile
		// In multiplayer games was causing the LOS test to miss the target the first frame as it was hitting the firer
		Asura_LOS_OutputData	xLOSEntOutputData;
		xLOSEntOutputData.Reset( fDistance ); 

		unsigned int auIgnoreGuids[2] = { GetGuid(), GetOwnerGuid() };
		AVP_LOS::ExceptionFunctor xFunctor( xLOSEntOutputData, 0, NULL, 2, auIgnoreGuids );
		Asura_Entity_System::TestEntitiesWithLOSFunctor( &xLOSInputData, xFunctor );

#if 0
		{
			const Asura_Vector_3&	xEntityPosition	= GetPosition();

			// Debugging...
			Asura_OutputToDebugger(("xEntityPosition = (%.3f, %.3f, %.3f), xPosition = (%.3f, %.3f, %.3f), xDirection = (%.3f, %.3f, %.3f), fDistance = %.3f, fTimeStep = %.3f\n",  xEntityPosition[0], xEntityPosition[1], xEntityPosition[2],  xPosition[0], xPosition[1], xPosition[2],  xDirection[0], xDirection[1], xDirection[2],  fDistance,  fTimeStep));
		}
#endif

		// Did we hit an entity? Must be nearer than the environment hit.
		if ((!xLOSEnvOutputData.m_bHitEnvironment || (xLOSEntOutputData.m_fDistanceToIntersection < xLOSEnvOutputData.m_fDistanceToIntersection))
			&& (xLOSEntOutputData.m_fDistanceToIntersection < fDistance) && (xLOSEntOutputData.m_uHitEntity != 0) && (xLOSEntOutputData.m_uHitEntity != m_uOwnerGuid))
		{
			OnCollision(xLOSInputData, xLOSEntOutputData);
			bHit = true;
		}

		// Did we hit the environment? Can pass through entities. (unless we exploded when we hit someone)
		if ((xLOSEnvOutputData.m_fDistanceToIntersection < fDistance) && xLOSEnvOutputData.m_bHitEnvironment && !IsDestroyed() )
		{
			OnCollision(xLOSInputData, xLOSEnvOutputData);
			bHit = true;
		}

		m_xPrevLOSPosition += xDirection * fDistance;
	}

	return bHit;
}

/// End of function 'AVP_ServerEntity_Projectile::CheckForCollisionsUsingLOS'. ///



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

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ServerEntity_Projectile::Launch
// Author       : Andy Tate
// Date         : 15-Nov-2006
////////////////////////////////////////////////////////////////////////////////

void AVP_ServerEntity_Projectile::Launch (const Asura_Vector_3 &xPosition,
										  const Asura_Matrix_3x3 &xOrientation,
										  const Asura_Vector_3 &xVelocity,
										  const Asura_Guid uGuidToIgnore,
										  float fDamageMultiplier)
{
	const AVP_ClientEntity_PhysicalObject*	pxClientToIgnore		= AVP_ClientEntity_PhysicalObject::FindEntity(uGuidToIgnore);
	const AVP_ServerEntity_PhysicalObject*	pxServerToIgnore		= AVP_ServerEntity_PhysicalObject::FindEntity(uGuidToIgnore);
	const Asura_Handle						iPhysicsHandleToIgnore	= (pxServerToIgnore->HasPhysicsObject())
																	? pxServerToIgnore->GetPhysicsObjectHandle()
																	: pxClientToIgnore->GetPhysicsObjectHandle();
	const Asura_Guid						uParentGuid				= GetParentGuid();

	// Remove any linkage we may have...
	if(uParentGuid != ASURA_GUID_UNREGISTERED)
	{
		UnlinkFromParent();
	}

	m_fDamageMultiplier = fDamageMultiplier;

	// Update the physics object...
	m_pxDynamicObject->SetPosition(xPosition);
	m_pxDynamicObject->SetOrientation(xOrientation);
	m_pxDynamicObject->ZeroAngularMomentum();
	m_pxDynamicObject->ZeroMomentum();
	m_pxDynamicObject->SetLinVelocity(xVelocity);
	m_pxDynamicObject->SetHandleToIgnore(iPhysicsHandleToIgnore);
	m_pxDynamicObject->SetFlagsOfPolysToIgnore(m_pxDynamicObject->GetFlagsOfPolysToIgnore() | AVP_PolygonFlag_IgnoreForPhysicsObjectCollision);

	// Ensure that we have an active entity...
	SetEntityActive(true);
		
	// Call the appropriate event...
	OnLaunch();

	MakeClientUpdateMessage();

	// we do this so that we don't miss collisions on the first frame.
	CheckForCollisionsUsingLOS( xPosition, xVelocity );
}

/// End of function 'AVP_ServerEntity_Projectile::Launch'. ///

void AVP_ServerEntity_Projectile::MakeClientUpdateMessage (void)
{
	Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_CLIENTUPDATE, sizeof(AVP_Message_ProjectileClientUpdate), GetClassification());

	if(pxMessage != NULL)
	{
		// Fill in the message...
		AVP_Message_ProjectileClientUpdate* pxData = static_cast<AVP_Message_ProjectileClientUpdate*>(pxMessage->GetData());
		pxData->FillProjectile(this);

		// Send the message...
		Send(pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES);
	}
}

/// End of file. ///
