//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: CTF CurrencyPack.
//
//=============================================================================//
#include "cbase.h"
#include "items.h"
#include "tf_gamerules.h"
#include "tf_shareddefs.h"
#include "tf_player.h"
#include "tf_team.h"
#include "engine/IEngineSound.h"
#include "entity_currencypack.h"
#include "tf_gamestats.h"
#include "tf_mann_vs_machine_stats.h"
#include "world.h"
#include "particle_parse.h"
#include "player_vs_environment/tf_population_manager.h"
#include "collisionutils.h"
#include "tf_objective_resource.h"

//=============================================================================
//
// CTF CurrencyPack defines.
//

#define TF_CURRENCYPACK_PICKUP_SOUND	"MVM.MoneyPickup"
#define TF_CURRENCYPACK_VANISH_SOUND	"MVM.MoneyVanish"

#define TF_CURRENCYPACK_BLINK_PERIOD	5.0f		// how long pack blinks before it vanishes
#define TF_CURRENCYPACK_BLINK_DURATION  0.25f		// how long a blink lasts

#define TF_CURRENCYPACK_GLOW_THINK_TIME	0.1f		// how often should we check if cash should glow


LINK_ENTITY_TO_CLASS( item_currencypack_large, CCurrencyPack );
LINK_ENTITY_TO_CLASS( item_currencypack_medium, CCurrencyPackMedium );
LINK_ENTITY_TO_CLASS( item_currencypack_small, CCurrencyPackSmall );

LINK_ENTITY_TO_CLASS( item_currencypack_custom, CCurrencyPackCustom );

IMPLEMENT_SERVERCLASS_ST( CCurrencyPack, DT_CurrencyPack )
	SendPropBool( SENDINFO( m_bDistributed ) ),
END_SEND_TABLE()

IMPLEMENT_AUTO_LIST( ICurrencyPackAutoList );


