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.

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