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.

491 lines
13 KiB

  1. //========= Copyright 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_FIELD( m_iClassIgnore, FIELD_INTEGER ),
  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. }
  139. void CEnvExplosion::Spawn( void )
  140. {
  141. Precache();
  142. SetSolid( SOLID_NONE );
  143. AddEffects( EF_NODRAW );
  144. SetMoveType( MOVETYPE_NONE );
  145. /*
  146. if ( m_iMagnitude > 250 )
  147. {
  148. m_iMagnitude = 250;
  149. }
  150. */
  151. float flSpriteScale;
  152. flSpriteScale = ( m_iMagnitude - 50) * 0.6;
  153. // Control the clamping of the fireball sprite
  154. if( m_spawnflags & SF_ENVEXPLOSION_NOCLAMPMIN )
  155. {
  156. // Don't inhibit clamping altogether. Just relax it a bit.
  157. if ( flSpriteScale < 1 )
  158. {
  159. flSpriteScale = 1;
  160. }
  161. }
  162. else
  163. {
  164. if ( flSpriteScale < 10 )
  165. {
  166. flSpriteScale = 10;
  167. }
  168. }
  169. if( m_spawnflags & SF_ENVEXPLOSION_NOCLAMPMAX )
  170. {
  171. // We may need to adjust this to suit designers' needs.
  172. if ( flSpriteScale > 200 )
  173. {
  174. flSpriteScale = 200;
  175. }
  176. }
  177. else
  178. {
  179. if ( flSpriteScale > 50 )
  180. {
  181. flSpriteScale = 50;
  182. }
  183. }
  184. m_spriteScale = (int)flSpriteScale;
  185. m_iCustomDamageType = -1;
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Purpose: Input handler for making the explosion explode.
  189. //-----------------------------------------------------------------------------
  190. void CEnvExplosion::InputExplode( inputdata_t &inputdata )
  191. {
  192. trace_t tr;
  193. SetModelName( NULL_STRING );//invisible
  194. SetSolid( SOLID_NONE );// intangible
  195. Vector vecSpot = GetAbsOrigin() + Vector( 0 , 0 , 8 );
  196. UTIL_TraceLine( vecSpot, vecSpot + Vector( 0, 0, -40 ), (MASK_SOLID_BRUSHONLY | MASK_WATER), this, COLLISION_GROUP_NONE, &tr );
  197. // Pull out of the wall a bit. We used to move the explosion origin itself, but that seems unnecessary, not to mention a
  198. // little weird when you consider that it might be in hierarchy. Instead we just calculate a new virtual position at
  199. // which to place the explosion. We don't use that new position to calculate radius damage because according to Steve's
  200. // comment, that adversely affects the force vector imparted on explosion victims when they ragdoll.
  201. Vector vecExplodeOrigin = GetAbsOrigin();
  202. if ( tr.fraction != 1.0 )
  203. {
  204. vecExplodeOrigin = tr.endpos + (tr.plane.normal * 24 );
  205. }
  206. // draw decal
  207. if (! ( m_spawnflags & SF_ENVEXPLOSION_NODECAL))
  208. {
  209. UTIL_DecalTrace( &tr, "Scorch" );
  210. }
  211. // It's stupid that this entity's spawnflags and the flags for the
  212. // explosion temp ent don't match up. But because they don't, we
  213. // have to reinterpret some of the spawnflags to determine which
  214. // flags to pass to the temp ent.
  215. int nFlags = TE_EXPLFLAG_NONE;
  216. if( m_spawnflags & SF_ENVEXPLOSION_NOFIREBALL )
  217. {
  218. nFlags |= TE_EXPLFLAG_NOFIREBALL;
  219. }
  220. if( m_spawnflags & SF_ENVEXPLOSION_NOSOUND )
  221. {
  222. nFlags |= TE_EXPLFLAG_NOSOUND;
  223. }
  224. if ( m_spawnflags & SF_ENVEXPLOSION_RND_ORIENT )
  225. {
  226. nFlags |= TE_EXPLFLAG_ROTATE;
  227. }
  228. if ( m_nRenderMode == kRenderTransAlpha )
  229. {
  230. nFlags |= TE_EXPLFLAG_DRAWALPHA;
  231. }
  232. else if ( m_nRenderMode != kRenderTransAdd )
  233. {
  234. nFlags |= TE_EXPLFLAG_NOADDITIVE;
  235. }
  236. if( m_spawnflags & SF_ENVEXPLOSION_NOPARTICLES )
  237. {
  238. nFlags |= TE_EXPLFLAG_NOPARTICLES;
  239. }
  240. if( m_spawnflags & SF_ENVEXPLOSION_NODLIGHTS )
  241. {
  242. nFlags |= TE_EXPLFLAG_NODLIGHTS;
  243. }
  244. if ( m_spawnflags & SF_ENVEXPLOSION_NOFIREBALLSMOKE )
  245. {
  246. nFlags |= TE_EXPLFLAG_NOFIREBALLSMOKE;
  247. }
  248. //Get the damage override if specified
  249. int iRadius = ( m_iRadiusOverride > 0 ) ? m_iRadiusOverride : ( m_iMagnitude * 2.5f );
  250. CPASFilter filter( vecExplodeOrigin );
  251. te->Explosion( filter, 0.0,
  252. &vecExplodeOrigin,
  253. ( m_sFireballSprite < 1 ) ? g_sModelIndexFireball : m_sFireballSprite,
  254. !( m_spawnflags & SF_ENVEXPLOSION_NOFIREBALL ) ? ( m_spriteScale / 10.0 ) : 0.0,
  255. 15,
  256. nFlags,
  257. iRadius,
  258. m_iMagnitude );
  259. // do damage
  260. if ( !( m_spawnflags & SF_ENVEXPLOSION_NODAMAGE ) )
  261. {
  262. CBaseEntity *pAttacker = GetOwnerEntity() ? GetOwnerEntity() : this;
  263. // Only calculate damage type if we didn't get a custom one passed in
  264. int iDamageType = m_iCustomDamageType;
  265. if ( iDamageType == -1 )
  266. {
  267. iDamageType = HasSpawnFlags( SF_ENVEXPLOSION_GENERIC_DAMAGE ) ? DMG_GENERIC : DMG_BLAST;
  268. }
  269. CTakeDamageInfo info( m_hInflictor ? m_hInflictor : this, pAttacker, m_iMagnitude, iDamageType );
  270. if( HasSpawnFlags( SF_ENVEXPLOSION_SURFACEONLY ) )
  271. {
  272. info.AddDamageType( DMG_BLAST_SURFACE );
  273. }
  274. if ( m_flDamageForce )
  275. {
  276. // Not the right direction, but it'll be fixed up by RadiusDamage.
  277. info.SetDamagePosition( GetAbsOrigin() );
  278. info.SetDamageForce( Vector( m_flDamageForce, 0, 0 ) );
  279. }
  280. RadiusDamage( info, GetAbsOrigin(), iRadius, m_iClassIgnore, m_hEntityIgnore.Get() );
  281. }
  282. SetThink( &CEnvExplosion::Smoke );
  283. SetNextThink( gpGlobals->curtime + 0.3 );
  284. // Only do these effects if we're not submerged
  285. if ( UTIL_PointContents( GetAbsOrigin() ) & CONTENTS_WATER )
  286. {
  287. // draw sparks
  288. if ( !( m_spawnflags & SF_ENVEXPLOSION_NOSPARKS ) )
  289. {
  290. int sparkCount = random->RandomInt(0,3);
  291. for ( int i = 0; i < sparkCount; i++ )
  292. {
  293. QAngle angles;
  294. VectorAngles( tr.plane.normal, angles );
  295. Create( "spark_shower", vecExplodeOrigin, angles, NULL );
  296. }
  297. }
  298. }
  299. }
  300. void CEnvExplosion::Smoke( void )
  301. {
  302. if ( !(m_spawnflags & SF_ENVEXPLOSION_REPEATABLE) )
  303. {
  304. UTIL_Remove( this );
  305. }
  306. }
  307. // HACKHACK -- create one of these and fake a keyvalue to get the right explosion setup
  308. void ExplosionCreate( const Vector &center, const QAngle &angles,
  309. CBaseEntity *pOwner, int magnitude, int radius, int nSpawnFlags, float flExplosionForce, CBaseEntity *pInflictor, int iCustomDamageType,
  310. const EHANDLE *ignoredEntity , Class_T ignoredClass )
  311. {
  312. char buf[128];
  313. CEnvExplosion *pExplosion = (CEnvExplosion*)CBaseEntity::Create( "env_explosion", center, angles, pOwner );
  314. Q_snprintf( buf,sizeof(buf), "%3d", magnitude );
  315. char *szKeyName = "iMagnitude";
  316. char *szValue = buf;
  317. pExplosion->KeyValue( szKeyName, szValue );
  318. pExplosion->AddSpawnFlags( nSpawnFlags );
  319. if ( radius )
  320. {
  321. Q_snprintf( buf,sizeof(buf), "%d", radius );
  322. pExplosion->KeyValue( "iRadiusOverride", buf );
  323. }
  324. if ( flExplosionForce != 0.0f )
  325. {
  326. Q_snprintf( buf,sizeof(buf), "%.3f", flExplosionForce );
  327. pExplosion->KeyValue( "DamageForce", buf );
  328. }
  329. variant_t emptyVariant;
  330. pExplosion->m_nRenderMode = kRenderTransAdd;
  331. pExplosion->SetOwnerEntity( pOwner );
  332. pExplosion->Spawn();
  333. pExplosion->m_hInflictor = pInflictor;
  334. pExplosion->SetCustomDamageType( iCustomDamageType );
  335. if (ignoredEntity)
  336. {
  337. pExplosion->m_hEntityIgnore = *ignoredEntity;
  338. }
  339. pExplosion->m_iClassIgnore = ignoredClass;
  340. pExplosion->AcceptInput( "Explode", NULL, NULL, emptyVariant, 0 );
  341. }
  342. void ExplosionCreate( const Vector &center, const QAngle &angles,
  343. CBaseEntity *pOwner, int magnitude, int radius, bool doDamage, float flExplosionForce, bool bSurfaceOnly, bool bSilent, int iCustomDamageType )
  344. {
  345. // For E3, no sparks
  346. int nFlags = SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE;
  347. if ( !doDamage )
  348. {
  349. nFlags |= SF_ENVEXPLOSION_NODAMAGE;
  350. }
  351. if( bSurfaceOnly )
  352. {
  353. nFlags |= SF_ENVEXPLOSION_SURFACEONLY;
  354. }
  355. if( bSilent )
  356. {
  357. nFlags |= SF_ENVEXPLOSION_NOSOUND;
  358. }
  359. ExplosionCreate( center, angles, pOwner, magnitude, radius, nFlags, flExplosionForce, NULL, iCustomDamageType );
  360. }
  361. // this version lets you specify classes or entities to be ignored
  362. void ExplosionCreate( const Vector &center, const QAngle &angles,
  363. CBaseEntity *pOwner, int magnitude, int radius, bool doDamage,
  364. const EHANDLE *ignoredEntity, Class_T ignoredClass,
  365. float flExplosionForce , bool bSurfaceOnly , bool bSilent , int iCustomDamageType )
  366. {
  367. // For E3, no sparks
  368. int nFlags = SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE;
  369. if ( !doDamage )
  370. {
  371. nFlags |= SF_ENVEXPLOSION_NODAMAGE;
  372. }
  373. if( bSurfaceOnly )
  374. {
  375. nFlags |= SF_ENVEXPLOSION_SURFACEONLY;
  376. }
  377. if( bSilent )
  378. {
  379. nFlags |= SF_ENVEXPLOSION_NOSOUND;
  380. }
  381. ExplosionCreate( center, angles, pOwner, magnitude, radius, nFlags, flExplosionForce, NULL, iCustomDamageType, ignoredEntity, ignoredClass );
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose: Draw any debug text overlays
  385. // Output : Current text offset from the top
  386. //-----------------------------------------------------------------------------
  387. int CEnvExplosion::DrawDebugTextOverlays( void )
  388. {
  389. int text_offset = BaseClass::DrawDebugTextOverlays();
  390. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  391. {
  392. char tempstr[512];
  393. Q_snprintf(tempstr,sizeof(tempstr)," magnitude: %i", m_iMagnitude);
  394. EntityText(text_offset,tempstr,0);
  395. text_offset++;
  396. }
  397. return text_offset;
  398. }