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