//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Game-specific impact effect hooks
//
//=============================================================================//
#include "cbase.h"
#include "fx.h"
#include "c_te_effect_dispatch.h"
#include "tier0/vprof.h"
#include "fx_line.h"
#include "fx_quad.h"
#include "view.h"
#include "particles_localspace.h"
#include "dlight.h"
#include "iefx.h"
#include "ClientEffectPrecacheSystem.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

extern Vector GetTracerOrigin( const CEffectData &data );
extern void FX_TracerSound( const Vector &start, const Vector &end, int iTracerType );

extern ConVar muzzleflash_light;


CLIENTEFFECT_REGISTER_BEGIN( PrecacheTracers )
CLIENTEFFECT_MATERIAL( "effects/ar2ground2" )
CLIENTEFFECT_MATERIAL( "effects/gunshiptracer" )
CLIENTEFFECT_MATERIAL( "effects/combinemuzzle1" )
CLIENTEFFECT_MATERIAL( "effects/combinemuzzle2_nocull" )
CLIENTEFFECT_REGISTER_END()

//-----------------------------------------------------------------------------
// Purpose: Gunship's Tracer
//-----------------------------------------------------------------------------
void GunshipTracerCallback( const CEffectData &data )
{
	float flVelocity = data.m_flScale;
	bool bWhiz = (data.m_fFlags & TRACER_FLAG_WHIZ);
	FX_GunshipTracer( (Vector&)data.m_vStart, (Vector&)data.m_vOrigin, flVelocity, bWhiz );
}

DECLARE_CLIENT_EFFECT( "GunshipTracer", GunshipTracerCallback );


//-----------------------------------------------------------------------------
// Purpose: Strider's Tracer
//-----------------------------------------------------------------------------
void StriderTracerCallback( const CEffectData &data )
{
	float flVelocity = data.m_flScale;
	bool bWhiz = (data.m_fFlags & TRACER_FLAG_WHIZ);
	FX_StriderTracer( (Vector&)data.m_vStart, (Vector&)data.m_vOrigin, flVelocity, bWhiz );
}

DECLARE_CLIENT_EFFECT( "StriderTracer", StriderTracerCallback );


//-----------------------------------------------------------------------------
// Purpose: Gauss Gun's Tracer
//-----------------------------------------------------------------------------
void GaussTracerCallback( const CEffectData &data )
{
	float flVelocity = data.m_flScale;
	bool bWhiz = (data.m_fFlags & TRACER_FLAG_WHIZ);
	FX_GaussTracer( (Vector&)data.m_vStart, (Vector&)data.m_vOrigin, flVelocity, bWhiz );
}

DECLARE_CLIENT_EFFECT( "GaussTracer", GaussTracerCallback );


//-----------------------------------------------------------------------------
// Purpose: Airboat gun tracers 
//-----------------------------------------------------------------------------
void AirboatGunHeavyTracerCallback( const CEffectData &data )
{
	// Grab the data
	Vector vecStart = GetTracerOrigin( data );
	float flVelocity = data.m_flScale;

	// Use default velocity if none specified
	if ( !flVelocity )
	{
		flVelocity = 8000;
	}

	//Get out shot direction and length
	Vector vecShotDir;
	VectorSubtract( data.m_vOrigin, vecStart, vecShotDir );
	float flTotalDist = VectorNormalize( vecShotDir );

	// Don't make small tracers
	if ( flTotalDist <= 64 )
		return;

	float flLength = random->RandomFloat( 300.0f, 400.0f );
	float flLife = ( flTotalDist + flLength ) / flVelocity;	//NOTENOTE: We want the tail to finish its run as well
	
	// Add it
	FX_AddDiscreetLine( vecStart, vecShotDir, flVelocity, flLength, flTotalDist, 5.0f, flLife, "effects/gunshiptracer" );
}

DECLARE_CLIENT_EFFECT( "AirboatGunHeavyTracer", AirboatGunHeavyTracerCallback );

