/**
*
* \file AVP_ServerEntity_MultiDamageVolume.cpp
*
* Created by Andrew Weinkove - 14/04/2004
*
* Copyright (C) 2004 Rebellion, All Rights Reserved.
*
* Project-implementation of Multi-Damage volumes
*
**/

#include "Core/Asura.h"

#include "Entity/Triggers/AVP_ServerEntity_MultiDamageVolume.h"

#include "Network/Asura_Network.h"

#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Damage/AVP_EntityDamage_Messages.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Alien/AVP_ServerActor_Alien.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AlienQueen/AVP_ServerActor_AlienQueen.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_ServerActor_Human.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ServerActor_Predator.h"
#include "Volume/Asura_VolumeGroup.h"
#include "Volume/Asura_Volume_Cuboid.h"


#include "Core/Asura_Timers.h"

#ifdef ASURA_TOOLS
const char* AVP_ServerEntity_MultiDamageVolume::s_aszAVPVolumeTypes[NUM_AVP_MULTIDAMAGEVOLUME_TYPES] = 
{
	"Predator AI",
	"Alien AI",
	"Human AI",
	"Alien Queen AI",
	"Player",
};
#endif //ASURA_TOOLS

u_int g_uChunkVersion = 2;
float AVP_ServerEntity_MultiDamageVolume::s_fTimeBetweenChecks = 1.0f;

Asura_Hashed_List<AVP_ServerEntity_MultiDamageVolume> AVP_ServerEntity_MultiDamageVolume::s_xList(3);

AVP_ServerEntity_MultiDamageVolume::AVP_ServerEntity_MultiDamageVolume( Asura_Guid uGuid )
: Asura_ServerEntity_MultiDamageVolume( uGuid )
{
	// set the Player flag to be on by default here in the project specific constructor. 
	m_iListFlags = ( 1 << AVP_MDVT_PLAYER );
	m_fTimeToNextCheck = 0.0f;

	s_xList.AddToList(this);
}

AVP_ServerEntity_MultiDamageVolume::~AVP_ServerEntity_MultiDamageVolume()
{
	s_xList.RemoveFromList(this);
}

void AVP_ServerEntity_MultiDamageVolume::ApplyDamage()
{
// 	(AW - 09.02.2004) - If we're using dynamics, we only do this once so do it immediately to
// 		// make this feel more responsive.
	if (!m_bUsesDynamicsWhenActive)
	{
		m_fTimeToNextCheck -= Asura_Timers::GetGameTimeSlice();
		if (m_fTimeToNextCheck > 0.0f)
		{
			return;
		}
		m_fTimeToNextCheck = s_fTimeBetweenChecks;
		if (m_bInstantDeath)
		{
			// (AW - 17.03.2005) - Bit faster for instant death ones. This is because these are 
			// sometimes used to bolster falling damage when jumping out of helicopters and it 
			// feels wrong if there's a delay before death.
			m_fTimeToNextCheck *= 0.2f;
		}
	}

	if (m_iListFlags & (1 << AVP_MDVT_PREDATOR))
	{
		for( Asura_Hashed_List_ConstIt<AVP_ServerActor_Predator> xIt(AVP_ServerActor_Predator::GetList()); !xIt.Done(); xIt.Next() )
		{
			const AVP_ServerActor_Predator* pxPredator = xIt.GetCurrent();
			const Asura_Vector_3 xPosition = pxPredator->GetPosition();

			if (!pxPredator->IsDead() &&
				!pxPredator->IsPlayer()&&
				Contains(xPosition))
			{
				ApplyDamageToActor(pxPredator);
			}
		}
	}
	if (m_iListFlags & (1 << AVP_MDVT_ALIEN))
	{
		for( Asura_Hashed_List_ConstIt<AVP_ServerActor_Alien> xIt(AVP_ServerActor_Alien::GetList()); !xIt.Done(); xIt.Next() )
		{
			const AVP_ServerActor_Alien* pxAlien = xIt.GetCurrent();
			const Asura_Vector_3 xPosition = pxAlien->GetPosition();

			if (!pxAlien->IsDead()	&&
				!pxAlien->IsPlayer()&&
				Contains(xPosition))
			{
				ApplyDamageToActor(pxAlien);
			}
		}
	}
	if (m_iListFlags & (1 << AVP_MDVT_HUMAN))
	{
		for( Asura_Hashed_List_ConstIt<AVP_ServerActor_Human> xIt(AVP_ServerActor_Human::GetList()); !xIt.Done(); xIt.Next() )
		{
			const AVP_ServerActor_Human* pxHuman = xIt.GetCurrent();
			const Asura_Vector_3 xPosition = pxHuman->GetPosition();
			
			if (!pxHuman->IsDead() &&
				!pxHuman->IsPlayer()&&
				Contains(xPosition))
			{
				ApplyDamageToActor(pxHuman);
			}
		}
	}
	if (m_iListFlags & (1 << AVP_MDVT_ALIENQUEEN))
	{
		for( Asura_Hashed_List_ConstIt<AVP_ServerActor_AlienQueen> xIt(AVP_ServerActor_AlienQueen::GetList()); !xIt.Done(); xIt.Next() )
		{
			const AVP_ServerActor_AlienQueen* pxAlienQueen = xIt.GetCurrent();
			const Asura_Vector_3 xPosition = pxAlienQueen->GetPosition();

			if (!pxAlienQueen->IsDead() &&			
				!pxAlienQueen->IsPlayer()&&
				Contains(xPosition))
			{
				ApplyDamageToActor(pxAlienQueen);
			}
		}
	}
	if (m_iListFlags & (1 << AVP_MDVT_PLAYER))
	{
		for( Asura_Hashed_List_ConstIt<AVP_ServerActor_Vertebrate> xIt( AVP_ServerActor_Vertebrate::GetList() ); !xIt.Done(); xIt.Next() )
		{
			const AVP_ServerActor_Vertebrate* pxVertebrate = xIt.GetCurrent();
			const Asura_Vector_3 xPosition = pxVertebrate->GetPosition();

			if ( !pxVertebrate->IsDead() &&			
				 pxVertebrate->IsPlayer()&&
				 Contains( xPosition ) )
			{
				// if this entity is a Player, not dead and in the zone apply the damage. 
				ApplyDamageToActor(pxVertebrate);
			}
		}
	}	
}

