//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //============================================================================= #include "cbase.h" #include "basegrenade_shared.h" #include "fx_interpvalue.h" #include "fx_envelope.h" #include "materialsystem/imaterialvar.h" #include "particles_simple.h" #include "particles_attractor.h" // FIXME: Move out extern void DrawSpriteTangentSpace( const Vector &vecOrigin, float flWidth, float flHeight, color32 color ); #define EXPLOSION_DURATION 3.0f //----------------------------------------------------------------------------- // Explosion effect for hopwire //----------------------------------------------------------------------------- class C_HopwireExplosion : public C_EnvelopeFX { typedef C_EnvelopeFX BaseClass; public: C_HopwireExplosion( void ) : m_hOwner( NULL ) { m_FXCoreScale.SetAbsolute( 0.0f ); m_FXCoreAlpha.SetAbsolute( 0.0f ); } virtual void Update( void ); virtual int DrawModel( int flags ); virtual void GetRenderBounds( Vector& mins, Vector& maxs ); bool SetupEmitters( void ); void AddParticles( void ); void SetOwner( C_BaseEntity *pOwner ); void StartExplosion( void ); void StopExplosion( void ); void StartPreExplosion( void ); private: CInterpolatedValue m_FXCoreScale; CInterpolatedValue m_FXCoreAlpha; CSmartPtr m_pSimpleEmitter; CSmartPtr m_pAttractorEmitter; TimedEvent m_ParticleTimer; CHandle m_hOwner; }; //----------------------------------------------------------------------------- // Purpose: Setup the emitters we'll be using //----------------------------------------------------------------------------- bool C_HopwireExplosion::SetupEmitters( void ) { // Setup the basic core emitter if ( m_pSimpleEmitter.IsValid() == false ) { m_pSimpleEmitter = CSimpleEmitter::Create( "hopwirecore" ); if ( m_pSimpleEmitter.IsValid() == false ) return false; } // Setup the attractor emitter if ( m_pAttractorEmitter.IsValid() == false ) { m_pAttractorEmitter = CParticleAttractor::Create( GetRenderOrigin(), "hopwireattractor" ); if ( m_pAttractorEmitter.IsValid() == false ) return false; } return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_HopwireExplosion::AddParticles( void ) { // Make sure the emitters are setup properly if ( SetupEmitters() == false ) return; float tempDelta = gpGlobals->frametime; while( m_ParticleTimer.NextEvent( tempDelta ) ) { // ======================== // Attracted dust particles // ======================== // Update our attractor point m_pAttractorEmitter->SetAttractorOrigin( GetRenderOrigin() ); Vector offset; SimpleParticle *sParticle; offset = GetRenderOrigin() + RandomVector( -256.0f, 256.0f ); sParticle = (SimpleParticle *) m_pAttractorEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_Fleck_Cement[0], offset ); if ( sParticle == NULL ) return; sParticle->m_vecVelocity = Vector(0,0,8); sParticle->m_flDieTime = 0.5f; sParticle->m_flLifetime = 0.0f; sParticle->m_flRoll = Helper_RandomInt( 0, 360 ); sParticle->m_flRollDelta = 1.0f; float alpha = random->RandomFloat( 128.0f, 200.0f ); sParticle->m_uchColor[0] = alpha; sParticle->m_uchColor[1] = alpha; sParticle->m_uchColor[2] = alpha; sParticle->m_uchStartAlpha = alpha; sParticle->m_uchEndAlpha = alpha; sParticle->m_uchStartSize = random->RandomInt( 1, 4 ); sParticle->m_uchEndSize = 0; // ======================== // Core effects // ======================== // Reset our sort origin m_pSimpleEmitter->SetSortOrigin( GetRenderOrigin() ); // Base of the core effect sParticle = (SimpleParticle *) m_pSimpleEmitter->AddParticle( sizeof(SimpleParticle), m_pSimpleEmitter->GetPMaterial( "effects/strider_muzzle" ), GetRenderOrigin() ); if ( sParticle == NULL ) return; sParticle->m_vecVelocity = vec3_origin; sParticle->m_flDieTime = 0.2f; sParticle->m_flLifetime = 0.0f; sParticle->m_flRoll = Helper_RandomInt( 0, 360 ); sParticle->m_flRollDelta = 4.0f; alpha = random->RandomInt( 32, 200 ); sParticle->m_uchColor[0] = alpha; sParticle->m_uchColor[1] = alpha; sParticle->m_uchColor[2] = alpha; sParticle->m_uchStartAlpha = 0; sParticle->m_uchEndAlpha = alpha; sParticle->m_uchStartSize = 255; sParticle->m_uchEndSize = 0; // Make sure we encompass the complete particle here! m_pSimpleEmitter->SetParticleCullRadius( sParticle->m_uchEndSize ); // ========================= // Dust ring effect // ========================= if ( random->RandomInt( 0, 5 ) != 1 ) return; Vector vecDustColor; vecDustColor.x = 0.35f; vecDustColor.y = 0.3f; vecDustColor.z = 0.25f; Vector color; int numRingSprites = 8; float yaw; Vector forward, vRight, vForward; vForward = Vector( 0, 1, 0 ); vRight = Vector( 1, 0, 0 ); float yawOfs = random->RandomFloat( 0, 359 ); for ( int i = 0; i < numRingSprites; i++ ) { yaw = ( (float) i / (float) numRingSprites ) * 360.0f; yaw += yawOfs; forward = ( vRight * sin( DEG2RAD( yaw) ) ) + ( vForward * cos( DEG2RAD( yaw ) ) ); VectorNormalize( forward ); trace_t tr; UTIL_TraceLine( GetRenderOrigin(), GetRenderOrigin()+(Vector(0, 0, -1024)), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); offset = ( RandomVector( -4.0f, 4.0f ) + tr.endpos ) + ( forward * 512.0f ); sParticle = (SimpleParticle *) m_pSimpleEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_DustPuff[random->RandomInt(0,1)], offset ); if ( sParticle != NULL ) { sParticle->m_flLifetime = 0.0f; sParticle->m_flDieTime = random->RandomFloat( 0.25f, 0.5f ); sParticle->m_vecVelocity = forward * -random->RandomFloat( 1000, 1500 ); sParticle->m_vecVelocity[2] += 128.0f; #if __EXPLOSION_DEBUG debugoverlay->AddLineOverlay( m_vecOrigin, m_vecOrigin + sParticle->m_vecVelocity, 255, 0, 0, false, 3 ); #endif sParticle->m_uchColor[0] = vecDustColor.x * 255.0f; sParticle->m_uchColor[1] = vecDustColor.y * 255.0f; sParticle->m_uchColor[2] = vecDustColor.z * 255.0f; sParticle->m_uchStartSize = random->RandomInt( 32, 128 ); sParticle->m_uchEndSize = 200; sParticle->m_uchStartAlpha = random->RandomFloat( 16, 64 ); sParticle->m_uchEndAlpha = 0; sParticle->m_flRoll = random->RandomInt( 0, 360 ); sParticle->m_flRollDelta = random->RandomFloat( -16.0f, 16.0f ); } } } } //----------------------------------------------------------------------------- // Purpose: // Input : *pOwner - //----------------------------------------------------------------------------- void C_HopwireExplosion::SetOwner( C_BaseEntity *pOwner ) { m_hOwner = pOwner; } //----------------------------------------------------------------------------- // Purpose: Updates the internal values for the effect //----------------------------------------------------------------------------- void C_HopwireExplosion::Update( void ) { if ( m_hOwner ) { SetRenderOrigin( m_hOwner->GetRenderOrigin() ); } BaseClass::Update(); } //----------------------------------------------------------------------------- // Purpose: Updates and renders all effects //----------------------------------------------------------------------------- int C_HopwireExplosion::DrawModel( int flags ) { AddParticles(); CMatRenderContextPtr pRenderContext( materials ); pRenderContext->Flush(); UpdateRefractTexture(); IMaterial *pMat = materials->FindMaterial( "effects/strider_pinch_dudv", TEXTURE_GROUP_CLIENT_EFFECTS ); float refract = m_FXCoreAlpha.Interp( gpGlobals->curtime ); float scale = m_FXCoreScale.Interp( gpGlobals->curtime ); IMaterialVar *pVar = pMat->FindVar( "$refractamount", NULL ); pVar->SetFloatValue( refract ); pRenderContext->Bind( pMat, (IClientRenderable*)this ); float sin1 = sinf( gpGlobals->curtime * 10 ); float sin2 = sinf( gpGlobals->curtime ); float scaleY = ( sin1 * sin2 ) * 32.0f; float scaleX = (sin2 * sin2) * 32.0f; // FIXME: The ball needs to sort properly at all times static color32 white = {255,255,255,255}; DrawSpriteTangentSpace( GetRenderOrigin() + ( CurrentViewForward() * 128.0f ), scale+scaleX, scale+scaleY, white ); return 1; } //----------------------------------------------------------------------------- // Purpose: Returns the bounds relative to the origin (render bounds) //----------------------------------------------------------------------------- void C_HopwireExplosion::GetRenderBounds( Vector& mins, Vector& maxs ) { float scale = m_FXCoreScale.Interp( gpGlobals->curtime ); mins.Init( -scale, -scale, -scale ); maxs.Init( scale, scale, scale ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_HopwireExplosion::StartExplosion( void ) { m_FXCoreScale.Init( 300.0f, 500.0f, 2.0f, INTERP_SPLINE ); m_FXCoreAlpha.Init( 0.0f, 0.1f, 1.5f, INTERP_SPLINE ); // Particle timer m_ParticleTimer.Init( 60 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_HopwireExplosion::StopExplosion( void ) { m_FXCoreAlpha.InitFromCurrent( 0.0f, 1.0f, INTERP_SPLINE ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_HopwireExplosion::StartPreExplosion( void ) { } //----------------------------------------------------------------------------- // Hopwire client class //----------------------------------------------------------------------------- class C_GrenadeHopwire : public C_BaseGrenade { DECLARE_CLASS( C_GrenadeHopwire, C_BaseGrenade ); DECLARE_CLIENTCLASS(); public: C_GrenadeHopwire( void ); virtual int DrawModel( int flags ); virtual void OnDataChanged( DataUpdateType_t updateType ); virtual void ReceiveMessage( int classID, bf_read &msg ); private: C_HopwireExplosion m_ExplosionEffect; // Explosion effect information and drawing }; IMPLEMENT_CLIENTCLASS_DT( C_GrenadeHopwire, DT_GrenadeHopwire, CGrenadeHopwire ) END_RECV_TABLE() #define HOPWIRE_START_EXPLOSION 0 #define HOPWIRE_STOP_EXPLOSION 1 #define HOPWIRE_START_PRE_EXPLOSION 2 //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- C_GrenadeHopwire::C_GrenadeHopwire( void ) { m_ExplosionEffect.SetActive( false ); } //----------------------------------------------------------------------------- // Purpose: Receive messages from the server // Input : classID - class to receive the message // &msg - message in question //----------------------------------------------------------------------------- void C_GrenadeHopwire::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 HOPWIRE_START_EXPLOSION: { m_ExplosionEffect.SetActive(); m_ExplosionEffect.SetOwner( this ); m_ExplosionEffect.StartExplosion(); } break; case HOPWIRE_STOP_EXPLOSION: { m_ExplosionEffect.StopExplosion(); } break; default: break; } } //----------------------------------------------------------------------------- // Purpose: // Input : updateType - //----------------------------------------------------------------------------- void C_GrenadeHopwire::OnDataChanged( DataUpdateType_t updateType ) { BaseClass::OnDataChanged( updateType ); m_ExplosionEffect.Update(); } //----------------------------------------------------------------------------- // Purpose: // Input : flags - //----------------------------------------------------------------------------- int C_GrenadeHopwire::DrawModel( int flags ) { if ( m_ExplosionEffect.IsActive() ) return 1; return BaseClass::DrawModel( flags ); }