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.

456 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Hand grenade
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "npcevent.h"
  9. #include "hl1mp_basecombatweapon_shared.h"
  10. //#include "basecombatcharacter.h"
  11. //#include "AI_BaseNPC.h"
  12. #ifdef CLIENT_DLL
  13. #include "hl1/hl1_c_player.h"
  14. #else
  15. #include "hl1_player.h"
  16. #endif
  17. #include "gamerules.h"
  18. #include "in_buttons.h"
  19. #ifdef CLIENT_DLL
  20. #else
  21. #include "soundent.h"
  22. #include "game.h"
  23. #endif
  24. #include "vstdlib/random.h"
  25. #include "engine/IEngineSound.h"
  26. #ifdef CLIENT_DLL
  27. #else
  28. #include "hl1_basegrenade.h"
  29. #endif
  30. #define HANDGRENADE_MODEL "models/w_grenade.mdl"
  31. #ifndef CLIENT_DLL
  32. extern ConVar sk_plr_dmg_grenade;
  33. //-----------------------------------------------------------------------------
  34. // CHandGrenade
  35. //-----------------------------------------------------------------------------
  36. LINK_ENTITY_TO_CLASS( grenade_hand, CHandGrenade );
  37. BEGIN_DATADESC( CHandGrenade )
  38. DEFINE_ENTITYFUNC( BounceTouch ),
  39. END_DATADESC()
  40. void CHandGrenade::Spawn( void )
  41. {
  42. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
  43. SetSolid( SOLID_BBOX );
  44. AddSolidFlags( FSOLID_NOT_STANDABLE );
  45. SetModel( HANDGRENADE_MODEL );
  46. UTIL_SetSize( this, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
  47. m_bHasWarnedAI = false;
  48. }
  49. void CHandGrenade::Precache( void )
  50. {
  51. BaseClass::Precache( );
  52. PrecacheScriptSound( "Weapon_HandGrenade.GrenadeBounce" );
  53. PrecacheModel( HANDGRENADE_MODEL );
  54. }
  55. void CHandGrenade::ShootTimed( CBaseCombatCharacter *pOwner, Vector vecVelocity, float flTime )
  56. {
  57. SetAbsVelocity( vecVelocity );
  58. SetThrower( pOwner );
  59. SetOwnerEntity( pOwner );
  60. SetTouch( &CHandGrenade::BounceTouch ); // Bounce if touched
  61. m_flDetonateTime = gpGlobals->curtime + flTime;
  62. SetThink( &CBaseGrenade::TumbleThink );
  63. SetNextThink( gpGlobals->curtime + 0.1 );
  64. if ( flTime < 0.1 )
  65. {
  66. SetNextThink( gpGlobals->curtime );
  67. SetAbsVelocity( vec3_origin );
  68. }
  69. // SetSequence( SelectWeightedSequence( ACT_GRENADE_TOSS ) );
  70. SetSequence( 0 );
  71. m_flPlaybackRate = 1.0;
  72. SetAbsAngles( QAngle( 0,0,60) );
  73. AngularImpulse angImpulse;
  74. angImpulse[0] = random->RandomInt( -200, 200 );
  75. angImpulse[1] = random->RandomInt( 400, 500 );
  76. angImpulse[2] = random->RandomInt( -100, 100 );
  77. ApplyLocalAngularVelocityImpulse( angImpulse );
  78. SetGravity( UTIL_ScaleForGravity( 400 ) ); // use a lower gravity for grenades to make them easier to see
  79. SetFriction( 0.8 );
  80. SetDamage( sk_plr_dmg_grenade.GetFloat() );
  81. SetDamageRadius( GetDamage() * 2.5 );
  82. }
  83. void CHandGrenade ::BounceSound( void )
  84. {
  85. EmitSound( "Weapon_HandGrenade.GrenadeBounce" );
  86. }
  87. void CHandGrenade::BounceTouch( CBaseEntity *pOther )
  88. {
  89. if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS) )
  90. return;
  91. // don't hit the guy that launched this grenade
  92. if ( pOther == GetThrower() )
  93. return;
  94. // Do a special test for players
  95. if ( pOther->IsPlayer() )
  96. {
  97. // Never hit a player again (we'll explode and fixup anyway)
  98. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  99. }
  100. // only do damage if we're moving fairly fast
  101. if ( (pOther->m_takedamage != DAMAGE_NO) && (m_flNextAttack < gpGlobals->curtime && GetAbsVelocity().Length() > 100))
  102. {
  103. if ( GetThrower() )
  104. {
  105. trace_t tr;
  106. tr = CBaseEntity::GetTouchTrace( );
  107. ClearMultiDamage( );
  108. Vector forward;
  109. AngleVectors( GetAbsAngles(), &forward );
  110. CTakeDamageInfo info( this, GetThrower(), 1, DMG_CLUB );
  111. CalculateMeleeDamageForce( &info, forward, tr.endpos );
  112. pOther->DispatchTraceAttack( info, forward, &tr );
  113. ApplyMultiDamage();
  114. }
  115. m_flNextAttack = gpGlobals->curtime + 1.0; // debounce
  116. }
  117. Vector vecTestVelocity;
  118. // m_vecAngVelocity = Vector (300, 300, 300);
  119. // this is my heuristic for modulating the grenade velocity because grenades dropped purely vertical
  120. // or thrown very far tend to slow down too quickly for me to always catch just by testing velocity.
  121. // trimming the Z velocity a bit seems to help quite a bit.
  122. vecTestVelocity = GetAbsVelocity();
  123. vecTestVelocity.z *= 0.45;
  124. if ( !m_bHasWarnedAI && vecTestVelocity.Length() <= 60 )
  125. {
  126. // grenade is moving really slow. It's probably very close to where it will ultimately stop moving.
  127. // emit the danger sound.
  128. // register a radius louder than the explosion, so we make sure everyone gets out of the way
  129. CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), m_flDamage / 0.4, 0.3 );
  130. m_bHasWarnedAI = TRUE;
  131. }
  132. // HACKHACK - On ground isn't always set, so look for ground underneath
  133. trace_t tr;
  134. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,10), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  135. if ( tr.fraction < 1.0 )
  136. {
  137. // add a bit of static friction
  138. // SetAbsVelocity( GetAbsVelocity() * 0.8 );
  139. SetSequence( SelectWeightedSequence( ACT_IDLE ) );
  140. SetAbsAngles( vec3_angle );
  141. }
  142. // play bounce sound
  143. BounceSound();
  144. m_flPlaybackRate = GetAbsVelocity().Length() / 200.0;
  145. if (m_flPlaybackRate > 1.0)
  146. m_flPlaybackRate = 1;
  147. else if (m_flPlaybackRate < 0.5)
  148. m_flPlaybackRate = 0;
  149. }
  150. #endif
  151. #ifdef CLIENT_DLL
  152. #define CWeaponHandGrenade C_WeaponHandGrenade
  153. #endif
  154. //-----------------------------------------------------------------------------
  155. // CWeaponHandGrenade
  156. //-----------------------------------------------------------------------------
  157. class CWeaponHandGrenade : public CBaseHL1MPCombatWeapon
  158. {
  159. DECLARE_CLASS( CWeaponHandGrenade, CBaseHL1MPCombatWeapon );
  160. public:
  161. DECLARE_NETWORKCLASS();
  162. DECLARE_PREDICTABLE();
  163. CWeaponHandGrenade( void );
  164. void Precache( void );
  165. void PrimaryAttack( void );
  166. void WeaponIdle( void );
  167. bool Deploy( void );
  168. bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
  169. // DECLARE_SERVERCLASS();
  170. DECLARE_DATADESC();
  171. private:
  172. // float m_flStartThrow;
  173. // float m_flReleaseThrow;
  174. CNetworkVar( float, m_flStartThrow );
  175. CNetworkVar( float, m_flReleaseThrow );
  176. };
  177. IMPLEMENT_NETWORKCLASS_ALIASED( WeaponHandGrenade, DT_WeaponHandGrenade );
  178. BEGIN_NETWORK_TABLE( CWeaponHandGrenade, DT_WeaponHandGrenade )
  179. #ifdef CLIENT_DLL
  180. RecvPropFloat( RECVINFO( m_flStartThrow ) ),
  181. RecvPropFloat( RECVINFO( m_flReleaseThrow ) ),
  182. #else
  183. SendPropFloat( SENDINFO( m_flStartThrow ) ),
  184. SendPropFloat( SENDINFO( m_flReleaseThrow ) ),
  185. #endif
  186. END_NETWORK_TABLE()
  187. BEGIN_PREDICTION_DATA( CWeaponHandGrenade )
  188. #ifdef CLIENT_DLL
  189. DEFINE_PRED_FIELD( m_flStartThrow, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  190. DEFINE_PRED_FIELD( m_flReleaseThrow, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  191. #endif
  192. END_PREDICTION_DATA()
  193. LINK_ENTITY_TO_CLASS( weapon_handgrenade, CWeaponHandGrenade );
  194. PRECACHE_WEAPON_REGISTER( weapon_handgrenade );
  195. //IMPLEMENT_SERVERCLASS_ST( CWeaponHandGrenade, DT_WeaponHandGrenade )
  196. //END_SEND_TABLE()
  197. BEGIN_DATADESC( CWeaponHandGrenade )
  198. END_DATADESC()
  199. //-----------------------------------------------------------------------------
  200. // Purpose: Constructor
  201. //-----------------------------------------------------------------------------
  202. CWeaponHandGrenade::CWeaponHandGrenade( void )
  203. {
  204. m_bReloadsSingly = false;
  205. m_bFiresUnderwater = true;
  206. }
  207. //-----------------------------------------------------------------------------
  208. // Purpose:
  209. //-----------------------------------------------------------------------------
  210. void CWeaponHandGrenade::Precache( void )
  211. {
  212. #ifndef CLIENT_DLL
  213. UTIL_PrecacheOther( "grenade_hand" );
  214. #endif
  215. BaseClass::Precache();
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Purpose:
  219. //-----------------------------------------------------------------------------
  220. void CWeaponHandGrenade::PrimaryAttack( void )
  221. {
  222. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  223. if ( !pPlayer )
  224. {
  225. return;
  226. }
  227. if ( ( m_flStartThrow <= 0 ) && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
  228. {
  229. m_flStartThrow = gpGlobals->curtime;
  230. m_flReleaseThrow = 0;
  231. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  232. SetWeaponIdleTime( gpGlobals->curtime + 0.5 );
  233. }
  234. }
  235. void CWeaponHandGrenade::WeaponIdle( void )
  236. {
  237. if ( m_flReleaseThrow == 0 && m_flStartThrow )
  238. m_flReleaseThrow = gpGlobals->curtime;
  239. if ( !HasWeaponIdleTimeElapsed() )
  240. return;
  241. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  242. if ( !pPlayer )
  243. {
  244. return;
  245. }
  246. if ( m_flStartThrow )
  247. {
  248. Vector vecAiming = pPlayer->GetAutoaimVector( 0 );
  249. QAngle angThrow;
  250. VectorAngles( vecAiming, angThrow );
  251. Vector vecUp;
  252. Vector vecRight;
  253. AngleVectors( angThrow, NULL, &vecRight, &vecUp );
  254. if ( angThrow.x > 180 ) // player is pitching up
  255. angThrow.x = -15 - ( 360 - angThrow.x ) * ( ( 90 - 10 ) / 90.0 );
  256. else // player is pitching down
  257. angThrow.x = -15 + angThrow.x * ( ( 90 + 10 ) / 90.0 );
  258. float flVel = ( 90 - angThrow.x ) * 4;
  259. if ( flVel > 500 )
  260. flVel = 500;
  261. Vector vecFwd;
  262. AngleVectors( angThrow, &vecFwd );
  263. Vector vecSrc = pPlayer->EyePosition() + vecFwd * 16;
  264. Vector vecThrow = vecFwd * flVel + pPlayer->GetAbsVelocity();
  265. QAngle angles;
  266. VectorAngles( vecThrow, angles );
  267. #ifndef CLIENT_DLL
  268. CHandGrenade *pGrenade = (CHandGrenade*)Create( "grenade_hand", vecSrc, angles );
  269. if ( pGrenade )
  270. {
  271. // always explode 3 seconds after the pin was pulled
  272. float flTime = m_flStartThrow - gpGlobals->curtime + 3.0;
  273. if ( flTime < 0 )
  274. {
  275. flTime = 0;
  276. }
  277. pGrenade->ShootTimed( pPlayer, vecThrow, flTime );
  278. }
  279. #endif
  280. if ( flVel < 500 )
  281. {
  282. SendWeaponAnim( ACT_HANDGRENADE_THROW1 );
  283. }
  284. else if ( flVel < 1000 )
  285. {
  286. SendWeaponAnim( ACT_HANDGRENADE_THROW2 );
  287. }
  288. else
  289. {
  290. SendWeaponAnim( ACT_HANDGRENADE_THROW3 );
  291. }
  292. // player "shoot" animation
  293. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  294. m_flReleaseThrow = 0;
  295. m_flStartThrow = 0;
  296. SetWeaponIdleTime( gpGlobals->curtime + 0.5 );
  297. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
  298. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
  299. pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
  300. return;
  301. }
  302. else if ( m_flReleaseThrow > 0 )
  303. {
  304. // we've finished the throw, restart.
  305. m_flStartThrow = 0;
  306. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
  307. {
  308. SendWeaponAnim( ACT_VM_DRAW );
  309. }
  310. else
  311. {
  312. // RetireWeapon();
  313. return;
  314. }
  315. SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) );
  316. m_flReleaseThrow = -1;
  317. return;
  318. }
  319. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
  320. {
  321. float flRand = random->RandomFloat( 0, 1 );
  322. if ( flRand <= 0.75 )
  323. {
  324. SendWeaponAnim( ACT_VM_IDLE );
  325. SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) );// how long till we do this again.
  326. }
  327. else
  328. {
  329. SendWeaponAnim( ACT_VM_FIDGET );
  330. }
  331. }
  332. }
  333. bool CWeaponHandGrenade::Deploy( void )
  334. {
  335. m_flReleaseThrow = -1;
  336. return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_DRAW, (char*)GetAnimPrefix() );
  337. }
  338. bool CWeaponHandGrenade::Holster( CBaseCombatWeapon *pSwitchingTo )
  339. {
  340. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  341. if ( !pPlayer )
  342. {
  343. return false;
  344. }
  345. if ( m_flStartThrow > 0 )
  346. {
  347. return false;
  348. }
  349. if ( !BaseClass::Holster( pSwitchingTo ) )
  350. {
  351. return false;
  352. }
  353. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
  354. {
  355. #ifndef CLIENT_DLL
  356. SetThink( &CWeaponHandGrenade::DestroyItem );
  357. SetNextThink( gpGlobals->curtime + 0.1 );
  358. #endif
  359. }
  360. pPlayer->SetNextAttack( gpGlobals->curtime + 0.5 );
  361. return true;
  362. }