//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======//
//
// Purpose: TF Base Grenade.
//
//=============================================================================//
#include "cbase.h"
#include "tf_weaponbase.h"
#include "tf_weaponbase_grenade.h"
#include "tf_gamerules.h"
#include "npcevent.h"
#include "engine/IEngineSound.h"
#include "in_buttons.h"	
#include "tf_weaponbase_grenadeproj.h"
#include "eventlist.h"

// Client specific.
#ifdef CLIENT_DLL
#include "c_tf_player.h"
// Server specific.
#else
#include "tf_player.h"
#include "items.h"
#endif

#define GRENADE_TIMER	1.5f			// seconds

//=============================================================================
//
// TF Grenade tables.
//

IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponBaseGrenade, DT_TFWeaponBaseGrenade )

BEGIN_NETWORK_TABLE( CTFWeaponBaseGrenade, DT_TFWeaponBaseGrenade )
// Client specific.
#ifdef CLIENT_DLL
	RecvPropBool( RECVINFO( m_bPrimed ) ),
	RecvPropFloat( RECVINFO( m_flThrowTime ) ),
	RecvPropBool( RECVINFO( m_bThrow ) ),
// Server specific.
#else
	SendPropBool( SENDINFO( m_bPrimed ) ),
	SendPropTime( SENDINFO( m_flThrowTime ) ),
	SendPropBool( SENDINFO( m_bThrow ) ),
#endif
END_NETWORK_TABLE()