//-----------------------------------------------------------------------------
// Purpose: Airboat gun tracers 
//-----------------------------------------------------------------------------
void AirboatGunTracerCallback( const CEffectData &data )
{
	// Grab the data
	Vector vecStart = GetTracerOrigin( data );
	float flVelocity = data.m_flScale;

	// Use default velocity if none specified
	if ( !flVelocity )
	{
		flVelocity = 10000;
	}

	//Get out shot direction and length
	Vector vecShotDir;
	VectorSubtract( data.m_vOrigin, vecStart, vecShotDir );
	float flTotalDist = VectorNormalize( vecShotDir );

	// Don't make small tracers
	if ( flTotalDist <= 64 )
		return;

	float flLength = random->RandomFloat( 256.0f, 384.0f );
	float flLife = ( flTotalDist + flLength ) / flVelocity;	//NOTENOTE: We want the tail to finish its run as well
	
	// Add it
	FX_AddDiscreetLine( vecStart, vecShotDir, flVelocity, flLength, flTotalDist, 2.0f, flLife, "effects/gunshiptracer" );
}

DECLARE_CLIENT_EFFECT( "AirboatGunTracer", AirboatGunTracerCallback );


//-----------------------------------------------------------------------------
// Purpose: Airboat gun tracers 
//-----------------------------------------------------------------------------
void HelicopterTracerCallback( const CEffectData &data )
{
	// Grab the data
	Vector vecStart = GetTracerOrigin( data );
	float flVelocity = data.m_flScale;

	// Use default velocity if none specified
	if ( !flVelocity )
	{
		flVelocity = 8000;
	}

	//Get out shot direction and length
	Vector vecShotDir;
	VectorSubtract( data.m_vOrigin, vecStart, vecShotDir );
	float flTotalDist = VectorNormalize( vecShotDir );

	// Don't make small tracers
	if ( flTotalDist <= 256 )
		return;

	float flLength = random->RandomFloat( 256.0f, 384.0f );
	float flLife = ( flTotalDist + flLength ) / flVelocity;	//NOTENOTE: We want the tail to finish its run as well
	
	// Add it
	FX_AddDiscreetLine( vecStart, vecShotDir, flVelocity, flLength, flTotalDist, 5.0f, flLife, "effects/gunshiptracer" );

	if (data.m_fFlags & TRACER_FLAG_WHIZ)
	{
		FX_TracerSound( vecStart, data.m_vOrigin, TRACER_TYPE_GUNSHIP );
	}
}

