text: //========= Copyright  1996-2005, Valve Corporation, All rights reserved. ============//
  //
  // Purpose: 
  //
  //=============================================================================//
  
  #include "cbase.h"
  #include "weapon_c4.h"
  #include "in_buttons.h"
  #include "cs_gamerules.h"
  #include "decals.h"
  #include "SoundEmitterSystem/isoundemittersystembase.h"
  #include "keyvalues.h"
  #include "fx_cs_shared.h"
  #include "obstacle_pushaway.h"
  #include "particle_parse.h"
  #include "mathlib/vector.h"
  
  #if defined( CLIENT_DLL )
  	#include "c_cs_player.h"
  	#include "HUD/sfweaponselection.h"
  #else
  	#include "cs_player.h"
  	#include "explode.h"
  	#include "mapinfo.h"
  	#include "team.h"
  	#include "func_bomb_target.h"
  	#include "vguiscreen.h"
  	#include "bot.h"
  	#include "cs_player.h"
  
  	#include "cs_gamestats.h"
  	#include "cs_achievement_constants.h"
  
  	#include "cvisibilitymonitor.h"
  	#include "cs_entity_spotting.h"
  
  #endif
  
  // memdbgon must be the last include file in a .cpp file!!!
  #include "tier0/memdbgon.h"
  
  
  #define BLINK_INTERVAL 2.0
  #define PLANTED_C4_MODEL "models/weapons/w_ied_dropped.mdl"
  #define HEIST_MODE_C4_TIME 25
  
  #define WEAPON_C4_ARM_TIME	3.0
  
  #ifndef CLIENT_DLL
  
  #define WEAPON_C4_UPDATE_LAST_VALID_PLAYER_HELD_POSITION_INTERVAL	0.2
  
  // amount of time a player can stop defusing and continue
  const float C4_DEFUSE_GRACE_PERIOD = 0.5f;
  
  // amount of time a player is forced to continue defusing after not USEing. this effects other player's ability to interrupt
  const float C4_DEFUSE_LOCKIN_PERIOD = 0.05f;	
  
  extern ConVar mp_anyone_can_pickup_c4;
  extern ConVar mp_c4_cannot_be_defused;
  
  CON_COMMAND_F( clear_bombs, "", FCVAR_CHEAT )
  {
  	FOR_EACH_VEC( g_PlantedC4s, iBomb )
  	{
  		CPlantedC4 * pC4 = g_PlantedC4s[iBomb];
  		if ( pC4 )
  		{
  			UTIL_Remove( pC4 );
  		}
  	}
  	g_PlantedC4s.RemoveAll();
  }
  
  LINK_ENTITY_TO_CLASS( planted_c4, CPlantedC4 );
  PRECACHE_REGISTER( planted_c4 );
  
  BEGIN_DATADESC( CPlantedC4 )
  	DEFINE_FUNCTION( C4Think ),
  	//Outputs
  	DEFINE_OUTPUT( m_OnBombBeginDefuse, "OnBombBeginDefuse" ),
  	DEFINE_OUTPUT( m_OnBombDefused, "OnBombDefused" ),
  	DEFINE_OUTPUT( m_OnBombDefuseAborted, "OnBombDefuseAborted" ),
  END_DATADESC()
  	
  
  IMPLEMENT_SERVERCLASS_ST( CPlantedC4, DT_PlantedC4 )
  	SendPropBool( SENDINFO(m_bBombTicking) ),
  	SendPropFloat( SENDINFO(m_flC4Blow), 0, SPROP_NOSCALE ),
  	SendPropFloat( SENDINFO(m_flTimerLength), 0, SPROP_NOSCALE ),
  	SendPropFloat( SENDINFO(m_flDefuseLength), 0, SPROP_NOSCALE ),
  	SendPropFloat( SENDINFO(m_flDefuseCountDown), 0, SPROP_NOSCALE ),
  	SendPropBool( SENDINFO(m_bBombDefused) ),
  	SendPropEHandle( SENDINFO(m_hBombDefuser) ),
  END_SEND_TABLE()
  
  	
  BEGIN_PREDICTION_DATA( CPlantedC4 )
  END_PREDICTION_DATA()
  
  
  
  	CUtlVector< CPlantedC4* > g_PlantedC4s;
  
  
  	CPlantedC4::CPlantedC4()
  	{
  		g_PlantedC4s.AddToTail( this );
  
  		// [tj] No planter initially
  		m_pPlanter = NULL;
  		m_pBombDefuser = NULL; //No Defuser Initially
  
  		// [tj] Assume this is the original owner
  		m_bPlantedAfterPickup = false;
  
  		m_bTrainingPlacedByPlayer = false;
  		m_bVoiceAlertFired = false;
  
  		SetSpotRules( CCSEntitySpotting::SPOT_RULE_CT | CCSEntitySpotting::SPOT_RULE_ALWAYS_SEEN_BY_T );
  	}
  
  	CPlantedC4::~CPlantedC4()
  	{
  		g_PlantedC4s.FindAndRemove( this );
  
  		RemoveControlPanels();
  	}
  
  	void CPlantedC4::Spawn()
  	{
  		BaseClass::Spawn();
  
  		SetMoveType( MOVETYPE_NONE );
  		SetSolid( SOLID_NONE );
  		AddFlag( FL_OBJECT );
  
  		SetModel( PLANTED_C4_MODEL );
  		SetSequence(1);	// this sequence keeps the toggle switch in the 'up' position
  
  		SetCollisionBounds( Vector( 0, 0, 0 ), Vector( 8, 8, 8 ) );
  
  		// Detonate in "time" seconds
  		SetThink( &CPlantedC4::C4Think );
  
  		SetNextThink( gpGlobals->curtime + 0.1f );
  
  		if ( CSGameRules() && CSGameRules()->IsPlayingCoopMission() && mp_anyone_can_pickup_c4.GetBool() )
  			m_flTimerLength = 9999;
  		else
  			m_flTimerLength = mp_c4timer.GetInt();
  
  		m_flC4Blow = gpGlobals->curtime + m_flTimerLength;
  		m_fLastDefuseTime = 0.0f;
  		m_bBeingDefused = false;
  		m_bHasExploded = false;
  		m_bBombDefused = false;
  
  		SetFriction( 0.9 );
  
  		m_flDefuseLength = 0.0f;
  
  		SpawnControlPanels();
  
  		VisibilityMonitor_AddEntity( this, 600.0f, NULL, NULL );
  	}
  
  	int CPlantedC4::UpdateTransmitState()
  	{
  		return SetTransmitState( FL_EDICT_FULLCHECK );
  	}
  
  	int CPlantedC4::ShouldTransmit( const CCheckTransmitInfo *pInfo )
  	{
  		// Terrorists always need this object for the radar
  		// Everybody needs it for hiding the round timer and showing the planted C4 scenario icon
  		return FL_EDICT_ALWAYS;
  	}
  
  	void CPlantedC4::Precache()
  	{
  		PrecacheModel( "models/weapons/w_c4_planted.mdl" ); // old, unused
  		PrecacheModel( PLANTED_C4_MODEL );
  		PrecacheModel( "models/props/de_overpass/balloon.mdl" );
  		PrecacheParticleSystem( "weapon_confetti_balloons" );
  		PrecacheModel( "models/weapons/w_eq_multimeter.mdl" );
  		PrecacheVGuiScreen( "c4_panel" );
  	}
  
  	void CPlantedC4::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
  	{
  		pPanelName = "c4_panel";
  	}
  
  	void CPlantedC4::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName )
  	{
  		pPanelName = "vgui_screen";
  	}
  
  	//-----------------------------------------------------------------------------
  	// This is called by the base object when it's time to spawn the control panels
  	//-----------------------------------------------------------------------------
  	void CPlantedC4::SpawnControlPanels()
  	{
  		char buf[64];
  
  		// FIXME: Deal with dynamically resizing control panels?
  
  		// If we're attached to an entity, spawn control panels on it instead of use
  		CBaseAnimating *pEntityToSpawnOn = this;
  		char *pOrgLL = "controlpanel%d_ll";
  		char *pOrgUR = "controlpanel%d_ur";
  		char *pAttachmentNameLL = pOrgLL;
  		char *pAttachmentNameUR = pOrgUR;
  
  		Assert( pEntityToSpawnOn );
  
  		// Lookup the attachment point...
  		int nPanel;
  		for ( nPanel = 0; true; ++nPanel )
  		{
  			Q_snprintf( buf, sizeof( buf ), pAttachmentNameLL, nPanel );
  			int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  			if (nLLAttachmentIndex <= 0)
  			{
  				// Try and use my panels then
  				pEntityToSpawnOn = this;
  				Q_snprintf( buf, sizeof( buf ), pOrgLL, nPanel );
  				nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  				if (nLLAttachmentIndex <= 0)
  					return;
  			}
  
  			Q_snprintf( buf, sizeof( buf ), pAttachmentNameUR, nPanel );
  			int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  			if (nURAttachmentIndex <= 0)
  			{
  				// Try and use my panels then
  				Q_snprintf( buf, sizeof( buf ), pOrgUR, nPanel );
  				nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  				if (nURAttachmentIndex <= 0)
  					return;
  			}
  
  			const char *pScreenName;
  			GetControlPanelInfo( nPanel, pScreenName );
  			if (!pScreenName)
  				continue;
  
  			const char *pScreenClassname;
  			GetControlPanelClassName( nPanel, pScreenClassname );
  			if ( !pScreenClassname )
  				continue;
  
  			// Compute the screen size from the attachment points...
  			matrix3x4_t	panelToWorld;
  			pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld );
  
  			matrix3x4_t	worldToPanel;
  			MatrixInvert( panelToWorld, worldToPanel );
  
  			// Now get the lower right position + transform into panel space
  			Vector lr, lrlocal;
  			pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld );
  			MatrixGetColumn( panelToWorld, 3, lr );
  			VectorTransform( lr, worldToPanel, lrlocal );
  
  			float flWidth = fabs( lrlocal.x );
  			float flHeight = fabs( lrlocal.y );
  
  			CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex );
  			pScreen->ChangeTeam( GetTeamNumber() );
  			pScreen->SetActualSize( flWidth, flHeight );
  			pScreen->SetActive( true );
  			pScreen->MakeVisibleOnlyToTeammates( false );
  			int nScreen = m_hScreens.AddToTail( );
  			m_hScreens[nScreen].Set( pScreen );			
  		}
  	}
  
  	void CPlantedC4::RemoveControlPanels()
  	{
  		// Clear off any screens that are still live.
  		for ( int ii = m_hScreens.Count(); --ii >= 0; )
  		{
  			DestroyVGuiScreen( m_hScreens[ii].Get() );
  		}
  		m_hScreens.RemoveAll();
  	}
  
  	void CPlantedC4::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
  	{
  		// Are we already marked for transmission?
  		if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
  			return;
  
  		BaseClass::SetTransmit( pInfo, bAlways );
  
  		// Force our screens to be sent too.
  		for ( int i=0; i < m_hScreens.Count(); i++ )
  		{
  			CVGuiScreen *pScreen = m_hScreens[i].Get();
  			pScreen->SetTransmit( pInfo, bAlways );
  		}
  	}
  
  	CPlantedC4* CPlantedC4::ShootSatchelCharge( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles )
  	{
  		CPlantedC4 *pGrenade;
  		bool bTrainingPlacedByPlayer = false;
  		if ( CSGameRules()->IsPlayingTraining() )
  		{
  			pGrenade = dynamic_cast< CPlantedC4Training* >( CreateEntityByName( "planted_c4_training" ) );
  			bTrainingPlacedByPlayer = true;
  		}
  		else
  		{
  			pGrenade = dynamic_cast< CPlantedC4* >( CreateEntityByName( "planted_c4" ) );
  		}
  
  		if ( pGrenade )
  		{
  			vecAngles[0] = 0;
  			vecAngles[2] = 0;
  			pGrenade->Init( pevOwner, vecStart, vecAngles, bTrainingPlacedByPlayer );
  			return pGrenade;
  		}
  		else
  		{
  			Warning( "Can't create planted_c4 entity!\n" );
  			return NULL;
  		}
  	}
  
  
  	void CPlantedC4::Init( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles, bool bTrainingPlacedByPlayer )
  	{
  		SetAbsOrigin( vecStart );
  		SetAbsAngles( vecAngles );
  		SetOwnerEntity( pevOwner );
  		SetPlanter( pevOwner );
  
  		m_bTrainingPlacedByPlayer = bTrainingPlacedByPlayer;
  		if ( !m_bTrainingPlacedByPlayer )
  			m_bBombTicking = true;
  
  		Spawn();
  	}
  
  	void CPlantedC4::C4Think()
  	{
  		if (!IsInWorld())
  		{
  			UTIL_Remove( this );
  			return;
  		}
  
  		// network the defuser handle
  		if ( m_bBeingDefused )
  		{
  			m_hBombDefuser = m_pBombDefuser;
  			SetBodygroupPreset( "show_clips" );
  		}
  		else
  		{
  			m_hBombDefuser = INVALID_EHANDLE;
  			SetBodygroupPreset( "hide_clips" );
  		}
  
  		if ( m_bHasExploded )
  		{
  			if ( CSGameRules()->IsPlayingTraining() )
  			{
  				SetThink( &CBaseEntity::SUB_Remove );
  				SetNextThink( gpGlobals->curtime + 1.0f );
  
  				if ( m_pBombDefuser )
  				{
  					m_pBombDefuser->m_bIsDefusing = false;
  					m_pBombDefuser->SetProgressBarTime( 0 );
  					m_pBombDefuser->OnCanceledDefuse();
  					m_pBombDefuser = NULL;
  				}
  
  				m_bBeingDefused = false;
  				m_flDefuseCountDown = 0;
  				m_flDefuseLength = 0;
  			}
  
  			return;
  		}
  
  		//Bomb is not ticking, don't think anymore
  		if( !IsBombActive() )
  		{
  			SetThink( NULL );
  			return;
  		}
  		// [hpe:jason] Decrease the latency between c4 think updates
  		SetNextThink( gpGlobals->curtime + 0.05f );
  
  		// let the bots hear the bomb beeping
  		// BOTPORT: Emit beep events at same time as client effects
  		IGameEvent * event = gameeventmanager->CreateEvent( "bomb_beep" );
  		if( event )
  		{
  			event->SetInt( "entindex", entindex() );
  			gameeventmanager->FireEvent( event );
  		}
  		
  		// 4 seconds before the bomb blows up, have anyone close by on each team say something about it going to blow
  		if (m_flC4Blow - 3.0 <= gpGlobals->curtime && !m_bVoiceAlertFired)
  		{
  			CCSPlayer *pCT = NULL;
  			CCSPlayer *pT = NULL;
  			for ( int i = 1; i <= MAX_PLAYERS; i++ )
  			{
  				CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  				if ( pPlayer && pPlayer->IsAlive() )
  				{
  					if ( !pCT && pPlayer->GetTeamNumber() == TEAM_CT && pCT != m_pBombDefuser )
  					{
  						if ((GetAbsOrigin() - pPlayer->GetAbsOrigin()).AsVector2D().IsLengthLessThan( 1200.0 ))
  							pCT = pPlayer;
  					}
  					else if ( !pT && pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  					{
  						if ((GetAbsOrigin() - pPlayer->GetAbsOrigin()).AsVector2D().IsLengthLessThan( 1200.0 ))
  							pT = pPlayer;
  					}
  				}
  
  				// we have a player from both teams, don't need to find anymore
  				if ( pCT && pT )
  					break;
  			}
  
  			if ( pCT && (!m_bBeingDefused || (m_bBeingDefused && (m_flDefuseCountDown > m_flC4Blow) ) ) )
  			{
  				pCT->Radio( "Radio.GetOutOfThere", "", true );
  				m_bVoiceAlertFired = true;
  			}
  
  			if ( pT )
  			{
  				pT->Radio( "Radio.GetOutOfThere", "", true );
  				m_bVoiceAlertFired = true;
  			}
  		}
  
  		// IF the timer has expired ! blow this bomb up!
  		if (m_flC4Blow <= gpGlobals->curtime)
  		{
  			// kick off the person trying to defuse the bomb
  			if ( m_pBombDefuser )
  			{
  				m_pBombDefuser->m_bIsDefusing = false;
  				m_pBombDefuser->SetProgressBarTime( 0 );
  				m_pBombDefuser->OnCanceledDefuse();
  				m_pBombDefuser = NULL;
  				m_bBeingDefused = false;
  			}
  
  			// for the music, we added a tension filled second after the bomb has ceased to be defusable and explode 1 second after
  			if (m_flC4Blow + 1.0f <= gpGlobals->curtime)
  			{
  				// give the bomber credit for planting the bomb
  				CCSPlayer* pBombOwner = ToCSPlayer(GetOwnerEntity());
  
  //				NOTE[pmf]: removed by design decision
  // 				if ( pBombOwner )
  // 				{
  // 					if (CSGameRules()->m_iRoundWinStatus == WINNER_NONE)
  // 						pBombOwner->IncrementFragCount( 3 );
  // 				}
  
  				CSGameRules()->m_bBombDropped = false;
  
  				trace_t tr;
  				Vector vecSpot = GetAbsOrigin();
  				vecSpot[2] += 8;
  
  				UTIL_TraceLine( vecSpot, vecSpot + Vector ( 0, 0, -40 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  
  				Explode( &tr, DMG_BLAST );
  
  				CSGameRules()->m_bBombPlanted = false;
  
  				CCS_GameStats.Event_BombExploded(pBombOwner);
  
  				IGameEvent * event = gameeventmanager->CreateEvent( "bomb_exploded" );
  				if( event )
  				{
  					event->SetInt( "userid", pBombOwner?pBombOwner->GetUserID():-1 );
  					event->SetInt( "site", m_iBombSiteIndex );
  					event->SetInt( "priority", 20 ); // bomb_exploded
  					gameeventmanager->FireEvent( event );
  				}
  
  				RemoveControlPanels();
  
  				// skip additional processing once the bomb has exploded
  				return;
  			}
  
  		}
  
  
  		// make sure our defuser exists
  		if ( m_bBeingDefused && (m_pBombDefuser == NULL) )
  		{
  			m_bBeingDefused = false;
  		}
  
  
  		//if the defusing process has started
  		if ( m_bBeingDefused && (m_pBombDefuser != NULL) && mp_c4_cannot_be_defused.GetBool() == false )
  		{
  			//if the defusing process has not ended yet
  			if ( gpGlobals->curtime < m_flDefuseCountDown )
  			{
  				int iOnGround = FBitSet( m_pBombDefuser->GetFlags(), FL_ONGROUND );
  
  				const CUserCmd *pCmd = m_pBombDefuser->GetLastUserCommand();
  				bool bPlayerStoppedHoldingUse = !(pCmd->buttons & IN_USE) && (gpGlobals->curtime > m_fLastDefuseTime + C4_DEFUSE_LOCKIN_PERIOD);
  
  				CConfigurationForHighPriorityUseEntity_t cfgUseEntity;
  				bool bPlayerUseIsValidNow = m_pBombDefuser->GetUseConfigurationForHighPriorityUseEntity( this, cfgUseEntity ) &&
  					( cfgUseEntity.m_pEntity == this ) && cfgUseEntity.UseByPlayerNow( m_pBombDefuser, cfgUseEntity.k_EPlayerUseType_Progress );
  
  				//if the bomb defuser has stopped defusing the bomb
  				if ( bPlayerStoppedHoldingUse || !bPlayerUseIsValidNow || !iOnGround )
  				{
  					if ( !iOnGround && m_pBombDefuser->IsAlive() )
  						ClientPrint( m_pBombDefuser, HUD_PRINTCENTER, "#SFUI_Notice_C4_Defuse_Must_Be_On_Ground");
  
  					// tell the bots someone has aborted defusing
  					IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortdefuse" );
  					if( event )
  					{
  						event->SetInt("userid", m_pBombDefuser->GetUserID() );
  						event->SetInt( "priority", 5 ); // bomb_abortdefuse
  						gameeventmanager->FireEvent( event );
  					}
  
  					//cancel the progress bar
  					m_pBombDefuser->SetProgressBarTime( 0 );
  					m_pBombDefuser->OnCanceledDefuse();
  
  					// release the player from being frozen
  					m_pBombDefuser->m_bIsDefusing = false;
  					m_bBeingDefused = false;
  				}
  
  				return;
  			}
  			// training has to pick elements
  			else if ( m_pBombDefuser->IsAlive() && CSGameRules()->IsPlayingTraining() )
  			{
  				Vector soundPosition = m_pBombDefuser->GetAbsOrigin() + Vector( 0, 0, 5 );
  				CPASAttenuationFilter filter( soundPosition );
  
  				IGameEvent * event = gameeventmanager->CreateEvent( "bomb_defused" );
  				if( event )
  				{
  					event->SetInt("userid", m_pBombDefuser->GetUserID() );
  					event->SetInt("site", m_iBombSiteIndex );
  					event->SetInt( "priority", 5 ); // bomb_defused
  					gameeventmanager->FireEvent( event );
  				}
  
  				EmitSound( filter, 0, "c4.disarmfinish", &GetAbsOrigin() );
  				
  				// The bomb has just been disarmed.. Check to see if the round should end now
  				m_bBombTicking = false;
  
  				// release the player from being frozen
  				m_pBombDefuser->m_bIsDefusing = false;
  
  				// Clear their progress bar.
  				m_pBombDefuser->SetProgressBarTime( 0 );
  
  				m_pBombDefuser = NULL;
  				m_bBeingDefused = false;
  
  				m_flDefuseLength = 10;
  
  				m_OnBombDefused.FireOutput(this, m_pBombDefuser);
  				return;
  			}
  			//if the defuse process has ended, kill the c4 (for safety we also check whether the Terrorists have already won the round in which case we cannot score the defuse!)
  			else if ( m_pBombDefuser->IsAlive() && ( CSGameRules()->m_iRoundWinStatus != WINNER_TER ) )
  			{
  				bool roundWasAlreadyWon = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE );	// This condition checks whether CTs already won the round
  				// Check if there are Terrorists alive
  				bool bTerroristsAlive = false;
  				for ( int i = 1; i <= MAX_PLAYERS; i++ )
  				{
  					CCSPlayer* pCheckPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  					if ( !pCheckPlayer )
  						continue;
  					if ( pCheckPlayer->GetTeamNumber() != TEAM_TERRORIST )
  						continue;
  					if ( pCheckPlayer->IsAlive() )
  					{
  						bTerroristsAlive = true;
  						break;
  					}
  				}
  
  				// set down-to-the-wire defuse fun fact
  				m_pBombDefuser->SetDefusedBombWithThisTimeRemaining( m_flC4Blow - gpGlobals->curtime );
  
  				CCS_GameStats.Event_BombDefused(m_pBombDefuser);
  				CSGameRules()->ScoreBombDefuse( m_pBombDefuser, ( bTerroristsAlive && !roundWasAlreadyWon ) );
  				m_pBombDefuser->AddAccountAward( PlayerCashAward::BOMB_DEFUSED );
  
  
  				// record in matchstats
  				if ( CSGameRules()->ShouldRecordMatchStats() )
  				{
  					int iCurrentRound = CSGameRules()->GetTotalRoundsPlayed();
  					++ m_pBombDefuser->m_iMatchStats_Objective.GetForModify( iCurrentRound );
  
  					// Keep track of Match stats in QMM data
  					if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_pBombDefuser->GetHumanPlayerAccountID() ) )
  					{
  						pQMM->m_iMatchStats_Objective[ iCurrentRound ] = m_pBombDefuser->m_iMatchStats_Objective.Get( iCurrentRound );
  					}
  				}
  
  
  				if ( !roundWasAlreadyWon )
  				{
  					// All alive CTs also get assistance credit for bomb defuse.
  					// This way if all Terrorists are eliminated then all alive CTs get 1pt and defuser gets 2pt;
  					// if a Terrorist remains alive then the defuser gets 5 pts and all other alive
  					// teammates get 1 pt for assist / suppressing fire.
  					for ( int i = 1; i <= MAX_PLAYERS; i++ )
  					{
  						CCSPlayer* pCheckPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  						if ( !pCheckPlayer )
  							continue;
  						if ( pCheckPlayer->GetTeamNumber() != TEAM_CT )
  							continue;
  						if ( pCheckPlayer->IsAlive() )
  							CSGameRules()->ScoreBombDefuse( pCheckPlayer, false );
  					}
  				}
  
  				IGameEvent * event = gameeventmanager->CreateEvent( "bomb_defused" );
  				if( event )
  				{
  					event->SetInt("userid", m_pBombDefuser->GetUserID() );
  					event->SetInt("site", m_iBombSiteIndex );
  					event->SetInt( "priority", 5 ); // bomb_defused
  					gameeventmanager->FireEvent( event );
  
  					m_pBombDefuser->AwardAchievement( CSWinBombDefuse );
  
  					float timeToDetonation = ( m_flC4Blow - gpGlobals->curtime );
  
  					if ( (timeToDetonation > 0.0f) && (timeToDetonation <= AchievementConsts::BombDefuseCloseCall_MaxTimeRemaining) )
  					{
  						// Give achievement for defusing with < 1 second before detonation
  						m_pBombDefuser->AwardAchievement( CSBombDefuseCloseCall );
  					}
  
  					// [dwenger] Added for fun-fact support
  					if ( m_pBombDefuser->PickedUpDefuser() )
  					{
  						// Defuser kit was picked up, so set the fun fact
  						m_pBombDefuser->SetDefusedWithPickedUpKit( true );
  					}
  				}
  
  			
  				Vector soundPosition = m_pBombDefuser->GetAbsOrigin() + Vector( 0, 0, 5 );
  				CPASAttenuationFilter filter( soundPosition );
  
  				EmitSound( filter, 0, "c4.disarmfinish", &GetAbsOrigin() );
  
  				// The bomb has just been disarmed.. Check to see if the round should end now
  				m_bBombTicking = false;
  
  				// release the player from being frozen
  				m_pBombDefuser->m_bIsDefusing = false;
  
  				CSGameRules()->m_bBombDefused = true;
  				
  				if ( CSGameRules() && CSGameRules()->IsCSGOBirthday() )
  				{
  					DispatchParticleEffect( "weapon_confetti_balloons", GetAbsOrigin(), QAngle( 0, 0, 0 ) );
  					CPASAttenuationFilter filter( this );
  					filter.UsePredictionRules();
  					EmitSound( filter, entindex(), "Weapon_PartyHorn.Single" );
  					//EmitSound( filter, entindex(), "Birthday_PartyHorn.VO" );
  					//C_BaseEntity::EmitSound(filter, SOUND_FROM_LOCAL_PLAYER, "Birthday_PartyHorn.VO");
  				}
  
  				// Setup MVP granting class in case round wasn't already won
  				class CPlantedC4DefusedMVP : public CCSGameRules::ICalculateEndOfRoundMVPHook_t
  				{
  				public:
  					virtual CCSPlayer* CalculateEndOfRoundMVP() OVERRIDE
  					{
  						if( m_pBombDefuser->HasControlledBotThisRound() )
  						{ 
  							// [dkorus] if we controlled a bot this round, use standard MVP conditions
  							return CSGameRules()->CalculateEndOfRoundMVP();
  						}
  
  						bool bTerroristsAlive = false;
  						for ( int i = 1; i <= MAX_PLAYERS; i++ )
  						{
  							CCSPlayer* pCheckPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  							if ( !pCheckPlayer )
  								continue;
  							if ( pCheckPlayer->GetTeamNumber() != TEAM_TERRORIST )
  								continue;
  							if ( pCheckPlayer->IsAlive() )
  							{
  								bTerroristsAlive = true;
  								break;
  							}
  						}
  
  						if ( bTerroristsAlive || ( m_pBombDefuser->GetNumRoundKills() && !m_pBombDefuser->m_iNumRoundTKs ) )
  						{
  							m_pBombDefuser->IncrementNumMVPs( CSMVP_BOMBDEFUSE );
  							return m_pBombDefuser;
  						}
  
  						if ( CCSPlayer *pDefaultMvp = CSGameRules()->CalculateEndOfRoundMVP() )
  							return pDefaultMvp;
  
  						m_pBombDefuser->IncrementNumMVPs( CSMVP_BOMBDEFUSE );
  						return m_pBombDefuser;
  					}
  					CHandle<CCSPlayer> m_pBombDefuser;
  				} mvpHook;
  				mvpHook.m_pBombDefuser = m_pBombDefuser;
  				if ( !roundWasAlreadyWon )
  					CSGameRules()->m_pfnCalculateEndOfRoundMVPHook = &mvpHook;
  
  				// [menglish] Give the bomb defuser an mvp if they ended the round
  				CSGameRules()->CheckWinConditions();
  
  				// Reset the MVP hook
  				if ( !roundWasAlreadyWon )
  					CSGameRules()->m_pfnCalculateEndOfRoundMVPHook = NULL;
  
  //				NOTE[pmf]: removed by design decision
  // 				// give the defuser credit for defusing the bomb
  // 				m_pBombDefuser->IncrementFragCount( 3 );
  
  				CSGameRules()->m_bBombDropped = false;
  				CSGameRules()->m_bBombPlanted = false;
  				
  				// Clear their progress bar.
  				m_pBombDefuser->SetProgressBarTime( 0 );
  
  				m_pBombDefuser = NULL;
  				m_bBeingDefused = false;
  				m_bBombDefused = true;
  
  				m_flDefuseLength = 10;
  
  				m_OnBombDefused.FireOutput(this, m_pBombDefuser);
  
  				return;
  			}
  
  			//if it gets here then the previouse defuser has taken off or been killed
  			m_OnBombDefuseAborted.FireOutput(this, m_pBombDefuser);
  
  			// tell the bots someone has aborted defusing
  			IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortdefuse" );
  			if ( event )
  			{
  				event->SetInt("userid", m_pBombDefuser->GetUserID() );
  				event->SetInt( "priority", 5 ); // bomb_abortdefuse
  				gameeventmanager->FireEvent( event );
  			}
  
  			// release the player from being frozen
  			m_pBombDefuser->m_bIsDefusing = false;
  			m_bBeingDefused = false;
  			m_pBombDefuser = NULL;
  		}
  	}
  
  	// Regular explosions
  	void CPlantedC4::Explode( trace_t *pTrace, int bitsDamageType )
  	{
  		// Check to see if the round is over after the bomb went off...
  		CSGameRules()->m_bTargetBombed = true;
  		m_bBombTicking = false;
  		m_bHasExploded = true;
  		m_bBombDefused = false;
  
  		bool roundWasAlreadyWon = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE );
  
  		// MVP hook to award the MVP to person who planted the bomb
  		class CPlantedC4ExplodedMVP : public CCSGameRules::ICalculateEndOfRoundMVPHook_t
  		{
  		public:
  			virtual CCSPlayer* CalculateEndOfRoundMVP() OVERRIDE
  			{
  				// All alive Terrorists also get credit for bomb exploding,
  				// this intentionally may include the original planter.
  				// This way if bomb planter survives until explosion he will
  				// get 2 pts and all other alive teammates will get 1 pt
  				// If bomb planter doesn't survive then he gets 1 pt and all
  				// teammates who remained alive defending the bomb get 1 pt
  				for ( int i = 1; i <= MAX_PLAYERS; i++ )
  				{
  					CCSPlayer* pCheckPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  					if ( !pCheckPlayer )
  						continue;
  					if ( pCheckPlayer->GetTeamNumber() != TEAM_TERRORIST )
  						continue;
  					if ( pCheckPlayer->IsAlive() )
  						CSGameRules()->ScoreBombExploded( pCheckPlayer );
  				}
  
  				if ( !pBombOwner )
  					return CSGameRules()->CalculateEndOfRoundMVP();
  
  				// Person who planted the bomb gets credit for the explosion
  				CSGameRules()->ScoreBombExploded( pBombOwner );
  
  				if( pBombOwner->HasControlledBotThisRound() )
  				{ 
  					// [dkorus] if we controlled a bot this round, use standard MVP conditions
  					return CSGameRules()->CalculateEndOfRoundMVP();
  				}
  				else 
  				{
  					pBombOwner->IncrementNumMVPs( CSMVP_BOMBPLANT );
  					return pBombOwner;
  				}
  			}
  			CCSPlayer *pBombOwner;
  		} mvpHook;
  		mvpHook.pBombOwner = ToCSPlayer( GetOwnerEntity() );
  
  		if ( !roundWasAlreadyWon )
  			CSGameRules()->m_pfnCalculateEndOfRoundMVPHook = &mvpHook;
  
  		bool bWin = CSGameRules()->CheckWinConditions();
  		if ( bWin && mvpHook.pBombOwner )
  		{
  			mvpHook.pBombOwner->AwardAchievement( CSWinBombPlant );
  
  			//[tj]more specific achievement for planting the bomb after recovering it.
  			if ( m_bPlantedAfterPickup )
  			{
  				mvpHook.pBombOwner->AwardAchievement( CSWinBombPlantAfterRecovery );
  			}
  		}
  
  		if ( !roundWasAlreadyWon )
  			CSGameRules()->m_pfnCalculateEndOfRoundMVPHook = NULL;
  
  
  		// Do the Damage
  		float flBombRadius;
  		
  		if ( CSGameRules()->IsPlayingGunGameTRBomb() )
  		{
  			flBombRadius = 300;
  		}
  		else
  		{
  			flBombRadius = 500;
  		}
  
  		if ( g_pMapInfo )
  			flBombRadius = g_pMapInfo->m_flBombRadius;
  
  		// Output to the bomb target ent
  		CBaseEntity *pTarget = NULL;
  		variant_t emptyVariant;
  		while ((pTarget = gEntList.FindEntityByClassname( pTarget, "func_bomb_target" )) != NULL)
  		{
  			//Adrian - But only to the one we want!
  			if ( pTarget->entindex() != m_iBombSiteIndex )
  				 continue;
  			
  			pTarget->AcceptInput( "BombExplode", this, this, emptyVariant, 0 );
  				break;
  		}	
  
  		// Pull out of the wall a bit
  		if ( pTrace->fraction != 1.0 )
  		{
  			SetAbsOrigin( pTrace->endpos + (pTrace->plane.normal * 0.6) );
  		}
  
  		{
  			Vector pos = GetAbsOrigin() + Vector( 0,0,8 );
  
  			// Make sure that the bomb exploding is a reliable effect that all clients on this server and GOTV receive
  			CReliableBroadcastRecipientFilter filterBombExplodeReliable;
  
  			// Try using the new particle system instead of temp ents
  			QAngle	vecAngles;
  			DispatchParticleEffect( "explosion_c4_500", pos, vecAngles, ( CBaseEntity * ) NULL, int( -1 ), &filterBombExplodeReliable );
  		}
  
  		// Sound! for everyone
  		CBroadcastRecipientFilter filter;
  		EmitSound( filter, 0, "c4.explode", &GetAbsOrigin() );
  
  		// Decal!
  		UTIL_DecalTrace( pTrace, "Scorch" );
  
  		
  		// Shake!
  		UTIL_ScreenShake( pTrace->endpos, 25.0, 150.0, 1.0, 3000, SHAKE_START );
  
  
  		SetOwnerEntity( NULL ); // can't traceline attack owner if this is set
  
  		CSGameRules()->RadiusDamage( 
  			CTakeDamageInfo( this, GetOwnerEntity(), flBombRadius, bitsDamageType ),
  			GetAbsOrigin(),
  			flBombRadius * 3.5,	//Matt - don't ask me, this is how CS does it.
  			CLASS_NONE,
  			true );	// IGNORE THE WORLD!!
  
  		// send director message, that something important happed here
  		/*
  		MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR );
  			WRITE_BYTE ( 9 );	// command length in bytes
  			WRITE_BYTE ( DRC_CMD_EVENT );	// bomb explode
  			WRITE_SHORT( ENTINDEX(this->edict()) );	// index number of primary entity
  			WRITE_SHORT( 0 );	// index number of secondary entity
  			WRITE_LONG( 15 | DRC_FLAG_FINAL );   // eventflags (priority and flags)
  		MESSAGE_END();
  		*/
  	}
  
  	
  	// For CTs to defuse the c4
  	void CPlantedC4::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  	{
  		//Can't defuse if its already defused or if it has blown up (or training)
  		if( !IsBombActive() || m_flC4Blow < gpGlobals->curtime || mp_c4_cannot_be_defused.GetBool() == true )
  		{
  			SetUse( NULL );
  			return;
  		}
  
  		CCSPlayer *player = dynamic_cast< CCSPlayer* >( pActivator );
  
  		// Can't defuse a bomb if we're not CT or we're in a no defuse area.
  		if ( !player || player->GetTeamNumber() != TEAM_CT || player->m_bInNoDefuseArea)
  			return;
  
  		// make sure human players can see the bomb (or the bomb's nearby line-of-sight points)
  		// to ensure we aren't defusing from the room below or something
  		if ( !player->IsBot() )
  		{
  			trace_t result;
  			bool bFoundBomb = false;
  
  			Vector vecLineOfSightPoints[] = {
  				Vector(  0,  0,  1 ),
  				Vector(  5,  5,  5 ),
  				Vector( -5,  5,  5 ),
  				Vector(  5, -5,  5 ),
  				Vector( -5, -5,  5 ),
  				Vector(  0,  0,  5 )
  			};
  			int nNumLOSpts = ARRAYSIZE( vecLineOfSightPoints );
  
  			for ( int i=0; i<nNumLOSpts; i++ )
  			{
  				Vector vecLineOfSightTarget = VectorTransform( vecLineOfSightPoints[i], EntityToWorldTransform() );
  				//debugoverlay->AddBoxOverlay( vecLineOfSightTarget, Vector(-0.5f,-0.5f,-0.5f), Vector(0.5f,0.5f,0.5f), QAngle(0,0,0), 0,0,255,255, 5 );
  
  				UTIL_TraceLine( player->EyePosition(), vecLineOfSightTarget, CONTENTS_SOLID, this, COLLISION_GROUP_NONE, &result );
  
  				if ( result.fraction < 1.0f )
  				{
  					//debugoverlay->AddLineOverlay( result.startpos, result.endpos, 255,0,0, 255, 0.15f, 10 );
  					continue;
  				}
  				//debugoverlay->AddLineOverlay( result.startpos, result.endpos, 0,255,0, 255, 0.15f, 10 );
  
  				// this trace succeeded, so we're ok to defuse.
  				bFoundBomb = true;
  				break;
  			}
  
  			if ( !bFoundBomb )
  				return;
  		}
  
  		if ( m_bBeingDefused )
  		{
  			if ( player != m_pBombDefuser )
  			{
  				if ( player->m_iNextTimeCheck < gpGlobals->curtime )
  				{
  					ClientPrint( player, HUD_PRINTCENTER, "#SFUI_Notice_Bomb_Already_Being_Defused" );
  					player->m_iNextTimeCheck = gpGlobals->curtime + 1.f;
  				}
  				return;
  			}
  
  			m_fLastDefuseTime = gpGlobals->curtime;
  		}
  		else
  		{
  			// freeze the player in place while defusing
  
  			IGameEvent * event = gameeventmanager->CreateEvent("bomb_begindefuse" );
  			if( event )
  			{
  				event->SetInt( "userid", player->GetUserID() );
  				if ( player->HasDefuser() )
  				{
  					event->SetInt( "haskit", 1 );
  				}
  				else
  				{
  					event->SetInt( "haskit", 0 );
  				}
  				event->SetInt( "priority", 5 ); // bomb_begindefuse
  				gameeventmanager->FireEvent( event );
  			}
  
  			Vector soundPosition = player->GetAbsOrigin() + Vector( 0, 0, 5 );
  			CPASAttenuationFilter filter( soundPosition );
  			EmitSound( filter, 0, "c4.disarmstart", &GetAbsOrigin() );
  
  /*
  			if ( m_pBombDefuser == player && gpGlobals->curtime < ( m_fLastDefuseTime + C4_DEFUSE_GRACE_PERIOD ) )
  			{
  				// if we're allowing the player to continue defusing, push the completion time ahead by the appropriate amount
  				float fTimeGap = m_fLastDefuseTime - gpGlobals->curtime;
  				m_flDefuseCountDown += fTimeGap;
  
  				// we don't have a method for setting up the progress bar "in progress", so do it manually
  				player->m_iProgressBarDuration = m_flDefuseLength;
  				player->m_flProgressBarStartTime = m_flDefuseCountDown - m_flDefuseLength;
  			}
  			else
  */
  			{
  				m_flDefuseLength = player->HasDefuser() ? 5 : 10;
  				m_flDefuseCountDown = gpGlobals->curtime + m_flDefuseLength;
  				player->SetProgressBarTime( m_flDefuseLength );
  			}
  
  			m_pBombDefuser = player;
  			m_bBeingDefused = TRUE;
  			player->m_bIsDefusing = true;
  			
  			m_fLastDefuseTime = gpGlobals->curtime;
  
  			//start the progress bar
  
  			player->OnStartedDefuse();
  
  			m_OnBombBeginDefuse.FireOutput(this, player);
  		}
  	}
  
  	void CPlantedC4::ActivateSetTimerLength( float flTimerLength )
  	{
  		if ( flTimerLength < 0 )
  		{
  			Error( "ActivateSetTimerLength value is less than 0!" );
  			return;
  		}
  
  		// Detonate in "time" seconds
  		m_flTimerLength = flTimerLength;
  		m_flC4Blow = gpGlobals->curtime + m_flTimerLength;
  
  		SetThink( &CPlantedC4::C4Think );
  		SetNextThink( gpGlobals->curtime + 0.1f );
  
  		m_bBombTicking = true;
  	}
  
  ///////////////////////////////////////////////////////////
  // Training version of the C4 - doesn't explode
  ///////////////////////////////////////////////////////////
  
  	LINK_ENTITY_TO_CLASS( planted_c4_training, CPlantedC4Training );
  	PRECACHE_REGISTER( planted_c4_training );
  
  	BEGIN_PREDICTION_DATA( CPlantedC4Training )
  	END_PREDICTION_DATA()
  
  	BEGIN_DATADESC( CPlantedC4Training )
  
  	// Inputs
  	DEFINE_INPUTFUNC( FIELD_FLOAT, "ActivateSetTimerLength", InputActivateSetTimerLength ),
  	//Outputs
  	DEFINE_OUTPUT( m_OnBombExploded, "OnBombExploded" ),
  
  	END_DATADESC()
  
  	void CPlantedC4Training::InputActivateSetTimerLength( inputdata_t &inputdata )
  	{
  		ActivateSetTimerLength( inputdata.value.Float() );
  	}
  
  	void CPlantedC4Training::Explode( trace_t *pTrace, int bitsDamageType )
  	{
  		// Check to see if the round is over after the bomb went off...
  		CSGameRules()->m_bTargetBombed = true;
  		m_bBombTicking = false;
  		m_bHasExploded = true;
  
  		// Pull out of the wall a bit
  		if ( pTrace->fraction != 1.0 )
  		{
  			SetAbsOrigin( pTrace->endpos + (pTrace->plane.normal * 0.6) );
  		}
  
  		QAngle	vecAngles;
  		DispatchParticleEffect( "c4_train_ground_effect", GetAbsOrigin(), vecAngles );
  
  		// Sound! for everyone
  		CBroadcastRecipientFilter filter;
  		EmitSound( filter, 0, "tr.C4Explode", &GetAbsOrigin() );
  
  		// Decal!
  		//UTIL_DecalTrace( pTrace, "Scorch" );
  
  		// Shake!
  		//UTIL_ScreenShake( pTrace->endpos, 25.0, 150.0, 1.0, 3000, SHAKE_START );
  
  		SetOwnerEntity( NULL ); // can't traceline attack owner if this is set
  
  		m_OnBombExploded.FireOutput(this, this);
  	}
  #endif // #ifndef CLIENT_DLL
  
  
  
  // -------------------------------------------------------------------------------- //
  // Tables.
  // -------------------------------------------------------------------------------- //
  
  IMPLEMENT_NETWORKCLASS_ALIASED( C4, DT_WeaponC4 )
  
  BEGIN_NETWORK_TABLE( CC4, DT_WeaponC4 )
  	#ifdef CLIENT_DLL
  		RecvPropBool( RECVINFO( m_bStartedArming ) ),
  		RecvPropBool( RECVINFO( m_bBombPlacedAnimation ) ),
  		RecvPropFloat( RECVINFO( m_fArmedTime ) ),
  		RecvPropBool( RECVINFO( m_bShowC4LED ) ),
  		RecvPropBool( RECVINFO( m_bIsPlantingViaUse ) )
  	#else
  		SendPropBool( SENDINFO( m_bStartedArming ) ),
  		SendPropBool( SENDINFO( m_bBombPlacedAnimation ) ),
  		SendPropFloat( SENDINFO( m_fArmedTime ), 0, SPROP_NOSCALE ),
  		SendPropBool( SENDINFO( m_bShowC4LED ) ),
  		SendPropBool( SENDINFO( m_bIsPlantingViaUse ) )	
  	#endif
  END_NETWORK_TABLE()
  
  #if defined CLIENT_DLL
  BEGIN_PREDICTION_DATA( CC4 )
  	DEFINE_PRED_FIELD( m_bStartedArming, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  	DEFINE_PRED_FIELD( m_bBombPlacedAnimation, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  	DEFINE_PRED_FIELD( m_fArmedTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  	DEFINE_PRED_FIELD( m_bShowC4LED, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  	DEFINE_PRED_FIELD( m_bIsPlantingViaUse, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  END_PREDICTION_DATA()
  #endif
  
  LINK_ENTITY_TO_CLASS_ALIASED( weapon_c4, C4 );
  PRECACHE_REGISTER( weapon_c4 );
  
  
  
  // -------------------------------------------------------------------------------- //
  // Globals.
  // -------------------------------------------------------------------------------- //
  
  const float DROPPED_LIGHT_INTERVAL = 1.0f;
  
  // -------------------------------------------------------------------------------- //
  // CC4 implementation.
  // -------------------------------------------------------------------------------- //
  
  CC4::CC4()
  {
  	m_bDroppedFromDeath = false;
  
  #if defined( CLIENT_DLL )
  	m_szScreenText[0] = '\0';
  	m_bShowC4LED = false;
  #else
  	SetSpotRules( CCSEntitySpotting::SPOT_RULE_CT | CCSEntitySpotting::SPOT_RULE_ALWAYS_SEEN_BY_T );
  	m_vecLastValidPlayerHeldPosition = Vector(0,0,0);
  #endif
  
  	m_bIsPlantingViaUse = false;
  }
  
  
  CC4::~CC4()
  {
  }
  
  void CC4::Spawn()
  {
  	BaseClass::Spawn();
  
  	//Don't allow players to shoot the C4 around
  	SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  
  	//Don't be damaged / moved by explosions
  	m_takedamage = DAMAGE_NO;
  
  	m_bBombPlanted = false;
  	
  #if defined( CLIENT_DLL )
  
  	SetNextClientThink( gpGlobals->curtime + DROPPED_LIGHT_INTERVAL );
  
  #else
  	SetNextThink( gpGlobals->curtime );
  
  #endif
  
  }
  
  void CC4::ItemPostFrame()
  {
  	CCSPlayer *pPlayer = GetPlayerOwner();
  	if ( !pPlayer )
  		return;
  
  	// Disable all the firing code.. the C4 grenade is all custom.
  	if ( pPlayer->m_nButtons & IN_ATTACK || (pPlayer->m_nButtons & IN_USE && m_bIsPlantingViaUse) )
  	{
  		if ( gpGlobals->curtime >= m_flNextPrimaryAttack ) 
  			PrimaryAttack();
  	}
  	else
  	{
  		WeaponIdle();
  	}
  
  	if ( !(pPlayer->m_nButtons & IN_USE) )
  		m_bIsPlantingViaUse = false;
  	
  }
  
  void CC4::WeaponReset( void )
  {
  	m_bShowC4LED = false;
  
  	BaseClass::WeaponReset();
  }
  
  #if defined( CLIENT_DLL )
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  	void CC4::OnDataChanged( DataUpdateType_t type )
  	{
  		BaseClass::OnDataChanged( type );
  	}
  
  	void CC4::UpdateOnRemove( void )
  	{
  		BaseClass::UpdateOnRemove();
  
  		// when a c4 is removed, force the local player to update thier inventory screen
  		if ( !C_BasePlayer::GetLocalPlayer() || !engine->IsLocalPlayerResolvable() )
  			return; // early out if local player does not exsist
  
  		C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
  		if ( pPlayer )
  		{
  			SFWeaponSelection *pHudWS = GET_HUDELEMENT( SFWeaponSelection );
  			if ( pHudWS )
  			{
  				pHudWS->ShowAndUpdateSelection( WEPSELECT_SWITCH, NULL );
  			}
  		}
  	}
  
  	void CC4::ClientThink( void )
  	{
  		BaseClass::ClientThink();
  		
  		SetNextClientThink( gpGlobals->curtime + DROPPED_LIGHT_INTERVAL );
  		
  		// This think function is just for updating the blinking light of the dropped bomb.
  		// So if we have an owner, we don't want to blink.
  		if ( IsDormant() || NULL != GetPlayerOwner() || !CSGameRules()->m_bBombDropped )
  		{
  			return;
  		}
  		
  
  		int ledAttachmentIndex = LookupAttachment("led");
  		DispatchParticleEffect( "c4_timer_light_dropped", PATTACH_POINT_FOLLOW, this, ledAttachmentIndex, false, -1 );
  
  	}
  
  	bool CC4::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
  	{
  		if( event == 7001 )
  		{
  			// "Custom code" c4 disabled
  			//
  			/*//if we're clearing the screen text, or have no custom description, set the text normally using the passed param.
  			if (   !V_strcmp( options, "" )
  				|| !V_strcmp( options, "*******" )
  				|| GetEconItemView() == NULL
  				|| CALL_ATTRIB_HOOK_BOOL( custom_bombcode ) == false
  				|| GetEconItemView()->GetCustomDesc() == NULL
  				)
  			{
  				Q_strncpy( m_szScreenText, options, 16 );
  			}
  			else
  			{
  				//otherwise we have a custom code to display
  				CEconItemView *pItem = GetEconItemView();
  				if ( pItem && pItem->GetCustomDesc() )
  				{
  					if ( strlen(m_szScreenText) >= 7 )
  					{
  						//if the 7-char code is full, replace it with asterisks (this will happen at the end of the plant animation anyway)
  						Q_strncpy( m_szScreenText, "*******\0", 8 );
  					}
  					else
  					{
  						//we're still under 7 chars, so add an additional char of the custom code
  						Q_strncpy( m_szScreenText, pItem->GetCustomDesc(), strlen(m_szScreenText)+2 );
  					}
  				}
  			}*/
  
  			//set the screen text to the string in 'options'
  			Q_strncpy( m_szScreenText, options, 16 );
  
  			return true;
  		}
  		return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
  	}
  
  	char *CC4::GetScreenText( void )
  	{
  		return m_szScreenText;
  	}
  
  	void CC4::CreateLEDEffect( void )
  	{
  		if ( GetPlayerOwner() )
  			GetPlayerOwner()->CreateC4Effect( GetWeaponForEffect(), true );
  	}
  
  	void CC4::RemoveLEDEffect( void )
  	{
  		if ( GetPlayerOwner() )
  			GetPlayerOwner()->RemoveC4Effect( true );
  	}
  
  #else
  
  void CC4::PhysicsTouchTriggers(const Vector *pPrevAbsOrigin)
  {
  	// Normally items like ammo or weapons aren't expected to touch other triggers, but C4 is a special case
  	edict_t *pEntity = edict();
  
  	if (pEntity && !IsWorld())
  	{
  		Assert(CollisionProp());
  
  		//Dropped bombs are both solid and have no owner. In this state, unlike other weapons, they can  
  		//now touch triggers so long they haven't had their position reset by a bomb reset trigger.
  		if ( IsSolid() && (GetPlayerOwner() == NULL) )
  		{
  			SetCheckUntouch(true);
  			engine->SolidMoved(pEntity, CollisionProp(), pPrevAbsOrigin, sm_bAccurateTriggerBboxChecks);
  		}
  	}
  }
  
  #endif //CLIENT_DLL
  
  #ifdef GAME_DLL
  	
  	void CC4::Think()
  	{
  		//If the bomb is held by an alive player standing on the ground, then we can use this
  		//position as the last known valid position to respawn the bomb if it gets reset.
  		CCSPlayer *pPlayer = GetPlayerOwner();
  		if ( pPlayer && pPlayer->IsAlive() && FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
  		{
  			m_vecLastValidPlayerHeldPosition = pPlayer->GetAbsOrigin();
  		}
  
  		SetNextThink( gpGlobals->curtime + WEAPON_C4_UPDATE_LAST_VALID_PLAYER_HELD_POSITION_INTERVAL );
  	}
  
  	void CC4::ResetToLastValidPlayerHeldPosition()
  	{
  		//When reset, the bomb returns to its last known valid position.
  		CCSPlayer *pPlayer = GetPlayerOwner();
  		if ( !pPlayer && GetAbsOrigin() != m_vecLastValidPlayerHeldPosition )
  		{
  			// Teleport the bomb facing up so the flashing light is clearly visible.
  			Vector vecResetPos = m_vecLastValidPlayerHeldPosition + Vector(0,0,8);
  			QAngle angResetAng = QAngle( 0, RandomInt(0,360), 0 );
  
  			// trace to ground
  			trace_t c4TeleportTrace;
  			UTIL_TraceHull( vecResetPos, vecResetPos + Vector(0,0,-8), Vector(-3,-3,-1), Vector(3,3,1), MASK_PLAYERSOLID, NULL, COLLISION_GROUP_PLAYER_MOVEMENT, &c4TeleportTrace );
  			if ( !c4TeleportTrace.startsolid && c4TeleportTrace.DidHit() )
  			{
  				vecResetPos += ( c4TeleportTrace.fraction * Vector(0,0,-8) );
  			}
  
  			Teleport( &vecResetPos, &angResetAng, NULL );
  
  			// Set the physics object asleep so it doesn't tumble off precarious ledges and keep resetting.
  			IPhysicsObject *pObj = VPhysicsGetObject();
  			if ( pObj )
  				pObj->Sleep();
  
  		}
  	}
  
  	unsigned int CC4::PhysicsSolidMaskForEntity( void ) const
  	{
  		return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_PLAYERCLIP;
  	}
  
  	void CC4::Precache()
  	{
  		PrecacheVGuiScreen( "c4_view_panel" );
  
  		PrecacheScriptSound( "c4.disarmfinish" );
  		PrecacheScriptSound( "c4.explode" );
  		PrecacheScriptSound( "c4.disarmstart" );
  		PrecacheScriptSound( "c4.plant" );
  		PrecacheScriptSound( "C4.PlantSound" );
  		PrecacheScriptSound( "c4.click" );
  
  		// training
  		PrecacheParticleSystem( "c4_train_ground_effect" );
  		PrecacheScriptSound( "tr.C4Explode" );
  
  		BaseClass::Precache();
  	}
  
  	int CC4::UpdateTransmitState()
  	{
  		return SetTransmitState( FL_EDICT_FULLCHECK );
  	}
  
  	int CC4::ShouldTransmit( const CCheckTransmitInfo *pInfo )
  	{
  		CBasePlayer *pPlayer = ToBasePlayer( CBaseEntity::Instance( pInfo->m_pClientEnt ) );
  		if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT )
  		{
  			return FL_EDICT_PVSCHECK;
  		}
  		else
  		{
  			// This is needed for the instructor message
  			return FL_EDICT_ALWAYS;
  		}
  	}
  
  	//-----------------------------------------------------------------------------
  	// Purpose: Gets info about the control panels
  	//-----------------------------------------------------------------------------
  	void CC4::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
  	{
  		pPanelName = "c4_view_panel";
  	}
  
  	bool CC4::ShouldRemoveOnRoundRestart()
  	{
  		// Doesn't matter if we have an owner or not.. always remove the C4 when the round restarts.
  		// The gamerules will give another C4 to some lucky player.
  		CCSPlayer *pPlayer = GetPlayerOwner();
  		if ( pPlayer && pPlayer->GetActiveWeapon() == this )
  			engine->ClientCommand( pPlayer->edict(), "lastinv reset\n" );
  		return true;
  	}
  #endif
  
  bool CC4::Deploy( )
  {
  	//m_bShowC4LED = true;
  #ifdef CLIENT_DLL
  	//CreateLEDEffect();
  #endif
  
  	return BaseClass::Deploy();
  }
  
  bool CC4::Holster( CBaseCombatWeapon *pSwitchingTo )
  {
  
  #ifdef GAME_DLL
  	CCSPlayer *pPlayer = GetPlayerOwner();
  	if ( pPlayer )
  		pPlayer->SetProgressBarTime( 0 );
  
  	if ( m_bStartedArming )
  	{
  		AbortBombPlant();
  	}
  #else
  	//RemoveLEDEffect();
  #endif
  
  	//m_bShowC4LED = false;
  
  	return BaseClass::Holster( pSwitchingTo );
  }
  
  void CC4::PrimaryAttack()
  {
  	bool	bArmingTimeSatisfied = false;
  	CCSPlayer *pPlayer = GetPlayerOwner();
  	if ( !pPlayer )
  		return;
  
  	int onGround = FBitSet( pPlayer->GetFlags(), FL_ONGROUND );
  	CBaseEntity *groundEntity = (onGround) ? pPlayer->GetGroundEntity() : NULL;
  	trace_t trPlant;
  	if ( groundEntity )
  	{
  		// Don't let us stand on players, breakables, or pushaway physics objects to plant
  		if ( groundEntity->IsPlayer() ||
  			IsPushableEntity( groundEntity ) ||
  #ifndef CLIENT_DLL
  			IsBreakableEntity( groundEntity ) ||
  #endif // !CLIENT_DLL
  			IsPushAwayEntity( groundEntity ) )
  		{
  			onGround = false;
  		}
  
  		if ( onGround )
  		{
  			Vector vecStart = GetAbsOrigin() + Vector(0,0,8);
  			Vector vecEnd = GetAbsOrigin() + Vector(0,0,-38);
  			UTIL_TraceHull( vecStart, vecEnd, Vector( -3, -3, 0 ), Vector( 3, 3, 16 ), MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &trPlant );
  			if ( trPlant.fraction == 1.0 )
  			{
  				onGround = false;
  			}
  		}
  	}
  
  	if( m_bStartedArming == false && m_bBombPlanted == false )
  	{
  		if( pPlayer->m_bInBombZone && onGround )
  		{
  			m_bStartedArming = true;
  			m_fArmedTime = gpGlobals->curtime + WEAPON_C4_ARM_TIME;
  			m_bBombPlacedAnimation = false;
  
  			pPlayer->m_bDuckOverride = true;
  
  #if !defined( CLIENT_DLL )			
  			pPlayer->SetAttemptedBombPlace();
  
  			// init the beep flags
  			int i;
  			for( i=0;i<NUM_BEEPS;i++ )
  				m_bPlayedArmingBeeps[i] = false;
  
  			// freeze the player in place while planting
  
  			// player "arming bomb" animation
  			pPlayer->SetAnimation( PLAYER_ATTACK1 );
  	
  			pPlayer->SetNextAttack( gpGlobals->curtime );
  
  			IGameEvent * event = gameeventmanager->CreateEvent( "bomb_beginplant" );
  			if( event )
  			{
  				event->SetInt("userid", pPlayer->GetUserID() );
  				event->SetInt("site", pPlayer->m_iBombSiteIndex );
  				event->SetInt( "priority", 5 ); // bomb_beginplant
  				gameeventmanager->FireEvent( event );
  			}
  
  			PlayPlantInitSound();
  
  			if ( pPlayer && !pPlayer->IsBot() && pPlayer->m_flC4PlantTalkTimer < gpGlobals->curtime )
  			{
  				// for console, we don't want to show the chat text because it almost always overlaps 
  				// with the bomb planted alert text in the center of the screen
  				if ( IsGameConsole() || engine->IsDedicatedServerForXbox() || engine->IsDedicatedServerForPS3() )
  					pPlayer->Radio( "PlantingBomb", "", true );
  				else
  					pPlayer->Radio( "PlantingBomb", "#Cstrike_TitlesTXT_Planting_Bomb", true );
  				pPlayer->m_flC4PlantTalkTimer = gpGlobals->curtime + 10.0f;
  			}
  #endif
  
  			SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  
  			#ifndef CLIENT_DLL
  			if ( pPlayer && !pPlayer->IsDormant() )
  			{
  				pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN_PRIMARY );
  			}
  			#endif
  			//FX_PlantBomb( pPlayer->entindex(), pPlayer->Weapon_ShootPosition(), PLANTBOMB_PLANT );
  		}
  		else
  		{
  			if ( !pPlayer->GetUseEntity() )
  			{
  				if ( !pPlayer->m_bInBombZone )
  				{
  					ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_C4_Plant_At_Bomb_Spot");
  
  #if defined( CLIENT_DLL )
  					STEAMWORKS_TESTSECRET_AMORTIZE(5);
  #endif
  				}
  				else
  				{
  					ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_C4_Plant_Must_Be_On_Ground");
  
  #if defined( CLIENT_DLL )
  					STEAMWORKS_TESTSECRET_AMORTIZE(7);
  #endif
  				}
  			}
  
  			m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  			return;
  		}
  	}
  	else
  	{
  		if ( !onGround || !pPlayer->m_bInBombZone )
  		{
  			if( !pPlayer->m_bInBombZone )
  			{
  				ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_C4_Arming_Cancelled" );
  			}
  			else
  			{
  				ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_C4_Plant_Must_Be_On_Ground" );
  
  #if defined( CLIENT_DLL )
  				STEAMWORKS_TESTSECRET_AMORTIZE(9);
  #endif
  			}
  
  			AbortBombPlant();
  
  			if(m_bBombPlacedAnimation == true) //this means the placement animation is canceled
  			{
  				SendWeaponAnim( ACT_VM_DRAW );
  			}
  			else
  			{
  				SendWeaponAnim( ACT_VM_IDLE );
  			}
  			
  			return;
  		}
  		else
  		{
  		// we no longer play arming beeps to all player, we only play the initialization sound
  // #ifndef CLIENT_DLL
  // 			PlayArmingBeeps();
  // #endif
  
  			if( gpGlobals->curtime >= m_fArmedTime ) //the c4 is ready to be armed
  			{
  				//check to make sure the player is still in the bomb target area
  				bArmingTimeSatisfied = true;
  			}
  			else if( ( gpGlobals->curtime >= (m_fArmedTime - 0.75) ) && ( !m_bBombPlacedAnimation ) )
  			{
  				//call the c4 Placement animation 
  				m_bBombPlacedAnimation = true;
  
  				SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  				
  #if !defined( CLIENT_DLL )
  				// player "place" animation
  				//pPlayer->SetAnimation( PLAYER_HOLDBOMB );
  #endif
  			}
  		}
  	}
  
  	if ( bArmingTimeSatisfied && m_bStartedArming )
  	{
  		m_bStartedArming = false;
  		m_fArmedTime = 0;
  		
  		if( pPlayer->m_bInBombZone )
  		{
  #if !defined( CLIENT_DLL )
  			CPlantedC4 *pC4 = CPlantedC4::ShootSatchelCharge( pPlayer, pPlayer->GetAbsOrigin(), pPlayer->GetAbsAngles() );
  
  			if ( pC4 && trPlant.fraction < 1.0 )
  			{
  				pC4->SetBombSiteIndex( pPlayer->m_iBombSiteIndex );
  				pC4->SetAbsOrigin( trPlant.endpos );
  
  				//bomb aligns to planted surface normal within a threshold
  				if ( fabs(trPlant.plane.normal.Dot(Vector(0,0,1))) > 0.65f )
  				{
  					//get the player forward vector
  					Vector vecFlatForward;
  					VectorCopy( pPlayer->Forward(), vecFlatForward );
  					vecFlatForward.z = 0;
  
  					//derive c4 forward and right
  					Vector vecC4Right = CrossProduct( vecFlatForward.Normalized(), trPlant.plane.normal );
  					Vector vecC4Forward = CrossProduct( vecC4Right, trPlant.plane.normal );
  					
  					QAngle C4Angle;
  					VectorAngles( -vecC4Forward, trPlant.plane.normal, C4Angle );
  					pC4->SetAbsAngles( C4Angle );
  				}
  
  				CBombTarget *pBombTarget = (CBombTarget*)UTIL_EntityByIndex( pPlayer->m_iBombSiteIndex );
  				
  				if ( pBombTarget )
  				{
  					CBaseEntity *pAttachPoint = gEntList.FindEntityByName( NULL, pBombTarget->GetBombMountTarget() );
  
  					if ( pAttachPoint )
  					{
  						pC4->SetAbsOrigin( pAttachPoint->GetAbsOrigin() );
  						pC4->SetAbsAngles( pAttachPoint->GetAbsAngles() );
  						pC4->SetParent( pAttachPoint );
  					}
  
  					variant_t emptyVariant;
  					pBombTarget->AcceptInput( "BombPlanted", pC4, pC4, emptyVariant, 0 );
  				}
  
  				// [tj] If the bomb is planted by someone that picked it up after the 
  				//      original owner was killed, pass that along to the planted bomb
  				pC4->SetPlantedAfterPickup( m_bDroppedFromDeath );
  			}
  
  			// Determine how elapsed time from start of round until the bomb was planted
  			float plantingTime = gpGlobals->curtime - CSGameRules()->GetRoundStartTime();
  
  			// Award achievement to bomb planter if time <= 25 seconds
  			if ( (plantingTime > 0.0f) && 
  				 (plantingTime <= AchievementConsts::FastBombPlant_Time) &&
  				 !CSGameRules()->IsPlayingGunGameTRBomb() )
  			{
  				pPlayer->AwardAchievement( CSPlantBombWithin25Seconds );
  			}
  
  			pPlayer->SetLastWeaponBeforeAutoSwitchToC4( NULL ); // completed a bomb plant, this clears out our saved value for switching back to a saved weapon
  			pPlayer->SetBombPlacedTime( gpGlobals->curtime );
  			CCS_GameStats.Event_BombPlanted( pPlayer );
  			CSGameRules()->ScoreBombPlant( pPlayer );
  
  			// record in matchstats
  			if ( CSGameRules()->ShouldRecordMatchStats() )
  			{
  				int iCurrentRound = CSGameRules()->GetTotalRoundsPlayed();
  				++ pPlayer->m_iMatchStats_Objective.GetForModify( iCurrentRound );
  
  				// Keep track of Match stats in QMM data
  				if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( pPlayer->GetHumanPlayerAccountID() ) )
  				{
  					pQMM->m_iMatchStats_Objective[ iCurrentRound ] = pPlayer->m_iMatchStats_Objective.Get( iCurrentRound );
  				}
  			}
  
  			pPlayer->AddAccountAward( PlayerCashAward::BOMB_PLANTED );
  
  			IGameEvent * event = gameeventmanager->CreateEvent( "bomb_planted" );
  			if( event )
  			{
  				event->SetInt("userid", pPlayer->GetUserID() );
  				event->SetInt("site", pPlayer->m_iBombSiteIndex );
  				event->SetInt("posx", pPlayer->GetAbsOrigin().x );
  				event->SetInt("posy", pPlayer->GetAbsOrigin().y );
  				event->SetInt( "priority", 5 ); // bomb_planted
  				gameeventmanager->FireEvent( event );
  			}
  
  			// Fire a beep event also so the bots have a chance to hear the bomb
  			event = gameeventmanager->CreateEvent( "bomb_beep" );
  
  			if ( event )
  			{
  				event->SetInt( "entindex", entindex() );
  				gameeventmanager->FireEvent( event );
  			}
  
  			pPlayer->SetProgressBarTime( 0 );
  
  			CSGameRules()->m_bBombDropped = false;
  			CSGameRules()->m_bBombPlanted = true;
  
  
  			// Play the plant sound.
  			// don't play a bomb plant sound for everyone anymore ?
  			Vector plantPosition = pPlayer->GetAbsOrigin() + Vector( 0, 0, 5 );
  			CPASAttenuationFilter filter( plantPosition );
  			EmitSound( filter, 0, "c4.plantquiet", &GetAbsOrigin() );
  
  			// No more c4!
  			pPlayer->Weapon_Drop( this, NULL, NULL );
  			UTIL_Remove( this );
  
  			pPlayer->m_bDuckOverride = false;
  #endif
  			//don't allow the planting to start over again next frame.
  			m_bBombPlanted = true;
  
  #if !defined( CLIENT_DLL )
  			if ( CSGameRules()->IsPlayingCooperativeGametype() )
  				CSGameRules()->CheckWinConditions();// %plant bomb%
  #endif
  
  			return;
  		}
  		else
  		{
  			ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_C4_Activated_At_Bomb_Spot" );
  
  #if !defined( CLIENT_DLL )
  			//pPlayer->SetAnimation( PLAYER_HOLDBOMB );
  
  			IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortplant" );
  			if( event )
  			{
  				event->SetInt("userid", pPlayer->GetUserID() );
  				event->SetInt("site", pPlayer->m_iBombSiteIndex );
  				event->SetInt( "priority", 5 ); // bomb_abortplant
  				gameeventmanager->FireEvent( event );
  			}
  #endif
  
  			m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  			return;
  		}
  	}
  
  	m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
  	SetWeaponIdleTime( gpGlobals->curtime + SharedRandomFloat("C4IdleTime", 10, 15 ) );
  }
  
  void CC4::WeaponIdle()
  {
  	// if the player releases the attack button cancel the arming sequence
  	if ( m_bStartedArming )
  	{
  		AbortBombPlant();
  
  		CCSPlayer *pPlayer = GetPlayerOwner();
  
  		pPlayer->m_bDuckOverride = false;
  
  		// TODO: make this use SendWeaponAnim and activities when the C4 has the activities hooked up.
  		if ( pPlayer )
  		{
  			SendWeaponAnim( ACT_VM_IDLE );
  			pPlayer->SetNextAttack( gpGlobals->curtime );
  		}
  
  		if(m_bBombPlacedAnimation == true) //this means the placement animation is canceled
  			SendWeaponAnim( ACT_VM_DRAW );
  		else
  			SendWeaponAnim( ACT_VM_IDLE );
  	}
  }
  
  void CC4::UpdateShieldState( void )
  {
  	//ADRIANTODO
  	CCSPlayer *pPlayer = GetPlayerOwner();
  	if ( !pPlayer )
  		return;
  	
  	if ( pPlayer->HasShield() )
  	{
  		pPlayer->SetShieldDrawnState( false );
  
  		CBaseViewModel *pVM = pPlayer->GetViewModel( 1 );
  
  		if ( pVM )
  		{
  			pVM->AddEffects( EF_NODRAW );
  		}
  			//pPlayer->SetHitBoxSet( 3 );
  	}
  	else
  		BaseClass::UpdateShieldState();
  }
  
  
  int m_iBeepFrames[NUM_BEEPS] = { 20, 29, 37, 44, 50, 59, 65 };
  int iNumArmingAnimFrames = 83;
  
  void CC4::PlayArmingBeeps( void )
  {
  	float flStartTime = m_fArmedTime - WEAPON_C4_ARM_TIME;
  
  	float flProgress = ( gpGlobals->curtime - flStartTime ) / ( WEAPON_C4_ARM_TIME - 0.75 );
  
  	int currentFrame = (int)( (float)iNumArmingAnimFrames * flProgress );
  
  	int i;
  	for( i=0;i<NUM_BEEPS;i++ )
  	{
  		if( currentFrame <= m_iBeepFrames[i] )
  		{
  			break;
  		}
  		else if( !m_bPlayedArmingBeeps[i] )
  		{
  			m_bPlayedArmingBeeps[i] = true;
  
  			CCSPlayer *owner = GetPlayerOwner();
  			if ( !owner && !owner->IsAlive() )
  				break;
  
  			Vector soundPosition = owner->GetAbsOrigin() + Vector( 0, 0, 5 );
  			CPASAttenuationFilter filter( soundPosition );
  
  			filter.RemoveRecipient( owner );
  
  			// remove anyone that is first person spec'ing the planter
  			int i;
  			CBasePlayer *pPlayer;
  			for( i=1;i<=gpGlobals->maxClients;i++ )
  			{
  				pPlayer = UTIL_PlayerByIndex( i );
  
  				if ( !pPlayer )
  					continue;
  
  				if( pPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pPlayer->GetObserverTarget() == GetOwner() )
  				{
  					filter.RemoveRecipient( pPlayer );
  				}
  			}
  
  			EmitSound(filter, 0, "c4.click", &GetAbsOrigin() );
  			
  			break;
  		}
  	}
  }
  
  void CC4::PlayPlantInitSound( void )
  {
  	CCSPlayer *owner = GetPlayerOwner();
  	if ( !owner && !owner->IsAlive() )
  		return;
  
  	Vector soundPosition = owner->GetAbsOrigin() + Vector( 0, 0, 5 );
  	CPASAttenuationFilter filter( soundPosition );
  
  	filter.RemoveRecipient( owner );
  
  	// remove anyone that is first person spec'ing the planter
  	int i;
  	CBasePlayer *pPlayer;
  	for( i=1;i<=gpGlobals->maxClients;i++ )
  	{
  		pPlayer = UTIL_PlayerByIndex( i );
  
  		if ( !pPlayer )
  			continue;
  
  		if( pPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pPlayer->GetObserverTarget() == GetOwner() )
  		{
  			filter.RemoveRecipient( pPlayer );
  		}
  	}
  
  	EmitSound(filter, 0, "c4.initiate", &GetAbsOrigin() );
  }
  
  float CC4::GetMaxSpeed() const
  {
  	if ( m_bStartedArming )
  		return CS_PLAYER_SPEED_STOPPED;
  	else
  		return BaseClass::GetMaxSpeed();
  }
  
  
  void CC4::OnPickedUp( CBaseCombatCharacter *pNewOwner )
  {
  	BaseClass::OnPickedUp( pNewOwner );
  
  #if !defined( CLIENT_DLL )
  	CCSPlayer *pPlayer = dynamic_cast<CCSPlayer *>( pNewOwner );
  
  	IGameEvent * event = gameeventmanager->CreateEvent( "bomb_pickup" );
  	if ( event )
  	{
  		event->SetInt( "userid", pPlayer->GetUserID() );
  		event->SetInt( "priority", 5 ); // bomb_pickup
  		gameeventmanager->FireEvent( event );
  	}
  
  	CSGameRules()->m_bBombDropped = false;
  
  	if ( !CSGameRules()->IsPlayingTraining() )
  		ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_Got_Bomb" );
  
  	pPlayer->SetBombPickupTime( gpGlobals->curtime );
  #endif
  }
  
  // HACK - Ask Mike Booth...
  #ifndef CLIENT_DLL
  	#include "cs_bot.h"
  #endif
  
  // memdbgon must be the last include file in a .cpp file!!!
  #include "tier0/memdbgon.h"
  
  void CC4::Drop( const Vector &vecVelocity )
  {
  #if !defined( CLIENT_DLL )
  	if ( !CSGameRules()->m_bBombPlanted ) // its not dropped if its planted
  	{
  		// tell the bots about the dropped bomb
  		TheCSBots()->SetLooseBomb( this );
  
  		CSGameRules()->m_bBombDropped = true;
  
  		CBasePlayer *pPlayer = dynamic_cast<CBasePlayer *>(GetOwnerEntity());
  		Assert( pPlayer );
  		if ( pPlayer )
  		{
  			CCSPlayer *pCCSPlayer = dynamic_cast<CCSPlayer *>( pPlayer );
  
  			pCCSPlayer->SetBombDroppedTime( gpGlobals->curtime );
  			IGameEvent * event = gameeventmanager->CreateEvent( "bomb_dropped" );
  			if ( event )
  			{
  				event->SetInt( "userid", pPlayer->GetUserID() );
  				event->SetInt( "entindex", entindex() );
  				event->SetInt( "priority", 5 ); // bomb_dropped
  				gameeventmanager->FireEvent( event );
  
  				//event->SetInt( "entindex", iTest );
  			}
  		}
  	}
  #endif
  
  #if defined( CLIENT_DLL )
  	STEAMWORKS_TESTSECRET_AMORTIZE( 13 );
  #endif
  
  	if ( m_bStartedArming )
  		AbortBombPlant();  // stop arming sequence
  
  	BaseClass::Drop( vecVelocity );
  }
  
  void CC4::AbortBombPlant()
  {
  	m_bStartedArming = false; 
  
  	CCSPlayer *pPlayer = GetPlayerOwner();
  	if ( !pPlayer )
  		return;
  
  #if !defined( CLIENT_DLL )
  
  	m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  
  	pPlayer->SetProgressBarTime( 0 );
  
  	IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortplant" );
  	if( event )
  	{
  		event->SetInt("userid", pPlayer->GetUserID() );
  		event->SetInt("site", pPlayer->m_iBombSiteIndex );
  		event->SetInt( "priority", 5 ); // bomb_abortplant
  		gameeventmanager->FireEvent( event );
  	}
  
  	if( pPlayer->GetLastWeaponBeforeAutoSwitchToC4() != NULL )
  	{
  		CBaseViewModel *vm = pPlayer->GetViewModel();
  		if ( vm )
  		{
  			vm->AddEffects( EF_NODRAW );
  		}
  
  		pPlayer->Weapon_Switch( pPlayer->GetLastWeaponBeforeAutoSwitchToC4() );
  		pPlayer->SetLastWeaponBeforeAutoSwitchToC4( NULL );
  	}
  
  #else
  
  	// Clear the numbers from the screen if we just aborted.
  	m_szScreenText[0] = '\0';
  
  #endif
  
  	#ifndef CLIENT_DLL
  	if ( pPlayer && !pPlayer->IsDormant() )
  	{
  		pPlayer->DoAnimationEvent( PLAYERANIMEVENT_CLEAR_FIRING );
  	}
  	#endif
  	//FX_PlantBomb( pPlayer->entindex(), pPlayer->Weapon_ShootPosition(), PLANTBOMB_ABORT );
  }
  