mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-28 17:13:03 +00:00
1049 lines
31 KiB
C++
1049 lines
31 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
#include "cbase.h"
|
|
#include "c_ai_basenpc.h"
|
|
#include "c_te_particlesystem.h"
|
|
#include "fx.h"
|
|
#include "fx_sparks.h"
|
|
#include "c_tracer.h"
|
|
#include "clientsideeffects.h"
|
|
#include "iefx.h"
|
|
#include "dlight.h"
|
|
#include "bone_setup.h"
|
|
#include "c_rope.h"
|
|
#include "fx_line.h"
|
|
#include "c_sprite.h"
|
|
#include "view.h"
|
|
#include "view_scene.h"
|
|
#include "materialsystem/imaterialvar.h"
|
|
#include "simple_keys.h"
|
|
#include "fx_envelope.h"
|
|
#include "iclientvehicle.h"
|
|
#include "engine/ivdebugoverlay.h"
|
|
#include "particles_localspace.h"
|
|
#include "dlight.h"
|
|
#include "iefx.h"
|
|
#include "c_te_effect_dispatch.h"
|
|
#include "tier0/vprof.h"
|
|
#include "clienteffectprecachesystem.h"
|
|
#include <bitbuf.h>
|
|
#include "fx_water.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#define STRIDER_MSG_BIG_SHOT 1
|
|
#define STRIDER_MSG_STREAKS 2
|
|
#define STRIDER_MSG_DEAD 3
|
|
|
|
#define STOMP_IK_SLOT 11
|
|
const int NUM_STRIDER_IK_TARGETS = 6;
|
|
|
|
const float STRIDERFX_BIG_SHOT_TIME = 1.25f;
|
|
const float STRIDERFX_END_ALL_TIME = 4.0f;
|
|
|
|
class C_StriderFX : public C_EnvelopeFX
|
|
{
|
|
public:
|
|
typedef C_EnvelopeFX BaseClass;
|
|
|
|
C_StriderFX();
|
|
~C_StriderFX()
|
|
{
|
|
EffectShutdown();
|
|
}
|
|
|
|
|
|
void Update( C_BaseEntity *pOwner, const Vector &targetPos );
|
|
|
|
// Returns the bounds relative to the origin (render bounds)
|
|
virtual void GetRenderBounds( Vector& mins, Vector& maxs )
|
|
{
|
|
ClearBounds( mins, maxs );
|
|
AddPointToBounds( m_worldPosition, mins, maxs );
|
|
AddPointToBounds( m_targetPosition, mins, maxs );
|
|
mins -= GetRenderOrigin();
|
|
maxs -= GetRenderOrigin();
|
|
}
|
|
|
|
virtual void EffectInit( int entityIndex, int attachment )
|
|
{
|
|
m_limitHitTime = 0;
|
|
BaseClass::EffectInit( entityIndex, attachment );
|
|
}
|
|
virtual void EffectShutdown( void )
|
|
{
|
|
m_limitHitTime = 0;
|
|
BaseClass::EffectShutdown();
|
|
}
|
|
|
|
virtual int DrawModel( int flags );
|
|
virtual void LimitTime( float tmax )
|
|
{
|
|
float dt = tmax - m_t;
|
|
if ( dt < 0 )
|
|
{
|
|
dt = 0;
|
|
}
|
|
m_limitHitTime = gpGlobals->curtime + dt;
|
|
BaseClass::LimitTime( tmax );
|
|
}
|
|
|
|
C_BaseEntity *m_pOwner;
|
|
Vector m_targetPosition;
|
|
Vector m_beamEndPosition;
|
|
pixelvis_handle_t m_queryHandleGun;
|
|
pixelvis_handle_t m_queryHandleBeamEnd;
|
|
float m_limitHitTime;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
class C_Strider : public C_AI_BaseNPC
|
|
{
|
|
DECLARE_CLASS( C_Strider, C_AI_BaseNPC );
|
|
public:
|
|
DECLARE_CLIENTCLASS();
|
|
DECLARE_INTERPOLATION();
|
|
|
|
|
|
C_Strider();
|
|
virtual ~C_Strider();
|
|
|
|
// model specific
|
|
virtual void ReceiveMessage( int classID, bf_read &msg );
|
|
virtual void CalculateIKLocks( float currentTime )
|
|
{
|
|
// NOTE: All strider IK is solved on the server, enable this to do it client-side
|
|
//BaseClass::CalculateIKLocks( currentTime );
|
|
if ( m_pIk && m_pIk->m_target.Count() )
|
|
{
|
|
Assert(m_pIk->m_target.Count() > STOMP_IK_SLOT);
|
|
// HACKHACK: Hardcoded 11??? Not a cleaner way to do this
|
|
CIKTarget &target = m_pIk->m_target[STOMP_IK_SLOT];
|
|
target.SetPos( m_vecHitPos );
|
|
// target.latched.pos = m_vecHitPos;
|
|
|
|
for ( int i = 0; i < NUM_STRIDER_IK_TARGETS; i++ )
|
|
{
|
|
CIKTarget &target = m_pIk->m_target[i];
|
|
target.SetPos( m_vecIKTarget[i] );
|
|
#if 0
|
|
debugoverlay->AddBoxOverlay( m_vecIKTarget[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2), QAngle( 0, 0, 0 ), (int)255*m_pIk->m_target[i].est.latched, 0, 0, 0, 0 );
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void OnDataChanged( DataUpdateType_t updateType );
|
|
virtual void GetRenderBounds( Vector& theMins, Vector& theMaxs );
|
|
virtual void ClientThink();
|
|
|
|
private:
|
|
C_Strider( const C_Strider & );
|
|
C_StriderFX m_cannonFX;
|
|
Vector m_vecHitPos;
|
|
Vector m_vecIKTarget[NUM_STRIDER_IK_TARGETS];
|
|
CInterpolatedVar< Vector > m_iv_vecHitPos;
|
|
CInterpolatedVarArray< Vector, NUM_STRIDER_IK_TARGETS > m_iv_vecIKTarget;
|
|
Vector m_vecRenderMins;
|
|
Vector m_vecRenderMaxs;
|
|
|
|
float m_flNextRopeCutTime;
|
|
};
|
|
|
|
IMPLEMENT_CLIENTCLASS_DT(C_Strider, DT_NPC_Strider, CNPC_Strider)
|
|
RecvPropVector(RECVINFO(m_vecHitPos)),
|
|
RecvPropVector(RECVINFO(m_vecIKTarget[0])),
|
|
RecvPropVector(RECVINFO(m_vecIKTarget[1])),
|
|
RecvPropVector(RECVINFO(m_vecIKTarget[2])),
|
|
RecvPropVector(RECVINFO(m_vecIKTarget[3])),
|
|
RecvPropVector(RECVINFO(m_vecIKTarget[4])),
|
|
RecvPropVector(RECVINFO(m_vecIKTarget[5])),
|
|
END_RECV_TABLE()
|
|
|
|
C_StriderFX::C_StriderFX()
|
|
{
|
|
m_pOwner = NULL;
|
|
m_active = false;
|
|
}
|
|
|
|
void C_StriderFX::Update( C_BaseEntity *pOwner, const Vector &targetPos )
|
|
{
|
|
BaseClass::Update();
|
|
|
|
m_pOwner = pOwner;
|
|
|
|
if ( m_active )
|
|
{
|
|
m_targetPosition = targetPos;
|
|
}
|
|
}
|
|
|
|
// --on gun
|
|
// warpy sprite bit
|
|
// darkening sprite
|
|
// glowy blue flare sprite
|
|
// bubble warpy sprite
|
|
// after glow sprite
|
|
|
|
// --on line of sight
|
|
// narrow beam
|
|
// wide beam
|
|
|
|
// --on impact point
|
|
// sparkly white bits
|
|
// sparkly white streaks
|
|
// pale blue particle steam
|
|
|
|
enum
|
|
{
|
|
STRIDERFX_WARP_SCALE = 0,
|
|
STRIDERFX_DARKNESS,
|
|
STRIDERFX_FLARE_COLOR,
|
|
STRIDERFX_FLARE_SIZE,
|
|
STRIDERFX_BUBBLE_SIZE,
|
|
STRIDERFX_BUBBLE_REFRACT,
|
|
|
|
STRIDERFX_NARROW_BEAM_COLOR,
|
|
STRIDERFX_NARROW_BEAM_SIZE,
|
|
|
|
STRIDERFX_WIDE_BEAM_COLOR,
|
|
STRIDERFX_WIDE_BEAM_SIZE,
|
|
|
|
STRIDERFX_AFTERGLOW_COLOR,
|
|
|
|
STRIDERFX_WIDE_BEAM_LENGTH,
|
|
|
|
STRIDERFX_SPARK_COUNT,
|
|
STRIDERFX_STREAK_COUNT,
|
|
STRIDERFX_STEAM_COUNT,
|
|
|
|
|
|
// must be last
|
|
STRIDERFX_PARAMETERS,
|
|
};
|
|
|
|
class CStriderFXEnvelope
|
|
{
|
|
public:
|
|
CStriderFXEnvelope();
|
|
|
|
void AddKey( int parameterIndex, const CSimpleKeyInterp &key )
|
|
{
|
|
Assert( parameterIndex >= 0 && parameterIndex < STRIDERFX_PARAMETERS );
|
|
|
|
if ( parameterIndex >= 0 && parameterIndex < STRIDERFX_PARAMETERS )
|
|
{
|
|
m_parameters[parameterIndex].Insert( key );
|
|
}
|
|
|
|
}
|
|
|
|
CSimpleKeyList m_parameters[STRIDERFX_PARAMETERS];
|
|
};
|
|
|
|
// NOTE: Beam widths are half-widths or radii, so this is a beam that represents a cylinder with 2" radius
|
|
const float NARROW_BEAM_WIDTH = 2;
|
|
const float WIDE_BEAM_WIDTH = 16;
|
|
const float FLARE_SIZE = 128;
|
|
const float DARK_SIZE = 64;
|
|
const float AFTERGLOW_SIZE = 64;
|
|
|
|
const float WARP_SIZE = 512;
|
|
const float WARP_REFRACT = 0.075f;
|
|
const float WARP_BUBBLE_SIZE = 256;
|
|
const float WARP_BUBBLE_REFRACT = 1.0f;
|
|
|
|
CStriderFXEnvelope::CStriderFXEnvelope()
|
|
{
|
|
AddKey( STRIDERFX_WARP_SCALE, CSimpleKeyInterp( 0, KEY_LINEAR, 0 ) );
|
|
AddKey( STRIDERFX_WARP_SCALE, CSimpleKeyInterp( 1.25, KEY_ACCELERATE, 1 ) );
|
|
AddKey( STRIDERFX_WARP_SCALE, CSimpleKeyInterp( 1.25, KEY_LINEAR, 1 ) );
|
|
AddKey( STRIDERFX_WARP_SCALE, CSimpleKeyInterp( 1.3, KEY_LINEAR, 0 ) );
|
|
|
|
AddKey( STRIDERFX_DARKNESS, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
|
|
AddKey( STRIDERFX_DARKNESS, CSimpleKeyInterp( 0.5, KEY_SPLINE, 1 ) );
|
|
AddKey( STRIDERFX_DARKNESS, CSimpleKeyInterp( 1.0, KEY_LINEAR, 1 ) );
|
|
AddKey( STRIDERFX_DARKNESS, CSimpleKeyInterp( 1.25, KEY_SPLINE, 0 ) );
|
|
AddKey( STRIDERFX_DARKNESS, CSimpleKeyInterp( 2.0, KEY_SPLINE, 0 ) );
|
|
|
|
AddKey( STRIDERFX_FLARE_COLOR, CSimpleKeyInterp( 0, KEY_LINEAR, 0 ) );
|
|
AddKey( STRIDERFX_FLARE_COLOR, CSimpleKeyInterp( 0.5, KEY_LINEAR, 0 ) );
|
|
AddKey( STRIDERFX_FLARE_COLOR, CSimpleKeyInterp( 1.25, KEY_ACCELERATE, 1 ) );
|
|
AddKey( STRIDERFX_FLARE_COLOR, CSimpleKeyInterp( 1.5, KEY_LINEAR, 1 ) );
|
|
AddKey( STRIDERFX_FLARE_COLOR, CSimpleKeyInterp( 2.0, KEY_SPLINE, 0 ) );
|
|
|
|
AddKey( STRIDERFX_FLARE_SIZE, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
|
|
AddKey( STRIDERFX_FLARE_SIZE, CSimpleKeyInterp( 1.0, KEY_LINEAR, 1 ) );
|
|
AddKey( STRIDERFX_FLARE_SIZE, CSimpleKeyInterp( 2.0, KEY_LINEAR, 1 ) );
|
|
|
|
AddKey( STRIDERFX_BUBBLE_SIZE, CSimpleKeyInterp( 1.3, KEY_LINEAR, 0.5 ) );
|
|
AddKey( STRIDERFX_BUBBLE_SIZE, CSimpleKeyInterp( 2.0, KEY_DECELERATE, 2 ) );
|
|
|
|
AddKey( STRIDERFX_BUBBLE_REFRACT, CSimpleKeyInterp( 1.3, KEY_LINEAR, 1 ) );
|
|
AddKey( STRIDERFX_BUBBLE_REFRACT, CSimpleKeyInterp( 2.0, KEY_LINEAR, 0 ) );
|
|
|
|
AddKey( STRIDERFX_NARROW_BEAM_COLOR, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
|
|
AddKey( STRIDERFX_NARROW_BEAM_COLOR, CSimpleKeyInterp( 1.25, KEY_ACCELERATE, 1.0 ) );
|
|
AddKey( STRIDERFX_NARROW_BEAM_COLOR, CSimpleKeyInterp( 1.5, KEY_SPLINE, 0 ) );
|
|
|
|
AddKey( STRIDERFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
|
|
AddKey( STRIDERFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 0.5, KEY_ACCELERATE, 1 ) );
|
|
AddKey( STRIDERFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 1.25, KEY_LINEAR, 1 ) );
|
|
AddKey( STRIDERFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 1.5, KEY_DECELERATE, 2 ) );
|
|
|
|
AddKey( STRIDERFX_WIDE_BEAM_COLOR, CSimpleKeyInterp( 1.25, KEY_LINEAR, 0 ) );
|
|
AddKey( STRIDERFX_WIDE_BEAM_COLOR, CSimpleKeyInterp( 1.5, KEY_SPLINE, 1 ) );
|
|
AddKey( STRIDERFX_WIDE_BEAM_COLOR, CSimpleKeyInterp( 1.75, KEY_LINEAR, 1 ) );
|
|
AddKey( STRIDERFX_WIDE_BEAM_COLOR, CSimpleKeyInterp( 2.1, KEY_SPLINE, 0 ) );
|
|
|
|
AddKey( STRIDERFX_WIDE_BEAM_SIZE, CSimpleKeyInterp( 1.25, KEY_LINEAR, 1 ) );
|
|
AddKey( STRIDERFX_WIDE_BEAM_SIZE, CSimpleKeyInterp( 2.1, KEY_LINEAR, 1 ) );
|
|
|
|
AddKey( STRIDERFX_AFTERGLOW_COLOR, CSimpleKeyInterp( 1.0, KEY_LINEAR, 0 ) );
|
|
AddKey( STRIDERFX_AFTERGLOW_COLOR, CSimpleKeyInterp( 1.25, KEY_SPLINE, 1 ) );
|
|
AddKey( STRIDERFX_AFTERGLOW_COLOR, CSimpleKeyInterp( 3.0, KEY_LINEAR, 1 ) );
|
|
AddKey( STRIDERFX_AFTERGLOW_COLOR, CSimpleKeyInterp( 3.5, KEY_ACCELERATE, 0 ) );
|
|
|
|
AddKey( STRIDERFX_WIDE_BEAM_LENGTH, CSimpleKeyInterp( 1.25, KEY_LINEAR, 1.0 ) );
|
|
AddKey( STRIDERFX_WIDE_BEAM_LENGTH, CSimpleKeyInterp( 1.5, KEY_ACCELERATE, 0.0 ) );
|
|
AddKey( STRIDERFX_WIDE_BEAM_LENGTH, CSimpleKeyInterp( 2.1, KEY_LINEAR, 0 ) );
|
|
|
|
//AddKey( STRIDERFX_SPARK_COUNT,
|
|
//AddKey( STRIDERFX_STREAK_COUNT,
|
|
//AddKey( STRIDERFX_STEAM_COUNT,
|
|
}
|
|
|
|
CStriderFXEnvelope g_StriderCannonEnvelope;
|
|
|
|
void ScaleColor( color32 &out, const color32 &in, float scale )
|
|
{
|
|
out.r = (byte)(int)((float)in.r * scale);
|
|
out.g = (byte)(int)((float)in.g * scale);
|
|
out.b = (byte)(int)((float)in.b * scale);
|
|
out.a = (byte)(int)((float)in.a * scale);
|
|
}
|
|
|
|
void DrawSpriteTangentSpace( const Vector &vecOrigin, float flWidth, float flHeight, color32 color )
|
|
{
|
|
unsigned char pColor[4] = { color.r, color.g, color.b, color.a };
|
|
|
|
// Generate half-widths
|
|
flWidth *= 0.5f;
|
|
flHeight *= 0.5f;
|
|
|
|
// Compute direction vectors for the sprite
|
|
Vector fwd, right( 1, 0, 0 ), up( 0, 1, 0 );
|
|
VectorSubtract( CurrentViewOrigin(), vecOrigin, fwd );
|
|
float flDist = VectorNormalize( fwd );
|
|
if (flDist >= 1e-3)
|
|
{
|
|
CrossProduct( CurrentViewUp(), fwd, right );
|
|
flDist = VectorNormalize( right );
|
|
if (flDist >= 1e-3)
|
|
{
|
|
CrossProduct( fwd, right, up );
|
|
}
|
|
else
|
|
{
|
|
// In this case, fwd == g_vecVUp, it's right above or
|
|
// below us in screen space
|
|
CrossProduct( fwd, CurrentViewRight(), up );
|
|
VectorNormalize( up );
|
|
CrossProduct( up, fwd, right );
|
|
}
|
|
}
|
|
|
|
Vector left = -right;
|
|
Vector down = -up;
|
|
Vector back = -fwd;
|
|
|
|
CMeshBuilder meshBuilder;
|
|
Vector point;
|
|
CMatRenderContextPtr pRenderContext( materials );
|
|
IMesh* pMesh = pRenderContext->GetDynamicMesh( );
|
|
|
|
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
|
|
|
|
meshBuilder.Color4ubv (pColor);
|
|
meshBuilder.TexCoord2f (0, 0, 1);
|
|
VectorMA (vecOrigin, -flHeight, up, point);
|
|
VectorMA (point, -flWidth, right, point);
|
|
meshBuilder.TangentS3fv( left.Base() );
|
|
meshBuilder.TangentT3fv( down.Base() );
|
|
meshBuilder.Normal3fv( back.Base() );
|
|
meshBuilder.Position3fv (point.Base());
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
meshBuilder.Color4ubv (pColor);
|
|
meshBuilder.TexCoord2f (0, 0, 0);
|
|
VectorMA (vecOrigin, flHeight, up, point);
|
|
VectorMA (point, -flWidth, right, point);
|
|
meshBuilder.TangentS3fv( left.Base() );
|
|
meshBuilder.TangentT3fv( down.Base() );
|
|
meshBuilder.Normal3fv( back.Base() );
|
|
meshBuilder.Position3fv (point.Base());
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
meshBuilder.Color4ubv (pColor);
|
|
meshBuilder.TexCoord2f (0, 1, 0);
|
|
VectorMA (vecOrigin, flHeight, up, point);
|
|
VectorMA (point, flWidth, right, point);
|
|
meshBuilder.TangentS3fv( left.Base() );
|
|
meshBuilder.TangentT3fv( down.Base() );
|
|
meshBuilder.Normal3fv( back.Base() );
|
|
meshBuilder.Position3fv (point.Base());
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
meshBuilder.Color4ubv (pColor);
|
|
meshBuilder.TexCoord2f (0, 1, 1);
|
|
VectorMA (vecOrigin, -flHeight, up, point);
|
|
VectorMA (point, flWidth, right, point);
|
|
meshBuilder.TangentS3fv( left.Base() );
|
|
meshBuilder.TangentT3fv( down.Base() );
|
|
meshBuilder.Normal3fv( back.Base() );
|
|
meshBuilder.Position3fv (point.Base());
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
meshBuilder.End();
|
|
pMesh->Draw();
|
|
}
|
|
|
|
|
|
void Strider_DrawSprite( const Vector &vecOrigin, float size, const color32 &color )
|
|
{
|
|
DrawSpriteTangentSpace( vecOrigin, size, size, color );
|
|
}
|
|
|
|
|
|
void Strider_DrawLine( const Vector &start, const Vector &end, float width, IMaterial *pMaterial, const color32 &color )
|
|
{
|
|
FX_DrawLineFade( start, end, width, pMaterial, color, 8.0f );
|
|
}
|
|
|
|
int C_StriderFX::DrawModel( int )
|
|
{
|
|
static color32 white = {255,255,255,255};
|
|
Vector params[STRIDERFX_PARAMETERS];
|
|
bool hasParam[STRIDERFX_PARAMETERS];
|
|
|
|
if ( !m_active )
|
|
return 1;
|
|
|
|
C_BaseEntity *ent = cl_entitylist->GetEnt( m_entityIndex );
|
|
if ( ent )
|
|
{
|
|
QAngle angles;
|
|
ent->GetAttachment( m_attachment, m_worldPosition, angles );
|
|
}
|
|
|
|
// This forces time to drive from the main clock instead of being integrated per-draw below
|
|
// that way the effect moves on even when culled for visibility
|
|
if ( m_limitHitTime > 0 && m_tMax > 0 )
|
|
{
|
|
float dt = m_limitHitTime - gpGlobals->curtime;
|
|
if ( dt < 0 )
|
|
{
|
|
dt = 0;
|
|
}
|
|
// if the clock needs to move, update it.
|
|
if ( m_tMax - dt > m_t )
|
|
{
|
|
m_t = m_tMax - dt;
|
|
m_beamEndPosition = m_worldPosition;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// don't have enough info to derive the time, integrate current frame time
|
|
m_t += gpGlobals->frametime;
|
|
if ( m_tMax > 0 )
|
|
{
|
|
m_t = clamp( m_t, 0, m_tMax );
|
|
m_beamEndPosition = m_worldPosition;
|
|
}
|
|
}
|
|
float t = m_t;
|
|
|
|
bool hasAny = false;
|
|
memset( hasParam, 0, sizeof(hasParam) );
|
|
for ( int i = 0; i < STRIDERFX_PARAMETERS; i++ )
|
|
{
|
|
hasParam[i] = g_StriderCannonEnvelope.m_parameters[i].Interp( params[i], t );
|
|
hasAny = hasAny || hasParam[i];
|
|
}
|
|
|
|
pixelvis_queryparams_t gunParams;
|
|
gunParams.Init(m_worldPosition, 4.0f);
|
|
float gunFractionVisible = PixelVisibility_FractionVisible( gunParams, &m_queryHandleGun );
|
|
bool gunVisible = gunFractionVisible > 0.0f ? true : false;
|
|
|
|
// draw the narrow beam
|
|
if ( hasParam[STRIDERFX_NARROW_BEAM_COLOR] && hasParam[STRIDERFX_NARROW_BEAM_SIZE] )
|
|
{
|
|
IMaterial *pMat = materials->FindMaterial( "sprites/bluelaser1", TEXTURE_GROUP_CLIENT_EFFECTS );
|
|
float width = NARROW_BEAM_WIDTH * params[STRIDERFX_NARROW_BEAM_SIZE].x;
|
|
color32 color;
|
|
float bright = params[STRIDERFX_NARROW_BEAM_COLOR].x;
|
|
ScaleColor( color, white, bright );
|
|
|
|
Strider_DrawLine( m_beamEndPosition, m_targetPosition, width, pMat, color );
|
|
}
|
|
|
|
// draw the wide beam
|
|
if ( hasParam[STRIDERFX_WIDE_BEAM_COLOR] && hasParam[STRIDERFX_WIDE_BEAM_SIZE] )
|
|
{
|
|
IMaterial *pMat = materials->FindMaterial( "effects/blueblacklargebeam", TEXTURE_GROUP_CLIENT_EFFECTS );
|
|
float width = WIDE_BEAM_WIDTH * params[STRIDERFX_WIDE_BEAM_SIZE].x;
|
|
color32 color;
|
|
float bright = params[STRIDERFX_WIDE_BEAM_COLOR].x;
|
|
ScaleColor( color, white, bright );
|
|
Vector wideBeamEnd = m_beamEndPosition;
|
|
if ( hasParam[STRIDERFX_WIDE_BEAM_LENGTH] )
|
|
{
|
|
float amt = params[STRIDERFX_WIDE_BEAM_LENGTH].x;
|
|
wideBeamEnd = m_beamEndPosition * amt + m_targetPosition * (1-amt);
|
|
}
|
|
|
|
Strider_DrawLine( wideBeamEnd, m_targetPosition, width, pMat, color );
|
|
}
|
|
|
|
// after glow sprite
|
|
bool updated = false;
|
|
CMatRenderContextPtr pRenderContext( materials );
|
|
// warpy sprite bit
|
|
if ( hasParam[STRIDERFX_WARP_SCALE] && !hasParam[STRIDERFX_BUBBLE_SIZE] && gunVisible )
|
|
{
|
|
if ( !updated )
|
|
{
|
|
updated = true;
|
|
pRenderContext->Flush();
|
|
UpdateRefractTexture();
|
|
}
|
|
|
|
IMaterial *pMat = materials->FindMaterial( "effects/strider_pinch_dudv", TEXTURE_GROUP_CLIENT_EFFECTS );
|
|
float size = WARP_SIZE;
|
|
float refract = params[STRIDERFX_WARP_SCALE].x * WARP_REFRACT * gunFractionVisible;
|
|
|
|
pRenderContext->Bind( pMat, (IClientRenderable*)this );
|
|
IMaterialVar *pVar = pMat->FindVar( "$refractamount", NULL );
|
|
pVar->SetFloatValue( refract );
|
|
Strider_DrawSprite( m_worldPosition, size, white );
|
|
}
|
|
// darkening sprite
|
|
// glowy blue flare sprite
|
|
if ( hasParam[STRIDERFX_FLARE_COLOR] && hasParam[STRIDERFX_FLARE_SIZE] && hasParam[STRIDERFX_DARKNESS] && gunVisible )
|
|
{
|
|
IMaterial *pMat = materials->FindMaterial( "effects/blueblackflash", TEXTURE_GROUP_CLIENT_EFFECTS );
|
|
float size = FLARE_SIZE * params[STRIDERFX_FLARE_SIZE].x;
|
|
color32 color;
|
|
float bright = params[STRIDERFX_FLARE_COLOR].x * gunFractionVisible;
|
|
ScaleColor( color, white, bright );
|
|
color.a = (int)(255 * params[STRIDERFX_DARKNESS].x);
|
|
pRenderContext->Bind( pMat, (IClientRenderable*)this );
|
|
Strider_DrawSprite( m_worldPosition, size, color );
|
|
}
|
|
// bubble warpy sprite
|
|
if ( hasParam[STRIDERFX_BUBBLE_SIZE] )
|
|
{
|
|
Vector wideBeamEnd = m_beamEndPosition;
|
|
if ( hasParam[STRIDERFX_WIDE_BEAM_LENGTH] )
|
|
{
|
|
float amt = params[STRIDERFX_WIDE_BEAM_LENGTH].x;
|
|
wideBeamEnd = m_beamEndPosition * amt + m_targetPosition * (1-amt);
|
|
}
|
|
pixelvis_queryparams_t endParams;
|
|
endParams.Init(wideBeamEnd, 4.0f, 0.001f);
|
|
float endFractionVisible = PixelVisibility_FractionVisible( endParams, &m_queryHandleBeamEnd );
|
|
bool endVisible = endFractionVisible > 0.0f ? true : false;
|
|
|
|
if ( endVisible )
|
|
{
|
|
if ( !updated )
|
|
{
|
|
updated = true;
|
|
pRenderContext->Flush();
|
|
UpdateRefractTexture();
|
|
}
|
|
IMaterial *pMat = materials->FindMaterial( "effects/strider_bulge_dudv", TEXTURE_GROUP_CLIENT_EFFECTS );
|
|
float refract = endFractionVisible * WARP_BUBBLE_REFRACT * params[STRIDERFX_BUBBLE_REFRACT].x;
|
|
float size = WARP_BUBBLE_SIZE * params[STRIDERFX_BUBBLE_SIZE].x;
|
|
IMaterialVar *pVar = pMat->FindVar( "$refractamount", NULL );
|
|
pVar->SetFloatValue( refract );
|
|
|
|
pRenderContext->Bind( pMat, (IClientRenderable*)this );
|
|
Strider_DrawSprite( wideBeamEnd, size, white );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// call this to have the check ready on the first frame
|
|
pixelvis_queryparams_t endParams;
|
|
endParams.Init(m_beamEndPosition, 4.0f, 0.001f);
|
|
PixelVisibility_FractionVisible( endParams, &m_queryHandleBeamEnd );
|
|
}
|
|
if ( hasParam[STRIDERFX_AFTERGLOW_COLOR] && gunVisible )
|
|
{
|
|
IMaterial *pMat = materials->FindMaterial( "effects/blueblackflash", TEXTURE_GROUP_CLIENT_EFFECTS );
|
|
float size = AFTERGLOW_SIZE;// * params[STRIDERFX_FLARE_SIZE].x;
|
|
color32 color;
|
|
float bright = params[STRIDERFX_AFTERGLOW_COLOR].x * gunFractionVisible;
|
|
ScaleColor( color, white, bright );
|
|
|
|
pRenderContext->Bind( pMat, (IClientRenderable*)this );
|
|
Strider_DrawSprite( m_worldPosition, size, color );
|
|
|
|
dlight_t *dl = effects->CL_AllocDlight( m_entityIndex );
|
|
dl->origin = m_worldPosition;
|
|
dl->color.r = 40;
|
|
dl->color.g = 60;
|
|
dl->color.b = 255;
|
|
dl->color.exponent = 5;
|
|
dl->radius = bright * 128;
|
|
dl->die = gpGlobals->curtime + 0.001;
|
|
}
|
|
|
|
if ( m_t >= STRIDERFX_END_ALL_TIME && !hasAny )
|
|
{
|
|
EffectShutdown();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Strider class implementation
|
|
//-----------------------------------------------------------------------------
|
|
C_Strider::C_Strider() :
|
|
m_iv_vecHitPos("C_Strider::m_iv_vecHitPos"),
|
|
m_iv_vecIKTarget("C_Strider::m_iv_vecIKTarget")
|
|
{
|
|
AddVar( &m_vecHitPos, &m_iv_vecHitPos, LATCH_ANIMATION_VAR );
|
|
|
|
memset(m_vecIKTarget, 0, sizeof(m_vecIKTarget));
|
|
AddVar( &m_vecIKTarget, &m_iv_vecIKTarget, LATCH_ANIMATION_VAR );
|
|
|
|
m_flNextRopeCutTime = 0;
|
|
}
|
|
|
|
C_Strider::~C_Strider()
|
|
{
|
|
}
|
|
|
|
void C_Strider::ReceiveMessage( int classID, bf_read &msg )
|
|
{
|
|
if ( classID != GetClientClass()->m_ClassID )
|
|
{
|
|
// message is for subclass
|
|
BaseClass::ReceiveMessage( classID, msg );
|
|
return;
|
|
}
|
|
|
|
int messageType = msg.ReadByte();
|
|
switch( messageType )
|
|
{
|
|
case STRIDER_MSG_STREAKS:
|
|
{
|
|
Vector pos;
|
|
msg.ReadBitVec3Coord( pos );
|
|
m_cannonFX.SetRenderOrigin( pos );
|
|
m_cannonFX.EffectInit( entindex(), LookupAttachment( "BigGun" ) );
|
|
m_cannonFX.LimitTime( STRIDERFX_BIG_SHOT_TIME );
|
|
}
|
|
break;
|
|
|
|
case STRIDER_MSG_BIG_SHOT:
|
|
{
|
|
Vector tmp;
|
|
msg.ReadBitVec3Coord( tmp );
|
|
m_cannonFX.SetTime( STRIDERFX_BIG_SHOT_TIME );
|
|
m_cannonFX.LimitTime( STRIDERFX_END_ALL_TIME );
|
|
}
|
|
break;
|
|
|
|
case STRIDER_MSG_DEAD:
|
|
{
|
|
m_cannonFX.EffectShutdown();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void C_Strider::OnDataChanged( DataUpdateType_t updateType )
|
|
{
|
|
if ( updateType == DATA_UPDATE_CREATED )
|
|
{
|
|
// We need to have our render bounds defined or shadow creation won't work correctly
|
|
ClientThink();
|
|
ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_ALWAYS );
|
|
}
|
|
|
|
BaseClass::OnDataChanged( updateType );
|
|
|
|
m_cannonFX.Update( this, m_vecHitPos );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Recompute my rendering box
|
|
//-----------------------------------------------------------------------------
|
|
void C_Strider::ClientThink()
|
|
{
|
|
// The reason why this is here, as opposed to in SetObjectCollisionBox,
|
|
// is because of IK. The code below recomputes bones so as to get at the hitboxes,
|
|
// which causes IK to trigger, which causes raycasts against the other entities to occur,
|
|
// which is illegal to do while in the Relink phase.
|
|
|
|
ComputeEntitySpaceHitboxSurroundingBox( &m_vecRenderMins, &m_vecRenderMaxs );
|
|
// UNDONE: Disabled this until we can get closer to a final map and tune
|
|
#if 0
|
|
// Cut ropes.
|
|
if ( gpGlobals->curtime >= m_flNextRopeCutTime )
|
|
{
|
|
// Blow the bbox out a little.
|
|
Vector vExtendedMins = vecMins - Vector( 50, 50, 50 );
|
|
Vector vExtendedMaxs = vecMaxs + Vector( 50, 50, 50 );
|
|
|
|
C_RopeKeyframe *ropes[512];
|
|
int nRopes = C_RopeKeyframe::GetRopesIntersectingAABB( ropes, ARRAYSIZE( ropes ), GetAbsOrigin() + vExtendedMins, GetAbsOrigin() + vExtendedMaxs );
|
|
for ( int i=0; i < nRopes; i++ )
|
|
{
|
|
C_RopeKeyframe *pRope = ropes[i];
|
|
|
|
if ( pRope->GetEndEntity() )
|
|
{
|
|
Vector vPos;
|
|
if ( pRope->GetEndPointPos( 1, vPos ) )
|
|
{
|
|
// Detach the endpoint.
|
|
pRope->SetEndEntity( NULL );
|
|
|
|
// Make some spark effect here..
|
|
g_pEffects->Sparks( vPos );
|
|
}
|
|
}
|
|
}
|
|
|
|
m_flNextRopeCutTime = gpGlobals->curtime + 0.5;
|
|
}
|
|
#endif
|
|
|
|
// True argument because the origin may have stayed the same, but the size is expected to always change
|
|
g_pClientShadowMgr->AddToDirtyShadowList( this, true );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Recompute my rendering box
|
|
//-----------------------------------------------------------------------------
|
|
void C_Strider::GetRenderBounds( Vector& theMins, Vector& theMaxs )
|
|
{
|
|
theMins = m_vecRenderMins;
|
|
theMaxs = m_vecRenderMaxs;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Strider muzzle flashes
|
|
//-----------------------------------------------------------------------------
|
|
void MuzzleFlash_Strider( ClientEntityHandle_t hEntity, int attachmentIndex )
|
|
{
|
|
VPROF_BUDGET( "MuzzleFlash_Strider", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
|
|
|
|
// If the client hasn't seen this entity yet, bail.
|
|
matrix3x4_t matAttachment;
|
|
if ( !FX_GetAttachmentTransform( hEntity, attachmentIndex, matAttachment ) )
|
|
return;
|
|
|
|
CSmartPtr<CLocalSpaceEmitter> pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash_Strider", hEntity, attachmentIndex );
|
|
|
|
SimpleParticle *pParticle;
|
|
Vector forward(1,0,0), offset; //NOTENOTE: All coords are in local space
|
|
|
|
float flScale = random->RandomFloat( 3.0f, 4.0f );
|
|
|
|
float burstSpeed = random->RandomFloat( 400.0f, 600.0f );
|
|
|
|
#define FRONT_LENGTH 12
|
|
|
|
// Front flash
|
|
for ( int i = 1; i < FRONT_LENGTH; i++ )
|
|
{
|
|
offset = (forward * (i*2.0f*flScale));
|
|
|
|
pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), 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-(i))/(FRONT_LENGTH*0.75f)) * 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 8
|
|
|
|
burstSpeed = random->RandomFloat( 400.0f, 600.0f );
|
|
|
|
// Diagonal flash
|
|
for ( int i = 1; i < SIDE_LENGTH; i++ )
|
|
{
|
|
offset = (dir * (i*flScale));
|
|
|
|
pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), 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( 400.0f, 600.0f );
|
|
|
|
// Diagonal flash
|
|
for ( int i = 1; i < SIDE_LENGTH; i++ )
|
|
{
|
|
offset = (-dir * (i*flScale));
|
|
|
|
pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), 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( 400.0f, 600.0f );
|
|
|
|
// Top flash
|
|
for ( int i = 1; i < SIDE_LENGTH; i++ )
|
|
{
|
|
offset = (dir * (i*flScale));
|
|
|
|
pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), 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 ), pSimple->GetPMaterial( "effects/strider_muzzle" ), 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;
|
|
|
|
Vector origin;
|
|
MatrixGetColumn( matAttachment, 3, &origin );
|
|
|
|
int entityIndex = ClientEntityList().HandleToEntIndex( hEntity );
|
|
if ( entityIndex >= 0 )
|
|
{
|
|
dlight_t *el = effects->CL_AllocElight( LIGHT_INDEX_MUZZLEFLASH + entityIndex );
|
|
|
|
el->origin = origin;
|
|
|
|
el->color.r = 64;
|
|
el->color.g = 128;
|
|
el->color.b = 255;
|
|
el->color.exponent = 5;
|
|
|
|
el->radius = random->RandomInt( 100, 150 );
|
|
el->decay = el->radius / 0.05f;
|
|
el->die = gpGlobals->curtime + 0.1f;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : &data -
|
|
//-----------------------------------------------------------------------------
|
|
void StriderMuzzleFlashCallback( const CEffectData &data )
|
|
{
|
|
MuzzleFlash_Strider( data.m_hEntity, data.m_nAttachmentIndex );
|
|
}
|
|
|
|
DECLARE_CLIENT_EFFECT( "StriderMuzzleFlash", StriderMuzzleFlashCallback );
|
|
|
|
#define BLOOD_MIN_SPEED 64.0f*2.0f
|
|
#define BLOOD_MAX_SPEED 256.0f*8.0f
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : &origin -
|
|
// &normal -
|
|
// scale -
|
|
//-----------------------------------------------------------------------------
|
|
void StriderBlood( const Vector &origin, const Vector &normal, float scale )
|
|
{
|
|
VPROF_BUDGET( "StriderBlood", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
|
|
|
|
//Find area ambient light color and use it to tint smoke
|
|
Vector worldLight = WorldGetLightForPoint( origin, true );
|
|
Vector tint;
|
|
float luminosity;
|
|
UTIL_GetNormalizedColorTintAndLuminosity( worldLight, &tint, &luminosity );
|
|
|
|
// We only take a portion of the tint
|
|
tint = (tint * 0.25f)+(Vector(0.75f,0.75f,0.75f));
|
|
|
|
// Rescale to a character range
|
|
luminosity = MAX( 200, luminosity*255 );
|
|
|
|
CSmartPtr<CSplashParticle> pSimple = CSplashParticle::Create( "splish" );
|
|
pSimple->SetSortOrigin( origin );
|
|
|
|
int i;
|
|
float flScale = scale / 8.0f;
|
|
|
|
PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/slime1" );
|
|
|
|
float length = 0.2f;
|
|
Vector vForward, vRight, vUp;
|
|
Vector offDir;
|
|
|
|
TrailParticle *tParticle;
|
|
|
|
CSmartPtr<CTrailParticles> sparkEmitter = CTrailParticles::Create( "splash" );
|
|
|
|
if ( !sparkEmitter )
|
|
return;
|
|
|
|
sparkEmitter->SetSortOrigin( origin );
|
|
sparkEmitter->m_ParticleCollision.SetGravity( 600.0f );
|
|
sparkEmitter->SetFlag( bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
|
|
sparkEmitter->SetVelocityDampen( 2.0f );
|
|
|
|
//Dump out drops
|
|
Vector offset;
|
|
for ( i = 0; i < 64; i++ )
|
|
{
|
|
offset = origin;
|
|
offset[0] += random->RandomFloat( -8.0f, 8.0f ) * flScale;
|
|
offset[1] += random->RandomFloat( -8.0f, 8.0f ) * flScale;
|
|
offset[2] += random->RandomFloat( -8.0f, 8.0f ) * flScale;
|
|
|
|
tParticle = (TrailParticle *) sparkEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset );
|
|
|
|
if ( tParticle == NULL )
|
|
break;
|
|
|
|
tParticle->m_flLifetime = 0.0f;
|
|
tParticle->m_flDieTime = 1.0f;
|
|
|
|
offDir = normal + RandomVector( -1.0f, 1.0f );
|
|
|
|
tParticle->m_vecVelocity = offDir * random->RandomFloat( BLOOD_MIN_SPEED * flScale * 2.0f, BLOOD_MAX_SPEED * flScale * 2.0f );
|
|
tParticle->m_vecVelocity[2] += random->RandomFloat( 8.0f, 32.0f ) * flScale;
|
|
|
|
tParticle->m_flWidth = random->RandomFloat( 20.0f, 26.0f ) * flScale;
|
|
tParticle->m_flLength = random->RandomFloat( length*0.5f, length ) * flScale;
|
|
|
|
int nColor = random->RandomInt( luminosity*0.75f, luminosity );
|
|
tParticle->m_color.r = nColor * tint.x;
|
|
tParticle->m_color.g = nColor * tint.y;
|
|
tParticle->m_color.b = nColor * tint.z;
|
|
tParticle->m_color.a = 255;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : &data -
|
|
//-----------------------------------------------------------------------------
|
|
void StriderBloodCallback( const CEffectData &data )
|
|
{
|
|
StriderBlood( data.m_vOrigin, data.m_vNormal, data.m_flScale );
|
|
}
|
|
|
|
DECLARE_CLIENT_EFFECT( "StriderBlood", StriderBloodCallback );
|
|
|