//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "view.h" #include "viewrender.h" #include "c_tracer.h" #include "dlight.h" #include "precache_register.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 PRECACHE_REGISTER_BEGIN( GLOBAL, PrecacheEffectSparks ) #ifndef DOTA_DLL PRECACHE( MATERIAL, "effects/spark" ) PRECACHE( MATERIAL, "effects/energysplash" ) PRECACHE( MATERIAL, "effects/energyball" ) PRECACHE( MATERIAL, "sprites/rico1" ) PRECACHE( MATERIAL, "sprites/rico1_noz" ) PRECACHE( MATERIAL, "sprites/blueflare1" ) PRECACHE( MATERIAL, "effects/yellowflare" ) PRECACHE( MATERIAL, "effects/combinemuzzle1_nocull" ) PRECACHE( MATERIAL, "effects/combinemuzzle2_nocull" ) PRECACHE( MATERIAL, "effects/yellowflare_noz" ) #endif PRECACHE_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 ) { int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT(); 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(nSlot), 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<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 Tracer_Draw( pIterator->GetParticleDraw(), start, (delta*scale), 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 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 ); } // // Little sparks // CSmartPtr 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 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; */ } //----------------------------------------------------------------------------- // 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 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 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 ); 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 );