
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_InventoryWeapon_Mine.cpp
// Author : Chris Wallace
// Date   : 25-Feb-2009
//
// Copyright (C) 2009 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header...
#include "Inventory/Items/Weapons/AVP_InventoryWeapon_Mine.h"

// Asura independent header-files...
#include "Blueprint/Asura_Blueprint.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "Entity/PhysicalObject/Moves/Asura_Movement_System.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"

// Game independent header-files...
#include "Actor/Vertebrates/AVP_Vertebrate_Focus.h"
#include "Actor/Vertebrates/Predator/AVP_Predator_AnimationHandler.h"
#include "Actor/Vertebrates/Predator/AVP_Predator_InputHandler.h"
#include "Animation/AVP_AnimFlags.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Core/AVP_Game_Core.h"
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Damage/AVP_EntityDamage_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_Actor_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_ClientActor_Vertebrate.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_ServerActor_Vertebrate.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ClientActor_Predator.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ServerActor_Predator.h"
#include "Entity/PhysicalObject/Projectile/AVP_ClientProjectile_Disc.h"
#include "Entity/PhysicalObject/Projectile/AVP_ServerProjectile_Disc.h"
#include "Entity/PhysicalObject/Projectile/AVP_Projectile_Messages.h"
#include "Entity/PhysicalObject/Projectile/AVP_GenericProjectileData.h"
#include "Inventory/AVP_Inventory_Group.h"
#include "Inventory/AVP_Inventory_HashIDs.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Inventory/Items/Weapons/AVP_RangedWeapon_ClientEffectsHandler.h"
#include "Moves/AVP_Movement_System.h"

AVP_InventoryWeapon_Mine::AVP_InventoryWeapon_Mine( AVP_Inventory_Group& xParentGroup, const Asura_Hash_ID uHash, Asura_Hash_ID uBlueprintHash )
: PARENT( xParentGroup, uHash, uBlueprintHash )
{
	m_eItemType = AVP_INVENTORYITEMTYPE_PREDATORMINE;
}

int AVP_InventoryWeapon_Mine::GetSubTypeIndexFromUsageType(const u_int uUsageType) const
{
	switch(uUsageType)
	{
		case UT_CHARGED_SHOT_PRIMARY:
		{
			return( ST_PRIMARY );
		}

		case UT_DETONATE_ALL:
		{
			return( ST_SECONDARY );
		}

		case UT_NONE:
		case UT_RELOAD_ALL:
		case UT_MELEE:
		{
			return(-1);
		}

		default:
		{
			Asura_Assert(false, "Unexpected usage-type for this inventory-item.");
			return(-1);
		}
	}
}

void AVP_InventoryWeapon_Mine::StartUsing( const u_int uUsageType )
{
	PARENT::StartUsing( uUsageType );

	switch( uUsageType )
	{
		case UT_CHARGED_SHOT_PRIMARY:
		case UT_DETONATE_ALL:
		{
			HandleStartFiringEffects( uUsageType );
		}
		break;

		default:
		{
			// do nothing
		}
		break;
	}
}

void AVP_InventoryWeapon_Mine::ChargeClient( AVP_RangedWeapon_SubType* pxSubType )
{
	// Suppress normal charge behaviour for clients
}

void AVP_InventoryWeapon_Mine::Use(void)
{
	PARENT::ChargeClient( static_cast<AVP_RangedWeapon_SubType*>(GetSubTypeFromUsageType( GetCurrentUsageType() )) );
	PARENT::Use();
}

void AVP_InventoryWeapon_Mine::StopUsing( const u_int uUsageType )
{
	switch ( uUsageType )
	{
		case UT_CHARGED_SHOT_PRIMARY:
		{
			HandleStopFiringEffects( uUsageType, true );

			AVP_ClientEntity_Actor* pxActor = GetParentSystem().GetParentClient();
			AVP_Actor_AnimationHandler* pxAnimationHandler = pxActor ? pxActor->GetAnimationHandler() : NULL;
			if ( pxAnimationHandler != NULL )
			{
				pxAnimationHandler->AttachItem( this );
			}
			m_fChargedPower = 0.0f; // reset charge

			//AVP_InventoryItem_SubType* pxSubType = GetSubTypeFromUsageType( GetCurrentUsageType() );
			//pxSubType->IncreaseQuantity( 1.0f );
		}
		break;

		default:
		{
			// do nothing
		}
		break;
	}
}

