diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/fire.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/game/server/fire.cpp')
| -rw-r--r-- | sp/src/game/server/fire.cpp | 2926 |
1 files changed, 1463 insertions, 1463 deletions
diff --git a/sp/src/game/server/fire.cpp b/sp/src/game/server/fire.cpp index 75a11399..ee5fd24c 100644 --- a/sp/src/game/server/fire.cpp +++ b/sp/src/game/server/fire.cpp @@ -1,1463 +1,1463 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-//---------------------------------------------------------
-//---------------------------------------------------------
-#include "cbase.h"
-#include "decals.h"
-#include "fire.h"
-#include "entitylist.h"
-#include "basecombatcharacter.h"
-#include "ndebugoverlay.h"
-#include "engine/IEngineSound.h"
-#include "ispatialpartition.h"
-#include "collisionutils.h"
-#include "tier0/vprof.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-
-/********************************************************************
- NOTE: if you are looking at this file becase you would like flares
- to be considered as fires (and thereby trigger gas traps), be aware
- that the env_flare class is actually found in weapon_flaregun.cpp
- and is really a repurposed piece of ammunition. (env_flare isn't the
- rod-like safety flare prop, but rather the bit of flame on the end.)
-
- You will have some difficulty making it work here, because CFlare
- does not inherit from CFire and will thus not be enumerated by
- CFireSphere::EnumElement(). In order to have flares be detected and
- used by this system, you will need to promote certain member functions
- of CFire into an interface class from which both CFire and CFlare
- inherit. You will also need to modify CFireSphere::EnumElement so that
- it properly disambiguates between fires and flares.
-
- For some partial work towards this end, see changelist 192474.
-
- ********************************************************************/
-
-
-
-
-#define FIRE_HEIGHT 256.0f
-#define FIRE_SCALE_FROM_SIZE(firesize) (firesize * (1/FIRE_HEIGHT))
-
-#define FIRE_MAX_GROUND_OFFSET 24.0f //(2 feet)
-
-#define DEFAULT_ATTACK_TIME 4.0f
-#define DEFAULT_DECAY_TIME 8.0f
-
-// UNDONE: This shouldn't be constant but depend on specific fire
-#define FIRE_WIDTH 128
-#define FIRE_MINS Vector(-20,-20,0 ) // Sould be FIRE_WIDTH in size
-#define FIRE_MAXS Vector( 20, 20,20) // Sould be FIRE_WIDTH in size
-#define FIRE_SPREAD_DAMAGE_MULTIPLIER 2.0
-
-#define FIRE_MAX_HEAT_LEVEL 64.0f
-#define FIRE_NORMAL_ATTACK_TIME 20.0f
-#define FIRE_THINK_INTERVAL 0.1
-
-ConVar fire_maxabsorb( "fire_maxabsorb", "50" );
-ConVar fire_absorbrate( "fire_absorbrate", "3" );
-ConVar fire_extscale("fire_extscale", "12");
-ConVar fire_extabsorb("fire_extabsorb", "5");
-ConVar fire_heatscale( "fire_heatscale", "1.0" );
-ConVar fire_incomingheatscale( "fire_incomingheatscale", "0.1" );
-ConVar fire_dmgscale( "fire_dmgscale", "0.1" );
-ConVar fire_dmgbase( "fire_dmgbase", "1" );
-ConVar fire_growthrate( "fire_growthrate", "1.0" );
-ConVar fire_dmginterval( "fire_dmginterval", "1.0" );
-
-#define VPROF_FIRE(s) VPROF( s )
-
-class CFire : public CBaseEntity
-{
-public:
- DECLARE_CLASS( CFire, CBaseEntity );
-
- int DrawDebugTextOverlays(void);
-
- CFire( void );
-
- virtual void UpdateOnRemove( void );
-
- void Precache( void );
- void Init( const Vector &position, float scale, float attackTime, float fuel, int flags, int fireType );
- bool GoOut();
-
- void BurnThink();
- void GoOutThink();
- void GoOutInSeconds( float seconds );
-
- void SetOwner( CBaseEntity *hOwner ) { m_hOwner = hOwner; }
-
- void Scale( float end, float time );
- void AddHeat( float heat, bool selfHeat = false );
- int OnTakeDamage( const CTakeDamageInfo &info );
-
- bool IsBurning( void ) const;
-
- bool GetFireDimensions( Vector *pFireMins, Vector *pFireMaxs );
-
- void Extinguish( float heat );
- void DestroyEffect();
-
- virtual void Update( float simTime );
-
- void Spawn( void );
- void Activate( void );
- void StartFire( void );
- void Start();
- void SetToOutSize()
- {
- UTIL_SetSize( this, Vector(-8,-8,0), Vector(8,8,8) );
- }
-
- float GetHeatLevel() { return m_flHeatLevel; }
-
- virtual int UpdateTransmitState();
-
- void DrawDebugGeometryOverlays(void)
- {
- if (m_debugOverlays & OVERLAY_BBOX_BIT)
- {
- if ( m_lastDamage > gpGlobals->curtime && m_flHeatAbsorb > 0 )
- {
- NDebugOverlay::EntityBounds(this, 88, 255, 128, 0 ,0);
- char tempstr[512];
- Q_snprintf( tempstr, sizeof(tempstr), "Heat: %.1f", m_flHeatAbsorb );
- EntityText(1,tempstr, 0);
- }
- else if ( !IsBurning() )
- {
- NDebugOverlay::EntityBounds(this, 88, 88, 128, 0 ,0);
- }
-
- if ( IsBurning() )
- {
- Vector mins, maxs;
- if ( GetFireDimensions( &mins, &maxs ) )
- {
- NDebugOverlay::Box(GetAbsOrigin(), mins, maxs, 128, 0, 0, 10, 0);
- }
- }
-
-
- }
- BaseClass::DrawDebugGeometryOverlays();
- }
-
- void Disable();
-
- //Inputs
- void InputStartFire( inputdata_t &inputdata );
- void InputExtinguish( inputdata_t &inputdata );
- void InputExtinguishTemporary( inputdata_t &inputdata );
- void InputEnable( inputdata_t &inputdata );
- void InputDisable( inputdata_t &inputdata );
-
-protected:
-
- void Spread( void );
- void SpawnEffect( fireType_e type, float scale );
-
- CHandle<CBaseFire> m_hEffect;
- EHANDLE m_hOwner;
-
- int m_nFireType;
-
- float m_flFuel;
- float m_flDamageTime;
- float m_lastDamage;
- float m_flFireSize; // size of the fire in world units
-
- float m_flHeatLevel; // Used as a "health" for the fire. > 0 means the fire is burning
- float m_flHeatAbsorb; // This much heat must be "absorbed" before it gets transferred to the flame size
- float m_flDamageScale;
-
- float m_flMaxHeat;
- float m_flLastHeatLevel;
-
- //NOTENOTE: Lifetime is an expression of the sum total of these amounts plus the global time when started
- float m_flAttackTime; //Amount of time to scale up
-
- bool m_bEnabled;
- bool m_bStartDisabled;
- bool m_bDidActivate;
-
-
- COutputEvent m_OnIgnited;
- COutputEvent m_OnExtinguished;
-
- DECLARE_DATADESC();
-};
-
-class CFireSphere : public IPartitionEnumerator
-{
-public:
- CFireSphere( CFire **pList, int listMax, bool onlyActiveFires, const Vector &origin, float radius );
- // This gets called by the enumeration methods with each element
- // that passes the test.
- virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity );
-
- int GetCount() { return m_count; }
- bool AddToList( CFire *pEntity );
-
-private:
- Vector m_origin;
- float m_radiusSqr;
- CFire **m_pList;
- int m_listMax;
- int m_count;
- bool m_onlyActiveFires;
-};
-
-CFireSphere::CFireSphere( CFire **pList, int listMax, bool onlyActiveFires, const Vector &origin, float radius )
-{
- m_pList = pList;
- m_listMax = listMax;
- m_count = 0;
- m_onlyActiveFires = onlyActiveFires;
- m_origin = origin;
- m_radiusSqr = radius * radius;
-}
-
-bool CFireSphere::AddToList( CFire *pFire )
-{
- if ( m_count >= m_listMax )
- return false;
- m_pList[m_count] = pFire;
- m_count++;
- return true;
-}
-
-IterationRetval_t CFireSphere::EnumElement( IHandleEntity *pHandleEntity )
-{
- CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
- if ( pEntity )
- {
- // UNDONE: Measure which of these is faster
-// CFire *pFire = dynamic_cast<CFire *>(pEntity);
- if ( !FClassnameIs( pEntity, "env_fire" ) )
- return ITERATION_CONTINUE;
-
- CFire *pFire = static_cast<CFire *>(pEntity);
- if ( pFire )
- {
- if ( !m_onlyActiveFires || pFire->IsBurning() )
- {
- if ( (m_origin - pFire->GetAbsOrigin()).LengthSqr() < m_radiusSqr )
- {
- if ( !AddToList( pFire ) )
- return ITERATION_STOP;
- }
- }
- }
- }
-
- return ITERATION_CONTINUE;
-}
-
-
-int FireSystem_GetFiresInSphere( CFire **pList, int listMax, bool onlyActiveFires, const Vector &origin, float radius )
-{
- CFireSphere sphereEnum( pList, listMax, onlyActiveFires, origin, radius );
- partition->EnumerateElementsInSphere( PARTITION_ENGINE_NON_STATIC_EDICTS, origin, radius, false, &sphereEnum );
-
- return sphereEnum.GetCount();
-}
-
-
-bool FireSystem_IsValidFirePosition( const Vector &position, float testRadius )
-{
- CFire *pList[1];
- int count = FireSystem_GetFiresInSphere( pList, ARRAYSIZE(pList), true, position, testRadius );
- if ( count > 0 )
- return false;
- return true;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose :
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-bool FireSystem_IsFireInWall( Vector &position, fireType_e type )
-{
- // Don't check natural fire against walls
- if (type == FIRE_NATURAL)
- return false;
-
- trace_t tr;
- UTIL_TraceHull( position, position+Vector(0,0,0.1), FIRE_MINS,FIRE_MAXS,MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
- if (tr.fraction != 1.0 || tr.startsolid)
- {
- //NDebugOverlay::Box(position,FIRE_MINS,FIRE_MAXS,255,0,0,50,10);
- return true;
- }
- //NDebugOverlay::Box(position,FIRE_MINS,FIRE_MAXS,0,255,0,50,10);
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Determines whether or not a new fire may be placed at a given location
-// Input : &position - where we are trying to put the new fire
-// separationRadius - the maximum distance fires must be apart from one another
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool FireSystem_CanAddFire( Vector *position, float separationRadius, fireType_e type, int flags )
-{
- //See if we found a fire inside the sphere
- if ( !FireSystem_IsValidFirePosition( *position, separationRadius ) )
- return false;
-
- // Unless our fire is floating, make sure were not too high
- if (!(flags & SF_FIRE_DONT_DROP))
- {
- trace_t tr;
- Vector startpos = *position;
- Vector endpos = *position;
-
- startpos[2] += 1;
- endpos[2] -= FIRE_MAX_GROUND_OFFSET;
-
- UTIL_TraceLine( startpos, endpos, MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
-
- //See if we're floating too high
- if ( ( tr.allsolid ) || ( tr.startsolid) || ( tr.fraction == 1.0f ) )
- {
- return false;
- }
-
- //TODO: If we've hit an entity here, start it on fire
- CBaseEntity *pEntity = tr.m_pEnt;
-
- if ( ENTINDEX( pEntity->edict() ) != 0 )
- {
- return false;
- }
- }
-
-
-
- // Check if fire is in a wall, if so try shifting around a bit
- if (FireSystem_IsFireInWall( *position, type ))
- {
- Vector vTestPos = *position;
- vTestPos.x += 10;
- if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type ))
- {
- *position = vTestPos;
- return true;
- }
- vTestPos.y += 10;
- if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type ))
- {
- *position = vTestPos;
- return true;
- }
- vTestPos.y -= 20;
- if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type ))
- {
- *position = vTestPos;
- return true;
- }
- vTestPos.x -= 20;
- if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type ))
- {
- *position = vTestPos;
- return true;
- }
- return false;
- }
-
- //Able to add here
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Starts a fire at a specified location
-// Input : &position - position to start the fire at
-// flags - any special modifiers
-//-----------------------------------------------------------------------------
-bool FireSystem_StartFire( const Vector &position, float fireHeight, float attack, float fuel, int flags, CBaseEntity *owner, fireType_e type )
-{
- VPROF_FIRE( "FireSystem_StartFire1" );
-
- Vector testPos = position;
- //Must be okay to add fire here
- if ( FireSystem_CanAddFire( &testPos, 16.0f, type, flags ) == false )
- {
- CFire *pFires[16];
- int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), true, position, 16.0f );
- for ( int i = 0; i < fireCount; i++ )
- {
- // add to this fire
- pFires[i]->AddHeat( fireHeight, false );
- }
-
- return false;
- }
-
- //Create a new fire entity
- CFire *fire = (CFire *) CreateEntityByName( "env_fire" );
-
- if ( fire == NULL )
- return false;
-
- //Spawn the fire
- // Fires not placed by a designer should be cleaned up automatically (not catch fire again)
- fire->AddSpawnFlags( SF_FIRE_DIE_PERMANENT );
- fire->Spawn();
- fire->Init( testPos, fireHeight, attack, fuel, flags, type );
- fire->Start();
- fire->SetOwner( owner );
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Starts a fire on a specified model.
-// Input : pEntity - The model entity to catch on fire.
-// fireHeight -
-// attack -
-// fuel -
-// flags -
-// owner -
-// type -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool FireSystem_StartFire( CBaseAnimating *pEntity, float fireHeight, float attack, float fuel, int flags, CBaseEntity *owner, fireType_e type )
-{
- VPROF_FIRE( "FireSystem_StartFire2" );
-
- Vector position = pEntity->GetAbsOrigin();
- Vector testPos = position;
-
- // Make sure its a valid position for fire (not in a wall, etc)
- if ( FireSystem_CanAddFire( &testPos, 16.0f, type, flags ) == false )
- {
- // Contribute heat to all fires within 16 units of this fire.
- CFire *pFires[16];
- int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), true, position, 16.0f );
- for ( int i = 0; i < fireCount; i++ )
- {
- pFires[i]->AddHeat( fireHeight, false );
- }
-
- return false;
- }
-
- // Create a new fire entity
- CFire *fire = (CFire *) CreateEntityByName( "env_fire" );
- if ( fire == NULL )
- {
- return false;
- }
-
- // Spawn the fire.
- // Fires not placed by a designer should be cleaned up automatically (not catch fire again).
- fire->AddSpawnFlags( SF_FIRE_DIE_PERMANENT );
- fire->Spawn();
- fire->Init( testPos, fireHeight, attack, fuel, flags, type );
- fire->Start();
- fire->SetOwner( owner );
-
- return true;
-}
-
-
-void FireSystem_ExtinguishInRadius( const Vector &origin, float radius, float rate )
-{
- // UNDONE: pass this instead of percent
- float heat = (1-rate) * fire_extscale.GetFloat();
-
- CFire *pFires[32];
- int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), false, origin, radius );
- for ( int i = 0; i < fireCount; i++ )
- {
- pFires[i]->Extinguish( heat );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &origin -
-// radius -
-// heat -
-//-----------------------------------------------------------------------------
-void FireSystem_AddHeatInRadius( const Vector &origin, float radius, float heat )
-{
- VPROF_FIRE( "FireSystem_AddHeatInRadius" );
-
- CFire *pFires[32];
-
- int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), false, origin, radius );
- for ( int i = 0; i < fireCount; i++ )
- {
- pFires[i]->AddHeat( heat );
- }
-}
-
-//-----------------------------------------------------------------------------
-
-bool FireSystem_GetFireDamageDimensions( CBaseEntity *pEntity, Vector *pFireMins, Vector *pFireMaxs )
-{
- CFire *pFire = dynamic_cast<CFire *>(pEntity);
-
- if ( pFire && pFire->GetFireDimensions( pFireMins, pFireMaxs ) )
- {
- *pFireMins /= FIRE_SPREAD_DAMAGE_MULTIPLIER;
- *pFireMaxs /= FIRE_SPREAD_DAMAGE_MULTIPLIER;
- return true;
- }
- pFireMins->Init();
- pFireMaxs->Init();
- return false;
-}
-
-
-//==================================================
-// CFire
-//==================================================
-BEGIN_DATADESC( CFire )
-
- DEFINE_FIELD( m_hEffect, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
- DEFINE_KEYFIELD( m_nFireType, FIELD_INTEGER, "firetype" ),
-
- DEFINE_FIELD( m_flFuel, FIELD_FLOAT ),
- DEFINE_FIELD( m_flDamageTime, FIELD_TIME ),
- DEFINE_FIELD( m_lastDamage, FIELD_TIME ),
- DEFINE_KEYFIELD( m_flFireSize, FIELD_FLOAT, "firesize" ),
-
- DEFINE_KEYFIELD( m_flHeatLevel, FIELD_FLOAT, "ignitionpoint" ),
- DEFINE_FIELD( m_flHeatAbsorb, FIELD_FLOAT ),
- DEFINE_KEYFIELD( m_flDamageScale,FIELD_FLOAT, "damagescale" ),
-
- DEFINE_FIELD( m_flMaxHeat, FIELD_FLOAT ),
- //DEFINE_FIELD( m_flLastHeatLevel, FIELD_FLOAT ),
-
- DEFINE_KEYFIELD( m_flAttackTime, FIELD_FLOAT, "fireattack" ),
- DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
- DEFINE_KEYFIELD( m_bStartDisabled, FIELD_BOOLEAN, "StartDisabled" ),
- DEFINE_FIELD( m_bDidActivate, FIELD_BOOLEAN ),
-
- DEFINE_FUNCTION( BurnThink ),
- DEFINE_FUNCTION( GoOutThink ),
-
-
-
- DEFINE_INPUTFUNC( FIELD_VOID, "StartFire", InputStartFire ),
- DEFINE_INPUTFUNC( FIELD_FLOAT, "Extinguish", InputExtinguish ),
- DEFINE_INPUTFUNC( FIELD_FLOAT, "ExtinguishTemporary", InputExtinguishTemporary ),
-
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
-
- DEFINE_OUTPUT( m_OnIgnited, "OnIgnited" ),
- DEFINE_OUTPUT( m_OnExtinguished, "OnExtinguished" ),
-
-END_DATADESC()
-
-LINK_ENTITY_TO_CLASS( env_fire, CFire );
-
-//==================================================
-// CFire
-//==================================================
-
-CFire::CFire( void )
-{
- m_flFuel = 0.0f;
- m_flAttackTime = 0.0f;
- m_flDamageTime = 0.0f;
- m_lastDamage = 0;
- m_nFireType = FIRE_NATURAL;
-
- //Spreading
- m_flHeatAbsorb = 8.0f;
- m_flHeatLevel = 0;
-
- // Must be in the constructor!
- AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
-}
-
-//-----------------------------------------------------------------------------
-// UpdateOnRemove
-//-----------------------------------------------------------------------------
-void CFire::UpdateOnRemove( void )
-{
- //Stop any looping sounds that might be playing
- StopSound( "Fire.Plasma" );
-
- DestroyEffect();
-
- // Chain at end to mimic destructor unwind order
- BaseClass::UpdateOnRemove();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CFire::Precache( void )
-{
- if ( m_nFireType == FIRE_NATURAL )
- {
- UTIL_PrecacheOther("_firesmoke");
-
- if ( m_spawnflags & SF_FIRE_SMOKELESS )
- {
- PrecacheParticleSystem( "env_fire_tiny" );
- PrecacheParticleSystem( "env_fire_small" );
- PrecacheParticleSystem( "env_fire_medium" );
- PrecacheParticleSystem( "env_fire_large" );
- }
- else
- {
- PrecacheParticleSystem( "env_fire_tiny_smoke" );
- PrecacheParticleSystem( "env_fire_small_smoke" );
- PrecacheParticleSystem( "env_fire_medium_smoke" );
- PrecacheParticleSystem( "env_fire_large_smoke" );
- }
- }
-
- if ( m_nFireType == FIRE_PLASMA )
- {
- UTIL_PrecacheOther("_plasma");
- }
-
- PrecacheScriptSound( "Fire.Plasma" );
-}
-
-//------------------------------------------------------------------------------
-// Purpose : Input handler for starting the fire.
-//------------------------------------------------------------------------------
-void CFire::InputStartFire( inputdata_t &inputdata )
-{
- if ( !m_bEnabled )
- return;
-
- StartFire();
-}
-
-void CFire::InputEnable( inputdata_t &inputdata )
-{
- m_bEnabled = true;
-}
-
-void CFire::InputDisable( inputdata_t &inputdata )
-{
- Disable();
-}
-
-void CFire::Disable()
-{
- m_bEnabled = false;
- if ( IsBurning() )
- {
- GoOut();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &inputdata -
-//-----------------------------------------------------------------------------
-void CFire::InputExtinguish( inputdata_t &inputdata )
-{
- m_spawnflags &= ~SF_FIRE_INFINITE;
- GoOutInSeconds( inputdata.value.Float() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &inputdata -
-//-----------------------------------------------------------------------------
-void CFire::InputExtinguishTemporary( inputdata_t &inputdata )
-{
- GoOutInSeconds( inputdata.value.Float() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Starts burning.
-//-----------------------------------------------------------------------------
-void CFire::StartFire( void )
-{
- if ( m_hEffect != NULL )
- return;
-
- // Trace down and start a fire there. Nothing fancy yet.
- Vector vFirePos;
- trace_t tr;
- if ( m_spawnflags & SF_FIRE_DONT_DROP )
- {
- vFirePos = GetAbsOrigin();
- }
- else
- {
- UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1024 ), MASK_FIRE_SOLID, this, COLLISION_GROUP_NONE, &tr );
- vFirePos = tr.endpos;
- }
-
- int spawnflags = m_spawnflags;
- m_spawnflags |= SF_FIRE_START_ON;
- Init( vFirePos, m_flFireSize, m_flAttackTime, GetHealth(), m_spawnflags, (fireType_e) m_nFireType );
- Start();
- m_spawnflags = spawnflags;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CFire::Spawn( void )
-{
- BaseClass::Spawn();
-
- Precache();
-
- m_takedamage = DAMAGE_NO;
-
- SetSolid( SOLID_NONE );
- AddEffects( EF_NODRAW );
- SetToOutSize();
-
- // set up the ignition point
- m_flHeatAbsorb = m_flHeatLevel * 0.05;
- m_flHeatLevel = 0;
- Init( GetAbsOrigin(), m_flFireSize, m_flAttackTime, m_flFuel, m_spawnflags, m_nFireType );
-
- if( m_bStartDisabled )
- {
- Disable();
- }
- else
- {
- m_bEnabled = true;
- }
-}
-
-int CFire::UpdateTransmitState()
-{
- // Don't want to be FL_EDICT_DONTSEND because our fire entity may make us transmit.
- return SetTransmitState( FL_EDICT_ALWAYS );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CFire::Activate( void )
-{
- BaseClass::Activate();
-
- //See if we should start active
- if ( !m_bDidActivate && ( m_spawnflags & SF_FIRE_START_ON ) )
- {
- m_flHeatLevel = m_flMaxHeat;
-
- StartFire();
- }
-
- m_bDidActivate = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CFire::SpawnEffect( fireType_e type, float scale )
-{
- CBaseFire *pEffect = NULL;
- switch ( type )
- {
- default:
- case FIRE_NATURAL:
- {
- CFireSmoke *fireSmoke = (CFireSmoke *) CreateEntityByName( "_firesmoke" );
- fireSmoke->EnableSmoke( ( m_spawnflags & SF_FIRE_SMOKELESS )==false );
- fireSmoke->EnableGlow( ( m_spawnflags & SF_FIRE_NO_GLOW )==false );
- fireSmoke->EnableVisibleFromAbove( ( m_spawnflags & SF_FIRE_VISIBLE_FROM_ABOVE )!=false );
-
- pEffect = fireSmoke;
- m_nFireType = FIRE_NATURAL;
- m_takedamage = DAMAGE_YES;
- }
- break;
-
- case FIRE_PLASMA:
- {
- CPlasma *plasma = (CPlasma *) CreateEntityByName( "_plasma" );
- plasma->EnableSmoke( true );
-
- pEffect = plasma;
- m_nFireType = FIRE_PLASMA;
- m_takedamage = DAMAGE_YES;
-
- // Start burn sound
- EmitSound( "Fire.Plasma" );
- }
- break;
- }
-
- UTIL_SetOrigin( pEffect, GetAbsOrigin() );
- pEffect->Spawn();
- pEffect->SetParent( this );
- pEffect->Scale( m_flFireSize, m_flFireSize, 0 );
- //Start it going
- pEffect->Enable( ( m_spawnflags & SF_FIRE_START_ON ) );
- m_hEffect = pEffect;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Spawn and initialize the fire
-// Input : &position - where the fire resides
-// lifetime -
-//-----------------------------------------------------------------------------
-void CFire::Init( const Vector &position, float scale, float attackTime, float fuel, int flags, int fireType )
-{
- m_flAttackTime = attackTime;
-
- m_spawnflags = flags;
- m_nFireType = fireType;
-
- if ( flags & SF_FIRE_INFINITE )
- {
- fuel = 0;
- }
- m_flFuel = fuel;
- if ( m_flFuel )
- {
- m_spawnflags |= SF_FIRE_DIE_PERMANENT;
- }
-
- Vector localOrigin = position;
- if ( GetMoveParent() )
- {
- EntityMatrix parentMatrix;
- parentMatrix.InitFromEntity( GetMoveParent() );
- localOrigin = parentMatrix.WorldToLocal( position );
- }
- UTIL_SetOrigin( this, localOrigin );
-
- SetSolid( SOLID_NONE );
- m_flFireSize = scale;
- m_flMaxHeat = FIRE_MAX_HEAT_LEVEL * FIRE_SCALE_FROM_SIZE(scale);
- //See if we should start on
- if ( m_spawnflags & SF_FIRE_START_FULL )
- {
- m_flHeatLevel = m_flMaxHeat;
- }
- m_flLastHeatLevel = 0;
-
-}
-
-void CFire::Start()
-{
- float boxWidth = (m_flFireSize * (FIRE_WIDTH/FIRE_HEIGHT))*0.5f;
- UTIL_SetSize(this, Vector(-boxWidth,-boxWidth,0),Vector(boxWidth,boxWidth,m_flFireSize));
-
- //Spawn the client-side effect
- SpawnEffect( (fireType_e)m_nFireType, FIRE_SCALE_FROM_SIZE(m_flFireSize) );
- m_OnIgnited.FireOutput( this, this );
- SetThink( &CFire::BurnThink );
- m_flDamageTime = 0;
- // think right now
- BurnThink();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Determines whether or not the fire is still active
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CFire::IsBurning( void ) const
-{
- if ( m_flHeatLevel > 0 )
- return true;
-
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the damage box of the fire
-//-----------------------------------------------------------------------------
-bool CFire::GetFireDimensions( Vector *pFireMins, Vector *pFireMaxs )
-{
- if ( m_flHeatLevel <= 0 )
- {
- pFireMins->Init();
- pFireMaxs->Init();
- return false;
- }
-
- float scale = m_flHeatLevel / m_flMaxHeat;
- float damageRadius = scale * m_flFireSize * FIRE_WIDTH / FIRE_HEIGHT * 0.5;
-
- damageRadius *= FIRE_SPREAD_DAMAGE_MULTIPLIER; //FIXME: Trying slightly larger radius for burning
-
- if ( damageRadius < 16 )
- {
- damageRadius = 16;
- }
-
- pFireMins->Init(-damageRadius,-damageRadius,0);
- pFireMaxs->Init(damageRadius,damageRadius,m_flFireSize*scale);
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Update the fire and its children
-//-----------------------------------------------------------------------------
-void CFire::Update( float simTime )
-{
- VPROF_FIRE( "CFire::Update" );
-
- if ( m_flFuel != 0 )
- {
- m_flFuel -= simTime;
- if ( m_flFuel <= 0 )
- {
- GoOutInSeconds( 1 );
- return;
- }
- }
-
- float strength = m_flHeatLevel / FIRE_MAX_HEAT_LEVEL;
- if ( m_flHeatLevel != m_flLastHeatLevel )
- {
- m_flLastHeatLevel = m_flHeatLevel;
- // Make the effect the appropriate size given the heat level
- m_hEffect->Scale( strength, 0.5f );
- }
- // add heat to myself (grow)
- float addedHeat = (m_flAttackTime > 0) ? m_flMaxHeat / m_flAttackTime : m_flMaxHeat;
- addedHeat *= simTime * fire_growthrate.GetFloat();
- AddHeat( addedHeat, true );
-
- // add heat to nearby fires
- float outputHeat = strength * m_flHeatLevel;
-
- Vector fireMins;
- Vector fireMaxs;
- Vector fireEntityDamageMins;
- Vector fireEntityDamageMaxs;
-
- GetFireDimensions( &fireMins, &fireMaxs );
-
- if ( FIRE_SPREAD_DAMAGE_MULTIPLIER != 1.0 ) // if set to 1.0, optimizer will remove this code
- {
- fireEntityDamageMins = fireMins / FIRE_SPREAD_DAMAGE_MULTIPLIER;
- fireEntityDamageMaxs = fireMaxs / FIRE_SPREAD_DAMAGE_MULTIPLIER;
- }
-
- //NDebugOverlay::Box( GetAbsOrigin(), fireMins, fireMaxs, 255, 255, 255, 0, fire_dmginterval.GetFloat() );
- fireMins += GetAbsOrigin();
- fireMaxs += GetAbsOrigin();
-
- if ( FIRE_SPREAD_DAMAGE_MULTIPLIER != 1.0 )
- {
- fireEntityDamageMins += GetAbsOrigin();
- fireEntityDamageMaxs += GetAbsOrigin();
- }
-
- CBaseEntity *pNearby[256];
- CFire *pFires[16];
- int nearbyCount = UTIL_EntitiesInBox( pNearby, ARRAYSIZE(pNearby), fireMins, fireMaxs, 0 );
- int fireCount = 0;
- int i;
-
- // is it time to do damage?
- bool damage = false;
- int outputDamage = 0;
- if ( m_flDamageTime <= gpGlobals->curtime )
- {
- m_flDamageTime = gpGlobals->curtime + fire_dmginterval.GetFloat();
- outputDamage = (fire_dmgbase.GetFloat() + outputHeat * fire_dmgscale.GetFloat() * m_flDamageScale) * fire_dmginterval.GetFloat();
- if ( outputDamage )
- {
- damage = true;
- }
- }
- int damageFlags = (m_nFireType == FIRE_NATURAL) ? DMG_BURN : DMG_PLASMA;
- for ( i = 0; i < nearbyCount; i++ )
- {
- CBaseEntity *pOther = pNearby[i];
-
- if ( pOther == this )
- {
- continue;
- }
- else if ( FClassnameIs( pOther, "env_fire" ) )
- {
- if ( fireCount < ARRAYSIZE(pFires) )
- {
- pFires[fireCount] = (CFire *)pOther;
- fireCount++;
- }
- continue;
- }
- else if ( pOther->m_takedamage == DAMAGE_NO )
- {
- pNearby[i] = NULL;
- }
- else if ( damage )
- {
- bool bDoDamage;
-
- if ( FIRE_SPREAD_DAMAGE_MULTIPLIER != 1.0 && !pOther->IsPlayer() ) // if set to 1.0, optimizer will remove this code
- {
- Vector otherMins, otherMaxs;
- pOther->CollisionProp()->WorldSpaceAABB( &otherMins, &otherMaxs );
- bDoDamage = IsBoxIntersectingBox( otherMins, otherMaxs,
- fireEntityDamageMins, fireEntityDamageMaxs );
-
- }
- else
- bDoDamage = true;
-
- if ( bDoDamage )
- {
- // Make sure can actually see entity (don't damage through walls)
- trace_t tr;
- UTIL_TraceLine( this->WorldSpaceCenter(), pOther->WorldSpaceCenter(), MASK_FIRE_SOLID, pOther, COLLISION_GROUP_NONE, &tr );
-
- if (tr.fraction == 1.0 && !tr.startsolid)
- {
- pOther->TakeDamage( CTakeDamageInfo( this, this, outputDamage, damageFlags ) );
- }
- }
- }
- }
-
- outputHeat *= fire_heatscale.GetFloat() * simTime;
-
- if ( fireCount > 0 )
- {
- outputHeat /= fireCount;
- for ( i = 0; i < fireCount; i++ )
- {
- pFires[i]->AddHeat( outputHeat, false );
- }
- }
-}
-
-// Destroy any effect I have
-void CFire::DestroyEffect()
-{
- CBaseFire *pEffect = m_hEffect;
- if ( pEffect != NULL )
- {
- //disable the graphics and remove the entity
- pEffect->Enable( false );
- UTIL_Remove( pEffect );
- }
-}
-//-----------------------------------------------------------------------------
-// Purpose: Think
-//-----------------------------------------------------------------------------
-void CFire::BurnThink( void )
-{
- SetNextThink( gpGlobals->curtime + FIRE_THINK_INTERVAL );
-
- Update( FIRE_THINK_INTERVAL );
-}
-
-void CFire::GoOutThink()
-{
- GoOut();
-}
-
-void CFire::GoOutInSeconds( float seconds )
-{
- Scale( 0.0f, seconds );
-
- SetThink( &CFire::GoOutThink );
- SetNextThink( gpGlobals->curtime + seconds );
-}
-
-//------------------------------------------------------------------------------
-// Purpose : Blasts of significant size blow out fires that take damage
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-int CFire::OnTakeDamage( const CTakeDamageInfo &info )
-{
- return 0;
-}
-
-void CFire::AddHeat( float heat, bool selfHeat )
-{
- if ( m_bEnabled )
- {
- if ( !selfHeat )
- {
- if ( IsBurning() )
- {
- // scale back the incoming heat from surrounding fires
- // if I've already ignited
- heat *= fire_incomingheatscale.GetFloat();
- }
- }
- m_lastDamage = gpGlobals->curtime + 0.5;
- bool start = m_flHeatLevel <= 0 ? true : false;
- if ( m_flHeatAbsorb > 0 )
- {
- float absorbDamage = heat * fire_absorbrate.GetFloat();
- if ( absorbDamage > m_flHeatAbsorb )
- {
- heat -= m_flHeatAbsorb / fire_absorbrate.GetFloat();
- m_flHeatAbsorb = 0;
- }
- else
- {
- m_flHeatAbsorb -= absorbDamage;
- heat = 0;
- }
- }
-
- m_flHeatLevel += heat;
- if ( start && m_flHeatLevel > 0 && m_hEffect == NULL )
- {
- StartFire();
- }
- if ( m_flHeatLevel > m_flMaxHeat )
- m_flHeatLevel = m_flMaxHeat;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : end -
-// time -
-//-----------------------------------------------------------------------------
-void CFire::Scale( float end, float time )
-{
- CBaseFire *pEffect = m_hEffect;
- if ( pEffect )
- {
- pEffect->Scale( end, time );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : time -
-//-----------------------------------------------------------------------------
-void CFire::Extinguish( float heat )
-{
- if ( !m_bEnabled )
- return;
-
- m_lastDamage = gpGlobals->curtime + 0.5;
- bool out = m_flHeatLevel > 0 ? true : false;
-
- m_flHeatLevel -= heat;
- m_flHeatAbsorb += fire_extabsorb.GetFloat() * heat;
- if ( m_flHeatAbsorb > fire_maxabsorb.GetFloat() )
- {
- m_flHeatAbsorb = fire_maxabsorb.GetFloat();
- }
-
- // drift toward the average attack time after being sprayed
- // some fires are heavily scripted so their attack looks weird
- // once interacted with. Basically, this blends out the scripting
- // as the fire is sprayed with the extinguisher.
- float averageAttackTime = m_flMaxHeat * (FIRE_NORMAL_ATTACK_TIME/FIRE_MAX_HEAT_LEVEL);
- m_flAttackTime = Approach( averageAttackTime, m_flAttackTime, 2 * gpGlobals->frametime );
-
- if ( m_flHeatLevel <= 0 )
- {
- m_flHeatLevel = 0;
- if ( out )
- {
- GoOut();
- }
- }
-}
-
-bool CFire::GoOut()
-{
- //Signal death
- m_OnExtinguished.FireOutput( this, this );
-
- DestroyEffect();
- m_flHeatLevel -= 20;
- if ( m_flHeatLevel > 0 )
- m_flHeatLevel = 0;
-
- m_flLastHeatLevel = m_flHeatLevel;
- SetThink(NULL);
- SetNextThink( TICK_NEVER_THINK );
- if ( m_spawnflags & SF_FIRE_DIE_PERMANENT )
- {
- UTIL_Remove( this );
- return true;
- }
- SetToOutSize();
-
- return false;
-}
-
-//==================================================
-// CEnvFireSource is a source of heat that the player
-// cannot put out
-//==================================================
-
-#define FIRESOURCE_THINK_TIME 0.25 // seconds to
-
-#define SF_FIRESOURCE_START_ON 0x0001
-
-class CEnvFireSource : public CBaseEntity
-{
- DECLARE_CLASS( CEnvFireSource, CBaseEntity );
-public:
- void Spawn();
- void Think();
- void TurnOn();
- void TurnOff();
- void InputEnable( inputdata_t &inputdata );
- void InputDisable( inputdata_t &inputdata );
-
- DECLARE_DATADESC();
-
-private:
- bool m_bEnabled;
- float m_radius;
- float m_damage;
-};
-
-BEGIN_DATADESC( CEnvFireSource )
-
- DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
- DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "fireradius" ),
- DEFINE_KEYFIELD( m_damage,FIELD_FLOAT, "firedamage" ),
-
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
-
-
-END_DATADESC()
-
-LINK_ENTITY_TO_CLASS( env_firesource, CEnvFireSource );
-
-void CEnvFireSource::Spawn()
-{
- if ( m_spawnflags & SF_FIRESOURCE_START_ON )
- {
- TurnOn();
- }
- else
- {
- TurnOff();
- }
-}
-
-void CEnvFireSource::Think()
-{
- if ( !m_bEnabled )
- return;
- SetNextThink( gpGlobals->curtime + FIRESOURCE_THINK_TIME );
-
- CFire *pFires[128];
- int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), false, GetAbsOrigin(), m_radius );
-
- for ( int i = 0; i < fireCount; i++ )
- {
- pFires[i]->AddHeat( m_damage * FIRESOURCE_THINK_TIME );
- }
-}
-
-void CEnvFireSource::TurnOn()
-{
- if ( m_bEnabled )
- return;
-
- m_bEnabled = true;
- SetNextThink( gpGlobals->curtime );
-}
-
-void CEnvFireSource::TurnOff()
-{
- if ( !m_bEnabled )
- return;
-
- m_bEnabled = false;
- SetNextThink( TICK_NEVER_THINK );
-}
-void CEnvFireSource::InputEnable( inputdata_t &inputdata )
-{
- TurnOn();
-}
-void CEnvFireSource::InputDisable( inputdata_t &inputdata )
-{
- TurnOff();
-}
-
-//==================================================
-// CEnvFireSensor detects changes in heat
-//==================================================
-#define SF_FIRESENSOR_START_ON 1
-
-class CEnvFireSensor : public CBaseEntity
-{
- DECLARE_CLASS( CEnvFireSensor, CBaseEntity );
-public:
- void Spawn();
- void Think();
- void TurnOn();
- void TurnOff();
- void InputEnable( inputdata_t &inputdata );
- void InputDisable( inputdata_t &inputdata );
-
- DECLARE_DATADESC();
-
-private:
- bool m_bEnabled;
- bool m_bHeatAtLevel;
- float m_radius;
- float m_targetLevel;
- float m_targetTime;
- float m_levelTime;
-
- COutputEvent m_OnHeatLevelStart;
- COutputEvent m_OnHeatLevelEnd;
-};
-
-BEGIN_DATADESC( CEnvFireSensor )
-
- DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "fireradius" ),
- DEFINE_KEYFIELD( m_targetLevel, FIELD_FLOAT, "heatlevel" ),
- DEFINE_KEYFIELD( m_targetTime, FIELD_FLOAT, "heattime" ),
-
- DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bHeatAtLevel, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_levelTime, FIELD_FLOAT ),
-
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
-
- DEFINE_OUTPUT( m_OnHeatLevelStart, "OnHeatLevelStart"),
- DEFINE_OUTPUT( m_OnHeatLevelEnd, "OnHeatLevelEnd"),
-
-END_DATADESC()
-
-LINK_ENTITY_TO_CLASS( env_firesensor, CEnvFireSensor );
-
-void CEnvFireSensor::Spawn()
-{
- if ( m_spawnflags & SF_FIRESENSOR_START_ON )
- {
- TurnOn();
- }
- else
- {
- TurnOff();
- }
-}
-
-void CEnvFireSensor::Think()
-{
- if ( !m_bEnabled )
- return;
-
- float time = m_targetTime * 0.25;
- if ( time < 0.1 )
- {
- time = 0.1;
- }
- SetNextThink( gpGlobals->curtime + time );
-
- float heat = 0;
- CFire *pFires[128];
- int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), true, GetAbsOrigin(), m_radius );
- for ( int i = 0; i < fireCount; i++ )
- {
- heat += pFires[i]->GetHeatLevel();
- }
-
- if ( heat >= m_targetLevel )
- {
- m_levelTime += time;
- if ( m_levelTime >= m_targetTime )
- {
- if ( !m_bHeatAtLevel )
- {
- m_bHeatAtLevel = true;
- m_OnHeatLevelStart.FireOutput( this, this );
- }
- }
- }
- else
- {
- m_levelTime = 0;
- if ( m_bHeatAtLevel )
- {
- m_bHeatAtLevel = false;
- m_OnHeatLevelEnd.FireOutput( this, this );
- }
- }
-}
-
-void CEnvFireSensor::TurnOn()
-{
- if ( m_bEnabled )
- return;
-
- m_bEnabled = true;
- SetNextThink( gpGlobals->curtime );
- m_bHeatAtLevel = false;
- m_levelTime = 0;
-}
-
-void CEnvFireSensor::TurnOff()
-{
- if ( !m_bEnabled )
- return;
-
- m_bEnabled = false;
- SetNextThink( TICK_NEVER_THINK );
- if ( m_bHeatAtLevel )
- {
- m_bHeatAtLevel = false;
- m_OnHeatLevelEnd.FireOutput( this, this );
- }
-
-}
-void CEnvFireSensor::InputEnable( inputdata_t &inputdata )
-{
- TurnOn();
-}
-void CEnvFireSensor::InputDisable( inputdata_t &inputdata )
-{
- TurnOff();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw any debug text overlays
-// Output : Current text offset from the top
-//-----------------------------------------------------------------------------
-int CFire::DrawDebugTextOverlays( void )
-{
- int text_offset = BaseClass::DrawDebugTextOverlays();
-
- if (m_debugOverlays & OVERLAY_TEXT_BIT)
- {
- char tempstr[512];
-
- // print flame size
- Q_snprintf(tempstr,sizeof(tempstr)," size: %f", m_flFireSize);
- EntityText(text_offset,tempstr,0);
- text_offset++;
- }
- return text_offset;
-}
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +//--------------------------------------------------------- +//--------------------------------------------------------- +#include "cbase.h" +#include "decals.h" +#include "fire.h" +#include "entitylist.h" +#include "basecombatcharacter.h" +#include "ndebugoverlay.h" +#include "engine/IEngineSound.h" +#include "ispatialpartition.h" +#include "collisionutils.h" +#include "tier0/vprof.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +/******************************************************************** + NOTE: if you are looking at this file becase you would like flares + to be considered as fires (and thereby trigger gas traps), be aware + that the env_flare class is actually found in weapon_flaregun.cpp + and is really a repurposed piece of ammunition. (env_flare isn't the + rod-like safety flare prop, but rather the bit of flame on the end.) + + You will have some difficulty making it work here, because CFlare + does not inherit from CFire and will thus not be enumerated by + CFireSphere::EnumElement(). In order to have flares be detected and + used by this system, you will need to promote certain member functions + of CFire into an interface class from which both CFire and CFlare + inherit. You will also need to modify CFireSphere::EnumElement so that + it properly disambiguates between fires and flares. + + For some partial work towards this end, see changelist 192474. + + ********************************************************************/ + + + + +#define FIRE_HEIGHT 256.0f +#define FIRE_SCALE_FROM_SIZE(firesize) (firesize * (1/FIRE_HEIGHT)) + +#define FIRE_MAX_GROUND_OFFSET 24.0f //(2 feet) + +#define DEFAULT_ATTACK_TIME 4.0f +#define DEFAULT_DECAY_TIME 8.0f + +// UNDONE: This shouldn't be constant but depend on specific fire +#define FIRE_WIDTH 128 +#define FIRE_MINS Vector(-20,-20,0 ) // Sould be FIRE_WIDTH in size +#define FIRE_MAXS Vector( 20, 20,20) // Sould be FIRE_WIDTH in size +#define FIRE_SPREAD_DAMAGE_MULTIPLIER 2.0 + +#define FIRE_MAX_HEAT_LEVEL 64.0f +#define FIRE_NORMAL_ATTACK_TIME 20.0f +#define FIRE_THINK_INTERVAL 0.1 + +ConVar fire_maxabsorb( "fire_maxabsorb", "50" ); +ConVar fire_absorbrate( "fire_absorbrate", "3" ); +ConVar fire_extscale("fire_extscale", "12"); +ConVar fire_extabsorb("fire_extabsorb", "5"); +ConVar fire_heatscale( "fire_heatscale", "1.0" ); +ConVar fire_incomingheatscale( "fire_incomingheatscale", "0.1" ); +ConVar fire_dmgscale( "fire_dmgscale", "0.1" ); +ConVar fire_dmgbase( "fire_dmgbase", "1" ); +ConVar fire_growthrate( "fire_growthrate", "1.0" ); +ConVar fire_dmginterval( "fire_dmginterval", "1.0" ); + +#define VPROF_FIRE(s) VPROF( s ) + +class CFire : public CBaseEntity +{ +public: + DECLARE_CLASS( CFire, CBaseEntity ); + + int DrawDebugTextOverlays(void); + + CFire( void ); + + virtual void UpdateOnRemove( void ); + + void Precache( void ); + void Init( const Vector &position, float scale, float attackTime, float fuel, int flags, int fireType ); + bool GoOut(); + + void BurnThink(); + void GoOutThink(); + void GoOutInSeconds( float seconds ); + + void SetOwner( CBaseEntity *hOwner ) { m_hOwner = hOwner; } + + void Scale( float end, float time ); + void AddHeat( float heat, bool selfHeat = false ); + int OnTakeDamage( const CTakeDamageInfo &info ); + + bool IsBurning( void ) const; + + bool GetFireDimensions( Vector *pFireMins, Vector *pFireMaxs ); + + void Extinguish( float heat ); + void DestroyEffect(); + + virtual void Update( float simTime ); + + void Spawn( void ); + void Activate( void ); + void StartFire( void ); + void Start(); + void SetToOutSize() + { + UTIL_SetSize( this, Vector(-8,-8,0), Vector(8,8,8) ); + } + + float GetHeatLevel() { return m_flHeatLevel; } + + virtual int UpdateTransmitState(); + + void DrawDebugGeometryOverlays(void) + { + if (m_debugOverlays & OVERLAY_BBOX_BIT) + { + if ( m_lastDamage > gpGlobals->curtime && m_flHeatAbsorb > 0 ) + { + NDebugOverlay::EntityBounds(this, 88, 255, 128, 0 ,0); + char tempstr[512]; + Q_snprintf( tempstr, sizeof(tempstr), "Heat: %.1f", m_flHeatAbsorb ); + EntityText(1,tempstr, 0); + } + else if ( !IsBurning() ) + { + NDebugOverlay::EntityBounds(this, 88, 88, 128, 0 ,0); + } + + if ( IsBurning() ) + { + Vector mins, maxs; + if ( GetFireDimensions( &mins, &maxs ) ) + { + NDebugOverlay::Box(GetAbsOrigin(), mins, maxs, 128, 0, 0, 10, 0); + } + } + + + } + BaseClass::DrawDebugGeometryOverlays(); + } + + void Disable(); + + //Inputs + void InputStartFire( inputdata_t &inputdata ); + void InputExtinguish( inputdata_t &inputdata ); + void InputExtinguishTemporary( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + +protected: + + void Spread( void ); + void SpawnEffect( fireType_e type, float scale ); + + CHandle<CBaseFire> m_hEffect; + EHANDLE m_hOwner; + + int m_nFireType; + + float m_flFuel; + float m_flDamageTime; + float m_lastDamage; + float m_flFireSize; // size of the fire in world units + + float m_flHeatLevel; // Used as a "health" for the fire. > 0 means the fire is burning + float m_flHeatAbsorb; // This much heat must be "absorbed" before it gets transferred to the flame size + float m_flDamageScale; + + float m_flMaxHeat; + float m_flLastHeatLevel; + + //NOTENOTE: Lifetime is an expression of the sum total of these amounts plus the global time when started + float m_flAttackTime; //Amount of time to scale up + + bool m_bEnabled; + bool m_bStartDisabled; + bool m_bDidActivate; + + + COutputEvent m_OnIgnited; + COutputEvent m_OnExtinguished; + + DECLARE_DATADESC(); +}; + +class CFireSphere : public IPartitionEnumerator +{ +public: + CFireSphere( CFire **pList, int listMax, bool onlyActiveFires, const Vector &origin, float radius ); + // This gets called by the enumeration methods with each element + // that passes the test. + virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ); + + int GetCount() { return m_count; } + bool AddToList( CFire *pEntity ); + +private: + Vector m_origin; + float m_radiusSqr; + CFire **m_pList; + int m_listMax; + int m_count; + bool m_onlyActiveFires; +}; + +CFireSphere::CFireSphere( CFire **pList, int listMax, bool onlyActiveFires, const Vector &origin, float radius ) +{ + m_pList = pList; + m_listMax = listMax; + m_count = 0; + m_onlyActiveFires = onlyActiveFires; + m_origin = origin; + m_radiusSqr = radius * radius; +} + +bool CFireSphere::AddToList( CFire *pFire ) +{ + if ( m_count >= m_listMax ) + return false; + m_pList[m_count] = pFire; + m_count++; + return true; +} + +IterationRetval_t CFireSphere::EnumElement( IHandleEntity *pHandleEntity ) +{ + CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() ); + if ( pEntity ) + { + // UNDONE: Measure which of these is faster +// CFire *pFire = dynamic_cast<CFire *>(pEntity); + if ( !FClassnameIs( pEntity, "env_fire" ) ) + return ITERATION_CONTINUE; + + CFire *pFire = static_cast<CFire *>(pEntity); + if ( pFire ) + { + if ( !m_onlyActiveFires || pFire->IsBurning() ) + { + if ( (m_origin - pFire->GetAbsOrigin()).LengthSqr() < m_radiusSqr ) + { + if ( !AddToList( pFire ) ) + return ITERATION_STOP; + } + } + } + } + + return ITERATION_CONTINUE; +} + + +int FireSystem_GetFiresInSphere( CFire **pList, int listMax, bool onlyActiveFires, const Vector &origin, float radius ) +{ + CFireSphere sphereEnum( pList, listMax, onlyActiveFires, origin, radius ); + partition->EnumerateElementsInSphere( PARTITION_ENGINE_NON_STATIC_EDICTS, origin, radius, false, &sphereEnum ); + + return sphereEnum.GetCount(); +} + + +bool FireSystem_IsValidFirePosition( const Vector &position, float testRadius ) +{ + CFire *pList[1]; + int count = FireSystem_GetFiresInSphere( pList, ARRAYSIZE(pList), true, position, testRadius ); + if ( count > 0 ) + return false; + return true; +} + + +//------------------------------------------------------------------------------ +// Purpose : +// Input : +// Output : +//------------------------------------------------------------------------------ +bool FireSystem_IsFireInWall( Vector &position, fireType_e type ) +{ + // Don't check natural fire against walls + if (type == FIRE_NATURAL) + return false; + + trace_t tr; + UTIL_TraceHull( position, position+Vector(0,0,0.1), FIRE_MINS,FIRE_MAXS,MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr ); + if (tr.fraction != 1.0 || tr.startsolid) + { + //NDebugOverlay::Box(position,FIRE_MINS,FIRE_MAXS,255,0,0,50,10); + return true; + } + //NDebugOverlay::Box(position,FIRE_MINS,FIRE_MAXS,0,255,0,50,10); + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Determines whether or not a new fire may be placed at a given location +// Input : &position - where we are trying to put the new fire +// separationRadius - the maximum distance fires must be apart from one another +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool FireSystem_CanAddFire( Vector *position, float separationRadius, fireType_e type, int flags ) +{ + //See if we found a fire inside the sphere + if ( !FireSystem_IsValidFirePosition( *position, separationRadius ) ) + return false; + + // Unless our fire is floating, make sure were not too high + if (!(flags & SF_FIRE_DONT_DROP)) + { + trace_t tr; + Vector startpos = *position; + Vector endpos = *position; + + startpos[2] += 1; + endpos[2] -= FIRE_MAX_GROUND_OFFSET; + + UTIL_TraceLine( startpos, endpos, MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr ); + + //See if we're floating too high + if ( ( tr.allsolid ) || ( tr.startsolid) || ( tr.fraction == 1.0f ) ) + { + return false; + } + + //TODO: If we've hit an entity here, start it on fire + CBaseEntity *pEntity = tr.m_pEnt; + + if ( ENTINDEX( pEntity->edict() ) != 0 ) + { + return false; + } + } + + + + // Check if fire is in a wall, if so try shifting around a bit + if (FireSystem_IsFireInWall( *position, type )) + { + Vector vTestPos = *position; + vTestPos.x += 10; + if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type )) + { + *position = vTestPos; + return true; + } + vTestPos.y += 10; + if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type )) + { + *position = vTestPos; + return true; + } + vTestPos.y -= 20; + if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type )) + { + *position = vTestPos; + return true; + } + vTestPos.x -= 20; + if (FireSystem_IsValidFirePosition( vTestPos, separationRadius ) && !FireSystem_IsFireInWall( vTestPos, type )) + { + *position = vTestPos; + return true; + } + return false; + } + + //Able to add here + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Starts a fire at a specified location +// Input : &position - position to start the fire at +// flags - any special modifiers +//----------------------------------------------------------------------------- +bool FireSystem_StartFire( const Vector &position, float fireHeight, float attack, float fuel, int flags, CBaseEntity *owner, fireType_e type ) +{ + VPROF_FIRE( "FireSystem_StartFire1" ); + + Vector testPos = position; + //Must be okay to add fire here + if ( FireSystem_CanAddFire( &testPos, 16.0f, type, flags ) == false ) + { + CFire *pFires[16]; + int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), true, position, 16.0f ); + for ( int i = 0; i < fireCount; i++ ) + { + // add to this fire + pFires[i]->AddHeat( fireHeight, false ); + } + + return false; + } + + //Create a new fire entity + CFire *fire = (CFire *) CreateEntityByName( "env_fire" ); + + if ( fire == NULL ) + return false; + + //Spawn the fire + // Fires not placed by a designer should be cleaned up automatically (not catch fire again) + fire->AddSpawnFlags( SF_FIRE_DIE_PERMANENT ); + fire->Spawn(); + fire->Init( testPos, fireHeight, attack, fuel, flags, type ); + fire->Start(); + fire->SetOwner( owner ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Starts a fire on a specified model. +// Input : pEntity - The model entity to catch on fire. +// fireHeight - +// attack - +// fuel - +// flags - +// owner - +// type - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool FireSystem_StartFire( CBaseAnimating *pEntity, float fireHeight, float attack, float fuel, int flags, CBaseEntity *owner, fireType_e type ) +{ + VPROF_FIRE( "FireSystem_StartFire2" ); + + Vector position = pEntity->GetAbsOrigin(); + Vector testPos = position; + + // Make sure its a valid position for fire (not in a wall, etc) + if ( FireSystem_CanAddFire( &testPos, 16.0f, type, flags ) == false ) + { + // Contribute heat to all fires within 16 units of this fire. + CFire *pFires[16]; + int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), true, position, 16.0f ); + for ( int i = 0; i < fireCount; i++ ) + { + pFires[i]->AddHeat( fireHeight, false ); + } + + return false; + } + + // Create a new fire entity + CFire *fire = (CFire *) CreateEntityByName( "env_fire" ); + if ( fire == NULL ) + { + return false; + } + + // Spawn the fire. + // Fires not placed by a designer should be cleaned up automatically (not catch fire again). + fire->AddSpawnFlags( SF_FIRE_DIE_PERMANENT ); + fire->Spawn(); + fire->Init( testPos, fireHeight, attack, fuel, flags, type ); + fire->Start(); + fire->SetOwner( owner ); + + return true; +} + + +void FireSystem_ExtinguishInRadius( const Vector &origin, float radius, float rate ) +{ + // UNDONE: pass this instead of percent + float heat = (1-rate) * fire_extscale.GetFloat(); + + CFire *pFires[32]; + int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), false, origin, radius ); + for ( int i = 0; i < fireCount; i++ ) + { + pFires[i]->Extinguish( heat ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &origin - +// radius - +// heat - +//----------------------------------------------------------------------------- +void FireSystem_AddHeatInRadius( const Vector &origin, float radius, float heat ) +{ + VPROF_FIRE( "FireSystem_AddHeatInRadius" ); + + CFire *pFires[32]; + + int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), false, origin, radius ); + for ( int i = 0; i < fireCount; i++ ) + { + pFires[i]->AddHeat( heat ); + } +} + +//----------------------------------------------------------------------------- + +bool FireSystem_GetFireDamageDimensions( CBaseEntity *pEntity, Vector *pFireMins, Vector *pFireMaxs ) +{ + CFire *pFire = dynamic_cast<CFire *>(pEntity); + + if ( pFire && pFire->GetFireDimensions( pFireMins, pFireMaxs ) ) + { + *pFireMins /= FIRE_SPREAD_DAMAGE_MULTIPLIER; + *pFireMaxs /= FIRE_SPREAD_DAMAGE_MULTIPLIER; + return true; + } + pFireMins->Init(); + pFireMaxs->Init(); + return false; +} + + +//================================================== +// CFire +//================================================== +BEGIN_DATADESC( CFire ) + + DEFINE_FIELD( m_hEffect, FIELD_EHANDLE ), + DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_nFireType, FIELD_INTEGER, "firetype" ), + + DEFINE_FIELD( m_flFuel, FIELD_FLOAT ), + DEFINE_FIELD( m_flDamageTime, FIELD_TIME ), + DEFINE_FIELD( m_lastDamage, FIELD_TIME ), + DEFINE_KEYFIELD( m_flFireSize, FIELD_FLOAT, "firesize" ), + + DEFINE_KEYFIELD( m_flHeatLevel, FIELD_FLOAT, "ignitionpoint" ), + DEFINE_FIELD( m_flHeatAbsorb, FIELD_FLOAT ), + DEFINE_KEYFIELD( m_flDamageScale,FIELD_FLOAT, "damagescale" ), + + DEFINE_FIELD( m_flMaxHeat, FIELD_FLOAT ), + //DEFINE_FIELD( m_flLastHeatLevel, FIELD_FLOAT ), + + DEFINE_KEYFIELD( m_flAttackTime, FIELD_FLOAT, "fireattack" ), + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + DEFINE_KEYFIELD( m_bStartDisabled, FIELD_BOOLEAN, "StartDisabled" ), + DEFINE_FIELD( m_bDidActivate, FIELD_BOOLEAN ), + + DEFINE_FUNCTION( BurnThink ), + DEFINE_FUNCTION( GoOutThink ), + + + + DEFINE_INPUTFUNC( FIELD_VOID, "StartFire", InputStartFire ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "Extinguish", InputExtinguish ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "ExtinguishTemporary", InputExtinguishTemporary ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + + DEFINE_OUTPUT( m_OnIgnited, "OnIgnited" ), + DEFINE_OUTPUT( m_OnExtinguished, "OnExtinguished" ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( env_fire, CFire ); + +//================================================== +// CFire +//================================================== + +CFire::CFire( void ) +{ + m_flFuel = 0.0f; + m_flAttackTime = 0.0f; + m_flDamageTime = 0.0f; + m_lastDamage = 0; + m_nFireType = FIRE_NATURAL; + + //Spreading + m_flHeatAbsorb = 8.0f; + m_flHeatLevel = 0; + + // Must be in the constructor! + AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); +} + +//----------------------------------------------------------------------------- +// UpdateOnRemove +//----------------------------------------------------------------------------- +void CFire::UpdateOnRemove( void ) +{ + //Stop any looping sounds that might be playing + StopSound( "Fire.Plasma" ); + + DestroyEffect(); + + // Chain at end to mimic destructor unwind order + BaseClass::UpdateOnRemove(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFire::Precache( void ) +{ + if ( m_nFireType == FIRE_NATURAL ) + { + UTIL_PrecacheOther("_firesmoke"); + + if ( m_spawnflags & SF_FIRE_SMOKELESS ) + { + PrecacheParticleSystem( "env_fire_tiny" ); + PrecacheParticleSystem( "env_fire_small" ); + PrecacheParticleSystem( "env_fire_medium" ); + PrecacheParticleSystem( "env_fire_large" ); + } + else + { + PrecacheParticleSystem( "env_fire_tiny_smoke" ); + PrecacheParticleSystem( "env_fire_small_smoke" ); + PrecacheParticleSystem( "env_fire_medium_smoke" ); + PrecacheParticleSystem( "env_fire_large_smoke" ); + } + } + + if ( m_nFireType == FIRE_PLASMA ) + { + UTIL_PrecacheOther("_plasma"); + } + + PrecacheScriptSound( "Fire.Plasma" ); +} + +//------------------------------------------------------------------------------ +// Purpose : Input handler for starting the fire. +//------------------------------------------------------------------------------ +void CFire::InputStartFire( inputdata_t &inputdata ) +{ + if ( !m_bEnabled ) + return; + + StartFire(); +} + +void CFire::InputEnable( inputdata_t &inputdata ) +{ + m_bEnabled = true; +} + +void CFire::InputDisable( inputdata_t &inputdata ) +{ + Disable(); +} + +void CFire::Disable() +{ + m_bEnabled = false; + if ( IsBurning() ) + { + GoOut(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CFire::InputExtinguish( inputdata_t &inputdata ) +{ + m_spawnflags &= ~SF_FIRE_INFINITE; + GoOutInSeconds( inputdata.value.Float() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CFire::InputExtinguishTemporary( inputdata_t &inputdata ) +{ + GoOutInSeconds( inputdata.value.Float() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Starts burning. +//----------------------------------------------------------------------------- +void CFire::StartFire( void ) +{ + if ( m_hEffect != NULL ) + return; + + // Trace down and start a fire there. Nothing fancy yet. + Vector vFirePos; + trace_t tr; + if ( m_spawnflags & SF_FIRE_DONT_DROP ) + { + vFirePos = GetAbsOrigin(); + } + else + { + UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1024 ), MASK_FIRE_SOLID, this, COLLISION_GROUP_NONE, &tr ); + vFirePos = tr.endpos; + } + + int spawnflags = m_spawnflags; + m_spawnflags |= SF_FIRE_START_ON; + Init( vFirePos, m_flFireSize, m_flAttackTime, GetHealth(), m_spawnflags, (fireType_e) m_nFireType ); + Start(); + m_spawnflags = spawnflags; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFire::Spawn( void ) +{ + BaseClass::Spawn(); + + Precache(); + + m_takedamage = DAMAGE_NO; + + SetSolid( SOLID_NONE ); + AddEffects( EF_NODRAW ); + SetToOutSize(); + + // set up the ignition point + m_flHeatAbsorb = m_flHeatLevel * 0.05; + m_flHeatLevel = 0; + Init( GetAbsOrigin(), m_flFireSize, m_flAttackTime, m_flFuel, m_spawnflags, m_nFireType ); + + if( m_bStartDisabled ) + { + Disable(); + } + else + { + m_bEnabled = true; + } +} + +int CFire::UpdateTransmitState() +{ + // Don't want to be FL_EDICT_DONTSEND because our fire entity may make us transmit. + return SetTransmitState( FL_EDICT_ALWAYS ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFire::Activate( void ) +{ + BaseClass::Activate(); + + //See if we should start active + if ( !m_bDidActivate && ( m_spawnflags & SF_FIRE_START_ON ) ) + { + m_flHeatLevel = m_flMaxHeat; + + StartFire(); + } + + m_bDidActivate = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFire::SpawnEffect( fireType_e type, float scale ) +{ + CBaseFire *pEffect = NULL; + switch ( type ) + { + default: + case FIRE_NATURAL: + { + CFireSmoke *fireSmoke = (CFireSmoke *) CreateEntityByName( "_firesmoke" ); + fireSmoke->EnableSmoke( ( m_spawnflags & SF_FIRE_SMOKELESS )==false ); + fireSmoke->EnableGlow( ( m_spawnflags & SF_FIRE_NO_GLOW )==false ); + fireSmoke->EnableVisibleFromAbove( ( m_spawnflags & SF_FIRE_VISIBLE_FROM_ABOVE )!=false ); + + pEffect = fireSmoke; + m_nFireType = FIRE_NATURAL; + m_takedamage = DAMAGE_YES; + } + break; + + case FIRE_PLASMA: + { + CPlasma *plasma = (CPlasma *) CreateEntityByName( "_plasma" ); + plasma->EnableSmoke( true ); + + pEffect = plasma; + m_nFireType = FIRE_PLASMA; + m_takedamage = DAMAGE_YES; + + // Start burn sound + EmitSound( "Fire.Plasma" ); + } + break; + } + + UTIL_SetOrigin( pEffect, GetAbsOrigin() ); + pEffect->Spawn(); + pEffect->SetParent( this ); + pEffect->Scale( m_flFireSize, m_flFireSize, 0 ); + //Start it going + pEffect->Enable( ( m_spawnflags & SF_FIRE_START_ON ) ); + m_hEffect = pEffect; +} + +//----------------------------------------------------------------------------- +// Purpose: Spawn and initialize the fire +// Input : &position - where the fire resides +// lifetime - +//----------------------------------------------------------------------------- +void CFire::Init( const Vector &position, float scale, float attackTime, float fuel, int flags, int fireType ) +{ + m_flAttackTime = attackTime; + + m_spawnflags = flags; + m_nFireType = fireType; + + if ( flags & SF_FIRE_INFINITE ) + { + fuel = 0; + } + m_flFuel = fuel; + if ( m_flFuel ) + { + m_spawnflags |= SF_FIRE_DIE_PERMANENT; + } + + Vector localOrigin = position; + if ( GetMoveParent() ) + { + EntityMatrix parentMatrix; + parentMatrix.InitFromEntity( GetMoveParent() ); + localOrigin = parentMatrix.WorldToLocal( position ); + } + UTIL_SetOrigin( this, localOrigin ); + + SetSolid( SOLID_NONE ); + m_flFireSize = scale; + m_flMaxHeat = FIRE_MAX_HEAT_LEVEL * FIRE_SCALE_FROM_SIZE(scale); + //See if we should start on + if ( m_spawnflags & SF_FIRE_START_FULL ) + { + m_flHeatLevel = m_flMaxHeat; + } + m_flLastHeatLevel = 0; + +} + +void CFire::Start() +{ + float boxWidth = (m_flFireSize * (FIRE_WIDTH/FIRE_HEIGHT))*0.5f; + UTIL_SetSize(this, Vector(-boxWidth,-boxWidth,0),Vector(boxWidth,boxWidth,m_flFireSize)); + + //Spawn the client-side effect + SpawnEffect( (fireType_e)m_nFireType, FIRE_SCALE_FROM_SIZE(m_flFireSize) ); + m_OnIgnited.FireOutput( this, this ); + SetThink( &CFire::BurnThink ); + m_flDamageTime = 0; + // think right now + BurnThink(); +} + +//----------------------------------------------------------------------------- +// Purpose: Determines whether or not the fire is still active +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CFire::IsBurning( void ) const +{ + if ( m_flHeatLevel > 0 ) + return true; + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Get the damage box of the fire +//----------------------------------------------------------------------------- +bool CFire::GetFireDimensions( Vector *pFireMins, Vector *pFireMaxs ) +{ + if ( m_flHeatLevel <= 0 ) + { + pFireMins->Init(); + pFireMaxs->Init(); + return false; + } + + float scale = m_flHeatLevel / m_flMaxHeat; + float damageRadius = scale * m_flFireSize * FIRE_WIDTH / FIRE_HEIGHT * 0.5; + + damageRadius *= FIRE_SPREAD_DAMAGE_MULTIPLIER; //FIXME: Trying slightly larger radius for burning + + if ( damageRadius < 16 ) + { + damageRadius = 16; + } + + pFireMins->Init(-damageRadius,-damageRadius,0); + pFireMaxs->Init(damageRadius,damageRadius,m_flFireSize*scale); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Update the fire and its children +//----------------------------------------------------------------------------- +void CFire::Update( float simTime ) +{ + VPROF_FIRE( "CFire::Update" ); + + if ( m_flFuel != 0 ) + { + m_flFuel -= simTime; + if ( m_flFuel <= 0 ) + { + GoOutInSeconds( 1 ); + return; + } + } + + float strength = m_flHeatLevel / FIRE_MAX_HEAT_LEVEL; + if ( m_flHeatLevel != m_flLastHeatLevel ) + { + m_flLastHeatLevel = m_flHeatLevel; + // Make the effect the appropriate size given the heat level + m_hEffect->Scale( strength, 0.5f ); + } + // add heat to myself (grow) + float addedHeat = (m_flAttackTime > 0) ? m_flMaxHeat / m_flAttackTime : m_flMaxHeat; + addedHeat *= simTime * fire_growthrate.GetFloat(); + AddHeat( addedHeat, true ); + + // add heat to nearby fires + float outputHeat = strength * m_flHeatLevel; + + Vector fireMins; + Vector fireMaxs; + Vector fireEntityDamageMins; + Vector fireEntityDamageMaxs; + + GetFireDimensions( &fireMins, &fireMaxs ); + + if ( FIRE_SPREAD_DAMAGE_MULTIPLIER != 1.0 ) // if set to 1.0, optimizer will remove this code + { + fireEntityDamageMins = fireMins / FIRE_SPREAD_DAMAGE_MULTIPLIER; + fireEntityDamageMaxs = fireMaxs / FIRE_SPREAD_DAMAGE_MULTIPLIER; + } + + //NDebugOverlay::Box( GetAbsOrigin(), fireMins, fireMaxs, 255, 255, 255, 0, fire_dmginterval.GetFloat() ); + fireMins += GetAbsOrigin(); + fireMaxs += GetAbsOrigin(); + + if ( FIRE_SPREAD_DAMAGE_MULTIPLIER != 1.0 ) + { + fireEntityDamageMins += GetAbsOrigin(); + fireEntityDamageMaxs += GetAbsOrigin(); + } + + CBaseEntity *pNearby[256]; + CFire *pFires[16]; + int nearbyCount = UTIL_EntitiesInBox( pNearby, ARRAYSIZE(pNearby), fireMins, fireMaxs, 0 ); + int fireCount = 0; + int i; + + // is it time to do damage? + bool damage = false; + int outputDamage = 0; + if ( m_flDamageTime <= gpGlobals->curtime ) + { + m_flDamageTime = gpGlobals->curtime + fire_dmginterval.GetFloat(); + outputDamage = (fire_dmgbase.GetFloat() + outputHeat * fire_dmgscale.GetFloat() * m_flDamageScale) * fire_dmginterval.GetFloat(); + if ( outputDamage ) + { + damage = true; + } + } + int damageFlags = (m_nFireType == FIRE_NATURAL) ? DMG_BURN : DMG_PLASMA; + for ( i = 0; i < nearbyCount; i++ ) + { + CBaseEntity *pOther = pNearby[i]; + + if ( pOther == this ) + { + continue; + } + else if ( FClassnameIs( pOther, "env_fire" ) ) + { + if ( fireCount < ARRAYSIZE(pFires) ) + { + pFires[fireCount] = (CFire *)pOther; + fireCount++; + } + continue; + } + else if ( pOther->m_takedamage == DAMAGE_NO ) + { + pNearby[i] = NULL; + } + else if ( damage ) + { + bool bDoDamage; + + if ( FIRE_SPREAD_DAMAGE_MULTIPLIER != 1.0 && !pOther->IsPlayer() ) // if set to 1.0, optimizer will remove this code + { + Vector otherMins, otherMaxs; + pOther->CollisionProp()->WorldSpaceAABB( &otherMins, &otherMaxs ); + bDoDamage = IsBoxIntersectingBox( otherMins, otherMaxs, + fireEntityDamageMins, fireEntityDamageMaxs ); + + } + else + bDoDamage = true; + + if ( bDoDamage ) + { + // Make sure can actually see entity (don't damage through walls) + trace_t tr; + UTIL_TraceLine( this->WorldSpaceCenter(), pOther->WorldSpaceCenter(), MASK_FIRE_SOLID, pOther, COLLISION_GROUP_NONE, &tr ); + + if (tr.fraction == 1.0 && !tr.startsolid) + { + pOther->TakeDamage( CTakeDamageInfo( this, this, outputDamage, damageFlags ) ); + } + } + } + } + + outputHeat *= fire_heatscale.GetFloat() * simTime; + + if ( fireCount > 0 ) + { + outputHeat /= fireCount; + for ( i = 0; i < fireCount; i++ ) + { + pFires[i]->AddHeat( outputHeat, false ); + } + } +} + +// Destroy any effect I have +void CFire::DestroyEffect() +{ + CBaseFire *pEffect = m_hEffect; + if ( pEffect != NULL ) + { + //disable the graphics and remove the entity + pEffect->Enable( false ); + UTIL_Remove( pEffect ); + } +} +//----------------------------------------------------------------------------- +// Purpose: Think +//----------------------------------------------------------------------------- +void CFire::BurnThink( void ) +{ + SetNextThink( gpGlobals->curtime + FIRE_THINK_INTERVAL ); + + Update( FIRE_THINK_INTERVAL ); +} + +void CFire::GoOutThink() +{ + GoOut(); +} + +void CFire::GoOutInSeconds( float seconds ) +{ + Scale( 0.0f, seconds ); + + SetThink( &CFire::GoOutThink ); + SetNextThink( gpGlobals->curtime + seconds ); +} + +//------------------------------------------------------------------------------ +// Purpose : Blasts of significant size blow out fires that take damage +// Input : +// Output : +//------------------------------------------------------------------------------ +int CFire::OnTakeDamage( const CTakeDamageInfo &info ) +{ + return 0; +} + +void CFire::AddHeat( float heat, bool selfHeat ) +{ + if ( m_bEnabled ) + { + if ( !selfHeat ) + { + if ( IsBurning() ) + { + // scale back the incoming heat from surrounding fires + // if I've already ignited + heat *= fire_incomingheatscale.GetFloat(); + } + } + m_lastDamage = gpGlobals->curtime + 0.5; + bool start = m_flHeatLevel <= 0 ? true : false; + if ( m_flHeatAbsorb > 0 ) + { + float absorbDamage = heat * fire_absorbrate.GetFloat(); + if ( absorbDamage > m_flHeatAbsorb ) + { + heat -= m_flHeatAbsorb / fire_absorbrate.GetFloat(); + m_flHeatAbsorb = 0; + } + else + { + m_flHeatAbsorb -= absorbDamage; + heat = 0; + } + } + + m_flHeatLevel += heat; + if ( start && m_flHeatLevel > 0 && m_hEffect == NULL ) + { + StartFire(); + } + if ( m_flHeatLevel > m_flMaxHeat ) + m_flHeatLevel = m_flMaxHeat; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : end - +// time - +//----------------------------------------------------------------------------- +void CFire::Scale( float end, float time ) +{ + CBaseFire *pEffect = m_hEffect; + if ( pEffect ) + { + pEffect->Scale( end, time ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : time - +//----------------------------------------------------------------------------- +void CFire::Extinguish( float heat ) +{ + if ( !m_bEnabled ) + return; + + m_lastDamage = gpGlobals->curtime + 0.5; + bool out = m_flHeatLevel > 0 ? true : false; + + m_flHeatLevel -= heat; + m_flHeatAbsorb += fire_extabsorb.GetFloat() * heat; + if ( m_flHeatAbsorb > fire_maxabsorb.GetFloat() ) + { + m_flHeatAbsorb = fire_maxabsorb.GetFloat(); + } + + // drift toward the average attack time after being sprayed + // some fires are heavily scripted so their attack looks weird + // once interacted with. Basically, this blends out the scripting + // as the fire is sprayed with the extinguisher. + float averageAttackTime = m_flMaxHeat * (FIRE_NORMAL_ATTACK_TIME/FIRE_MAX_HEAT_LEVEL); + m_flAttackTime = Approach( averageAttackTime, m_flAttackTime, 2 * gpGlobals->frametime ); + + if ( m_flHeatLevel <= 0 ) + { + m_flHeatLevel = 0; + if ( out ) + { + GoOut(); + } + } +} + +bool CFire::GoOut() +{ + //Signal death + m_OnExtinguished.FireOutput( this, this ); + + DestroyEffect(); + m_flHeatLevel -= 20; + if ( m_flHeatLevel > 0 ) + m_flHeatLevel = 0; + + m_flLastHeatLevel = m_flHeatLevel; + SetThink(NULL); + SetNextThink( TICK_NEVER_THINK ); + if ( m_spawnflags & SF_FIRE_DIE_PERMANENT ) + { + UTIL_Remove( this ); + return true; + } + SetToOutSize(); + + return false; +} + +//================================================== +// CEnvFireSource is a source of heat that the player +// cannot put out +//================================================== + +#define FIRESOURCE_THINK_TIME 0.25 // seconds to + +#define SF_FIRESOURCE_START_ON 0x0001 + +class CEnvFireSource : public CBaseEntity +{ + DECLARE_CLASS( CEnvFireSource, CBaseEntity ); +public: + void Spawn(); + void Think(); + void TurnOn(); + void TurnOff(); + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + + DECLARE_DATADESC(); + +private: + bool m_bEnabled; + float m_radius; + float m_damage; +}; + +BEGIN_DATADESC( CEnvFireSource ) + + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "fireradius" ), + DEFINE_KEYFIELD( m_damage,FIELD_FLOAT, "firedamage" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( env_firesource, CEnvFireSource ); + +void CEnvFireSource::Spawn() +{ + if ( m_spawnflags & SF_FIRESOURCE_START_ON ) + { + TurnOn(); + } + else + { + TurnOff(); + } +} + +void CEnvFireSource::Think() +{ + if ( !m_bEnabled ) + return; + SetNextThink( gpGlobals->curtime + FIRESOURCE_THINK_TIME ); + + CFire *pFires[128]; + int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), false, GetAbsOrigin(), m_radius ); + + for ( int i = 0; i < fireCount; i++ ) + { + pFires[i]->AddHeat( m_damage * FIRESOURCE_THINK_TIME ); + } +} + +void CEnvFireSource::TurnOn() +{ + if ( m_bEnabled ) + return; + + m_bEnabled = true; + SetNextThink( gpGlobals->curtime ); +} + +void CEnvFireSource::TurnOff() +{ + if ( !m_bEnabled ) + return; + + m_bEnabled = false; + SetNextThink( TICK_NEVER_THINK ); +} +void CEnvFireSource::InputEnable( inputdata_t &inputdata ) +{ + TurnOn(); +} +void CEnvFireSource::InputDisable( inputdata_t &inputdata ) +{ + TurnOff(); +} + +//================================================== +// CEnvFireSensor detects changes in heat +//================================================== +#define SF_FIRESENSOR_START_ON 1 + +class CEnvFireSensor : public CBaseEntity +{ + DECLARE_CLASS( CEnvFireSensor, CBaseEntity ); +public: + void Spawn(); + void Think(); + void TurnOn(); + void TurnOff(); + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + + DECLARE_DATADESC(); + +private: + bool m_bEnabled; + bool m_bHeatAtLevel; + float m_radius; + float m_targetLevel; + float m_targetTime; + float m_levelTime; + + COutputEvent m_OnHeatLevelStart; + COutputEvent m_OnHeatLevelEnd; +}; + +BEGIN_DATADESC( CEnvFireSensor ) + + DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "fireradius" ), + DEFINE_KEYFIELD( m_targetLevel, FIELD_FLOAT, "heatlevel" ), + DEFINE_KEYFIELD( m_targetTime, FIELD_FLOAT, "heattime" ), + + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bHeatAtLevel, FIELD_BOOLEAN ), + DEFINE_FIELD( m_levelTime, FIELD_FLOAT ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + + DEFINE_OUTPUT( m_OnHeatLevelStart, "OnHeatLevelStart"), + DEFINE_OUTPUT( m_OnHeatLevelEnd, "OnHeatLevelEnd"), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( env_firesensor, CEnvFireSensor ); + +void CEnvFireSensor::Spawn() +{ + if ( m_spawnflags & SF_FIRESENSOR_START_ON ) + { + TurnOn(); + } + else + { + TurnOff(); + } +} + +void CEnvFireSensor::Think() +{ + if ( !m_bEnabled ) + return; + + float time = m_targetTime * 0.25; + if ( time < 0.1 ) + { + time = 0.1; + } + SetNextThink( gpGlobals->curtime + time ); + + float heat = 0; + CFire *pFires[128]; + int fireCount = FireSystem_GetFiresInSphere( pFires, ARRAYSIZE(pFires), true, GetAbsOrigin(), m_radius ); + for ( int i = 0; i < fireCount; i++ ) + { + heat += pFires[i]->GetHeatLevel(); + } + + if ( heat >= m_targetLevel ) + { + m_levelTime += time; + if ( m_levelTime >= m_targetTime ) + { + if ( !m_bHeatAtLevel ) + { + m_bHeatAtLevel = true; + m_OnHeatLevelStart.FireOutput( this, this ); + } + } + } + else + { + m_levelTime = 0; + if ( m_bHeatAtLevel ) + { + m_bHeatAtLevel = false; + m_OnHeatLevelEnd.FireOutput( this, this ); + } + } +} + +void CEnvFireSensor::TurnOn() +{ + if ( m_bEnabled ) + return; + + m_bEnabled = true; + SetNextThink( gpGlobals->curtime ); + m_bHeatAtLevel = false; + m_levelTime = 0; +} + +void CEnvFireSensor::TurnOff() +{ + if ( !m_bEnabled ) + return; + + m_bEnabled = false; + SetNextThink( TICK_NEVER_THINK ); + if ( m_bHeatAtLevel ) + { + m_bHeatAtLevel = false; + m_OnHeatLevelEnd.FireOutput( this, this ); + } + +} +void CEnvFireSensor::InputEnable( inputdata_t &inputdata ) +{ + TurnOn(); +} +void CEnvFireSensor::InputDisable( inputdata_t &inputdata ) +{ + TurnOff(); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CFire::DrawDebugTextOverlays( void ) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + + // print flame size + Q_snprintf(tempstr,sizeof(tempstr)," size: %f", m_flFireSize); + EntityText(text_offset,tempstr,0); + text_offset++; + } + return text_offset; +} |