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.

480 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "basehlcombatweapon.h"
  8. #include "player.h"
  9. #include "gamerules.h"
  10. #include "grenade_frag.h"
  11. #include "npcevent.h"
  12. #include "engine/IEngineSound.h"
  13. #include "items.h"
  14. #include "in_buttons.h"
  15. #include "soundent.h"
  16. #include "gamestats.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. #define GRENADE_TIMER 3.0f //Seconds
  20. #define GRENADE_PAUSED_NO 0
  21. #define GRENADE_PAUSED_PRIMARY 1
  22. #define GRENADE_PAUSED_SECONDARY 2
  23. #define GRENADE_RADIUS 4.0f // inches
  24. //-----------------------------------------------------------------------------
  25. // Fragmentation grenades
  26. //-----------------------------------------------------------------------------
  27. class CWeaponFrag: public CBaseHLCombatWeapon
  28. {
  29. DECLARE_CLASS( CWeaponFrag, CBaseHLCombatWeapon );
  30. public:
  31. DECLARE_SERVERCLASS();
  32. public:
  33. CWeaponFrag();
  34. void Precache( void );
  35. void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
  36. void PrimaryAttack( void );
  37. void SecondaryAttack( void );
  38. void DecrementAmmo( CBaseCombatCharacter *pOwner );
  39. void ItemPostFrame( void );
  40. bool Deploy( void );
  41. bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
  42. int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; }
  43. bool Reload( void );
  44. bool ShouldDisplayHUDHint() { return true; }
  45. private:
  46. void ThrowGrenade( CBasePlayer *pPlayer );
  47. void RollGrenade( CBasePlayer *pPlayer );
  48. void LobGrenade( CBasePlayer *pPlayer );
  49. // check a throw from vecSrc. If not valid, move the position back along the line to vecEye
  50. void CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc );
  51. bool m_bRedraw; //Draw the weapon again after throwing a grenade
  52. int m_AttackPaused;
  53. bool m_fDrawbackFinished;
  54. DECLARE_ACTTABLE();
  55. DECLARE_DATADESC();
  56. };
  57. BEGIN_DATADESC( CWeaponFrag )
  58. DEFINE_FIELD( m_bRedraw, FIELD_BOOLEAN ),
  59. DEFINE_FIELD( m_AttackPaused, FIELD_INTEGER ),
  60. DEFINE_FIELD( m_fDrawbackFinished, FIELD_BOOLEAN ),
  61. END_DATADESC()
  62. acttable_t CWeaponFrag::m_acttable[] =
  63. {
  64. { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true },
  65. };
  66. IMPLEMENT_ACTTABLE(CWeaponFrag);
  67. IMPLEMENT_SERVERCLASS_ST(CWeaponFrag, DT_WeaponFrag)
  68. END_SEND_TABLE()
  69. LINK_ENTITY_TO_CLASS( weapon_frag, CWeaponFrag );
  70. PRECACHE_WEAPON_REGISTER(weapon_frag);
  71. CWeaponFrag::CWeaponFrag() :
  72. CBaseHLCombatWeapon(),
  73. m_bRedraw( false )
  74. {
  75. NULL;
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Purpose:
  79. //-----------------------------------------------------------------------------
  80. void CWeaponFrag::Precache( void )
  81. {
  82. BaseClass::Precache();
  83. UTIL_PrecacheOther( "npc_grenade_frag" );
  84. PrecacheScriptSound( "WeaponFrag.Throw" );
  85. PrecacheScriptSound( "WeaponFrag.Roll" );
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Purpose:
  89. //-----------------------------------------------------------------------------
  90. bool CWeaponFrag::Deploy( void )
  91. {
  92. m_bRedraw = false;
  93. m_fDrawbackFinished = false;
  94. return BaseClass::Deploy();
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose:
  98. // Output : Returns true on success, false on failure.
  99. //-----------------------------------------------------------------------------
  100. bool CWeaponFrag::Holster( CBaseCombatWeapon *pSwitchingTo )
  101. {
  102. m_bRedraw = false;
  103. m_fDrawbackFinished = false;
  104. return BaseClass::Holster( pSwitchingTo );
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Purpose:
  108. // Input : *pEvent -
  109. // *pOperator -
  110. //-----------------------------------------------------------------------------
  111. void CWeaponFrag::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  112. {
  113. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  114. bool fThrewGrenade = false;
  115. switch( pEvent->event )
  116. {
  117. case EVENT_WEAPON_SEQUENCE_FINISHED:
  118. m_fDrawbackFinished = true;
  119. break;
  120. case EVENT_WEAPON_THROW:
  121. ThrowGrenade( pOwner );
  122. DecrementAmmo( pOwner );
  123. fThrewGrenade = true;
  124. break;
  125. case EVENT_WEAPON_THROW2:
  126. RollGrenade( pOwner );
  127. DecrementAmmo( pOwner );
  128. fThrewGrenade = true;
  129. break;
  130. case EVENT_WEAPON_THROW3:
  131. LobGrenade( pOwner );
  132. DecrementAmmo( pOwner );
  133. fThrewGrenade = true;
  134. break;
  135. default:
  136. BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
  137. break;
  138. }
  139. #define RETHROW_DELAY 0.5
  140. if( fThrewGrenade )
  141. {
  142. m_flNextPrimaryAttack = gpGlobals->curtime + RETHROW_DELAY;
  143. m_flNextSecondaryAttack = gpGlobals->curtime + RETHROW_DELAY;
  144. m_flTimeWeaponIdle = FLT_MAX; //NOTE: This is set once the animation has finished up!
  145. // Make a sound designed to scare snipers back into their holes!
  146. CBaseCombatCharacter *pOwner = GetOwner();
  147. if( pOwner )
  148. {
  149. Vector vecSrc = pOwner->Weapon_ShootPosition();
  150. Vector vecDir;
  151. AngleVectors( pOwner->EyeAngles(), &vecDir );
  152. trace_t tr;
  153. UTIL_TraceLine( vecSrc, vecSrc + vecDir * 1024, MASK_SOLID_BRUSHONLY, pOwner, COLLISION_GROUP_NONE, &tr );
  154. CSoundEnt::InsertSound( SOUND_DANGER_SNIPERONLY, tr.endpos, 384, 0.2, pOwner );
  155. }
  156. }
  157. }
  158. //-----------------------------------------------------------------------------
  159. // Purpose:
  160. // Output : Returns true on success, false on failure.
  161. //-----------------------------------------------------------------------------
  162. bool CWeaponFrag::Reload( void )
  163. {
  164. if ( !HasPrimaryAmmo() )
  165. return false;
  166. if ( ( m_bRedraw ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
  167. {
  168. //Redraw the weapon
  169. SendWeaponAnim( ACT_VM_DRAW );
  170. //Update our times
  171. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  172. m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
  173. m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration();
  174. //Mark this as done
  175. m_bRedraw = false;
  176. }
  177. return true;
  178. }
  179. //-----------------------------------------------------------------------------
  180. // Purpose:
  181. //-----------------------------------------------------------------------------
  182. void CWeaponFrag::SecondaryAttack( void )
  183. {
  184. if ( m_bRedraw )
  185. return;
  186. if ( !HasPrimaryAmmo() )
  187. return;
  188. CBaseCombatCharacter *pOwner = GetOwner();
  189. if ( pOwner == NULL )
  190. return;
  191. CBasePlayer *pPlayer = ToBasePlayer( pOwner );
  192. if ( pPlayer == NULL )
  193. return;
  194. // Note that this is a secondary attack and prepare the grenade attack to pause.
  195. m_AttackPaused = GRENADE_PAUSED_SECONDARY;
  196. SendWeaponAnim( ACT_VM_PULLBACK_LOW );
  197. // Don't let weapon idle interfere in the middle of a throw!
  198. m_flTimeWeaponIdle = FLT_MAX;
  199. m_flNextSecondaryAttack = FLT_MAX;
  200. // If I'm now out of ammo, switch away
  201. if ( !HasPrimaryAmmo() )
  202. {
  203. pPlayer->SwitchToNextBestWeapon( this );
  204. }
  205. }
  206. //-----------------------------------------------------------------------------
  207. // Purpose:
  208. //-----------------------------------------------------------------------------
  209. void CWeaponFrag::PrimaryAttack( void )
  210. {
  211. if ( m_bRedraw )
  212. return;
  213. CBaseCombatCharacter *pOwner = GetOwner();
  214. if ( pOwner == NULL )
  215. {
  216. return;
  217. }
  218. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );;
  219. if ( !pPlayer )
  220. return;
  221. // Note that this is a primary attack and prepare the grenade attack to pause.
  222. m_AttackPaused = GRENADE_PAUSED_PRIMARY;
  223. SendWeaponAnim( ACT_VM_PULLBACK_HIGH );
  224. // Put both of these off indefinitely. We do not know how long
  225. // the player will hold the grenade.
  226. m_flTimeWeaponIdle = FLT_MAX;
  227. m_flNextPrimaryAttack = FLT_MAX;
  228. // If I'm now out of ammo, switch away
  229. if ( !HasPrimaryAmmo() )
  230. {
  231. pPlayer->SwitchToNextBestWeapon( this );
  232. }
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Purpose:
  236. // Input : *pOwner -
  237. //-----------------------------------------------------------------------------
  238. void CWeaponFrag::DecrementAmmo( CBaseCombatCharacter *pOwner )
  239. {
  240. pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose:
  244. //-----------------------------------------------------------------------------
  245. void CWeaponFrag::ItemPostFrame( void )
  246. {
  247. if( m_fDrawbackFinished )
  248. {
  249. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  250. if (pOwner)
  251. {
  252. switch( m_AttackPaused )
  253. {
  254. case GRENADE_PAUSED_PRIMARY:
  255. if( !(pOwner->m_nButtons & IN_ATTACK) )
  256. {
  257. SendWeaponAnim( ACT_VM_THROW );
  258. m_fDrawbackFinished = false;
  259. }
  260. break;
  261. case GRENADE_PAUSED_SECONDARY:
  262. if( !(pOwner->m_nButtons & IN_ATTACK2) )
  263. {
  264. //See if we're ducking
  265. if ( pOwner->m_nButtons & IN_DUCK )
  266. {
  267. //Send the weapon animation
  268. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  269. }
  270. else
  271. {
  272. //Send the weapon animation
  273. SendWeaponAnim( ACT_VM_HAULBACK );
  274. }
  275. m_fDrawbackFinished = false;
  276. }
  277. break;
  278. default:
  279. break;
  280. }
  281. }
  282. }
  283. BaseClass::ItemPostFrame();
  284. if ( m_bRedraw )
  285. {
  286. if ( IsViewModelSequenceFinished() )
  287. {
  288. Reload();
  289. }
  290. }
  291. }
  292. // check a throw from vecSrc. If not valid, move the position back along the line to vecEye
  293. void CWeaponFrag::CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc )
  294. {
  295. trace_t tr;
  296. UTIL_TraceHull( vecEye, vecSrc, -Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2), Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2),
  297. pPlayer->PhysicsSolidMaskForEntity(), pPlayer, pPlayer->GetCollisionGroup(), &tr );
  298. if ( tr.DidHit() )
  299. {
  300. vecSrc = tr.endpos;
  301. }
  302. }
  303. //-----------------------------------------------------------------------------
  304. // Purpose:
  305. // Input : *pPlayer -
  306. //-----------------------------------------------------------------------------
  307. void CWeaponFrag::ThrowGrenade( CBasePlayer *pPlayer )
  308. {
  309. Vector vecEye = pPlayer->EyePosition();
  310. Vector vForward, vRight;
  311. pPlayer->EyeVectors( &vForward, &vRight, NULL );
  312. Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f;
  313. CheckThrowPosition( pPlayer, vecEye, vecSrc );
  314. // vForward[0] += 0.1f;
  315. vForward[2] += 0.1f;
  316. Vector vecThrow;
  317. pPlayer->GetVelocity( &vecThrow, NULL );
  318. vecThrow += vForward * 1200;
  319. Fraggrenade_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer, GRENADE_TIMER, false );
  320. m_bRedraw = true;
  321. WeaponSound( SINGLE );
  322. m_iPrimaryAttacks++;
  323. gamestats->Event_WeaponFired( pPlayer, true, GetClassname() );
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Purpose:
  327. // Input : *pPlayer -
  328. //-----------------------------------------------------------------------------
  329. void CWeaponFrag::LobGrenade( CBasePlayer *pPlayer )
  330. {
  331. Vector vecEye = pPlayer->EyePosition();
  332. Vector vForward, vRight;
  333. pPlayer->EyeVectors( &vForward, &vRight, NULL );
  334. Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f + Vector( 0, 0, -8 );
  335. CheckThrowPosition( pPlayer, vecEye, vecSrc );
  336. Vector vecThrow;
  337. pPlayer->GetVelocity( &vecThrow, NULL );
  338. vecThrow += vForward * 350 + Vector( 0, 0, 50 );
  339. Fraggrenade_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(200,random->RandomInt(-600,600),0), pPlayer, GRENADE_TIMER, false );
  340. WeaponSound( WPN_DOUBLE );
  341. m_bRedraw = true;
  342. m_iPrimaryAttacks++;
  343. gamestats->Event_WeaponFired( pPlayer, true, GetClassname() );
  344. }
  345. //-----------------------------------------------------------------------------
  346. // Purpose:
  347. // Input : *pPlayer -
  348. //-----------------------------------------------------------------------------
  349. void CWeaponFrag::RollGrenade( CBasePlayer *pPlayer )
  350. {
  351. // BUGBUG: Hardcoded grenade width of 4 - better not change the model :)
  352. Vector vecSrc;
  353. pPlayer->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecSrc );
  354. vecSrc.z += GRENADE_RADIUS;
  355. Vector vecFacing = pPlayer->BodyDirection2D( );
  356. // no up/down direction
  357. vecFacing.z = 0;
  358. VectorNormalize( vecFacing );
  359. trace_t tr;
  360. UTIL_TraceLine( vecSrc, vecSrc - Vector(0,0,16), MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
  361. if ( tr.fraction != 1.0 )
  362. {
  363. // compute forward vec parallel to floor plane and roll grenade along that
  364. Vector tangent;
  365. CrossProduct( vecFacing, tr.plane.normal, tangent );
  366. CrossProduct( tr.plane.normal, tangent, vecFacing );
  367. }
  368. vecSrc += (vecFacing * 18.0);
  369. CheckThrowPosition( pPlayer, pPlayer->WorldSpaceCenter(), vecSrc );
  370. Vector vecThrow;
  371. pPlayer->GetVelocity( &vecThrow, NULL );
  372. vecThrow += vecFacing * 700;
  373. // put it on its side
  374. QAngle orientation(0,pPlayer->GetLocalAngles().y,-90);
  375. // roll it
  376. AngularImpulse rotSpeed(0,0,720);
  377. Fraggrenade_Create( vecSrc, orientation, vecThrow, rotSpeed, pPlayer, GRENADE_TIMER, false );
  378. WeaponSound( SPECIAL1 );
  379. m_bRedraw = true;
  380. m_iPrimaryAttacks++;
  381. gamestats->Event_WeaponFired( pPlayer, true, GetClassname() );
  382. }