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

#include "cbase.h"
#include "grenade_satchel.h"
#include "player.h"
#include "soundenvelope.h"
#include "engine/IEngineSound.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

ConVar    sk_plr_dmg_satchel		( "sk_plr_dmg_satchel","0");
ConVar    sk_npc_dmg_satchel		( "sk_npc_dmg_satchel","0");
ConVar    sk_satchel_radius			( "sk_satchel_radius","0");

BEGIN_DATADESC( CSatchelCharge )

	DEFINE_SOUNDPATCH( m_soundSlide ),

	DEFINE_FIELD( m_flSlideVolume, FIELD_FLOAT ),
	DEFINE_FIELD( m_flNextBounceSoundTime, FIELD_TIME ),
	DEFINE_FIELD( m_bInAir, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_vLastPosition, FIELD_POSITION_VECTOR ),
	DEFINE_FIELD( m_pMyWeaponSLAM, FIELD_CLASSPTR ),
	DEFINE_FIELD( m_bIsAttached, FIELD_BOOLEAN ),

	// Function Pointers
	DEFINE_FUNCTION( SatchelTouch ),
	DEFINE_FUNCTION( SatchelThink ),
	DEFINE_FUNCTION( SatchelUse ),

END_DATADESC()

LINK_ENTITY_TO_CLASS( npc_satchel, CSatchelCharge );

//=========================================================
// Deactivate - do whatever it is we do to an orphaned 
// satchel when we don't want it in the world anymore.
//=========================================================
void CSatchelCharge::Deactivate( void )
{
	AddSolidFlags( FSOLID_NOT_SOLID );
	UTIL_Remove( this );
}


void CSatchelCharge::Spawn( void )
{
	Precache( );
	// motor
	SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
	SetSolid( SOLID_BBOX ); 
	SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
	SetModel( "models/Weapons/w_slam.mdl" );

	UTIL_SetSize(this, Vector( -6, -6, -2), Vector(6, 6, 2));

	SetTouch( SatchelTouch );
	SetUse( SatchelUse );
	SetThink( SatchelThink );
	SetNextThink( gpGlobals->curtime + 0.1f );

	m_flDamage		= sk_plr_dmg_satchel.GetFloat();
	m_DmgRadius		= sk_satchel_radius.GetFloat();
	m_takedamage	= DAMAGE_YES;
	m_iHealth		= 1;

	SetGravity( UTIL_ScaleForGravity( 560 ) );	// slightly lower gravity
	SetFriction( 1.0 );
	SetSequence( 1 );

	m_bIsAttached			= false;
	m_bInAir				= true;
	m_flSlideVolume			= -1.0;
	m_flNextBounceSoundTime	= 0;

	m_vLastPosition	= vec3_origin;

	InitSlideSound();
}

//-----------------------------------------------------------------------------

void CSatchelCharge::InitSlideSound(void)
{
	CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
	CPASAttenuationFilter filter( this );
	m_soundSlide = controller.SoundCreate( filter, entindex(), CHAN_STATIC, "SatchelCharge.Slide", ATTN_NORM );	
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
void CSatchelCharge::KillSlideSound(void)
{
	CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
	controller.CommandClear( m_soundSlide );
	controller.SoundFadeOut( m_soundSlide, 0.0 );
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
void CSatchelCharge::SatchelUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
	KillSlideSound();
	SetThink( Detonate );
	SetNextThink( gpGlobals->curtime );
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
void CSatchelCharge::SatchelTouch( CBaseEntity *pOther )
{
	Assert( pOther );
	if ( !pOther->IsSolid() )
		return;

	// If successfully thrown and touching the 
	// NPC that released this grenade, pick it up
	if ( pOther == GetThrower() && GetOwnerEntity() == NULL )
	{
		CBasePlayer *pPlayer = ToBasePlayer( m_pMyWeaponSLAM->GetOwner() );
		if (pPlayer)
		{
			// Give the player ammo
			pPlayer->GiveAmmo(1, m_pMyWeaponSLAM->m_iSecondaryAmmoType);

			CPASAttenuationFilter filter( pPlayer, "SatchelCharge.Pickup" );
			EmitSound( filter, pPlayer->entindex(), "SatchelCharge.Pickup" );

			m_bIsLive = false;

			// Take weapon out of detonate mode if necessary
			if (!m_pMyWeaponSLAM->AnyUndetonatedCharges())
			{
				m_pMyWeaponSLAM->m_bDetonatorArmed			= false;
				m_pMyWeaponSLAM->m_bNeedDetonatorHolster	= true;

				// Put detonator away right away
				m_pMyWeaponSLAM->SetWeaponIdleTime( gpGlobals->curtime );
			}

			// Kill any sliding sound
			KillSlideSound();

			// Remove satchel charge from world
			UTIL_Remove( this );
			return;
		}

	}

	StudioFrameAdvance( );

	// Is it attached to a wall?
	if (m_bIsAttached)
	{
		return;
	}

	SetGravity( 1 );// normal gravity now

	// HACKHACK - On ground isn't always set, so look for ground underneath
	trace_t tr;
	UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,10), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );

	if ( tr.fraction < 1.0 )
	{
		// add a bit of static friction
		SetAbsVelocity( GetAbsVelocity() * 0.85 );
		SetLocalAngularVelocity( GetLocalAngularVelocity() * 0.8 );
	}

	UpdateSlideSound();

	if (m_bInAir)
	{
		BounceSound();
		m_bInAir = false;
	}

}

