//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//

#include "cbase.h"
#include "weapon_dodbasegrenade.h"
#include "in_buttons.h"
#include "dod_gamerules.h"


#if defined( CLIENT_DLL )
	#include "c_dod_player.h"
#else
	#include "dod_player.h"
#endif

extern ConVar dod_bonusround;

IMPLEMENT_NETWORKCLASS_ALIASED( WeaponDODBaseGrenade, DT_WeaponDODBaseGrenade )

BEGIN_NETWORK_TABLE(CWeaponDODBaseGrenade, DT_WeaponDODBaseGrenade)
#if !defined( CLIENT_DLL )
	SendPropBool( SENDINFO( m_bPinPulled ) ),
	SendPropBool( SENDINFO( m_bArmed ) ),
#else
	RecvPropBool( RECVINFO( m_bPinPulled ) ),
	RecvPropBool( RECVINFO( m_bArmed ) ),
#endif
END_NETWORK_TABLE()

BEGIN_PREDICTION_DATA( CWeaponDODBaseGrenade )
END_PREDICTION_DATA()

LINK_ENTITY_TO_CLASS( weapon_basedodgrenade, CWeaponDODBaseGrenade );

acttable_t CWeaponDODBaseGrenade::m_acttable[] = 
{
	// Move this out to the specific grenades???
	{ ACT_DOD_STAND_AIM,					ACT_DOD_STAND_AIM_GREN_FRAG,				false },
	{ ACT_DOD_CROUCH_AIM,					ACT_DOD_CROUCH_AIM_GREN_FRAG,				false },
	{ ACT_DOD_CROUCHWALK_AIM,				ACT_DOD_CROUCHWALK_AIM_GREN_FRAG,			false },
	{ ACT_DOD_WALK_AIM,						ACT_DOD_WALK_AIM_GREN_FRAG,					false },
	{ ACT_DOD_RUN_AIM,						ACT_DOD_RUN_AIM_GREN_FRAG,					false },
	{ ACT_PRONE_IDLE,						ACT_DOD_PRONE_AIM_GREN_FRAG,				false },
	{ ACT_PRONE_FORWARD,					ACT_DOD_PRONEWALK_AIM_GREN_FRAG,			false },
	{ ACT_DOD_STAND_IDLE,					ACT_DOD_STAND_AIM_GREN_FRAG,				false },
	{ ACT_DOD_CROUCH_IDLE,					ACT_DOD_CROUCH_AIM_GREN_FRAG,				false },
	{ ACT_DOD_CROUCHWALK_IDLE,				ACT_DOD_CROUCHWALK_AIM_GREN_FRAG,			false },
	{ ACT_DOD_WALK_IDLE,					ACT_DOD_WALK_AIM_GREN_FRAG,					false },
	{ ACT_DOD_RUN_IDLE,						ACT_DOD_RUN_AIM_GREN_FRAG,					false },
	{ ACT_SPRINT,							ACT_DOD_SPRINT_AIM_GREN_FRAG,				false },

	{ ACT_RANGE_ATTACK1,				ACT_DOD_PRIMARYATTACK_GREN_FRAG,		false },
	{ ACT_DOD_PRIMARYATTACK_PRONE,		ACT_DOD_PRIMARYATTACK_PRONE_GREN_FRAG,	false },
	{ ACT_DOD_PRIMARYATTACK_CROUCH,		ACT_DOD_PRIMARYATTACK_CROUCH_GREN_FRAG, false },

	// Hand Signals
	{ ACT_DOD_HS_IDLE,						ACT_DOD_HS_IDLE_STICKGRENADE,			false },
	{ ACT_DOD_HS_CROUCH,					ACT_DOD_HS_CROUCH_STICKGRENADE,			false },
};

IMPLEMENT_ACTTABLE( CWeaponDODBaseGrenade );

