//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Bugbait weapon to summon and direct antlions
//
//=============================================================================//

#include "cbase.h"
#include "grenade_bugbait.h"
#include "decals.h"
#include "smoke_trail.h"
#include "soundent.h"
#include "engine/IEngineSound.h"
#include "npc_bullseye.h"
#include "entitylist.h"
#include "npc_combine.h"
#include "antlion_maker.h"
#include "eventqueue.h"

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

// Setup the sensor list template

CEntityClassList<CBugBaitSensor> g_BugBaitSensorList;
template <> CBugBaitSensor *CEntityClassList<CBugBaitSensor>::m_pClassList = NULL;

CBugBaitSensor* GetBugBaitSensorList()
{
	return g_BugBaitSensorList.m_pClassList;
}

CBugBaitSensor::CBugBaitSensor( void )
{
	g_BugBaitSensorList.Insert( this );
}

CBugBaitSensor::~CBugBaitSensor( void )
{
	g_BugBaitSensorList.Remove( this );
}

BEGIN_DATADESC( CBugBaitSensor )

	// This is re-set up in the constructor
	//DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ),

	DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "Enabled" ),
	DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),

	DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
	DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
	DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),

	// Function Pointers
	DEFINE_OUTPUT( m_OnBaited, "OnBaited" ),

END_DATADESC()

LINK_ENTITY_TO_CLASS( point_bugbait, CBugBaitSensor );

//=============================================================================
// Bugbait grenade
//=============================================================================

#define GRENADE_MODEL "models/weapons/w_bugbait.mdl"

BEGIN_DATADESC( CGrenadeBugBait )

	DEFINE_FIELD( m_flGracePeriodEndsAt, FIELD_TIME ),
	DEFINE_FIELD( m_pSporeTrail, FIELD_CLASSPTR ),

	// Function Pointers
	DEFINE_ENTITYFUNC( BugBaitTouch ),
	DEFINE_THINKFUNC( ThinkBecomeSolid ),

END_DATADESC()


LINK_ENTITY_TO_CLASS( npc_grenade_bugbait, CGrenadeBugBait );

//Radius of the bugbait's effect on other creatures
ConVar bugbait_radius( "bugbait_radius", "512" );
ConVar bugbait_hear_radius( "bugbait_hear_radius", "2500" );
ConVar bugbait_distract_time( "bugbait_distract_time", "5" );
ConVar bugbait_grenade_radius( "bugbait_grenade_radius", "150" );

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGrenadeBugBait::Spawn( void )
{
	Precache();

	SetModel( GRENADE_MODEL );
	SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
	SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_DEFAULT );
	SetSolid( SOLID_BBOX ); 

	UTIL_SetSize( this, Vector( -2, -2, -2), Vector( 2, 2, 2 ) );

	SetTouch( &CGrenadeBugBait::BugBaitTouch );

	m_takedamage = DAMAGE_NO;

	m_pSporeTrail = NULL;

	/*
	m_pSporeTrail = SporeTrail::CreateSporeTrail();
	
	m_pSporeTrail->m_bEmit				= true;
	m_pSporeTrail->m_flSpawnRate		= 100.0f;
	m_pSporeTrail->m_flParticleLifetime	= 1.0f;
	m_pSporeTrail->m_flStartSize		= 1.0f;
	m_pSporeTrail->m_flEndSize			= 1.0f;
	m_pSporeTrail->m_flSpawnRadius		= 8.0f;

	m_pSporeTrail->m_vecEndColor		= Vector( 0, 0, 0 );
	*/
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGrenadeBugBait::Precache( void )
{
	PrecacheModel( GRENADE_MODEL );

	PrecacheScriptSound( "GrenadeBugBait.Splat" );

	BaseClass::Precache();
}

#define	NUM_SPLASHES	6

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGrenadeBugBait::BugBaitTouch( CBaseEntity *pOther )
{
	Assert( pOther );
	if ( !pOther->IsSolid() )
		return;

	if ( m_pSporeTrail != NULL )
	{
		m_pSporeTrail->m_bEmit = false;
	}

	//Do effect for the hit
	SporeExplosion *pSporeExplosion = SporeExplosion::CreateSporeExplosion();

	if ( pSporeExplosion )
	{
		Vector	dir = -GetAbsVelocity();
		VectorNormalize( dir );

		QAngle	angles;
		VectorAngles( dir, angles );

		pSporeExplosion->SetLocalAngles( angles );
		pSporeExplosion->SetLocalOrigin( GetAbsOrigin() );
		pSporeExplosion->m_flSpawnRate			= 8.0f;
		pSporeExplosion->m_flParticleLifetime	= 2.0f;
		pSporeExplosion->SetRenderColor( 0.0f, 0.5f, 0.25f, 0.15f );

		pSporeExplosion->m_flStartSize = 32.0f;
		pSporeExplosion->m_flEndSize = 64.0f;
		pSporeExplosion->m_flSpawnRadius = 32.0f;

		pSporeExplosion->SetLifetime( bugbait_distract_time.GetFloat() );
	}

	trace_t	tr;
	Vector traceDir = GetAbsVelocity();

	VectorNormalize( traceDir );

	UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + traceDir * 64, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );

	if ( tr.fraction < 1.0f )
	{
		UTIL_DecalTrace( &tr, "BeerSplash" );	//TODO: Use real decal
	}

	//Make a splat sound
	CPASAttenuationFilter filter( this );
	EmitSound( filter, entindex(), "GrenadeBugBait.Splat" );

	//Make sure we want to call antlions
	if ( ActivateBugbaitTargets( GetThrower(), GetAbsOrigin(), false ) == false )
	{
		//Alert any antlions around
		CSoundEnt::InsertSound( SOUND_BUGBAIT, GetAbsOrigin(), bugbait_hear_radius.GetInt(), bugbait_distract_time.GetFloat(), GetThrower() );
	}

	// Tell all spawners to now fight to this position
	g_AntlionMakerManager.BroadcastFightGoal( GetAbsOrigin() );

	//Go away
	UTIL_Remove( this );
}

