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

#include "cbase.h"
#include "mortar_round.h"
#include "engine/IEngineSound.h"
#include "tf_gamerules.h"
#include "tf_team.h"



// Damage CVars
ConVar	weapon_mortar_shell_damage( "weapon_mortar_shell_damage","0", FCVAR_NONE, "Mortar's standard shell maximum damage" );
ConVar	weapon_mortar_shell_radius( "weapon_mortar_shell_radius","0", FCVAR_NONE, "Mortar's standard shell splash radius" );
ConVar	weapon_mortar_starburst_damage( "weapon_mortar_starburst_damage","0", FCVAR_NONE, "Mortar's starburst maximum damage" );
ConVar	weapon_mortar_starburst_radius( "weapon_mortar_starburst_radius","0", FCVAR_NONE, "Mortar's starburst splash radius" );
ConVar	weapon_mortar_cluster_shells( "weapon_mortar_cluster_shells","0", FCVAR_NONE, "Number of shells a mortar cluster round bursts into" );

//=====================================================================================================
// MORTAR ROUND
//=====================================================================================================
BEGIN_DATADESC( CMortarRound )

	// Function Pointers
	DEFINE_FUNCTION( MissileTouch ),
	DEFINE_FUNCTION( FallThink ),

END_DATADESC()

LINK_ENTITY_TO_CLASS( mortar_round, CMortarRound );
PRECACHE_WEAPON_REGISTER(mortar_round);

