//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $Workfile:     $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "model_types.h"
#include "view_shared.h"
#include "iviewrender.h"
#include "tempentity.h"
#include "dlight.h"
#include "tempent.h"
#include "c_te_legacytempents.h"
#include "clientsideeffects.h"
#include "cl_animevent.h"
#include "iefx.h"
#include "engine/IEngineSound.h"
#include "env_wind_shared.h"
#include "clienteffectprecachesystem.h"
#include "fx_sparks.h"
#include "fx.h"
#include "movevars_shared.h"
#include "engine/ivmodelinfo.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "view.h"
#include "tier0/vprof.h"
#include "particles_localspace.h"
#include "physpropclientside.h"
#include "tier0/icommandline.h"
#include "datacache/imdlcache.h"
#include "engine/ivdebugoverlay.h"
#include "effect_dispatch_data.h"
#include "c_te_effect_dispatch.h"
#include "c_props.h"
#include "c_basedoor.h"

// NOTE: Always include this last!
#include "tier0/memdbgon.h"

extern ConVar muzzleflash_light;

#define TENT_WIND_ACCEL 50

//Precache the effects
#ifndef TF_CLIENT_DLL
CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectMuzzleFlash )

	CLIENTEFFECT_MATERIAL( "effects/muzzleflash1" )
	CLIENTEFFECT_MATERIAL( "effects/muzzleflash2" )
	CLIENTEFFECT_MATERIAL( "effects/muzzleflash3" )
	CLIENTEFFECT_MATERIAL( "effects/muzzleflash4" )
	CLIENTEFFECT_MATERIAL( "effects/muzzleflash1_noz" )
	CLIENTEFFECT_MATERIAL( "effects/muzzleflash2_noz" )
	CLIENTEFFECT_MATERIAL( "effects/muzzleflash3_noz" )
	CLIENTEFFECT_MATERIAL( "effects/muzzleflash4_noz" )
#ifndef CSTRIKE_DLL
	CLIENTEFFECT_MATERIAL( "effects/combinemuzzle1" )
	CLIENTEFFECT_MATERIAL( "effects/combinemuzzle2" )
	CLIENTEFFECT_MATERIAL( "effects/combinemuzzle1_noz" )
	CLIENTEFFECT_MATERIAL( "effects/combinemuzzle2_noz" )
	CLIENTEFFECT_MATERIAL( "effects/strider_muzzle" )
#endif
CLIENTEFFECT_REGISTER_END()
#endif

//Whether or not to eject brass from weapons
ConVar cl_ejectbrass( "cl_ejectbrass", "1" );

ConVar func_break_max_pieces( "func_break_max_pieces", "15", FCVAR_ARCHIVE | FCVAR_REPLICATED );

ConVar cl_fasttempentcollision( "cl_fasttempentcollision", "5" );

#if !defined( HL1_CLIENT_DLL )		// HL1 implements a derivative of CTempEnts
// Temp entity interface
static CTempEnts g_TempEnts;
// Expose to rest of the client .dll
ITempEnts *tempents = ( ITempEnts * )&g_TempEnts;
#endif




C_LocalTempEntity::C_LocalTempEntity()
{
#ifdef _DEBUG
	tentOffset.Init();
	m_vecTempEntVelocity.Init();
	m_vecTempEntAngVelocity.Init();
	m_vecNormal.Init();
#endif
	m_vecTempEntAcceleration.Init();
	m_pfnDrawHelper = 0;
	m_pszImpactEffect = NULL;
}


#if defined( CSTRIKE_DLL ) || defined (SDK_DLL )

#define TE_RIFLE_SHELL 1024
#define TE_PISTOL_SHELL 2048
#define TE_SHOTGUN_SHELL 4096

#endif

//-----------------------------------------------------------------------------
// Purpose: Prepare a temp entity for creation
// Input  : time - 
//			*model - 
//-----------------------------------------------------------------------------
void C_LocalTempEntity::Prepare( const model_t *pmodel, float time )
{
	Interp_SetupMappings( GetVarMapping() );

	index = -1;
	Clear();

	// Use these to set per-frame and termination conditions / actions
	flags = FTENT_NONE;		
	die = time + 0.75;
	SetModelPointer( pmodel );
	SetRenderMode( kRenderNormal );
	m_nRenderFX = kRenderFxNone;
	m_nBody = 0;
	m_nSkin = 0;
	fadeSpeed = 0.5;
	hitSound = 0;
	clientIndex = -1;
	bounceFactor = 1;
	m_nFlickerFrame = 0;
	m_bParticleCollision = false;
}

//-----------------------------------------------------------------------------
// Sets the velocity
//-----------------------------------------------------------------------------
void C_LocalTempEntity::SetVelocity( const Vector &vecVelocity )
{
	m_vecTempEntVelocity = vecVelocity;
}

