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.

418 lines
11 KiB

  1. //===== Copyright � 1996-2005, 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. #ifdef INFESTED_DLL
  15. #include "asw_fire.h"
  16. #else
  17. #include "fire.h"
  18. #endif
  19. #include "shareddefs.h"
  20. #include "ai_link.h"
  21. #include "ai_node.h"
  22. #include "ai_network.h"
  23. #include "ai_localnavigator.h"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. BEGIN_DATADESC( CEntityFlame )
  27. DEFINE_FIELD( m_flLifetime, FIELD_TIME ),
  28. DEFINE_FIELD( m_flSize, FIELD_FLOAT ),
  29. DEFINE_FIELD( m_hEntAttached, FIELD_EHANDLE ),
  30. DEFINE_FIELD( m_iDangerSound, FIELD_INTEGER ),
  31. DEFINE_FIELD( m_bCheapEffect, FIELD_BOOLEAN ),
  32. // DEFINE_FIELD( m_bPlayingSound, FIELD_BOOLEAN ),
  33. // DEFINE_FIELD( m_DangerLinks, CUtlVector< CAI_Link* > ),
  34. DEFINE_FUNCTION( FlameThink ),
  35. END_DATADESC()
  36. IMPLEMENT_SERVERCLASS_ST( CEntityFlame, DT_EntityFlame )
  37. SendPropEHandle( SENDINFO( m_hEntAttached ) ),
  38. SendPropBool( SENDINFO( m_bCheapEffect ) ),
  39. END_SEND_TABLE()
  40. #ifndef INFESTED_DLL
  41. LINK_ENTITY_TO_CLASS( entityflame, CEntityFlame );
  42. #endif
  43. PRECACHE_REGISTER(entityflame);
  44. //-----------------------------------------------------------------------------
  45. // Purpose:
  46. //-----------------------------------------------------------------------------
  47. CEntityFlame::CEntityFlame( void )
  48. {
  49. m_flSize = 0.0f;
  50. m_flLifetime = gpGlobals->curtime;
  51. m_bPlayingSound = false;
  52. m_iDangerSound = SOUNDLIST_EMPTY;
  53. m_bCheapEffect = false;
  54. m_hObstacle = OBSTACLE_INVALID;
  55. }
  56. void CEntityFlame::UpdateOnRemove()
  57. {
  58. // Sometimes the entity I'm burning gets destroyed by other means,
  59. // which kills me. Make sure to stop the burning sound.
  60. if ( m_bPlayingSound )
  61. {
  62. EmitSound( "General.StopBurning" );
  63. m_bPlayingSound = false;
  64. }
  65. if ( m_iDangerSound != SOUNDLIST_EMPTY )
  66. {
  67. CSoundEnt::FreeSound( m_iDangerSound );
  68. m_iDangerSound = SOUNDLIST_EMPTY;
  69. }
  70. int nCount = m_DangerLinks.Count();
  71. for ( int i = 0; i < nCount; ++i )
  72. {
  73. CAI_Link *pLink = m_DangerLinks[i];
  74. --pLink->m_nDangerCount;
  75. }
  76. m_DangerLinks.RemoveAll();
  77. if ( m_hObstacle != OBSTACLE_INVALID )
  78. {
  79. CAI_LocalNavigator::RemoveGlobalObstacle( m_hObstacle );
  80. m_hObstacle = OBSTACLE_INVALID;
  81. }
  82. BaseClass::UpdateOnRemove();
  83. }
  84. void CEntityFlame::Precache()
  85. {
  86. BaseClass::Precache();
  87. #ifndef DOTA_DLL
  88. PrecacheParticleSystem( "burning_character" );
  89. PrecacheParticleSystem( "burning_gib_01" );
  90. PrecacheScriptSound( "General.StopBurning" );
  91. PrecacheScriptSound( "General.BurningFlesh" );
  92. PrecacheScriptSound( "General.BurningObject" );
  93. #endif
  94. }
  95. void CEntityFlame::Spawn()
  96. {
  97. BaseClass::Spawn();
  98. m_flLifetime = gpGlobals->curtime;
  99. SetThink( &CEntityFlame::FlameThink );
  100. SetNextThink( gpGlobals->curtime + 0.1f );
  101. //Send to the client even though we don't have a model
  102. AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  103. #ifdef HL2_EP3
  104. m_iDangerSound = CSoundEnt::InsertSound( SOUND_DANGER | SOUND_CONTEXT_FROM_FIRE | SOUND_CONTEXT_FOLLOW_OWNER,
  105. GetAbsOrigin(), m_flSize * 2.0f, FLT_MAX, this );
  106. #endif
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Since we don't save/load danger links, we need to reacquire them here
  110. //-----------------------------------------------------------------------------
  111. void CEntityFlame::Activate()
  112. {
  113. BaseClass::Activate();
  114. #ifdef HL2_EP3
  115. #if 0
  116. // Mark nearby links as dangerous
  117. const Vector &vecOrigin = GetAbsOrigin();
  118. float flMaxDistSqr = m_flSize * m_flSize;
  119. m_hObstacle = CAI_LocalNavigator::AddGlobalObstacle( vecOrigin, m_flSize, AIMST_AVOID_DANGER );
  120. for ( int i = 0; i < g_pBigAINet->NumNodes(); i++ )
  121. {
  122. CAI_Node *pSrcNode = g_pBigAINet->GetNode( i );
  123. int nSrcNodeId = pSrcNode->GetId();
  124. for ( int j = 0; j < pSrcNode->NumLinks(); j++ )
  125. {
  126. CAI_Link *pLink = pSrcNode->GetLinkByIndex( j );
  127. int nDstNodeId = pLink->DestNodeID( nSrcNodeId );
  128. // Eliminates double-checking of links
  129. if ( nDstNodeId < nSrcNodeId )
  130. continue;
  131. CAI_Node *pDstNode = g_pBigAINet->GetNode( nDstNodeId );
  132. float flDistSqr = CalcDistanceSqrToLineSegment( vecOrigin, pSrcNode->GetOrigin(), pDstNode->GetOrigin() );
  133. if ( flDistSqr > flMaxDistSqr )
  134. continue;
  135. ++pLink->m_nDangerCount;
  136. m_DangerLinks.AddToTail( pLink );
  137. }
  138. }
  139. #endif
  140. #endif // HL2_EP3
  141. }
  142. void CEntityFlame::UseCheapEffect( bool bCheap )
  143. {
  144. m_bCheapEffect = bCheap;
  145. }
  146. //-----------------------------------------------------------------------------
  147. // Purpose: Creates a flame and attaches it to a target entity.
  148. // Input : pTarget -
  149. //-----------------------------------------------------------------------------
  150. CEntityFlame *CEntityFlame::Create( CBaseEntity *pTarget, float flLifetime, float flSize /*= 0.0f*/, bool bUseHitboxes /*= true*/ )
  151. {
  152. CEntityFlame *pFlame = (CEntityFlame *)CreateEntityByName( "entityflame" );
  153. if ( pFlame == NULL )
  154. return NULL;
  155. if ( flSize <= 0.0f )
  156. {
  157. float xSize = pTarget->CollisionProp()->OBBMaxs().x - pTarget->CollisionProp()->OBBMins().x;
  158. float ySize = pTarget->CollisionProp()->OBBMaxs().y - pTarget->CollisionProp()->OBBMins().y;
  159. flSize = ( xSize + ySize ) * 0.5f;
  160. if ( flSize < 16.0f )
  161. {
  162. flSize = 16.0f;
  163. }
  164. }
  165. if ( flLifetime <= 0.0f )
  166. {
  167. flLifetime = 2.0f;
  168. }
  169. pFlame->m_flSize = flSize;
  170. pFlame->Spawn();
  171. UTIL_SetOrigin( pFlame, pTarget->GetAbsOrigin() );
  172. pFlame->AttachToEntity( pTarget );
  173. pFlame->SetLifetime( flLifetime );
  174. pFlame->Activate();
  175. return pFlame;
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose: Attaches the flame to an entity and moves with it
  179. // Input : pTarget - target entity to attach to
  180. //-----------------------------------------------------------------------------
  181. void CEntityFlame::AttachToEntity( CBaseEntity *pTarget )
  182. {
  183. // For networking to the client.
  184. m_hEntAttached = pTarget;
  185. if( pTarget->IsNPC() )
  186. {
  187. EmitSound( "General.BurningFlesh" );
  188. }
  189. else
  190. {
  191. EmitSound( "General.BurningObject" );
  192. }
  193. m_bPlayingSound = true;
  194. // So our heat emitter follows the entity around on the server.
  195. SetParent( pTarget );
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Purpose:
  199. // Input : lifetime -
  200. //-----------------------------------------------------------------------------
  201. void CEntityFlame::SetLifetime( float lifetime )
  202. {
  203. m_flLifetime = gpGlobals->curtime + lifetime;
  204. }
  205. float CEntityFlame::GetRemainingLife( void ) const
  206. {
  207. return m_flLifetime - gpGlobals->curtime;
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose: Burn targets around us
  211. //-----------------------------------------------------------------------------
  212. void CEntityFlame::FlameThink( void )
  213. {
  214. // Assure that this function will be ticked again even if we early-out in the if below.
  215. SetNextThink( gpGlobals->curtime + FLAME_DAMAGE_INTERVAL );
  216. if ( !m_hEntAttached.Get() )
  217. {
  218. UTIL_Remove( this );
  219. return;
  220. }
  221. if ( m_hEntAttached->GetFlags() & FL_TRANSRAGDOLL )
  222. {
  223. SetRenderAlpha( 0 );
  224. return;
  225. }
  226. CAI_BaseNPC *pNPC = m_hEntAttached->MyNPCPointer();
  227. if ( pNPC && !pNPC->IsAlive() )
  228. {
  229. UTIL_Remove( this );
  230. // Notify the NPC that it's no longer burning!
  231. pNPC->Extinguish();
  232. return;
  233. }
  234. if ( m_hEntAttached->GetWaterLevel() > WL_NotInWater )
  235. {
  236. Vector mins, maxs;
  237. mins = m_hEntAttached->WorldSpaceCenter();
  238. maxs = mins;
  239. maxs.z = m_hEntAttached->WorldSpaceCenter().z;
  240. maxs.x += 32;
  241. maxs.y += 32;
  242. mins.z -= 32;
  243. mins.x -= 32;
  244. mins.y -= 32;
  245. UTIL_Bubbles( mins, maxs, 12 );
  246. }
  247. // See if we're done burning, or our attached ent has vanished
  248. if ( m_flLifetime < gpGlobals->curtime || m_hEntAttached == NULL )
  249. {
  250. EmitSound( "General.StopBurning" );
  251. m_bPlayingSound = false;
  252. SetThink( &CEntityFlame::SUB_Remove );
  253. SetNextThink( gpGlobals->curtime + 0.5f );
  254. // Notify anything we're attached to
  255. if ( m_hEntAttached )
  256. {
  257. CBaseCombatCharacter *pAttachedCC = m_hEntAttached->MyCombatCharacterPointer();
  258. if( pAttachedCC )
  259. {
  260. // Notify the NPC that it's no longer burning!
  261. pAttachedCC->Extinguish();
  262. }
  263. }
  264. return;
  265. }
  266. if ( m_hEntAttached )
  267. {
  268. // Do radius damage ignoring the entity I'm attached to. This will harm things around me.
  269. RadiusDamage( CTakeDamageInfo( this, this, 4.0f, DMG_BURN ), GetAbsOrigin(), m_flSize/2, CLASS_NONE, m_hEntAttached );
  270. // Directly harm the entity I'm attached to. This is so we can precisely control how much damage the entity
  271. // that is on fire takes without worrying about the flame's position relative to the bodytarget (which is the
  272. // distance that the radius damage code uses to determine how much damage to inflict)
  273. m_hEntAttached->TakeDamage( CTakeDamageInfo( this, this, FLAME_DIRECT_DAMAGE, DMG_BURN | DMG_DIRECT ) );
  274. if( !m_hEntAttached->IsNPC() && hl2_episodic.GetBool() )
  275. {
  276. const float ENTITYFLAME_MOVE_AWAY_DIST = 24.0f;
  277. // Make a sound near my origin, and up a little higher (in case I'm on the ground, so NPC's still hear it)
  278. CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin(), ENTITYFLAME_MOVE_AWAY_DIST, 0.1f, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
  279. CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin() + Vector( 0, 0, 48.0f ), ENTITYFLAME_MOVE_AWAY_DIST, 0.1f, this, SOUNDENT_CHANNEL_REPEATING );
  280. }
  281. }
  282. else
  283. {
  284. RadiusDamage( CTakeDamageInfo( this, this, FLAME_RADIUS_DAMAGE, DMG_BURN ), GetAbsOrigin(), m_flSize/2, CLASS_NONE, NULL );
  285. }
  286. FireSystem_AddHeatInRadius( GetAbsOrigin(), m_flSize/2, 2.0f );
  287. }
  288. //-----------------------------------------------------------------------------
  289. // Igniter
  290. //-----------------------------------------------------------------------------
  291. class CEnvEntityIgniter : public CBaseEntity
  292. {
  293. public:
  294. DECLARE_CLASS( CEnvEntityIgniter, CBaseEntity );
  295. DECLARE_DATADESC();
  296. virtual void Precache();
  297. protected:
  298. void InputIgnite( inputdata_t &inputdata );
  299. float m_flLifetime;
  300. };
  301. BEGIN_DATADESC( CEnvEntityIgniter )
  302. DEFINE_KEYFIELD( m_flLifetime, FIELD_FLOAT, "lifetime" ),
  303. DEFINE_INPUTFUNC( FIELD_VOID, "Ignite", InputIgnite ),
  304. END_DATADESC()
  305. LINK_ENTITY_TO_CLASS( env_entity_igniter, CEnvEntityIgniter );
  306. //-----------------------------------------------------------------------------
  307. // Purpose: Ignites entities
  308. //-----------------------------------------------------------------------------
  309. void CEnvEntityIgniter::Precache()
  310. {
  311. BaseClass::Precache();
  312. UTIL_PrecacheOther( "entityflame" );
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose: Ignites entities
  316. //-----------------------------------------------------------------------------
  317. void CEnvEntityIgniter::InputIgnite( inputdata_t &inputdata )
  318. {
  319. if ( m_target == NULL_STRING )
  320. return;
  321. CBaseEntity *pTarget = NULL;
  322. while ( (pTarget = gEntList.FindEntityGeneric(pTarget, STRING(m_target), this, inputdata.pActivator)) != NULL )
  323. {
  324. // Combat characters know how to catch themselves on fire.
  325. CBaseCombatCharacter *pBCC = pTarget->MyCombatCharacterPointer();
  326. if (pBCC)
  327. {
  328. // DVS TODO: consider promoting Ignite to CBaseEntity and doing everything here
  329. pBCC->Ignite( m_flLifetime );
  330. continue;
  331. }
  332. // Everything else, we handle here.
  333. CEntityFlame::Create( pTarget, m_flLifetime );
  334. }
  335. }