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.

373 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Pistol - hand gun
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "npcevent.h"
  9. #include "basehlcombatweapon.h"
  10. #include "basecombatcharacter.h"
  11. #include "ai_basenpc.h"
  12. #include "player.h"
  13. #include "gamerules.h"
  14. #include "in_buttons.h"
  15. #include "soundent.h"
  16. #include "game.h"
  17. #include "vstdlib/random.h"
  18. #include "gamestats.h"
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. #define PISTOL_FASTEST_REFIRE_TIME 0.1f
  22. #define PISTOL_FASTEST_DRY_REFIRE_TIME 0.2f
  23. #define PISTOL_ACCURACY_SHOT_PENALTY_TIME 0.2f // Applied amount of time each shot adds to the time we must recover from
  24. #define PISTOL_ACCURACY_MAXIMUM_PENALTY_TIME 1.5f // Maximum penalty to deal out
  25. ConVar pistol_use_new_accuracy( "pistol_use_new_accuracy", "1" );
  26. //-----------------------------------------------------------------------------
  27. // CWeaponPistol
  28. //-----------------------------------------------------------------------------
  29. class CWeaponPistol : public CBaseHLCombatWeapon
  30. {
  31. DECLARE_DATADESC();
  32. public:
  33. DECLARE_CLASS( CWeaponPistol, CBaseHLCombatWeapon );
  34. CWeaponPistol(void);
  35. DECLARE_SERVERCLASS();
  36. void Precache( void );
  37. void ItemPostFrame( void );
  38. void ItemPreFrame( void );
  39. void ItemBusyFrame( void );
  40. void PrimaryAttack( void );
  41. void AddViewKick( void );
  42. void DryFire( void );
  43. void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
  44. void UpdatePenaltyTime( void );
  45. int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; }
  46. Activity GetPrimaryAttackActivity( void );
  47. virtual bool Reload( void );
  48. virtual const Vector& GetBulletSpread( void )
  49. {
  50. // Handle NPCs first
  51. static Vector npcCone = VECTOR_CONE_5DEGREES;
  52. if ( GetOwner() && GetOwner()->IsNPC() )
  53. return npcCone;
  54. static Vector cone;
  55. if ( pistol_use_new_accuracy.GetBool() )
  56. {
  57. float ramp = RemapValClamped( m_flAccuracyPenalty,
  58. 0.0f,
  59. PISTOL_ACCURACY_MAXIMUM_PENALTY_TIME,
  60. 0.0f,
  61. 1.0f );
  62. // We lerp from very accurate to inaccurate over time
  63. VectorLerp( VECTOR_CONE_1DEGREES, VECTOR_CONE_6DEGREES, ramp, cone );
  64. }
  65. else
  66. {
  67. // Old value
  68. cone = VECTOR_CONE_4DEGREES;
  69. }
  70. return cone;
  71. }
  72. virtual int GetMinBurst()
  73. {
  74. return 1;
  75. }
  76. virtual int GetMaxBurst()
  77. {
  78. return 3;
  79. }
  80. virtual float GetFireRate( void )
  81. {
  82. return 0.5f;
  83. }
  84. DECLARE_ACTTABLE();
  85. private:
  86. float m_flSoonestPrimaryAttack;
  87. float m_flLastAttackTime;
  88. float m_flAccuracyPenalty;
  89. int m_nNumShotsFired;
  90. };
  91. IMPLEMENT_SERVERCLASS_ST(CWeaponPistol, DT_WeaponPistol)
  92. END_SEND_TABLE()
  93. LINK_ENTITY_TO_CLASS( weapon_pistol, CWeaponPistol );
  94. PRECACHE_WEAPON_REGISTER( weapon_pistol );
  95. BEGIN_DATADESC( CWeaponPistol )
  96. DEFINE_FIELD( m_flSoonestPrimaryAttack, FIELD_TIME ),
  97. DEFINE_FIELD( m_flLastAttackTime, FIELD_TIME ),
  98. DEFINE_FIELD( m_flAccuracyPenalty, FIELD_FLOAT ), //NOTENOTE: This is NOT tracking game time
  99. DEFINE_FIELD( m_nNumShotsFired, FIELD_INTEGER ),
  100. END_DATADESC()
  101. acttable_t CWeaponPistol::m_acttable[] =
  102. {
  103. { ACT_IDLE, ACT_IDLE_PISTOL, true },
  104. { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_PISTOL, true },
  105. { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, true },
  106. { ACT_RELOAD, ACT_RELOAD_PISTOL, true },
  107. { ACT_WALK_AIM, ACT_WALK_AIM_PISTOL, true },
  108. { ACT_RUN_AIM, ACT_RUN_AIM_PISTOL, true },
  109. { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_PISTOL,true },
  110. { ACT_RELOAD_LOW, ACT_RELOAD_PISTOL_LOW, false },
  111. { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_PISTOL_LOW, false },
  112. { ACT_COVER_LOW, ACT_COVER_PISTOL_LOW, false },
  113. { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_PISTOL_LOW, false },
  114. { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, false },
  115. { ACT_WALK, ACT_WALK_PISTOL, false },
  116. { ACT_RUN, ACT_RUN_PISTOL, false },
  117. };
  118. IMPLEMENT_ACTTABLE( CWeaponPistol );
  119. //-----------------------------------------------------------------------------
  120. // Purpose: Constructor
  121. //-----------------------------------------------------------------------------
  122. CWeaponPistol::CWeaponPistol( void )
  123. {
  124. m_flSoonestPrimaryAttack = gpGlobals->curtime;
  125. m_flAccuracyPenalty = 0.0f;
  126. m_fMinRange1 = 24;
  127. m_fMaxRange1 = 1500;
  128. m_fMinRange2 = 24;
  129. m_fMaxRange2 = 200;
  130. m_bFiresUnderwater = true;
  131. }
  132. //-----------------------------------------------------------------------------
  133. // Purpose:
  134. //-----------------------------------------------------------------------------
  135. void CWeaponPistol::Precache( void )
  136. {
  137. BaseClass::Precache();
  138. }
  139. //-----------------------------------------------------------------------------
  140. // Purpose:
  141. // Input :
  142. // Output :
  143. //-----------------------------------------------------------------------------
  144. void CWeaponPistol::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  145. {
  146. switch( pEvent->event )
  147. {
  148. case EVENT_WEAPON_PISTOL_FIRE:
  149. {
  150. Vector vecShootOrigin, vecShootDir;
  151. vecShootOrigin = pOperator->Weapon_ShootPosition();
  152. CAI_BaseNPC *npc = pOperator->MyNPCPointer();
  153. ASSERT( npc != NULL );
  154. vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin );
  155. CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() );
  156. WeaponSound( SINGLE_NPC );
  157. pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2 );
  158. pOperator->DoMuzzleFlash();
  159. m_iClip1 = m_iClip1 - 1;
  160. }
  161. break;
  162. default:
  163. BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
  164. break;
  165. }
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose:
  169. //-----------------------------------------------------------------------------
  170. void CWeaponPistol::DryFire( void )
  171. {
  172. WeaponSound( EMPTY );
  173. SendWeaponAnim( ACT_VM_DRYFIRE );
  174. m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_DRY_REFIRE_TIME;
  175. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose:
  179. //-----------------------------------------------------------------------------
  180. void CWeaponPistol::PrimaryAttack( void )
  181. {
  182. if ( ( gpGlobals->curtime - m_flLastAttackTime ) > 0.5f )
  183. {
  184. m_nNumShotsFired = 0;
  185. }
  186. else
  187. {
  188. m_nNumShotsFired++;
  189. }
  190. m_flLastAttackTime = gpGlobals->curtime;
  191. m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME;
  192. CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, GetOwner() );
  193. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  194. if( pOwner )
  195. {
  196. // Each time the player fires the pistol, reset the view punch. This prevents
  197. // the aim from 'drifting off' when the player fires very quickly. This may
  198. // not be the ideal way to achieve this, but it's cheap and it works, which is
  199. // great for a feature we're evaluating. (sjb)
  200. pOwner->ViewPunchReset();
  201. }
  202. BaseClass::PrimaryAttack();
  203. // Add an accuracy penalty which can move past our maximum penalty time if we're really spastic
  204. m_flAccuracyPenalty += PISTOL_ACCURACY_SHOT_PENALTY_TIME;
  205. m_iPrimaryAttacks++;
  206. gamestats->Event_WeaponFired( pOwner, true, GetClassname() );
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Purpose:
  210. //-----------------------------------------------------------------------------
  211. void CWeaponPistol::UpdatePenaltyTime( void )
  212. {
  213. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  214. if ( pOwner == NULL )
  215. return;
  216. // Check our penalty time decay
  217. if ( ( ( pOwner->m_nButtons & IN_ATTACK ) == false ) && ( m_flSoonestPrimaryAttack < gpGlobals->curtime ) )
  218. {
  219. m_flAccuracyPenalty -= gpGlobals->frametime;
  220. m_flAccuracyPenalty = clamp( m_flAccuracyPenalty, 0.0f, PISTOL_ACCURACY_MAXIMUM_PENALTY_TIME );
  221. }
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Purpose:
  225. //-----------------------------------------------------------------------------
  226. void CWeaponPistol::ItemPreFrame( void )
  227. {
  228. UpdatePenaltyTime();
  229. BaseClass::ItemPreFrame();
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Purpose:
  233. //-----------------------------------------------------------------------------
  234. void CWeaponPistol::ItemBusyFrame( void )
  235. {
  236. UpdatePenaltyTime();
  237. BaseClass::ItemBusyFrame();
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose: Allows firing as fast as button is pressed
  241. //-----------------------------------------------------------------------------
  242. void CWeaponPistol::ItemPostFrame( void )
  243. {
  244. BaseClass::ItemPostFrame();
  245. if ( m_bInReload )
  246. return;
  247. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  248. if ( pOwner == NULL )
  249. return;
  250. //Allow a refire as fast as the player can click
  251. if ( ( ( pOwner->m_nButtons & IN_ATTACK ) == false ) && ( m_flSoonestPrimaryAttack < gpGlobals->curtime ) )
  252. {
  253. m_flNextPrimaryAttack = gpGlobals->curtime - 0.1f;
  254. }
  255. else if ( ( pOwner->m_nButtons & IN_ATTACK ) && ( m_flNextPrimaryAttack < gpGlobals->curtime ) && ( m_iClip1 <= 0 ) )
  256. {
  257. DryFire();
  258. }
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Purpose:
  262. // Output : int
  263. //-----------------------------------------------------------------------------
  264. Activity CWeaponPistol::GetPrimaryAttackActivity( void )
  265. {
  266. if ( m_nNumShotsFired < 1 )
  267. return ACT_VM_PRIMARYATTACK;
  268. if ( m_nNumShotsFired < 2 )
  269. return ACT_VM_RECOIL1;
  270. if ( m_nNumShotsFired < 3 )
  271. return ACT_VM_RECOIL2;
  272. return ACT_VM_RECOIL3;
  273. }
  274. //-----------------------------------------------------------------------------
  275. //-----------------------------------------------------------------------------
  276. bool CWeaponPistol::Reload( void )
  277. {
  278. bool fRet = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
  279. if ( fRet )
  280. {
  281. WeaponSound( RELOAD );
  282. m_flAccuracyPenalty = 0.0f;
  283. }
  284. return fRet;
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose:
  288. //-----------------------------------------------------------------------------
  289. void CWeaponPistol::AddViewKick( void )
  290. {
  291. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  292. if ( pPlayer == NULL )
  293. return;
  294. QAngle viewPunch;
  295. viewPunch.x = random->RandomFloat( 0.25f, 0.5f );
  296. viewPunch.y = random->RandomFloat( -.6f, .6f );
  297. viewPunch.z = 0.0f;
  298. //Add it to the view punch
  299. pPlayer->ViewPunch( viewPunch );
  300. }