void AVP_InventoryWeapon_Mine::SendCreateProjectileMessage( const AVP_GenericProjectileData* pxProjectileData, const AVP_RangedWeapon_SubType* pxSubType, u_int uUsageType, Asura_Hash_ID uMuzMP, float fRange, float fMaxRange, const Asura_Vector_3& xActorVel, const Asura_Vector_3& xFirePosition, const Asura_Vector_3& xShotDirection, const Asura_Matrix_3x3& xShotOrientation, Asura_Guid* puTargetGuid /*=NULL*/ )
{
	Asura_Guid uOwnerGuid = GetParentSystem().GetOwnerGuid();
	if ( GetParentSystem().IsMaster() )
	{
		Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSGID_CREATE_PROJECTILE_MINE, sizeof( AVP_Message_CreateProjectile_Mine ) );
		if ( pxMessage != NULL )
		{
			AVP_Message_CreateProjectile_Mine* pxData = static_cast< AVP_Message_CreateProjectile_Mine* >( pxMessage->GetData() );
			
			// Fill-in the message...
			pxData->SetProjectileBlueprintHash( pxProjectileData->GetProjectileHash() );
			pxData->SetPosition( xFirePosition );
			pxData->SetOrientation( Asura_Quat(xShotOrientation) );
			pxData->SetActorVelocity( xActorVel );

			float fDamageScale = 1.0f;
			if ( uUsageType == UT_CHARGED_SHOT_PRIMARY || uUsageType == UT_CHARGED_SHOT_SECONDARY )
			{
				fDamageScale = 1.0f;//pxSubType->GetDamageMultiplierPerCharge() * m_fChargedPower;
			}
			pxData->SetDamageScale( fDamageScale );
			pxData->SetLaunch( true );

			if(puTargetGuid)
			{
				pxData->SetTargetGuid(*puTargetGuid);
			}
			else
			{
				pxData->SetTargetGuid(ASURA_GUID_UNREGISTERED);
			}

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

void AVP_InventoryWeapon_Mine::DestroyAllMines(void)
{
	if(GetParentSystem().IsServerSide())
	{
		AVP_ServerActor_Predator* pxPredServer = static_cast<AVP_ServerActor_Predator*>(GetParentSystem().GetParentServer());
		if(pxPredServer)
		{
			Asura_ListElt<Asura_Guid>* pxCur = pxPredServer->GetMineGuidList()->Front();
			while(pxCur)
			{
				Asura_Guid uGuid = pxCur->Data();
				SendDestroyMineMessage(uGuid);
				pxCur = pxCur->Next(); // Get next while exists
			}

			pxPredServer->GetMineGuidList()->Clear();
		}
	}
}


void AVP_InventoryWeapon_Mine::SendDestroyMineMessage(Asura_Guid uMineGuid)
{
//	if ( GetParentSystem().IsMaster() )
	{
		Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSGID_DESTROY_MINE );
		if ( pxMessage )
		{
			pxMessage->SetFromGuid( GetParentSystem().GetOwnerGuid() );
			pxMessage->SetToGuid( uMineGuid );
			
			Asura_Network::Send( pxMessage );
		}
	}
}

void AVP_InventoryWeapon_Mine::ProcessPlayerInput( const AVP_ClientEntity_Actor& xActor )
{
	const AVP_Actor_InputHandler* pxInputHandler = xActor.GetInputHandler();
	const State eCurrentState = GetCurrentState();
	if ( pxInputHandler != NULL && ( GetTimeInState() > 0.0f ) )
	{
		if ( pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::SECONDARY_ATTACK_TAP ) > 0.0f )
		{
			// Detonate all mines in area	

			// CW - E3 HACK - Removed manual detonate as conflicts with focus mode.
			//RequestUsing( UT_DETONATE_ALL );
		}
		else if(pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::PRIMARY_ATTACK_TAP ) > 0.0f) // throw disc on press
		{
			if ( eCurrentState == IDLING)
			{
				AVP_RangedWeapon_SubType* pxSubType = static_cast<AVP_RangedWeapon_SubType*>(GetSubTypeFromUsageType(UT_CHARGED_SHOT_PRIMARY));
				if(pxSubType)
				{
					if(pxSubType->GetCurrentQuantity() >= pxSubType->GetMinChargePerShot())
					{
						RequestUsing( UT_CHARGED_SHOT_PRIMARY ); // only request if we have enough to charge it
					}
				}
			}
		}
	}
}

void AVP_InventoryWeapon_Mine::CalculateFiringDataForSubType( const AVP_ClientEntity_Actor& xActor, Asura_Hash_ID uMuzMP, float fMuzVel, float fMaxRange, Asura_Vector_3& xFirePosition, Asura_Vector_3& xFireDirection, Asura_Matrix_3x3& xMuzzleOri, float& fRange, Asura_Guid* puTargetGuid /*= NULL*/ )
{
	if(!xActor.GetMarkerLocationFromHash( uMuzMP, &xFirePosition, &xMuzzleOri ))
	{
		xFirePosition	= xActor.GetCurrentPosition();
		xMuzzleOri		= xActor.GetCurrentOrientation();
	}
	fRange = fMaxRange;
	Asura_AVector3 xAimDir;
	xActor.GetAimDirection( xAimDir );
	xFireDirection = xAimDir;
}

