You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3439 lines
90 KiB
3439 lines
90 KiB
//========= 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 = { (uint8)r, (uint8)g, (uint8)b, (uint8)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; |
|
} |
|
|
|
|
|
} |
|
|
|
|