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.

335 lines
9.4 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Flame entity to be attached to target entity. Serves two purposes:
  4. //
  5. // 1) An entity that can be placed by a level designer and triggered
  6. // to ignite a target entity.
  7. //
  8. // 2) An entity that can be created at runtime to ignite a target entity.
  9. //
  10. //=============================================================================//
  11. #include "cbase.h"
  12. #include "EntityFlame.h"
  13. #include "ai_basenpc.h"
  14. #include "fire.h"
  15. #include "shareddefs.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. BEGIN_DATADESC( CEntityFlame )
  19. DEFINE_KEYFIELD( m_flLifetime, FIELD_FLOAT, "lifetime" ),
  20. DEFINE_FIELD( m_flSize, FIELD_FLOAT ),
  21. DEFINE_FIELD( m_hEntAttached, FIELD_EHANDLE ),
  22. DEFINE_FIELD( m_bUseHitboxes, FIELD_BOOLEAN ),
  23. DEFINE_FIELD( m_iNumHitboxFires, FIELD_INTEGER ),
  24. DEFINE_FIELD( m_flHitboxFireScale, FIELD_FLOAT ),
  25. // DEFINE_FIELD( m_bPlayingSound, FIELD_BOOLEAN ),
  26. DEFINE_FUNCTION( FlameThink ),
  27. DEFINE_INPUTFUNC( FIELD_VOID, "Ignite", InputIgnite ),
  28. END_DATADESC()
  29. IMPLEMENT_SERVERCLASS_ST( CEntityFlame, DT_EntityFlame )
  30. SendPropEHandle( SENDINFO( m_hEntAttached ) ),
  31. END_SEND_TABLE()
  32. LINK_ENTITY_TO_CLASS( entityflame, CEntityFlame );
  33. LINK_ENTITY_TO_CLASS( env_entity_igniter, CEntityFlame );
  34. PRECACHE_REGISTER(entityflame);
  35. //-----------------------------------------------------------------------------
  36. // Purpose:
  37. //-----------------------------------------------------------------------------
  38. CEntityFlame::CEntityFlame( void )
  39. {
  40. m_flSize = 0.0f;
  41. m_iNumHitboxFires = 10;
  42. m_flHitboxFireScale = 1.0f;
  43. m_flLifetime = 0.0f;
  44. m_bPlayingSound = false;
  45. }
  46. void CEntityFlame::UpdateOnRemove()
  47. {
  48. // Sometimes the entity I'm burning gets destroyed by other means,
  49. // which kills me. Make sure to stop the burning sound.
  50. if ( m_bPlayingSound )
  51. {
  52. EmitSound( "General.StopBurning" );
  53. m_bPlayingSound = false;
  54. }
  55. BaseClass::UpdateOnRemove();
  56. }
  57. void CEntityFlame::Precache()
  58. {
  59. BaseClass::Precache();
  60. PrecacheScriptSound( "General.StopBurning" );
  61. PrecacheScriptSound( "General.BurningFlesh" );
  62. PrecacheScriptSound( "General.BurningObject" );
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Purpose:
  66. // Input : inputdata -
  67. //-----------------------------------------------------------------------------
  68. void CEntityFlame::InputIgnite( inputdata_t &inputdata )
  69. {
  70. if (m_target != NULL_STRING)
  71. {
  72. CBaseEntity *pTarget = NULL;
  73. while ((pTarget = gEntList.FindEntityGeneric(pTarget, STRING(m_target), this, inputdata.pActivator)) != NULL)
  74. {
  75. // Combat characters know how to catch themselves on fire.
  76. CBaseCombatCharacter *pBCC = pTarget->MyCombatCharacterPointer();
  77. if (pBCC)
  78. {
  79. // DVS TODO: consider promoting Ignite to CBaseEntity and doing everything here
  80. pBCC->Ignite(m_flLifetime);
  81. }
  82. // Everything else, we handle here.
  83. else
  84. {
  85. CEntityFlame *pFlame = CEntityFlame::Create(pTarget);
  86. if (pFlame)
  87. {
  88. pFlame->SetLifetime(m_flLifetime);
  89. }
  90. }
  91. }
  92. }
  93. }
  94. //-----------------------------------------------------------------------------
  95. // Purpose: Creates a flame and attaches it to a target entity.
  96. // Input : pTarget -
  97. //-----------------------------------------------------------------------------
  98. CEntityFlame *CEntityFlame::Create( CBaseEntity *pTarget, bool useHitboxes )
  99. {
  100. CEntityFlame *pFlame = (CEntityFlame *) CreateEntityByName( "entityflame" );
  101. if ( pFlame == NULL )
  102. return NULL;
  103. float xSize = pTarget->CollisionProp()->OBBMaxs().x - pTarget->CollisionProp()->OBBMins().x;
  104. float ySize = pTarget->CollisionProp()->OBBMaxs().y - pTarget->CollisionProp()->OBBMins().y;
  105. float size = ( xSize + ySize ) * 0.5f;
  106. if ( size < 16.0f )
  107. {
  108. size = 16.0f;
  109. }
  110. UTIL_SetOrigin( pFlame, pTarget->GetAbsOrigin() );
  111. pFlame->m_flSize = size;
  112. pFlame->SetThink( &CEntityFlame::FlameThink );
  113. pFlame->SetNextThink( gpGlobals->curtime + 0.1f );
  114. pFlame->AttachToEntity( pTarget );
  115. pFlame->SetLifetime( 2.0f );
  116. //Send to the client even though we don't have a model
  117. pFlame->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  118. pFlame->SetUseHitboxes( useHitboxes );
  119. return pFlame;
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Attaches the flame to an entity and moves with it
  123. // Input : pTarget - target entity to attach to
  124. //-----------------------------------------------------------------------------
  125. void CEntityFlame::AttachToEntity( CBaseEntity *pTarget )
  126. {
  127. // For networking to the client.
  128. m_hEntAttached = pTarget;
  129. if( pTarget->IsNPC() )
  130. {
  131. EmitSound( "General.BurningFlesh" );
  132. }
  133. else
  134. {
  135. EmitSound( "General.BurningObject" );
  136. }
  137. m_bPlayingSound = true;
  138. // So our heat emitter follows the entity around on the server.
  139. SetParent( pTarget );
  140. }
  141. //-----------------------------------------------------------------------------
  142. // Purpose:
  143. // Input : lifetime -
  144. //-----------------------------------------------------------------------------
  145. void CEntityFlame::SetLifetime( float lifetime )
  146. {
  147. m_flLifetime = gpGlobals->curtime + lifetime;
  148. }
  149. //-----------------------------------------------------------------------------
  150. // Purpose:
  151. // Input : use -
  152. //-----------------------------------------------------------------------------
  153. void CEntityFlame::SetUseHitboxes( bool use )
  154. {
  155. m_bUseHitboxes = use;
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose:
  159. // Input : iNumHitBoxFires -
  160. //-----------------------------------------------------------------------------
  161. void CEntityFlame::SetNumHitboxFires( int iNumHitboxFires )
  162. {
  163. m_iNumHitboxFires = iNumHitboxFires;
  164. }
  165. //-----------------------------------------------------------------------------
  166. // Purpose:
  167. // Input : flHitboxFireScale -
  168. //-----------------------------------------------------------------------------
  169. void CEntityFlame::SetHitboxFireScale( float flHitboxFireScale )
  170. {
  171. m_flHitboxFireScale = flHitboxFireScale;
  172. }
  173. float CEntityFlame::GetRemainingLife( void )
  174. {
  175. return m_flLifetime - gpGlobals->curtime;
  176. }
  177. int CEntityFlame::GetNumHitboxFires( void )
  178. {
  179. return m_iNumHitboxFires;
  180. }
  181. float CEntityFlame::GetHitboxFireScale( void )
  182. {
  183. return m_flHitboxFireScale;
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose: Burn targets around us
  187. //-----------------------------------------------------------------------------
  188. void CEntityFlame::FlameThink( void )
  189. {
  190. // Assure that this function will be ticked again even if we early-out in the if below.
  191. SetNextThink( gpGlobals->curtime + FLAME_DAMAGE_INTERVAL );
  192. if ( m_hEntAttached )
  193. {
  194. if ( m_hEntAttached->GetFlags() & FL_TRANSRAGDOLL )
  195. {
  196. SetRenderColorA( 0 );
  197. return;
  198. }
  199. CAI_BaseNPC *pNPC = m_hEntAttached->MyNPCPointer();
  200. if ( pNPC && !pNPC->IsAlive() )
  201. {
  202. UTIL_Remove( this );
  203. // Notify the NPC that it's no longer burning!
  204. pNPC->Extinguish();
  205. return;
  206. }
  207. if( m_hEntAttached->GetWaterLevel() > 0 )
  208. {
  209. Vector mins, maxs;
  210. mins = m_hEntAttached->WorldSpaceCenter();
  211. maxs = mins;
  212. maxs.z = m_hEntAttached->WorldSpaceCenter().z;
  213. maxs.x += 32;
  214. maxs.y += 32;
  215. mins.z -= 32;
  216. mins.x -= 32;
  217. mins.y -= 32;
  218. UTIL_Bubbles( mins, maxs, 12 );
  219. }
  220. }
  221. else
  222. {
  223. UTIL_Remove( this );
  224. return;
  225. }
  226. // See if we're done burning, or our attached ent has vanished
  227. if ( m_flLifetime < gpGlobals->curtime || m_hEntAttached == NULL )
  228. {
  229. EmitSound( "General.StopBurning" );
  230. m_bPlayingSound = false;
  231. SetThink( &CEntityFlame::SUB_Remove );
  232. SetNextThink( gpGlobals->curtime + 0.5f );
  233. // Notify anything we're attached to
  234. if ( m_hEntAttached )
  235. {
  236. CBaseCombatCharacter *pAttachedCC = m_hEntAttached->MyCombatCharacterPointer();
  237. if( pAttachedCC )
  238. {
  239. // Notify the NPC that it's no longer burning!
  240. pAttachedCC->Extinguish();
  241. }
  242. }
  243. return;
  244. }
  245. if ( m_hEntAttached )
  246. {
  247. // Do radius damage ignoring the entity I'm attached to. This will harm things around me.
  248. RadiusDamage( CTakeDamageInfo( this, this, 4.0f, DMG_BURN ), GetAbsOrigin(), m_flSize/2, CLASS_NONE, m_hEntAttached );
  249. // Directly harm the entity I'm attached to. This is so we can precisely control how much damage the entity
  250. // that is on fire takes without worrying about the flame's position relative to the bodytarget (which is the
  251. // distance that the radius damage code uses to determine how much damage to inflict)
  252. m_hEntAttached->TakeDamage( CTakeDamageInfo( this, this, FLAME_DIRECT_DAMAGE, DMG_BURN | DMG_DIRECT ) );
  253. if( !m_hEntAttached->IsNPC() && hl2_episodic.GetBool() )
  254. {
  255. const float ENTITYFLAME_MOVE_AWAY_DIST = 24.0f;
  256. // Make a sound near my origin, and up a little higher (in case I'm on the ground, so NPC's still hear it)
  257. CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin(), ENTITYFLAME_MOVE_AWAY_DIST, 0.1f, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
  258. CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin() + Vector( 0, 0, 48.0f ), ENTITYFLAME_MOVE_AWAY_DIST, 0.1f, this, SOUNDENT_CHANNEL_REPEATING );
  259. }
  260. }
  261. else
  262. {
  263. RadiusDamage( CTakeDamageInfo( this, this, FLAME_RADIUS_DAMAGE, DMG_BURN ), GetAbsOrigin(), m_flSize/2, CLASS_NONE, NULL );
  264. }
  265. FireSystem_AddHeatInRadius( GetAbsOrigin(), m_flSize/2, 2.0f );
  266. }
  267. //-----------------------------------------------------------------------------
  268. // Purpose:
  269. // Input : pEnt -
  270. //-----------------------------------------------------------------------------
  271. void CreateEntityFlame(CBaseEntity *pEnt)
  272. {
  273. CEntityFlame::Create( pEnt );
  274. }