//=============================================================================
//
// CTF CurrencyPack functions.
//

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CCurrencyPack::CCurrencyPack()
{
	m_nAmount = 0;
	m_nWaveNumber = MannVsMachineStats_GetCurrentWave();
	m_bTouched = false;
	m_bClaimed = false;
	m_bDistributed = false;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CCurrencyPack::~CCurrencyPack()
{
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCurrencyPack::UpdateOnRemove( void )
{
	BaseClass::UpdateOnRemove();

	if ( g_pPopulationManager && !m_bTouched )
	{
		if ( !m_bDistributed )
		{
			g_pPopulationManager->OnCurrencyPackFade();
		}

		DispatchParticleEffect( "mvm_cash_explosion", GetAbsOrigin(), GetAbsAngles() );
	}

	if ( !m_bDistributed && TFObjectiveResource() )
	{
		TFObjectiveResource()->AddMvMWorldMoney( -m_nAmount );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Always transmitted to clients
//-----------------------------------------------------------------------------
int CCurrencyPack::UpdateTransmitState( void )
{
	return SetTransmitState( FL_EDICT_ALWAYS );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CCurrencyPack::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
	return FL_EDICT_ALWAYS;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCurrencyPack::Spawn( void )
{
	BaseClass::Spawn();
	m_blinkCount = 0;
	m_blinkTimer.Invalidate();
	SetContextThink( &CCurrencyPack::BlinkThink, gpGlobals->curtime + GetLifeTime() - TF_CURRENCYPACK_BLINK_PERIOD - RandomFloat( 0.0, TF_CURRENCYPACK_BLINK_DURATION ), "CurrencyPackWaitingToBlinkThink" );
	
	// Force collision size to see if this fixes a bunch of stuck-in-geo issues goes away
	SetCollisionBounds( Vector( -10, -10, -10 ), Vector( 10, 10, 10 ) );

	if ( m_bDistributed )
	{
		DispatchParticleEffect( "mvm_cash_embers_red", PATTACH_ABSORIGIN_FOLLOW, this );
	}
	else
	{
		DispatchParticleEffect( "mvm_cash_embers", PATTACH_ABSORIGIN_FOLLOW, this );
	}

	// Store when this drops for time-based accounting - like with wave collection bonus
	m_nWaveNumber = MannVsMachineStats_GetCurrentWave();

	// if m_nAmount != 0, we already call SetAmount
	m_nAmount = m_nAmount == 0 ? TFGameRules()->CalculateCurrencyAmount_ByType( GetPackSize() ) : m_nAmount;
	MannVsMachineStats_RoundEvent_CreditsDropped( m_nWaveNumber, m_nAmount );
	if ( !m_bDistributed && TFObjectiveResource() )
	{
		TFObjectiveResource()->AddMvMWorldMoney( m_nAmount );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Blink off/on when about to expire and play expire sound
//-----------------------------------------------------------------------------
void CCurrencyPack::BlinkThink( void )
{
	// This means the pack was claimed by a player via Radius Currency Collection and
	// is likely flying toward them.  Regardless, one second later it fires Touch().
	if ( IsClaimed() )
		return;

	++m_blinkCount;

	SetRenderMode( kRenderTransAlpha );

	if ( m_blinkCount & 0x1 )
	{
		SetRenderColorA( 25 );
	}
	else
	{
		SetRenderColorA( 255 );
	}

	SetContextThink( &CCurrencyPack::BlinkThink, gpGlobals->curtime + TF_CURRENCYPACK_BLINK_DURATION, "CurrencyPackBlinkThink" );
}


//-----------------------------------------------------------------------------
// Become touchable when we are at rest
//-----------------------------------------------------------------------------
void CCurrencyPack::ComeToRest( void )
{
	BaseClass::ComeToRest();

	if ( IsClaimed() || m_bDistributed )
		return;

	// if we've come to rest in an area with no nav, just grant the money to the player
	if ( TheNavMesh->GetNavArea( GetAbsOrigin() ) == NULL )
	{
		TFGameRules()->DistributeCurrencyAmount( m_nAmount );
		m_bTouched = true;
		UTIL_Remove( this );

		return;
	}

	// See if we've come to rest in a trigger_hurt
	for ( int i = 0; i < ITriggerHurtAutoList::AutoList().Count(); i++ )
	{
		CTriggerHurt *pTrigger = static_cast<CTriggerHurt*>( ITriggerHurtAutoList::AutoList()[i] );
		if ( !pTrigger->m_bDisabled )
		{
			Vector vecMins, vecMaxs;
			pTrigger->GetCollideable()->WorldSpaceSurroundingBounds( &vecMins, &vecMaxs );
			if ( IsPointInBox( GetCollideable()->GetCollisionOrigin(), vecMins, vecMaxs ) )
			{
				TFGameRules()->DistributeCurrencyAmount( m_nAmount );

				m_bTouched = true;
				UTIL_Remove( this );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Sets the value of a custom pack
//-----------------------------------------------------------------------------
void CCurrencyPack::SetAmount( float nAmount )
{
	Assert( GetPackSize() == TF_CURRENCY_PACK_CUSTOM );	// Never set an amount unless we're custom
	m_nAmount = nAmount;
}

//-----------------------------------------------------------------------------
// Purpose: Distribute the money right away
//-----------------------------------------------------------------------------
void CCurrencyPack::DistributedBy( CBasePlayer* pMoneyMaker )
{
	TFGameRules()->DistributeCurrencyAmount( m_nAmount );

	if ( pMoneyMaker )
	{
		CTF_GameStats.Event_PlayerCollectedCurrency( pMoneyMaker, m_nAmount );
	}

	m_bDistributed = true;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCurrencyPack::Precache( void )
{
	PrecacheScriptSound( TF_CURRENCYPACK_PICKUP_SOUND );
	PrecacheScriptSound( TF_CURRENCYPACK_VANISH_SOUND );
	PrecacheParticleSystem( "mvm_cash_embers" );
	PrecacheParticleSystem( "mvm_cash_explosion" );
	BaseClass::Precache();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CCurrencyPack::MyTouch( CBasePlayer *pPlayer )
{
	if ( ValidTouch( pPlayer ) && !m_bTouched )
	{
		CTFPlayer *pTFTouchPlayer = ToTFPlayer( pPlayer );
		if ( !pTFTouchPlayer )
			return false;

		if ( pTFTouchPlayer->IsBot() )
			return false;

		if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
		{
			// Prevent losing team from grabbing money - screws up stats in checkpoints
			if ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN )
			{
				if ( TFGameRules()->GetWinningTeam() != pTFTouchPlayer->GetTeamNumber() )
					return false;
			}

			// Scouts gain health when grabbing currency packs
			if ( pTFTouchPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_SCOUT )
			{
				const int nCurHealth = pTFTouchPlayer->GetHealth();
				const int nMaxHealth = pTFTouchPlayer->GetMaxHealth();
				int nHealth = nCurHealth < nMaxHealth ? 50 : 25;

				// If we cross the border into insanity, scale the reward
				const int nHealthCap = nMaxHealth * 4;
				if ( nCurHealth > nHealthCap )
				{
					nHealth = RemapValClamped( nCurHealth, nHealthCap, (nHealthCap * 1.5f), 20, 5 );
				}

				pTFTouchPlayer->TakeHealth( nHealth, DMG_IGNORE_MAXHEALTH );
			}

			MannVsMachineStats_PlayerEvent_PickedUpCredits( pTFTouchPlayer, m_nWaveNumber, m_nAmount );

			IGameEvent *event = gameeventmanager->CreateEvent( "mvm_pickup_currency" );
			if ( event )
			{
				event->SetInt( "player", pTFTouchPlayer->entindex() );
				event->SetInt( "currency", m_nAmount );
				gameeventmanager->FireEvent( event );
			}

			// is the money blinking and about to burn up?
			if ( m_blinkCount > 0 )
			{
				pTFTouchPlayer->AwardAchievement( ACHIEVEMENT_TF_MVM_PICKUP_MONEY_ABOUT_TO_EXPIRE );
			}
		}

		CReliableBroadcastRecipientFilter filter;
		EmitSound( filter, entindex(), TF_CURRENCYPACK_PICKUP_SOUND );
		
		if ( !m_bDistributed )
		{
			TFGameRules()->DistributeCurrencyAmount( m_nAmount, pTFTouchPlayer );
			CTF_GameStats.Event_PlayerCollectedCurrency( pTFTouchPlayer, m_nAmount );
		}

		if ( ( !pTFTouchPlayer->IsPlayerClass( TF_CLASS_SPY ) ) ||
			 ( !pTFTouchPlayer->m_Shared.IsStealthed() && !pTFTouchPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) && !pTFTouchPlayer->m_Shared.InCond( TF_COND_DISGUISED ) ) )
		{
			pTFTouchPlayer->SpeakConceptIfAllowed( MP_CONCEPT_MVM_MONEY_PICKUP );
		}

		pTFTouchPlayer->SetLastObjectiveTime( gpGlobals->curtime );
		
		m_bTouched = true;
	}

	return m_bTouched;
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CCurrencyPackCustom::GetDefaultPowerupModel( void )
{ 
	// Custom packs should always be set to a value by hand
	Assert( m_nAmount > 0 );

	// Try to pick a model that's appropriate to our drop amount (which is in our multiplier)
	if ( m_nAmount >= 25 )
		return "models/items/currencypack_large.mdl"; 
	if ( m_nAmount >= 10 )
		return "models/items/currencypack_medium.mdl"; 
	return "models/items/currencypack_small.mdl";
}

