
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_ClientProjectile_GenericProjectile.cpp
// Author : Chris Wallace
// Date   : 27-Oct-2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

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

// Asura independent header-files...
#include "Animation/Asura_Animation.h"
#include "Animation/Asura_AnimationController_Animation.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "Network/Asura_Message.h"
#include "Sound/Asura_Sound_Event_Storage.h"
#include "SpecialFX/pfx/Asura_PFX_Entity_Effect.h"

// Game independent header-files...
#include "Actor/Vertebrates/AVP_Vertebrate_AnimationHandler.h"
#include "Animation/AVP_AnimationAttachment_Ragdoll.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Camera/AVP_CameraShake_System.h"
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/AVP_Messages.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ClientActor_Predator.h"
#include "Entity/PhysicalObject/Projectile/AVP_Projectile_Messages.h"
#include "Entity/PhysicalObject/Projectile/AVP_GenericProjectileData.h"
#include "Input/AVP_Rumble_System.h"


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

static const Asura_Hash_ID g_auCorpseMarkerPointHashes[ AVP_MAX_CORPSES ] = 
{
	0xb5426da4,	// corpse_01
	0xb5426da5,	// corpse_02
	0xb5426da6,	// corpse_03
	0xb5426da7,	// corpse_04
	0xb5426da8	// corpse_05
};

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

Asura_Hashed_List<AVP_ClientProjectile_GenericProjectile>	AVP_ClientProjectile_GenericProjectile::s_xList;


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


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

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientProjectile_GenericProjectile::AVP_ClientProjectile_GenericProjectile
// Author       : Chris Wallace
// Date         : 27-Oct-2006
////////////////////////////////////////////////////////////////////////////////

AVP_ClientProjectile_GenericProjectile::AVP_ClientProjectile_GenericProjectile( const AVP_Message_ProjectileClientUpdate* pxData )
: m_pxAnimation( NULL )
, m_pxProjectileData( AVP_GenericProjectileData::GetDataByHash( pxData->GetBlueprintHash() ) )
, m_iPFXTrailHandle( ASURA_INVALID_HANDLE )
, m_bPFXAttached( false )
, m_bIsEmbedded( false )
, m_fScale( 1.0f )
, m_iSoundHandle( ASURA_INVALID_HANDLE )
, m_pxContainerAttachment( NULL )
, m_pxPFXAttachment( NULL )
, m_uNumCorpses( 0 )
{
	// Set the classification...
	Classification = AVP_ENTITYCLASS_GENERICPROJECTILE;
	
	SetBlueprintTypeHashID( uAVP_BLUEPRINT_TYPE_PROJECTILES );

	m_fScale = pxData->GetScale();

	Asura_Hash_ID uSkin = m_pxProjectileData->GetSkin();
	if ( uSkin != ASURA_HASH_ID_UNSET )
	{
		// Initialise the shape-instance...
		Asura_Shape_Instance& xShapeInstance = GetShapeInstance();
		m_pxAnimation	= new Asura_Animation( uSkin );
		xShapeInstance.SetAnimationPointer( m_pxAnimation );
		SetClientControlsShapeSkinAndAnim(true);

		Asura_Hash_ID uRestAnim = m_pxProjectileData->GetRestAnim();
		if ( uRestAnim != ASURA_HASH_ID_UNSET )
		{
			u_int uFlags = AAC_FLAG_ACTIVE | AAC_FLAG_DESTROY_WHEN_FINISHED | AACA_FLAG_LOOPED;
			u_int uPriority = AVP_AAPC_GENERAL_ANIM;
			Asura_AnimationBlend* pxBlend = new Asura_AnimationBlend( 1.0f );
			Asura_AnimationController_Animation* pxAACA = m_pxAnimation->CreateControllerAnimation( uFlags, uPriority, uRestAnim, pxBlend );
			if ( pxAACA == NULL )
			{
				Asura_Assert( false, "Unable to create rest anim" );
				delete pxBlend;
			}
			else
			{
				pxAACA->Play();
			}
		}

		// cloakable projectiles
		AVP_ClientActor_Predator* pxPredator = AVP_ClientActor_Predator::FindEntity( pxData->GetOwnerGuid() );
		if ( pxPredator != NULL && pxPredator->IsCloaked(false) )
		{
			m_pxAnimation->SetParentEntity( pxData->GetOwnerGuid() );
			xShapeInstance.SetAlpha(0.5f);
		}
	}

	AttachContainerTrail();


	// Don't forget to store us in the list...
	s_xList.AddToList(this);
	Asura_Entity_Client::AddToUpdateableClientList(this);
}