BEGIN_PREDICTION_DATA( CTFWeaponBaseGrenade )
#ifdef CLIENT_DLL
	DEFINE_PRED_FIELD( m_bPrimed, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_flThrowTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_bThrow, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
#endif
END_PREDICTION_DATA()

LINK_ENTITY_TO_CLASS( tf_weaponbase_grenade, CTFWeaponBaseGrenade );

//=============================================================================
//
// TF Grenade functions.
//

CTFWeaponBaseGrenade::CTFWeaponBaseGrenade()
{
}

//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::Spawn( void )
{
	BaseClass::Spawn();

	SetViewModelIndex( 1 );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::Precache()
{
	BaseClass::Precache();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTFWeaponBaseGrenade::IsPrimed( void )
{
	return m_bPrimed;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTFWeaponBaseGrenade::Deploy( void )
{
	if ( BaseClass::Deploy() )
	{
		Prime();
		return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::Prime() 
{
	CTFWeaponInfo weaponInfo = GetTFWpnData();
	m_flThrowTime = gpGlobals->curtime + weaponInfo.m_flPrimerTime;
	m_bPrimed = true;

#ifndef CLIENT_DLL
	if ( GetWeaponID() != TF_WEAPON_GRENADE_SMOKE_BOMB )
	{
		// Get the player owning the weapon.
		CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
		if ( !pPlayer )
			return;

		pPlayer->RemoveInvisibility();
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::Throw() 
{
	if ( !m_bPrimed )
		return;

	m_bPrimed = false;
	m_bThrow = false;

	// Get the owning player.
	CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
	if ( !pPlayer )
		return;

#ifdef GAME_DLL
	// Calculate the time remaining.
	float flTime = m_flThrowTime - gpGlobals->curtime;
	bool bExplodingInHand = ( flTime <= 0.0f );

	// Players who are dying may not have their death state set, so check that too
	bool bExplodingOnDeath = ( !pPlayer->IsAlive() || pPlayer->StateGet() == TF_STATE_DYING );

	Vector vecSrc, vecThrow;
	vecSrc = pPlayer->Weapon_ShootPosition();

	if ( bExplodingInHand || bExplodingOnDeath )
	{
		vecThrow = vec3_origin;
	}
	else
	{
		// Determine the throw angle and velocity.
		QAngle angThrow = pPlayer->LocalEyeAngles();
		if ( angThrow.x < 90.0f )
		{
			angThrow.x = -10.0f + angThrow.x * ( ( 90.0f + 10.0f ) / 90.0f );
		}
		else
		{
			angThrow.x = 360.0f - angThrow.x;
			angThrow.x = -10.0f + angThrow.x * -( ( 90.0f - 10.0f ) / 90.0f );
		}

		// Adjust for the lowering of the spawn point
		angThrow.x -= 10;

		float flVelocity = ( 90.0f - angThrow.x ) * 8.0f;
		if ( flVelocity > 950.0f )
		{
			flVelocity = 950.0f;
		}

		Vector vForward, vRight, vUp;
		AngleVectors( angThrow, &vForward, &vRight, &vUp );

		// Throw from the player's left hand position.
		vecSrc += vForward * 16.0f + vRight * -8.0f + vUp * -20.0f;

		vecThrow = vForward * flVelocity;
	}

#if 0
	// Debug!!!
	char str[256];
	Q_snprintf( str, sizeof( str ),"GrenadeTime = %f\n", flTime );
	NDebugOverlay::ScreenText( 0.5f, 0.38f, str, 255, 255, 255, 255, 2.0f );
#endif

	QAngle vecAngles = RandomAngle( 0, 360 );

	// Create the projectile and send in the time remaining.
	if ( !bExplodingInHand )
	{
		EmitGrenade( vecSrc, vecAngles, vecThrow, AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ), pPlayer, flTime );
	}
	else
	{
		// We're holding onto an exploding grenade
		CTFWeaponBaseGrenadeProj *pGrenade = EmitGrenade( vecSrc, vecAngles, vecThrow, AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ), pPlayer, 0.0 );
		if ( pGrenade )
		{
			pGrenade->Detonate();
		}
	}

	// The grenade is about to be destroyed, so it won't be able to holster.
	// Handle the viewmodel hiding for it.
	if ( bExplodingInHand || bExplodingOnDeath )
	{
		SendWeaponAnim( ACT_VM_IDLE );
		CBaseViewModel *vm = pPlayer->GetViewModel( 1 );
		if ( vm )
		{
			vm->AddEffects( EF_NODRAW );
		}
	}
#endif

	// Reset the throw time
	m_flThrowTime = 0.0f;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTFWeaponBaseGrenade::ShouldDetonate( void )
{
	return ( m_flThrowTime != 0.0f ) && ( m_flThrowTime < gpGlobals->curtime );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::ItemPostFrame()
{
	CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
	if ( !pPlayer )
		return;

	if ( m_bPrimed )
	{
		// Is our timer up? If so, blow up immediately
		if ( ShouldDetonate() )
		{
			Throw();
			return;
		}

		if ( !m_bThrow && !( pPlayer->m_nButtons & IN_GRENADE1 || pPlayer->m_nButtons & IN_GRENADE2 ) )
		{
			// Start throwing
			pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_GRENADE );
			m_bThrow = true;
		}		
	}

	if ( m_bThrow )
	{
		if ( GetActivity() != ACT_VM_PRIMARYATTACK )
		{
			// Start the throw animation
			if ( !SendWeaponAnim( ACT_VM_PRIMARYATTACK ) )
			{
				Throw();
			}
		}
		else if ( HasWeaponIdleTimeElapsed() )
		{
			// The Throw call here exists solely to catch the lone case of thirdperson where the 
			// viewmodel isn't being drawn, and hence the anim event doesn't trigger and force a throw.
			// In all other cases, it'll do nothing because the grenade has already been thrown.
			Throw();
		}
		return;
	}

	if ( !m_bPrimed && !m_bThrow )
	{
		// Once we've finished being holstered, we'll be hidden. When that happens,
		// tell our player that we're all done with the grenade throw.
		if ( IsEffectActive(EF_NODRAW) )
		{
			pPlayer->FinishThrowGrenade();
			return;
		}

		// We've been thrown. Go away.
		if ( HasWeaponIdleTimeElapsed() )
		{
			Holster();
		}
	}

	// Go straight to idle anim when deploy is done
	if ( m_flTimeWeaponIdle <= gpGlobals->curtime )
	{
		SendWeaponAnim( ACT_VM_IDLE );
	}
}

bool CTFWeaponBaseGrenade::ShouldLowerMainWeapon( void )
{
	return GetTFWpnData().m_bLowerWeapon;
}

//=============================================================================
//
// Client specific functions.
//
#ifdef CLIENT_DLL

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTFWeaponBaseGrenade::ShouldDraw( void )
{
	if ( !BaseClass::ShouldDraw() )
	{
		// Grenades need to be visible whenever they're being primed & thrown
		if ( !m_bPrimed )
			return false;

		// Don't draw primed grenades for local player in first person players
		if ( GetOwner() == C_BasePlayer::GetLocalPlayer() && !C_BasePlayer::ShouldDrawLocalPlayer() )
			return false;
	}

	return true;
}

//=============================================================================
//
// Server specific functions.
//
#else

BEGIN_DATADESC( CTFWeaponBaseGrenade )
END_DATADESC()

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::HandleAnimEvent( animevent_t *pEvent )
{
	if ( (pEvent->type & AE_TYPE_NEWEVENTSYSTEM) )
	{
		if ( pEvent->event == AE_WPN_PRIMARYATTACK )
		{
			Throw();
			return;
		}
	}

	BaseClass::HandleAnimEvent( pEvent );	
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CTFWeaponBaseGrenadeProj *CTFWeaponBaseGrenade::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flTime, int iFlags )
{
	Assert( 0 && "CBaseCSGrenade::EmitGrenade should not be called. Make sure to implement this in your subclass!\n" );
	return NULL;
}

#endif