//-----------------------------------------------------------------------------
// Purpose: Activate any nearby bugbait targets
//			Returns true if the bugbait target wants to suppress the call.
//-----------------------------------------------------------------------------
bool CGrenadeBugBait::ActivateBugbaitTargets( CBaseEntity *pOwner, Vector vecOrigin, bool bSqueezed )
{
	//Attempt to activate any spawners in a radius around the bugbait
	CBaseEntity*	pList[100];
	Vector			delta( bugbait_grenade_radius.GetFloat(), bugbait_grenade_radius.GetFloat(), bugbait_grenade_radius.GetFloat() );
	bool			suppressCall = false;

	int count = UTIL_EntitiesInBox( pList, 100, vecOrigin - delta, vecOrigin + delta, 0 );
	
	// If the bugbait's been thrown, look for nearby targets to affect
	if ( !bSqueezed )
	{
		for ( int i = 0; i < count; i++ )
		{
			// If close enough, make combine soldiers freak out when hit
			if ( UTIL_DistApprox( pList[i]->WorldSpaceCenter(), vecOrigin ) < bugbait_grenade_radius.GetFloat() )
			{
				// Must be a soldier
				if ( FClassnameIs( pList[i], "npc_combine_s") )
				{
					CNPC_Combine *pCombine = assert_cast<CNPC_Combine *>(pList[i]);

					if ( pCombine != NULL )
					{
						trace_t tr;
						UTIL_TraceLine( vecOrigin, pCombine->EyePosition(), MASK_ALL, pOwner, COLLISION_GROUP_NONE, &tr);

						if ( tr.fraction == 1.0 || tr.m_pEnt == pCombine )
						{
							// Randomize the start time a little so multiple combine hit by 
							// the same bugbait don't all dance in synch.
							g_EventQueue.AddEvent( pCombine, "HitByBugbait", RandomFloat(0, 0.5), pOwner, pOwner );
						}
					}
				}
			}
		}

	}
	// Iterate over all sensors to see if they detected this impact
	for ( CBugBaitSensor *pSensor = GetBugBaitSensorList(); pSensor != NULL; pSensor = pSensor->m_pNext )
	{
		if ( pSensor == NULL )
			continue;

		if ( pSensor->IsDisabled() )
			continue;

		if ( bSqueezed && pSensor->DetectsSqueeze() == false )
			continue;

		if ( !bSqueezed && pSensor->DetectsThrown() == false )
			continue;

		//Make sure we're within range of the sensor
		if ( pSensor->GetRadius() > ( pSensor->GetAbsOrigin() - vecOrigin ).Length() )
		{
			//Tell the sensor it's been hit
			if ( pSensor->Baited( pOwner ) )
			{
				//If we're suppressing the call to antlions, then don't make a bugbait sound
				if ( pSensor->SuppressCall() )
				{
					suppressCall = true;
				}
			}
		}
	}

	return suppressCall;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGrenadeBugBait::ThinkBecomeSolid( void )
{
	SetThink( NULL );
	RemoveSolidFlags( FSOLID_NOT_SOLID );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : duration - 
//-----------------------------------------------------------------------------
void CGrenadeBugBait::SetGracePeriod( float duration )
{
	SetThink( &CGrenadeBugBait::ThinkBecomeSolid );
	SetNextThink( gpGlobals->curtime + duration );

	// Become unsolid	
	AddSolidFlags( FSOLID_NOT_SOLID );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &position - 
//			&angles - 
//			&velocity - 
//			&angVelocity - 
//			*owner - 
// Output : CBaseGrenade
//-----------------------------------------------------------------------------
CGrenadeBugBait *BugBaitGrenade_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const QAngle &angVelocity, CBaseEntity *owner )
{
	CGrenadeBugBait *pGrenade = (CGrenadeBugBait *) CBaseEntity::Create( "npc_grenade_bugbait", position, angles, owner );
	
	if ( pGrenade != NULL )
	{
		pGrenade->SetLocalAngularVelocity( angVelocity );
		pGrenade->SetAbsVelocity( velocity );
		pGrenade->SetThrower( ToBaseCombatCharacter( owner ) );
	}

	return pGrenade;
}