CWeaponDODBaseGrenade::CWeaponDODBaseGrenade()
{
	m_bRedraw = false;
	m_bPinPulled = false;
	SetArmed( false );

	m_iAltFireHint = HINT_USE_PRIME;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CWeaponDODBaseGrenade::CanHolster( void )
{
	// can only holster hand grenades when not primed!
	return ( m_bPinPulled == false && !IsArmed() );
}

#ifdef CLIENT_DLL

	void CWeaponDODBaseGrenade::PrimaryAttack()
	{
		//nothing on the client
	}

#else

	BEGIN_DATADESC( CWeaponDODBaseGrenade )
		DEFINE_FIELD( m_bRedraw, FIELD_BOOLEAN ),
		DEFINE_INPUTFUNC( FIELD_FLOAT, "DetonateTime", InputSetDetonateTime ),
	END_DATADESC()

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void CWeaponDODBaseGrenade::Precache()
	{
		PrecacheScriptSound( "Weapon_Grenade.Throw" );

		// Precache all the grenade minimap icons. 
		PrecacheMaterial( "sprites/minimap_icons/minimap_riflegren_ger" );
		PrecacheMaterial( "sprites/minimap_icons/minimap_riflegren_us" );
		PrecacheMaterial( "sprites/minimap_icons/grenade_hltv" );
		PrecacheMaterial( "sprites/minimap_icons/stick_hltv" );
		PrecacheMaterial( "sprites/minimap_icons/minimap_smoke_us" );
		PrecacheMaterial( "sprites/minimap_icons/minimap_smoke_ger" );

		BaseClass::Precache();
	}
	
	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	bool CWeaponDODBaseGrenade::Deploy()
	{
		m_bRedraw = false;
		m_bPinPulled = false;

		return BaseClass::Deploy();
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	// Output : Returns true on success, false on failure.
	//-----------------------------------------------------------------------------
	bool CWeaponDODBaseGrenade::Holster( CBaseCombatWeapon *pSwitchingTo )
	{
		m_bRedraw = false;

#ifndef CLIENT_DLL
		// If they attempt to switch weapons before the throw animation is done, 
		// allow it, but kill the weapon if we have to.

		CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() );

		if( pPlayer && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
		{
			pPlayer->Weapon_Drop( this, NULL, NULL );
			UTIL_Remove(this);
		}
#endif

		return BaseClass::Holster( pSwitchingTo );
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	// Output : Returns true on success, false on failure.
	//-----------------------------------------------------------------------------
	bool CWeaponDODBaseGrenade::Reload()
	{
		if ( ( m_bRedraw ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
		{
			//Redraw the weapon
			SendWeaponAnim( GetDrawActivity() );

			//Update our times
			m_flNextPrimaryAttack	= gpGlobals->curtime + SequenceDuration();
			m_flNextSecondaryAttack	= gpGlobals->curtime + SequenceDuration();
			m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration();

			//Mark this as done
			m_bRedraw = false;
		}

		return true;
	}

enum
{
	THROW_THROW	= 0,
	THROW_PRIME
};
	
	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void CWeaponDODBaseGrenade::PrimaryAttack()
	{
		if ( IsArmed() )	// live grenade already?
		{
			StartThrow( THROW_THROW );
		}
		else
		{
			StartThrow( THROW_PRIME );
		}
	}

	void CWeaponDODBaseGrenade::StartThrow( int throwType )
	{
		if( GetPlayerOwner()->GetWaterLevel() > 2 )
		{
			PlayEmptySound();
			m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
			return;
		}

		if ( !m_bPinPulled )
		{
			m_bPinPulled = true; 

			SendWeaponAnim( GetPrimaryAttackActivity() );

			// Can't prime an already primed grenade!
			if ( !IsArmed() && throwType == THROW_PRIME )
			{
				SetArmed( true );

				m_flDetonateTime = gpGlobals->curtime + GetDetonateTimerLength();

				// start the hissing noise
			}

			m_flTimeWeaponIdle		= gpGlobals->curtime + SequenceDuration();
			m_flNextPrimaryAttack	= gpGlobals->curtime + SequenceDuration();
			m_flNextSecondaryAttack	= gpGlobals->curtime + SequenceDuration();
		}
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void CWeaponDODBaseGrenade::ItemPostFrame()
	{
		CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() );
		if ( !pPlayer )
			return;

		CBaseViewModel *pViewModel = pPlayer->GetViewModel( m_nViewModelIndex );
		if ( !pViewModel )
			return;

		if( IsArmed() && gpGlobals->curtime > m_flDetonateTime )
		{
			//Drop it!
			DropGrenade();
			m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
			m_flNextSecondaryAttack = gpGlobals->curtime + 1.0;
			return;
		}

		if ( m_bPinPulled &&
			 !(pPlayer->m_nButtons & IN_ATTACK) && 
			 !(pPlayer->m_nButtons & IN_ATTACK2) // If they let go of the fire button, they want to throw the grenade.
			 )
		{
			pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE );

			ThrowGrenade(false);

			m_bRedraw = true;
			m_bPinPulled = false;
			SetArmed( false );

			DecrementAmmo( pPlayer );		

			SendWeaponAnim( ACT_VM_THROW );
		}
		else
		{
			BaseClass::ItemPostFrame();
			
			if ( m_bRedraw )
			{
				if (pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
				{
					pPlayer->Weapon_Drop( this, NULL, NULL );
					UTIL_Remove(this);
				}
				else
                    Reload();
			}
		}
	}


	//-----------------------------------------------------------------------------
	// Purpose: 
	// Input  : *pOwner - 
	//-----------------------------------------------------------------------------
	void CWeaponDODBaseGrenade::DecrementAmmo( CBaseCombatCharacter *pOwner )
	{
		pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );

		if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
		{
            pOwner->Weapon_Drop( this, NULL, NULL );
			UTIL_Remove(this);
		}
	}

	void CWeaponDODBaseGrenade::DropGrenade( void )
	{
		CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() );
		if ( !pPlayer )
			return;

		pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE );

		bool bThrow = true;

		DODRoundState state = DODGameRules()->State_Get();

		// If the player is holding a nade that is going to explode
		// and the round is over, not allowing them to throw it
		// just don't emit a grenade.
		if ( dod_bonusround.GetBool() )
		{
			int team = pPlayer->GetTeamNumber();

			if ( ( team == TEAM_ALLIES && state == STATE_AXIS_WIN ) || 
				( team == TEAM_AXIS && state == STATE_ALLIES_WIN ) )
			{
				bThrow = false;
			}
		}

		if ( bThrow )
			ThrowGrenade(true);

		m_bRedraw = true;
		m_bPinPulled = false;
		SetArmed( false );

		DecrementAmmo( pPlayer );		

		SendWeaponAnim( ACT_VM_THROW );
	}

	ConVar dod_grenadespeed( "dod_grenadespeed", "4.2", FCVAR_CHEAT );
	ConVar dod_grenademaxspeed( "dod_grenademaxspeed", "1400", FCVAR_CHEAT );
	ConVar dod_grenademinspeed( "dod_grenademinspeed", "500", FCVAR_CHEAT );

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void CWeaponDODBaseGrenade::ThrowGrenade( bool bDrop )
	{
		CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
		if ( !pPlayer )
		{
			Assert( false );
			return;
		}

		QAngle angThrow = pPlayer->LocalEyeAngles();

		Vector vForward, vRight, vUp;

		if( angThrow.x > 180 )
			angThrow.x -= 360;

		if (angThrow.x < 0)
			angThrow.x = -10 + angThrow.x * ((90 - 10) / 90.0);
		else
			angThrow.x = -10 + angThrow.x * ((90 + 10) / 90.0);

		AngleVectors( angThrow, &vForward, &vRight, &vUp );
		
		Vector eyes = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset();
		Vector vecSrc = eyes + vForward * 16; 	

		// Start with the player's velocity as the grenade vel
		Vector vecPlayerVel;
		pPlayer->GetVelocity( &vecPlayerVel, NULL );

		// Get player angles, not eye angles!
		QAngle angPlayerAngles = pPlayer->GetAbsAngles();
		Vector vecPlayerForward;
		AngleVectors( angPlayerAngles, &vecPlayerForward );
		Vector vecThrow = DotProduct( vecPlayerVel, vecPlayerForward ) * vecPlayerForward;

		if( bDrop )
		{
			vecThrow = vForward;
		}
		else	// we are throwing the grenade
		{
			// change the speed depending on the throw angle
			// straight down is 0%, straight up is 100%, linear between
			float flSpeed = ( 90 - angThrow.x ) * dod_grenadespeed.GetFloat();

			//Msg( "speed %.f\n", flSpeed );

			flSpeed = clamp( flSpeed, dod_grenademinspeed.GetFloat(), dod_grenademaxspeed.GetFloat() );

			vecThrow += vForward * flSpeed;
		}

		trace_t tr;
		UTIL_TraceLine( eyes, vecSrc, MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
		
		// don't go into the ground
		if( tr.fraction < 1.0 ) 
		{
			vecSrc = tr.endpos;
		}

		float flTimeLeft;

		if ( IsArmed() )
			flTimeLeft = MAX( 0, m_flDetonateTime - gpGlobals->curtime );
		else
			flTimeLeft = GetDetonateTimerLength();

		EmitGrenade( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer, flTimeLeft );

		pPlayer->EmitSound( "Weapon_Grenade.Throw" );

#ifndef CLIENT_DLL
		IGameEvent * event = gameeventmanager->CreateEvent( "dod_stats_weapon_attack" );
		if ( event )
		{
			event->SetInt( "attacker", pPlayer->GetUserID() );
			event->SetInt( "weapon", GetStatsWeaponID() );

			gameeventmanager->FireEvent( event );
		}
#endif	//CLIENT_DLL
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void CWeaponDODBaseGrenade::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime /* = GRENADE_FUSE_LENGTH */ )
	{
		Assert( 0 && "CBaseCSGrenade::EmitGrenade should not be called. Make sure to implement this in your subclass!\n" );
	}

	//-----------------------------------------------------------------------------
	// Purpose:
	//-----------------------------------------------------------------------------
	bool CWeaponDODBaseGrenade::AllowsAutoSwitchFrom( void ) const
	{
		return ( m_bPinPulled == false );
	}

	void CWeaponDODBaseGrenade::InputSetDetonateTime( inputdata_t &inputdata )
	{
		SetDetonateTime( inputdata.value.Float() );
	}

#endif	// !CLIENT_DLL

Activity CWeaponDODBaseGrenade::GetIdleActivity( void )
{
	if ( IsArmed() )
		return ACT_VM_IDLE_EMPTY;
	else
		return ACT_VM_IDLE;
}

Activity CWeaponDODBaseGrenade::GetPrimaryAttackActivity( void )
{
	if ( IsArmed() )
		return ACT_VM_PRIMARYATTACK_EMPTY;
	else
		return ACT_VM_PULLPIN;
}

Activity CWeaponDODBaseGrenade::GetDrawActivity( void )
{
	if ( IsArmed() )
		return ACT_VM_DRAW_EMPTY;
	else
		return ACT_VM_DRAW;
}