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.

377 lines
9.7 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: TF Base Grenade.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "tf_weaponbase.h"
  8. #include "tf_weaponbase_grenade.h"
  9. #include "tf_gamerules.h"
  10. #include "npcevent.h"
  11. #include "engine/IEngineSound.h"
  12. #include "in_buttons.h"
  13. #include "tf_weaponbase_grenadeproj.h"
  14. #include "eventlist.h"
  15. // Client specific.
  16. #ifdef CLIENT_DLL
  17. #include "c_tf_player.h"
  18. // Server specific.
  19. #else
  20. #include "tf_player.h"
  21. #include "items.h"
  22. #endif
  23. #define GRENADE_TIMER 1.5f // seconds
  24. //=============================================================================
  25. //
  26. // TF Grenade tables.
  27. //
  28. IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponBaseGrenade, DT_TFWeaponBaseGrenade )
  29. BEGIN_NETWORK_TABLE( CTFWeaponBaseGrenade, DT_TFWeaponBaseGrenade )
  30. // Client specific.
  31. #ifdef CLIENT_DLL
  32. RecvPropBool( RECVINFO( m_bPrimed ) ),
  33. RecvPropFloat( RECVINFO( m_flThrowTime ) ),
  34. RecvPropBool( RECVINFO( m_bThrow ) ),
  35. // Server specific.
  36. #else
  37. SendPropBool( SENDINFO( m_bPrimed ) ),
  38. SendPropTime( SENDINFO( m_flThrowTime ) ),
  39. SendPropBool( SENDINFO( m_bThrow ) ),
  40. #endif
  41. END_NETWORK_TABLE()
  42. BEGIN_PREDICTION_DATA( CTFWeaponBaseGrenade )
  43. #ifdef CLIENT_DLL
  44. DEFINE_PRED_FIELD( m_bPrimed, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  45. DEFINE_PRED_FIELD( m_flThrowTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  46. DEFINE_PRED_FIELD( m_bThrow, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  47. #endif
  48. END_PREDICTION_DATA()
  49. LINK_ENTITY_TO_CLASS( tf_weaponbase_grenade, CTFWeaponBaseGrenade );
  50. //=============================================================================
  51. //
  52. // TF Grenade functions.
  53. //
  54. CTFWeaponBaseGrenade::CTFWeaponBaseGrenade()
  55. {
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose: Constructor.
  59. //-----------------------------------------------------------------------------
  60. void CTFWeaponBaseGrenade::Spawn( void )
  61. {
  62. BaseClass::Spawn();
  63. SetViewModelIndex( 1 );
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose:
  67. //-----------------------------------------------------------------------------
  68. void CTFWeaponBaseGrenade::Precache()
  69. {
  70. BaseClass::Precache();
  71. }
  72. //-----------------------------------------------------------------------------
  73. // Purpose:
  74. //-----------------------------------------------------------------------------
  75. bool CTFWeaponBaseGrenade::IsPrimed( void )
  76. {
  77. return m_bPrimed;
  78. }
  79. //-----------------------------------------------------------------------------
  80. // Purpose:
  81. //-----------------------------------------------------------------------------
  82. bool CTFWeaponBaseGrenade::Deploy( void )
  83. {
  84. if ( BaseClass::Deploy() )
  85. {
  86. Prime();
  87. return true;
  88. }
  89. return false;
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose:
  93. //-----------------------------------------------------------------------------
  94. void CTFWeaponBaseGrenade::Prime()
  95. {
  96. CTFWeaponInfo weaponInfo = GetTFWpnData();
  97. m_flThrowTime = gpGlobals->curtime + weaponInfo.m_flPrimerTime;
  98. m_bPrimed = true;
  99. #ifndef CLIENT_DLL
  100. if ( GetWeaponID() != TF_WEAPON_GRENADE_SMOKE_BOMB )
  101. {
  102. // Get the player owning the weapon.
  103. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  104. if ( !pPlayer )
  105. return;
  106. pPlayer->RemoveInvisibility();
  107. }
  108. #endif
  109. }
  110. //-----------------------------------------------------------------------------
  111. // Purpose:
  112. //-----------------------------------------------------------------------------
  113. void CTFWeaponBaseGrenade::Throw()
  114. {
  115. if ( !m_bPrimed )
  116. return;
  117. m_bPrimed = false;
  118. m_bThrow = false;
  119. // Get the owning player.
  120. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  121. if ( !pPlayer )
  122. return;
  123. #ifdef GAME_DLL
  124. // Calculate the time remaining.
  125. float flTime = m_flThrowTime - gpGlobals->curtime;
  126. bool bExplodingInHand = ( flTime <= 0.0f );
  127. // Players who are dying may not have their death state set, so check that too
  128. bool bExplodingOnDeath = ( !pPlayer->IsAlive() || pPlayer->StateGet() == TF_STATE_DYING );
  129. Vector vecSrc, vecThrow;
  130. vecSrc = pPlayer->Weapon_ShootPosition();
  131. if ( bExplodingInHand || bExplodingOnDeath )
  132. {
  133. vecThrow = vec3_origin;
  134. }
  135. else
  136. {
  137. // Determine the throw angle and velocity.
  138. QAngle angThrow = pPlayer->LocalEyeAngles();
  139. if ( angThrow.x < 90.0f )
  140. {
  141. angThrow.x = -10.0f + angThrow.x * ( ( 90.0f + 10.0f ) / 90.0f );
  142. }
  143. else
  144. {
  145. angThrow.x = 360.0f - angThrow.x;
  146. angThrow.x = -10.0f + angThrow.x * -( ( 90.0f - 10.0f ) / 90.0f );
  147. }
  148. // Adjust for the lowering of the spawn point
  149. angThrow.x -= 10;
  150. float flVelocity = ( 90.0f - angThrow.x ) * 8.0f;
  151. if ( flVelocity > 950.0f )
  152. {
  153. flVelocity = 950.0f;
  154. }
  155. Vector vForward, vRight, vUp;
  156. AngleVectors( angThrow, &vForward, &vRight, &vUp );
  157. // Throw from the player's left hand position.
  158. vecSrc += vForward * 16.0f + vRight * -8.0f + vUp * -20.0f;
  159. vecThrow = vForward * flVelocity;
  160. }
  161. #if 0
  162. // Debug!!!
  163. char str[256];
  164. Q_snprintf( str, sizeof( str ),"GrenadeTime = %f\n", flTime );
  165. NDebugOverlay::ScreenText( 0.5f, 0.38f, str, 255, 255, 255, 255, 2.0f );
  166. #endif
  167. QAngle vecAngles = RandomAngle( 0, 360 );
  168. // Create the projectile and send in the time remaining.
  169. if ( !bExplodingInHand )
  170. {
  171. EmitGrenade( vecSrc, vecAngles, vecThrow, AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ), pPlayer, flTime );
  172. }
  173. else
  174. {
  175. // We're holding onto an exploding grenade
  176. CTFWeaponBaseGrenadeProj *pGrenade = EmitGrenade( vecSrc, vecAngles, vecThrow, AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ), pPlayer, 0.0 );
  177. if ( pGrenade )
  178. {
  179. pGrenade->Detonate();
  180. }
  181. }
  182. // The grenade is about to be destroyed, so it won't be able to holster.
  183. // Handle the viewmodel hiding for it.
  184. if ( bExplodingInHand || bExplodingOnDeath )
  185. {
  186. SendWeaponAnim( ACT_VM_IDLE );
  187. CBaseViewModel *vm = pPlayer->GetViewModel( 1 );
  188. if ( vm )
  189. {
  190. vm->AddEffects( EF_NODRAW );
  191. }
  192. }
  193. #endif
  194. // Reset the throw time
  195. m_flThrowTime = 0.0f;
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Purpose:
  199. //-----------------------------------------------------------------------------
  200. bool CTFWeaponBaseGrenade::ShouldDetonate( void )
  201. {
  202. return ( m_flThrowTime != 0.0f ) && ( m_flThrowTime < gpGlobals->curtime );
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Purpose:
  206. //-----------------------------------------------------------------------------
  207. void CTFWeaponBaseGrenade::ItemPostFrame()
  208. {
  209. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  210. if ( !pPlayer )
  211. return;
  212. if ( m_bPrimed )
  213. {
  214. // Is our timer up? If so, blow up immediately
  215. if ( ShouldDetonate() )
  216. {
  217. Throw();
  218. return;
  219. }
  220. if ( !m_bThrow && !( pPlayer->m_nButtons & IN_GRENADE1 || pPlayer->m_nButtons & IN_GRENADE2 ) )
  221. {
  222. // Start throwing
  223. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_GRENADE );
  224. m_bThrow = true;
  225. }
  226. }
  227. if ( m_bThrow )
  228. {
  229. if ( GetActivity() != ACT_VM_PRIMARYATTACK )
  230. {
  231. // Start the throw animation
  232. if ( !SendWeaponAnim( ACT_VM_PRIMARYATTACK ) )
  233. {
  234. Throw();
  235. }
  236. }
  237. else if ( HasWeaponIdleTimeElapsed() )
  238. {
  239. // The Throw call here exists solely to catch the lone case of thirdperson where the
  240. // viewmodel isn't being drawn, and hence the anim event doesn't trigger and force a throw.
  241. // In all other cases, it'll do nothing because the grenade has already been thrown.
  242. Throw();
  243. }
  244. return;
  245. }
  246. if ( !m_bPrimed && !m_bThrow )
  247. {
  248. // Once we've finished being holstered, we'll be hidden. When that happens,
  249. // tell our player that we're all done with the grenade throw.
  250. if ( IsEffectActive(EF_NODRAW) )
  251. {
  252. pPlayer->FinishThrowGrenade();
  253. return;
  254. }
  255. // We've been thrown. Go away.
  256. if ( HasWeaponIdleTimeElapsed() )
  257. {
  258. Holster();
  259. }
  260. }
  261. // Go straight to idle anim when deploy is done
  262. if ( m_flTimeWeaponIdle <= gpGlobals->curtime )
  263. {
  264. SendWeaponAnim( ACT_VM_IDLE );
  265. }
  266. }
  267. bool CTFWeaponBaseGrenade::ShouldLowerMainWeapon( void )
  268. {
  269. return GetTFWpnData().m_bLowerWeapon;
  270. }
  271. //=============================================================================
  272. //
  273. // Client specific functions.
  274. //
  275. #ifdef CLIENT_DLL
  276. //-----------------------------------------------------------------------------
  277. // Purpose:
  278. //-----------------------------------------------------------------------------
  279. bool CTFWeaponBaseGrenade::ShouldDraw( void )
  280. {
  281. if ( !BaseClass::ShouldDraw() )
  282. {
  283. // Grenades need to be visible whenever they're being primed & thrown
  284. if ( !m_bPrimed )
  285. return false;
  286. // Don't draw primed grenades for local player in first person players
  287. if ( !(ToPlayer(GetOwner())->ShouldDrawThisPlayer()) )
  288. return false;
  289. }
  290. return true;
  291. }
  292. //=============================================================================
  293. //
  294. // Server specific functions.
  295. //
  296. #else
  297. BEGIN_DATADESC( CTFWeaponBaseGrenade )
  298. END_DATADESC()
  299. //-----------------------------------------------------------------------------
  300. // Purpose:
  301. //-----------------------------------------------------------------------------
  302. void CTFWeaponBaseGrenade::HandleAnimEvent( animevent_t *pEvent )
  303. {
  304. if ( (pEvent->type & AE_TYPE_NEWEVENTSYSTEM) )
  305. {
  306. if ( pEvent->event == AE_WPN_PRIMARYATTACK )
  307. {
  308. Throw();
  309. return;
  310. }
  311. }
  312. BaseClass::HandleAnimEvent( pEvent );
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose:
  316. //-----------------------------------------------------------------------------
  317. CTFWeaponBaseGrenadeProj *CTFWeaponBaseGrenade::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flTime, int iFlags )
  318. {
  319. Assert( 0 && "CBaseCSGrenade::EmitGrenade should not be called. Make sure to implement this in your subclass!\n" );
  320. return NULL;
  321. }
  322. #endif