/// End of function 'AVP_ClientProjectile_GenericProjectile::AVP_ClientProjectile_GenericProjectile'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientProjectile_GenericProjectile::~AVP_ClientProjectile_GenericProjectile
// Author       : Chris Wallace
// Date         : 3-Oct-2006
////////////////////////////////////////////////////////////////////////////////

AVP_ClientProjectile_GenericProjectile::~AVP_ClientProjectile_GenericProjectile ()
{
	Asura_Sound_Event_System::Stop(m_iSoundHandle);

	DestroyTrails();

	s_xList.RemoveFromList( this );
}

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


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

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientProjectile_GenericProjectile::Create
// Author       : Chris Wallace
// Date         : 27-Oct-2006
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientProjectile_GenericProjectile::Create( const Asura_Message *pxMessage )
{
	const AVP_Message_ProjectileClientUpdate* pxData = static_cast< const AVP_Message_ProjectileClientUpdate* >( pxMessage->GetData() );
	AVP_ClientProjectile_GenericProjectile*	pxClient	= new AVP_ClientProjectile_GenericProjectile( pxData );

	// Pass the message to our new client...
	pxClient->HandleMessage(pxMessage);
}

/// End of function 'AVP_ClientProjectile_GenericProjectile::Create'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientProjectile_GenericProjectile::Update
// Author       : Chris Wallace
// Date         : 30-Oct-2006
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientProjectile_GenericProjectile::Update (void)
{
	Asura_Hash_ID uMarkerHash = m_pxProjectileData->GetTrailMarkerPoint();
	Asura_Handle iPFXEffectID = ASURA_INVALID_HANDLE;
	Asura_Hash_ID uPFXHash = m_pxProjectileData->GetTrailPFX();

	if(uPFXHash != ASURA_HASH_ID_UNSET)
	{
		if ( m_iPFXTrailHandle == ASURA_INVALID_HANDLE && !IsDestroyed() )
		{
			if(!m_bPFXAttached)
			{
				iPFXEffectID = Asura_PFX_Entity_Effect::Create(GetCurrentPosition(), GetCurrentOrientation(), m_pxProjectileData->GetTrailPFX(), 0, ASURA_PFX_EFFECT_CAMERA_FLAGS | ASURA_PFX_EFFECT_PRESERVE_WHILE_INACTIVE, GetGuid());
				Asura_PFX_Entity_Effect* pxEffect = static_cast< Asura_PFX_Entity_Effect* >( Asura_PFX_System::GetEffect( iPFXEffectID ) );
				if ( pxEffect )
				{
					pxEffect->SetSizeScale( m_fScale );
				}
			
				Asura_Animation* pxAnim  = GetAnimation();
				if(pxAnim && uMarkerHash != ASURA_HASH_ID_UNSET)
				{
					m_pxPFXAttachment = new Asura_AnimationAttachment_PFX(iPFXEffectID, true);
					if (m_pxPFXAttachment)
					{
						m_bPFXAttached = m_pxPFXAttachment->LinkAttachment(pxAnim, uMarkerHash, false);
					}		
				}
				else
				{
					// Store the handle
					m_iPFXTrailHandle = iPFXEffectID;
				}
			}
		}
		else
		{
			if(!m_bPFXAttached) // No marker so not attached, move manually
			{
				Asura_PFX_Entity_Effect* pxEffect = static_cast< Asura_PFX_Entity_Effect* >( Asura_PFX_System::GetEffect( m_iPFXTrailHandle ) );
				if ( pxEffect )
				{
					pxEffect->SetPosition( GetCurrentPosition() );
					pxEffect->SetOrientation( GetCurrentOrientation() );
				}
			}
		}
	}


	UpdateProjectileSound();

	Asura_Guid uOwnerGuid = m_pxAnimation != NULL ? m_pxAnimation->GetParentEntityGUID() : ASURA_GUID_UNREGISTERED;
	if ( uOwnerGuid != ASURA_GUID_UNREGISTERED )
	{
		AVP_ClientActor_Predator* pxPredator = AVP_ClientActor_Predator::FindEntity( uOwnerGuid );
		if ( pxPredator != NULL && !pxPredator->IsCloaked(false) )
		{
			m_pxAnimation->SetParentEntity( ASURA_GUID_UNREGISTERED );
			GetShapeInstance().SetAlpha( 1.0f );
		}
	}

	PARENT::Update();
}

