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