DECLARE_CLIENT_EFFECT( "HelicopterTracer", HelicopterTracerCallback );

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : start - 
//			end - 
//-----------------------------------------------------------------------------
void FX_PlayerAR2Tracer( const Vector &start, const Vector &end )
{
	VPROF_BUDGET( "FX_PlayerAR2Tracer", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
	
	Vector	shotDir, dStart, dEnd;
	float	length;

	//Find the direction of the tracer
	VectorSubtract( end, start, shotDir );
	length = VectorNormalize( shotDir );

	//We don't want to draw them if they're too close to us
	if ( length < 128 )
		return;

	//Randomly place the tracer along this line, with a random length
	VectorMA( start, random->RandomFloat( 0.0f, 8.0f ), shotDir, dStart );
	VectorMA( dStart, min( length, random->RandomFloat( 256.0f, 1024.0f ) ), shotDir, dEnd );

	//Create the line
	CFXStaticLine *tracerLine = new CFXStaticLine( "Tracer", dStart, dEnd, random->RandomFloat( 6.0f, 12.0f ), 0.01f, "effects/gunshiptracer", 0 );
	assert( tracerLine );

	//Throw it into the list
	clienteffects->AddEffect( tracerLine );
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : start - 
//			end - 
//			velocity - 
//			makeWhiz - 
//-----------------------------------------------------------------------------
void FX_AR2Tracer( Vector& start, Vector& end, int velocity, bool makeWhiz )
{
	VPROF_BUDGET( "FX_AR2Tracer", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
	
	//Don't make small tracers
	float dist;
	Vector dir;

	VectorSubtract( end, start, dir );
	dist = VectorNormalize( dir );

	// Don't make short tracers.
	if ( dist < 128 )
		return;

	float length = random->RandomFloat( 128.0f, 256.0f );
	float life = ( dist + length ) / velocity;	//NOTENOTE: We want the tail to finish its run as well
	
	//Add it
	FX_AddDiscreetLine( start, dir, velocity, length, dist, random->RandomFloat( 0.5f, 1.5f ), life, "effects/gunshiptracer" );

	if( makeWhiz )
	{
		FX_TracerSound( start, end, TRACER_TYPE_GUNSHIP );	
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void AR2TracerCallback( const CEffectData &data )
{
	C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
	
	if ( player == NULL )
		return;

	// Grab the data
	Vector vecStart = GetTracerOrigin( data );
	float flVelocity = data.m_flScale;
	bool bWhiz = (data.m_fFlags & TRACER_FLAG_WHIZ);
	int iEntIndex = data.m_nEntIndex;

	if ( iEntIndex && iEntIndex == player->index )
	{
		Vector	foo = data.m_vStart;
		QAngle	vangles;
		Vector	vforward, vright, vup;

		engine->GetViewAngles( vangles );
		AngleVectors( vangles, &vforward, &vright, &vup );

		VectorMA( data.m_vStart, 4, vright, foo );
		foo[2] -= 0.5f;

		FX_PlayerAR2Tracer( foo, (Vector&)data.m_vOrigin );
		return;
	}
	
	// Use default velocity if none specified
	if ( !flVelocity )
	{
		flVelocity = 8000;
	}

	// Do tracer effect
	FX_AR2Tracer( (Vector&)vecStart, (Vector&)data.m_vOrigin, flVelocity, bWhiz );
}

DECLARE_CLIENT_EFFECT( "AR2Tracer", AR2TracerCallback );

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &data - 
//-----------------------------------------------------------------------------
void AR2ExplosionCallback( const CEffectData &data )
{
	float lifetime = random->RandomFloat( 0.4f, 0.75f );

	// Ground splash
	FX_AddQuad( data.m_vOrigin, 
				data.m_vNormal, 
				data.m_flRadius, 
				data.m_flRadius * 4.0f,
				0.85f, 
				1.0f,
				0.0f,
				0.25f,
				random->RandomInt( 0, 360 ), 
				random->RandomFloat( -4, 4 ), 
				Vector( 1.0f, 1.0f, 1.0f ), 
				lifetime,
				"effects/combinemuzzle1",
				(FXQUAD_BIAS_SCALE|FXQUAD_BIAS_ALPHA) );

	Vector	vRight, vUp;
	VectorVectors( data.m_vNormal, vRight, vUp );

	Vector	start, end;
	
	float radius = data.m_flRadius * 0.15f;

	unsigned int flags;

	// Base vertical shaft
	FXLineData_t lineData;

	start = data.m_vOrigin;
	end = start + ( data.m_vNormal * random->RandomFloat( radius*2.0f, radius*4.0f ) );

	if ( random->RandomInt( 0, 1 ) )
	{
		flags |= FXSTATICLINE_FLIP_HORIZONTAL;
	}
	else
	{
		flags = 0;
	}

	lineData.m_flDieTime = lifetime;
	
	lineData.m_flStartAlpha= 1.0f;
	lineData.m_flEndAlpha = 0.0f;
	
	lineData.m_flStartScale = radius*4;
	lineData.m_flEndScale = radius*5; 

	lineData.m_pMaterial = materials->FindMaterial( "effects/ar2ground2", 0, 0 );

	lineData.m_vecStart = start;
	lineData.m_vecStartVelocity = vec3_origin;

	lineData.m_vecEnd = end;
	lineData.m_vecEndVelocity = data.m_vNormal * random->RandomFloat( 200, 350 );

	FX_AddLine( lineData );

	// Inner filler shaft
	start = data.m_vOrigin;
	end = start + ( data.m_vNormal * random->RandomFloat( 16, radius*0.25f ) );

	if ( random->RandomInt( 0, 1 ) )
	{
		flags |= FXSTATICLINE_FLIP_HORIZONTAL;
	}
	else
	{
		flags = 0;
	}

	lineData.m_flDieTime = lifetime - 0.1f;
	
	lineData.m_flStartAlpha= 1.0f;
	lineData.m_flEndAlpha = 0.0f;
	
	lineData.m_flStartScale = radius*2;
	lineData.m_flEndScale = radius*4; 

	lineData.m_pMaterial = materials->FindMaterial( "effects/ar2ground2", 0, 0 );

	lineData.m_vecStart = start;
	lineData.m_vecStartVelocity = vec3_origin;

	lineData.m_vecEnd = end;
	lineData.m_vecEndVelocity = data.m_vNormal * random->RandomFloat( 64, 128 );

	FX_AddLine( lineData );
}

DECLARE_CLIENT_EFFECT( "AR2Explosion", AR2ExplosionCallback );

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &data - 
//-----------------------------------------------------------------------------
void AR2ImpactCallback( const CEffectData &data )
{
	FX_AddQuad( data.m_vOrigin, 
				data.m_vNormal, 
				random->RandomFloat( 24, 32 ),
				0,
				0.75f, 
				1.0f,
				0.0f,
				0.4f,
				random->RandomInt( 0, 360 ), 
				0,
				Vector( 1.0f, 1.0f, 1.0f ), 
				0.25f, 
				"effects/combinemuzzle2_nocull",
				(FXQUAD_BIAS_SCALE|FXQUAD_BIAS_ALPHA) );
}

DECLARE_CLIENT_EFFECT( "AR2Impact", AR2ImpactCallback );

//-----------------------------------------------------------------------------
// Airboat muzzle flashes
//-----------------------------------------------------------------------------
void MuzzleFlash_Airboat( int entityIndex, int attachmentIndex )
{
	VPROF_BUDGET( "MuzzleFlash_Airboat", VPROF_BUDGETGROUP_PARTICLE_RENDERING );

	matrix3x4_t	matAttachment;
	// If the client hasn't seen this entity yet, bail.
	if ( !FX_GetAttachmentTransform( entityIndex, attachmentIndex, matAttachment ) )
		return;

	CSmartPtr<CLocalSpaceEmitter> pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash", entityIndex, attachmentIndex );

	SimpleParticle *pParticle;
	Vector			forward(1,0,0), offset; //NOTENOTE: All coords are in local space

	float flScale = random->RandomFloat( 0.75f, 2.5f );

	// Flash
	for ( int i = 1; i < 7; i++ )
	{
		offset = (forward * (i*6.0f*flScale));

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), offset );
			
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= 0.01f;

		pParticle->m_vecVelocity.Init();

		pParticle->m_uchColor[0]	= 255;
		pParticle->m_uchColor[1]	= 255;
		pParticle->m_uchColor[2]	= 255;

		pParticle->m_uchStartAlpha	= 255;
		pParticle->m_uchEndAlpha	= 128;

		pParticle->m_uchStartSize	= ( (random->RandomFloat( 6.0f, 8.0f ) * (9-(i))/7) * flScale );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}

	// Tack on the smoke
	pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "sprites/ar2_muzzle1" ), vec3_origin );
		
	if ( pParticle == NULL )
		return;

	pParticle->m_flLifetime		= 0.0f;
	pParticle->m_flDieTime		= 0.05f;

	pParticle->m_vecVelocity.Init();

	pParticle->m_uchColor[0]	= 255;
	pParticle->m_uchColor[1]	= 255;
	pParticle->m_uchColor[2]	= 255;

	pParticle->m_uchStartAlpha	= 255;
	pParticle->m_uchEndAlpha	= 128;

	pParticle->m_uchStartSize	= random->RandomFloat( 16.0f, 24.0f );
	pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
	
	float spokePos = random->RandomInt( 0, 5 );

	pParticle->m_flRoll			= (360.0/6.0f)*spokePos;
	pParticle->m_flRollDelta	= 0.0f;
	
	// Grab the origin out of the transform for the attachment
	Vector		origin;
	MatrixGetColumn( matAttachment, 3, &origin );
	
	if ( muzzleflash_light.GetInt() )
	{
		dlight_t *el = effects->CL_AllocElight( LIGHT_INDEX_MUZZLEFLASH + entityIndex );

		el->origin	= origin;

		el->color.r = 255;
		el->color.g = 192;
		el->color.b = 64;
		el->color.exponent = 5;

		el->radius	= random->RandomInt( 64, 128 );
		el->decay	= el->radius / 0.05f;
		el->die		= gpGlobals->curtime + 0.1f;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &data - 
//-----------------------------------------------------------------------------
void AirboatMuzzleFlashCallback( const CEffectData &data )
{
	MuzzleFlash_Airboat( data.m_nEntIndex, data.m_nAttachmentIndex );
}

DECLARE_CLIENT_EFFECT( "AirboatMuzzleFlash", AirboatMuzzleFlashCallback );

//-----------------------------------------------------------------------------
// Chopper muzzle flashes
//-----------------------------------------------------------------------------
void MuzzleFlash_Chopper( int entityIndex, int attachmentIndex )
{
	VPROF_BUDGET( "MuzzleFlash_Chopper", VPROF_BUDGETGROUP_PARTICLE_RENDERING );

	matrix3x4_t	matAttachment;
	// If the client hasn't seen this entity yet, bail.
	if ( !FX_GetAttachmentTransform( entityIndex, attachmentIndex, matAttachment ) )
		return;
	
	CSmartPtr<CLocalSpaceEmitter> pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash", entityIndex, attachmentIndex );

	SimpleParticle *pParticle;
	Vector			forward(1,0,0), offset; //NOTENOTE: All coords are in local space

	float flScale = random->RandomFloat( 2.5f, 4.5f );

	// Flash
	for ( int i = 1; i < 7; i++ )
	{
		offset = (forward * (i*2.0f*flScale));

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), offset );
			
		if ( pParticle == NULL )
			return;

		pParticle->m_flLifetime		= 0.0f;
		pParticle->m_flDieTime		= random->RandomFloat( 0.05f, 0.1f );

		pParticle->m_vecVelocity.Init();

		pParticle->m_uchColor[0]	= 255;
		pParticle->m_uchColor[1]	= 255;
		pParticle->m_uchColor[2]	= 255;

		pParticle->m_uchStartAlpha	= 255;
		pParticle->m_uchEndAlpha	= 128;

		pParticle->m_uchStartSize	= ( (random->RandomFloat( 6.0f, 8.0f ) * (10-(i))/7) * flScale );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}
	
	// Grab the origin out of the transform for the attachment
	Vector		origin;
	MatrixGetColumn( matAttachment, 3, &origin );	

	if ( muzzleflash_light.GetInt() )
	{
		dlight_t *el = effects->CL_AllocElight( LIGHT_INDEX_MUZZLEFLASH + entityIndex );

		el->origin	= origin;

		el->color.r = 255;
		el->color.g = 192;
		el->color.b = 64;
		el->color.exponent = 6;

		el->radius	= random->RandomInt( 128, 256 );
		el->decay	= el->radius / 0.05f;
		el->die		= gpGlobals->curtime + 0.1f;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &data - 
//-----------------------------------------------------------------------------
void ChopperMuzzleFlashCallback( const CEffectData &data )
{
	MuzzleFlash_Chopper( data.m_nEntIndex, data.m_nAttachmentIndex );
}

DECLARE_CLIENT_EFFECT( "ChopperMuzzleFlash", ChopperMuzzleFlashCallback );



//-----------------------------------------------------------------------------
// Gunship muzzle flashes
//-----------------------------------------------------------------------------
void MuzzleFlash_Gunship( int entityIndex, int attachmentIndex )
{
	VPROF_BUDGET( "MuzzleFlash_Gunship", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
	matrix3x4_t	matAttachment;
	// If the client hasn't seen this entity yet, bail.
	if ( !FX_GetAttachmentTransform( entityIndex, attachmentIndex, matAttachment ) )
		return;

	CSmartPtr<CLocalSpaceEmitter> pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash", entityIndex, attachmentIndex );

	SimpleParticle *pParticle;
	Vector			forward(1,0,0), offset; //NOTENOTE: All coords are in local space

	float flScale = random->RandomFloat( 2.5f, 4.5f );

	// Flash
	offset = (forward * (2.0f*flScale));

	pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "effects/gunshipmuzzle" ), offset );
	if ( pParticle == NULL )
		return;

	pParticle->m_flLifetime		= 0.0f;
	pParticle->m_flDieTime		= random->RandomFloat( 0.05f, 0.1f );

	pParticle->m_vecVelocity.Init();

	pParticle->m_uchColor[0]	= 255;
	pParticle->m_uchColor[1]	= 255;
	pParticle->m_uchColor[2]	= 255;

	pParticle->m_uchStartAlpha	= 255;
	pParticle->m_uchEndAlpha	= 128;

	pParticle->m_uchStartSize	= ( (random->RandomFloat( 6.0f, 8.0f ) * 10.0/7.0) * flScale );
	pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
	pParticle->m_flRoll			= random->RandomInt( 0, 360 );
	pParticle->m_flRollDelta	= 0.0f;
	
	// Grab the origin out of the transform for the attachment
	Vector		origin;
	MatrixGetColumn( matAttachment, 3, &origin );	

	if ( muzzleflash_light.GetInt() )
	{
		dlight_t *el = effects->CL_AllocElight( LIGHT_INDEX_MUZZLEFLASH + entityIndex );

		el->origin	= origin;

		el->color.r = 255;
		el->color.g = 192;
		el->color.b = 64;
		el->color.exponent = 6;

		el->radius	= random->RandomInt( 128, 256 );
		el->decay	= el->radius / 0.05f;
		el->die		= gpGlobals->curtime + 0.1f;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &data - 
//-----------------------------------------------------------------------------
void GunshipMuzzleFlashCallback( const CEffectData &data )
{
	MuzzleFlash_Gunship( data.m_nEntIndex, data.m_nAttachmentIndex );
}

DECLARE_CLIENT_EFFECT( "GunshipMuzzleFlash", GunshipMuzzleFlashCallback );
