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.

1132 lines
31 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Weapon Base Gun
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_weaponbase_gun.h"
  8. #include "tf_fx_shared.h"
  9. #include "effect_dispatch_data.h"
  10. #include "takedamageinfo.h"
  11. #include "tf_projectile_nail.h"
  12. #include "tf_weapon_jar.h"
  13. #include "tf_weapon_flaregun.h"
  14. #include "tf_projectile_energy_ring.h"
  15. #if !defined( CLIENT_DLL ) // Server specific.
  16. #include "tf_gamestats.h"
  17. #include "tf_player.h"
  18. #include "tf_fx.h"
  19. #include "te_effect_dispatch.h"
  20. #include "tf_projectile_flare.h"
  21. #include "tf_projectile_rocket.h"
  22. #include "tf_projectile_arrow.h"
  23. #include "tf_projectile_energy_ball.h"
  24. #include "tf_weapon_grenade_pipebomb.h"
  25. #include "te.h"
  26. #else // Client specific.
  27. #include "c_tf_player.h"
  28. #include "c_te_effect_dispatch.h"
  29. #include "c_tf_gamestats.h"
  30. #endif
  31. //=============================================================================
  32. //
  33. // TFWeaponBase Gun tables.
  34. //
  35. IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponBaseGun, DT_TFWeaponBaseGun )
  36. BEGIN_NETWORK_TABLE( CTFWeaponBaseGun, DT_TFWeaponBaseGun )
  37. END_NETWORK_TABLE()
  38. BEGIN_PREDICTION_DATA( CTFWeaponBaseGun )
  39. END_PREDICTION_DATA()
  40. // Server specific.
  41. #if !defined( CLIENT_DLL )
  42. BEGIN_DATADESC( CTFWeaponBaseGun )
  43. DEFINE_THINKFUNC( ZoomOutIn ),
  44. DEFINE_THINKFUNC( ZoomOut ),
  45. DEFINE_THINKFUNC( ZoomIn ),
  46. END_DATADESC()
  47. #endif
  48. //=============================================================================
  49. //
  50. // TFWeaponBase Gun functions.
  51. //
  52. //-----------------------------------------------------------------------------
  53. // Purpose: Constructor.
  54. //-----------------------------------------------------------------------------
  55. CTFWeaponBaseGun::CTFWeaponBaseGun()
  56. {
  57. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
  58. m_iAmmoToAdd = 0;
  59. }
  60. //-----------------------------------------------------------------------------
  61. // Purpose:
  62. //-----------------------------------------------------------------------------
  63. void CTFWeaponBaseGun::PrimaryAttack( void )
  64. {
  65. float flUberChargeAmmoPerShot = UberChargeAmmoPerShot();
  66. if ( flUberChargeAmmoPerShot > 0.0f )
  67. {
  68. if ( !HasPrimaryAmmo() )
  69. return;
  70. }
  71. // Check for ammunition.
  72. if ( m_iClip1 <= 0 && m_iClip1 != -1 )
  73. return;
  74. // Are we capable of firing again?
  75. if ( m_flNextPrimaryAttack > gpGlobals->curtime )
  76. return;
  77. // Get the player owning the weapon.
  78. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  79. if ( !pPlayer )
  80. return;
  81. if ( !CanAttack() )
  82. return;
  83. float flFireDelay = ApplyFireDelay( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay );
  84. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayer, flFireDelay, hwn_mult_postfiredelay );
  85. // Some weapons change fire delay based on player's health
  86. float flReducedHealthBonus = 1.0f;
  87. CALL_ATTRIB_HOOK_FLOAT( flReducedHealthBonus, mult_postfiredelay_with_reduced_health );
  88. if ( flReducedHealthBonus != 1.0f )
  89. {
  90. flReducedHealthBonus = RemapValClamped( pPlayer->HealthFraction(), 0.2f, 0.9f, flReducedHealthBonus, 1.0f );
  91. flFireDelay *= flReducedHealthBonus;
  92. }
  93. if ( pPlayer->m_Shared.InCond( TF_COND_BLASTJUMPING ) )
  94. {
  95. CALL_ATTRIB_HOOK_FLOAT( flFireDelay, rocketjump_attackrate_bonus );
  96. }
  97. else
  98. {
  99. CALL_ATTRIB_HOOK_FLOAT( flFireDelay, mul_nonrocketjump_attackrate );
  100. }
  101. if ( m_iPrimaryAmmoType == TF_AMMO_METAL )
  102. {
  103. if ( GetOwner() && GetAmmoPerShot() > GetOwner()->GetAmmoCount( m_iPrimaryAmmoType ) )
  104. {
  105. WeaponSound( EMPTY );
  106. m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay;
  107. return;
  108. }
  109. }
  110. CalcIsAttackCritical();
  111. #ifndef CLIENT_DLL
  112. if ( pPlayer->m_Shared.IsStealthed() && ShouldRemoveInvisibilityOnPrimaryAttack() )
  113. {
  114. pPlayer->RemoveInvisibility();
  115. }
  116. // Minigun has custom handling
  117. if ( GetWeaponID() != TF_WEAPON_MINIGUN )
  118. {
  119. pPlayer->SpeakWeaponFire();
  120. }
  121. CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
  122. #else
  123. C_CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
  124. #endif
  125. // Minigun has custom handling
  126. if ( GetWeaponID() != TF_WEAPON_MINIGUN )
  127. {
  128. // Set the weapon mode.
  129. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
  130. }
  131. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  132. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  133. CBaseEntity* pProj = FireProjectile( pPlayer );
  134. ModifyProjectile( pProj );
  135. if ( !UsesClipsForAmmo1() )
  136. {
  137. // Sniper rifles and such don't actually reload, so we hook reduced reload here
  138. float flBaseFireDelay = flFireDelay;
  139. CALL_ATTRIB_HOOK_FLOAT( flFireDelay, fast_reload );
  140. float flPlaybackRate = flFireDelay == 0.f ? 0.f : flBaseFireDelay / flFireDelay;
  141. if ( pPlayer->GetViewModel( 0 ) )
  142. {
  143. pPlayer->GetViewModel( 0 )->SetPlaybackRate( flPlaybackRate );
  144. }
  145. if ( pPlayer->GetViewModel( 1 ) )
  146. {
  147. pPlayer->GetViewModel( 1 )->SetPlaybackRate( flPlaybackRate );
  148. }
  149. }
  150. // Set next attack times.
  151. m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay;
  152. // Don't push out secondary attack, because our secondary fire
  153. // systems are all separate from primary fire (sniper zooming, demoman pipebomb detonating, etc)
  154. //m_flNextSecondaryAttack = gpGlobals->curtime + m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay;
  155. // Set the idle animation times based on the sequence duration, so that we play full fire animations
  156. // that last longer than the refire rate may allow.
  157. if ( Clip1() > 0 )
  158. {
  159. SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
  160. }
  161. else
  162. {
  163. SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
  164. }
  165. // Check the reload mode and behave appropriately.
  166. if ( m_bReloadsSingly )
  167. {
  168. m_iReloadMode.Set( TF_RELOAD_START );
  169. }
  170. #ifdef STAGING_ONLY
  171. // Remove Cond if I attack
  172. if ( pPlayer->m_Shared.InCond( TF_COND_NO_COMBAT_SPEED_BOOST ) )
  173. {
  174. pPlayer->m_Shared.RemoveCond( TF_COND_NO_COMBAT_SPEED_BOOST );
  175. }
  176. #endif
  177. m_flLastPrimaryAttackTime = gpGlobals->curtime;
  178. if ( ShouldRemoveDisguiseOnPrimaryAttack() )
  179. {
  180. pPlayer->RemoveDisguise();
  181. }
  182. }
  183. bool CTFWeaponBaseGun::ShouldRemoveDisguiseOnPrimaryAttack() const
  184. {
  185. int iAttr = 0;
  186. CALL_ATTRIB_HOOK_INT( iAttr, keep_disguise_on_attack );
  187. if ( iAttr )
  188. return false;
  189. return true;
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose:
  193. //-----------------------------------------------------------------------------
  194. void CTFWeaponBaseGun::SecondaryAttack( void )
  195. {
  196. // semi-auto behaviour
  197. if ( m_bInAttack2 )
  198. return;
  199. // Get the player owning the weapon.
  200. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  201. if ( !pPlayer )
  202. return;
  203. pPlayer->DoClassSpecialSkill();
  204. m_bInAttack2 = true;
  205. #ifdef STAGING_ONLY
  206. // Remove Cond if I attack
  207. if ( pPlayer->m_Shared.InCond( TF_COND_NO_COMBAT_SPEED_BOOST ) )
  208. {
  209. pPlayer->m_Shared.RemoveCond( TF_COND_NO_COMBAT_SPEED_BOOST );
  210. }
  211. #endif
  212. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
  213. }
  214. CBaseEntity *CTFWeaponBaseGun::FireProjectile( CTFPlayer *pPlayer )
  215. {
  216. // New behavior: allow weapons to have attributes to specify what sort of
  217. // projectile they fire.
  218. int iProjectile = 0;
  219. CALL_ATTRIB_HOOK_INT( iProjectile, override_projectile_type );
  220. // Previous default behavior: ask the weapon type for what sort of projectile
  221. // to launch.
  222. if ( iProjectile == 0 )
  223. {
  224. iProjectile = GetWeaponProjectileType();
  225. }
  226. CBaseEntity *pProjectile = NULL;
  227. // Anyone ever hear of a factory? This is a disgrace.
  228. switch( iProjectile )
  229. {
  230. case TF_PROJECTILE_BULLET:
  231. FireBullet( pPlayer );
  232. //pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  233. break;
  234. case TF_PROJECTILE_ROCKET:
  235. pProjectile = FireRocket( pPlayer, iProjectile );
  236. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  237. break;
  238. case TF_PROJECTILE_SYRINGE:
  239. #ifdef STAGING_ONLY
  240. case TF_PROJECTILE_TRANQ:
  241. #endif // STAGING_ONLY
  242. pProjectile = FireNail( pPlayer, iProjectile );
  243. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  244. break;
  245. case TF_PROJECTILE_FLARE:
  246. pProjectile = FireFlare( pPlayer );
  247. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  248. break;
  249. case TF_PROJECTILE_PIPEBOMB:
  250. case TF_PROJECTILE_PIPEBOMB_REMOTE:
  251. case TF_PROJECTILE_PIPEBOMB_PRACTICE:
  252. case TF_PROJECTILE_CANNONBALL:
  253. pProjectile = FirePipeBomb( pPlayer, iProjectile );
  254. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  255. break;
  256. case TF_PROJECTILE_JAR:
  257. case TF_PROJECTILE_JAR_MILK:
  258. case TF_PROJECTILE_CLEAVER:
  259. case TF_PROJECTILE_THROWABLE:
  260. case TF_PROJECTILE_FESTIVE_JAR:
  261. case TF_PROJECTILE_BREADMONSTER_JARATE:
  262. case TF_PROJECTILE_BREADMONSTER_MADMILK:
  263. pProjectile = FireJar( pPlayer );
  264. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  265. break;
  266. case TF_PROJECTILE_ARROW:
  267. case TF_PROJECTILE_HEALING_BOLT:
  268. case TF_PROJECTILE_BUILDING_REPAIR_BOLT:
  269. case TF_PROJECTILE_FESTIVE_ARROW:
  270. case TF_PROJECTILE_FESTIVE_HEALING_BOLT:
  271. #ifdef STAGING_ONLY
  272. case TF_PROJECTILE_SNIPERBULLET:
  273. case TF_PROJECTILE_MILK_BOLT:
  274. #endif
  275. case TF_PROJECTILE_GRAPPLINGHOOK:
  276. pProjectile = FireArrow( pPlayer, ProjectileType_t( iProjectile ) );
  277. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  278. break;
  279. case TF_PROJECTILE_FLAME_ROCKET:
  280. pProjectile = FireFlameRocket( pPlayer );
  281. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_SECONDARY );
  282. break;
  283. case TF_PROJECTILE_ENERGY_BALL:
  284. pProjectile = FireEnergyBall( pPlayer );
  285. if ( ShouldPlayFireAnim() )
  286. {
  287. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  288. }
  289. break;
  290. case TF_PROJECTILE_ENERGY_RING:
  291. pProjectile = FireEnergyBall( pPlayer, true );
  292. if ( ShouldPlayFireAnim() )
  293. {
  294. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  295. }
  296. break;
  297. case TF_PROJECTILE_NONE:
  298. default:
  299. // do nothing!
  300. DevMsg( "Weapon does not have a projectile type set\n" );
  301. break;
  302. }
  303. RemoveProjectileAmmo( pPlayer );
  304. m_flLastFireTime = gpGlobals->curtime;
  305. DoFireEffects();
  306. UpdatePunchAngles( pPlayer );
  307. #ifdef GAME_DLL
  308. // Some game modes may allow any class to have stealth. Continuous-fire weapons like the
  309. // minigun might be firing when stealth is applied, so we try removing it from here, too.
  310. if ( pPlayer->m_Shared.IsStealthed() && ShouldRemoveInvisibilityOnPrimaryAttack() )
  311. {
  312. pPlayer->RemoveInvisibility();
  313. }
  314. #endif // GAME_DLL
  315. return pProjectile;
  316. }
  317. //-----------------------------------------------------------------------------
  318. void CTFWeaponBaseGun::RemoveProjectileAmmo( CTFPlayer *pPlayer )
  319. {
  320. if ( m_iClip1 != -1 )
  321. {
  322. m_iClip1 -= GetAmmoPerShot();
  323. }
  324. else
  325. {
  326. if ( m_iWeaponMode == TF_WEAPON_PRIMARY_MODE )
  327. {
  328. pPlayer->RemoveAmmo( GetAmmoPerShot(), m_iPrimaryAmmoType );
  329. #ifndef CLIENT_DLL
  330. // delayed ammo adding for the onhit attribute
  331. if ( m_iAmmoToAdd > 0 )
  332. {
  333. pPlayer->GiveAmmo( m_iAmmoToAdd, m_iPrimaryAmmoType );
  334. m_iAmmoToAdd = 0;
  335. }
  336. #endif
  337. }
  338. else
  339. {
  340. pPlayer->RemoveAmmo( GetAmmoPerShot(), m_iSecondaryAmmoType );
  341. #ifndef CLIENT_DLL
  342. // delayed ammo adding for the onhit attribute
  343. if ( m_iAmmoToAdd > 0 )
  344. {
  345. pPlayer->GiveAmmo( m_iAmmoToAdd, m_iSecondaryAmmoType );
  346. m_iAmmoToAdd = 0;
  347. }
  348. #endif
  349. }
  350. }
  351. }
  352. //-----------------------------------------------------------------------------
  353. // Purpose:
  354. //-----------------------------------------------------------------------------
  355. bool CTFWeaponBaseGun::HasPrimaryAmmo( void )
  356. {
  357. if ( m_iPrimaryAmmoType == TF_AMMO_METAL )
  358. {
  359. if ( GetOwner() && ( GetOwner()->GetAmmoCount( m_iPrimaryAmmoType ) < GetAmmoPerShot() ) )
  360. return false;
  361. }
  362. return BaseClass::HasPrimaryAmmo();
  363. }
  364. //-----------------------------------------------------------------------------
  365. // Purpose:
  366. //-----------------------------------------------------------------------------
  367. bool CTFWeaponBaseGun::CanDeploy( void )
  368. {
  369. if ( m_iPrimaryAmmoType == TF_AMMO_METAL )
  370. {
  371. if ( GetOwner() && ( GetOwner()->GetAmmoCount( m_iPrimaryAmmoType ) < GetAmmoPerShot() ) )
  372. return false;
  373. }
  374. return BaseClass::CanDeploy();
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose:
  378. //-----------------------------------------------------------------------------
  379. bool CTFWeaponBaseGun::CanBeSelected( void )
  380. {
  381. if ( m_iPrimaryAmmoType == TF_AMMO_METAL )
  382. {
  383. if ( GetOwner() && ( GetOwner()->GetAmmoCount( m_iPrimaryAmmoType ) < GetAmmoPerShot() ) )
  384. return false;
  385. }
  386. return BaseClass::CanBeSelected();
  387. }
  388. //-----------------------------------------------------------------------------
  389. // Purpose:
  390. //-----------------------------------------------------------------------------
  391. int CTFWeaponBaseGun::GetAmmoPerShot( void )
  392. {
  393. if ( IsEnergyWeapon() )
  394. return 0;
  395. else
  396. {
  397. int iAmmoPerShot = 0;
  398. CALL_ATTRIB_HOOK_INT( iAmmoPerShot, mod_ammo_per_shot );
  399. if ( iAmmoPerShot > 0 )
  400. return iAmmoPerShot;
  401. return m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_iAmmoPerShot;
  402. }
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Purpose:
  406. //-----------------------------------------------------------------------------
  407. void CTFWeaponBaseGun::UpdatePunchAngles( CTFPlayer *pPlayer )
  408. {
  409. // Update the player's punch angle.
  410. QAngle angle = pPlayer->GetPunchAngle();
  411. float flPunchAngle = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flPunchAngle;
  412. if ( flPunchAngle > 0 )
  413. {
  414. angle.x -= SharedRandomInt( "ShotgunPunchAngle", ( flPunchAngle - 1 ), ( flPunchAngle + 1 ) );
  415. pPlayer->SetPunchAngle( angle );
  416. }
  417. }
  418. //-----------------------------------------------------------------------------
  419. // Purpose: Fire a bullet!
  420. //-----------------------------------------------------------------------------
  421. void CTFWeaponBaseGun::FireBullet( CTFPlayer *pPlayer )
  422. {
  423. PlayWeaponShootSound();
  424. FX_FireBullets(
  425. this,
  426. pPlayer->entindex(),
  427. pPlayer->Weapon_ShootPosition(),
  428. pPlayer->EyeAngles() + pPlayer->GetPunchAngle(),
  429. GetWeaponID(),
  430. m_iWeaponMode,
  431. CBaseEntity::GetPredictionRandomSeed( UseServerRandomSeed() ) & 255,
  432. GetWeaponSpread(),
  433. GetProjectileDamage(),
  434. IsCurrentAttackACrit() );
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose: Fire a rocket
  438. //-----------------------------------------------------------------------------
  439. CBaseEntity *CTFWeaponBaseGun::FireRocket( CTFPlayer *pPlayer, int iRocketType )
  440. {
  441. PlayWeaponShootSound();
  442. // Server only - create the rocket.
  443. #ifdef GAME_DLL
  444. Vector vecSrc;
  445. QAngle angForward;
  446. Vector vecOffset( 23.5f, 12.0f, -3.0f );
  447. if ( pPlayer->GetFlags() & FL_DUCKING )
  448. {
  449. vecOffset.z = 8.0f;
  450. }
  451. GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward, false );
  452. trace_t trace;
  453. Vector vecEye = pPlayer->EyePosition();
  454. CTraceFilterSimple traceFilter( this, COLLISION_GROUP_NONE );
  455. UTIL_TraceLine( vecEye, vecSrc, MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
  456. CTFProjectile_Rocket *pProjectile = CTFProjectile_Rocket::Create( this, trace.endpos, angForward, pPlayer, pPlayer );
  457. if ( pProjectile )
  458. {
  459. pProjectile->SetCritical( IsCurrentAttackACrit() );
  460. pProjectile->SetDamage( GetProjectileDamage() );
  461. }
  462. return pProjectile;
  463. #endif
  464. return NULL;
  465. }
  466. //-----------------------------------------------------------------------------
  467. // Purpose: Fire an energy ball
  468. //-----------------------------------------------------------------------------
  469. CBaseEntity *CTFWeaponBaseGun::FireEnergyBall( CTFPlayer *pPlayer, bool bRing )
  470. {
  471. PlayWeaponShootSound();
  472. Vector vecSrc;
  473. QAngle angForward;
  474. Vector vecOffset( 23.5f, -8.0f, -3.0f );
  475. if ( pPlayer->GetFlags() & FL_DUCKING )
  476. {
  477. vecOffset.z = 8.0f;
  478. }
  479. GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward, false );
  480. trace_t trace;
  481. Vector vecEye = pPlayer->EyePosition();
  482. CTraceFilterSimple traceFilter( this, COLLISION_GROUP_NONE );
  483. UTIL_TraceLine( vecEye, vecSrc, MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
  484. if ( bRing )
  485. {
  486. CTFProjectile_EnergyRing* pProjectile = CTFProjectile_EnergyRing::Create( this, trace.endpos, angForward,
  487. GetProjectileSpeed(), GetProjectileGravity(), pPlayer, pPlayer, GetParticleColor(1), GetParticleColor(2), IsCurrentAttackACrit() );
  488. if ( pProjectile )
  489. {
  490. pProjectile->SetWeaponID( GetWeaponID() );
  491. pProjectile->SetCritical( IsCurrentAttackACrit() );
  492. #ifdef GAME_DLL
  493. pProjectile->SetDamage( GetProjectileDamage() );
  494. #endif
  495. }
  496. return pProjectile;
  497. }
  498. else
  499. {
  500. #ifdef GAME_DLL
  501. CTFProjectile_EnergyBall* pProjectile = CTFProjectile_EnergyBall::Create( trace.endpos, angForward, GetProjectileSpeed(), GetProjectileGravity(), pPlayer, pPlayer );
  502. if ( pProjectile )
  503. {
  504. pProjectile->SetLauncher( this );
  505. pProjectile->SetCritical( IsCurrentAttackACrit() );
  506. pProjectile->SetDamage( GetProjectileDamage() );
  507. }
  508. return pProjectile;
  509. #endif
  510. }
  511. return NULL;
  512. }
  513. //-----------------------------------------------------------------------------
  514. // Purpose: Fire a projectile nail
  515. //-----------------------------------------------------------------------------
  516. CBaseEntity *CTFWeaponBaseGun::FireNail( CTFPlayer *pPlayer, int iSpecificNail )
  517. {
  518. PlayWeaponShootSound();
  519. Vector vecSrc;
  520. QAngle angForward;
  521. // Add some spread
  522. float flSpread = 1.5;
  523. flSpread += GetProjectileSpread();
  524. CTFBaseProjectile *pProjectile = NULL;
  525. switch( iSpecificNail )
  526. {
  527. case TF_PROJECTILE_SYRINGE:
  528. {
  529. Vector vecOffset( 16, 6, -8 );
  530. GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward );
  531. angForward.x += RandomFloat( -flSpread, flSpread );
  532. angForward.y += RandomFloat( -flSpread, flSpread );
  533. pProjectile = CTFProjectile_Syringe::Create( vecSrc, angForward, this, pPlayer, pPlayer, IsCurrentAttackACrit() );
  534. }
  535. break;
  536. #ifdef STAGING_ONLY
  537. case TF_PROJECTILE_TRANQ:
  538. {
  539. Vector vecOffset( 16, 6, 0 );
  540. GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward );
  541. pProjectile = CTFProjectile_Tranq::Create( vecSrc, angForward, this, pPlayer, pPlayer, IsCurrentAttackACrit() );
  542. }
  543. break;
  544. #endif // STAGING_ONLY
  545. default:
  546. Assert(0);
  547. }
  548. if ( pProjectile )
  549. {
  550. pProjectile->SetWeaponID( GetWeaponID() );
  551. pProjectile->SetCritical( IsCurrentAttackACrit() );
  552. #ifdef GAME_DLL
  553. pProjectile->SetLauncher( this );
  554. pProjectile->SetDamage( GetProjectileDamage() );
  555. #endif
  556. }
  557. return pProjectile;
  558. }
  559. //-----------------------------------------------------------------------------
  560. // Purpose: Fire a pipe bomb
  561. //-----------------------------------------------------------------------------
  562. CBaseEntity *CTFWeaponBaseGun::FirePipeBomb( CTFPlayer *pPlayer, int iPipeBombType )
  563. {
  564. PlayWeaponShootSound();
  565. #ifdef GAME_DLL
  566. QAngle angEyes = pPlayer->EyeAngles();
  567. float flSpreadAngle = 0.0f;
  568. CALL_ATTRIB_HOOK_FLOAT( flSpreadAngle, projectile_spread_angle );
  569. if ( flSpreadAngle > 0.0f )
  570. {
  571. QAngle angSpread = RandomAngle( -flSpreadAngle, flSpreadAngle );
  572. angSpread.z = 0.0f;
  573. angEyes += angSpread;
  574. DevMsg( "Fire bomb at %f %f %f\n", XYZ(angEyes) );
  575. }
  576. Vector vecForward, vecRight, vecUp;
  577. AngleVectors( angEyes, &vecForward, &vecRight, &vecUp );
  578. // Create grenades here!!
  579. float fRight = 8.f;
  580. if ( IsViewModelFlipped() )
  581. {
  582. fRight *= -1;
  583. }
  584. Vector vecSrc = pPlayer->Weapon_ShootPosition();
  585. vecSrc += vecForward * 16.0f + vecRight * fRight + vecUp * -6.0f;
  586. trace_t trace;
  587. Vector vecEye = pPlayer->EyePosition();
  588. CTraceFilterSimple traceFilter( this, COLLISION_GROUP_NONE );
  589. UTIL_TraceHull( vecEye, vecSrc, -Vector(8,8,8), Vector(8,8,8), MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
  590. // If we started in solid, don't let them fire at all
  591. if ( trace.startsolid )
  592. return NULL;
  593. float flLaunchSpeed = GetProjectileSpeed();
  594. CALL_ATTRIB_HOOK_FLOAT( flLaunchSpeed, mult_projectile_range );
  595. Vector vecVelocity = ( vecForward * flLaunchSpeed ) + ( vecUp * 200.0f ) + ( random->RandomFloat( -10.0f, 10.0f ) * vecRight ) +
  596. ( random->RandomFloat( -10.0f, 10.0f ) * vecUp );
  597. float flMultDmg = 1.f;
  598. CALL_ATTRIB_HOOK_FLOAT( flMultDmg, mult_dmg );
  599. // no spin for loch-n-load
  600. Vector angImpulse = AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 );
  601. int iNoSpin = 0;
  602. CALL_ATTRIB_HOOK_INT( iNoSpin, grenade_no_spin );
  603. if ( iNoSpin )
  604. {
  605. angImpulse.Zero();
  606. }
  607. CTFGrenadePipebombProjectile *pProjectile = CTFGrenadePipebombProjectile::Create( trace.endpos, angEyes, vecVelocity, angImpulse, pPlayer, GetTFWpnData(), iPipeBombType, flMultDmg );
  608. if ( pProjectile )
  609. {
  610. pProjectile->SetCritical( IsCurrentAttackACrit() );
  611. pProjectile->SetLauncher( this );
  612. //float flFizzle = 0;
  613. //CALL_ATTRIB_HOOK_FLOAT( flFizzle, stickybomb_fizzle_time );
  614. //if ( flFizzle )
  615. //{
  616. // pProjectile->SetDetonateTimerLength( flFizzle );
  617. //}
  618. CAttribute_String attrCustomModelName;
  619. GetCustomProjectileModel( &attrCustomModelName );
  620. if ( attrCustomModelName.has_value() )
  621. {
  622. pProjectile->SetModel( attrCustomModelName.value().c_str() );
  623. }
  624. }
  625. return pProjectile;
  626. #endif
  627. return NULL;
  628. }
  629. //-----------------------------------------------------------------------------
  630. // Purpose: Fire a flare
  631. //-----------------------------------------------------------------------------
  632. CBaseEntity *CTFWeaponBaseGun::FireFlare( CTFPlayer *pPlayer )
  633. {
  634. PlayWeaponShootSound();
  635. // Server only - create the flare.
  636. #ifdef GAME_DLL
  637. Vector vecSrc;
  638. QAngle angForward;
  639. Vector vecOffset( 23.5f, 12.0f, -3.0f );
  640. if ( pPlayer->GetFlags() & FL_DUCKING )
  641. {
  642. vecOffset.z = 8.0f;
  643. }
  644. GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward, false );
  645. CTFProjectile_Flare *pProjectile = CTFProjectile_Flare::Create( this, vecSrc, angForward, pPlayer, pPlayer );
  646. if ( pProjectile )
  647. {
  648. pProjectile->SetLauncher( this );
  649. pProjectile->SetCritical( IsCurrentAttackACrit() );
  650. pProjectile->SetDamage( GetProjectileDamage() );
  651. CTFFlareGun *pFlareGun = dynamic_cast<CTFFlareGun *>( this );
  652. if ( pFlareGun && pFlareGun->GetFlareGunType() == FLAREGUN_DETONATE )
  653. {
  654. pFlareGun->AddFlare( pProjectile );
  655. }
  656. }
  657. return pProjectile;
  658. #endif
  659. return NULL;
  660. }
  661. //-----------------------------------------------------------------------------
  662. // Purpose: Fire an arrow
  663. //-----------------------------------------------------------------------------
  664. CBaseEntity *CTFWeaponBaseGun::FireArrow( CTFPlayer *pPlayer, ProjectileType_t projectileType )
  665. {
  666. PlayWeaponShootSound();
  667. // Server only - create the rocket.
  668. #ifdef GAME_DLL
  669. Vector vecSrc;
  670. QAngle angForward;
  671. Vector vecOffset( 23.5f, -8.0f, -3.0f );
  672. #ifdef STAGING_ONLY
  673. if ( projectileType == TF_PROJECTILE_SNIPERBULLET )
  674. {
  675. // Center the bullet while zoomed, otherwise flip the arrow cause arrows are dumb
  676. if ( pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
  677. {
  678. vecOffset = Vector( 32, 0, -2.0f );
  679. }
  680. else
  681. {
  682. vecOffset.y = 8.0f;
  683. }
  684. }
  685. #endif // STAGING_ONLY
  686. GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward, false );
  687. CTFProjectile_Arrow *pProjectile = CTFProjectile_Arrow::Create( vecSrc, angForward, GetProjectileSpeed(), GetProjectileGravity(), projectileType, pPlayer, pPlayer );
  688. if ( pProjectile )
  689. {
  690. pProjectile->SetLauncher( this );
  691. pProjectile->SetCritical( IsCurrentAttackACrit() );
  692. pProjectile->SetDamage( GetProjectileDamage() );
  693. int iPenetrate = 0;
  694. CALL_ATTRIB_HOOK_INT( iPenetrate, projectile_penetration );
  695. if ( iPenetrate == 1 )
  696. {
  697. pProjectile->SetPenetrate( true );
  698. }
  699. pProjectile->SetCollisionGroup( TFCOLLISION_GROUP_ROCKET_BUT_NOT_WITH_OTHER_ROCKETS );
  700. }
  701. return pProjectile;
  702. #endif
  703. return NULL;
  704. }
  705. //-----------------------------------------------------------------------------
  706. // Purpose: Toss a Jar...of something...
  707. //-----------------------------------------------------------------------------
  708. CBaseEntity *CTFWeaponBaseGun::FireJar( CTFPlayer *pPlayer )
  709. {
  710. return NULL;
  711. }
  712. //-----------------------------------------------------------------------------
  713. // Purpose:
  714. //-----------------------------------------------------------------------------
  715. CBaseEntity *CTFWeaponBaseGun::FireFlameRocket( CTFPlayer *pPlayer )
  716. {
  717. return NULL;
  718. }
  719. //-----------------------------------------------------------------------------
  720. // Purpose:
  721. //-----------------------------------------------------------------------------
  722. void CTFWeaponBaseGun::PlayWeaponShootSound( void )
  723. {
  724. if ( IsCurrentAttackACrit() )
  725. {
  726. WeaponSound( BURST );
  727. }
  728. else
  729. {
  730. WeaponSound( SINGLE );
  731. }
  732. }
  733. //-----------------------------------------------------------------------------
  734. // Purpose:
  735. //-----------------------------------------------------------------------------
  736. float CTFWeaponBaseGun::GetWeaponSpread( void )
  737. {
  738. float fSpread = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flSpread;
  739. CALL_ATTRIB_HOOK_FLOAT( fSpread, mult_spread_scale );
  740. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  741. if ( pPlayer )
  742. {
  743. if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_PRECISION )
  744. {
  745. if ( GetWeaponID() == TF_WEAPON_MINIGUN )
  746. {
  747. fSpread *= 0.4f;
  748. }
  749. else
  750. {
  751. fSpread *= 0.1f;
  752. }
  753. }
  754. // Some weapons change fire delay based on player's health
  755. float flReducedHealthBonus = 1.0f;
  756. CALL_ATTRIB_HOOK_FLOAT( flReducedHealthBonus, panic_attack_negative );
  757. if ( flReducedHealthBonus != 1.0f )
  758. {
  759. flReducedHealthBonus = RemapValClamped( pPlayer->HealthFraction(), 0.2f, 0.9f, flReducedHealthBonus, 1.0f );
  760. fSpread *= flReducedHealthBonus;
  761. }
  762. }
  763. return fSpread;
  764. }
  765. //-----------------------------------------------------------------------------
  766. void CTFWeaponBaseGun::GetCustomProjectileModel( CAttribute_String *attrCustomProjModel )
  767. {
  768. // Must still add these to a precache somewhere
  769. // ie CTFGrenadePipebombProjectile::Precache()
  770. static CSchemaAttributeDefHandle pAttrDef_ProjectileEntityName( "custom projectile model" );
  771. CEconItemView *pItem = GetAttributeContainer()->GetItem();
  772. if ( pAttrDef_ProjectileEntityName && pItem )
  773. {
  774. pItem->FindAttribute( pAttrDef_ProjectileEntityName, attrCustomProjModel );
  775. }
  776. }
  777. //-----------------------------------------------------------------------------
  778. // Purpose: Accessor for damage, so sniper etc can modify damage
  779. //-----------------------------------------------------------------------------
  780. float CTFWeaponBaseGun::GetProjectileDamage( void )
  781. {
  782. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  783. float flDamage = (float)m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_nDamage;
  784. CALL_ATTRIB_HOOK_FLOAT( flDamage, mult_dmg );
  785. // Some weapons mod dmg when not disguised
  786. bool bDisguised = pPlayer && pPlayer->m_Shared.InCond( TF_COND_DISGUISED );
  787. if ( bDisguised )
  788. {
  789. CALL_ATTRIB_HOOK_FLOAT( flDamage, mult_dmg_disguised );
  790. }
  791. if ( pPlayer && ( pPlayer->IsPlayerClass( TF_CLASS_SOLDIER ) || pPlayer->IsPlayerClass( TF_CLASS_PYRO ) ) )
  792. {
  793. float flRageDamage = 1.f;
  794. CALL_ATTRIB_HOOK_FLOAT( flRageDamage, rage_damage );
  795. if ( flRageDamage > 1.f )
  796. {
  797. float flRageRatio = pPlayer->m_Shared.GetRageMeter() / 100.f;
  798. flRageDamage = (flRageDamage - 1.f) * flRageRatio;
  799. flDamage *= 1.f + flRageDamage;
  800. }
  801. }
  802. #ifdef GAME_DLL
  803. float flMedicHealDamageBonus = 1.f;
  804. CALL_ATTRIB_HOOK_FLOAT( flMedicHealDamageBonus, medic_healed_damage_bonus );
  805. if ( flMedicHealDamageBonus > 1.f )
  806. {
  807. if ( pPlayer )
  808. {
  809. int numHealers = pPlayer->m_Shared.GetNumHealers();
  810. bool bHealedByMedic = false;
  811. for ( int i=0; i<numHealers; i++ )
  812. {
  813. if ( ToTFPlayer( pPlayer->m_Shared.GetHealerByIndex( i ) ) != NULL )
  814. {
  815. bHealedByMedic = true;
  816. }
  817. }
  818. if ( bHealedByMedic )
  819. {
  820. flDamage *= flMedicHealDamageBonus;
  821. }
  822. }
  823. }
  824. if ( GetWeaponProjectileType() == TF_PROJECTILE_BULLET )
  825. {
  826. float flScaleDamage = 1.f;
  827. CALL_ATTRIB_HOOK_FLOAT( flScaleDamage, accuracy_scales_damage );
  828. if ( flScaleDamage > 1.f )
  829. {
  830. // Bullets fired vs hit ratio over last x.x second(s)
  831. if ( gpGlobals->curtime < GetLastHitTime() + 0.7f )
  832. {
  833. float flRatio = (float)m_iHitsInTime / (float)m_iFiredInTime;
  834. float flDmgMod = RemapValClamped( flRatio, 0.f, 1.f, 1.f, flScaleDamage );
  835. // DevMsg( "A: %f - D: %f\n", flRatio, flDmgMod );
  836. // DevMsg( "H: %d - F: %d\n", m_iHitsInTime, m_iFiredInTime );
  837. flDamage *= flDmgMod;
  838. }
  839. else
  840. {
  841. m_iHitsInTime = 1;
  842. m_iFiredInTime = 1;
  843. }
  844. }
  845. }
  846. #endif
  847. return flDamage;
  848. }
  849. //-----------------------------------------------------------------------------
  850. // Purpose:
  851. //-----------------------------------------------------------------------------
  852. bool CTFWeaponBaseGun::Holster( CBaseCombatWeapon *pSwitchingTo )
  853. {
  854. // Server specific.
  855. #if !defined( CLIENT_DLL )
  856. // Make sure to zoom out before we holster the weapon.
  857. ZoomOut();
  858. SetContextThink( NULL, 0, ZOOM_CONTEXT );
  859. #endif
  860. return BaseClass::Holster( pSwitchingTo );
  861. }
  862. //-----------------------------------------------------------------------------
  863. // Purpose:
  864. // NOTE: Should this be put into fire gun
  865. //-----------------------------------------------------------------------------
  866. void CTFWeaponBaseGun::DoFireEffects()
  867. {
  868. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  869. if ( !pPlayer )
  870. return;
  871. if ( ShouldDoMuzzleFlash() )
  872. {
  873. pPlayer->DoMuzzleFlash();
  874. }
  875. }
  876. //-----------------------------------------------------------------------------
  877. // Purpose:
  878. //-----------------------------------------------------------------------------
  879. void CTFWeaponBaseGun::ToggleZoom( void )
  880. {
  881. // Toggle the zoom.
  882. CBasePlayer *pPlayer = GetPlayerOwner();
  883. if ( pPlayer )
  884. {
  885. if( pPlayer->GetFOV() >= 75 )
  886. {
  887. ZoomIn();
  888. }
  889. else
  890. {
  891. ZoomOut();
  892. }
  893. }
  894. // Get the zoom animation time.
  895. m_flNextSecondaryAttack = gpGlobals->curtime + 1.2;
  896. }
  897. //-----------------------------------------------------------------------------
  898. // Purpose:
  899. //-----------------------------------------------------------------------------
  900. void CTFWeaponBaseGun::ZoomIn( void )
  901. {
  902. // The the owning player.
  903. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  904. if ( !pPlayer )
  905. return;
  906. // Set the weapon zoom.
  907. // TODO: The weapon fov should be gotten from the script file.
  908. float fBaseZoom = TF_WEAPON_ZOOM_FOV;
  909. // Disabled this for now, because we have no attributes using it
  910. //CALL_ATTRIB_HOOK_FLOAT( fBaseZoom, mult_zoom_fov );
  911. pPlayer->SetFOV( pPlayer, fBaseZoom, 0.1f );
  912. pPlayer->m_Shared.AddCond( TF_COND_ZOOMED );
  913. #if defined( CLIENT_DLL )
  914. // Doing this allows us to show/hide the player viewmodel/localmodel
  915. pPlayer->UpdateVisibility();
  916. #endif
  917. }
  918. //-----------------------------------------------------------------------------
  919. // Purpose:
  920. //-----------------------------------------------------------------------------
  921. void CTFWeaponBaseGun::ZoomOut( void )
  922. {
  923. // The the owning player.
  924. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  925. if ( !pPlayer )
  926. return;
  927. if ( pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
  928. {
  929. // Set the FOV to 0 set the default FOV.
  930. pPlayer->SetFOV( pPlayer, 0, 0.1f );
  931. pPlayer->m_Shared.RemoveCond( TF_COND_ZOOMED );
  932. }
  933. #if defined( CLIENT_DLL )
  934. // Doing this allows us to show/hide the player viewmodel/localmodel
  935. pPlayer->UpdateVisibility();
  936. #endif
  937. }
  938. //-----------------------------------------------------------------------------
  939. // Purpose:
  940. //-----------------------------------------------------------------------------
  941. void CTFWeaponBaseGun::ZoomOutIn( void )
  942. {
  943. //Zoom out, set think to zoom back in.
  944. ZoomOut();
  945. SetContextThink( &CTFWeaponBaseGun::ZoomIn, gpGlobals->curtime + ZOOM_REZOOM_TIME, ZOOM_CONTEXT );
  946. }
  947. //-----------------------------------------------------------------------------
  948. bool CTFWeaponBaseGun::HasLastShotCritical( void )
  949. {
  950. if ( m_iClip1 == 1 )
  951. {
  952. int iAttr = 0;
  953. CALL_ATTRIB_HOOK_INT( iAttr, last_shot_crits );
  954. if ( iAttr )
  955. {
  956. return true;
  957. }
  958. }
  959. return false;
  960. }