Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1328 lines
39 KiB

//========= Copyright 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 "clienteffectprecachesystem.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
#ifndef TF_CLIENT_DLL
CLIENTEFFECT_REGISTER_BEGIN( PrecacheMuzzleFlash )
CLIENTEFFECT_MATERIAL( "effects/muzzleflash1" )
CLIENTEFFECT_MATERIAL( "effects/muzzleflash2" )
CLIENTEFFECT_MATERIAL( "effects/muzzleflash3" )
CLIENTEFFECT_MATERIAL( "effects/muzzleflash4" )
#ifndef CSTRIKE_DLL
CLIENTEFFECT_MATERIAL( "effects/bluemuzzle" )
CLIENTEFFECT_MATERIAL( "effects/gunshipmuzzle" )
CLIENTEFFECT_MATERIAL( "effects/gunshiptracer" )
#ifndef HL2MP
CLIENTEFFECT_MATERIAL( "effects/huntertracer" )
#endif
CLIENTEFFECT_MATERIAL( "sprites/physcannon_bluelight2" )
CLIENTEFFECT_MATERIAL( "effects/combinemuzzle1" )
CLIENTEFFECT_MATERIAL( "effects/combinemuzzle2" )
CLIENTEFFECT_MATERIAL( "effects/combinemuzzle2_nocull" )
#endif
CLIENTEFFECT_REGISTER_END()
#endif
//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<CSimpleEmitter> 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 );
// If the material isn't available, let's not do anything.
if ( g_Mat_SMG_Muzzleflash[0] == NULL )
{
return;
}
CSmartPtr<CLocalSpaceEmitter> 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*)(intp)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<CSimpleEmitter> 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<CSimpleEmitter> 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<CSmokeEmitter> 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<CSimpleEmitter> 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 ) )
{
// Move it towards the camera
Vector vecFlash = tr.endpos;
Vector vecForward;
AngleVectors( MainViewAngles(), &vecForward );
vecFlash -= (vecForward * 8);
g_pEffects->EnergySplash( vecFlash, -vecForward, false );
// End glow
CSmartPtr<CSimpleEmitter> 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 );