mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-22 22:27:05 +00:00
3440 lines
90 KiB
C++
3440 lines
90 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $Workfile: $
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
#include "cbase.h"
|
|
#include "model_types.h"
|
|
#include "view_shared.h"
|
|
#include "iviewrender.h"
|
|
#include "tempentity.h"
|
|
#include "dlight.h"
|
|
#include "tempent.h"
|
|
#include "c_te_legacytempents.h"
|
|
#include "clientsideeffects.h"
|
|
#include "cl_animevent.h"
|
|
#include "iefx.h"
|
|
#include "engine/IEngineSound.h"
|
|
#include "env_wind_shared.h"
|
|
#include "clienteffectprecachesystem.h"
|
|
#include "fx_sparks.h"
|
|
#include "fx.h"
|
|
#include "movevars_shared.h"
|
|
#include "engine/ivmodelinfo.h"
|
|
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
|
#include "view.h"
|
|
#include "tier0/vprof.h"
|
|
#include "particles_localspace.h"
|
|
#include "physpropclientside.h"
|
|
#include "tier0/icommandline.h"
|
|
#include "datacache/imdlcache.h"
|
|
#include "engine/ivdebugoverlay.h"
|
|
#include "effect_dispatch_data.h"
|
|
#include "c_te_effect_dispatch.h"
|
|
#include "c_props.h"
|
|
#include "c_basedoor.h"
|
|
|
|
// NOTE: Always include this last!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
extern ConVar muzzleflash_light;
|
|
|
|
#define TENT_WIND_ACCEL 50
|
|
|
|
//Precache the effects
|
|
#ifndef TF_CLIENT_DLL
|
|
CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectMuzzleFlash )
|
|
|
|
CLIENTEFFECT_MATERIAL( "effects/muzzleflash1" )
|
|
CLIENTEFFECT_MATERIAL( "effects/muzzleflash2" )
|
|
CLIENTEFFECT_MATERIAL( "effects/muzzleflash3" )
|
|
CLIENTEFFECT_MATERIAL( "effects/muzzleflash4" )
|
|
CLIENTEFFECT_MATERIAL( "effects/muzzleflash1_noz" )
|
|
CLIENTEFFECT_MATERIAL( "effects/muzzleflash2_noz" )
|
|
CLIENTEFFECT_MATERIAL( "effects/muzzleflash3_noz" )
|
|
CLIENTEFFECT_MATERIAL( "effects/muzzleflash4_noz" )
|
|
#ifndef CSTRIKE_DLL
|
|
CLIENTEFFECT_MATERIAL( "effects/combinemuzzle1" )
|
|
CLIENTEFFECT_MATERIAL( "effects/combinemuzzle2" )
|
|
CLIENTEFFECT_MATERIAL( "effects/combinemuzzle1_noz" )
|
|
CLIENTEFFECT_MATERIAL( "effects/combinemuzzle2_noz" )
|
|
CLIENTEFFECT_MATERIAL( "effects/strider_muzzle" )
|
|
#endif
|
|
CLIENTEFFECT_REGISTER_END()
|
|
#endif
|
|
|
|
//Whether or not to eject brass from weapons
|
|
ConVar cl_ejectbrass( "cl_ejectbrass", "1" );
|
|
|
|
ConVar func_break_max_pieces( "func_break_max_pieces", "15", FCVAR_ARCHIVE | FCVAR_REPLICATED );
|
|
|
|
ConVar cl_fasttempentcollision( "cl_fasttempentcollision", "5" );
|
|
|
|
#if !defined( HL1_CLIENT_DLL ) // HL1 implements a derivative of CTempEnts
|
|
// Temp entity interface
|
|
static CTempEnts g_TempEnts;
|
|
// Expose to rest of the client .dll
|
|
ITempEnts *tempents = ( ITempEnts * )&g_TempEnts;
|
|
#endif
|
|
|
|
|
|
|
|
|
|
C_LocalTempEntity::C_LocalTempEntity()
|
|
{
|
|
#ifdef _DEBUG
|
|
tentOffset.Init();
|
|
m_vecTempEntVelocity.Init();
|
|
m_vecTempEntAngVelocity.Init();
|
|
m_vecNormal.Init();
|
|
#endif
|
|
m_vecTempEntAcceleration.Init();
|
|
m_pfnDrawHelper = 0;
|
|
m_pszImpactEffect = NULL;
|
|
}
|
|
|
|
|
|
#if defined( CSTRIKE_DLL ) || defined (SDK_DLL )
|
|
|
|
#define TE_RIFLE_SHELL 1024
|
|
#define TE_PISTOL_SHELL 2048
|
|
#define TE_SHOTGUN_SHELL 4096
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Prepare a temp entity for creation
|
|
// Input : time -
|
|
// *model -
|
|
//-----------------------------------------------------------------------------
|
|
void C_LocalTempEntity::Prepare( const model_t *pmodel, float time )
|
|
{
|
|
Interp_SetupMappings( GetVarMapping() );
|
|
|
|
index = -1;
|
|
Clear();
|
|
|
|
// Use these to set per-frame and termination conditions / actions
|
|
flags = FTENT_NONE;
|
|
die = time + 0.75;
|
|
SetModelPointer( pmodel );
|
|
SetRenderMode( kRenderNormal );
|
|
m_nRenderFX = kRenderFxNone;
|
|
m_nBody = 0;
|
|
m_nSkin = 0;
|
|
fadeSpeed = 0.5;
|
|
hitSound = 0;
|
|
clientIndex = -1;
|
|
bounceFactor = 1;
|
|
m_nFlickerFrame = 0;
|
|
m_bParticleCollision = false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the velocity
|
|
//-----------------------------------------------------------------------------
|
|
void C_LocalTempEntity::SetVelocity( const Vector &vecVelocity )
|
|
{
|
|
m_vecTempEntVelocity = vecVelocity;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the velocity
|
|
//-----------------------------------------------------------------------------
|
|
void C_LocalTempEntity::SetAcceleration( const Vector &vecVelocity )
|
|
{
|
|
m_vecTempEntAcceleration = vecVelocity;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int C_LocalTempEntity::DrawStudioModel( int flags )
|
|
{
|
|
VPROF_BUDGET( "C_LocalTempEntity::DrawStudioModel", VPROF_BUDGETGROUP_MODEL_RENDERING );
|
|
int drawn = 0;
|
|
|
|
if ( !GetModel() || modelinfo->GetModelType( GetModel() ) != mod_studio )
|
|
return drawn;
|
|
|
|
// Make sure m_pstudiohdr is valid for drawing
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
if ( !GetModelPtr() )
|
|
return drawn;
|
|
|
|
if ( m_pfnDrawHelper )
|
|
{
|
|
drawn = ( *m_pfnDrawHelper )( this, flags );
|
|
}
|
|
else
|
|
{
|
|
drawn = modelrender->DrawModel(
|
|
flags,
|
|
this,
|
|
MODEL_INSTANCE_INVALID,
|
|
index,
|
|
GetModel(),
|
|
GetAbsOrigin(),
|
|
GetAbsAngles(),
|
|
m_nSkin,
|
|
m_nBody,
|
|
m_nHitboxSet );
|
|
}
|
|
return drawn;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : flags -
|
|
//-----------------------------------------------------------------------------
|
|
int C_LocalTempEntity::DrawModel( int flags )
|
|
{
|
|
int drawn = 0;
|
|
|
|
if ( !GetModel() )
|
|
{
|
|
return drawn;
|
|
}
|
|
|
|
if ( GetRenderMode() == kRenderNone )
|
|
return drawn;
|
|
|
|
if ( this->flags & FTENT_BEOCCLUDED )
|
|
{
|
|
// Check normal
|
|
Vector vecDelta = (GetAbsOrigin() - MainViewOrigin());
|
|
VectorNormalize( vecDelta );
|
|
float flDot = DotProduct( m_vecNormal, vecDelta );
|
|
if ( flDot > 0 )
|
|
{
|
|
float flAlpha = RemapVal( MIN(flDot,0.3), 0, 0.3, 0, 1 );
|
|
flAlpha = MAX( 1.0, tempent_renderamt - (tempent_renderamt * flAlpha) );
|
|
SetRenderColorA( flAlpha );
|
|
}
|
|
}
|
|
|
|
switch ( modelinfo->GetModelType( GetModel() ) )
|
|
{
|
|
case mod_sprite:
|
|
drawn = DrawSprite(
|
|
this,
|
|
GetModel(),
|
|
GetAbsOrigin(),
|
|
GetAbsAngles(),
|
|
m_flFrame, // sprite frame to render
|
|
m_nBody > 0 ? cl_entitylist->GetBaseEntity( m_nBody ) : NULL, // attach to
|
|
m_nSkin, // attachment point
|
|
GetRenderMode(), // rendermode
|
|
m_nRenderFX, // renderfx
|
|
m_clrRender->a, // alpha
|
|
m_clrRender->r,
|
|
m_clrRender->g,
|
|
m_clrRender->b,
|
|
m_flSpriteScale // sprite scale
|
|
);
|
|
break;
|
|
case mod_studio:
|
|
drawn = DrawStudioModel( flags );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return drawn;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool C_LocalTempEntity::IsActive( void )
|
|
{
|
|
bool active = true;
|
|
|
|
float life = die - gpGlobals->curtime;
|
|
|
|
if ( life < 0 )
|
|
{
|
|
if ( flags & FTENT_FADEOUT )
|
|
{
|
|
int alpha;
|
|
if (GetRenderMode() == kRenderNormal)
|
|
{
|
|
SetRenderMode( kRenderTransTexture );
|
|
}
|
|
|
|
alpha = tempent_renderamt * ( 1 + life * fadeSpeed );
|
|
|
|
if ( alpha <= 0 )
|
|
{
|
|
active = false;
|
|
alpha = 0;
|
|
}
|
|
|
|
SetRenderColorA( alpha );
|
|
}
|
|
else
|
|
{
|
|
active = false;
|
|
}
|
|
}
|
|
|
|
// Never die tempents only die when their die is cleared
|
|
if ( flags & FTENT_NEVERDIE )
|
|
{
|
|
active = (die != 0);
|
|
}
|
|
|
|
return active;
|
|
}
|
|
|
|
bool C_LocalTempEntity::Frame( float frametime, int framenumber )
|
|
{
|
|
float fastFreq = gpGlobals->curtime * 5.5;
|
|
float gravity = -frametime * GetCurrentGravity();
|
|
float gravitySlow = gravity * 0.5;
|
|
float traceFraction = 1;
|
|
|
|
Assert( !GetMoveParent() );
|
|
|
|
m_vecPrevLocalOrigin = GetLocalOrigin();
|
|
|
|
m_vecTempEntVelocity = m_vecTempEntVelocity + ( m_vecTempEntAcceleration * frametime );
|
|
|
|
if ( flags & FTENT_PLYRATTACHMENT )
|
|
{
|
|
if ( IClientEntity *pClient = cl_entitylist->GetClientEntity( clientIndex ) )
|
|
{
|
|
SetLocalOrigin( pClient->GetAbsOrigin() + tentOffset );
|
|
}
|
|
}
|
|
else if ( flags & FTENT_SINEWAVE )
|
|
{
|
|
x += m_vecTempEntVelocity[0] * frametime;
|
|
y += m_vecTempEntVelocity[1] * frametime;
|
|
|
|
SetLocalOrigin( Vector(
|
|
x + sin( m_vecTempEntVelocity[2] + gpGlobals->curtime /* * anim.prevframe */ ) * (10*m_flSpriteScale),
|
|
y + sin( m_vecTempEntVelocity[2] + fastFreq + 0.7 ) * (8*m_flSpriteScale),
|
|
GetLocalOriginDim( Z_INDEX ) + m_vecTempEntVelocity[2] * frametime ) );
|
|
}
|
|
else if ( flags & FTENT_SPIRAL )
|
|
{
|
|
float s, c;
|
|
SinCos( m_vecTempEntVelocity[2] + fastFreq, &s, &c );
|
|
|
|
SetLocalOrigin( GetLocalOrigin() + Vector(
|
|
m_vecTempEntVelocity[0] * frametime + 8 * sin( gpGlobals->curtime * 20 ),
|
|
m_vecTempEntVelocity[1] * frametime + 4 * sin( gpGlobals->curtime * 30 ),
|
|
m_vecTempEntVelocity[2] * frametime ) );
|
|
}
|
|
else
|
|
{
|
|
SetLocalOrigin( GetLocalOrigin() + m_vecTempEntVelocity * frametime );
|
|
}
|
|
|
|
if ( flags & FTENT_SPRANIMATE )
|
|
{
|
|
m_flFrame += frametime * m_flFrameRate;
|
|
if ( m_flFrame >= m_flFrameMax )
|
|
{
|
|
m_flFrame = m_flFrame - (int)(m_flFrame);
|
|
|
|
if ( !(flags & FTENT_SPRANIMATELOOP) )
|
|
{
|
|
// this animating sprite isn't set to loop, so destroy it.
|
|
die = 0.0f;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if ( flags & FTENT_SPRCYCLE )
|
|
{
|
|
m_flFrame += frametime * 10;
|
|
if ( m_flFrame >= m_flFrameMax )
|
|
{
|
|
m_flFrame = m_flFrame - (int)(m_flFrame);
|
|
}
|
|
}
|
|
|
|
if ( flags & FTENT_SMOKEGROWANDFADE )
|
|
{
|
|
m_flSpriteScale += frametime * 0.5f;
|
|
//m_clrRender.a -= frametime * 1500;
|
|
}
|
|
|
|
if ( flags & FTENT_ROTATE )
|
|
{
|
|
SetLocalAngles( GetLocalAngles() + m_vecTempEntAngVelocity * frametime );
|
|
}
|
|
else if ( flags & FTENT_ALIGNTOMOTION )
|
|
{
|
|
if ( m_vecTempEntVelocity.Length() > 0.0f )
|
|
{
|
|
QAngle angles;
|
|
VectorAngles( m_vecTempEntVelocity, angles );
|
|
SetAbsAngles( angles );
|
|
}
|
|
}
|
|
|
|
if ( flags & (FTENT_COLLIDEALL | FTENT_COLLIDEWORLD | FTENT_COLLIDEPROPS ) )
|
|
{
|
|
Vector traceNormal;
|
|
traceNormal.Init();
|
|
bool bShouldCollide = true;
|
|
|
|
trace_t trace;
|
|
|
|
if ( flags & (FTENT_COLLIDEALL | FTENT_COLLIDEPROPS) )
|
|
{
|
|
Vector vPrevOrigin = m_vecPrevLocalOrigin;
|
|
|
|
if ( cl_fasttempentcollision.GetInt() > 0 && flags & FTENT_USEFASTCOLLISIONS )
|
|
{
|
|
if ( m_iLastCollisionFrame + cl_fasttempentcollision.GetInt() > gpGlobals->framecount )
|
|
{
|
|
bShouldCollide = false;
|
|
}
|
|
else
|
|
{
|
|
if ( m_vLastCollisionOrigin != vec3_origin )
|
|
{
|
|
vPrevOrigin = m_vLastCollisionOrigin;
|
|
}
|
|
|
|
m_iLastCollisionFrame = gpGlobals->framecount;
|
|
bShouldCollide = true;
|
|
}
|
|
}
|
|
|
|
if ( bShouldCollide == true )
|
|
{
|
|
// If the FTENT_COLLISIONGROUP flag is set, use the entity's collision group
|
|
int collisionGroup = COLLISION_GROUP_NONE;
|
|
if ( flags & FTENT_COLLISIONGROUP )
|
|
{
|
|
collisionGroup = GetCollisionGroup();
|
|
}
|
|
|
|
UTIL_TraceLine( vPrevOrigin, GetLocalOrigin(), MASK_SOLID, GetOwnerEntity(), collisionGroup, &trace );
|
|
|
|
if ( (flags & FTENT_COLLIDEPROPS) && trace.m_pEnt )
|
|
{
|
|
bool bIsDynamicProp = ( NULL != dynamic_cast<CDynamicProp *>( trace.m_pEnt ) );
|
|
bool bIsDoor = ( NULL != dynamic_cast<CBaseDoor *>( trace.m_pEnt ) );
|
|
if ( !bIsDynamicProp && !bIsDoor && !trace.m_pEnt->IsWorld() ) // Die on props, doors, and the world.
|
|
return true;
|
|
}
|
|
|
|
// Make sure it didn't bump into itself... (?!?)
|
|
if (
|
|
(trace.fraction != 1) &&
|
|
( (trace.DidHitWorld()) ||
|
|
(trace.m_pEnt != ClientEntityList().GetEnt(clientIndex)) )
|
|
)
|
|
{
|
|
traceFraction = trace.fraction;
|
|
VectorCopy( trace.plane.normal, traceNormal );
|
|
}
|
|
|
|
m_vLastCollisionOrigin = trace.endpos;
|
|
}
|
|
}
|
|
else if ( flags & FTENT_COLLIDEWORLD )
|
|
{
|
|
CTraceFilterWorldOnly traceFilter;
|
|
UTIL_TraceLine( m_vecPrevLocalOrigin, GetLocalOrigin(), MASK_SOLID, &traceFilter, &trace );
|
|
if ( trace.fraction != 1 )
|
|
{
|
|
traceFraction = trace.fraction;
|
|
VectorCopy( trace.plane.normal, traceNormal );
|
|
}
|
|
}
|
|
|
|
if ( traceFraction != 1 ) // Decent collision now, and damping works
|
|
{
|
|
float proj, damp;
|
|
SetLocalOrigin( trace.endpos );
|
|
|
|
// Damp velocity
|
|
damp = bounceFactor;
|
|
if ( flags & (FTENT_GRAVITY|FTENT_SLOWGRAVITY) )
|
|
{
|
|
damp *= 0.5;
|
|
if ( traceNormal[2] > 0.9 ) // Hit floor?
|
|
{
|
|
if ( m_vecTempEntVelocity[2] <= 0 && m_vecTempEntVelocity[2] >= gravity*3 )
|
|
{
|
|
damp = 0; // Stop
|
|
flags &= ~(FTENT_ROTATE|FTENT_GRAVITY|FTENT_SLOWGRAVITY|FTENT_COLLIDEWORLD|FTENT_SMOKETRAIL);
|
|
SetLocalAnglesDim( X_INDEX, 0 );
|
|
SetLocalAnglesDim( Z_INDEX, 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( flags & (FTENT_CHANGERENDERONCOLLIDE) )
|
|
{
|
|
m_RenderGroup = RENDER_GROUP_OTHER;
|
|
flags &= ~FTENT_CHANGERENDERONCOLLIDE;
|
|
}
|
|
|
|
if (hitSound)
|
|
{
|
|
tempents->PlaySound(this, damp);
|
|
}
|
|
|
|
if ( m_pszImpactEffect )
|
|
{
|
|
CEffectData data;
|
|
//data.m_vOrigin = newOrigin;
|
|
data.m_vOrigin = trace.endpos;
|
|
data.m_vStart = trace.startpos;
|
|
data.m_nSurfaceProp = trace.surface.surfaceProps;
|
|
data.m_nHitBox = trace.hitbox;
|
|
|
|
data.m_nDamageType = TEAM_UNASSIGNED;
|
|
|
|
IClientNetworkable *pClient = cl_entitylist->GetClientEntity( clientIndex );
|
|
|
|
if ( pClient )
|
|
{
|
|
C_BasePlayer *pPlayer = dynamic_cast<C_BasePlayer*>(pClient);
|
|
if( pPlayer )
|
|
{
|
|
data.m_nDamageType = pPlayer->GetTeamNumber();
|
|
}
|
|
}
|
|
|
|
if ( trace.m_pEnt )
|
|
{
|
|
data.m_hEntity = ClientEntityList().EntIndexToHandle( trace.m_pEnt->entindex() );
|
|
}
|
|
DispatchEffect( m_pszImpactEffect, data );
|
|
}
|
|
|
|
// Check for a collision and stop the particle system.
|
|
if ( flags & FTENT_CLIENTSIDEPARTICLES )
|
|
{
|
|
// Stop the emission of particles on collision - removed from the ClientEntityList on removal from the tempent pool.
|
|
ParticleProp()->StopEmission();
|
|
m_bParticleCollision = true;
|
|
}
|
|
|
|
if (flags & FTENT_COLLIDEKILL)
|
|
{
|
|
// die on impact
|
|
flags &= ~FTENT_FADEOUT;
|
|
die = gpGlobals->curtime;
|
|
}
|
|
else if ( flags & FTENT_ATTACHTOTARGET)
|
|
{
|
|
// If we've hit the world, just stop moving
|
|
if ( trace.DidHitWorld() && !( trace.surface.flags & SURF_SKY ) )
|
|
{
|
|
m_vecTempEntVelocity = vec3_origin;
|
|
m_vecTempEntAcceleration = vec3_origin;
|
|
|
|
// Remove movement flags so we don't keep tracing
|
|
flags &= ~(FTENT_COLLIDEALL | FTENT_COLLIDEWORLD);
|
|
}
|
|
else
|
|
{
|
|
// Couldn't attach to this entity. Die.
|
|
flags &= ~FTENT_FADEOUT;
|
|
die = gpGlobals->curtime;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Reflect velocity
|
|
if ( damp != 0 )
|
|
{
|
|
proj = ((Vector)m_vecTempEntVelocity).Dot(traceNormal);
|
|
VectorMA( m_vecTempEntVelocity, -proj*2, traceNormal, m_vecTempEntVelocity );
|
|
// Reflect rotation (fake)
|
|
SetLocalAnglesDim( Y_INDEX, -GetLocalAnglesDim( Y_INDEX ) );
|
|
}
|
|
|
|
if ( damp != 1 )
|
|
{
|
|
VectorScale( m_vecTempEntVelocity, damp, m_vecTempEntVelocity );
|
|
SetLocalAngles( GetLocalAngles() * 0.9 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if ( (flags & FTENT_FLICKER) && framenumber == m_nFlickerFrame )
|
|
{
|
|
dlight_t *dl = effects->CL_AllocDlight (LIGHT_INDEX_TE_DYNAMIC);
|
|
VectorCopy (GetLocalOrigin(), dl->origin);
|
|
dl->radius = 60;
|
|
dl->color.r = 255;
|
|
dl->color.g = 120;
|
|
dl->color.b = 0;
|
|
dl->die = gpGlobals->curtime + 0.01;
|
|
}
|
|
|
|
if ( flags & FTENT_SMOKETRAIL )
|
|
{
|
|
Assert( !"FIXME: Rework smoketrail to be client side\n" );
|
|
}
|
|
|
|
// add gravity if we didn't collide in this frame
|
|
if ( traceFraction == 1 )
|
|
{
|
|
if ( flags & FTENT_GRAVITY )
|
|
m_vecTempEntVelocity[2] += gravity;
|
|
else if ( flags & FTENT_SLOWGRAVITY )
|
|
m_vecTempEntVelocity[2] += gravitySlow;
|
|
}
|
|
|
|
if ( flags & FTENT_WINDBLOWN )
|
|
{
|
|
Vector vecWind;
|
|
GetWindspeedAtTime( gpGlobals->curtime, vecWind );
|
|
|
|
for ( int i = 0 ; i < 2 ; i++ )
|
|
{
|
|
if ( m_vecTempEntVelocity[i] < vecWind[i] )
|
|
{
|
|
m_vecTempEntVelocity[i] += ( frametime * TENT_WIND_ACCEL );
|
|
|
|
// clamp
|
|
if ( m_vecTempEntVelocity[i] > vecWind[i] )
|
|
m_vecTempEntVelocity[i] = vecWind[i];
|
|
}
|
|
else if (m_vecTempEntVelocity[i] > vecWind[i] )
|
|
{
|
|
m_vecTempEntVelocity[i] -= ( frametime * TENT_WIND_ACCEL );
|
|
|
|
// clamp.
|
|
if ( m_vecTempEntVelocity[i] < vecWind[i] )
|
|
m_vecTempEntVelocity[i] = vecWind[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Attach a particle effect to a temp entity.
|
|
//-----------------------------------------------------------------------------
|
|
CNewParticleEffect* C_LocalTempEntity::AddParticleEffect( const char *pszParticleEffect )
|
|
{
|
|
// Do we have a valid particle effect.
|
|
if ( !pszParticleEffect || ( pszParticleEffect[0] == '\0' ) )
|
|
return NULL;
|
|
|
|
// Check to see that we don't already have a particle effect.
|
|
if ( ( flags & FTENT_CLIENTSIDEPARTICLES ) != 0 )
|
|
return NULL;
|
|
|
|
// Add the entity to the ClientEntityList and create the particle system.
|
|
ClientEntityList().AddNonNetworkableEntity( this );
|
|
CNewParticleEffect* pEffect = ParticleProp()->Create( pszParticleEffect, PATTACH_ABSORIGIN_FOLLOW );
|
|
|
|
// Set the particle flag on the temp entity and save the name of the particle effect.
|
|
flags |= FTENT_CLIENTSIDEPARTICLES;
|
|
SetParticleEffect( pszParticleEffect );
|
|
|
|
return pEffect;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: This helper keeps track of batches of "breakmodels" so that they can all share the lighting origin
|
|
// of the first of the group (because the server sends down 15 chunks at a time, and rebuilding 15 light cache
|
|
// entries for a map with a lot of worldlights is really slow).
|
|
//-----------------------------------------------------------------------------
|
|
class CBreakableHelper
|
|
{
|
|
public:
|
|
void Insert( C_LocalTempEntity *entity, bool isSlave );
|
|
void Remove( C_LocalTempEntity *entity );
|
|
|
|
void Clear();
|
|
|
|
const Vector *GetLightingOrigin( C_LocalTempEntity *entity );
|
|
|
|
private:
|
|
|
|
// A context is the first master until the next one, which starts a new context
|
|
struct BreakableList_t
|
|
{
|
|
unsigned int context;
|
|
C_LocalTempEntity *entity;
|
|
};
|
|
|
|
CUtlLinkedList< BreakableList_t, unsigned short > m_Breakables;
|
|
unsigned int m_nCurrentContext;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Adds brekable to list, starts new context if needed
|
|
// Input : *entity -
|
|
// isSlave -
|
|
//-----------------------------------------------------------------------------
|
|
void CBreakableHelper::Insert( C_LocalTempEntity *entity, bool isSlave )
|
|
{
|
|
// A master signifies the start of a new run of broken objects
|
|
if ( !isSlave )
|
|
{
|
|
++m_nCurrentContext;
|
|
}
|
|
|
|
BreakableList_t entry;
|
|
entry.context = m_nCurrentContext;
|
|
entry.entity = entity;
|
|
|
|
m_Breakables.AddToTail( entry );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Removes all instances of entity in the list
|
|
// Input : *entity -
|
|
//-----------------------------------------------------------------------------
|
|
void CBreakableHelper::Remove( C_LocalTempEntity *entity )
|
|
{
|
|
for ( unsigned short i = m_Breakables.Head(); i != m_Breakables.InvalidIndex() ; )
|
|
{
|
|
unsigned short n = m_Breakables.Next( i );
|
|
|
|
if ( m_Breakables[ i ].entity == entity )
|
|
{
|
|
m_Breakables.Remove( i );
|
|
}
|
|
|
|
i = n;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: For a given breakable, find the "first" or head object and use it's current
|
|
// origin as the lighting origin for the entire group of objects
|
|
// Input : *entity -
|
|
// Output : const Vector
|
|
//-----------------------------------------------------------------------------
|
|
const Vector *CBreakableHelper::GetLightingOrigin( C_LocalTempEntity *entity )
|
|
{
|
|
unsigned int nCurContext = 0;
|
|
C_LocalTempEntity *head = NULL;
|
|
FOR_EACH_LL( m_Breakables, i )
|
|
{
|
|
BreakableList_t& e = m_Breakables[ i ];
|
|
|
|
if ( e.context != nCurContext )
|
|
{
|
|
nCurContext = e.context;
|
|
head = e.entity;
|
|
}
|
|
|
|
if ( e.entity == entity )
|
|
{
|
|
Assert( head );
|
|
return head ? &head->GetAbsOrigin() : NULL;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Wipe breakable helper list
|
|
// Input : -
|
|
//-----------------------------------------------------------------------------
|
|
void CBreakableHelper::Clear()
|
|
{
|
|
m_Breakables.RemoveAll();
|
|
m_nCurrentContext = 0;
|
|
}
|
|
|
|
static CBreakableHelper g_BreakableHelper;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: See if it's in the breakable helper list and, if so, remove
|
|
// Input : -
|
|
//-----------------------------------------------------------------------------
|
|
void C_LocalTempEntity::OnRemoveTempEntity()
|
|
{
|
|
g_BreakableHelper.Remove( this );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CTempEnts::CTempEnts( void ) :
|
|
m_TempEntsPool( ( MAX_TEMP_ENTITIES / 20 ), CUtlMemoryPool::GROW_SLOW )
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CTempEnts::~CTempEnts( void )
|
|
{
|
|
m_TempEntsPool.Clear();
|
|
m_TempEnts.RemoveAll();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create a fizz effect
|
|
// Input : *pent -
|
|
// modelIndex -
|
|
// density -
|
|
//-----------------------------------------------------------------------------
|
|
void CTempEnts::FizzEffect( C_BaseEntity *pent, int modelIndex, int density, int current )
|
|
{
|
|
C_LocalTempEntity *pTemp;
|
|
const model_t *model;
|
|
int i, width, depth, count, frameCount;
|
|
float maxHeight, speed, xspeed, yspeed;
|
|
Vector origin;
|
|
Vector mins, maxs;
|
|
|
|
if ( !pent->GetModel() || !modelIndex )
|
|
return;
|
|
|
|
model = modelinfo->GetModel( modelIndex );
|
|
if ( !model )
|
|
return;
|
|
|
|
count = density + 1;
|
|
density = count * 3 + 6;
|
|
|
|
modelinfo->GetModelBounds( pent->GetModel(), mins, maxs );
|
|
|
|
maxHeight = maxs[2] - mins[2];
|
|
width = maxs[0] - mins[0];
|
|
depth = maxs[1] - mins[1];
|
|
speed = current;
|
|
|
|
SinCos( pent->GetLocalAngles()[1]*M_PI/180, &yspeed, &xspeed );
|
|
xspeed *= speed;
|
|
yspeed *= speed;
|
|
frameCount = modelinfo->GetModelFrameCount( model );
|
|
|
|
for (i=0 ; i<count ; i++)
|
|
{
|
|
origin[0] = mins[0] + random->RandomInt(0,width-1);
|
|
origin[1] = mins[1] + random->RandomInt(0,depth-1);
|
|
origin[2] = mins[2];
|
|
pTemp = TempEntAlloc( origin, model );
|
|
if (!pTemp)
|
|
return;
|
|
|
|
pTemp->flags |= FTENT_SINEWAVE;
|
|
|
|
pTemp->x = origin[0];
|
|
pTemp->y = origin[1];
|
|
|
|
float zspeed = random->RandomInt(80,140);
|
|
pTemp->SetVelocity( Vector(xspeed, yspeed, zspeed) );
|
|
pTemp->die = gpGlobals->curtime + (maxHeight / zspeed) - 0.1;
|
|
pTemp->m_flFrame = random->RandomInt(0,frameCount-1);
|
|
// Set sprite scale
|
|
pTemp->m_flSpriteScale = 1.0 / random->RandomFloat(2,5);
|
|
pTemp->SetRenderMode( kRenderTransAlpha );
|
|
pTemp->SetRenderColorA( 255 );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create bubbles
|
|
// Input : *mins -
|
|
// *maxs -
|
|
// height -
|
|
// modelIndex -
|
|
// count -
|
|
// speed -
|
|
//-----------------------------------------------------------------------------
|
|
void CTempEnts::Bubbles( const Vector &mins, const Vector &maxs, float height, int modelIndex, int count, float speed )
|
|
{
|
|
C_LocalTempEntity *pTemp;
|
|
const model_t *model;
|
|
int i, frameCount;
|
|
float sine, cosine;
|
|
Vector origin;
|
|
|
|
if ( !modelIndex )
|
|
return;
|
|
|
|
model = modelinfo->GetModel( modelIndex );
|
|
if ( !model )
|
|
return;
|
|
|
|
frameCount = modelinfo->GetModelFrameCount( model );
|
|
|
|
for (i=0 ; i<count ; i++)
|
|
{
|
|
origin[0] = random->RandomInt( mins[0], maxs[0] );
|
|
origin[1] = random->RandomInt( mins[1], maxs[1] );
|
|
origin[2] = random->RandomInt( mins[2], maxs[2] );
|
|
pTemp = TempEntAlloc( origin, model );
|
|
if (!pTemp)
|
|
return;
|
|
|
|
pTemp->flags |= FTENT_SINEWAVE;
|
|
|
|
pTemp->x = origin[0];
|
|
pTemp->y = origin[1];
|
|
SinCos( random->RandomInt( -3, 3 ), &sine, &cosine );
|
|
|
|
float zspeed = random->RandomInt(80,140);
|
|
pTemp->SetVelocity( Vector(speed * cosine, speed * sine, zspeed) );
|
|
pTemp->die = gpGlobals->curtime + ((height - (origin[2] - mins[2])) / zspeed) - 0.1;
|
|
pTemp->m_flFrame = random->RandomInt( 0, frameCount-1 );
|
|
|
|
// Set sprite scale
|
|
pTemp->m_flSpriteScale = 1.0 / random->RandomFloat(4,16);
|
|
pTemp->SetRenderMode( kRenderTransAlpha );
|
|
|
|
pTemp->SetRenderColor( 255, 255, 255, 192 );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create bubble trail
|
|
// Input : *start -
|
|
// *end -
|
|
// height -
|
|
// modelIndex -
|
|
// count -
|
|
// speed -
|
|
//-----------------------------------------------------------------------------
|
|
void CTempEnts::BubbleTrail( const Vector &start, const Vector &end, float flWaterZ, int modelIndex, int count, float speed )
|
|
{
|
|
C_LocalTempEntity *pTemp;
|
|
const model_t *model;
|
|
int i, frameCount;
|
|
float dist, angle;
|
|
Vector origin;
|
|
|
|
if ( !modelIndex )
|
|
return;
|
|
|
|
model = modelinfo->GetModel( modelIndex );
|
|
if ( !model )
|
|
return;
|
|
|
|
frameCount = modelinfo->GetModelFrameCount( model );
|
|
|
|
for (i=0 ; i<count ; i++)
|
|
{
|
|
dist = random->RandomFloat( 0, 1.0 );
|
|
VectorLerp( start, end, dist, origin );
|
|
pTemp = TempEntAlloc( origin, model );
|
|
if (!pTemp)
|
|
return;
|
|
|
|
pTemp->flags |= FTENT_SINEWAVE;
|
|
|
|
pTemp->x = origin[0];
|
|
pTemp->y = origin[1];
|
|
angle = random->RandomInt( -3, 3 );
|
|
|
|
float zspeed = random->RandomInt(80,140);
|
|
pTemp->SetVelocity( Vector(speed * cos(angle), speed * sin(angle), zspeed) );
|
|
pTemp->die = gpGlobals->curtime + ((flWaterZ - origin[2]) / zspeed) - 0.1;
|
|
pTemp->m_flFrame = random->RandomInt(0,frameCount-1);
|
|
// Set sprite scale
|
|
pTemp->m_flSpriteScale = 1.0 / random->RandomFloat(4,8);
|
|
pTemp->SetRenderMode( kRenderTransAlpha );
|
|
|
|
pTemp->SetRenderColor( 255, 255, 255, 192 );
|
|
}
|
|
}
|
|
|
|
#define SHARD_VOLUME 12.0 // on shard ever n^3 units
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Only used by BreakModel temp ents for now. Allows them to share a single
|
|
// lighting origin amongst a group of objects. If the master object goes away, the next object
|
|
// in the group becomes the new lighting origin, etc.
|
|
// Input : *entity -
|
|
// flags -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int BreakModelDrawHelper( C_LocalTempEntity *entity, int flags )
|
|
{
|
|
ModelRenderInfo_t sInfo;
|
|
sInfo.flags = flags;
|
|
sInfo.pRenderable = entity;
|
|
sInfo.instance = MODEL_INSTANCE_INVALID;
|
|
sInfo.entity_index = entity->index;
|
|
sInfo.pModel = entity->GetModel();
|
|
sInfo.origin = entity->GetRenderOrigin();
|
|
sInfo.angles = entity->GetRenderAngles();
|
|
sInfo.skin = entity->m_nSkin;
|
|
sInfo.body = entity->m_nBody;
|
|
sInfo.hitboxset = entity->m_nHitboxSet;
|
|
|
|
// This is the main change, look up a lighting origin from the helper singleton
|
|
const Vector *pLightingOrigin = g_BreakableHelper.GetLightingOrigin( entity );
|
|
if ( pLightingOrigin )
|
|
{
|
|
sInfo.pLightingOrigin = pLightingOrigin;
|
|
}
|
|
|
|
int drawn = modelrender->DrawModelEx( sInfo );
|
|
return drawn;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create model shattering shards
|
|
// Input : *pos -
|
|
// *size -
|
|
// *dir -
|
|
// random -
|
|
// life -
|
|
// count -
|
|
// modelIndex -
|
|
// flags -
|
|
//-----------------------------------------------------------------------------
|
|
void CTempEnts::BreakModel( const Vector &pos, const QAngle &angles, const Vector &size, const Vector &dir,
|
|
float randRange, float life, int count, int modelIndex, char flags)
|
|
{
|
|
int i, frameCount;
|
|
C_LocalTempEntity *pTemp;
|
|
const model_t *pModel;
|
|
|
|
if (!modelIndex)
|
|
return;
|
|
|
|
pModel = modelinfo->GetModel( modelIndex );
|
|
if ( !pModel )
|
|
return;
|
|
|
|
// See g_BreakableHelper above for notes...
|
|
bool isSlave = ( flags & BREAK_SLAVE ) ? true : false;
|
|
|
|
frameCount = modelinfo->GetModelFrameCount( pModel );
|
|
|
|
if (count == 0)
|
|
{
|
|
// assume surface (not volume)
|
|
count = (size[0] * size[1] + size[1] * size[2] + size[2] * size[0])/(3 * SHARD_VOLUME * SHARD_VOLUME);
|
|
}
|
|
|
|
if ( count > func_break_max_pieces.GetInt() )
|
|
{
|
|
count = func_break_max_pieces.GetInt();
|
|
}
|
|
|
|
matrix3x4_t transform;
|
|
AngleMatrix( angles, pos, transform );
|
|
for ( i = 0; i < count; i++ )
|
|
{
|
|
Vector vecLocalSpot, vecSpot;
|
|
|
|
// fill up the box with stuff
|
|
vecLocalSpot[0] = random->RandomFloat(-0.5,0.5) * size[0];
|
|
vecLocalSpot[1] = random->RandomFloat(-0.5,0.5) * size[1];
|
|
vecLocalSpot[2] = random->RandomFloat(-0.5,0.5) * size[2];
|
|
VectorTransform( vecLocalSpot, transform, vecSpot );
|
|
|
|
pTemp = TempEntAlloc(vecSpot, pModel);
|
|
|
|
if (!pTemp)
|
|
return;
|
|
|
|
// keep track of break_type, so we know how to play sound on collision
|
|
pTemp->hitSound = flags;
|
|
|
|
if ( modelinfo->GetModelType( pModel ) == mod_sprite )
|
|
{
|
|
pTemp->m_flFrame = random->RandomInt(0,frameCount-1);
|
|
}
|
|
else if ( modelinfo->GetModelType( pModel ) == mod_studio )
|
|
{
|
|
pTemp->m_nBody = random->RandomInt(0,frameCount-1);
|
|
}
|
|
|
|
pTemp->flags |= FTENT_COLLIDEWORLD | FTENT_FADEOUT | FTENT_SLOWGRAVITY;
|
|
|
|
if ( random->RandomInt(0,255) < 200 )
|
|
{
|
|
pTemp->flags |= FTENT_ROTATE;
|
|
pTemp->m_vecTempEntAngVelocity[0] = random->RandomFloat(-256,255);
|
|
pTemp->m_vecTempEntAngVelocity[1] = random->RandomFloat(-256,255);
|
|
pTemp->m_vecTempEntAngVelocity[2] = random->RandomFloat(-256,255);
|
|
}
|
|
|
|
if ( (random->RandomInt(0,255) < 100 ) && (flags & BREAK_SMOKE) )
|
|
{
|
|
pTemp->flags |= FTENT_SMOKETRAIL;
|
|
}
|
|
|
|
if ((flags & BREAK_GLASS) || (flags & BREAK_TRANS))
|
|
{
|
|
pTemp->SetRenderMode( kRenderTransTexture );
|
|
pTemp->SetRenderColorA( 128 );
|
|
pTemp->tempent_renderamt = 128;
|
|
pTemp->bounceFactor = 0.3f;
|
|
}
|
|
else
|
|
{
|
|
pTemp->SetRenderMode( kRenderNormal );
|
|
pTemp->tempent_renderamt = 255; // Set this for fadeout
|
|
}
|
|
|
|
pTemp->SetVelocity( Vector( dir[0] + random->RandomFloat(-randRange,randRange),
|
|
dir[1] + random->RandomFloat(-randRange,randRange),
|
|
dir[2] + random->RandomFloat( 0,randRange) ) );
|
|
|
|
pTemp->die = gpGlobals->curtime + life + random->RandomFloat(0,1); // Add an extra 0-1 secs of life
|
|
|
|
// We use a special rendering function because these objects will want to share their lighting
|
|
// origin with the first/master object. Prevents a huge spike in Light Cache building in maps
|
|
// with many worldlights.
|
|
pTemp->SetDrawHelper( BreakModelDrawHelper );
|
|
g_BreakableHelper.Insert( pTemp, isSlave );
|
|
}
|
|
}
|
|
|
|
void CTempEnts::PhysicsProp( int modelindex, int skin, const Vector& pos, const QAngle &angles, const Vector& vel, int flags, int effects )
|
|
{
|
|
C_PhysPropClientside *pEntity = C_PhysPropClientside::CreateNew();
|
|
|
|
if ( !pEntity )
|
|
return;
|
|
|
|
const model_t *model = modelinfo->GetModel( modelindex );
|
|
|
|
if ( !model )
|
|
{
|
|
DevMsg("CTempEnts::PhysicsProp: model index %i not found\n", modelindex );
|
|
return;
|
|
}
|
|
|
|
pEntity->SetModelName( modelinfo->GetModelName(model) );
|
|
pEntity->m_nSkin = skin;
|
|
pEntity->SetAbsOrigin( pos );
|
|
pEntity->SetAbsAngles( angles );
|
|
pEntity->SetPhysicsMode( PHYSICS_MULTIPLAYER_CLIENTSIDE );
|
|
pEntity->SetEffects( effects );
|
|
|
|
if ( !pEntity->Initialize() )
|
|
{
|
|
pEntity->Release();
|
|
return;
|
|
}
|
|
|
|
IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
|
|
|
|
if( pPhysicsObject )
|
|
{
|
|
pPhysicsObject->AddVelocity( &vel, NULL );
|
|
}
|
|
else
|
|
{
|
|
// failed to create a physics object
|
|
pEntity->Release();
|
|
return;
|
|
}
|
|
|
|
if ( flags & 1 )
|
|
{
|
|
pEntity->SetHealth( 0 );
|
|
pEntity->Break();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create a clientside projectile
|
|
// Input : vecOrigin -
|
|
// vecVelocity -
|
|
// modelindex -
|
|
// lifetime -
|
|
// *pOwner -
|
|
//-----------------------------------------------------------------------------
|
|
C_LocalTempEntity *CTempEnts::ClientProjectile( const Vector& vecOrigin, const Vector& vecVelocity, const Vector& vecAcceleration, int modelIndex, int lifetime, CBaseEntity *pOwner, const char *pszImpactEffect, const char *pszParticleEffect )
|
|
{
|
|
C_LocalTempEntity *pTemp;
|
|
const model_t *model;
|
|
|
|
if ( !modelIndex )
|
|
return NULL;
|
|
|
|
model = modelinfo->GetModel( modelIndex );
|
|
if ( !model )
|
|
{
|
|
Warning("ClientProjectile: No model %d!\n", modelIndex);
|
|
return NULL;
|
|
}
|
|
|
|
pTemp = TempEntAlloc( vecOrigin, model );
|
|
if (!pTemp)
|
|
return NULL;
|
|
|
|
pTemp->SetVelocity( vecVelocity );
|
|
pTemp->SetAcceleration( vecAcceleration );
|
|
QAngle angles;
|
|
VectorAngles( vecVelocity, angles );
|
|
pTemp->SetAbsAngles( angles );
|
|
pTemp->SetAbsOrigin( vecOrigin );
|
|
pTemp->die = gpGlobals->curtime + lifetime;
|
|
pTemp->flags = FTENT_COLLIDEALL | FTENT_ATTACHTOTARGET | FTENT_ALIGNTOMOTION;
|
|
pTemp->clientIndex = ( pOwner != NULL ) ? pOwner->entindex() : 0;
|
|
pTemp->SetOwnerEntity( pOwner );
|
|
pTemp->SetImpactEffect( pszImpactEffect );
|
|
if ( pszParticleEffect )
|
|
{
|
|
// Add the entity to the ClientEntityList and create the particle system.
|
|
ClientEntityList().AddNonNetworkableEntity( pTemp );
|
|
pTemp->ParticleProp()->Create( pszParticleEffect, PATTACH_ABSORIGIN_FOLLOW );
|
|
|
|
// Set the particle flag on the temp entity and save the name of the particle effect.
|
|
pTemp->flags |= FTENT_CLIENTSIDEPARTICLES;
|
|
pTemp->SetParticleEffect( pszParticleEffect );
|
|
}
|
|
return pTemp;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create sprite TE
|
|
// Input : *pos -
|
|
// *dir -
|
|
// scale -
|
|
// modelIndex -
|
|
// rendermode -
|
|
// renderfx -
|
|
// a -
|
|
// life -
|
|
// flags -
|
|
// Output : C_LocalTempEntity
|
|
//-----------------------------------------------------------------------------
|
|
C_LocalTempEntity *CTempEnts::TempSprite( const Vector &pos, const Vector &dir, float scale, int modelIndex, int rendermode, int renderfx, float a, float life, int flags, const Vector &normal )
|
|
{
|
|
C_LocalTempEntity *pTemp;
|
|
const model_t *model;
|
|
int frameCount;
|
|
|
|
if ( !modelIndex )
|
|
return NULL;
|
|
|
|
model = modelinfo->GetModel( modelIndex );
|
|
if ( !model )
|
|
{
|
|
Warning("No model %d!\n", modelIndex);
|
|
return NULL;
|
|
}
|
|
|
|
frameCount = modelinfo->GetModelFrameCount( model );
|
|
|
|
pTemp = TempEntAlloc( pos, model );
|
|
if (!pTemp)
|
|
return NULL;
|
|
|
|
pTemp->m_flFrameMax = frameCount - 1;
|
|
pTemp->m_flFrameRate = 10;
|
|
pTemp->SetRenderMode( (RenderMode_t)rendermode );
|
|
pTemp->m_nRenderFX = renderfx;
|
|
pTemp->m_flSpriteScale = scale;
|
|
pTemp->tempent_renderamt = a * 255;
|
|
pTemp->m_vecNormal = normal;
|
|
pTemp->SetRenderColor( 255, 255, 255, a * 255 );
|
|
|
|
pTemp->flags |= flags;
|
|
|
|
pTemp->SetVelocity( dir );
|
|
pTemp->SetLocalOrigin( pos );
|
|
if ( life )
|
|
pTemp->die = gpGlobals->curtime + life;
|
|
else
|
|
pTemp->die = gpGlobals->curtime + (frameCount * 0.1) + 1;
|
|
|
|
pTemp->m_flFrame = 0;
|
|
return pTemp;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Spray sprite
|
|
// Input : *pos -
|
|
// *dir -
|
|
// modelIndex -
|
|
// count -
|
|
// speed -
|
|
// iRand -
|
|
//-----------------------------------------------------------------------------
|
|
void CTempEnts::Sprite_Spray( const Vector &pos, const Vector &dir, int modelIndex, int count, int speed, int iRand )
|
|
{
|
|
C_LocalTempEntity *pTemp;
|
|
const model_t *pModel;
|
|
float noise;
|
|
float znoise;
|
|
int frameCount;
|
|
int i;
|
|
|
|
noise = (float)iRand / 100;
|
|
|
|
// more vertical displacement
|
|
znoise = noise * 1.5;
|
|
|
|
if ( znoise > 1 )
|
|
{
|
|
znoise = 1;
|
|
}
|
|
|
|
pModel = modelinfo->GetModel( modelIndex );
|
|
|
|
if ( !pModel )
|
|
{
|
|
Warning("No model %d!\n", modelIndex);
|
|
return;
|
|
}
|
|
|
|
frameCount = modelinfo->GetModelFrameCount( pModel ) - 1;
|
|
|
|
for ( i = 0; i < count; i++ )
|
|
{
|
|
pTemp = TempEntAlloc( pos, pModel );
|
|
if (!pTemp)
|
|
return;
|
|
|
|
pTemp->SetRenderMode( kRenderTransAlpha );
|
|
pTemp->SetRenderColor( 255, 255, 255, 255 );
|
|
pTemp->tempent_renderamt = 255;
|
|
pTemp->m_nRenderFX = kRenderFxNoDissipation;
|
|
//pTemp->scale = random->RandomFloat( 0.1, 0.25 );
|
|
pTemp->m_flSpriteScale = 0.5;
|
|
pTemp->flags |= FTENT_FADEOUT | FTENT_SLOWGRAVITY;
|
|
pTemp->fadeSpeed = 2.0;
|
|
|
|
// make the spittle fly the direction indicated, but mix in some noise.
|
|
Vector velocity;
|
|
velocity.x = dir[ 0 ] + random->RandomFloat ( -noise, noise );
|
|
velocity.y = dir[ 1 ] + random->RandomFloat ( -noise, noise );
|
|
velocity.z = dir[ 2 ] + random->RandomFloat ( 0, znoise );
|
|
velocity *= random->RandomFloat( (speed * 0.8), (speed * 1.2) );
|
|
pTemp->SetVelocity( velocity );
|
|
|
|
pTemp->SetLocalOrigin( pos );
|
|
|
|
pTemp->die = gpGlobals->curtime + 0.35;
|
|
|
|
pTemp->m_flFrame = random->RandomInt( 0, frameCount );
|
|
}
|
|
}
|
|
|
|
void CTempEnts::Sprite_Trail( const Vector &vecStart, const Vector &vecEnd, int modelIndex, int nCount, float flLife, float flSize, float flAmplitude, int nRenderamt, float flSpeed )
|
|
{
|
|
C_LocalTempEntity *pTemp;
|
|
const model_t *pModel;
|
|
int flFrameCount;
|
|
|
|
pModel = modelinfo->GetModel( modelIndex );
|
|
|
|
if ( !pModel )
|
|
{
|
|
Warning("No model %d!\n", modelIndex);
|
|
return;
|
|
}
|
|
|
|
flFrameCount = modelinfo->GetModelFrameCount( pModel );
|
|
|
|
Vector vecDelta;
|
|
VectorSubtract( vecEnd, vecStart, vecDelta );
|
|
|
|
Vector vecDir;
|
|
VectorCopy( vecDelta, vecDir );
|
|
VectorNormalize( vecDir );
|
|
|
|
flAmplitude /= 256.0;
|
|
|
|
for ( int i = 0 ; i < nCount; i++ )
|
|
{
|
|
Vector vecPos;
|
|
|
|
// Be careful of divide by 0 when using 'count' here...
|
|
if ( i == 0 )
|
|
{
|
|
VectorMA( vecStart, 0, vecDelta, vecPos );
|
|
}
|
|
else
|
|
{
|
|
VectorMA( vecStart, i / (nCount - 1.0), vecDelta, vecPos );
|
|
}
|
|
|
|
pTemp = TempEntAlloc( vecPos, pModel );
|
|
if (!pTemp)
|
|
return;
|
|
|
|
pTemp->flags |= FTENT_COLLIDEWORLD | FTENT_SPRCYCLE | FTENT_FADEOUT | FTENT_SLOWGRAVITY;
|
|
|
|
Vector vecVel = vecDir * flSpeed;
|
|
vecVel.x += random->RandomInt( -127,128 ) * flAmplitude;
|
|
vecVel.y += random->RandomInt( -127,128 ) * flAmplitude;
|
|
vecVel.z += random->RandomInt( -127,128 ) * flAmplitude;
|
|
pTemp->SetVelocity( vecVel );
|
|
pTemp->SetLocalOrigin( vecPos );
|
|
|
|
pTemp->m_flSpriteScale = flSize;
|
|
pTemp->SetRenderMode( kRenderGlow );
|
|
pTemp->m_nRenderFX = kRenderFxNoDissipation;
|
|
pTemp->tempent_renderamt = nRenderamt;
|
|
pTemp->SetRenderColor( 255, 255, 255 );
|
|
|
|
pTemp->m_flFrame = random->RandomInt( 0, flFrameCount - 1 );
|
|
pTemp->m_flFrameMax = flFrameCount - 1;
|
|
pTemp->die = gpGlobals->curtime + flLife + random->RandomFloat( 0, 4 );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Attaches entity to player
|
|
// Input : client -
|
|
// modelIndex -
|
|
// zoffset -
|
|
// life -
|
|
//-----------------------------------------------------------------------------
|
|
void CTempEnts::AttachTentToPlayer( int client, int modelIndex, float zoffset, float life )
|
|
{
|
|
C_LocalTempEntity *pTemp;
|
|
const model_t *pModel;
|
|
Vector position;
|
|
int frameCount;
|
|
|
|
if ( client <= 0 || client > gpGlobals->maxClients )
|
|
{
|
|
Warning("Bad client in AttachTentToPlayer()!\n");
|
|
return;
|
|
}
|
|
|
|
IClientEntity *clientClass = cl_entitylist->GetClientEntity( client );
|
|
if ( !clientClass )
|
|
{
|
|
Warning("Couldn't get IClientEntity for %i\n", client );
|
|
return;
|
|
}
|
|
|
|
pModel = modelinfo->GetModel( modelIndex );
|
|
|
|
if ( !pModel )
|
|
{
|
|
Warning("No model %d!\n", modelIndex);
|
|
return;
|
|
}
|
|
|
|
VectorCopy( clientClass->GetAbsOrigin(), position );
|
|
position[ 2 ] += zoffset;
|
|
|
|
pTemp = TempEntAllocHigh( position, pModel );
|
|
if (!pTemp)
|
|
{
|
|
Warning("No temp ent.\n");
|
|
return;
|
|
}
|
|
|
|
pTemp->SetRenderMode( kRenderNormal );
|
|
pTemp->SetRenderColorA( 255 );
|
|
pTemp->tempent_renderamt = 255;
|
|
pTemp->m_nRenderFX = kRenderFxNoDissipation;
|
|
|
|
pTemp->clientIndex = client;
|
|
pTemp->tentOffset[ 0 ] = 0;
|
|
pTemp->tentOffset[ 1 ] = 0;
|
|
pTemp->tentOffset[ 2 ] = zoffset;
|
|
pTemp->die = gpGlobals->curtime + life;
|
|
pTemp->flags |= FTENT_PLYRATTACHMENT | FTENT_PERSIST;
|
|
|
|
// is the model a sprite?
|
|
if ( modelinfo->GetModelType( pTemp->GetModel() ) == mod_sprite )
|
|
{
|
|
frameCount = modelinfo->GetModelFrameCount( pModel );
|
|
pTemp->m_flFrameMax = frameCount - 1;
|
|
pTemp->flags |= FTENT_SPRANIMATE | FTENT_SPRANIMATELOOP;
|
|
pTemp->m_flFrameRate = 10;
|
|
}
|
|
else
|
|
{
|
|
// no animation support for attached clientside studio models.
|
|
pTemp->m_flFrameMax = 0;
|
|
}
|
|
|
|
pTemp->m_flFrame = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Detach entity from player
|
|
//-----------------------------------------------------------------------------
|
|
void CTempEnts::KillAttachedTents( int client )
|
|
{
|
|
if ( client <= 0 || client > gpGlobals->maxClients )
|
|
{
|
|
Warning("Bad client in KillAttachedTents()!\n");
|
|
return;
|
|
}
|
|
|
|
FOR_EACH_LL( m_TempEnts, i )
|
|
{
|
|
C_LocalTempEntity *pTemp = m_TempEnts[ i ];
|
|
|
|
if ( pTemp->flags & FTENT_PLYRATTACHMENT )
|
|
{
|
|
// this TENT is player attached.
|
|
// if it is attached to this client, set it to die instantly.
|
|
if ( pTemp->clientIndex == client )
|
|
{
|
|
pTemp->die = gpGlobals->curtime;// good enough, it will die on next tent update.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create ricochet sprite
|
|
// Input : *pos -
|
|
// *pmodel -
|
|
// duration -
|
|
// scale -
|
|
//-----------------------------------------------------------------------------
|
|
void CTempEnts::RicochetSprite( const Vector &pos, model_t *pmodel, float duration, float scale )
|
|
{
|
|
C_LocalTempEntity *pTemp;
|
|
|
|
pTemp = TempEntAlloc( pos, pmodel );
|
|
if (!pTemp)
|
|
return;
|
|
|
|
pTemp->SetRenderMode( kRenderGlow );
|
|
pTemp->m_nRenderFX = kRenderFxNoDissipation;
|
|
pTemp->SetRenderColorA( 200 );
|
|
pTemp->tempent_renderamt = 200;
|
|
pTemp->m_flSpriteScale = scale;
|
|
pTemp->flags = FTENT_FADEOUT;
|
|
|
|
pTemp->SetVelocity( vec3_origin );
|
|
|
|
pTemp->SetLocalOrigin( pos );
|
|
|
|
pTemp->fadeSpeed = 8;
|
|
pTemp->die = gpGlobals->curtime;
|
|
|
|
pTemp->m_flFrame = 0;
|
|
pTemp->SetLocalAnglesDim( Z_INDEX, 45 * random->RandomInt( 0, 7 ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create blood sprite
|
|
// Input : *org -
|
|
// colorindex -
|
|
// modelIndex -
|
|
// modelIndex2 -
|
|
// size -
|
|
//-----------------------------------------------------------------------------
|
|
void CTempEnts::BloodSprite( const Vector &org, int r, int g, int b, int a, int modelIndex, int modelIndex2, float size )
|
|
{
|
|
const model_t *model;
|
|
|
|
//Validate the model first
|
|
if ( modelIndex && (model = modelinfo->GetModel( modelIndex ) ) != NULL )
|
|
{
|
|
C_LocalTempEntity *pTemp;
|
|
int frameCount = modelinfo->GetModelFrameCount( model );
|
|
color32 impactcolor = { (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;
|
|
}
|
|
|
|
|
|
}
|
|
|