Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

350 lines
8.4 KiB

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