/// End of function 'AVP_ClientProjectile_GenericProjectile::Update'. ///

void AVP_ClientProjectile_GenericProjectile::UpdateProjectileSound(void)
{
	if ( m_pxProjectileData->GetSoundEventHash() != ASURA_HASH_ID_UNSET )
	{
		if (m_iSoundHandle != ASURA_INVALID_HANDLE && !Asura_Sound_Event_System::IsPlaying(m_iSoundHandle))
		{
			Asura_Sound_Event_System::Stop( m_iSoundHandle );
		}
		if ( m_iSoundHandle == ASURA_INVALID_HANDLE )
		{
			//create the sound here
			const Asura_Sound_Event* pxSoundEvent = Asura_Sound_Event_Storage::FindEvent( m_pxProjectileData->GetSoundEventHash() );
			Asura_Sound_Event_System::PlayEvent( pxSoundEvent, &m_iSoundHandle, &GetCurrentPosition(), 1.0f, GetGuid() );
		}
		else
		{
			//If we still have a sound playing update it's position
			Asura_Sound_Event_System::UpdatePosition( m_iSoundHandle, GetCurrentPosition() );
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientProjectile_GenericProjectile::HandleMessage
// Author       : Chris Wallace
// Date         : 21-Feb-2007
////////////////////////////////////////////////////////////////////////////////

bool AVP_ClientProjectile_GenericProjectile::HandleMessage (const Asura_Message* pxMessage)
{
	// What sort of message is it?
	switch(pxMessage->GetMsgID())
	{
		case ASURA_MSG_CLIENTUPDATE:
		{
			// Catch projectile specific date
			const AVP_Message_GenericProjectileClientUpdate*	pxData	= static_cast<const AVP_Message_GenericProjectileClientUpdate*>(pxMessage->GetData());
			ProcessClientUpdate(pxData, pxMessage->GetSentTime());
			return PARENT::HandleMessage( pxMessage );

		}
		case ASURA_MSG_DESTRUCT:
		{
			MakeExplosion();
			return(PARENT::HandleMessage(pxMessage));
		}
		// projectiles don't get damaged (but message handled)
		case AVP_MSGID_ENTITYDAMAGE_APPLY_TO_ENTITY:
		{
			return(true);
		}

		default:
		{
			return(PARENT::HandleMessage(pxMessage));
		}
	}		// End of switch.
}

/// End of function 'AVP_ClientProjectile_GenericProjectile::HandleMessage'. ///

void AVP_ClientProjectile_GenericProjectile::AddRagdollCorpse( AVP_Vertebrate_AnimationHandler* pxVertAnimHandler, int iBoneID )
{
	for ( u_int uItr = m_uNumCorpses - 1; uItr < AVP_MAX_CORPSES; --uItr )
	{
		if ( ( uItr + 1 ) < AVP_MAX_CORPSES && ( uItr + 1 ) < m_pxProjectileData->GetNumCorpsesToCollect() )
		{
			m_apxRagdollAttachments[ uItr ]->SetDeleteWhenUnlinked( false );
			m_apxRagdollAttachments[ uItr ]->UnlinkAttachment( false );
			m_apxRagdollAttachments[ uItr ]->SetDeleteWhenUnlinked( true );
			m_apxRagdollAttachments[ uItr ]->LinkAttachment( GetShapeInstance().GetAnimation(), g_auCorpseMarkerPointHashes[ uItr + 1 ] );
			m_apxRagdollAttachments[ uItr + 1 ] = m_apxRagdollAttachments[ uItr ];
		}
		else
		{
			m_apxRagdollAttachments[ uItr ]->UnlinkAttachment( true );
			--m_uNumCorpses;
		}
	}
	m_apxRagdollAttachments[ 0 ] = new AVP_AnimationAttachment_Ragdoll( pxVertAnimHandler->GetRagdollHandle(), iBoneID );
	m_apxRagdollAttachments[ 0 ]->LinkAttachment( GetShapeInstance().GetAnimation(), g_auCorpseMarkerPointHashes[ 0 ] );
	++m_uNumCorpses;
}

bool AVP_ClientProjectile_GenericProjectile::CollectsCorpses()
{
	return m_pxProjectileData->GetNumCorpsesToCollect() > 0;
}

void AVP_ClientProjectile_GenericProjectile::MakeExplosion()
{
	//create an explosion here.
	if ( m_pxProjectileData->GetExplosionPFX() != ASURA_HASH_ID_UNSET )
	{
		int iEffect = Asura_PFX_Entity_Effect::Create( GetCurrentPosition(), GetCurrentOrientation(), m_pxProjectileData->GetExplosionPFX(), 0, ASURA_PFX_EFFECT_CAMERA_FLAGS, GetGuid() );
		Asura_PFX_Entity_Effect* pxEffect = static_cast< Asura_PFX_Entity_Effect* >( Asura_PFX_System::GetEffect( iEffect ) );
		if ( pxEffect )
		{
			pxEffect->SetSizeScale( m_fScale );
		}
	}

	if(m_pxProjectileData->GetCameraShakeHash() != ASURA_HASH_ID_UNSET) // Do a cam-shake event...
	{
		AVP_CameraShake_System::StartShake( m_pxProjectileData->GetCameraShakeHash(), GetGuid() );
	}

	DestroyTrails();

	// Rumble
	if( AVP_Settings::GetCurrentProfile()->GetControls().GetVibrate() && m_pxProjectileData->GetVibrationHash() != ASURA_HASH_ID_UNSET)
	{
		AVP_Rumble_System::AddWorldRumble( m_pxProjectileData->GetVibrationHash(), GetCurrentPosition(),  
										   m_pxProjectileData->GetVibrationOuterRadius(),  m_pxProjectileData->GetVibrationInnerRadius() );
	}
}

void AVP_ClientProjectile_GenericProjectile::ProcessClientUpdate( const AVP_Message_GenericProjectileClientUpdate* pxData, const float fServerTime, const bool bProcessPosAndOri /*= true*/ )
{
	if ( m_bIsEmbedded != pxData->GetIsEmbedded() )
	{
		m_bIsEmbedded = pxData->GetIsEmbedded();
		OnEmbed();
	}
}


void AVP_ClientProjectile_GenericProjectile::AttachContainerTrail()
{
	if( m_pxAnimation && m_pxProjectileData )
	{
		Asura_Hash_ID uContainerHash = m_pxProjectileData->GetContainerTrailHash();

		if( uContainerHash != ASURA_HASH_ID_UNSET )
		{		
			const Asura_Hash_ID uMarkerHash = m_pxProjectileData->GetTrailMarkerPoint();

			if(uMarkerHash != ASURA_HASH_ID_UNSET)
			{
				m_pxContainerAttachment = new Asura_AnimationAttachment_ClientContainerInstance( uContainerHash, Asura_Container_InputState::On() );
				if (m_pxContainerAttachment)
				{
					m_pxContainerAttachment->LinkAttachment(m_pxAnimation, uMarkerHash, false);	
				}
			}
		}
	}
}

void AVP_ClientProjectile_GenericProjectile::DestroyTrails()
{
	if (m_pxContainerAttachment != NULL)
	{
		if (m_pxContainerAttachment->IsLinked())
		{
			m_pxContainerAttachment->UnlinkAttachment(false);
		}
		
		delete m_pxContainerAttachment;
		m_pxContainerAttachment = NULL;
	}

	if(m_pxPFXAttachment)
	{
		if(m_pxPFXAttachment->IsLinked())
		{
			m_pxPFXAttachment->UnlinkAttachment(false);
		}
		delete m_pxPFXAttachment;
		m_pxPFXAttachment = NULL;
	}

	if(m_iPFXTrailHandle != ASURA_INVALID_HANDLE)
	{
		Asura_PFX_Entity_Effect* pxEffect = static_cast< Asura_PFX_Entity_Effect* >( Asura_PFX_System::GetEffect( m_iPFXTrailHandle ) );

		if ( pxEffect != NULL )
		{
			pxEffect->Stop( false );
		}
		m_iPFXTrailHandle = ASURA_INVALID_HANDLE;
	}
}


/// End of file. ///
