Counter Strike : Global Offensive Source Code
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.

605 lines
17 KiB

  1. //===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "decals.h"
  9. #include "basegrenade_shared.h"
  10. #include "shake.h"
  11. #include "engine/IEngineSound.h"
  12. #include "particle_parse.h"
  13. #if !defined( CLIENT_DLL )
  14. #include "soundent.h"
  15. #include "entitylist.h"
  16. #include "GameStats.h"
  17. #endif
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. extern int g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the fireball
  21. extern int g_sModelIndexWExplosion; // (in combatweapon.cpp) holds the index for the underwater explosion
  22. extern int g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud
  23. extern ConVar sk_plr_dmg_grenade;
  24. #if !defined( CLIENT_DLL )
  25. // Global Savedata for friction modifier
  26. BEGIN_DATADESC( CBaseGrenade )
  27. // nextGrenade
  28. DEFINE_FIELD( m_hThrower, FIELD_EHANDLE ),
  29. // m_fRegisteredSound ???
  30. DEFINE_FIELD( m_bIsLive, FIELD_BOOLEAN ),
  31. DEFINE_FIELD( m_DmgRadius, FIELD_FLOAT ),
  32. DEFINE_FIELD( m_flDetonateTime, FIELD_TIME ),
  33. DEFINE_FIELD( m_flWarnAITime, FIELD_TIME ),
  34. DEFINE_FIELD( m_flDamage, FIELD_FLOAT ),
  35. DEFINE_FIELD( m_iszBounceSound, FIELD_STRING ),
  36. DEFINE_FIELD( m_bHasWarnedAI, FIELD_BOOLEAN ),
  37. // Function Pointers
  38. DEFINE_THINKFUNC( Smoke ),
  39. DEFINE_ENTITYFUNC( BounceTouch ),
  40. DEFINE_ENTITYFUNC( SlideTouch ),
  41. DEFINE_ENTITYFUNC( ExplodeTouch ),
  42. DEFINE_USEFUNC( DetonateUse ),
  43. DEFINE_THINKFUNC( DangerSoundThink ),
  44. DEFINE_THINKFUNC( PreDetonate ),
  45. DEFINE_THINKFUNC( Detonate ),
  46. DEFINE_THINKFUNC( TumbleThink ),
  47. END_DATADESC()
  48. void SendProxy_CropFlagsToPlayerFlagBitsLength( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID);
  49. #endif
  50. IMPLEMENT_NETWORKCLASS_ALIASED( BaseGrenade, DT_BaseGrenade )
  51. BEGIN_NETWORK_TABLE( CBaseGrenade, DT_BaseGrenade )
  52. #if !defined( CLIENT_DLL )
  53. SendPropFloat( SENDINFO( m_flDamage ), 10, SPROP_ROUNDDOWN, 0.0, 256.0f ),
  54. SendPropFloat( SENDINFO( m_DmgRadius ), 10, SPROP_ROUNDDOWN, 0.0, 1024.0f ),
  55. SendPropInt( SENDINFO( m_bIsLive ), 1, SPROP_UNSIGNED ),
  56. // SendPropTime( SENDINFO( m_flDetonateTime ) ),
  57. SendPropEHandle( SENDINFO( m_hThrower ) ),
  58. SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
  59. SendPropVector( SENDINFO( m_vecVelocity ), 0, SPROP_NOSCALE ),
  60. // HACK: Use same flag bits as player for now
  61. SendPropInt ( SENDINFO(m_fFlags), PLAYER_FLAG_BITS, SPROP_UNSIGNED, SendProxy_CropFlagsToPlayerFlagBitsLength ),
  62. #else
  63. RecvPropFloat( RECVINFO( m_flDamage ) ),
  64. RecvPropFloat( RECVINFO( m_DmgRadius ) ),
  65. RecvPropInt( RECVINFO( m_bIsLive ) ),
  66. // RecvPropTime( RECVINFO( m_flDetonateTime ) ),
  67. RecvPropEHandle( RECVINFO( m_hThrower ) ),
  68. // Need velocity from grenades to make animation system work correctly when running
  69. RecvPropVector( RECVINFO(m_vecVelocity), 0, RecvProxy_LocalVelocity ),
  70. RecvPropInt( RECVINFO( m_fFlags ) ),
  71. #endif
  72. END_NETWORK_TABLE()
  73. LINK_ENTITY_TO_CLASS_ALIASED( grenade, BaseGrenade );
  74. #if defined( CLIENT_DLL )
  75. BEGIN_PREDICTION_DATA( CBaseGrenade )
  76. DEFINE_PRED_FIELD( m_hThrower, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
  77. DEFINE_PRED_FIELD( m_bIsLive, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  78. DEFINE_PRED_FIELD( m_DmgRadius, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  79. // DEFINE_PRED_FIELD_TOL( m_flDetonateTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
  80. DEFINE_PRED_FIELD( m_flDamage, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  81. DEFINE_PRED_FIELD_TOL( m_vecVelocity, FIELD_VECTOR, FTYPEDESC_INSENDTABLE, 0.5f ),
  82. DEFINE_PRED_FIELD_TOL( m_flNextAttack, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
  83. // DEFINE_FIELD( m_fRegisteredSound, FIELD_BOOLEAN ),
  84. // DEFINE_FIELD( m_iszBounceSound, FIELD_STRING ),
  85. END_PREDICTION_DATA()
  86. #endif
  87. // Grenades flagged with this will be triggered when the owner calls detonateSatchelCharges
  88. #define SF_DETONATE 0x0001
  89. #define MAX_WATER_SURFACE_DISTANCE 512
  90. // UNDONE: temporary scorching for PreAlpha - find a less sleazy permenant solution.
  91. void CBaseGrenade::Explode( trace_t *pTrace, int bitsDamageType )
  92. {
  93. #if !defined( CLIENT_DLL )
  94. SetModelName( NULL_STRING );//invisible
  95. AddSolidFlags( FSOLID_NOT_SOLID );
  96. m_takedamage = DAMAGE_NO;
  97. // Pull out of the wall a bit
  98. if ( pTrace->fraction != 1.0 )
  99. {
  100. SetAbsOrigin( pTrace->endpos + (pTrace->plane.normal * 0.6) );
  101. }
  102. Vector vecAbsOrigin = GetAbsOrigin();
  103. int contents = UTIL_PointContents ( vecAbsOrigin, MASK_ALL );
  104. #if defined( TF_DLL )
  105. // Since this code only runs on the server, make sure it shows the tempents it creates.
  106. // This solves a problem with remote detonating the pipebombs (client wasn't seeing the explosion effect)
  107. CDisablePredictionFiltering disabler;
  108. #endif
  109. // Try using the new particle system instead of temp ents
  110. surfacedata_t *pSurfaceData = physprops->GetSurfaceData( pTrace->surface.surfaceProps );
  111. const char *pEffectName = GetParticleSystemName( contents, pSurfaceData );
  112. if ( pEffectName != NULL )
  113. {
  114. Vector vecParticleOrigin = vecAbsOrigin;
  115. if ( contents & MASK_WATER )
  116. {
  117. // Find our water surface by tracing up till we're out of the water
  118. trace_t tr;
  119. Vector vecTrace( 0, 0, MAX_WATER_SURFACE_DISTANCE );
  120. UTIL_TraceLine( vecParticleOrigin, vecParticleOrigin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
  121. // If we didn't start in water, we're above it
  122. if ( tr.startsolid == false )
  123. {
  124. // Look downward to find the surface
  125. vecTrace.Init( 0, 0, -MAX_WATER_SURFACE_DISTANCE );
  126. UTIL_TraceLine( vecParticleOrigin, vecParticleOrigin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
  127. // If we hit it, setup the explosion
  128. if ( tr.fraction < 1.0f )
  129. {
  130. vecParticleOrigin = tr.endpos;
  131. }
  132. }
  133. else if ( tr.fractionleftsolid )
  134. {
  135. // Otherwise we came out of the water at this point
  136. vecParticleOrigin = vecParticleOrigin + (vecTrace * tr.fractionleftsolid);
  137. }
  138. }
  139. QAngle vecAngles;
  140. DispatchParticleEffect( pEffectName, vecParticleOrigin, vecAngles );
  141. }
  142. else
  143. {
  144. if ( pTrace->fraction != 1.0 )
  145. {
  146. Vector vecNormal = pTrace->plane.normal;
  147. surfacedata_t *pdata = physprops->GetSurfaceData( pTrace->surface.surfaceProps );
  148. CPASFilter filter( vecAbsOrigin );
  149. te->Explosion( filter, -1.0, // don't apply cl_interp delay
  150. vecAbsOrigin,
  151. !( contents & MASK_WATER ) ? g_sModelIndexFireball : g_sModelIndexWExplosion,
  152. m_DmgRadius * .03,
  153. 25,
  154. TE_EXPLFLAG_NONE,
  155. m_DmgRadius,
  156. m_flDamage,
  157. &vecNormal,
  158. (char) pdata->game.material );
  159. }
  160. else
  161. {
  162. CPASFilter filter( vecAbsOrigin );
  163. te->Explosion( filter, -1.0, // don't apply cl_interp delay
  164. vecAbsOrigin,
  165. !( contents & MASK_WATER ) ? g_sModelIndexFireball : g_sModelIndexWExplosion,
  166. m_DmgRadius * .03,
  167. 25,
  168. TE_EXPLFLAG_NONE,
  169. m_DmgRadius,
  170. m_flDamage );
  171. }
  172. }
  173. #if !defined( CLIENT_DLL )
  174. CSoundEnt::InsertSound ( SOUND_COMBAT, GetAbsOrigin(), BASEGRENADE_EXPLOSION_VOLUME, 3.0 );
  175. #endif
  176. // Use the thrower's position as the reported position
  177. Vector vecReported = m_hThrower ? m_hThrower->GetAbsOrigin() : vec3_origin;
  178. EmitSound( "BaseGrenade.Explode" );
  179. CTakeDamageInfo info( this, m_hThrower, GetBlastForce(), GetAbsOrigin(), m_flDamage, bitsDamageType, 0, &vecReported );
  180. RadiusDamage( info, GetAbsOrigin(), m_DmgRadius, CLASS_NONE, NULL );
  181. UTIL_DecalTrace( pTrace, "Scorch" );
  182. SetThink( &CBaseGrenade::SUB_Remove );
  183. SetTouch( NULL );
  184. SetSolid( SOLID_NONE );
  185. AddEffects( EF_NODRAW );
  186. SetAbsVelocity( vec3_origin );
  187. #if HL2_EPISODIC
  188. // Because the grenade is zipped out of the world instantly, the EXPLOSION sound that it makes for
  189. // the AI is also immediately destroyed. For this reason, we now make the grenade entity inert and
  190. // throw it away in 1/10th of a second instead of right away. Removing the grenade instantly causes
  191. // intermittent bugs with env_microphones who are listening for explosions. They will 'randomly' not
  192. // hear explosion sounds when the grenade is removed and the SoundEnt thinks (and removes the sound)
  193. // before the env_microphone thinks and hears the sound.
  194. SetNextThink( gpGlobals->curtime + 0.1 );
  195. #else
  196. SetNextThink( gpGlobals->curtime );
  197. #endif//HL2_EPISODIC
  198. #if defined( HL2_DLL )
  199. CBasePlayer *pPlayer = ToBasePlayer( m_hThrower.Get() );
  200. if ( pPlayer )
  201. {
  202. gamestats->Event_WeaponHit( pPlayer, true, "weapon_frag", info );
  203. }
  204. #endif
  205. #endif
  206. }
  207. void CBaseGrenade::Smoke( void )
  208. {
  209. Vector vecAbsOrigin = GetAbsOrigin();
  210. if ( UTIL_PointContents ( vecAbsOrigin, MASK_WATER ) & MASK_WATER )
  211. {
  212. UTIL_Bubbles( vecAbsOrigin - Vector( 64, 64, 64 ), vecAbsOrigin + Vector( 64, 64, 64 ), 100 );
  213. }
  214. else
  215. {
  216. CPVSFilter filter( vecAbsOrigin );
  217. te->Smoke( filter, 0.0,
  218. &vecAbsOrigin, g_sModelIndexSmoke,
  219. m_DmgRadius * 0.03,
  220. 24 );
  221. }
  222. #if !defined( CLIENT_DLL )
  223. SetThink ( &CBaseGrenade::SUB_Remove );
  224. #endif
  225. SetNextThink( gpGlobals->curtime );
  226. }
  227. void CBaseGrenade::Event_Killed( const CTakeDamageInfo &info )
  228. {
  229. Detonate( );
  230. }
  231. #if !defined( CLIENT_DLL )
  232. //-----------------------------------------------------------------------------
  233. // Purpose:
  234. //-----------------------------------------------------------------------------
  235. void CBaseGrenade::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  236. {
  237. // Support player pickup
  238. if ( useType == USE_TOGGLE )
  239. {
  240. CBasePlayer *pPlayer = ToBasePlayer( pActivator );
  241. if ( pPlayer )
  242. {
  243. pPlayer->PickupObject( this );
  244. return;
  245. }
  246. }
  247. // Pass up so we still call any custom Use function
  248. BaseClass::Use( pActivator, pCaller, useType, value );
  249. }
  250. #endif
  251. //-----------------------------------------------------------------------------
  252. // Purpose: Timed grenade, this think is called when time runs out.
  253. //-----------------------------------------------------------------------------
  254. void CBaseGrenade::DetonateUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  255. {
  256. SetThink( &CBaseGrenade::Detonate );
  257. SetNextThink( gpGlobals->curtime );
  258. }
  259. void CBaseGrenade::PreDetonate( void )
  260. {
  261. #if !defined( CLIENT_DLL )
  262. CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), 400, 1.5, this );
  263. #endif
  264. SetThink( &CBaseGrenade::Detonate );
  265. SetNextThink( gpGlobals->curtime + 1.5 );
  266. }
  267. void CBaseGrenade::Detonate( void )
  268. {
  269. trace_t tr;
  270. Vector vecSpot;// trace starts here!
  271. SetThink( NULL );
  272. vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 8 );
  273. UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -32 ), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, & tr);
  274. if( tr.startsolid )
  275. {
  276. // Since we blindly moved the explosion origin vertically, we may have inadvertently moved the explosion into a solid,
  277. // in which case nothing is going to be harmed by the grenade's explosion because all subsequent traces will startsolid.
  278. // If this is the case, we do the downward trace again from the actual origin of the grenade. (sjb) 3/8/2007 (for ep2_outland_09)
  279. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -32), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, &tr );
  280. }
  281. Explode( &tr, DMG_BLAST );
  282. if ( GetShakeAmplitude() )
  283. {
  284. UTIL_ScreenShake( GetAbsOrigin(), GetShakeAmplitude(), 150.0, 1.0, GetShakeRadius(), SHAKE_START );
  285. }
  286. }
  287. //
  288. // Contact grenade, explode when it touches something
  289. //
  290. void CBaseGrenade::ExplodeTouch( CBaseEntity *pOther )
  291. {
  292. trace_t tr;
  293. Vector vecSpot;// trace starts here!
  294. Assert( pOther );
  295. if ( !pOther->IsSolid() )
  296. return;
  297. Vector velDir = GetAbsVelocity();
  298. VectorNormalize( velDir );
  299. vecSpot = GetAbsOrigin() - velDir * 32;
  300. UTIL_TraceLine( vecSpot, vecSpot + velDir * 64, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  301. Explode( &tr, DMG_BLAST );
  302. }
  303. void CBaseGrenade::DangerSoundThink( void )
  304. {
  305. if (!IsInWorld())
  306. {
  307. Remove( );
  308. return;
  309. }
  310. #if !defined( CLIENT_DLL )
  311. CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, GetAbsVelocity().Length( ), 0.2, this );
  312. #endif
  313. SetNextThink( gpGlobals->curtime + 0.2 );
  314. if (GetWaterLevel() != WL_NotInWater)
  315. {
  316. SetAbsVelocity( GetAbsVelocity() * 0.5 );
  317. }
  318. }
  319. void CBaseGrenade::BounceTouch( CBaseEntity *pOther )
  320. {
  321. if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS) )
  322. return;
  323. // don't hit the guy that launched this grenade
  324. if ( pOther == GetThrower() )
  325. return;
  326. // only do damage if we're moving fairly fast
  327. if ( (pOther->m_takedamage != DAMAGE_NO) && (m_flNextAttack < gpGlobals->curtime && GetAbsVelocity().Length() > 100))
  328. {
  329. if (m_hThrower)
  330. {
  331. #if !defined( CLIENT_DLL )
  332. trace_t tr;
  333. tr = CBaseEntity::GetTouchTrace( );
  334. ClearMultiDamage( );
  335. Vector forward;
  336. AngleVectors( GetLocalAngles(), &forward, NULL, NULL );
  337. CTakeDamageInfo info( this, m_hThrower, 1, DMG_CLUB );
  338. CalculateMeleeDamageForce( &info, GetAbsVelocity(), GetAbsOrigin() );
  339. pOther->DispatchTraceAttack( info, forward, &tr );
  340. ApplyMultiDamage();
  341. #endif
  342. }
  343. m_flNextAttack = gpGlobals->curtime + 1.0; // debounce
  344. }
  345. Vector vecTestVelocity;
  346. // m_vecAngVelocity = Vector (300, 300, 300);
  347. // this is my heuristic for modulating the grenade velocity because grenades dropped purely vertical
  348. // or thrown very far tend to slow down too quickly for me to always catch just by testing velocity.
  349. // trimming the Z velocity a bit seems to help quite a bit.
  350. vecTestVelocity = GetAbsVelocity();
  351. vecTestVelocity.z *= 0.45;
  352. if ( !m_bHasWarnedAI && vecTestVelocity.Length() <= 60 )
  353. {
  354. // grenade is moving really slow. It's probably very close to where it will ultimately stop moving.
  355. // emit the danger sound.
  356. // register a radius louder than the explosion, so we make sure everyone gets out of the way
  357. #if !defined( CLIENT_DLL )
  358. CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), m_flDamage / 0.4, 0.3, this );
  359. #endif
  360. m_bHasWarnedAI = true;
  361. }
  362. if (GetFlags() & FL_ONGROUND)
  363. {
  364. // add a bit of static friction
  365. // SetAbsVelocity( GetAbsVelocity() * 0.8 );
  366. // SetSequence( random->RandomInt( 1, 1 ) ); // FIXME: missing tumble animations
  367. }
  368. else
  369. {
  370. // play bounce sound
  371. BounceSound();
  372. }
  373. m_flPlaybackRate = GetAbsVelocity().Length() / 200.0;
  374. if (GetPlaybackRate() > 1.0)
  375. m_flPlaybackRate = 1;
  376. else if (GetPlaybackRate() < 0.5)
  377. m_flPlaybackRate = 0;
  378. }
  379. void CBaseGrenade::SlideTouch( CBaseEntity *pOther )
  380. {
  381. // don't hit the guy that launched this grenade
  382. if ( pOther == GetThrower() )
  383. return;
  384. // m_vecAngVelocity = Vector (300, 300, 300);
  385. if (GetFlags() & FL_ONGROUND)
  386. {
  387. // add a bit of static friction
  388. // SetAbsVelocity( GetAbsVelocity() * 0.95 );
  389. if (GetAbsVelocity().x != 0 || GetAbsVelocity().y != 0)
  390. {
  391. // maintain sliding sound
  392. }
  393. }
  394. else
  395. {
  396. BounceSound();
  397. }
  398. }
  399. void CBaseGrenade ::BounceSound( void )
  400. {
  401. // Doesn't need to do anything anymore! Physics makes the sound.
  402. }
  403. void CBaseGrenade ::TumbleThink( void )
  404. {
  405. if (!IsInWorld())
  406. {
  407. Remove( );
  408. return;
  409. }
  410. StudioFrameAdvance( );
  411. SetNextThink( gpGlobals->curtime + 0.1f );
  412. //
  413. // Emit a danger sound one second before exploding.
  414. //
  415. if (m_flDetonateTime - 1 < gpGlobals->curtime)
  416. {
  417. #if !defined( CLIENT_DLL )
  418. CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * (m_flDetonateTime - gpGlobals->curtime), 400, 0.1, this );
  419. #endif
  420. }
  421. if (m_flDetonateTime <= gpGlobals->curtime)
  422. {
  423. SetThink( &CBaseGrenade::Detonate );
  424. }
  425. if (GetWaterLevel() != WL_NotInWater)
  426. {
  427. SetAbsVelocity( GetAbsVelocity() * 0.5 );
  428. m_flPlaybackRate = 0.2;
  429. }
  430. }
  431. void CBaseGrenade::Precache( void )
  432. {
  433. BaseClass::Precache( );
  434. PrecacheScriptSound( "BaseGrenade.Explode" );
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose:
  438. // Output : CBaseCombatCharacter
  439. //-----------------------------------------------------------------------------
  440. CBaseCombatCharacter *CBaseGrenade::GetThrower( void )
  441. {
  442. CBaseCombatCharacter *pResult = ToBaseCombatCharacter( m_hThrower );
  443. if ( !pResult && GetOwnerEntity() != NULL )
  444. {
  445. pResult = ToBaseCombatCharacter( GetOwnerEntity() );
  446. }
  447. return pResult;
  448. }
  449. //-----------------------------------------------------------------------------
  450. void CBaseGrenade::SetThrower( CBaseCombatCharacter *pThrower )
  451. {
  452. m_hThrower = pThrower;
  453. // if this is the first thrower, set it as the original thrower
  454. if ( NULL == m_hOriginalThrower )
  455. {
  456. m_hOriginalThrower = pThrower;
  457. }
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Purpose: Destructor
  461. // Input :
  462. // Output :
  463. //-----------------------------------------------------------------------------
  464. CBaseGrenade::~CBaseGrenade(void)
  465. {
  466. };
  467. //-----------------------------------------------------------------------------
  468. // Purpose: Constructor
  469. // Input :
  470. // Output :
  471. //-----------------------------------------------------------------------------
  472. CBaseGrenade::CBaseGrenade(void)
  473. #ifdef CLIENT_DLL
  474. :m_GlowObject( this, Vector( 1.0f, 1.0f, 1.0f ), 0.0f, false, false )
  475. #endif
  476. {
  477. m_hThrower = NULL;
  478. m_hOriginalThrower = NULL;
  479. m_bIsLive = false;
  480. m_DmgRadius = 100;
  481. m_flDetonateTime = 0;
  482. m_bHasWarnedAI = false;
  483. SetSimulatedEveryTick( true );
  484. };