//-----------------------------------------------------------------------------
// Sets the velocity
//-----------------------------------------------------------------------------
void C_LocalTempEntity::SetAcceleration( const Vector &vecVelocity )
{
	m_vecTempEntAcceleration = vecVelocity;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Output : int
//-----------------------------------------------------------------------------
int C_LocalTempEntity::DrawStudioModel( int flags )
{
	VPROF_BUDGET( "C_LocalTempEntity::DrawStudioModel", VPROF_BUDGETGROUP_MODEL_RENDERING );
	int drawn = 0;

	if ( !GetModel() || modelinfo->GetModelType( GetModel() ) != mod_studio )
		return drawn;
	
	// Make sure m_pstudiohdr is valid for drawing
	MDLCACHE_CRITICAL_SECTION();
	if ( !GetModelPtr() )
		return drawn;

	if ( m_pfnDrawHelper )
	{
		drawn = ( *m_pfnDrawHelper )( this, flags );
	}
	else
	{
		drawn = modelrender->DrawModel( 
			flags, 
			this,
			MODEL_INSTANCE_INVALID,
			index, 
			GetModel(),
			GetAbsOrigin(),
			GetAbsAngles(),
			m_nSkin,
			m_nBody,
			m_nHitboxSet );
	}
	return drawn;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flags - 
//-----------------------------------------------------------------------------
int	C_LocalTempEntity::DrawModel( int flags )
{
	int drawn = 0;

	if ( !GetModel() )
	{
		return drawn;
	}

	if ( GetRenderMode() == kRenderNone )
		return drawn;

	if ( this->flags & FTENT_BEOCCLUDED )
	{
		// Check normal
		Vector vecDelta = (GetAbsOrigin() - MainViewOrigin());
		VectorNormalize( vecDelta );
		float flDot = DotProduct( m_vecNormal, vecDelta );
		if ( flDot > 0 )
		{
			float flAlpha = RemapVal( MIN(flDot,0.3), 0, 0.3, 0, 1 );
			flAlpha = MAX( 1.0, tempent_renderamt - (tempent_renderamt * flAlpha) );
			SetRenderColorA( flAlpha );
		}
	}

	switch ( modelinfo->GetModelType( GetModel() ) )
	{
	case mod_sprite:
		drawn = DrawSprite( 
			this,
			GetModel(), 
			GetAbsOrigin(), 
			GetAbsAngles(), 
			m_flFrame,  // sprite frame to render
			m_nBody > 0 ? cl_entitylist->GetBaseEntity( m_nBody ) : NULL,  // attach to
			m_nSkin,  // attachment point
			GetRenderMode(), // rendermode
			m_nRenderFX, // renderfx
			m_clrRender->a, // alpha
			m_clrRender->r,
			m_clrRender->g,
			m_clrRender->b,
			m_flSpriteScale		  // sprite scale
			);
		break;
	case mod_studio:
		drawn = DrawStudioModel( flags );
		break;
	default:
		break;
	}

	return drawn;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool C_LocalTempEntity::IsActive( void )
{
	bool active = true;

	float life = die - gpGlobals->curtime;
	
	if ( life < 0 )
	{
		if ( flags & FTENT_FADEOUT )
		{
			int alpha;
			if (GetRenderMode() == kRenderNormal)
			{
				SetRenderMode( kRenderTransTexture );
			}
			
			alpha = tempent_renderamt * ( 1 + life * fadeSpeed );
			
			if ( alpha <= 0 )
			{
				active = false;
				alpha = 0;
			}

			SetRenderColorA( alpha );
		}
		else 
		{
			active = false;
		}
	}

	// Never die tempents only die when their die is cleared
	if ( flags & FTENT_NEVERDIE )
	{
		active = (die != 0);
	}

	return active;
}

bool C_LocalTempEntity::Frame( float frametime, int framenumber )
{
	float fastFreq = gpGlobals->curtime * 5.5;
	float gravity = -frametime * GetCurrentGravity();
	float gravitySlow = gravity * 0.5;
	float traceFraction = 1;

	Assert( !GetMoveParent() );

	m_vecPrevLocalOrigin = GetLocalOrigin();

	m_vecTempEntVelocity = m_vecTempEntVelocity + ( m_vecTempEntAcceleration * frametime );

	if ( flags & FTENT_PLYRATTACHMENT )
	{
		if ( IClientEntity *pClient = cl_entitylist->GetClientEntity( clientIndex ) )
		{
			SetLocalOrigin( pClient->GetAbsOrigin() + tentOffset );
		}
	}
	else if ( flags & FTENT_SINEWAVE )
	{
		x += m_vecTempEntVelocity[0] * frametime;
		y += m_vecTempEntVelocity[1] * frametime;

		SetLocalOrigin( Vector(
			x + sin( m_vecTempEntVelocity[2] + gpGlobals->curtime /* * anim.prevframe */ ) * (10*m_flSpriteScale),
			y + sin( m_vecTempEntVelocity[2] + fastFreq + 0.7 ) * (8*m_flSpriteScale),
			GetLocalOriginDim( Z_INDEX ) + m_vecTempEntVelocity[2] * frametime ) );
	}
	else if ( flags & FTENT_SPIRAL )
	{
		float s, c;
		SinCos( m_vecTempEntVelocity[2] + fastFreq, &s, &c );

		SetLocalOrigin( GetLocalOrigin() + Vector(
			m_vecTempEntVelocity[0] * frametime + 8 * sin( gpGlobals->curtime * 20 ),
			m_vecTempEntVelocity[1] * frametime + 4 * sin( gpGlobals->curtime * 30 ),
			m_vecTempEntVelocity[2] * frametime ) );
	}
	else
	{
		SetLocalOrigin( GetLocalOrigin() + m_vecTempEntVelocity * frametime );
	}
	
	if ( flags & FTENT_SPRANIMATE )
	{
		m_flFrame += frametime * m_flFrameRate;
		if ( m_flFrame >= m_flFrameMax )
		{
			m_flFrame = m_flFrame - (int)(m_flFrame);

			if ( !(flags & FTENT_SPRANIMATELOOP) )
			{
				// this animating sprite isn't set to loop, so destroy it.
				die = 0.0f;
				return false;
			}
		}
	}
	else if ( flags & FTENT_SPRCYCLE )
	{
		m_flFrame += frametime * 10;
		if ( m_flFrame >= m_flFrameMax )
		{
			m_flFrame = m_flFrame - (int)(m_flFrame);
		}
	}

	if ( flags & FTENT_SMOKEGROWANDFADE )
	{
		m_flSpriteScale += frametime * 0.5f;
		//m_clrRender.a -= frametime * 1500;
	}

	if ( flags & FTENT_ROTATE )
	{
		SetLocalAngles( GetLocalAngles() + m_vecTempEntAngVelocity * frametime );
	}
	else if ( flags & FTENT_ALIGNTOMOTION )
	{
		if ( m_vecTempEntVelocity.Length() > 0.0f )
		{
			QAngle angles;
			VectorAngles( m_vecTempEntVelocity, angles );
			SetAbsAngles( angles );
		}
	}

	if ( flags & (FTENT_COLLIDEALL | FTENT_COLLIDEWORLD | FTENT_COLLIDEPROPS ) )
	{
		Vector	traceNormal;
		traceNormal.Init();
		bool bShouldCollide = true;

		trace_t trace;

		if ( flags & (FTENT_COLLIDEALL | FTENT_COLLIDEPROPS) )
		{
			Vector vPrevOrigin = m_vecPrevLocalOrigin;

			if ( cl_fasttempentcollision.GetInt() > 0 && flags & FTENT_USEFASTCOLLISIONS )
			{
				if ( m_iLastCollisionFrame + cl_fasttempentcollision.GetInt() > gpGlobals->framecount )
				{
					bShouldCollide = false;
				}
				else
				{
					if ( m_vLastCollisionOrigin != vec3_origin )
					{
						vPrevOrigin = m_vLastCollisionOrigin;
					}

					m_iLastCollisionFrame = gpGlobals->framecount;
					bShouldCollide = true; 
				}
			}

			if ( bShouldCollide == true )
			{
				// If the FTENT_COLLISIONGROUP flag is set, use the entity's collision group
				int collisionGroup = COLLISION_GROUP_NONE;
				if ( flags & FTENT_COLLISIONGROUP )
				{
					collisionGroup = GetCollisionGroup();
				}

				UTIL_TraceLine( vPrevOrigin, GetLocalOrigin(), MASK_SOLID, GetOwnerEntity(), collisionGroup, &trace );

				if ( (flags & FTENT_COLLIDEPROPS) && trace.m_pEnt )
				{
					bool bIsDynamicProp = ( NULL != dynamic_cast<CDynamicProp *>( trace.m_pEnt ) );
					bool bIsDoor = ( NULL != dynamic_cast<CBaseDoor *>( trace.m_pEnt ) );
					if ( !bIsDynamicProp && !bIsDoor && !trace.m_pEnt->IsWorld() ) // Die on props, doors, and the world.
						return true;
				}

				// Make sure it didn't bump into itself... (?!?)
				if  ( 
					(trace.fraction != 1) && 
						( (trace.DidHitWorld()) || 
						  (trace.m_pEnt != ClientEntityList().GetEnt(clientIndex)) ) 
					)
				{
					traceFraction = trace.fraction;
					VectorCopy( trace.plane.normal, traceNormal );
				}

				m_vLastCollisionOrigin = trace.endpos;
			}
		}
		else if ( flags & FTENT_COLLIDEWORLD )
		{
			CTraceFilterWorldOnly traceFilter;
			UTIL_TraceLine( m_vecPrevLocalOrigin, GetLocalOrigin(), MASK_SOLID, &traceFilter, &trace );
			if ( trace.fraction != 1 )
			{
				traceFraction = trace.fraction;
				VectorCopy( trace.plane.normal, traceNormal );
			}
		}
		
		if ( traceFraction != 1  )	// Decent collision now, and damping works
		{
			float  proj, damp;
			SetLocalOrigin( trace.endpos );
			
			// Damp velocity
			damp = bounceFactor;
			if ( flags & (FTENT_GRAVITY|FTENT_SLOWGRAVITY) )
			{
				damp *= 0.5;
				if ( traceNormal[2] > 0.9 )		// Hit floor?
				{
					if ( m_vecTempEntVelocity[2] <= 0 && m_vecTempEntVelocity[2] >= gravity*3 )
					{
						damp = 0;		// Stop
						flags &= ~(FTENT_ROTATE|FTENT_GRAVITY|FTENT_SLOWGRAVITY|FTENT_COLLIDEWORLD|FTENT_SMOKETRAIL);
						SetLocalAnglesDim( X_INDEX, 0 );
						SetLocalAnglesDim( Z_INDEX, 0 );
					}
				}
			}

			if ( flags & (FTENT_CHANGERENDERONCOLLIDE) )
			{
				m_RenderGroup = RENDER_GROUP_OTHER;
				flags &= ~FTENT_CHANGERENDERONCOLLIDE;
			}	

			if (hitSound)
			{
				tempents->PlaySound(this, damp);
			}

			if ( m_pszImpactEffect )
			{
				CEffectData data;
				//data.m_vOrigin = newOrigin;
				data.m_vOrigin = trace.endpos;
				data.m_vStart = trace.startpos;
				data.m_nSurfaceProp = trace.surface.surfaceProps;
				data.m_nHitBox = trace.hitbox;

				data.m_nDamageType = TEAM_UNASSIGNED;

				IClientNetworkable *pClient = cl_entitylist->GetClientEntity( clientIndex );

				if ( pClient )
				{
					C_BasePlayer *pPlayer = dynamic_cast<C_BasePlayer*>(pClient);
					if( pPlayer )
					{
						data.m_nDamageType = pPlayer->GetTeamNumber();
					}
				}

				if ( trace.m_pEnt )
				{
					data.m_hEntity = ClientEntityList().EntIndexToHandle( trace.m_pEnt->entindex() );
				}
				DispatchEffect( m_pszImpactEffect, data );
			}

			// Check for a collision and stop the particle system.
			if ( flags & FTENT_CLIENTSIDEPARTICLES )
			{
				// Stop the emission of particles on collision - removed from the ClientEntityList on removal from the tempent pool.
				ParticleProp()->StopEmission();
				m_bParticleCollision = true;
			}

			if (flags & FTENT_COLLIDEKILL)
			{
				// die on impact
				flags &= ~FTENT_FADEOUT;	
				die = gpGlobals->curtime;			
			}
			else if ( flags & FTENT_ATTACHTOTARGET)
			{
				// If we've hit the world, just stop moving
				if ( trace.DidHitWorld() && !( trace.surface.flags & SURF_SKY ) )
				{
					m_vecTempEntVelocity = vec3_origin;
					m_vecTempEntAcceleration = vec3_origin;

					// Remove movement flags so we don't keep tracing
					flags &= ~(FTENT_COLLIDEALL | FTENT_COLLIDEWORLD);
				}
				else
				{
					// Couldn't attach to this entity. Die.
					flags &= ~FTENT_FADEOUT;
					die = gpGlobals->curtime;
				}
			}
			else
			{
				// Reflect velocity
				if ( damp != 0 )
				{
					proj = ((Vector)m_vecTempEntVelocity).Dot(traceNormal);
					VectorMA( m_vecTempEntVelocity, -proj*2, traceNormal, m_vecTempEntVelocity );
					// Reflect rotation (fake)
					SetLocalAnglesDim( Y_INDEX, -GetLocalAnglesDim( Y_INDEX ) );
				}
				
				if ( damp != 1 )
				{
					VectorScale( m_vecTempEntVelocity, damp, m_vecTempEntVelocity );
					SetLocalAngles( GetLocalAngles() * 0.9 );
				}
			}
		}
	}


	if ( (flags & FTENT_FLICKER) && framenumber == m_nFlickerFrame )
	{
		dlight_t *dl = effects->CL_AllocDlight (LIGHT_INDEX_TE_DYNAMIC);
		VectorCopy (GetLocalOrigin(), dl->origin);
		dl->radius = 60;
		dl->color.r = 255;
		dl->color.g = 120;
		dl->color.b = 0;
		dl->die = gpGlobals->curtime + 0.01;
	}

	if ( flags & FTENT_SMOKETRAIL )
	{
		 Assert( !"FIXME:  Rework smoketrail to be client side\n" );
	}

	// add gravity if we didn't collide in this frame
	if ( traceFraction == 1 )
	{
		if ( flags & FTENT_GRAVITY )
			m_vecTempEntVelocity[2] += gravity;
		else if ( flags & FTENT_SLOWGRAVITY )
			m_vecTempEntVelocity[2] += gravitySlow;
	}

	if ( flags & FTENT_WINDBLOWN )
	{
#ifdef MAPBASE
		Vector vecWind = GetWindspeedAtLocation( GetAbsOrigin() );
#else
		Vector vecWind;
		GetWindspeedAtTime( gpGlobals->curtime, vecWind );
#endif

		for ( int i = 0 ; i < 2 ; i++ )
		{
			if ( m_vecTempEntVelocity[i] < vecWind[i] )
			{
				m_vecTempEntVelocity[i] += ( frametime * TENT_WIND_ACCEL );

				// clamp
				if ( m_vecTempEntVelocity[i] > vecWind[i] )
					m_vecTempEntVelocity[i] = vecWind[i];
			}
			else if (m_vecTempEntVelocity[i] > vecWind[i] )
			{
				m_vecTempEntVelocity[i] -= ( frametime * TENT_WIND_ACCEL );

				// clamp.
				if ( m_vecTempEntVelocity[i] < vecWind[i] )
					m_vecTempEntVelocity[i] = vecWind[i];
			}
		}
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Attach a particle effect to a temp entity.
//-----------------------------------------------------------------------------
CNewParticleEffect* C_LocalTempEntity::AddParticleEffect( const char *pszParticleEffect )
{
	// Do we have a valid particle effect.
	if ( !pszParticleEffect || ( pszParticleEffect[0] == '\0' ) )
		return NULL;

	// Check to see that we don't already have a particle effect.
	if ( ( flags & FTENT_CLIENTSIDEPARTICLES ) != 0 )
		return NULL;

	// Add the entity to the ClientEntityList and create the particle system.
	ClientEntityList().AddNonNetworkableEntity( this );
	CNewParticleEffect* pEffect = ParticleProp()->Create( pszParticleEffect, PATTACH_ABSORIGIN_FOLLOW );

	// Set the particle flag on the temp entity and save the name of the particle effect.
	flags |= FTENT_CLIENTSIDEPARTICLES;
	SetParticleEffect( pszParticleEffect );

	return pEffect;
}

//-----------------------------------------------------------------------------
// Purpose: This helper keeps track of batches of "breakmodels" so that they can all share the lighting origin
//  of the first of the group (because the server sends down 15 chunks at a time, and rebuilding 15 light cache
//  entries for a map with a lot of worldlights is really slow).
//-----------------------------------------------------------------------------
class CBreakableHelper
{
public:
	void	Insert( C_LocalTempEntity *entity, bool isSlave );
	void	Remove( C_LocalTempEntity *entity );

	void	Clear();

	const Vector *GetLightingOrigin( C_LocalTempEntity *entity );

private:

	// A context is the first master until the next one, which starts a new context
	struct BreakableList_t
	{
		unsigned int		context;
		C_LocalTempEntity	*entity;
	};

	CUtlLinkedList< BreakableList_t, unsigned short >	m_Breakables;
	unsigned int			m_nCurrentContext;
};

//-----------------------------------------------------------------------------
// Purpose: Adds brekable to list, starts new context if needed
// Input  : *entity - 
//			isSlave - 
//-----------------------------------------------------------------------------
void CBreakableHelper::Insert( C_LocalTempEntity *entity, bool isSlave )
{
	// A master signifies the start of a new run of broken objects
	if ( !isSlave )
	{
		++m_nCurrentContext;
	}
	
	BreakableList_t entry;
	entry.context = m_nCurrentContext;
	entry.entity = entity;

	m_Breakables.AddToTail( entry );
}

//-----------------------------------------------------------------------------
// Purpose: Removes all instances of entity in the list
// Input  : *entity - 
//-----------------------------------------------------------------------------
void CBreakableHelper::Remove( C_LocalTempEntity *entity )
{
	for ( unsigned short i = m_Breakables.Head(); i != m_Breakables.InvalidIndex() ; )
	{
		unsigned short n = m_Breakables.Next( i );

		if ( m_Breakables[ i ].entity == entity )
		{
			m_Breakables.Remove( i );
		}

		i = n;
	}
}

//-----------------------------------------------------------------------------
// Purpose: For a given breakable, find the "first" or head object and use it's current
//  origin as the lighting origin for the entire group of objects
// Input  : *entity - 
// Output : const Vector
//-----------------------------------------------------------------------------
const Vector *CBreakableHelper::GetLightingOrigin( C_LocalTempEntity *entity )
{
	unsigned int nCurContext = 0;
	C_LocalTempEntity *head = NULL;
	FOR_EACH_LL( m_Breakables, i )
	{
		BreakableList_t& e = m_Breakables[ i ];

		if ( e.context != nCurContext )
		{
			nCurContext = e.context;
			head = e.entity;
		}

		if ( e.entity == entity )
		{
			Assert( head );
			return head ? &head->GetAbsOrigin() : NULL;
		}
	}
	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Wipe breakable helper list
// Input  :  - 
//-----------------------------------------------------------------------------
void CBreakableHelper::Clear()
{
	m_Breakables.RemoveAll();
	m_nCurrentContext = 0;
}

static CBreakableHelper g_BreakableHelper;

//-----------------------------------------------------------------------------
// Purpose: See if it's in the breakable helper list and, if so, remove
// Input  :  - 
//-----------------------------------------------------------------------------
void C_LocalTempEntity::OnRemoveTempEntity()
{
	g_BreakableHelper.Remove( this );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CTempEnts::CTempEnts( void ) :
	m_TempEntsPool( ( MAX_TEMP_ENTITIES / 20 ), CUtlMemoryPool::GROW_SLOW )
{
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CTempEnts::~CTempEnts( void )
{
	m_TempEntsPool.Clear();
	m_TempEnts.RemoveAll();
}

//-----------------------------------------------------------------------------
// Purpose: Create a fizz effect
// Input  : *pent - 
//			modelIndex - 
//			density - 
//-----------------------------------------------------------------------------
void CTempEnts::FizzEffect( C_BaseEntity *pent, int modelIndex, int density, int current )
{
	C_LocalTempEntity		*pTemp;
	const model_t	*model;
	int				i, width, depth, count, frameCount;
	float			maxHeight, speed, xspeed, yspeed;
	Vector			origin;
	Vector			mins, maxs;

	if ( !pent->GetModel() || !modelIndex ) 
		return;

	model = modelinfo->GetModel( modelIndex );
	if ( !model )
		return;

	count = density + 1;
	density = count * 3 + 6;

	modelinfo->GetModelBounds( pent->GetModel(), mins, maxs );

	maxHeight = maxs[2] - mins[2];
	width = maxs[0] - mins[0];
	depth = maxs[1] - mins[1];
	speed = current;

	SinCos( pent->GetLocalAngles()[1]*M_PI/180, &yspeed, &xspeed );
	xspeed *= speed;
	yspeed *= speed;
	frameCount = modelinfo->GetModelFrameCount( model );

	for (i=0 ; i<count ; i++)
	{
		origin[0] = mins[0] + random->RandomInt(0,width-1);
		origin[1] = mins[1] + random->RandomInt(0,depth-1);
		origin[2] = mins[2];
		pTemp = TempEntAlloc( origin, model );
		if (!pTemp)
			return;

		pTemp->flags |= FTENT_SINEWAVE;

		pTemp->x = origin[0];
		pTemp->y = origin[1];

		float zspeed = random->RandomInt(80,140);
		pTemp->SetVelocity( Vector(xspeed, yspeed, zspeed) );
		pTemp->die = gpGlobals->curtime + (maxHeight / zspeed) - 0.1;
		pTemp->m_flFrame = random->RandomInt(0,frameCount-1);
		// Set sprite scale
		pTemp->m_flSpriteScale = 1.0 / random->RandomFloat(2,5);
		pTemp->SetRenderMode( kRenderTransAlpha );
		pTemp->SetRenderColorA( 255 );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Create bubbles
// Input  : *mins - 
//			*maxs - 
//			height - 
//			modelIndex - 
//			count - 
//			speed - 
//-----------------------------------------------------------------------------
void CTempEnts::Bubbles( const Vector &mins, const Vector &maxs, float height, int modelIndex, int count, float speed )
{
	C_LocalTempEntity			*pTemp;
	const model_t		*model;
	int					i, frameCount;
	float				sine, cosine;
	Vector				origin;

	if ( !modelIndex ) 
		return;

	model = modelinfo->GetModel( modelIndex );
	if ( !model )
		return;

	frameCount = modelinfo->GetModelFrameCount( model );

	for (i=0 ; i<count ; i++)
	{
		origin[0] = random->RandomInt( mins[0], maxs[0] );
		origin[1] = random->RandomInt( mins[1], maxs[1] );
		origin[2] = random->RandomInt( mins[2], maxs[2] );
		pTemp = TempEntAlloc( origin, model );
		if (!pTemp)
			return;

		pTemp->flags |= FTENT_SINEWAVE;

		pTemp->x = origin[0];
		pTemp->y = origin[1];
		SinCos( random->RandomInt( -3, 3 ), &sine, &cosine );
		
		float zspeed = random->RandomInt(80,140);
		pTemp->SetVelocity( Vector(speed * cosine, speed * sine, zspeed) );
		pTemp->die = gpGlobals->curtime + ((height - (origin[2] - mins[2])) / zspeed) - 0.1;
		pTemp->m_flFrame = random->RandomInt( 0, frameCount-1 );
		
		// Set sprite scale
		pTemp->m_flSpriteScale = 1.0 / random->RandomFloat(4,16);
		pTemp->SetRenderMode( kRenderTransAlpha );
		
		pTemp->SetRenderColor( 255, 255, 255, 192 );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Create bubble trail
// Input  : *start - 
//			*end - 
//			height - 
//			modelIndex - 
//			count - 
//			speed - 
//-----------------------------------------------------------------------------
void CTempEnts::BubbleTrail( const Vector &start, const Vector &end, float flWaterZ, int modelIndex, int count, float speed )
{
	C_LocalTempEntity			*pTemp;
	const model_t		*model;
	int					i, frameCount;
	float				dist, angle;
	Vector				origin;

	if ( !modelIndex ) 
		return;

	model = modelinfo->GetModel( modelIndex );
	if ( !model )
		return;

	frameCount = modelinfo->GetModelFrameCount( model );

	for (i=0 ; i<count ; i++)
	{
		dist = random->RandomFloat( 0, 1.0 );
		VectorLerp( start, end, dist, origin );
		pTemp = TempEntAlloc( origin, model );
		if (!pTemp)
			return;

		pTemp->flags |= FTENT_SINEWAVE;

		pTemp->x = origin[0];
		pTemp->y = origin[1];
		angle = random->RandomInt( -3, 3 );

		float zspeed = random->RandomInt(80,140);
		pTemp->SetVelocity( Vector(speed * cos(angle), speed * sin(angle), zspeed) );
		pTemp->die = gpGlobals->curtime + ((flWaterZ - origin[2]) / zspeed) - 0.1;
		pTemp->m_flFrame = random->RandomInt(0,frameCount-1);
		// Set sprite scale
		pTemp->m_flSpriteScale = 1.0 / random->RandomFloat(4,8);
		pTemp->SetRenderMode( kRenderTransAlpha );
		
		pTemp->SetRenderColor( 255, 255, 255, 192 );
	}
}

#define SHARD_VOLUME 12.0	// on shard ever n^3 units

//-----------------------------------------------------------------------------
// Purpose: Only used by BreakModel temp ents for now.  Allows them to share a single
//  lighting origin amongst a group of objects.  If the master object goes away, the next object
//  in the group becomes the new lighting origin, etc.
// Input  : *entity - 
//			flags - 
// Output : int
//-----------------------------------------------------------------------------
int BreakModelDrawHelper( C_LocalTempEntity *entity, int flags )
{
	ModelRenderInfo_t sInfo;
	sInfo.flags = flags;
	sInfo.pRenderable = entity;
	sInfo.instance = MODEL_INSTANCE_INVALID;
	sInfo.entity_index = entity->index;
	sInfo.pModel = entity->GetModel();
	sInfo.origin = entity->GetRenderOrigin();
	sInfo.angles = entity->GetRenderAngles();
	sInfo.skin = entity->m_nSkin;
	sInfo.body = entity->m_nBody;
	sInfo.hitboxset = entity->m_nHitboxSet;

	// This is the main change, look up a lighting origin from the helper singleton
	const Vector *pLightingOrigin = g_BreakableHelper.GetLightingOrigin( entity );
	if ( pLightingOrigin )
	{
		sInfo.pLightingOrigin = pLightingOrigin;
	}

	int drawn = modelrender->DrawModelEx( sInfo );
	return drawn;
}

//-----------------------------------------------------------------------------
// Purpose: Create model shattering shards
// Input  : *pos - 
//			*size - 
//			*dir - 
//			random - 
//			life - 
//			count - 
//			modelIndex - 
//			flags - 
//-----------------------------------------------------------------------------
void CTempEnts::BreakModel( const Vector &pos, const QAngle &angles, const Vector &size, const Vector &dir, 
						   float randRange, float life, int count, int modelIndex, char flags)
{
	int					i, frameCount;
	C_LocalTempEntity			*pTemp;
	const model_t		*pModel;

	if (!modelIndex) 
		return;

	pModel = modelinfo->GetModel( modelIndex );
	if ( !pModel )
		return;

	// See g_BreakableHelper above for notes...
	bool isSlave = ( flags & BREAK_SLAVE ) ? true : false;

	frameCount = modelinfo->GetModelFrameCount( pModel );

	if (count == 0)
	{
		// assume surface (not volume)
		count = (size[0] * size[1] + size[1] * size[2] + size[2] * size[0])/(3 * SHARD_VOLUME * SHARD_VOLUME);
	}

	if ( count > func_break_max_pieces.GetInt() )
	{
		count = func_break_max_pieces.GetInt();
	}

	matrix3x4_t transform;
	AngleMatrix( angles, pos, transform );
	for ( i = 0; i < count; i++ ) 
	{
		Vector vecLocalSpot, vecSpot;

		// fill up the box with stuff
		vecLocalSpot[0] = random->RandomFloat(-0.5,0.5) * size[0];
		vecLocalSpot[1] = random->RandomFloat(-0.5,0.5) * size[1];
		vecLocalSpot[2] = random->RandomFloat(-0.5,0.5) * size[2];
		VectorTransform( vecLocalSpot, transform, vecSpot );

		pTemp = TempEntAlloc(vecSpot, pModel);
		
		if (!pTemp)
			return;

		// keep track of break_type, so we know how to play sound on collision
		pTemp->hitSound = flags;
		
		if ( modelinfo->GetModelType( pModel ) == mod_sprite )
		{
			pTemp->m_flFrame = random->RandomInt(0,frameCount-1);
		}
		else if ( modelinfo->GetModelType( pModel ) == mod_studio )
		{
			pTemp->m_nBody = random->RandomInt(0,frameCount-1);
		}

		pTemp->flags |= FTENT_COLLIDEWORLD | FTENT_FADEOUT | FTENT_SLOWGRAVITY;

		if ( random->RandomInt(0,255) < 200 ) 
		{
			pTemp->flags |= FTENT_ROTATE;
			pTemp->m_vecTempEntAngVelocity[0] = random->RandomFloat(-256,255);
			pTemp->m_vecTempEntAngVelocity[1] = random->RandomFloat(-256,255);
			pTemp->m_vecTempEntAngVelocity[2] = random->RandomFloat(-256,255);
		}

		if ( (random->RandomInt(0,255) < 100 ) && (flags & BREAK_SMOKE) )
		{
			pTemp->flags |= FTENT_SMOKETRAIL;
		}

		if ((flags & BREAK_GLASS) || (flags & BREAK_TRANS))
		{
			pTemp->SetRenderMode( kRenderTransTexture );
			pTemp->SetRenderColorA( 128 );
			pTemp->tempent_renderamt = 128;
			pTemp->bounceFactor = 0.3f;
		}
		else
		{
			pTemp->SetRenderMode( kRenderNormal );
			pTemp->tempent_renderamt = 255;		// Set this for fadeout
		}

		pTemp->SetVelocity( Vector( dir[0] + random->RandomFloat(-randRange,randRange),
							dir[1] + random->RandomFloat(-randRange,randRange),
							dir[2] + random->RandomFloat(   0,randRange) ) );

		pTemp->die = gpGlobals->curtime + life + random->RandomFloat(0,1);	// Add an extra 0-1 secs of life

		// We use a special rendering function because these objects will want to share their lighting
		//  origin with the first/master object.  Prevents a huge spike in Light Cache building in maps
		//  with many worldlights.
		pTemp->SetDrawHelper( BreakModelDrawHelper );
		g_BreakableHelper.Insert( pTemp, isSlave );
	}
}

void CTempEnts::PhysicsProp( int modelindex, int skin, const Vector& pos, const QAngle &angles, const Vector& vel, int flags, int effects )
{
	C_PhysPropClientside *pEntity = C_PhysPropClientside::CreateNew();
	
	if ( !pEntity )
		return;

	const model_t *model = modelinfo->GetModel( modelindex );

	if ( !model )
	{
		DevMsg("CTempEnts::PhysicsProp: model index %i not found\n", modelindex );
		return;
	}

	pEntity->SetModelName( modelinfo->GetModelName(model) );
	pEntity->m_nSkin = skin;
	pEntity->SetAbsOrigin( pos );
	pEntity->SetAbsAngles( angles );
	pEntity->SetPhysicsMode( PHYSICS_MULTIPLAYER_CLIENTSIDE );
	pEntity->SetEffects( effects );

	if ( !pEntity->Initialize() )
	{
		pEntity->Release();
		return;
	}

	IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();

	if( pPhysicsObject )
	{
		pPhysicsObject->AddVelocity( &vel, NULL );
	}
	else
	{
		// failed to create a physics object
		pEntity->Release();
		return;
	}

	if ( flags & 1 )
	{
		pEntity->SetHealth( 0 );
		pEntity->Break();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Create a clientside projectile
// Input  : vecOrigin - 
//			vecVelocity - 
//			modelindex - 
//			lifetime - 
//			*pOwner - 
//-----------------------------------------------------------------------------
C_LocalTempEntity *CTempEnts::ClientProjectile( const Vector& vecOrigin, const Vector& vecVelocity, const Vector& vecAcceleration, int modelIndex, int lifetime, CBaseEntity *pOwner, const char *pszImpactEffect, const char *pszParticleEffect )
{
	C_LocalTempEntity	*pTemp;
	const model_t		*model;

	if ( !modelIndex ) 
		return NULL;

	model = modelinfo->GetModel( modelIndex );
	if ( !model )
	{
		Warning("ClientProjectile: No model %d!\n", modelIndex);
		return NULL;
	}

	pTemp = TempEntAlloc( vecOrigin, model );
	if (!pTemp)
		return NULL;

	pTemp->SetVelocity( vecVelocity );
	pTemp->SetAcceleration( vecAcceleration );
	QAngle angles;
	VectorAngles( vecVelocity, angles );
	pTemp->SetAbsAngles( angles );
	pTemp->SetAbsOrigin( vecOrigin );
	pTemp->die = gpGlobals->curtime + lifetime;
	pTemp->flags = FTENT_COLLIDEALL | FTENT_ATTACHTOTARGET | FTENT_ALIGNTOMOTION;
	pTemp->clientIndex = ( pOwner != NULL ) ? pOwner->entindex() : 0; 
	pTemp->SetOwnerEntity( pOwner );
	pTemp->SetImpactEffect( pszImpactEffect );
	if ( pszParticleEffect )
	{
		// Add the entity to the ClientEntityList and create the particle system.
		ClientEntityList().AddNonNetworkableEntity( pTemp );
		pTemp->ParticleProp()->Create( pszParticleEffect, PATTACH_ABSORIGIN_FOLLOW );

		// Set the particle flag on the temp entity and save the name of the particle effect.
		pTemp->flags |= FTENT_CLIENTSIDEPARTICLES;
	 	pTemp->SetParticleEffect( pszParticleEffect );
	}
	return pTemp;
}

//-----------------------------------------------------------------------------
// Purpose: Create sprite TE
// Input  : *pos - 
//			*dir - 
//			scale - 
//			modelIndex - 
//			rendermode - 
//			renderfx - 
//			a - 
//			life - 
//			flags - 
// Output : C_LocalTempEntity
//-----------------------------------------------------------------------------
C_LocalTempEntity *CTempEnts::TempSprite( const Vector &pos, const Vector &dir, float scale, int modelIndex, int rendermode, int renderfx, float a, float life, int flags, const Vector &normal )
{
	C_LocalTempEntity			*pTemp;
	const model_t		*model;
	int					frameCount;

	if ( !modelIndex ) 
		return NULL;

	model = modelinfo->GetModel( modelIndex );
	if ( !model )
	{
		Warning("No model %d!\n", modelIndex);
		return NULL;
	}

	frameCount = modelinfo->GetModelFrameCount( model );

	pTemp = TempEntAlloc( pos, model );
	if (!pTemp)
		return NULL;

	pTemp->m_flFrameMax = frameCount - 1;
	pTemp->m_flFrameRate = 10;
	pTemp->SetRenderMode( (RenderMode_t)rendermode );
	pTemp->m_nRenderFX = renderfx;
	pTemp->m_flSpriteScale = scale;
	pTemp->tempent_renderamt = a * 255;
	pTemp->m_vecNormal = normal;
	pTemp->SetRenderColor( 255, 255, 255, a * 255 );

	pTemp->flags |= flags;

	pTemp->SetVelocity( dir );
	pTemp->SetLocalOrigin( pos );
	if ( life )
		pTemp->die = gpGlobals->curtime + life;
	else
		pTemp->die = gpGlobals->curtime + (frameCount * 0.1) + 1;

	pTemp->m_flFrame = 0;
	return pTemp;
}

//-----------------------------------------------------------------------------
// Purpose: Spray sprite
// Input  : *pos - 
//			*dir - 
//			modelIndex - 
//			count - 
//			speed - 
//			iRand - 
//-----------------------------------------------------------------------------
void CTempEnts::Sprite_Spray( const Vector &pos, const Vector &dir, int modelIndex, int count, int speed, int iRand )
{
	C_LocalTempEntity			*pTemp;
	const model_t		*pModel;
	float				noise;
	float				znoise;
	int					frameCount;
	int					i;

	noise = (float)iRand / 100;

	// more vertical displacement
	znoise = noise * 1.5;
	
	if ( znoise > 1 )
	{
		znoise = 1;
	}

	pModel = modelinfo->GetModel( modelIndex );
	
	if ( !pModel )
	{
		Warning("No model %d!\n", modelIndex);
		return;
	}

	frameCount = modelinfo->GetModelFrameCount( pModel ) - 1;

	for ( i = 0; i < count; i++ )
	{
		pTemp = TempEntAlloc( pos, pModel );
		if (!pTemp)
			return;

		pTemp->SetRenderMode( kRenderTransAlpha );
		pTemp->SetRenderColor( 255, 255, 255, 255 );
		pTemp->tempent_renderamt = 255;
		pTemp->m_nRenderFX = kRenderFxNoDissipation;
		//pTemp->scale = random->RandomFloat( 0.1, 0.25 );
		pTemp->m_flSpriteScale = 0.5;
		pTemp->flags |= FTENT_FADEOUT | FTENT_SLOWGRAVITY;
		pTemp->fadeSpeed = 2.0;

		// make the spittle fly the direction indicated, but mix in some noise.
		Vector velocity;
		velocity.x = dir[ 0 ] + random->RandomFloat ( -noise, noise );
		velocity.y = dir[ 1 ] + random->RandomFloat ( -noise, noise );
		velocity.z = dir[ 2 ] + random->RandomFloat ( 0, znoise );
		velocity *= random->RandomFloat( (speed * 0.8), (speed * 1.2) );
		pTemp->SetVelocity( velocity );

		pTemp->SetLocalOrigin( pos );

		pTemp->die = gpGlobals->curtime + 0.35;

		pTemp->m_flFrame = random->RandomInt( 0, frameCount );
	}
}

void CTempEnts::Sprite_Trail( const Vector &vecStart, const Vector &vecEnd, int modelIndex, int nCount, float flLife, float flSize, float flAmplitude, int nRenderamt, float flSpeed )
{
	C_LocalTempEntity	*pTemp;
	const model_t		*pModel;
	int					flFrameCount;

	pModel = modelinfo->GetModel( modelIndex );
	
	if ( !pModel )
	{
		Warning("No model %d!\n", modelIndex);
		return;
	}

	flFrameCount = modelinfo->GetModelFrameCount( pModel );

	Vector vecDelta;
	VectorSubtract( vecEnd, vecStart, vecDelta );

	Vector vecDir;
	VectorCopy( vecDelta, vecDir );
	VectorNormalize( vecDir );

	flAmplitude /= 256.0;

	for ( int i = 0 ; i < nCount; i++ )
	{
		Vector vecPos;

		// Be careful of divide by 0 when using 'count' here...
		if ( i == 0 )
		{
			VectorMA( vecStart, 0, vecDelta, vecPos );
		}
		else
		{
			VectorMA( vecStart, i / (nCount - 1.0), vecDelta, vecPos );
		}

		pTemp = TempEntAlloc( vecPos, pModel );
		if (!pTemp)
			return;

		pTemp->flags |= FTENT_COLLIDEWORLD | FTENT_SPRCYCLE | FTENT_FADEOUT | FTENT_SLOWGRAVITY;

		Vector vecVel = vecDir * flSpeed;
		vecVel.x += random->RandomInt( -127,128 ) * flAmplitude;
		vecVel.y += random->RandomInt( -127,128 ) * flAmplitude;
		vecVel.z += random->RandomInt( -127,128 ) * flAmplitude;
		pTemp->SetVelocity( vecVel );
		pTemp->SetLocalOrigin( vecPos );

		pTemp->m_flSpriteScale		= flSize;
		pTemp->SetRenderMode( kRenderGlow );
		pTemp->m_nRenderFX			= kRenderFxNoDissipation;
		pTemp->tempent_renderamt	= nRenderamt;
		pTemp->SetRenderColor( 255, 255, 255 );

		pTemp->m_flFrame	= random->RandomInt( 0, flFrameCount - 1 );
		pTemp->m_flFrameMax	= flFrameCount - 1;
		pTemp->die			= gpGlobals->curtime + flLife + random->RandomFloat( 0, 4 );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Attaches entity to player
// Input  : client - 
//			modelIndex - 
//			zoffset - 
//			life - 
//-----------------------------------------------------------------------------
void CTempEnts::AttachTentToPlayer( int client, int modelIndex, float zoffset, float life )
{
	C_LocalTempEntity			*pTemp;
	const model_t		*pModel;
	Vector				position;
	int					frameCount;

	if ( client <= 0 || client > gpGlobals->maxClients )
	{
		Warning("Bad client in AttachTentToPlayer()!\n");
		return;
	}

	IClientEntity *clientClass = cl_entitylist->GetClientEntity( client );
	if ( !clientClass )
	{
		Warning("Couldn't get IClientEntity for %i\n", client );
		return;
	}

	pModel = modelinfo->GetModel( modelIndex );
	
	if ( !pModel )
	{
		Warning("No model %d!\n", modelIndex);
		return;
	}

	VectorCopy( clientClass->GetAbsOrigin(), position );
	position[ 2 ] += zoffset;

	pTemp = TempEntAllocHigh( position, pModel );
	if (!pTemp)
	{
		Warning("No temp ent.\n");
		return;
	}

	pTemp->SetRenderMode( kRenderNormal );
	pTemp->SetRenderColorA( 255 );
	pTemp->tempent_renderamt = 255;
	pTemp->m_nRenderFX = kRenderFxNoDissipation;
	
	pTemp->clientIndex = client;
	pTemp->tentOffset[ 0 ] = 0;
	pTemp->tentOffset[ 1 ] = 0;
	pTemp->tentOffset[ 2 ] = zoffset;
	pTemp->die = gpGlobals->curtime + life;
	pTemp->flags |= FTENT_PLYRATTACHMENT | FTENT_PERSIST;

	// is the model a sprite?
	if ( modelinfo->GetModelType( pTemp->GetModel() ) == mod_sprite )
	{
		frameCount = modelinfo->GetModelFrameCount( pModel );
		pTemp->m_flFrameMax = frameCount - 1;
		pTemp->flags |= FTENT_SPRANIMATE | FTENT_SPRANIMATELOOP;
		pTemp->m_flFrameRate = 10;
	}
	else
	{
		// no animation support for attached clientside studio models.
		pTemp->m_flFrameMax = 0;
	}

	pTemp->m_flFrame = 0;
}

//-----------------------------------------------------------------------------
// Purpose: Detach entity from player
//-----------------------------------------------------------------------------
void CTempEnts::KillAttachedTents( int client )
{
	if ( client <= 0 || client > gpGlobals->maxClients )
	{
		Warning("Bad client in KillAttachedTents()!\n");
		return;
	}

	FOR_EACH_LL( m_TempEnts, i )
	{
		C_LocalTempEntity *pTemp = m_TempEnts[ i ];

		if ( pTemp->flags & FTENT_PLYRATTACHMENT )
		{
			// this TENT is player attached.
			// if it is attached to this client, set it to die instantly.
			if ( pTemp->clientIndex == client )
			{
				pTemp->die = gpGlobals->curtime;// good enough, it will die on next tent update. 
			}
		}
	}
}



//-----------------------------------------------------------------------------
// Purpose: Create ricochet sprite
// Input  : *pos - 
//			*pmodel - 
//			duration - 
//			scale - 
//-----------------------------------------------------------------------------
void CTempEnts::RicochetSprite( const Vector &pos, model_t *pmodel, float duration, float scale )
{
	C_LocalTempEntity	*pTemp;

	pTemp = TempEntAlloc( pos, pmodel );
	if (!pTemp)
		return;

	pTemp->SetRenderMode( kRenderGlow );
	pTemp->m_nRenderFX = kRenderFxNoDissipation;
	pTemp->SetRenderColorA( 200 );
	pTemp->tempent_renderamt = 200;
	pTemp->m_flSpriteScale = scale;
	pTemp->flags = FTENT_FADEOUT;

	pTemp->SetVelocity( vec3_origin );

	pTemp->SetLocalOrigin( pos );
	
	pTemp->fadeSpeed = 8;
	pTemp->die = gpGlobals->curtime;

	pTemp->m_flFrame = 0;
	pTemp->SetLocalAnglesDim( Z_INDEX, 45 * random->RandomInt( 0, 7 ) );
}

//-----------------------------------------------------------------------------
// Purpose: Create blood sprite
// Input  : *org - 
//			colorindex - 
//			modelIndex - 
//			modelIndex2 - 
//			size - 
//-----------------------------------------------------------------------------
void CTempEnts::BloodSprite( const Vector &org, int r, int g, int b, int a, int modelIndex, int modelIndex2, float size )
{
	const model_t			*model;

	//Validate the model first
	if ( modelIndex && (model = modelinfo->GetModel( modelIndex ) ) != NULL )
	{
		C_LocalTempEntity		*pTemp;
		int						frameCount = modelinfo->GetModelFrameCount( model );
		color32					impactcolor = { r, g, b, a };

		//Large, single blood sprite is a high-priority tent
		if ( ( pTemp = TempEntAllocHigh( org, model ) ) != NULL )
		{
			pTemp->SetRenderMode( kRenderTransTexture );
			pTemp->m_nRenderFX		= kRenderFxClampMinScale;
			pTemp->m_flSpriteScale	= random->RandomFloat( size / 25, size / 35);
			pTemp->flags			= FTENT_SPRANIMATE;
 
			pTemp->m_clrRender		= impactcolor;
			pTemp->tempent_renderamt= pTemp->m_clrRender->a;

			pTemp->SetVelocity( vec3_origin );

			pTemp->m_flFrameRate	= frameCount * 4; // Finish in 0.250 seconds
			pTemp->die				= gpGlobals->curtime + (frameCount / pTemp->m_flFrameRate); // Play the whole thing Once

			pTemp->m_flFrame		= 0;
			pTemp->m_flFrameMax		= frameCount - 1;
			pTemp->bounceFactor		= 0;
			pTemp->SetLocalAnglesDim( Z_INDEX, random->RandomInt( 0, 360 ) );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Create default sprite TE
// Input  : *pos - 
//			spriteIndex - 
//			framerate - 
// Output : C_LocalTempEntity
//-----------------------------------------------------------------------------
C_LocalTempEntity *CTempEnts::DefaultSprite( const Vector &pos, int spriteIndex, float framerate )
{
	C_LocalTempEntity		*pTemp;
	int				frameCount;
	const model_t	*pSprite;

	// don't spawn while paused
	if ( gpGlobals->frametime == 0.0 )
		return NULL;

	pSprite = modelinfo->GetModel( spriteIndex );
	if ( !spriteIndex || !pSprite || modelinfo->GetModelType( pSprite ) != mod_sprite )
	{
		DevWarning( 1,"No Sprite %d!\n", spriteIndex);
		return NULL;
	}

	frameCount = modelinfo->GetModelFrameCount( pSprite );

	pTemp = TempEntAlloc( pos, pSprite );
	if (!pTemp)
		return NULL;

	pTemp->m_flFrameMax = frameCount - 1;
	pTemp->m_flSpriteScale = 1.0;
	pTemp->flags |= FTENT_SPRANIMATE;
	if ( framerate == 0 )
		framerate = 10;

	pTemp->m_flFrameRate = framerate;
	pTemp->die = gpGlobals->curtime + (float)frameCount / framerate;
	pTemp->m_flFrame = 0;
	pTemp->SetLocalOrigin( pos );

	return pTemp;
}

//-----------------------------------------------------------------------------
// Purpose: Create sprite smoke
// Input  : *pTemp - 
//			scale - 
//-----------------------------------------------------------------------------
void CTempEnts::Sprite_Smoke( C_LocalTempEntity *pTemp, float scale )
{
	if ( !pTemp )
		return;

	pTemp->SetRenderMode( kRenderTransAlpha );
	pTemp->m_nRenderFX = kRenderFxNone;
	pTemp->SetVelocity( Vector( 0, 0, 30 ) );
	int iColor = random->RandomInt(20,35);
	pTemp->SetRenderColor( iColor,
		iColor,
		iColor,
		255 );
	pTemp->SetLocalOriginDim( Z_INDEX, pTemp->GetLocalOriginDim( Z_INDEX ) + 20 );
	pTemp->m_flSpriteScale = scale;
	pTemp->flags = FTENT_WINDBLOWN;

}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pos1 - 
//			angles - 
//			type - 
//-----------------------------------------------------------------------------
void CTempEnts::EjectBrass( const Vector &pos1, const QAngle &angles, const QAngle &gunAngles, int type )
{
	if ( cl_ejectbrass.GetBool() == false )
		return;

	const model_t *pModel = m_pShells[type];
	
	if ( pModel == NULL )
		return;

	C_LocalTempEntity	*pTemp = TempEntAlloc( pos1, pModel );

	if ( pTemp == NULL )
		return;

	//Keep track of shell type
	if ( type == 2 )
	{
		pTemp->hitSound = BOUNCE_SHOTSHELL;
	}
	else
	{
		pTemp->hitSound = BOUNCE_SHELL;
	}

	pTemp->m_nBody	= 0;

	pTemp->flags |= ( FTENT_COLLIDEWORLD | FTENT_FADEOUT | FTENT_GRAVITY | FTENT_ROTATE );

	pTemp->m_vecTempEntAngVelocity[0] = random->RandomFloat(-1024,1024);
	pTemp->m_vecTempEntAngVelocity[1] = random->RandomFloat(-1024,1024);
	pTemp->m_vecTempEntAngVelocity[2] = random->RandomFloat(-1024,1024);

	//Face forward
	pTemp->SetAbsAngles( gunAngles );

	pTemp->SetRenderMode( kRenderNormal );
	pTemp->tempent_renderamt = 255;		// Set this for fadeout

	Vector	dir;

	AngleVectors( angles, &dir );

	dir *= random->RandomFloat( 150.0f, 200.0f );

	pTemp->SetVelocity( Vector(dir[0] + random->RandomFloat(-64,64),
						dir[1] + random->RandomFloat(-64,64),
						dir[2] + random->RandomFloat(  0,64) ) );

	pTemp->die = gpGlobals->curtime + 1.0f + random->RandomFloat( 0.0f, 1.0f );	// Add an extra 0-1 secs of life	
}

//-----------------------------------------------------------------------------
// Purpose: Create some simple physically simulated models
//-----------------------------------------------------------------------------
C_LocalTempEntity * CTempEnts::SpawnTempModel( const model_t *pModel, const Vector &vecOrigin, const QAngle &vecAngles, const Vector &vecVelocity, float flLifeTime, int iFlags )
{
	Assert( pModel );

	// Alloc a new tempent
	C_LocalTempEntity *pTemp = TempEntAlloc( vecOrigin, pModel );
	if ( !pTemp )
		return NULL;

	pTemp->SetAbsAngles( vecAngles );
	pTemp->m_nBody	= 0;
	pTemp->flags |= iFlags;
	pTemp->m_vecTempEntAngVelocity[0] = random->RandomFloat(-255,255);
	pTemp->m_vecTempEntAngVelocity[1] = random->RandomFloat(-255,255);
	pTemp->m_vecTempEntAngVelocity[2] = random->RandomFloat(-255,255);
	pTemp->SetRenderMode( kRenderNormal );
	pTemp->tempent_renderamt = 255;
	pTemp->SetVelocity( vecVelocity );
	pTemp->die = gpGlobals->curtime + flLifeTime;

	return pTemp;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : type - 
//			entityIndex - 
//			attachmentIndex - 
//			firstPerson - 
//-----------------------------------------------------------------------------
void CTempEnts::MuzzleFlash( int type, ClientEntityHandle_t hEntity, int attachmentIndex, bool firstPerson )
{
	switch( type )
	{
	case MUZZLEFLASH_COMBINE:
		if ( firstPerson )
		{
			MuzzleFlash_Combine_Player( hEntity, attachmentIndex );
		}
		else
		{
			MuzzleFlash_Combine_NPC( hEntity, attachmentIndex );
		}
		break;

	case MUZZLEFLASH_SMG1:
		if ( firstPerson )
		{
			MuzzleFlash_SMG1_Player( hEntity, attachmentIndex );
		}
		else
		{
			MuzzleFlash_SMG1_NPC( hEntity, attachmentIndex );
		}
		break;

	case MUZZLEFLASH_PISTOL:
		if ( firstPerson )
		{
			MuzzleFlash_Pistol_Player( hEntity, attachmentIndex );
		}
		else
		{
			MuzzleFlash_Pistol_NPC( hEntity, attachmentIndex );
		}
		break;
	case MUZZLEFLASH_SHOTGUN:
		if ( firstPerson )
		{
			MuzzleFlash_Shotgun_Player( hEntity, attachmentIndex );
		}
		else
		{
			MuzzleFlash_Shotgun_NPC( hEntity, attachmentIndex );
		}
		break;
	case MUZZLEFLASH_357:
		if ( firstPerson )
		{
			MuzzleFlash_357_Player( hEntity, attachmentIndex );
		}
		break;
	case MUZZLEFLASH_RPG:
		if ( firstPerson )
		{
			// MuzzleFlash_RPG_Player( hEntity, attachmentIndex );
		}
		else
		{
			MuzzleFlash_RPG_NPC( hEntity, attachmentIndex );
		}
		break;
		break;
	default:
		{
			//NOTENOTE: This means you specified an invalid muzzleflash type, check your spelling?
			Assert( 0 );
		}
		break;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Play muzzle flash
// Input  : *pos1 - 
//			type - 
//-----------------------------------------------------------------------------
void CTempEnts::MuzzleFlash( const Vector& pos1, const QAngle& angles, int type, ClientEntityHandle_t hEntity, bool firstPerson )
{
#ifdef CSTRIKE_DLL

	return;

#else

	//NOTENOTE: This function is becoming obsolete as the muzzles are moved over to being local to attachments

	switch ( type )
	{
	//
	// Shotgun
	//
	case MUZZLEFLASH_SHOTGUN:
		if ( firstPerson )
		{
			MuzzleFlash_Shotgun_Player( hEntity, 1 );
		}
		else
		{
			MuzzleFlash_Shotgun_NPC( hEntity, 1 );
		}
		break;

	// UNDONE: These need their own effects/sprites.  For now use the pistol
	// SMG1
#if defined ( HL2MP )		//  HACK for hl2mp, make the default muzzleflash the smg muzzleflash for weapons like the RPG that are using 'type 0'
	default:
#endif // HL2MP
	case MUZZLEFLASH_SMG1:
		if ( firstPerson )
		{
			MuzzleFlash_SMG1_Player( hEntity, 1 );
		}
		else
		{
			MuzzleFlash_SMG1_NPC( hEntity, 1 );
		}
		break;

	// SMG2
	case MUZZLEFLASH_SMG2:
	case MUZZLEFLASH_PISTOL:
		if ( firstPerson )
		{
			MuzzleFlash_Pistol_Player( hEntity, 1 );
		}
		else
		{
			MuzzleFlash_Pistol_NPC( hEntity, 1 );
		}
		break;

	case MUZZLEFLASH_COMBINE:
		if ( firstPerson )
		{
			//FIXME: These should go away
			MuzzleFlash_Combine_Player( hEntity, 1 );
		}
		else
		{
			//FIXME: These should go away
			MuzzleFlash_Combine_NPC( hEntity, 1 );
		}
		break;
	
#if !defined ( HL2MP )	//  HACK for hl2mp, make the default muzzleflash the smg muzzleflash for weapons like the RPG that are using 'type 0'
	default:
		// There's no supported muzzle flash for the type specified!
		Assert(0);
		break;
#endif // HL2MP
	}

#endif

}

//-----------------------------------------------------------------------------
// Purpose: Create explosion sprite
// Input  : *pTemp - 
//			scale - 
//			flags - 
//-----------------------------------------------------------------------------
void CTempEnts::Sprite_Explode( C_LocalTempEntity *pTemp, float scale, int flags )
{
	if ( !pTemp )
		return;

	if ( flags & TE_EXPLFLAG_NOADDITIVE )
	{
		// solid sprite
		pTemp->SetRenderMode( kRenderNormal );
		pTemp->SetRenderColorA( 255 ); 
	}
	else if( flags & TE_EXPLFLAG_DRAWALPHA )
	{
		// alpha sprite
		pTemp->SetRenderMode( kRenderTransAlpha ); 
		pTemp->SetRenderColorA( 180 );
	}
	else
	{
		// additive sprite
		pTemp->SetRenderMode( kRenderTransAdd );
		pTemp->SetRenderColorA( 180 );
	}

	if ( flags & TE_EXPLFLAG_ROTATE )
	{
		pTemp->SetLocalAnglesDim( Z_INDEX, random->RandomInt( 0, 360 ) );
	}

	pTemp->m_nRenderFX = kRenderFxNone;
	pTemp->SetVelocity( Vector( 0, 0, 8 ) );
	pTemp->SetRenderColor( 255, 255, 255 );
	pTemp->SetLocalOriginDim( Z_INDEX, pTemp->GetLocalOriginDim( Z_INDEX ) + 10 );
	pTemp->m_flSpriteScale = scale;
}

enum
{
	SHELL_NONE = 0,
	SHELL_SMALL,
	SHELL_BIG,
	SHELL_SHOTGUN,
};

//-----------------------------------------------------------------------------
// Purpose: Clear existing temp entities
//-----------------------------------------------------------------------------
void CTempEnts::Clear( void )
{
	FOR_EACH_LL( m_TempEnts, i )
	{
		C_LocalTempEntity *p = m_TempEnts[ i ];

		m_TempEntsPool.Free( p );
	}

	m_TempEnts.RemoveAll();
	g_BreakableHelper.Clear();
}

C_LocalTempEntity *CTempEnts::FindTempEntByID( int nID, int nSubID )
{
	// HACK HACK: We're using skin and hitsounds as a hacky way to store an ID and sub-ID for later identification
	FOR_EACH_LL( m_TempEnts, i )
	{
		C_LocalTempEntity *p = m_TempEnts[ i ];
		if ( p && p->m_nSkin == nID && p->hitSound == nSubID )
		{
			return p;
		}
	}

	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Allocate temp entity ( normal/low priority )
// Input  : *org - 
//			*model - 
// Output : C_LocalTempEntity
//-----------------------------------------------------------------------------
C_LocalTempEntity *CTempEnts::TempEntAlloc( const Vector& org, const model_t *model )
{
	C_LocalTempEntity		*pTemp;

	if ( !model )
	{
		DevWarning( 1, "Can't create temporary entity with NULL model!\n" );
		return NULL;
	}

	pTemp = TempEntAlloc();

	if ( !pTemp )
	{
		DevWarning( 1, "Overflow %d temporary ents!\n", MAX_TEMP_ENTITIES );
		return NULL;
	}

	m_TempEnts.AddToTail( pTemp );

	pTemp->Prepare( model, gpGlobals->curtime );

	pTemp->priority = TENTPRIORITY_LOW;
	pTemp->SetAbsOrigin( org );

	pTemp->m_RenderGroup = RENDER_GROUP_OTHER;
	pTemp->AddToLeafSystem( pTemp->m_RenderGroup );

	if ( CommandLine()->CheckParm( "-tools" ) != NULL )
	{
#ifdef _DEBUG
		static bool first = true;
		if ( first )
		{
			Msg( "Currently not recording tempents, since recording them as entites causes them to be deleted as entities, even though they were allocated through the tempent pool. (crash)\n" );
			first = false;
		}
#endif
//		ClientEntityList().AddNonNetworkableEntity(	pTemp );
	}

	return pTemp;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : C_LocalTempEntity
//-----------------------------------------------------------------------------
C_LocalTempEntity *CTempEnts::TempEntAlloc()
{
	if ( m_TempEnts.Count() >= MAX_TEMP_ENTITIES )
		return NULL;

	C_LocalTempEntity *pTemp = m_TempEntsPool.AllocZero();
	return pTemp;
}

void CTempEnts::TempEntFree( int index )
{
	C_LocalTempEntity *pTemp = m_TempEnts[ index ];
	if ( pTemp )
	{
		// Remove from the active list.
		m_TempEnts.Remove( index );

		// Cleanup its data.
		pTemp->RemoveFromLeafSystem();

		// Remove the tempent from the ClientEntityList before removing it from the pool.
		if ( ( pTemp->flags & FTENT_CLIENTSIDEPARTICLES ) )
		{			
			// Stop the particle emission if this hasn't happened already - collision or system timing out on its own.
			if ( !pTemp->m_bParticleCollision )
			{
				pTemp->ParticleProp()->StopEmission();
			}
			ClientEntityList().RemoveEntity( pTemp->GetRefEHandle() );
		}

		pTemp->OnRemoveTempEntity();
	
		m_TempEntsPool.Free( pTemp );
	}
}


// Free the first low priority tempent it finds.
bool CTempEnts::FreeLowPriorityTempEnt()
{
	int next = 0;
	for( int i = m_TempEnts.Head(); i != m_TempEnts.InvalidIndex(); i = next )
	{
		next = m_TempEnts.Next( i );

		C_LocalTempEntity *pActive = m_TempEnts[ i ];

		if ( pActive->priority == TENTPRIORITY_LOW )
		{
			TempEntFree( i );
			return true;
		}
	}

	return false;
}


//-----------------------------------------------------------------------------
// Purpose: Allocate a temp entity, if there are no slots, kick out a low priority
//  one if possible
// Input  : *org - 
//			*model - 
// Output : C_LocalTempEntity
//-----------------------------------------------------------------------------
C_LocalTempEntity *CTempEnts::TempEntAllocHigh( const Vector& org, const model_t *model )
{
	C_LocalTempEntity		*pTemp;

	if ( !model )
	{
		DevWarning( 1, "temporary ent model invalid\n" );
		return NULL;
	}

	pTemp = TempEntAlloc();
	if ( !pTemp )
	{
		// no temporary ents free, so find the first active low-priority temp ent 
		// and overwrite it.
		FreeLowPriorityTempEnt();

		pTemp = TempEntAlloc();
	}

	
	if ( !pTemp )
	{
		// didn't find anything? The tent list is either full of high-priority tents
		// or all tents in the list are still due to live for > 10 seconds. 
		DevWarning( 1,"Couldn't alloc a high priority TENT (max %i)!\n", MAX_TEMP_ENTITIES );
		return NULL;
	}

	m_TempEnts.AddToTail( pTemp );

	pTemp->Prepare( model, gpGlobals->curtime );

	pTemp->priority = TENTPRIORITY_HIGH;
	pTemp->SetLocalOrigin( org );

	pTemp->m_RenderGroup = RENDER_GROUP_OTHER;
	pTemp->AddToLeafSystem( pTemp->m_RenderGroup );

	if ( CommandLine()->CheckParm( "-tools" ) != NULL )
	{
		ClientEntityList().AddNonNetworkableEntity(	pTemp );
	}

	return pTemp;
}

//-----------------------------------------------------------------------------
// Purpose: Play sound when temp ent collides with something
// Input  : *pTemp - 
//			damp - 
//-----------------------------------------------------------------------------
void CTempEnts::PlaySound ( C_LocalTempEntity *pTemp, float damp )
{
	const char	*soundname = NULL;
	float fvol;
	bool isshellcasing = false;
	int zvel;

	switch ( pTemp->hitSound )
	{
	default:
		return;	// null sound

	case BOUNCE_GLASS:
		{
			soundname = "Bounce.Glass";
		}
		break;

	case BOUNCE_METAL:
		{
			soundname = "Bounce.Metal";
		}
		break;

	case BOUNCE_FLESH:
		{
			soundname = "Bounce.Flesh";
		}
		break;

	case BOUNCE_WOOD:
		{
			soundname = "Bounce.Wood";
		}
		break;

	case BOUNCE_SHRAP:
		{
			soundname = "Bounce.Shrapnel";
		}
		break;

	case BOUNCE_SHOTSHELL:
		{
			soundname = "Bounce.ShotgunShell";
			isshellcasing = true; // shell casings have different playback parameters
		}
		break;

	case BOUNCE_SHELL:
		{
			soundname = "Bounce.Shell";
			isshellcasing = true; // shell casings have different playback parameters
		}
		break;

	case BOUNCE_CONCRETE:
		{
			soundname = "Bounce.Concrete";
		}
		break;

#ifdef CSTRIKE_DLL

		case TE_PISTOL_SHELL:
		{
			soundname = "Bounce.PistolShell";
		}
		break;

		case TE_RIFLE_SHELL:
		{
			soundname = "Bounce.RifleShell";
		}
		break;

		case TE_SHOTGUN_SHELL:
		{
			soundname = "Bounce.ShotgunShell";
		}
		break;
#endif
	}

	zvel = abs( pTemp->GetVelocity()[2] );
		
	// only play one out of every n

	if ( isshellcasing )
	{	
		// play first bounce, then 1 out of 3		
		if ( zvel < 200 && random->RandomInt(0,3) )
			return;
	}
	else
	{
		if ( random->RandomInt(0,5) ) 
			return;
	}

	CSoundParameters params;
	if ( !C_BaseEntity::GetParametersForSound( soundname, params, NULL ) )
		return;

	fvol = params.volume;

	if ( damp > 0.0 )
	{
		int pitch;
		
		if ( isshellcasing )
		{
			fvol *= MIN (1.0, ((float)zvel) / 350.0); 
		}
		else
		{
			fvol *= MIN (1.0, ((float)zvel) / 450.0); 
		}
		
		if ( !random->RandomInt(0,3) && !isshellcasing )
		{
			pitch = random->RandomInt( params.pitchlow, params.pitchhigh );
		}
		else
		{
			pitch = params.pitch;
		}

		CLocalPlayerFilter filter;

		EmitSound_t ep;
		ep.m_nChannel = params.channel;
		ep.m_pSoundName =  params.soundname;
		ep.m_flVolume = fvol;
		ep.m_SoundLevel = params.soundlevel;
		ep.m_nPitch = pitch;
		ep.m_pOrigin = &pTemp->GetAbsOrigin();

		C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
	}
}
					
//-----------------------------------------------------------------------------
// Purpose: Add temp entity to visible entities list of it's in PVS
// Input  : *pEntity - 
// Output : int
//-----------------------------------------------------------------------------
int CTempEnts::AddVisibleTempEntity( C_LocalTempEntity *pEntity )
{
	int i;
	Vector mins, maxs;
	Vector model_mins, model_maxs;

	if ( !pEntity->GetModel() )
		return 0;

	modelinfo->GetModelBounds( pEntity->GetModel(), model_mins, model_maxs );

	for (i=0 ; i<3 ; i++)
	{
		mins[i] = pEntity->GetAbsOrigin()[i] + model_mins[i];
		maxs[i] = pEntity->GetAbsOrigin()[i] + model_maxs[i];
	}

	// FIXME: Vis isn't setup by the time we get here, so this call fails if 
	//		  you try to add a tempent before the first frame is drawn, and it's
	//		  one frame behind the rest of the time. Fix this.
	// does the box intersect a visible leaf?
	//if ( engine->IsBoxInViewCluster( mins, maxs ) )
	{
		// Temporary entities have no corresponding element in cl_entitylist
		pEntity->index = -1;
		
		// Add to list
		if( pEntity->m_RenderGroup == RENDER_GROUP_OTHER )
		{
			pEntity->AddToLeafSystem();
		}
		else
		{
			pEntity->AddToLeafSystem( pEntity->m_RenderGroup );
		}

		return 1;
	}
	return 0;
}

//-----------------------------------------------------------------------------
// Purpose: Runs Temp Ent simulation routines
//-----------------------------------------------------------------------------
void CTempEnts::Update(void)
{
	VPROF_("CTempEnts::Update", 1, VPROF_BUDGETGROUP_CLIENT_SIM, false, BUDGETFLAG_CLIENT);
	static int gTempEntFrame = 0;
	float		frametime;

	// Don't simulate while loading
	if ( ( m_TempEnts.Count() == 0 ) || !engine->IsInGame() )		
	{
		return;
	}

	// !!!BUGBUG	-- This needs to be time based
	gTempEntFrame = (gTempEntFrame+1) & 31;

	frametime = gpGlobals->frametime;

	// in order to have tents collide with players, we have to run the player prediction code so
	// that the client has the player list. We run this code once when we detect any COLLIDEALL 
	// tent, then set this BOOL to true so the code doesn't get run again if there's more than
	// one COLLIDEALL ent for this update. (often are).

	// !!! Don't simulate while paused....  This is sort of a hack, revisit.
	if ( frametime == 0 )
	{
		FOR_EACH_LL( m_TempEnts, i )
		{
			C_LocalTempEntity *current = m_TempEnts[ i ];

			AddVisibleTempEntity( current );
		}
	}
	else
	{
		int next = 0;
		for( int i = m_TempEnts.Head(); i != m_TempEnts.InvalidIndex(); i = next )
		{
			next = m_TempEnts.Next( i );

			C_LocalTempEntity *current = m_TempEnts[ i ];

			// Kill it
			if ( !current->IsActive() || !current->Frame( frametime, gTempEntFrame ) )
			{
				TempEntFree( i );
			}
			else
			{
				// Cull to PVS (not frustum cull, just PVS)
				if ( !AddVisibleTempEntity( current ) )
				{
					if ( !( current->flags & FTENT_PERSIST ) ) 
					{
						// If we can't draw it this frame, just dump it.
						current->die = gpGlobals->curtime;
						// Don't fade out, just die
						current->flags &= ~FTENT_FADEOUT;

						TempEntFree( i );
					}
				}
			}
		}
	}
}

// Recache tempents which might have been flushed
void CTempEnts::LevelInit()
{
#ifndef TF_CLIENT_DLL
	m_pSpriteMuzzleFlash[0] = (model_t *)engine->LoadModel( "sprites/ar2_muzzle1.vmt" );
	m_pSpriteMuzzleFlash[1] = (model_t *)engine->LoadModel( "sprites/muzzleflash4.vmt" );
	m_pSpriteMuzzleFlash[2] = (model_t *)engine->LoadModel( "sprites/muzzleflash4.vmt" );

	m_pSpriteAR2Flash[0] = (model_t *)engine->LoadModel( "sprites/ar2_muzzle1b.vmt" );
	m_pSpriteAR2Flash[1] = (model_t *)engine->LoadModel( "sprites/ar2_muzzle2b.vmt" );
	m_pSpriteAR2Flash[2] = (model_t *)engine->LoadModel( "sprites/ar2_muzzle3b.vmt" );
	m_pSpriteAR2Flash[3] = (model_t *)engine->LoadModel( "sprites/ar2_muzzle4b.vmt" );

	m_pSpriteCombineFlash[0] = (model_t *)engine->LoadModel( "effects/combinemuzzle1.vmt" );
	m_pSpriteCombineFlash[1] = (model_t *)engine->LoadModel( "effects/combinemuzzle2.vmt" );

	m_pShells[0] = (model_t *) engine->LoadModel( "models/weapons/shell.mdl" );
	m_pShells[1] = (model_t *) engine->LoadModel( "models/weapons/rifleshell.mdl" );
	m_pShells[2] = (model_t *) engine->LoadModel( "models/weapons/shotgun_shell.mdl" );
#endif

#if defined( HL1_CLIENT_DLL )
	m_pHL1Shell			= (model_t *)engine->LoadModel( "models/shell.mdl" );
	m_pHL1ShotgunShell	= (model_t *)engine->LoadModel( "models/shotgunshell.mdl" );
#endif

#if defined( CSTRIKE_DLL ) || defined ( SDK_DLL )
	m_pCS_9MMShell		= (model_t *)engine->LoadModel( "models/Shells/shell_9mm.mdl" );
	m_pCS_57Shell		= (model_t *)engine->LoadModel( "models/Shells/shell_57.mdl" );
	m_pCS_12GaugeShell	= (model_t *)engine->LoadModel( "models/Shells/shell_12gauge.mdl" );
	m_pCS_556Shell		= (model_t *)engine->LoadModel( "models/Shells/shell_556.mdl" );
	m_pCS_762NATOShell	= (model_t *)engine->LoadModel( "models/Shells/shell_762nato.mdl" );
	m_pCS_338MAGShell	= (model_t *)engine->LoadModel( "models/Shells/shell_338mag.mdl" );
#endif
}


//-----------------------------------------------------------------------------
// Purpose: Initialize TE system
//-----------------------------------------------------------------------------
void CTempEnts::Init (void)
{
	m_pSpriteMuzzleFlash[0] = NULL;
	m_pSpriteMuzzleFlash[1] = NULL;
	m_pSpriteMuzzleFlash[2] = NULL;

	m_pSpriteAR2Flash[0] = NULL;
	m_pSpriteAR2Flash[1] = NULL;
	m_pSpriteAR2Flash[2] = NULL;
	m_pSpriteAR2Flash[3] = NULL;

	m_pSpriteCombineFlash[0] = NULL;
	m_pSpriteCombineFlash[1] = NULL;

	m_pShells[0] = NULL;
	m_pShells[1] = NULL;
	m_pShells[2] = NULL;

#if defined( HL1_CLIENT_DLL )
	m_pHL1Shell			= NULL;
	m_pHL1ShotgunShell	= NULL;
#endif

#if defined( CSTRIKE_DLL ) || defined ( SDK_DLL )
	m_pCS_9MMShell		= NULL;
	m_pCS_57Shell		= NULL;
	m_pCS_12GaugeShell	= NULL;
	m_pCS_556Shell		= NULL;
	m_pCS_762NATOShell	= NULL;
	m_pCS_338MAGShell	= NULL;
#endif

	// Clear out lists to start
	Clear();
}


void CTempEnts::LevelShutdown()
{
	// Free all active tempents.
	Clear();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTempEnts::Shutdown()
{
	LevelShutdown();
}

//-----------------------------------------------------------------------------
// Purpose: Cache off all material references
// Input  : *pEmitter - Emitter used for material lookup
//-----------------------------------------------------------------------------
inline void CTempEnts::CacheMuzzleFlashes( void )
{
	int i;
	for ( i = 0; i < 4; i++ )
	{
		if ( m_Material_MuzzleFlash_Player[i] == NULL )
		{
			m_Material_MuzzleFlash_Player[i] = ParticleMgr()->GetPMaterial( VarArgs( "effects/muzzleflash%d_noz", i+1 ) );
		}
	}

	for ( i = 0; i < 4; i++ )
	{
		if ( m_Material_MuzzleFlash_NPC[i] == NULL )
		{
			m_Material_MuzzleFlash_NPC[i] = ParticleMgr()->GetPMaterial( VarArgs( "effects/muzzleflash%d", i+1 ) );
		}
	}

	for ( i = 0; i < 2; i++ )
	{
		if ( m_Material_Combine_MuzzleFlash_Player[i] == NULL )
		{
			m_Material_Combine_MuzzleFlash_Player[i] = ParticleMgr()->GetPMaterial( VarArgs( "effects/combinemuzzle%d_noz", i+1 ) );
		}
	}

	for ( i = 0; i < 2; i++ )
	{
		if ( m_Material_Combine_MuzzleFlash_NPC[i] == NULL )
		{
			m_Material_Combine_MuzzleFlash_NPC[i] = ParticleMgr()->GetPMaterial( VarArgs( "effects/combinemuzzle%d", i+1 ) );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : entityIndex - 
//			attachmentIndex - 
//-----------------------------------------------------------------------------
void CTempEnts::MuzzleFlash_Combine_Player( ClientEntityHandle_t hEntity, int attachmentIndex )
{
	VPROF_BUDGET( "MuzzleFlash_Combine_Player", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
	CSmartPtr<CLocalSpaceEmitter> pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash", hEntity, attachmentIndex, FLE_VIEWMODEL );

	CacheMuzzleFlashes();

	SimpleParticle *pParticle;
	Vector			forward(1,0,0), offset; //NOTENOTE: All coords are in local space

	float flScale = random->RandomFloat( 2.0f, 2.25f );

	pSimple->SetDrawBeforeViewModel( true );

	// Flash
	for ( int i = 1; i < 6; i++ )
	{
		offset = (forward * (i*8.0f*flScale));

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), m_Material_Combine_MuzzleFlash_Player[random->RandomInt(0,1)], offset );
			
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= 0.025f;

		pParticle->m_vecVelocity.Init();

		pParticle->m_uchColor[0]	= 255;
		pParticle->m_uchColor[1]	= 255;
		pParticle->m_uchColor[2]	= 200+random->RandomInt(0,55);

		pParticle->m_uchStartAlpha	= 255;
		pParticle->m_uchEndAlpha	= 255;

		pParticle->m_uchStartSize	= ( (random->RandomFloat( 6.0f, 8.0f ) * (12-(i))/12) * flScale );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}

	// Tack on the smoke
	pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), m_Material_Combine_MuzzleFlash_Player[random->RandomInt(0,1)], vec3_origin );
		
	if ( pParticle == NULL )
		return;

	pParticle->m_flLifetime		= 0.0f;
	pParticle->m_flDieTime		= 0.025f;

	pParticle->m_vecVelocity.Init();

	pParticle->m_uchColor[0]	= 255;
	pParticle->m_uchColor[1]	= 255;
	pParticle->m_uchColor[2]	= 255;

	pParticle->m_uchStartAlpha	= random->RandomInt( 64, 128 );
	pParticle->m_uchEndAlpha	= 32;

	pParticle->m_uchStartSize	= random->RandomFloat( 10.0f, 16.0f );
	pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
	
	pParticle->m_flRoll			= random->RandomInt( 0, 360 );
	pParticle->m_flRollDelta	= 0.0f;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &origin - 
//			&angles - 
//			entityIndex - 
//-----------------------------------------------------------------------------
void CTempEnts::MuzzleFlash_Combine_NPC( ClientEntityHandle_t hEntity, int attachmentIndex )
{
	VPROF_BUDGET( "MuzzleFlash_Combine_NPC", VPROF_BUDGETGROUP_PARTICLE_RENDERING );

	// If the material isn't available, let's not do anything.
	if ( g_Mat_Combine_Muzzleflash[0] == NULL )
	{
		return;
	}

	CSmartPtr<CLocalSpaceEmitter> pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash_Combine_NPC", hEntity, attachmentIndex );

	SimpleParticle *pParticle;
	Vector			forward(1,0,0), offset; //NOTENOTE: All coords are in local space

	float flScale = random->RandomFloat( 1.0f, 1.5f );

	float burstSpeed = random->RandomFloat( 50.0f, 150.0f );

#define	FRONT_LENGTH 6

	// Front flash
	for ( int i = 1; i < FRONT_LENGTH; i++ )
	{
		offset = (forward * (i*2.0f*flScale));

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Mat_Combine_Muzzleflash[random->RandomInt(0,1)], offset );
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= 0.1f;

		pParticle->m_vecVelocity = forward * burstSpeed;

		pParticle->m_uchColor[0]	= 255;
		pParticle->m_uchColor[1]	= 255;
		pParticle->m_uchColor[2]	= 255;

		pParticle->m_uchStartAlpha	= 255.0f;
		pParticle->m_uchEndAlpha	= 0;

		pParticle->m_uchStartSize	= ( (random->RandomFloat( 6.0f, 8.0f ) * (FRONT_LENGTH*1.25f-(i))/(FRONT_LENGTH)) * flScale );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}
	
	Vector right(0,1,0), up(0,0,1);
	Vector dir = right - up;

#define	SIDE_LENGTH	6

	burstSpeed = random->RandomFloat( 50.0f, 150.0f );

	// Diagonal flash
	for ( int i = 1; i < SIDE_LENGTH; i++ )
	{
		offset = (dir * (i*flScale));

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Mat_Combine_Muzzleflash[random->RandomInt(0,1)], offset );
			
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= 0.2f;

		pParticle->m_vecVelocity = dir * burstSpeed * 0.25f;

		pParticle->m_uchColor[0]	= 255;
		pParticle->m_uchColor[1]	= 255;
		pParticle->m_uchColor[2]	= 255;

		pParticle->m_uchStartAlpha	= 255;
		pParticle->m_uchEndAlpha	= 0;

		pParticle->m_uchStartSize	= ( (random->RandomFloat( 2.0f, 4.0f ) * (SIDE_LENGTH-(i))/(SIDE_LENGTH*0.5f)) * flScale );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}

	dir = right + up;
	burstSpeed = random->RandomFloat( 50.0f, 150.0f );

	// Diagonal flash
	for ( int i = 1; i < SIDE_LENGTH; i++ )
	{
		offset = (-dir * (i*flScale));

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Mat_Combine_Muzzleflash[random->RandomInt(0,1)], offset );
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= 0.2f;

		pParticle->m_vecVelocity = dir * -burstSpeed * 0.25f;

		pParticle->m_uchColor[0]	= 255;
		pParticle->m_uchColor[1]	= 255;
		pParticle->m_uchColor[2]	= 255;

		pParticle->m_uchStartAlpha	= 255;
		pParticle->m_uchEndAlpha	= 0;

		pParticle->m_uchStartSize	= ( (random->RandomFloat( 2.0f, 4.0f ) * (SIDE_LENGTH-(i))/(SIDE_LENGTH*0.5f)) * flScale );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}

	dir = up;
	burstSpeed = random->RandomFloat( 50.0f, 150.0f );

	// Top flash
	for ( int i = 1; i < SIDE_LENGTH; i++ )
	{
		offset = (dir * (i*flScale));

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Mat_Combine_Muzzleflash[random->RandomInt(0,1)], offset );
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= 0.2f;

		pParticle->m_vecVelocity = dir * burstSpeed * 0.25f;

		pParticle->m_uchColor[0]	= 255;
		pParticle->m_uchColor[1]	= 255;
		pParticle->m_uchColor[2]	= 255;

		pParticle->m_uchStartAlpha	= 255;
		pParticle->m_uchEndAlpha	= 0;

		pParticle->m_uchStartSize	= ( (random->RandomFloat( 2.0f, 4.0f ) * (SIDE_LENGTH-(i))/(SIDE_LENGTH*0.5f)) * flScale );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}

	pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Mat_Combine_Muzzleflash[2], vec3_origin );
	if ( pParticle == NULL )
		return;

	pParticle->m_flLifetime		= 0.0f;
	pParticle->m_flDieTime		= random->RandomFloat( 0.3f, 0.4f );

	pParticle->m_vecVelocity.Init();

	pParticle->m_uchColor[0]	= 255;
	pParticle->m_uchColor[1]	= 255;
	pParticle->m_uchColor[2]	= 255;

	pParticle->m_uchStartAlpha	= 255;
	pParticle->m_uchEndAlpha	= 0;

	pParticle->m_uchStartSize	= flScale * random->RandomFloat( 12.0f, 16.0f );
	pParticle->m_uchEndSize		= 0.0f;
	pParticle->m_flRoll			= random->RandomInt( 0, 360 );
	pParticle->m_flRollDelta	= 0.0f;

	matrix3x4_t	matAttachment;
	Vector		origin;
	
	// Grab the origin out of the transform for the attachment
	if ( FX_GetAttachmentTransform( hEntity, attachmentIndex, matAttachment ) )
	{
		origin.x = matAttachment[0][3];
		origin.y = matAttachment[1][3];
		origin.z = matAttachment[2][3];
	}
	else
	{
		//NOTENOTE: If you're here, you've specified an entity or an attachment that is invalid
		Assert(0);
		return;
	}

	if ( muzzleflash_light.GetBool() )
	{
		C_BaseEntity *pEnt = ClientEntityList().GetBaseEntityFromHandle( hEntity );
		if ( pEnt )
		{
			dlight_t *el = effects->CL_AllocElight( LIGHT_INDEX_MUZZLEFLASH + pEnt->entindex() );

			el->origin	= origin;

			el->color.r = 64;
			el->color.g = 128;
			el->color.b = 255;
			el->color.exponent = 5;

			el->radius	= random->RandomInt( 32, 128 );
			el->decay	= el->radius / 0.05f;
			el->die		= gpGlobals->curtime + 0.05f;
		}
	}
}

//==================================================
// Purpose: 
// Input: 
//==================================================

void CTempEnts::MuzzleFlash_AR2_NPC( const Vector &origin, const QAngle &angles, ClientEntityHandle_t hEntity )
{
	//Draw the cloud of fire
	FX_MuzzleEffect( origin, angles, 1.0f, hEntity );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTempEnts::MuzzleFlash_SMG1_NPC( ClientEntityHandle_t hEntity, int attachmentIndex )
{
	//Draw the cloud of fire
	FX_MuzzleEffectAttached( 1.0f, hEntity, attachmentIndex, NULL, true );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTempEnts::MuzzleFlash_SMG1_Player( ClientEntityHandle_t hEntity, int attachmentIndex )
{
	VPROF_BUDGET( "MuzzleFlash_SMG1_Player", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
	CSmartPtr<CLocalSpaceEmitter> pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash_SMG1_Player", hEntity, attachmentIndex, FLE_VIEWMODEL );

	CacheMuzzleFlashes();

	SimpleParticle *pParticle;
	Vector			forward(1,0,0), offset; //NOTENOTE: All coords are in local space

	float flScale = random->RandomFloat( 1.25f, 1.5f );

	pSimple->SetDrawBeforeViewModel( true );

	// Flash
	for ( int i = 1; i < 6; i++ )
	{
		offset = (forward * (i*8.0f*flScale));

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), m_Material_MuzzleFlash_Player[random->RandomInt(0,3)], offset );
			
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= 0.025f;

		pParticle->m_vecVelocity.Init();

		pParticle->m_uchColor[0]	= 255;
		pParticle->m_uchColor[1]	= 255;
		pParticle->m_uchColor[2]	= 200+random->RandomInt(0,55);

		pParticle->m_uchStartAlpha	= 255;
		pParticle->m_uchEndAlpha	= 255;

		pParticle->m_uchStartSize	= ( (random->RandomFloat( 6.0f, 8.0f ) * (8-(i))/6) * flScale );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}
}

//==================================================
// Purpose: 
// Input: 
//==================================================

void CTempEnts::MuzzleFlash_Shotgun_Player( ClientEntityHandle_t hEntity, int attachmentIndex )
{
	VPROF_BUDGET( "MuzzleFlash_Shotgun_Player", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
	CSmartPtr<CSimpleEmitter> pSimple = CSimpleEmitter::Create( "MuzzleFlash_Shotgun_Player" );

	pSimple->SetDrawBeforeViewModel( true );

	CacheMuzzleFlashes();

	Vector origin;
	QAngle angles;

	// Get our attachment's transformation matrix
	FX_GetAttachmentTransform( hEntity, attachmentIndex, &origin, &angles );

	pSimple->GetBinding().SetBBox( origin - Vector( 4, 4, 4 ), origin + Vector( 4, 4, 4 ) );

	Vector forward;
	AngleVectors( angles, &forward, NULL, NULL );

	SimpleParticle *pParticle;
	Vector offset;

	float flScale = random->RandomFloat( 1.25f, 1.5f );

	// Flash
	for ( int i = 1; i < 6; i++ )
	{
		offset = origin + (forward * (i*8.0f*flScale));

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), m_Material_MuzzleFlash_Player[random->RandomInt(0,3)], offset );
			
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= 0.0001f;

		pParticle->m_vecVelocity.Init();

		pParticle->m_uchColor[0]	= 255;
		pParticle->m_uchColor[1]	= 255;
		pParticle->m_uchColor[2]	= 200+random->RandomInt(0,55);

		pParticle->m_uchStartAlpha	= 255;
		pParticle->m_uchEndAlpha	= 255;

		pParticle->m_uchStartSize	= ( (random->RandomFloat( 6.0f, 8.0f ) * (8-(i))/6) * flScale );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}
}

//==================================================
// Purpose: 
// Input: 
//==================================================

void CTempEnts::MuzzleFlash_Shotgun_NPC( ClientEntityHandle_t hEntity, int attachmentIndex )
{
	//Draw the cloud of fire
	FX_MuzzleEffectAttached( 0.75f, hEntity, attachmentIndex );

	// If the material isn't available, let's not do anything else.
	if ( g_Mat_SMG_Muzzleflash[0] == NULL )
	{
		return;
	}

	QAngle	angles;

	Vector	forward;
	int		i;

	// Setup the origin.
	Vector	origin;
	IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( hEntity );
	if ( !pRenderable )
		return;

	pRenderable->GetAttachment( attachmentIndex, origin, angles );
	AngleVectors( angles, &forward );

	//Embers less often
	if ( random->RandomInt( 0, 2 ) == 0 )
	{
		//Embers
		CSmartPtr<CEmberEffect> pEmbers = CEmberEffect::Create( "muzzle_embers" );
		pEmbers->SetSortOrigin( origin );

		SimpleParticle	*pParticle;

		int	numEmbers = random->RandomInt( 0, 4 );

		for ( int i = 0; i < numEmbers; i++ )
		{
			pParticle = (SimpleParticle *) pEmbers->AddParticle( sizeof( SimpleParticle ), g_Mat_SMG_Muzzleflash[0], origin );
				
			if ( pParticle == NULL )
				return;

			pParticle->m_flLifetime		= 0.0f;
			pParticle->m_flDieTime		= random->RandomFloat( 0.2f, 0.4f );

			pParticle->m_vecVelocity.Random( -0.05f, 0.05f );
			pParticle->m_vecVelocity += forward;
			VectorNormalize( pParticle->m_vecVelocity );

			pParticle->m_vecVelocity	*= random->RandomFloat( 64.0f, 256.0f );

			pParticle->m_uchColor[0]	= 255;
			pParticle->m_uchColor[1]	= 128;
			pParticle->m_uchColor[2]	= 64;

			pParticle->m_uchStartAlpha	= 255;
			pParticle->m_uchEndAlpha	= 0;

			pParticle->m_uchStartSize	= 1;
			pParticle->m_uchEndSize		= 0;

			pParticle->m_flRoll			= 0;
			pParticle->m_flRollDelta	= 0;
		}
	}

	//
	// Trails
	//
	
	CSmartPtr<CTrailParticles> pTrails = CTrailParticles::Create( "MuzzleFlash_Shotgun_NPC" );
	pTrails->SetSortOrigin( origin );

	TrailParticle	*pTrailParticle;

	pTrails->SetFlag( bitsPARTICLE_TRAIL_FADE );
	pTrails->m_ParticleCollision.SetGravity( 0.0f );

	int	numEmbers = random->RandomInt( 4, 8 );

	for ( i = 0; i < numEmbers; i++ )
	{
		pTrailParticle = (TrailParticle *) pTrails->AddParticle( sizeof( TrailParticle ), g_Mat_SMG_Muzzleflash[0], origin );
			
		if ( pTrailParticle == NULL )
			return;

		pTrailParticle->m_flLifetime		= 0.0f;
		pTrailParticle->m_flDieTime		= random->RandomFloat( 0.1f, 0.2f );

		float spread = 0.05f;

		pTrailParticle->m_vecVelocity.Random( -spread, spread );
		pTrailParticle->m_vecVelocity += forward;
		
		VectorNormalize( pTrailParticle->m_vecVelocity );
		VectorNormalize( forward );

		float dot = forward.Dot( pTrailParticle->m_vecVelocity );

		dot = (1.0f-fabs(dot)) / spread;
		pTrailParticle->m_vecVelocity *= (random->RandomFloat( 256.0f, 1024.0f ) * (1.0f-dot));

		Color32Init( pTrailParticle->m_color, 255, 242, 191, 255 );

		pTrailParticle->m_flLength	= 0.05f;
		pTrailParticle->m_flWidth	= random->RandomFloat( 0.25f, 0.5f );
	}
}

//==================================================
// Purpose: 
//==================================================
void CTempEnts::MuzzleFlash_357_Player( ClientEntityHandle_t hEntity, int attachmentIndex )
{
	VPROF_BUDGET( "MuzzleFlash_357_Player", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
	CSmartPtr<CSimpleEmitter> pSimple = CSimpleEmitter::Create( "MuzzleFlash_357_Player" );

	pSimple->SetDrawBeforeViewModel( true );

	CacheMuzzleFlashes();

	Vector origin;
	QAngle angles;

	// Get our attachment's transformation matrix
	FX_GetAttachmentTransform( hEntity, attachmentIndex, &origin, &angles );

	pSimple->GetBinding().SetBBox( origin - Vector( 4, 4, 4 ), origin + Vector( 4, 4, 4 ) );

	Vector forward;
	AngleVectors( angles, &forward, NULL, NULL );

	SimpleParticle *pParticle;
	Vector			offset;

	// Smoke
	offset = origin + forward * 8.0f;

	pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Mat_DustPuff[0], offset );
		
	if ( pParticle == NULL )
		return;

	pParticle->m_flLifetime		= 0.0f;
	pParticle->m_flDieTime		= random->RandomFloat( 0.5f, 1.0f );

	pParticle->m_vecVelocity.Init();
	pParticle->m_vecVelocity = forward * random->RandomFloat( 8.0f, 64.0f );
	pParticle->m_vecVelocity[2] += random->RandomFloat( 4.0f, 16.0f );

	int color = random->RandomInt( 200, 255 );
	pParticle->m_uchColor[0]	= color;
	pParticle->m_uchColor[1]	= color;
	pParticle->m_uchColor[2]	= color;

	pParticle->m_uchStartAlpha	= random->RandomInt( 64, 128 );
	pParticle->m_uchEndAlpha	= 0;

	pParticle->m_uchStartSize	= random->RandomInt( 2, 4 );
	pParticle->m_uchEndSize		= pParticle->m_uchStartSize * 8.0f;
	pParticle->m_flRoll			= random->RandomInt( 0, 360 );
	pParticle->m_flRollDelta	= random->RandomFloat( -0.5f, 0.5f );

	float flScale = random->RandomFloat( 1.25f, 1.5f );

	// Flash
	for ( int i = 1; i < 6; i++ )
	{
		offset = origin + (forward * (i*8.0f*flScale));

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), m_Material_MuzzleFlash_Player[random->RandomInt(0,3)], offset );
			
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= 0.01f;

		pParticle->m_vecVelocity.Init();

		pParticle->m_uchColor[0]	= 255;
		pParticle->m_uchColor[1]	= 255;
		pParticle->m_uchColor[2]	= 200+random->RandomInt(0,55);

		pParticle->m_uchStartAlpha	= 255;
		pParticle->m_uchEndAlpha	= 255;

		pParticle->m_uchStartSize	= ( (random->RandomFloat( 6.0f, 8.0f ) * (8-(i))/6) * flScale );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}
}

//==================================================
// Purpose: 
// Input: 
//==================================================

void CTempEnts::MuzzleFlash_Pistol_Player( ClientEntityHandle_t hEntity, int attachmentIndex )
{
	VPROF_BUDGET( "MuzzleFlash_Pistol_Player", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
	CSmartPtr<CSimpleEmitter> pSimple = CSimpleEmitter::Create( "MuzzleFlash_Pistol_Player" );
	pSimple->SetDrawBeforeViewModel( true );

	CacheMuzzleFlashes();

	Vector origin;
	QAngle angles;

	// Get our attachment's transformation matrix
	FX_GetAttachmentTransform( hEntity, attachmentIndex, &origin, &angles );

	pSimple->GetBinding().SetBBox( origin - Vector( 4, 4, 4 ), origin + Vector( 4, 4, 4 ) );

	Vector forward;
	AngleVectors( angles, &forward, NULL, NULL );

	SimpleParticle *pParticle;
	Vector			offset;

	// Smoke
	offset = origin + forward * 8.0f;

	if ( random->RandomInt( 0, 3 ) != 0 )
	{
		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Mat_DustPuff[0], offset );
			
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= random->RandomFloat( 0.25f, 0.5f );

		pParticle->m_vecVelocity.Init();
		pParticle->m_vecVelocity = forward * random->RandomFloat( 48.0f, 64.0f );
		pParticle->m_vecVelocity[2] += random->RandomFloat( 4.0f, 16.0f );

		int color = random->RandomInt( 200, 255 );
		pParticle->m_uchColor[0]	= color;
		pParticle->m_uchColor[1]	= color;
		pParticle->m_uchColor[2]	= color;

		pParticle->m_uchStartAlpha	= random->RandomInt( 64, 128 );
		pParticle->m_uchEndAlpha	= 0;

		pParticle->m_uchStartSize	= random->RandomInt( 2, 4 );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize * 4.0f;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= random->RandomFloat( -0.1f, 0.1f );
	}

	float flScale = random->RandomFloat( 1.0f, 1.25f );

	// Flash
	for ( int i = 1; i < 6; i++ )
	{
		offset = origin + (forward * (i*4.0f*flScale));

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), m_Material_MuzzleFlash_Player[random->RandomInt(0,3)], offset );
			
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= 0.01f;

		pParticle->m_vecVelocity.Init();

		pParticle->m_uchColor[0]	= 255;
		pParticle->m_uchColor[1]	= 255;
		pParticle->m_uchColor[2]	= 200+random->RandomInt(0,55);

		pParticle->m_uchStartAlpha	= 255;
		pParticle->m_uchEndAlpha	= 255;

		pParticle->m_uchStartSize	= ( (random->RandomFloat( 6.0f, 8.0f ) * (8-(i))/6) * flScale );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}
}

//==================================================
// Purpose: 
// Input: 
//==================================================

void CTempEnts::MuzzleFlash_Pistol_NPC( ClientEntityHandle_t hEntity, int attachmentIndex )
{
	FX_MuzzleEffectAttached( 0.5f, hEntity, attachmentIndex, NULL, true );
}




//==================================================
// Purpose: 
// Input: 
//==================================================

void CTempEnts::MuzzleFlash_RPG_NPC( ClientEntityHandle_t hEntity, int attachmentIndex )
{
	//Draw the cloud of fire
	FX_MuzzleEffectAttached( 1.5f, hEntity, attachmentIndex );

}



void CTempEnts::RocketFlare( const Vector& pos )
{
	C_LocalTempEntity	*pTemp;
	const model_t		*model;
	int					nframeCount;

	model = (model_t *)engine->LoadModel( "sprites/animglow01.vmt" );
	if ( !model )
	{
		return;
	}

	nframeCount = modelinfo->GetModelFrameCount( model );

	pTemp = TempEntAlloc( pos, model );
	if ( !pTemp )
		return;

	pTemp->m_flFrameMax = nframeCount - 1;
	pTemp->SetRenderMode( kRenderGlow );
	pTemp->m_nRenderFX = kRenderFxNoDissipation;
	pTemp->tempent_renderamt = 255;
	pTemp->m_flFrameRate = 1.0;
	pTemp->m_flFrame = random->RandomInt( 0, nframeCount - 1);
	pTemp->m_flSpriteScale = 1.0;
	pTemp->SetAbsOrigin( pos );
	pTemp->die = gpGlobals->curtime + 0.01;
}


void CTempEnts::HL1EjectBrass( const Vector &vecPosition, const QAngle &angAngles, const Vector &vecVelocity, int nType )
{
	const model_t *pModel = NULL;

#if defined( HL1_CLIENT_DLL )
	switch ( nType )
	{
	case 0:
	default:
		pModel = m_pHL1Shell;
		break;
	case 1:
		pModel = m_pHL1ShotgunShell;
		break;
	}
#endif
	if ( pModel == NULL )
		return;

	C_LocalTempEntity	*pTemp = TempEntAlloc( vecPosition, pModel );

	if ( pTemp == NULL )
		return;

	switch ( nType )
	{
	case 0:
	default:
		pTemp->hitSound = BOUNCE_SHELL;
		break;
	case 1:
		pTemp->hitSound = BOUNCE_SHOTSHELL;
		break;
	}

	pTemp->m_nBody	= 0;
	pTemp->flags |= ( FTENT_COLLIDEWORLD | FTENT_FADEOUT | FTENT_GRAVITY | FTENT_ROTATE );

	pTemp->m_vecTempEntAngVelocity[0] = random->RandomFloat( -512,511 );
	pTemp->m_vecTempEntAngVelocity[1] = random->RandomFloat( -256,255 );
	pTemp->m_vecTempEntAngVelocity[2] = random->RandomFloat( -256,255 );

	//Face forward
	pTemp->SetAbsAngles( angAngles );

	pTemp->SetRenderMode( kRenderNormal );
	pTemp->tempent_renderamt	= 255;		// Set this for fadeout

	pTemp->SetVelocity( vecVelocity );

	pTemp->die = gpGlobals->curtime + 2.5;
}

#define SHELLTYPE_PISTOL	0
#define SHELLTYPE_RIFLE		1
#define SHELLTYPE_SHOTGUN	2


void CTempEnts::CSEjectBrass( const Vector &vecPosition, const QAngle &angVelocity, int nVelocity, int shellType, CBasePlayer *pShooter )
{
	const model_t *pModel = NULL;
	int hitsound = TE_BOUNCE_SHELL;

#if defined ( CSTRIKE_DLL ) || defined ( SDK_DLL )

	switch( shellType )
	{
	default:
	case CS_SHELL_9MM:
		hitsound = TE_PISTOL_SHELL;
		pModel = m_pCS_9MMShell;
		break;
	case CS_SHELL_57:
		hitsound = TE_PISTOL_SHELL;
		pModel = m_pCS_57Shell;
		break;
	case CS_SHELL_12GAUGE:
		hitsound = TE_SHOTGUN_SHELL;
		pModel = m_pCS_12GaugeShell;
		break;
	case CS_SHELL_556:
		hitsound = TE_RIFLE_SHELL;
		pModel = m_pCS_556Shell;
		break;
	case CS_SHELL_762NATO:
		hitsound = TE_RIFLE_SHELL;
		pModel = m_pCS_762NATOShell;
		break;
	case CS_SHELL_338MAG:
		hitsound = TE_RIFLE_SHELL;
		pModel = m_pCS_338MAGShell;
		break;
	}
#endif

	if ( pModel == NULL )
		return;

	Vector forward, right, up;
	Vector velocity;
	Vector origin;
	QAngle angle;
	
	// Add some randomness to the velocity

	AngleVectors( angVelocity, &forward, &right, &up );
	
	velocity = forward * nVelocity * random->RandomFloat( 1.2, 2.8 ) +
			   up * random->RandomFloat( -10, 10 ) +
			   right * random->RandomFloat( -20, 20 );

	if( pShooter )
		velocity += pShooter->GetAbsVelocity();

	C_LocalTempEntity *pTemp = TempEntAlloc( vecPosition, pModel );
	if ( !pTemp )
		return;

	if( pShooter )
		pTemp->SetAbsAngles( pShooter->EyeAngles() );
	else
		pTemp->SetAbsAngles( vec3_angle );

	pTemp->SetVelocity( velocity );

	pTemp->hitSound = hitsound;

	pTemp->SetGravity( 0.4 );

	pTemp->m_nBody	= 0;
	pTemp->flags = FTENT_FADEOUT | FTENT_GRAVITY | FTENT_COLLIDEALL | FTENT_HITSOUND | FTENT_ROTATE | FTENT_CHANGERENDERONCOLLIDE;

	pTemp->m_vecTempEntAngVelocity[0] = random->RandomFloat(-256,256);
	pTemp->m_vecTempEntAngVelocity[1] = random->RandomFloat(-256,256);
	pTemp->m_vecTempEntAngVelocity[2] = 0;
	pTemp->SetRenderMode( kRenderNormal );
	pTemp->tempent_renderamt = 255;
	
	pTemp->die = gpGlobals->curtime + 10;

	bool bViewModelBrass = false;

	if ( pShooter && pShooter->GetObserverMode() == OBS_MODE_IN_EYE )
	{
		// we are spectating the shooter in first person view
		pShooter = ToBasePlayer( pShooter->GetObserverTarget() );
		bViewModelBrass = true;
	}

	if ( pShooter )
	{
		pTemp->clientIndex = pShooter->entindex();
		bViewModelBrass |= pShooter->IsLocalPlayer();
	}
	else
	{
		pTemp->clientIndex = 0;
	}

	if ( bViewModelBrass )
	{
		// for viewmodel brass put it in the viewmodel renderer group
		pTemp->m_RenderGroup = RENDER_GROUP_VIEW_MODEL_OPAQUE;
	}

	
}

