//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:		Antlion - nasty bug
//
// $NoKeywords: $
//=============================================================================//

#include "cbase.h"
#include "ai_hint.h"
#include "ai_squad.h"
#include "ai_moveprobe.h"
#include "ai_route.h"
#include "npcevent.h"
#include "gib.h"
#include "entitylist.h"
#include "ndebugoverlay.h"
#include "antlion_dust.h"
#include "engine/IEngineSound.h"
#include "globalstate.h"
#include "movevars_shared.h"
#include "te_effect_dispatch.h"
#include "vehicle_base.h"
#include "mapentities.h"
#include "antlion_maker.h"
#include "npc_antlion.h"
#include "decals.h"
#include "hl2_shareddefs.h"

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

//Debug visualization
ConVar	g_debug_antlion( "g_debug_antlion", "0" );

ConVar	sk_antlion_health( "sk_antlion_health", "0" );
ConVar	sk_antlion_swipe_damage( "sk_antlion_swipe_damage", "0" );
ConVar	sk_antlion_jump_damage( "sk_antlion_jump_damage", "0" );

ConVar  g_test_new_antlion_jump( "g_test_new_antlion_jump", "1", FCVAR_ARCHIVE );
 
extern ConVar bugbait_radius;

int AE_ANTLION_WALK_FOOTSTEP;
int AE_ANTLION_MELEE_HIT1;
int AE_ANTLION_MELEE_HIT2;
int AE_ANTLION_MELEE_POUNCE;
int AE_ANTLION_FOOTSTEP_SOFT;
int AE_ANTLION_FOOTSTEP_HEAVY;
int AE_ANTLION_START_JUMP;
int AE_ANTLION_BURROW_IN;
int AE_ANTLION_BURROW_OUT;
int AE_ANTLION_VANISH;
int AE_ANTLION_OPEN_WINGS;
int AE_ANTLION_CLOSE_WINGS;
int AE_ANTLION_MELEE1_SOUND;
int AE_ANTLION_MELEE2_SOUND;

//Attack range definitions
#define	ANTLION_MELEE1_RANGE		100.0f
#define	ANTLION_MELEE2_RANGE		64.0f
#define	ANTLION_MELEE2_RANGE_MAX	175.0f
#define	ANTLION_MELEE2_RANGE_MIN	64.0f
#define	ANTLION_JUMP_MIN			128.0f

#define	ANTLION_JUMP_MAX_RISE		512.0f
#define	ANTLION_JUMP_MAX			1024.0f

#define	ANTLION_MIN_BUGBAIT_GOAL_TARGET_RADIUS	512

//Interaction IDs
int g_interactionAntlionFoundTarget = 0;

#define	ANTLION_MODEL	"models/antlion.mdl"

#define	ANTLION_BURROW_IN	0
#define	ANTLION_BURROW_OUT	1

#define	ANTLION_BUGBAIT_NAV_TOLERANCE	200

#define	ANTLION_OBEY_FOLLOW_TIME	5.0f


//==================================================
// AntlionSquadSlots
//==================================================

enum
{	
	SQUAD_SLOT_ANTLION_JUMP = LAST_SHARED_SQUADSLOT,
};

//==================================================
// Antlion Activities
//==================================================

int ACT_ANTLION_JUMP_START;
int	ACT_ANTLION_DISTRACT;
int ACT_ANTLION_DISTRACT_ARRIVED;
int ACT_ANTLION_BURROW_IN;
int ACT_ANTLION_BURROW_OUT;
int ACT_ANTLION_BURROW_IDLE;
int	ACT_ANTLION_RUN_AGITATED;
int ACT_ANTLION_FLIP;
int ACT_ANTLION_POUNCE;
int ACT_ANTLION_POUNCE_MOVING;
int ACT_ANTLION_DROWN;
int ACT_ANTLION_LAND;

//==================================================
// CNPC_Antlion
//==================================================

CNPC_Antlion::CNPC_Antlion( void )
{
	m_flIdleDelay	= 0.0f;
	m_flBurrowTime	= 0.0f;
	m_flJumpTime	= 0.0f;
	m_flPounceTime	= 0.0f;
	m_flObeyFollowTime = 0.0f;

	m_flAlertRadius	= 256.0f;
	m_flFieldOfView	= -0.5f;

	m_bStartBurrowed	= false;
	m_bAgitatedSound	= false;
	m_bWingsOpen		= false;
	
	//FIXME: This isn't a sure bet
	m_flIgnoreSoundTime	= 0.0f;
	m_bHasHeardSound	= false;

	m_flNextAcknowledgeTime = 0.0f;
	m_flNextJumpPushTime = 0.0f;

	m_vecLastJumpAttempt.Init();
	m_vecSavedJump.Init();

	m_hFightGoalTarget = NULL;
	m_hFollowTarget = NULL;
	m_bLoopingStarted = false;

	m_bForcedStuckJump = false;
}

LINK_ENTITY_TO_CLASS( npc_antlion, CNPC_Antlion );

//==================================================
// CNPC_Antlion::m_DataDesc
//==================================================

BEGIN_DATADESC( CNPC_Antlion )

	DEFINE_KEYFIELD( m_bStartBurrowed,		FIELD_BOOLEAN,	"startburrowed" ),
	DEFINE_KEYFIELD( m_bIgnoreBugbait,		FIELD_BOOLEAN,	"ignorebugbait" ),
	DEFINE_KEYFIELD( m_flAlertRadius,		FIELD_FLOAT,	"radius" ),
	DEFINE_KEYFIELD( m_flEludeDistance,		FIELD_FLOAT,	"eludedist" ),

	DEFINE_FIELD( m_flIdleDelay,			FIELD_TIME ),
	DEFINE_FIELD( m_flBurrowTime,			FIELD_TIME ),
	DEFINE_FIELD( m_flJumpTime,				FIELD_TIME ),
	DEFINE_FIELD( m_flPounceTime,			FIELD_TIME ),
	DEFINE_FIELD( m_iContext,				FIELD_INTEGER ),
	DEFINE_FIELD( m_vecSavedJump,			FIELD_VECTOR ),
	DEFINE_FIELD( m_vecLastJumpAttempt,		FIELD_VECTOR ),
	DEFINE_FIELD( m_flIgnoreSoundTime,		FIELD_TIME ),
	DEFINE_FIELD( m_vecHeardSound,			FIELD_POSITION_VECTOR ),
	DEFINE_FIELD( m_bHasHeardSound,			FIELD_BOOLEAN ),
	DEFINE_FIELD( m_bAgitatedSound,			FIELD_BOOLEAN ),
	DEFINE_FIELD( m_bWingsOpen,				FIELD_BOOLEAN ),
	DEFINE_FIELD( m_flNextAcknowledgeTime,	FIELD_TIME ),
	DEFINE_FIELD( m_hFollowTarget,			FIELD_EHANDLE ),
	DEFINE_FIELD( m_hFightGoalTarget,		FIELD_EHANDLE ),
	DEFINE_FIELD( m_strParentSpawner,		FIELD_STRING ),
	DEFINE_FIELD( m_flSuppressFollowTime,	FIELD_FLOAT ),
	DEFINE_FIELD( m_MoveState,				FIELD_INTEGER ),
	DEFINE_FIELD( m_flObeyFollowTime,		FIELD_TIME ),
	DEFINE_FIELD( m_bLeapAttack,			FIELD_BOOLEAN ),
	DEFINE_FIELD( m_bDisableJump,			FIELD_BOOLEAN ),
	DEFINE_FIELD( m_flTimeDrown,			FIELD_TIME ),
	DEFINE_FIELD( m_flNextJumpPushTime,		FIELD_TIME ),
	DEFINE_FIELD( m_bForcedStuckJump,		FIELD_BOOLEAN ),
	// DEFINE_FIELD( m_bLoopingStarted, FIELD_BOOLEAN ),
	//			  m_FollowBehavior
	
	DEFINE_INPUTFUNC( FIELD_VOID,	"Unburrow", InputUnburrow ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"Burrow",	InputBurrow ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"BurrowAway",	InputBurrowAway ),
	DEFINE_INPUTFUNC( FIELD_STRING,	"FightToPosition", InputFightToPosition ),
	DEFINE_INPUTFUNC( FIELD_STRING,	"StopFightToPosition", InputStopFightToPosition ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"EnableJump", InputEnableJump ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"DisableJump", InputDisableJump ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"IgnoreBugbait", InputIgnoreBugbait ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"HearBugbait", InputHearBugbait ),

	DEFINE_OUTPUT( m_OnReachFightGoal, "OnReachFightGoal" ),

	// Function Pointers
	DEFINE_ENTITYFUNC( Touch ),
	DEFINE_USEFUNC( BurrowUse ),