void CSatchelCharge::UpdateSlideSound( void )
{	
	if (!m_soundSlide)
	{
		return;
	}

	float volume = GetAbsVelocity().Length2D()/1000;
	if (volume < 0.01 && m_soundSlide)
	{
		KillSlideSound();
		return;
	}
		// HACKHACK - On ground isn't always set, so look for ground underneath
	trace_t tr;
	UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,10), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );

	CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();

	
	if ( tr.fraction < 1.0 )
	{
		if (m_flSlideVolume == -1.0)
		{
			controller.CommandClear( m_soundSlide );
			controller.Play( m_soundSlide, 1.0, 100 );
			m_flSlideVolume = 1.0;
		}
		else 
		{
			float volume = GetAbsVelocity().Length()/1000;
			if ( volume < m_flSlideVolume )
			{
				m_flSlideVolume = volume;
				controller.CommandClear( m_soundSlide );
				controller.SoundChangeVolume( m_soundSlide, volume, 0.1 );
			}
		}
	}
	else 
	{
		controller.CommandClear( m_soundSlide );
		controller.SoundChangeVolume( m_soundSlide, 0.0, 0.01 );
		m_flSlideVolume = -1.0;
		m_bInAir = true;
		return;
	}
}

void CSatchelCharge::SatchelThink( void )
{
	// If attached resize so player can pick up off wall
	if (m_bIsAttached)
	{
		UTIL_SetSize(this, Vector( -2, -2, -6), Vector(2, 2, 6));
	}

	UpdateSlideSound();

	// See if I can lose my owner (has dropper moved out of way?)
	// Want do this so owner can shoot the satchel charge
	if (GetOwnerEntity())
	{
		trace_t tr;
		Vector	vUpABit = GetAbsOrigin();
		vUpABit.z += 5.0;

		CBaseEntity* saveOwner	= GetOwnerEntity();
		SetOwnerEntity( NULL );
		UTIL_TraceEntity( this, GetAbsOrigin(), vUpABit, MASK_SOLID, &tr );
		if ( tr.startsolid || tr.fraction != 1.0 )
		{
			SetOwnerEntity( saveOwner );
		}
	}
	
	// Bounce movement code gets this think stuck occasionally so check if I've 
	// succeeded in moving, otherwise kill my motions.
	else if ((GetAbsOrigin() - m_vLastPosition).LengthSqr()<1)
	{
		SetAbsVelocity( vec3_origin );

		QAngle angVel = GetLocalAngularVelocity();
		angVel.y  = 0;
		SetLocalAngularVelocity( angVel );

		// Kill any remaining sound
		KillSlideSound();

		// Clear think function
		SetThink(NULL);
		return;
	}
	m_vLastPosition= GetAbsOrigin();

	StudioFrameAdvance( );
	SetNextThink( gpGlobals->curtime + 0.1f );

	if (!IsInWorld())
	{
		// Kill any remaining sound
		KillSlideSound();

		UTIL_Remove( this );
		return;
	}

	// Is it attached to a wall?
	if (m_bIsAttached)
	{
		return;
	}

	Vector vecNewVel = GetAbsVelocity();
	if (GetWaterLevel() == 3)
	{
		SetMoveType( MOVETYPE_FLY );
		vecNewVel *= 0.8;
		vecNewVel.z += 8;
		SetLocalAngularVelocity( GetLocalAngularVelocity() * 0.9 );
	}
	else if (GetWaterLevel() == 0)
	{
		SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
	}
	else
	{
		vecNewVel.z -= 8;
	}
	SetAbsVelocity( vecNewVel );
}

void CSatchelCharge::Precache( void )
{
	PrecacheModel("models/Weapons/w_slam.mdl");

	PrecacheScriptSound( "SatchelCharge.Pickup" );
	PrecacheScriptSound( "SatchelCharge.Bounce" );

	PrecacheScriptSound( "SatchelCharge.Slide" );
}

void CSatchelCharge::BounceSound( void )
{
	if (gpGlobals->curtime > m_flNextBounceSoundTime)
	{
		EmitSound( "SatchelCharge.Bounce" );

		m_flNextBounceSoundTime = gpGlobals->curtime + 0.1;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
// Input  :
// Output :
//-----------------------------------------------------------------------------
CSatchelCharge::CSatchelCharge(void)
{
	m_vLastPosition.Init();
	m_pMyWeaponSLAM = NULL;
}

CSatchelCharge::~CSatchelCharge(void)
{
	CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
	controller.SoundDestroy( m_soundSlide );
}