You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
351 lines
8.4 KiB
351 lines
8.4 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
#include "cbase.h"
|
||
|
#include "c_gasoline_blob.h"
|
||
|
#include "gasoline_shared.h"
|
||
|
#include "engine/IEngineSound.h"
|
||
|
#include "clienteffectprecachesystem.h"
|
||
|
|
||
|
static CUtlLinkedList<C_GasolineBlob*, int> g_GasolineBlobs;
|
||
|
|
||
|
// If multiple blobs are within this distance to each other, then only one will
|
||
|
// play a sound.
|
||
|
#define BLOB_SOUND_RELATED_DISTANCE 600
|
||
|
|
||
|
|
||
|
#define PUDDLE_START_SIZE 35
|
||
|
#define PUDDLE_END_SIZE 65
|
||
|
#define PUDDLE_GROW_TIME 0.5
|
||
|
|
||
|
#define PUDDLE_FADE_TIME 1.0
|
||
|
|
||
|
|
||
|
CLIENTEFFECT_REGISTER_BEGIN( PrecacheGasolineBlob )
|
||
|
CLIENTEFFECT_MATERIAL( "decals/puddle" )
|
||
|
CLIENTEFFECT_REGISTER_END()
|
||
|
|
||
|
// ------------------------------------------------------------------------------------------------ //
|
||
|
// CGasolineEmitter.
|
||
|
// ------------------------------------------------------------------------------------------------ //
|
||
|
|
||
|
CSmartPtr<CGasolineEmitter> CGasolineEmitter::Create( C_GasolineBlob *pBlob )
|
||
|
{
|
||
|
CGasolineEmitter *pEmitter = new CGasolineEmitter;
|
||
|
|
||
|
pEmitter->m_pBlob = pBlob;
|
||
|
|
||
|
pEmitter->m_hFireMaterial = pEmitter->GetPMaterial( "particle/fire" );
|
||
|
pEmitter->m_hUnlitMaterial = pEmitter->GetPMaterial( "sprites/env_particles" );
|
||
|
|
||
|
pEmitter->m_Timer.Init( 40 );
|
||
|
|
||
|
return pEmitter;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CGasolineEmitter::UpdateFire( float frametime )
|
||
|
{
|
||
|
float flLifetime = gpGlobals->curtime - m_pBlob->m_flCreateTime;
|
||
|
|
||
|
float litPercent = 1;
|
||
|
if ( m_pBlob->IsLit() )
|
||
|
{
|
||
|
litPercent = 1 - (flLifetime / m_pBlob->m_flMaxLifetime);
|
||
|
if ( litPercent <= 0 )
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Don't show a burn effect for a blob that hasn't hit anything yet.
|
||
|
// If you do, it tends to make the flamethrower effect look weird.
|
||
|
if ( !m_pBlob->IsStopped() )
|
||
|
return;
|
||
|
|
||
|
// Make a coordinate system in which to spawn the particles. It
|
||
|
Vector vUp, vRight;
|
||
|
vUp.Init();
|
||
|
vRight.Init();
|
||
|
if ( m_pBlob->IsStopped() )
|
||
|
{
|
||
|
QAngle angles;
|
||
|
VectorAngles( m_pBlob->GetSurfaceNormal(), angles );
|
||
|
AngleVectors( angles, NULL, &vRight, &vUp );
|
||
|
}
|
||
|
|
||
|
PMaterialHandle hMaterial = m_hFireMaterial;
|
||
|
float flParticleLifetime = 1;
|
||
|
float flRadius = 7;
|
||
|
unsigned char uchColor[4] = { 255, 128, 0, 128 };
|
||
|
float flMaxZVel = 29;
|
||
|
|
||
|
|
||
|
float curDelta = frametime;
|
||
|
while ( m_Timer.NextEvent( curDelta ) )
|
||
|
{
|
||
|
// Based on how close we are to expiring, show less particles.
|
||
|
if ( RandomFloat( 0, 1 ) > litPercent )
|
||
|
continue;
|
||
|
|
||
|
Vector vPos = m_pBlob->GetAbsOrigin();
|
||
|
if ( m_pBlob->IsStopped() )
|
||
|
{
|
||
|
float flAngle = RandomFloat( 0, M_PI * 2 );
|
||
|
float flDist = RandomFloat( 0, GASOLINE_BLOB_RADIUS );
|
||
|
vPos += vRight * (cos( flAngle ) * flDist);
|
||
|
vPos += vUp * ( sin( flAngle ) * flDist );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vPos += RandomVector( -GASOLINE_BLOB_RADIUS, GASOLINE_BLOB_RADIUS );
|
||
|
}
|
||
|
|
||
|
SimpleParticle *pParticle = AddSimpleParticle( hMaterial, vPos, flParticleLifetime, flRadius );
|
||
|
if ( pParticle )
|
||
|
{
|
||
|
pParticle->m_uchColor[0] = uchColor[0];
|
||
|
pParticle->m_uchColor[1] = uchColor[1];
|
||
|
pParticle->m_uchColor[2] = uchColor[2];
|
||
|
|
||
|
pParticle->m_uchEndAlpha = 0;
|
||
|
pParticle->m_uchStartAlpha = uchColor[3];
|
||
|
|
||
|
pParticle->m_vecVelocity.x = RandomFloat( -2, 2 );
|
||
|
pParticle->m_vecVelocity.y = RandomFloat( -2, 2 );
|
||
|
pParticle->m_vecVelocity.z = RandomFloat( 3, flMaxZVel );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// ------------------------------------------------------------------------------------------------ //
|
||
|
// C_GasolineBlob.
|
||
|
// ------------------------------------------------------------------------------------------------ //
|
||
|
|
||
|
IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_GasolineBlob, DT_GasolineBlob, CGasolineBlob )
|
||
|
RecvPropInt( RECVINFO( m_BlobFlags ) ),
|
||
|
RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
|
||
|
RecvPropInt( RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent ),
|
||
|
RecvPropFloat( RECVINFO( m_flLitStartTime ) ),
|
||
|
RecvPropFloat( RECVINFO( m_flCreateTime ) ),
|
||
|
RecvPropFloat( RECVINFO( m_flMaxLifetime ) ),
|
||
|
RecvPropInt( RECVINFO( m_iTeamNum ) ),
|
||
|
RecvPropVector( RECVINFO( m_vSurfaceNormal ) )
|
||
|
END_RECV_TABLE()
|
||
|
|
||
|
|
||
|
C_GasolineBlob::C_GasolineBlob()
|
||
|
{
|
||
|
m_pEmitter = CGasolineEmitter::Create( this );
|
||
|
m_vSurfaceNormal.Init();
|
||
|
m_flLitStartTime = 0;
|
||
|
m_bSoundOn = false;
|
||
|
g_GasolineBlobs.AddToTail( this );
|
||
|
m_flPuddleSize = PUDDLE_START_SIZE;
|
||
|
m_flPuddleFade = 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
C_GasolineBlob::~C_GasolineBlob()
|
||
|
{
|
||
|
g_GasolineBlobs.FindAndRemove( this );
|
||
|
StopSound();
|
||
|
|
||
|
// If a bunch of nearby blobs weren't playing a sound because we were, have them start their sound now.
|
||
|
FOR_EACH_LL( g_GasolineBlobs, i )
|
||
|
{
|
||
|
C_GasolineBlob *pBlob = g_GasolineBlobs[i];
|
||
|
|
||
|
if ( pBlob->IsSoundRelatedTo( this ) )
|
||
|
pBlob->CheckStartSound();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool C_GasolineBlob::IsLit() const
|
||
|
{
|
||
|
return (m_BlobFlags & BLOBFLAG_LIT) != 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool C_GasolineBlob::IsStopped() const
|
||
|
{
|
||
|
return (m_BlobFlags & BLOBFLAG_STOPPED) != 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
const Vector& C_GasolineBlob::GetSurfaceNormal() const
|
||
|
{
|
||
|
return m_vSurfaceNormal;
|
||
|
}
|
||
|
|
||
|
|
||
|
float C_GasolineBlob::GetLitStartTime() const
|
||
|
{
|
||
|
return m_flLitStartTime;
|
||
|
}
|
||
|
|
||
|
|
||
|
void C_GasolineBlob::OnDataChanged( DataUpdateType_t type )
|
||
|
{
|
||
|
BaseClass::OnDataChanged( type );
|
||
|
|
||
|
if ( type == DATA_UPDATE_CREATED )
|
||
|
{
|
||
|
SetNextClientThink( CLIENT_THINK_ALWAYS );
|
||
|
}
|
||
|
|
||
|
CheckStartSound();
|
||
|
}
|
||
|
|
||
|
|
||
|
void C_GasolineBlob::ClientThink()
|
||
|
{
|
||
|
if ( m_pEmitter.IsValid() )
|
||
|
m_pEmitter->UpdateFire( gpGlobals->frametime );
|
||
|
|
||
|
// Grow the puddle a little.
|
||
|
if ( IsStopped() )
|
||
|
{
|
||
|
if ( IsLit() )
|
||
|
{
|
||
|
// Fade out after we get lit.
|
||
|
m_flPuddleFade -= gpGlobals->frametime / PUDDLE_FADE_TIME;
|
||
|
m_flPuddleFade = MAX( m_flPuddleFade, 0 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Grow the puddle until it's at its max size.
|
||
|
m_flPuddleSize += gpGlobals->frametime * ( PUDDLE_END_SIZE - PUDDLE_START_SIZE ) / PUDDLE_GROW_TIME;
|
||
|
m_flPuddleSize = MIN( m_flPuddleSize, PUDDLE_END_SIZE );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool C_GasolineBlob::ShouldDraw()
|
||
|
{
|
||
|
return IsStopped() && (m_flPuddleFade > 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
int C_GasolineBlob::DrawModel( int flags )
|
||
|
{
|
||
|
// Generate a basis.
|
||
|
QAngle angles;
|
||
|
VectorAngles( m_vSurfaceNormal, angles );
|
||
|
|
||
|
Vector vRight, vUp;
|
||
|
AngleVectors( angles, NULL, &vRight, &vUp );
|
||
|
|
||
|
|
||
|
float flAlpha = m_flPuddleFade * RemapVal( m_flPuddleSize, PUDDLE_START_SIZE, PUDDLE_END_SIZE, 0, 1 );
|
||
|
if ( flAlpha <= 0 )
|
||
|
return 0;
|
||
|
|
||
|
|
||
|
// Draw the puddle.
|
||
|
IMaterial *pMat = materials->FindMaterial( "decals/puddle", TEXTURE_GROUP_DECAL );
|
||
|
IMesh *pMesh = materials->GetDynamicMesh( true, NULL, NULL, pMat );
|
||
|
CMeshBuilder mb;
|
||
|
mb.Begin( pMesh, MATERIAL_QUADS, 1 );
|
||
|
|
||
|
Vector v;
|
||
|
|
||
|
v = GetAbsOrigin() + m_vSurfaceNormal + vRight*m_flPuddleSize - vUp*m_flPuddleSize;
|
||
|
mb.Position3f( v.x, v.y, v.z );
|
||
|
mb.Color4f( 1, 1, 1, flAlpha );
|
||
|
mb.Normal3f( VectorExpand( m_vSurfaceNormal ) );
|
||
|
mb.TexCoord2f( 0, 1, 0 );
|
||
|
mb.AdvanceVertex();
|
||
|
|
||
|
v = GetAbsOrigin() + m_vSurfaceNormal + vRight*m_flPuddleSize + vUp*m_flPuddleSize;
|
||
|
mb.Position3f( v.x, v.y, v.z );
|
||
|
mb.Color4f( 1, 1, 1, flAlpha );
|
||
|
mb.Normal3f( VectorExpand( m_vSurfaceNormal ) );
|
||
|
mb.TexCoord2f( 0, 1, 1 );
|
||
|
mb.AdvanceVertex();
|
||
|
|
||
|
v = GetAbsOrigin() + m_vSurfaceNormal - vRight*m_flPuddleSize + vUp*m_flPuddleSize;
|
||
|
mb.Position3f( v.x, v.y, v.z );
|
||
|
mb.Color4f( 1, 1, 1, flAlpha );
|
||
|
mb.Normal3f( VectorExpand( m_vSurfaceNormal ) );
|
||
|
mb.TexCoord2f( 0, 0, 1 );
|
||
|
mb.AdvanceVertex();
|
||
|
|
||
|
v = GetAbsOrigin() + m_vSurfaceNormal - vRight*m_flPuddleSize - vUp*m_flPuddleSize;
|
||
|
mb.Position3f( v.x, v.y, v.z );
|
||
|
mb.Color4f( 1, 1, 1, flAlpha );
|
||
|
mb.Normal3f( VectorExpand( m_vSurfaceNormal ) );
|
||
|
mb.TexCoord2f( 0, 0, 0 );
|
||
|
mb.AdvanceVertex();
|
||
|
|
||
|
mb.End( false, true );
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool C_GasolineBlob::IsSoundRelatedTo( const C_GasolineBlob *pBlob ) const
|
||
|
{
|
||
|
return pBlob->GetAbsOrigin().DistTo( GetAbsOrigin() ) < BLOB_SOUND_RELATED_DISTANCE;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool C_GasolineBlob::IsPlayingBurningSound() const
|
||
|
{
|
||
|
return m_bSoundOn;
|
||
|
}
|
||
|
|
||
|
|
||
|
void C_GasolineBlob::CheckStartSound()
|
||
|
{
|
||
|
if ( IsPlayingBurningSound() || (m_BlobFlags & BLOBFLAG_STOPPED) == 0 || !IsLit() )
|
||
|
return;
|
||
|
|
||
|
|
||
|
// First, make sure no nearby blob is playing the sound.
|
||
|
FOR_EACH_LL( g_GasolineBlobs, i )
|
||
|
{
|
||
|
C_GasolineBlob *pBlob = g_GasolineBlobs[i];
|
||
|
|
||
|
if ( pBlob != this && pBlob->IsSoundRelatedTo( this ) )
|
||
|
{
|
||
|
// If it's already playing a sound, then don't start our sound.
|
||
|
if ( pBlob->IsPlayingBurningSound() )
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
StartSound();
|
||
|
}
|
||
|
|
||
|
|
||
|
void C_GasolineBlob::StartSound()
|
||
|
{
|
||
|
if ( !m_bSoundOn )
|
||
|
{
|
||
|
EmitSound( "GasolineBlob.FlameSound" );
|
||
|
|
||
|
m_bSoundOn = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void C_GasolineBlob::StopSound()
|
||
|
{
|
||
|
if ( m_bSoundOn )
|
||
|
{
|
||
|
BaseClass::StopSound( "GasolineBlob.FlameSound" );
|
||
|
m_bSoundOn = false;
|
||
|
}
|
||
|
}
|
||
|
|