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.

2271 lines
61 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: TF Sniper Rifle
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "tf_fx_shared.h"
  8. #include "tf_weapon_sniperrifle.h"
  9. #include "in_buttons.h"
  10. #include "tf_gamerules.h"
  11. // Client specific.
  12. #ifdef CLIENT_DLL
  13. #include "view.h"
  14. #include "beamdraw.h"
  15. #include "vgui/ISurface.h"
  16. #include <vgui/ILocalize.h>
  17. #include "vgui_controls/Controls.h"
  18. #include "hud_crosshair.h"
  19. #include "functionproxy.h"
  20. #include "materialsystem/imaterialvar.h"
  21. #include "toolframework_client.h"
  22. #include "input.h"
  23. #include "client_virtualreality.h"
  24. #include "sourcevr/isourcevirtualreality.h"
  25. // forward declarations
  26. void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );
  27. #else
  28. #include "tf_gamerules.h"
  29. #include "tf_fx.h"
  30. #endif
  31. #define TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC 50.0
  32. #define TF_WEAPON_SNIPERRIFLE_UNCHARGE_PER_SEC 75.0
  33. #define TF_WEAPON_SNIPERRIFLE_DAMAGE_MIN 50
  34. #define TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX 150
  35. #define TF_WEAPON_SNIPERRIFLE_RELOAD_TIME 1.5f
  36. #define TF_WEAPON_SNIPERRIFLE_ZOOM_TIME 0.3f
  37. #define TF_WEAPON_SNIPERRIFLE_NO_CRIT_AFTER_ZOOM_TIME 0.2f
  38. #define SNIPER_DOT_SPRITE_RED "effects/sniperdot_red.vmt"
  39. #define SNIPER_DOT_SPRITE_BLUE "effects/sniperdot_blue.vmt"
  40. #define SNIPER_CHARGE_BEAM_RED "tfc_sniper_charge_red"
  41. #define SNIPER_CHARGE_BEAM_BLUE "tfc_sniper_charge_blue"
  42. #ifdef CLIENT_DLL
  43. ConVar tf_sniper_fullcharge_bell( "tf_sniper_fullcharge_bell", "0", FCVAR_ARCHIVE );
  44. #endif
  45. //=============================================================================
  46. //
  47. // Weapon Sniper Rifles tables.
  48. //
  49. IMPLEMENT_NETWORKCLASS_ALIASED( TFSniperRifle, DT_TFSniperRifle )
  50. BEGIN_NETWORK_TABLE_NOBASE( CTFSniperRifle, DT_SniperRifleLocalData )
  51. #if !defined( CLIENT_DLL )
  52. SendPropFloat( SENDINFO(m_flChargedDamage), 0, SPROP_NOSCALE | SPROP_CHANGES_OFTEN ),
  53. #else
  54. RecvPropFloat( RECVINFO(m_flChargedDamage) ),
  55. #endif
  56. END_NETWORK_TABLE()
  57. BEGIN_NETWORK_TABLE( CTFSniperRifle, DT_TFSniperRifle )
  58. #if !defined( CLIENT_DLL )
  59. SendPropDataTable( "SniperRifleLocalData", 0, &REFERENCE_SEND_TABLE( DT_SniperRifleLocalData ), SendProxy_SendLocalWeaponDataTable ),
  60. #else
  61. RecvPropDataTable( "SniperRifleLocalData", 0, 0, &REFERENCE_RECV_TABLE( DT_SniperRifleLocalData ) ),
  62. #endif
  63. END_NETWORK_TABLE()
  64. BEGIN_PREDICTION_DATA( CTFSniperRifle )
  65. #ifdef CLIENT_DLL
  66. DEFINE_PRED_FIELD( m_flUnzoomTime, FIELD_FLOAT, 0 ),
  67. DEFINE_PRED_FIELD( m_flRezoomTime, FIELD_FLOAT, 0 ),
  68. DEFINE_PRED_FIELD( m_bRezoomAfterShot, FIELD_BOOLEAN, 0 ),
  69. DEFINE_PRED_FIELD( m_flChargedDamage, FIELD_FLOAT, 0 ),
  70. #endif
  71. END_PREDICTION_DATA()
  72. LINK_ENTITY_TO_CLASS( tf_weapon_sniperrifle, CTFSniperRifle );
  73. PRECACHE_WEAPON_REGISTER( tf_weapon_sniperrifle );
  74. BEGIN_DATADESC( CTFSniperRifle )
  75. DEFINE_FIELD( m_flUnzoomTime, FIELD_FLOAT ),
  76. DEFINE_FIELD( m_flRezoomTime, FIELD_FLOAT ),
  77. DEFINE_FIELD( m_bRezoomAfterShot, FIELD_BOOLEAN ),
  78. DEFINE_FIELD( m_flChargedDamage, FIELD_FLOAT ),
  79. END_DATADESC()
  80. //=============================================================================
  81. IMPLEMENT_NETWORKCLASS_ALIASED( TFSniperRifleDecap, DT_TFSniperRifleDecap )
  82. BEGIN_NETWORK_TABLE( CTFSniperRifleDecap, DT_TFSniperRifleDecap )
  83. END_NETWORK_TABLE()
  84. BEGIN_PREDICTION_DATA( CTFSniperRifleDecap )
  85. END_PREDICTION_DATA()
  86. LINK_ENTITY_TO_CLASS( tf_weapon_sniperrifle_decap, CTFSniperRifleDecap );
  87. PRECACHE_WEAPON_REGISTER( tf_weapon_sniperrifle_decap );
  88. //=============================================================================
  89. IMPLEMENT_NETWORKCLASS_ALIASED( TFSniperRifleClassic, DT_TFSniperRifleClassic )
  90. BEGIN_NETWORK_TABLE( CTFSniperRifleClassic, DT_TFSniperRifleClassic )
  91. #if !defined( CLIENT_DLL )
  92. SendPropBool( SENDINFO(m_bCharging) ),
  93. #else
  94. RecvPropBool( RECVINFO(m_bCharging) ),
  95. #endif
  96. END_NETWORK_TABLE()
  97. BEGIN_PREDICTION_DATA( CTFSniperRifleClassic )
  98. #ifdef CLIENT_DLL
  99. DEFINE_PRED_FIELD( m_bCharging, FIELD_BOOLEAN, 0 ),
  100. #endif
  101. END_PREDICTION_DATA()
  102. LINK_ENTITY_TO_CLASS( tf_weapon_sniperrifle_classic, CTFSniperRifleClassic );
  103. PRECACHE_WEAPON_REGISTER( tf_weapon_sniperrifle_classic );
  104. //=============================================================================
  105. #ifdef STAGING_ONLY
  106. IMPLEMENT_NETWORKCLASS_ALIASED( TFSniperRifleRevolver, DT_TFSniperRifleRevolver )
  107. BEGIN_NETWORK_TABLE( CTFSniperRifleRevolver, DT_TFSniperRifleRevolver )
  108. END_NETWORK_TABLE()
  109. BEGIN_PREDICTION_DATA( CTFSniperRifleRevolver )
  110. END_PREDICTION_DATA()
  111. LINK_ENTITY_TO_CLASS( tf_weapon_sniperrifle_revolver, CTFSniperRifleRevolver );
  112. PRECACHE_WEAPON_REGISTER( tf_weapon_sniperrifle_revolver );
  113. #endif // STAGING_ONLY
  114. //=============================================================================
  115. //
  116. // Weapon Sniper Rifles functions.
  117. //
  118. //-----------------------------------------------------------------------------
  119. // Purpose: Constructor.
  120. //-----------------------------------------------------------------------------
  121. CTFSniperRifle::CTFSniperRifle()
  122. {
  123. // Server specific.
  124. #ifdef GAME_DLL
  125. m_hSniperDot = NULL;
  126. #else
  127. m_bPlayedBell = false;
  128. #endif
  129. m_bCurrentShotIsHeadshot = false;
  130. m_flChargedDamage = 0.0f;
  131. m_flChargePerSec = TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC;
  132. m_bWasAimedAtEnemy = false;
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Purpose: Destructor.
  136. //-----------------------------------------------------------------------------
  137. CTFSniperRifle::~CTFSniperRifle()
  138. {
  139. // Server specific.
  140. #ifdef GAME_DLL
  141. DestroySniperDot();
  142. #endif
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Purpose:
  146. //-----------------------------------------------------------------------------
  147. void CTFSniperRifle::Spawn()
  148. {
  149. m_iAltFireHint = HINT_ALTFIRE_SNIPERRIFLE;
  150. BaseClass::Spawn();
  151. ResetTimers();
  152. }
  153. //-----------------------------------------------------------------------------
  154. // Purpose:
  155. //-----------------------------------------------------------------------------
  156. void CTFSniperRifle::Precache()
  157. {
  158. BaseClass::Precache();
  159. PrecacheModel( SNIPER_DOT_SPRITE_RED );
  160. PrecacheModel( SNIPER_DOT_SPRITE_BLUE );
  161. PrecacheScriptSound( "doomsday.warhead" );
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose:
  165. //-----------------------------------------------------------------------------
  166. void CTFSniperRifle::ResetTimers( void )
  167. {
  168. m_flUnzoomTime = -1;
  169. m_flRezoomTime = -1;
  170. m_bRezoomAfterShot = false;
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Purpose:
  174. //-----------------------------------------------------------------------------
  175. bool CTFSniperRifle::Reload( void )
  176. {
  177. // We currently don't reload.
  178. return true;
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose:
  182. //-----------------------------------------------------------------------------
  183. bool CTFSniperRifle::CanHolster( void ) const
  184. {
  185. CTFPlayer *pPlayer = GetTFPlayerOwner();
  186. if ( pPlayer )
  187. {
  188. // TF_COND_MELEE_ONLY need to be able to immediately holster and switch to melee weapon
  189. if ( pPlayer->m_Shared.InCond( TF_COND_MELEE_ONLY ) )
  190. return true;
  191. // don't allow us to holster this weapon if we're in the process of zooming and
  192. // we've just fired the weapon (next primary attack is only 1.5 seconds after firing)
  193. if ( ( pPlayer->GetFOV() < pPlayer->GetDefaultFOV() ) && ( m_flNextPrimaryAttack > gpGlobals->curtime ) )
  194. return false;
  195. }
  196. return BaseClass::CanHolster();
  197. }
  198. //-----------------------------------------------------------------------------
  199. // Purpose:
  200. //-----------------------------------------------------------------------------
  201. bool CTFSniperRifle::Holster( CBaseCombatWeapon *pSwitchingTo )
  202. {
  203. // Server specific.
  204. #ifdef GAME_DLL
  205. // Destroy the sniper dot.
  206. DestroySniperDot();
  207. #endif
  208. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  209. if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
  210. {
  211. ZoomOut();
  212. }
  213. m_flChargedDamage = 0.0f;
  214. #ifdef CLIENT_DLL
  215. m_bPlayedBell = false;
  216. #endif
  217. ResetTimers();
  218. return BaseClass::Holster( pSwitchingTo );
  219. }
  220. void CTFSniperRifle::WeaponReset( void )
  221. {
  222. BaseClass::WeaponReset();
  223. ZoomOut();
  224. m_bCurrentShotIsHeadshot = false;
  225. m_flChargePerSec = TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC;
  226. }
  227. //-----------------------------------------------------------------------------
  228. // Purpose:
  229. //-----------------------------------------------------------------------------
  230. bool CTFSniperRifle::OwnerCanJump( void )
  231. {
  232. return gpGlobals->curtime > m_flUnzoomTime;
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Purpose:
  236. //-----------------------------------------------------------------------------
  237. void CTFSniperRifle::HandleZooms( void )
  238. {
  239. // Get the owning player.
  240. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  241. if ( !pPlayer )
  242. return;
  243. // Handle the zoom when taunting.
  244. if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
  245. {
  246. if ( pPlayer->m_Shared.InCond( TF_COND_AIMING ) )
  247. {
  248. ToggleZoom();
  249. }
  250. //Don't rezoom in the middle of a taunt.
  251. ResetTimers();
  252. }
  253. if ( m_flUnzoomTime > 0 && gpGlobals->curtime > m_flUnzoomTime )
  254. {
  255. if ( m_bRezoomAfterShot )
  256. {
  257. ZoomOutIn();
  258. m_bRezoomAfterShot = false;
  259. }
  260. else
  261. {
  262. ZoomOut();
  263. }
  264. m_flUnzoomTime = -1;
  265. }
  266. if ( m_flRezoomTime > 0 )
  267. {
  268. if ( gpGlobals->curtime > m_flRezoomTime )
  269. {
  270. ZoomIn();
  271. m_flRezoomTime = -1;
  272. }
  273. }
  274. if ( ( pPlayer->m_nButtons & IN_ATTACK2 ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
  275. {
  276. // If we're in the process of rezooming, just cancel it
  277. if ( m_flRezoomTime > 0 || m_flUnzoomTime > 0 )
  278. {
  279. // Prevent them from rezooming in less time than they would have
  280. m_flNextSecondaryAttack = m_flRezoomTime + TF_WEAPON_SNIPERRIFLE_ZOOM_TIME;
  281. m_flRezoomTime = -1;
  282. }
  283. else
  284. {
  285. Zoom();
  286. }
  287. }
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Purpose:
  291. //-----------------------------------------------------------------------------
  292. void CTFSniperRifle::ItemPostFrame( void )
  293. {
  294. // If we're lowered, we're not allowed to fire
  295. if ( m_bLowered )
  296. return;
  297. // Get the owning player.
  298. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  299. if ( !pPlayer )
  300. return;
  301. if ( !CanAttack() )
  302. {
  303. if ( IsZoomed() )
  304. {
  305. ToggleZoom();
  306. }
  307. return;
  308. }
  309. HandleZooms();
  310. #ifdef GAME_DLL
  311. // Update the sniper dot position if we have one
  312. if ( m_hSniperDot )
  313. {
  314. UpdateSniperDot();
  315. }
  316. #endif
  317. // Start charging when we're zoomed in, and allowed to fire
  318. if ( pPlayer->m_Shared.IsJumping() )
  319. {
  320. // Unzoom if we're jumping
  321. if ( IsZoomed() )
  322. {
  323. ToggleZoom();
  324. }
  325. m_flChargedDamage = 0.0f;
  326. m_bRezoomAfterShot = false;
  327. m_flRezoomTime = -1.f;
  328. }
  329. else if ( m_flNextSecondaryAttack <= gpGlobals->curtime )
  330. {
  331. // Don't start charging in the time just after a shot before we unzoom to play rack anim.
  332. if ( pPlayer->m_Shared.InCond( TF_COND_AIMING ) && !m_bRezoomAfterShot )
  333. {
  334. float fSniperRifleChargePerSec = m_flChargePerSec;
  335. ApplyChargeSpeedModifications( fSniperRifleChargePerSec );
  336. fSniperRifleChargePerSec += SniperRifleChargeRateMod();
  337. // we don't want sniper charge rate to go too high.
  338. fSniperRifleChargePerSec = clamp( fSniperRifleChargePerSec, 0, 2.f * TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC );
  339. m_flChargedDamage = MIN( m_flChargedDamage + gpGlobals->frametime * fSniperRifleChargePerSec, TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX );
  340. #ifdef CLIENT_DLL
  341. // play the recharged bell if we're fully charged
  342. if ( IsFullyCharged() && !m_bPlayedBell )
  343. {
  344. m_bPlayedBell = true;
  345. if ( tf_sniper_fullcharge_bell.GetBool() )
  346. {
  347. C_TFPlayer::GetLocalTFPlayer()->EmitSound( "TFPlayer.ReCharged" );
  348. }
  349. }
  350. #endif
  351. }
  352. else
  353. {
  354. m_flChargedDamage = MAX( 0, m_flChargedDamage - gpGlobals->frametime * TF_WEAPON_SNIPERRIFLE_UNCHARGE_PER_SEC );
  355. }
  356. }
  357. // Fire.
  358. if ( pPlayer->m_nButtons & IN_ATTACK )
  359. {
  360. Fire( pPlayer );
  361. }
  362. // Idle.
  363. if ( !( ( pPlayer->m_nButtons & IN_ATTACK) || ( pPlayer->m_nButtons & IN_ATTACK2 ) ) )
  364. {
  365. // No fire buttons down or reloading
  366. if ( !ReloadOrSwitchWeapons() && ( m_bInReload == false ) )
  367. {
  368. WeaponIdle();
  369. }
  370. }
  371. // Sniper Rage (Hitman's heatmaker)
  372. // Activate on 'R'
  373. // no longer need full charge
  374. if ( (pPlayer->m_nButtons & IN_RELOAD ) && pPlayer->m_Shared.GetRageMeter() > 1.0f )
  375. {
  376. int iBuffType = 0;
  377. CALL_ATTRIB_HOOK_INT( iBuffType, set_buff_type );
  378. if ( iBuffType > 0 )
  379. {
  380. pPlayer->m_Shared.ActivateRageBuff( pPlayer, iBuffType );
  381. }
  382. }
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose:
  386. //-----------------------------------------------------------------------------
  387. void CTFSniperRifle::PlayWeaponShootSound( void )
  388. {
  389. if ( TFGameRules()->GameModeUsesUpgrades() )
  390. {
  391. PlayUpgradedShootSound( "Weapon_Upgrade.DamageBonus" );
  392. }
  393. if ( !IsFullyCharged() )
  394. {
  395. float flDamageBonus = 1.0f;
  396. CALL_ATTRIB_HOOK_FLOAT( flDamageBonus, sniper_full_charge_damage_bonus );
  397. if ( flDamageBonus > 1.0f )
  398. {
  399. WeaponSound( SPECIAL3 );
  400. return;
  401. }
  402. }
  403. BaseClass::PlayWeaponShootSound();
  404. }
  405. //-----------------------------------------------------------------------------
  406. // Purpose:
  407. //-----------------------------------------------------------------------------
  408. bool CTFSniperRifle::Lower( void )
  409. {
  410. if ( BaseClass::Lower() )
  411. {
  412. if ( IsZoomed() )
  413. {
  414. ToggleZoom();
  415. }
  416. return true;
  417. }
  418. return false;
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Purpose: Secondary attack.
  422. //-----------------------------------------------------------------------------
  423. void CTFSniperRifle::Zoom( void )
  424. {
  425. // Don't allow the player to zoom in while jumping
  426. CTFPlayer *pPlayer = GetTFPlayerOwner();
  427. if ( pPlayer && pPlayer->m_Shared.IsJumping() )
  428. {
  429. if ( pPlayer->GetFOV() >= 75 )
  430. return;
  431. }
  432. ToggleZoom();
  433. // at least 0.1 seconds from now, but don't stomp a previous value
  434. m_flNextPrimaryAttack = MAX( m_flNextPrimaryAttack, gpGlobals->curtime + 0.1 );
  435. m_flNextSecondaryAttack = gpGlobals->curtime + TF_WEAPON_SNIPERRIFLE_ZOOM_TIME;
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose:
  439. //-----------------------------------------------------------------------------
  440. void CTFSniperRifle::ZoomOutIn( void )
  441. {
  442. ZoomOut();
  443. CTFPlayer *pPlayer = GetTFPlayerOwner();
  444. if ( pPlayer && pPlayer->ShouldAutoRezoom() )
  445. {
  446. float flRezoomDelay = 0.9f;
  447. if ( !UsesClipsForAmmo1() )
  448. {
  449. // Since sniper rifles don't actually use clips the fast reload hook also affects unzoom and zoom delays
  450. ApplyScopeSpeedModifications( flRezoomDelay );
  451. }
  452. m_flRezoomTime = gpGlobals->curtime + flRezoomDelay;
  453. }
  454. else
  455. {
  456. m_flNextSecondaryAttack = gpGlobals->curtime + 1.0f;
  457. }
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Purpose:
  461. //-----------------------------------------------------------------------------
  462. void CTFSniperRifle::ZoomIn( void )
  463. {
  464. // Start aiming.
  465. CTFPlayer *pPlayer = GetTFPlayerOwner();
  466. if ( !pPlayer )
  467. return;
  468. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
  469. return;
  470. BaseClass::ZoomIn();
  471. pPlayer->m_Shared.AddCond( TF_COND_AIMING );
  472. pPlayer->TeamFortress_SetSpeed();
  473. #ifdef GAME_DLL
  474. // Create the sniper dot.
  475. CreateSniperDot();
  476. pPlayer->ClearExpression();
  477. #endif
  478. }
  479. //-----------------------------------------------------------------------------
  480. bool CTFSniperRifle::IsZoomed( void )
  481. {
  482. CTFPlayer *pPlayer = GetTFPlayerOwner();
  483. if ( pPlayer )
  484. {
  485. return pPlayer->m_Shared.InCond( TF_COND_ZOOMED );
  486. }
  487. return false;
  488. }
  489. //-----------------------------------------------------------------------------
  490. //
  491. // Have we been zoomed in long enough for our shot to do max damage
  492. //
  493. bool CTFSniperRifle::IsFullyCharged( void ) const
  494. {
  495. return m_flChargedDamage >= TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX;
  496. }
  497. //-----------------------------------------------------------------------------
  498. // Purpose:
  499. //-----------------------------------------------------------------------------
  500. void CTFSniperRifle::ZoomOut( void )
  501. {
  502. BaseClass::ZoomOut();
  503. // Stop aiming
  504. CTFPlayer *pPlayer = GetTFPlayerOwner();
  505. if ( !pPlayer )
  506. return;
  507. pPlayer->m_Shared.RemoveCond( TF_COND_AIMING );
  508. pPlayer->TeamFortress_SetSpeed();
  509. #ifdef GAME_DLL
  510. // Destroy the sniper dot.
  511. DestroySniperDot();
  512. pPlayer->ClearExpression();
  513. #endif
  514. // if we are thinking about zooming, cancel it
  515. m_flUnzoomTime = -1;
  516. m_flRezoomTime = -1;
  517. m_bRezoomAfterShot = false;
  518. m_flChargedDamage = 0.0f;
  519. #ifdef CLIENT_DLL
  520. m_bPlayedBell = false;
  521. #endif
  522. }
  523. //-----------------------------------------------------------------------------
  524. // Purpose:
  525. //-----------------------------------------------------------------------------
  526. void CTFSniperRifle::ApplyScopeSpeedModifications( float &flBaseRef )
  527. {
  528. CALL_ATTRIB_HOOK_FLOAT( flBaseRef, fast_reload );
  529. // Prototype hack
  530. CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
  531. if ( pPlayer )
  532. {
  533. if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_HASTE || pPlayer->m_Shared.GetCarryingRuneType() == RUNE_PRECISION )
  534. {
  535. flBaseRef *= 0.5f;
  536. }
  537. else if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_KING || pPlayer->m_Shared.InCond( TF_COND_KING_BUFFED ) )
  538. {
  539. flBaseRef *= 0.75f;
  540. }
  541. int iMaster = 0;
  542. CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iMaster, ability_master_sniper );
  543. if ( iMaster )
  544. {
  545. flBaseRef *= RemapValClamped( iMaster, 1, 2, 0.6f, 0.3f );
  546. }
  547. }
  548. }
  549. //-----------------------------------------------------------------------------
  550. // Purpose:
  551. //-----------------------------------------------------------------------------
  552. void CTFSniperRifle::ApplyChargeSpeedModifications( float &flBaseRef )
  553. {
  554. CALL_ATTRIB_HOOK_FLOAT( flBaseRef, mult_sniper_charge_per_sec );
  555. CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
  556. if ( pPlayer )
  557. {
  558. Vector vForward;
  559. AngleVectors( pPlayer->EyeAngles() + pPlayer->GetPunchAngle(), &vForward );
  560. Vector vShootPos = pPlayer->Weapon_ShootPosition();
  561. trace_t tr;
  562. UTIL_TraceLine( vShootPos, vShootPos + vForward * m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flRange, MASK_BLOCKLOS_AND_NPCS, pPlayer, COLLISION_GROUP_NONE, &tr );
  563. CTFPlayer *pTarget = ToTFPlayer( tr.m_pEnt );
  564. if ( pTarget && pTarget->IsAlive() && pTarget->GetTeamNumber() != pPlayer->GetTeamNumber() &&
  565. !( pTarget->m_Shared.IsStealthed() && !pTarget->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ) )
  566. {
  567. CALL_ATTRIB_HOOK_FLOAT( flBaseRef, mult_sniper_charge_per_sec_with_enemy_under_crosshair );
  568. int nBeep = 0;
  569. CALL_ATTRIB_HOOK_FLOAT( nBeep, sniper_beep_with_enemy_under_crosshair );
  570. if ( nBeep > 0 && !m_bWasAimedAtEnemy )
  571. {
  572. pPlayer->EmitSound( "doomsday.warhead" );
  573. }
  574. m_bWasAimedAtEnemy = true;
  575. }
  576. else
  577. {
  578. m_bWasAimedAtEnemy = false;
  579. }
  580. if ( pPlayer && ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_PRECISION || pPlayer->m_Shared.GetCarryingRuneType() == RUNE_HASTE ) )
  581. {
  582. flBaseRef *= 3.0f;
  583. }
  584. else if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_KING || pPlayer->m_Shared.InCond( TF_COND_KING_BUFFED ) )
  585. {
  586. flBaseRef *= 1.5f;
  587. }
  588. // Prototype hack
  589. int iMaster = 0;
  590. CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iMaster, ability_master_sniper );
  591. if ( iMaster )
  592. {
  593. flBaseRef *= RemapValClamped( iMaster, 1, 2, 1.5f, 3.f );
  594. }
  595. }
  596. }
  597. //-----------------------------------------------------------------------------
  598. // Purpose:
  599. //-----------------------------------------------------------------------------
  600. bool CTFSniperRifle::MustBeZoomedToFire( void )
  601. {
  602. int iModOnlyFireZoomed = 0;
  603. CALL_ATTRIB_HOOK_INT( iModOnlyFireZoomed, sniper_only_fire_zoomed );
  604. return ( iModOnlyFireZoomed != 0 );
  605. }
  606. //-----------------------------------------------------------------------------
  607. // Purpose:
  608. //-----------------------------------------------------------------------------
  609. void CTFSniperRifle::HandleNoScopeFireDeny( void )
  610. {
  611. if ( m_flNextEmptySoundTime < gpGlobals->curtime )
  612. {
  613. WeaponSound( SPECIAL2 );
  614. #ifdef CLIENT_DLL
  615. ParticleProp()->Init( this );
  616. ParticleProp()->Create( "dxhr_sniper_fizzle", PATTACH_POINT_FOLLOW, "muzzle" );
  617. #endif
  618. m_flNextEmptySoundTime = gpGlobals->curtime + 0.5;
  619. }
  620. }
  621. //-----------------------------------------------------------------------------
  622. // Purpose:
  623. //-----------------------------------------------------------------------------
  624. ETFDmgCustom CTFSniperRifle::GetPenetrateType() const
  625. {
  626. if ( IsFullyCharged() )
  627. {
  628. int iPenetrate = 0;
  629. CALL_ATTRIB_HOOK_INT( iPenetrate, sniper_penetrate_players_when_charged );
  630. if ( iPenetrate > 0 )
  631. return TF_DMG_CUSTOM_PENETRATE_ALL_PLAYERS;
  632. }
  633. return BaseClass::GetPenetrateType();
  634. }
  635. #ifdef SIXENSE
  636. //-----------------------------------------------------------------------------
  637. // Purpose:
  638. //-----------------------------------------------------------------------------
  639. float CTFSniperRifle::GetRezoomTime() const
  640. {
  641. return m_flRezoomTime;
  642. }
  643. #endif
  644. //-----------------------------------------------------------------------------
  645. // Purpose:
  646. //-----------------------------------------------------------------------------
  647. void CTFSniperRifle::Fire( CTFPlayer *pPlayer )
  648. {
  649. // Check the ammo. We don't use clip ammo, check the primary ammo type.
  650. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
  651. {
  652. HandleFireOnEmpty();
  653. return;
  654. }
  655. // Some weapons can only fire while zoomed
  656. if ( MustBeZoomedToFire() )
  657. {
  658. if ( !IsZoomed() )
  659. {
  660. HandleNoScopeFireDeny();
  661. return;
  662. }
  663. }
  664. if ( m_flNextPrimaryAttack > gpGlobals->curtime )
  665. return;
  666. // Fire the sniper shot.
  667. PrimaryAttack();
  668. if ( IsZoomed() )
  669. {
  670. // If we have more bullets, zoom out, play the bolt animation and zoom back in
  671. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
  672. {
  673. // do not zoom out if we're under rage or about to enter it
  674. if ( !( pPlayer->m_Shared.InCond( TF_COND_SNIPERCHARGE_RAGE_BUFF ) ) )
  675. {
  676. float flUnzoomDelay = 0.5f;
  677. if ( !UsesClipsForAmmo1() )
  678. {
  679. // Since sniper rifles don't actually use clips the fast reload hook also affects unzoom and zoom delays
  680. ApplyScopeSpeedModifications( flUnzoomDelay );
  681. }
  682. SetRezoom( true, flUnzoomDelay ); // zoom out in 0.5 seconds, then rezoom
  683. }
  684. }
  685. else
  686. {
  687. //just zoom out
  688. SetRezoom( false, 0.5f ); // just zoom out in 0.5 seconds
  689. }
  690. }
  691. else
  692. {
  693. float flZoomDelay = SequenceDuration();
  694. // Since sniper rifles don't actually use clips the fast reload hook also affects zoom delays
  695. ApplyScopeSpeedModifications( flZoomDelay );
  696. // Prevent primary fire preventing zooms
  697. m_flNextSecondaryAttack = gpGlobals->curtime + flZoomDelay;
  698. }
  699. m_flChargedDamage = 0.0f;
  700. #ifdef GAME_DLL
  701. if ( m_hSniperDot )
  702. {
  703. m_hSniperDot->ResetChargeTime();
  704. }
  705. #endif
  706. }
  707. //-----------------------------------------------------------------------------
  708. // Purpose:
  709. //-----------------------------------------------------------------------------
  710. void CTFSniperRifle::SetRezoom( bool bRezoom, float flDelay )
  711. {
  712. m_flUnzoomTime = gpGlobals->curtime + flDelay;
  713. m_bRezoomAfterShot = bRezoom;
  714. }
  715. //-----------------------------------------------------------------------------
  716. // Purpose:
  717. // Output : float
  718. //-----------------------------------------------------------------------------
  719. float CTFSniperRifle::GetProjectileDamage( void )
  720. {
  721. float flDamage = MAX( m_flChargedDamage, TF_WEAPON_SNIPERRIFLE_DAMAGE_MIN );
  722. float flDamageMod = 1.f;
  723. CALL_ATTRIB_HOOK_FLOAT( flDamageMod, mult_dmg );
  724. flDamage *= flDamageMod;
  725. if ( TFGameRules() && TFGameRules()->IsPowerupMode() )
  726. {
  727. CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
  728. if ( pPlayer && pPlayer->m_Shared.GetCarryingRuneType() == RUNE_PRECISION )
  729. {
  730. flDamage *= 2.f;
  731. }
  732. }
  733. if ( IsFullyCharged() )
  734. {
  735. CALL_ATTRIB_HOOK_FLOAT( flDamage, sniper_full_charge_damage_bonus );
  736. }
  737. return flDamage;
  738. }
  739. //-----------------------------------------------------------------------------
  740. // Purpose:
  741. //-----------------------------------------------------------------------------
  742. int CTFSniperRifle::GetDamageType( void ) const
  743. {
  744. // Only do hit location damage if we're zoomed
  745. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  746. if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
  747. return BaseClass::GetDamageType();
  748. int nDamageType = BaseClass::GetDamageType() & ~DMG_USE_HITLOCATIONS;
  749. return nDamageType;
  750. }
  751. //-----------------------------------------------------------------------------
  752. // Purpose:
  753. //-----------------------------------------------------------------------------
  754. void CTFSniperRifle::CreateSniperDot( void )
  755. {
  756. // Server specific.
  757. #ifdef GAME_DLL
  758. // Check to see if we have already been created?
  759. if ( m_hSniperDot )
  760. return;
  761. // Get the owning player (make sure we have one).
  762. CBaseCombatCharacter *pPlayer = GetOwner();
  763. if ( !pPlayer )
  764. return;
  765. // Create the sniper dot, but do not make it visible yet.
  766. m_hSniperDot = CSniperDot::Create( GetAbsOrigin(), pPlayer, true );
  767. m_hSniperDot->ChangeTeam( pPlayer->GetTeamNumber() );
  768. #endif
  769. }
  770. //-----------------------------------------------------------------------------
  771. // Purpose:
  772. //-----------------------------------------------------------------------------
  773. void CTFSniperRifle::DestroySniperDot( void )
  774. {
  775. // Server specific.
  776. #ifdef GAME_DLL
  777. // Destroy the sniper dot.
  778. if ( m_hSniperDot )
  779. {
  780. UTIL_Remove( m_hSniperDot );
  781. m_hSniperDot = NULL;
  782. }
  783. #endif
  784. }
  785. //-----------------------------------------------------------------------------
  786. // Purpose:
  787. //-----------------------------------------------------------------------------
  788. void CTFSniperRifle::UpdateSniperDot( void )
  789. {
  790. // Server specific.
  791. #ifdef GAME_DLL
  792. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  793. if ( !pPlayer )
  794. return;
  795. // Get the start and endpoints.
  796. Vector vecMuzzlePos = pPlayer->Weapon_ShootPosition();
  797. Vector forward;
  798. pPlayer->EyeVectors( &forward );
  799. Vector vecEndPos = vecMuzzlePos + ( forward * MAX_TRACE_LENGTH );
  800. trace_t trace;
  801. UTIL_TraceLine( vecMuzzlePos, vecEndPos, ( MASK_SHOT & ~CONTENTS_WINDOW ), GetOwner(), COLLISION_GROUP_NONE, &trace );
  802. // Update the sniper dot.
  803. if ( m_hSniperDot )
  804. {
  805. CBaseEntity *pEntity = NULL;
  806. if ( trace.DidHitNonWorldEntity() )
  807. {
  808. pEntity = trace.m_pEnt;
  809. if ( !pEntity || !pEntity->m_takedamage )
  810. {
  811. pEntity = NULL;
  812. }
  813. }
  814. m_hSniperDot->Update( pEntity, trace.endpos, trace.plane.normal );
  815. }
  816. #endif
  817. }
  818. //-----------------------------------------------------------------------------
  819. // Purpose:
  820. //-----------------------------------------------------------------------------
  821. bool CTFSniperRifle::CanFireCriticalShot( bool bIsHeadshot )
  822. {
  823. m_bCurrentAttackIsCrit = false;
  824. m_bCurrentShotIsHeadshot = false;
  825. if ( !BaseClass::CanFireCriticalShot( bIsHeadshot ) )
  826. return false;
  827. CTFPlayer *pPlayer = GetTFPlayerOwner();
  828. if ( pPlayer && pPlayer->m_Shared.IsCritBoosted() )
  829. {
  830. m_bCurrentShotIsHeadshot = bIsHeadshot;
  831. return true;
  832. }
  833. // If we don't auto crit on a headshot, use standard criteria to determine other crits.
  834. if ( GetRifleType() == RIFLE_JARATE )
  835. {
  836. return false; // Never auto crit on headshot.
  837. }
  838. // can only fire a crit shot if this is a headshot, unless we're critboosted
  839. if ( !bIsHeadshot )
  840. return false;
  841. int iFullChargeHeadShotPenalty = 0;
  842. CALL_ATTRIB_HOOK_INT( iFullChargeHeadShotPenalty, sniper_no_headshot_without_full_charge );
  843. if ( iFullChargeHeadShotPenalty != 0 )
  844. {
  845. if ( !IsFullyCharged() )
  846. return false;
  847. }
  848. int iCanCritNoScope = 0;
  849. CALL_ATTRIB_HOOK_INT( iCanCritNoScope, sniper_crit_no_scope );
  850. if ( iCanCritNoScope == 0 )
  851. {
  852. if ( pPlayer )
  853. {
  854. // no crits if they're not zoomed
  855. if ( pPlayer->GetFOV() >= pPlayer->GetDefaultFOV() )
  856. {
  857. return false;
  858. }
  859. // no crits for 0.2 seconds after starting to zoom
  860. if ( ( gpGlobals->curtime - pPlayer->GetFOVTime() ) < TF_WEAPON_SNIPERRIFLE_NO_CRIT_AFTER_ZOOM_TIME )
  861. {
  862. return false;
  863. }
  864. }
  865. }
  866. m_bCurrentAttackIsCrit = true;
  867. m_bCurrentShotIsHeadshot = bIsHeadshot;
  868. return true;
  869. }
  870. //-----------------------------------------------------------------------------
  871. // Purpose: Our owner was stunned.
  872. //-----------------------------------------------------------------------------
  873. void CTFSniperRifle::OnControlStunned( void )
  874. {
  875. BaseClass::OnControlStunned();
  876. ZoomOut();
  877. }
  878. //-----------------------------------------------------------------------------
  879. // Purpose:
  880. //-----------------------------------------------------------------------------
  881. int CTFSniperRifle::GetCustomDamageType() const
  882. {
  883. if ( IsJarateRifle() )
  884. {
  885. return TF_DMG_CUSTOM_PENETRATE_NONBURNING_TEAMMATE;
  886. }
  887. return TF_DMG_CUSTOM_PENETRATE_MY_TEAM;
  888. }
  889. //-----------------------------------------------------------------------------
  890. // Purpose:
  891. //-----------------------------------------------------------------------------
  892. void CTFSniperRifle::Detach( void )
  893. {
  894. if ( IsZoomed() )
  895. {
  896. ToggleZoom();
  897. }
  898. BaseClass::Detach();
  899. }
  900. #ifdef GAME_DLL
  901. //-----------------------------------------------------------------------------
  902. // Purpose:
  903. //-----------------------------------------------------------------------------
  904. void CTFSniperRifle::OnPlayerKill( CTFPlayer *pVictim, const CTakeDamageInfo &info )
  905. {
  906. BaseClass::OnPlayerKill( pVictim, info );
  907. if ( m_iConsecutiveKills == 3 )
  908. {
  909. CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
  910. if ( pPlayer )
  911. {
  912. pPlayer->AwardAchievement( ACHIEVEMENT_TF_SNIPER_RIFLE_NO_MISSING );
  913. }
  914. }
  915. }
  916. //-----------------------------------------------------------------------------
  917. // Purpose:
  918. //-----------------------------------------------------------------------------
  919. void CTFSniperRifle::OnBulletFire( int iEnemyPlayersHit )
  920. {
  921. BaseClass::OnBulletFire( iEnemyPlayersHit );
  922. // Did we completely miss?
  923. CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
  924. if( iEnemyPlayersHit == 0 && pPlayer && pPlayer->m_Shared.InCond( TF_COND_AIMING ) )
  925. {
  926. EconEntity_OnOwnerKillEaterEventNoPartner( assert_cast<CEconEntity *>( this ), pPlayer, kKillEaterEvent_NEGATIVE_SniperShotsMissed );
  927. }
  928. }
  929. //-----------------------------------------------------------------------------
  930. // Purpose:
  931. //-----------------------------------------------------------------------------
  932. void CTFSniperRifle::ExplosiveHeadShot( CTFPlayer *pAttacker, CTFPlayer *pVictim )
  933. {
  934. if ( !pAttacker )
  935. return;
  936. if ( !pVictim )
  937. return;
  938. int iExplosiveShot = 0;
  939. CALL_ATTRIB_HOOK_INT_ON_OTHER ( pAttacker, iExplosiveShot, explosive_sniper_shot );
  940. // Stun the source
  941. float flStunDuration = 1.f + ( ( (float)iExplosiveShot - 1.f ) * 0.5f );
  942. float flStunAmt = pVictim->IsMiniBoss() ? 0.5f : RemapValClamped( iExplosiveShot, 1, 3, 0.5f, 0.8f );
  943. pVictim->m_Shared.StunPlayer( flStunDuration, flStunAmt, TF_STUN_MOVEMENT, pAttacker );
  944. // Generate an explosion and look for nearby bots
  945. float flDmgRange = 125.f + iExplosiveShot * 25.f;
  946. float flDmg = 130.f + iExplosiveShot * 20.f;
  947. CBaseEntity *pObjects[ 32 ];
  948. int nCount = UTIL_EntitiesInSphere( pObjects, ARRAYSIZE( pObjects ), pVictim->GetAbsOrigin(), flDmgRange, FL_CLIENT );
  949. for ( int i = 0; i < nCount; i++ )
  950. {
  951. if ( !pObjects[i] )
  952. continue;
  953. if ( !pObjects[i]->IsAlive() )
  954. continue;
  955. if ( pObjects[i] == pVictim )
  956. continue;
  957. if ( pAttacker->InSameTeam( pObjects[i] ) )
  958. continue;
  959. if ( !pVictim->FVisible( pObjects[i], MASK_OPAQUE ) )
  960. continue;
  961. CTFPlayer *pTFPlayer = static_cast<CTFPlayer *>( pObjects[i] );
  962. if ( !pTFPlayer )
  963. continue;
  964. if ( pTFPlayer->m_Shared.InCond( TF_COND_PHASE ) || pTFPlayer->m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) )
  965. continue;
  966. if ( pTFPlayer->m_Shared.IsInvulnerable() )
  967. continue;
  968. // Stun
  969. flStunAmt = pTFPlayer->IsMiniBoss() ? 0.5f : RemapValClamped( iExplosiveShot, 1, 3, 0.5f, 0.8f );
  970. pTFPlayer->m_Shared.StunPlayer( flStunDuration, flStunAmt, TF_STUN_MOVEMENT, pAttacker );
  971. // DoT
  972. pTFPlayer->m_Shared.MakeBleed( pAttacker, this, 0.1f, flDmg );
  973. // Shoot a beam at them
  974. CPVSFilter filter( pTFPlayer->WorldSpaceCenter() );
  975. Vector vStart = pVictim->EyePosition();
  976. Vector vEnd = pTFPlayer->EyePosition();
  977. te_tf_particle_effects_control_point_t controlPoint = { PATTACH_ABSORIGIN, vEnd };
  978. TE_TFParticleEffectComplex( filter, 0.0f, "dxhr_arm_muzzleflash", vStart, QAngle( 0, 0, 0 ), NULL, &controlPoint, pTFPlayer, PATTACH_CUSTOMORIGIN );
  979. pTFPlayer->EmitSound( "Weapon_Upgrade.ExplosiveHeadshot" );
  980. }
  981. }
  982. #endif
  983. //-----------------------------------------------------------------------------
  984. // Purpose:
  985. //-----------------------------------------------------------------------------
  986. bool CTFSniperRifle::IsJarateRifle( void ) const
  987. {
  988. return GetJarateTimeInternal() > 0.f;
  989. }
  990. //-----------------------------------------------------------------------------
  991. // Purpose:
  992. //-----------------------------------------------------------------------------
  993. float CTFSniperRifle::GetJarateTime( void ) const
  994. {
  995. if ( m_flChargedDamage > 0.f )
  996. {
  997. return GetJarateTimeInternal();
  998. }
  999. else
  1000. return 0.0f;
  1001. }
  1002. //-----------------------------------------------------------------------------
  1003. // Purpose:
  1004. //-----------------------------------------------------------------------------
  1005. float CTFSniperRifle::GetJarateTimeInternal( void ) const
  1006. {
  1007. float flMaxJarateTime = 0.0f;
  1008. CALL_ATTRIB_HOOK_FLOAT( flMaxJarateTime, jarate_duration );
  1009. if ( flMaxJarateTime > 0 )
  1010. {
  1011. const float flMinJarateTime = 2.f;
  1012. float flDuration = RemapValClamped( m_flChargedDamage, TF_WEAPON_SNIPERRIFLE_DAMAGE_MIN, TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX, flMinJarateTime, flMaxJarateTime );
  1013. return flDuration;
  1014. }
  1015. return 0.f;
  1016. }
  1017. //-----------------------------------------------------------------------------
  1018. // Purpose: UI Progress (same as GetProgress() without the division by 100.0f)
  1019. //-----------------------------------------------------------------------------
  1020. bool CTFSniperRifle::IsRageFull( void )
  1021. {
  1022. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1023. if ( !pPlayer )
  1024. {
  1025. return false;
  1026. }
  1027. return ( pPlayer->m_Shared.GetRageMeter() >= 100.0f );
  1028. }
  1029. //-----------------------------------------------------------------------------
  1030. // Purpose:
  1031. //-----------------------------------------------------------------------------
  1032. bool CTFSniperRifle::EffectMeterShouldFlash( void )
  1033. {
  1034. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1035. if ( !pPlayer )
  1036. {
  1037. return false;
  1038. }
  1039. if ( pPlayer && ( IsRageFull() || pPlayer->m_Shared.IsRageDraining() ) )
  1040. {
  1041. return true;
  1042. }
  1043. return false;
  1044. }
  1045. //-----------------------------------------------------------------------------
  1046. // Purpose: UI Progress
  1047. //-----------------------------------------------------------------------------
  1048. float CTFSniperRifle::GetProgress( void )
  1049. {
  1050. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1051. if ( !pPlayer )
  1052. {
  1053. return 0.f;
  1054. }
  1055. return pPlayer->m_Shared.GetRageMeter() / 100.0f;
  1056. }
  1057. //=============================================================================
  1058. //
  1059. // Client specific functions.
  1060. //
  1061. #ifdef CLIENT_DLL
  1062. //-----------------------------------------------------------------------------
  1063. // Purpose:
  1064. //-----------------------------------------------------------------------------
  1065. bool CTFSniperRifle::ShouldEjectBrass()
  1066. {
  1067. if ( GetJarateTimeInternal() > 0.f )
  1068. return false;
  1069. else
  1070. return true;
  1071. }
  1072. //-----------------------------------------------------------------------------
  1073. // Purpose:
  1074. //-----------------------------------------------------------------------------
  1075. float CTFSniperRifle::GetHUDDamagePerc( void )
  1076. {
  1077. return (m_flChargedDamage / TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX);
  1078. }
  1079. //-----------------------------------------------------------------------------
  1080. // Returns the sniper chargeup from 0 to 1
  1081. //-----------------------------------------------------------------------------
  1082. class CProxySniperRifleCharge : public CResultProxy
  1083. {
  1084. public:
  1085. void OnBind( void *pC_BaseEntity );
  1086. };
  1087. void CProxySniperRifleCharge::OnBind( void *pC_BaseEntity )
  1088. {
  1089. Assert( m_pResult );
  1090. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  1091. if ( GetSpectatorTarget() != 0 && GetSpectatorMode() == OBS_MODE_IN_EYE )
  1092. {
  1093. pPlayer = (C_TFPlayer *)UTIL_PlayerByIndex( GetSpectatorTarget() );
  1094. }
  1095. if ( pPlayer )
  1096. {
  1097. CTFSniperRifle *pWeapon = assert_cast<CTFSniperRifle*>(pPlayer->GetActiveTFWeapon());
  1098. if ( pWeapon )
  1099. {
  1100. float flChargeValue = ( ( 1.0 - pWeapon->GetHUDDamagePerc() ) * 0.8 ) + 0.6;
  1101. VMatrix mat, temp;
  1102. Vector2D center( 0.5, 0.5 );
  1103. MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
  1104. // scale
  1105. {
  1106. Vector2D scale( 1.0f, 0.25f );
  1107. MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
  1108. MatrixMultiply( temp, mat, mat );
  1109. }
  1110. MatrixBuildTranslation( temp, center.x, center.y, 0.0f );
  1111. MatrixMultiply( temp, mat, mat );
  1112. // translation
  1113. {
  1114. Vector2D translation( 0.0f, flChargeValue );
  1115. MatrixBuildTranslation( temp, translation.x, translation.y, 0.0f );
  1116. MatrixMultiply( temp, mat, mat );
  1117. }
  1118. m_pResult->SetMatrixValue( mat );
  1119. }
  1120. }
  1121. if ( ToolsEnabled() )
  1122. {
  1123. ToolFramework_RecordMaterialParams( GetMaterial() );
  1124. }
  1125. }
  1126. EXPOSE_INTERFACE( CProxySniperRifleCharge, IMaterialProxy, "SniperRifleCharge" IMATERIAL_PROXY_INTERFACE_VERSION );
  1127. #endif
  1128. //=============================================================================
  1129. //
  1130. // Laser Dot functions.
  1131. //
  1132. IMPLEMENT_NETWORKCLASS_ALIASED( SniperDot, DT_SniperDot )
  1133. BEGIN_NETWORK_TABLE( CSniperDot, DT_SniperDot )
  1134. #ifdef CLIENT_DLL
  1135. RecvPropFloat( RECVINFO( m_flChargeStartTime ) ),
  1136. #else
  1137. SendPropTime( SENDINFO( m_flChargeStartTime ) ),
  1138. #endif
  1139. END_NETWORK_TABLE()
  1140. LINK_ENTITY_TO_CLASS( env_sniperdot, CSniperDot );
  1141. BEGIN_DATADESC( CSniperDot )
  1142. DEFINE_FIELD( m_vecSurfaceNormal, FIELD_VECTOR ),
  1143. DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ),
  1144. END_DATADESC()
  1145. //-----------------------------------------------------------------------------
  1146. // Purpose: Constructor.
  1147. //-----------------------------------------------------------------------------
  1148. CSniperDot::CSniperDot( void )
  1149. {
  1150. m_vecSurfaceNormal.Init();
  1151. m_hTargetEnt = NULL;
  1152. #ifdef CLIENT_DLL
  1153. m_hSpriteMaterial = NULL;
  1154. m_laserBeamEffect = NULL;
  1155. #endif
  1156. ResetChargeTime();
  1157. }
  1158. //-----------------------------------------------------------------------------
  1159. // Purpose: Destructor.
  1160. //-----------------------------------------------------------------------------
  1161. CSniperDot::~CSniperDot( void )
  1162. {
  1163. #ifdef CLIENT_DLL
  1164. if ( m_laserBeamEffect )
  1165. {
  1166. ParticleProp()->StopEmissionAndDestroyImmediately( m_laserBeamEffect );
  1167. m_laserBeamEffect = NULL;
  1168. }
  1169. #endif
  1170. }
  1171. //-----------------------------------------------------------------------------
  1172. // Purpose:
  1173. // Input : &origin -
  1174. // Output : CSniperDot
  1175. //-----------------------------------------------------------------------------
  1176. CSniperDot *CSniperDot::Create( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
  1177. {
  1178. // Client specific.
  1179. #ifdef CLIENT_DLL
  1180. return NULL;
  1181. // Server specific.
  1182. #else
  1183. // Create the sniper dot entity.
  1184. CSniperDot *pDot = static_cast<CSniperDot*>( CBaseEntity::Create( "env_sniperdot", origin, QAngle( 0.0f, 0.0f, 0.0f ) ) );
  1185. if ( !pDot )
  1186. return NULL;
  1187. //Create the graphic
  1188. pDot->SetMoveType( MOVETYPE_NONE );
  1189. pDot->AddSolidFlags( FSOLID_NOT_SOLID );
  1190. pDot->AddEffects( EF_NOSHADOW );
  1191. UTIL_SetSize( pDot, -Vector( 4.0f, 4.0f, 4.0f ), Vector( 4.0f, 4.0f, 4.0f ) );
  1192. // Set owner.
  1193. pDot->SetOwnerEntity( pOwner );
  1194. // Force updates even though we don't have a model.
  1195. pDot->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  1196. return pDot;
  1197. #endif
  1198. }
  1199. //-----------------------------------------------------------------------------
  1200. // Purpose:
  1201. //-----------------------------------------------------------------------------
  1202. void CSniperDot::Update( CBaseEntity *pTarget, const Vector &vecOrigin, const Vector &vecNormal )
  1203. {
  1204. SetAbsOrigin( vecOrigin );
  1205. m_vecSurfaceNormal = vecNormal;
  1206. m_hTargetEnt = pTarget;
  1207. }
  1208. //-----------------------------------------------------------------------------
  1209. // Purpose:
  1210. //-----------------------------------------------------------------------------
  1211. Vector CSniperDot::GetChasePosition()
  1212. {
  1213. return GetAbsOrigin() - m_vecSurfaceNormal * 10;
  1214. }
  1215. //=============================================================================
  1216. //
  1217. // Client specific functions.
  1218. //
  1219. #ifdef CLIENT_DLL
  1220. bool CSniperDot::GetRenderingPositions( C_TFPlayer *pPlayer, Vector &vecAttachment, Vector &vecEndPos, float &flSize )
  1221. {
  1222. if ( !pPlayer )
  1223. return false;
  1224. // Get the sprite rendering position.
  1225. flSize = 6.0;
  1226. bool bScaleSizeByDistance = false;
  1227. const float c_fMaxSizeDistVR = 384.0f;
  1228. const float c_flMaxSizeDistUnzoomed = 200.0f;
  1229. if ( !pPlayer->IsDormant() )
  1230. {
  1231. Vector vecDir;
  1232. QAngle angles;
  1233. float flDist = MAX_TRACE_LENGTH;
  1234. // Always draw the dot in front of our faces when in first-person.
  1235. if ( pPlayer->IsLocalPlayer() )
  1236. {
  1237. // Take our view position and orientation
  1238. vecAttachment = CurrentViewOrigin();
  1239. vecDir = CurrentViewForward();
  1240. // Clamp the forward distance for the sniper's firstperson
  1241. flDist = c_fMaxSizeDistVR;
  1242. flSize = 2.0;
  1243. // Make the dot bigger when charging and not zoomed in (The Classic)
  1244. if ( pPlayer->m_Shared.InCond( TF_COND_AIMING ) && !pPlayer->m_Shared.InCond( TF_COND_ZOOMED ))
  1245. {
  1246. flSize = 4.0f;
  1247. bScaleSizeByDistance = true;
  1248. }
  1249. if ( UseVR() )
  1250. {
  1251. // The view direction is not exactly the same as the weapon direction because of stereo, calibration, etc.
  1252. g_ClientVirtualReality.OverrideWeaponHudAimVectors ( &vecAttachment, &vecDir );
  1253. // No clamping, thanks - we need the distance to be correct so that
  1254. // vergence works properly, and we'll scale the size up accordingly.
  1255. flDist = MAX_TRACE_LENGTH;
  1256. bScaleSizeByDistance = true;
  1257. }
  1258. }
  1259. else
  1260. {
  1261. // Take the owning player eye position and direction.
  1262. vecAttachment = pPlayer->EyePosition();
  1263. QAngle anglesEye = pPlayer->EyeAngles();
  1264. AngleVectors( anglesEye, &vecDir );
  1265. }
  1266. trace_t tr;
  1267. CTraceFilterIgnoreFriendlyCombatItems filter( pPlayer, COLLISION_GROUP_NONE, pPlayer->GetTeamNumber() );
  1268. UTIL_TraceLine( vecAttachment, vecAttachment + ( vecDir * flDist ), MASK_SHOT, &filter, &tr );
  1269. // Backup off the hit plane, towards the source
  1270. vecEndPos = tr.endpos + vecDir * -4;
  1271. if ( UseVR() )
  1272. {
  1273. float fDist = ( vecEndPos - vecAttachment ).Length();
  1274. if ( fDist > c_fMaxSizeDistVR )
  1275. {
  1276. // Scale the dot up so it's still visible in first person.
  1277. flSize *= ( fDist * ( 1.0f / c_fMaxSizeDistVR ) );
  1278. }
  1279. }
  1280. else if ( bScaleSizeByDistance )
  1281. {
  1282. float fDist = ( vecEndPos - vecAttachment ).Length();
  1283. if ( fDist > c_flMaxSizeDistUnzoomed )
  1284. {
  1285. // Scale the dot up so it's still visible in first person.
  1286. flSize *= ( fDist * ( 1.0f / c_flMaxSizeDistUnzoomed ) );
  1287. }
  1288. }
  1289. }
  1290. else
  1291. {
  1292. // Just use our position if we can't predict it otherwise.
  1293. vecAttachment = GetAbsOrigin();
  1294. vecEndPos = GetAbsOrigin();
  1295. }
  1296. return true;
  1297. }
  1298. //-----------------------------------------------------------------------------
  1299. // Purpose:
  1300. // TFTODO: Make the sniper dot get brighter the more damage it will do.
  1301. //-----------------------------------------------------------------------------
  1302. int CSniperDot::DrawModel( int flags )
  1303. {
  1304. // Get the owning player.
  1305. C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
  1306. if ( !pPlayer )
  1307. return -1;
  1308. Vector vecAttachement;
  1309. Vector vecEndPos;
  1310. float flSize;
  1311. if ( !GetRenderingPositions( pPlayer, vecAttachement, vecEndPos, flSize ) )
  1312. {
  1313. return -1;
  1314. }
  1315. // Draw our laser dot in space.
  1316. CMatRenderContextPtr pRenderContext( materials );
  1317. pRenderContext->Bind( m_hSpriteMaterial, this );
  1318. CTFWeaponBase *pBaseWeapon = pPlayer->GetActiveTFWeapon();
  1319. CTFSniperRifle *pWeapon = dynamic_cast< CTFSniperRifle* >( pBaseWeapon );
  1320. float flLifeTime = gpGlobals->curtime - m_flChargeStartTime;
  1321. float flChargePerSec = TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC;
  1322. if ( pWeapon )
  1323. {
  1324. pWeapon->ApplyChargeSpeedModifications( flChargePerSec );
  1325. }
  1326. // Sniper Rage
  1327. if ( pPlayer->m_Shared.InCond( TF_COND_SNIPERCHARGE_RAGE_BUFF ) )
  1328. {
  1329. flChargePerSec *= 1.25f;
  1330. }
  1331. float flStrength;
  1332. if ( pWeapon )
  1333. {
  1334. flStrength = pWeapon->GetHUDDamagePerc();
  1335. // FIXME: We should find out what's causing this and fix it.
  1336. AssertMsg1( flStrength >= ( 0.0f - FLT_EPSILON ) && flStrength <= ( 1.0f + FLT_EPSILON ), "GetHUDDamagePerc returned out of range value: %f", flStrength );
  1337. flStrength = clamp( flStrength, 0.1f, 1.0f );
  1338. }
  1339. else
  1340. {
  1341. flStrength = RemapValClamped( flLifeTime, 0.0, TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX / flChargePerSec, 0.1f, 1.0f );
  1342. }
  1343. color32 innercolor = { 255, 255, 255, 255 };
  1344. color32 outercolor = { 255, 255, 255, 128 };
  1345. DrawSprite( vecEndPos, flSize, flSize, outercolor );
  1346. DrawSprite( vecEndPos, flSize * flStrength, flSize * flStrength, innercolor );
  1347. // Successful.
  1348. return 1;
  1349. }
  1350. //-----------------------------------------------------------------------------
  1351. // Purpose:
  1352. //-----------------------------------------------------------------------------
  1353. bool CSniperDot::ShouldDraw( void )
  1354. {
  1355. if ( IsEffectActive( EF_NODRAW ) )
  1356. return false;
  1357. // Don't draw the sniper dot when in thirdperson.
  1358. if ( ::input->CAM_IsThirdPerson() )
  1359. return false;
  1360. return true;
  1361. }
  1362. void CSniperDot::ClientThink( void )
  1363. {
  1364. // snipers have laser sights in PvE mode
  1365. if ( TFGameRules()->IsPVEModeActive() && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  1366. {
  1367. C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
  1368. if ( pPlayer )
  1369. {
  1370. if ( !m_laserBeamEffect )
  1371. {
  1372. m_laserBeamEffect = ParticleProp()->Create( "laser_sight_beam", PATTACH_ABSORIGIN_FOLLOW );
  1373. }
  1374. if ( m_laserBeamEffect )
  1375. {
  1376. m_laserBeamEffect->SetSortOrigin( m_laserBeamEffect->GetRenderOrigin() );
  1377. m_laserBeamEffect->SetControlPoint( 2, Vector( 0, 0, 255 ) );
  1378. Vector vecAttachment;
  1379. Vector vecEndPos;
  1380. float flSize;
  1381. if ( pPlayer->GetAttachment( "eye_1", vecAttachment ) )
  1382. {
  1383. m_laserBeamEffect->SetControlPoint( 1, vecAttachment );
  1384. }
  1385. else if ( GetRenderingPositions( pPlayer, vecAttachment, vecEndPos, flSize ) )
  1386. {
  1387. m_laserBeamEffect->SetControlPoint( 1, vecAttachment );
  1388. }
  1389. }
  1390. }
  1391. }
  1392. }
  1393. //-----------------------------------------------------------------------------
  1394. // Purpose:
  1395. //-----------------------------------------------------------------------------
  1396. void CSniperDot::OnDataChanged( DataUpdateType_t updateType )
  1397. {
  1398. if ( updateType == DATA_UPDATE_CREATED )
  1399. {
  1400. if ( GetTeamNumber() == TF_TEAM_BLUE )
  1401. {
  1402. m_hSpriteMaterial.Init( SNIPER_DOT_SPRITE_BLUE, TEXTURE_GROUP_CLIENT_EFFECTS );
  1403. }
  1404. else
  1405. {
  1406. m_hSpriteMaterial.Init( SNIPER_DOT_SPRITE_RED, TEXTURE_GROUP_CLIENT_EFFECTS );
  1407. }
  1408. SetNextClientThink( CLIENT_THINK_ALWAYS );
  1409. }
  1410. }
  1411. #endif // CLIENT_DLL
  1412. #ifdef GAME_DLL
  1413. void CTFSniperRifleDecap::OnPlayerKill( CTFPlayer *pVictim, const CTakeDamageInfo &info )
  1414. {
  1415. BaseClass::OnPlayerKill( pVictim, info );
  1416. CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
  1417. if ( pPlayer && IsHeadshot( info.GetDamageCustom() ) )
  1418. {
  1419. // If we got a headshot kill, increment our number of decapitations.
  1420. int iDecaps = pPlayer->m_Shared.GetDecapitations() + 1;
  1421. pPlayer->m_Shared.SetDecapitations( iDecaps );
  1422. }
  1423. }
  1424. #endif // GAME_DLL
  1425. static const int MAX_HEAD_BONUS = 6;
  1426. //-----------------------------------------------------------------------------
  1427. // Purpose:
  1428. //-----------------------------------------------------------------------------
  1429. float CTFSniperRifleDecap::SniperRifleChargeRateMod()
  1430. {
  1431. return ( .25f * ( MIN( GetCount(), MAX_HEAD_BONUS ) - 2 ) ) * TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC;
  1432. }
  1433. //-----------------------------------------------------------------------------
  1434. // Purpose:
  1435. //-----------------------------------------------------------------------------
  1436. int CTFSniperRifleDecap::GetCount( void )
  1437. {
  1438. CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
  1439. if ( pPlayer )
  1440. {
  1441. return pPlayer->m_Shared.GetDecapitations();
  1442. }
  1443. return 0;
  1444. }
  1445. //-----------------------------------------------------------------------------
  1446. // Purpose: Constructor.
  1447. //-----------------------------------------------------------------------------
  1448. CTFSniperRifleClassic::CTFSniperRifleClassic()
  1449. {
  1450. m_bCharging = false;
  1451. #ifdef CLIENT_DLL
  1452. m_pChargedEffect = NULL;
  1453. #endif
  1454. }
  1455. //-----------------------------------------------------------------------------
  1456. // Purpose: Destructor.
  1457. //-----------------------------------------------------------------------------
  1458. CTFSniperRifleClassic::~CTFSniperRifleClassic()
  1459. {
  1460. #ifdef CLIENT_DLL
  1461. if ( m_pChargedEffect )
  1462. {
  1463. ParticleProp()->StopEmissionAndDestroyImmediately( m_pChargedEffect );
  1464. m_pChargedEffect = NULL;
  1465. }
  1466. #endif
  1467. }
  1468. //-----------------------------------------------------------------------------
  1469. // Purpose:
  1470. //-----------------------------------------------------------------------------
  1471. void CTFSniperRifleClassic::Precache()
  1472. {
  1473. BaseClass::Precache();
  1474. PrecacheParticleSystem( SNIPER_CHARGE_BEAM_RED );
  1475. PrecacheParticleSystem( SNIPER_CHARGE_BEAM_BLUE );
  1476. }
  1477. //-----------------------------------------------------------------------------
  1478. // Purpose:
  1479. //-----------------------------------------------------------------------------
  1480. void CTFSniperRifleClassic::ZoomOut( void )
  1481. {
  1482. CTFWeaponBaseGun::ZoomOut(); // intentionally skipping CTFSniperRifle::ZoomOut()
  1483. }
  1484. //-----------------------------------------------------------------------------
  1485. // Purpose:
  1486. //-----------------------------------------------------------------------------
  1487. void CTFSniperRifleClassic::ZoomIn( void )
  1488. {
  1489. // Start aiming.
  1490. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1491. if ( !pPlayer )
  1492. return;
  1493. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
  1494. return;
  1495. CTFWeaponBaseGun::ZoomIn(); // intentionally skipping CTFSniperRifle::ZoomIn()
  1496. }
  1497. //-----------------------------------------------------------------------------
  1498. // Purpose: Secondary attack.
  1499. //-----------------------------------------------------------------------------
  1500. void CTFSniperRifleClassic::Zoom( void )
  1501. {
  1502. ToggleZoom();
  1503. // at least 0.1 seconds from now, but don't stomp a previous value
  1504. m_flNextSecondaryAttack = gpGlobals->curtime + TF_WEAPON_SNIPERRIFLE_ZOOM_TIME;
  1505. }
  1506. //-----------------------------------------------------------------------------
  1507. // Purpose:
  1508. //-----------------------------------------------------------------------------
  1509. void CTFSniperRifleClassic::HandleZooms( void )
  1510. {
  1511. // Get the owning player.
  1512. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  1513. if ( !pPlayer )
  1514. return;
  1515. // Handle the zoom when taunting.
  1516. if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
  1517. {
  1518. if ( IsZoomed() )
  1519. {
  1520. ToggleZoom();
  1521. return;
  1522. }
  1523. }
  1524. if ( ( pPlayer->m_nButtons & IN_ATTACK2 ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
  1525. {
  1526. Zoom();
  1527. }
  1528. }
  1529. #ifdef CLIENT_DLL
  1530. void CTFSniperRifleClassic::OnDataChanged( DataUpdateType_t updateType )
  1531. {
  1532. BaseClass::OnDataChanged( updateType );
  1533. ManageChargeBeam();
  1534. }
  1535. #endif
  1536. //-----------------------------------------------------------------------------
  1537. // Purpose:
  1538. //-----------------------------------------------------------------------------
  1539. void CTFSniperRifleClassic::ItemPostFrame( void )
  1540. {
  1541. // If we're lowered, we're not allowed to fire
  1542. if ( m_bLowered )
  1543. return;
  1544. // Get the owning player.
  1545. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  1546. if ( !pPlayer )
  1547. return;
  1548. if ( !CanAttack() )
  1549. {
  1550. if ( IsZoomed() )
  1551. {
  1552. ToggleZoom();
  1553. }
  1554. WeaponReset();
  1555. return;
  1556. }
  1557. HandleZooms();
  1558. #ifdef GAME_DLL
  1559. // Update the sniper dot position if we have one
  1560. if ( m_hSniperDot )
  1561. {
  1562. UpdateSniperDot();
  1563. }
  1564. #endif
  1565. if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
  1566. {
  1567. WeaponReset();
  1568. return;
  1569. }
  1570. if ( ( pPlayer->m_nButtons & IN_ATTACK ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) )
  1571. {
  1572. if ( !m_bCharging )
  1573. {
  1574. pPlayer->m_Shared.AddCond( TF_COND_AIMING );
  1575. pPlayer->TeamFortress_SetSpeed();
  1576. m_bCharging = true;
  1577. #ifdef GAME_DLL
  1578. // Create the sniper dot.
  1579. CreateSniperDot();
  1580. pPlayer->ClearExpression();
  1581. #endif
  1582. }
  1583. float fSniperRifleChargePerSec = m_flChargePerSec;
  1584. ApplyChargeSpeedModifications( fSniperRifleChargePerSec );
  1585. fSniperRifleChargePerSec += SniperRifleChargeRateMod();
  1586. // we don't want sniper charge rate to go too high.
  1587. fSniperRifleChargePerSec = clamp( fSniperRifleChargePerSec, 0, 2.f * TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC );
  1588. m_flChargedDamage = MIN( m_flChargedDamage + gpGlobals->frametime * fSniperRifleChargePerSec, TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX );
  1589. #ifdef CLIENT_DLL
  1590. // play the recharged bell if we're fully charged
  1591. if ( IsFullyCharged() && !m_bPlayedBell )
  1592. {
  1593. m_bPlayedBell = true;
  1594. if ( tf_sniper_fullcharge_bell.GetBool() )
  1595. {
  1596. C_TFPlayer::GetLocalTFPlayer()->EmitSound( "TFPlayer.ReCharged" );
  1597. }
  1598. }
  1599. #endif
  1600. }
  1601. else if ( m_bCharging )
  1602. {
  1603. if ( pPlayer->GetGroundEntity() )
  1604. {
  1605. Fire( pPlayer );
  1606. }
  1607. else
  1608. {
  1609. pPlayer->EmitSound( "Player.DenyWeaponSelection" );
  1610. }
  1611. WeaponReset();
  1612. }
  1613. else
  1614. {
  1615. // Idle.
  1616. // No fire buttons down or reloading
  1617. if ( !ReloadOrSwitchWeapons() && ( m_bInReload == false ) )
  1618. {
  1619. WeaponIdle();
  1620. }
  1621. }
  1622. }
  1623. //-----------------------------------------------------------------------------
  1624. // Purpose:
  1625. //-----------------------------------------------------------------------------
  1626. int CTFSniperRifleClassic::GetDamageType( void ) const
  1627. {
  1628. return CTFWeaponBaseGun::GetDamageType(); // intentionally skipping CTFSniperRifle::GetDamageType()
  1629. }
  1630. //-----------------------------------------------------------------------------
  1631. // Purpose:
  1632. //-----------------------------------------------------------------------------
  1633. bool CTFSniperRifleClassic::Holster( CBaseCombatWeapon *pSwitchingTo )
  1634. {
  1635. WeaponReset();
  1636. return BaseClass::Holster( pSwitchingTo );
  1637. }
  1638. //-----------------------------------------------------------------------------
  1639. // Purpose:
  1640. //-----------------------------------------------------------------------------
  1641. bool CTFSniperRifleClassic::Deploy( void )
  1642. {
  1643. WeaponReset();
  1644. return BaseClass::Deploy();
  1645. }
  1646. //-----------------------------------------------------------------------------
  1647. // Purpose:
  1648. //-----------------------------------------------------------------------------
  1649. void CTFSniperRifleClassic::WeaponReset( void )
  1650. {
  1651. m_flChargedDamage = 0.0f;
  1652. m_bCharging = false;
  1653. #ifdef CLIENT_DLL
  1654. ManageChargeBeam();
  1655. #endif
  1656. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  1657. if ( pPlayer )
  1658. {
  1659. pPlayer->m_Shared.RemoveCond( TF_COND_AIMING );
  1660. pPlayer->TeamFortress_SetSpeed();
  1661. }
  1662. #ifdef GAME_DLL
  1663. // Destroy the sniper dot.
  1664. DestroySniperDot();
  1665. if ( pPlayer )
  1666. {
  1667. pPlayer->ClearExpression();
  1668. }
  1669. #else
  1670. m_bPlayedBell = false;
  1671. #endif
  1672. m_bCurrentShotIsHeadshot = false;
  1673. m_flChargePerSec = TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC;
  1674. CTFWeaponBase::WeaponReset(); // intentionally skipping CTFSniperRifle::WeaponReset()
  1675. }
  1676. //-----------------------------------------------------------------------------
  1677. // Purpose:
  1678. //-----------------------------------------------------------------------------
  1679. bool CTFSniperRifleClassic::Lower( void )
  1680. {
  1681. if ( BaseClass::Lower() )
  1682. {
  1683. WeaponReset();
  1684. return true;
  1685. }
  1686. return false;
  1687. }
  1688. #ifdef CLIENT_DLL
  1689. //-----------------------------------------------------------------------------
  1690. // Purpose:
  1691. //-----------------------------------------------------------------------------
  1692. void CTFSniperRifleClassic::ManageChargeBeam( void )
  1693. {
  1694. if ( m_bCharging )
  1695. {
  1696. if ( !m_pChargedEffect )
  1697. {
  1698. m_pChargedEffect = ParticleProp()->Create( ( GetTeamNumber() == TF_TEAM_RED ) ? SNIPER_CHARGE_BEAM_RED : SNIPER_CHARGE_BEAM_BLUE, PATTACH_POINT_FOLLOW, "laser" );
  1699. }
  1700. }
  1701. else
  1702. {
  1703. if ( m_pChargedEffect )
  1704. {
  1705. ParticleProp()->StopEmissionAndDestroyImmediately( m_pChargedEffect );
  1706. m_pChargedEffect = NULL;
  1707. }
  1708. }
  1709. }
  1710. #endif
  1711. //-----------------------------------------------------------------------------
  1712. // Purpose:
  1713. //-----------------------------------------------------------------------------
  1714. void CTFSniperRifleClassic::Detach( void )
  1715. {
  1716. WeaponReset();
  1717. BaseClass::Detach();
  1718. }
  1719. #ifdef STAGING_ONLY
  1720. // ********************************************************************************************************
  1721. // CTFSniperRifleRevolver
  1722. // ********************************************************************************************************
  1723. void CTFSniperRifleRevolver::PrimaryAttack()
  1724. {
  1725. BaseClass::PrimaryAttack();
  1726. #ifdef GAME_DLL
  1727. // Head bob
  1728. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1729. if ( pPlayer /*&& pPlayer->m_Shared.InCond( TF_COND_ZOOMED )*/ )
  1730. {
  1731. float impulse = RandomFloat( -2.0f, -1.0f );
  1732. if ( pPlayer->GetFlags() & FL_DUCKING )
  1733. {
  1734. impulse = RandomFloat( -0.5f, -0.2f );
  1735. }
  1736. pPlayer->ApplyPunchImpulseX( impulse );
  1737. }
  1738. float flCharge = (m_flChargedDamage / TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX);
  1739. if ( flCharge > 0.99 )
  1740. {
  1741. // Only at full charge do you get fast attack speed
  1742. // reduce the time between attacks
  1743. float flCurrTime = gpGlobals->curtime;
  1744. //float flTimeBetweenShots = m_flNextPrimaryAttack - flCurrTime;
  1745. //float flTime = RemapVal( flCharge, 0.0, 1, flTimeBetweenShots, 0.2 );
  1746. m_flNextPrimaryAttack = flCurrTime + 0.3;
  1747. }
  1748. #endif
  1749. }
  1750. //-----------------------------------------------------------------------------
  1751. float CTFSniperRifleRevolver::GetProjectileDamage()
  1752. {
  1753. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1754. if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
  1755. {
  1756. float flCharge = ( m_flChargedDamage / TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX );
  1757. if ( flCharge > 0.99 )
  1758. {
  1759. return 75.0f; // Full Charge dmg bonus is less then the normal one (150)
  1760. }
  1761. return 50.0f;
  1762. }
  1763. return 40.0;
  1764. }
  1765. //-----------------------------------------------------------------------------
  1766. bool CTFSniperRifleRevolver::Reload( void )
  1767. {
  1768. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1769. if ( !pPlayer )
  1770. return false;
  1771. // do not reload if zoomed unless you are empty
  1772. if ( m_iClip1 > 0 && pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
  1773. return false;
  1774. bool bReload = CTFWeaponBaseGun::Reload(); // intentionally skipping CTFSniperRifle::Reload().
  1775. if ( bReload && m_iClip1 <= 0 && m_iClip1 != -1 )
  1776. {
  1777. if ( pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
  1778. {
  1779. ZoomOut();
  1780. m_bRezoomAfterShot = pPlayer->ShouldAutoRezoom();
  1781. }
  1782. }
  1783. return bReload;
  1784. }
  1785. //-----------------------------------------------------------------------------
  1786. void CTFSniperRifleRevolver::ZoomIn( void )
  1787. {
  1788. // Start aiming.
  1789. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1790. if ( !pPlayer )
  1791. return;
  1792. if ( m_iClip1 <= 0 && m_iClip1 != -1 )
  1793. return;
  1794. pPlayer->m_Shared.AddCond( TF_COND_AIMING );
  1795. pPlayer->TeamFortress_SetSpeed();
  1796. m_flChargedDamage = 0;
  1797. #ifdef GAME_DLL
  1798. // Create the sniper dot.
  1799. CreateSniperDot();
  1800. pPlayer->ClearExpression();
  1801. #endif
  1802. CTFWeaponBaseGun::ZoomIn(); // intentionally skipping CTFSniperRifle::ZoomIn()
  1803. }
  1804. //-----------------------------------------------------------------------------
  1805. void CTFSniperRifleRevolver::ZoomOut( void )
  1806. {
  1807. // Start aiming.
  1808. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1809. if ( !pPlayer )
  1810. return;
  1811. pPlayer->m_Shared.RemoveCond( TF_COND_AIMING );
  1812. pPlayer->TeamFortress_SetSpeed();
  1813. m_flChargedDamage = 0;
  1814. #ifdef GAME_DLL
  1815. // Destroy the sniper dot.
  1816. DestroySniperDot();
  1817. pPlayer->ClearExpression();
  1818. #endif
  1819. CTFWeaponBaseGun::ZoomOut(); // intentionally skipping CTFSniperRifle::ZoomOut()
  1820. }
  1821. //-----------------------------------------------------------------------------
  1822. void CTFSniperRifleRevolver::ItemPostFrame( void )
  1823. {
  1824. // If we're lowered, we're not allowed to fire
  1825. if ( m_bLowered )
  1826. return;
  1827. // Get the owning player.
  1828. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  1829. if ( !pPlayer )
  1830. return;
  1831. if ( !CanAttack() )
  1832. {
  1833. if ( IsZoomed() )
  1834. {
  1835. ToggleZoom();
  1836. }
  1837. WeaponReset();
  1838. }
  1839. if ( m_bRezoomAfterShot && m_iClip1 > 0 )
  1840. {
  1841. Zoom();
  1842. m_bRezoomAfterShot = false;
  1843. }
  1844. HandleZooms();
  1845. #ifdef GAME_DLL
  1846. // Update the sniper dot position if we have one
  1847. if ( m_hSniperDot )
  1848. {
  1849. UpdateSniperDot();
  1850. }
  1851. #endif
  1852. if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
  1853. {
  1854. WeaponReset();
  1855. return;
  1856. }
  1857. // Handle Charge Meter
  1858. if ( m_flNextSecondaryAttack <= gpGlobals->curtime )
  1859. {
  1860. // Don't start charging in the time just after a shot before we unzoom to play rack anim.
  1861. if ( pPlayer->m_Shared.InCond( TF_COND_AIMING ) && !m_bRezoomAfterShot )
  1862. {
  1863. float fSniperRifleChargePerSec = m_flChargePerSec;
  1864. ApplyChargeSpeedModifications( fSniperRifleChargePerSec );
  1865. fSniperRifleChargePerSec += SniperRifleChargeRateMod();
  1866. // we don't want sniper charge rate to go too high.
  1867. fSniperRifleChargePerSec = clamp( fSniperRifleChargePerSec, 0, 2.f * TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC );
  1868. m_flChargedDamage = MIN( m_flChargedDamage + gpGlobals->frametime * fSniperRifleChargePerSec, TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX );
  1869. #ifdef CLIENT_DLL
  1870. // play the recharged bell if we're fully charged
  1871. if ( IsFullyCharged() && !m_bPlayedBell )
  1872. {
  1873. m_bPlayedBell = true;
  1874. if ( tf_sniper_fullcharge_bell.GetBool() )
  1875. {
  1876. C_TFPlayer::GetLocalTFPlayer()->EmitSound( "TFPlayer.ReCharged" );
  1877. }
  1878. }
  1879. #endif
  1880. }
  1881. else
  1882. {
  1883. m_flChargedDamage = MAX( 0, m_flChargedDamage - gpGlobals->frametime * TF_WEAPON_SNIPERRIFLE_UNCHARGE_PER_SEC );
  1884. }
  1885. }
  1886. return CTFWeaponBaseGun::ItemPostFrame(); // intentionally skipping CTFSniperRifle::ItemPostFrame(). This should just fire the gun
  1887. }
  1888. //-----------------------------------------------------------------------------
  1889. bool CTFSniperRifleRevolver::CanFireCriticalShot( bool bIsHeadshot )
  1890. {
  1891. return CTFSniperRifle::CanFireCriticalShot( bIsHeadshot ); // Skip TFC Sniper Rifle
  1892. }
  1893. //-----------------------------------------------------------------------------
  1894. //
  1895. ConVar tf_sniper_bolt_speed( "tf_sniper_bolt_speed", "3000", FCVAR_REPLICATED, "Dev Convar - Speed of projectile for Revolver Sniper");
  1896. ConVar tf_sniper_bolt_gravity( "tf_sniper_bolt_gravity", "0.1", FCVAR_REPLICATED, "Dev Convar - Gravity of projectile for Revolver Sniper");
  1897. float CTFSniperRifleRevolver::GetProjectileSpeed( void )
  1898. {
  1899. //return 4900.0;
  1900. return tf_sniper_bolt_speed.GetFloat();
  1901. }
  1902. //-----------------------------------------------------------------------------
  1903. float CTFSniperRifleRevolver::GetProjectileGravity( void )
  1904. {
  1905. //return 0.1;
  1906. return tf_sniper_bolt_gravity.GetFloat();
  1907. }
  1908. #endif // STAGING_ONLY