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.

438 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "weapon_csbase.h"
  8. #include "gamerules.h"
  9. #include "npcevent.h"
  10. #include "engine/IEngineSound.h"
  11. #include "weapon_basecsgrenade.h"
  12. #include "in_buttons.h"
  13. #include "datacache/imdlcache.h"
  14. #ifdef CLIENT_DLL
  15. #include "c_cs_player.h"
  16. #else
  17. #include "cs_player.h"
  18. #include "items.h"
  19. #include "../../server/cstrike/cs_gamestats.h"
  20. #endif
  21. #define GRENADE_TIMER 1.5f //Seconds
  22. IMPLEMENT_NETWORKCLASS_ALIASED( BaseCSGrenade, DT_BaseCSGrenade )
  23. BEGIN_NETWORK_TABLE(CBaseCSGrenade, DT_BaseCSGrenade)
  24. #ifndef CLIENT_DLL
  25. SendPropBool( SENDINFO(m_bRedraw) ),
  26. SendPropBool( SENDINFO(m_bPinPulled) ),
  27. SendPropFloat( SENDINFO(m_fThrowTime), 0, SPROP_NOSCALE ),
  28. #else
  29. RecvPropBool( RECVINFO(m_bRedraw) ),
  30. RecvPropBool( RECVINFO(m_bPinPulled) ),
  31. RecvPropFloat( RECVINFO(m_fThrowTime) ),
  32. #endif
  33. END_NETWORK_TABLE()
  34. #if defined CLIENT_DLL
  35. BEGIN_PREDICTION_DATA( CBaseCSGrenade )
  36. DEFINE_PRED_FIELD( m_bRedraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  37. DEFINE_PRED_FIELD( m_bRedraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  38. END_PREDICTION_DATA()
  39. #endif
  40. LINK_ENTITY_TO_CLASS( weapon_basecsgrenade, CBaseCSGrenade );
  41. #ifndef CLIENT_DLL
  42. ConVar sv_ignoregrenaderadio( "sv_ignoregrenaderadio", "0", 0, "Turn off Fire in the hole messages" );
  43. #endif
  44. CBaseCSGrenade::CBaseCSGrenade()
  45. {
  46. m_bRedraw = false;
  47. m_bPinPulled = false;
  48. m_fThrowTime = 0;
  49. }
  50. //-----------------------------------------------------------------------------
  51. // Purpose:
  52. //-----------------------------------------------------------------------------
  53. void CBaseCSGrenade::Precache()
  54. {
  55. BaseClass::Precache();
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose:
  59. //-----------------------------------------------------------------------------
  60. bool CBaseCSGrenade::Deploy()
  61. {
  62. m_bRedraw = false;
  63. m_bPinPulled = false;
  64. m_fThrowTime = 0;
  65. #ifndef CLIENT_DLL
  66. // if we're officially out of grenades, ditch this weapon
  67. CCSPlayer *pPlayer = GetPlayerOwner();
  68. if ( !pPlayer )
  69. return false;
  70. if( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
  71. {
  72. pPlayer->Weapon_Drop( this, NULL, NULL );
  73. UTIL_Remove(this);
  74. return false;
  75. }
  76. #endif
  77. return BaseClass::Deploy();
  78. }
  79. //-----------------------------------------------------------------------------
  80. // Purpose:
  81. // Output : Returns true on success, false on failure.
  82. //-----------------------------------------------------------------------------
  83. bool CBaseCSGrenade::Holster( CBaseCombatWeapon *pSwitchingTo )
  84. {
  85. m_bRedraw = false;
  86. m_bPinPulled = false; // when this is holstered make sure the pin isn�t pulled.
  87. m_fThrowTime = 0;
  88. #ifndef CLIENT_DLL
  89. // If they attempt to switch weapons before the throw animation is done,
  90. // allow it, but kill the weapon if we have to.
  91. CCSPlayer *pPlayer = GetPlayerOwner();
  92. if ( !pPlayer )
  93. return false;
  94. if( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
  95. {
  96. CBaseCombatCharacter *pOwner = (CBaseCombatCharacter *)pPlayer;
  97. pOwner->Weapon_Drop( this );
  98. UTIL_Remove(this);
  99. }
  100. #endif
  101. return BaseClass::Holster( pSwitchingTo );
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Purpose:
  105. //-----------------------------------------------------------------------------
  106. void CBaseCSGrenade::PrimaryAttack()
  107. {
  108. if ( m_bRedraw || m_bPinPulled || m_fThrowTime > 0.0f )
  109. return;
  110. CCSPlayer *pPlayer = GetPlayerOwner();
  111. if ( !pPlayer || pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
  112. return;
  113. // The pull pin animation has to finish, then we wait until they aren't holding the primary
  114. // attack button, then throw the grenade.
  115. SendWeaponAnim( ACT_VM_PULLPIN );
  116. m_bPinPulled = true;
  117. // Don't let weapon idle interfere in the middle of a throw!
  118. MDLCACHE_CRITICAL_SECTION();
  119. SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
  120. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  121. }
  122. //-----------------------------------------------------------------------------
  123. // Purpose:
  124. //-----------------------------------------------------------------------------
  125. void CBaseCSGrenade::SecondaryAttack()
  126. {
  127. if ( m_bRedraw )
  128. return;
  129. CCSPlayer *pPlayer = GetPlayerOwner();
  130. if ( pPlayer == NULL )
  131. return;
  132. //See if we're ducking
  133. if ( pPlayer->GetFlags() & FL_DUCKING )
  134. {
  135. //Send the weapon animation
  136. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  137. }
  138. else
  139. {
  140. //Send the weapon animation
  141. SendWeaponAnim( ACT_VM_HAULBACK );
  142. }
  143. // Don't let weapon idle interfere in the middle of a throw!
  144. SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
  145. m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
  146. }
  147. //-----------------------------------------------------------------------------
  148. // Purpose:
  149. // Output : Returns true on success, false on failure.
  150. //-----------------------------------------------------------------------------
  151. bool CBaseCSGrenade::Reload()
  152. {
  153. if ( ( m_bRedraw ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
  154. {
  155. //Redraw the weapon
  156. SendWeaponAnim( ACT_VM_DRAW );
  157. //Update our times
  158. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  159. m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
  160. SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
  161. //Mark this as done
  162. // m_bRedraw = false;
  163. }
  164. return true;
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose:
  168. //-----------------------------------------------------------------------------
  169. void CBaseCSGrenade::ItemPostFrame()
  170. {
  171. CCSPlayer *pPlayer = GetPlayerOwner();
  172. if ( !pPlayer )
  173. return;
  174. CBaseViewModel *vm = pPlayer->GetViewModel( m_nViewModelIndex );
  175. if ( !vm )
  176. return;
  177. // If they let go of the fire button, they want to throw the grenade.
  178. if ( m_bPinPulled && !(pPlayer->m_nButtons & IN_ATTACK) )
  179. {
  180. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE );
  181. StartGrenadeThrow();
  182. MDLCACHE_CRITICAL_SECTION();
  183. m_bPinPulled = false;
  184. SendWeaponAnim( ACT_VM_THROW );
  185. SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
  186. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); // we're still throwing, so reset our next primary attack
  187. #ifndef CLIENT_DLL
  188. IGameEvent * event = gameeventmanager->CreateEvent( "weapon_fire" );
  189. if( event )
  190. {
  191. const char *weaponName = STRING( m_iClassname );
  192. if ( strncmp( weaponName, "weapon_", 7 ) == 0 )
  193. {
  194. weaponName += 7;
  195. }
  196. event->SetInt( "userid", pPlayer->GetUserID() );
  197. event->SetString( "weapon", weaponName );
  198. gameeventmanager->FireEvent( event );
  199. }
  200. #endif
  201. }
  202. else if ((m_fThrowTime > 0) && (m_fThrowTime < gpGlobals->curtime))
  203. {
  204. // only decrement our ammo when we actually create the projectile
  205. DecrementAmmo( pPlayer );
  206. ThrowGrenade();
  207. }
  208. else if( m_bRedraw )
  209. {
  210. // Has the throw animation finished playing
  211. if( m_flTimeWeaponIdle < gpGlobals->curtime )
  212. {
  213. #ifdef GAME_DLL
  214. // if we're officially out of grenades, ditch this weapon
  215. if( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
  216. {
  217. pPlayer->Weapon_Drop( this, NULL, NULL );
  218. UTIL_Remove(this);
  219. }
  220. else
  221. {
  222. pPlayer->SwitchToNextBestWeapon( this );
  223. }
  224. #endif
  225. return; //don't animate this grenade any more!
  226. }
  227. }
  228. else if( !m_bRedraw )
  229. {
  230. BaseClass::ItemPostFrame();
  231. }
  232. }
  233. #ifdef CLIENT_DLL
  234. void CBaseCSGrenade::DecrementAmmo( CBaseCombatCharacter *pOwner )
  235. {
  236. }
  237. void CBaseCSGrenade::DropGrenade()
  238. {
  239. m_bRedraw = true;
  240. m_fThrowTime = 0.0f;
  241. }
  242. void CBaseCSGrenade::ThrowGrenade()
  243. {
  244. m_bRedraw = true;
  245. m_fThrowTime = 0.0f;
  246. }
  247. void CBaseCSGrenade::StartGrenadeThrow()
  248. {
  249. m_fThrowTime = gpGlobals->curtime + 0.1f;
  250. }
  251. #else
  252. BEGIN_DATADESC( CBaseCSGrenade )
  253. DEFINE_FIELD( m_bRedraw, FIELD_BOOLEAN ),
  254. END_DATADESC()
  255. int CBaseCSGrenade::CapabilitiesGet()
  256. {
  257. return bits_CAP_WEAPON_RANGE_ATTACK1;
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Purpose:
  261. // Input : *pOwner -
  262. //-----------------------------------------------------------------------------
  263. void CBaseCSGrenade::DecrementAmmo( CBaseCombatCharacter *pOwner )
  264. {
  265. pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
  266. }
  267. void CBaseCSGrenade::StartGrenadeThrow()
  268. {
  269. m_fThrowTime = gpGlobals->curtime + 0.1f;
  270. }
  271. void CBaseCSGrenade::ThrowGrenade()
  272. {
  273. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  274. if ( !pPlayer )
  275. {
  276. Assert( false );
  277. return;
  278. }
  279. QAngle angThrow = pPlayer->LocalEyeAngles();
  280. Vector vForward, vRight, vUp;
  281. if ( angThrow.x < 0 )
  282. {
  283. angThrow.x += 360; // make sure we have a positive angle from LocalEyeAngles()
  284. }
  285. if ( angThrow.x < 90 )
  286. angThrow.x = -10 + angThrow.x * ((90 + 10) / 90.0);
  287. else
  288. {
  289. angThrow.x = 360.0f - angThrow.x;
  290. angThrow.x = -10 + angThrow.x * -((90 - 10) / 90.0);
  291. }
  292. float flVel = (90 - angThrow.x) * 6;
  293. if (flVel > 750)
  294. flVel = 750;
  295. AngleVectors( angThrow, &vForward, &vRight, &vUp );
  296. Vector vecSrc = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset();
  297. // We want to throw the grenade from 16 units out. But that can cause problems if we're facing
  298. // a thin wall. Do a hull trace to be safe.
  299. trace_t trace;
  300. Vector mins( -2, -2, -2 );
  301. Vector maxs( 2, 2, 2 );
  302. UTIL_TraceHull( vecSrc, vecSrc + vForward * 16, mins, maxs, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &trace );
  303. vecSrc = trace.endpos;
  304. Vector vecThrow = vForward * flVel + pPlayer->GetAbsVelocity();
  305. EmitGrenade( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer );
  306. m_bRedraw = true;
  307. m_fThrowTime = 0.0f;
  308. CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
  309. if( pCSPlayer )
  310. {
  311. if ( !sv_ignoregrenaderadio.GetBool() )
  312. {
  313. pCSPlayer->Radio( "Radio.FireInTheHole", "#Cstrike_TitlesTXT_Fire_in_the_hole" );
  314. }
  315. CCS_GameStats.IncrementStat(pCSPlayer, CSSTAT_GRENADES_THROWN, 1);
  316. }
  317. }
  318. void CBaseCSGrenade::DropGrenade()
  319. {
  320. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  321. if ( !pPlayer )
  322. {
  323. Assert( false );
  324. return;
  325. }
  326. Vector vForward;
  327. pPlayer->EyeVectors( &vForward );
  328. Vector vecSrc = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset() + vForward * 16;
  329. Vector vecVel = pPlayer->GetAbsVelocity();
  330. EmitGrenade( vecSrc, vec3_angle, vecVel, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer );
  331. CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
  332. if( pCSPlayer )
  333. {
  334. CCS_GameStats.IncrementStat(pCSPlayer, CSSTAT_GRENADES_THROWN, 1);
  335. }
  336. m_bRedraw = true;
  337. m_fThrowTime = 0.0f;
  338. }
  339. void CBaseCSGrenade::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer )
  340. {
  341. Assert( 0 && "CBaseCSGrenade::EmitGrenade should not be called. Make sure to implement this in your subclass!\n" );
  342. }
  343. bool CBaseCSGrenade::AllowsAutoSwitchFrom( void ) const
  344. {
  345. return !m_bPinPulled;
  346. }
  347. #endif