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.

1492 lines
38 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. //---------------------------------------------------------
  9. //---------------------------------------------------------
  10. #include "cbase.h"
  11. #include "decals.h"
  12. #include "fire.h"
  13. #include "entitylist.h"
  14. #include "basecombatcharacter.h"
  15. #include "ndebugoverlay.h"
  16. #include "engine/IEngineSound.h"
  17. #include "ispatialpartition.h"
  18. #include "collisionutils.h"
  19. #include "tier0/vprof.h"
  20. #include "nav.h"
  21. #include "nav_mesh.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. /********************************************************************
  25. NOTE: if you are looking at this file becase you would like flares
  26. to be considered as fires (and thereby trigger gas traps), be aware
  27. that the env_flare class is actually found in weapon_flaregun.cpp
  28. and is really a repurposed piece of ammunition. (env_flare isn't the
  29. rod-like safety flare prop, but rather the bit of flame on the end.)
  30. You will have some difficulty making it work here, because CFlare
  31. does not inherit from CFire and will thus not be enumerated by
  32. CFireSphere::EnumElement(). In order to have flares be detected and
  33. used by this system, you will need to promote certain member functions
  34. of CFire into an interface class from which both CFire and CFlare
  35. inherit. You will also need to modify CFireSphere::EnumElement so that
  36. it properly disambiguates between fires and flares.
  37. For some partial work towards this end, see changelist 192474.
  38. ********************************************************************/
  39. #define FIRE_HEIGHT 256.0f
  40. #define FIRE_SCALE_FROM_SIZE(firesize) (firesize * (1/FIRE_HEIGHT))
  41. #define FIRE_MAX_GROUND_OFFSET 24.0f //(2 feet)
  42. #define DEFAULT_ATTACK_TIME 4.0f
  43. #define DEFAULT_DECAY_TIME 8.0f
  44. // UNDONE: This shouldn't be constant but depend on specific fire
  45. #define FIRE_WIDTH 128
  46. #define FIRE_MINS Vector(-20,-20,0 ) // Sould be FIRE_WIDTH in size
  47. #define FIRE_MAXS Vector( 20, 20,20) // Sould be FIRE_WIDTH in size
  48. #define FIRE_SPREAD_DAMAGE_MULTIPLIER 2.0
  49. #define FIRE_MAX_HEAT_LEVEL 64.0f
  50. #define FIRE_NORMAL_ATTACK_TIME 20.0f
  51. #define FIRE_THINK_INTERVAL 0.1
  52. ConVar fire_maxabsorb( "fire_maxabsorb", "50" );
  53. ConVar fire_absorbrate( "fire_absorbrate", "3" );
  54. ConVar fire_extscale("fire_extscale", "12");
  55. ConVar fire_extabsorb("fire_extabsorb", "5");
  56. ConVar fire_heatscale( "fire_heatscale", "1.0" );
  57. ConVar fire_incomingheatscale( "fire_incomingheatscale", "0.1" );
  58. ConVar fire_dmgscale( "fire_dmgscale", "0.1" );
  59. ConVar fire_dmgbase( "fire_dmgbase", "1" );
  60. ConVar fire_growthrate( "fire_growthrate", "1.0" );
  61. ConVar fire_dmginterval( "fire_dmginterval", "1.0" );
  62. #define VPROF_FIRE(s) VPROF( s )
  63. class CFire : public CBaseEntity
  64. {
  65. public:
  66. DECLARE_CLASS( CFire, CBaseEntity );
  67. int DrawDebugTextOverlays(void);
  68. CFire( void );
  69. virtual void UpdateOnRemove( void );
  70. void Precache( void );
  71. void Init( const Vector &position, float scale, float attackTime, float fuel, int flags, int fireType );
  72. bool GoOut();
  73. void BurnThink();
  74. void GoOutThink();
  75. void GoOutInSeconds( float seconds );
  76. void SetOwner( CBaseEntity *hOwner ) { m_hOwner = hOwner; }
  77. void Scale( float end, float time );
  78. void AddHeat( float heat, bool selfHeat = false );
  79. int OnTakeDamage( const CTakeDamageInfo &info );
  80. bool IsBurning( void ) const;
  81. bool GetFireDimensions( Vector *pFireMins, Vector *pFireMaxs );
  82. void Extinguish( float heat );
  83. void DestroyEffect();
  84. virtual void Update( float simTime );
  85. void Spawn( void );
  86. void Activate( void );
  87. void StartFire( void );
  88. void Start();
  89. void SetToOutSize()
  90. {
  91. UTIL_SetSize( this, Vector(-8,-8,0), Vector(8,8,8) );
  92. }
  93. float GetHeatLevel() { return m_flHeatLevel; }
  94. virtual int UpdateTransmitState();
  95. void DrawDebugGeometryOverlays(void)
  96. {
  97. if (m_debugOverlays & OVERLAY_BBOX_BIT)
  98. {
  99. if ( m_lastDamage > gpGlobals->curtime && m_flHeatAbsorb > 0 )
  100. {
  101. NDebugOverlay::EntityBounds(this, 88, 255, 128, 0 ,0);
  102. char tempstr[512];
  103. Q_snprintf( tempstr, sizeof(tempstr), "Heat: %.1f", m_flHeatAbsorb );
  104. EntityText(1,tempstr, 0);
  105. }
  106. else if ( !IsBurning() )
  107. {
  108. NDebugOverlay::EntityBounds(this, 88, 88, 128, 0 ,0);
  109. }
  110. if ( IsBurning() )
  111. {
  112. Vector mins, maxs;
  113. if ( GetFireDimensions( &mins, &maxs ) )
  114. {
  115. NDebugOverlay::Box(GetAbsOrigin(), mins, maxs, 128, 0, 0, 10, 0);
  116. }
  117. }
  118. }
  119. BaseClass::DrawDebugGeometryOverlays();
  120. }
  121. void Disable();
  122. //Inputs
  123. void InputStartFire( inputdata_t &inputdata );
  124. void InputExtinguish( inputdata_t &inputdata );
  125. void InputExtinguishTemporary( inputdata_t &inputdata );
  126. void InputEnable( inputdata_t &inputdata );
  127. void InputDisable( inputdata_t &inputdata );
  128. protected:
  129. void Spread( void );
  130. void SpawnEffect( fireType_e type, float scale );
  131. CHandle<CBaseFire> m_hEffect;
  132. EHANDLE m_hOwner;
  133. int m_nFireType;
  134. float m_flFuel;
  135. float m_flDamageTime;
  136. float m_lastDamage;
  137. float m_flFireSize; // size of the fire in world units
  138. float m_flLastNavUpdateTime; // last time we told the nav mesh about ourselves
  139. float m_flHeatLevel; // Used as a "health" for the fire. > 0 means the fire is burning
  140. float m_flHeatAbsorb; // This much heat must be "absorbed" before it gets transferred to the flame size
  141. float m_flDamageScale;
  142. float m_flMaxHeat;
  143. float m_flLastHeatLevel;
  144. //NOTENOTE: Lifetime is an expression of the sum total of these amounts plus the global time when started
  145. float m_flAttackTime; //Amount of time to scale up
  146. bool m_bEnabled;
  147. bool m_bStartDisabled;
  148. bool m_bDidActivate;
  149. COutputEvent m_OnIgnited;
  150. COutputEvent m_OnExtinguished;
  151. DECLARE_DATADESC();
  152. };
  153. class CFireSphere : public IPartitionEnumerator
  154. {
  155. public:
  156. CFireSphere( CFire **pList, int listMax, bool onlyActiveFires, const Vector &origin, float radius );
  157. // This gets called by the enumeration methods with each element
  158. // that passes the test.
  159. virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity );
  160. int GetCount() { return m_count; }
  161. bool AddToList( CFire *pEntity );
  162. private:
  163. Vector m_origin;
  164. float m_radiusSqr;
  165. CFire **m_pList;
  166. int m_listMax;
  167. int m_count;
  168. bool m_onlyActiveFires;
  169. };
  170. CFireSphere::CFireSphere( CFire **pList, int listMax, bool onlyActiveFires, const Vector &origin, float radius )
  171. {
  172. m_pList = pList;
  173. m_listMax = listMax;
  174. m_count = 0;
  175. m_onlyActiveFires = onlyActiveFires;
  176. m_origin = origin;
  177. m_radiusSqr = radius * radius;
  178. }
  179. bool CFireSphere::AddToList( CFire *pFire )
  180. {
  181. if ( m_count >= m_listMax )
  182. return false;
  183. m_pList[m_count] = pFire;
  184. m_count++;
  185. return true;
  186. }
  187. IterationRetval_t CFireSphere::EnumElement( IHandleEntity *pHandleEntity )
  188. {
  189. CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
  190. if ( pEntity )
  191. {
  192. // UNDONE: Measure which of these is faster
  193. // CFire *pFire = dynamic_cast<CFire *>(pEntity);
  194. if ( !FClassnameIs( pEntity, "env_fire" ) )
  195. return ITERATION_CONTINUE;
  196. CFire *pFire = static_cast<CFire *>(pEntity);
  197. if ( pFire )
  198. {
  199. if ( !m_onlyActiveFires || pFire->IsBurning() )
  200. {
  201. if ( (m_origin - pFire->GetAbsOrigin()).LengthSqr() < m_radiusSqr )
  202. {
  203. if ( !AddToList( pFire ) )
  204. return ITERATION_STOP;
  205. }
  206. }
  207. }
  208. }
  209. return ITERATION_CONTINUE;
  210. }
  211. int FireSystem_GetFiresInSphere( CFire **pList, int listMax, bool onlyActiveFires, const Vector &origin, float radius )
  212. {
  213. CFireSphere sphereEnum( pList, listMax, onlyActiveFires, origin, radius );
  214. ::partition->EnumerateElementsInSphere( PARTITION_ENGINE_NON_STATIC_EDICTS, origin, radius, false, &sphereEnum );
  215. return sphereEnum.GetCount();
  216. }
  217. bool FireSystem_IsValidFirePosition( const Vector &position, float testRadius )
  218. {
  219. CFire *pList[1];
  220. int count = FireSystem_GetFiresInSphere( pList, ARRAYSIZE(pList), true, position, testRadius );
  221. if ( count > 0 )
  222. return false;
  223. return true;
  224. }
  225. //------------------------------------------------------------------------------
  226. // Purpose :
  227. // Input :
  228. // Output :
  229. //------------------------------------------------------------------------------
  230. bool FireSystem_IsFireInWall( Vector &position, fireType_e type )
  231. {
  232. // Don't check natural fire against walls
  233. if (type == FIRE_NATURAL)
  234. return false;
  235. trace_t tr;
  236. UTIL_TraceHull( position, position+Vector(0,0,0.1), FIRE_MINS,FIRE_MAXS,MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
  237. if (tr.fraction != 1.0 || tr.startsolid)
  238. {
  239. //NDebugOverlay::Box(position,FIRE_MINS,FIRE_MAXS,255,0,0,50,10);
  240. return true;
  241. }
  242. //NDebugOverlay::Box(position,FIRE_MINS,FIRE_MAXS,0,255,0,50,10);
  243. return false;
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Purpose: Determines whether or not a new fire may be placed at a given location
  247. // Input : &position - where we are trying to put the new fire
  248. // separationRadius - the maximum distance fires must be apart from one another
  249. // Output : Returns true on success, false on failure.
  250. //-----------------------------------------------------------------------------
  251. bool FireSystem_CanAddFire( Vector *position, float separationRadius, fireType_e type, int flags )
  252. {
  253. //See if we found a fire inside the sphere
  254. if ( !FireSystem_IsValidFirePosition( *position, separationRadius ) )
  255. return false;
  256. // Unless our fire is floating, make sure were not too high
  257. if (!(flags & SF_FIRE_DONT_DROP))
  258. {
  259. trace_t tr;
  260. Vector startpos = *position;
  261. Vector endpos = *position;
  262. startpos[2] += 1;
  263. endpos[2] -= FIRE_MAX_GROUND_OFFSET;
  264. UTIL_TraceLine( startpos, endpos, MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
  265. //See if we're floating too high
  266. if ( ( tr.allsolid ) || ( tr.startsolid) || ( tr.fraction == 1.0f ) )
  267. {
  268. return false;
  269. }
  270. //TODO: If we've hit an entity here, start it on fire
  271. CBaseEntity *pEntity = tr.m_pEnt;
  272. if ( ENTINDEX( pEntity->edict() ) != 0 )
  273. {
  274. return false;
  275. }
  276. }
  277. // Check if fire is in a wall, if so try shifting around a bit
  278. if (FireSystem_IsFireInWall( *position, type ))
  279. {
  280. Vector vTestPos = *position;
  281. vTestPos.x += 10;
  282. if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type ))
  283. {
  284. *position = vTestPos;
  285. return true;
  286. }
  287. vTestPos.y += 10;
  288. if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type ))
  289. {
  290. *position = vTestPos;
  291. return true;
  292. }
  293. vTestPos.y -= 20;
  294. if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type ))
  295. {
  296. *position = vTestPos;
  297. return true;
  298. }
  299. vTestPos.x -= 20;
  300. if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type ))
  301. {
  302. *position = vTestPos;
  303. return true;
  304. }
  305. return false;
  306. }
  307. //Able to add here
  308. return true;
  309. }
  310. //-----------------------------------------------------------------------------
  311. // Purpose: Starts a fire at a specified location
  312. // Input : &position - position to start the fire at
  313. // flags - any special modifiers
  314. //-----------------------------------------------------------------------------
  315. bool FireSystem_StartFire( const Vector &position, float fireHeight, float attack, float fuel, int flags, CBaseEntity *owner, fireType_e type )
  316. {
  317. VPROF_FIRE( "FireSystem_StartFire1" );
  318. Vector testPos = position;
  319. //Must be okay to add fire here
  320. if ( FireSystem_CanAddFire( &testPos, 16.0f, type, flags ) == false )
  321. {
  322. CFire *pFires[16];
  323. int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), true, position, 16.0f );
  324. for ( int i = 0; i < fireCount; i++ )
  325. {
  326. // add to this fire
  327. pFires[i]->AddHeat( fireHeight, false );
  328. }
  329. return false;
  330. }
  331. //Create a new fire entity
  332. CFire *fire = (CFire *) CreateEntityByName( "env_fire" );
  333. if ( fire == NULL )
  334. return false;
  335. //Spawn the fire
  336. // Fires not placed by a designer should be cleaned up automatically (not catch fire again)
  337. fire->AddSpawnFlags( SF_FIRE_DIE_PERMANENT );
  338. fire->Spawn();
  339. fire->Init( testPos, fireHeight, attack, fuel, flags, type );
  340. fire->Start();
  341. fire->SetOwner( owner );
  342. return true;
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Purpose: Starts a fire on a specified model.
  346. // Input : pEntity - The model entity to catch on fire.
  347. // fireHeight -
  348. // attack -
  349. // fuel -
  350. // flags -
  351. // owner -
  352. // type -
  353. // Output : Returns true on success, false on failure.
  354. //-----------------------------------------------------------------------------
  355. bool FireSystem_StartFire( CBaseAnimating *pEntity, float fireHeight, float attack, float fuel, int flags, CBaseEntity *owner, fireType_e type )
  356. {
  357. VPROF_FIRE( "FireSystem_StartFire2" );
  358. Vector position = pEntity->GetAbsOrigin();
  359. Vector testPos = position;
  360. // Make sure its a valid position for fire (not in a wall, etc)
  361. if ( FireSystem_CanAddFire( &testPos, 16.0f, type, flags ) == false )
  362. {
  363. // Contribute heat to all fires within 16 units of this fire.
  364. CFire *pFires[16];
  365. int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), true, position, 16.0f );
  366. for ( int i = 0; i < fireCount; i++ )
  367. {
  368. pFires[i]->AddHeat( fireHeight, false );
  369. }
  370. return false;
  371. }
  372. // Create a new fire entity
  373. CFire *fire = (CFire *) CreateEntityByName( "env_fire" );
  374. if ( fire == NULL )
  375. {
  376. return false;
  377. }
  378. // Spawn the fire.
  379. // Fires not placed by a designer should be cleaned up automatically (not catch fire again).
  380. fire->AddSpawnFlags( SF_FIRE_DIE_PERMANENT );
  381. fire->Spawn();
  382. fire->Init( testPos, fireHeight, attack, fuel, flags, type );
  383. fire->Start();
  384. fire->SetOwner( owner );
  385. return true;
  386. }
  387. void FireSystem_ExtinguishInRadius( const Vector &origin, float radius, float rate )
  388. {
  389. // UNDONE: pass this instead of percent
  390. float heat = (1-rate) * fire_extscale.GetFloat();
  391. CFire *pFires[32];
  392. int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), false, origin, radius );
  393. for ( int i = 0; i < fireCount; i++ )
  394. {
  395. pFires[i]->Extinguish( heat );
  396. }
  397. }
  398. //-----------------------------------------------------------------------------
  399. // Purpose:
  400. // Input : &origin -
  401. // radius -
  402. // heat -
  403. //-----------------------------------------------------------------------------
  404. void FireSystem_AddHeatInRadius( const Vector &origin, float radius, float heat )
  405. {
  406. VPROF_FIRE( "FireSystem_AddHeatInRadius" );
  407. CFire *pFires[32];
  408. int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), false, origin, radius );
  409. for ( int i = 0; i < fireCount; i++ )
  410. {
  411. pFires[i]->AddHeat( heat );
  412. }
  413. }
  414. //-----------------------------------------------------------------------------
  415. bool FireSystem_GetFireDamageDimensions( CBaseEntity *pEntity, Vector *pFireMins, Vector *pFireMaxs )
  416. {
  417. CFire *pFire = dynamic_cast<CFire *>(pEntity);
  418. if ( pFire && pFire->GetFireDimensions( pFireMins, pFireMaxs ) )
  419. {
  420. *pFireMins /= FIRE_SPREAD_DAMAGE_MULTIPLIER;
  421. *pFireMaxs /= FIRE_SPREAD_DAMAGE_MULTIPLIER;
  422. return true;
  423. }
  424. pFireMins->Init();
  425. pFireMaxs->Init();
  426. return false;
  427. }
  428. //==================================================
  429. // CFire
  430. //==================================================
  431. BEGIN_DATADESC( CFire )
  432. DEFINE_FIELD( m_hEffect, FIELD_EHANDLE ),
  433. DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
  434. DEFINE_KEYFIELD( m_nFireType, FIELD_INTEGER, "firetype" ),
  435. DEFINE_FIELD( m_flFuel, FIELD_FLOAT ),
  436. DEFINE_FIELD( m_flDamageTime, FIELD_TIME ),
  437. DEFINE_FIELD( m_lastDamage, FIELD_TIME ),
  438. DEFINE_KEYFIELD( m_flFireSize, FIELD_FLOAT, "firesize" ),
  439. DEFINE_KEYFIELD( m_flHeatLevel, FIELD_FLOAT, "ignitionpoint" ),
  440. DEFINE_FIELD( m_flHeatAbsorb, FIELD_FLOAT ),
  441. DEFINE_KEYFIELD( m_flDamageScale,FIELD_FLOAT, "damagescale" ),
  442. DEFINE_FIELD( m_flMaxHeat, FIELD_FLOAT ),
  443. //DEFINE_FIELD( m_flLastHeatLevel, FIELD_FLOAT ),
  444. DEFINE_KEYFIELD( m_flAttackTime, FIELD_FLOAT, "fireattack" ),
  445. DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
  446. DEFINE_KEYFIELD( m_bStartDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  447. DEFINE_FIELD( m_bDidActivate, FIELD_BOOLEAN ),
  448. DEFINE_FUNCTION( BurnThink ),
  449. DEFINE_FUNCTION( GoOutThink ),
  450. DEFINE_INPUTFUNC( FIELD_VOID, "StartFire", InputStartFire ),
  451. DEFINE_INPUTFUNC( FIELD_FLOAT, "Extinguish", InputExtinguish ),
  452. DEFINE_INPUTFUNC( FIELD_FLOAT, "ExtinguishTemporary", InputExtinguishTemporary ),
  453. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  454. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  455. DEFINE_OUTPUT( m_OnIgnited, "OnIgnited" ),
  456. DEFINE_OUTPUT( m_OnExtinguished, "OnExtinguished" ),
  457. END_DATADESC()
  458. LINK_ENTITY_TO_CLASS( env_fire, CFire );
  459. //==================================================
  460. // CFire
  461. //==================================================
  462. CFire::CFire( void )
  463. {
  464. m_flFuel = 0.0f;
  465. m_flAttackTime = 0.0f;
  466. m_flDamageTime = 0.0f;
  467. m_lastDamage = 0;
  468. m_nFireType = FIRE_NATURAL;
  469. //Spreading
  470. m_flHeatAbsorb = 8.0f;
  471. m_flHeatLevel = 0;
  472. // Must be in the constructor!
  473. AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
  474. }
  475. //-----------------------------------------------------------------------------
  476. // UpdateOnRemove
  477. //-----------------------------------------------------------------------------
  478. void CFire::UpdateOnRemove( void )
  479. {
  480. //Stop any looping sounds that might be playing
  481. StopSound( "Fire.Plasma" );
  482. DestroyEffect();
  483. // Chain at end to mimic destructor unwind order
  484. BaseClass::UpdateOnRemove();
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Purpose:
  488. //-----------------------------------------------------------------------------
  489. void CFire::Precache( void )
  490. {
  491. if ( m_nFireType == FIRE_NATURAL )
  492. {
  493. UTIL_PrecacheOther("_firesmoke");
  494. if ( m_spawnflags & SF_FIRE_SMOKELESS )
  495. {
  496. PrecacheParticleSystem( "env_fire_tiny" );
  497. PrecacheParticleSystem( "env_fire_small" );
  498. PrecacheParticleSystem( "env_fire_medium" );
  499. PrecacheParticleSystem( "env_fire_large" );
  500. }
  501. else
  502. {
  503. PrecacheParticleSystem( "env_fire_tiny_smoke" );
  504. PrecacheParticleSystem( "env_fire_small_smoke" );
  505. PrecacheParticleSystem( "env_fire_medium_smoke" );
  506. PrecacheParticleSystem( "env_fire_large_smoke" );
  507. }
  508. }
  509. if ( m_nFireType == FIRE_PLASMA )
  510. {
  511. UTIL_PrecacheOther("_plasma");
  512. }
  513. PrecacheScriptSound( "Fire.Plasma" );
  514. }
  515. //------------------------------------------------------------------------------
  516. // Purpose : Input handler for starting the fire.
  517. //------------------------------------------------------------------------------
  518. void CFire::InputStartFire( inputdata_t &inputdata )
  519. {
  520. if ( !m_bEnabled )
  521. return;
  522. StartFire();
  523. }
  524. void CFire::InputEnable( inputdata_t &inputdata )
  525. {
  526. m_bEnabled = true;
  527. }
  528. void CFire::InputDisable( inputdata_t &inputdata )
  529. {
  530. Disable();
  531. }
  532. void CFire::Disable()
  533. {
  534. m_bEnabled = false;
  535. if ( IsBurning() )
  536. {
  537. GoOut();
  538. }
  539. }
  540. //-----------------------------------------------------------------------------
  541. // Purpose:
  542. // Input : &inputdata -
  543. //-----------------------------------------------------------------------------
  544. void CFire::InputExtinguish( inputdata_t &inputdata )
  545. {
  546. m_spawnflags &= ~SF_FIRE_INFINITE;
  547. GoOutInSeconds( inputdata.value.Float() );
  548. }
  549. //-----------------------------------------------------------------------------
  550. // Purpose:
  551. // Input : &inputdata -
  552. //-----------------------------------------------------------------------------
  553. void CFire::InputExtinguishTemporary( inputdata_t &inputdata )
  554. {
  555. GoOutInSeconds( inputdata.value.Float() );
  556. }
  557. //-----------------------------------------------------------------------------
  558. // Purpose: Starts burning.
  559. //-----------------------------------------------------------------------------
  560. void CFire::StartFire( void )
  561. {
  562. if ( m_hEffect != NULL )
  563. return;
  564. // Trace down and start a fire there. Nothing fancy yet.
  565. Vector vFirePos;
  566. trace_t tr;
  567. if ( m_spawnflags & SF_FIRE_DONT_DROP )
  568. {
  569. vFirePos = GetAbsOrigin();
  570. }
  571. else
  572. {
  573. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1024 ), MASK_FIRE_SOLID, this, COLLISION_GROUP_NONE, &tr );
  574. vFirePos = tr.endpos;
  575. }
  576. int spawnflags = m_spawnflags;
  577. m_spawnflags |= SF_FIRE_START_ON;
  578. Init( vFirePos, m_flFireSize, m_flAttackTime, GetHealth(), m_spawnflags, (fireType_e) m_nFireType );
  579. Start();
  580. m_spawnflags = spawnflags;
  581. }
  582. //-----------------------------------------------------------------------------
  583. // Purpose:
  584. //-----------------------------------------------------------------------------
  585. void CFire::Spawn( void )
  586. {
  587. BaseClass::Spawn();
  588. Precache();
  589. m_takedamage = DAMAGE_NO;
  590. SetSolid( SOLID_NONE );
  591. AddEffects( EF_NODRAW );
  592. SetToOutSize();
  593. // set up the ignition point
  594. m_flHeatAbsorb = m_flHeatLevel * 0.05;
  595. m_flHeatLevel = 0;
  596. Init( GetAbsOrigin(), m_flFireSize, m_flAttackTime, m_flFuel, m_spawnflags, m_nFireType );
  597. if( m_bStartDisabled )
  598. {
  599. Disable();
  600. }
  601. else
  602. {
  603. m_bEnabled = true;
  604. }
  605. }
  606. int CFire::UpdateTransmitState()
  607. {
  608. // Don't want to be FL_EDICT_DONTSEND because our fire entity may make us transmit.
  609. return SetTransmitState( FL_EDICT_ALWAYS );
  610. }
  611. //-----------------------------------------------------------------------------
  612. // Purpose:
  613. //-----------------------------------------------------------------------------
  614. void CFire::Activate( void )
  615. {
  616. BaseClass::Activate();
  617. //See if we should start active
  618. if ( !m_bDidActivate && ( m_spawnflags & SF_FIRE_START_ON ) )
  619. {
  620. m_flHeatLevel = m_flMaxHeat;
  621. StartFire();
  622. }
  623. m_bDidActivate = true;
  624. }
  625. //-----------------------------------------------------------------------------
  626. // Purpose:
  627. //-----------------------------------------------------------------------------
  628. void CFire::SpawnEffect( fireType_e type, float scale )
  629. {
  630. CBaseFire *pEffect = NULL;
  631. switch ( type )
  632. {
  633. default:
  634. case FIRE_NATURAL:
  635. {
  636. CFireSmoke *fireSmoke = (CFireSmoke *) CreateEntityByName( "_firesmoke" );
  637. fireSmoke->EnableSmoke( ( m_spawnflags & SF_FIRE_SMOKELESS )==false );
  638. fireSmoke->EnableGlow( ( m_spawnflags & SF_FIRE_NO_GLOW )==false );
  639. fireSmoke->EnableVisibleFromAbove( ( m_spawnflags & SF_FIRE_VISIBLE_FROM_ABOVE )!=false );
  640. pEffect = fireSmoke;
  641. m_nFireType = FIRE_NATURAL;
  642. m_takedamage = DAMAGE_YES;
  643. }
  644. break;
  645. case FIRE_PLASMA:
  646. {
  647. CPlasma *plasma = (CPlasma *) CreateEntityByName( "_plasma" );
  648. plasma->EnableSmoke( true );
  649. pEffect = plasma;
  650. m_nFireType = FIRE_PLASMA;
  651. m_takedamage = DAMAGE_YES;
  652. // Start burn sound
  653. EmitSound( "Fire.Plasma" );
  654. }
  655. break;
  656. }
  657. UTIL_SetOrigin( pEffect, GetAbsOrigin() );
  658. pEffect->Spawn();
  659. pEffect->SetParent( this );
  660. pEffect->Scale( m_flFireSize, m_flFireSize, 0 );
  661. //Start it going
  662. pEffect->Enable( ( m_spawnflags & SF_FIRE_START_ON ) );
  663. m_hEffect = pEffect;
  664. }
  665. //-----------------------------------------------------------------------------
  666. // Purpose: Spawn and initialize the fire
  667. // Input : &position - where the fire resides
  668. // lifetime -
  669. //-----------------------------------------------------------------------------
  670. void CFire::Init( const Vector &position, float scale, float attackTime, float fuel, int flags, int fireType )
  671. {
  672. m_flAttackTime = attackTime;
  673. m_spawnflags = flags;
  674. m_nFireType = fireType;
  675. if ( flags & SF_FIRE_INFINITE )
  676. {
  677. fuel = 0;
  678. }
  679. m_flFuel = fuel;
  680. if ( m_flFuel )
  681. {
  682. m_spawnflags |= SF_FIRE_DIE_PERMANENT;
  683. }
  684. Vector localOrigin = position;
  685. if ( GetMoveParent() )
  686. {
  687. EntityMatrix parentMatrix;
  688. parentMatrix.InitFromEntity( GetMoveParent() );
  689. localOrigin = parentMatrix.WorldToLocal( position );
  690. }
  691. UTIL_SetOrigin( this, localOrigin );
  692. SetSolid( SOLID_NONE );
  693. m_flFireSize = scale;
  694. m_flMaxHeat = FIRE_MAX_HEAT_LEVEL * FIRE_SCALE_FROM_SIZE(scale);
  695. //See if we should start on
  696. if ( m_spawnflags & SF_FIRE_START_FULL )
  697. {
  698. m_flHeatLevel = m_flMaxHeat;
  699. }
  700. m_flLastHeatLevel = 0;
  701. }
  702. void CFire::Start()
  703. {
  704. float boxWidth = (m_flFireSize * (FIRE_WIDTH/FIRE_HEIGHT))*0.5f;
  705. UTIL_SetSize(this, Vector(-boxWidth,-boxWidth,0),Vector(boxWidth,boxWidth,m_flFireSize));
  706. //Spawn the client-side effect
  707. SpawnEffect( (fireType_e)m_nFireType, FIRE_SCALE_FROM_SIZE(m_flFireSize) );
  708. m_OnIgnited.FireOutput( this, this );
  709. SetThink( &CFire::BurnThink );
  710. m_flLastNavUpdateTime = 0.0f;
  711. m_flDamageTime = 0;
  712. // think right now
  713. BurnThink();
  714. }
  715. //-----------------------------------------------------------------------------
  716. // Purpose: Determines whether or not the fire is still active
  717. // Output : Returns true on success, false on failure.
  718. //-----------------------------------------------------------------------------
  719. bool CFire::IsBurning( void ) const
  720. {
  721. if ( m_flHeatLevel > 0 )
  722. return true;
  723. return false;
  724. }
  725. //-----------------------------------------------------------------------------
  726. // Purpose: Get the damage box of the fire
  727. //-----------------------------------------------------------------------------
  728. bool CFire::GetFireDimensions( Vector *pFireMins, Vector *pFireMaxs )
  729. {
  730. if ( m_flHeatLevel <= 0 )
  731. {
  732. pFireMins->Init();
  733. pFireMaxs->Init();
  734. return false;
  735. }
  736. float scale = m_flHeatLevel / m_flMaxHeat;
  737. float damageRadius = scale * m_flFireSize * FIRE_WIDTH / FIRE_HEIGHT * 0.5;
  738. damageRadius *= FIRE_SPREAD_DAMAGE_MULTIPLIER; //FIXME: Trying slightly larger radius for burning
  739. if ( damageRadius < 16 )
  740. {
  741. damageRadius = 16;
  742. }
  743. pFireMins->Init(-damageRadius,-damageRadius,0);
  744. pFireMaxs->Init(damageRadius,damageRadius,m_flFireSize*scale);
  745. return true;
  746. }
  747. //-----------------------------------------------------------------------------
  748. // Purpose: Update the fire and its children
  749. //-----------------------------------------------------------------------------
  750. void CFire::Update( float simTime )
  751. {
  752. VPROF_FIRE( "CFire::Update" );
  753. if ( m_flFuel != 0 )
  754. {
  755. m_flFuel -= simTime;
  756. if ( m_flFuel <= 0 )
  757. {
  758. GoOutInSeconds( 1 );
  759. return;
  760. }
  761. }
  762. float strength = m_flHeatLevel / FIRE_MAX_HEAT_LEVEL;
  763. if ( m_flHeatLevel != m_flLastHeatLevel )
  764. {
  765. m_flLastHeatLevel = m_flHeatLevel;
  766. // Make the effect the appropriate size given the heat level
  767. m_hEffect->Scale( strength, 0.5f );
  768. }
  769. // add heat to myself (grow)
  770. float addedHeat = (m_flAttackTime > 0) ? m_flMaxHeat / m_flAttackTime : m_flMaxHeat;
  771. addedHeat *= simTime * fire_growthrate.GetFloat();
  772. AddHeat( addedHeat, true );
  773. // add heat to nearby fires
  774. float outputHeat = strength * m_flHeatLevel;
  775. Vector fireMins;
  776. Vector fireMaxs;
  777. Vector fireEntityDamageMins;
  778. Vector fireEntityDamageMaxs;
  779. GetFireDimensions( &fireMins, &fireMaxs );
  780. if ( FIRE_SPREAD_DAMAGE_MULTIPLIER != 1.0 ) // if set to 1.0, optimizer will remove this code
  781. {
  782. fireEntityDamageMins = fireMins / FIRE_SPREAD_DAMAGE_MULTIPLIER;
  783. fireEntityDamageMaxs = fireMaxs / FIRE_SPREAD_DAMAGE_MULTIPLIER;
  784. }
  785. //NDebugOverlay::Box( GetAbsOrigin(), fireMins, fireMaxs, 255, 255, 255, 0, fire_dmginterval.GetFloat() );
  786. fireMins += GetAbsOrigin();
  787. fireMaxs += GetAbsOrigin();
  788. if ( FIRE_SPREAD_DAMAGE_MULTIPLIER != 1.0 )
  789. {
  790. fireEntityDamageMins += GetAbsOrigin();
  791. fireEntityDamageMaxs += GetAbsOrigin();
  792. }
  793. CBaseEntity *pNearby[256];
  794. CFire *pFires[16];
  795. int nearbyCount = UTIL_EntitiesInBox( pNearby, ARRAYSIZE(pNearby), fireMins, fireMaxs, 0 );
  796. int fireCount = 0;
  797. int i;
  798. // is it time to do damage?
  799. bool damage = false;
  800. int outputDamage = 0;
  801. if ( m_flDamageTime <= gpGlobals->curtime )
  802. {
  803. m_flDamageTime = gpGlobals->curtime + fire_dmginterval.GetFloat();
  804. outputDamage = (fire_dmgbase.GetFloat() + outputHeat * fire_dmgscale.GetFloat() * m_flDamageScale) * fire_dmginterval.GetFloat();
  805. if ( outputDamage )
  806. {
  807. damage = true;
  808. }
  809. }
  810. int damageFlags = (m_nFireType == FIRE_NATURAL) ? DMG_BURN : DMG_PLASMA;
  811. for ( i = 0; i < nearbyCount; i++ )
  812. {
  813. CBaseEntity *pOther = pNearby[i];
  814. if ( pOther == this )
  815. continue;
  816. if ( FClassnameIs( pOther, "env_fire" ) )
  817. {
  818. if ( fireCount < ARRAYSIZE(pFires) )
  819. {
  820. pFires[fireCount] = (CFire *)pOther;
  821. fireCount++;
  822. }
  823. continue;
  824. }
  825. if ( pOther->m_takedamage == DAMAGE_NO )
  826. continue;
  827. if ( !damage )
  828. continue;
  829. if ( FIRE_SPREAD_DAMAGE_MULTIPLIER != 1.0 || ( pOther->CollisionProp()->GetSurroundingBoundsType() == USE_ROTATION_EXPANDED_SEQUENCE_BOUNDS ) )
  830. {
  831. // Do a tighter bounds for players and
  832. Vector otherMins, otherMaxs;
  833. pOther->CollisionProp()->WorldSpaceAABB( &otherMins, &otherMaxs );
  834. bool bDoDamage = IsBoxIntersectingBox( otherMins, otherMaxs, fireEntityDamageMins, fireEntityDamageMaxs );
  835. if ( !bDoDamage )
  836. continue;
  837. }
  838. // Make sure can actually see entity (don't damage through walls)
  839. trace_t tr;
  840. UTIL_TraceLine( this->WorldSpaceCenter(), pOther->WorldSpaceCenter(), MASK_FIRE_SOLID, pOther, COLLISION_GROUP_NONE, &tr );
  841. if (tr.fraction == 1.0 && !tr.startsolid)
  842. {
  843. pOther->TakeDamage( CTakeDamageInfo( this, this, outputDamage, damageFlags ) );
  844. }
  845. }
  846. outputHeat *= fire_heatscale.GetFloat() * simTime;
  847. if ( fireCount > 0 )
  848. {
  849. outputHeat /= fireCount;
  850. for ( i = 0; i < fireCount; i++ )
  851. {
  852. pFires[i]->AddHeat( outputHeat, false );
  853. }
  854. }
  855. }
  856. // Destroy any effect I have
  857. void CFire::DestroyEffect()
  858. {
  859. CBaseFire *pEffect = m_hEffect;
  860. if ( pEffect != NULL )
  861. {
  862. //disable the graphics and remove the entity
  863. pEffect->Enable( false );
  864. UTIL_Remove( pEffect );
  865. }
  866. }
  867. //-----------------------------------------------------------------------------
  868. // Purpose: Think
  869. //-----------------------------------------------------------------------------
  870. void CFire::BurnThink( void )
  871. {
  872. SetNextThink( gpGlobals->curtime + FIRE_THINK_INTERVAL );
  873. Update( FIRE_THINK_INTERVAL );
  874. #if 0
  875. // Noone is currently using the NavMesh!
  876. // only need to update the nav mesh infrequently
  877. const float NavUpdateDelta = 5.0f;
  878. if ( gpGlobals->curtime > m_flLastNavUpdateTime + NavUpdateDelta )
  879. {
  880. m_flLastNavUpdateTime = gpGlobals->curtime;
  881. // mark overlapping nav areas as "damaging"
  882. NavAreaCollector overlap;
  883. Extent extent;
  884. CollisionProp()->WorldSpaceAABB( &extent.lo, &extent.hi );
  885. extent.lo.z -= HumanHeight;
  886. // bloat extents enough to ensure any non-damaging area is actually safe
  887. // bloat in Z as well to catch nav areas that may be slightly above/below ground
  888. const float DangerBloat = 32.0f;
  889. Vector dangerBloat( DangerBloat, DangerBloat, DangerBloat );
  890. extent.lo -= dangerBloat;
  891. extent.hi += dangerBloat;
  892. TheNavMesh->ForAllAreasOverlappingExtent( overlap, extent );
  893. FOR_EACH_VEC( overlap.m_area, it )
  894. {
  895. CNavArea *area = overlap.m_area[ it ];
  896. area->MarkAsDamaging( NavUpdateDelta + 1.0f );
  897. }
  898. }
  899. #endif
  900. }
  901. void CFire::GoOutThink()
  902. {
  903. GoOut();
  904. }
  905. void CFire::GoOutInSeconds( float seconds )
  906. {
  907. Scale( 0.0f, seconds );
  908. SetThink( &CFire::GoOutThink );
  909. SetNextThink( gpGlobals->curtime + seconds );
  910. }
  911. //------------------------------------------------------------------------------
  912. // Purpose : Blasts of significant size blow out fires that take damage
  913. // Input :
  914. // Output :
  915. //------------------------------------------------------------------------------
  916. int CFire::OnTakeDamage( const CTakeDamageInfo &info )
  917. {
  918. return 0;
  919. }
  920. void CFire::AddHeat( float heat, bool selfHeat )
  921. {
  922. if ( m_bEnabled )
  923. {
  924. if ( !selfHeat )
  925. {
  926. if ( IsBurning() )
  927. {
  928. // scale back the incoming heat from surrounding fires
  929. // if I've already ignited
  930. heat *= fire_incomingheatscale.GetFloat();
  931. }
  932. }
  933. m_lastDamage = gpGlobals->curtime + 0.5;
  934. bool start = m_flHeatLevel <= 0 ? true : false;
  935. if ( m_flHeatAbsorb > 0 )
  936. {
  937. float absorbDamage = heat * fire_absorbrate.GetFloat();
  938. if ( absorbDamage > m_flHeatAbsorb )
  939. {
  940. heat -= m_flHeatAbsorb / fire_absorbrate.GetFloat();
  941. m_flHeatAbsorb = 0;
  942. }
  943. else
  944. {
  945. m_flHeatAbsorb -= absorbDamage;
  946. heat = 0;
  947. }
  948. }
  949. m_flHeatLevel += heat;
  950. if ( start && m_flHeatLevel > 0 && m_hEffect == NULL )
  951. {
  952. StartFire();
  953. }
  954. if ( m_flHeatLevel > m_flMaxHeat )
  955. m_flHeatLevel = m_flMaxHeat;
  956. }
  957. }
  958. //-----------------------------------------------------------------------------
  959. // Purpose:
  960. // Input : end -
  961. // time -
  962. //-----------------------------------------------------------------------------
  963. void CFire::Scale( float end, float time )
  964. {
  965. CBaseFire *pEffect = m_hEffect;
  966. if ( pEffect )
  967. {
  968. pEffect->Scale( end, time );
  969. }
  970. }
  971. //-----------------------------------------------------------------------------
  972. // Purpose:
  973. // Input : time -
  974. //-----------------------------------------------------------------------------
  975. void CFire::Extinguish( float heat )
  976. {
  977. if ( !m_bEnabled )
  978. return;
  979. m_lastDamage = gpGlobals->curtime + 0.5;
  980. bool out = m_flHeatLevel > 0 ? true : false;
  981. m_flHeatLevel -= heat;
  982. m_flHeatAbsorb += fire_extabsorb.GetFloat() * heat;
  983. if ( m_flHeatAbsorb > fire_maxabsorb.GetFloat() )
  984. {
  985. m_flHeatAbsorb = fire_maxabsorb.GetFloat();
  986. }
  987. // drift toward the average attack time after being sprayed
  988. // some fires are heavily scripted so their attack looks weird
  989. // once interacted with. Basically, this blends out the scripting
  990. // as the fire is sprayed with the extinguisher.
  991. float averageAttackTime = m_flMaxHeat * (FIRE_NORMAL_ATTACK_TIME/FIRE_MAX_HEAT_LEVEL);
  992. m_flAttackTime = Approach( averageAttackTime, m_flAttackTime, 2 * gpGlobals->frametime );
  993. if ( m_flHeatLevel <= 0 )
  994. {
  995. m_flHeatLevel = 0;
  996. if ( out )
  997. {
  998. GoOut();
  999. }
  1000. }
  1001. }
  1002. bool CFire::GoOut()
  1003. {
  1004. //Signal death
  1005. m_OnExtinguished.FireOutput( this, this );
  1006. DestroyEffect();
  1007. m_flHeatLevel -= 20;
  1008. if ( m_flHeatLevel > 0 )
  1009. m_flHeatLevel = 0;
  1010. m_flLastHeatLevel = m_flHeatLevel;
  1011. SetThink(NULL);
  1012. SetNextThink( TICK_NEVER_THINK );
  1013. if ( m_spawnflags & SF_FIRE_DIE_PERMANENT )
  1014. {
  1015. UTIL_Remove( this );
  1016. return true;
  1017. }
  1018. SetToOutSize();
  1019. return false;
  1020. }
  1021. //==================================================
  1022. // CEnvFireSource is a source of heat that the player
  1023. // cannot put out
  1024. //==================================================
  1025. #define FIRESOURCE_THINK_TIME 0.25 // seconds to
  1026. #define SF_FIRESOURCE_START_ON 0x0001
  1027. class CEnvFireSource : public CBaseEntity
  1028. {
  1029. DECLARE_CLASS( CEnvFireSource, CBaseEntity );
  1030. public:
  1031. void Spawn();
  1032. void Think();
  1033. void TurnOn();
  1034. void TurnOff();
  1035. void InputEnable( inputdata_t &inputdata );
  1036. void InputDisable( inputdata_t &inputdata );
  1037. DECLARE_DATADESC();
  1038. private:
  1039. bool m_bEnabled;
  1040. float m_radius;
  1041. float m_damage;
  1042. };
  1043. BEGIN_DATADESC( CEnvFireSource )
  1044. DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
  1045. DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "fireradius" ),
  1046. DEFINE_KEYFIELD( m_damage,FIELD_FLOAT, "firedamage" ),
  1047. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  1048. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  1049. END_DATADESC()
  1050. LINK_ENTITY_TO_CLASS( env_firesource, CEnvFireSource );
  1051. void CEnvFireSource::Spawn()
  1052. {
  1053. if ( m_spawnflags & SF_FIRESOURCE_START_ON )
  1054. {
  1055. TurnOn();
  1056. }
  1057. else
  1058. {
  1059. TurnOff();
  1060. }
  1061. }
  1062. void CEnvFireSource::Think()
  1063. {
  1064. if ( !m_bEnabled )
  1065. return;
  1066. SetNextThink( gpGlobals->curtime + FIRESOURCE_THINK_TIME );
  1067. CFire *pFires[128];
  1068. int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), false, GetAbsOrigin(), m_radius );
  1069. for ( int i = 0; i < fireCount; i++ )
  1070. {
  1071. pFires[i]->AddHeat( m_damage * FIRESOURCE_THINK_TIME );
  1072. }
  1073. }
  1074. void CEnvFireSource::TurnOn()
  1075. {
  1076. if ( m_bEnabled )
  1077. return;
  1078. m_bEnabled = true;
  1079. SetNextThink( gpGlobals->curtime );
  1080. }
  1081. void CEnvFireSource::TurnOff()
  1082. {
  1083. if ( !m_bEnabled )
  1084. return;
  1085. m_bEnabled = false;
  1086. SetNextThink( TICK_NEVER_THINK );
  1087. }
  1088. void CEnvFireSource::InputEnable( inputdata_t &inputdata )
  1089. {
  1090. TurnOn();
  1091. }
  1092. void CEnvFireSource::InputDisable( inputdata_t &inputdata )
  1093. {
  1094. TurnOff();
  1095. }
  1096. //==================================================
  1097. // CEnvFireSensor detects changes in heat
  1098. //==================================================
  1099. #define SF_FIRESENSOR_START_ON 1
  1100. class CEnvFireSensor : public CBaseEntity
  1101. {
  1102. DECLARE_CLASS( CEnvFireSensor, CBaseEntity );
  1103. public:
  1104. void Spawn();
  1105. void Think();
  1106. void TurnOn();
  1107. void TurnOff();
  1108. void InputEnable( inputdata_t &inputdata );
  1109. void InputDisable( inputdata_t &inputdata );
  1110. DECLARE_DATADESC();
  1111. private:
  1112. bool m_bEnabled;
  1113. bool m_bHeatAtLevel;
  1114. float m_radius;
  1115. float m_targetLevel;
  1116. float m_targetTime;
  1117. float m_levelTime;
  1118. COutputEvent m_OnHeatLevelStart;
  1119. COutputEvent m_OnHeatLevelEnd;
  1120. };
  1121. BEGIN_DATADESC( CEnvFireSensor )
  1122. DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "fireradius" ),
  1123. DEFINE_KEYFIELD( m_targetLevel, FIELD_FLOAT, "heatlevel" ),
  1124. DEFINE_KEYFIELD( m_targetTime, FIELD_FLOAT, "heattime" ),
  1125. DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
  1126. DEFINE_FIELD( m_bHeatAtLevel, FIELD_BOOLEAN ),
  1127. DEFINE_FIELD( m_levelTime, FIELD_FLOAT ),
  1128. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  1129. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  1130. DEFINE_OUTPUT( m_OnHeatLevelStart, "OnHeatLevelStart"),
  1131. DEFINE_OUTPUT( m_OnHeatLevelEnd, "OnHeatLevelEnd"),
  1132. END_DATADESC()
  1133. LINK_ENTITY_TO_CLASS( env_firesensor, CEnvFireSensor );
  1134. void CEnvFireSensor::Spawn()
  1135. {
  1136. if ( m_spawnflags & SF_FIRESENSOR_START_ON )
  1137. {
  1138. TurnOn();
  1139. }
  1140. else
  1141. {
  1142. TurnOff();
  1143. }
  1144. }
  1145. void CEnvFireSensor::Think()
  1146. {
  1147. if ( !m_bEnabled )
  1148. return;
  1149. float time = m_targetTime * 0.25;
  1150. if ( time < 0.1 )
  1151. {
  1152. time = 0.1;
  1153. }
  1154. SetNextThink( gpGlobals->curtime + time );
  1155. float heat = 0;
  1156. CFire *pFires[128];
  1157. int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), true, GetAbsOrigin(), m_radius );
  1158. for ( int i = 0; i < fireCount; i++ )
  1159. {
  1160. heat += pFires[i]->GetHeatLevel();
  1161. }
  1162. if ( heat >= m_targetLevel )
  1163. {
  1164. m_levelTime += time;
  1165. if ( m_levelTime >= m_targetTime )
  1166. {
  1167. if ( !m_bHeatAtLevel )
  1168. {
  1169. m_bHeatAtLevel = true;
  1170. m_OnHeatLevelStart.FireOutput( this, this );
  1171. }
  1172. }
  1173. }
  1174. else
  1175. {
  1176. m_levelTime = 0;
  1177. if ( m_bHeatAtLevel )
  1178. {
  1179. m_bHeatAtLevel = false;
  1180. m_OnHeatLevelEnd.FireOutput( this, this );
  1181. }
  1182. }
  1183. }
  1184. void CEnvFireSensor::TurnOn()
  1185. {
  1186. if ( m_bEnabled )
  1187. return;
  1188. m_bEnabled = true;
  1189. SetNextThink( gpGlobals->curtime );
  1190. m_bHeatAtLevel = false;
  1191. m_levelTime = 0;
  1192. }
  1193. void CEnvFireSensor::TurnOff()
  1194. {
  1195. if ( !m_bEnabled )
  1196. return;
  1197. m_bEnabled = false;
  1198. SetNextThink( TICK_NEVER_THINK );
  1199. if ( m_bHeatAtLevel )
  1200. {
  1201. m_bHeatAtLevel = false;
  1202. m_OnHeatLevelEnd.FireOutput( this, this );
  1203. }
  1204. }
  1205. void CEnvFireSensor::InputEnable( inputdata_t &inputdata )
  1206. {
  1207. TurnOn();
  1208. }
  1209. void CEnvFireSensor::InputDisable( inputdata_t &inputdata )
  1210. {
  1211. TurnOff();
  1212. }
  1213. //-----------------------------------------------------------------------------
  1214. // Purpose: Draw any debug text overlays
  1215. // Output : Current text offset from the top
  1216. //-----------------------------------------------------------------------------
  1217. int CFire::DrawDebugTextOverlays( void )
  1218. {
  1219. int text_offset = BaseClass::DrawDebugTextOverlays();
  1220. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  1221. {
  1222. char tempstr[512];
  1223. // print flame size
  1224. Q_snprintf(tempstr,sizeof(tempstr)," size: %f", m_flFireSize);
  1225. EntityText(text_offset,tempstr,0);
  1226. text_offset++;
  1227. }
  1228. return text_offset;
  1229. }