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.

619 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //
  4. //=============================================================================
  5. #include "cbase.h"
  6. #include "tf_weapon_flaregun.h"
  7. #include "tf_fx_shared.h"
  8. #include "in_buttons.h"
  9. // Client specific.
  10. #ifdef CLIENT_DLL
  11. #include "c_tf_player.h"
  12. #include "soundenvelope.h"
  13. // Server specific.
  14. #else
  15. #include "tf_gamestats.h"
  16. #include "tf_player.h"
  17. #endif
  18. //=============================================================================
  19. //
  20. // Weapon Flare Gun tables.
  21. //
  22. IMPLEMENT_NETWORKCLASS_ALIASED( TFFlareGun, DT_WeaponFlareGun )
  23. BEGIN_NETWORK_TABLE( CTFFlareGun, DT_WeaponFlareGun )
  24. #ifdef CLIENT_DLL
  25. RecvPropFloat( RECVINFO( m_flChargeBeginTime ) ),
  26. #else
  27. SendPropFloat( SENDINFO( m_flChargeBeginTime ) ),
  28. #endif
  29. END_NETWORK_TABLE()
  30. BEGIN_PREDICTION_DATA( CTFFlareGun )
  31. END_PREDICTION_DATA()
  32. LINK_ENTITY_TO_CLASS( tf_weapon_flaregun, CTFFlareGun );
  33. PRECACHE_WEAPON_REGISTER( tf_weapon_flaregun );
  34. // Server specific.
  35. #ifndef CLIENT_DLL
  36. BEGIN_DATADESC( CTFFlareGun )
  37. END_DATADESC()
  38. #endif
  39. //============================
  40. IMPLEMENT_NETWORKCLASS_ALIASED( TFFlareGun_Revenge, DT_WeaponFlareGun_Revenge )
  41. BEGIN_NETWORK_TABLE( CTFFlareGun_Revenge, DT_WeaponFlareGun_Revenge )
  42. #ifdef CLIENT_DLL
  43. RecvPropFloat( RECVINFO( m_fLastExtinguishTime ) ),
  44. #else
  45. SendPropFloat( SENDINFO( m_fLastExtinguishTime ) ),
  46. #endif
  47. END_NETWORK_TABLE()
  48. BEGIN_PREDICTION_DATA( CTFFlareGun_Revenge )
  49. END_PREDICTION_DATA()
  50. LINK_ENTITY_TO_CLASS( tf_weapon_flaregun_revenge, CTFFlareGun_Revenge );
  51. PRECACHE_WEAPON_REGISTER( tf_weapon_flaregun_revenge );
  52. //=============================================================================
  53. //
  54. // Weapon Flare Gun functions.
  55. //
  56. //-----------------------------------------------------------------------------
  57. // Purpose:
  58. //-----------------------------------------------------------------------------
  59. CTFFlareGun::CTFFlareGun()
  60. {
  61. m_bEffectsThinking = false;
  62. m_flLastDenySoundTime = 0.0f;
  63. #ifdef CLIENT_DLL
  64. m_bReadyToFire = false;
  65. #endif
  66. StopCharge();
  67. }
  68. CTFFlareGun::~CTFFlareGun()
  69. {
  70. DestroySounds();
  71. }
  72. //-----------------------------------------------------------------------------
  73. // Purpose:
  74. //-----------------------------------------------------------------------------
  75. void CTFFlareGun::Precache()
  76. {
  77. BaseClass::Precache();
  78. PrecacheParticleSystem( "stickybombtrail_blue" );
  79. PrecacheParticleSystem( "stickybombtrail_red" );
  80. PrecacheParticleSystem( "critical_grenade_blue" );
  81. PrecacheParticleSystem( "critical_grenade_red" );
  82. }
  83. void CTFFlareGun::DestroySounds( void )
  84. {
  85. StopCharge();
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Purpose:
  89. //-----------------------------------------------------------------------------
  90. void CTFFlareGun::PrimaryAttack( void )
  91. {
  92. // Get the player owning the weapon.
  93. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  94. if ( !pOwner )
  95. return;
  96. if ( m_flChargeBeginTime > 0.0f )
  97. return;
  98. // Don't attack if we're underwater
  99. if ( pOwner->GetWaterLevel() != WL_Eyes )
  100. {
  101. BaseClass::PrimaryAttack();
  102. }
  103. else
  104. {
  105. if ( gpGlobals->curtime > m_flLastDenySoundTime )
  106. {
  107. WeaponSound( SPECIAL2 );
  108. m_flLastDenySoundTime = gpGlobals->curtime + 1.0f;
  109. }
  110. }
  111. #ifdef CLIENT_DLL
  112. m_bReadyToFire = false;
  113. #endif
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Purpose: Detonate flare
  117. //-----------------------------------------------------------------------------
  118. void CTFFlareGun::SecondaryAttack( void )
  119. {
  120. if ( GetFlareGunType() != FLAREGUN_DETONATE )
  121. return;
  122. if ( !CanAttack() )
  123. return;
  124. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  125. if ( !pPlayer )
  126. return;
  127. #ifdef GAME_DLL
  128. if ( m_iFlareCount )
  129. {
  130. int iCount = m_Flares.Count();
  131. for ( int i = 0; i < iCount; i++ )
  132. {
  133. CTFProjectile_Flare *pTemp = m_Flares[i];
  134. if ( pTemp )
  135. {
  136. pTemp->Detonate();
  137. }
  138. }
  139. }
  140. #endif // GAME_DLL
  141. }
  142. #ifdef GAME_DLL
  143. //-----------------------------------------------------------------------------
  144. // Purpose:
  145. //-----------------------------------------------------------------------------
  146. void CTFFlareGun::AddFlare( CTFProjectile_Flare *pFlare )
  147. {
  148. FlareHandle hHandle;
  149. hHandle = pFlare;
  150. m_Flares.AddToTail( hHandle );
  151. m_iFlareCount = m_Flares.Count();
  152. }
  153. //-----------------------------------------------------------------------------
  154. // Purpose:
  155. //-----------------------------------------------------------------------------
  156. void CTFFlareGun::DeathNotice( CBaseEntity *pVictim )
  157. {
  158. Assert( dynamic_cast<CTFProjectile_Flare*>( pVictim ) );
  159. FlareHandle hHandle;
  160. hHandle = (CTFProjectile_Flare*)pVictim;
  161. m_Flares.FindAndRemove( hHandle );
  162. m_iFlareCount = m_Flares.Count();
  163. }
  164. #endif
  165. bool CTFFlareGun::Holster( CBaseCombatWeapon *pSwitchingTo )
  166. {
  167. #ifdef CLIENT_DLL
  168. m_bEffectsThinking = false;
  169. StopCharge();
  170. m_bReadyToFire = false;
  171. #endif
  172. return BaseClass::Holster( pSwitchingTo );
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Purpose:
  176. //-----------------------------------------------------------------------------
  177. bool CTFFlareGun::Deploy( void )
  178. {
  179. #ifdef CLIENT_DLL
  180. m_bEffectsThinking = true;
  181. SetContextThink( &CTFFlareGun::ClientEffectsThink, gpGlobals->curtime + 0.25f, "EFFECTS_THINK" );
  182. m_bReadyToFire = false;
  183. #endif
  184. return BaseClass::Deploy();
  185. }
  186. void CTFFlareGun::WeaponReset( void )
  187. {
  188. BaseClass::WeaponReset();
  189. #if defined( CLIENT_DLL )
  190. StopCharge();
  191. #endif
  192. }
  193. void CTFFlareGun::ItemPostFrame( void )
  194. {
  195. BaseClass::ItemPostFrame();
  196. if ( m_flChargeBeginTime > 0.0f )
  197. {
  198. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  199. if ( !pPlayer )
  200. return;
  201. // If we're not holding down the attack button
  202. if ( !(pPlayer->m_nButtons & IN_ATTACK2) )
  203. {
  204. StopCharge();
  205. }
  206. else
  207. {
  208. ChargePostFrame();
  209. }
  210. }
  211. #ifdef CLIENT_DLL
  212. if ( !m_bEffectsThinking )
  213. {
  214. m_bEffectsThinking = true;
  215. SetContextThink( &CTFFlareGun::ClientEffectsThink, gpGlobals->curtime + 0.25f, "EFFECTS_THINK" );
  216. }
  217. #endif
  218. }
  219. #ifdef CLIENT_DLL
  220. void CTFFlareGun::DispatchMuzzleFlash( const char* effectName, C_BaseEntity* pAttachEnt )
  221. {
  222. DispatchParticleEffect( effectName, PATTACH_POINT_FOLLOW, pAttachEnt, "muzzle", GetParticleColor( 1 ), GetParticleColor( 2 ) );
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose:
  226. //-----------------------------------------------------------------------------
  227. void CTFFlareGun::ClientEffectsThink( void )
  228. {
  229. CTFPlayer *pPlayer = GetTFPlayerOwner();
  230. if ( !pPlayer )
  231. return;
  232. if ( !pPlayer->IsLocalPlayer() )
  233. return;
  234. if ( !pPlayer->GetViewModel() )
  235. return;
  236. if ( !m_bEffectsThinking )
  237. return;
  238. if ( !GetOwner() || GetOwner()->GetActiveWeapon() != this )
  239. {
  240. m_bEffectsThinking = false;
  241. }
  242. else
  243. {
  244. SetContextThink( &CTFFlareGun::ClientEffectsThink, gpGlobals->curtime + 0.25f, "EFFECTS_THINK" );
  245. }
  246. if ( GetFlareGunType() == FLAREGUN_GRORDBORT && m_flNextPrimaryAttack <= gpGlobals->curtime )
  247. {
  248. ParticleProp()->Init( this );
  249. CNewParticleEffect* pEffect = ParticleProp()->Create( "drg_bison_idle", PATTACH_POINT_FOLLOW, "muzzle" );
  250. if ( pEffect )
  251. {
  252. pEffect->SetControlPoint( CUSTOM_COLOR_CP1, GetParticleColor( 1 ) );
  253. pEffect->SetControlPoint( CUSTOM_COLOR_CP2, GetParticleColor( 2 ) );
  254. }
  255. ParticleProp()->Create( "drg_manmelter_idle", PATTACH_POINT_FOLLOW, "muzzle" );
  256. if ( !m_bReadyToFire )
  257. {
  258. m_bReadyToFire = true;
  259. EmitSound( "Weapon_SniperRailgun.NonScoped" );
  260. }
  261. }
  262. }
  263. void CTFFlareGun::StartChargeEffects()
  264. {
  265. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  266. if ( pPlayer )
  267. {
  268. DispatchParticleEffect( GetChargeEffect(), PATTACH_POINT_FOLLOW, GetAppropriateWorldOrViewModel(), "muzzle", GetParticleColor( 1 ), GetParticleColor( 2 ) );
  269. }
  270. }
  271. void CTFFlareGun::StopChargeEffects()
  272. {
  273. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  274. if ( pPlayer )
  275. {
  276. GetAppropriateWorldOrViewModel()->ParticleProp()->StopParticlesNamed( GetChargeEffect(), false );
  277. }
  278. }
  279. #endif
  280. void CTFFlareGun::StartCharge( void )
  281. {
  282. StartChargeStartTime();
  283. #ifdef CLIENT_DLL
  284. if ( !m_pChargeLoop )
  285. {
  286. CLocalPlayerFilter filter;
  287. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  288. m_pChargeLoop = controller.SoundCreate( filter, entindex(), GetShootSound( WPN_DOUBLE ) );
  289. controller.Play( m_pChargeLoop, 1.0, 100 );
  290. }
  291. StartChargeEffects();
  292. #endif
  293. }
  294. void CTFFlareGun::StopCharge( void )
  295. {
  296. m_flChargeBeginTime = 0.0f;
  297. #ifdef CLIENT_DLL
  298. if ( m_pChargeLoop )
  299. {
  300. CSoundEnvelopeController::GetController().SoundDestroy( m_pChargeLoop );
  301. }
  302. m_pChargeLoop = NULL;
  303. StopChargeEffects();
  304. #endif
  305. }
  306. CTFFlareGun_Revenge::CTFFlareGun_Revenge()
  307. {
  308. m_fLastExtinguishTime = 0.0f;
  309. #ifdef CLIENT_DLL
  310. m_nOldRevengeCrits = 0;
  311. #endif
  312. }
  313. void CTFFlareGun_Revenge::Precache()
  314. {
  315. BaseClass::Precache();
  316. PrecacheParticleSystem( "drg_manmelter_vacuum" );
  317. PrecacheParticleSystem( "drg_manmelter_vacuum_flames" );
  318. PrecacheParticleSystem( "drg_manmelter_muzzleflash" );
  319. }
  320. int CTFFlareGun_Revenge::GetCustomDamageType() const
  321. {
  322. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  323. if ( pOwner )
  324. {
  325. int iRevengeCrits = pOwner->m_Shared.GetRevengeCrits();
  326. return iRevengeCrits > 0 ? TF_DMG_CUSTOM_SHOTGUN_REVENGE_CRIT : TF_DMG_CUSTOM_NONE;
  327. }
  328. return TF_DMG_CUSTOM_NONE;
  329. }
  330. bool CTFFlareGun_Revenge::Holster( CBaseCombatWeapon *pSwitchingTo )
  331. {
  332. #ifdef GAME_DLL
  333. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  334. if ( pOwner && pOwner->m_Shared.GetRevengeCrits() )
  335. {
  336. pOwner->m_Shared.RemoveCond( TF_COND_CRITBOOSTED );
  337. }
  338. #endif
  339. StopCharge();
  340. return BaseClass::Holster( pSwitchingTo );
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Purpose:
  344. //-----------------------------------------------------------------------------
  345. bool CTFFlareGun_Revenge::Deploy( void )
  346. {
  347. #ifdef GAME_DLL
  348. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  349. if ( pOwner && pOwner->m_Shared.GetRevengeCrits() )
  350. {
  351. pOwner->m_Shared.AddCond( TF_COND_CRITBOOSTED );
  352. }
  353. #endif
  354. StopCharge();
  355. return BaseClass::Deploy();
  356. }
  357. #ifdef GAME_DLL
  358. //-----------------------------------------------------------------------------
  359. // Purpose: Reset revenge crits when the flaregun is changed
  360. //-----------------------------------------------------------------------------
  361. void CTFFlareGun_Revenge::Detach( void )
  362. {
  363. CTFPlayer *pPlayer = GetTFPlayerOwner();
  364. if ( pPlayer )
  365. {
  366. pPlayer->m_Shared.SetRevengeCrits( 0 );
  367. pPlayer->m_Shared.RemoveCond( TF_COND_CRITBOOSTED );
  368. }
  369. BaseClass::Detach();
  370. }
  371. #endif
  372. //-----------------------------------------------------------------------------
  373. // Purpose:
  374. //-----------------------------------------------------------------------------
  375. int CTFFlareGun_Revenge::GetCount( void )
  376. {
  377. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  378. if ( pOwner )
  379. {
  380. return pOwner->m_Shared.GetRevengeCrits();
  381. }
  382. return 0;
  383. }
  384. void CTFFlareGun_Revenge::PrimaryAttack()
  385. {
  386. if ( !CanAttack() )
  387. return;
  388. BaseClass::PrimaryAttack();
  389. // Lower the reveng crit count
  390. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  391. if ( pOwner )
  392. {
  393. int iNewRevengeCrits = MAX( pOwner->m_Shared.GetRevengeCrits() - 1, 0 );
  394. pOwner->m_Shared.SetRevengeCrits( iNewRevengeCrits );
  395. }
  396. }
  397. void CTFFlareGun_Revenge::SecondaryAttack( void )
  398. {
  399. if ( m_flNextSecondaryAttack > gpGlobals->curtime )
  400. {
  401. return;
  402. }
  403. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  404. if ( !pOwner )
  405. return;
  406. if ( GetChargeBeginTime() == 0.0f )
  407. {
  408. StartCharge();
  409. #ifdef GAME_DLL
  410. //SendWeaponAnim( ACT_VM_PULLBACK );
  411. #endif
  412. }
  413. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
  414. }
  415. void CTFFlareGun_Revenge::ChargePostFrame( void )
  416. {
  417. BaseClass::ChargePostFrame();
  418. if ( gpGlobals->curtime > m_fLastExtinguishTime + 0.5f )
  419. {
  420. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  421. if ( pOwner )
  422. {
  423. // Extinguish friends
  424. Vector vecEye = pOwner->EyePosition();
  425. Vector vecForward, vecRight, vecUp;
  426. AngleVectors( pOwner->EyeAngles(), &vecForward, NULL, NULL );
  427. const Vector vHull = Vector( 16.0f, 16.0f, 16.0f );
  428. trace_t tr;
  429. UTIL_TraceHull( vecEye, vecEye + vecForward * 256.0f, -vHull, vHull, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
  430. CTFPlayer *pTarget = ToTFPlayer( tr.m_pEnt );
  431. if ( pTarget )
  432. {
  433. #ifdef GAME_DLL
  434. // Get the player that ignited them before we extinguish
  435. CTFPlayer *pBurner = pTarget->m_Shared.GetOriginalBurnAttacker();
  436. #endif
  437. if ( ExtinguishPlayerInternal( pTarget, pOwner ) )
  438. {
  439. m_fLastExtinguishTime = gpGlobals->curtime;
  440. #ifdef GAME_DLL
  441. // Make sure the team isn't burning themselves to earn crits
  442. if ( pBurner && pBurner->GetTeamNumber() != pOwner->GetTeamNumber() )
  443. {
  444. // Grant revenge crits
  445. pOwner->m_Shared.SetRevengeCrits( pOwner->m_Shared.GetRevengeCrits() + 1 );
  446. // Return health to the Pyro.
  447. int iRestoreHealthOnExtinguish = 0;
  448. CALL_ATTRIB_HOOK_INT( iRestoreHealthOnExtinguish, extinguish_restores_health );
  449. if ( iRestoreHealthOnExtinguish > 0 && pOwner->TakeHealth( 20, DMG_GENERIC ) > 0 )
  450. {
  451. IGameEvent *healevent = gameeventmanager->CreateEvent( "player_healonhit" );
  452. if ( healevent )
  453. {
  454. healevent->SetInt( "amount", iRestoreHealthOnExtinguish );
  455. healevent->SetInt( "entindex", pOwner->entindex() );
  456. item_definition_index_t healingItemDef = INVALID_ITEM_DEF_INDEX;
  457. if ( GetAttributeContainer() && GetAttributeContainer()->GetItem() )
  458. {
  459. healingItemDef = GetAttributeContainer()->GetItem()->GetItemDefIndex();
  460. }
  461. healevent->SetInt( "weapon_def_index", healingItemDef );
  462. gameeventmanager->FireEvent( healevent );
  463. }
  464. }
  465. }
  466. #endif
  467. }
  468. }
  469. }
  470. }
  471. }
  472. #ifdef GAME_DLL
  473. extern void ExtinguishPlayer( CEconEntity *pExtinguisher, CTFPlayer *pOwner, CTFPlayer *pTarget, const char *pExtinguisherName );
  474. #endif // GAME_DLL
  475. bool CTFFlareGun_Revenge::ExtinguishPlayerInternal( CTFPlayer *pTarget, CTFPlayer *pOwner )
  476. {
  477. if ( pTarget->GetTeamNumber() == pOwner->GetTeamNumber() )
  478. {
  479. if ( pTarget->m_Shared.InCond( TF_COND_BURNING ) )
  480. {
  481. #ifdef GAME_DLL
  482. ExtinguishPlayer( this, pOwner, pTarget, GetName() );
  483. #endif // GAME_DLL
  484. return true;
  485. }
  486. }
  487. return false;
  488. }
  489. #ifdef CLIENT_DLL
  490. void CTFFlareGun_Revenge::OnDataChanged( DataUpdateType_t type )
  491. {
  492. BaseClass::OnDataChanged( type );
  493. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  494. if ( pOwner )
  495. {
  496. if ( m_nOldRevengeCrits < pOwner->m_Shared.GetRevengeCrits() )
  497. {
  498. DoAbsorbEffect();
  499. }
  500. m_nOldRevengeCrits = pOwner->m_Shared.GetRevengeCrits();
  501. }
  502. }
  503. void CTFFlareGun_Revenge::DoAbsorbEffect( void )
  504. {
  505. WeaponSound( SPECIAL1 );
  506. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  507. if ( pPlayer )
  508. {
  509. DispatchParticleEffect( "drg_manmelter_vacuum_flames", PATTACH_POINT_FOLLOW, GetAppropriateWorldOrViewModel(), "muzzle", GetParticleColor( 1 ), GetParticleColor( 2 ) );
  510. }
  511. }
  512. #endif