CMortarRound::CMortarRound()
{
	m_pSmokeTrail = NULL;
	m_pLauncher = NULL;
	m_iRoundType = MA_SHELL;
	UseClientSideAnimation();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMortarRound::Precache( void )
{
	PrecacheModel( "models/weapons/w_grenade.mdl" );

	PrecacheScriptSound( "MortarRound.StopSound" );
	PrecacheScriptSound( "MortarRound.IncomingSound" );

}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CMortarRound::Spawn( void )
{
	Precache();

	SetMoveType( MOVETYPE_FLYGRAVITY );
	SetSolid( SOLID_BBOX );
	SetModel( "models/weapons/w_grenade.mdl" );
	UTIL_SetSize( this, vec3_origin, vec3_origin );

	SetTouch( MissileTouch );
	SetCollisionGroup( TFCOLLISION_GROUP_WEAPON );

	// Trail smoke
	m_pSmokeTrail = SmokeTrail::CreateSmokeTrail();
	if ( m_pSmokeTrail )
	{
		m_pSmokeTrail->m_SpawnRate = 90;
		m_pSmokeTrail->m_ParticleLifetime = 1.5;
		m_pSmokeTrail->m_StartColor.Init(0.0, 0.0, 0.0);
		m_pSmokeTrail->m_EndColor.Init( 0.5,0.5,0.5 );
		m_pSmokeTrail->m_StartSize = 10;
		m_pSmokeTrail->m_EndSize = 50;
		m_pSmokeTrail->m_SpawnRadius = 1;
		m_pSmokeTrail->m_MinSpeed = 15;
		m_pSmokeTrail->m_MaxSpeed = 25;
		m_pSmokeTrail->SetLifetime(15);
		m_pSmokeTrail->FollowEntity( this );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CMortarRound::MissileTouch( CBaseEntity *pOther )
{
	CMortarRound *pRound = dynamic_cast< CMortarRound* >(pOther);
	if ( pRound )
		return;

	// Stop emitting smoke/sound
	m_pSmokeTrail->SetEmit(false);
	EmitSound( "MortarRound.StopSound" );

	// Create an explosion.
	if ( m_iRoundType == MA_STARBURST )
	{
		// Small explosion, blind people in the area
		float flBlind = 0;

		// Shift it up a bit for the explosion
		SetLocalOrigin( GetAbsOrigin() + Vector(0,0,32) );
		CPASFilter filter( GetAbsOrigin() );
		te->Explosion( filter, 0.0, &GetAbsOrigin(), g_sModelIndexFireball, 3.0, 15, TE_EXPLFLAG_NONE, 512, 100 );
		RadiusDamage( CTakeDamageInfo( this, GetOwnerEntity(), weapon_mortar_starburst_damage.GetFloat(), DMG_BLAST ), GetAbsOrigin(), weapon_mortar_starburst_radius.GetFloat(), CLASS_NONE, NULL );

		// Blind all players nearby
		for ( int i = 1; i <= gpGlobals->maxClients; i++ )
		{
			CBaseTFPlayer *pPlayer = ToBaseTFPlayer( UTIL_PlayerByIndex(i) );
			if ( pPlayer  )
			{
				Vector vecSrc = pPlayer->EyePosition();
				Vector vecToTarget = (vecSrc - GetAbsOrigin());
				float flLength = VectorNormalize( vecToTarget );
				// If the player's looking at the grenade, blind him a lot
				if ( flLength < 2048 )
				{
					Vector forward, right, up;
					AngleVectors( pPlayer->pl.v_angle, &forward, &right, &up );
					float flDot = DotProduct( vecToTarget, forward );

//					Msg( "Dot: %f\n", flDot );

					// Make sure it's in front of the player
					if ( flDot < 0.0f )
					{
						flDot = fabs( DotProduct(vecToTarget, right ) ) + fabs( DotProduct(vecToTarget, up ) );

						// Open LOS?
						trace_t tr;
						UTIL_TraceLine( vecSrc, GetAbsOrigin(), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
						if ( ( tr.fraction == 1.0f ) || ( tr.m_pEnt == pPlayer ) )
						{
							flBlind = flDot;
						}
						else
						{
							// Blocked blind is only half as effective
							flBlind = flDot * 0.5;
						}
					}
					else if ( flLength < 512 )
					{
						// Otherwise, if the player's near the grenade blind him a little
						flBlind = 0.2;
					}


					// Flash the screen red
					color32 white = {255,255,255, 255};
					white.a = MIN( (flBlind * 255), 255 );
					UTIL_ScreenFade( pPlayer, white, 0.3, 5.0, 0  );
				}
			}
		}

		UTIL_Remove( this );
	}
	else 
	{
		// Large explosion

		// Shift it up a bit for the explosion
		SetLocalOrigin( GetAbsOrigin() + Vector(0,0,64) );
		CPASFilter filter( GetAbsOrigin() );
		te->Explosion( filter, 0.0,	&GetAbsOrigin(), g_sModelIndexFireball, 10.0, 15, TE_EXPLFLAG_NONE, 512, 300 );
		RadiusDamage( CTakeDamageInfo( this, GetOwnerEntity(), weapon_mortar_shell_damage.GetFloat(), DMG_BLAST ), GetAbsOrigin(), weapon_mortar_shell_radius.GetFloat(), CLASS_NONE, NULL );
		UTIL_Remove( this );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Play a fall sound when the mortar round begins to fall
//-----------------------------------------------------------------------------
void CMortarRound::FallThink( void )
{
	if ( m_pLauncher )
	{
		CRecipientFilter filter;
		filter.AddAllPlayers();
		EmitSound( filter, entindex(), "MortarRound.IncomingSound" );

		// Cluster bombs split up in the air
		if ( m_iRoundType == MA_CLUSTER )
		{
			Vector forward, right;
			QAngle angles;
			VectorAngles( GetAbsVelocity(), angles );
			SetLocalAngles( angles );
			AngleVectors( GetLocalAngles(), &forward, &right, NULL );
			for ( int i = 0; i < weapon_mortar_cluster_shells.GetInt(); i++ )
			{
				Vector vecVelocity = GetAbsVelocity();
				vecVelocity += forward * random->RandomFloat( -200, 200 );
				vecVelocity += right * random->RandomFloat( -200, 200 );

				CMortarRound *pRound = CMortarRound::Create( GetAbsOrigin(), vecVelocity, GetOwnerEntity() ? GetOwnerEntity()->edict() : NULL );
				pRound->SetLauncher( m_pLauncher );
				pRound->ChangeTeam( GetTeamNumber() );
				pRound->m_iRoundType = MA_SHELL;
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Keep a pointer to the Launcher
//-----------------------------------------------------------------------------
void CMortarRound::SetLauncher( CVehicleMortar *pLauncher )
{
	m_pLauncher = pLauncher;
}

//-----------------------------------------------------------------------------
// Purpose: Set the point at which we should start playing the fall sound
//-----------------------------------------------------------------------------
void CMortarRound::SetFallTime( float flFallTime )
{
	SetThink( FallThink );
	SetNextThink( gpGlobals->curtime + flFallTime );
}

//-----------------------------------------------------------------------------
// Purpose: Set the round's type
//-----------------------------------------------------------------------------
void CMortarRound::SetRoundType( int iRoundType )
{
	m_iRoundType = iRoundType;
}

//-----------------------------------------------------------------------------
// Purpose: Create a missile
//-----------------------------------------------------------------------------
CMortarRound* CMortarRound::Create( const Vector &vecOrigin, const Vector &vecVelocity, edict_t *pentOwner = NULL )
{
	CMortarRound *pGrenade = (CMortarRound*)CreateEntityByName("mortar_round");

	UTIL_SetOrigin( pGrenade, vecOrigin );
	pGrenade->SetOwnerEntity( Instance( pentOwner ) );
	pGrenade->Spawn();
	pGrenade->SetAbsVelocity( vecVelocity );
	QAngle angles;
	VectorAngles( vecVelocity, angles );
	pGrenade->SetLocalAngles( angles );

	return pGrenade;
}