source-engine/game/client/hl2/c_npc_combinegunship.cpp

477 lines
13 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "c_basehelicopter.h"
#include "fx_impact.h"
#include "IEffects.h"
#include "simple_keys.h"
#include "fx_envelope.h"
#include "fx_line.h"
#include "iefx.h"
#include "dlight.h"
#include "c_sprite.h"
#include "clienteffectprecachesystem.h"
#include <bitbuf.h>
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define GUNSHIP_MSG_BIG_SHOT 1
#define GUNSHIP_MSG_STREAKS 2
#define GUNSHIP_MSG_DEAD 3
#define GUNSHIPFX_BIG_SHOT_TIME 3.0f
CLIENTEFFECT_REGISTER_BEGIN( PrecacheGunshipFX )
CLIENTEFFECT_MATERIAL( "sprites/bluelaser1" )
CLIENTEFFECT_REGISTER_END()
//-----------------------------------------------------------------------------
// Big belly shot FX
//-----------------------------------------------------------------------------
class C_GunshipFX : public C_EnvelopeFX
{
public:
typedef C_EnvelopeFX BaseClass;
C_GunshipFX();
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 int DrawModel( int flags );
C_BaseEntity *m_pOwner;
Vector m_targetPosition;
Vector m_beamEndPosition;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
C_GunshipFX::C_GunshipFX( void )
{
m_pOwner = NULL;
}
enum
{
GUNSHIPFX_WARP_SCALE = 0,
GUNSHIPFX_DARKNESS,
GUNSHIPFX_FLARE_COLOR,
GUNSHIPFX_FLARE_SIZE,
GUNSHIPFX_NARROW_BEAM_COLOR,
GUNSHIPFX_NARROW_BEAM_SIZE,
GUNSHIPFX_WIDE_BEAM_COLOR,
GUNSHIPFX_WIDE_BEAM_SIZE,
GUNSHIPFX_AFTERGLOW_COLOR,
GUNSHIPFX_WIDE_BEAM_LENGTH,
// must be last
GUNSHIPFX_PARAMETERS,
};
class CGunshipFXEnvelope
{
public:
CGunshipFXEnvelope();
void AddKey( int parameterIndex, const CSimpleKeyInterp &key )
{
Assert( parameterIndex >= 0 && parameterIndex < GUNSHIPFX_PARAMETERS );
if ( parameterIndex >= 0 && parameterIndex < GUNSHIPFX_PARAMETERS )
{
m_parameters[parameterIndex].Insert( key );
}
}
CSimpleKeyList m_parameters[GUNSHIPFX_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 = 32;
const float WIDE_BEAM_WIDTH = 2;
const float FLARE_SIZE = 128;
const float DARK_SIZE = 16;
const float AFTERGLOW_SIZE = 64;
CGunshipFXEnvelope::CGunshipFXEnvelope()
{
// Glow flare
AddKey( GUNSHIPFX_FLARE_COLOR, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
AddKey( GUNSHIPFX_FLARE_COLOR, CSimpleKeyInterp( 0.5, KEY_SPLINE, 1 ) );
AddKey( GUNSHIPFX_FLARE_COLOR, CSimpleKeyInterp( 2.9, KEY_LINEAR, 1 ) );
AddKey( GUNSHIPFX_FLARE_COLOR, CSimpleKeyInterp( 3.2, KEY_DECELERATE, 0 ) );
AddKey( GUNSHIPFX_FLARE_SIZE, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
AddKey( GUNSHIPFX_FLARE_SIZE, CSimpleKeyInterp( 2.9, KEY_ACCELERATE, 1 ) );
AddKey( GUNSHIPFX_FLARE_SIZE, CSimpleKeyInterp( 5.0, KEY_LINEAR, 1 ) );
// Ground beam
AddKey( GUNSHIPFX_NARROW_BEAM_COLOR, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0.0 ) );
AddKey( GUNSHIPFX_NARROW_BEAM_COLOR, CSimpleKeyInterp( 2.5, KEY_SPLINE, 1.0 ) );
AddKey( GUNSHIPFX_NARROW_BEAM_COLOR, CSimpleKeyInterp( 3.0, KEY_LINEAR, 1 ) );
AddKey( GUNSHIPFX_NARROW_BEAM_COLOR, CSimpleKeyInterp( 3.2, KEY_ACCELERATE, 0 ) );
AddKey( GUNSHIPFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
AddKey( GUNSHIPFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 2.5, KEY_SPLINE, 0.25 ) );
AddKey( GUNSHIPFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 2.8, KEY_ACCELERATE, 1 ) );
AddKey( GUNSHIPFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 3.0, KEY_ACCELERATE, 4 ) );
AddKey( GUNSHIPFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 3.2, KEY_DECELERATE, 0 ) );
// Glow color on the ship
AddKey( GUNSHIPFX_AFTERGLOW_COLOR, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
AddKey( GUNSHIPFX_AFTERGLOW_COLOR, CSimpleKeyInterp( 3.0, KEY_LINEAR, 1 ) );
AddKey( GUNSHIPFX_AFTERGLOW_COLOR, CSimpleKeyInterp( 5.0, KEY_SPLINE, 0 ) );
}
CGunshipFXEnvelope g_GunshipCannonEnvelope;
static 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);
}
static 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;
CMatRenderContextPtr pRenderContext( materials );
CMeshBuilder meshBuilder;
Vector point;
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 Gunship_DrawSprite( const Vector &vecOrigin, float size, const color32 &color, bool glow )
{
if ( glow )
{
pixelvis_queryparams_t params;
params.Init( vecOrigin );
if ( PixelVisibility_FractionVisible( params, NULL ) <= 0.0f )
return;
}
DrawSpriteTangentSpace( vecOrigin, size, size, color );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : int -
//-----------------------------------------------------------------------------
int C_GunshipFX::DrawModel( int )
{
static color32 white = {255,255,255,255};
Vector params[GUNSHIPFX_PARAMETERS];
bool hasParam[GUNSHIPFX_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 );
}
Vector test;
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 < GUNSHIPFX_PARAMETERS; i++ )
{
hasParam[i] = g_GunshipCannonEnvelope.m_parameters[i].Interp( params[i], t );
hasAny = hasAny || hasParam[i];
}
// draw the narrow beam
if ( hasParam[GUNSHIPFX_NARROW_BEAM_COLOR] && hasParam[GUNSHIPFX_NARROW_BEAM_SIZE] )
{
IMaterial *pMat = materials->FindMaterial( "sprites/bluelaser1", TEXTURE_GROUP_CLIENT_EFFECTS );
float width = NARROW_BEAM_WIDTH * params[GUNSHIPFX_NARROW_BEAM_SIZE].x;
color32 color;
float bright = params[GUNSHIPFX_NARROW_BEAM_COLOR].x;
ScaleColor( color, white, bright );
//Gunship_DrawLine( m_beamEndPosition, m_targetPosition, width, pMat, color );
FX_DrawLine( m_beamEndPosition, m_targetPosition, width, pMat, color );
}
// glowy blue flare sprite
if ( hasParam[GUNSHIPFX_FLARE_COLOR] && hasParam[GUNSHIPFX_FLARE_SIZE] )
{
IMaterial *pMat = materials->FindMaterial( "effects/blueblackflash", TEXTURE_GROUP_CLIENT_EFFECTS );
float size = FLARE_SIZE * params[GUNSHIPFX_FLARE_SIZE].x;
color32 color;
float bright = params[GUNSHIPFX_FLARE_COLOR].x;
ScaleColor( color, white, bright );
color.a = (int)(255 * params[GUNSHIPFX_DARKNESS].x);
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->Bind( pMat, (IClientRenderable*)this );
Gunship_DrawSprite( m_worldPosition, size, color, true );
}
if ( hasParam[GUNSHIPFX_AFTERGLOW_COLOR] )
{
// Muzzle effect
dlight_t *dl = effects->CL_AllocDlight( m_entityIndex );
dl->origin = m_worldPosition;
dl->color.r = 40*params[GUNSHIPFX_AFTERGLOW_COLOR].x;
dl->color.g = 60*params[GUNSHIPFX_AFTERGLOW_COLOR].x;
dl->color.b = 255*params[GUNSHIPFX_AFTERGLOW_COLOR].x;
dl->color.exponent = 5;
dl->radius = 128.0f;
dl->die = gpGlobals->curtime + 0.001;
}
if ( m_t >= 4.0 && !hasAny )
{
EffectShutdown();
}
return 1;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOwner -
// &targetPos -
//-----------------------------------------------------------------------------
void C_GunshipFX::Update( C_BaseEntity *pOwner, const Vector &targetPos )
{
BaseClass::Update();
m_pOwner = pOwner;
if ( m_active )
{
m_targetPosition = targetPos;
}
}
//-----------------------------------------------------------------------------
// Gunship
//-----------------------------------------------------------------------------
class C_CombineGunship : public C_BaseHelicopter
{
DECLARE_CLASS( C_CombineGunship, C_BaseHelicopter );
public:
DECLARE_CLIENTCLASS();
C_CombineGunship( void ) {}
virtual ~C_CombineGunship( void )
{
m_cannonFX.EffectShutdown();
}
C_GunshipFX m_cannonFX;
Vector m_vecHitPos;
//-----------------------------------------------------------------------------
// Purpose:
// Input : length -
// *data -
// Output : void
//-----------------------------------------------------------------------------
void 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 GUNSHIP_MSG_STREAKS:
{
Vector pos;
msg.ReadBitVec3Coord( pos );
m_cannonFX.SetRenderOrigin( pos );
m_cannonFX.EffectInit( entindex(), LookupAttachment( "BellyGun" ) );
m_cannonFX.LimitTime( GUNSHIPFX_BIG_SHOT_TIME );
}
break;
case GUNSHIP_MSG_BIG_SHOT:
{
Vector tmp;
msg.ReadBitVec3Coord( tmp );
m_cannonFX.SetTime( GUNSHIPFX_BIG_SHOT_TIME );
m_cannonFX.LimitTime( 0 );
}
break;
case GUNSHIP_MSG_DEAD:
{
m_cannonFX.EffectShutdown();
}
break;
}
}
void OnDataChanged( DataUpdateType_t updateType )
{
BaseClass::OnDataChanged( updateType );
m_cannonFX.Update( this, m_vecHitPos );
}
virtual RenderGroup_t GetRenderGroup()
{
if ( hl2_episodic.GetBool() == true )
{
return RENDER_GROUP_TWOPASS;
}
else
{
return BaseClass::GetRenderGroup();
}
}
private:
C_CombineGunship( const C_CombineGunship & ) {}
};
IMPLEMENT_CLIENTCLASS_DT( C_CombineGunship, DT_CombineGunship, CNPC_CombineGunship )
RecvPropVector(RECVINFO(m_vecHitPos)),
END_RECV_TABLE()
//-----------------------------------------------------------------------------
// Purpose: Handle gunship impacts
//-----------------------------------------------------------------------------
void ImpactGunshipCallback( const CEffectData &data )
{
trace_t tr;
Vector vecOrigin, vecStart, vecShotDir;
int iMaterial, iDamageType, iHitbox;
short nSurfaceProp;
C_BaseEntity *pEntity = ParseImpactData( data, &vecOrigin, &vecStart, &vecShotDir, nSurfaceProp, iMaterial, iDamageType, iHitbox );
if ( !pEntity )
return;
// If we hit, perform our custom effects and play the sound
if ( Impact( vecOrigin, vecStart, iMaterial, iDamageType, iHitbox, pEntity, tr ) )
{
// Check for custom effects based on the Decal index
PerformCustomEffects( vecOrigin, tr, vecShotDir, iMaterial, 3 );
}
PlayImpactSound( pEntity, tr, vecOrigin, nSurfaceProp );
}
DECLARE_CLIENT_EFFECT( "ImpactGunship", ImpactGunshipCallback );