//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//

#include "cbase.h"
#include "npc_antlion.h"
#include "antlion_maker.h"
#include "saverestore_utlvector.h"
#include "mapentities.h"
#include "decals.h"
#include "iservervehicle.h"
#include "antlion_dust.h"
#include "smoke_trail.h"

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

CAntlionMakerManager g_AntlionMakerManager( "CAntlionMakerManager" );

static const char *s_pPoolThinkContext = "PoolThinkContext";
static const char *s_pBlockedEffectsThinkContext = "BlockedEffectsThinkContext";
static const char *s_pBlockedCheckContext = "BlockedCheckContext";

ConVar g_debug_antlionmaker( "g_debug_antlionmaker", "0", FCVAR_CHEAT );


#define ANTLION_MAKER_PLAYER_DETECT_RADIUS	512
#define ANTLION_MAKER_BLOCKED_MASS			250.0f		// half the weight of a car
#define ANTLION_MAKE_SPORE_SPAWNRATE		25.0f

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &vFightGoal - 
//-----------------------------------------------------------------------------
void CAntlionMakerManager::BroadcastFightGoal( const Vector &vFightGoal )
{
	CAntlionTemplateMaker *pMaker;

	for ( int i=0; i < m_Makers.Count(); i++ )
	{
		pMaker = m_Makers[i];

		if ( pMaker && pMaker->ShouldHearBugbait() )
		{
			pMaker->SetFightTarget( vFightGoal );
			pMaker->SetChildMoveState( ANTLION_MOVE_FIGHT_TO_GOAL );
			pMaker->UpdateChildren();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pFightGoal - 
//-----------------------------------------------------------------------------
void CAntlionMakerManager::BroadcastFightGoal( CBaseEntity *pFightGoal )
{
	CAntlionTemplateMaker *pMaker;

	for ( int i=0; i < m_Makers.Count(); i++ )
	{
		pMaker = m_Makers[i];

		if ( pMaker && pMaker->ShouldHearBugbait() )
		{
			pMaker->SetFightTarget( pFightGoal );
			pMaker->SetChildMoveState( ANTLION_MOVE_FIGHT_TO_GOAL );
			pMaker->UpdateChildren();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pFightGoal - 
//-----------------------------------------------------------------------------
void CAntlionMakerManager::BroadcastFollowGoal( CBaseEntity *pFollowGoal )
{
	CAntlionTemplateMaker *pMaker;

	for ( int i=0; i < m_Makers.Count(); i++ )
	{
		pMaker = m_Makers[i];

		if ( pMaker && pMaker->ShouldHearBugbait() )
		{
			//pMaker->SetFightTarget( NULL );
			pMaker->SetFollowTarget( pFollowGoal );
			pMaker->SetChildMoveState( ANTLION_MOVE_FOLLOW );
			pMaker->UpdateChildren();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CAntlionMakerManager::GatherMakers( void )
{
	CBaseEntity				*pSearch = NULL;
	CAntlionTemplateMaker	*pMaker;

	m_Makers.Purge();

	// Find these all once
	while ( ( pSearch = gEntList.FindEntityByClassname( pSearch, "npc_antlion_template_maker" ) ) != NULL )
	{
		pMaker = static_cast<CAntlionTemplateMaker *>(pSearch);

		m_Makers.AddToTail( pMaker );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CAntlionMakerManager::LevelInitPostEntity( void )
{
	//Find all antlion makers
	GatherMakers();
}

//-----------------------------------------------------------------------------
// Antlion template maker
//-----------------------------------------------------------------------------

LINK_ENTITY_TO_CLASS( npc_antlion_template_maker, CAntlionTemplateMaker );

//DT Definition
BEGIN_DATADESC( CAntlionTemplateMaker )

	DEFINE_KEYFIELD( m_strSpawnGroup,	FIELD_STRING,	"spawngroup" ),
	DEFINE_KEYFIELD( m_strSpawnTarget,	FIELD_STRING,	"spawntarget" ),
	DEFINE_KEYFIELD( m_flSpawnRadius,	FIELD_FLOAT,	"spawnradius" ),
	DEFINE_KEYFIELD( m_strFightTarget,	FIELD_STRING,	"fighttarget" ),
	DEFINE_KEYFIELD( m_strFollowTarget,	FIELD_STRING,	"followtarget" ),
	DEFINE_KEYFIELD( m_bIgnoreBugbait,	FIELD_BOOLEAN,	"ignorebugbait" ),
	DEFINE_KEYFIELD( m_flVehicleSpawnDistance,	FIELD_FLOAT,	"vehicledistance" ),
	DEFINE_KEYFIELD( m_flWorkerSpawnRate,	FIELD_FLOAT,	"workerspawnrate" ),

	DEFINE_FIELD( m_nChildMoveState,	FIELD_INTEGER ),
	DEFINE_FIELD( m_hFightTarget,		FIELD_EHANDLE ),
	DEFINE_FIELD( m_hProxyTarget,		FIELD_EHANDLE ),
	DEFINE_FIELD( m_hFollowTarget,		FIELD_EHANDLE ),
	DEFINE_FIELD( m_iSkinCount,			FIELD_INTEGER ),
	DEFINE_FIELD( m_flBlockedBumpTime,  FIELD_TIME ),
	DEFINE_FIELD( m_bBlocked,			FIELD_BOOLEAN ),

	DEFINE_UTLVECTOR( m_Children,		FIELD_EHANDLE ),

	DEFINE_KEYFIELD( m_iPool,			FIELD_INTEGER,	"pool_start" ),
	DEFINE_KEYFIELD( m_iMaxPool,		FIELD_INTEGER,	"pool_max" ),
	DEFINE_KEYFIELD( m_iPoolRegenAmount,FIELD_INTEGER,	"pool_regen_amount" ),
	DEFINE_KEYFIELD( m_flPoolRegenTime,	FIELD_FLOAT,	"pool_regen_time" ),

	DEFINE_INPUTFUNC( FIELD_STRING, "SetFightTarget",		InputSetFightTarget ),
	DEFINE_INPUTFUNC( FIELD_STRING, "SetFollowTarget",		InputSetFollowTarget ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"ClearFollowTarget",	InputClearFollowTarget ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"ClearFightTarget",		InputClearFightTarget ),
	DEFINE_INPUTFUNC( FIELD_FLOAT,	"SetSpawnRadius",		InputSetSpawnRadius ),
	DEFINE_INPUTFUNC( FIELD_INTEGER, "AddToPool",			InputAddToPool ),
	DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMaxPool",			InputSetMaxPool ),
	DEFINE_INPUTFUNC( FIELD_INTEGER, "SetPoolRegenAmount",	InputSetPoolRegenAmount ),
	DEFINE_INPUTFUNC( FIELD_FLOAT,	 "SetPoolRegenTime",	InputSetPoolRegenTime ),
	DEFINE_INPUTFUNC( FIELD_STRING,	 "ChangeDestinationGroup",	InputChangeDestinationGroup ),
	DEFINE_OUTPUT( m_OnAllBlocked, "OnAllBlocked" ),

	DEFINE_KEYFIELD( m_bCreateSpores,			FIELD_BOOLEAN,	"createspores" ),

	DEFINE_THINKFUNC( PoolRegenThink ),
	DEFINE_THINKFUNC( FindNodesCloseToPlayer ),
	DEFINE_THINKFUNC( BlockedCheckFunc ),

END_DATADESC()

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CAntlionTemplateMaker::CAntlionTemplateMaker( void )
{
	m_hFightTarget = NULL;
	m_hProxyTarget = NULL;
	m_hFollowTarget = NULL;
	m_nChildMoveState = ANTLION_MOVE_FREE;
	m_iSkinCount = 0;
	m_flBlockedBumpTime = 0.0f;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CAntlionTemplateMaker::~CAntlionTemplateMaker( void )
{
	DestroyProxyTarget();
	m_Children.Purge();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pAnt - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::AddChild( CNPC_Antlion *pAnt )
{
	m_Children.AddToTail( pAnt );
	m_nLiveChildren = m_Children.Count();

	pAnt->SetOwnerEntity( this );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pAnt - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::RemoveChild( CNPC_Antlion *pAnt )
{
	m_Children.FindAndRemove( pAnt );
	m_nLiveChildren = m_Children.Count();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::FixupOrphans( void )
{
	CBaseEntity		*pSearch = NULL;
	CNPC_Antlion	*pAntlion = NULL;

	// Iterate through all antlions and see if there are any orphans
	while ( ( pSearch = gEntList.FindEntityByClassname( pSearch, "npc_antlion" ) ) != NULL )
	{
		pAntlion = dynamic_cast<CNPC_Antlion *>(pSearch);

		// See if it's a live orphan
		if ( pAntlion && pAntlion->GetOwnerEntity() == NULL && pAntlion->IsAlive() )
		{
			// See if its parent was named the same as we are
			if ( stricmp( pAntlion->GetParentSpawnerName(), STRING( GetEntityName() ) ) == 0 )
			{
				// Relink us to this antlion, he's come through a transition and was orphaned
				AddChild( pAntlion );
			}
		}
	}	
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::PrecacheTemplateEntity( CBaseEntity *pEntity )
{
	BaseClass::PrecacheTemplateEntity( pEntity );

	// If we can spawn workers, precache the worker as well.			
	if ( m_flWorkerSpawnRate != 0 )
	{
		pEntity->AddSpawnFlags( SF_ANTLION_WORKER );
		pEntity->Precache();
	}
}	


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::Activate( void )
{
	FixupOrphans();

	BaseClass::Activate();

	// Are we using the pool behavior for coast?
	if ( m_iMaxPool )
	{
		if ( !m_flPoolRegenTime )
		{
			Msg("%s using pool behavior without a specified pool regen time.\n", GetClassname() );
			m_flPoolRegenTime = 0.1;
		}

		// Start up our think cycle unless we're reloading this map (which would reset it)
		if ( m_bDisabled == false && gpGlobals->eLoadType != MapLoad_LoadGame )
		{
			// Start our pool regeneration cycle
			SetContextThink( &CAntlionTemplateMaker::PoolRegenThink, gpGlobals->curtime + m_flPoolRegenTime, s_pPoolThinkContext );

			// Start our blocked effects cycle
			if ( hl2_episodic.GetBool() == true && HasSpawnFlags( SF_ANTLIONMAKER_DO_BLOCKEDEFFECTS ) )
			{
				SetContextThink( &CAntlionTemplateMaker::FindNodesCloseToPlayer, gpGlobals->curtime + 1.0f, s_pBlockedEffectsThinkContext );
			}
		}
	}

	ActivateAllSpores();
}

void CAntlionTemplateMaker::ActivateSpore( const char* sporename, Vector vOrigin )
{
	if ( m_bCreateSpores == false )
		return;

	char szName[64];
	Q_snprintf( szName, sizeof( szName ), "%s_spore", sporename );

	SporeExplosion *pSpore = (SporeExplosion*)gEntList.FindEntityByName( NULL, szName );

	//One already exists...
	if ( pSpore )
	{	
		if ( pSpore->m_bDisabled == true )
		{
			inputdata_t inputdata;
			pSpore->InputEnable( inputdata );
		}

		return;
	}

	CBaseEntity *pEnt = CreateEntityByName( "env_sporeexplosion" );

	if ( pEnt )
	{
		pSpore = dynamic_cast<SporeExplosion*>(pEnt);
		
		if ( pSpore )
		{
			pSpore->SetAbsOrigin( vOrigin );
			pSpore->SetName( AllocPooledString( szName ) );
			pSpore->m_flSpawnRate = ANTLION_MAKE_SPORE_SPAWNRATE;
		}
	}
}

void CAntlionTemplateMaker::DisableSpore( const char* sporename )
{
	if ( m_bCreateSpores == false )
		return;

	char szName[64];
	Q_snprintf( szName, sizeof( szName ), "%s_spore", sporename );

	SporeExplosion *pSpore = (SporeExplosion*)gEntList.FindEntityByName( NULL, szName );

	if ( pSpore && pSpore->m_bDisabled == false )
	{	
		inputdata_t inputdata;
		pSpore->InputDisable( inputdata );
		return;
	}
}

void CAntlionTemplateMaker::ActivateAllSpores( void )
{
	if ( m_bDisabled == true )
		return;

	if ( m_bCreateSpores == false )
		return;

	CHintCriteria	hintCriteria;

	hintCriteria.SetGroup( m_strSpawnGroup );
	hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );

	CUtlVector<CAI_Hint *> hintList;
	CAI_HintManager::FindAllHints( vec3_origin, hintCriteria, &hintList );

	for ( int i = 0; i < hintList.Count(); i++ )
	{
		CAI_Hint *pTestHint = hintList[i];

		if ( pTestHint )
		{
			bool bBlank;
			if ( !AllHintsFromClusterBlocked( pTestHint, bBlank ) )
			{
				ActivateSpore( STRING( pTestHint->GetEntityName() ), pTestHint->GetAbsOrigin() );
			}
		}
	}
}

void CAntlionTemplateMaker::DisableAllSpores( void )
{
	CHintCriteria	hintCriteria;

	hintCriteria.SetGroup( m_strSpawnGroup );
	hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );

	CUtlVector<CAI_Hint *> hintList;
	CAI_HintManager::FindAllHints( vec3_origin, hintCriteria, &hintList );

	for ( int i = 0; i < hintList.Count(); i++ )
	{
		CAI_Hint *pTestHint = hintList[i];

		if ( pTestHint )
		{
			DisableSpore( STRING( pTestHint->GetEntityName() ) );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : CBaseEntity
//-----------------------------------------------------------------------------
CBaseEntity *CAntlionTemplateMaker::GetFightTarget( void )
{
	if ( m_hFightTarget != NULL )
		return m_hFightTarget;

	return m_hProxyTarget;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : CBaseEntity
//-----------------------------------------------------------------------------
CBaseEntity *CAntlionTemplateMaker::GetFollowTarget( void )
{
	return m_hFollowTarget;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::UpdateChildren( void )
{
	//Update all children
	CNPC_Antlion *pAntlion = NULL;

	// Move through our child list
	int i=0;
	for ( ; i < m_Children.Count(); i++ )
	{
		pAntlion = m_Children[i];
		
		//HACKHACK
		//Let's just fix this up.
		//This guy might have been killed in another level and we just came back.
		if ( pAntlion == NULL )
		{
			m_Children.Remove( i );
			i--;
			continue;
		}
		
		if ( pAntlion->m_lifeState != LIFE_ALIVE )
			 continue;

		pAntlion->SetFightTarget( GetFightTarget() );
		pAntlion->SetFollowTarget( GetFollowTarget() );
		pAntlion->SetMoveState( m_nChildMoveState );
	}

	m_nLiveChildren = i;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : strTarget - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::SetFightTarget( string_t strTarget, CBaseEntity *pActivator, CBaseEntity *pCaller )
{
	if ( HasSpawnFlags( SF_ANTLIONMAKER_RANDOM_FIGHT_TARGET ) )
	{
		CBaseEntity *pSearch = m_hFightTarget;

		for ( int i = random->RandomInt(1,5); i > 0; i-- )
			pSearch = gEntList.FindEntityByName( pSearch, strTarget, this, pActivator, pCaller );

		if ( pSearch != NULL )
		{
			SetFightTarget( pSearch );
		}
		else
		{
			SetFightTarget( gEntList.FindEntityByName( NULL, strTarget, this, pActivator, pCaller ) );
		}
	}
	else 
	{
		SetFightTarget( gEntList.FindEntityByName( NULL, strTarget, this, pActivator, pCaller ) );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pEntity - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::SetFightTarget( CBaseEntity *pEntity )
{
	m_hFightTarget = pEntity;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &position - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::SetFightTarget( const Vector &position )
{
	CreateProxyTarget( position );
	
	m_hFightTarget = NULL;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pTarget - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::SetFollowTarget( CBaseEntity *pTarget )
{
	m_hFollowTarget = pTarget;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pTarget - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::SetFollowTarget( string_t strTarget, CBaseEntity *pActivator, CBaseEntity *pCaller )
{
	CBaseEntity *pSearch = gEntList.FindEntityByName( NULL, strTarget, NULL, pActivator, pCaller );

	if ( pSearch != NULL )
	{
		SetFollowTarget( pSearch );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : state - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::SetChildMoveState( AntlionMoveState_e state )
{
	m_nChildMoveState = state;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &position - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::CreateProxyTarget( const Vector &position )
{
	// Create if we don't have one
	if ( m_hProxyTarget == NULL )
	{
		m_hProxyTarget = CreateEntityByName( "info_target" );
	}

	// Update if we do
	if ( m_hProxyTarget != NULL )
	{
		m_hProxyTarget->SetAbsOrigin( position );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::DestroyProxyTarget( void )
{
	if ( m_hProxyTarget )
	{
		UTIL_Remove( m_hProxyTarget );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : bIgnoreSolidEntities - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAntlionTemplateMaker::CanMakeNPC( bool bIgnoreSolidEntities )
{
	if ( m_nMaxLiveChildren == 0 )
		 return false;

	if ( !HasSpawnFlags( SF_ANTLIONMAKER_SPAWN_CLOSE_TO_TARGET ) )
	{
		if ( m_strSpawnGroup == NULL_STRING )
			 return BaseClass::CanMakeNPC( bIgnoreSolidEntities );
	}

	if ( m_nMaxLiveChildren > 0 && m_nLiveChildren >= m_nMaxLiveChildren )
		return false;

	// If we're spawning from a pool, ensure the pool has an antlion in it
	if ( m_iMaxPool && !m_iPool )
		return false;

	if ( (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI) == bits_debugDisableAI )
		return false;

	return true;
}

void CAntlionTemplateMaker::Enable( void )
{
	BaseClass::Enable();

	if ( m_iMaxPool )
	{
		SetContextThink( &CAntlionTemplateMaker::PoolRegenThink, gpGlobals->curtime + m_flPoolRegenTime, s_pPoolThinkContext );
	}

	if ( hl2_episodic.GetBool() == true && HasSpawnFlags( SF_ANTLIONMAKER_DO_BLOCKEDEFFECTS ) )
	{
		SetContextThink( &CAntlionTemplateMaker::FindNodesCloseToPlayer, gpGlobals->curtime + 1.0f, s_pBlockedEffectsThinkContext );
	}

	ActivateAllSpores();
}

void CAntlionTemplateMaker::Disable( void )
{
	BaseClass::Disable();

	SetContextThink( NULL, gpGlobals->curtime, s_pPoolThinkContext );
	SetContextThink( NULL, gpGlobals->curtime, s_pBlockedEffectsThinkContext );

	DisableAllSpores();
}


//-----------------------------------------------------------------------------
// Randomly turn it into an antlion worker if that is enabled for this maker.
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::ChildPreSpawn( CAI_BaseNPC *pChild )
{
	BaseClass::ChildPreSpawn( pChild );

	if ( ( m_flWorkerSpawnRate > 0 ) && ( random->RandomFloat( 0, 1 ) < m_flWorkerSpawnRate ) )
	{
		pChild->AddSpawnFlags( SF_ANTLION_WORKER );
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::MakeNPC( void )
{
	// If we're not restricting to hint groups, spawn as normal
	if ( !HasSpawnFlags( SF_ANTLIONMAKER_SPAWN_CLOSE_TO_TARGET ) )
	{
		if ( m_strSpawnGroup == NULL_STRING )
		{
			BaseClass::MakeNPC();
			return;
		}
	}

	if ( CanMakeNPC( true ) == false )
		return;

	// Set our defaults
	Vector	targetOrigin = GetAbsOrigin();
	QAngle	targetAngles = GetAbsAngles();

	// Look for our target entity
	CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_strSpawnTarget, this );

	// Take its position if it exists
	if ( pTarget != NULL )
	{
		UTIL_PredictedPosition( pTarget, 1.5f, &targetOrigin );
	}

	Vector	spawnOrigin = vec3_origin;

	CAI_Hint *pNode = NULL;

	bool bRandom = HasSpawnFlags( SF_ANTLIONMAKER_RANDOM_SPAWN_NODE );

	if ( HasSpawnFlags( SF_ANTLIONMAKER_SPAWN_CLOSE_TO_TARGET ) )
	{
		if ( FindNearTargetSpawnPosition( spawnOrigin, m_flSpawnRadius, pTarget ) == false )
			return;
	}
	else
	{
		// If we can't find a spawn position, then we can't spawn this time
		if ( FindHintSpawnPosition( targetOrigin, m_flSpawnRadius, m_strSpawnGroup, &pNode, bRandom ) == false )
			return;

		pNode->GetPosition( HULL_MEDIUM, &spawnOrigin );
	}
	
	// Point at the current position of the enemy
	if ( pTarget != NULL )
	{
		targetOrigin = pTarget->GetAbsOrigin();
	}	
 	
	// Create the entity via a template
	CAI_BaseNPC	*pent = NULL;
	CBaseEntity *pEntity = NULL;
	MapEntity_ParseEntity( pEntity, STRING(m_iszTemplateData), NULL );
	
	if ( pEntity != NULL )
	{
		pent = (CAI_BaseNPC *) pEntity;
	}

	if ( pent == NULL )
	{
		Warning("NULL Ent in NPCMaker!\n" );
		return;
	}
	
	if ( !HasSpawnFlags( SF_ANTLIONMAKER_SPAWN_CLOSE_TO_TARGET ) )
	{
		// Lock this hint node
		pNode->Lock( pEntity );
		
		// Unlock it in two seconds, this forces subsequent antlions to 
		// reject this point as a spawn point to spread them out a bit
		pNode->Unlock( 2.0f );
	}

	m_OnSpawnNPC.Set( pEntity, pEntity, this );

	pent->AddSpawnFlags( SF_NPC_FALL_TO_GROUND );

	ChildPreSpawn( pent );

	// Put us at the desired location
	pent->SetLocalOrigin( spawnOrigin );

	QAngle	spawnAngles;

	if ( pTarget )
	{
		// Face our spawning direction
		Vector	spawnDir = ( targetOrigin - spawnOrigin );
		VectorNormalize( spawnDir );

		VectorAngles( spawnDir, spawnAngles );
		spawnAngles[PITCH] = 0.0f;
		spawnAngles[ROLL] = 0.0f;
	}
	else if ( pNode )
	{
		spawnAngles = QAngle( 0, pNode->Yaw(), 0 );
	}

	pent->SetLocalAngles( spawnAngles );	
	DispatchSpawn( pent );
	
	pent->Activate();

	m_iSkinCount = ( m_iSkinCount + 1 ) % ANTLION_SKIN_COUNT;
	pent->m_nSkin = m_iSkinCount; 

	ChildPostSpawn( pent );

	// Hold onto the child
	CNPC_Antlion *pAntlion = dynamic_cast<CNPC_Antlion *>(pent);

	AddChild( pAntlion );

	m_bBlocked = false;
	SetContextThink( NULL, -1, s_pBlockedCheckContext );

	pAntlion->ClearBurrowPoint( spawnOrigin );

	if (!(m_spawnflags & SF_NPCMAKER_INF_CHILD))
	{
		if ( m_iMaxPool )
		{
			m_iPool--;

			if ( g_debug_antlionmaker.GetInt() == 2 )
			{
				Msg("SPAWNED: Pool: %d (max %d) (Regenerating %d every %f)\n", m_iPool, m_iMaxPool, m_iPoolRegenAmount, m_flPoolRegenTime );
			}
		}
		else
		{
			m_nMaxNumNPCs--;
		}

		if ( IsDepleted() )
		{
			m_OnAllSpawned.FireOutput( this, this );

			// Disable this forever.  Don't kill it because it still gets death notices
			SetThink( NULL );
			SetUse( NULL );
		}
	}
}

bool CAntlionTemplateMaker::FindPositionOnFoot( Vector &origin, float radius, CBaseEntity *pTarget )
{
	int iMaxTries = 10;
	Vector vSpawnOrigin = pTarget->GetAbsOrigin();

	while ( iMaxTries > 0 )
	{
		vSpawnOrigin.x += random->RandomFloat( -radius, radius );
		vSpawnOrigin.y += random->RandomFloat( -radius, radius );
		vSpawnOrigin.z += 96;

		if ( ValidateSpawnPosition( vSpawnOrigin, pTarget ) == false )
		{
			iMaxTries--;
			continue;
		}

		origin = vSpawnOrigin;
		return true;
	}

	return false;
}

bool CAntlionTemplateMaker::FindPositionOnVehicle( Vector &origin, float radius, CBaseEntity *pTarget )
{
	int iMaxTries = 10;
	Vector vSpawnOrigin = pTarget->GetAbsOrigin();
	vSpawnOrigin.z += 96;

	if ( pTarget == NULL )
		 return false;

	while ( iMaxTries > 0 )
	{
		Vector vForward, vRight;
		
		pTarget->GetVectors( &vForward, &vRight, NULL );

		float flSpeed = (pTarget->GetSmoothedVelocity().Length() * m_flVehicleSpawnDistance) * random->RandomFloat( 1.0f, 1.5f );
	
		vSpawnOrigin = vSpawnOrigin + (vForward * flSpeed) + vRight * random->RandomFloat( -radius, radius );

		if ( ValidateSpawnPosition( vSpawnOrigin, pTarget ) == false )
		{
			iMaxTries--;
			continue;
		}

		origin = vSpawnOrigin;
		return true;
	}

	return false;
}

bool CAntlionTemplateMaker::ValidateSpawnPosition( Vector &vOrigin, CBaseEntity *pTarget )
{
	trace_t	tr;
	UTIL_TraceLine( vOrigin, vOrigin - Vector( 0, 0, 1024 ), MASK_BLOCKLOS | CONTENTS_WATER, NULL, COLLISION_GROUP_NONE, &tr );

	if ( g_debug_antlionmaker.GetInt() == 1 )
		 NDebugOverlay::Line( vOrigin, tr.endpos, 0, 255, 0, false, 5 );
		
	// Make sure this point is clear 
	if ( tr.fraction != 1.0 )
	{
		if ( tr.contents & ( CONTENTS_WATER ) )
			 return false;

		const surfacedata_t *psurf = physprops->GetSurfaceData( tr.surface.surfaceProps );

		if ( psurf )
		{
			if ( g_debug_antlionmaker.GetInt() == 1 )
			{
				char szText[16];

				Q_snprintf( szText, 16, "Material %c", psurf->game.material );
				NDebugOverlay::Text( vOrigin, szText, true, 5 );
			}

			if ( psurf->game.material != CHAR_TEX_SAND )
				return false;
		}

		if ( CAntlionRepellant::IsPositionRepellantFree( tr.endpos ) == false )
			 return false;
	
		trace_t trCheck;
		UTIL_TraceHull( tr.endpos, tr.endpos + Vector(0,0,5), NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), MASK_NPCSOLID, NULL, COLLISION_GROUP_NONE, &trCheck );

		if ( trCheck.DidHit() == false )
		{
			if ( g_debug_antlionmaker.GetInt() == 1 )
				 NDebugOverlay::Box( tr.endpos + Vector(0,0,5), NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 0, 255, 0, 128, 5 );
		
			if ( pTarget )
			{
				if ( pTarget->IsPlayer() )
				{
					CBaseEntity *pVehicle = NULL;
					CBasePlayer *pPlayer = dynamic_cast < CBasePlayer *> ( pTarget );

					if ( pPlayer && pPlayer->GetVehicle() )
						 pVehicle = ((CBasePlayer *)pTarget)->GetVehicle()->GetVehicleEnt();

					CTraceFilterSkipTwoEntities traceFilter( pPlayer, pVehicle, COLLISION_GROUP_NONE );

					trace_t trVerify;
					
					Vector vVerifyOrigin = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset();
					float flZOffset = NAI_Hull::Maxs( HULL_MEDIUM ).z;
					UTIL_TraceLine( vVerifyOrigin, tr.endpos + Vector( 0, 0, flZOffset ), MASK_BLOCKLOS | CONTENTS_WATER, &traceFilter, &trVerify );

					if ( trVerify.fraction != 1.0f )
					{
						const surfacedata_t *psurf = physprops->GetSurfaceData( trVerify.surface.surfaceProps );

						if ( psurf )
						{
							if ( psurf->game.material == CHAR_TEX_DIRT )
							{
								if ( g_debug_antlionmaker.GetInt() == 1 )
								{
									NDebugOverlay::Line( vVerifyOrigin, trVerify.endpos, 255, 0, 0, false, 5 );
								}

								return false;
							}
						}
					}

					if ( g_debug_antlionmaker.GetInt() == 1 )
					{
						NDebugOverlay::Line( vVerifyOrigin, trVerify.endpos, 0, 255, 0, false, 5 );
					}
				}
			}

	
			vOrigin = trCheck.endpos + Vector(0,0,5);
			return true;
		}
		else
		{
			if ( g_debug_antlionmaker.GetInt() == 1 )
				 NDebugOverlay::Box( tr.endpos + Vector(0,0,5), NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 255, 0, 0, 128, 5 );

			return false;
		}
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: Find a position near the player to spawn the new antlion at
// Input  : &origin - search origin
//			radius - search radius
//			*retOrigin - found origin (if any)
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAntlionTemplateMaker::FindNearTargetSpawnPosition( Vector &origin, float radius, CBaseEntity *pTarget )
{
	if ( pTarget )
	{
		CBaseEntity *pVehicle = NULL;

		if ( pTarget->IsPlayer() )
		{
			CBasePlayer *pPlayer = ((CBasePlayer *)pTarget);

			if ( pPlayer->GetVehicle() )
				 pVehicle = ((CBasePlayer *)pTarget)->GetVehicle()->GetVehicleEnt();
		}

		if ( pVehicle )
		     return FindPositionOnVehicle( origin, radius, pVehicle );
		else 
			 return FindPositionOnFoot( origin, radius, pTarget );
	}

	return false;
}


//-----------------------------------------------------------------------------
// Purpose: Find a hint position to spawn the new antlion at
// Input  : &origin - search origin
//			radius - search radius
//			hintGroupName - search hint group name
//			*retOrigin - found origin (if any)
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAntlionTemplateMaker::FindHintSpawnPosition( const Vector &origin, float radius, string_t hintGroupName, CAI_Hint **pHint, bool bRandom )
{
	CAI_Hint *pChosenHint = NULL;

	CHintCriteria	hintCriteria;

	hintCriteria.SetGroup( hintGroupName );
	hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );

	if ( bRandom )
	{
		hintCriteria.SetFlag( bits_HINT_NODE_RANDOM );
	}
	else
	{
		hintCriteria.SetFlag( bits_HINT_NODE_NEAREST );
	}
	
	// If requested, deny nodes that can be seen by the player
	if ( m_spawnflags & SF_NPCMAKER_HIDEFROMPLAYER )
	{
		hintCriteria.SetFlag( bits_HINT_NODE_NOT_VISIBLE_TO_PLAYER );
	}

	hintCriteria.AddIncludePosition( origin, radius );

	if ( bRandom == true )
	{
		pChosenHint = CAI_HintManager::FindHintRandom( NULL, origin, hintCriteria );
	}
	else
	{
		pChosenHint = CAI_HintManager::FindHint( origin, hintCriteria );
	}

	if ( pChosenHint != NULL )
	{
		bool bChosenHintBlocked = false;

		if ( AllHintsFromClusterBlocked( pChosenHint, bChosenHintBlocked ) )
		{
			if ( ( GetIndexForThinkContext( s_pBlockedCheckContext ) == NO_THINK_CONTEXT ) ||
				( GetNextThinkTick( s_pBlockedCheckContext ) == TICK_NEVER_THINK ) )
			{
				SetContextThink( &CAntlionTemplateMaker::BlockedCheckFunc, gpGlobals->curtime + 2.0f, s_pBlockedCheckContext );
			}

			return false;
		}
		
		if ( bChosenHintBlocked == true )
		{
			return false;
		}

		*pHint = pChosenHint;
		return true;
	}

	return false;
}

void CAntlionTemplateMaker::DoBlockedEffects( CBaseEntity *pBlocker, Vector vOrigin )
{
	// If the object blocking the hole is a physics object, wobble it a bit.
	if( pBlocker )
	{
		IPhysicsObject *pPhysObj = pBlocker->VPhysicsGetObject();

		if( pPhysObj && pPhysObj->IsAsleep() )
		{
			// Don't bonk the object unless it is at rest.
			float x = RandomFloat( -5000, 5000 );
			float y = RandomFloat( -5000, 5000 );

			Vector vecForce = Vector( x, y, RandomFloat(10000, 15000) );
			pPhysObj->ApplyForceCenter( vecForce );

			UTIL_CreateAntlionDust( vOrigin, vec3_angle, true );
			pBlocker->EmitSound( "NPC_Antlion.MeleeAttackSingle_Muffled" );
			pBlocker->EmitSound( "NPC_Antlion.TrappedMetal" );


			m_flBlockedBumpTime = gpGlobals->curtime + random->RandomFloat( 1.75, 2.75 );
		}
	}
}

CBaseEntity *CAntlionTemplateMaker::AllHintsFromClusterBlocked( CAI_Hint *pNode, bool &bChosenHintBlocked )
{
	// Only do this for episodic content!
	if ( hl2_episodic.GetBool() == false )
		return NULL;

	CBaseEntity *pBlocker = NULL;

	if ( pNode != NULL )
	{
		int iNumBlocked = 0;
		int iNumNodes = 0;

		CHintCriteria	hintCriteria;

		hintCriteria.SetGroup( m_strSpawnGroup );
		hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );

		CUtlVector<CAI_Hint *> hintList;
		CAI_HintManager::FindAllHints( vec3_origin, hintCriteria, &hintList );
	
		for ( int i = 0; i < hintList.Count(); i++ )
		{
			CAI_Hint *pTestHint = hintList[i];

			if ( pTestHint )
			{
				if ( pTestHint->NameMatches( pNode->GetEntityName() ) )
				{
					bool bBlocked;

					iNumNodes++;

					Vector spawnOrigin;
					pTestHint->GetPosition( HULL_MEDIUM, &spawnOrigin );

					bBlocked = false;

					CBaseEntity*	pList[20];
				
					int count = UTIL_EntitiesInBox( pList, 20, spawnOrigin + NAI_Hull::Mins( HULL_MEDIUM ), spawnOrigin + NAI_Hull::Maxs( HULL_MEDIUM ), 0 );

					//Iterate over all the possible targets
					for ( int i = 0; i < count; i++ )
					{
						if ( pList[i]->GetMoveType() != MOVETYPE_VPHYSICS )
							continue;

						if ( PhysGetEntityMass( pList[i] ) > ANTLION_MAKER_BLOCKED_MASS )
						{
							bBlocked = true;
							iNumBlocked++;
							pBlocker = pList[i];

							if ( pTestHint == pNode )
							{
								bChosenHintBlocked = true;
							}

							break;
						}
					}

					if ( g_debug_antlionmaker.GetInt() == 1 )
					{
						if ( bBlocked ) 
						{
							NDebugOverlay::Box( spawnOrigin + Vector(0,0,5), NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 255, 0, 0, 128, 0.25f );
						}
						else
						{
							NDebugOverlay::Box( spawnOrigin + Vector(0,0,5), NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 0, 255, 0, 128, 0.25f );
						}
					}
				}
			}
		}

		//All the nodes from this cluster are blocked so start playing the effects.
		if ( iNumNodes > 0 && iNumBlocked == iNumNodes )
		{
			return pBlocker;
		}
	}

	return NULL;
}

void CAntlionTemplateMaker::FindNodesCloseToPlayer( void )
{
	SetContextThink( &CAntlionTemplateMaker::FindNodesCloseToPlayer, gpGlobals->curtime + random->RandomFloat( 0.75, 1.75 ), s_pBlockedEffectsThinkContext );

	CBasePlayer *pPlayer = AI_GetSinglePlayer();

	if ( pPlayer == NULL )
		 return;

	CHintCriteria hintCriteria;

	float flRadius = ANTLION_MAKER_PLAYER_DETECT_RADIUS;

	hintCriteria.SetGroup( m_strSpawnGroup );
	hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );
	hintCriteria.AddIncludePosition( pPlayer->GetAbsOrigin(), ANTLION_MAKER_PLAYER_DETECT_RADIUS );

	CUtlVector<CAI_Hint *> hintList;

	if ( CAI_HintManager::FindAllHints( vec3_origin, hintCriteria, &hintList ) <= 0 )
		return;

	CUtlVector<string_t> m_BlockedNames;

	//----
	//What we do here is find all hints of the same name (cluster name) and figure out if all of them are blocked.
	//If they are then we only need to play the blocked effects once
	//---
	for ( int i = 0; i < hintList.Count(); i++ )
	{
		CAI_Hint *pNode = hintList[i];

		if ( pNode && pNode->HintMatchesCriteria( NULL, hintCriteria, pPlayer->GetAbsOrigin(), &flRadius ) )
		{
			bool bClusterAlreadyBlocked = false;

			//Have one of the nodes from this cluster been checked for blockage? If so then there's no need to do block checks again for this cluster.
			for ( int iStringCount = 0; iStringCount < m_BlockedNames.Count(); iStringCount++ )
			{
				if ( pNode->NameMatches( m_BlockedNames[iStringCount] ) )
				{
					bClusterAlreadyBlocked = true;
					break;
				}
			}

			if ( bClusterAlreadyBlocked == true )
				continue;

			Vector vHintPos;
			pNode->GetPosition( HULL_MEDIUM, &vHintPos );
		
			bool bBlank;
			if ( CBaseEntity *pBlocker = AllHintsFromClusterBlocked( pNode, bBlank ) )
			{
				DisableSpore( STRING( pNode->GetEntityName() ) );
				DoBlockedEffects( pBlocker, vHintPos );
				m_BlockedNames.AddToTail( pNode->GetEntityName() );
			}
			else
			{
				ActivateSpore( STRING( pNode->GetEntityName() ), pNode->GetAbsOrigin() );
			}
		}
	}
}

void CAntlionTemplateMaker::BlockedCheckFunc( void )
{
	SetContextThink( &CAntlionTemplateMaker::BlockedCheckFunc, -1, s_pBlockedCheckContext );

	if ( m_bBlocked == true )
		 return;

	CUtlVector<CAI_Hint *> hintList;
	int iBlocked = 0;

	CHintCriteria	hintCriteria;

	hintCriteria.SetGroup( m_strSpawnGroup );
	hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );

	if ( CAI_HintManager::FindAllHints( vec3_origin, hintCriteria, &hintList ) > 0 )
	{
		for ( int i = 0; i < hintList.Count(); i++ )
		{
			CAI_Hint *pNode = hintList[i];

			if ( pNode )
			{
				Vector vHintPos;
				pNode->GetPosition( AI_GetSinglePlayer(), &vHintPos );

				CBaseEntity*	pList[20];
				int count = UTIL_EntitiesInBox( pList, 20, vHintPos + NAI_Hull::Mins( HULL_MEDIUM ), vHintPos + NAI_Hull::Maxs( HULL_MEDIUM ), 0 );

				//Iterate over all the possible targets
				for ( int i = 0; i < count; i++ )
				{
					if ( pList[i]->GetMoveType() != MOVETYPE_VPHYSICS )
						continue;

					if ( PhysGetEntityMass( pList[i] ) > ANTLION_MAKER_BLOCKED_MASS )
					{
						iBlocked++;
						break;
					}
				}
			}
		}
	}

	if ( iBlocked > 0 && hintList.Count() == iBlocked )
	{
		m_bBlocked = true;
		m_OnAllBlocked.FireOutput( this, this );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Makes the antlion immediatley unburrow if it started burrowed
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::ChildPostSpawn( CAI_BaseNPC *pChild )
{
	CNPC_Antlion *pAntlion = static_cast<CNPC_Antlion*>(pChild);

	if ( pAntlion == NULL )
		return;

	// Unburrow the spawned antlion immediately
	if ( pAntlion->m_bStartBurrowed )
	{
		pAntlion->BurrowUse( this, this, USE_ON, 0.0f );
	}

	// Set us to a follow target, if we have one
	if ( GetFollowTarget() )
	{
		pAntlion->SetFollowTarget( GetFollowTarget() );
	}
	else if ( ( m_strFollowTarget != NULL_STRING ) )
	{
		// If we don't already have a fight target, set it up
		SetFollowTarget( m_strFollowTarget );

		if ( GetFightTarget() == NULL )
		{
			SetChildMoveState( ANTLION_MOVE_FOLLOW );

			// If it's valid, fight there
			if ( GetFollowTarget() != NULL )
			{
				pAntlion->SetFollowTarget( GetFollowTarget() );
			}
		}
	}
	// See if we need to send them on their way to a fight goal
	if ( GetFightTarget() && !HasSpawnFlags( SF_ANTLIONMAKER_RANDOM_FIGHT_TARGET ) )
	{
		pAntlion->SetFightTarget( GetFightTarget() );
	}
	else if ( m_strFightTarget != NULL_STRING )
	{
		// If we don't already have a fight target, set it up
		SetFightTarget( m_strFightTarget );	
		SetChildMoveState( ANTLION_MOVE_FIGHT_TO_GOAL );

		// If it's valid, fight there
		if ( GetFightTarget() != NULL )
		{
			pAntlion->SetFightTarget( GetFightTarget() );
		}
	}

	// Set us to the desired movement state
	pAntlion->SetMoveState( m_nChildMoveState );

	// Save our name for level transitions
	pAntlion->SetParentSpawnerName( STRING( GetEntityName() ) );

	if ( m_hIgnoreEntity != NULL )
	{
		pChild->SetOwnerEntity( m_hIgnoreEntity );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::InputSetFightTarget( inputdata_t &inputdata )
{
	// Set our new goal
	m_strFightTarget = MAKE_STRING( inputdata.value.String() );

	SetFightTarget( m_strFightTarget, inputdata.pActivator, inputdata.pCaller );
	SetChildMoveState( ANTLION_MOVE_FIGHT_TO_GOAL );
	
	UpdateChildren();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::InputSetFollowTarget( inputdata_t &inputdata )
{
	// Set our new goal
	m_strFollowTarget = MAKE_STRING( inputdata.value.String() );

	SetFollowTarget( m_strFollowTarget, inputdata.pActivator, inputdata.pCaller );
	SetChildMoveState( ANTLION_MOVE_FOLLOW );
	
	UpdateChildren();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::InputClearFightTarget( inputdata_t &inputdata )
{
	SetFightTarget( NULL );
	SetChildMoveState( ANTLION_MOVE_FOLLOW );

	UpdateChildren();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::InputClearFollowTarget( inputdata_t &inputdata )
{
	SetFollowTarget( NULL );
	SetChildMoveState( ANTLION_MOVE_FIGHT_TO_GOAL );

	UpdateChildren();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::InputSetSpawnRadius( inputdata_t &inputdata )
{
	m_flSpawnRadius = inputdata.value.Float();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::InputAddToPool( inputdata_t &inputdata )
{
	PoolAdd( inputdata.value.Int() );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::InputSetMaxPool( inputdata_t &inputdata )
{
	m_iMaxPool = inputdata.value.Int();
	if ( m_iPool > m_iMaxPool )
	{
		m_iPool = m_iMaxPool;
	}

	// Stop regenerating if we're supposed to stop using the pool
	if ( !m_iMaxPool )
	{
		SetContextThink( NULL, gpGlobals->curtime, s_pPoolThinkContext );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::InputSetPoolRegenAmount( inputdata_t &inputdata )
{
	m_iPoolRegenAmount = inputdata.value.Int();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::InputSetPoolRegenTime( inputdata_t &inputdata )
{
	m_flPoolRegenTime = inputdata.value.Float();

	if ( m_flPoolRegenTime != 0.0f )
	{
		SetContextThink( &CAntlionTemplateMaker::PoolRegenThink, gpGlobals->curtime + m_flPoolRegenTime, s_pPoolThinkContext );
	}
	else
	{
		SetContextThink( NULL, gpGlobals->curtime, s_pPoolThinkContext );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Pool behavior for coast
// Input  : iNumToAdd - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::PoolAdd( int iNumToAdd )
{
	m_iPool = clamp( m_iPool + iNumToAdd, 0, m_iMaxPool );
}

//-----------------------------------------------------------------------------
// Purpose: Regenerate the pool
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::PoolRegenThink( void )
{
	if ( m_iPool < m_iMaxPool )
	{
		m_iPool = clamp( m_iPool + m_iPoolRegenAmount, 0, m_iMaxPool );

		if ( g_debug_antlionmaker.GetInt() == 2 )
		{
			Msg("REGENERATED: Pool: %d (max %d) (Regenerating %d every %f)\n", m_iPool, m_iMaxPool, m_iPoolRegenAmount, m_flPoolRegenTime );
		}
	}

	SetContextThink( &CAntlionTemplateMaker::PoolRegenThink, gpGlobals->curtime + m_flPoolRegenTime, s_pPoolThinkContext );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pVictim - 
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::DeathNotice( CBaseEntity *pVictim )
{
	CNPC_Antlion *pAnt = dynamic_cast<CNPC_Antlion *>(pVictim);
	if ( pAnt == NULL )
		return;

	// Take it out of our list
	RemoveChild( pAnt );

	// Check if all live children are now dead
	if ( m_nLiveChildren <= 0 )
	{
		// Fire the output for this case
		m_OnAllLiveChildrenDead.FireOutput( this, this );

		bool bPoolDepleted = ( m_iMaxPool != 0 && m_iPool == 0 );
		if ( bPoolDepleted || IsDepleted() )
		{
			// Signal that all our children have been spawned and are now dead
			m_OnAllSpawnedDead.FireOutput( this, this );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: If this had a finite number of children, return true if they've all
//			been created.
//-----------------------------------------------------------------------------
bool CAntlionTemplateMaker::IsDepleted( void )
{
	// If we're running pool behavior, we're never depleted
	if ( m_iMaxPool )
		return false;

	return BaseClass::IsDepleted();
}

//-----------------------------------------------------------------------------
// Purpose: Change the spawn group the maker is using
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::InputChangeDestinationGroup( inputdata_t &inputdata )
{
	// FIXME: This function is redundant to the base class version, remove the m_strSpawnGroup
	m_strSpawnGroup = inputdata.value.StringID();
}

//-----------------------------------------------------------------------------
// Purpose: Draw debugging text for the spawner
//-----------------------------------------------------------------------------
int CAntlionTemplateMaker::DrawDebugTextOverlays( void )
{
	// We don't want the base class info, it's not useful to us
	int text_offset = BaseClass::DrawDebugTextOverlays();

	if ( m_debugOverlays & OVERLAY_TEXT_BIT )
	{
		char tempstr[255];
		
		// Print the state of the spawner
		if ( m_bDisabled )
		{
			Q_strncpy( tempstr, "State: Disabled\n", sizeof(tempstr) );
		}
		else
		{
			Q_strncpy( tempstr, "State: Enabled\n", sizeof(tempstr) );
		}

		EntityText( text_offset, tempstr, 0 );
		text_offset++;

		// Print follow information
		if ( m_strFollowTarget != NULL_STRING )
		{
			Q_snprintf( tempstr, sizeof(tempstr), "Follow Target: %s\n", STRING( m_strFollowTarget ) );
		}
		else
		{
			Q_strncpy( tempstr, "Follow Target : NONE\n", sizeof(tempstr) );
		}

		EntityText( text_offset, tempstr, 0 );
		text_offset++;

		// Print fight information
		if ( m_strFightTarget != NULL_STRING )
		{
			Q_snprintf( tempstr, sizeof(tempstr), "Fight Target: %s\n", STRING( m_strFightTarget ) );
		}
		else
		{
			Q_strncpy( tempstr, "Fight Target : NONE\n", sizeof(tempstr) );
		}

		EntityText( text_offset, tempstr, 0 );
		text_offset++;

		// Print spawning criteria information
		if ( m_strSpawnTarget != NULL_STRING )
		{
			Q_snprintf( tempstr, sizeof(tempstr), "Spawn Target: %s\n", STRING( m_strSpawnTarget ) );
		}
		else
		{
			Q_strncpy( tempstr, "Spawn Target : NONE\n", sizeof(tempstr) );
		}

		EntityText( text_offset, tempstr, 0 );
		text_offset++;

		// Print the chilrens' state
		Q_snprintf( tempstr, sizeof(tempstr), "Spawn Frequency: %f\n", m_flSpawnFrequency );
		EntityText( text_offset, tempstr, 0 );
		text_offset++;

		// Print the spawn radius
		Q_snprintf( tempstr, sizeof(tempstr), "Spawn Radius: %.02f units\n", m_flSpawnRadius );
		EntityText( text_offset, tempstr, 0 );
		text_offset++;

		// Print the spawn group we're using
		if ( m_strSpawnGroup != NULL_STRING )
		{
			Q_snprintf( tempstr, sizeof(tempstr), "Spawn Group: %s\n", STRING( m_strSpawnGroup ) );
			EntityText( text_offset, tempstr, 0 );
			text_offset++;
		}

		// Print the chilrens' state
		Q_snprintf( tempstr, sizeof(tempstr), "Live Children: (%d/%d)\n", m_nLiveChildren, m_nMaxLiveChildren );
		EntityText( text_offset, tempstr, 0 );
		text_offset++;

		// Print pool information
		if ( m_iMaxPool )
		{
			// Print the pool's state
			Q_snprintf( tempstr, sizeof(tempstr), "Pool: (%d/%d) (%d per regen)\n", m_iPool, m_iMaxPool, m_iPoolRegenAmount );
			EntityText( text_offset, tempstr, 0 );
			text_offset++;

			float flTimeRemaining = GetNextThink( s_pPoolThinkContext ) - gpGlobals->curtime;

			if ( flTimeRemaining < 0.0f )
			{
				flTimeRemaining = 0.0f;
			}

			// Print the pool's regeneration state
			Q_snprintf( tempstr, sizeof(tempstr), "Pool Regen Time: %.02f sec. (%.02f remaining)\n", m_flPoolRegenTime, flTimeRemaining );
			EntityText( text_offset, tempstr, 0 );
			text_offset++;
		}
	}

	return text_offset;	
}

//-----------------------------------------------------------------------------
// Purpose: Draw debugging overlays for the spawner
//-----------------------------------------------------------------------------
void CAntlionTemplateMaker::DrawDebugGeometryOverlays( void )
{
	BaseClass::DrawDebugGeometryOverlays();

	if ( m_debugOverlays & OVERLAY_TEXT_BIT )
	{
		float r, g, b;

		// Color by active state
		if ( m_bDisabled )
		{
			r = 255.0f;
			g = 0.0f;
			b = 0.0f;
		}
		else
		{
			r = 0.0f;
			g = 255.0f;
			b = 0.0f;
		}

		// Draw ourself
		NDebugOverlay::Box( GetAbsOrigin(), -Vector(8,8,8), Vector(8,8,8), r, g, b, true, 0.05f );

		// Draw lines to our spawngroup hints
		if ( m_strSpawnGroup != NULL_STRING )
		{
			// Draw lines to our active hint groups
			AIHintIter_t iter;
			CAI_Hint *pHint = CAI_HintManager::GetFirstHint( &iter );
			while ( pHint != NULL )
			{
				// Must be of the hint group we care about
				if ( pHint->GetGroup() != m_strSpawnGroup )
				{
					pHint = CAI_HintManager::GetNextHint( &iter );
					continue;
				}

				// Draw an arrow to the spot
				NDebugOverlay::VertArrow( GetAbsOrigin(), pHint->GetAbsOrigin() + Vector( 0, 0, 32 ), 8.0f, r, g, b, 0, true, 0.05f );
				
				// Draw a box to represent where it's sitting
				Vector vecForward;
				AngleVectors( pHint->GetAbsAngles(), &vecForward );
				NDebugOverlay::BoxDirection( pHint->GetAbsOrigin(), -Vector(32,32,0), Vector(32,32,16), vecForward, r, g, b, true, 0.05f );
				
				// Move to the next
				pHint = CAI_HintManager::GetNextHint( &iter );
			}
		}

		// Draw a line to the spawn target (if it exists)
		if ( m_strSpawnTarget != NULL_STRING )
		{
			// Find all the possible targets
			CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_strSpawnTarget );
			if ( pTarget != NULL )
			{
				NDebugOverlay::VertArrow( GetAbsOrigin(), pTarget->WorldSpaceCenter(), 4.0f, 255, 255, 255, 0, true, 0.05f );
			}
		}

		// Draw a line to the follow target (if it exists)
		if ( m_strFollowTarget != NULL_STRING )
		{
			// Find all the possible targets
			CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_strFollowTarget );
			if ( pTarget != NULL )
			{
				NDebugOverlay::VertArrow( GetAbsOrigin(), pTarget->WorldSpaceCenter(), 4.0f, 255, 255, 0, 0, true, 0.05f );
			}
		}

		// Draw a line to the fight target (if it exists)
		if ( m_strFightTarget != NULL_STRING )
		{
			// Find all the possible targets
			CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_strFightTarget );
			if ( pTarget != NULL )
			{
				NDebugOverlay::VertArrow( GetAbsOrigin(), pTarget->WorldSpaceCenter(), 4.0f, 255, 0, 0, 0, true, 0.05f );
			}
		}
	}
}
