summaryrefslogtreecommitdiff
path: root/game/client/fx_sparks.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/fx_sparks.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/fx_sparks.cpp')
-rw-r--r--game/client/fx_sparks.cpp1537
1 files changed, 1537 insertions, 0 deletions
diff --git a/game/client/fx_sparks.cpp b/game/client/fx_sparks.cpp
new file mode 100644
index 0000000..38df4db
--- /dev/null
+++ b/game/client/fx_sparks.cpp
@@ -0,0 +1,1537 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "view.h"
+#include "viewrender.h"
+#include "c_tracer.h"
+#include "dlight.h"
+#include "clienteffectprecachesystem.h"
+#include "fx_sparks.h"
+#include "iefx.h"
+#include "c_te_effect_dispatch.h"
+#include "tier0/vprof.h"
+#include "fx_quad.h"
+#include "fx.h"
+#include "c_pixel_visibility.h"
+#include "particles_ez.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//Precahce the effects
+CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectSparks )
+CLIENTEFFECT_MATERIAL( "effects/spark" )
+CLIENTEFFECT_MATERIAL( "effects/energysplash" )
+CLIENTEFFECT_MATERIAL( "effects/energyball" )
+CLIENTEFFECT_MATERIAL( "sprites/rico1" )
+CLIENTEFFECT_MATERIAL( "sprites/rico1_noz" )
+CLIENTEFFECT_MATERIAL( "sprites/blueflare1" )
+CLIENTEFFECT_MATERIAL( "effects/yellowflare" )
+CLIENTEFFECT_MATERIAL( "effects/combinemuzzle1_nocull" )
+CLIENTEFFECT_MATERIAL( "effects/combinemuzzle2_nocull" )
+CLIENTEFFECT_MATERIAL( "effects/yellowflare_noz" )
+CLIENTEFFECT_REGISTER_END()
+
+PMaterialHandle g_Material_Spark = NULL;
+
+static ConVar fx_drawmetalspark( "fx_drawmetalspark", "1", FCVAR_DEVELOPMENTONLY, "Draw metal spark effects." );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &pos -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool EffectOccluded( const Vector &pos, pixelvis_handle_t *queryHandle )
+{
+ if ( !queryHandle )
+ {
+ // NOTE: This is called by networking code before the current view is set up.
+ // so use the main view instead
+ trace_t tr;
+ UTIL_TraceLine( pos, MainViewOrigin(), MASK_OPAQUE, NULL, COLLISION_GROUP_NONE, &tr );
+
+ return ( tr.fraction < 1.0f ) ? true : false;
+ }
+ pixelvis_queryparams_t params;
+ params.Init(pos);
+
+ return PixelVisibility_FractionVisible( params, queryHandle ) > 0.0f ? false : true;
+}
+
+
+CSimpleGlowEmitter::CSimpleGlowEmitter( const char *pDebugName, const Vector &sortOrigin, float flDeathTime )
+ : CSimpleEmitter( pDebugName )
+{
+ SetSortOrigin( sortOrigin );
+ m_queryHandle = 0;
+ m_wasTested = 0;
+ m_isVisible = 0;
+ m_startTime = gpGlobals->curtime;
+ m_flDeathTime = flDeathTime;
+}
+
+CSimpleGlowEmitter *CSimpleGlowEmitter::Create( const char *pDebugName, const Vector &sortOrigin, float flDeathTime )
+{
+ return new CSimpleGlowEmitter( pDebugName, sortOrigin, flDeathTime );
+}
+
+
+void CSimpleGlowEmitter::SimulateParticles( CParticleSimulateIterator *pIterator )
+{
+ if ( gpGlobals->curtime > m_flDeathTime )
+ {
+ pIterator->RemoveAllParticles();
+ return;
+ }
+
+ if ( !WasTestedInView(1<<0) )
+ return;
+
+ BaseClass::SimulateParticles( pIterator );
+}
+
+bool CSimpleGlowEmitter::WasTestedInView( unsigned char viewMask )
+{
+ return (m_wasTested & viewMask) ? true : false;
+}
+
+bool CSimpleGlowEmitter::IsVisibleInView( unsigned char viewMask )
+{
+ return (m_isVisible & viewMask) ? true : false;
+}
+
+void CSimpleGlowEmitter::SetTestedInView( unsigned char viewMask, bool bTested )
+{
+ m_wasTested &= ~viewMask;
+ if ( bTested )
+ {
+ m_wasTested |= viewMask;
+ }
+}
+
+void CSimpleGlowEmitter::SetVisibleInView( unsigned char viewMask, bool bVisible )
+{
+ m_isVisible &= ~viewMask;
+ if ( bVisible )
+ {
+ m_isVisible |= viewMask;
+ }
+}
+
+unsigned char CSimpleGlowEmitter::CurrentViewMask() const
+{
+ int viewId = (int)CurrentViewID();
+ viewId = clamp(viewId, 0, 7);
+ return 1<<viewId;
+}
+
+void CSimpleGlowEmitter::RenderParticles( CParticleRenderIterator *pIterator )
+{
+ unsigned char viewMask = CurrentViewMask();
+ if ( !WasTestedInView(CurrentViewMask()) )
+ {
+ pixelvis_queryparams_t params;
+ params.Init(GetSortOrigin());
+
+ float visible = PixelVisibility_FractionVisible( params, &m_queryHandle );
+ if ( visible == 0.0f )
+ {
+ if ( (gpGlobals->curtime - m_startTime) <= 0.1f )
+ return;
+ SetVisibleInView(viewMask, false);
+ }
+ else
+ {
+ SetVisibleInView(viewMask, true);
+ }
+
+ SetTestedInView(viewMask, true);
+ }
+ if ( !IsVisibleInView(viewMask) )
+ return;
+
+ BaseClass::RenderParticles( pIterator );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CTrailParticles::CTrailParticles( const char *pDebugName ) : CSimpleEmitter( pDebugName )
+{
+ m_fFlags = 0;
+ m_flVelocityDampen = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Test for surrounding collision surfaces for quick collision testing for the particle system
+// Input : &origin - starting position
+// *dir - direction of movement (if NULL, will do a point emission test in four directions)
+// angularSpread - looseness of the spread
+// minSpeed - minimum speed
+// maxSpeed - maximum speed
+// gravity - particle gravity for the sytem
+// dampen - dampening amount on collisions
+// flags - extra information
+//-----------------------------------------------------------------------------
+void CTrailParticles::Setup( const Vector &origin, const Vector *direction, float angularSpread, float minSpeed, float maxSpeed, float gravity, float dampen, int flags, bool bNotCollideable )
+{
+ //Take the flags
+ if ( !bNotCollideable )
+ {
+ SetFlag( (flags|bitsPARTICLE_TRAIL_COLLIDE) ); //Force this if they've called this function
+ }
+ else
+ {
+ SetFlag( flags );
+ }
+
+ //See if we've specified a direction
+ m_ParticleCollision.Setup( origin, direction, angularSpread, minSpeed, maxSpeed, gravity, dampen );
+}
+
+
+void CTrailParticles::RenderParticles( CParticleRenderIterator *pIterator )
+{
+ const TrailParticle *pParticle = (const TrailParticle*)pIterator->GetFirst();
+ while ( pParticle )
+ {
+ //Get our remaining time
+ float lifePerc = 1.0f - ( pParticle->m_flLifetime / pParticle->m_flDieTime );
+ float scale = (pParticle->m_flLength*lifePerc);
+
+ if ( scale < 0.01f )
+ scale = 0.01f;
+
+ Vector start, delta;
+
+ //NOTE: We need to do everything in screen space
+ TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, start );
+ float sortKey = start.z;
+
+ Vector3DMultiply( ParticleMgr()->GetModelView(), pParticle->m_vecVelocity, delta );
+
+ float color[4];
+ float ramp = 1.0;
+
+ // Fade in for the first few frames
+ if ( pParticle->m_flLifetime <= 0.3 && m_fFlags & bitsPARTICLE_TRAIL_FADE_IN )
+ {
+ ramp = pParticle->m_flLifetime;
+ }
+ else if ( m_fFlags & bitsPARTICLE_TRAIL_FADE )
+ {
+ ramp = ( 1.0f - ( pParticle->m_flLifetime / pParticle->m_flDieTime ) );
+ }
+
+ color[0] = pParticle->m_color.r * ramp * (1.0f / 255.0f);
+ color[1] = pParticle->m_color.g * ramp * (1.0f / 255.0f);
+ color[2] = pParticle->m_color.b * ramp * (1.0f / 255.0f);
+ color[3] = pParticle->m_color.a * ramp * (1.0f / 255.0f);
+
+ float flLength = (pParticle->m_vecVelocity * scale).Length();//( delta - pos ).Length();
+ float flWidth = ( flLength < pParticle->m_flWidth ) ? flLength : pParticle->m_flWidth;
+
+ //See if we should fade
+ Vector vecScaledDelta = (delta*scale);
+ Tracer_Draw( pIterator->GetParticleDraw(), start, vecScaledDelta, flWidth, color );
+
+ pParticle = (const TrailParticle*)pIterator->GetNext( sortKey );
+ }
+}
+
+
+void CTrailParticles::SimulateParticles( CParticleSimulateIterator *pIterator )
+{
+ //Turn off collision if we're not told to do it
+ if (( m_fFlags & bitsPARTICLE_TRAIL_COLLIDE )==false)
+ {
+ m_ParticleCollision.ClearActivePlanes();
+ }
+
+ TrailParticle *pParticle = (TrailParticle*)pIterator->GetFirst();
+ while ( pParticle )
+ {
+ const float timeDelta = pIterator->GetTimeDelta();
+
+ //Simulate the movement with collision
+ trace_t trace;
+ m_ParticleCollision.MoveParticle( pParticle->m_Pos, pParticle->m_vecVelocity, NULL, timeDelta, &trace );
+
+ //Laterally dampen if asked to do so
+ if ( m_fFlags & bitsPARTICLE_TRAIL_VELOCITY_DAMPEN )
+ {
+ float attenuation = 1.0f - (timeDelta * m_flVelocityDampen);
+
+ if ( attenuation < 0.0f )
+ attenuation = 0.0f;
+
+ //Laterally dampen
+ pParticle->m_vecVelocity[0] *= attenuation;
+ pParticle->m_vecVelocity[1] *= attenuation;
+ pParticle->m_vecVelocity[2] *= attenuation;
+ }
+
+ //Should this particle die?
+ pParticle->m_flLifetime += timeDelta;
+
+ if ( pParticle->m_flLifetime >= pParticle->m_flDieTime )
+ pIterator->RemoveParticle( pParticle );
+
+ pParticle = (TrailParticle*)pIterator->GetNext();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Electric spark
+// Input : &pos - origin point of effect
+//-----------------------------------------------------------------------------
+#define SPARK_ELECTRIC_SPREAD 0.0f
+#define SPARK_ELECTRIC_MINSPEED 64.0f
+#define SPARK_ELECTRIC_MAXSPEED 300.0f
+#define SPARK_ELECTRIC_GRAVITY 800.0f
+#define SPARK_ELECTRIC_DAMPEN 0.3f
+
+void FX_ElectricSpark( const Vector &pos, int nMagnitude, int nTrailLength, const Vector *vecDir )
+{
+ VPROF_BUDGET( "FX_ElectricSpark", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
+ CSmartPtr<CTrailParticles> pSparkEmitter = CTrailParticles::Create( "FX_ElectricSpark 1" );
+
+ if ( !pSparkEmitter )
+ {
+ Assert(0);
+ return;
+ }
+
+ if ( g_Material_Spark == NULL )
+ {
+ g_Material_Spark = pSparkEmitter->GetPMaterial( "effects/spark" );
+ }
+
+ //Setup our collision information
+ pSparkEmitter->Setup( (Vector &) pos,
+ NULL,
+ SPARK_ELECTRIC_SPREAD,
+ SPARK_ELECTRIC_MINSPEED,
+ SPARK_ELECTRIC_MAXSPEED,
+ SPARK_ELECTRIC_GRAVITY,
+ SPARK_ELECTRIC_DAMPEN,
+ bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
+
+ pSparkEmitter->SetSortOrigin( pos );
+
+ //
+ // Big sparks.
+ //
+ Vector dir;
+ int numSparks = nMagnitude * nMagnitude * random->RandomFloat( 2, 4 );
+
+ int i;
+ TrailParticle *pParticle;
+ for ( i = 0; i < numSparks; i++ )
+ {
+ pParticle = (TrailParticle *) pSparkEmitter->AddParticle( sizeof(TrailParticle), g_Material_Spark, pos );
+
+ if ( pParticle == NULL )
+ return;
+
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = nMagnitude * random->RandomFloat( 1.0f, 2.0f );
+
+ dir.Random( -1.0f, 1.0f );
+ dir[2] = random->RandomFloat( 0.5f, 1.0f );
+
+ if ( vecDir )
+ {
+ dir += 2 * (*vecDir);
+ VectorNormalize( dir );
+ }
+
+ pParticle->m_flWidth = random->RandomFloat( 2.0f, 5.0f );
+ pParticle->m_flLength = nTrailLength * random->RandomFloat( 0.02, 0.05f );
+
+ pParticle->m_vecVelocity = dir * random->RandomFloat( SPARK_ELECTRIC_MINSPEED, SPARK_ELECTRIC_MAXSPEED );
+
+ Color32Init( pParticle->m_color, 255, 255, 255, 255 );
+ }
+
+#ifdef _XBOX
+
+ //
+ // Cap
+ //
+
+ SimpleParticle sParticle;
+
+ sParticle.m_Pos = pos;
+ sParticle.m_flLifetime = 0.0f;
+ sParticle.m_flDieTime = 0.2f;
+
+ sParticle.m_vecVelocity.Init();
+
+ sParticle.m_uchColor[0] = 255;
+ sParticle.m_uchColor[1] = 255;
+ sParticle.m_uchColor[2] = 255;
+ sParticle.m_uchStartAlpha = 255;
+ sParticle.m_uchEndAlpha = 255;
+ sParticle.m_uchStartSize = nMagnitude * random->RandomInt( 4, 8 );
+ sParticle.m_uchEndSize = 0;
+ sParticle.m_flRoll = random->RandomInt( 0, 360 );
+ sParticle.m_flRollDelta = 0.0f;
+
+ AddSimpleParticle( &sParticle, ParticleMgr()->GetPMaterial( "effects/yellowflare" ) );
+
+#else
+
+ //
+ // Little sparks
+ //
+
+ CSmartPtr<CTrailParticles> pSparkEmitter2 = CTrailParticles::Create( "FX_ElectricSpark 2" );
+
+ if ( !pSparkEmitter2 )
+ {
+ Assert(0);
+ return;
+ }
+
+ pSparkEmitter2->SetSortOrigin( pos );
+
+ pSparkEmitter2->m_ParticleCollision.SetGravity( 400.0f );
+ pSparkEmitter2->SetFlag( bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
+
+ numSparks = nMagnitude * random->RandomInt( 16, 32 );
+
+ // Dump out sparks
+ for ( i = 0; i < numSparks; i++ )
+ {
+ pParticle = (TrailParticle *) pSparkEmitter2->AddParticle( sizeof(TrailParticle), g_Material_Spark, pos );
+
+ if ( pParticle == NULL )
+ return;
+
+ pParticle->m_flLifetime = 0.0f;
+
+ dir.Random( -1.0f, 1.0f );
+ if ( vecDir )
+ {
+ dir += *vecDir;
+ VectorNormalize( dir );
+ }
+
+ pParticle->m_flWidth = random->RandomFloat( 2.0f, 4.0f );
+ pParticle->m_flLength = nTrailLength * random->RandomFloat( 0.02f, 0.03f );
+ pParticle->m_flDieTime = nMagnitude * random->RandomFloat( 0.1f, 0.2f );
+
+ pParticle->m_vecVelocity = dir * random->RandomFloat( 128, 256 );
+
+ Color32Init( pParticle->m_color, 255, 255, 255, 255 );
+ }
+
+ //
+ // Caps
+ //
+ CSmartPtr<CSimpleGlowEmitter> pSimple = CSimpleGlowEmitter::Create( "FX_ElectricSpark 3", pos, gpGlobals->curtime + 0.2 );
+
+ // NOTE: None of these will render unless the effect is visible!
+ //
+ // Inner glow
+ //
+ SimpleParticle *sParticle;
+
+ sParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "effects/yellowflare_noz" ), pos );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_flLifetime = 0.0f;
+ sParticle->m_flDieTime = 0.2f;
+
+ sParticle->m_vecVelocity.Init();
+
+ sParticle->m_uchColor[0] = 255;
+ sParticle->m_uchColor[1] = 255;
+ sParticle->m_uchColor[2] = 255;
+ sParticle->m_uchStartAlpha = 255;
+ sParticle->m_uchEndAlpha = 255;
+ sParticle->m_uchStartSize = nMagnitude * random->RandomInt( 4, 8 );
+ sParticle->m_uchEndSize = 0;
+ sParticle->m_flRoll = random->RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 0.0f;
+
+ sParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "effects/yellowflare_noz" ), pos );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_flLifetime = 0.0f;
+ sParticle->m_flDieTime = 0.2f;
+
+ sParticle->m_vecVelocity.Init();
+
+ float fColor = random->RandomInt( 32, 64 );
+ sParticle->m_uchColor[0] = fColor;
+ sParticle->m_uchColor[1] = fColor;
+ sParticle->m_uchColor[2] = fColor;
+ sParticle->m_uchStartAlpha = fColor;
+ sParticle->m_uchEndAlpha = 0;
+ sParticle->m_uchStartSize = nMagnitude * random->RandomInt( 32, 64 );
+ sParticle->m_uchEndSize = 0;
+ sParticle->m_flRoll = random->RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = random->RandomFloat( -1.0f, 1.0f );
+
+ //
+ // Smoke
+ //
+ Vector sOffs;
+
+ sOffs[0] = pos[0] + random->RandomFloat( -4.0f, 4.0f );
+ sOffs[1] = pos[1] + random->RandomFloat( -4.0f, 4.0f );
+ sOffs[2] = pos[2];
+
+ sParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Mat_DustPuff[1], sOffs );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_flLifetime = 0.0f;
+ sParticle->m_flDieTime = 1.0f;
+
+ sParticle->m_vecVelocity.Init();
+
+ sParticle->m_vecVelocity[2] = 16.0f;
+
+ sParticle->m_vecVelocity[0] = random->RandomFloat( -16.0f, 16.0f );
+ sParticle->m_vecVelocity[1] = random->RandomFloat( -16.0f, 16.0f );
+
+ sParticle->m_uchColor[0] = 255;
+ sParticle->m_uchColor[1] = 255;
+ sParticle->m_uchColor[2] = 200;
+ sParticle->m_uchStartAlpha = random->RandomInt( 16, 32 );
+ sParticle->m_uchEndAlpha = 0;
+ sParticle->m_uchStartSize = random->RandomInt( 4, 8 );
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize*4.0f;
+ sParticle->m_flRoll = random->RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = random->RandomFloat( -2.0f, 2.0f );
+
+ //
+ // Dlight
+ //
+
+ /*
+ dlight_t *dl= effects->CL_AllocDlight ( 0 );
+
+ dl->origin = pos;
+ dl->color.r = dl->color.g = dl->color.b = 250;
+ dl->radius = random->RandomFloat(16,32);
+ dl->die = gpGlobals->curtime + 0.001;
+ */
+
+#endif // !_XBOX
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sparks created by scraping metal
+// Input : &position - start
+// &normal - direction of spark travel
+//-----------------------------------------------------------------------------
+
+#define METAL_SCRAPE_MINSPEED 128.0f
+#define METAL_SCRAPE_MAXSPEED 512.0f
+#define METAL_SCRAPE_SPREAD 0.3f
+#define METAL_SCRAPE_GRAVITY 800.0f
+#define METAL_SCRAPE_DAMPEN 0.4f
+
+void FX_MetalScrape( Vector &position, Vector &normal )
+{
+ VPROF_BUDGET( "FX_MetalScrape", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
+ Vector offset = position + ( normal * 1.0f );
+
+ CSmartPtr<CTrailParticles> sparkEmitter = CTrailParticles::Create( "FX_MetalScrape 1" );
+
+ if ( !sparkEmitter )
+ return;
+
+ sparkEmitter->SetSortOrigin( offset );
+
+ //Setup our collision information
+ sparkEmitter->Setup( offset,
+ &normal,
+ METAL_SCRAPE_SPREAD,
+ METAL_SCRAPE_MINSPEED,
+ METAL_SCRAPE_MAXSPEED,
+ METAL_SCRAPE_GRAVITY,
+ METAL_SCRAPE_DAMPEN,
+ bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
+
+ int numSparks = random->RandomInt( 4, 8 );
+
+ if ( g_Material_Spark == NULL )
+ {
+ g_Material_Spark = sparkEmitter->GetPMaterial( "effects/spark" );
+ }
+
+ Vector dir;
+ TrailParticle *pParticle;
+ float length = 0.06f;
+
+ //Dump out sparks
+ for ( int i = 0; i < numSparks; i++ )
+ {
+ pParticle = (TrailParticle *) sparkEmitter->AddParticle( sizeof(TrailParticle), g_Material_Spark, offset );
+
+ if ( pParticle == NULL )
+ return;
+
+ pParticle->m_flLifetime = 0.0f;
+
+ float spreadOfs = random->RandomFloat( 0.0f, 2.0f );
+
+ dir[0] = normal[0] + random->RandomFloat( -(METAL_SCRAPE_SPREAD*spreadOfs), (METAL_SCRAPE_SPREAD*spreadOfs) );
+ dir[1] = normal[1] + random->RandomFloat( -(METAL_SCRAPE_SPREAD*spreadOfs), (METAL_SCRAPE_SPREAD*spreadOfs) );
+ dir[2] = normal[2] + random->RandomFloat( -(METAL_SCRAPE_SPREAD*spreadOfs), (METAL_SCRAPE_SPREAD*spreadOfs) );
+
+ pParticle->m_flWidth = random->RandomFloat( 2.0f, 5.0f );
+ pParticle->m_flLength = random->RandomFloat( length*0.25f, length );
+ pParticle->m_flDieTime = random->RandomFloat( 2.0f, 2.0f );
+
+ pParticle->m_vecVelocity = dir * random->RandomFloat( (METAL_SCRAPE_MINSPEED*(2.0f-spreadOfs)), (METAL_SCRAPE_MAXSPEED*(2.0f-spreadOfs)) );
+
+ Color32Init( pParticle->m_color, 255, 255, 255, 255 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ricochet spark on metal
+// Input : &position - origin of effect
+// &normal - normal of the surface struck
+//-----------------------------------------------------------------------------
+#define METAL_SPARK_SPREAD 0.5f
+#define METAL_SPARK_MINSPEED 128.0f
+#define METAL_SPARK_MAXSPEED 512.0f
+#define METAL_SPARK_GRAVITY 400.0f
+#define METAL_SPARK_DAMPEN 0.25f
+
+void FX_MetalSpark( const Vector &position, const Vector &direction, const Vector &surfaceNormal, int iScale )
+{
+ VPROF_BUDGET( "FX_MetalSpark", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
+
+ if ( !fx_drawmetalspark.GetBool() )
+ return;
+
+ //
+ // Emitted particles
+ //
+
+ Vector offset = position + ( surfaceNormal * 1.0f );
+
+ CSmartPtr<CTrailParticles> sparkEmitter = CTrailParticles::Create( "FX_MetalSpark 1" );
+
+ if ( sparkEmitter == NULL )
+ return;
+
+ //Setup our information
+ sparkEmitter->SetSortOrigin( offset );
+ sparkEmitter->SetFlag( bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
+ sparkEmitter->SetVelocityDampen( 8.0f );
+ sparkEmitter->SetGravity( METAL_SPARK_GRAVITY );
+ sparkEmitter->SetCollisionDamped( METAL_SPARK_DAMPEN );
+ sparkEmitter->GetBinding().SetBBox( offset - Vector( 32, 32, 32 ), offset + Vector( 32, 32, 32 ) );
+
+ int numSparks = random->RandomInt( 4, 8 ) * ( iScale * 2 );
+ numSparks = (int)( 0.5f + (float)numSparks * g_pParticleSystemMgr->ParticleThrottleScaling() );
+
+ if ( g_Material_Spark == NULL )
+ {
+ g_Material_Spark = sparkEmitter->GetPMaterial( "effects/spark" );
+ }
+
+ TrailParticle *pParticle;
+ Vector dir;
+ float length = 0.1f;
+
+ //Dump out sparks
+ for ( int i = 0; i < numSparks; i++ )
+ {
+ pParticle = (TrailParticle *) sparkEmitter->AddParticle( sizeof(TrailParticle), g_Material_Spark, offset );
+
+ if ( pParticle == NULL )
+ return;
+
+ pParticle->m_flLifetime = 0.0f;
+
+ if( iScale > 1 && i%3 == 0 )
+ {
+ // Every third spark goes flying far if we're having a big batch of sparks.
+ pParticle->m_flDieTime = random->RandomFloat( 0.15f, 0.25f );
+ }
+ else
+ {
+ pParticle->m_flDieTime = random->RandomFloat( 0.05f, 0.1f );
+ }
+
+ float spreadOfs = random->RandomFloat( 0.0f, 2.0f );
+
+ dir[0] = direction[0] + random->RandomFloat( -(METAL_SPARK_SPREAD*spreadOfs), (METAL_SPARK_SPREAD*spreadOfs) );
+ dir[1] = direction[1] + random->RandomFloat( -(METAL_SPARK_SPREAD*spreadOfs), (METAL_SPARK_SPREAD*spreadOfs) );
+ dir[2] = direction[2] + random->RandomFloat( -(METAL_SPARK_SPREAD*spreadOfs), (METAL_SPARK_SPREAD*spreadOfs) );
+
+ VectorNormalize( dir );
+
+ pParticle->m_flWidth = random->RandomFloat( 1.0f, 4.0f );
+ pParticle->m_flLength = random->RandomFloat( length*0.25f, length );
+
+ pParticle->m_vecVelocity = dir * random->RandomFloat( (METAL_SPARK_MINSPEED*(2.0f-spreadOfs)), (METAL_SPARK_MAXSPEED*(2.0f-spreadOfs)) );
+
+ Color32Init( pParticle->m_color, 255, 255, 255, 255 );
+ }
+
+ //
+ // Impact point glow
+ //
+
+ FXQuadData_t data;
+
+ data.SetMaterial( "effects/yellowflare" );
+ data.SetColor( 1.0f, 1.0f, 1.0f );
+ data.SetOrigin( offset );
+ data.SetNormal( surfaceNormal );
+ data.SetAlpha( 1.0f, 0.0f );
+ data.SetLifeTime( 0.1f );
+ data.SetYaw( random->RandomInt( 0, 360 ) );
+
+ int scale = random->RandomInt( 24, 28 );
+ data.SetScale( scale, 0 );
+
+ FX_AddQuad( data );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Spark effect. Nothing but sparks.
+// Input : &pos - origin point of effect
+//-----------------------------------------------------------------------------
+#define SPARK_SPREAD 3.0f
+#define SPARK_GRAVITY 800.0f
+#define SPARK_DAMPEN 0.3f
+
+void FX_Sparks( const Vector &pos, int nMagnitude, int nTrailLength, const Vector &vecDir, float flWidth, float flMinSpeed, float flMaxSpeed, char *pSparkMaterial )
+{
+ VPROF_BUDGET( "FX_Sparks", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
+ CSmartPtr<CTrailParticles> pSparkEmitter = CTrailParticles::Create( "FX_Sparks 1" );
+
+ if ( !pSparkEmitter )
+ {
+ Assert(0);
+ return;
+ }
+
+ PMaterialHandle hMaterial;
+ if ( pSparkMaterial )
+ {
+ hMaterial = pSparkEmitter->GetPMaterial( pSparkMaterial );
+ }
+ else
+ {
+ if ( g_Material_Spark == NULL )
+ {
+ g_Material_Spark = pSparkEmitter->GetPMaterial( "effects/spark" );
+ }
+
+ hMaterial = g_Material_Spark;
+ }
+
+ //Setup our collision information
+ pSparkEmitter->Setup( (Vector &) pos,
+ NULL,
+ SPARK_SPREAD,
+ flMinSpeed,
+ flMaxSpeed,
+ SPARK_GRAVITY,
+ SPARK_DAMPEN,
+ bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
+
+ pSparkEmitter->SetSortOrigin( pos );
+
+ //
+ // Big sparks.
+ //
+ Vector dir;
+ int numSparks = nMagnitude * nMagnitude * random->RandomFloat( 2, 4 );
+
+ int i;
+ TrailParticle *pParticle;
+ for ( i = 0; i < numSparks; i++ )
+ {
+ pParticle = (TrailParticle *) pSparkEmitter->AddParticle( sizeof(TrailParticle), hMaterial, pos );
+
+ if ( pParticle == NULL )
+ return;
+
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = nMagnitude * random->RandomFloat( 1.0f, 2.0f );
+
+ float spreadOfs = random->RandomFloat( 0.0f, 2.0f );
+ dir[0] = vecDir[0] + random->RandomFloat( -(SPARK_SPREAD*spreadOfs), (SPARK_SPREAD*spreadOfs) );
+ dir[1] = vecDir[1] + random->RandomFloat( -(SPARK_SPREAD*spreadOfs), (SPARK_SPREAD*spreadOfs) );
+ dir[2] = vecDir[2] + random->RandomFloat( -(SPARK_SPREAD*spreadOfs), (SPARK_SPREAD*spreadOfs) );
+ pParticle->m_vecVelocity = dir * random->RandomFloat( (flMinSpeed*(2.0f-spreadOfs)), (flMaxSpeed*(2.0f-spreadOfs)) );
+
+ pParticle->m_flWidth = flWidth + random->RandomFloat( 0.0f, 0.5f );
+ pParticle->m_flLength = nTrailLength * random->RandomFloat( 0.02, 0.05f );
+ Color32Init( pParticle->m_color, 255, 255, 255, 255 );
+ }
+
+ //
+ // Little sparks
+ //
+ CSmartPtr<CTrailParticles> pSparkEmitter2 = CTrailParticles::Create( "FX_ElectricSpark 2" );
+
+ if ( !pSparkEmitter2 )
+ {
+ Assert(0);
+ return;
+ }
+
+ if ( pSparkMaterial )
+ {
+ hMaterial = pSparkEmitter->GetPMaterial( pSparkMaterial );
+ }
+ else
+ {
+ if ( g_Material_Spark == NULL )
+ {
+ g_Material_Spark = pSparkEmitter2->GetPMaterial( "effects/spark" );
+ }
+
+ hMaterial = g_Material_Spark;
+ }
+
+ pSparkEmitter2->SetSortOrigin( pos );
+
+ pSparkEmitter2->m_ParticleCollision.SetGravity( 400.0f );
+ pSparkEmitter2->SetFlag( bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
+
+ numSparks = nMagnitude * random->RandomInt( 4, 8 );
+
+ // Dump out sparks
+ for ( i = 0; i < numSparks; i++ )
+ {
+ pParticle = (TrailParticle *) pSparkEmitter2->AddParticle( sizeof(TrailParticle), hMaterial, pos );
+
+ if ( pParticle == NULL )
+ return;
+
+ pParticle->m_flLifetime = 0.0f;
+
+ dir.Random( -1.0f, 1.0f );
+ dir += vecDir;
+ VectorNormalize( dir );
+
+ pParticle->m_flWidth = (flWidth * 0.25) + random->RandomFloat( 0.0f, 0.5f );
+ pParticle->m_flLength = nTrailLength * random->RandomFloat( 0.02f, 0.03f );
+ pParticle->m_flDieTime = nMagnitude * random->RandomFloat( 0.3f, 0.5f );
+
+ pParticle->m_vecVelocity = dir * random->RandomFloat( flMinSpeed, flMaxSpeed );
+
+ Color32Init( pParticle->m_color, 255, 255, 255, 255 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Energy splash for plasma/beam weapon impacts
+// Input : &pos - origin point of effect
+//-----------------------------------------------------------------------------
+#define ENERGY_SPLASH_SPREAD 0.7f
+#define ENERGY_SPLASH_MINSPEED 128.0f
+#define ENERGY_SPLASH_MAXSPEED 160.0f
+#define ENERGY_SPLASH_GRAVITY 800.0f
+#define ENERGY_SPLASH_DAMPEN 0.3f
+
+void FX_EnergySplash( const Vector &pos, const Vector &normal, int nFlags )
+{
+ VPROF_BUDGET( "FX_EnergySplash", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
+ Vector offset = pos + ( normal * 2.0f );
+
+ // Quick flash
+ FX_AddQuad( pos,
+ normal,
+ 64.0f,
+ 0,
+ 0.75f,
+ 1.0f,
+ 0.0f,
+ 0.4f,
+ random->RandomInt( 0, 360 ),
+ 0,
+ Vector( 1.0f, 1.0f, 1.0f ),
+ 0.25f,
+ "effects/combinemuzzle1_nocull",
+ (FXQUAD_BIAS_SCALE|FXQUAD_BIAS_ALPHA) );
+
+ // Lingering burn
+ FX_AddQuad( pos,
+ normal,
+ 16,
+ 32,
+ 0.75f,
+ 1.0f,
+ 0.0f,
+ 0.4f,
+ random->RandomInt( 0, 360 ),
+ 0,
+ Vector( 1.0f, 1.0f, 1.0f ),
+ 0.5f,
+ "effects/combinemuzzle2_nocull",
+ (FXQUAD_BIAS_SCALE|FXQUAD_BIAS_ALPHA) );
+
+ SimpleParticle *sParticle;
+
+ CSmartPtr<CSimpleEmitter> pEmitter;
+
+ pEmitter = CSimpleEmitter::Create( "C_EntityDissolve" );
+ pEmitter->SetSortOrigin( pos );
+
+ if ( g_Material_Spark == NULL )
+ {
+ g_Material_Spark = pEmitter->GetPMaterial( "effects/spark" );
+ }
+
+ // Anime ground effects
+ for ( int j = 0; j < 8; j++ )
+ {
+ offset.x = random->RandomFloat( -8.0f, 8.0f );
+ offset.y = random->RandomFloat( -8.0f, 8.0f );
+ offset.z = random->RandomFloat( 0.0f, 4.0f );
+
+ offset += pos;
+
+ sParticle = (SimpleParticle *) pEmitter->AddParticle( sizeof(SimpleParticle), g_Material_Spark, offset );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_vecVelocity = Vector( Helper_RandomFloat( -4.0f, 4.0f ), Helper_RandomFloat( -4.0f, 4.0f ), Helper_RandomFloat( 16.0f, 64.0f ) );
+
+ sParticle->m_uchStartSize = random->RandomFloat( 2, 4 );
+
+ sParticle->m_flDieTime = random->RandomFloat( 0.4f, 0.6f );
+
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+
+ float alpha = 255;
+
+ sParticle->m_flRollDelta = Helper_RandomFloat( -4.0f, 4.0f );
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = 0;
+ sParticle->m_uchEndSize = 0;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Micro-Explosion effect
+// Input : &position - origin of effect
+// &normal - normal of the surface struck
+//-----------------------------------------------------------------------------
+
+#define MICRO_EXPLOSION_MINSPEED 100.0f
+#define MICRO_EXPLOSION_MAXSPEED 150.0f
+#define MICRO_EXPLOSION_SPREAD 1.0f
+#define MICRO_EXPLOSION_GRAVITY 0.0f
+#define MICRO_EXPLOSION_DAMPEN 0.4f
+
+void FX_MicroExplosion( Vector &position, Vector &normal )
+{
+ VPROF_BUDGET( "FX_MicroExplosion", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
+ Vector offset = position + ( normal * 2.0f );
+
+ CSmartPtr<CTrailParticles> sparkEmitter = CTrailParticles::Create( "FX_MicroExplosion 1" );
+
+ if ( !sparkEmitter )
+ return;
+
+ sparkEmitter->SetSortOrigin( offset );
+
+ //Setup our collision information
+ sparkEmitter->Setup( offset,
+ &normal,
+ MICRO_EXPLOSION_SPREAD,
+ MICRO_EXPLOSION_MINSPEED,
+ MICRO_EXPLOSION_MAXSPEED,
+ MICRO_EXPLOSION_GRAVITY,
+ MICRO_EXPLOSION_DAMPEN,
+ bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
+
+ int numSparks = random->RandomInt( 8, 16 );
+
+ if ( g_Material_Spark == NULL )
+ {
+ g_Material_Spark = sparkEmitter->GetPMaterial( "effects/spark" );
+ }
+
+ TrailParticle *pParticle;
+ Vector dir, vOfs;
+ float length = 0.2f;
+
+ //Fast lines
+ for ( int i = 0; i < numSparks; i++ )
+ {
+ pParticle = (TrailParticle *) sparkEmitter->AddParticle( sizeof(TrailParticle), g_Material_Spark, offset );
+
+ if ( pParticle )
+ {
+ pParticle->m_flLifetime = 0.0f;
+
+ float ramp = ( (float) i / (float)numSparks );
+
+ dir[0] = normal[0] + random->RandomFloat( -MICRO_EXPLOSION_SPREAD*ramp, MICRO_EXPLOSION_SPREAD*ramp );
+ dir[1] = normal[1] + random->RandomFloat( -MICRO_EXPLOSION_SPREAD*ramp, MICRO_EXPLOSION_SPREAD*ramp );
+ dir[2] = normal[2] + random->RandomFloat( -MICRO_EXPLOSION_SPREAD*ramp, MICRO_EXPLOSION_SPREAD*ramp );
+
+ pParticle->m_flWidth = random->RandomFloat( 5.0f, 10.0f );
+ pParticle->m_flLength = (length*((1.0f-ramp)*(1.0f-ramp)*0.5f));
+ pParticle->m_flDieTime = 0.2f;
+ pParticle->m_vecVelocity = dir * random->RandomFloat( MICRO_EXPLOSION_MINSPEED*(1.5f-ramp), MICRO_EXPLOSION_MAXSPEED*(1.5f-ramp) );
+
+ Color32Init( pParticle->m_color, 255, 255, 255, 255 );
+ }
+ }
+
+ //
+ // Filler
+ //
+
+ CSmartPtr<CSimpleEmitter> pSimple = CSimpleEmitter::Create( "FX_MicroExplosion 2" );
+ pSimple->SetSortOrigin( offset );
+
+ SimpleParticle *sParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "sprites/rico1" ), offset );
+
+ if ( sParticle )
+ {
+ sParticle->m_flLifetime = 0.0f;
+ sParticle->m_flDieTime = 0.3f;
+
+ sParticle->m_vecVelocity.Init();
+
+ sParticle->m_uchColor[0] = 255;
+ sParticle->m_uchColor[1] = 255;
+ sParticle->m_uchColor[2] = 255;
+ sParticle->m_uchStartAlpha = random->RandomInt( 128, 255 );
+ sParticle->m_uchEndAlpha = 0;
+ sParticle->m_uchStartSize = random->RandomInt( 12, 16 );
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize;
+ sParticle->m_flRoll = random->RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 0.0f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ugly prototype explosion effect
+//-----------------------------------------------------------------------------
+#define EXPLOSION_MINSPEED 300.0f
+#define EXPLOSION_MAXSPEED 300.0f
+#define EXPLOSION_SPREAD 0.8f
+#define EXPLOSION_GRAVITY 800.0f
+#define EXPLOSION_DAMPEN 0.4f
+
+#define EXPLOSION_FLECK_MIN_SPEED 150.0f
+#define EXPLOSION_FLECK_MAX_SPEED 350.0f
+#define EXPLOSION_FLECK_GRAVITY 800.0f
+#define EXPLOSION_FLECK_DAMPEN 0.3f
+#define EXPLOSION_FLECK_ANGULAR_SPRAY 0.8f
+
+void FX_Explosion( Vector& origin, Vector& normal, char materialType )
+{
+ VPROF_BUDGET( "FX_Explosion", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
+ Vector offset = origin + ( normal * 2.0f );
+
+ CSmartPtr<CTrailParticles> pSparkEmitter = CTrailParticles::Create( "FX_Explosion 1" );
+ if ( !pSparkEmitter )
+ return;
+
+ // Get color data from our hit point
+ IMaterial *pTraceMaterial;
+ Vector diffuseColor, baseColor;
+ pTraceMaterial = engine->TraceLineMaterialAndLighting( origin, normal * -16.0f, diffuseColor, baseColor );
+ // Get final light value
+ float r = pow( diffuseColor[0], 1.0f/2.2f ) * baseColor[0];
+ float g = pow( diffuseColor[1], 1.0f/2.2f ) * baseColor[1];
+ float b = pow( diffuseColor[2], 1.0f/2.2f ) * baseColor[2];
+
+ if ( g_Material_Spark == NULL )
+ {
+ g_Material_Spark = pSparkEmitter->GetPMaterial( "effects/spark" );
+ }
+
+ // Setup our collision information
+ pSparkEmitter->Setup( offset,
+ &normal,
+ EXPLOSION_SPREAD,
+ EXPLOSION_MINSPEED,
+ EXPLOSION_MAXSPEED,
+ EXPLOSION_GRAVITY,
+ EXPLOSION_DAMPEN,
+ bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
+
+ pSparkEmitter->SetSortOrigin( offset );
+
+ Vector dir;
+ int numSparks = random->RandomInt( 8,16 );
+
+ // Dump out sparks
+ int i;
+ for ( i = 0; i < numSparks; i++ )
+ {
+ TrailParticle *pParticle = (TrailParticle *) pSparkEmitter->AddParticle( sizeof(TrailParticle), g_Material_Spark, offset );
+
+ if ( pParticle == NULL )
+ break;
+
+ pParticle->m_flLifetime = 0.0f;
+
+ pParticle->m_flWidth = random->RandomFloat( 5.0f, 10.0f );
+ pParticle->m_flLength = random->RandomFloat( 0.05, 0.1f );
+ pParticle->m_flDieTime = random->RandomFloat( 1.0f, 2.0f );
+
+ dir[0] = normal[0] + random->RandomFloat( -EXPLOSION_SPREAD, EXPLOSION_SPREAD );
+ dir[1] = normal[1] + random->RandomFloat( -EXPLOSION_SPREAD, EXPLOSION_SPREAD );
+ dir[2] = normal[2] + random->RandomFloat( -EXPLOSION_SPREAD, EXPLOSION_SPREAD );
+ pParticle->m_vecVelocity = dir * random->RandomFloat( EXPLOSION_MINSPEED, EXPLOSION_MAXSPEED );
+
+ Color32Init( pParticle->m_color, 255, 255, 255, 255 );
+ }
+
+
+ // Chunks o'dirt
+ // Only create dirt chunks on concrete/world
+ if ( materialType == 'C' || materialType == 'W' )
+ {
+ CSmartPtr<CFleckParticles> fleckEmitter = CFleckParticles::Create( "FX_Explosion 10", offset, Vector(5,5,5) );
+ if ( !fleckEmitter )
+ return;
+
+ // Setup our collision information
+ fleckEmitter->m_ParticleCollision.Setup( offset, &normal, EXPLOSION_FLECK_ANGULAR_SPRAY, EXPLOSION_FLECK_MIN_SPEED, EXPLOSION_FLECK_MAX_SPEED, EXPLOSION_FLECK_GRAVITY, EXPLOSION_FLECK_DAMPEN );
+
+ PMaterialHandle *hMaterialArray;
+
+ switch ( materialType )
+ {
+ case 'C':
+ case 'c':
+ default:
+ hMaterialArray = g_Mat_Fleck_Cement;
+ break;
+ }
+
+ int numFlecks = random->RandomInt( 48, 64 );
+ // Dump out flecks
+ for ( i = 0; i < numFlecks; i++ )
+ {
+ FleckParticle *pParticle = (FleckParticle *) fleckEmitter->AddParticle( sizeof(FleckParticle), hMaterialArray[random->RandomInt(0,1)], offset );
+
+ if ( pParticle == NULL )
+ break;
+
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = 3.0f;
+ dir[0] = normal[0] + random->RandomFloat( -EXPLOSION_FLECK_ANGULAR_SPRAY, EXPLOSION_FLECK_ANGULAR_SPRAY );
+ dir[1] = normal[1] + random->RandomFloat( -EXPLOSION_FLECK_ANGULAR_SPRAY, EXPLOSION_FLECK_ANGULAR_SPRAY );
+ dir[2] = normal[2] + random->RandomFloat( -EXPLOSION_FLECK_ANGULAR_SPRAY, EXPLOSION_FLECK_ANGULAR_SPRAY );
+ pParticle->m_uchSize = random->RandomInt( 2, 6 );
+ pParticle->m_vecVelocity = dir * ( random->RandomFloat( EXPLOSION_FLECK_MIN_SPEED, EXPLOSION_FLECK_MAX_SPEED ) * ( 7 - pParticle->m_uchSize ) );
+ pParticle->m_flRoll = random->RandomFloat( 0, 360 );
+ pParticle->m_flRollDelta = random->RandomFloat( 0, 360 );
+
+ float colorRamp = random->RandomFloat( 0.75f, 1.5f );
+ pParticle->m_uchColor[0] = MIN( 1.0f, r*colorRamp )*255.0f;
+ pParticle->m_uchColor[1] = MIN( 1.0f, g*colorRamp )*255.0f;
+ pParticle->m_uchColor[2] = MIN( 1.0f, b*colorRamp )*255.0f;
+ }
+ }
+
+ // Large sphere bursts
+ CSmartPtr<CSimpleEmitter> pSimpleEmitter = CSimpleEmitter::Create( "FX_Explosion 1" );
+ PMaterialHandle hSphereMaterial = g_Mat_DustPuff[1];
+ Vector vecBurstOrigin = offset + normal * 8.0;
+ pSimpleEmitter->SetSortOrigin( vecBurstOrigin );
+ SimpleParticle *pSphereParticle = (SimpleParticle *) pSimpleEmitter->AddParticle( sizeof(SimpleParticle), hSphereMaterial, vecBurstOrigin );
+ if ( pSphereParticle )
+ {
+ pSphereParticle->m_flLifetime = 0.0f;
+ pSphereParticle->m_flDieTime = 0.3f;
+ pSphereParticle->m_uchStartAlpha = 150.0;
+ pSphereParticle->m_uchEndAlpha = 64.0;
+ pSphereParticle->m_uchStartSize = 0.0;
+ pSphereParticle->m_uchEndSize = 255.0;
+ pSphereParticle->m_vecVelocity = Vector(0,0,0);
+
+ float colorRamp = random->RandomFloat( 0.75f, 1.5f );
+ pSphereParticle->m_uchColor[0] = MIN( 1.0f, r*colorRamp )*255.0f;
+ pSphereParticle->m_uchColor[1] = MIN( 1.0f, g*colorRamp )*255.0f;
+ pSphereParticle->m_uchColor[2] = MIN( 1.0f, b*colorRamp )*255.0f;
+ }
+
+ // Throw some smoke balls out around the normal
+ int numBalls = 12;
+ Vector vecRight, vecForward, vecUp;
+ QAngle vecAngles;
+ VectorAngles( normal, vecAngles );
+ AngleVectors( vecAngles, NULL, &vecRight, &vecUp );
+ for ( i = 0; i < numBalls; i++ )
+ {
+ SimpleParticle *pParticle = (SimpleParticle *) pSimpleEmitter->AddParticle( sizeof(SimpleParticle), hSphereMaterial, vecBurstOrigin );
+ if ( pParticle )
+ {
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = 0.25f;
+ pParticle->m_uchStartAlpha = 128.0;
+ pParticle->m_uchEndAlpha = 64.0;
+ pParticle->m_uchStartSize = 16.0;
+ pParticle->m_uchEndSize = 64.0;
+
+ float flAngle = ((float)i * M_PI * 2) / numBalls;
+ float x = cos( flAngle );
+ float y = sin( flAngle );
+ pParticle->m_vecVelocity = (vecRight*x + vecUp*y) * 1024.0;
+
+ float colorRamp = random->RandomFloat( 0.75f, 1.5f );
+ pParticle->m_uchColor[0] = MIN( 1.0f, r*colorRamp )*255.0f;
+ pParticle->m_uchColor[1] = MIN( 1.0f, g*colorRamp )*255.0f;
+ pParticle->m_uchColor[2] = MIN( 1.0f, b*colorRamp )*255.0f;
+ }
+ }
+
+ // Create a couple of big, floating smoke clouds
+ CSmartPtr<CSimpleEmitter> pSmokeEmitter = CSimpleEmitter::Create( "FX_Explosion 2" );
+ pSmokeEmitter->SetSortOrigin( offset );
+ for ( i = 0; i < 2; i++ )
+ {
+ SimpleParticle *pParticle = (SimpleParticle *) pSmokeEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_DustPuff[1], offset );
+ if ( pParticle == NULL )
+ break;
+
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = random->RandomFloat( 2.0f, 3.0f );
+ pParticle->m_uchStartSize = 64;
+ pParticle->m_uchEndSize = 255;
+ dir[0] = normal[0] + random->RandomFloat( -0.8f, 0.8f );
+ dir[1] = normal[1] + random->RandomFloat( -0.8f, 0.8f );
+ dir[2] = normal[2] + random->RandomFloat( -0.8f, 0.8f );
+ pParticle->m_vecVelocity = dir * random->RandomFloat( 2.0f, 24.0f )*(i+1);
+ pParticle->m_uchStartAlpha = 160;
+ pParticle->m_uchEndAlpha = 0;
+ pParticle->m_flRoll = random->RandomFloat( 180, 360 );
+ pParticle->m_flRollDelta = random->RandomFloat( -1, 1 );
+
+ float colorRamp = random->RandomFloat( 0.5f, 1.25f );
+ pParticle->m_uchColor[0] = MIN( 1.0f, r*colorRamp )*255.0f;
+ pParticle->m_uchColor[1] = MIN( 1.0f, g*colorRamp )*255.0f;
+ pParticle->m_uchColor[2] = MIN( 1.0f, b*colorRamp )*255.0f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : origin -
+// normal -
+//-----------------------------------------------------------------------------
+void FX_ConcussiveExplosion( Vector &origin, Vector &normal )
+{
+ VPROF_BUDGET( "FX_ConcussiveExplosion", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
+ Vector offset = origin + ( normal * 2.0f );
+ Vector dir;
+ int i;
+
+ //
+ // Smoke
+ //
+
+ CSmartPtr<CSimpleEmitter> pSmokeEmitter = CSimpleEmitter::Create( "FX_ConcussiveExplosion 1" );
+ pSmokeEmitter->SetSortOrigin( offset );
+
+ //Quick moving sprites
+ for ( i = 0; i < 16; i++ )
+ {
+ SimpleParticle *pParticle = (SimpleParticle *) pSmokeEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_DustPuff[1], offset );
+
+ if ( pParticle == NULL )
+ return;
+
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = random->RandomFloat( 0.2f, 0.4f );
+ pParticle->m_uchStartSize = random->RandomInt( 4, 8 );
+ pParticle->m_uchEndSize = random->RandomInt( 32, 64 );
+
+ dir[0] = random->RandomFloat( -1.0f, 1.0f );
+ dir[1] = random->RandomFloat( -1.0f, 1.0f );
+ dir[2] = random->RandomFloat( -1.0f, 1.0f );
+
+ pParticle->m_vecVelocity = dir * random->RandomFloat( 64.0f, 128.0f );
+ pParticle->m_uchStartAlpha = random->RandomInt( 64, 128 );
+ pParticle->m_uchEndAlpha = 0;
+ pParticle->m_flRoll = random->RandomFloat( 180, 360 );
+ pParticle->m_flRollDelta = random->RandomFloat( -4, 4 );
+
+ int colorRamp = random->RandomFloat( 235, 255 );
+ pParticle->m_uchColor[0] = colorRamp;
+ pParticle->m_uchColor[1] = colorRamp;
+ pParticle->m_uchColor[2] = colorRamp;
+ }
+
+ //Slow lingering sprites
+ for ( i = 0; i < 2; i++ )
+ {
+ SimpleParticle *pParticle = (SimpleParticle *) pSmokeEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_DustPuff[1], offset );
+ if ( pParticle == NULL )
+ return;
+
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = random->RandomFloat( 1.0f, 2.0f );
+ pParticle->m_uchStartSize = random->RandomInt( 32, 64 );
+ pParticle->m_uchEndSize = random->RandomInt( 100, 128 );
+
+ dir[0] = normal[0] + random->RandomFloat( -0.8f, 0.8f );
+ dir[1] = normal[1] + random->RandomFloat( -0.8f, 0.8f );
+ dir[2] = normal[2] + random->RandomFloat( -0.8f, 0.8f );
+
+ pParticle->m_vecVelocity = dir * random->RandomFloat( 16.0f, 32.0f );
+
+ pParticle->m_uchStartAlpha = random->RandomInt( 32, 64 );
+ pParticle->m_uchEndAlpha = 0;
+ pParticle->m_flRoll = random->RandomFloat( 180, 360 );
+ pParticle->m_flRollDelta = random->RandomFloat( -1, 1 );
+
+ int colorRamp = random->RandomFloat( 235, 255 );
+ pParticle->m_uchColor[0] = colorRamp;
+ pParticle->m_uchColor[1] = colorRamp;
+ pParticle->m_uchColor[2] = colorRamp;
+ }
+
+
+ //
+ // Quick sphere
+ //
+
+ CSmartPtr<CSimpleEmitter> pSimple = CSimpleEmitter::Create( "FX_ConcussiveExplosion 2" );
+
+ pSimple->SetSortOrigin( offset );
+
+ SimpleParticle *pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof(SimpleParticle), pSimple->GetPMaterial( "effects/blueflare1" ), offset );
+
+ if ( pParticle )
+ {
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = 0.1f;
+ pParticle->m_vecVelocity.Init();
+ pParticle->m_flRoll = random->RandomFloat( 180, 360 );
+ pParticle->m_flRollDelta = random->RandomFloat( -1, 1 );
+
+ pParticle->m_uchColor[0] = pParticle->m_uchColor[1] = pParticle->m_uchColor[2] = 128;
+
+ pParticle->m_uchStartAlpha = 255;
+ pParticle->m_uchEndAlpha = 0;
+
+ pParticle->m_uchStartSize = 16;
+ pParticle->m_uchEndSize = 64;
+ }
+
+ pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof(SimpleParticle), pSimple->GetPMaterial( "effects/blueflare1" ), offset );
+
+ if ( pParticle )
+ {
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = 0.2f;
+ pParticle->m_vecVelocity.Init();
+ pParticle->m_flRoll = random->RandomFloat( 180, 360 );
+ pParticle->m_flRollDelta = random->RandomFloat( -1, 1 );
+ pParticle->m_uchColor[0] = pParticle->m_uchColor[1] = pParticle->m_uchColor[2] = 32;
+
+ pParticle->m_uchStartAlpha = 64;
+ pParticle->m_uchEndAlpha = 0;
+
+ pParticle->m_uchStartSize = 64;
+ pParticle->m_uchEndSize = 128;
+ }
+
+
+ //
+ // Dlight
+ //
+
+ dlight_t *dl= effects->CL_AllocDlight ( 0 );
+
+ dl->origin = offset;
+ dl->color.r = dl->color.g = dl->color.b = 64;
+ dl->radius = random->RandomFloat(128,256);
+ dl->die = gpGlobals->curtime + 0.1;
+
+
+ //
+ // Moving lines
+ //
+
+ TrailParticle *pTrailParticle;
+ CSmartPtr<CTrailParticles> pSparkEmitter = CTrailParticles::Create( "FX_ConcussiveExplosion 3" );
+ PMaterialHandle hMaterial;
+ int numSparks;
+
+ if ( pSparkEmitter.IsValid() )
+ {
+ hMaterial= pSparkEmitter->GetPMaterial( "effects/blueflare1" );
+
+ pSparkEmitter->SetSortOrigin( offset );
+ pSparkEmitter->m_ParticleCollision.SetGravity( 0.0f );
+
+ numSparks = random->RandomInt( 16, 32 );
+
+ //Dump out sparks
+ for ( i = 0; i < numSparks; i++ )
+ {
+ pTrailParticle = (TrailParticle *) pSparkEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset );
+
+ if ( pTrailParticle == NULL )
+ return;
+
+ pTrailParticle->m_flLifetime = 0.0f;
+
+ dir.Random( -1.0f, 1.0f );
+
+ pTrailParticle->m_flWidth = random->RandomFloat( 1.0f, 2.0f );
+ pTrailParticle->m_flLength = random->RandomFloat( 0.01f, 0.1f );
+ pTrailParticle->m_flDieTime = random->RandomFloat( 0.1f, 0.2f );
+
+ pTrailParticle->m_vecVelocity = dir * random->RandomFloat( 800, 1000 );
+
+ float colorRamp = random->RandomFloat( 0.75f, 1.0f );
+ FloatToColor32( pTrailParticle->m_color, colorRamp, colorRamp, 1.0f, 1.0f );
+ }
+ }
+
+ //Moving particles
+ CSmartPtr<CTrailParticles> pCollisionEmitter = CTrailParticles::Create( "FX_ConcussiveExplosion 4" );
+
+ if ( pCollisionEmitter.IsValid() )
+ {
+ //Setup our collision information
+ pCollisionEmitter->Setup( (Vector &) offset,
+ NULL,
+ SPARK_ELECTRIC_SPREAD,
+ SPARK_ELECTRIC_MINSPEED*6,
+ SPARK_ELECTRIC_MAXSPEED*6,
+ -400,
+ SPARK_ELECTRIC_DAMPEN,
+ bitsPARTICLE_TRAIL_FADE );
+
+ pCollisionEmitter->SetSortOrigin( offset );
+
+ numSparks = random->RandomInt( 8, 16 );
+ hMaterial = pCollisionEmitter->GetPMaterial( "effects/blueflare1" );
+
+ //Dump out sparks
+ for ( i = 0; i < numSparks; i++ )
+ {
+ pTrailParticle = (TrailParticle *) pCollisionEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset );
+
+ if ( pTrailParticle == NULL )
+ return;
+
+ pTrailParticle->m_flLifetime = 0.0f;
+
+ dir.Random( -1.0f, 1.0f );
+ dir[2] = random->RandomFloat( 0.0f, 0.75f );
+
+ pTrailParticle->m_flWidth = random->RandomFloat( 1.0f, 2.0f );
+ pTrailParticle->m_flLength = random->RandomFloat( 0.01f, 0.1f );
+ pTrailParticle->m_flDieTime = random->RandomFloat( 0.2f, 1.0f );
+
+ pTrailParticle->m_vecVelocity = dir * random->RandomFloat( 128, 512 );
+
+ float colorRamp = random->RandomFloat( 0.75f, 1.0f );
+ FloatToColor32( pTrailParticle->m_color, colorRamp, colorRamp, 1.0f, 1.0f );
+ }
+ }
+}
+
+
+void FX_SparkFan( Vector &position, Vector &normal )
+{
+ Vector offset = position + ( normal * 1.0f );
+
+ CSmartPtr<CTrailParticles> sparkEmitter = CTrailParticles::Create( "FX_MetalScrape 1" );
+
+ if ( !sparkEmitter )
+ return;
+
+ sparkEmitter->SetSortOrigin( offset );
+
+ //Setup our collision information
+ sparkEmitter->Setup( offset,
+ &normal,
+ METAL_SCRAPE_SPREAD,
+ METAL_SCRAPE_MINSPEED,
+ METAL_SCRAPE_MAXSPEED,
+ METAL_SCRAPE_GRAVITY,
+ METAL_SCRAPE_DAMPEN,
+ bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
+
+ if ( g_Material_Spark == NULL )
+ {
+ g_Material_Spark = sparkEmitter->GetPMaterial( "effects/spark" );
+ }
+
+ TrailParticle *pParticle;
+ Vector dir;
+
+ float length = 0.06f;
+
+ //Dump out sparks
+ for ( int i = 0; i < 35; i++ )
+ {
+ pParticle = (TrailParticle *) sparkEmitter->AddParticle( sizeof(TrailParticle), g_Material_Spark, offset );
+ if ( pParticle == NULL )
+ return;
+
+ pParticle->m_flLifetime = 0.0f;
+
+ float spreadOfs = random->RandomFloat( 0.0f, 2.0f );
+
+ dir[0] = normal[0] + random->RandomFloat( -(METAL_SCRAPE_SPREAD*spreadOfs), (METAL_SCRAPE_SPREAD*spreadOfs) );
+ dir[1] = normal[1] + random->RandomFloat( -(METAL_SCRAPE_SPREAD*spreadOfs), (METAL_SCRAPE_SPREAD*spreadOfs) );
+ dir[2] = normal[2] + random->RandomFloat( -(METAL_SCRAPE_SPREAD*spreadOfs), (METAL_SCRAPE_SPREAD*spreadOfs) );
+
+ pParticle->m_flWidth = random->RandomFloat( 2.0f, 5.0f );
+ pParticle->m_flLength = random->RandomFloat( length*0.25f, length );
+ pParticle->m_flDieTime = random->RandomFloat( 2.0f, 2.0f );
+
+ pParticle->m_vecVelocity = dir * random->RandomFloat( (METAL_SCRAPE_MINSPEED*(2.0f-spreadOfs)), (METAL_SCRAPE_MAXSPEED*(2.0f-spreadOfs)) );
+
+ Color32Init( pParticle->m_color, 255, 255, 255, 255 );
+ }
+}
+
+
+void ManhackSparkCallback( const CEffectData & data )
+{
+ Vector vecNormal;
+ Vector vecPosition;
+ QAngle angles;
+
+ vecPosition = data.m_vOrigin;
+ vecNormal = data.m_vNormal;
+ angles = data.m_vAngles;
+
+ FX_SparkFan( vecPosition, vecNormal );
+}
+
+DECLARE_CLIENT_EFFECT( "ManhackSparks", ManhackSparkCallback );