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.

503 lines
13 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements an explosion entity and a support spark shower entity.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "decals.h"
  9. #include "explode.h"
  10. #include "ai_basenpc.h"
  11. #include "IEffects.h"
  12. #include "vstdlib/random.h"
  13. #include "tier1/strtools.h"
  14. #include "shareddefs.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. //-----------------------------------------------------------------------------
  18. // Purpose: Spark shower, created by the explosion entity.
  19. //-----------------------------------------------------------------------------
  20. class CShower : public CPointEntity
  21. {
  22. public:
  23. DECLARE_CLASS( CShower, CPointEntity );
  24. void Spawn( void );
  25. void Think( void );
  26. void Touch( CBaseEntity *pOther );
  27. int ObjectCaps( void ) { return FCAP_DONT_SAVE; }
  28. };
  29. LINK_ENTITY_TO_CLASS( spark_shower, CShower );
  30. void CShower::Spawn( void )
  31. {
  32. Vector vecForward;
  33. AngleVectors( GetLocalAngles(), &vecForward );
  34. Vector vecNewVelocity;
  35. vecNewVelocity = random->RandomFloat( 200, 300 ) * vecForward;
  36. vecNewVelocity.x += random->RandomFloat(-100.f,100.f);
  37. vecNewVelocity.y += random->RandomFloat(-100.f,100.f);
  38. if ( vecNewVelocity.z >= 0 )
  39. vecNewVelocity.z += 200;
  40. else
  41. vecNewVelocity.z -= 200;
  42. SetAbsVelocity( vecNewVelocity );
  43. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
  44. SetGravity( UTIL_ScaleForGravity( 400 ) ); // fall a bit more slowly than normal
  45. SetNextThink( gpGlobals->curtime + 0.1f );
  46. SetSolid( SOLID_NONE );
  47. UTIL_SetSize(this, vec3_origin, vec3_origin );
  48. AddEffects( EF_NODRAW );
  49. m_flSpeed = random->RandomFloat( 0.5, 1.5 );
  50. SetLocalAngles( vec3_angle );
  51. }
  52. void CShower::Think( void )
  53. {
  54. g_pEffects->Sparks( GetAbsOrigin() );
  55. m_flSpeed -= 0.1;
  56. if ( m_flSpeed > 0 )
  57. SetNextThink( gpGlobals->curtime + 0.1f );
  58. else
  59. UTIL_Remove( this );
  60. SetGroundEntity( NULL );
  61. }
  62. void CShower::Touch( CBaseEntity *pOther )
  63. {
  64. Vector vecNewVelocity = GetAbsVelocity();
  65. if ( GetFlags() & FL_ONGROUND )
  66. vecNewVelocity *= 0.1;
  67. else
  68. vecNewVelocity *= 0.6;
  69. if ( (vecNewVelocity.x*vecNewVelocity.x+vecNewVelocity.y*vecNewVelocity.y) < 10.0 )
  70. m_flSpeed = 0;
  71. SetAbsVelocity( vecNewVelocity );
  72. }
  73. class CEnvExplosion : public CPointEntity
  74. {
  75. public:
  76. DECLARE_CLASS( CEnvExplosion, CPointEntity );
  77. CEnvExplosion( void )
  78. {
  79. // Default to invalid.
  80. m_sFireballSprite = -1;
  81. };
  82. void Precache( void );
  83. void Spawn( );
  84. void Smoke ( void );
  85. void SetCustomDamageType( int iType ) { m_iCustomDamageType = iType; }
  86. bool KeyValue( const char *szKeyName, const char *szValue );
  87. int DrawDebugTextOverlays(void);
  88. // Input handlers
  89. void InputExplode( inputdata_t &inputdata );
  90. DECLARE_DATADESC();
  91. int m_iMagnitude;// how large is the fireball? how much damage?
  92. int m_iRadiusOverride;// For use when m_iMagnitude results in larger radius than designer desires.
  93. int m_spriteScale; // what's the exact fireball sprite scale?
  94. float m_flDamageForce; // How much damage force should we use?
  95. string_t m_iszFireballSprite;
  96. short m_sFireballSprite;
  97. EHANDLE m_hInflictor;
  98. int m_iCustomDamageType;
  99. // passed along to the RadiusDamage call
  100. int m_iClassIgnore;
  101. EHANDLE m_hEntityIgnore;
  102. };
  103. LINK_ENTITY_TO_CLASS( env_explosion, CEnvExplosion );
  104. BEGIN_DATADESC( CEnvExplosion )
  105. DEFINE_KEYFIELD( m_iMagnitude, FIELD_INTEGER, "iMagnitude" ),
  106. DEFINE_KEYFIELD( m_iRadiusOverride, FIELD_INTEGER, "iRadiusOverride" ),
  107. DEFINE_FIELD( m_spriteScale, FIELD_INTEGER ),
  108. DEFINE_KEYFIELD( m_flDamageForce, FIELD_FLOAT, "DamageForce" ),
  109. DEFINE_FIELD( m_iszFireballSprite, FIELD_STRING ),
  110. DEFINE_FIELD( m_sFireballSprite, FIELD_SHORT ),
  111. DEFINE_FIELD( m_hInflictor, FIELD_EHANDLE ),
  112. DEFINE_FIELD( m_iCustomDamageType, FIELD_INTEGER ),
  113. DEFINE_KEYFIELD( m_iClassIgnore, FIELD_INTEGER, "ignoredClass" ),
  114. DEFINE_KEYFIELD( m_hEntityIgnore, FIELD_EHANDLE, "ignoredEntity" ),
  115. // Function Pointers
  116. DEFINE_THINKFUNC( Smoke ),
  117. // Inputs
  118. DEFINE_INPUTFUNC(FIELD_VOID, "Explode", InputExplode),
  119. END_DATADESC()
  120. bool CEnvExplosion::KeyValue( const char *szKeyName, const char *szValue )
  121. {
  122. if (FStrEq(szKeyName, "fireballsprite"))
  123. {
  124. m_iszFireballSprite = AllocPooledString( szValue );
  125. }
  126. else
  127. {
  128. return BaseClass::KeyValue( szKeyName, szValue );
  129. }
  130. return true;
  131. }
  132. void CEnvExplosion::Precache( void )
  133. {
  134. if ( m_iszFireballSprite != NULL_STRING )
  135. {
  136. m_sFireballSprite = PrecacheModel( STRING( m_iszFireballSprite ) );
  137. }
  138. //PrecacheParticleSystem( "freeze_explosion" ) ;
  139. PrecacheScriptSound( "explode_3" );
  140. }
  141. void CEnvExplosion::Spawn( void )
  142. {
  143. Precache();
  144. SetSolid( SOLID_NONE );
  145. AddEffects( EF_NODRAW );
  146. SetMoveType( MOVETYPE_NONE );
  147. /*
  148. if ( m_iMagnitude > 250 )
  149. {
  150. m_iMagnitude = 250;
  151. }
  152. */
  153. float flSpriteScale;
  154. flSpriteScale = ( m_iMagnitude - 50) * 0.6;
  155. // Control the clamping of the fireball sprite
  156. if( m_spawnflags & SF_ENVEXPLOSION_NOCLAMPMIN )
  157. {
  158. // Don't inhibit clamping altogether. Just relax it a bit.
  159. if ( flSpriteScale < 1 )
  160. {
  161. flSpriteScale = 1;
  162. }
  163. }
  164. else
  165. {
  166. if ( flSpriteScale < 10 )
  167. {
  168. flSpriteScale = 10;
  169. }
  170. }
  171. if( m_spawnflags & SF_ENVEXPLOSION_NOCLAMPMAX )
  172. {
  173. // We may need to adjust this to suit designers' needs.
  174. if ( flSpriteScale > 200 )
  175. {
  176. flSpriteScale = 200;
  177. }
  178. }
  179. else
  180. {
  181. if ( flSpriteScale > 50 )
  182. {
  183. flSpriteScale = 50;
  184. }
  185. }
  186. m_spriteScale = (int)flSpriteScale;
  187. m_iCustomDamageType = -1;
  188. }
  189. //-----------------------------------------------------------------------------
  190. // Purpose: Input handler for making the explosion explode.
  191. //-----------------------------------------------------------------------------
  192. void CEnvExplosion::InputExplode( inputdata_t &inputdata )
  193. {
  194. trace_t tr;
  195. SetModelName( NULL_STRING );//invisible
  196. SetSolid( SOLID_NONE );// intangible
  197. Vector vecSpot = GetAbsOrigin() + Vector( 0 , 0 , 8 );
  198. UTIL_TraceLine( vecSpot, vecSpot + Vector( 0, 0, -40 ), (MASK_SOLID_BRUSHONLY | MASK_WATER), this, COLLISION_GROUP_NONE, &tr );
  199. // Pull out of the wall a bit. We used to move the explosion origin itself, but that seems unnecessary, not to mention a
  200. // little weird when you consider that it might be in hierarchy. Instead we just calculate a new virtual position at
  201. // which to place the explosion. We don't use that new position to calculate radius damage because according to Steve's
  202. // comment, that adversely affects the force vector imparted on explosion victims when they ragdoll.
  203. Vector vecExplodeOrigin = GetAbsOrigin();
  204. if ( tr.fraction != 1.0 )
  205. {
  206. vecExplodeOrigin = tr.endpos + (tr.plane.normal * 24 );
  207. }
  208. // draw decal
  209. if (! ( m_spawnflags & SF_ENVEXPLOSION_NODECAL ))
  210. {
  211. if ( ! ( m_spawnflags & SF_ENVEXPLOSION_ICE ))
  212. UTIL_DecalTrace( &tr, "Scorch" );
  213. else
  214. UTIL_DecalTrace( &tr, "Ice_Explosion_Decal" );
  215. }
  216. // It's stupid that this entity's spawnflags and the flags for the
  217. // explosion temp ent don't match up. But because they don't, we
  218. // have to reinterpret some of the spawnflags to determine which
  219. // flags to pass to the temp ent.
  220. int nFlags = TE_EXPLFLAG_NONE;
  221. if( m_spawnflags & SF_ENVEXPLOSION_NOFIREBALL )
  222. {
  223. nFlags |= TE_EXPLFLAG_NOFIREBALL;
  224. }
  225. if( m_spawnflags & SF_ENVEXPLOSION_NOSOUND )
  226. {
  227. nFlags |= TE_EXPLFLAG_NOSOUND;
  228. }
  229. if ( m_spawnflags & SF_ENVEXPLOSION_RND_ORIENT )
  230. {
  231. nFlags |= TE_EXPLFLAG_ROTATE;
  232. }
  233. if ( m_nRenderMode == kRenderTransAlpha )
  234. {
  235. nFlags |= TE_EXPLFLAG_DRAWALPHA;
  236. }
  237. else if ( m_nRenderMode != kRenderTransAdd )
  238. {
  239. nFlags |= TE_EXPLFLAG_NOADDITIVE;
  240. }
  241. if( m_spawnflags & SF_ENVEXPLOSION_NOPARTICLES )
  242. {
  243. nFlags |= TE_EXPLFLAG_NOPARTICLES;
  244. }
  245. if( !(m_spawnflags & SF_ENVEXPLOSION_NODLIGHTS) )
  246. {
  247. nFlags |= TE_EXPLFLAG_DLIGHT;
  248. }
  249. if ( m_spawnflags & SF_ENVEXPLOSION_NOFIREBALLSMOKE )
  250. {
  251. nFlags |= TE_EXPLFLAG_NOFIREBALLSMOKE;
  252. }
  253. if ( m_spawnflags & SF_ENVEXPLOSION_ICE )
  254. {
  255. nFlags |= TE_EXPLFLAG_ICE;
  256. }
  257. //Get the damage override if specified
  258. int iRadius = ( m_iRadiusOverride > 0 ) ? m_iRadiusOverride : ( m_iMagnitude * 2.5f );
  259. CPASFilter filter( vecExplodeOrigin );
  260. te->Explosion( filter, 0.0,
  261. vecExplodeOrigin,
  262. ( m_sFireballSprite < 1 ) ? g_sModelIndexFireball : m_sFireballSprite,
  263. !( m_spawnflags & SF_ENVEXPLOSION_NOFIREBALL ) ? ( m_spriteScale / 10.0 ) : 0.0,
  264. 15,
  265. nFlags,
  266. iRadius,
  267. m_iMagnitude );
  268. // do damage
  269. if ( !( m_spawnflags & SF_ENVEXPLOSION_NODAMAGE ) )
  270. {
  271. CBaseEntity *pAttacker = GetOwnerEntity() ? GetOwnerEntity() : this;
  272. // Only calculate damage type if we didn't get a custom one passed in
  273. int iDamageType = m_iCustomDamageType;
  274. if ( iDamageType == -1 )
  275. {
  276. iDamageType = HasSpawnFlags( SF_ENVEXPLOSION_GENERIC_DAMAGE ) ? DMG_GENERIC : DMG_BLAST;
  277. }
  278. CTakeDamageInfo info( m_hInflictor ? m_hInflictor : this, pAttacker, m_iMagnitude, iDamageType );
  279. if( HasSpawnFlags( SF_ENVEXPLOSION_SURFACEONLY ) )
  280. {
  281. info.AddDamageType( DMG_BLAST_SURFACE );
  282. }
  283. if ( m_flDamageForce )
  284. {
  285. // Not the right direction, but it'll be fixed up by RadiusDamage.
  286. info.SetDamagePosition( GetAbsOrigin() );
  287. info.SetDamageForce( Vector( m_flDamageForce, 0, 0 ) );
  288. }
  289. RadiusDamage( info, GetAbsOrigin(), iRadius, m_iClassIgnore, m_hEntityIgnore.Get() );
  290. }
  291. SetThink( &CEnvExplosion::Smoke );
  292. SetNextThink( gpGlobals->curtime + 0.3 );
  293. // Only do these effects if we're not submerged
  294. if ( UTIL_PointContents( GetAbsOrigin(), MASK_WATER ) & CONTENTS_WATER )
  295. {
  296. // draw sparks
  297. if ( !( m_spawnflags & SF_ENVEXPLOSION_NOSPARKS ) )
  298. {
  299. int sparkCount = random->RandomInt(0,3);
  300. for ( int i = 0; i < sparkCount; i++ )
  301. {
  302. QAngle angles;
  303. VectorAngles( tr.plane.normal, angles );
  304. Create( "spark_shower", vecExplodeOrigin, angles, NULL );
  305. }
  306. }
  307. }
  308. }
  309. void CEnvExplosion::Smoke( void )
  310. {
  311. if ( !(m_spawnflags & SF_ENVEXPLOSION_REPEATABLE) )
  312. {
  313. UTIL_Remove( this );
  314. }
  315. }
  316. // HACKHACK -- create one of these and fake a keyvalue to get the right explosion setup
  317. void ExplosionCreate( const Vector &center, const QAngle &angles,
  318. CBaseEntity *pOwner, int magnitude, int radius, int nSpawnFlags, float flExplosionForce, CBaseEntity *pInflictor, int iCustomDamageType,
  319. const EHANDLE *ignoredEntity , Class_T ignoredClass )
  320. {
  321. char buf[128];
  322. CEnvExplosion *pExplosion = (CEnvExplosion*)CBaseEntity::Create( "env_explosion", center, angles, pOwner );
  323. Q_snprintf( buf,sizeof(buf), "%3d", magnitude );
  324. char *szKeyName = "iMagnitude";
  325. char *szValue = buf;
  326. pExplosion->KeyValue( szKeyName, szValue );
  327. pExplosion->AddSpawnFlags( nSpawnFlags );
  328. if ( radius )
  329. {
  330. Q_snprintf( buf,sizeof(buf), "%d", radius );
  331. pExplosion->KeyValue( "iRadiusOverride", buf );
  332. }
  333. if ( flExplosionForce != 0.0f )
  334. {
  335. Q_snprintf( buf,sizeof(buf), "%.3f", flExplosionForce );
  336. pExplosion->KeyValue( "DamageForce", buf );
  337. }
  338. variant_t emptyVariant;
  339. pExplosion->m_nRenderMode = kRenderTransAdd;
  340. pExplosion->SetOwnerEntity( pOwner );
  341. pExplosion->Spawn();
  342. pExplosion->m_hInflictor = pInflictor;
  343. pExplosion->SetCustomDamageType( iCustomDamageType );
  344. if (ignoredEntity)
  345. {
  346. pExplosion->m_hEntityIgnore = *ignoredEntity;
  347. }
  348. pExplosion->m_iClassIgnore = ignoredClass;
  349. pExplosion->AcceptInput( "Explode", NULL, NULL, emptyVariant, 0 );
  350. }
  351. void ExplosionCreate( const Vector &center, const QAngle &angles,
  352. CBaseEntity *pOwner, int magnitude, int radius, bool doDamage, float flExplosionForce, bool bSurfaceOnly, bool bSilent, int iCustomDamageType )
  353. {
  354. // For E3, no sparks
  355. int nFlags = SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE;
  356. if ( !doDamage )
  357. {
  358. nFlags |= SF_ENVEXPLOSION_NODAMAGE;
  359. }
  360. if( bSurfaceOnly )
  361. {
  362. nFlags |= SF_ENVEXPLOSION_SURFACEONLY;
  363. }
  364. if( bSilent )
  365. {
  366. nFlags |= SF_ENVEXPLOSION_NOSOUND;
  367. }
  368. ExplosionCreate( center, angles, pOwner, magnitude, radius, nFlags, flExplosionForce, NULL, iCustomDamageType );
  369. }
  370. // this version lets you specify classes or entities to be ignored
  371. void ExplosionCreate( const Vector &center, const QAngle &angles,
  372. CBaseEntity *pOwner, int magnitude, int radius, bool doDamage,
  373. const EHANDLE *ignoredEntity, Class_T ignoredClass,
  374. float flExplosionForce , bool bSurfaceOnly , bool bSilent , int iCustomDamageType )
  375. {
  376. // For E3, no sparks
  377. int nFlags = SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE;
  378. if ( !doDamage )
  379. {
  380. nFlags |= SF_ENVEXPLOSION_NODAMAGE;
  381. }
  382. if( bSurfaceOnly )
  383. {
  384. nFlags |= SF_ENVEXPLOSION_SURFACEONLY;
  385. }
  386. if( bSilent )
  387. {
  388. nFlags |= SF_ENVEXPLOSION_NOSOUND;
  389. }
  390. ExplosionCreate( center, angles, pOwner, magnitude, radius, nFlags, flExplosionForce, NULL, iCustomDamageType, ignoredEntity, ignoredClass );
  391. }
  392. //-----------------------------------------------------------------------------
  393. // Purpose: Draw any debug text overlays
  394. // Output : Current text offset from the top
  395. //-----------------------------------------------------------------------------
  396. int CEnvExplosion::DrawDebugTextOverlays( void )
  397. {
  398. int text_offset = BaseClass::DrawDebugTextOverlays();
  399. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  400. {
  401. char tempstr[512];
  402. Q_snprintf(tempstr,sizeof(tempstr)," magnitude: %i", m_iMagnitude);
  403. EntityText(text_offset,tempstr,0);
  404. text_offset++;
  405. }
  406. return text_offset;
  407. }