source-engine/game/client/c_te_legacytempents.cpp
2022-03-01 23:00:42 +03:00

3440 lines
90 KiB
C++

//========= 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 )
{
Vector vecWind;
GetWindspeedAtTime( gpGlobals->curtime, vecWind );
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
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;
default:
// There's no supported muzzle flash for the type specified!
Assert(0);
break;
}
#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;
}
}