text: //========= Copyright Valve Corporation, All rights reserved. ============//
  //
  // Purpose: 
  //
  //=============================================================================//
  
  #include "cbase.h"
  #include "smokegrenade_projectile.h"
  #include "sendproxy.h"
  #include "particle_smokegrenade.h"
  #include "cs_player.h"
  #include "KeyValues.h"
  #include "bot_manager.h"
  #include "weapon_csbase.h"
  
  #define GRENADE_MODEL "models/Weapons/w_eq_smokegrenade_thrown.mdl"
  
  
  LINK_ENTITY_TO_CLASS( smokegrenade_projectile, CSmokeGrenadeProjectile );
  PRECACHE_WEAPON_REGISTER( smokegrenade_projectile );
  
  BEGIN_DATADESC( CSmokeGrenadeProjectile )
  	DEFINE_THINKFUNC( Think_Detonate ),
  	DEFINE_THINKFUNC( Think_Fade ),
  	DEFINE_THINKFUNC( Think_Remove )
  END_DATADESC()
  
  
  CSmokeGrenadeProjectile* CSmokeGrenadeProjectile::Create( 
  	const Vector &position, 
  	const QAngle &angles, 
  	const Vector &velocity, 
  	const AngularImpulse &angVelocity, 
  	CBaseCombatCharacter *pOwner )
  {
  	CSmokeGrenadeProjectile *pGrenade = (CSmokeGrenadeProjectile*)CBaseEntity::Create( "smokegrenade_projectile", position, angles, pOwner );
  	
  	// Set the timer for 1 second less than requested. We're going to issue a SOUND_DANGER
  	// one second before detonation.
  	pGrenade->SetTimer( 1.5 );
  	pGrenade->SetAbsVelocity( velocity );
  	pGrenade->SetupInitialTransmittedGrenadeVelocity( velocity );
  	pGrenade->SetThrower( pOwner );
  	pGrenade->SetGravity( 0.55 );
  	pGrenade->SetFriction( 0.7 );
  	pGrenade->m_flDamage = 100;
  	pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
  	pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );	
  	pGrenade->SetTouch( &CBaseGrenade::BounceTouch );
  
  	pGrenade->SetGravity( BaseClass::GetGrenadeGravity() );
  	pGrenade->SetFriction( BaseClass::GetGrenadeFriction() );
  	pGrenade->SetElasticity( BaseClass::GetGrenadeElasticity() );
  	pGrenade->m_bDidSmokeEffect = false;
  
  	pGrenade->m_pWeaponInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE );
  
  	return pGrenade;
  }
  
  
  void CSmokeGrenadeProjectile::SetTimer( float timer )
  {
  	SetThink( &CSmokeGrenadeProjectile::Think_Detonate );
  	SetNextThink( gpGlobals->curtime + timer );
  
  	TheBots->SetGrenadeRadius( this, 0.0f );
  }
  
  void CSmokeGrenadeProjectile::Think_Detonate()
  {
  	if ( GetAbsVelocity().Length() > 0.1 )
  	{
  		// Still moving. Don't detonate yet.
  		SetNextThink( gpGlobals->curtime + 0.2 );
  		return;
  	}
  
  	TheBots->SetGrenadeRadius( this, SmokeGrenadeRadius );
  
  	// Ok, we've stopped rolling or whatever. Now detonate.
  	ParticleSmokeGrenade *pGren = (ParticleSmokeGrenade*)CBaseEntity::Create( PARTICLESMOKEGRENADE_ENTITYNAME, GetAbsOrigin(), QAngle(0,0,0), NULL );
  	if ( pGren )
  	{
  		pGren->FillVolume();
  		pGren->SetFadeTime( 15, 20 );
  		pGren->SetAbsOrigin( GetAbsOrigin() );
  
  		//tell the hostages about the smoke!
  		CBaseEntity *pEntity = NULL;
  		variant_t var;	//send the location of the smoke?
  		var.SetVector3D( GetAbsOrigin() );
  		while ( ( pEntity = gEntList.FindEntityByClassname( pEntity, "hostage_entity" ) ) != NULL)
  		{
  			//send to hostages that have a resonable chance of being in it while its still smoking
  			if( (GetAbsOrigin() - pEntity->GetAbsOrigin()).Length() < 1000 )
  				pEntity->AcceptInput( "smokegrenade", this, this, var, 0 );
  		}
  
  		// tell the bots a smoke grenade has exploded
  		CCSPlayer *player = ToCSPlayer(GetThrower());
  		if ( player )
  		{
  			IGameEvent * event = gameeventmanager->CreateEvent( "smokegrenade_detonate" );
  			if ( event )
  			{
  				event->SetInt( "userid", player->GetUserID() );
  				event->SetFloat( "x", GetAbsOrigin().x );
  				event->SetFloat( "y", GetAbsOrigin().y );
  				event->SetFloat( "z", GetAbsOrigin().z );
  				gameeventmanager->FireEvent( event );
  			}
  		}
  	}
  
  	m_hSmokeEffect = pGren;
  	m_bDidSmokeEffect = true;
  
  	EmitSound( "BaseSmokeEffect.Sound" );
  
  	m_nRenderMode = kRenderTransColor;
  	SetNextThink( gpGlobals->curtime + 5 );
  	SetThink( &CSmokeGrenadeProjectile::Think_Fade );
  }
  
  
  // Fade the projectile out over time before making it disappear
  void CSmokeGrenadeProjectile::Think_Fade()
  {
  	SetNextThink( gpGlobals->curtime );
  
  	color32 c = GetRenderColor();
  	c.a -= 1;
  	SetRenderColor( c.r, c.b, c.g, c.a );
  
  	if ( !c.a )
  	{
  		TheBots->RemoveGrenade( this );
  
  		SetModelName( NULL_STRING );//invisible
  		SetNextThink( gpGlobals->curtime + 20 );
  		SetThink( &CSmokeGrenadeProjectile::Think_Remove );	// Spit out smoke for 10 seconds.
  		SetSolid( SOLID_NONE );
  	}
  }
  
  
  void CSmokeGrenadeProjectile::Think_Remove()
  {
  	if ( m_hSmokeEffect.Get() )
  		UTIL_Remove( m_hSmokeEffect );
  
  	TheBots->RemoveGrenade( this );
  
  	SetModelName( NULL_STRING );//invisible
  	SetSolid( SOLID_NONE );
  	SetMoveType( MOVETYPE_NONE );
  }
  
  //Implement this so we never call the base class,
  //but this should never be called either.
  void CSmokeGrenadeProjectile::Detonate( void )
  {
  	Assert(!"Smoke grenade handles its own detonation");
  }
  
  
  void CSmokeGrenadeProjectile::Spawn()
  {
  	SetModel( GRENADE_MODEL );
  	BaseClass::Spawn();
  }
  
  
  void CSmokeGrenadeProjectile::Precache()
  {
  	PrecacheModel( GRENADE_MODEL );
  	PrecacheScriptSound( "BaseSmokeEffect.Sound" );
  	PrecacheScriptSound( "SmokeGrenade.Bounce" );
  	BaseClass::Precache();
  }
  
  void CSmokeGrenadeProjectile::BounceSound( void )
  {
  	if ( !m_bDidSmokeEffect )
  	{
  		EmitSound( "SmokeGrenade.Bounce" );
  	}
  }
  