void AVP_ServerEntity_MultiDamageVolume::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	// Write the version...
	xStream << g_uChunkVersion;

	// Write the parent data...
	PARENT_ASURA::WriteToChunkStream(xStream);
}

void AVP_ServerEntity_MultiDamageVolume::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	// Write the version...
	u_int uVersion;
	xStream >> uVersion;

	// Write the parent data...
	PARENT_ASURA::ReadFromChunkStream(xStream);

	if(uVersion > g_uChunkVersion)
	{
		Asura_Assert(false, "Unsupported version");
	}
	else if( uVersion < 2 )
	{
		// CAH, if the version is less than 2 and the player flag is not raised raise it here. 
		// Because: player flag was added at revision 2 and we want any previously saved volumes to have player ticked by default. 
		if( !( m_iListFlags & ( 1 << AVP_MDVT_PLAYER ) ) ) 
		{
			m_iListFlags += ( 1 << AVP_MDVT_PLAYER );
		}
	}
}

#ifdef ASURA_TOOLS
void AVP_ServerEntity_MultiDamageVolume::ApplyChangesForTemplateInstance(class Tools_EntityTemplateInstance* pxInstance)
{
	PARENT_ASURA::ApplyChangesForTemplateInstance(pxInstance);
}

bool AVP_ServerEntity_MultiDamageVolume::CanBeIncludedInTemplate() const
{
	return true;
}
#endif //ASURA_TOOLS

void AVP_ServerEntity_MultiDamageVolume::ApplyDamageToActor( const AVP_ServerEntity_Actor* pxActor )
{
	float fDamage = m_fDamageRate;
	if (m_bInstantDeath)
	{
		fDamage = Asura_Maths::LargeFloat;
	}

	Asura_Message* pxMessage = Asura_Network::CreateMessage(AVP_MSGID_ENTITYDAMAGE_REQUEST_VICTIM, sizeof(AVP_Message_EntityDamage_RequestVictim));

	if(pxMessage != NULL)
	{
		AVP_Message_EntityDamage_RequestVictim*	pxData			= static_cast<AVP_Message_EntityDamage_RequestVictim*>(pxMessage->GetData());
		const Asura_Guid						uAttackerGuid	= GetGuid();
		const Asura_Guid						uVictimGuid		= pxActor->GetGuid();
		const Asura_Vector_3&					xPosition		= pxActor->GetPosition();
		const Asura_Matrix_3x3&					xOrientation	= pxActor->GetOrientation();
		const Asura_Vector_3					xDirection(xOrientation.e31, xOrientation.e32, xOrientation.e33);
		const Asura_Vector_3					xNormal(xOrientation.e21, xOrientation.e22, xOrientation.e23);

		const AVP_EntityDamage_RequestVictim	xDamage(uAVP_BLUEPRINT_NAME_DAMAGE_VOLUME,
														uAttackerGuid,
														ASURA_HASH_ID_UNSET,
														uVictimGuid,
														fDamage,
														xPosition,
														xDirection,
														xNormal);

		pxData->SetDamage(xDamage);
		Send(pxMessage, pxActor->GetGuid());
	}
}


