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.

552 lines
14 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. #include "c_te_effect_dispatch.h"
  12. #else
  13. #include "hl2mp_player.h"
  14. #include "te_effect_dispatch.h"
  15. #include "grenade_frag.h"
  16. #endif
  17. #include "weapon_ar2.h"
  18. #include "effect_dispatch_data.h"
  19. #include "weapon_hl2mpbasehlmpcombatweapon.h"
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. #define GRENADE_TIMER 2.5f //Seconds
  23. #define GRENADE_PAUSED_NO 0
  24. #define GRENADE_PAUSED_PRIMARY 1
  25. #define GRENADE_PAUSED_SECONDARY 2
  26. #define GRENADE_RADIUS 4.0f // inches
  27. #define GRENADE_DAMAGE_RADIUS 250.0f
  28. #ifdef CLIENT_DLL
  29. #define CWeaponFrag C_WeaponFrag
  30. #endif
  31. //-----------------------------------------------------------------------------
  32. // Fragmentation grenades
  33. //-----------------------------------------------------------------------------
  34. class CWeaponFrag: public CBaseHL2MPCombatWeapon
  35. {
  36. DECLARE_CLASS( CWeaponFrag, CBaseHL2MPCombatWeapon );
  37. public:
  38. DECLARE_NETWORKCLASS();
  39. DECLARE_PREDICTABLE();
  40. CWeaponFrag();
  41. void Precache( void );
  42. void PrimaryAttack( void );
  43. void SecondaryAttack( void );
  44. void DecrementAmmo( CBaseCombatCharacter *pOwner );
  45. void ItemPostFrame( void );
  46. bool Deploy( void );
  47. bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
  48. bool Reload( void );
  49. #ifndef CLIENT_DLL
  50. void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
  51. #endif
  52. void ThrowGrenade( CBasePlayer *pPlayer );
  53. bool IsPrimed( bool ) { return ( m_AttackPaused != 0 ); }
  54. private:
  55. void RollGrenade( CBasePlayer *pPlayer );
  56. void LobGrenade( CBasePlayer *pPlayer );
  57. // check a throw from vecSrc. If not valid, move the position back along the line to vecEye
  58. void CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc );
  59. CNetworkVar( bool, m_bRedraw ); //Draw the weapon again after throwing a grenade
  60. CNetworkVar( int, m_AttackPaused );
  61. CNetworkVar( bool, m_fDrawbackFinished );
  62. CWeaponFrag( const CWeaponFrag & );
  63. #ifndef CLIENT_DLL
  64. DECLARE_ACTTABLE();
  65. #endif
  66. };
  67. #ifndef CLIENT_DLL
  68. acttable_t CWeaponFrag::m_acttable[] =
  69. {
  70. { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_GRENADE, false },
  71. { ACT_HL2MP_RUN, ACT_HL2MP_RUN_GRENADE, false },
  72. { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_GRENADE, false },
  73. { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_GRENADE, false },
  74. { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE, false },
  75. { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_GRENADE, false },
  76. { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_GRENADE, false },
  77. };
  78. IMPLEMENT_ACTTABLE(CWeaponFrag);
  79. #endif
  80. IMPLEMENT_NETWORKCLASS_ALIASED( WeaponFrag, DT_WeaponFrag )
  81. BEGIN_NETWORK_TABLE( CWeaponFrag, DT_WeaponFrag )
  82. #ifdef CLIENT_DLL
  83. RecvPropBool( RECVINFO( m_bRedraw ) ),
  84. RecvPropBool( RECVINFO( m_fDrawbackFinished ) ),
  85. RecvPropInt( RECVINFO( m_AttackPaused ) ),
  86. #else
  87. SendPropBool( SENDINFO( m_bRedraw ) ),
  88. SendPropBool( SENDINFO( m_fDrawbackFinished ) ),
  89. SendPropInt( SENDINFO( m_AttackPaused ) ),
  90. #endif
  91. END_NETWORK_TABLE()
  92. #ifdef CLIENT_DLL
  93. BEGIN_PREDICTION_DATA( CWeaponFrag )
  94. DEFINE_PRED_FIELD( m_bRedraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  95. DEFINE_PRED_FIELD( m_fDrawbackFinished, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  96. DEFINE_PRED_FIELD( m_AttackPaused, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  97. END_PREDICTION_DATA()
  98. #endif
  99. LINK_ENTITY_TO_CLASS( weapon_frag, CWeaponFrag );
  100. PRECACHE_WEAPON_REGISTER(weapon_frag);
  101. CWeaponFrag::CWeaponFrag( void ) :
  102. CBaseHL2MPCombatWeapon()
  103. {
  104. m_bRedraw = false;
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Purpose:
  108. //-----------------------------------------------------------------------------
  109. void CWeaponFrag::Precache( void )
  110. {
  111. BaseClass::Precache();
  112. #ifndef CLIENT_DLL
  113. UTIL_PrecacheOther( "npc_grenade_frag" );
  114. #endif
  115. PrecacheScriptSound( "WeaponFrag.Throw" );
  116. PrecacheScriptSound( "WeaponFrag.Roll" );
  117. }
  118. #ifndef CLIENT_DLL
  119. //-----------------------------------------------------------------------------
  120. // Purpose:
  121. // Input : *pEvent -
  122. // *pOperator -
  123. //-----------------------------------------------------------------------------
  124. void CWeaponFrag::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  125. {
  126. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  127. bool fThrewGrenade = false;
  128. switch( pEvent->event )
  129. {
  130. case EVENT_WEAPON_SEQUENCE_FINISHED:
  131. m_fDrawbackFinished = true;
  132. break;
  133. case EVENT_WEAPON_THROW:
  134. ThrowGrenade( pOwner );
  135. DecrementAmmo( pOwner );
  136. fThrewGrenade = true;
  137. break;
  138. case EVENT_WEAPON_THROW2:
  139. RollGrenade( pOwner );
  140. DecrementAmmo( pOwner );
  141. fThrewGrenade = true;
  142. break;
  143. case EVENT_WEAPON_THROW3:
  144. LobGrenade( pOwner );
  145. DecrementAmmo( pOwner );
  146. fThrewGrenade = true;
  147. break;
  148. default:
  149. BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
  150. break;
  151. }
  152. #define RETHROW_DELAY 0.5
  153. if( fThrewGrenade )
  154. {
  155. m_flNextPrimaryAttack = gpGlobals->curtime + RETHROW_DELAY;
  156. m_flNextSecondaryAttack = gpGlobals->curtime + RETHROW_DELAY;
  157. m_flTimeWeaponIdle = FLT_MAX; //NOTE: This is set once the animation has finished up!
  158. }
  159. }
  160. #endif
  161. //-----------------------------------------------------------------------------
  162. // Purpose:
  163. //-----------------------------------------------------------------------------
  164. bool CWeaponFrag::Deploy( void )
  165. {
  166. m_bRedraw = false;
  167. m_fDrawbackFinished = false;
  168. return BaseClass::Deploy();
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose:
  172. // Output : Returns true on success, false on failure.
  173. //-----------------------------------------------------------------------------
  174. bool CWeaponFrag::Holster( CBaseCombatWeapon *pSwitchingTo )
  175. {
  176. m_bRedraw = false;
  177. m_fDrawbackFinished = false;
  178. return BaseClass::Holster( pSwitchingTo );
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose:
  182. // Output : Returns true on success, false on failure.
  183. //-----------------------------------------------------------------------------
  184. bool CWeaponFrag::Reload( void )
  185. {
  186. if ( !HasPrimaryAmmo() )
  187. return false;
  188. if ( ( m_bRedraw ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
  189. {
  190. //Redraw the weapon
  191. SendWeaponAnim( ACT_VM_DRAW );
  192. //Update our times
  193. m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
  194. m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
  195. m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration();
  196. //Mark this as done
  197. m_bRedraw = false;
  198. }
  199. return true;
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Purpose:
  203. //-----------------------------------------------------------------------------
  204. void CWeaponFrag::SecondaryAttack( void )
  205. {
  206. if ( m_bRedraw )
  207. return;
  208. if ( !HasPrimaryAmmo() )
  209. return;
  210. CBaseCombatCharacter *pOwner = GetOwner();
  211. if ( pOwner == NULL )
  212. return;
  213. CBasePlayer *pPlayer = ToBasePlayer( pOwner );
  214. if ( pPlayer == NULL )
  215. return;
  216. // Note that this is a secondary attack and prepare the grenade attack to pause.
  217. m_AttackPaused = GRENADE_PAUSED_SECONDARY;
  218. SendWeaponAnim( ACT_VM_PULLBACK_LOW );
  219. // Don't let weapon idle interfere in the middle of a throw!
  220. m_flTimeWeaponIdle = FLT_MAX;
  221. m_flNextSecondaryAttack = FLT_MAX;
  222. // If I'm now out of ammo, switch away
  223. if ( !HasPrimaryAmmo() )
  224. {
  225. pPlayer->SwitchToNextBestWeapon( this );
  226. }
  227. }
  228. //-----------------------------------------------------------------------------
  229. // Purpose:
  230. //-----------------------------------------------------------------------------
  231. void CWeaponFrag::PrimaryAttack( void )
  232. {
  233. if ( m_bRedraw )
  234. return;
  235. CBaseCombatCharacter *pOwner = GetOwner();
  236. if ( pOwner == NULL )
  237. {
  238. return;
  239. }
  240. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );;
  241. if ( !pPlayer )
  242. return;
  243. // Note that this is a primary attack and prepare the grenade attack to pause.
  244. m_AttackPaused = GRENADE_PAUSED_PRIMARY;
  245. SendWeaponAnim( ACT_VM_PULLBACK_HIGH );
  246. // Put both of these off indefinitely. We do not know how long
  247. // the player will hold the grenade.
  248. m_flTimeWeaponIdle = FLT_MAX;
  249. m_flNextPrimaryAttack = FLT_MAX;
  250. // If I'm now out of ammo, switch away
  251. if ( !HasPrimaryAmmo() )
  252. {
  253. pPlayer->SwitchToNextBestWeapon( this );
  254. }
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose:
  258. // Input : *pOwner -
  259. //-----------------------------------------------------------------------------
  260. void CWeaponFrag::DecrementAmmo( CBaseCombatCharacter *pOwner )
  261. {
  262. pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose:
  266. //-----------------------------------------------------------------------------
  267. void CWeaponFrag::ItemPostFrame( void )
  268. {
  269. if( m_fDrawbackFinished )
  270. {
  271. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  272. if (pOwner)
  273. {
  274. switch( m_AttackPaused )
  275. {
  276. case GRENADE_PAUSED_PRIMARY:
  277. if( !(pOwner->m_nButtons & IN_ATTACK) )
  278. {
  279. SendWeaponAnim( ACT_VM_THROW );
  280. m_fDrawbackFinished = false;
  281. }
  282. break;
  283. case GRENADE_PAUSED_SECONDARY:
  284. if( !(pOwner->m_nButtons & IN_ATTACK2) )
  285. {
  286. //See if we're ducking
  287. if ( pOwner->m_nButtons & IN_DUCK )
  288. {
  289. //Send the weapon animation
  290. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  291. }
  292. else
  293. {
  294. //Send the weapon animation
  295. SendWeaponAnim( ACT_VM_HAULBACK );
  296. }
  297. m_fDrawbackFinished = false;
  298. }
  299. break;
  300. default:
  301. break;
  302. }
  303. }
  304. }
  305. BaseClass::ItemPostFrame();
  306. if ( m_bRedraw )
  307. {
  308. if ( IsViewModelSequenceFinished() )
  309. {
  310. Reload();
  311. }
  312. }
  313. }
  314. // check a throw from vecSrc. If not valid, move the position back along the line to vecEye
  315. void CWeaponFrag::CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc )
  316. {
  317. trace_t tr;
  318. UTIL_TraceHull( vecEye, vecSrc, -Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2), Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2),
  319. pPlayer->PhysicsSolidMaskForEntity(), pPlayer, pPlayer->GetCollisionGroup(), &tr );
  320. if ( tr.DidHit() )
  321. {
  322. vecSrc = tr.endpos;
  323. }
  324. }
  325. void DropPrimedFragGrenade( CHL2MP_Player *pPlayer, CBaseCombatWeapon *pGrenade )
  326. {
  327. CWeaponFrag *pWeaponFrag = dynamic_cast<CWeaponFrag*>( pGrenade );
  328. if ( pWeaponFrag )
  329. {
  330. pWeaponFrag->ThrowGrenade( pPlayer );
  331. pWeaponFrag->DecrementAmmo( pPlayer );
  332. }
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose:
  336. // Input : *pPlayer -
  337. //-----------------------------------------------------------------------------
  338. void CWeaponFrag::ThrowGrenade( CBasePlayer *pPlayer )
  339. {
  340. #ifndef CLIENT_DLL
  341. Vector vecEye = pPlayer->EyePosition();
  342. Vector vForward, vRight;
  343. pPlayer->EyeVectors( &vForward, &vRight, NULL );
  344. Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f;
  345. CheckThrowPosition( pPlayer, vecEye, vecSrc );
  346. // vForward[0] += 0.1f;
  347. vForward[2] += 0.1f;
  348. Vector vecThrow;
  349. pPlayer->GetVelocity( &vecThrow, NULL );
  350. vecThrow += vForward * 1200;
  351. CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer, GRENADE_TIMER, false );
  352. if ( pGrenade )
  353. {
  354. if ( pPlayer && pPlayer->m_lifeState != LIFE_ALIVE )
  355. {
  356. pPlayer->GetVelocity( &vecThrow, NULL );
  357. IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
  358. if ( pPhysicsObject )
  359. {
  360. pPhysicsObject->SetVelocity( &vecThrow, NULL );
  361. }
  362. }
  363. pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
  364. pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS );
  365. }
  366. #endif
  367. m_bRedraw = true;
  368. WeaponSound( SINGLE );
  369. // player "shoot" animation
  370. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose:
  374. // Input : *pPlayer -
  375. //-----------------------------------------------------------------------------
  376. void CWeaponFrag::LobGrenade( CBasePlayer *pPlayer )
  377. {
  378. #ifndef CLIENT_DLL
  379. Vector vecEye = pPlayer->EyePosition();
  380. Vector vForward, vRight;
  381. pPlayer->EyeVectors( &vForward, &vRight, NULL );
  382. Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f + Vector( 0, 0, -8 );
  383. CheckThrowPosition( pPlayer, vecEye, vecSrc );
  384. Vector vecThrow;
  385. pPlayer->GetVelocity( &vecThrow, NULL );
  386. vecThrow += vForward * 350 + Vector( 0, 0, 50 );
  387. CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(200,random->RandomInt(-600,600),0), pPlayer, GRENADE_TIMER, false );
  388. if ( pGrenade )
  389. {
  390. pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
  391. pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS );
  392. }
  393. #endif
  394. WeaponSound( WPN_DOUBLE );
  395. // player "shoot" animation
  396. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  397. m_bRedraw = true;
  398. }
  399. //-----------------------------------------------------------------------------
  400. // Purpose:
  401. // Input : *pPlayer -
  402. //-----------------------------------------------------------------------------
  403. void CWeaponFrag::RollGrenade( CBasePlayer *pPlayer )
  404. {
  405. #ifndef CLIENT_DLL
  406. // BUGBUG: Hardcoded grenade width of 4 - better not change the model :)
  407. Vector vecSrc;
  408. pPlayer->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecSrc );
  409. vecSrc.z += GRENADE_RADIUS;
  410. Vector vecFacing = pPlayer->BodyDirection2D( );
  411. // no up/down direction
  412. vecFacing.z = 0;
  413. VectorNormalize( vecFacing );
  414. trace_t tr;
  415. UTIL_TraceLine( vecSrc, vecSrc - Vector(0,0,16), MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
  416. if ( tr.fraction != 1.0 )
  417. {
  418. // compute forward vec parallel to floor plane and roll grenade along that
  419. Vector tangent;
  420. CrossProduct( vecFacing, tr.plane.normal, tangent );
  421. CrossProduct( tr.plane.normal, tangent, vecFacing );
  422. }
  423. vecSrc += (vecFacing * 18.0);
  424. CheckThrowPosition( pPlayer, pPlayer->WorldSpaceCenter(), vecSrc );
  425. Vector vecThrow;
  426. pPlayer->GetVelocity( &vecThrow, NULL );
  427. vecThrow += vecFacing * 700;
  428. // put it on its side
  429. QAngle orientation(0,pPlayer->GetLocalAngles().y,-90);
  430. // roll it
  431. AngularImpulse rotSpeed(0,0,720);
  432. CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, orientation, vecThrow, rotSpeed, pPlayer, GRENADE_TIMER, false );
  433. if ( pGrenade )
  434. {
  435. pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
  436. pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS );
  437. }
  438. #endif
  439. WeaponSound( SPECIAL1 );
  440. // player "shoot" animation
  441. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  442. m_bRedraw = true;
  443. }