//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "engine/IEngineSound.h" #include "particles_simple.h" #include "particles_localspace.h" #include "dlight.h" #include "iefx.h" #include "clientsideeffects.h" #include "precache_register.h" #include "glow_overlay.h" #include "effect_dispatch_data.h" #include "c_te_effect_dispatch.h" #include "tier0/vprof.h" #include "tier1/keyvalues.h" #include "effect_color_tables.h" #include "iviewrender_beams.h" #include "view.h" #include "ieffects.h" #include "fx.h" #include "c_te_legacytempents.h" #include "toolframework_client.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //Precahce the effects PRECACHE_REGISTER_BEGIN( GLOBAL, PrecacheMuzzleFlash ) PRECACHE( MATERIAL, "effects/muzzleflash1" ) PRECACHE( MATERIAL, "effects/muzzleflash2" ) PRECACHE( MATERIAL, "effects/muzzleflash3" ) PRECACHE( MATERIAL, "effects/muzzleflash4" ) PRECACHE( MATERIAL, "effects/bluemuzzle" ) PRECACHE( MATERIAL, "effects/gunshipmuzzle" ) PRECACHE( MATERIAL, "effects/gunshiptracer" ) PRECACHE( MATERIAL, "effects/huntertracer" ) PRECACHE( MATERIAL, "sprites/physcannon_bluelight2" ) PRECACHE( MATERIAL, "effects/combinemuzzle1" ) PRECACHE( MATERIAL, "effects/combinemuzzle2" ) PRECACHE( MATERIAL, "effects/combinemuzzle2_nocull" ) PRECACHE_REGISTER_END() //Whether or not we should emit a dynamic light ConVar muzzleflash_light( "muzzleflash_light", "1", FCVAR_ARCHIVE ); extern void FX_TracerSound( const Vector &start, const Vector &end, int iTracerType ); //=================================================================== //=================================================================== class CImpactOverlay : public CWarpOverlay { public: virtual bool Update( void ) { m_flLifetime += gpGlobals->frametime; const float flTotalLifetime = 0.1f; if ( m_flLifetime < flTotalLifetime ) { float flColorScale = 1.0f - ( m_flLifetime / flTotalLifetime ); for( int i=0; i < m_nSprites; i++ ) { m_Sprites[i].m_vColor = m_vBaseColors[i] * flColorScale; m_Sprites[i].m_flHorzSize += 1.0f * gpGlobals->frametime; m_Sprites[i].m_flVertSize += 1.0f * gpGlobals->frametime; } return true; } return false; } public: float m_flLifetime; Vector m_vBaseColors[MAX_SUN_LAYERS]; }; //----------------------------------------------------------------------------- // Purpose: Play random ricochet sound // Input : *pos - //----------------------------------------------------------------------------- void FX_RicochetSound( const Vector& pos ) { Vector org = pos; CLocalPlayerFilter filter; C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, "FX_RicochetSound.Ricochet", &org ); } //----------------------------------------------------------------------------- // Purpose: // Input : entityIndex - // attachmentIndex - // *origin - // *angles - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool FX_GetAttachmentTransform( ClientEntityHandle_t hEntity, int attachmentIndex, Vector *origin, QAngle *angles ) { // Validate our input if ( ( hEntity == INVALID_EHANDLE_INDEX ) || ( attachmentIndex < 1 ) ) { if ( origin != NULL ) { *origin = vec3_origin; } if ( angles != NULL ) { *angles = QAngle(0,0,0); } return false; } // Get the actual entity IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( hEntity ); if ( pRenderable ) { Vector attachOrigin; QAngle attachAngles; // Find the attachment's matrix pRenderable->GetAttachment( attachmentIndex, attachOrigin, attachAngles ); if ( origin != NULL ) { *origin = attachOrigin; } if ( angles != NULL ) { *angles = attachAngles; } return true; } return false; } //----------------------------------------------------------------------------- // Purpose: // Input : entityIndex - // attachmentIndex - // &transform - //----------------------------------------------------------------------------- bool FX_GetAttachmentTransform( ClientEntityHandle_t hEntity, int attachmentIndex, matrix3x4_t &transform ) { Vector origin; QAngle angles; if ( FX_GetAttachmentTransform( hEntity, attachmentIndex, &origin, &angles ) ) { AngleMatrix( angles, origin, transform ); return true; } // Entity doesn't exist SetIdentityMatrix( transform ); return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void FX_MuzzleEffect( const Vector &origin, const QAngle &angles, float scale, ClientEntityHandle_t hEntity, unsigned char *pFlashColor, bool bOneFrame ) { VPROF_BUDGET( "FX_MuzzleEffect", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); CSmartPtr pSimple = CSimpleEmitter::Create( "MuzzleFlash" ); pSimple->SetSortOrigin( origin ); SimpleParticle *pParticle; Vector forward, offset; AngleVectors( angles, &forward ); float flScale = random->RandomFloat( scale-0.25f, scale+0.25f ); if ( flScale < 0.5f ) { flScale = 0.5f; } else if ( flScale > 8.0f ) { flScale = 8.0f; } // // Flash // int i; for ( i = 1; i < 9; i++ ) { offset = origin + (forward * (i*2.0f*scale)); pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/muzzleflash%d", random->RandomInt(1,4) ) ), offset ); if ( pParticle == NULL ) return; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = /*bOneFrame ? 0.0001f : */0.1f; pParticle->m_vecVelocity.Init(); if ( !pFlashColor ) { pParticle->m_uchColor[0] = 255; pParticle->m_uchColor[1] = 255; pParticle->m_uchColor[2] = 255; } else { pParticle->m_uchColor[0] = pFlashColor[0]; pParticle->m_uchColor[1] = pFlashColor[1]; pParticle->m_uchColor[2] = pFlashColor[2]; } pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 128; pParticle->m_uchStartSize = (random->RandomFloat( 6.0f, 9.0f ) * (12-(i))/9) * flScale; pParticle->m_uchEndSize = pParticle->m_uchStartSize; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = 0.0f; } // // Smoke // /* for ( i = 0; i < 4; i++ ) { pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "particle/particle_smokegrenade" ), origin ); if ( pParticle == NULL ) return; alpha = random->RandomInt( 32, 84 ); color = random->RandomInt( 64, 164 ); pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = random->RandomFloat( 0.5f, 1.0f ); pParticle->m_vecVelocity.Random( -0.5f, 0.5f ); pParticle->m_vecVelocity += forward; VectorNormalize( pParticle->m_vecVelocity ); pParticle->m_vecVelocity *= random->RandomFloat( 16.0f, 32.0f ); pParticle->m_vecVelocity[2] += random->RandomFloat( 4.0f, 16.0f ); pParticle->m_uchColor[0] = color; pParticle->m_uchColor[1] = color; pParticle->m_uchColor[2] = color; pParticle->m_uchStartAlpha = alpha; pParticle->m_uchEndAlpha = 0; pParticle->m_uchStartSize = random->RandomInt( 4, 8 ) * flScale; pParticle->m_uchEndSize = pParticle->m_uchStartSize*2; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = random->RandomFloat( -4.0f, 4.0f ); } */ } //----------------------------------------------------------------------------- // Purpose: // Input : scale - // attachmentIndex - // bOneFrame - //----------------------------------------------------------------------------- void FX_MuzzleEffectAttached( float scale, ClientEntityHandle_t hEntity, int attachmentIndex, unsigned char *pFlashColor, bool bOneFrame ) { VPROF_BUDGET( "FX_MuzzleEffect", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); CSmartPtr pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash", hEntity, attachmentIndex ); Assert( pSimple ); if ( pSimple == NULL ) return; // Lock our bounding box pSimple->GetBinding().SetBBox( -( Vector( 16, 16, 16 ) * scale ), ( Vector( 16, 16, 16 ) * scale ) ); SimpleParticle *pParticle; Vector forward(1,0,0), offset; float flScale = random->RandomFloat( scale-0.25f, scale+0.25f ); if ( flScale < 0.5f ) { flScale = 0.5f; } else if ( flScale > 8.0f ) { flScale = 8.0f; } // // Flash // int i; for ( i = 1; i < 9; i++ ) { offset = (forward * (i*2.0f*scale)); pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Mat_SMG_Muzzleflash[random->RandomInt(0,3)], offset ); if ( pParticle == NULL ) return; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = bOneFrame ? 0.0001f : 0.1f; pParticle->m_vecVelocity.Init(); if ( !pFlashColor ) { pParticle->m_uchColor[0] = 255; pParticle->m_uchColor[1] = 255; pParticle->m_uchColor[2] = 255; } else { pParticle->m_uchColor[0] = pFlashColor[0]; pParticle->m_uchColor[1] = pFlashColor[1]; pParticle->m_uchColor[2] = pFlashColor[2]; } pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 128; pParticle->m_uchStartSize = (random->RandomFloat( 6.0f, 9.0f ) * (12-(i))/9) * flScale; pParticle->m_uchEndSize = pParticle->m_uchStartSize; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = 0.0f; } if ( !ToolsEnabled() ) return; if ( !clienttools->IsInRecordingMode() ) return; C_BaseEntity *pEnt = ClientEntityList().GetBaseEntityFromHandle( hEntity ); if ( pEnt ) { pEnt->RecordToolMessage(); } // NOTE: Particle system destruction message will be sent by the particle effect itself. int nId = pSimple->AllocateToolParticleEffectId(); KeyValues *msg = new KeyValues( "OldParticleSystem_Create" ); msg->SetString( "name", "FX_MuzzleEffectAttached" ); msg->SetInt( "id", nId ); msg->SetFloat( "time", gpGlobals->curtime ); KeyValues *pEmitter = msg->FindKey( "DmeSpriteEmitter", true ); pEmitter->SetInt( "count", 9 ); pEmitter->SetFloat( "duration", 0 ); pEmitter->SetString( "material", "effects/muzzleflash2" ); // FIXME - create DmeMultiMaterialSpriteEmitter to support the 4 materials of muzzleflash pEmitter->SetInt( "active", true ); KeyValues *pInitializers = pEmitter->FindKey( "initializers", true ); KeyValues *pPosition = pInitializers->FindKey( "DmeLinearAttachedPositionInitializer", true ); pPosition->SetPtr( "entindex", (void*)pEnt->entindex() ); pPosition->SetInt( "attachmentIndex", attachmentIndex ); pPosition->SetFloat( "linearOffsetX", 2.0f * scale ); // TODO - create a DmeConstantLifetimeInitializer KeyValues *pLifetime = pInitializers->FindKey( "DmeRandomLifetimeInitializer", true ); pLifetime->SetFloat( "minLifetime", bOneFrame ? 1.0f / 24.0f : 0.1f ); pLifetime->SetFloat( "maxLifetime", bOneFrame ? 1.0f / 24.0f : 0.1f ); KeyValues *pVelocity = pInitializers->FindKey( "DmeConstantVelocityInitializer", true ); pVelocity->SetFloat( "velocityX", 0.0f ); pVelocity->SetFloat( "velocityY", 0.0f ); pVelocity->SetFloat( "velocityZ", 0.0f ); KeyValues *pRoll = pInitializers->FindKey( "DmeRandomRollInitializer", true ); pRoll->SetFloat( "minRoll", 0.0f ); pRoll->SetFloat( "maxRoll", 360.0f ); // TODO - create a DmeConstantRollSpeedInitializer KeyValues *pRollSpeed = pInitializers->FindKey( "DmeRandomRollSpeedInitializer", true ); pRollSpeed->SetFloat( "minRollSpeed", 0.0f ); pRollSpeed->SetFloat( "maxRollSpeed", 0.0f ); // TODO - create a DmeConstantColorInitializer KeyValues *pColor = pInitializers->FindKey( "DmeRandomInterpolatedColorInitializer", true ); Color color( pFlashColor ? pFlashColor[ 0 ] : 255, pFlashColor ? pFlashColor[ 1 ] : 255, pFlashColor ? pFlashColor[ 2 ] : 255, 255 ); pColor->SetColor( "color1", color ); pColor->SetColor( "color2", color ); // TODO - create a DmeConstantAlphaInitializer KeyValues *pAlpha = pInitializers->FindKey( "DmeRandomAlphaInitializer", true ); pAlpha->SetInt( "minStartAlpha", 255 ); pAlpha->SetInt( "maxStartAlpha", 255 ); pAlpha->SetInt( "minEndAlpha", 128 ); pAlpha->SetInt( "maxEndAlpha", 128 ); // size = rand(6..9) * indexed(12/9..4/9) * flScale = rand(6..9) * ( 4f + f * i ) KeyValues *pSize = pInitializers->FindKey( "DmeMuzzleFlashSizeInitializer", true ); float f = flScale / 9.0f; pSize->SetFloat( "indexedBase", 4.0f * f ); pSize->SetFloat( "indexedDelta", f ); pSize->SetFloat( "minRandomFactor", 6.0f ); pSize->SetFloat( "maxRandomFactor", 9.0f ); /* KeyValues *pUpdaters = pEmitter->FindKey( "updaters", true ); pUpdaters->FindKey( "DmePositionVelocityUpdater", true ); pUpdaters->FindKey( "DmeRollUpdater", true ); pUpdaters->FindKey( "DmeAlphaLinearUpdater", true ); pUpdaters->FindKey( "DmeSizeUpdater", true ); */ ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); msg->deleteThis(); } //----------------------------------------------------------------------------- // Old-style muzzle flashes //----------------------------------------------------------------------------- void MuzzleFlashCallback( const CEffectData &data ) { Vector vecOrigin = data.m_vOrigin; QAngle vecAngles = data.m_vAngles; if ( data.entindex() > 0 ) { IClientRenderable *pRenderable = data.GetRenderable(); if ( !pRenderable ) return; if ( data.m_nAttachmentIndex ) { //FIXME: We also need to allocate these particles into an attachment space setup pRenderable->GetAttachment( data.m_nAttachmentIndex, vecOrigin, vecAngles ); } else { vecOrigin = pRenderable->GetRenderOrigin(); vecAngles = pRenderable->GetRenderAngles(); } } tempents->MuzzleFlash( vecOrigin, vecAngles, data.m_fFlags & (~MUZZLEFLASH_FIRSTPERSON), data.m_hEntity, (data.m_fFlags & MUZZLEFLASH_FIRSTPERSON) != 0 ); } DECLARE_CLIENT_EFFECT( MuzzleFlash, MuzzleFlashCallback ); //----------------------------------------------------------------------------- // Purpose: // Input : &origin - // &velocity - // scale - // numParticles - // *pColor - // iAlpha - // *pMaterial - // flRoll - // flRollDelta - //----------------------------------------------------------------------------- CSmartPtr FX_Smoke( const Vector &origin, const Vector &velocity, float scale, int numParticles, float flDietime, unsigned char *pColor, int iAlpha, const char *pMaterial, float flRoll, float flRollDelta ) { VPROF_BUDGET( "FX_Smoke", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); CSmartPtr pSimple = CSimpleEmitter::Create( "FX_Smoke" ); pSimple->SetSortOrigin( origin ); SimpleParticle *pParticle; // Smoke for ( int i = 0; i < numParticles; i++ ) { PMaterialHandle hMaterial = pSimple->GetPMaterial( pMaterial ); pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, origin ); if ( pParticle == NULL ) return NULL; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = flDietime; pParticle->m_vecVelocity = velocity; for( int i = 0; i < 3; ++i ) { pParticle->m_uchColor[i] = pColor[i]; } pParticle->m_uchStartAlpha = iAlpha; pParticle->m_uchEndAlpha = 0; pParticle->m_uchStartSize = scale; pParticle->m_uchEndSize = pParticle->m_uchStartSize*2; pParticle->m_flRoll = flRoll; pParticle->m_flRollDelta = flRollDelta; } return pSimple; } //----------------------------------------------------------------------------- // Purpose: Smoke puffs //----------------------------------------------------------------------------- void FX_Smoke( const Vector &origin, const QAngle &angles, float scale, int numParticles, unsigned char *pColor, int iAlpha ) { VPROF_BUDGET( "FX_Smoke", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); Vector vecVelocity; Vector vecForward; // Smoke for ( int i = 0; i < numParticles; i++ ) { // Velocity AngleVectors( angles, &vecForward ); vecVelocity.Random( -0.5f, 0.5f ); vecVelocity += vecForward; VectorNormalize( vecVelocity ); vecVelocity *= random->RandomFloat( 16.0f, 32.0f ); vecVelocity[2] += random->RandomFloat( 4.0f, 16.0f ); // Color unsigned char particlecolor[3]; if ( !pColor ) { int color = random->RandomInt( 64, 164 ); particlecolor[0] = color; particlecolor[1] = color; particlecolor[2] = color; } else { particlecolor[0] = pColor[0]; particlecolor[1] = pColor[1]; particlecolor[2] = pColor[2]; } // Alpha int alpha = iAlpha; if ( alpha == -1 ) { alpha = random->RandomInt( 10, 25 ); } // Scale int iSize = random->RandomInt( 4, 8 ) * scale; // Roll float flRoll = random->RandomInt( 0, 360 ); float flRollDelta = random->RandomFloat( -4.0f, 4.0f ); //pParticle->m_uchEndSize = pParticle->m_uchStartSize*2; FX_Smoke( origin, vecVelocity, iSize, 1, random->RandomFloat( 0.5f, 1.0f ), particlecolor, alpha, "particle/particle_smokegrenade", flRoll, flRollDelta ); } } //----------------------------------------------------------------------------- // Purpose: Smoke Emitter // This is an emitter that keeps spitting out particles for its lifetime // It won't create particles if it's not in view, so it's best when short lived //----------------------------------------------------------------------------- class CSmokeEmitter : public CSimpleEmitter { typedef CSimpleEmitter BaseClass; public: CSmokeEmitter( ClientEntityHandle_t hEntity, int nAttachment, const char *pDebugName ) : CSimpleEmitter( pDebugName ) { m_hEntity = hEntity; m_nAttachmentIndex = nAttachment; m_flDeathTime = 0; m_flLastParticleSpawnTime = 0; } // Create static CSmokeEmitter *Create( ClientEntityHandle_t hEntity, int nAttachment, const char *pDebugName="smoke" ) { return new CSmokeEmitter( hEntity, nAttachment, pDebugName ); } void SetLifeTime( float flTime ) { m_flDeathTime = gpGlobals->curtime + flTime; } void SetSpurtAngle( QAngle &vecAngles ) { AngleVectors( vecAngles, &m_vecSpurtForward ); } void SetSpurtColor( const Vector4D &pColor ) { for ( int i = 0; i <= 3; i++ ) { m_SpurtColor[i] = pColor[i]; } } void SetSpawnRate( float flRate ) { m_flSpawnRate = flRate; } void CreateSpurtParticles( void ) { SimpleParticle *pParticle; // PMaterialHandle hMaterial = GetPMaterial( "particle/particle_smokegrenade" ); Vector vecOrigin = m_vSortOrigin; IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle(m_hEntity); if ( pRenderable && m_nAttachmentIndex ) { QAngle tmp; pRenderable->GetAttachment( m_nAttachmentIndex, vecOrigin, tmp ); SetSortOrigin( vecOrigin ); } // Smoke int numParticles = RandomInt( 1,2 ); for ( int i = 0; i < numParticles; i++ ) { pParticle = (SimpleParticle *) AddParticle( sizeof( SimpleParticle ), g_Mat_DustPuff[0], vecOrigin ); if ( pParticle == NULL ) break; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = RandomFloat( 0.5, 1.0 ); // Random velocity around the angles forward Vector vecVelocity; vecVelocity.Random( -0.1f, 0.1f ); vecVelocity += m_vecSpurtForward; VectorNormalize( vecVelocity ); vecVelocity *= RandomFloat( 160.0f, 640.0f ); pParticle->m_vecVelocity = vecVelocity; // Randomize the color a little int color[3][2]; for( int i = 0; i < 3; ++i ) { color[i][0] = MAX( 0, m_SpurtColor[i] - 64 ); color[i][1] = MIN( 255, m_SpurtColor[i] + 64 ); } pParticle->m_uchColor[0] = random->RandomInt( color[0][0], color[0][1] ); pParticle->m_uchColor[1] = random->RandomInt( color[1][0], color[1][1] ); pParticle->m_uchColor[2] = random->RandomInt( color[2][0], color[2][1] ); pParticle->m_uchStartAlpha = m_SpurtColor[3]; pParticle->m_uchEndAlpha = 0; pParticle->m_uchStartSize = RandomInt( 50, 60 ); pParticle->m_uchEndSize = pParticle->m_uchStartSize*3; pParticle->m_flRoll = RandomFloat( 0, 360 ); pParticle->m_flRollDelta = RandomFloat( -4.0f, 4.0f ); } m_flLastParticleSpawnTime = gpGlobals->curtime + m_flSpawnRate; } virtual void SimulateParticles( CParticleSimulateIterator *pIterator ) { Particle *pParticle = pIterator->GetFirst(); while ( pParticle ) { // If our lifetime isn't up, create more particles if ( m_flDeathTime > gpGlobals->curtime ) { if ( m_flLastParticleSpawnTime <= gpGlobals->curtime ) { CreateSpurtParticles(); } } pParticle = pIterator->GetNext(); } BaseClass::SimulateParticles( pIterator ); } private: float m_flDeathTime; float m_flLastParticleSpawnTime; float m_flSpawnRate; Vector m_vecSpurtForward; Vector4D m_SpurtColor; ClientEntityHandle_t m_hEntity; int m_nAttachmentIndex; CSmokeEmitter( const CSmokeEmitter & ); // not defined, not accessible }; //----------------------------------------------------------------------------- // Purpose: Small hose gas spurt //----------------------------------------------------------------------------- void FX_BuildSmoke( Vector &vecOrigin, QAngle &vecAngles, ClientEntityHandle_t hEntity, int nAttachment, float flLifeTime, const Vector4D &pColor ) { CSmartPtr pSimple = CSmokeEmitter::Create( hEntity, nAttachment, "FX_Smoke" ); pSimple->SetSortOrigin( vecOrigin ); pSimple->SetLifeTime( flLifeTime ); pSimple->SetSpurtAngle( vecAngles ); pSimple->SetSpurtColor( pColor ); pSimple->SetSpawnRate( 0.03 ); pSimple->CreateSpurtParticles(); } //----------------------------------------------------------------------------- // Purpose: Green hose gas spurt //----------------------------------------------------------------------------- void SmokeCallback( const CEffectData &data ) { Vector vecOrigin = data.m_vOrigin; QAngle vecAngles = data.m_vAngles; Vector4D color( 50,50,50,255 ); FX_BuildSmoke( vecOrigin, vecAngles, data.m_hEntity, data.m_nAttachmentIndex, 100.0, color ); } DECLARE_CLIENT_EFFECT( Smoke, SmokeCallback ); //----------------------------------------------------------------------------- // Purpose: Shockwave for gunship bullet impacts! // Input : &pos - // // NOTES: -Don't draw this effect when the viewer is very far away. //----------------------------------------------------------------------------- void FX_GunshipImpact( const Vector &pos, const Vector &normal, float r, float g, float b ) { VPROF_BUDGET( "FX_GunshipImpact", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); if ( CImpactOverlay *pOverlay = new CImpactOverlay ) { pOverlay->m_flLifetime = 0; VectorMA( pos, 1.0f, normal, pOverlay->m_vPos ); // Doesn't show up on terrain if you don't do this(sjb) pOverlay->m_nSprites = 1; pOverlay->m_vBaseColors[0].Init( r, g, b ); pOverlay->m_Sprites[0].m_flHorzSize = 0.01f; pOverlay->m_Sprites[0].m_flVertSize = 0.01f; pOverlay->Activate(); } } //----------------------------------------------------------------------------- // Purpose: // Input : data - //----------------------------------------------------------------------------- void GunshipImpactCallback( const CEffectData & data ) { Vector vecPosition; vecPosition = data.m_vOrigin; FX_GunshipImpact( vecPosition, Vector( 0, 0, 1 ), 100, 0, 200 ); } DECLARE_CLIENT_EFFECT( GunshipImpact, GunshipImpactCallback ); //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CommandPointerCallback( const CEffectData & data ) { int size = COLOR_TABLE_SIZE( commandercolors ); for( int i = 0 ; i < size ; i++ ) { if( commandercolors[ i ].index == data.m_nColor ) { FX_GunshipImpact( data.m_vOrigin, Vector( 0, 0, 1 ), commandercolors[ i ].r, commandercolors[ i ].g, commandercolors[ i ].b ); return; } } } DECLARE_CLIENT_EFFECT( CommandPointer, CommandPointerCallback ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void FX_GunshipMuzzleEffect( const Vector &origin, const QAngle &angles, float scale, ClientEntityHandle_t hEntity, unsigned char *pFlashColor ) { VPROF_BUDGET( "FX_GunshipMuzzleEffect", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); CSmartPtr pSimple = CSimpleEmitter::Create( "MuzzleFlash" ); pSimple->SetSortOrigin( origin ); SimpleParticle *pParticle; Vector forward, offset; AngleVectors( angles, &forward ); // // Flash // offset = origin; pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "effects/gunshipmuzzle" ), offset ); if ( pParticle == NULL ) return; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = 0.15f; pParticle->m_vecVelocity.Init(); pParticle->m_uchStartSize = random->RandomFloat( 40.0, 50.0 ); pParticle->m_uchEndSize = pParticle->m_uchStartSize; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = 0.15f; pParticle->m_uchColor[0] = 255; pParticle->m_uchColor[1] = 255; pParticle->m_uchColor[2] = 255; pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 255; } //----------------------------------------------------------------------------- // Purpose: // Input : start - // end - // velocity - // makeWhiz - //----------------------------------------------------------------------------- void FX_GunshipTracer( Vector& start, Vector& end, int velocity, bool makeWhiz ) { VPROF_BUDGET( "FX_GunshipTracer", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); Vector vNear, dStart, dEnd, shotDir; float totalDist; //Get out shot direction and length VectorSubtract( end, start, shotDir ); totalDist = VectorNormalize( shotDir ); //Don't make small tracers if ( totalDist <= 256 ) return; float length = random->RandomFloat( 128.0f, 256.0f ); float life = ( totalDist + length ) / velocity; //NOTENOTE: We want the tail to finish its run as well //Add it FX_AddDiscreetLine( start, shotDir, velocity, length, totalDist, 5.0f, life, "effects/gunshiptracer" ); if( makeWhiz ) { FX_TracerSound( start, end, TRACER_TYPE_GUNSHIP ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void FX_StriderMuzzleEffect( const Vector &origin, const QAngle &angles, float scale, ClientEntityHandle_t hEntity, unsigned char *pFlashColor ) { Vector vecDir; AngleVectors( angles, &vecDir ); float life = 0.3f; float speed = 100.0f; for( int i = 0 ; i < 5 ; i++ ) { FX_AddDiscreetLine( origin, vecDir, speed, 32, speed * life, 5.0f, life, "effects/bluespark" ); speed *= 1.5f; } } //----------------------------------------------------------------------------- // Purpose: // Input : start - // end - // velocity - // makeWhiz - //----------------------------------------------------------------------------- void FX_StriderTracer( Vector& start, Vector& end, int velocity, bool makeWhiz ) { VPROF_BUDGET( "FX_StriderTracer", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); Vector vNear, dStart, dEnd, shotDir; float totalDist; //Get out shot direction and length VectorSubtract( end, start, shotDir ); totalDist = VectorNormalize( shotDir ); //Don't make small tracers if ( totalDist <= 256 ) return; float length = random->RandomFloat( 64.0f, 128.0f ); float life = ( totalDist + length ) / velocity; //NOTENOTE: We want the tail to finish its run as well //Add it FX_AddDiscreetLine( start, shotDir, velocity, length, totalDist, 2.5f, life, "effects/gunshiptracer" ); if( makeWhiz ) { FX_TracerSound( start, end, TRACER_TYPE_STRIDER ); } } //----------------------------------------------------------------------------- // Purpose: // Input : start - // end - // velocity - // makeWhiz - //----------------------------------------------------------------------------- void FX_HunterTracer( Vector& start, Vector& end, int velocity, bool makeWhiz ) { VPROF_BUDGET( "FX_HunterTracer", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); Vector vNear, dStart, dEnd, shotDir; float totalDist; // Get out shot direction and length VectorSubtract( end, start, shotDir ); totalDist = VectorNormalize( shotDir ); // Make short tracers in close quarters // float flMinLength = MIN( totalDist, 128.0f ); // float flMaxLength = MIN( totalDist, 128.0f ); float length = 128.0f;//random->RandomFloat( flMinLength, flMaxLength ); float life = ( totalDist + length ) / velocity; // NOTENOTE: We want the tail to finish its run as well // Add it FX_AddDiscreetLine( start, shotDir, velocity*0.5f, length, totalDist, 2.0f, life, "effects/huntertracer" ); if( makeWhiz ) { FX_TracerSound( start, end, TRACER_TYPE_STRIDER ); } } //----------------------------------------------------------------------------- // Purpose: // Input : start - // end - // velocity - // makeWhiz - //----------------------------------------------------------------------------- void FX_GaussTracer( Vector& start, Vector& end, int velocity, bool makeWhiz ) { VPROF_BUDGET( "FX_GaussTracer", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); Vector vNear, dStart, dEnd, shotDir; float totalDist; //Get out shot direction and length VectorSubtract( end, start, shotDir ); totalDist = VectorNormalize( shotDir ); //Don't make small tracers if ( totalDist <= 256 ) return; float length = random->RandomFloat( 250.0f, 500.0f ); float life = ( totalDist + length ) / velocity; //NOTENOTE: We want the tail to finish its run as well //Add it FX_AddDiscreetLine( start, shotDir, velocity, length, totalDist, random->RandomFloat( 5.0f, 8.0f ), life, "effects/spark" ); } //----------------------------------------------------------------------------- // Purpose: Create a tesla effect between two points //----------------------------------------------------------------------------- void FX_BuildTesla( C_BaseEntity *pEntity, const Vector &vecOrigin, const Vector &vecEnd, const char *pModelName, float flBeamWidth, const Vector &vColor, int nFlags, float flTimeVisible ) { BeamInfo_t beamInfo; beamInfo.m_nType = TE_BEAMTESLA; beamInfo.m_vecStart = vecOrigin; beamInfo.m_vecEnd = vecEnd; beamInfo.m_pszModelName = pModelName; beamInfo.m_flHaloScale = 0.0; beamInfo.m_flLife = flTimeVisible; beamInfo.m_flWidth = flBeamWidth; beamInfo.m_flEndWidth = 1; beamInfo.m_flFadeLength = 0.3; beamInfo.m_flAmplitude = 16; beamInfo.m_flBrightness = 200.0; beamInfo.m_flSpeed = 0.0; beamInfo.m_nStartFrame = 0.0; beamInfo.m_flFrameRate = 1.0; beamInfo.m_flRed = vColor.x * 255.0; beamInfo.m_flGreen = vColor.y * 255.0; beamInfo.m_flBlue = vColor.z * 255.0; beamInfo.m_nSegments = 20; beamInfo.m_bRenderable = true; beamInfo.m_nFlags = nFlags; beams->CreateBeamPoints( beamInfo ); } void FX_Tesla( const CTeslaInfo &teslaInfo ) { C_BaseEntity *pEntity = ClientEntityList().GetEnt( teslaInfo.m_nEntIndex ); // Send out beams around us int iNumBeamsAround = (teslaInfo.m_nBeams * 2) / 3; // (2/3 of the beams are placed around in a circle) int iNumRandomBeams = teslaInfo.m_nBeams - iNumBeamsAround; int iTotalBeams = iNumBeamsAround + iNumRandomBeams; float flYawOffset = RandomFloat(0,360); for ( int i = 0; i < iTotalBeams; i++ ) { // Make a couple of tries at it int iTries = -1; Vector vecForward; trace_t tr; do { iTries++; // Some beams are deliberatly aimed around the point, the rest are random. if ( i < iNumBeamsAround ) { QAngle vecTemp = teslaInfo.m_vAngles; vecTemp[YAW] += anglemod( flYawOffset + ((360 / iTotalBeams) * i) ); AngleVectors( vecTemp, &vecForward ); // Randomly angle it up or down vecForward.z = RandomFloat( -1, 1 ); } else { vecForward = RandomVector( -1, 1 ); } VectorNormalize( vecForward ); UTIL_TraceLine( teslaInfo.m_vPos, teslaInfo.m_vPos + (vecForward * teslaInfo.m_flRadius), MASK_SHOT, pEntity, COLLISION_GROUP_NONE, &tr ); } while ( tr.fraction >= 1.0 && iTries < 3 ); Vector vecEnd = tr.endpos - (vecForward * 8); // Only spark & glow if we hit something if ( tr.fraction < 1.0 ) { if ( !EffectOccluded( tr.endpos, 0 ) ) { int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT(); // Move it towards the camera Vector vecFlash = tr.endpos; Vector vecForward; AngleVectors( MainViewAngles(nSlot), &vecForward ); vecFlash -= (vecForward * 8); g_pEffects->EnergySplash( vecFlash, -vecForward, false ); // End glow CSmartPtr pSimple = CSimpleEmitter::Create( "dust" ); pSimple->SetSortOrigin( vecFlash ); SimpleParticle *pParticle; pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "effects/tesla_glow_noz" ), vecFlash ); if ( pParticle != NULL ) { pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = RandomFloat( 0.5, 1 ); pParticle->m_vecVelocity = vec3_origin; Vector color( 1,1,1 ); float colorRamp = RandomFloat( 0.75f, 1.25f ); pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f; pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f; pParticle->m_uchStartSize = RandomFloat( 6,13 ); pParticle->m_uchEndSize = pParticle->m_uchStartSize - 2; pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 10; pParticle->m_flRoll = RandomFloat( 0,360 ); pParticle->m_flRollDelta = 0; } } } // Build the tesla FX_BuildTesla( pEntity, teslaInfo.m_vPos, tr.endpos, teslaInfo.m_pszSpriteName, teslaInfo.m_flBeamWidth, teslaInfo.m_vColor, FBEAM_ONLYNOISEONCE, teslaInfo.m_flTimeVisible ); } } //----------------------------------------------------------------------------- // Purpose: Tesla effect //----------------------------------------------------------------------------- void BuildTeslaCallback( const CEffectData &data ) { if ( data.entindex() < 0 ) return; CTeslaInfo teslaInfo; teslaInfo.m_vPos = data.m_vOrigin; teslaInfo.m_vAngles = data.m_vAngles; teslaInfo.m_nEntIndex = data.entindex(); teslaInfo.m_flBeamWidth = 5; teslaInfo.m_vColor.Init( 1, 1, 1 ); teslaInfo.m_flTimeVisible = 0.3; teslaInfo.m_flRadius = 192; teslaInfo.m_nBeams = 6; teslaInfo.m_pszSpriteName = "sprites/physbeam.vmt"; FX_Tesla( teslaInfo ); } //----------------------------------------------------------------------------- // Purpose: Tesla hitbox //----------------------------------------------------------------------------- void FX_BuildTeslaHitbox( C_BaseEntity *pEntity, int nStartAttachment, int nEndAttachment, float flBeamWidth, const Vector &vColor, float flTimeVisible ) { // One along the body BeamInfo_t beamInfo; beamInfo.m_nType = TE_BEAMTESLA; beamInfo.m_vecStart = vec3_origin; beamInfo.m_vecEnd = vec3_origin; beamInfo.m_pszModelName = "sprites/lgtning.vmt"; beamInfo.m_flHaloScale = 8.0f; beamInfo.m_flLife = 0.01f; beamInfo.m_flWidth = random->RandomFloat( 3.0f, 6.0f ); beamInfo.m_flEndWidth = 0.0f; beamInfo.m_flFadeLength = 0.0f; beamInfo.m_flAmplitude = random->RandomInt( 16, 32 ); beamInfo.m_flBrightness = 255.0f; beamInfo.m_flSpeed = 32.0; beamInfo.m_nStartFrame = 0.0; beamInfo.m_flFrameRate = 30.0; beamInfo.m_flRed = vColor.x * 255.0; beamInfo.m_flGreen = vColor.y * 255.0; beamInfo.m_flBlue = vColor.z * 255.0; beamInfo.m_nSegments = 32; beamInfo.m_bRenderable = true; beamInfo.m_pStartEnt = beamInfo.m_pEndEnt = NULL; beamInfo.m_nFlags = (FBEAM_USE_HITBOXES); beamInfo.m_pStartEnt = pEntity; beamInfo.m_nStartAttachment = nStartAttachment; beamInfo.m_pEndEnt = pEntity; beamInfo.m_nEndAttachment = nEndAttachment; beams->CreateBeamEntPoint( beamInfo ); // One out to the world trace_t tr; Vector randomDir; randomDir = RandomVector( -1.0f, 1.0f ); VectorNormalize( randomDir ); UTIL_TraceLine( pEntity->WorldSpaceCenter(), pEntity->WorldSpaceCenter() + ( randomDir * 100 ), MASK_SOLID_BRUSHONLY, pEntity, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction < 1.0f ) { beamInfo.m_nType = TE_BEAMTESLA; beamInfo.m_vecStart = vec3_origin; beamInfo.m_vecEnd = tr.endpos; beamInfo.m_pszModelName = "sprites/lgtning.vmt"; beamInfo.m_flHaloScale = 8.0f; beamInfo.m_flLife = 0.05f; beamInfo.m_flWidth = random->RandomFloat( 2.0f, 6.0f ); beamInfo.m_flEndWidth = 0.0f; beamInfo.m_flFadeLength = 0.0f; beamInfo.m_flAmplitude = random->RandomInt( 16, 32 ); beamInfo.m_flBrightness = 255.0f; beamInfo.m_flSpeed = 32.0; beamInfo.m_nStartFrame = 0.0; beamInfo.m_flFrameRate = 30.0; beamInfo.m_flRed = vColor.x * 255.0; beamInfo.m_flGreen = vColor.y * 255.0; beamInfo.m_flBlue = vColor.z * 255.0; beamInfo.m_nSegments = 32; beamInfo.m_bRenderable = true; beamInfo.m_pStartEnt = beamInfo.m_pEndEnt = NULL; beamInfo.m_pStartEnt = pEntity; beamInfo.m_nStartAttachment = nStartAttachment; beams->CreateBeamEntPoint( beamInfo ); } // Create an elight to illuminate the target if ( pEntity != NULL ) { dlight_t *el = effects->CL_AllocElight( LIGHT_INDEX_TE_DYNAMIC + pEntity->entindex() ); // Randomly place it el->origin = pEntity->WorldSpaceCenter() + RandomVector( -32, 32 ); el->color.r = 235; el->color.g = 235; el->color.b = 255; el->color.exponent = 4; el->radius = random->RandomInt( 32, 128 ); el->decay = el->radius / 0.1f; el->die = gpGlobals->curtime + 0.1f; } } //----------------------------------------------------------------------------- // Purpose: Tesla effect //----------------------------------------------------------------------------- void FX_BuildTeslaHitbox( const CEffectData &data ) { Vector vColor( 1, 1, 1 ); C_BaseEntity *pEntity = ClientEntityList().GetEnt( data.entindex() ); C_BaseAnimating *pAnimating = pEntity ? pEntity->GetBaseAnimating() : NULL; if (!pAnimating) return; studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( pAnimating->GetModel() ); if (!pStudioHdr) return; mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pAnimating->GetHitboxSet() ); if ( !set ) return; matrix3x4_t *hitboxbones[MAXSTUDIOBONES]; if ( !pAnimating->HitboxToWorldTransforms( hitboxbones ) ) return; int nBeamCount = (int)(data.m_flMagnitude + 0.5f); for ( int i = 0; i < nBeamCount; ++i ) { int nStartHitBox = random->RandomInt( 1, set->numhitboxes ); int nEndHitBox = random->RandomInt( 1, set->numhitboxes ); FX_BuildTeslaHitbox( pEntity, nStartHitBox, nEndHitBox, data.m_flScale, vColor, random->RandomFloat( 0.05f, 0.2f ) ); } } DECLARE_CLIENT_EFFECT( TeslaHitboxes, FX_BuildTeslaHitbox ); //----------------------------------------------------------------------------- // Purpose: Tesla effect //----------------------------------------------------------------------------- void FX_BuildTeslaZap( const CEffectData &data ) { // Build the tesla, only works on entities C_BaseEntity *pEntity = data.GetEntity(); if ( !pEntity ) return; Vector vColor( 1, 1, 1 ); BeamInfo_t beamInfo; beamInfo.m_nType = TE_BEAMTESLA; beamInfo.m_pStartEnt = pEntity; beamInfo.m_nStartAttachment = data.m_nAttachmentIndex; beamInfo.m_pEndEnt = NULL; beamInfo.m_vecEnd = data.m_vOrigin; beamInfo.m_pszModelName = "sprites/physbeam.vmt"; beamInfo.m_flHaloScale = 0.0; beamInfo.m_flLife = 0.3f; beamInfo.m_flWidth = data.m_flScale; beamInfo.m_flEndWidth = 1; beamInfo.m_flFadeLength = 0.3; beamInfo.m_flAmplitude = 16; beamInfo.m_flBrightness = 200.0; beamInfo.m_flSpeed = 0.0; beamInfo.m_nStartFrame = 0.0; beamInfo.m_flFrameRate = 1.0; beamInfo.m_flRed = vColor.x * 255.0; beamInfo.m_flGreen = vColor.y * 255.0; beamInfo.m_flBlue = vColor.z * 255.0; beamInfo.m_nSegments = 20; beamInfo.m_bRenderable = true; beamInfo.m_nFlags = 0; beams->CreateBeamEntPoint( beamInfo ); } DECLARE_CLIENT_EFFECT( TeslaZap, FX_BuildTeslaZap );