source-engine/game/client/hl2/c_strider.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

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 );