void AVP_InventoryWeapon_Mine::UpdateClientState( AVP_ClientEntity_Actor& xClient )
{
	PARENT::UpdateClientState( xClient );

	//AVP_InventoryItem_SubType* pxSubType = GetSubTypeFromUsageType( GetCurrentUsageType() );
	AVP_Actor_AnimationHandler* pxAnimationHandler = xClient.GetAnimationHandler();
	bool bAction = ( m_pxClientEffectsHandler != NULL && m_pxClientEffectsHandler->GetActorAnimFlagsForLastFrame( xClient ) & AVP_ANIMFLAG_ACTION_1 ) != 0;

	if ( bAction )
	{
		if ( GetCurrentState() == USING)
		{
			if(GetCurrentUsageType() == UT_CHARGED_SHOT_PRIMARY )
			{
				if ( GetParentSystem().IsMaster() )
				{
					Use();
				}
				
				if ( pxAnimationHandler != NULL )
				{
					pxAnimationHandler->DetachItem( this );
					DestroyAnimation();
				}
				//pxSubType->SetCurrentQuantity( 0.0f );
			}
		}
		
	}

	if ( GetCurrentState() == USING && !m_pxClientEffectsHandler->IsActorAnimPlaying( xClient ) && GetTimeInState() > 0.0f )
	{
		RequestIdling( false );
	}
}

void AVP_InventoryWeapon_Mine::UpdateServerState( AVP_ServerEntity_Actor& xServer )
{
	PARENT::UpdateServerState( xServer );

	AVP_ClientActor_Vertebrate* pxClient = AVP_ClientActor_Vertebrate::FindEntity( GetParentSystem().GetOwnerGuid() );
	if (!pxClient) { return; }

	bool bAction = false;
	if(GetCurrentState() != INACTIVE)
	{
		const AVP_Inventory_System* pxInventory = pxClient->GetInventorySystem();
		if (pxInventory)
		{
			const AVP_Inventory_Item* pxItem = static_cast< const AVP_Inventory_Item* >( pxInventory->GetCurrentItem(uAVP_INVENTORY_GROUPHASH_WEAPON) );
			if ( pxItem != NULL && pxItem->GetItemType() == AVP_INVENTORYITEMTYPE_PREDATORMINE )
			{
				const AVP_InventoryWeapon_Mine* pxClientMineItem = static_cast< const AVP_InventoryWeapon_Mine* >(pxItem);
				bAction = ( pxClientMineItem->m_pxClientEffectsHandler != NULL && pxClientMineItem->m_pxClientEffectsHandler->GetActorAnimFlagsForLastFrame(  *pxClient ) & AVP_ANIMFLAG_ACTION_1 ) != 0;
			}	
		}
	}
	
	if ( bAction )
	{
		AVP_InventoryItem_SubType* pxSubType = GetSubTypeFromUsageType( GetCurrentUsageType() );

		if ( GetCurrentState() == USING)
		{
			if(GetCurrentUsageType() == UT_DETONATE_ALL)
			{
				DestroyAllMines();
			}
			else if(GetCurrentUsageType() == UT_CHARGED_SHOT_PRIMARY && pxSubType && (pxSubType->GetCurrentQuantity() > 0.0f) )
			{
				if ( GetParentSystem().IsMaster() )
				{
					Use();
				}
			}
		}
	}

	if ( GetParentSystem().IsMaster() ) // only master requests states
	{
		if ( GetCurrentState() == USING && GetTimeInState() > 0.0f )
		{
			RequestIdling( false );
		}
	}
}


void AVP_InventoryWeapon_Mine::OnAttachItem( Asura_Animation* pxWeaponAnimation )
{
	PARENT::OnAttachItem( pxWeaponAnimation );

	if( m_pxContainerAttachment && m_pxContainerAttachment->IsLinked() )
	{
		Asura_Container_InputState xInputStateInHand;
		xInputStateInHand.SetNumInputs(1);
		xInputStateInHand.SetStateHash(0, 0x7017128A );	// Asura_GetHashID("In Hand")

		m_pxContainerAttachment->ChangeState( xInputStateInHand );
	}
}

void AVP_InventoryWeapon_Mine::OnDetachItem()
{
	PARENT::OnDetachItem();

	if( m_pxContainerAttachment )
	{
		m_pxContainerAttachment->ChangeState( Asura_Container_InputState::Off() );
	}
}


/// End of file. ///
