text: //========= 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
  #if !(defined(TF_CLIENT_DLL) || defined(TF_EP_CLIENT))
  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*)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 );
  
  