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.

657 lines
19 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "basecsgrenade_projectile.h"
  8. extern ConVar sv_gravity;
  9. #ifdef CLIENT_DLL
  10. #include "c_cs_player.h"
  11. #include "hltvcamera.h"
  12. #include "in_buttons.h"
  13. #include <vgui/IInput.h>
  14. #include "vgui_controls/Controls.h"
  15. #else
  16. #include "bot_manager.h"
  17. #include "cs_player.h"
  18. #include "soundent.h"
  19. #include "te_effect_dispatch.h"
  20. #include "keyvalues.h"
  21. #include "cs_gamestats.h"
  22. #include "cs_simple_hostage.h"
  23. #include "Effects/chicken.h"
  24. BEGIN_DATADESC( CBaseCSGrenadeProjectile )
  25. DEFINE_THINKFUNC( DangerSoundThink ),
  26. END_DATADESC()
  27. #define GRENADE_FAILSAFE_MAX_BOUNCES 20
  28. #endif
  29. // NOTE: This has to be the last file included!
  30. #include "tier0/memdbgon.h"
  31. #ifdef CLIENT_DLL
  32. extern ConVar spec_show_xray;
  33. extern ConVar sv_grenade_trajectory;
  34. extern ConVar sv_grenade_trajectory_time_spectator;
  35. extern ConVar sv_grenade_trajectory_thickness;
  36. extern ConVar sv_grenade_trajectory_dash;
  37. #endif
  38. IMPLEMENT_NETWORKCLASS_ALIASED( BaseCSGrenadeProjectile, DT_BaseCSGrenadeProjectile )
  39. BEGIN_NETWORK_TABLE( CBaseCSGrenadeProjectile, DT_BaseCSGrenadeProjectile )
  40. #ifdef CLIENT_DLL
  41. RecvPropVector( RECVINFO( m_vInitialVelocity ) ),
  42. RecvPropInt( RECVINFO( m_nBounces ) )
  43. #else
  44. SendPropVector( SENDINFO( m_vInitialVelocity ),
  45. 20, // nbits
  46. 0, // flags
  47. -3000, // low value
  48. 3000 // high value
  49. ),
  50. SendPropInt( SENDINFO( m_nBounces ) )
  51. #endif
  52. END_NETWORK_TABLE()
  53. #ifdef CLIENT_DLL
  54. //-----------------------------------------------------------------------------
  55. // Purpose:
  56. //-----------------------------------------------------------------------------
  57. CBaseCSGrenadeProjectile::~CBaseCSGrenadeProjectile()
  58. {
  59. flNextTrailLineTime = gpGlobals->curtime;
  60. CreateGrenadeTrail();
  61. }
  62. void CBaseCSGrenadeProjectile::PostDataUpdate( DataUpdateType_t type )
  63. {
  64. BaseClass::PostDataUpdate( type );
  65. //C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
  66. if ( type == DATA_UPDATE_CREATED )
  67. {
  68. SetNextClientThink( gpGlobals->curtime );
  69. vecLastTrailLinePos = GetLocalOrigin();
  70. flNextTrailLineTime = gpGlobals->curtime + 0.1;
  71. // Now stick our initial velocity into the interpolation history
  72. CInterpolatedVar< Vector > &interpolator = GetOriginInterpolator();
  73. interpolator.ClearHistory();
  74. float changeTime = GetLastChangeTime( LATCH_SIMULATION_VAR );
  75. // Add a sample 1 second back.
  76. Vector vCurOrigin = GetLocalOrigin() - m_vInitialVelocity;
  77. interpolator.AddToHead( changeTime - 1.0, &vCurOrigin, false );
  78. // Add the current sample.
  79. vCurOrigin = GetLocalOrigin();
  80. interpolator.AddToHead( changeTime, &vCurOrigin, false );
  81. // IGameEvent * event = gameeventmanager->CreateEvent( "grenade_projectile_created" );
  82. // C_CSPlayer *pPlayer = dynamic_cast<C_CSPlayer*>( GetThrower() );
  83. // if( event && pPlayer )
  84. // {
  85. // event->SetInt( "owneruserid", pPlayer ? pPlayer->GetUserID() : 0 );
  86. // event->SetInt( "grenade", GetGrenadeType() );
  87. // gameeventmanager->FireEvent( event );
  88. // }
  89. //if ( pLocalPlayer )
  90. // pLocalPlayer->NotifyPlayerOfThrownGrenade( this, GetThrower(), GetGrenadeType() );
  91. }
  92. else
  93. {
  94. }
  95. }
  96. void CBaseCSGrenadeProjectile::ClientThink( void )
  97. {
  98. BaseClass::ClientThink();
  99. SetNextClientThink( gpGlobals->curtime );
  100. if ( flNextTrailLineTime <= gpGlobals->curtime )
  101. {
  102. CreateGrenadeTrail();
  103. }
  104. }
  105. void CBaseCSGrenadeProjectile::CreateGrenadeTrail( void )
  106. {
  107. C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
  108. if ( !pLocalPlayer )
  109. return;
  110. if ( flNextTrailLineTime <= gpGlobals->curtime )
  111. {
  112. bool bRenderForSpectator = CanSeeSpectatorOnlyTools() && spec_show_xray.GetInt();
  113. if ( sv_grenade_trajectory_time_spectator.GetFloat() > 0.0f && sv_grenade_trajectory.GetInt() == 0 && pLocalPlayer && ( bRenderForSpectator || ( !pLocalPlayer->IsAlive() && ( pLocalPlayer->GetObserverMode() > OBS_MODE_FREEZECAM ) ) ) )
  114. {
  115. bool bRender = false;
  116. if ( ( GetTeamNumber() == TEAM_CT ) && ( bRenderForSpectator || ( pLocalPlayer->GetTeamNumber() == TEAM_CT ) ) )
  117. bRender = true;
  118. else if ( ( GetTeamNumber() == TEAM_TERRORIST ) && ( bRenderForSpectator || ( pLocalPlayer->GetTeamNumber() == TEAM_TERRORIST ) ) )
  119. bRender = true;
  120. if ( bRender )
  121. {
  122. // Grenade trails for spectators
  123. //CInterpolatedVar< Vector > &interpolator = GetOriginInterpolator();
  124. QAngle angGrTrajAngles;
  125. Vector vec3tempOrientation = ( vecLastTrailLinePos - GetLocalOrigin() );
  126. VectorAngles( vec3tempOrientation, angGrTrajAngles );
  127. float flGrTraThickness = sv_grenade_trajectory_thickness.GetFloat();
  128. Vector vec3_GrTrajMin = Vector( 0, -flGrTraThickness, -flGrTraThickness );
  129. Vector vec3_GrTrajMax = Vector( vec3tempOrientation.Length(), flGrTraThickness, flGrTraThickness );
  130. bool bDotted = ( sv_grenade_trajectory_dash.GetInt() && ( fmod( gpGlobals->curtime, 0.1f ) < 0.05f ) );
  131. Color traceColor;
  132. if ( GetTeamNumber() == TEAM_CT )
  133. {
  134. traceColor[0] = 114;
  135. traceColor[1] = 155;
  136. traceColor[2] = 221;
  137. }
  138. else
  139. {
  140. traceColor[0] = 224;
  141. traceColor[1] = 175;
  142. traceColor[2] = 86;
  143. }
  144. if ( bDotted )
  145. {
  146. traceColor[0] /= 10;
  147. traceColor[1] /= 10;
  148. traceColor[2] /= 10;
  149. }
  150. //Add extruded box shapes to glow pass to build the arc
  151. GlowObjectManager().AddGlowBox( GetLocalOrigin(), angGrTrajAngles, vec3_GrTrajMin, vec3_GrTrajMax, traceColor, sv_grenade_trajectory_time_spectator.GetFloat() );
  152. //Make the grenade projectile itself glow
  153. if ( !m_GlowObject.IsRendering() )
  154. {
  155. m_GlowObject.SetColor( Vector( traceColor[0] / 255.0f, traceColor[1] / 255.0f, traceColor[2] / 255.0f ) );
  156. m_GlowObject.SetAlpha( 0.3f );
  157. m_GlowObject.SetGlowAlphaCappedByRenderAlpha( true );
  158. m_GlowObject.SetGlowAlphaFunctionOfMaxVelocity( 50.0f );
  159. m_GlowObject.SetGlowAlphaMax( 0.3f );
  160. m_GlowObject.SetRenderFlags( true, true );
  161. }
  162. }
  163. }
  164. vecLastTrailLinePos = GetLocalOrigin();
  165. flNextTrailLineTime = gpGlobals->curtime + 0.05;
  166. }
  167. }
  168. int CBaseCSGrenadeProjectile::DrawModel( int flags, const RenderableInstance_t &instance )
  169. {
  170. // During the first half-second of our life, don't draw ourselves if he's
  171. // still playing his throw animation.
  172. // (better yet, we could draw ourselves in his hand).
  173. if ( GetThrower() != C_BasePlayer::GetLocalPlayer() )
  174. {
  175. if ( gpGlobals->curtime - m_flSpawnTime < 0.5 )
  176. {
  177. C_CSPlayer *pPlayer = dynamic_cast<C_CSPlayer*>( GetThrower() );
  178. if ( pPlayer && pPlayer->m_PlayerAnimState->IsThrowingGrenade() )
  179. {
  180. return 0;
  181. }
  182. }
  183. }
  184. return BaseClass::DrawModel( flags, instance );
  185. }
  186. void CBaseCSGrenadeProjectile::Spawn()
  187. {
  188. m_flSpawnTime = gpGlobals->curtime;
  189. BaseClass::Spawn();
  190. }
  191. #else
  192. void CBaseCSGrenadeProjectile::PostConstructor( const char *className )
  193. {
  194. BaseClass::PostConstructor( className );
  195. TheBots->AddGrenade( this );
  196. }
  197. CBaseCSGrenadeProjectile::~CBaseCSGrenadeProjectile()
  198. {
  199. TheBots->RemoveGrenade( this );
  200. }
  201. void CBaseCSGrenadeProjectile::Precache()
  202. {
  203. BaseClass::Precache();
  204. PrecacheEffect( "gunshotsplash" );
  205. }
  206. void CBaseCSGrenadeProjectile::Spawn( void )
  207. {
  208. Precache();
  209. BaseClass::Spawn();
  210. SetSolidFlags( FSOLID_NOT_STANDABLE );
  211. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
  212. SetSolid( SOLID_BBOX ); // So it will collide with physics props!
  213. AddFlag( FL_GRENADE );
  214. m_lastHitPlayer = NULL;
  215. // smaller, cube bounding box so we rest on the ground
  216. Vector min = Vector( -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE );
  217. Vector max = Vector( GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE );
  218. SetSize( min, max );
  219. if ( CollisionProp( ) )
  220. CollisionProp( )->SetCollisionBounds( min, max );
  221. m_nBounces = 0;
  222. }
  223. int CBaseCSGrenadeProjectile::UpdateTransmitState()
  224. {
  225. // always call ShouldTransmit() for grenades
  226. return SetTransmitState( FL_EDICT_FULLCHECK );
  227. }
  228. int CBaseCSGrenadeProjectile::ShouldTransmit( const CCheckTransmitInfo *pInfo )
  229. {
  230. CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt );
  231. if ( pRecipientEntity->IsPlayer() )
  232. {
  233. CBasePlayer *pRecipientPlayer = static_cast<CBasePlayer*>( pRecipientEntity );
  234. // always transmit to the thrower of the grenade
  235. if ( pRecipientPlayer && ( (GetThrower() && pRecipientPlayer == GetThrower()) ||
  236. pRecipientPlayer->GetTeamNumber() == TEAM_SPECTATOR) )
  237. {
  238. return FL_EDICT_ALWAYS;
  239. }
  240. }
  241. return FL_EDICT_PVSCHECK;
  242. }
  243. void CBaseCSGrenadeProjectile::DangerSoundThink( void )
  244. {
  245. if (!IsInWorld())
  246. {
  247. Remove( );
  248. return;
  249. }
  250. if( gpGlobals->curtime > m_flDetonateTime )
  251. {
  252. Detonate();
  253. return;
  254. }
  255. CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, GetAbsVelocity().Length( ), 0.2 );
  256. SetNextThink( gpGlobals->curtime + 0.2 );
  257. if (GetWaterLevel() != WL_NotInWater)
  258. {
  259. SetAbsVelocity( GetAbsVelocity() * 0.5 );
  260. }
  261. }
  262. //Sets the time at which the grenade will explode
  263. void CBaseCSGrenadeProjectile::SetDetonateTimerLength( float timer )
  264. {
  265. m_flDetonateTime = gpGlobals->curtime + timer;
  266. }
  267. unsigned int CBaseCSGrenadeProjectile::PhysicsSolidMaskForEntity( void ) const
  268. {
  269. if ( GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
  270. {
  271. return ((CONTENTS_GRENADECLIP | MASK_SOLID) & ~CONTENTS_MONSTER);
  272. }
  273. else
  274. {
  275. return (CONTENTS_GRENADECLIP|MASK_SOLID|MASK_VISIBLE_AND_NPCS|CONTENTS_HITBOX) & ~(CONTENTS_DEBRIS);
  276. }
  277. }
  278. void CBaseCSGrenadeProjectile::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecMove )
  279. {
  280. const float kSleepVelocity = 20.0f;
  281. const float kSleepVelocitySquared = kSleepVelocity * kSleepVelocity;
  282. // Verify that we have an entity.
  283. CBaseEntity *pEntity = trace.m_pEnt;
  284. Assert( pEntity );
  285. if ( pEntity )
  286. {
  287. CChicken *pChicken = dynamic_cast< CChicken* >( pEntity );
  288. if (pChicken)
  289. {
  290. // hurt the chicken
  291. CTakeDamageInfo info( this, this, 10, DMG_CLUB );
  292. pChicken->DispatchTraceAttack( info, GetAbsVelocity().Normalized(), &trace );
  293. ApplyMultiDamage();
  294. return;
  295. }
  296. }
  297. // if its breakable glass and we kill it, don't bounce.
  298. // give some damage to the glass, and if it breaks, pass
  299. // through it.
  300. bool breakthrough = false;
  301. if( pEntity && FClassnameIs( pEntity, "func_breakable" ) )
  302. {
  303. breakthrough = true;
  304. }
  305. if( pEntity && FClassnameIs( pEntity, "func_breakable_surf" ) )
  306. {
  307. breakthrough = true;
  308. }
  309. if( pEntity && FClassnameIs( pEntity, "prop_physics_multiplayer" ) && pEntity->GetMaxHealth() > 0 && pEntity->m_takedamage == DAMAGE_YES )
  310. {
  311. breakthrough = true;
  312. }
  313. // this one is tricky because BounceTouch hits breakable propers before we hit this function and the damage is already applied there (CBaseGrenade::BounceTouch( CBaseEntity *pOther ))
  314. // by the time we hit this, the prop hasn't been removed yet, but it broke, is set to not take anymore damage and is marked for deletion - we have to cover this case here
  315. if( pEntity && FClassnameIs( pEntity, "prop_dynamic" ) && pEntity->GetMaxHealth() > 0 && (pEntity->m_takedamage == DAMAGE_YES || (pEntity->m_takedamage == DAMAGE_NO && pEntity->IsEFlagSet( EFL_KILLME ))) )
  316. {
  317. breakthrough = true;
  318. }
  319. if ( breakthrough )
  320. {
  321. CTakeDamageInfo info( this, this, 10, DMG_CLUB );
  322. pEntity->DispatchTraceAttack( info, GetAbsVelocity().Normalized(), &trace );
  323. ApplyMultiDamage();
  324. if( pEntity->m_iHealth <= 0 )
  325. {
  326. // slow our flight a little bit
  327. Vector vel = GetAbsVelocity();
  328. vel *= 0.4;
  329. SetAbsVelocity( vel );
  330. return;
  331. }
  332. }
  333. //Assume all surfaces have the same elasticity
  334. float flSurfaceElasticity = 1.0;
  335. //Don't bounce off of players with perfect elasticity
  336. if ( pEntity && pEntity->IsPlayer() )
  337. {
  338. flSurfaceElasticity = 0.3f;
  339. // and do slight damage to players on the opposite team
  340. if ( GetTeamNumber() != pEntity->GetTeamNumber() )
  341. {
  342. CTakeDamageInfo info( this, GetThrower(), 2, DMG_GENERIC );
  343. pEntity->TakeDamage( info );
  344. }
  345. }
  346. //Don't bounce twice on a selection of problematic entities
  347. bool bIsProjectile = dynamic_cast< CBaseCSGrenadeProjectile* >( pEntity ) != NULL;
  348. if ( pEntity && !pEntity->IsWorld() && m_lastHitPlayer.Get() == pEntity )
  349. {
  350. bool bIsHostage = dynamic_cast< CHostage* >( pEntity ) != NULL;
  351. if ( pEntity->IsPlayer() || bIsHostage || bIsProjectile )
  352. {
  353. //DevMsg( "Setting %s to DEBRIS, it is in group %i, it hit %s in group %i\n", this->GetClassname(), this->GetCollisionGroup(), pEntity->GetClassname(), pEntity->GetCollisionGroup() );
  354. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  355. if ( bIsProjectile )
  356. {
  357. //DevMsg( "Setting %s to DEBRIS, it is in group %i.\n", pEntity->GetClassname(), pEntity->GetCollisionGroup() );
  358. pEntity->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  359. }
  360. return;
  361. }
  362. }
  363. if ( pEntity )
  364. {
  365. m_lastHitPlayer = pEntity;
  366. }
  367. float flTotalElasticity = GetElasticity() * flSurfaceElasticity;
  368. flTotalElasticity = clamp( flTotalElasticity, 0.0f, 0.9f );
  369. // NOTE: A backoff of 2.0f is a reflection
  370. Vector vecAbsVelocity;
  371. PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, 2.0f );
  372. vecAbsVelocity *= flTotalElasticity;
  373. // Get the total velocity (player + conveyors, etc.)
  374. VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecMove );
  375. float flSpeedSqr = DotProduct( vecMove, vecMove );
  376. bool bIsWeapon = dynamic_cast< CBaseCombatWeapon* >( pEntity ) != NULL;
  377. // Stop if on ground or if we bounce and our velocity is really low (keeps it from bouncing infinitely)
  378. if ( pEntity &&
  379. ( ( trace.plane.normal.z > 0.7f ) || (trace.plane.normal.z > 0.1f && flSpeedSqr < kSleepVelocitySquared) ) &&
  380. ( pEntity->IsStandable() || bIsProjectile || bIsWeapon || pEntity->IsWorld() )
  381. )
  382. {
  383. // clip it again to emulate old behavior and keep it from bouncing up like crazy when you throw it at the ground on the first toss
  384. if ( flSpeedSqr > 96000 )
  385. {
  386. float alongDist = DotProduct( vecAbsVelocity.Normalized(), trace.plane.normal );
  387. if ( alongDist > 0.5f )
  388. {
  389. float flBouncePadding = (1.0f - alongDist) + 0.5f;
  390. vecAbsVelocity *= flBouncePadding;
  391. }
  392. }
  393. SetAbsVelocity( vecAbsVelocity );
  394. if ( flSpeedSqr < kSleepVelocitySquared )
  395. {
  396. SetGroundEntity( pEntity );
  397. // Reset velocities.
  398. SetAbsVelocity( vec3_origin );
  399. SetLocalAngularVelocity( vec3_angle );
  400. //align to the ground so we're not standing on end
  401. QAngle angle;
  402. VectorAngles( trace.plane.normal, angle );
  403. // rotate randomly in yaw
  404. angle[1] = random->RandomFloat( 0, 360 );
  405. // TODO: rotate around trace.plane.normal
  406. SetAbsAngles( angle );
  407. }
  408. else
  409. {
  410. Vector vecBaseDir = GetBaseVelocity();
  411. if ( !vecBaseDir.IsZero() )
  412. {
  413. VectorNormalize( vecBaseDir );
  414. Vector vecDelta = GetBaseVelocity() - vecAbsVelocity;
  415. float flScale = vecDelta.Dot( vecBaseDir );
  416. vecAbsVelocity += GetBaseVelocity() * flScale;
  417. }
  418. VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecMove );
  419. PhysicsPushEntity( vecMove, &trace );
  420. }
  421. }
  422. else
  423. {
  424. SetAbsVelocity( vecAbsVelocity );
  425. VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecMove );
  426. PhysicsPushEntity( vecMove, &trace );
  427. }
  428. BounceSound();
  429. // tell the bots a grenade has bounced
  430. CCSPlayer *player = ToCSPlayer(GetThrower());
  431. if ( player )
  432. {
  433. IGameEvent * event = gameeventmanager->CreateEvent( "grenade_bounce" );
  434. if ( event )
  435. {
  436. event->SetInt( "userid", player->GetUserID() );
  437. event->SetFloat( "x", GetAbsOrigin().x );
  438. event->SetFloat( "y", GetAbsOrigin().y );
  439. event->SetFloat( "z", GetAbsOrigin().z );
  440. gameeventmanager->FireEvent( event );
  441. }
  442. }
  443. OnBounced();
  444. if (m_nBounces > GRENADE_FAILSAFE_MAX_BOUNCES )
  445. {
  446. //failsafe detonate after 20 bounces
  447. SetAbsVelocity( vec3_origin );
  448. DetonateOnNextThink();
  449. SetNextThink( gpGlobals->curtime );
  450. SetMoveType( MOVETYPE_NONE );
  451. }
  452. else
  453. {
  454. m_nBounces++;
  455. }
  456. }
  457. void CBaseCSGrenadeProjectile::SetupInitialTransmittedGrenadeVelocity( const Vector &velocity )
  458. {
  459. m_vInitialVelocity = velocity;
  460. }
  461. #define MAX_WATER_SURFACE_DISTANCE 512
  462. void CBaseCSGrenadeProjectile::Splash()
  463. {
  464. Vector centerPoint = GetAbsOrigin();
  465. Vector normal( 0, 0, 1 );
  466. // Find our water surface by tracing up till we're out of the water
  467. trace_t tr;
  468. Vector vecTrace( 0, 0, MAX_WATER_SURFACE_DISTANCE );
  469. UTIL_TraceLine( centerPoint, centerPoint + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
  470. // If we didn't start in water, we're above it
  471. if ( tr.startsolid == false )
  472. {
  473. // Look downward to find the surface
  474. vecTrace.Init( 0, 0, -MAX_WATER_SURFACE_DISTANCE );
  475. UTIL_TraceLine( centerPoint, centerPoint + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
  476. // If we hit it, setup the explosion
  477. if ( tr.fraction < 1.0f )
  478. {
  479. centerPoint = tr.endpos;
  480. }
  481. else
  482. {
  483. //NOTENOTE: We somehow got into a splash without being near water?
  484. Assert( 0 );
  485. }
  486. }
  487. else if ( tr.fractionleftsolid )
  488. {
  489. // Otherwise we came out of the water at this point
  490. centerPoint = centerPoint + (vecTrace * tr.fractionleftsolid);
  491. }
  492. else
  493. {
  494. // Use default values, we're really deep
  495. }
  496. CEffectData data;
  497. data.m_vOrigin = centerPoint;
  498. data.m_vNormal = normal;
  499. data.m_flScale = random->RandomFloat( 1.0f, 2.0f );
  500. if ( GetWaterType() & CONTENTS_SLIME )
  501. {
  502. data.m_fFlags |= FX_WATER_IN_SLIME;
  503. }
  504. DispatchEffect( "gunshotsplash", data );
  505. }
  506. // Add a row to ogs for the explosion of this grenade. Damage instances are recorded separately.
  507. void CBaseCSGrenadeProjectile::RecordDetonation( void )
  508. {
  509. // I hate having to call this from so many places since half the grenades override the standard detonate and the rest dont...
  510. // If this triggers, it's because either somebody changed the way some grenade code chains to base methods or it's taking an uncommon code path
  511. // that needs to be investigated.
  512. if ( m_bDetonationRecorded )
  513. {
  514. Warning( "Detonation of grenade '%s' attempted to record twice!\n", GetDebugName() );
  515. Assert( 0 );
  516. return;
  517. }
  518. CCSPlayer::StartNewBulletGroup();
  519. SWeaponHitData *pHitData = new SWeaponHitData;
  520. if ( pHitData->InitAsGrenadeDetonation( this, CCSPlayer::GetBulletGroup() ) )
  521. {
  522. CCS_GameStats.RecordWeaponHit( pHitData ); // submission deletes the struct.
  523. m_bDetonationRecorded = true;
  524. }
  525. else
  526. {
  527. delete pHitData;
  528. }
  529. }
  530. void CBaseCSGrenadeProjectile::Explode( trace_t *pTrace, int bitsDamageType )
  531. {
  532. RecordDetonation();
  533. BaseClass::Explode( pTrace, bitsDamageType );
  534. }
  535. #endif // !CLIENT_DLL