Team Fortress 2 Source Code as on 22/4/2020
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.

1352 lines
37 KiB

  1. //========= Copyright 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. #if defined( CLIENT_DLL )
  16. #include "c_cs_player.h"
  17. #else
  18. #include "cs_player.h"
  19. #include "explode.h"
  20. #include "mapinfo.h"
  21. #include "team.h"
  22. #include "func_bomb_target.h"
  23. #include "vguiscreen.h"
  24. #include "bot.h"
  25. #include "cs_player.h"
  26. #include <KeyValues.h>
  27. //=============================================================================
  28. // HPE_BEGIN
  29. // [dwenger] Necessary for stats tracking
  30. //=============================================================================
  31. #include "cs_gamestats.h"
  32. #include "cs_achievement_constants.h"
  33. //=============================================================================
  34. // HPE_END
  35. //=============================================================================
  36. #endif
  37. // memdbgon must be the last include file in a .cpp file!!!
  38. #include "tier0/memdbgon.h"
  39. #define BLINK_INTERVAL 2.0
  40. #define PLANTED_C4_MODEL "models/weapons/w_c4_planted.mdl"
  41. #define HEIST_MODE_C4_TIME 25
  42. int g_sModelIndexC4Glow = -1;
  43. #define WEAPON_C4_ARM_TIME 3.0
  44. #ifdef CLIENT_DLL
  45. #else
  46. LINK_ENTITY_TO_CLASS( planted_c4, CPlantedC4 );
  47. PRECACHE_REGISTER( planted_c4 );
  48. BEGIN_DATADESC( CPlantedC4 )
  49. DEFINE_FUNCTION( C4Think )
  50. END_DATADESC()
  51. IMPLEMENT_SERVERCLASS_ST( CPlantedC4, DT_PlantedC4 )
  52. SendPropBool( SENDINFO(m_bBombTicking) ),
  53. SendPropFloat( SENDINFO(m_flC4Blow), 0, SPROP_NOSCALE ),
  54. SendPropFloat( SENDINFO(m_flTimerLength), 0, SPROP_NOSCALE ),
  55. SendPropFloat( SENDINFO(m_flDefuseLength), 0, SPROP_NOSCALE ),
  56. SendPropFloat( SENDINFO(m_flDefuseCountDown), 0, SPROP_NOSCALE ),
  57. END_SEND_TABLE()
  58. BEGIN_PREDICTION_DATA( CPlantedC4 )
  59. END_PREDICTION_DATA()
  60. CUtlVector< CPlantedC4* > g_PlantedC4s;
  61. CPlantedC4::CPlantedC4()
  62. {
  63. g_PlantedC4s.AddToTail( this );
  64. //=============================================================================
  65. // HPE_BEGIN:
  66. //=============================================================================
  67. // [tj] No planter initially
  68. m_pPlanter = NULL;
  69. // [tj] Assume this is the original owner
  70. m_bPlantedAfterPickup = false;
  71. //=============================================================================
  72. // HPE_END
  73. //=============================================================================
  74. }
  75. CPlantedC4::~CPlantedC4()
  76. {
  77. g_PlantedC4s.FindAndRemove( this );
  78. int i;
  79. // Kill the control panels
  80. for ( i = m_hScreens.Count(); --i >= 0; )
  81. {
  82. DestroyVGuiScreen( m_hScreens[i].Get() );
  83. }
  84. m_hScreens.RemoveAll();
  85. }
  86. int CPlantedC4::UpdateTransmitState()
  87. {
  88. return SetTransmitState( FL_EDICT_FULLCHECK );
  89. }
  90. int CPlantedC4::ShouldTransmit( const CCheckTransmitInfo *pInfo )
  91. {
  92. // Terrorists always need this object for the radar
  93. // Everybody needs it for hiding the round timer and showing the planted C4 scenario icon
  94. return FL_EDICT_ALWAYS;
  95. }
  96. void CPlantedC4::Precache()
  97. {
  98. g_sModelIndexC4Glow = PrecacheModel( "sprites/ledglow.vmt" );
  99. PrecacheModel( PLANTED_C4_MODEL );
  100. PrecacheVGuiScreen( "c4_panel" );
  101. engine->ForceModelBounds( PLANTED_C4_MODEL, Vector( -7, -13, -3 ), Vector( 9, 12, 11 ) );
  102. PrecacheParticleSystem( "bomb_explosion_huge" );
  103. }
  104. void CPlantedC4::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
  105. {
  106. pPanelName = "c4_panel";
  107. }
  108. void CPlantedC4::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName )
  109. {
  110. pPanelName = "vgui_screen";
  111. }
  112. //-----------------------------------------------------------------------------
  113. // This is called by the base object when it's time to spawn the control panels
  114. //-----------------------------------------------------------------------------
  115. void CPlantedC4::SpawnControlPanels()
  116. {
  117. char buf[64];
  118. // FIXME: Deal with dynamically resizing control panels?
  119. // If we're attached to an entity, spawn control panels on it instead of use
  120. CBaseAnimating *pEntityToSpawnOn = this;
  121. const char *pOrgLL = "controlpanel%d_ll";
  122. const char *pOrgUR = "controlpanel%d_ur";
  123. const char *pAttachmentNameLL = pOrgLL;
  124. const char *pAttachmentNameUR = pOrgUR;
  125. Assert( pEntityToSpawnOn );
  126. // Lookup the attachment point...
  127. int nPanel;
  128. for ( nPanel = 0; true; ++nPanel )
  129. {
  130. Q_snprintf( buf, sizeof( buf ), pAttachmentNameLL, nPanel );
  131. int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  132. if (nLLAttachmentIndex <= 0)
  133. {
  134. // Try and use my panels then
  135. pEntityToSpawnOn = this;
  136. Q_snprintf( buf, sizeof( buf ), pOrgLL, nPanel );
  137. nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  138. if (nLLAttachmentIndex <= 0)
  139. return;
  140. }
  141. Q_snprintf( buf, sizeof( buf ), pAttachmentNameUR, nPanel );
  142. int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  143. if (nURAttachmentIndex <= 0)
  144. {
  145. // Try and use my panels then
  146. Q_snprintf( buf, sizeof( buf ), pOrgUR, nPanel );
  147. nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
  148. if (nURAttachmentIndex <= 0)
  149. return;
  150. }
  151. const char *pScreenName;
  152. GetControlPanelInfo( nPanel, pScreenName );
  153. if (!pScreenName)
  154. continue;
  155. const char *pScreenClassname;
  156. GetControlPanelClassName( nPanel, pScreenClassname );
  157. if ( !pScreenClassname )
  158. continue;
  159. // Compute the screen size from the attachment points...
  160. matrix3x4_t panelToWorld;
  161. pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld );
  162. matrix3x4_t worldToPanel;
  163. MatrixInvert( panelToWorld, worldToPanel );
  164. // Now get the lower right position + transform into panel space
  165. Vector lr, lrlocal;
  166. pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld );
  167. MatrixGetColumn( panelToWorld, 3, lr );
  168. VectorTransform( lr, worldToPanel, lrlocal );
  169. float flWidth = lrlocal.x;
  170. float flHeight = lrlocal.y;
  171. CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex );
  172. pScreen->ChangeTeam( GetTeamNumber() );
  173. pScreen->SetActualSize( flWidth, flHeight );
  174. pScreen->SetActive( true );
  175. pScreen->MakeVisibleOnlyToTeammates( false );
  176. int nScreen = m_hScreens.AddToTail( );
  177. m_hScreens[nScreen].Set( pScreen );
  178. }
  179. }
  180. void CPlantedC4::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
  181. {
  182. // Are we already marked for transmission?
  183. if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
  184. return;
  185. BaseClass::SetTransmit( pInfo, bAlways );
  186. // Force our screens to be sent too.
  187. for ( int i=0; i < m_hScreens.Count(); i++ )
  188. {
  189. CVGuiScreen *pScreen = m_hScreens[i].Get();
  190. pScreen->SetTransmit( pInfo, bAlways );
  191. }
  192. }
  193. CPlantedC4* CPlantedC4::ShootSatchelCharge( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles )
  194. {
  195. CPlantedC4 *pGrenade = dynamic_cast< CPlantedC4* >( CreateEntityByName( "planted_c4" ) );
  196. if ( pGrenade )
  197. {
  198. vecAngles[0] = 0;
  199. vecAngles[2] = 0;
  200. pGrenade->Init( pevOwner, vecStart, vecAngles );
  201. return pGrenade;
  202. }
  203. else
  204. {
  205. Warning( "Can't create planted_c4 entity!\n" );
  206. return NULL;
  207. }
  208. }
  209. void CPlantedC4::Init( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles )
  210. {
  211. SetMoveType( MOVETYPE_NONE );
  212. SetSolid( SOLID_NONE );
  213. SetModel( PLANTED_C4_MODEL ); // Change this to c4 model
  214. SetCollisionBounds( Vector( 0, 0, 0 ), Vector( 8, 8, 8 ) );
  215. SetAbsOrigin( vecStart );
  216. SetAbsAngles( vecAngles );
  217. SetOwnerEntity( pevOwner );
  218. //=============================================================================
  219. // HPE_BEGIN:
  220. // [tj] Set the planter when the bomb is planted.
  221. //=============================================================================
  222. SetPlanter( pevOwner );
  223. //=============================================================================
  224. // HPE_END
  225. //=============================================================================
  226. // Detonate in "time" seconds
  227. SetThink( &CPlantedC4::C4Think );
  228. SetNextThink( gpGlobals->curtime + 0.1f );
  229. m_flTimerLength = mp_c4timer.GetInt();
  230. m_flC4Blow = gpGlobals->curtime + m_flTimerLength;
  231. m_flNextDefuse = 0;
  232. m_bStartDefuse = false;
  233. m_bBombTicking = true;
  234. SetFriction( 0.9 );
  235. m_flDefuseLength = 0.0f;
  236. SpawnControlPanels();
  237. }
  238. void CPlantedC4::C4Think()
  239. {
  240. if (!IsInWorld())
  241. {
  242. UTIL_Remove( this );
  243. return;
  244. }
  245. //Bomb is dead, don't think anymore
  246. if( !m_bBombTicking )
  247. {
  248. SetThink( NULL );
  249. return;
  250. }
  251. SetNextThink( gpGlobals->curtime + 0.12 );
  252. #ifndef CLIENT_DLL
  253. // let the bots hear the bomb beeping
  254. // BOTPORT: Emit beep events at same time as client effects
  255. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_beep" );
  256. if( event )
  257. {
  258. event->SetInt( "entindex", entindex() );
  259. gameeventmanager->FireEvent( event );
  260. }
  261. #endif
  262. // IF the timer has expired ! blow this bomb up!
  263. if (m_flC4Blow <= gpGlobals->curtime)
  264. {
  265. // give the defuser credit for defusing the bomb
  266. CCSPlayer* pBombOwner = ToCSPlayer(GetOwnerEntity());
  267. if ( pBombOwner )
  268. {
  269. if (CSGameRules()->m_iRoundWinStatus == WINNER_NONE)
  270. pBombOwner->IncrementFragCount( 3 );
  271. }
  272. CSGameRules()->m_bBombDropped = false;
  273. trace_t tr;
  274. Vector vecSpot = GetAbsOrigin();
  275. vecSpot[2] += 8;
  276. UTIL_TraceLine( vecSpot, vecSpot + Vector ( 0, 0, -40 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  277. Explode( &tr, DMG_BLAST );
  278. CSGameRules()->m_bBombPlanted = false;
  279. CCS_GameStats.Event_BombExploded(pBombOwner);
  280. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_exploded" );
  281. if( event )
  282. {
  283. event->SetInt( "userid", pBombOwner?pBombOwner->GetUserID():-1 );
  284. event->SetInt( "site", m_iBombSiteIndex );
  285. event->SetInt( "priority", 9 );
  286. gameeventmanager->FireEvent( event );
  287. }
  288. // skip additional processing once the bomb has exploded
  289. return;
  290. }
  291. //if the defusing process has started
  292. if ((m_bStartDefuse == true) && (m_pBombDefuser != NULL))
  293. {
  294. //if the defusing process has not ended yet
  295. if ( m_flDefuseCountDown > gpGlobals->curtime)
  296. {
  297. int iOnGround = FBitSet( m_pBombDefuser->GetFlags(), FL_ONGROUND );
  298. //if the bomb defuser has stopped defusing the bomb
  299. if( m_flNextDefuse < gpGlobals->curtime || !iOnGround )
  300. {
  301. if ( !iOnGround && m_pBombDefuser->IsAlive() )
  302. ClientPrint( m_pBombDefuser, HUD_PRINTCENTER, "#C4_Defuse_Must_Be_On_Ground");
  303. // release the player from being frozen
  304. m_pBombDefuser->m_bIsDefusing = false;
  305. #ifndef CLIENT_DLL
  306. // tell the bots someone has aborted defusing
  307. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortdefuse" );
  308. if( event )
  309. {
  310. event->SetInt("userid", m_pBombDefuser->GetUserID() );
  311. event->SetInt( "priority", 6 );
  312. gameeventmanager->FireEvent( event );
  313. }
  314. #endif
  315. //cancel the progress bar
  316. m_pBombDefuser->SetProgressBarTime( 0 );
  317. m_pBombDefuser->OnCanceledDefuse();
  318. m_pBombDefuser = NULL;
  319. m_bStartDefuse = false;
  320. m_flDefuseCountDown = 0;
  321. m_flDefuseLength = 0; //force it to show completely defused
  322. }
  323. return;
  324. }
  325. //if the defuse process has ended, kill the c4
  326. if ( !m_pBombDefuser->IsDead() )
  327. {
  328. //=============================================================================
  329. // HPE_BEGIN
  330. // [dwenger] Stats update for bomb defusing
  331. //=============================================================================
  332. CCS_GameStats.Event_BombDefused( m_pBombDefuser );
  333. //=============================================================================
  334. // HPE_END
  335. //=============================================================================
  336. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_defused" );
  337. if( event )
  338. {
  339. event->SetInt("userid", m_pBombDefuser->GetUserID() );
  340. event->SetInt("site", m_iBombSiteIndex );
  341. event->SetInt( "priority", 9 );
  342. gameeventmanager->FireEvent( event );
  343. //=============================================================================
  344. // HPE_BEGIN
  345. // [dwenger] Server-side processing for defusing bombs
  346. //=============================================================================
  347. m_pBombDefuser->AwardAchievement(CSWinBombDefuse);
  348. float timeToDetonation = (m_flC4Blow - gpGlobals->curtime);
  349. if ((timeToDetonation > 0.0f) && (timeToDetonation <= AchievementConsts::BombDefuseCloseCall_MaxTimeRemaining))
  350. {
  351. // Give achievement for defusing with < 1 second before detonation
  352. m_pBombDefuser->AwardAchievement(CSBombDefuseCloseCall);
  353. }
  354. if ((timeToDetonation > 0.0f) && (m_pBombDefuser->HasDefuser()) && (timeToDetonation < AchievementConsts::BombDefuseNeededKit_MaxTime))
  355. {
  356. // Give achievement for defusing with a defuse kit when not having the kit would have taken too long
  357. m_pBombDefuser->AwardAchievement(CSDefuseAndNeededKit);
  358. }
  359. // [dwenger] Added for fun-fact support
  360. if ( m_pBombDefuser->PickedUpDefuser() )
  361. {
  362. // Defuser kit was picked up, so set the fun fact
  363. m_pBombDefuser->SetDefusedWithPickedUpKit(true);
  364. }
  365. //=============================================================================
  366. // HPE_END
  367. //=============================================================================
  368. }
  369. Vector soundPosition = m_pBombDefuser->GetAbsOrigin() + Vector( 0, 0, 5 );
  370. CPASAttenuationFilter filter( soundPosition );
  371. EmitSound( filter, entindex(), "c4.disarmfinish" );
  372. // The bomb has just been disarmed.. Check to see if the round should end now
  373. m_bBombTicking = false;
  374. // release the player from being frozen
  375. m_pBombDefuser->m_bIsDefusing = false;
  376. CSGameRules()->m_bBombDefused = true;
  377. //=============================================================================
  378. // HPE_BEGIN:
  379. // [menglish] Give the bomb defuser an mvp if they ended the round
  380. //=============================================================================
  381. bool roundWasAlreadyWon = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE);
  382. if(CSGameRules()->CheckWinConditions() && !roundWasAlreadyWon)
  383. {
  384. m_pBombDefuser->IncrementNumMVPs( CSMVP_BOMBDEFUSE );
  385. }
  386. //=============================================================================
  387. // HPE_END
  388. //=============================================================================
  389. // give the defuser credit for defusing the bomb
  390. m_pBombDefuser->IncrementFragCount( 3 );
  391. CSGameRules()->m_bBombDropped = false;
  392. CSGameRules()->m_bBombPlanted = false;
  393. // Clear their progress bar.
  394. m_pBombDefuser->SetProgressBarTime( 0 );
  395. m_pBombDefuser = NULL;
  396. m_bStartDefuse = false;
  397. m_flDefuseLength = 10;
  398. return;
  399. }
  400. //if it gets here then the previouse defuser has taken off or been killed
  401. #ifndef CLIENT_DLL
  402. // tell the bots someone has aborted defusing
  403. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortdefuse" );
  404. if ( event )
  405. {
  406. event->SetInt("userid", m_pBombDefuser->GetUserID() );
  407. event->SetInt( "priority", 6 );
  408. gameeventmanager->FireEvent( event );
  409. }
  410. #endif
  411. // release the player from being frozen
  412. m_pBombDefuser->m_bIsDefusing = false;
  413. m_bStartDefuse = false;
  414. m_pBombDefuser = NULL;
  415. }
  416. }
  417. // Regular explosions
  418. void CPlantedC4::Explode( trace_t *pTrace, int bitsDamageType )
  419. {
  420. // Check to see if the round is over after the bomb went off...
  421. CSGameRules()->m_bTargetBombed = true;
  422. m_bBombTicking = false;
  423. //=============================================================================
  424. // HPE_BEGIN:
  425. // [tj] Saving off this value so we can see if the detonation is what caused the round to end.
  426. //=============================================================================
  427. bool roundWasAlreadyWon = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE);
  428. //=============================================================================
  429. // HPE_END
  430. //=============================================================================
  431. bool bWin = CSGameRules()->CheckWinConditions();
  432. //=============================================================================
  433. // HPE_BEGIN
  434. //=============================================================================
  435. // [dwenger] Server-side processing for winning round by planting a bomb
  436. if (bWin)
  437. {
  438. CCSPlayer *pBombOwner = ToCSPlayer( GetOwnerEntity() );
  439. if ( pBombOwner )
  440. {
  441. pBombOwner->AwardAchievement(CSWinBombPlant);
  442. //[tj]more specific achievement for planting the bomb after recovering it.
  443. if (m_bPlantedAfterPickup)
  444. {
  445. pBombOwner->AwardAchievement(CSWinBombPlantAfterRecovery);
  446. }
  447. // [menglish] awarding mvp to bomb planter
  448. if (!roundWasAlreadyWon)
  449. {
  450. pBombOwner->IncrementNumMVPs( CSMVP_BOMBPLANT );
  451. }
  452. }
  453. }
  454. //=============================================================================
  455. // HPE_END
  456. //=============================================================================
  457. // Do the Damage
  458. float flBombRadius = 500;
  459. if ( g_pMapInfo )
  460. flBombRadius = g_pMapInfo->m_flBombRadius;
  461. // Output to the bomb target ent
  462. CBaseEntity *pTarget = NULL;
  463. variant_t emptyVariant;
  464. while ((pTarget = gEntList.FindEntityByClassname( pTarget, "func_bomb_target" )) != NULL)
  465. {
  466. //Adrian - But only to the one we want!
  467. if ( pTarget->entindex() != m_iBombSiteIndex )
  468. continue;
  469. pTarget->AcceptInput( "BombExplode", this, this, emptyVariant, 0 );
  470. break;
  471. }
  472. // Pull out of the wall a bit
  473. if ( pTrace->fraction != 1.0 )
  474. {
  475. SetAbsOrigin( pTrace->endpos + (pTrace->plane.normal * 0.6) );
  476. }
  477. {
  478. Vector pos = GetAbsOrigin() + Vector( 0,0,8 );
  479. // add an explosion TE so it affects clientside physics
  480. CPASFilter filter( pos );
  481. te->Explosion( filter, 0.0,
  482. &pos,
  483. g_sModelIndexFireball,
  484. 50.0,
  485. 25,
  486. TE_EXPLFLAG_NONE,
  487. flBombRadius * 3.5,
  488. 200 );
  489. }
  490. // Sound! for everyone
  491. CBroadcastRecipientFilter filter;
  492. EmitSound( filter, entindex(), "c4.explode" );
  493. // Decal!
  494. UTIL_DecalTrace( pTrace, "Scorch" );
  495. // Shake!
  496. UTIL_ScreenShake( pTrace->endpos, 25.0, 150.0, 1.0, 3000, SHAKE_START );
  497. SetOwnerEntity( NULL ); // can't traceline attack owner if this is set
  498. CSGameRules()->RadiusDamage(
  499. CTakeDamageInfo( this, GetOwnerEntity(), flBombRadius, bitsDamageType ),
  500. GetAbsOrigin(),
  501. flBombRadius * 3.5, //Matt - don't ask me, this is how CS does it.
  502. CLASS_NONE,
  503. true ); // IGNORE THE WORLD!!
  504. // send director message, that something important happed here
  505. /*
  506. MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR );
  507. WRITE_BYTE ( 9 ); // command length in bytes
  508. WRITE_BYTE ( DRC_CMD_EVENT ); // bomb explode
  509. WRITE_SHORT( ENTINDEX(this->edict()) ); // index number of primary entity
  510. WRITE_SHORT( 0 ); // index number of secondary entity
  511. WRITE_LONG( 15 | DRC_FLAG_FINAL ); // eventflags (priority and flags)
  512. MESSAGE_END();
  513. */
  514. }
  515. // For CTs to defuse the c4
  516. void CPlantedC4::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  517. {
  518. //Can't defuse if its already defused or if it has blown up
  519. if( !m_bBombTicking )
  520. {
  521. SetUse( NULL );
  522. return;
  523. }
  524. CCSPlayer *player = dynamic_cast< CCSPlayer* >( pActivator );
  525. if ( !player || player->GetTeamNumber() != TEAM_CT )
  526. return;
  527. if ( m_bStartDefuse )
  528. {
  529. if ( player != m_pBombDefuser )
  530. {
  531. if ( player->m_iNextTimeCheck < gpGlobals->curtime )
  532. {
  533. ClientPrint( player, HUD_PRINTCENTER, "#Bomb_Already_Being_Defused" );
  534. player->m_iNextTimeCheck = gpGlobals->curtime + 1;
  535. }
  536. return;
  537. }
  538. m_flNextDefuse = gpGlobals->curtime + 0.5;
  539. }
  540. else
  541. {
  542. // freeze the player in place while defusing
  543. IGameEvent * event = gameeventmanager->CreateEvent("bomb_begindefuse" );
  544. if( event )
  545. {
  546. event->SetInt( "userid", player->GetUserID() );
  547. if ( player->HasDefuser() )
  548. {
  549. event->SetInt( "haskit", 1 );
  550. // TODO show messages on clients on event
  551. ClientPrint( player, HUD_PRINTCENTER, "#Defusing_Bomb_With_Defuse_Kit" );
  552. }
  553. else
  554. {
  555. event->SetInt( "haskit", 0 );
  556. // TODO show messages on clients on event
  557. ClientPrint( player, HUD_PRINTCENTER, "#Defusing_Bomb_Without_Defuse_Kit" );
  558. }
  559. event->SetInt( "priority", 8 );
  560. gameeventmanager->FireEvent( event );
  561. }
  562. Vector soundPosition = player->GetAbsOrigin() + Vector( 0, 0, 5 );
  563. CPASAttenuationFilter filter( soundPosition );
  564. EmitSound( filter, entindex(), "c4.disarmstart" );
  565. m_flDefuseLength = player->HasDefuser() ? 5 : 10;
  566. m_flNextDefuse = gpGlobals->curtime + 0.5;
  567. m_pBombDefuser = player;
  568. m_bStartDefuse = TRUE;
  569. player->m_bIsDefusing = true;
  570. m_flDefuseCountDown = gpGlobals->curtime + m_flDefuseLength;
  571. //start the progress bar
  572. player->SetProgressBarTime( m_flDefuseLength );
  573. player->OnStartedDefuse();
  574. }
  575. }
  576. #endif
  577. // -------------------------------------------------------------------------------- //
  578. // Tables.
  579. // -------------------------------------------------------------------------------- //
  580. IMPLEMENT_NETWORKCLASS_ALIASED( C4, DT_WeaponC4 )
  581. BEGIN_NETWORK_TABLE( CC4, DT_WeaponC4 )
  582. #ifdef CLIENT_DLL
  583. RecvPropBool( RECVINFO( m_bStartedArming ) ),
  584. RecvPropBool( RECVINFO( m_bBombPlacedAnimation ) ),
  585. RecvPropFloat( RECVINFO( m_fArmedTime ) )
  586. #else
  587. SendPropBool( SENDINFO( m_bStartedArming ) ),
  588. SendPropBool( SENDINFO( m_bBombPlacedAnimation ) ),
  589. SendPropFloat( SENDINFO( m_fArmedTime ), 0, SPROP_NOSCALE )
  590. #endif
  591. END_NETWORK_TABLE()
  592. #if defined CLIENT_DLL
  593. BEGIN_PREDICTION_DATA( CC4 )
  594. DEFINE_PRED_FIELD( m_bStartedArming, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  595. DEFINE_PRED_FIELD( m_bBombPlacedAnimation, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  596. DEFINE_PRED_FIELD( m_fArmedTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE )
  597. END_PREDICTION_DATA()
  598. #endif
  599. LINK_ENTITY_TO_CLASS( weapon_c4, CC4 );
  600. PRECACHE_WEAPON_REGISTER( weapon_c4 );
  601. // -------------------------------------------------------------------------------- //
  602. // Globals.
  603. // -------------------------------------------------------------------------------- //
  604. CUtlVector< CC4* > g_C4s;
  605. // -------------------------------------------------------------------------------- //
  606. // CC4 implementation.
  607. // -------------------------------------------------------------------------------- //
  608. CC4::CC4()
  609. {
  610. g_C4s.AddToTail( this );
  611. m_bDroppedFromDeath = false;
  612. #if defined( CLIENT_DLL )
  613. m_szScreenText[0] = '\0';
  614. #endif
  615. }
  616. CC4::~CC4()
  617. {
  618. g_C4s.FindAndRemove( this );
  619. }
  620. void CC4::Spawn()
  621. {
  622. BaseClass::Spawn();
  623. //Don't allow players to shoot the C4 around
  624. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  625. //Don't be damaged / moved by explosions
  626. m_takedamage = DAMAGE_NO;
  627. m_bBombPlanted = false;
  628. }
  629. void CC4::ItemPostFrame()
  630. {
  631. CCSPlayer *pPlayer = GetPlayerOwner();
  632. if ( !pPlayer )
  633. return;
  634. // Disable all the firing code.. the C4 grenade is all custom.
  635. if ( pPlayer->m_nButtons & IN_ATTACK )
  636. {
  637. PrimaryAttack();
  638. }
  639. else
  640. {
  641. WeaponIdle();
  642. }
  643. }
  644. #if defined( CLIENT_DLL )
  645. bool CC4::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
  646. {
  647. if( event == 7001 )
  648. {
  649. //set the screen text to the string in 'options'
  650. Q_strncpy( m_szScreenText, options, 16 );
  651. return true;
  652. }
  653. return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
  654. }
  655. char *CC4::GetScreenText( void )
  656. {
  657. if( m_bStartedArming )
  658. return m_szScreenText;
  659. else
  660. return "";
  661. }
  662. #endif //CLIENT_DLL
  663. #ifdef GAME_DLL
  664. unsigned int CC4::PhysicsSolidMaskForEntity( void ) const
  665. {
  666. return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_PLAYERCLIP;
  667. }
  668. void CC4::Precache()
  669. {
  670. PrecacheVGuiScreen( "c4_view_panel" );
  671. PrecacheScriptSound( "c4.disarmfinish" );
  672. PrecacheScriptSound( "c4.explode" );
  673. PrecacheScriptSound( "c4.disarmstart" );
  674. PrecacheScriptSound( "c4.plant" );
  675. PrecacheScriptSound( "C4.PlantSound" );
  676. BaseClass::Precache();
  677. }
  678. //-----------------------------------------------------------------------------
  679. // Purpose: Gets info about the control panels
  680. //-----------------------------------------------------------------------------
  681. void CC4::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
  682. {
  683. pPanelName = "c4_view_panel";
  684. }
  685. bool CC4::Holster( CBaseCombatWeapon *pSwitchingTo )
  686. {
  687. CCSPlayer *pPlayer = GetPlayerOwner();
  688. if ( pPlayer )
  689. pPlayer->SetProgressBarTime( 0 );
  690. if ( m_bStartedArming )
  691. {
  692. AbortBombPlant();
  693. }
  694. return BaseClass::Holster( pSwitchingTo );
  695. }
  696. bool CC4::ShouldRemoveOnRoundRestart()
  697. {
  698. // Doesn't matter if we have an owner or not.. always remove the C4 when the round restarts.
  699. // The gamerules will give another C4 to some lucky player.
  700. CCSPlayer *pPlayer = GetPlayerOwner();
  701. if ( pPlayer && pPlayer->GetActiveWeapon() == this )
  702. engine->ClientCommand( pPlayer->edict(), "lastinv reset\n" );
  703. return true;
  704. }
  705. #endif
  706. void CC4::PrimaryAttack()
  707. {
  708. bool bArmingTimeSatisfied = false;
  709. CCSPlayer *pPlayer = GetPlayerOwner();
  710. if ( !pPlayer )
  711. return;
  712. int onGround = FBitSet( pPlayer->GetFlags(), FL_ONGROUND );
  713. CBaseEntity *groundEntity = (onGround) ? pPlayer->GetGroundEntity() : NULL;
  714. if ( groundEntity )
  715. {
  716. // Don't let us stand on players, breakables, or pushaway physics objects to plant
  717. if ( groundEntity->IsPlayer() ||
  718. IsPushableEntity( groundEntity ) ||
  719. #ifndef CLIENT_DLL
  720. IsBreakableEntity( groundEntity ) ||
  721. #endif // !CLIENT_DLL
  722. IsPushAwayEntity( groundEntity ) )
  723. {
  724. onGround = false;
  725. }
  726. }
  727. if( m_bStartedArming == false && m_bBombPlanted == false )
  728. {
  729. if( pPlayer->m_bInBombZone && onGround )
  730. {
  731. m_bStartedArming = true;
  732. m_fArmedTime = gpGlobals->curtime + WEAPON_C4_ARM_TIME;
  733. m_bBombPlacedAnimation = false;
  734. #if !defined( CLIENT_DLL )
  735. // init the beep flags
  736. int i;
  737. for( i=0;i<NUM_BEEPS;i++ )
  738. m_bPlayedArmingBeeps[i] = false;
  739. // freeze the player in place while planting
  740. // player "arming bomb" animation
  741. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  742. pPlayer->SetNextAttack( gpGlobals->curtime );
  743. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_beginplant" );
  744. if( event )
  745. {
  746. event->SetInt("userid", pPlayer->GetUserID() );
  747. event->SetInt("site", pPlayer->m_iBombSiteIndex );
  748. event->SetInt( "priority", 8 );
  749. gameeventmanager->FireEvent( event );
  750. }
  751. #endif
  752. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  753. FX_PlantBomb( pPlayer->entindex(), pPlayer->Weapon_ShootPosition(), PLANTBOMB_PLANT );
  754. }
  755. else
  756. {
  757. if ( !pPlayer->m_bInBombZone )
  758. {
  759. ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Plant_At_Bomb_Spot");
  760. }
  761. else
  762. {
  763. ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Plant_Must_Be_On_Ground");
  764. }
  765. m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  766. return;
  767. }
  768. }
  769. else
  770. {
  771. if ( !onGround || !pPlayer->m_bInBombZone )
  772. {
  773. if( !pPlayer->m_bInBombZone )
  774. {
  775. ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Arming_Cancelled" );
  776. }
  777. else
  778. {
  779. ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Plant_Must_Be_On_Ground" );
  780. }
  781. AbortBombPlant();
  782. if(m_bBombPlacedAnimation == true) //this means the placement animation is canceled
  783. {
  784. SendWeaponAnim( ACT_VM_DRAW );
  785. }
  786. else
  787. {
  788. SendWeaponAnim( ACT_VM_IDLE );
  789. }
  790. return;
  791. }
  792. else
  793. {
  794. #ifndef CLIENT_DLL
  795. PlayArmingBeeps();
  796. #endif
  797. if( gpGlobals->curtime >= m_fArmedTime ) //the c4 is ready to be armed
  798. {
  799. //check to make sure the player is still in the bomb target area
  800. bArmingTimeSatisfied = true;
  801. }
  802. else if( ( gpGlobals->curtime >= (m_fArmedTime - 0.75) ) && ( !m_bBombPlacedAnimation ) )
  803. {
  804. //call the c4 Placement animation
  805. m_bBombPlacedAnimation = true;
  806. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  807. #if !defined( CLIENT_DLL )
  808. // player "place" animation
  809. //pPlayer->SetAnimation( PLAYER_HOLDBOMB );
  810. #endif
  811. }
  812. }
  813. }
  814. if ( bArmingTimeSatisfied && m_bStartedArming )
  815. {
  816. m_bStartedArming = false;
  817. m_fArmedTime = 0;
  818. if( pPlayer->m_bInBombZone )
  819. {
  820. #if !defined( CLIENT_DLL )
  821. CPlantedC4 *pC4 = CPlantedC4::ShootSatchelCharge( pPlayer, pPlayer->GetAbsOrigin(), pPlayer->GetAbsAngles() );
  822. if ( pC4 )
  823. {
  824. pC4->SetBombSiteIndex( pPlayer->m_iBombSiteIndex );
  825. trace_t tr;
  826. UTIL_TraceEntity( pC4, GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,-200), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  827. pC4->SetAbsOrigin( tr.endpos );
  828. CBombTarget *pBombTarget = (CBombTarget*)UTIL_EntityByIndex( pPlayer->m_iBombSiteIndex );
  829. if ( pBombTarget )
  830. {
  831. CBaseEntity *pAttachPoint = gEntList.FindEntityByName( NULL, pBombTarget->GetBombMountTarget() );
  832. if ( pAttachPoint )
  833. {
  834. pC4->SetAbsOrigin( pAttachPoint->GetAbsOrigin() );
  835. pC4->SetAbsAngles( pAttachPoint->GetAbsAngles() );
  836. pC4->SetParent( pAttachPoint );
  837. }
  838. variant_t emptyVariant;
  839. pBombTarget->AcceptInput( "BombPlanted", pC4, pC4, emptyVariant, 0 );
  840. }
  841. // [tj] If the bomb is planted by someone that picked it up after the
  842. // original owner was killed, pass that along to the planted bomb
  843. pC4->SetPlantedAfterPickup( m_bDroppedFromDeath );
  844. }
  845. //=============================================================================
  846. // HPE_BEGIN
  847. // [dwenger] Stats update for bomb planting
  848. //=============================================================================
  849. // Determine how elapsed time from start of round until the bomb was planted
  850. float plantingTime = gpGlobals->curtime - CSGameRules()->GetRoundStartTime();
  851. // Award achievement to bomb planter if time <= 25 seconds
  852. if ((plantingTime > 0.0f) && (plantingTime <= AchievementConsts::FastBombPlant_Time))
  853. {
  854. pPlayer->AwardAchievement(CSPlantBombWithin25Seconds);
  855. }
  856. CCS_GameStats.Event_BombPlanted( pPlayer );
  857. //=============================================================================
  858. // HPE_END
  859. //=============================================================================
  860. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_planted" );
  861. if( event )
  862. {
  863. event->SetInt("userid", pPlayer->GetUserID() );
  864. event->SetInt("site", pPlayer->m_iBombSiteIndex );
  865. event->SetInt("posx", pPlayer->GetAbsOrigin().x );
  866. event->SetInt("posy", pPlayer->GetAbsOrigin().y );
  867. event->SetInt( "priority", 8 );
  868. gameeventmanager->FireEvent( event );
  869. }
  870. // Fire a beep event also so the bots have a chance to hear the bomb
  871. event = gameeventmanager->CreateEvent( "bomb_beep" );
  872. if ( event )
  873. {
  874. event->SetInt( "entindex", entindex() );
  875. gameeventmanager->FireEvent( event );
  876. }
  877. pPlayer->SetProgressBarTime( 0 );
  878. CSGameRules()->m_bBombDropped = false;
  879. CSGameRules()->m_bBombPlanted = true;
  880. // Play the plant sound.
  881. Vector plantPosition = pPlayer->GetAbsOrigin() + Vector( 0, 0, 5 );
  882. CPASAttenuationFilter filter( plantPosition );
  883. EmitSound( filter, entindex(), "c4.plant" );
  884. // No more c4!
  885. pPlayer->Weapon_Drop( this, NULL, NULL );
  886. UTIL_Remove( this );
  887. #endif
  888. //don't allow the planting to start over again next frame.
  889. m_bBombPlanted = true;
  890. return;
  891. }
  892. else
  893. {
  894. ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Activated_At_Bomb_Spot" );
  895. #if !defined( CLIENT_DLL )
  896. //pPlayer->SetAnimation( PLAYER_HOLDBOMB );
  897. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortplant" );
  898. if( event )
  899. {
  900. event->SetInt("userid", pPlayer->GetUserID() );
  901. event->SetInt("site", pPlayer->m_iBombSiteIndex );
  902. event->SetInt( "priority", 8 );
  903. gameeventmanager->FireEvent( event );
  904. }
  905. #endif
  906. m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  907. return;
  908. }
  909. }
  910. m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
  911. SetWeaponIdleTime( gpGlobals->curtime + SharedRandomFloat("C4IdleTime", 10, 15 ) );
  912. }
  913. void CC4::WeaponIdle()
  914. {
  915. // if the player releases the attack button cancel the arming sequence
  916. if ( m_bStartedArming )
  917. {
  918. AbortBombPlant();
  919. CCSPlayer *pPlayer = GetPlayerOwner();
  920. // TODO: make this use SendWeaponAnim and activities when the C4 has the activities hooked up.
  921. if ( pPlayer )
  922. {
  923. SendWeaponAnim( ACT_VM_IDLE );
  924. pPlayer->SetNextAttack( gpGlobals->curtime );
  925. }
  926. if(m_bBombPlacedAnimation == true) //this means the placement animation is canceled
  927. SendWeaponAnim( ACT_VM_DRAW );
  928. else
  929. SendWeaponAnim( ACT_VM_IDLE );
  930. }
  931. }
  932. void CC4::UpdateShieldState( void )
  933. {
  934. //ADRIANTODO
  935. CCSPlayer *pPlayer = GetPlayerOwner();
  936. if ( !pPlayer )
  937. return;
  938. if ( pPlayer->HasShield() )
  939. {
  940. pPlayer->SetShieldDrawnState( false );
  941. CBaseViewModel *pVM = pPlayer->GetViewModel( 1 );
  942. if ( pVM )
  943. {
  944. pVM->AddEffects( EF_NODRAW );
  945. }
  946. //pPlayer->SetHitBoxSet( 3 );
  947. }
  948. else
  949. BaseClass::UpdateShieldState();
  950. }
  951. int m_iBeepFrames[NUM_BEEPS] = { 27, 37, 45, 51, 57, 63, 67 };
  952. int iNumArmingAnimFrames = 83;
  953. void CC4::PlayArmingBeeps( void )
  954. {
  955. float flStartTime = m_fArmedTime - WEAPON_C4_ARM_TIME;
  956. float flProgress = ( gpGlobals->curtime - flStartTime ) / ( WEAPON_C4_ARM_TIME - 0.75 );
  957. int currentFrame = (int)( (float)iNumArmingAnimFrames * flProgress );
  958. int i;
  959. for( i=0;i<NUM_BEEPS;i++ )
  960. {
  961. if( currentFrame <= m_iBeepFrames[i] )
  962. {
  963. break;
  964. }
  965. else if( !m_bPlayedArmingBeeps[i] )
  966. {
  967. m_bPlayedArmingBeeps[i] = true;
  968. CCSPlayer *owner = GetPlayerOwner();
  969. Vector soundPosition = owner->GetAbsOrigin() + Vector( 0, 0, 5 );
  970. CPASAttenuationFilter filter( soundPosition );
  971. filter.RemoveRecipient( owner );
  972. // remove anyone that is first person spec'ing the planter
  973. int i;
  974. CBasePlayer *pPlayer;
  975. for( i=1;i<=gpGlobals->maxClients;i++ )
  976. {
  977. pPlayer = UTIL_PlayerByIndex( i );
  978. if ( !pPlayer )
  979. continue;
  980. if( pPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pPlayer->GetObserverTarget() == GetOwner() )
  981. {
  982. filter.RemoveRecipient( pPlayer );
  983. }
  984. }
  985. EmitSound(filter, entindex(), "c4.click");
  986. break;
  987. }
  988. }
  989. }
  990. float CC4::GetMaxSpeed() const
  991. {
  992. if ( m_bStartedArming )
  993. return CS_PLAYER_SPEED_STOPPED;
  994. else
  995. return BaseClass::GetMaxSpeed();
  996. }
  997. void CC4::OnPickedUp( CBaseCombatCharacter *pNewOwner )
  998. {
  999. BaseClass::OnPickedUp( pNewOwner );
  1000. #if !defined( CLIENT_DLL )
  1001. CCSPlayer *pPlayer = dynamic_cast<CCSPlayer *>( pNewOwner );
  1002. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_pickup" );
  1003. if ( event )
  1004. {
  1005. event->SetInt( "userid", pPlayer->GetUserID() );
  1006. event->SetInt( "priority", 6 );
  1007. gameeventmanager->FireEvent( event );
  1008. }
  1009. if ( pPlayer->m_bShowHints && !(pPlayer->m_iDisplayHistoryBits & DHF_BOMB_RETRIEVED) )
  1010. {
  1011. pPlayer->m_iDisplayHistoryBits |= DHF_BOMB_RETRIEVED;
  1012. pPlayer->HintMessage( "#Hint_you_have_the_bomb", false );
  1013. }
  1014. else
  1015. {
  1016. ClientPrint( pPlayer, HUD_PRINTCENTER, "#Got_bomb" );
  1017. }
  1018. pPlayer->SetBombPickupTime(gpGlobals->curtime);
  1019. #endif
  1020. }
  1021. // HACK - Ask Mike Booth...
  1022. #ifndef CLIENT_DLL
  1023. #include "cs_bot.h"
  1024. #endif
  1025. // memdbgon must be the last include file in a .cpp file!!!
  1026. #include "tier0/memdbgon.h"
  1027. void CC4::Drop( const Vector &vecVelocity )
  1028. {
  1029. #if !defined( CLIENT_DLL )
  1030. if ( !CSGameRules()->m_bBombPlanted ) // its not dropped if its planted
  1031. {
  1032. // tell the bots about the dropped bomb
  1033. TheCSBots()->SetLooseBomb( this );
  1034. CBasePlayer *pPlayer = dynamic_cast<CBasePlayer *>(GetOwnerEntity());
  1035. Assert( pPlayer );
  1036. if ( pPlayer )
  1037. {
  1038. IGameEvent * event = gameeventmanager->CreateEvent("bomb_dropped" );
  1039. if ( event )
  1040. {
  1041. event->SetInt( "userid", pPlayer->GetUserID() );
  1042. event->SetInt( "priority", 6 );
  1043. gameeventmanager->FireEvent( event );
  1044. }
  1045. }
  1046. }
  1047. #endif
  1048. if ( m_bStartedArming )
  1049. AbortBombPlant(); // stop arming sequence
  1050. BaseClass::Drop( vecVelocity );
  1051. }
  1052. void CC4::AbortBombPlant()
  1053. {
  1054. m_bStartedArming = false;
  1055. CCSPlayer *pPlayer = GetPlayerOwner();
  1056. if ( !pPlayer )
  1057. return;
  1058. #if !defined( CLIENT_DLL )
  1059. m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  1060. pPlayer->SetProgressBarTime( 0 );
  1061. IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortplant" );
  1062. if( event )
  1063. {
  1064. event->SetInt("userid", pPlayer->GetUserID() );
  1065. event->SetInt("site", pPlayer->m_iBombSiteIndex );
  1066. event->SetInt( "priority", 8 );
  1067. gameeventmanager->FireEvent( event );
  1068. }
  1069. #endif
  1070. FX_PlantBomb( pPlayer->entindex(), pPlayer->Weapon_ShootPosition(), PLANTBOMB_ABORT );
  1071. }