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.

634 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "npcevent.h"
  8. #include "in_buttons.h"
  9. #ifdef CLIENT_DLL
  10. #include "c_hl2mp_player.h"
  11. #else
  12. #include "hl2mp_player.h"
  13. #endif
  14. #include "weapon_hl2mpbasehlmpcombatweapon.h"
  15. #ifdef CLIENT_DLL
  16. #define CWeaponShotgun C_WeaponShotgun
  17. #endif
  18. extern ConVar sk_auto_reload_time;
  19. extern ConVar sk_plr_num_shotgun_pellets;
  20. class CWeaponShotgun : public CBaseHL2MPCombatWeapon
  21. {
  22. public:
  23. DECLARE_CLASS( CWeaponShotgun, CBaseHL2MPCombatWeapon );
  24. DECLARE_NETWORKCLASS();
  25. DECLARE_PREDICTABLE();
  26. private:
  27. CNetworkVar( bool, m_bNeedPump ); // When emptied completely
  28. CNetworkVar( bool, m_bDelayedFire1 ); // Fire primary when finished reloading
  29. CNetworkVar( bool, m_bDelayedFire2 ); // Fire secondary when finished reloading
  30. CNetworkVar( bool, m_bDelayedReload ); // Reload when finished pump
  31. public:
  32. virtual const Vector& GetBulletSpread( void )
  33. {
  34. static Vector cone = VECTOR_CONE_10DEGREES;
  35. return cone;
  36. }
  37. virtual int GetMinBurst() { return 1; }
  38. virtual int GetMaxBurst() { return 3; }
  39. bool StartReload( void );
  40. bool Reload( void );
  41. void FillClip( void );
  42. void FinishReload( void );
  43. void CheckHolsterReload( void );
  44. void Pump( void );
  45. // void WeaponIdle( void );
  46. void ItemHolsterFrame( void );
  47. void ItemPostFrame( void );
  48. void PrimaryAttack( void );
  49. void SecondaryAttack( void );
  50. void DryFire( void );
  51. virtual float GetFireRate( void ) { return 0.7; };
  52. #ifndef CLIENT_DLL
  53. DECLARE_ACTTABLE();
  54. #endif
  55. CWeaponShotgun(void);
  56. private:
  57. CWeaponShotgun( const CWeaponShotgun & );
  58. };
  59. IMPLEMENT_NETWORKCLASS_ALIASED( WeaponShotgun, DT_WeaponShotgun )
  60. BEGIN_NETWORK_TABLE( CWeaponShotgun, DT_WeaponShotgun )
  61. #ifdef CLIENT_DLL
  62. RecvPropBool( RECVINFO( m_bNeedPump ) ),
  63. RecvPropBool( RECVINFO( m_bDelayedFire1 ) ),
  64. RecvPropBool( RECVINFO( m_bDelayedFire2 ) ),
  65. RecvPropBool( RECVINFO( m_bDelayedReload ) ),
  66. #else
  67. SendPropBool( SENDINFO( m_bNeedPump ) ),
  68. SendPropBool( SENDINFO( m_bDelayedFire1 ) ),
  69. SendPropBool( SENDINFO( m_bDelayedFire2 ) ),
  70. SendPropBool( SENDINFO( m_bDelayedReload ) ),
  71. #endif
  72. END_NETWORK_TABLE()
  73. #ifdef CLIENT_DLL
  74. BEGIN_PREDICTION_DATA( CWeaponShotgun )
  75. DEFINE_PRED_FIELD( m_bNeedPump, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  76. DEFINE_PRED_FIELD( m_bDelayedFire1, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  77. DEFINE_PRED_FIELD( m_bDelayedFire2, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  78. DEFINE_PRED_FIELD( m_bDelayedReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  79. END_PREDICTION_DATA()
  80. #endif
  81. LINK_ENTITY_TO_CLASS( weapon_shotgun, CWeaponShotgun );
  82. PRECACHE_WEAPON_REGISTER(weapon_shotgun);
  83. #ifndef CLIENT_DLL
  84. acttable_t CWeaponShotgun::m_acttable[] =
  85. {
  86. { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SHOTGUN, false },
  87. { ACT_HL2MP_RUN, ACT_HL2MP_RUN_SHOTGUN, false },
  88. { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SHOTGUN, false },
  89. { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SHOTGUN, false },
  90. { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN, false },
  91. { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SHOTGUN, false },
  92. { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SHOTGUN, false },
  93. { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, false },
  94. };
  95. IMPLEMENT_ACTTABLE(CWeaponShotgun);
  96. #endif
  97. //-----------------------------------------------------------------------------
  98. // Purpose: Override so only reload one shell at a time
  99. // Input :
  100. // Output :
  101. //-----------------------------------------------------------------------------
  102. bool CWeaponShotgun::StartReload( void )
  103. {
  104. if ( m_bNeedPump )
  105. return false;
  106. CBaseCombatCharacter *pOwner = GetOwner();
  107. if ( pOwner == NULL )
  108. return false;
  109. if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
  110. return false;
  111. if (m_iClip1 >= GetMaxClip1())
  112. return false;
  113. int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
  114. if (j <= 0)
  115. return false;
  116. SendWeaponAnim( ACT_SHOTGUN_RELOAD_START );
  117. // Make shotgun shell visible
  118. SetBodygroup(1,0);
  119. pOwner->m_flNextAttack = gpGlobals->curtime;
  120. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  121. m_bInReload = true;
  122. return true;
  123. }
  124. //-----------------------------------------------------------------------------
  125. // Purpose: Override so only reload one shell at a time
  126. // Input :
  127. // Output :
  128. //-----------------------------------------------------------------------------
  129. bool CWeaponShotgun::Reload( void )
  130. {
  131. // Check that StartReload was called first
  132. if (!m_bInReload)
  133. {
  134. Warning("ERROR: Shotgun Reload called incorrectly!\n");
  135. }
  136. CBaseCombatCharacter *pOwner = GetOwner();
  137. if ( pOwner == NULL )
  138. return false;
  139. if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
  140. return false;
  141. if (m_iClip1 >= GetMaxClip1())
  142. return false;
  143. int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
  144. if (j <= 0)
  145. return false;
  146. FillClip();
  147. // Play reload on different channel as otherwise steals channel away from fire sound
  148. WeaponSound(RELOAD);
  149. SendWeaponAnim( ACT_VM_RELOAD );
  150. pOwner->m_flNextAttack = gpGlobals->curtime;
  151. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  152. return true;
  153. }
  154. //-----------------------------------------------------------------------------
  155. // Purpose: Play finish reload anim and fill clip
  156. // Input :
  157. // Output :
  158. //-----------------------------------------------------------------------------
  159. void CWeaponShotgun::FinishReload( void )
  160. {
  161. // Make shotgun shell invisible
  162. SetBodygroup(1,1);
  163. CBaseCombatCharacter *pOwner = GetOwner();
  164. if ( pOwner == NULL )
  165. return;
  166. m_bInReload = false;
  167. // Finish reload animation
  168. SendWeaponAnim( ACT_SHOTGUN_RELOAD_FINISH );
  169. pOwner->m_flNextAttack = gpGlobals->curtime;
  170. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Purpose: Play finish reload anim and fill clip
  174. // Input :
  175. // Output :
  176. //-----------------------------------------------------------------------------
  177. void CWeaponShotgun::FillClip( void )
  178. {
  179. CBaseCombatCharacter *pOwner = GetOwner();
  180. if ( pOwner == NULL )
  181. return;
  182. // Add them to the clip
  183. if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
  184. {
  185. if ( Clip1() < GetMaxClip1() )
  186. {
  187. m_iClip1++;
  188. pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
  189. }
  190. }
  191. }
  192. //-----------------------------------------------------------------------------
  193. // Purpose: Play weapon pump anim
  194. // Input :
  195. // Output :
  196. //-----------------------------------------------------------------------------
  197. void CWeaponShotgun::Pump( void )
  198. {
  199. CBaseCombatCharacter *pOwner = GetOwner();
  200. if ( pOwner == NULL )
  201. return;
  202. m_bNeedPump = false;
  203. if ( m_bDelayedReload )
  204. {
  205. m_bDelayedReload = false;
  206. StartReload();
  207. }
  208. WeaponSound( SPECIAL1 );
  209. // Finish reload animation
  210. SendWeaponAnim( ACT_SHOTGUN_PUMP );
  211. pOwner->m_flNextAttack = gpGlobals->curtime + SequenceDuration();
  212. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  213. }
  214. //-----------------------------------------------------------------------------
  215. // Purpose:
  216. //
  217. //
  218. //-----------------------------------------------------------------------------
  219. void CWeaponShotgun::DryFire( void )
  220. {
  221. WeaponSound(EMPTY);
  222. SendWeaponAnim( ACT_VM_DRYFIRE );
  223. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose:
  227. //
  228. //
  229. //-----------------------------------------------------------------------------
  230. void CWeaponShotgun::PrimaryAttack( void )
  231. {
  232. // Only the player fires this way so we can cast
  233. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  234. if (!pPlayer)
  235. {
  236. return;
  237. }
  238. // MUST call sound before removing a round from the clip of a CMachineGun
  239. WeaponSound(SINGLE);
  240. pPlayer->DoMuzzleFlash();
  241. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  242. // Don't fire again until fire animation has completed
  243. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  244. m_iClip1 -= 1;
  245. // player "shoot" animation
  246. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  247. Vector vecSrc = pPlayer->Weapon_ShootPosition( );
  248. Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
  249. FireBulletsInfo_t info( 7, vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType );
  250. info.m_pAttacker = pPlayer;
  251. // Fire the bullets, and force the first shot to be perfectly accuracy
  252. pPlayer->FireBullets( info );
  253. QAngle punch;
  254. punch.Init( SharedRandomFloat( "shotgunpax", -2, -1 ), SharedRandomFloat( "shotgunpay", -2, 2 ), 0 );
  255. pPlayer->ViewPunch( punch );
  256. if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
  257. {
  258. // HEV suit - indicate out of ammo condition
  259. pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
  260. }
  261. m_bNeedPump = true;
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Purpose:
  265. //
  266. //
  267. //-----------------------------------------------------------------------------
  268. void CWeaponShotgun::SecondaryAttack( void )
  269. {
  270. // Only the player fires this way so we can cast
  271. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  272. if (!pPlayer)
  273. {
  274. return;
  275. }
  276. pPlayer->m_nButtons &= ~IN_ATTACK2;
  277. // MUST call sound before removing a round from the clip of a CMachineGun
  278. WeaponSound(WPN_DOUBLE);
  279. pPlayer->DoMuzzleFlash();
  280. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  281. // Don't fire again until fire animation has completed
  282. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  283. m_iClip1 -= 2; // Shotgun uses same clip for primary and secondary attacks
  284. // player "shoot" animation
  285. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  286. Vector vecSrc = pPlayer->Weapon_ShootPosition();
  287. Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
  288. FireBulletsInfo_t info( 12, vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType );
  289. info.m_pAttacker = pPlayer;
  290. // Fire the bullets, and force the first shot to be perfectly accuracy
  291. pPlayer->FireBullets( info );
  292. pPlayer->ViewPunch( QAngle(SharedRandomFloat( "shotgunsax", -5, 5 ),0,0) );
  293. if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
  294. {
  295. // HEV suit - indicate out of ammo condition
  296. pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
  297. }
  298. m_bNeedPump = true;
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose: Override so shotgun can do mulitple reloads in a row
  302. //-----------------------------------------------------------------------------
  303. void CWeaponShotgun::ItemPostFrame( void )
  304. {
  305. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  306. if (!pOwner)
  307. {
  308. return;
  309. }
  310. if ( m_bNeedPump && ( pOwner->m_nButtons & IN_RELOAD ) )
  311. {
  312. m_bDelayedReload = true;
  313. }
  314. if (m_bInReload)
  315. {
  316. // If I'm primary firing and have one round stop reloading and fire
  317. if ((pOwner->m_nButtons & IN_ATTACK ) && (m_iClip1 >=1) && !m_bNeedPump )
  318. {
  319. m_bInReload = false;
  320. m_bNeedPump = false;
  321. m_bDelayedFire1 = true;
  322. }
  323. // If I'm secondary firing and have two rounds stop reloading and fire
  324. else if ((pOwner->m_nButtons & IN_ATTACK2 ) && (m_iClip1 >=2) && !m_bNeedPump )
  325. {
  326. m_bInReload = false;
  327. m_bNeedPump = false;
  328. m_bDelayedFire2 = true;
  329. }
  330. else if (m_flNextPrimaryAttack <= gpGlobals->curtime)
  331. {
  332. // If out of ammo end reload
  333. if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <=0)
  334. {
  335. FinishReload();
  336. return;
  337. }
  338. // If clip not full reload again
  339. if (m_iClip1 < GetMaxClip1())
  340. {
  341. Reload();
  342. return;
  343. }
  344. // Clip full, stop reloading
  345. else
  346. {
  347. FinishReload();
  348. return;
  349. }
  350. }
  351. }
  352. else
  353. {
  354. // Make shotgun shell invisible
  355. SetBodygroup(1,1);
  356. }
  357. if ((m_bNeedPump) && (m_flNextPrimaryAttack <= gpGlobals->curtime))
  358. {
  359. Pump();
  360. return;
  361. }
  362. // Shotgun uses same timing and ammo for secondary attack
  363. if ((m_bDelayedFire2 || pOwner->m_nButtons & IN_ATTACK2)&&(m_flNextPrimaryAttack <= gpGlobals->curtime))
  364. {
  365. m_bDelayedFire2 = false;
  366. if ( (m_iClip1 <= 1 && UsesClipsForAmmo1()))
  367. {
  368. // If only one shell is left, do a single shot instead
  369. if ( m_iClip1 == 1 )
  370. {
  371. PrimaryAttack();
  372. }
  373. else if (!pOwner->GetAmmoCount(m_iPrimaryAmmoType))
  374. {
  375. DryFire();
  376. }
  377. else
  378. {
  379. StartReload();
  380. }
  381. }
  382. // Fire underwater?
  383. else if (GetOwner()->GetWaterLevel() == 3 && m_bFiresUnderwater == false)
  384. {
  385. WeaponSound(EMPTY);
  386. m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
  387. return;
  388. }
  389. else
  390. {
  391. // If the firing button was just pressed, reset the firing time
  392. if ( pOwner->m_afButtonPressed & IN_ATTACK )
  393. {
  394. m_flNextPrimaryAttack = gpGlobals->curtime;
  395. }
  396. SecondaryAttack();
  397. }
  398. }
  399. else if ( (m_bDelayedFire1 || pOwner->m_nButtons & IN_ATTACK) && m_flNextPrimaryAttack <= gpGlobals->curtime)
  400. {
  401. m_bDelayedFire1 = false;
  402. if ( (m_iClip1 <= 0 && UsesClipsForAmmo1()) || ( !UsesClipsForAmmo1() && !pOwner->GetAmmoCount(m_iPrimaryAmmoType) ) )
  403. {
  404. if (!pOwner->GetAmmoCount(m_iPrimaryAmmoType))
  405. {
  406. DryFire();
  407. }
  408. else
  409. {
  410. StartReload();
  411. }
  412. }
  413. // Fire underwater?
  414. else if (pOwner->GetWaterLevel() == 3 && m_bFiresUnderwater == false)
  415. {
  416. WeaponSound(EMPTY);
  417. m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
  418. return;
  419. }
  420. else
  421. {
  422. // If the firing button was just pressed, reset the firing time
  423. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  424. if ( pPlayer && pPlayer->m_afButtonPressed & IN_ATTACK )
  425. {
  426. m_flNextPrimaryAttack = gpGlobals->curtime;
  427. }
  428. PrimaryAttack();
  429. }
  430. }
  431. if ( pOwner->m_nButtons & IN_RELOAD && UsesClipsForAmmo1() && !m_bInReload )
  432. {
  433. // reload when reload is pressed, or if no buttons are down and weapon is empty.
  434. StartReload();
  435. }
  436. else
  437. {
  438. // no fire buttons down
  439. m_bFireOnEmpty = false;
  440. if ( !HasAnyAmmo() && m_flNextPrimaryAttack < gpGlobals->curtime )
  441. {
  442. // weapon isn't useable, switch.
  443. if ( !(GetWeaponFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && pOwner->SwitchToNextBestWeapon( this ) )
  444. {
  445. m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
  446. return;
  447. }
  448. }
  449. else
  450. {
  451. // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
  452. if ( m_iClip1 <= 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime )
  453. {
  454. if (StartReload())
  455. {
  456. // if we've successfully started to reload, we're done
  457. return;
  458. }
  459. }
  460. }
  461. WeaponIdle( );
  462. return;
  463. }
  464. }
  465. //-----------------------------------------------------------------------------
  466. // Purpose: Constructor
  467. //-----------------------------------------------------------------------------
  468. CWeaponShotgun::CWeaponShotgun( void )
  469. {
  470. m_bReloadsSingly = true;
  471. m_bNeedPump = false;
  472. m_bDelayedFire1 = false;
  473. m_bDelayedFire2 = false;
  474. m_fMinRange1 = 0.0;
  475. m_fMaxRange1 = 500;
  476. m_fMinRange2 = 0.0;
  477. m_fMaxRange2 = 200;
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Purpose:
  481. //-----------------------------------------------------------------------------
  482. void CWeaponShotgun::ItemHolsterFrame( void )
  483. {
  484. // Must be player held
  485. if ( GetOwner() && GetOwner()->IsPlayer() == false )
  486. return;
  487. // We can't be active
  488. if ( GetOwner()->GetActiveWeapon() == this )
  489. return;
  490. // If it's been longer than three seconds, reload
  491. if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() )
  492. {
  493. // Reset the timer
  494. m_flHolsterTime = gpGlobals->curtime;
  495. if ( GetOwner() == NULL )
  496. return;
  497. if ( m_iClip1 == GetMaxClip1() )
  498. return;
  499. // Just load the clip with no animations
  500. int ammoFill = MIN( (GetMaxClip1() - m_iClip1), GetOwner()->GetAmmoCount( GetPrimaryAmmoType() ) );
  501. GetOwner()->RemoveAmmo( ammoFill, GetPrimaryAmmoType() );
  502. m_iClip1 += ammoFill;
  503. }
  504. }
  505. //==================================================
  506. // Purpose:
  507. //==================================================
  508. /*
  509. void CWeaponShotgun::WeaponIdle( void )
  510. {
  511. //Only the player fires this way so we can cast
  512. CBasePlayer *pPlayer = GetOwner()
  513. if ( pPlayer == NULL )
  514. return;
  515. //If we're on a target, play the new anim
  516. if ( pPlayer->IsOnTarget() )
  517. {
  518. SendWeaponAnim( ACT_VM_IDLE_ACTIVE );
  519. }
  520. }
  521. */