END_DATADESC()

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

	SetModel( ANTLION_MODEL );

	SetHullType(HULL_MEDIUM);
	SetHullSizeNormal();
	SetDefaultEyeOffset();
	
	SetNavType( NAV_GROUND );

	SetBloodColor( BLOOD_COLOR_YELLOW );

	m_NPCState	= NPC_STATE_NONE;
	m_iHealth	= sk_antlion_health.GetFloat();

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );

	SetMoveType( MOVETYPE_STEP );

	//Only do this if a squadname appears in the entity
	if ( m_SquadName != NULL_STRING )
	{
		CapabilitiesAdd( bits_CAP_SQUAD );
	}

	SetCollisionGroup( HL2COLLISION_GROUP_ANTLION );

	CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_MOVE_JUMP | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK2 );
	
	// JAY: Optimize these out for now
	if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == false )
		 CapabilitiesAdd( bits_CAP_SKIP_NAV_GROUND_CHECK );

	NPCInit();

	// Antlions will always pursue
	m_flDistTooFar = 9999999.0f;

	m_bDisableJump = false;

	//See if we're supposed to start burrowed
	if ( m_bStartBurrowed )
	{
		AddEffects( EF_NODRAW );
		AddFlag( FL_NOTARGET );
		m_spawnflags |= SF_NPC_GAG;
		AddSolidFlags( FSOLID_NOT_SOLID );
		m_takedamage	= DAMAGE_NO;

		SetState( NPC_STATE_IDLE );
		SetActivity( (Activity) ACT_ANTLION_BURROW_IDLE );
		SetSchedule( SCHED_ANTLION_WAIT_FOR_UNBORROW_TRIGGER );

		SetUse( &CNPC_Antlion::BurrowUse );
	}

	BaseClass::Spawn();

	m_nSkin = random->RandomInt( 0, ANTLION_SKIN_COUNT-1 );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::Activate( void )
{
	// If we're friendly to the player, setup a relationship to reflect it
	if ( IsAllied() )
	{
		// Handle all clients
		for ( int i = 1; i <= gpGlobals->maxClients; i++ )
		{
			CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );

			if ( pPlayer != NULL )
			{
				AddEntityRelationship( pPlayer, D_LI, 99 );
			}
		}
	}

	BaseClass::Activate();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::Precache( void )
{
	PrecacheModel( ANTLION_MODEL );

	PrecacheModel( "models/gibs/antlion_gib_large_1.mdl" );
	PrecacheModel( "models/gibs/antlion_gib_large_2.mdl" );
	PrecacheModel( "models/gibs/antlion_gib_large_3.mdl" );

	PrecacheModel( "models/gibs/antlion_gib_medium_1.mdl" );
	PrecacheModel( "models/gibs/antlion_gib_medium_2.mdl" );
	PrecacheModel( "models/gibs/antlion_gib_medium_3.mdl" );

	PrecacheModel( "models/gibs/antlion_gib_small_1.mdl" );
	PrecacheModel( "models/gibs/antlion_gib_small_2.mdl" );
	PrecacheModel( "models/gibs/antlion_gib_small_3.mdl" );

	PrecacheScriptSound( "NPC_Antlion.RunOverByVehicle" );
	PrecacheScriptSound( "NPC_Antlion.MeleeAttack" );
	PrecacheScriptSound( "NPC_Antlion.Footstep" );
	PrecacheScriptSound( "NPC_Antlion.BurrowIn" );
	PrecacheScriptSound( "NPC_Antlion.BurrowOut" );
	PrecacheScriptSound( "NPC_Antlion.FootstepSoft" );
	PrecacheScriptSound( "NPC_Antlion.FootstepHeavy" );
	PrecacheScriptSound( "NPC_Antlion.MeleeAttackSingle" );
	PrecacheScriptSound( "NPC_Antlion.MeleeAttackDouble" );
	PrecacheScriptSound( "NPC_Antlion.Distracted" );
	PrecacheScriptSound( "NPC_Antlion.Idle" );
	PrecacheScriptSound( "NPC_Antlion.Pain" );
	PrecacheScriptSound( "NPC_Antlion.Land" );
	PrecacheScriptSound( "NPC_Antlion.WingsOpen" );
	PrecacheScriptSound( "NPC_Antlion.LoopingAgitated" );
	PrecacheScriptSound( "NPC_Antlion.Distracted" );

	BaseClass::Precache();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
inline CBaseEntity *CNPC_Antlion::EntityToWatch( void )
{
	return ( m_hFollowTarget != NULL ) ? m_hFollowTarget.Get() : GetEnemy();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::UpdateHead( void )
{
	float yaw = GetPoseParameter( "head_yaw" );
	float pitch = GetPoseParameter( "head_pitch" );

	CBaseEntity *pTarget = EntityToWatch();

	if ( pTarget != NULL )
	{
		Vector	enemyDir = pTarget->WorldSpaceCenter() - WorldSpaceCenter();
		VectorNormalize( enemyDir );
		
		if ( DotProduct( enemyDir, BodyDirection3D() ) < 0.0f )
		{
			SetPoseParameter( "head_yaw",	UTIL_Approach( 0, yaw, 10 ) );
			SetPoseParameter( "head_pitch", UTIL_Approach( 0, pitch, 10 ) );
			
			return;
		}

		float facingYaw = VecToYaw( BodyDirection3D() );
		float yawDiff = VecToYaw( enemyDir );
		yawDiff = UTIL_AngleDiff( yawDiff, facingYaw + yaw );

		float facingPitch = UTIL_VecToPitch( BodyDirection3D() );
		float pitchDiff = UTIL_VecToPitch( enemyDir );
		pitchDiff = UTIL_AngleDiff( pitchDiff, facingPitch + pitch );

		SetPoseParameter( "head_yaw", UTIL_Approach( yaw + yawDiff, yaw, 50 ) );
		SetPoseParameter( "head_pitch", UTIL_Approach( pitch + pitchDiff, pitch, 50 ) );
	}
	else
	{
		SetPoseParameter( "head_yaw",	UTIL_Approach( 0, yaw, 10 ) );
		SetPoseParameter( "head_pitch", UTIL_Approach( 0, pitch, 10 ) );
	}
}

#define	ANTLION_VIEW_FIELD_NARROW	0.85f

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pEntity - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::FInViewCone( CBaseEntity *pEntity )
{
	m_flFieldOfView = ( GetEnemy() != NULL ) ? ANTLION_VIEW_FIELD_NARROW : VIEW_FIELD_WIDE;

	return BaseClass::FInViewCone( pEntity );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &vecSpot - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::FInViewCone( const Vector &vecSpot )
{
	m_flFieldOfView = ( GetEnemy() != NULL ) ? ANTLION_VIEW_FIELD_NARROW : VIEW_FIELD_WIDE;

	return BaseClass::FInViewCone( vecSpot );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pVictim - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::Event_Killed( const CTakeDamageInfo &info )
{
	//Turn off wings
	SetWings( false );
	VacateStrategySlot();

	if ( IsCurSchedule(SCHED_ANTLION_BURROW_IN) || IsCurSchedule(SCHED_ANTLION_BURROW_OUT) )
	{
		AddEFlags( EF_NOSHADOW );
	}

	if ( info.GetDamageType() & DMG_CRUSH )
	{
		CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, GetAbsOrigin(), 256, 0.5f, this );
	}

	BaseClass::Event_Killed( info );

	CBaseEntity *pAttacker = info.GetInflictor();

	if ( pAttacker && pAttacker->GetServerVehicle() && ShouldGib( info ) == true )
	{
		trace_t tr;
		UTIL_TraceLine( GetAbsOrigin() + Vector( 0, 0, 64 ), pAttacker->GetAbsOrigin(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
		UTIL_DecalTrace( &tr, "Antlion.Splat" );

		SpawnBlood( GetAbsOrigin(), g_vecAttackDir, BloodColor(), info.GetDamage() );

		CPASAttenuationFilter filter( this );
		EmitSound( filter, entindex(), "NPC_Antlion.RunOverByVehicle" );
	}

}

//-----------------------------------------------------------------------------
// Purpose: 
// Input: 
//==================================================

void CNPC_Antlion::MeleeAttack( float distance, float damage, QAngle &viewPunch, Vector &shove )
{
	Vector vecForceDir;

	// Always hurt bullseyes for now
	if ( ( GetEnemy() != NULL ) && ( GetEnemy()->Classify() == CLASS_BULLSEYE ) )
	{
		vecForceDir = (GetEnemy()->GetAbsOrigin() - GetAbsOrigin());
		CTakeDamageInfo info( this, this, damage, DMG_SLASH );
		CalculateMeleeDamageForce( &info, vecForceDir, GetEnemy()->GetAbsOrigin() );
		GetEnemy()->TakeDamage( info );
		return;
	}

	CBaseEntity *pHurt = CheckTraceHullAttack( distance, -Vector(16,16,32), Vector(16,16,32), damage, DMG_SLASH, 5.0f );

	if ( pHurt )
	{
		vecForceDir = ( pHurt->WorldSpaceCenter() - WorldSpaceCenter() );

		//FIXME: Until the interaction is setup, kill combine soldiers in one hit -- jdw
		if ( FClassnameIs( pHurt, "npc_combine_s" ) )
		{
			CTakeDamageInfo	dmgInfo( this, this, pHurt->m_iHealth+25, DMG_SLASH );
			CalculateMeleeDamageForce( &dmgInfo, vecForceDir, pHurt->GetAbsOrigin() );
			pHurt->TakeDamage( dmgInfo );
			return;
		}

		CBasePlayer *pPlayer = ToBasePlayer( pHurt );

		if ( pPlayer != NULL )
		{
			//Kick the player angles
			pPlayer->ViewPunch( viewPunch );

			Vector	dir = pHurt->GetAbsOrigin() - GetAbsOrigin();
			VectorNormalize(dir);

			QAngle angles;
			VectorAngles( dir, angles );
			Vector forward, right;
			AngleVectors( angles, &forward, &right, NULL );

			//Push the target back
			pHurt->ApplyAbsVelocityImpulse( - right * shove[1] - forward * shove[0] );
		}

		// Play a random attack hit sound
		EmitSound( "NPC_Antlion.MeleeAttack" );
	}
}

// Number of times the antlions will attempt to generate a random chase position
#define NUM_CHASE_POSITION_ATTEMPTS		3

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &targetPos - 
//			&result - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::FindChasePosition( const Vector &targetPos, Vector &result )
{
	if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == true )
	{
		 result = targetPos;
		 return true;
	}

	Vector runDir = ( targetPos - GetAbsOrigin() );
	VectorNormalize( runDir );
	
	Vector	vRight, vUp;
	VectorVectors( runDir, vRight, vUp );

	for ( int i = 0; i < NUM_CHASE_POSITION_ATTEMPTS; i++ )
	{
		result	= targetPos;
		result += -runDir * random->RandomInt( 64, 128 );
		result += vRight * random->RandomInt( -128, 128 );
		
		//FIXME: We need to do a more robust search here
		// Find a ground position and try to get there
		if ( GetGroundPosition( result, result ) )
			return true;
	}
	
	//TODO: If we're making multiple inquiries to this, make sure it's evenly spread

	if ( g_debug_antlion.GetInt() == 1 )
	{
		NDebugOverlay::Cross3D( result, -Vector(32,32,32), Vector(32,32,32), 255, 255, 0, true, 2.0f );
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &testPos - 
//-----------------------------------------------------------------------------
bool CNPC_Antlion::GetGroundPosition( const Vector &testPos, Vector &result )
{
	// Trace up to clear the ground
	trace_t	tr;
	AI_TraceHull( testPos, testPos + Vector( 0, 0, 64 ), NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );

	// If we're stuck in solid, this can't be valid
	if ( tr.allsolid )
	{
		if ( g_debug_antlion.GetInt() == 3 )
		{
			NDebugOverlay::BoxDirection( testPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ) + Vector( 0, 0, 128 ), Vector( 0, 0, 1 ), 255, 0, 0, true, 2.0f );
		}

		return false;
	}

	if ( g_debug_antlion.GetInt() == 3 )
	{
		NDebugOverlay::BoxDirection( testPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ) + Vector( 0, 0, 128 ), Vector( 0, 0, 1 ), 0, 255, 0, true, 2.0f );
	}

	// Trace down to find the ground
	AI_TraceHull( tr.endpos, tr.endpos - Vector( 0, 0, 128 ), NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );

	if ( g_debug_antlion.GetInt() == 3 )
	{
		NDebugOverlay::BoxDirection( tr.endpos, NAI_Hull::Mins( GetHullType() ) - Vector( 0, 0, 256 ), NAI_Hull::Maxs( GetHullType() ), Vector( 0, 0, 1 ), 255, 255, 0, true, 2.0f );
	}

	// We must end up on the floor with this trace
	if ( tr.fraction < 1.0f )
	{
		if ( g_debug_antlion.GetInt() == 3 )
		{
			NDebugOverlay::Cross3D( tr.endpos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), 255, 0, 0, true, 2.0f );
		}

		result = tr.endpos;
		return true;
	}

	// Ended up in open space
	return false;
}
void CNPC_Antlion::ManageFleeCapabilities( bool bEnable )
{
	if ( bEnable == false )
	{
		//Remove the jump capabilty when we build our route.
		//We'll enable it back again after the route has been built.
		CapabilitiesRemove( bits_CAP_MOVE_JUMP );

		if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == false  )
			 CapabilitiesRemove( bits_CAP_SKIP_NAV_GROUND_CHECK );
	}
	else
	{
		if ( m_bDisableJump == false )
			 CapabilitiesAdd( bits_CAP_MOVE_JUMP );

		if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == false  )
			 CapabilitiesAdd( bits_CAP_SKIP_NAV_GROUND_CHECK );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : soundType - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::GetPathToSoundFleePoint( int soundType )
{
	CSound *pSound = GetLoudestSoundOfType( soundType );

	if ( pSound == NULL  )
	{
		//NOTENOTE: If you're here, there's a disparity between Listen() and GetLoudestSoundOfType() - jdw
		TaskFail( "Unable to find thumper sound!" );
		return false;
	}

	ManageFleeCapabilities( false );

	//Try and find a hint-node first
	CHintCriteria	hintCriteria;

	hintCriteria.SetHintType( HINT_ANTLION_THUMPER_FLEE_POINT );
	hintCriteria.SetFlag( bits_HINT_NODE_NEAREST );
	hintCriteria.AddIncludePosition( WorldSpaceCenter(), 2500 );

	CAI_Hint *pHint = CAI_HintManager::FindHint( WorldSpaceCenter(), hintCriteria );

	Vector vecFleeGoal;
	Vector vecSoundPos = pSound->GetSoundOrigin();

	// Put the sound location on the same plane as the antlion.
	vecSoundPos.z = GetAbsOrigin().z;

	Vector vecFleeDir = GetAbsOrigin() - vecSoundPos;
	VectorNormalize( vecFleeDir );

	if ( pHint != NULL )
	{
		// Get our goal position
		pHint->GetPosition( this, &vecFleeGoal );

		// Find a route to that position
		AI_NavGoal_t goal( vecFleeGoal, (Activity) ACT_ANTLION_RUN_AGITATED, 128, AIN_DEF_FLAGS );

		if ( GetNavigator()->SetGoal( goal ) )
		{
			pHint->Lock( this );
			pHint->Unlock( 2.0f );

			GetNavigator()->SetArrivalActivity( (Activity) ACT_ANTLION_DISTRACT_ARRIVED );
			GetNavigator()->SetArrivalDirection( -vecFleeDir );

			ManageFleeCapabilities( true );
			return true;
		}
	}

	//Make us offset this a little at least
	float flFleeYaw = VecToYaw( vecFleeDir ) + random->RandomInt( -20, 20 );

	vecFleeDir = UTIL_YawToVector( flFleeYaw );

	// Move us to the outer radius of the noise (with some randomness)
	vecFleeGoal = vecSoundPos + vecFleeDir * ( pSound->Volume() + random->RandomInt( 32, 64 ) );

	// Find a route to that position
	AI_NavGoal_t goal( vecFleeGoal + Vector( 0, 0, 8 ), (Activity) ACT_ANTLION_RUN_AGITATED, 512, AIN_DEF_FLAGS );

	if ( GetNavigator()->SetGoal( goal ) )
	{
		GetNavigator()->SetArrivalActivity( (Activity) ACT_ANTLION_DISTRACT_ARRIVED );
		GetNavigator()->SetArrivalDirection( -vecFleeDir );

		ManageFleeCapabilities( true );
		return true;
	}

	ManageFleeCapabilities( true );
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pEvent - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::HandleAnimEvent( animevent_t *pEvent )
{
	if ( pEvent->event == AE_ANTLION_WALK_FOOTSTEP )
	{
		EmitSound( "NPC_Antlion.Footstep", pEvent->eventtime );
		return;
	}

	if ( pEvent->event == AE_ANTLION_MELEE_HIT1 )
	{
		MeleeAttack( ANTLION_MELEE1_RANGE, sk_antlion_swipe_damage.GetFloat(), QAngle( 20.0f, 0.0f, -12.0f ), Vector( -250.0f, 1.0f, 1.0f ) );
		return;
	}

	if ( pEvent->event == AE_ANTLION_MELEE_HIT2 )
	{
		MeleeAttack( ANTLION_MELEE1_RANGE, sk_antlion_swipe_damage.GetFloat(), QAngle( 20.0f, 0.0f, 0.0f ), Vector( -350.0f, 1.0f, 1.0f ) );
		return;
	}

	if ( pEvent->event == AE_ANTLION_MELEE_POUNCE )
	{
		MeleeAttack( ANTLION_MELEE2_RANGE, sk_antlion_swipe_damage.GetFloat(), QAngle( 4.0f, 0.0f, 0.0f ), Vector( -250.0f, 1.0f, 1.0f ) );
		return;
	}
		
	if ( pEvent->event == AE_ANTLION_OPEN_WINGS )
	{
		SetWings( true );
		return;
	}

	if ( pEvent->event == AE_ANTLION_CLOSE_WINGS )
	{
		SetWings( false );
		return;
	}

	if ( pEvent->event == AE_ANTLION_VANISH )
	{
		AddSolidFlags( FSOLID_NOT_SOLID );
		m_takedamage	= DAMAGE_NO;
		AddEffects( EF_NODRAW );
		SetWings( false );

		return;
	}

	if ( pEvent->event == AE_ANTLION_BURROW_IN )
	{
		//Burrowing sound
		EmitSound( "NPC_Antlion.BurrowIn" );

		//Shake the screen
		UTIL_ScreenShake( GetAbsOrigin(), 0.5f, 80.0f, 1.0f, 256.0f, SHAKE_START );

		//Throw dust up
		CreateDust();

		if ( GetHintNode() )
		{
			GetHintNode()->Unlock( 2.0f );
		}

		return;
	}

	if ( pEvent->event == AE_ANTLION_BURROW_OUT )
	{
		EmitSound( "NPC_Antlion.BurrowOut" );

		//Shake the screen
		UTIL_ScreenShake( GetAbsOrigin(), 0.5f, 80.0f, 1.0f, 256.0f, SHAKE_START );

		//Throw dust up
		CreateDust();

		RemoveEffects( EF_NODRAW );
		RemoveFlag( FL_NOTARGET );

		return;
	}

	if ( pEvent->event == AE_ANTLION_FOOTSTEP_SOFT )
	{
		EmitSound( "NPC_Antlion.FootstepSoft", pEvent->eventtime );
		return;
	}

	if ( pEvent->event == AE_ANTLION_FOOTSTEP_HEAVY )
	{
		EmitSound( "NPC_Antlion.FootstepHeavy", pEvent->eventtime );
		return;
	}
	
	
	if ( pEvent->event == AE_ANTLION_MELEE1_SOUND )
	{
		EmitSound( "NPC_Antlion.MeleeAttackSingle" );
		return;
	}
	
	if ( pEvent->event == AE_ANTLION_MELEE2_SOUND )
	{
		EmitSound( "NPC_Antlion.MeleeAttackDouble" );
		return;
	}

	if ( pEvent->event == AE_ANTLION_START_JUMP )
	{
		StartJump();
		return;
	}

	BaseClass::HandleAnimEvent( pEvent );
}

bool CNPC_Antlion::IsUnusableNode(int iNodeID, CAI_Hint *pHint)
{
	bool iBaseReturn = BaseClass::IsUnusableNode( iNodeID, pHint );

	if ( g_test_new_antlion_jump.GetBool() == 0 )
		 return iBaseReturn;

	CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( iNodeID );

	if ( pNode )
	{
		if ( pNode->IsLocked() )
			 return true;
	}

	return iBaseReturn;
}

void CNPC_Antlion::LockJumpNode( void )
{
	if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == false )
		 return;
	
	if ( GetNavigator()->GetPath() == NULL )
		 return;

	if ( g_test_new_antlion_jump.GetBool() == false )
		 return;

	AI_Waypoint_t *pWaypoint = GetNavigator()->GetPath()->GetCurWaypoint();

	while ( pWaypoint )
	{
		AI_Waypoint_t *pNextWaypoint = pWaypoint->GetNext();
		if ( pNextWaypoint && pNextWaypoint->NavType() == NAV_JUMP && pWaypoint->iNodeID != NO_NODE )
		{
			CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( pWaypoint->iNodeID );

			if ( pNode )
			{
				//NDebugOverlay::Box( pNode->GetOrigin(), Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 255, 0, 0, 0, 2 );
				pNode->Lock( 0.5f );
				break;
			}
		}
		else
		{
			pWaypoint = pWaypoint->GetNext();
		}
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CNPC_Antlion::OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
{
	bool iBaseReturn = BaseClass::OnObstructionPreSteer( pMoveGoal, distClear, pResult );

	if ( g_test_new_antlion_jump.GetBool() == false )
		 return iBaseReturn;

	if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == false )
		 return iBaseReturn;

	CAI_BaseNPC *pBlocker = pMoveGoal->directTrace.pObstruction->MyNPCPointer();

	if ( pBlocker && pBlocker->Classify() == CLASS_ANTLION )
	{
		// HACKHACK
		CNPC_Antlion *pAntlion = dynamic_cast< CNPC_Antlion * > ( pBlocker );

		if ( pAntlion )
		{
			if ( pAntlion->AllowedToBePushed() == true && GetEnemy() == NULL )
			{
				//NDebugOverlay::Box( pAntlion->GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 2 );
				pAntlion->GetMotor()->SetIdealYawToTarget( WorldSpaceCenter() );
				pAntlion->SetSchedule( SCHED_MOVE_AWAY );
				pAntlion->m_flNextJumpPushTime = gpGlobals->curtime + 2.0f;
			}
		}
	}

	return iBaseReturn;
}

bool NPC_Antlion_IsAntlion( CBaseEntity *pEntity )
{
	CNPC_Antlion *pAntlion = dynamic_cast<CNPC_Antlion *>(pEntity);

	return pAntlion ? true : false;
}

class CTraceFilterAntlion : public CTraceFilterEntitiesOnly
{
public:
	CTraceFilterAntlion( const CBaseEntity *pEntity ) { m_pIgnore = pEntity; }

	virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
	{
		CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );

		if ( m_pIgnore == pEntity )
			 return false;
		
		if ( pEntity->IsNPC() == false )
			 return false;
		
		if ( NPC_Antlion_IsAntlion( pEntity ) )
			 return true;
		
		return false;
	}
private:
	
	const CBaseEntity		*m_pIgnore;
};


//-----------------------------------------------------------------------------
// Purpose:  
//-----------------------------------------------------------------------------
void CNPC_Antlion::StartTask( const Task_t *pTask )
{
	switch ( pTask->iTask ) 
	{
	case TASK_ANNOUNCE_ATTACK:
		{
			EmitSound( "NPC_Antlion.MeleeAttackSingle" );
			TaskComplete();
			break;
		}

	case TASK_ANTLION_FACE_JUMP:
		break;

	case TASK_ANTLION_DROWN:
	{
		// Set the gravity really low here! Sink slowly
		SetGravity( 0 );
		SetAbsVelocity( vec3_origin );
		m_flTimeDrown = gpGlobals->curtime + 4;
		break;
	}

	case TASK_ANTLION_REACH_FIGHT_GOAL:

		m_OnReachFightGoal.FireOutput( this, this );
		TaskComplete();
		break;

	case TASK_ANTLION_DISMOUNT_NPC:
		{
			CBaseEntity *pGroundEnt = GetGroundEntity();
			
			if( pGroundEnt != NULL )
			{
				trace_t trace;
				CTraceFilterAntlion traceFilter( this );
				UTIL_TraceHull( GetAbsOrigin(), GetAbsOrigin(), WorldAlignMins(), WorldAlignMaxs(), MASK_SOLID, &traceFilter, &trace );

				if ( trace.m_pEnt )
				{
					OnTakeDamage( CTakeDamageInfo( this, this, m_iHealth+1, DMG_GENERIC ) );
					return;
				}

				// Jump behind the other NPC so I don't block their path.
				Vector vecJumpDir; 

				pGroundEnt->GetVectors( &vecJumpDir, NULL, NULL );

				SetGroundEntity( NULL );
				
				// Bump up
				UTIL_SetOrigin( this, GetAbsOrigin() + Vector( 0, 0 , 1 ) );
				
				SetAbsVelocity( vecJumpDir * -200 + Vector( 0, 0, 100 ) );

				SetActivity( (Activity) ACT_ANTLION_FLIP );
			}
			else
			{
				// Dead or gone now
				TaskComplete();
			}
		}

		break;

	case TASK_ANTLION_FACE_BUGBAIT:
			
		//Must have a saved sound
		//FIXME: This isn't assured to be still pointing to the right place, need to protect this
		if ( !m_bHasHeardSound )
		{
			TaskFail( "No remembered bug bait sound to run to!" );
			return;
		}

		GetMotor()->SetIdealYawToTargetAndUpdate( m_vecHeardSound );
		SetTurnActivity();

		break;
	
	case TASK_ANTLION_GET_PATH_TO_BUGBAIT:
		{
			//Must have a saved sound
			//FIXME: This isn't assured to be still pointing to the right place, need to protect this
			if ( !m_bHasHeardSound )
			{
				TaskFail( "No remembered bug bait sound to run to!" );
				return;
			}
			
			Vector	goalPos;

			// Find the position to chase to
			if ( FindChasePosition( m_vecHeardSound, goalPos ) )
			{
				AI_NavGoal_t goal( goalPos, (Activity) ACT_ANTLION_RUN_AGITATED, ANTLION_BUGBAIT_NAV_TOLERANCE );
				
				//Try to run directly there
				if ( GetNavigator()->SetGoal( goal, AIN_DISCARD_IF_FAIL ) == false )
				{
					//Try and get as close as possible otherwise
					AI_NavGoal_t nearGoal( GOALTYPE_LOCATION_NEAREST_NODE, goalPos, (Activity) ACT_ANTLION_RUN_AGITATED, ANTLION_BUGBAIT_NAV_TOLERANCE );

					if ( GetNavigator()->SetGoal( nearGoal, AIN_CLEAR_PREVIOUS_STATE ) )
					{
						//FIXME: HACK! The internal pathfinding is setting this without our consent, so override it!
						ClearCondition( COND_TASK_FAILED );

						LockJumpNode();
						TaskComplete();
						return;
					}
					else
					{
						TaskFail( "Antlion failed to find path to bugbait position\n" );
						return;
					}
				}
				else
				{
					LockJumpNode();
					TaskComplete();
					return;
				}
			}

			TaskFail( "Antlion failed to find path to bugbait position\n" );
			break;
		}

	case TASK_ANTLION_WAIT_FOR_TRIGGER:
		m_flIdleDelay = gpGlobals->curtime + 1.0f;

		break;

	case TASK_ANTLION_JUMP:
		
		if ( CheckLanding() )
		{
			TaskComplete();
		}

		break;

	case TASK_ANTLION_CHECK_FOR_UNBORROW:
		
		if ( ValidBurrowPoint( GetAbsOrigin() ) )
		{
			m_spawnflags &= ~SF_NPC_GAG;
			RemoveSolidFlags( FSOLID_NOT_SOLID );
			TaskComplete();
		}

		break;

	case TASK_ANTLION_BURROW_WAIT:
		
		if ( pTask->flTaskData == 1.0f )
		{
			//Set our next burrow time
			m_flBurrowTime = gpGlobals->curtime + random->RandomFloat( 1, 6 );
		}

		break;

	case TASK_ANTLION_FIND_BURROW_IN_POINT:
		
		if ( FindBurrow( GetAbsOrigin(), pTask->flTaskData, ANTLION_BURROW_IN ) == false )
		{
			TaskFail( "TASK_ANTLION_FIND_BURROW_IN_POINT: Unable to find burrow in position\n" );
		}
		else
		{
			TaskComplete();
		}

		break;

	case TASK_ANTLION_FIND_BURROW_OUT_POINT:
		
		if ( FindBurrow( GetAbsOrigin(), pTask->flTaskData, ANTLION_BURROW_OUT ) == false )
		{
			TaskFail( "TASK_ANTLION_FIND_BURROW_OUT_POINT: Unable to find burrow out position\n" );
		}
		else
		{
			TaskComplete();
		}

		break;

	case TASK_ANTLION_BURROW:
		Burrow();
		TaskComplete();

		break;

	case TASK_ANTLION_UNBURROW:
		Unburrow();
		TaskComplete();

		break;

	case TASK_ANTLION_VANISH:
		AddEffects( EF_NODRAW );
		AddFlag( FL_NOTARGET );
		m_spawnflags |= SF_NPC_GAG;
		
		// If the task parameter is non-zero, remove us when we vanish
		if ( pTask->flTaskData )
		{
			CBaseEntity *pOwner = GetOwnerEntity();
			
			if( pOwner != NULL )
			{
				pOwner->DeathNotice( this );
				SetOwnerEntity( NULL );
			}

			// NOTE: We can't UTIL_Remove here, because we're in the middle of running our AI, and
			//		 we'll crash later in the bowels of the AI. Remove ourselves next frame.
			SetThink( &CNPC_Antlion::SUB_Remove );
			SetNextThink( gpGlobals->curtime + 0.1 );
		}

		TaskComplete();

		break;

	case TASK_ANTLION_GET_THUMPER_ESCAPE_PATH:
		{
			if ( GetPathToSoundFleePoint( SOUND_THUMPER ) )			
			{
				TaskComplete();
			}
			else
			{
				TaskFail( FAIL_NO_REACHABLE_NODE );
			}
		}
		
		break;

	case TASK_ANTLION_GET_PHYSICS_DANGER_ESCAPE_PATH:
		{
			if ( GetPathToSoundFleePoint( SOUND_PHYSICS_DANGER ) )
			{
				TaskComplete();
			}
			else
			{
				TaskFail( FAIL_NO_REACHABLE_NODE );
			}
		}
		
		break;

	default:
		BaseClass::StartTask( pTask );
		break;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pTask - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::RunTask( const Task_t *pTask )
{
	switch ( pTask->iTask )
	{
	case TASK_ANTLION_FACE_JUMP:
		{
			Vector	jumpDir = m_vecSavedJump;
			VectorNormalize( jumpDir );
			
			QAngle	jumpAngles;
			VectorAngles( jumpDir, jumpAngles );

			GetMotor()->SetIdealYawAndUpdate( jumpAngles[YAW], AI_KEEP_YAW_SPEED );
			SetTurnActivity();
			
			if ( GetMotor()->DeltaIdealYaw() < 2 )
			{
				TaskComplete();
			}
		}

		break;

	case TASK_ANTLION_DROWN:
		if( gpGlobals->curtime > m_flTimeDrown )
		{
			OnTakeDamage( CTakeDamageInfo( this, this, m_iHealth+1, DMG_DROWN ) );
			TaskComplete();
		}
		break;

	case TASK_ANTLION_REACH_FIGHT_GOAL:
		break;

	case TASK_ANTLION_DISMOUNT_NPC:
		
		if ( GetFlags() & FL_ONGROUND )
		{
			CBaseEntity *pGroundEnt = GetGroundEntity();

			if ( ( pGroundEnt != NULL ) && ( ( pGroundEnt->MyNPCPointer() != NULL ) || pGroundEnt->GetSolidFlags() & FSOLID_NOT_STANDABLE ) )
			{
				// Jump behind the other NPC so I don't block their path.
				Vector vecJumpDir; 

				pGroundEnt->GetVectors( &vecJumpDir, NULL, NULL );

				SetGroundEntity( NULL );	
				
				// Bump up
				UTIL_SetOrigin( this, GetAbsOrigin() + Vector( 0, 0 , 1 ) );
				
				SetAbsVelocity( vecJumpDir * -200 + Vector( 0, 0, 100 ) );

				SetActivity( (Activity) ACT_ANTLION_FLIP );
			}
			else if ( IsActivityFinished() )
			{
				TaskComplete();
			}
		}
		
		break;

	case TASK_ANTLION_FACE_BUGBAIT:
			
		//Must have a saved sound
		//FIXME: This isn't assured to be still pointing to the right place, need to protect this
		if ( !m_bHasHeardSound )
		{
			TaskFail( "No remembered bug bait sound to run to!" );
			return;
		}

		GetMotor()->SetIdealYawToTargetAndUpdate( m_vecHeardSound );

		if ( FacingIdeal() )
		{
			TaskComplete();
		}

		break;

	case TASK_ANTLION_WAIT_FOR_TRIGGER:
		
		if ( ( m_flIdleDelay > gpGlobals->curtime ) || GetEntityName() != NULL_STRING )
			return;

		TaskComplete();

		break;

	case TASK_ANTLION_JUMP:

		if ( CheckLanding() )
		{
			TaskComplete();
		}

		break;

	case TASK_ANTLION_CHECK_FOR_UNBORROW:
		
		//Must wait for our next check time
		if ( m_flBurrowTime > gpGlobals->curtime )
			return;

		//See if we can pop up
		if ( ValidBurrowPoint( GetAbsOrigin() ) )
		{
			m_spawnflags &= ~SF_NPC_GAG;
			RemoveSolidFlags( FSOLID_NOT_SOLID );

			TaskComplete();
			return;
		}

		//Try again in a couple of seconds
		m_flBurrowTime = gpGlobals->curtime + random->RandomFloat( 0.5f, 1.0f );

		break;

	case TASK_ANTLION_BURROW_WAIT:
		
		//See if enough time has passed
		if ( m_flBurrowTime < gpGlobals->curtime )
		{
			TaskComplete();
		}
		
		break;

	default:
		BaseClass::RunTask( pTask );
		break;
	}
}

bool CNPC_Antlion::AllowedToBePushed( void )
{
	if ( IsMoving() == false && IsCurSchedule( SCHED_ANTLION_FLIP ) == false
		 && GetNavType() != NAV_JUMP && m_flNextJumpPushTime <= gpGlobals->curtime )
	{
		return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: Returns true if a reasonable jumping distance
// Input  :
// Output :
//-----------------------------------------------------------------------------
bool CNPC_Antlion::IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos ) const
{
	const float MAX_JUMP_RISE		= 512;
	const float MAX_JUMP_DROP		= 512;
	const float MAX_JUMP_DISTANCE	= 1024;
	const float MIN_JUMP_DISTANCE   = 128;

	if ( CAntlionRepellant::IsPositionRepellantFree( endPos ) == false )
		 return false;
	
	//Adrian: Don't try to jump if my destination is right next to me.
	if ( ( endPos - GetAbsOrigin()).Length() < MIN_JUMP_DISTANCE ) 
		 return false;

	if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) && g_test_new_antlion_jump.GetBool() == true )
	{
		trace_t	tr;
		AI_TraceHull( endPos, endPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
		
		if ( tr.m_pEnt )
		{
			CAI_BaseNPC *pBlocker = tr.m_pEnt->MyNPCPointer();

			if ( pBlocker && pBlocker->Classify() == CLASS_ANTLION )
			{
				// HACKHACK
				CNPC_Antlion *pAntlion = dynamic_cast< CNPC_Antlion * > ( pBlocker );

				if ( pAntlion )
				{
					if ( pAntlion->AllowedToBePushed() == true )
					{
					//	NDebugOverlay::Line( GetAbsOrigin(), endPos, 255, 0, 0, 0, 2 );
					//	NDebugOverlay::Box( pAntlion->GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 0, 0, 255, 0, 2 );
						pAntlion->GetMotor()->SetIdealYawToTarget( endPos );
						pAntlion->SetSchedule( SCHED_MOVE_AWAY );
						pAntlion->m_flNextJumpPushTime = gpGlobals->curtime + 2.0f;
					}
				}
			}
		}
	}

	return BaseClass::IsJumpLegal( startPos, apex, endPos, MAX_JUMP_RISE, MAX_JUMP_DROP, MAX_JUMP_DISTANCE );
}

bool CNPC_Antlion::IsFirmlyOnGround( void )
{
	if( !( GetFlags()&FL_ONGROUND ) )
		return false;

	trace_t tr;

	float flHeight =  fabs( GetHullMaxs().z - GetHullMins().z );
	
	Vector vOrigin = GetAbsOrigin() + Vector( GetHullMins().x, GetHullMins().y, 0 );
//	NDebugOverlay::Line( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5  ), 255, 0, 0, true, 5 );
	UTIL_TraceLine( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5  ), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );

	if ( tr.fraction != 1.0f )
		 return true;
	
	vOrigin = GetAbsOrigin() - Vector( GetHullMins().x, GetHullMins().y, 0 );
//	NDebugOverlay::Line( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5  ), 255, 0, 0, true, 5 );
	UTIL_TraceLine( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5  ), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );

	if ( tr.fraction != 1.0f )
		 return true;

	vOrigin = GetAbsOrigin() + Vector( GetHullMins().x, -GetHullMins().y, 0 );
//	NDebugOverlay::Line( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5  ), 255, 0, 0, true, 5 );
	UTIL_TraceLine( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5  ), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );

	if ( tr.fraction != 1.0f )
		 return true;

	vOrigin = GetAbsOrigin() + Vector( -GetHullMins().x, GetHullMins().y, 0 );
//	NDebugOverlay::Line( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5  ), 255, 0, 0, true, 5 );
	UTIL_TraceLine( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5  ), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );

	if ( tr.fraction != 1.0f )
		 return true;
	
	return false;
}

int CNPC_Antlion::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
{
	if ( m_FollowBehavior.GetNumFailedFollowAttempts() >= 2 )
	{
		if( IsFirmlyOnGround() == false )
		{
			Vector vecJumpDir; 
				
			vecJumpDir.z = 0;
			vecJumpDir.x = 0;
			vecJumpDir.y = 0;
			
			while( vecJumpDir.x == 0 && vecJumpDir.y == 0 )
			{
				vecJumpDir.x = random->RandomInt( -1, 1 ); 
				vecJumpDir.y = random->RandomInt( -1, 1 );
			}

			vecJumpDir.NormalizeInPlace();

			SetGroundEntity( NULL );
	
			m_vecSavedJump = vecJumpDir * 512 + Vector( 0, 0, 256 );
			m_bForcedStuckJump = true;
	
			return SCHED_ANTLION_JUMP;
		}
	}

	return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::ShouldJump( void )
{
	if ( GetEnemy() == NULL )
		return false;

	//Too soon to try to jump
	if ( m_flJumpTime > gpGlobals->curtime )
		return false;

	// only jump if you're on the ground
  	if (!(GetFlags() & FL_ONGROUND) || GetNavType() == NAV_JUMP )
		return false;

	// Don't jump if I'm not allowed
	if ( ( CapabilitiesGet() & bits_CAP_MOVE_JUMP ) == false )
		return false;

	Vector vEnemyForward, vForward;

	GetEnemy()->GetVectors( &vEnemyForward, NULL, NULL );
	GetVectors( &vForward, NULL, NULL );

	float flDot = DotProduct( vForward, vEnemyForward );

	if ( flDot < 0.5f )
		 flDot = 0.5f;

	Vector vecPredictedPos;

	//Get our likely position in two seconds
	UTIL_PredictedPosition( GetEnemy(), flDot * 2.5f, &vecPredictedPos );

	// Don't jump if we're already near the target
	if ( ( GetAbsOrigin() - vecPredictedPos ).LengthSqr() < (512*512) )
		return false;

	//Don't retest if the target hasn't moved enough
	//FIXME: Check your own distance from last attempt as well
	if ( ( ( m_vecLastJumpAttempt - vecPredictedPos ).LengthSqr() ) < (128*128) )
	{
		m_flJumpTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f );		
		return false;
	}

	Vector	targetDir = ( vecPredictedPos - GetAbsOrigin() );

	float flDist = VectorNormalize( targetDir );

	// don't jump at target it it's very close
	if (flDist < ANTLION_JUMP_MIN)
		return false;

	Vector	targetPos = vecPredictedPos + ( targetDir * (GetHullWidth()*4.0f) );

	if ( CAntlionRepellant::IsPositionRepellantFree( targetPos ) == false )
		 return false;

	// Try the jump
	AIMoveTrace_t moveTrace;
	GetMoveProbe()->MoveLimit( NAV_JUMP, GetAbsOrigin(), targetPos, MASK_NPCSOLID, GetNavTargetEntity(), &moveTrace );

	//See if it succeeded
	if ( IsMoveBlocked( moveTrace.fStatus ) )
	{
		if ( g_debug_antlion.GetInt() == 2 )
		{
			NDebugOverlay::Box( targetPos, GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 5 );
			NDebugOverlay::Line( GetAbsOrigin(), targetPos, 255, 0, 0, 0, 5 );
		}

		m_flJumpTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f );
		return false;
	}

	if ( g_debug_antlion.GetInt() == 2 )
	{
		NDebugOverlay::Box( targetPos, GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 5 );
		NDebugOverlay::Line( GetAbsOrigin(), targetPos, 0, 255, 0, 0, 5 );
	}

	//Save this jump in case the next time fails
	m_vecSavedJump = moveTrace.vJumpVelocity;
	m_vecLastJumpAttempt = targetPos;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : scheduleType - 
// Output : int
//-----------------------------------------------------------------------------
int CNPC_Antlion::TranslateSchedule( int scheduleType )
{
	if ( ( m_hFollowTarget != NULL ) || IsAllied() )
	{
		if ( ( scheduleType == SCHED_IDLE_STAND ) || ( scheduleType == SCHED_ALERT_STAND ) )
			return SCHED_ANTLION_BUGBAIT_IDLE_STAND;
	}

	return BaseClass::TranslateSchedule( scheduleType );
}

Activity CNPC_Antlion::NPC_TranslateActivity( Activity baseAct )
{
	return BaseClass::NPC_TranslateActivity( baseAct );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : int
//-----------------------------------------------------------------------------
int CNPC_Antlion::ChooseMoveSchedule( void )
{
	// See if we need to invalidate our fight goal
	if ( ShouldResumeFollow() )
	{
		// Set us back to following
		SetMoveState( ANTLION_MOVE_FOLLOW );

		// Tell our parent that we've swapped modes
		CAntlionTemplateMaker *pMaker = dynamic_cast<CAntlionTemplateMaker *>(GetOwnerEntity());

		if ( pMaker != NULL )
		{
			pMaker->SetChildMoveState( ANTLION_MOVE_FOLLOW );
		}
	}

	// Figure out our move state
	switch( m_MoveState )
	{
	case ANTLION_MOVE_FREE:
		return SCHED_NONE;	// Let the base class handle us
		break;

	// Fighting to a position
	case ANTLION_MOVE_FIGHT_TO_GOAL:
		{
			if ( m_hFightGoalTarget )
			{
				float targetDist = UTIL_DistApprox( WorldSpaceCenter(), m_hFightGoalTarget->GetAbsOrigin() );

				if ( targetDist > 256 )
				{
					Vector testPos;
					Vector targetPos = ( m_hFightGoalTarget ) ? m_hFightGoalTarget->GetAbsOrigin() : m_vSavePosition;

					// Find a suitable chase position
					if ( FindChasePosition( targetPos, testPos ) )
					{
						m_vSavePosition = testPos;
						return SCHED_ANTLION_RUN_TO_FIGHT_GOAL;
					}
				}
			}
		}
		break;

	// Following a goal
	case ANTLION_MOVE_FOLLOW:
		{
			if ( m_FollowBehavior.CanSelectSchedule() )
			{
				// See if we should burrow away if our target it too far off
				if ( ShouldAbandonFollow() )
					return SCHED_ANTLION_BURROW_AWAY;

				DeferSchedulingToBehavior( &m_FollowBehavior );
				return BaseClass::SelectSchedule();
			}
		}
		break;
	}

	return SCHED_NONE;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : int
//-----------------------------------------------------------------------------
int CNPC_Antlion::SelectSchedule( void )
{
	// Clear out this condition
	ClearCondition( COND_ANTLION_RECEIVED_ORDERS );

	// If we're supposed to be burrowed, stay there
	if ( m_bStartBurrowed )
		return SCHED_ANTLION_WAIT_FOR_UNBORROW_TRIGGER;

	// See if a friendly player is pushing us away
	if ( HasCondition( COND_PLAYER_PUSHING ) )
		return SCHED_MOVE_AWAY;

	//Flipped?
	if ( HasCondition( COND_ANTLION_FLIPPED ) )
	{
		ClearCondition( COND_ANTLION_FLIPPED );
		return SCHED_ANTLION_FLIP;
	}

	if( HasCondition( COND_ANTLION_IN_WATER ) )
	{
		// No matter what, drown in water
		return SCHED_ANTLION_DROWN;
	}

	// If we're flagged to burrow away when eluded, do so
	if ( ( m_spawnflags & SF_ANTLION_BURROW_ON_ELUDED ) && ( HasCondition( COND_ENEMY_UNREACHABLE ) || HasCondition( COND_ENEMY_TOO_FAR ) ) )
		return SCHED_ANTLION_BURROW_AWAY;

	//Hear a thumper?
	if ( HasCondition( COND_HEAR_THUMPER ) )
	{
		// Ignore thumpers that aren't visible
		CSound *pSound = GetLoudestSoundOfType( SOUND_THUMPER );
		
		if ( pSound )
		{
			PainSound();
			ClearCondition( COND_HEAR_THUMPER );

			return SCHED_ANTLION_FLEE_THUMPER;
		}
	}

	//Hear a physics danger sound?
	if( HasCondition( COND_HEAR_PHYSICS_DANGER ) )
	{
		PainSound();
		return SCHED_ANTLION_FLEE_PHYSICS_DANGER;
	}

	//On another NPC's head?
	if( HasCondition( COND_ANTLION_ON_NPC ) )
	{
		// You're on an NPC's head. Get off.
		return SCHED_ANTLION_DISMOUNT_NPC;
	}

	//Hear bug bait splattered?
	if ( HasCondition( COND_HEAR_BUGBAIT ) && ( m_bIgnoreBugbait == false ) )
	{
		//Play a special sound
		if ( m_flNextAcknowledgeTime < gpGlobals->curtime )
		{
			EmitSound( "NPC_Antlion.Distracted" );
			m_flNextAcknowledgeTime = gpGlobals->curtime + 1.0f;
		}
		
		m_flIdleDelay = gpGlobals->curtime + 4.0f;

		//If the sound is valid, act upon it
		if ( m_bHasHeardSound )
		{		
			//Mark anything in the area as more interesting
			CBaseEntity *pTarget = NULL;
			CBaseEntity *pNewEnemy = NULL;
			Vector		soundOrg = m_vecHeardSound;

			//Find all entities within that sphere
			while ( ( pTarget = gEntList.FindEntityInSphere( pTarget, soundOrg, bugbait_radius.GetInt() ) ) != NULL )
			{
				CAI_BaseNPC *pNPC = pTarget->MyNPCPointer();

				if ( pNPC == NULL )
					continue;

				if ( pNPC->CanBeAnEnemyOf( this ) == false )
					continue;

				//Check to see if the default relationship is hatred, and if so intensify that
				if ( ( IRelationType( pNPC ) == D_HT ) && ( pNPC->IsPlayer() == false ) )
				{
					AddEntityRelationship( pNPC, D_HT, 99 );
					
					//Try to spread out the enemy distribution
					if ( ( pNewEnemy == NULL ) || ( random->RandomInt( 0, 1 ) ) )
					{
						pNewEnemy = pNPC;
						continue;
					}
				}
			}
			
			// If we have a new enemy, take it
			if ( pNewEnemy != NULL )
			{
				//Setup our ignore info
				SetEnemy( pNewEnemy );
			}
			
			ClearCondition( COND_HEAR_BUGBAIT );

			return SCHED_ANTLION_CHASE_BUGBAIT;
		}
	}

	//Otherwise do basic state schedule selection
	switch ( m_NPCState )
	{	
	case NPC_STATE_COMBAT:
		{
			// Lunge at the enemy
			if ( HasCondition( COND_CAN_MELEE_ATTACK2 ) )
			{
				m_flPounceTime = gpGlobals->curtime + 1.5f;

				if ( m_bLeapAttack == true )
					 return SCHED_ANTLION_POUNCE_MOVING;
				else
					 return SCHED_ANTLION_POUNCE;
			}

			// Try to jump
			if ( HasCondition( COND_ANTLION_CAN_JUMP ) )
				return SCHED_ANTLION_JUMP;
		}
		break;

	default:
		{
			int	moveSched = ChooseMoveSchedule();

			if ( moveSched != SCHED_NONE )
				return moveSched;

		}
		break;
	}

	return BaseClass::SelectSchedule();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
{
	CTakeDamageInfo newInfo = info;

	Vector	vecShoveDir = vecDir;
	vecShoveDir.z = 0.0f;

	//Are we already flipped?
	if ( GetActivity() == ACT_ANTLION_FLIP )
	{
		//If we were hit by physics damage, move with it
		if ( newInfo.GetDamageType() & (DMG_CRUSH|DMG_PHYSGUN) )
		{
			PainSound();
			ApplyAbsVelocityImpulse( ( vecShoveDir * random->RandomInt( 500.0f, 1000.0f ) ) + Vector(0,0,64.0f) );
			SetGroundEntity( NULL );
		}

		//More vulnerable when flipped
		newInfo.ScaleDamage( 4.0f );
	}
	else if ( newInfo.GetDamageType() & (DMG_PHYSGUN) || 
			( newInfo.GetDamageType() & (DMG_BLAST|DMG_CRUSH) && newInfo.GetDamage() >= 25.0f ) )
	{
		//Grenades, physcannons, and physics impacts make us fuh-lip!
		
		//Don't flip off the deck
		if ( GetFlags() & FL_ONGROUND )
		{
			PainSound();

			SetCondition( COND_ANTLION_FLIPPED );

			//Get tossed!
			ApplyAbsVelocityImpulse( ( vecShoveDir * random->RandomInt( 500.0f, 1000.0f ) ) + Vector(0,0,64.0f) );
			SetGroundEntity( NULL );
		}
	}

	BaseClass::TraceAttack( newInfo, vecDir, ptr );
}

void CNPC_Antlion::StopLoopingSounds( void )
{
	if ( m_bLoopingStarted )
	{
		StopSound( "NPC_Antlion.WingsOpen" );
		m_bLoopingStarted = false;
	}
	if ( m_bAgitatedSound )
	{
		StopSound( "NPC_Antlion.LoopingAgitated" );
		m_bAgitatedSound = false;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::IdleSound( void )
{
	EmitSound( "NPC_Antlion.Idle" );
	m_flIdleDelay = gpGlobals->curtime + 4.0f;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::PainSound( void )
{
	EmitSound( "NPC_Antlion.Pain" );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : 
//-----------------------------------------------------------------------------
float CNPC_Antlion::GetIdealAccel( void ) const
{
	return GetIdealSpeed() * 2.0;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : float
//-----------------------------------------------------------------------------
float CNPC_Antlion::MaxYawSpeed( void )
{
	switch ( GetActivity() )
	{
	case ACT_IDLE:		
		return 32.0f;
		break;
	
	case ACT_WALK:
		return 16.0f;
		break;
	
	default:
	case ACT_RUN:
		return 32.0f;
		break;
	}

	return 32.0f;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::ShouldPlayIdleSound( void )
{
	//Only do idles in the right states
	if ( ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT ) )
		return false;

	//Gagged monsters don't talk
	if ( m_spawnflags & SF_NPC_GAG )
		return false;

	//Don't cut off another sound or play again too soon
	if ( m_flIdleDelay > gpGlobals->curtime )
		return false;

	//Randomize it a bit
	if ( random->RandomInt( 0, 20 ) )
		return false;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flDot - 
//			flDist - 
// Output : int
//-----------------------------------------------------------------------------
int CNPC_Antlion::MeleeAttack1Conditions( float flDot, float flDist )
{
#if 1 //NOTENOTE: Use predicted position melee attacks

	float		flPrDist, flPrDot;
	Vector		vecPrPos;
	Vector2D	vec2DPrDir;

	//Get our likely position in one half second
	UTIL_PredictedPosition( GetEnemy(), 0.5f, &vecPrPos );

	//Get the predicted distance and direction
	flPrDist	= ( vecPrPos - GetAbsOrigin() ).Length();
	vec2DPrDir	= ( vecPrPos - GetAbsOrigin() ).AsVector2D();

	Vector vecBodyDir = BodyDirection2D();

	Vector2D vec2DBodyDir = vecBodyDir.AsVector2D();
	
	flPrDot	= DotProduct2D ( vec2DPrDir, vec2DBodyDir );

	if ( flPrDist > ANTLION_MELEE1_RANGE )
		return COND_TOO_FAR_TO_ATTACK;

	if ( flPrDot < 0.5f )
		return COND_NOT_FACING_ATTACK;

#else

	if ( flDot < 0.5f )
		return COND_NOT_FACING_ATTACK;

	float flAdjustedDist = ANTLION_MELEE1_RANGE;

	if ( GetEnemy() )
	{
		CBasePlayer *pPlayer = ToBasePlayer( GetEnemy() );

		if ( pPlayer && pPlayer->IsInAVehicle() )
		{
			flAdjustedDist *= 2.0f;
		}
	}

	if ( flDist > flAdjustedDist )
		return COND_TOO_FAR_TO_ATTACK;

	trace_t	tr;
	AI_TraceHull( WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), -Vector(8,8,8), Vector(8,8,8), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );

	if ( tr.fraction < 1.0f )
		return 0;

#endif

	return COND_CAN_MELEE_ATTACK1;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flDot - 
//			flDist - 
// Output : int
//-----------------------------------------------------------------------------
int CNPC_Antlion::MeleeAttack2Conditions( float flDot, float flDist )
{
	// See if it's too soon to pounce again
	if ( m_flPounceTime > gpGlobals->curtime )
		return 0;

	float		flPrDist, flPrDot;
	Vector		vecPrPos;
	Vector2D	vec2DPrDir;

	//Get our likely position in one half second
	UTIL_PredictedPosition( GetEnemy(), 0.25f, &vecPrPos );

	//Get the predicted distance and direction
	flPrDist	= ( vecPrPos - GetAbsOrigin() ).Length();
	vec2DPrDir	= ( vecPrPos - GetAbsOrigin() ).AsVector2D();

	Vector vecBodyDir = BodyDirection2D();

	Vector2D vec2DBodyDir = vecBodyDir.AsVector2D();
	
	flPrDot	= DotProduct2D ( vec2DPrDir, vec2DBodyDir );

	if ( ( flPrDist > ANTLION_MELEE2_RANGE_MAX ) || ( flPrDist < ANTLION_MELEE2_RANGE_MIN ) )
	{
		m_flPounceTime = gpGlobals->curtime + 0.2f;
		return COND_TOO_FAR_TO_ATTACK;
	}

	trace_t	tr;
	AI_TraceHull( WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), -Vector(8,8,8), Vector(8,8,8), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );

	if ( tr.fraction < 1.0f )
		return 0;

	if ( IsMoving() )
		 m_bLeapAttack = true;
	else
		 m_bLeapAttack = false;

	return COND_CAN_MELEE_ATTACK2;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : interactionType - 
//			*data - 
//			*sender - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sender )
{
	//Check for a target found while burrowed
	if ( interactionType == g_interactionAntlionFoundTarget )
	{
		CBaseEntity	*pOther = (CBaseEntity *) data;
		
		//Randomly delay
		m_flBurrowTime = gpGlobals->curtime + random->RandomFloat( 0.5f, 1.0f );
		BurrowUse( pOther, pOther, USE_ON, 0.0f );

		return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::Alone( void )
{
	if ( m_pSquad == NULL )
		return true;

	if ( m_pSquad->NumMembers() <= 1 )
		return true;

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::StartJump( void )
{
	if ( m_bForcedStuckJump == false )
	{
		//Must be jumping at an enemy
		if ( GetEnemy() == NULL )
			return;

		//Don't jump if we're not on the ground
		if ( ( GetFlags() & FL_ONGROUND ) == false )
			return;
	}

	//Take us off the ground
	SetGroundEntity( NULL );
	SetAbsVelocity( m_vecSavedJump );

	m_bForcedStuckJump = false;

	//Setup our jump time so that we don't try it again too soon
	m_flJumpTime = gpGlobals->curtime + random->RandomInt( 2, 6 );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : sHint - 
//			nNodeNum - 
// Output : bool CAI_BaseNPC::FValidateHintType
//-----------------------------------------------------------------------------
bool CNPC_Antlion::FValidateHintType( CAI_Hint *pHint )
{
	switch ( m_iContext )
	{
	case ANTLION_BURROW_OUT:
		{			
			//See if this is a valid point
			Vector vHintPos;
			pHint->GetPosition(this,&vHintPos);

			if ( ValidBurrowPoint( vHintPos ) == false )
				return false;
		}
		break;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &origin - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::ClearBurrowPoint( const Vector &origin )
{
	CBaseEntity *pEntity = NULL;
	float		flDist;
	Vector		vecSpot, vecCenter, vecForce;

	//Cause a ruckus
	UTIL_ScreenShake( origin, 1.0f, 80.0f, 1.0f, 256.0f, SHAKE_START );

	//Iterate on all entities in the vicinity.
	for ( CEntitySphereQuery sphere( origin, 128 ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
	{
		if ( pEntity->m_takedamage != DAMAGE_NO && pEntity->Classify() != CLASS_PLAYER && pEntity->VPhysicsGetObject() )
		{
			vecSpot	 = pEntity->BodyTarget( origin );
			vecForce = ( vecSpot - origin ) + Vector( 0, 0, 16 );

			// decrease damage for an ent that's farther from the bomb.
			flDist = VectorNormalize( vecForce );

			//float mass = pEntity->VPhysicsGetObject()->GetMass();
			CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1.0f, 1.0f, 1.0f ), &vecCenter );

			if ( flDist <= 128.0f )
			{
				pEntity->VPhysicsGetObject()->Wake();
				pEntity->VPhysicsGetObject()->ApplyForceOffset( vecForce * 250.0f, vecCenter );
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: Determine whether a point is valid or not for burrowing up into
// Input  : &point - point to test for validity
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::ValidBurrowPoint( const Vector &point )
{
	trace_t	tr;

	AI_TraceHull( point, point+Vector(0,0,1), GetHullMins(), GetHullMaxs(), 
		MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );

	//See if we were able to get there
	if ( ( tr.startsolid ) || ( tr.allsolid ) || ( tr.fraction < 1.0f ) )
	{
		CBaseEntity *pEntity = tr.m_pEnt;

		//If it's a physics object, attempt to knock is away, unless it's a car
		if ( ( pEntity ) && ( pEntity->VPhysicsGetObject() ) && ( pEntity->GetServerVehicle() == NULL ) )
		{
			ClearBurrowPoint( point );
		}

		return false;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Finds a burrow point for the antlion
// Input  : distance - radius to search for burrow spot in
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::FindBurrow( const Vector &origin, float distance, int type, bool excludeNear )
{
	//Burrowing in?
	if ( type == ANTLION_BURROW_IN )
	{
		//Attempt to find a burrowing point
		CHintCriteria	hintCriteria;

		hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );
		hintCriteria.SetFlag( bits_HINT_NODE_NEAREST );

		hintCriteria.AddIncludePosition( origin, distance );
		
		if ( excludeNear )
		{
			hintCriteria.AddExcludePosition( origin, 128 );
		}

		CAI_Hint *pHint = CAI_HintManager::FindHint( this, hintCriteria );

		if ( pHint == NULL )
			return false;

		//Free up the node for use
		if ( GetHintNode() )
		{
			GetHintNode()->Unlock(0);
		}

		SetHintNode( pHint );

		//Lock the node
		pHint->Lock(this);

		//Setup our path and attempt to run there
		Vector vHintPos;
		GetHintNode()->GetPosition( this, &vHintPos );

		AI_NavGoal_t goal( vHintPos, ACT_RUN );

		return GetNavigator()->SetGoal( goal );
	}

	//Burrow out
	m_iContext = ANTLION_BURROW_OUT;

	CHintCriteria	hintCriteria;

	hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );
	hintCriteria.SetFlag( bits_HINT_NODE_NEAREST );

	if ( GetEnemy() != NULL )
	{
		hintCriteria.AddIncludePosition( GetEnemy()->GetAbsOrigin(), distance );
	}

	//Attempt to find an open burrow point
	CAI_Hint *pHint = CAI_HintManager::FindHint( this, hintCriteria );

	m_iContext = -1;

	if ( pHint == NULL )
		return false;

	//Free up the node for use
	if (GetHintNode())
	{
		GetHintNode()->Unlock(0);
	}

	SetHintNode( pHint );
	pHint->Lock(this);

	Vector burrowPoint;
	pHint->GetPosition(this,&burrowPoint);

	UTIL_SetOrigin( this, burrowPoint );

	//Burrowing out
	return true;
}

//-----------------------------------------------------------------------------
// Purpose:	Cause the antlion to unborrow
// Input  : *pActivator - 
//			*pCaller - 
//			useType - 
//			value - 
//-----------------------------------------------------------------------------

void CNPC_Antlion::BurrowUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
	//Don't allow us to do this again
	SetUse( NULL );
	
	//Allow idle sounds again
	m_spawnflags &= ~SF_NPC_GAG;

	//If the player activated this, then take them as an enemy
	if ( ( pCaller != NULL ) && ( pCaller->IsPlayer() ) )
	{
		SetEnemy( pActivator );
	}

	//Start trying to surface
	SetSchedule( SCHED_ANTLION_WAIT_UNBORROW );
}

//-----------------------------------------------------------------------------
// Purpose: Monitor the antlion's jump to play the proper landing sequence
//-----------------------------------------------------------------------------
bool CNPC_Antlion::CheckLanding( void )
{
	trace_t	tr;
	Vector	testPos;

	//Amount of time to predict forward
	const float	timeStep = 0.1f;

	//Roughly looks one second into the future
	testPos = GetAbsOrigin() + ( GetAbsVelocity() * timeStep );
	testPos[2] -= ( 0.5 * sv_gravity.GetFloat() * GetGravity() * timeStep * timeStep);

	if ( g_debug_antlion.GetInt() == 2 )
	{
		NDebugOverlay::Line( GetAbsOrigin(), testPos, 255, 0, 0, 0, 0.5f );
		NDebugOverlay::Cross3D( m_vecSavedJump, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, true, 0.5f );
	} 
	
	// Look below
	AI_TraceHull( GetAbsOrigin(), testPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );

	//See if we're about to contact, or have already contacted the ground
	if ( ( tr.fraction != 1.0f ) || ( GetFlags() & FL_ONGROUND ) )
	{
		int	sequence = SelectWeightedSequence( (Activity)ACT_ANTLION_LAND );

		if ( GetSequence() != sequence )
		{
			SetWings( false );
			VacateStrategySlot();
			SetIdealActivity( (Activity) ACT_ANTLION_LAND );

			CreateDust( false );
			EmitSound( "NPC_Antlion.Land" );

			if ( GetEnemy() && GetEnemy()->IsPlayer()  )
			{
				CBasePlayer *pPlayer = ToBasePlayer( GetEnemy() );

				if ( pPlayer && pPlayer->IsInAVehicle() == false )
					 MeleeAttack( ANTLION_MELEE1_RANGE, sk_antlion_swipe_damage.GetFloat(), QAngle( 4.0f, 0.0f, 0.0f ), Vector( -250.0f, 1.0f, 1.0f ) );
			}

			SetAbsVelocity( GetAbsVelocity() * 0.33f );
			return false;
		}

		return IsActivityFinished();
	}

	return false;
}



//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pEntity - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::QuerySeeEntity( CBaseEntity *pEntity )
{
	//If we're under the ground, don't look at enemies
	if ( IsEffectActive( EF_NODRAW ) )
		return false;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Turns the antlion's wings on or off
// Input  : state - on or off
//-----------------------------------------------------------------------------
void CNPC_Antlion::SetWings( bool state )
{
	if ( m_bWingsOpen == state )
		return;

	m_bWingsOpen = state;

	if ( m_bWingsOpen )
	{
		CPASAttenuationFilter filter( this, "NPC_Antlion.WingsOpen" );
		filter.MakeReliable();

		EmitSound( filter, entindex(), "NPC_Antlion.WingsOpen" );
		SetBodygroup( 1, 1 );
		m_bLoopingStarted = true;
	}
	else
	{
		StopSound( "NPC_Antlion.WingsOpen" );
		SetBodygroup( 1, 0 );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::Burrow( void )
{
	SetWings( false );

	//Stop us from taking damage and being solid
	m_spawnflags |= SF_NPC_GAG;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::Unburrow( void )
{
	m_bStartBurrowed = false;
	SetWings( false );

	//Become solid again and visible
	m_spawnflags &= ~SF_NPC_GAG;
	RemoveSolidFlags( FSOLID_NOT_SOLID );
	m_takedamage	= DAMAGE_YES;

	SetGroundEntity( NULL );

	//If we have an enemy, come out facing them
	if ( GetEnemy() )
	{
		Vector	dir = GetEnemy()->GetAbsOrigin() - GetAbsOrigin();
		VectorNormalize(dir);

		QAngle angles = GetAbsAngles();
		angles[ YAW ] = UTIL_VecToYaw( dir );
		SetLocalAngles( angles );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::InputUnburrow( inputdata_t &inputdata )
{
	if ( IsAlive() == false )
		return;

	SetSchedule( SCHED_ANTLION_WAIT_UNBORROW );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::InputBurrow( inputdata_t &inputdata )
{
	if ( IsAlive() == false )
		return;

	SetSchedule( SCHED_ANTLION_BURROW_IN );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::InputBurrowAway( inputdata_t &inputdata )
{
	if ( IsAlive() == false )
		return;

	SetSchedule( SCHED_ANTLION_BURROW_AWAY );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::CreateDust( bool placeDecal )
{
	trace_t	tr;
	AI_TraceLine( GetAbsOrigin()+Vector(0,0,1), GetAbsOrigin()-Vector(0,0,64), MASK_SOLID_BRUSHONLY | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP, this, COLLISION_GROUP_NONE, &tr );

	if ( tr.fraction < 1.0f )
	{
		surfacedata_t *pdata = physprops->GetSurfaceData( tr.surface.surfaceProps );

		if ( ( pdata->game.material == CHAR_TEX_CONCRETE ) || 
			 ( pdata->game.material == CHAR_TEX_DIRT ) ||
			 ( pdata->game.material == CHAR_TEX_SAND ) )
		{
			UTIL_CreateAntlionDust( tr.endpos + Vector(0,0,24), GetAbsAngles() );
			
			if ( placeDecal )
			{
				UTIL_DecalTrace( &tr, "Antlion.Unburrow" );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pSound - 
//-----------------------------------------------------------------------------
bool CNPC_Antlion::QueryHearSound( CSound *pSound )
{
	if ( !BaseClass::QueryHearSound( pSound ) )
		return false;
		
	if ( pSound->m_iType == SOUND_BUGBAIT )
	{
		//Must be more recent than the current
		if ( pSound->SoundExpirationTime() <= m_flIgnoreSoundTime )
			return false;

		//If we can hear it, store it
		m_bHasHeardSound = (pSound != NULL);
		if ( m_bHasHeardSound )
		{
			m_vecHeardSound = pSound->GetSoundOrigin();
			m_flIgnoreSoundTime	= pSound->SoundExpirationTime();
		}
	}

	//Do the normal behavior at this point
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Allows for modification of the interrupt mask for the current schedule.
//			In the most cases the base implementation should be called first.
//-----------------------------------------------------------------------------
void CNPC_Antlion::BuildScheduleTestBits( void )
{
	//Don't allow any modifications when scripted
	if ( m_NPCState == NPC_STATE_SCRIPT )
		return;

	// If we're allied with the player, don't be startled by him
	if ( IsAllied() )
	{
		ClearCustomInterruptCondition( COND_HEAR_PLAYER );
		SetCustomInterruptCondition( COND_PLAYER_PUSHING );
	}

	//Make sure we interrupt a run schedule if we can jump
	if ( IsCurSchedule(SCHED_CHASE_ENEMY) )
	{
		SetCustomInterruptCondition( COND_ANTLION_CAN_JUMP );
		SetCustomInterruptCondition( COND_ENEMY_UNREACHABLE );
	}

	if ( !IsCurSchedule( SCHED_ANTLION_DROWN ) )
	{
		// Interrupt any schedule unless already drowning.
		SetCustomInterruptCondition( COND_ANTLION_IN_WATER );
	}
	else
	{
		// Don't stop drowning just because you're in water!
		ClearCustomInterruptCondition( COND_ANTLION_IN_WATER );
	}

	// Make sure we don't stop in midair
	/*
	if ( GetActivity() == ACT_JUMP || GetActivity() == ACT_GLIDE || GetActivity() == ACT_LAND )
	{
		ClearCustomInterruptCondition( COND_NEW_ENEMY );
	}
	*/
	
	//Interrupt any schedule unless already fleeing, burrowing, burrowed, or unburrowing.
	if( !IsCurSchedule(SCHED_ANTLION_FLEE_THUMPER)			&& 		
		!IsCurSchedule(SCHED_ANTLION_FLEE_PHYSICS_DANGER)	&& 		
		!IsCurSchedule(SCHED_ANTLION_BURROW_IN)				&& 		
		!IsCurSchedule(SCHED_ANTLION_WAIT_UNBORROW)			&& 		
		!IsCurSchedule(SCHED_ANTLION_BURROW_OUT)			&&
		!IsCurSchedule(SCHED_ANTLION_BURROW_WAIT)			&&
		!IsCurSchedule(SCHED_ANTLION_WAIT_FOR_UNBORROW_TRIGGER)&&
		!IsCurSchedule(SCHED_ANTLION_WAIT_FOR_CLEAR_UNBORROW)&&
		!IsCurSchedule(SCHED_ANTLION_WAIT_UNBORROW)			&&
		!IsCurSchedule(SCHED_ANTLION_JUMP)					&&
		!IsCurSchedule(SCHED_ANTLION_FLIP)					&&
		!IsCurSchedule(SCHED_ANTLION_DISMOUNT_NPC)			&& 
		( GetFlags() & FL_ONGROUND ) )
	{
		// Only do these if not jumping as well
		if (!IsCurSchedule(SCHED_ANTLION_JUMP))
		{
			if ( GetEnemy() == NULL )
			{
				SetCustomInterruptCondition( COND_HEAR_PHYSICS_DANGER );
			}
			
			SetCustomInterruptCondition( COND_HEAR_THUMPER );
			SetCustomInterruptCondition( COND_HEAR_BUGBAIT );
			SetCustomInterruptCondition( COND_ANTLION_FLIPPED );

			if ( GetNavType() != NAV_JUMP )
				 SetCustomInterruptCondition( COND_ANTLION_RECEIVED_ORDERS );
		}

		SetCustomInterruptCondition( COND_ANTLION_ON_NPC );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pEnemy - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::IsValidEnemy( CBaseEntity *pEnemy )
{
	//See if antlions are friendly to the player in this map
	if ( IsAllied() && pEnemy->IsPlayer() )
		return false;

	//If we're chasing bugbait, close to within a certain radius before picking up enemies
	if ( IsCurSchedule( GetGlobalScheduleId( SCHED_ANTLION_CHASE_BUGBAIT ) ) && ( GetNavigator() != NULL ) )
	{
		//If the enemy is without the target radius, then don't allow them
		if ( ( GetNavigator()->IsGoalActive() ) && ( GetNavigator()->GetGoalPos() - pEnemy->GetAbsOrigin() ).Length() > bugbait_radius.GetFloat() )
			return false;
	}

	// If we're following an entity we limit our attack distances
	if ( m_FollowBehavior.GetFollowTarget() != NULL )
	{
		float enemyDist = ( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr();

		if ( m_flObeyFollowTime > gpGlobals->curtime )
		{
			// Unless we're right next to the enemy, follow our target
			if ( enemyDist > (128*128) )
				return false;
		}
		else
		{
			// Otherwise don't follow if the target is far 
			if ( enemyDist > (2000*2000) )
				return false;
		}
	}

	return BaseClass::IsValidEnemy( pEnemy );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::GatherConditions( void )
{
	BaseClass::GatherConditions();

	// See if I've landed on an NPC!
	CBaseEntity *pGroundEnt = GetGroundEntity();
	
	if ( ( ( pGroundEnt != NULL ) && ( pGroundEnt->GetSolidFlags() & FSOLID_NOT_STANDABLE ) ) && ( GetFlags() & FL_ONGROUND ) )
	{
		SetCondition( COND_ANTLION_ON_NPC );
	}
	else
	{
		ClearCondition( COND_ANTLION_ON_NPC );
	}

	// See if our follow target is too far off
/*	if ( m_hFollowTarget != NULL )
	{
		float targetDist = UTIL_DistApprox( WorldSpaceCenter(), m_hFollowTarget->GetAbsOrigin() );

		if ( targetDist > 400 )
		{
			SetCondition( COND_ANTLION_FOLLOW_TARGET_TOO_FAR );
		}
		else
		{
			ClearCondition( COND_ANTLION_FOLLOW_TARGET_TOO_FAR );
		}
	}*/

	if ( IsCurSchedule( SCHED_ANTLION_BURROW_WAIT ) == false && 
		 IsCurSchedule(SCHED_ANTLION_BURROW_IN) == false && 
		 IsCurSchedule(SCHED_ANTLION_BURROW_OUT) == false && 
		 IsCurSchedule(SCHED_FALL_TO_GROUND ) == false &&
		 IsEffectActive( EF_NODRAW ) == false )
	{
		if( m_lifeState == LIFE_ALIVE && GetWaterLevel() > 1 )
		{
			// Start Drowning!
			SetCondition( COND_ANTLION_IN_WATER );
		}
	}

	//Ignore the player pushing me if I'm flipped over!
	if ( IsCurSchedule( SCHED_ANTLION_FLIP ) )
		 ClearCondition( COND_PLAYER_PUSHING );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::PrescheduleThink( void )
{
	UpdateHead();

	Activity eActivity = GetActivity();

	//See if we need to play their agitated sound
	if ( ( eActivity == ACT_ANTLION_RUN_AGITATED ) && ( m_bAgitatedSound == false ) )
	{
		//Start sound
		CPASAttenuationFilter filter( this, "NPC_Antlion.LoopingAgitated" );
		filter.MakeReliable();

		EmitSound( filter, entindex(), "NPC_Antlion.LoopingAgitated" );
		m_bAgitatedSound = true;
	}
	else if ( ( eActivity != ACT_ANTLION_RUN_AGITATED ) && ( m_bAgitatedSound == true ) )
	{
		//Stop sound
		StopSound( "NPC_Antlion.LoopingAgitated" );
		m_bAgitatedSound = false;
	}

	//See if our wings got interrupted from being turned off
	if (    ( m_bWingsOpen ) &&
			( eActivity != ACT_ANTLION_JUMP_START ) && 
			( eActivity != ACT_JUMP ) && 
			( eActivity != ACT_GLIDE ) && 
			( eActivity != ACT_ANTLION_LAND ) && 
			( eActivity != ACT_ANTLION_DISTRACT ))
	{
		SetWings( false );
	}

	// Make sure we've turned off our burrow state if we're not in it
	if ( IsEffectActive( EF_NODRAW ) &&
		 ( eActivity != ACT_ANTLION_BURROW_IDLE ) &&
		 ( eActivity != ACT_ANTLION_BURROW_OUT ) &&
		 ( eActivity != ACT_ANTLION_BURROW_IN) )
	{
		DevMsg( "Antlion failed to unburrow properly!\n" );
		Assert( 0 );
		RemoveEffects( EF_NODRAW );
		RemoveSolidFlags( FSOLID_NOT_SOLID );
		m_takedamage	= DAMAGE_YES;
		RemoveFlag( FL_NOTARGET );
		m_spawnflags &= ~SF_NPC_GAG;
	}

	//New Enemy? Try to jump at him.
	if ( HasCondition( COND_NEW_ENEMY ) )
	{
		m_flJumpTime = 0.0f;
	}

	if ( ShouldJump() )
	{
		SetCondition( COND_ANTLION_CAN_JUMP );
	}
	else
	{
		ClearCondition( COND_ANTLION_CAN_JUMP );
	}

	BaseClass::PrescheduleThink();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flDamage - 
//			bitsDamageType - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::IsLightDamage( const CTakeDamageInfo &info )
{
	if ( ( random->RandomInt( 0, 1 ) ) && ( info.GetDamage() > 3 ) )
		return true;

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::IsAllied( void )
{
	return ( GlobalEntity_GetState( "antlion_allied" ) == GLOBAL_ON );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::ShouldResumeFollow( void )
{
	if ( IsAllied() == false )
		return false;

	if ( m_MoveState == ANTLION_MOVE_FOLLOW || m_hFollowTarget == NULL )
		return false;

	if ( m_flSuppressFollowTime > gpGlobals->curtime )
		return false;

	if ( GetEnemy() != NULL )
	{
		m_flSuppressFollowTime = gpGlobals->curtime + random->RandomInt( 5, 10 );
		return false;
	}

	//TODO: See if the follow target has wandered off too far from where we last followed them to
	
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::ShouldAbandonFollow( void )
{
	// Never give up if we can see the goal
	if ( m_FollowBehavior.FollowTargetVisible() )
		return false;

	// Never give up if we're too close
	float flDistance = UTIL_DistApprox2D( m_FollowBehavior.GetFollowTarget()->WorldSpaceCenter(), WorldSpaceCenter() );

	if ( flDistance < 1500 )
		return false;

	if ( flDistance > 1500 * 2.0f )
		return true;

	// If we've failed too many times, give up
	if ( m_FollowBehavior.GetNumFailedFollowAttempts() )
		return true;

	// If the target simply isn't reachable to us, give up
	if ( m_FollowBehavior.TargetIsUnreachable() )
		return true;

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pTarget - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::SetFightTarget( CBaseEntity *pTarget )
{
	m_hFightGoalTarget = pTarget;

	SetCondition( COND_ANTLION_RECEIVED_ORDERS );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::InputFightToPosition( inputdata_t &inputdata )
{
	if ( IsAlive() == false )
		return;

	CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), this );

	if ( pEntity != NULL )
	{
		SetFightTarget( pEntity );
		SetFollowTarget( NULL );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::InputStopFightToPosition( inputdata_t &inputdata )
{
	SetFightTarget( NULL );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pEnemy - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::GatherEnemyConditions( CBaseEntity *pEnemy )
{
	// Do the base class
	BaseClass::GatherEnemyConditions( pEnemy );

	// Only continue if we burrow when eluded
	if ( ( m_spawnflags & SF_ANTLION_BURROW_ON_ELUDED ) == false )
		return;

	// If we're not already too far away, check again
	//TODO: Check to make sure we don't already have a condition set that removes the need for this
	if ( HasCondition( COND_ENEMY_UNREACHABLE ) == false )
	{
		Vector	predPosition;
		UTIL_PredictedPosition( GetEnemy(), 1.0f, &predPosition );

		Vector	predDir = ( predPosition - GetAbsOrigin() );
		float	predLength = VectorNormalize( predDir );

		// See if we'll be outside our effective target range
		if ( predLength > m_flEludeDistance )
		{
			Vector	predVelDir = ( predPosition - GetEnemy()->GetAbsOrigin() );
			float	predSpeed  = VectorNormalize( predVelDir );

			// See if the enemy is moving mostly away from us
			if ( ( predSpeed > 512.0f ) && ( DotProduct( predVelDir, predDir ) > 0.0f ) )
			{
				// Mark the enemy as eluded and burrow away
				ClearEnemyMemory();
				SetEnemy( NULL );
				SetIdealState( NPC_STATE_ALERT );
				SetCondition( COND_ENEMY_UNREACHABLE );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &info - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::ShouldGib( const CTakeDamageInfo &info )
{
	if ( info.GetDamageType() & (DMG_NEVERGIB|DMG_DISSOLVE) )
		return false;

	if ( info.GetDamageType() & (DMG_ALWAYSGIB|DMG_BLAST) )
		return true;

	if ( m_iHealth < -20 )
		return true;

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::CorpseGib( const CTakeDamageInfo &info )
{
	CEffectData	data;
	
	data.m_vOrigin = WorldSpaceCenter();
	data.m_vNormal = data.m_vOrigin - info.GetDamagePosition();
	VectorNormalize( data.m_vNormal );
	
	data.m_flScale = RemapVal( m_iHealth, 0, -500, 1, 3 );
	data.m_flScale = clamp( data.m_flScale, 1, 3 );

	DispatchEffect( "AntlionGib", data );

	//CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, GetAbsOrigin(), 256, 0.5f, this );

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pOther - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::Touch( CBaseEntity *pOther )
{
	//See if the touching entity is a vehicle
	CBasePlayer *pPlayer = ToBasePlayer( AI_GetSinglePlayer() );
	
	if ( pPlayer && pPlayer->IsInAVehicle() )
	{
		IServerVehicle	*pVehicle = pPlayer->GetVehicle();
		CBaseEntity *pVehicleEnt = pVehicle->GetVehicleEnt();

		if ( pVehicleEnt == pOther )
		{
			CPropVehicleDriveable	*pDrivableVehicle = dynamic_cast<CPropVehicleDriveable *>( pVehicleEnt );

			if ( pDrivableVehicle != NULL )
			{
				//Get tossed!
				Vector	vecShoveDir = pOther->GetAbsVelocity();
				Vector	vecTargetDir = GetAbsOrigin() - pOther->GetAbsOrigin();
				
				VectorNormalize( vecShoveDir );
				VectorNormalize( vecTargetDir );

				bool bBurrowingOut = IsCurSchedule( SCHED_ANTLION_BURROW_OUT );

				if ( ( ( pDrivableVehicle->m_nRPM > 75 ) && DotProduct( vecShoveDir, vecTargetDir ) <= 0 ) || bBurrowingOut == true )
				{
					if ( GetActivity() == ACT_ANTLION_FLIP || bBurrowingOut == true )
					{
						float flDamage = m_iHealth;

						if ( random->RandomInt( 0, 10 ) > 4 )
							 flDamage += 25;
									
						CTakeDamageInfo	dmgInfo( pVehicleEnt, pPlayer, flDamage, DMG_VEHICLE );
					
						CalculateMeleeDamageForce( &dmgInfo, vecShoveDir, pOther->GetAbsOrigin() );
						TakeDamage( dmgInfo );
					}
					else
					{
						// We're being shoved
						PainSound();

						SetCondition( COND_ANTLION_FLIPPED );

						vecTargetDir[2] = 0.0f;

						ApplyAbsVelocityImpulse( ( vecTargetDir * 250.0f ) + Vector(0,0,64.0f) );
						SetGroundEntity( NULL );

						CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, GetAbsOrigin(), 256, 0.5f, this );
					}
				}
			}
		}
	}

	BaseClass::Touch( pOther );

	// Did the player touch me?
	if ( pOther->IsPlayer() )
	{
		// Don't test for this if the pusher isn't friendly
		if ( IsValidEnemy( pOther ) )
			return;

		// Ignore if pissed at player
		if ( m_afMemory & bits_MEMORY_PROVOKED )
			return;
	
		if ( !IsCurSchedule( SCHED_MOVE_AWAY ) && !IsCurSchedule( SCHED_ANTLION_BURROW_OUT ) )
			 TestPlayerPushing( pOther );
	}

	//Adrian: Explode if hit by gunship!
	//Maybe only do this if hit by the propellers?
	if ( pOther->IsNPC() )
	{
		if ( pOther->Classify() == CLASS_COMBINE_GUNSHIP )
		{
			float flDamage = m_iHealth + 25;
						
			CTakeDamageInfo	dmgInfo( pOther, pOther, flDamage, DMG_GENERIC );
			GuessDamageForce( &dmgInfo, (pOther->GetAbsOrigin() - GetAbsOrigin()), pOther->GetAbsOrigin() );
			TakeDamage( dmgInfo );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: turn in the direction of movement
// Output :
//-----------------------------------------------------------------------------
bool CNPC_Antlion::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval )
{
  	// FIXME: this will break scripted sequences that walk when they have an enemy
  	if ( GetEnemy() && GetNavigator()->GetMovementActivity() == ACT_RUN )
  	{
		Vector vecEnemyLKP = GetEnemyLKP();
		
		// Only start facing when we're close enough
		if ( UTIL_DistApprox( vecEnemyLKP, GetAbsOrigin() ) < 512 )
		{
			AddFacingTarget( GetEnemy(), vecEnemyLKP, 1.0, 0.2 );
		}
	}

	//Adrian: Make antlions face the thumper while they flee away.
	if ( IsCurSchedule( SCHED_ANTLION_FLEE_THUMPER ) )
	{
		CSound *pSound = GetLoudestSoundOfType( SOUND_THUMPER );

		if ( pSound )
		{
			AddFacingTarget( pSound->GetSoundOrigin(), 1.0, 0.5f );
		}
	}

	return BaseClass::OverrideMoveFacing( move, flInterval );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::InputDisableJump( inputdata_t &inputdata )
{
	m_bDisableJump = true;
	CapabilitiesRemove( bits_CAP_MOVE_JUMP );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Antlion::InputEnableJump( inputdata_t &inputdata )
{
	m_bDisableJump = false;
	CapabilitiesAdd( bits_CAP_MOVE_JUMP );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pTarget - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::SetFollowTarget( CBaseEntity *pTarget )
{
	m_FollowBehavior.SetFollowTarget( pTarget );
	m_hFollowTarget = pTarget;
	m_flObeyFollowTime = gpGlobals->curtime + ANTLION_OBEY_FOLLOW_TIME;

	SetCondition( COND_ANTLION_RECEIVED_ORDERS );

	// Play an acknowledgement noise
	if ( m_flNextAcknowledgeTime < gpGlobals->curtime )
	{
		EmitSound( "NPC_Antlion.Distracted" );
		m_flNextAcknowledgeTime = gpGlobals->curtime + 1.0f;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Antlion::CreateBehaviors( void )
{
	AddBehavior( &m_FollowBehavior );

	return BaseClass::CreateBehaviors();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::InputIgnoreBugbait( inputdata_t &inputdata )
{
	m_bIgnoreBugbait = true;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::InputHearBugbait( inputdata_t &inputdata )
{
	m_bIgnoreBugbait = false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : state - 
//-----------------------------------------------------------------------------
void CNPC_Antlion::SetMoveState( AntlionMoveState_e state )
{
	m_MoveState = state;

	switch( m_MoveState )
	{
	case ANTLION_MOVE_FOLLOW:

		m_FollowBehavior.SetFollowTarget( m_hFollowTarget );
		
		// Clear any previous state
		m_flSuppressFollowTime = 0;
		
		break;
	
	case ANTLION_MOVE_FIGHT_TO_GOAL:
		
		m_FollowBehavior.SetFollowTarget( NULL );

		// Keep the time we started this
		m_flSuppressFollowTime = gpGlobals->curtime + random->RandomInt( 10, 15 );
		break;

	default:
		break;
	}
}

// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CAntlionRepellant )
	DEFINE_KEYFIELD( m_flRepelRadius,	FIELD_FLOAT,	"repelradius" ),
	DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"Enable", InputEnable ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"Disable", InputDisable ),
END_DATADESC()

static CUtlVector< CHandle< CAntlionRepellant > >m_hRepellantList;


CAntlionRepellant::~CAntlionRepellant()
{
	m_hRepellantList.FindAndRemove( this );
}

void CAntlionRepellant::Spawn( void )
{
	BaseClass::Spawn();
	m_bEnabled = true;

	m_hRepellantList.AddToTail( this );
}

void CAntlionRepellant::InputEnable( inputdata_t &inputdata )
{
	m_bEnabled = true;

	if ( m_hRepellantList.HasElement( this ) == false )
		 m_hRepellantList.AddToTail( this );
}

void CAntlionRepellant::InputDisable( inputdata_t &inputdata )
{
	m_bEnabled = false;
	m_hRepellantList.FindAndRemove( this );
}

float CAntlionRepellant::GetRadius( void )
{
	if ( m_bEnabled == false )
		 return 0.0f;

	return m_flRepelRadius;
}

void CAntlionRepellant::OnRestore( void )
{
	BaseClass::OnRestore();

	if ( m_bEnabled == true )
	{
		if ( m_hRepellantList.HasElement( this ) == false )
			 m_hRepellantList.AddToTail( this );
	}
}

bool CAntlionRepellant::IsPositionRepellantFree( Vector vDesiredPos )
{
	for ( int i = 0; i < m_hRepellantList.Count(); i++ )
	{
		if ( m_hRepellantList[i] )
		{
			CAntlionRepellant *pRep = m_hRepellantList[i].Get();

			if ( pRep )
			{
				float flDist = (vDesiredPos - pRep->GetAbsOrigin()).Length();

				if ( flDist <= pRep->GetRadius() )
					 return false;
			}
		}
	}

	return true;
}

LINK_ENTITY_TO_CLASS( point_antlion_repellant, CAntlionRepellant);

//-----------------------------------------------------------------------------
//
// Schedules
//
//-----------------------------------------------------------------------------

AI_BEGIN_CUSTOM_NPC( npc_antlion, CNPC_Antlion )

	//Register our interactions
	DECLARE_INTERACTION( g_interactionAntlionFoundTarget )

	//Conditions
	DECLARE_CONDITION( COND_ANTLION_FLIPPED )
	DECLARE_CONDITION( COND_ANTLION_ON_NPC )
	DECLARE_CONDITION( COND_ANTLION_CAN_JUMP )
	DECLARE_CONDITION( COND_ANTLION_FOLLOW_TARGET_TOO_FAR )
	DECLARE_CONDITION( COND_ANTLION_RECEIVED_ORDERS )
	DECLARE_CONDITION( COND_ANTLION_IN_WATER )
		
	//Squad slots
	DECLARE_SQUADSLOT( SQUAD_SLOT_ANTLION_JUMP )

	//Tasks
	DECLARE_TASK( TASK_ANTLION_SET_CHARGE_GOAL )
	DECLARE_TASK( TASK_ANTLION_BURROW )
	DECLARE_TASK( TASK_ANTLION_UNBURROW )
	DECLARE_TASK( TASK_ANTLION_VANISH )
	DECLARE_TASK( TASK_ANTLION_FIND_BURROW_IN_POINT )
	DECLARE_TASK( TASK_ANTLION_FIND_BURROW_OUT_POINT )
	DECLARE_TASK( TASK_ANTLION_BURROW_WAIT )
	DECLARE_TASK( TASK_ANTLION_CHECK_FOR_UNBORROW )
	DECLARE_TASK( TASK_ANTLION_JUMP )
	DECLARE_TASK( TASK_ANTLION_WAIT_FOR_TRIGGER )
	DECLARE_TASK( TASK_ANTLION_GET_THUMPER_ESCAPE_PATH )
	DECLARE_TASK( TASK_ANTLION_GET_PATH_TO_BUGBAIT )
	DECLARE_TASK( TASK_ANTLION_FACE_BUGBAIT )
	DECLARE_TASK( TASK_ANTLION_DISMOUNT_NPC )
	DECLARE_TASK( TASK_ANTLION_REACH_FIGHT_GOAL )
	DECLARE_TASK( TASK_ANTLION_GET_PHYSICS_DANGER_ESCAPE_PATH )
	DECLARE_TASK( TASK_ANTLION_FACE_JUMP )
	DECLARE_TASK( TASK_ANTLION_DROWN )
	

	//Activities
	DECLARE_ACTIVITY( ACT_ANTLION_DISTRACT )
	DECLARE_ACTIVITY( ACT_ANTLION_DISTRACT_ARRIVED )
	DECLARE_ACTIVITY( ACT_ANTLION_JUMP_START )
	DECLARE_ACTIVITY( ACT_ANTLION_BURROW_IN )
	DECLARE_ACTIVITY( ACT_ANTLION_BURROW_OUT )
	DECLARE_ACTIVITY( ACT_ANTLION_BURROW_IDLE )
	DECLARE_ACTIVITY( ACT_ANTLION_RUN_AGITATED )
	DECLARE_ACTIVITY( ACT_ANTLION_FLIP )
	DECLARE_ACTIVITY( ACT_ANTLION_POUNCE )
	DECLARE_ACTIVITY( ACT_ANTLION_POUNCE_MOVING )
	DECLARE_ACTIVITY( ACT_ANTLION_DROWN )
	DECLARE_ACTIVITY( ACT_ANTLION_LAND )

	//Events
	DECLARE_ANIMEVENT( AE_ANTLION_WALK_FOOTSTEP )
	DECLARE_ANIMEVENT( AE_ANTLION_MELEE_HIT1 )
	DECLARE_ANIMEVENT( AE_ANTLION_MELEE_HIT2 )
	DECLARE_ANIMEVENT( AE_ANTLION_MELEE_POUNCE )
	DECLARE_ANIMEVENT( AE_ANTLION_FOOTSTEP_SOFT )
	DECLARE_ANIMEVENT( AE_ANTLION_FOOTSTEP_HEAVY )
	DECLARE_ANIMEVENT( AE_ANTLION_START_JUMP )
	DECLARE_ANIMEVENT( AE_ANTLION_BURROW_IN )
	DECLARE_ANIMEVENT( AE_ANTLION_BURROW_OUT )
	DECLARE_ANIMEVENT( AE_ANTLION_VANISH )
	DECLARE_ANIMEVENT( AE_ANTLION_OPEN_WINGS )
	DECLARE_ANIMEVENT( AE_ANTLION_CLOSE_WINGS )
	DECLARE_ANIMEVENT( AE_ANTLION_MELEE1_SOUND )
	DECLARE_ANIMEVENT( AE_ANTLION_MELEE2_SOUND )

	//Schedules

	//==================================================
	// Jump
	//==================================================

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_JUMP,

		"	Tasks"
		"		TASK_STOP_MOVING				0"
		"		TASK_ANTLION_FACE_JUMP			0"
		"		TASK_PLAY_SEQUENCE				ACTIVITY:ACT_ANTLION_JUMP_START"
		"		TASK_ANTLION_JUMP				0"
		""
		"	Interrupts"
		"		COND_TASK_FAILED"
	)

	//==================================================
	// Wait for unborrow (once burrow has been triggered)
	//==================================================

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_WAIT_UNBORROW,

		"	Tasks"
		"		TASK_ANTLION_BURROW_WAIT		0"
		"		TASK_SET_SCHEDULE				SCHEDULE:SCHED_ANTLION_WAIT_FOR_CLEAR_UNBORROW"
		""
		"	Interrupts"
		"		COND_TASK_FAILED"
	)

	//==================================================
	// Burrow Wait
	//==================================================

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_BURROW_WAIT,

		"	Tasks"
		"		TASK_SET_FAIL_SCHEDULE				SCHEDULE:SCHED_ANTLION_BURROW_WAIT"
		"		TASK_ANTLION_BURROW_WAIT			1"
		"		TASK_ANTLION_FIND_BURROW_OUT_POINT	1024"
		"		TASK_SET_SCHEDULE					SCHEDULE:SCHED_ANTLION_WAIT_FOR_CLEAR_UNBORROW"
		""
		"	Interrupts"
		"		COND_TASK_FAILED"
	)

	//==================================================
	// Burrow In
	//==================================================

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_BURROW_IN,

		"	Tasks"
		"		TASK_SET_FAIL_SCHEDULE				SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
		"		TASK_ANTLION_BURROW					0"
		"		TASK_PLAY_SEQUENCE					ACTIVITY:ACT_ANTLION_BURROW_IN"
		"		TASK_ANTLION_VANISH					0"
		"		TASK_SET_SCHEDULE					SCHEDULE:SCHED_ANTLION_BURROW_WAIT"
		""
		"	Interrupts"
		"		COND_TASK_FAILED"
	)

	//==================================================
	// Run to burrow in
	//==================================================

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_RUN_TO_BURROW_IN,

		"	Tasks"
		"		TASK_SET_FAIL_SCHEDULE				SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
		"		TASK_SET_TOLERANCE_DISTANCE			8"
		"		TASK_ANTLION_FIND_BURROW_IN_POINT	512"
		"		TASK_RUN_PATH						0"
		"		TASK_WAIT_FOR_MOVEMENT				0"
		"		TASK_SET_SCHEDULE					SCHEDULE:SCHED_ANTLION_BURROW_IN"
		""
		"	Interrupts"
		"		COND_TASK_FAILED"
		"		COND_GIVE_WAY"
		"		COND_CAN_MELEE_ATTACK1"
		"		COND_CAN_MELEE_ATTACK2"
	)

	//==================================================
	// Burrow Out
	//==================================================

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_BURROW_OUT,

		"	Tasks"
		"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_ANTLION_BURROW_WAIT"
		"		TASK_ANTLION_UNBURROW			0"
		"		TASK_PLAY_SEQUENCE				ACTIVITY:ACT_ANTLION_BURROW_OUT"
		""
		"	Interrupts"
		"		COND_TASK_FAILED"
	)

	//==================================================
	// Wait for unborrow (triggered)
	//==================================================

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_WAIT_FOR_UNBORROW_TRIGGER,

		"	Tasks"
		"		TASK_ANTLION_WAIT_FOR_TRIGGER	0"
		""
		"	Interrupts"
		"		COND_TASK_FAILED"
	)

	//==================================================
	// Wait for clear burrow spot (triggered)
	//==================================================

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_WAIT_FOR_CLEAR_UNBORROW,

		"	Tasks"
		"		TASK_SET_FAIL_SCHEDULE				SCHEDULE:SCHED_ANTLION_BURROW_WAIT"
		"		TASK_ANTLION_CHECK_FOR_UNBORROW		1"
		"		TASK_SET_SCHEDULE					SCHEDULE:SCHED_ANTLION_BURROW_OUT"
		""
		"	Interrupts"
		"		COND_TASK_FAILED"
	)

	//==================================================
	// Run from the sound of a thumper!
	//==================================================
	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_FLEE_THUMPER,
		
		"	Tasks"
		"		TASK_SET_FAIL_SCHEDULE					SCHEDULE:SCHED_IDLE_STAND"
		"		TASK_ANTLION_GET_THUMPER_ESCAPE_PATH	0"
		"		TASK_RUN_PATH							0"
		"		TASK_WAIT_FOR_MOVEMENT					0"
		"		TASK_STOP_MOVING						0"
		"		TASK_PLAY_SEQUENCE						ACTIVITY:ACT_ANTLION_DISTRACT_ARRIVED"
		""
		"	Interrupts"
		"		COND_TASK_FAILED"
		"		COND_ANTLION_FLIPPED"
	)

	//==================================================
	// SCHED_ANTLION_CHASE_BUGBAIT
	//==================================================
	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_CHASE_BUGBAIT,

		"	Tasks"
		"		TASK_STOP_MOVING					0"
		"		TASK_ANTLION_GET_PATH_TO_BUGBAIT	0"
		"		TASK_RUN_PATH						0"
		"		TASK_WAIT_FOR_MOVEMENT				0"
		"		TASK_STOP_MOVING					0"
		"		TASK_ANTLION_FACE_BUGBAIT			0"
		""
		"	Interrupts"
		"		COND_CAN_MELEE_ATTACK1"
		"		COND_SEE_ENEMY"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
	)

	//==================================================
	// SCHED_ANTLION_FLIP
	//==================================================
	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_FLIP,

		"	Tasks"
		"		TASK_STOP_MOVING	0"
		"		TASK_PLAY_SEQUENCE	ACTIVITY:ACT_ANTLION_FLIP"

		"	Interrupts"
		"		COND_TASK_FAILED"
	)
	
	//=========================================================
	// Headcrab has landed atop another NPC. Get down!
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_DISMOUNT_NPC,

		"	Tasks"
		"		TASK_STOP_MOVING			0"
		"		TASK_ANTLION_DISMOUNT_NPC	0"

		"	Interrupts"
	)

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_RUN_TO_FIGHT_GOAL,

		"	Tasks"
		"		TASK_SET_TOLERANCE_DISTANCE		128"
		"		TASK_GET_PATH_TO_SAVEPOSITION	0"
		"		TASK_RUN_PATH					0"
		"		TASK_WAIT_FOR_MOVEMENT			0"
		"		TASK_ANTLION_REACH_FIGHT_GOAL	0"

		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_HEAVY_DAMAGE"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
		"		COND_ANTLION_CAN_JUMP"
	)

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_RUN_TO_FOLLOW_GOAL,

		"	Tasks"
		"		TASK_SET_TOLERANCE_DISTANCE		128"
		"		TASK_GET_PATH_TO_SAVEPOSITION	0"
		"		TASK_RUN_PATH					0"
		"		TASK_WAIT_FOR_MOVEMENT			0"

		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_HEAVY_DAMAGE"
		"		COND_ANTLION_CAN_JUMP"
		"		COND_ANTLION_FOLLOW_TARGET_TOO_FAR"
	)

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_BUGBAIT_IDLE_STAND,

		"	Tasks"
		"		TASK_STOP_MOVING		0"
		"		TASK_FACE_PLAYER		0"
		"		TASK_SET_ACTIVITY		ACTIVITY:ACT_IDLE"
		"		TASK_WAIT				2"

		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_HEAVY_DAMAGE"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
		"		COND_HEAR_DANGER"
		"		COND_HEAR_COMBAT"
		"		COND_ANTLION_CAN_JUMP"
		"		COND_ANTLION_FOLLOW_TARGET_TOO_FAR"
		"		COND_GIVE_WAY"
	)

	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_BURROW_AWAY,

		"	Tasks"
		"		TASK_STOP_MOVING		0"
		"		TASK_ANTLION_BURROW		0"
		"		TASK_PLAY_SEQUENCE		ACTIVITY:ACT_ANTLION_BURROW_IN"
		"		TASK_ANTLION_VANISH		1"

		"	Interrupts"
	)

	//==================================================
	// Run from the sound of a physics crash
	//==================================================
	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_FLEE_PHYSICS_DANGER,
		
		"	Tasks"
		"		TASK_SET_FAIL_SCHEDULE						SCHEDULE:SCHED_CHASE_ENEMY"
		"		TASK_ANTLION_GET_PHYSICS_DANGER_ESCAPE_PATH	1024"
		"		TASK_RUN_PATH								0"
		"		TASK_WAIT_FOR_MOVEMENT						0"
		"		TASK_STOP_MOVING							0"
		""
		"	Interrupts"
		"		COND_TASK_FAILED"
	)

	// Pounce forward at our enemy
	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_POUNCE,

		"	Tasks"
		"		TASK_STOP_MOVING		0"
		"		TASK_FACE_ENEMY			0"
		"		TASK_ANNOUNCE_ATTACK	1"
		"		TASK_RESET_ACTIVITY		0"
		"		TASK_PLAY_SEQUENCE		ACTIVITY:ACT_ANTLION_POUNCE"

		"	Interrupts"
		"		COND_TASK_FAILED"
	)
	// Pounce forward at our enemy
	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_POUNCE_MOVING,

		"	Tasks"
		"		TASK_STOP_MOVING		0"
		"		TASK_FACE_ENEMY			0"
		"		TASK_ANNOUNCE_ATTACK	1"
		"		TASK_RESET_ACTIVITY		0"
		"		TASK_PLAY_SEQUENCE		ACTIVITY:ACT_ANTLION_POUNCE_MOVING"

		"	Interrupts"
		"		COND_TASK_FAILED"
	)

		//=========================================================
	// The irreversible process of drowning
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_ANTLION_DROWN,

		"	Tasks"
		"		TASK_SET_ACTIVITY			ACTIVITY:ACT_ANTLION_DROWN"
		"		TASK_ANTLION_DROWN			0"
		""
		"	Interrupts"
	)

AI_END_CUSTOM_NPC()


