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.

580 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_weapon_shotgun.h"
  8. #include "decals.h"
  9. #include "tf_fx_shared.h"
  10. #include "takedamageinfo.h"
  11. #include "tf_gamerules.h"
  12. // Client specific.
  13. #if defined( CLIENT_DLL )
  14. #include "c_tf_player.h"
  15. // Server specific.
  16. #else
  17. #include "tf_player.h"
  18. #include "ilagcompensationmanager.h"
  19. #include "collisionutils.h"
  20. #include "in_buttons.h"
  21. #endif
  22. //=============================================================================
  23. //
  24. // Weapon Shotgun tables.
  25. //
  26. CREATE_SIMPLE_WEAPON_TABLE( TFShotgun, tf_weapon_shotgun_primary )
  27. CREATE_SIMPLE_WEAPON_TABLE( TFShotgun_Soldier, tf_weapon_shotgun_soldier )
  28. CREATE_SIMPLE_WEAPON_TABLE( TFShotgun_HWG, tf_weapon_shotgun_hwg )
  29. CREATE_SIMPLE_WEAPON_TABLE( TFShotgun_Pyro, tf_weapon_shotgun_pyro )
  30. CREATE_SIMPLE_WEAPON_TABLE( TFScatterGun, tf_weapon_scattergun )
  31. CREATE_SIMPLE_WEAPON_TABLE( TFShotgun_Revenge, tf_weapon_sentry_revenge )
  32. CREATE_SIMPLE_WEAPON_TABLE( TFSodaPopper, tf_weapon_soda_popper )
  33. CREATE_SIMPLE_WEAPON_TABLE( TFPEPBrawlerBlaster, tf_weapon_pep_brawler_blaster )
  34. CREATE_SIMPLE_WEAPON_TABLE( TFShotgunBuildingRescue, tf_weapon_shotgun_building_rescue )
  35. #define SCATTERGUN_KNOCKBACK_MIN_DMG 30.0f
  36. #define SCATTERGUN_KNOCKBACK_MIN_RANGE_SQ 160000.0f //400x400
  37. //=============================================================================
  38. //
  39. // Weapon Shotgun functions.
  40. //
  41. bool CanScatterGunKnockBack( CTFWeaponBase *pWeapon, float flDamage, float flDistanceSq )
  42. {
  43. int nBulletKnockBack = 0;
  44. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, nBulletKnockBack, set_scattergun_has_knockback );
  45. if ( nBulletKnockBack != 0 )
  46. {
  47. if (flDamage > SCATTERGUN_KNOCKBACK_MIN_DMG && flDistanceSq < SCATTERGUN_KNOCKBACK_MIN_RANGE_SQ )
  48. return true;
  49. float flKnockbackMult = 1.0f;
  50. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWeapon, flKnockbackMult, scattergun_knockback_mult );
  51. if ( flKnockbackMult > 1.0f )
  52. return true;
  53. }
  54. return false;
  55. }
  56. //-----------------------------------------------------------------------------
  57. // Purpose:
  58. //-----------------------------------------------------------------------------
  59. CTFShotgun::CTFShotgun()
  60. {
  61. m_bReloadsSingly = true;
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose:
  65. //-----------------------------------------------------------------------------
  66. void CTFShotgun::PrimaryAttack()
  67. {
  68. if ( !CanAttack() )
  69. return;
  70. // Set the weapon mode.
  71. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
  72. BaseClass::PrimaryAttack();
  73. }
  74. //-----------------------------------------------------------------------------
  75. // Purpose:
  76. //-----------------------------------------------------------------------------
  77. void CTFShotgun::UpdatePunchAngles( CTFPlayer *pPlayer )
  78. {
  79. // Update the player's punch angle.
  80. QAngle angle = pPlayer->GetPunchAngle();
  81. float flPunchAngle = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flPunchAngle;
  82. angle.x -= SharedRandomInt( "ShotgunPunchAngle", ( flPunchAngle - 1 ), ( flPunchAngle + 1 ) );
  83. pPlayer->SetPunchAngle( angle );
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose:
  87. //-----------------------------------------------------------------------------
  88. void CTFShotgun::PlayWeaponShootSound( void )
  89. {
  90. BaseClass::PlayWeaponShootSound();
  91. if ( TFGameRules()->GameModeUsesUpgrades() )
  92. {
  93. PlayUpgradedShootSound( "Weapon_Upgrade.DamageBonus" );
  94. }
  95. }
  96. //-----------------------------------------------------------------------------
  97. // CTFShotgun_Revenge
  98. //-----------------------------------------------------------------------------
  99. CTFShotgun_Revenge::CTFShotgun_Revenge()
  100. {
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose:
  104. //-----------------------------------------------------------------------------
  105. void CTFShotgun_Revenge::Precache()
  106. {
  107. int iModelIndex = PrecacheModel( TF_WEAPON_TAUNT_FRONTIER_JUSTICE_GUITAR_MODEL );
  108. PrecacheGibsForModel( iModelIndex );
  109. PrecacheParticleSystem( "blood_impact_backscatter" );
  110. BaseClass::Precache();
  111. }
  112. //-----------------------------------------------------------------------------
  113. // Purpose:
  114. //-----------------------------------------------------------------------------
  115. void CTFShotgun_Revenge::PrimaryAttack()
  116. {
  117. if ( !CanAttack() )
  118. return;
  119. BaseClass::PrimaryAttack();
  120. // Do this after the attack, so that we know if we are doing custom damage
  121. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  122. if ( pOwner )
  123. {
  124. int iRevengeCrits = pOwner->m_Shared.GetRevengeCrits();
  125. pOwner->m_Shared.SetRevengeCrits( iRevengeCrits-1 );
  126. }
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. //-----------------------------------------------------------------------------
  131. void CTFShotgun_Revenge::SentryKilled( int iCrits )
  132. {
  133. int val = 0;
  134. CALL_ATTRIB_HOOK_INT( val, sentry_killed_revenge );
  135. if ( val == 1 )
  136. {
  137. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  138. if ( pOwner )
  139. {
  140. pOwner->m_Shared.SetRevengeCrits( pOwner->m_Shared.GetRevengeCrits() + iCrits );
  141. }
  142. }
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Purpose:
  146. //-----------------------------------------------------------------------------
  147. bool CTFShotgun_Revenge::Holster( CBaseCombatWeapon *pSwitchingTo )
  148. {
  149. #ifdef GAME_DLL
  150. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  151. if ( pOwner && pOwner->m_Shared.GetRevengeCrits() )
  152. {
  153. pOwner->m_Shared.RemoveCond( TF_COND_CRITBOOSTED );
  154. }
  155. #endif
  156. return BaseClass::Holster( pSwitchingTo );
  157. }
  158. //-----------------------------------------------------------------------------
  159. // Purpose:
  160. //-----------------------------------------------------------------------------
  161. bool CTFShotgun_Revenge::Deploy( void )
  162. {
  163. #ifdef GAME_DLL
  164. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  165. if ( pOwner && pOwner->m_Shared.GetRevengeCrits() )
  166. {
  167. pOwner->m_Shared.AddCond( TF_COND_CRITBOOSTED );
  168. }
  169. #endif
  170. return BaseClass::Deploy();
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Purpose:
  174. //-----------------------------------------------------------------------------
  175. int CTFShotgun_Revenge::GetCustomDamageType() const
  176. {
  177. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  178. if ( pOwner )
  179. {
  180. int iRevengeCrits = pOwner->m_Shared.GetRevengeCrits();
  181. return iRevengeCrits > 0 ? TF_DMG_CUSTOM_SHOTGUN_REVENGE_CRIT : TF_DMG_CUSTOM_NONE;
  182. }
  183. return TF_DMG_CUSTOM_NONE;
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose:
  187. //-----------------------------------------------------------------------------
  188. int CTFShotgun_Revenge::GetCount( void )
  189. {
  190. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  191. if ( pOwner )
  192. {
  193. return pOwner->m_Shared.GetRevengeCrits();
  194. }
  195. return 0;
  196. }
  197. #ifdef CLIENT_DLL
  198. //-----------------------------------------------------------------------------
  199. // Purpose:
  200. // ----------------------------------------------------------------------------
  201. void CTFShotgun_Revenge::SetWeaponVisible( bool visible )
  202. {
  203. if ( !visible )
  204. {
  205. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  206. if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) && pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_ENGINEER && pPlayer->m_Shared.GetTauntIndex() == TAUNT_BASE_WEAPON )
  207. {
  208. int nModelIndex = modelinfo->GetModelIndex( TF_WEAPON_TAUNT_FRONTIER_JUSTICE_GUITAR_MODEL );
  209. CUtlVector<breakmodel_t> guitarGibs;
  210. BuildGibList( guitarGibs, nModelIndex, 1.0f, COLLISION_GROUP_NONE );
  211. if ( guitarGibs.Count() > 0 )
  212. {
  213. Vector vForward, vRight, vUp;
  214. AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
  215. Vector vecBreakVelocity = Vector(0,0,200);
  216. AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 );
  217. Vector vecOrigin = GetAbsOrigin() + vForward*70 + vUp*10;
  218. QAngle vecAngle = GetAbsAngles();
  219. breakablepropparams_t breakParams( vecOrigin, vecAngle, vecBreakVelocity, angularImpulse );
  220. breakParams.impactEnergyScale = 1.0f;
  221. CreateGibsFromList( guitarGibs, nModelIndex, NULL, breakParams, NULL, -1 , false, true );
  222. }
  223. }
  224. }
  225. BaseClass::SetWeaponVisible( visible );
  226. }
  227. //-----------------------------------------------------------------------------
  228. // Purpose:
  229. // ----------------------------------------------------------------------------
  230. int CTFShotgun_Revenge::GetWorldModelIndex( void )
  231. {
  232. // Engineer guitar support.
  233. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  234. if ( pPlayer && pPlayer->GetPlayerClass() && ( pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_ENGINEER ) &&
  235. ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) ) && ( pPlayer->m_Shared.GetTauntIndex() == TAUNT_BASE_WEAPON ) )
  236. {
  237. // While we are taunting, replace our normal world model with the guitar.
  238. m_iWorldModelIndex = modelinfo->GetModelIndex( TF_WEAPON_TAUNT_FRONTIER_JUSTICE_GUITAR_MODEL );
  239. return m_iWorldModelIndex;
  240. }
  241. return BaseClass::GetWorldModelIndex();
  242. }
  243. #endif
  244. #ifdef GAME_DLL
  245. //-----------------------------------------------------------------------------
  246. // Purpose: Reset revenge crits when the shotgun is changed
  247. //-----------------------------------------------------------------------------
  248. void CTFShotgun_Revenge::Detach( void )
  249. {
  250. CTFPlayer *pPlayer = GetTFPlayerOwner();
  251. if ( pPlayer )
  252. {
  253. pPlayer->m_Shared.SetRevengeCrits( 0 );
  254. pPlayer->m_Shared.RemoveCond( TF_COND_CRITBOOSTED );
  255. }
  256. BaseClass::Detach();
  257. }
  258. #endif
  259. //-----------------------------------------------------------------------------
  260. // Purpose:
  261. //-----------------------------------------------------------------------------
  262. bool CTFScatterGun::Reload( void )
  263. {
  264. int iWeaponMod = 0;
  265. CALL_ATTRIB_HOOK_INT( iWeaponMod, set_scattergun_no_reload_single );
  266. if ( iWeaponMod == 1 )
  267. {
  268. m_bReloadsSingly = false;
  269. }
  270. return BaseClass::Reload();
  271. }
  272. #define JUMP_SPEED 268.3281572999747f
  273. extern ConVar tf_player_movement_stun_time;
  274. extern float AirBurstDamageForce( const Vector &size, float damage, float scale );
  275. //-----------------------------------------------------------------------------
  276. // Purpose:
  277. //-----------------------------------------------------------------------------
  278. void CTFScatterGun::FireBullet( CTFPlayer *pPlayer )
  279. {
  280. #ifndef CLIENT_DLL
  281. if ( HasKnockback() )
  282. {
  283. // Perform some knock back.
  284. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  285. if ( !pOwner )
  286. return;
  287. // No knockback during pre-round freeze.
  288. if ( TFGameRules() && (TFGameRules()->State_Get() == GR_STATE_PREROUND) )
  289. return;
  290. // Knock the firer back!
  291. if ( !(pOwner->GetFlags() & FL_ONGROUND) && !pPlayer->m_bScattergunJump )
  292. {
  293. pPlayer->m_bScattergunJump = true;
  294. pOwner->m_Shared.StunPlayer( 0.3f, 1.f, TF_STUN_MOVEMENT | TF_STUN_MOVEMENT_FORWARD_ONLY );
  295. float flForce = AirBurstDamageForce( pOwner->WorldAlignSize(), 60, 6.f );
  296. Vector vecForward;
  297. AngleVectors( pOwner->EyeAngles(), &vecForward );
  298. Vector vecForce = vecForward * -flForce;
  299. EntityMatrix mtxPlayer;
  300. mtxPlayer.InitFromEntity( pOwner );
  301. Vector vecAbsVelocity = pOwner->GetAbsVelocity();
  302. Vector vecAbsVelocityAsPoint = vecAbsVelocity + pOwner->GetAbsOrigin();
  303. Vector vecLocalVelocity = mtxPlayer.WorldToLocal( vecAbsVelocityAsPoint );
  304. vecLocalVelocity.x = -300;
  305. vecAbsVelocityAsPoint = mtxPlayer.LocalToWorld( vecLocalVelocity );
  306. vecAbsVelocity = vecAbsVelocityAsPoint - pOwner->GetAbsOrigin();
  307. pOwner->SetAbsVelocity( vecAbsVelocity );
  308. // Impulse an additional bit of Z push.
  309. pOwner->ApplyAbsVelocityImpulse( Vector(0,0,50.f) );
  310. // Slow player movement for a brief period of time.
  311. pOwner->RemoveFlag( FL_ONGROUND );
  312. }
  313. }
  314. #endif
  315. BaseClass::FireBullet( pPlayer );
  316. }
  317. //-----------------------------------------------------------------------------
  318. // Purpose:
  319. //-----------------------------------------------------------------------------
  320. void CTFScatterGun::ApplyPostHitEffects( const CTakeDamageInfo &inputInfo, CTFPlayer *pPlayer )
  321. {
  322. #ifndef CLIENT_DLL
  323. if ( !HasKnockback() )
  324. return;
  325. CTFPlayer *pAttacker = ToTFPlayer( inputInfo.GetAttacker() );
  326. if ( !pAttacker )
  327. return;
  328. CTFPlayer *pTarget = pPlayer;
  329. if ( !pTarget )
  330. return;
  331. if ( pTarget->m_Shared.GetWeaponKnockbackID() > -1 )
  332. return;
  333. float flDam = inputInfo.GetDamage();
  334. Vector vecDir = pAttacker->WorldSpaceCenter() - pTarget->WorldSpaceCenter();
  335. if ( !CanScatterGunKnockBack( this, flDam, vecDir.LengthSqr() ) )
  336. return;
  337. // Immune to pushback/knockback
  338. if ( pTarget->m_Shared.InCond( TF_COND_MEGAHEAL ) )
  339. return;
  340. VectorNormalize( vecDir );
  341. float flKnockbackMult = 3.0f;
  342. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( this, flKnockbackMult, scattergun_knockback_mult );
  343. float flForce = AirBurstDamageForce( pTarget->WorldAlignSize(), flDam, flKnockbackMult );
  344. Vector vecForce = vecDir * -flForce;
  345. vecForce.z += JUMP_SPEED;
  346. pTarget->ApplyAirBlastImpulse( vecForce );
  347. pTarget->m_Shared.StunPlayer( 0.3f, 1.f, TF_STUN_MOVEMENT | TF_STUN_MOVEMENT_FORWARD_ONLY, pAttacker );
  348. pTarget->m_Shared.SetWeaponKnockbackID( pAttacker->GetUserID() );
  349. #endif
  350. }
  351. //-----------------------------------------------------------------------------
  352. // Purpose:
  353. //-----------------------------------------------------------------------------
  354. void CTFScatterGun::FinishReload( void )
  355. {
  356. CTFPlayer* pOwner = ToTFPlayer( GetOwner() );
  357. if ( !pOwner )
  358. return;
  359. if ( UsesClipsForAmmo1() && !m_bReloadsSingly )
  360. {
  361. int primary = MIN( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
  362. m_iClip1 += primary;
  363. // Takes a whole clip worth of ammo to reload, causing us to lose whatever was chambered.
  364. pOwner->RemoveAmmo( GetMaxClip1(), m_iPrimaryAmmoType);
  365. }
  366. }
  367. //-----------------------------------------------------------------------------
  368. // Purpose:
  369. //-----------------------------------------------------------------------------
  370. bool CTFScatterGun::HasKnockback( void )
  371. {
  372. int iWeaponMod = 0;
  373. CALL_ATTRIB_HOOK_INT( iWeaponMod, set_scattergun_has_knockback );
  374. if ( iWeaponMod == 1 )
  375. return true;
  376. else
  377. return false;
  378. }
  379. //-----------------------------------------------------------------------------
  380. // Purpose: Play animation appropriate to ball status.
  381. //-----------------------------------------------------------------------------
  382. bool CTFScatterGun::SendWeaponAnim( int iActivity )
  383. {
  384. CTFPlayer *pPlayer = GetTFPlayerOwner();
  385. if ( !pPlayer )
  386. return BaseClass::SendWeaponAnim( iActivity );
  387. if ( HasKnockback() )
  388. {
  389. // Knockback version uses a different model and animation set.
  390. switch ( iActivity )
  391. {
  392. case ACT_VM_DRAW:
  393. iActivity = ACT_ITEM2_VM_DRAW;
  394. break;
  395. case ACT_VM_HOLSTER:
  396. iActivity = ACT_ITEM2_VM_HOLSTER;
  397. break;
  398. case ACT_VM_IDLE:
  399. iActivity = ACT_ITEM2_VM_IDLE;
  400. break;
  401. case ACT_VM_PULLBACK:
  402. iActivity = ACT_ITEM2_VM_PULLBACK;
  403. break;
  404. case ACT_VM_PRIMARYATTACK:
  405. iActivity = ACT_ITEM2_VM_PRIMARYATTACK;
  406. break;
  407. case ACT_VM_SECONDARYATTACK:
  408. iActivity = ACT_ITEM2_VM_SECONDARYATTACK;
  409. break;
  410. case ACT_VM_RELOAD:
  411. iActivity = ACT_ITEM2_VM_RELOAD;
  412. break;
  413. case ACT_VM_DRYFIRE:
  414. iActivity = ACT_ITEM2_VM_DRYFIRE;
  415. break;
  416. case ACT_VM_IDLE_TO_LOWERED:
  417. iActivity = ACT_ITEM2_VM_IDLE_TO_LOWERED;
  418. break;
  419. case ACT_VM_IDLE_LOWERED:
  420. iActivity = ACT_ITEM2_VM_IDLE_LOWERED;
  421. break;
  422. case ACT_VM_LOWERED_TO_IDLE:
  423. iActivity = ACT_ITEM2_VM_LOWERED_TO_IDLE;
  424. break;
  425. default:
  426. break;
  427. }
  428. }
  429. return BaseClass::SendWeaponAnim( iActivity );
  430. }
  431. #ifdef GAME_DLL
  432. //-----------------------------------------------------------------------------
  433. void CTFScatterGun::Equip( CBaseCombatCharacter *pOwner )
  434. {
  435. CTFPlayer *pPlayer = dynamic_cast<CTFPlayer*>( pOwner );
  436. if ( pPlayer )
  437. {
  438. pPlayer->m_Shared.SetScoutHypeMeter( 0.0f );
  439. }
  440. BaseClass::Equip( pOwner );
  441. }
  442. #endif // GAME_DLL
  443. //-----------------------------------------------------------------------------
  444. // CTFSodaPopper
  445. //-----------------------------------------------------------------------------
  446. float CTFSodaPopper::GetProgress( void )
  447. {
  448. CTFPlayer *pPlayer = GetTFPlayerOwner();
  449. if ( !pPlayer )
  450. return 0.f;
  451. return pPlayer->m_Shared.GetScoutHypeMeter() * 0.01f;
  452. }
  453. //-----------------------------------------------------------------------------
  454. void CTFSodaPopper::ItemBusyFrame( void )
  455. {
  456. #ifdef GAME_DLL
  457. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  458. if ( pOwner && pOwner->m_nButtons & IN_ATTACK2 )
  459. {
  460. // Check here so we can always activate buff when we want (similar to stickies)
  461. SecondaryAttack();
  462. }
  463. #endif
  464. BaseClass::ItemBusyFrame();
  465. }
  466. //-----------------------------------------------------------------------------
  467. void CTFSodaPopper::SecondaryAttack()
  468. {
  469. CTFPlayer *pPlayer = GetTFPlayerOwner( );
  470. if ( !pPlayer || pPlayer->m_Shared.IsHypeBuffed() )
  471. return;
  472. if ( pPlayer->m_Shared.GetScoutHypeMeter() >= 100.f )
  473. {
  474. pPlayer->m_Shared.AddCond( TF_COND_SODAPOPPER_HYPE );
  475. }
  476. }
  477. //-----------------------------------------------------------------------------
  478. float CTFPEPBrawlerBlaster::GetProgress( void )
  479. {
  480. CTFPlayer *pPlayer = GetTFPlayerOwner();
  481. if ( !pPlayer )
  482. return 0.f;
  483. return pPlayer->m_Shared.GetScoutHypeMeter() * 0.01f;
  484. }
  485. //-----------------------------------------------------------------------------
  486. float CTFShotgunBuildingRescue::GetProjectileSpeed( void )
  487. {
  488. return RemapValClamped( 0.75f, 0.0f, 1.f, 1800, 2600 ); // Temp, if we want to ramp.
  489. }
  490. //-----------------------------------------------------------------------------
  491. float CTFShotgunBuildingRescue::GetProjectileGravity( void )
  492. {
  493. return RemapValClamped( 0.75f, 0.0f, 1.f, 0.5f, 0.1f ); // Temp, if we want to ramp.
  494. }
  495. //-----------------------------------------------------------------------------
  496. bool CTFShotgunBuildingRescue::IsViewModelFlipped( void )
  497. {
  498. return !BaseClass::IsViewModelFlipped(); // Invert because arrows are backwards by default.
  499. }