Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2134 lines
59 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "weapon_c4.h"
  8. #include "in_buttons.h"
  9. #include "cs_gamerules.h"
  10. #include "decals.h"
  11. #include "SoundEmitterSystem/isoundemittersystembase.h"
  12. #include "keyvalues.h"
  13. #include "fx_cs_shared.h"
  14. #include "obstacle_pushaway.h"
  15. #include "particle_parse.h"
  16. #include "mathlib/vector.h"
  17. #if defined( CLIENT_DLL )
  18. #include "c_cs_player.h"
  19. #include "HUD/sfweaponselection.h"
  20. #else
  21. #include "cs_player.h"
  22. #include "explode.h"
  23. #include "mapinfo.h"
  24. #include "team.h"
  25. #include "func_bomb_target.h"
  26. #include "vguiscreen.h"
  27. #include "bot.h"
  28. #include "cs_player.h"
  29. #include "cs_gamestats.h"
  30. #include "cs_achievement_constants.h"
  31. #include "cvisibilitymonitor.h"
  32. #include "cs_entity_spotting.h"
  33. #endif
  34. // memdbgon must be the last include file in a .cpp file!!!
  35. #include "tier0/memdbgon.h"
  36. #define BLINK_INTERVAL 2.0
  37. #define PLANTED_C4_MODEL "models/weapons/w_ied_dropped.mdl"
  38. #define HEIST_MODE_C4_TIME 25
  39. #define WEAPON_C4_ARM_TIME 3.0
  40. #ifndef CLIENT_DLL
  41. #define WEAPON_C4_UPDATE_LAST_VALID_PLAYER_HELD_POSITION_INTERVAL 0.2
  42. // amount of time a player can stop defusing and continue
  43. const float C4_DEFUSE_GRACE_PERIOD = 0.5f;
  44. // amount of time a player is forced to continue defusing after not USEing. this effects other player's ability to interrupt
  45. const float C4_DEFUSE_LOCKIN_PERIOD = 0.05f;
  46. extern ConVar mp_anyone_can_pickup_c4;
  47. extern ConVar mp_c4_cannot_be_defused;
  48. CON_COMMAND_F( clear_bombs, "", FCVAR_CHEAT )
  49. {
  50. FOR_EACH_VEC( g_PlantedC4s, iBomb )
  51. {
  52. CPlantedC4 * pC4 = g_PlantedC4s[iBomb];
  53. if ( pC4 )
  54. {
  55. UTIL_Remove( pC4 );
  56. }
  57. }
  58. g_PlantedC4s.RemoveAll();
  59. }
  60. LINK_ENTITY_TO_CLASS( planted_c4, CPlantedC4 );
  61. PRECACHE_REGISTER( planted_c4 );
  62. BEGIN_DATADESC( CPlantedC4 )
  63. DEFINE_FUNCTION( C4Think ),
  64. //Outputs
  65. DEFINE_OUTPUT( m_OnBombBeginDefuse, "OnBombBeginDefuse" ),
  66. DEFINE_OUTPUT( m_OnBombDefused, "OnBombDefused" ),
  67. DEFINE_OUTPUT( m_OnBombDefuseAborted, "OnBombDefuseAborted" ),
  68. END_DATADESC()
  69. IMPLEMENT_SERVERCLASS_ST( CPlantedC4, DT_PlantedC4 )
  70. SendPropBool( SENDINFO(m_bBombTicking) ),
  71. SendPropFloat( SENDINFO(m_flC4Blow), 0, SPROP_NOSCALE ),
  72. SendPropFloat( SENDINFO(m_flTimerLength), 0, SPROP_NOSCALE ),
  73. SendPropFloat( SENDINFO(m_flDefuseLength), 0, SPROP_NOSCALE ),
  74. SendPropFloat( SENDINFO(m_flDefuseCountDown), 0, SPROP_NOSCALE ),
  75. SendPropBool( SENDINFO(m_bBombDefused) ),
  76. SendPropEHandle( SENDINFO(m_hBombDefuser) ),
  77. END_SEND_TABLE()
  78. BEGIN_PREDICTION_DATA( CPlantedC4 )
  79. END_PREDICTION_DATA()
  80. CUtlVector< CPlantedC4* > g_PlantedC4s;
  81. CPlantedC4::CPlantedC4()
  82. {
  83. g_PlantedC4s.AddToTail( this );
  84. // [tj] No planter initially
  85. m_pPlanter = NULL;
  86. m_pBombDefuser = NULL; //No Defuser Initially
  87. // [tj] Assume this is the original owner
  88. m_bPlantedAfterPickup = false;
  89. m_bTrainingPlacedByPlayer = false;
  90. m_bVoiceAlertFired = false;
  91. SetSpotRules( CCSEntitySpotting::SPOT_RULE_CT | CCSEntitySpotting::SPOT_RULE_ALWAYS_SEEN_BY_T );
  92. }
  93. CPlantedC4::~CPlantedC4()
  94. {
  95. g_PlantedC4s.FindAndRemove( this );
  96. RemoveControlPanels();
  97. }
  98. void CPlantedC4::Spawn()
  99. {
  100. BaseClass::Spawn();
  101. SetMoveType( MOVETYPE_NONE );
  102. SetSolid( SOLID_NONE );
  103. AddFlag( FL_OBJECT );
  104. SetModel( PLANTED_C4_MODEL );
  105. SetSequence(1); // this sequence keeps the toggle switch in the 'up' position
  106. SetCollisionBounds( Vector( 0, 0, 0 ), Vector( 8, 8, 8 ) );
  107. // Detonate in "time" seconds
  108. SetThink( &CPlantedC4::C4Think );
  109. SetNextThink( gpGlobals->curtime + 0.1f );
  110. if ( CSGameRules() && CSGameRules()->IsPlayingCoopMission() && mp_anyone_can_pickup_c4.GetBool() )
  111. m_flTimerLength = 9999;
  112. else
  113. m_flTimerLength = mp_c4timer.GetInt();
  114. m_flC4Blow = gpGlobals->curtime + m_flTimerLength;
  115. m_fLastDefuseTime = 0.0f;
  116. m_bBeingDefused = false;
  117. m_bHasExploded = false;
  118. m_bBombDefused = false;
  119. SetFriction( 0.9 );
  120. m_flDefuseLength = 0.0f;
  121. SpawnControlPanels();
  122. VisibilityMonitor_AddEntity( this, 600.0f, NULL, NULL );
  123. }
  124. int CPlantedC4::UpdateTransmitState()
  125. {
  126. return SetTransmitState( FL_EDICT_FULLCHECK );
  127. }
  128. int CPlantedC4::ShouldTransmit( const CCheckTransmitInfo *pInfo )
  129. {
  130. // Terrorists always need this object for the radar
  131. // Everybody needs it for hiding the round timer and showing the planted C4 scenario icon
  132. return FL_EDICT_ALWAYS;
  133. }
  134. void CPlantedC4::Precache()
  135. {
  136. PrecacheModel( "models/weapons/w_c4_planted.mdl" ); // old, unused
  137. PrecacheModel( PLANTED_C4_MODEL );
  138. PrecacheModel( "models/props/de_overpass/balloon.mdl" );
  139. PrecacheParticleSystem( "weapon_confetti_balloons" );
  140. PrecacheModel( "models/weapons/w_eq_multimeter.mdl" );
  141. PrecacheVGuiScreen( "c4_panel" );
  142. }
  143. void CPlantedC4::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
  144. {
  145. pPanelName = "c4_panel";
  146. }
  147. void CPlantedC4::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName )
  148. {
  149. pPanelName = "vgui_screen";
  150. }
  151. //-----------------------------------------------------------------------------
  152. // This is called by the base object when it's time to spawn the control panels
  153. //-----------------------------------------------------------------------------
  154. void CPlantedC4::SpawnControlPanels()
  155. {
  156. char buf[64];
  157. // FIXME: Deal with dynamically resizing control panels?
  158. // If we're attached to an entity, spawn control panels on it instead of use
  159. CBaseAnimating *pEntityToSpawnOn = this;
  160. char *pOrgLL = "controlpanel%d_ll";
  161. char *pOrgUR = "controlpanel%d_ur";
  162. char *pAttachmentNameLL = pOrgLL;
  163. char *pAttachmentNameUR = pOrgUR;
  164. Assert( pEntityToSpawnOn );
  165. // Lookup the attachment point...
  166. int nPanel;
  167. for ( nPanel = 0; true; ++nPanel )
  168. {
  169. Q_snprintf( buf, sizeof( buf ), pAttachmentNameLL, nPanel );
  170. int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  171. if (nLLAttachmentIndex <= 0)
  172. {
  173. // Try and use my panels then
  174. pEntityToSpawnOn = this;
  175. Q_snprintf( buf, sizeof( buf ), pOrgLL, nPanel );
  176. nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  177. if (nLLAttachmentIndex <= 0)
  178. return;
  179. }
  180. Q_snprintf( buf, sizeof( buf ), pAttachmentNameUR, nPanel );
  181. int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  182. if (nURAttachmentIndex <= 0)
  183. {
  184. // Try and use my panels then
  185. Q_snprintf( buf, sizeof( buf ), pOrgUR, nPanel );
  186. nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  187. if (nURAttachmentIndex <= 0)
  188. return;
  189. }
  190. const char *pScreenName;
  191. GetControlPanelInfo( nPanel, pScreenName );
  192. if (!pScreenName)
  193. continue;
  194. const char *pScreenClassname;
  195. GetControlPanelClassName( nPanel, pScreenClassname );
  196. if ( !pScreenClassname )
  197. continue;
  198. // Compute the screen size from the attachment points...
  199. matrix3x4_t panelToWorld;
  200. pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld );
  201. matrix3x4_t worldToPanel;
  202. MatrixInvert( panelToWorld, worldToPanel );
  203. // Now get the lower right position + transform into panel space
  204. Vector lr, lrlocal;
  205. pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld );
  206. MatrixGetColumn( panelToWorld, 3, lr );
  207. VectorTransform( lr, worldToPanel, lrlocal );
  208. float flWidth = fabs( lrlocal.x );
  209. float flHeight = fabs( lrlocal.y );
  210. CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex );
  211. pScreen->ChangeTeam( GetTeamNumber() );
  212. pScreen->SetActualSize( flWidth, flHeight );
  213. pScreen->SetActive( true );
  214. pScreen->MakeVisibleOnlyToTeammates( false );
  215. int nScreen = m_hScreens.AddToTail( );
  216. m_hScreens[nScreen].Set( pScreen );
  217. }
  218. }
  219. void CPlantedC4::RemoveControlPanels()
  220. {
  221. // Clear off any screens that are still live.
  222. for ( int ii = m_hScreens.Count(); --ii >= 0; )
  223. {
  224. DestroyVGuiScreen( m_hScreens[ii].Get() );
  225. }
  226. m_hScreens.RemoveAll();
  227. }
  228. void CPlantedC4::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
  229. {
  230. // Are we already marked for transmission?
  231. if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
  232. return;
  233. BaseClass::SetTransmit( pInfo, bAlways );
  234. // Force our screens to be sent too.
  235. for ( int i=0; i < m_hScreens.Count(); i++ )
  236. {
  237. CVGuiScreen *pScreen = m_hScreens[i].Get();
  238. pScreen->SetTransmit( pInfo, bAlways );
  239. }
  240. }
  241. CPlantedC4* CPlantedC4::ShootSatchelCharge( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles )
  242. {
  243. CPlantedC4 *pGrenade;
  244. bool bTrainingPlacedByPlayer = false;
  245. if ( CSGameRules()->IsPlayingTraining() )
  246. {
  247. pGrenade = dynamic_cast< CPlantedC4Training* >( CreateEntityByName( "planted_c4_training" ) );
  248. bTrainingPlacedByPlayer = true;
  249. }
  250. else
  251. {
  252. pGrenade = dynamic_cast< CPlantedC4* >( CreateEntityByName( "planted_c4" ) );
  253. }
  254. if ( pGrenade )
  255. {
  256. vecAngles[0] = 0;
  257. vecAngles[2] = 0;
  258. pGrenade->Init( pevOwner, vecStart, vecAngles, bTrainingPlacedByPlayer );
  259. return pGrenade;
  260. }
  261. else
  262. {
  263. Warning( "Can't create planted_c4 entity!\n" );
  264. return NULL;
  265. }
  266. }
  267. void CPlantedC4::Init( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles, bool bTrainingPlacedByPlayer )
  268. {
  269. SetAbsOrigin( vecStart );
  270. SetAbsAngles( vecAngles );
  271. SetOwnerEntity( pevOwner );
  272. SetPlanter( pevOwner );
  273. m_bTrainingPlacedByPlayer = bTrainingPlacedByPlayer;
  274. if ( !m_bTrainingPlacedByPlayer )
  275. m_bBombTicking = true;
  276. Spawn();
  277. }
  278. void CPlantedC4::C4Think()
  279. {
  280. if (!IsInWorld())
  281. {
  282. UTIL_Remove( this );
  283. return;
  284. }
  285. // network the defuser handle
  286. if ( m_bBeingDefused )
  287. {
  288. m_hBombDefuser = m_pBombDefuser;
  289. SetBodygroupPreset( "show_clips" );
  290. }
  291. else
  292. {
  293. m_hBombDefuser = INVALID_EHANDLE;
  294. SetBodygroupPreset( "hide_clips" );
  295. }
  296. if ( m_bHasExploded )
  297. {
  298. if ( CSGameRules()->IsPlayingTraining() )
  299. {
  300. SetThink( &CBaseEntity::SUB_Remove );
  301. SetNextThink( gpGlobals->curtime + 1.0f );
  302. if ( m_pBombDefuser )
  303. {
  304. m_pBombDefuser->m_bIsDefusing = false;
  305. m_pBombDefuser->SetProgressBarTime( 0 );
  306. m_pBombDefuser->OnCanceledDefuse();
  307. m_pBombDefuser = NULL;
  308. }
  309. m_bBeingDefused = false;
  310. m_flDefuseCountDown = 0;
  311. m_flDefuseLength = 0;
  312. }
  313. return;
  314. }
  315. //Bomb is not ticking, don't think anymore
  316. if( !IsBombActive() )
  317. {
  318. SetThink( NULL );
  319. return;
  320. }
  321. // [hpe:jason] Decrease the latency between c4 think updates
  322. SetNextThink( gpGlobals->curtime + 0.05f );
  323. // let the bots hear the bomb beeping
  324. // BOTPORT: Emit beep events at same time as client effects
  325. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_beep" );
  326. if( event )
  327. {
  328. event->SetInt( "entindex", entindex() );
  329. gameeventmanager->FireEvent( event );
  330. }
  331. // 4 seconds before the bomb blows up, have anyone close by on each team say something about it going to blow
  332. if (m_flC4Blow - 3.0 <= gpGlobals->curtime && !m_bVoiceAlertFired)
  333. {
  334. CCSPlayer *pCT = NULL;
  335. CCSPlayer *pT = NULL;
  336. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  337. {
  338. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  339. if ( pPlayer && pPlayer->IsAlive() )
  340. {
  341. if ( !pCT && pPlayer->GetTeamNumber() == TEAM_CT && pCT != m_pBombDefuser )
  342. {
  343. if ((GetAbsOrigin() - pPlayer->GetAbsOrigin()).AsVector2D().IsLengthLessThan( 1200.0 ))
  344. pCT = pPlayer;
  345. }
  346. else if ( !pT && pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  347. {
  348. if ((GetAbsOrigin() - pPlayer->GetAbsOrigin()).AsVector2D().IsLengthLessThan( 1200.0 ))
  349. pT = pPlayer;
  350. }
  351. }
  352. // we have a player from both teams, don't need to find anymore
  353. if ( pCT && pT )
  354. break;
  355. }
  356. if ( pCT && (!m_bBeingDefused || (m_bBeingDefused && (m_flDefuseCountDown > m_flC4Blow) ) ) )
  357. {
  358. pCT->Radio( "Radio.GetOutOfThere", "", true );
  359. m_bVoiceAlertFired = true;
  360. }
  361. if ( pT )
  362. {
  363. pT->Radio( "Radio.GetOutOfThere", "", true );
  364. m_bVoiceAlertFired = true;
  365. }
  366. }
  367. // IF the timer has expired ! blow this bomb up!
  368. if (m_flC4Blow <= gpGlobals->curtime)
  369. {
  370. // kick off the person trying to defuse the bomb
  371. if ( m_pBombDefuser )
  372. {
  373. m_pBombDefuser->m_bIsDefusing = false;
  374. m_pBombDefuser->SetProgressBarTime( 0 );
  375. m_pBombDefuser->OnCanceledDefuse();
  376. m_pBombDefuser = NULL;
  377. m_bBeingDefused = false;
  378. }
  379. // for the music, we added a tension filled second after the bomb has ceased to be defusable and explode 1 second after
  380. if (m_flC4Blow + 1.0f <= gpGlobals->curtime)
  381. {
  382. // give the bomber credit for planting the bomb
  383. CCSPlayer* pBombOwner = ToCSPlayer(GetOwnerEntity());
  384. // NOTE[pmf]: removed by design decision
  385. // if ( pBombOwner )
  386. // {
  387. // if (CSGameRules()->m_iRoundWinStatus == WINNER_NONE)
  388. // pBombOwner->IncrementFragCount( 3 );
  389. // }
  390. CSGameRules()->m_bBombDropped = false;
  391. trace_t tr;
  392. Vector vecSpot = GetAbsOrigin();
  393. vecSpot[2] += 8;
  394. UTIL_TraceLine( vecSpot, vecSpot + Vector ( 0, 0, -40 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  395. Explode( &tr, DMG_BLAST );
  396. CSGameRules()->m_bBombPlanted = false;
  397. CCS_GameStats.Event_BombExploded(pBombOwner);
  398. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_exploded" );
  399. if( event )
  400. {
  401. event->SetInt( "userid", pBombOwner?pBombOwner->GetUserID():-1 );
  402. event->SetInt( "site", m_iBombSiteIndex );
  403. event->SetInt( "priority", 20 ); // bomb_exploded
  404. gameeventmanager->FireEvent( event );
  405. }
  406. RemoveControlPanels();
  407. // skip additional processing once the bomb has exploded
  408. return;
  409. }
  410. }
  411. // make sure our defuser exists
  412. if ( m_bBeingDefused && (m_pBombDefuser == NULL) )
  413. {
  414. m_bBeingDefused = false;
  415. }
  416. //if the defusing process has started
  417. if ( m_bBeingDefused && (m_pBombDefuser != NULL) && mp_c4_cannot_be_defused.GetBool() == false )
  418. {
  419. //if the defusing process has not ended yet
  420. if ( gpGlobals->curtime < m_flDefuseCountDown )
  421. {
  422. int iOnGround = FBitSet( m_pBombDefuser->GetFlags(), FL_ONGROUND );
  423. const CUserCmd *pCmd = m_pBombDefuser->GetLastUserCommand();
  424. bool bPlayerStoppedHoldingUse = !(pCmd->buttons & IN_USE) && (gpGlobals->curtime > m_fLastDefuseTime + C4_DEFUSE_LOCKIN_PERIOD);
  425. CConfigurationForHighPriorityUseEntity_t cfgUseEntity;
  426. bool bPlayerUseIsValidNow = m_pBombDefuser->GetUseConfigurationForHighPriorityUseEntity( this, cfgUseEntity ) &&
  427. ( cfgUseEntity.m_pEntity == this ) && cfgUseEntity.UseByPlayerNow( m_pBombDefuser, cfgUseEntity.k_EPlayerUseType_Progress );
  428. //if the bomb defuser has stopped defusing the bomb
  429. if ( bPlayerStoppedHoldingUse || !bPlayerUseIsValidNow || !iOnGround )
  430. {
  431. if ( !iOnGround && m_pBombDefuser->IsAlive() )
  432. ClientPrint( m_pBombDefuser, HUD_PRINTCENTER, "#SFUI_Notice_C4_Defuse_Must_Be_On_Ground");
  433. // tell the bots someone has aborted defusing
  434. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortdefuse" );
  435. if( event )
  436. {
  437. event->SetInt("userid", m_pBombDefuser->GetUserID() );
  438. event->SetInt( "priority", 5 ); // bomb_abortdefuse
  439. gameeventmanager->FireEvent( event );
  440. }
  441. //cancel the progress bar
  442. m_pBombDefuser->SetProgressBarTime( 0 );
  443. m_pBombDefuser->OnCanceledDefuse();
  444. // release the player from being frozen
  445. m_pBombDefuser->m_bIsDefusing = false;
  446. m_bBeingDefused = false;
  447. }
  448. return;
  449. }
  450. // training has to pick elements
  451. else if ( m_pBombDefuser->IsAlive() && CSGameRules()->IsPlayingTraining() )
  452. {
  453. Vector soundPosition = m_pBombDefuser->GetAbsOrigin() + Vector( 0, 0, 5 );
  454. CPASAttenuationFilter filter( soundPosition );
  455. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_defused" );
  456. if( event )
  457. {
  458. event->SetInt("userid", m_pBombDefuser->GetUserID() );
  459. event->SetInt("site", m_iBombSiteIndex );
  460. event->SetInt( "priority", 5 ); // bomb_defused
  461. gameeventmanager->FireEvent( event );
  462. }
  463. EmitSound( filter, 0, "c4.disarmfinish", &GetAbsOrigin() );
  464. // The bomb has just been disarmed.. Check to see if the round should end now
  465. m_bBombTicking = false;
  466. // release the player from being frozen
  467. m_pBombDefuser->m_bIsDefusing = false;
  468. // Clear their progress bar.
  469. m_pBombDefuser->SetProgressBarTime( 0 );
  470. m_pBombDefuser = NULL;
  471. m_bBeingDefused = false;
  472. m_flDefuseLength = 10;
  473. m_OnBombDefused.FireOutput(this, m_pBombDefuser);
  474. return;
  475. }
  476. //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!)
  477. else if ( m_pBombDefuser->IsAlive() && ( CSGameRules()->m_iRoundWinStatus != WINNER_TER ) )
  478. {
  479. bool roundWasAlreadyWon = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE ); // This condition checks whether CTs already won the round
  480. // Check if there are Terrorists alive
  481. bool bTerroristsAlive = false;
  482. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  483. {
  484. CCSPlayer* pCheckPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  485. if ( !pCheckPlayer )
  486. continue;
  487. if ( pCheckPlayer->GetTeamNumber() != TEAM_TERRORIST )
  488. continue;
  489. if ( pCheckPlayer->IsAlive() )
  490. {
  491. bTerroristsAlive = true;
  492. break;
  493. }
  494. }
  495. // set down-to-the-wire defuse fun fact
  496. m_pBombDefuser->SetDefusedBombWithThisTimeRemaining( m_flC4Blow - gpGlobals->curtime );
  497. CCS_GameStats.Event_BombDefused(m_pBombDefuser);
  498. CSGameRules()->ScoreBombDefuse( m_pBombDefuser, ( bTerroristsAlive && !roundWasAlreadyWon ) );
  499. m_pBombDefuser->AddAccountAward( PlayerCashAward::BOMB_DEFUSED );
  500. // record in matchstats
  501. if ( CSGameRules()->ShouldRecordMatchStats() )
  502. {
  503. int iCurrentRound = CSGameRules()->GetTotalRoundsPlayed();
  504. ++ m_pBombDefuser->m_iMatchStats_Objective.GetForModify( iCurrentRound );
  505. // Keep track of Match stats in QMM data
  506. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_pBombDefuser->GetHumanPlayerAccountID() ) )
  507. {
  508. pQMM->m_iMatchStats_Objective[ iCurrentRound ] = m_pBombDefuser->m_iMatchStats_Objective.Get( iCurrentRound );
  509. }
  510. }
  511. if ( !roundWasAlreadyWon )
  512. {
  513. // All alive CTs also get assistance credit for bomb defuse.
  514. // This way if all Terrorists are eliminated then all alive CTs get 1pt and defuser gets 2pt;
  515. // if a Terrorist remains alive then the defuser gets 5 pts and all other alive
  516. // teammates get 1 pt for assist / suppressing fire.
  517. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  518. {
  519. CCSPlayer* pCheckPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  520. if ( !pCheckPlayer )
  521. continue;
  522. if ( pCheckPlayer->GetTeamNumber() != TEAM_CT )
  523. continue;
  524. if ( pCheckPlayer->IsAlive() )
  525. CSGameRules()->ScoreBombDefuse( pCheckPlayer, false );
  526. }
  527. }
  528. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_defused" );
  529. if( event )
  530. {
  531. event->SetInt("userid", m_pBombDefuser->GetUserID() );
  532. event->SetInt("site", m_iBombSiteIndex );
  533. event->SetInt( "priority", 5 ); // bomb_defused
  534. gameeventmanager->FireEvent( event );
  535. m_pBombDefuser->AwardAchievement( CSWinBombDefuse );
  536. float timeToDetonation = ( m_flC4Blow - gpGlobals->curtime );
  537. if ( (timeToDetonation > 0.0f) && (timeToDetonation <= AchievementConsts::BombDefuseCloseCall_MaxTimeRemaining) )
  538. {
  539. // Give achievement for defusing with < 1 second before detonation
  540. m_pBombDefuser->AwardAchievement( CSBombDefuseCloseCall );
  541. }
  542. // [dwenger] Added for fun-fact support
  543. if ( m_pBombDefuser->PickedUpDefuser() )
  544. {
  545. // Defuser kit was picked up, so set the fun fact
  546. m_pBombDefuser->SetDefusedWithPickedUpKit( true );
  547. }
  548. }
  549. Vector soundPosition = m_pBombDefuser->GetAbsOrigin() + Vector( 0, 0, 5 );
  550. CPASAttenuationFilter filter( soundPosition );
  551. EmitSound( filter, 0, "c4.disarmfinish", &GetAbsOrigin() );
  552. // The bomb has just been disarmed.. Check to see if the round should end now
  553. m_bBombTicking = false;
  554. // release the player from being frozen
  555. m_pBombDefuser->m_bIsDefusing = false;
  556. CSGameRules()->m_bBombDefused = true;
  557. if ( CSGameRules() && CSGameRules()->IsCSGOBirthday() )
  558. {
  559. DispatchParticleEffect( "weapon_confetti_balloons", GetAbsOrigin(), QAngle( 0, 0, 0 ) );
  560. CPASAttenuationFilter filter( this );
  561. filter.UsePredictionRules();
  562. EmitSound( filter, entindex(), "Weapon_PartyHorn.Single" );
  563. //EmitSound( filter, entindex(), "Birthday_PartyHorn.VO" );
  564. //C_BaseEntity::EmitSound(filter, SOUND_FROM_LOCAL_PLAYER, "Birthday_PartyHorn.VO");
  565. }
  566. // Setup MVP granting class in case round wasn't already won
  567. class CPlantedC4DefusedMVP : public CCSGameRules::ICalculateEndOfRoundMVPHook_t
  568. {
  569. public:
  570. virtual CCSPlayer* CalculateEndOfRoundMVP() OVERRIDE
  571. {
  572. if( m_pBombDefuser->HasControlledBotThisRound() )
  573. {
  574. // [dkorus] if we controlled a bot this round, use standard MVP conditions
  575. return CSGameRules()->CalculateEndOfRoundMVP();
  576. }
  577. bool bTerroristsAlive = false;
  578. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  579. {
  580. CCSPlayer* pCheckPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  581. if ( !pCheckPlayer )
  582. continue;
  583. if ( pCheckPlayer->GetTeamNumber() != TEAM_TERRORIST )
  584. continue;
  585. if ( pCheckPlayer->IsAlive() )
  586. {
  587. bTerroristsAlive = true;
  588. break;
  589. }
  590. }
  591. if ( bTerroristsAlive || ( m_pBombDefuser->GetNumRoundKills() && !m_pBombDefuser->m_iNumRoundTKs ) )
  592. {
  593. m_pBombDefuser->IncrementNumMVPs( CSMVP_BOMBDEFUSE );
  594. return m_pBombDefuser;
  595. }
  596. if ( CCSPlayer *pDefaultMvp = CSGameRules()->CalculateEndOfRoundMVP() )
  597. return pDefaultMvp;
  598. m_pBombDefuser->IncrementNumMVPs( CSMVP_BOMBDEFUSE );
  599. return m_pBombDefuser;
  600. }
  601. CHandle<CCSPlayer> m_pBombDefuser;
  602. } mvpHook;
  603. mvpHook.m_pBombDefuser = m_pBombDefuser;
  604. if ( !roundWasAlreadyWon )
  605. CSGameRules()->m_pfnCalculateEndOfRoundMVPHook = &mvpHook;
  606. // [menglish] Give the bomb defuser an mvp if they ended the round
  607. CSGameRules()->CheckWinConditions();
  608. // Reset the MVP hook
  609. if ( !roundWasAlreadyWon )
  610. CSGameRules()->m_pfnCalculateEndOfRoundMVPHook = NULL;
  611. // NOTE[pmf]: removed by design decision
  612. // // give the defuser credit for defusing the bomb
  613. // m_pBombDefuser->IncrementFragCount( 3 );
  614. CSGameRules()->m_bBombDropped = false;
  615. CSGameRules()->m_bBombPlanted = false;
  616. // Clear their progress bar.
  617. m_pBombDefuser->SetProgressBarTime( 0 );
  618. m_pBombDefuser = NULL;
  619. m_bBeingDefused = false;
  620. m_bBombDefused = true;
  621. m_flDefuseLength = 10;
  622. m_OnBombDefused.FireOutput(this, m_pBombDefuser);
  623. return;
  624. }
  625. //if it gets here then the previouse defuser has taken off or been killed
  626. m_OnBombDefuseAborted.FireOutput(this, m_pBombDefuser);
  627. // tell the bots someone has aborted defusing
  628. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortdefuse" );
  629. if ( event )
  630. {
  631. event->SetInt("userid", m_pBombDefuser->GetUserID() );
  632. event->SetInt( "priority", 5 ); // bomb_abortdefuse
  633. gameeventmanager->FireEvent( event );
  634. }
  635. // release the player from being frozen
  636. m_pBombDefuser->m_bIsDefusing = false;
  637. m_bBeingDefused = false;
  638. m_pBombDefuser = NULL;
  639. }
  640. }
  641. // Regular explosions
  642. void CPlantedC4::Explode( trace_t *pTrace, int bitsDamageType )
  643. {
  644. // Check to see if the round is over after the bomb went off...
  645. CSGameRules()->m_bTargetBombed = true;
  646. m_bBombTicking = false;
  647. m_bHasExploded = true;
  648. m_bBombDefused = false;
  649. bool roundWasAlreadyWon = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE );
  650. // MVP hook to award the MVP to person who planted the bomb
  651. class CPlantedC4ExplodedMVP : public CCSGameRules::ICalculateEndOfRoundMVPHook_t
  652. {
  653. public:
  654. virtual CCSPlayer* CalculateEndOfRoundMVP() OVERRIDE
  655. {
  656. // All alive Terrorists also get credit for bomb exploding,
  657. // this intentionally may include the original planter.
  658. // This way if bomb planter survives until explosion he will
  659. // get 2 pts and all other alive teammates will get 1 pt
  660. // If bomb planter doesn't survive then he gets 1 pt and all
  661. // teammates who remained alive defending the bomb get 1 pt
  662. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  663. {
  664. CCSPlayer* pCheckPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  665. if ( !pCheckPlayer )
  666. continue;
  667. if ( pCheckPlayer->GetTeamNumber() != TEAM_TERRORIST )
  668. continue;
  669. if ( pCheckPlayer->IsAlive() )
  670. CSGameRules()->ScoreBombExploded( pCheckPlayer );
  671. }
  672. if ( !pBombOwner )
  673. return CSGameRules()->CalculateEndOfRoundMVP();
  674. // Person who planted the bomb gets credit for the explosion
  675. CSGameRules()->ScoreBombExploded( pBombOwner );
  676. if( pBombOwner->HasControlledBotThisRound() )
  677. {
  678. // [dkorus] if we controlled a bot this round, use standard MVP conditions
  679. return CSGameRules()->CalculateEndOfRoundMVP();
  680. }
  681. else
  682. {
  683. pBombOwner->IncrementNumMVPs( CSMVP_BOMBPLANT );
  684. return pBombOwner;
  685. }
  686. }
  687. CCSPlayer *pBombOwner;
  688. } mvpHook;
  689. mvpHook.pBombOwner = ToCSPlayer( GetOwnerEntity() );
  690. if ( !roundWasAlreadyWon )
  691. CSGameRules()->m_pfnCalculateEndOfRoundMVPHook = &mvpHook;
  692. bool bWin = CSGameRules()->CheckWinConditions();
  693. if ( bWin && mvpHook.pBombOwner )
  694. {
  695. mvpHook.pBombOwner->AwardAchievement( CSWinBombPlant );
  696. //[tj]more specific achievement for planting the bomb after recovering it.
  697. if ( m_bPlantedAfterPickup )
  698. {
  699. mvpHook.pBombOwner->AwardAchievement( CSWinBombPlantAfterRecovery );
  700. }
  701. }
  702. if ( !roundWasAlreadyWon )
  703. CSGameRules()->m_pfnCalculateEndOfRoundMVPHook = NULL;
  704. // Do the Damage
  705. float flBombRadius;
  706. if ( CSGameRules()->IsPlayingGunGameTRBomb() )
  707. {
  708. flBombRadius = 300;
  709. }
  710. else
  711. {
  712. flBombRadius = 500;
  713. }
  714. if ( g_pMapInfo )
  715. flBombRadius = g_pMapInfo->m_flBombRadius;
  716. // Output to the bomb target ent
  717. CBaseEntity *pTarget = NULL;
  718. variant_t emptyVariant;
  719. while ((pTarget = gEntList.FindEntityByClassname( pTarget, "func_bomb_target" )) != NULL)
  720. {
  721. //Adrian - But only to the one we want!
  722. if ( pTarget->entindex() != m_iBombSiteIndex )
  723. continue;
  724. pTarget->AcceptInput( "BombExplode", this, this, emptyVariant, 0 );
  725. break;
  726. }
  727. // Pull out of the wall a bit
  728. if ( pTrace->fraction != 1.0 )
  729. {
  730. SetAbsOrigin( pTrace->endpos + (pTrace->plane.normal * 0.6) );
  731. }
  732. {
  733. Vector pos = GetAbsOrigin() + Vector( 0,0,8 );
  734. // Make sure that the bomb exploding is a reliable effect that all clients on this server and GOTV receive
  735. CReliableBroadcastRecipientFilter filterBombExplodeReliable;
  736. // Try using the new particle system instead of temp ents
  737. QAngle vecAngles;
  738. DispatchParticleEffect( "explosion_c4_500", pos, vecAngles, ( CBaseEntity * ) NULL, int( -1 ), &filterBombExplodeReliable );
  739. }
  740. // Sound! for everyone
  741. CBroadcastRecipientFilter filter;
  742. EmitSound( filter, 0, "c4.explode", &GetAbsOrigin() );
  743. // Decal!
  744. UTIL_DecalTrace( pTrace, "Scorch" );
  745. // Shake!
  746. UTIL_ScreenShake( pTrace->endpos, 25.0, 150.0, 1.0, 3000, SHAKE_START );
  747. SetOwnerEntity( NULL ); // can't traceline attack owner if this is set
  748. CSGameRules()->RadiusDamage(
  749. CTakeDamageInfo( this, GetOwnerEntity(), flBombRadius, bitsDamageType ),
  750. GetAbsOrigin(),
  751. flBombRadius * 3.5, //Matt - don't ask me, this is how CS does it.
  752. CLASS_NONE,
  753. true ); // IGNORE THE WORLD!!
  754. // send director message, that something important happed here
  755. /*
  756. MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR );
  757. WRITE_BYTE ( 9 ); // command length in bytes
  758. WRITE_BYTE ( DRC_CMD_EVENT ); // bomb explode
  759. WRITE_SHORT( ENTINDEX(this->edict()) ); // index number of primary entity
  760. WRITE_SHORT( 0 ); // index number of secondary entity
  761. WRITE_LONG( 15 | DRC_FLAG_FINAL ); // eventflags (priority and flags)
  762. MESSAGE_END();
  763. */
  764. }
  765. // For CTs to defuse the c4
  766. void CPlantedC4::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  767. {
  768. //Can't defuse if its already defused or if it has blown up (or training)
  769. if( !IsBombActive() || m_flC4Blow < gpGlobals->curtime || mp_c4_cannot_be_defused.GetBool() == true )
  770. {
  771. SetUse( NULL );
  772. return;
  773. }
  774. CCSPlayer *player = dynamic_cast< CCSPlayer* >( pActivator );
  775. // Can't defuse a bomb if we're not CT or we're in a no defuse area.
  776. if ( !player || player->GetTeamNumber() != TEAM_CT || player->m_bInNoDefuseArea)
  777. return;
  778. // make sure human players can see the bomb (or the bomb's nearby line-of-sight points)
  779. // to ensure we aren't defusing from the room below or something
  780. if ( !player->IsBot() )
  781. {
  782. trace_t result;
  783. bool bFoundBomb = false;
  784. Vector vecLineOfSightPoints[] = {
  785. Vector( 0, 0, 1 ),
  786. Vector( 5, 5, 5 ),
  787. Vector( -5, 5, 5 ),
  788. Vector( 5, -5, 5 ),
  789. Vector( -5, -5, 5 ),
  790. Vector( 0, 0, 5 )
  791. };
  792. int nNumLOSpts = ARRAYSIZE( vecLineOfSightPoints );
  793. for ( int i=0; i<nNumLOSpts; i++ )
  794. {
  795. Vector vecLineOfSightTarget = VectorTransform( vecLineOfSightPoints[i], EntityToWorldTransform() );
  796. //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 );
  797. UTIL_TraceLine( player->EyePosition(), vecLineOfSightTarget, CONTENTS_SOLID, this, COLLISION_GROUP_NONE, &result );
  798. if ( result.fraction < 1.0f )
  799. {
  800. //debugoverlay->AddLineOverlay( result.startpos, result.endpos, 255,0,0, 255, 0.15f, 10 );
  801. continue;
  802. }
  803. //debugoverlay->AddLineOverlay( result.startpos, result.endpos, 0,255,0, 255, 0.15f, 10 );
  804. // this trace succeeded, so we're ok to defuse.
  805. bFoundBomb = true;
  806. break;
  807. }
  808. if ( !bFoundBomb )
  809. return;
  810. }
  811. if ( m_bBeingDefused )
  812. {
  813. if ( player != m_pBombDefuser )
  814. {
  815. if ( player->m_iNextTimeCheck < gpGlobals->curtime )
  816. {
  817. ClientPrint( player, HUD_PRINTCENTER, "#SFUI_Notice_Bomb_Already_Being_Defused" );
  818. player->m_iNextTimeCheck = gpGlobals->curtime + 1.f;
  819. }
  820. return;
  821. }
  822. m_fLastDefuseTime = gpGlobals->curtime;
  823. }
  824. else
  825. {
  826. // freeze the player in place while defusing
  827. IGameEvent * event = gameeventmanager->CreateEvent("bomb_begindefuse" );
  828. if( event )
  829. {
  830. event->SetInt( "userid", player->GetUserID() );
  831. if ( player->HasDefuser() )
  832. {
  833. event->SetInt( "haskit", 1 );
  834. }
  835. else
  836. {
  837. event->SetInt( "haskit", 0 );
  838. }
  839. event->SetInt( "priority", 5 ); // bomb_begindefuse
  840. gameeventmanager->FireEvent( event );
  841. }
  842. Vector soundPosition = player->GetAbsOrigin() + Vector( 0, 0, 5 );
  843. CPASAttenuationFilter filter( soundPosition );
  844. EmitSound( filter, 0, "c4.disarmstart", &GetAbsOrigin() );
  845. /*
  846. if ( m_pBombDefuser == player && gpGlobals->curtime < ( m_fLastDefuseTime + C4_DEFUSE_GRACE_PERIOD ) )
  847. {
  848. // if we're allowing the player to continue defusing, push the completion time ahead by the appropriate amount
  849. float fTimeGap = m_fLastDefuseTime - gpGlobals->curtime;
  850. m_flDefuseCountDown += fTimeGap;
  851. // we don't have a method for setting up the progress bar "in progress", so do it manually
  852. player->m_iProgressBarDuration = m_flDefuseLength;
  853. player->m_flProgressBarStartTime = m_flDefuseCountDown - m_flDefuseLength;
  854. }
  855. else
  856. */
  857. {
  858. m_flDefuseLength = player->HasDefuser() ? 5 : 10;
  859. m_flDefuseCountDown = gpGlobals->curtime + m_flDefuseLength;
  860. player->SetProgressBarTime( m_flDefuseLength );
  861. }
  862. m_pBombDefuser = player;
  863. m_bBeingDefused = TRUE;
  864. player->m_bIsDefusing = true;
  865. m_fLastDefuseTime = gpGlobals->curtime;
  866. //start the progress bar
  867. player->OnStartedDefuse();
  868. m_OnBombBeginDefuse.FireOutput(this, player);
  869. }
  870. }
  871. void CPlantedC4::ActivateSetTimerLength( float flTimerLength )
  872. {
  873. if ( flTimerLength < 0 )
  874. {
  875. Error( "ActivateSetTimerLength value is less than 0!" );
  876. return;
  877. }
  878. // Detonate in "time" seconds
  879. m_flTimerLength = flTimerLength;
  880. m_flC4Blow = gpGlobals->curtime + m_flTimerLength;
  881. SetThink( &CPlantedC4::C4Think );
  882. SetNextThink( gpGlobals->curtime + 0.1f );
  883. m_bBombTicking = true;
  884. }
  885. ///////////////////////////////////////////////////////////
  886. // Training version of the C4 - doesn't explode
  887. ///////////////////////////////////////////////////////////
  888. LINK_ENTITY_TO_CLASS( planted_c4_training, CPlantedC4Training );
  889. PRECACHE_REGISTER( planted_c4_training );
  890. BEGIN_PREDICTION_DATA( CPlantedC4Training )
  891. END_PREDICTION_DATA()
  892. BEGIN_DATADESC( CPlantedC4Training )
  893. // Inputs
  894. DEFINE_INPUTFUNC( FIELD_FLOAT, "ActivateSetTimerLength", InputActivateSetTimerLength ),
  895. //Outputs
  896. DEFINE_OUTPUT( m_OnBombExploded, "OnBombExploded" ),
  897. END_DATADESC()
  898. void CPlantedC4Training::InputActivateSetTimerLength( inputdata_t &inputdata )
  899. {
  900. ActivateSetTimerLength( inputdata.value.Float() );
  901. }
  902. void CPlantedC4Training::Explode( trace_t *pTrace, int bitsDamageType )
  903. {
  904. // Check to see if the round is over after the bomb went off...
  905. CSGameRules()->m_bTargetBombed = true;
  906. m_bBombTicking = false;
  907. m_bHasExploded = true;
  908. // Pull out of the wall a bit
  909. if ( pTrace->fraction != 1.0 )
  910. {
  911. SetAbsOrigin( pTrace->endpos + (pTrace->plane.normal * 0.6) );
  912. }
  913. QAngle vecAngles;
  914. DispatchParticleEffect( "c4_train_ground_effect", GetAbsOrigin(), vecAngles );
  915. // Sound! for everyone
  916. CBroadcastRecipientFilter filter;
  917. EmitSound( filter, 0, "tr.C4Explode", &GetAbsOrigin() );
  918. // Decal!
  919. //UTIL_DecalTrace( pTrace, "Scorch" );
  920. // Shake!
  921. //UTIL_ScreenShake( pTrace->endpos, 25.0, 150.0, 1.0, 3000, SHAKE_START );
  922. SetOwnerEntity( NULL ); // can't traceline attack owner if this is set
  923. m_OnBombExploded.FireOutput(this, this);
  924. }
  925. #endif // #ifndef CLIENT_DLL
  926. // -------------------------------------------------------------------------------- //
  927. // Tables.
  928. // -------------------------------------------------------------------------------- //
  929. IMPLEMENT_NETWORKCLASS_ALIASED( C4, DT_WeaponC4 )
  930. BEGIN_NETWORK_TABLE( CC4, DT_WeaponC4 )
  931. #ifdef CLIENT_DLL
  932. RecvPropBool( RECVINFO( m_bStartedArming ) ),
  933. RecvPropBool( RECVINFO( m_bBombPlacedAnimation ) ),
  934. RecvPropFloat( RECVINFO( m_fArmedTime ) ),
  935. RecvPropBool( RECVINFO( m_bShowC4LED ) ),
  936. RecvPropBool( RECVINFO( m_bIsPlantingViaUse ) )
  937. #else
  938. SendPropBool( SENDINFO( m_bStartedArming ) ),
  939. SendPropBool( SENDINFO( m_bBombPlacedAnimation ) ),
  940. SendPropFloat( SENDINFO( m_fArmedTime ), 0, SPROP_NOSCALE ),
  941. SendPropBool( SENDINFO( m_bShowC4LED ) ),
  942. SendPropBool( SENDINFO( m_bIsPlantingViaUse ) )
  943. #endif
  944. END_NETWORK_TABLE()
  945. #if defined CLIENT_DLL
  946. BEGIN_PREDICTION_DATA( CC4 )
  947. DEFINE_PRED_FIELD( m_bStartedArming, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  948. DEFINE_PRED_FIELD( m_bBombPlacedAnimation, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  949. DEFINE_PRED_FIELD( m_fArmedTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  950. DEFINE_PRED_FIELD( m_bShowC4LED, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  951. DEFINE_PRED_FIELD( m_bIsPlantingViaUse, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  952. END_PREDICTION_DATA()
  953. #endif
  954. LINK_ENTITY_TO_CLASS_ALIASED( weapon_c4, C4 );
  955. PRECACHE_REGISTER( weapon_c4 );
  956. // -------------------------------------------------------------------------------- //
  957. // Globals.
  958. // -------------------------------------------------------------------------------- //
  959. const float DROPPED_LIGHT_INTERVAL = 1.0f;
  960. // -------------------------------------------------------------------------------- //
  961. // CC4 implementation.
  962. // -------------------------------------------------------------------------------- //
  963. CC4::CC4()
  964. {
  965. m_bDroppedFromDeath = false;
  966. #if defined( CLIENT_DLL )
  967. m_szScreenText[0] = '\0';
  968. m_bShowC4LED = false;
  969. #else
  970. SetSpotRules( CCSEntitySpotting::SPOT_RULE_CT | CCSEntitySpotting::SPOT_RULE_ALWAYS_SEEN_BY_T );
  971. m_vecLastValidPlayerHeldPosition = Vector(0,0,0);
  972. #endif
  973. m_bIsPlantingViaUse = false;
  974. }
  975. CC4::~CC4()
  976. {
  977. }
  978. void CC4::Spawn()
  979. {
  980. BaseClass::Spawn();
  981. //Don't allow players to shoot the C4 around
  982. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  983. //Don't be damaged / moved by explosions
  984. m_takedamage = DAMAGE_NO;
  985. m_bBombPlanted = false;
  986. #if defined( CLIENT_DLL )
  987. SetNextClientThink( gpGlobals->curtime + DROPPED_LIGHT_INTERVAL );
  988. #else
  989. SetNextThink( gpGlobals->curtime );
  990. #endif
  991. }
  992. void CC4::ItemPostFrame()
  993. {
  994. CCSPlayer *pPlayer = GetPlayerOwner();
  995. if ( !pPlayer )
  996. return;
  997. // Disable all the firing code.. the C4 grenade is all custom.
  998. if ( pPlayer->m_nButtons & IN_ATTACK || (pPlayer->m_nButtons & IN_USE && m_bIsPlantingViaUse) )
  999. {
  1000. if ( gpGlobals->curtime >= m_flNextPrimaryAttack )
  1001. PrimaryAttack();
  1002. }
  1003. else
  1004. {
  1005. WeaponIdle();
  1006. }
  1007. if ( !(pPlayer->m_nButtons & IN_USE) )
  1008. m_bIsPlantingViaUse = false;
  1009. }
  1010. void CC4::WeaponReset( void )
  1011. {
  1012. m_bShowC4LED = false;
  1013. BaseClass::WeaponReset();
  1014. }
  1015. #if defined( CLIENT_DLL )
  1016. //-----------------------------------------------------------------------------
  1017. // Purpose:
  1018. //-----------------------------------------------------------------------------
  1019. void CC4::OnDataChanged( DataUpdateType_t type )
  1020. {
  1021. BaseClass::OnDataChanged( type );
  1022. }
  1023. void CC4::UpdateOnRemove( void )
  1024. {
  1025. BaseClass::UpdateOnRemove();
  1026. // when a c4 is removed, force the local player to update thier inventory screen
  1027. if ( !C_BasePlayer::GetLocalPlayer() || !engine->IsLocalPlayerResolvable() )
  1028. return; // early out if local player does not exsist
  1029. C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
  1030. if ( pPlayer )
  1031. {
  1032. SFWeaponSelection *pHudWS = GET_HUDELEMENT( SFWeaponSelection );
  1033. if ( pHudWS )
  1034. {
  1035. pHudWS->ShowAndUpdateSelection( WEPSELECT_SWITCH, NULL );
  1036. }
  1037. }
  1038. }
  1039. void CC4::ClientThink( void )
  1040. {
  1041. BaseClass::ClientThink();
  1042. SetNextClientThink( gpGlobals->curtime + DROPPED_LIGHT_INTERVAL );
  1043. // This think function is just for updating the blinking light of the dropped bomb.
  1044. // So if we have an owner, we don't want to blink.
  1045. if ( IsDormant() || NULL != GetPlayerOwner() || !CSGameRules()->m_bBombDropped )
  1046. {
  1047. return;
  1048. }
  1049. int ledAttachmentIndex = LookupAttachment("led");
  1050. DispatchParticleEffect( "c4_timer_light_dropped", PATTACH_POINT_FOLLOW, this, ledAttachmentIndex, false, -1 );
  1051. }
  1052. bool CC4::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
  1053. {
  1054. if( event == 7001 )
  1055. {
  1056. // "Custom code" c4 disabled
  1057. //
  1058. /*//if we're clearing the screen text, or have no custom description, set the text normally using the passed param.
  1059. if ( !V_strcmp( options, "" )
  1060. || !V_strcmp( options, "*******" )
  1061. || GetEconItemView() == NULL
  1062. || CALL_ATTRIB_HOOK_BOOL( custom_bombcode ) == false
  1063. || GetEconItemView()->GetCustomDesc() == NULL
  1064. )
  1065. {
  1066. Q_strncpy( m_szScreenText, options, 16 );
  1067. }
  1068. else
  1069. {
  1070. //otherwise we have a custom code to display
  1071. CEconItemView *pItem = GetEconItemView();
  1072. if ( pItem && pItem->GetCustomDesc() )
  1073. {
  1074. if ( strlen(m_szScreenText) >= 7 )
  1075. {
  1076. //if the 7-char code is full, replace it with asterisks (this will happen at the end of the plant animation anyway)
  1077. Q_strncpy( m_szScreenText, "*******\0", 8 );
  1078. }
  1079. else
  1080. {
  1081. //we're still under 7 chars, so add an additional char of the custom code
  1082. Q_strncpy( m_szScreenText, pItem->GetCustomDesc(), strlen(m_szScreenText)+2 );
  1083. }
  1084. }
  1085. }*/
  1086. //set the screen text to the string in 'options'
  1087. Q_strncpy( m_szScreenText, options, 16 );
  1088. return true;
  1089. }
  1090. return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
  1091. }
  1092. char *CC4::GetScreenText( void )
  1093. {
  1094. return m_szScreenText;
  1095. }
  1096. void CC4::CreateLEDEffect( void )
  1097. {
  1098. if ( GetPlayerOwner() )
  1099. GetPlayerOwner()->CreateC4Effect( GetWeaponForEffect(), true );
  1100. }
  1101. void CC4::RemoveLEDEffect( void )
  1102. {
  1103. if ( GetPlayerOwner() )
  1104. GetPlayerOwner()->RemoveC4Effect( true );
  1105. }
  1106. #else
  1107. void CC4::PhysicsTouchTriggers(const Vector *pPrevAbsOrigin)
  1108. {
  1109. // Normally items like ammo or weapons aren't expected to touch other triggers, but C4 is a special case
  1110. edict_t *pEntity = edict();
  1111. if (pEntity && !IsWorld())
  1112. {
  1113. Assert(CollisionProp());
  1114. //Dropped bombs are both solid and have no owner. In this state, unlike other weapons, they can
  1115. //now touch triggers so long they haven't had their position reset by a bomb reset trigger.
  1116. if ( IsSolid() && (GetPlayerOwner() == NULL) )
  1117. {
  1118. SetCheckUntouch(true);
  1119. engine->SolidMoved(pEntity, CollisionProp(), pPrevAbsOrigin, sm_bAccurateTriggerBboxChecks);
  1120. }
  1121. }
  1122. }
  1123. #endif //CLIENT_DLL
  1124. #ifdef GAME_DLL
  1125. void CC4::Think()
  1126. {
  1127. //If the bomb is held by an alive player standing on the ground, then we can use this
  1128. //position as the last known valid position to respawn the bomb if it gets reset.
  1129. CCSPlayer *pPlayer = GetPlayerOwner();
  1130. if ( pPlayer && pPlayer->IsAlive() && FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
  1131. {
  1132. m_vecLastValidPlayerHeldPosition = pPlayer->GetAbsOrigin();
  1133. }
  1134. SetNextThink( gpGlobals->curtime + WEAPON_C4_UPDATE_LAST_VALID_PLAYER_HELD_POSITION_INTERVAL );
  1135. }
  1136. void CC4::ResetToLastValidPlayerHeldPosition()
  1137. {
  1138. //When reset, the bomb returns to its last known valid position.
  1139. CCSPlayer *pPlayer = GetPlayerOwner();
  1140. if ( !pPlayer && GetAbsOrigin() != m_vecLastValidPlayerHeldPosition )
  1141. {
  1142. // Teleport the bomb facing up so the flashing light is clearly visible.
  1143. Vector vecResetPos = m_vecLastValidPlayerHeldPosition + Vector(0,0,8);
  1144. QAngle angResetAng = QAngle( 0, RandomInt(0,360), 0 );
  1145. // trace to ground
  1146. trace_t c4TeleportTrace;
  1147. UTIL_TraceHull( vecResetPos, vecResetPos + Vector(0,0,-8), Vector(-3,-3,-1), Vector(3,3,1), MASK_PLAYERSOLID, NULL, COLLISION_GROUP_PLAYER_MOVEMENT, &c4TeleportTrace );
  1148. if ( !c4TeleportTrace.startsolid && c4TeleportTrace.DidHit() )
  1149. {
  1150. vecResetPos += ( c4TeleportTrace.fraction * Vector(0,0,-8) );
  1151. }
  1152. Teleport( &vecResetPos, &angResetAng, NULL );
  1153. // Set the physics object asleep so it doesn't tumble off precarious ledges and keep resetting.
  1154. IPhysicsObject *pObj = VPhysicsGetObject();
  1155. if ( pObj )
  1156. pObj->Sleep();
  1157. }
  1158. }
  1159. unsigned int CC4::PhysicsSolidMaskForEntity( void ) const
  1160. {
  1161. return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_PLAYERCLIP;
  1162. }
  1163. void CC4::Precache()
  1164. {
  1165. PrecacheVGuiScreen( "c4_view_panel" );
  1166. PrecacheScriptSound( "c4.disarmfinish" );
  1167. PrecacheScriptSound( "c4.explode" );
  1168. PrecacheScriptSound( "c4.disarmstart" );
  1169. PrecacheScriptSound( "c4.plant" );
  1170. PrecacheScriptSound( "C4.PlantSound" );
  1171. PrecacheScriptSound( "c4.click" );
  1172. // training
  1173. PrecacheParticleSystem( "c4_train_ground_effect" );
  1174. PrecacheScriptSound( "tr.C4Explode" );
  1175. BaseClass::Precache();
  1176. }
  1177. int CC4::UpdateTransmitState()
  1178. {
  1179. return SetTransmitState( FL_EDICT_FULLCHECK );
  1180. }
  1181. int CC4::ShouldTransmit( const CCheckTransmitInfo *pInfo )
  1182. {
  1183. CBasePlayer *pPlayer = ToBasePlayer( CBaseEntity::Instance( pInfo->m_pClientEnt ) );
  1184. if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT )
  1185. {
  1186. return FL_EDICT_PVSCHECK;
  1187. }
  1188. else
  1189. {
  1190. // This is needed for the instructor message
  1191. return FL_EDICT_ALWAYS;
  1192. }
  1193. }
  1194. //-----------------------------------------------------------------------------
  1195. // Purpose: Gets info about the control panels
  1196. //-----------------------------------------------------------------------------
  1197. void CC4::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
  1198. {
  1199. pPanelName = "c4_view_panel";
  1200. }
  1201. bool CC4::ShouldRemoveOnRoundRestart()
  1202. {
  1203. // Doesn't matter if we have an owner or not.. always remove the C4 when the round restarts.
  1204. // The gamerules will give another C4 to some lucky player.
  1205. CCSPlayer *pPlayer = GetPlayerOwner();
  1206. if ( pPlayer && pPlayer->GetActiveWeapon() == this )
  1207. engine->ClientCommand( pPlayer->edict(), "lastinv reset\n" );
  1208. return true;
  1209. }
  1210. #endif
  1211. bool CC4::Deploy( )
  1212. {
  1213. //m_bShowC4LED = true;
  1214. #ifdef CLIENT_DLL
  1215. //CreateLEDEffect();
  1216. #endif
  1217. return BaseClass::Deploy();
  1218. }
  1219. bool CC4::Holster( CBaseCombatWeapon *pSwitchingTo )
  1220. {
  1221. #ifdef GAME_DLL
  1222. CCSPlayer *pPlayer = GetPlayerOwner();
  1223. if ( pPlayer )
  1224. pPlayer->SetProgressBarTime( 0 );
  1225. if ( m_bStartedArming )
  1226. {
  1227. AbortBombPlant();
  1228. }
  1229. #else
  1230. //RemoveLEDEffect();
  1231. #endif
  1232. //m_bShowC4LED = false;
  1233. return BaseClass::Holster( pSwitchingTo );
  1234. }
  1235. void CC4::PrimaryAttack()
  1236. {
  1237. bool bArmingTimeSatisfied = false;
  1238. CCSPlayer *pPlayer = GetPlayerOwner();
  1239. if ( !pPlayer )
  1240. return;
  1241. int onGround = FBitSet( pPlayer->GetFlags(), FL_ONGROUND );
  1242. CBaseEntity *groundEntity = (onGround) ? pPlayer->GetGroundEntity() : NULL;
  1243. trace_t trPlant;
  1244. if ( groundEntity )
  1245. {
  1246. // Don't let us stand on players, breakables, or pushaway physics objects to plant
  1247. if ( groundEntity->IsPlayer() ||
  1248. IsPushableEntity( groundEntity ) ||
  1249. #ifndef CLIENT_DLL
  1250. IsBreakableEntity( groundEntity ) ||
  1251. #endif // !CLIENT_DLL
  1252. IsPushAwayEntity( groundEntity ) )
  1253. {
  1254. onGround = false;
  1255. }
  1256. if ( onGround )
  1257. {
  1258. Vector vecStart = GetAbsOrigin() + Vector(0,0,8);
  1259. Vector vecEnd = GetAbsOrigin() + Vector(0,0,-38);
  1260. UTIL_TraceHull( vecStart, vecEnd, Vector( -3, -3, 0 ), Vector( 3, 3, 16 ), MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &trPlant );
  1261. if ( trPlant.fraction == 1.0 )
  1262. {
  1263. onGround = false;
  1264. }
  1265. }
  1266. }
  1267. if( m_bStartedArming == false && m_bBombPlanted == false )
  1268. {
  1269. if( pPlayer->m_bInBombZone && onGround )
  1270. {
  1271. m_bStartedArming = true;
  1272. m_fArmedTime = gpGlobals->curtime + WEAPON_C4_ARM_TIME;
  1273. m_bBombPlacedAnimation = false;
  1274. pPlayer->m_bDuckOverride = true;
  1275. #if !defined( CLIENT_DLL )
  1276. pPlayer->SetAttemptedBombPlace();
  1277. // init the beep flags
  1278. int i;
  1279. for( i=0;i<NUM_BEEPS;i++ )
  1280. m_bPlayedArmingBeeps[i] = false;
  1281. // freeze the player in place while planting
  1282. // player "arming bomb" animation
  1283. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  1284. pPlayer->SetNextAttack( gpGlobals->curtime );
  1285. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_beginplant" );
  1286. if( event )
  1287. {
  1288. event->SetInt("userid", pPlayer->GetUserID() );
  1289. event->SetInt("site", pPlayer->m_iBombSiteIndex );
  1290. event->SetInt( "priority", 5 ); // bomb_beginplant
  1291. gameeventmanager->FireEvent( event );
  1292. }
  1293. PlayPlantInitSound();
  1294. if ( pPlayer && !pPlayer->IsBot() && pPlayer->m_flC4PlantTalkTimer < gpGlobals->curtime )
  1295. {
  1296. // for console, we don't want to show the chat text because it almost always overlaps
  1297. // with the bomb planted alert text in the center of the screen
  1298. if ( IsGameConsole() || engine->IsDedicatedServerForXbox() || engine->IsDedicatedServerForPS3() )
  1299. pPlayer->Radio( "PlantingBomb", "", true );
  1300. else
  1301. pPlayer->Radio( "PlantingBomb", "#Cstrike_TitlesTXT_Planting_Bomb", true );
  1302. pPlayer->m_flC4PlantTalkTimer = gpGlobals->curtime + 10.0f;
  1303. }
  1304. #endif
  1305. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  1306. #ifndef CLIENT_DLL
  1307. if ( pPlayer && !pPlayer->IsDormant() )
  1308. {
  1309. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN_PRIMARY );
  1310. }
  1311. #endif
  1312. //FX_PlantBomb( pPlayer->entindex(), pPlayer->Weapon_ShootPosition(), PLANTBOMB_PLANT );
  1313. }
  1314. else
  1315. {
  1316. if ( !pPlayer->GetUseEntity() )
  1317. {
  1318. if ( !pPlayer->m_bInBombZone )
  1319. {
  1320. ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_C4_Plant_At_Bomb_Spot");
  1321. #if defined( CLIENT_DLL )
  1322. STEAMWORKS_TESTSECRET_AMORTIZE(5);
  1323. #endif
  1324. }
  1325. else
  1326. {
  1327. ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_C4_Plant_Must_Be_On_Ground");
  1328. #if defined( CLIENT_DLL )
  1329. STEAMWORKS_TESTSECRET_AMORTIZE(7);
  1330. #endif
  1331. }
  1332. }
  1333. m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  1334. return;
  1335. }
  1336. }
  1337. else
  1338. {
  1339. if ( !onGround || !pPlayer->m_bInBombZone )
  1340. {
  1341. if( !pPlayer->m_bInBombZone )
  1342. {
  1343. ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_C4_Arming_Cancelled" );
  1344. }
  1345. else
  1346. {
  1347. ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_C4_Plant_Must_Be_On_Ground" );
  1348. #if defined( CLIENT_DLL )
  1349. STEAMWORKS_TESTSECRET_AMORTIZE(9);
  1350. #endif
  1351. }
  1352. AbortBombPlant();
  1353. if(m_bBombPlacedAnimation == true) //this means the placement animation is canceled
  1354. {
  1355. SendWeaponAnim( ACT_VM_DRAW );
  1356. }
  1357. else
  1358. {
  1359. SendWeaponAnim( ACT_VM_IDLE );
  1360. }
  1361. return;
  1362. }
  1363. else
  1364. {
  1365. // we no longer play arming beeps to all player, we only play the initialization sound
  1366. // #ifndef CLIENT_DLL
  1367. // PlayArmingBeeps();
  1368. // #endif
  1369. if( gpGlobals->curtime >= m_fArmedTime ) //the c4 is ready to be armed
  1370. {
  1371. //check to make sure the player is still in the bomb target area
  1372. bArmingTimeSatisfied = true;
  1373. }
  1374. else if( ( gpGlobals->curtime >= (m_fArmedTime - 0.75) ) && ( !m_bBombPlacedAnimation ) )
  1375. {
  1376. //call the c4 Placement animation
  1377. m_bBombPlacedAnimation = true;
  1378. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  1379. #if !defined( CLIENT_DLL )
  1380. // player "place" animation
  1381. //pPlayer->SetAnimation( PLAYER_HOLDBOMB );
  1382. #endif
  1383. }
  1384. }
  1385. }
  1386. if ( bArmingTimeSatisfied && m_bStartedArming )
  1387. {
  1388. m_bStartedArming = false;
  1389. m_fArmedTime = 0;
  1390. if( pPlayer->m_bInBombZone )
  1391. {
  1392. #if !defined( CLIENT_DLL )
  1393. CPlantedC4 *pC4 = CPlantedC4::ShootSatchelCharge( pPlayer, pPlayer->GetAbsOrigin(), pPlayer->GetAbsAngles() );
  1394. if ( pC4 && trPlant.fraction < 1.0 )
  1395. {
  1396. pC4->SetBombSiteIndex( pPlayer->m_iBombSiteIndex );
  1397. pC4->SetAbsOrigin( trPlant.endpos );
  1398. //bomb aligns to planted surface normal within a threshold
  1399. if ( fabs(trPlant.plane.normal.Dot(Vector(0,0,1))) > 0.65f )
  1400. {
  1401. //get the player forward vector
  1402. Vector vecFlatForward;
  1403. VectorCopy( pPlayer->Forward(), vecFlatForward );
  1404. vecFlatForward.z = 0;
  1405. //derive c4 forward and right
  1406. Vector vecC4Right = CrossProduct( vecFlatForward.Normalized(), trPlant.plane.normal );
  1407. Vector vecC4Forward = CrossProduct( vecC4Right, trPlant.plane.normal );
  1408. QAngle C4Angle;
  1409. VectorAngles( -vecC4Forward, trPlant.plane.normal, C4Angle );
  1410. pC4->SetAbsAngles( C4Angle );
  1411. }
  1412. CBombTarget *pBombTarget = (CBombTarget*)UTIL_EntityByIndex( pPlayer->m_iBombSiteIndex );
  1413. if ( pBombTarget )
  1414. {
  1415. CBaseEntity *pAttachPoint = gEntList.FindEntityByName( NULL, pBombTarget->GetBombMountTarget() );
  1416. if ( pAttachPoint )
  1417. {
  1418. pC4->SetAbsOrigin( pAttachPoint->GetAbsOrigin() );
  1419. pC4->SetAbsAngles( pAttachPoint->GetAbsAngles() );
  1420. pC4->SetParent( pAttachPoint );
  1421. }
  1422. variant_t emptyVariant;
  1423. pBombTarget->AcceptInput( "BombPlanted", pC4, pC4, emptyVariant, 0 );
  1424. }
  1425. // [tj] If the bomb is planted by someone that picked it up after the
  1426. // original owner was killed, pass that along to the planted bomb
  1427. pC4->SetPlantedAfterPickup( m_bDroppedFromDeath );
  1428. }
  1429. // Determine how elapsed time from start of round until the bomb was planted
  1430. float plantingTime = gpGlobals->curtime - CSGameRules()->GetRoundStartTime();
  1431. // Award achievement to bomb planter if time <= 25 seconds
  1432. if ( (plantingTime > 0.0f) &&
  1433. (plantingTime <= AchievementConsts::FastBombPlant_Time) &&
  1434. !CSGameRules()->IsPlayingGunGameTRBomb() )
  1435. {
  1436. pPlayer->AwardAchievement( CSPlantBombWithin25Seconds );
  1437. }
  1438. pPlayer->SetLastWeaponBeforeAutoSwitchToC4( NULL ); // completed a bomb plant, this clears out our saved value for switching back to a saved weapon
  1439. pPlayer->SetBombPlacedTime( gpGlobals->curtime );
  1440. CCS_GameStats.Event_BombPlanted( pPlayer );
  1441. CSGameRules()->ScoreBombPlant( pPlayer );
  1442. // record in matchstats
  1443. if ( CSGameRules()->ShouldRecordMatchStats() )
  1444. {
  1445. int iCurrentRound = CSGameRules()->GetTotalRoundsPlayed();
  1446. ++ pPlayer->m_iMatchStats_Objective.GetForModify( iCurrentRound );
  1447. // Keep track of Match stats in QMM data
  1448. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( pPlayer->GetHumanPlayerAccountID() ) )
  1449. {
  1450. pQMM->m_iMatchStats_Objective[ iCurrentRound ] = pPlayer->m_iMatchStats_Objective.Get( iCurrentRound );
  1451. }
  1452. }
  1453. pPlayer->AddAccountAward( PlayerCashAward::BOMB_PLANTED );
  1454. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_planted" );
  1455. if( event )
  1456. {
  1457. event->SetInt("userid", pPlayer->GetUserID() );
  1458. event->SetInt("site", pPlayer->m_iBombSiteIndex );
  1459. event->SetInt("posx", pPlayer->GetAbsOrigin().x );
  1460. event->SetInt("posy", pPlayer->GetAbsOrigin().y );
  1461. event->SetInt( "priority", 5 ); // bomb_planted
  1462. gameeventmanager->FireEvent( event );
  1463. }
  1464. // Fire a beep event also so the bots have a chance to hear the bomb
  1465. event = gameeventmanager->CreateEvent( "bomb_beep" );
  1466. if ( event )
  1467. {
  1468. event->SetInt( "entindex", entindex() );
  1469. gameeventmanager->FireEvent( event );
  1470. }
  1471. pPlayer->SetProgressBarTime( 0 );
  1472. CSGameRules()->m_bBombDropped = false;
  1473. CSGameRules()->m_bBombPlanted = true;
  1474. // Play the plant sound.
  1475. // don't play a bomb plant sound for everyone anymore ?
  1476. Vector plantPosition = pPlayer->GetAbsOrigin() + Vector( 0, 0, 5 );
  1477. CPASAttenuationFilter filter( plantPosition );
  1478. EmitSound( filter, 0, "c4.plantquiet", &GetAbsOrigin() );
  1479. // No more c4!
  1480. pPlayer->Weapon_Drop( this, NULL, NULL );
  1481. UTIL_Remove( this );
  1482. pPlayer->m_bDuckOverride = false;
  1483. #endif
  1484. //don't allow the planting to start over again next frame.
  1485. m_bBombPlanted = true;
  1486. #if !defined( CLIENT_DLL )
  1487. if ( CSGameRules()->IsPlayingCooperativeGametype() )
  1488. CSGameRules()->CheckWinConditions();// %plant bomb%
  1489. #endif
  1490. return;
  1491. }
  1492. else
  1493. {
  1494. ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_C4_Activated_At_Bomb_Spot" );
  1495. #if !defined( CLIENT_DLL )
  1496. //pPlayer->SetAnimation( PLAYER_HOLDBOMB );
  1497. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortplant" );
  1498. if( event )
  1499. {
  1500. event->SetInt("userid", pPlayer->GetUserID() );
  1501. event->SetInt("site", pPlayer->m_iBombSiteIndex );
  1502. event->SetInt( "priority", 5 ); // bomb_abortplant
  1503. gameeventmanager->FireEvent( event );
  1504. }
  1505. #endif
  1506. m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  1507. return;
  1508. }
  1509. }
  1510. m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
  1511. SetWeaponIdleTime( gpGlobals->curtime + SharedRandomFloat("C4IdleTime", 10, 15 ) );
  1512. }
  1513. void CC4::WeaponIdle()
  1514. {
  1515. // if the player releases the attack button cancel the arming sequence
  1516. if ( m_bStartedArming )
  1517. {
  1518. AbortBombPlant();
  1519. CCSPlayer *pPlayer = GetPlayerOwner();
  1520. pPlayer->m_bDuckOverride = false;
  1521. // TODO: make this use SendWeaponAnim and activities when the C4 has the activities hooked up.
  1522. if ( pPlayer )
  1523. {
  1524. SendWeaponAnim( ACT_VM_IDLE );
  1525. pPlayer->SetNextAttack( gpGlobals->curtime );
  1526. }
  1527. if(m_bBombPlacedAnimation == true) //this means the placement animation is canceled
  1528. SendWeaponAnim( ACT_VM_DRAW );
  1529. else
  1530. SendWeaponAnim( ACT_VM_IDLE );
  1531. }
  1532. }
  1533. void CC4::UpdateShieldState( void )
  1534. {
  1535. //ADRIANTODO
  1536. CCSPlayer *pPlayer = GetPlayerOwner();
  1537. if ( !pPlayer )
  1538. return;
  1539. if ( pPlayer->HasShield() )
  1540. {
  1541. pPlayer->SetShieldDrawnState( false );
  1542. CBaseViewModel *pVM = pPlayer->GetViewModel( 1 );
  1543. if ( pVM )
  1544. {
  1545. pVM->AddEffects( EF_NODRAW );
  1546. }
  1547. //pPlayer->SetHitBoxSet( 3 );
  1548. }
  1549. else
  1550. BaseClass::UpdateShieldState();
  1551. }
  1552. int m_iBeepFrames[NUM_BEEPS] = { 20, 29, 37, 44, 50, 59, 65 };
  1553. int iNumArmingAnimFrames = 83;
  1554. void CC4::PlayArmingBeeps( void )
  1555. {
  1556. float flStartTime = m_fArmedTime - WEAPON_C4_ARM_TIME;
  1557. float flProgress = ( gpGlobals->curtime - flStartTime ) / ( WEAPON_C4_ARM_TIME - 0.75 );
  1558. int currentFrame = (int)( (float)iNumArmingAnimFrames * flProgress );
  1559. int i;
  1560. for( i=0;i<NUM_BEEPS;i++ )
  1561. {
  1562. if( currentFrame <= m_iBeepFrames[i] )
  1563. {
  1564. break;
  1565. }
  1566. else if( !m_bPlayedArmingBeeps[i] )
  1567. {
  1568. m_bPlayedArmingBeeps[i] = true;
  1569. CCSPlayer *owner = GetPlayerOwner();
  1570. if ( !owner && !owner->IsAlive() )
  1571. break;
  1572. Vector soundPosition = owner->GetAbsOrigin() + Vector( 0, 0, 5 );
  1573. CPASAttenuationFilter filter( soundPosition );
  1574. filter.RemoveRecipient( owner );
  1575. // remove anyone that is first person spec'ing the planter
  1576. int i;
  1577. CBasePlayer *pPlayer;
  1578. for( i=1;i<=gpGlobals->maxClients;i++ )
  1579. {
  1580. pPlayer = UTIL_PlayerByIndex( i );
  1581. if ( !pPlayer )
  1582. continue;
  1583. if( pPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pPlayer->GetObserverTarget() == GetOwner() )
  1584. {
  1585. filter.RemoveRecipient( pPlayer );
  1586. }
  1587. }
  1588. EmitSound(filter, 0, "c4.click", &GetAbsOrigin() );
  1589. break;
  1590. }
  1591. }
  1592. }
  1593. void CC4::PlayPlantInitSound( void )
  1594. {
  1595. CCSPlayer *owner = GetPlayerOwner();
  1596. if ( !owner && !owner->IsAlive() )
  1597. return;
  1598. Vector soundPosition = owner->GetAbsOrigin() + Vector( 0, 0, 5 );
  1599. CPASAttenuationFilter filter( soundPosition );
  1600. filter.RemoveRecipient( owner );
  1601. // remove anyone that is first person spec'ing the planter
  1602. int i;
  1603. CBasePlayer *pPlayer;
  1604. for( i=1;i<=gpGlobals->maxClients;i++ )
  1605. {
  1606. pPlayer = UTIL_PlayerByIndex( i );
  1607. if ( !pPlayer )
  1608. continue;
  1609. if( pPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pPlayer->GetObserverTarget() == GetOwner() )
  1610. {
  1611. filter.RemoveRecipient( pPlayer );
  1612. }
  1613. }
  1614. EmitSound(filter, 0, "c4.initiate", &GetAbsOrigin() );
  1615. }
  1616. float CC4::GetMaxSpeed() const
  1617. {
  1618. if ( m_bStartedArming )
  1619. return CS_PLAYER_SPEED_STOPPED;
  1620. else
  1621. return BaseClass::GetMaxSpeed();
  1622. }
  1623. void CC4::OnPickedUp( CBaseCombatCharacter *pNewOwner )
  1624. {
  1625. BaseClass::OnPickedUp( pNewOwner );
  1626. #if !defined( CLIENT_DLL )
  1627. CCSPlayer *pPlayer = dynamic_cast<CCSPlayer *>( pNewOwner );
  1628. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_pickup" );
  1629. if ( event )
  1630. {
  1631. event->SetInt( "userid", pPlayer->GetUserID() );
  1632. event->SetInt( "priority", 5 ); // bomb_pickup
  1633. gameeventmanager->FireEvent( event );
  1634. }
  1635. CSGameRules()->m_bBombDropped = false;
  1636. if ( !CSGameRules()->IsPlayingTraining() )
  1637. ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_Got_Bomb" );
  1638. pPlayer->SetBombPickupTime( gpGlobals->curtime );
  1639. #endif
  1640. }
  1641. // HACK - Ask Mike Booth...
  1642. #ifndef CLIENT_DLL
  1643. #include "cs_bot.h"
  1644. #endif
  1645. // memdbgon must be the last include file in a .cpp file!!!
  1646. #include "tier0/memdbgon.h"
  1647. void CC4::Drop( const Vector &vecVelocity )
  1648. {
  1649. #if !defined( CLIENT_DLL )
  1650. if ( !CSGameRules()->m_bBombPlanted ) // its not dropped if its planted
  1651. {
  1652. // tell the bots about the dropped bomb
  1653. TheCSBots()->SetLooseBomb( this );
  1654. CSGameRules()->m_bBombDropped = true;
  1655. CBasePlayer *pPlayer = dynamic_cast<CBasePlayer *>(GetOwnerEntity());
  1656. Assert( pPlayer );
  1657. if ( pPlayer )
  1658. {
  1659. CCSPlayer *pCCSPlayer = dynamic_cast<CCSPlayer *>( pPlayer );
  1660. pCCSPlayer->SetBombDroppedTime( gpGlobals->curtime );
  1661. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_dropped" );
  1662. if ( event )
  1663. {
  1664. event->SetInt( "userid", pPlayer->GetUserID() );
  1665. event->SetInt( "entindex", entindex() );
  1666. event->SetInt( "priority", 5 ); // bomb_dropped
  1667. gameeventmanager->FireEvent( event );
  1668. //event->SetInt( "entindex", iTest );
  1669. }
  1670. }
  1671. }
  1672. #endif
  1673. #if defined( CLIENT_DLL )
  1674. STEAMWORKS_TESTSECRET_AMORTIZE( 13 );
  1675. #endif
  1676. if ( m_bStartedArming )
  1677. AbortBombPlant(); // stop arming sequence
  1678. BaseClass::Drop( vecVelocity );
  1679. }
  1680. void CC4::AbortBombPlant()
  1681. {
  1682. m_bStartedArming = false;
  1683. CCSPlayer *pPlayer = GetPlayerOwner();
  1684. if ( !pPlayer )
  1685. return;
  1686. #if !defined( CLIENT_DLL )
  1687. m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  1688. pPlayer->SetProgressBarTime( 0 );
  1689. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortplant" );
  1690. if( event )
  1691. {
  1692. event->SetInt("userid", pPlayer->GetUserID() );
  1693. event->SetInt("site", pPlayer->m_iBombSiteIndex );
  1694. event->SetInt( "priority", 5 ); // bomb_abortplant
  1695. gameeventmanager->FireEvent( event );
  1696. }
  1697. if( pPlayer->GetLastWeaponBeforeAutoSwitchToC4() != NULL )
  1698. {
  1699. CBaseViewModel *vm = pPlayer->GetViewModel();
  1700. if ( vm )
  1701. {
  1702. vm->AddEffects( EF_NODRAW );
  1703. }
  1704. pPlayer->Weapon_Switch( pPlayer->GetLastWeaponBeforeAutoSwitchToC4() );
  1705. pPlayer->SetLastWeaponBeforeAutoSwitchToC4( NULL );
  1706. }
  1707. #else
  1708. // Clear the numbers from the screen if we just aborted.
  1709. m_szScreenText[0] = '\0';
  1710. #endif
  1711. #ifndef CLIENT_DLL
  1712. if ( pPlayer && !pPlayer->IsDormant() )
  1713. {
  1714. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_CLEAR_FIRING );
  1715. }
  1716. #endif
  1717. //FX_PlantBomb( pPlayer->entindex(), pPlayer->Weapon_ShootPosition(), PLANTBOMB_ABORT );
  1718. }