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

#include "cbase.h"
#include "beam_shared.h"
#include "game.h"				// For skill levels
#include "globalstate.h"
#include "npc_talker.h"
#include "ai_motor.h"
#include "ai_schedule.h"
#include "scripted.h"
#include "basecombatweapon.h"
#include "soundent.h"
#include "npcevent.h"
#include "ai_hull.h"
#include "animation.h"
#include "ammodef.h"				// For DMG_CLUB
#include "Sprite.h"
#include "npc_vortigaunt.h"
#include "activitylist.h"
#include "player.h"
#include "items.h"
#include "basegrenade_shared.h"
#include "ai_interactions.h"
#include "IEffects.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "globals.h"
#include "effect_dispatch_data.h"
#include "te_effect_dispatch.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "physics_prop_ragdoll.h"
#include "RagdollBoogie.h"

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

#define	VORTIGAUNT_LIMP_HEALTH				20
#define	VORTIGAUNT_SENTENCE_VOLUME			(float)0.35 // volume of vortigaunt sentences
#define	VORTIGAUNT_VOL						0.35		// volume of vortigaunt sounds
#define	VORTIGAUNT_ATTN						ATTN_NORM	// attenutation of vortigaunt sentences
#define	VORTIGAUNT_HEAL_RECHARGE			15.0		// How long to rest between heals
#define	VORTIGAUNT_ZAP_GLOWGROW_TIME		0.5			// How long does glow last
#define	VORTIGAUNT_HEAL_GLOWGROW_TIME		1.4			// How long does glow last
#define	VORTIGAUNT_GLOWFADE_TIME			0.5			// How long does it fade
#define	VORTIGAUNT_STOMP_DIST				40
#define	VORTIGAUNT_BEAM_HURT				0
#define	VORTIGAUNT_BEAM_HEAL				1
#define	VORTIGAUNT_STOMP_TURN_OFFSET		20

#define	VORTIGAUNT_LEFT_CLAW				"leftclaw"
#define	VORTIGAUNT_RIGHT_CLAW				"rightclaw"

#define VORT_CURE "VORT_CURE"
#define VORT_CURESTOP "VORT_CURESTOP"
#define VORT_CURE_INTERRUPT "VORT_CURE_INTERRUPT"
#define VORT_ATTACK "VORT_ATTACK"
#define VORT_MAD "VORT_MAD"
#define VORT_SHOT "VORT_SHOT"
#define VORT_PAIN "VORT_PAIN"
#define VORT_DIE "VORT_DIE"
#define VORT_KILL "VORT_KILL"
#define VORT_LINE_FIRE "VORT_LINE_FIRE"
#define VORT_POK "VORT_POK"
#define VORT_EXTRACT_START "VORT_EXTRACT_START"
#define VORT_EXTRACT_FINISH "VORT_EXTRACT_FINISH"

// Target must be within this range to heal
#define	HEAL_RANGE			256

ConVar	sk_vortigaunt_health( "sk_vortigaunt_health","0");
ConVar	sk_vortigaunt_armor_charge( "sk_vortigaunt_armor_charge","30");
ConVar	sk_vortigaunt_dmg_claw( "sk_vortigaunt_dmg_claw","0");
ConVar	sk_vortigaunt_dmg_rake( "sk_vortigaunt_dmg_rake","0");
ConVar	sk_vortigaunt_dmg_zap( "sk_vortigaunt_dmg_zap","0");

//=========================================================
// Vortigaunt activities
//=========================================================
int	ACT_VORTIGAUNT_AIM;
int ACT_VORTIGAUNT_START_HEAL;
int ACT_VORTIGAUNT_HEAL_LOOP;
int ACT_VORTIGAUNT_END_HEAL;
int ACT_VORTIGAUNT_TO_ACTION;
int ACT_VORTIGAUNT_TO_IDLE;
int ACT_VORTIGAUNT_STOMP;
int ACT_VORTIGAUNT_DEFEND;
int ACT_VORTIGAUNT_TO_DEFEND;
int ACT_VORTIGAUNT_FROM_DEFEND;

//=========================================================
// Monster's Anim Events Go Here
//=========================================================
int AE_VORTIGAUNT_CLAW_LEFT;
int AE_VORTIGAUNT_CLAW_RIGHT;
int AE_VORTIGAUNT_ZAP_POWERUP;
int AE_VORTIGAUNT_ZAP_SHOOT;
int AE_VORTIGAUNT_ZAP_DONE;
int AE_VORTIGAUNT_HEAL_STARTGLOW;
int AE_VORTIGAUNT_HEAL_STARTBEAMS;
int AE_VORTIGAUNT_KICK;
int AE_VORTIGAUNT_STOMP;
int AE_VORTIGAUNT_HEAL_STARTSOUND;
int AE_VORTIGAUNT_SWING_SOUND;
int AE_VORTIGAUNT_SHOOT_SOUNDSTART;
int AE_VORTIGAUNT_DEFEND_BEAMS;


//-----------------------------------------------------------------------------
// Interactions
//-----------------------------------------------------------------------------
int	g_interactionVortigauntStomp		= 0;
int	g_interactionVortigauntStompFail	= 0;
int	g_interactionVortigauntStompHit		= 0;
int	g_interactionVortigauntKick			= 0;
int	g_interactionVortigauntClaw			= 0;

//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_Vortigaunt )

	DEFINE_FIELD( m_flNextNPCThink,		FIELD_TIME),
	DEFINE_ARRAY( m_pBeam,				FIELD_EHANDLE, VORTIGAUNT_MAX_BEAMS ),
	DEFINE_FIELD( m_iBeams,				FIELD_INTEGER),
	DEFINE_FIELD( m_nLightningSprite,		FIELD_INTEGER),
	DEFINE_FIELD( m_fGlowAge,				FIELD_FLOAT),
	DEFINE_FIELD( m_fGlowScale,			FIELD_FLOAT),
	DEFINE_FIELD( m_fGlowChangeTime,		FIELD_FLOAT),
	DEFINE_FIELD( m_bGlowTurningOn,		FIELD_BOOLEAN),
	DEFINE_FIELD( m_nCurGlowIndex,		FIELD_INTEGER),
	DEFINE_FIELD( m_pLeftHandGlow,		FIELD_EHANDLE),
	DEFINE_FIELD( m_pRightHandGlow,		FIELD_EHANDLE),
	DEFINE_FIELD( m_flNextHealTime,		FIELD_TIME),
	DEFINE_FIELD( m_nCurrentHealAmt,	FIELD_INTEGER),
	DEFINE_FIELD( m_nLastArmorAmt,		FIELD_INTEGER),
	DEFINE_FIELD( m_iSuitSound,			FIELD_INTEGER),
	DEFINE_FIELD( m_flSuitSoundTime,		FIELD_TIME),
	DEFINE_FIELD( m_painTime,				FIELD_TIME),
	DEFINE_FIELD( m_nextLineFireTime,		FIELD_TIME),
	DEFINE_FIELD( m_bInBarnacleMouth,		FIELD_BOOLEAN),
	DEFINE_KEYFIELD( m_bArmorRechargeEnabled, FIELD_BOOLEAN, "ArmorRechargeEnabled" ),
	DEFINE_FIELD( m_bForceArmorRecharge,	FIELD_BOOLEAN),
	DEFINE_FIELD( m_iCurrentRechargeGoal,	FIELD_INTEGER ),
	DEFINE_FIELD( m_hVictim,				FIELD_EHANDLE ),
	DEFINE_FIELD( m_bExtractingBugbait,		FIELD_BOOLEAN),
	DEFINE_FIELD( m_iLeftHandAttachment,	FIELD_INTEGER ),
	DEFINE_FIELD( m_iRightHandAttachment,	FIELD_INTEGER ),

	//								m_AssaultBehavior	(auto saved by AI)
	//								m_LeadBehavior		(auto saved by AI)
	// DEFINE_FIELD( m_bStopLoopingSounds, FIELD_BOOLEAN ),

	// Function Pointers
	DEFINE_USEFUNC( Use ),

	DEFINE_INPUTFUNC( FIELD_VOID,	"EnableArmorRecharge",	InputEnableArmorRecharge ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"DisableArmorRecharge", InputDisableArmorRecharge ),
	DEFINE_INPUTFUNC( FIELD_STRING,	"ChargeTarget",			InputChargeTarget ),
	DEFINE_INPUTFUNC( FIELD_STRING, "ExtractBugbait",		InputExtractBugbait ),

	// Outputs
	DEFINE_OUTPUT(m_OnFinishedExtractingBugbait,	"OnFinishedExtractingBugbait"),
	DEFINE_OUTPUT(m_OnFinishedChargingTarget,		"OnFinishedChargingTarget"),
	DEFINE_OUTPUT(m_OnPlayerUse,					"OnPlayerUse" ),

END_DATADESC()
LINK_ENTITY_TO_CLASS( npc_vortigaunt, CNPC_Vortigaunt );

// for special behavior with rollermines
static bool IsRoller( CBaseEntity *pRoller )
{
	return FClassnameIs( pRoller, "npc_rollermine" );
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
bool CNPC_Vortigaunt::IsHealPositionValid(void)
{
	CBasePlayer *pPlayer = AI_GetSinglePlayer();
	if ( !pPlayer )
		return false;

	Vector vecToPlayer = ( pPlayer->EyePosition() - EyePosition() );
	// Make sure he's still within heal range
	if ( vecToPlayer.LengthSqr() > (HEAL_RANGE*HEAL_RANGE) )
		return false;

	// Make sure the player stays in front of me.
	// Don't do a straight viewcone check, because my head turns to follow the player, and
	// that will allow the player to move too far around me. Instead, do a viewcone check
	// from my base facing.
	vecToPlayer.z = 0;
	VectorNormalize( vecToPlayer );
	Vector facingDir = BodyDirection2D();
	float flDot = DotProduct( vecToPlayer, facingDir );
	if ( flDot < VIEW_FIELD_NARROW )
		return false;

	// Now ensure he's still visible
	return ( FVisible( pPlayer ) );
}

//-----------------------------------------------------------------------------
// Purpose: Check the heal position. If it's bad, break out current schedule
//			and setup for healing at a later time.
//-----------------------------------------------------------------------------
bool CNPC_Vortigaunt::CheckHealPosition( void )
{
	if ( IsHealPositionValid() )
		return true;

	// Heal position isn't valid. Abort heal.
	Speak( VORT_CURE_INTERRUPT );
	ClearBeams();
	EndHandGlow();
	m_flNextHealTime = gpGlobals->curtime + 5.0;
	TaskFail(FAIL_BAD_POSITION);
	return false;
}

//------------------------------------------------------------------------------
// Purpose :
// Input   :
// Output  :
//------------------------------------------------------------------------------
#define VORT_HEAL_SENTENCE				0
#define VORT_DONE_HEAL_SENTENCE			1
#define	VORT_START_EXTRACT_SENTENCE		2
#define VORT_FINISH_EXTRACT_SENTENCE	3

void CNPC_Vortigaunt::SpeakSentence( int sentenceType )
{
	if (sentenceType == VORT_HEAL_SENTENCE)
	{
		if ( IsHealPositionValid() )
		{
			Speak( VORT_CURE );
		}
	}
	else if (sentenceType == VORT_DONE_HEAL_SENTENCE)
	{
		Speak( VORT_CURESTOP );
	}
	else if (sentenceType == VORT_START_EXTRACT_SENTENCE)
	{
		Speak( VORT_EXTRACT_START );
	}
	else if (sentenceType == VORT_FINISH_EXTRACT_SENTENCE)
	{
		Speak( VORT_EXTRACT_FINISH );
	}
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::StartTask( const Task_t *pTask )
{
	switch ( pTask->iTask)
	{

	case TASK_VORTIGAUNT_EXTRACT_WARMUP:
	{
		ResetIdealActivity( (Activity) ACT_VORTIGAUNT_TO_ACTION );
		break;
	}

	case TASK_VORTIGAUNT_EXTRACT:
	{
		SetActivity( (Activity) ACT_RANGE_ATTACK1 );
		break;
	}

	case TASK_VORTIGAUNT_EXTRACT_COOLDOWN:
	{
		ResetIdealActivity( (Activity)ACT_VORTIGAUNT_TO_IDLE );
		break;
	}

	case TASK_VORTIGAUNT_FIRE_EXTRACT_OUTPUT:
		{
			// Cheat, and fire both outputs
			m_OnFinishedExtractingBugbait.FireOutput( this, this );
			TaskComplete();
			break;
		}

	case TASK_VORTIGAUNT_HEAL_WARMUP:
		{
			ResetIdealActivity( (Activity)ACT_VORTIGAUNT_START_HEAL );
			break;
		}
	case TASK_VORTIGAUNT_HEAL:
		{
			ResetIdealActivity( (Activity)ACT_VORTIGAUNT_HEAL_LOOP );
			break;
		}
	case TASK_VORTIGAUNT_FACE_STOMP:
		{
			if (GetEnemy()!=NULL)
			{
				GetMotor()->SetIdealYaw( CalcIdealYaw ( GetEnemy()->GetLocalOrigin() ) + VORTIGAUNT_STOMP_TURN_OFFSET );
				SetTurnActivity(); 
			}
			else
			{
				TaskFail(FAIL_NO_ENEMY);
			}
			break;
		}
	case TASK_VORTIGAUNT_STOMP_ATTACK:
		{
			m_flLastAttackTime = gpGlobals->curtime;
			ResetIdealActivity( (Activity)ACT_VORTIGAUNT_STOMP );
			break;
		}
	case TASK_VORTIGAUNT_GRENADE_KILL:
		{
			if (GetTarget() == NULL)
			{
				TaskComplete();
			}
			// Used to set delay between beam shot and damage
			ClearWait();

			break;
		}
	case TASK_VORTIGAUNT_ZAP_GRENADE_OWNER:
		{
			if (GetEnemy() == NULL)
			{
				TaskComplete();
			}
			// Used to set delay between beam shot and damage
			ClearWait();

			break;
		}
	case TASK_VORTIGAUNT_WAIT_FOR_PLAYER:
		{
			// Wait for the player to get near (before starting the bugbait sequence)
			break;
		}
	default:
		{
			BaseClass::StartTask( pTask );	
			break;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::RunTask( const Task_t *pTask )
{
	switch ( pTask->iTask )
	{
	case TASK_RANGE_ATTACK1:
		{
			if (GetEnemy() != NULL)
			{
				if (GetEnemy()->IsPlayer())
				{
					m_flPlaybackRate = 1.5;
				}
				if (!GetEnemy()->IsAlive())
				{
					if( IsActivityFinished() )
					{
						TaskComplete();
						break;
					}
				}
				// This is along attack sequence so if the enemy
				// becomes occluded bail
				if (HasCondition( COND_ENEMY_OCCLUDED ))
				{
					TaskComplete();
					break;
				}
			}
			BaseClass::RunTask( pTask );
			break;
		}
	case TASK_MELEE_ATTACK1:
		{
			if (GetEnemy() == NULL)
			{
				if ( IsActivityFinished() )
				{
					TaskComplete();
				}
			}
			else
			{
				BaseClass::RunTask( pTask );
			}
			break;	
		}
	case TASK_MELEE_ATTACK2:
		{
			if ( IsActivityFinished() )
			{
				TaskComplete();
			}
			break;
		}
	case TASK_VORTIGAUNT_STOMP_ATTACK:
		{
			// Face enemy slightly off center 
			if (GetEnemy() != NULL)
			{
				GetMotor()->SetIdealYawAndUpdate( CalcIdealYaw ( GetEnemy()->GetLocalOrigin() ) + VORTIGAUNT_STOMP_TURN_OFFSET, AI_KEEP_YAW_SPEED );
			}

			if ( IsActivityFinished() )
			{
				TaskComplete();
			}
			break;
		}
	case TASK_VORTIGAUNT_FACE_STOMP:
		{
			if (GetEnemy()!=NULL)
			{
				GetMotor()->SetIdealYawAndUpdate( CalcIdealYaw( GetEnemy()->GetLocalOrigin() ) + VORTIGAUNT_STOMP_TURN_OFFSET );

				if ( FacingIdeal() )
				{
					TaskComplete();
				}
			}
			else
			{
				TaskFail(FAIL_NO_ENEMY);
			}
			break;
		}

	case TASK_VORTIGAUNT_EXTRACT_WARMUP:
	{
		if ( IsActivityFinished() )
		{
			TaskComplete();
		}
		break;
	}
	
	case TASK_VORTIGAUNT_EXTRACT:
	{
		if ( IsActivityFinished() )
		{
			TaskComplete();
		}
		break;
	}

	case TASK_VORTIGAUNT_EXTRACT_COOLDOWN:
	{
		if ( IsActivityFinished() )
		{
			m_bExtractingBugbait = false;
			TaskComplete();
		}
		break;
	}

	case TASK_VORTIGAUNT_HEAL_WARMUP:
		{
			if ( IsActivityFinished() )
			{
				TaskComplete();
			}

			if ( !CheckHealPosition() )
				return;
			break;
		}
	case TASK_VORTIGAUNT_GRENADE_KILL:
		{
			if (GetTarget() == NULL)
			{
				TaskComplete();
				break;
			}

			// Face the grenade
			GetMotor()->SetIdealYawToTargetAndUpdate( GetTarget()->GetLocalOrigin() );

			// Shoot beam when grenade is close
			float flDist = (GetTarget()->GetLocalOrigin() - GetLocalOrigin()).Length();

			if (flDist < 225)
			{
				ClawBeam(GetTarget(),16, m_iLeftHandAttachment );
				ClawBeam(GetTarget(),16, m_iRightHandAttachment );
			}
			if (flDist < 150)
			{
				CBaseGrenade* pGrenade = dynamic_cast<CBaseGrenade*>((CBaseEntity*)GetTarget());
				if (!pGrenade)
				{
					TaskComplete();
				}
				pGrenade->Detonate();
				TaskComplete();
			}
			break;
		}
	case TASK_VORTIGAUNT_ZAP_GRENADE_OWNER:
		{
			if (GetEnemy() == NULL)
			{
				TaskComplete();
				break;
			}

			// Face the grenade owner
			GetMotor()->SetIdealYawToTargetAndUpdate( GetEnemy()->GetLocalOrigin() );

			// Shoot beam only if enemy is close enough
			float flDist = (GetEnemy()->GetLocalOrigin() - GetLocalOrigin()).Length();
			if (flDist < 350)
			{
				if (!IsWaitSet()					||
					IsWaitFinished() )
				{
					ClawBeam(GetEnemy(),5, m_iLeftHandAttachment );
					ClawBeam(GetEnemy(),5, m_iRightHandAttachment );

					// Set a delay and then we do damage.  Store in
					// m_hVictim in case we are interrupted
					if (!IsWaitSet())
					{
						SetWait( 0.2 );
						m_hVictim		 = GetEnemy();
					}
				}
				else
				{
					if (m_hVictim != NULL)
					{
						CTakeDamageInfo info( this, this, 15, DMG_SHOCK );
						CalculateMeleeDamageForce( &info, (GetEnemy()->GetAbsOrigin() - GetAbsOrigin()), GetEnemy()->GetAbsOrigin() );
						m_hVictim->MyNPCPointer()->TakeDamage( info );
					}
					TaskComplete();
				}
			}
			else
			{
				// To far away to shoot
				TaskComplete();
			}
			break;
		}
	case TASK_VORTIGAUNT_HEAL:
		{
			CBasePlayer *pPlayer = AI_GetSinglePlayer();
			if (pPlayer)
			{
				if (!CheckHealPosition())
					return;

				// Charge the suit's armor
				pPlayer->IncrementArmorValue( 1, m_iCurrentRechargeGoal );

				// Stop healing once we've hit the maximum level we'll ever charge the player to.
				if ( pPlayer->ArmorValue() >= m_iCurrentRechargeGoal )
				{
					m_bForceArmorRecharge = false;
					m_flNextHealTime = gpGlobals->curtime + VORTIGAUNT_HEAL_RECHARGE;
					ClearBeams();
					EndHandGlow();
					TaskComplete();
					m_OnFinishedChargingTarget.FireOutput( this, this );
					return;
				}

				// ------------------------------------------
				//  Suit sound
				// ------------------------------------------
				// Play the on sound or the looping charging sound
				if (!m_iSuitSound)
				{
					m_iSuitSound++;
					EmitSound( "NPC_Vortigaunt.SuitOn" );
					m_flSuitSoundTime = 0.56 + gpGlobals->curtime;
				}
				if ((m_iSuitSound == 1) && (m_flSuitSoundTime <= gpGlobals->curtime))
				{
					m_iSuitSound++;

					CPASAttenuationFilter filter( this, "NPC_Vortigaunt.SuitCharge" );
					filter.MakeReliable();

					EmitSound( filter, entindex(), "NPC_Vortigaunt.SuitCharge" );

					m_bStopLoopingSounds = true;
				}
			}

			break;
		}

	case TASK_VORTIGAUNT_WAIT_FOR_PLAYER:
		{
			// Wait for the player to get near (before starting the bugbait sequence)
			// Get edict for one player
			CBasePlayer *pPlayer = AI_GetSinglePlayer();
			if ( pPlayer )
			{
				GetMotor()->SetIdealYawToTargetAndUpdate( pPlayer->GetAbsOrigin(), AI_KEEP_YAW_SPEED );
				SetTurnActivity();
				if ( GetMotor()->DeltaIdealYaw() < 10 )
				{
					// Wait for the player to get close enough
					if ( UTIL_DistApprox( GetAbsOrigin(), pPlayer->GetAbsOrigin() ) < 384 )
					{
						TaskComplete();
					}
				}
			}
			else
			{
				TaskFail(FAIL_NO_PLAYER);
			}
		}
		break;


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




//=========================================================
// GetSoundInterests - returns a bit mask indicating which types
// of sounds this monster regards. 
//=========================================================
int CNPC_Vortigaunt::GetSoundInterests ( void) 
{
	return	SOUND_WORLD	|
			SOUND_COMBAT	|
			SOUND_CARCASS	|
			SOUND_MEAT		|
			SOUND_GARBAGE	|
			SOUND_DANGER	|
			SOUND_PLAYER;
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
Class_T	CNPC_Vortigaunt::Classify ( void )
{
	return	CLASS_VORTIGAUNT;
}

//=========================================================
// ALertSound - barney says "Freeze!"
//=========================================================
void CNPC_Vortigaunt::AlertSound( void )
{
	if ( GetEnemy() != NULL )
	{
		if ( IsOkToCombatSpeak() )
		{
			Speak( VORT_ATTACK );
		}
	}

}
//=========================================================
// MaxYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
float CNPC_Vortigaunt::MaxYawSpeed ( void )
{
	switch ( GetActivity() )
	{
	case ACT_IDLE:		
		return 35;
		break;
	case ACT_WALK:
		return 35;
		break;
	case ACT_RUN:
		return 45;
		break;
	default:
		return 35;
		break;
	}
}

//------------------------------------------------------------------------------
// Purpose : For innate range attack
// Input   :
// Output  :
//------------------------------------------------------------------------------
int CNPC_Vortigaunt::RangeAttack1Conditions( float flDot, float flDist )
{
	if (GetEnemy() == NULL)
	{
		return( COND_NONE );
	}

	if ( gpGlobals->curtime < m_flNextAttack )
	{
		return( COND_NONE );
	}

	// Range attack is ineffective on manhack so never use it
	// Melee attack looks a lot better anyway
	if (GetEnemy()->Classify() == CLASS_MANHACK)
	{
		return( COND_NONE );
	}

	if ( flDist <= 70 )
	{
		return( COND_TOO_CLOSE_TO_ATTACK );
	}
	else if ( flDist > 1500 * 12 )	// 1500ft max
	{
		return( COND_TOO_FAR_TO_ATTACK );
	}
	else if ( flDot < 0.65 )
	{
		return( COND_NOT_FACING_ATTACK );
	}

	return( COND_CAN_RANGE_ATTACK1 );
}

//-----------------------------------------------------------------------------
// Purpose: For innate melee attack
// Input  :
// Output :
//-----------------------------------------------------------------------------
int CNPC_Vortigaunt::MeleeAttack1Conditions ( float flDot, float flDist )
{
	if (flDist > 70)
	{
		return COND_TOO_FAR_TO_ATTACK;
	}
	else if (flDot < 0.7)
	{
		// If I'm not facing attack clear TOOFAR which may have been set by range attack
		// Otherwise we may try to back away even when melee attack is valid
		ClearCondition(COND_TOO_FAR_TO_ATTACK);
		return COND_NOT_FACING_ATTACK;
	}

	// no melee attacks against rollermins
	if ( IsRoller(GetEnemy()) )
		return COND_NONE;

	return COND_CAN_MELEE_ATTACK1;
}

//------------------------------------------------------------------------------
// Purpose : Returns who I hit on a kick (or NULL)
// Input   :
// Output  :
//------------------------------------------------------------------------------
CBaseEntity*	CNPC_Vortigaunt::Kick( void )
{
	trace_t tr;

	Vector forward;
	AngleVectors( GetAbsAngles(), &forward );
	Vector vecStart = WorldSpaceCenter();
	Vector vecEnd = vecStart + (forward * 70);

	AI_TraceHull( vecStart, vecEnd, Vector(-16,-16,-18), Vector(16,16,18), 
		MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, &tr );

	return tr.m_pEnt;
}

//------------------------------------------------------------------------------
// Purpose : Vortigaunt claws at his enemy
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CNPC_Vortigaunt::Claw( int iAttachment)
{
	CBaseEntity *pHurt = CheckTraceHullAttack( 40, Vector(-10,-10,-10), Vector(10,10,10),sk_vortigaunt_dmg_claw.GetFloat(), DMG_SLASH );
	if ( pHurt )
	{
		pHurt->ViewPunch( QAngle(5,0,-18) );
		// If hit manhack make temp glow
		if (pHurt->Classify() == CLASS_MANHACK)
		{
			ClawBeam( pHurt, 16, iAttachment );
		}
		// Play a random attack hit sound
		EmitSound( "NPC_Vortigaunt.Claw" );
	}
}

//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//
// Returns number of events handled, 0 if none.
//=========================================================
void CNPC_Vortigaunt::HandleAnimEvent( animevent_t *pEvent )
{
	if ( pEvent->event == AE_VORTIGAUNT_CLAW_LEFT )
	{
		Claw( m_iLeftHandAttachment );
		return;
	}

	if ( pEvent->event == AE_VORTIGAUNT_CLAW_RIGHT )
	{
		Claw( m_iRightHandAttachment );
		return;
	}

	if ( pEvent->event == AE_VORTIGAUNT_ZAP_POWERUP )
	{
		// speed up attack when on hard
		if ( g_iSkillLevel == SKILL_HARD )
			 m_flPlaybackRate = 1.5;

		ArmBeam( -1 ,VORTIGAUNT_BEAM_HURT );
		ArmBeam( 1	,VORTIGAUNT_BEAM_HURT );
		BeamGlow();

		// Make hands glow if not already glowing
		if ( m_fGlowAge == 0 )
		{
			StartHandGlow( VORTIGAUNT_BEAM_HURT );
		}

		CPASAttenuationFilter filter( this );
		
		CSoundParameters params;
		if ( GetParametersForSound( "NPC_Vortigaunt.ZapPowerup", params, NULL ) )
		{
			EmitSound_t ep( params );
			ep.m_nPitch = 100 + m_iBeams * 10;
	
			EmitSound( filter, entindex(), ep );

			m_bStopLoopingSounds = true;
		}
		//m_nSkin = m_iBeams / 2;
		return;
	}

	if ( pEvent->event == AE_VORTIGAUNT_ZAP_SHOOT )
	{
		ClearBeams();

		ClearMultiDamage();

		ZapBeam( -1 );
		ZapBeam( 1 );
		EndHandGlow();

		EmitSound( "NPC_Vortigaunt.Shoot" );
		m_bStopLoopingSounds = true;
		ApplyMultiDamage();

		if ( m_bExtractingBugbait == true )
		{
			// Spawn bugbait!
			CBaseCombatWeapon *pWeapon = Weapon_Create( "weapon_bugbait" );
			if ( pWeapon )
			{
				// Starting above the body, spawn closer and closer to the vort until it's clear
				Vector vecSpawnOrigin = GetTarget()->WorldSpaceCenter() + Vector(0, 0, 32);
				int iNumAttempts = 4;
				Vector vecToVort = (WorldSpaceCenter() - vecSpawnOrigin);
				float flDistance = VectorNormalize( vecToVort ) / (iNumAttempts-1);
				for (int i = 0; i < iNumAttempts; i++ )
				{
					trace_t tr;
					CTraceFilterSkipTwoEntities traceFilter( GetTarget(), this, COLLISION_GROUP_NONE );
					AI_TraceLine( vecSpawnOrigin, vecSpawnOrigin, MASK_SHOT, &traceFilter, &tr );

					if ( tr.fraction == 1.0 && !tr.m_pEnt )
					{
						// Make sure it can fit there
						AI_TraceHull( vecSpawnOrigin, vecSpawnOrigin, -Vector(16,16,16), Vector(16,16,48), MASK_SHOT, &traceFilter, &tr );
						if ( tr.fraction == 1.0 && !tr.m_pEnt )
							break;
					}

					//NDebugOverlay::Box( vecSpawnOrigin, pWeapon->WorldAlignMins(), pWeapon->WorldAlignMins(), 255,0,0, 64, 100 );

					// Move towards the vort
					vecSpawnOrigin = vecSpawnOrigin + (vecToVort * flDistance);
				}

				// HACK: If we've still failed, just spawn it on the player 
				if ( i == iNumAttempts )
				{
					CBasePlayer	*pPlayer = AI_GetSinglePlayer();
					if ( pPlayer )
					{
						vecSpawnOrigin = pPlayer->WorldSpaceCenter();
					}
				}

				//NDebugOverlay::Box( vecSpawnOrigin, -Vector(20,20,20), Vector(20,20,20), 0,255,0, 64, 100 );

				pWeapon->SetAbsOrigin( vecSpawnOrigin );
				pWeapon->Drop( Vector(0,0,1) );
			}

			CEffectData	data;
			
			data.m_vOrigin = GetTarget()->WorldSpaceCenter();
			data.m_vNormal = WorldSpaceCenter() - GetTarget()->WorldSpaceCenter();
			VectorNormalize( data.m_vNormal );
			
			data.m_flScale = 4;

			DispatchEffect( "AntlionGib", data );
		}

		m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 0.5, 4.0 );
		return;
	}
	
	if ( pEvent->event == AE_VORTIGAUNT_ZAP_DONE )
	{
		ClearBeams( );
		return;
	}

	if ( pEvent->event == AE_VORTIGAUNT_HEAL_STARTGLOW )
	{
		// Make hands glow
		StartHandGlow(VORTIGAUNT_BEAM_HEAL);
		return;
	}
	
	if ( pEvent->event == AE_VORTIGAUNT_HEAL_STARTBEAMS )
	{
		if ( !CheckHealPosition())
			return;

		HealBeam(-1);
		return;

		//m_nSkin = m_iBeams / 2;
	}

	if( pEvent->event == AE_VORTIGAUNT_KICK )
	{
		CBaseEntity *pHurt = Kick();
		CBaseCombatCharacter* pBCC = ToBaseCombatCharacter( pHurt );

		if (pBCC)
		{
			Vector forward, up;
			AngleVectors( GetLocalAngles(), &forward, NULL, &up );

			if ( pBCC->DispatchInteraction( g_interactionVortigauntKick, NULL, this ) )
			{
				pBCC->ApplyAbsVelocityImpulse( forward * 300 + up * 250 );

				CTakeDamageInfo info( this, this, pBCC->m_iHealth+1, DMG_CLUB );
				CalculateMeleeDamageForce( &info, forward, pHurt->GetAbsOrigin() );
				pBCC->TakeDamage( info );
			}
			else
			{
				pBCC->ViewPunch( QAngle(15,0,0) );

				CTakeDamageInfo info( this, this, sk_vortigaunt_dmg_claw.GetFloat(), DMG_CLUB );
				CalculateMeleeDamageForce( &info, forward, pHurt->GetAbsOrigin() );
				pBCC->TakeDamage( info );
			}
			EmitSound( "NPC_Vortigaunt.Kick" );
		}

		return;
	}

	if ( pEvent->event == AE_VORTIGAUNT_STOMP )
	{
		CBaseCombatCharacter* pBCC = GetEnemyCombatCharacterPointer();

		if ( pBCC )
		{
			float fDist = (pBCC->GetLocalOrigin() - GetLocalOrigin()).Length();
			if (fDist > VORTIGAUNT_STOMP_DIST)
			{
				pBCC->DispatchInteraction( g_interactionVortigauntStompFail, NULL, this );
				return;
			}
			else
			{
				pBCC->DispatchInteraction( g_interactionVortigauntStompHit, NULL, this );
			}
			EmitSound( "NPC_Vortigaunt.Kick" );
		}

		return;
	}

	if ( pEvent->event == AE_VORTIGAUNT_HEAL_STARTSOUND )
	{
		CPASAttenuationFilter filter( this );

		CSoundParameters params;
		if ( GetParametersForSound( "NPC_Vortigaunt.StartHealLoop", params, NULL ) )
		{
			EmitSound_t ep( params );
			ep.m_nPitch = 100 + m_iBeams * 10;

			EmitSound( filter, entindex(), ep );

			m_bStopLoopingSounds = true;
		}
		return;
	}

	if ( pEvent->event == AE_VORTIGAUNT_SWING_SOUND )
	{
		EmitSound( "NPC_Vortigaunt.Swing" );	
		return;
	}

	if ( pEvent->event == AE_VORTIGAUNT_SHOOT_SOUNDSTART )
	{
		CPASAttenuationFilter filter( this );
		CSoundParameters params;
		if ( GetParametersForSound( "NPC_Vortigaunt.StartShootLoop", params, NULL ) )
		{
			EmitSound_t ep( params );
			ep.m_nPitch = 100 + m_iBeams * 10;

			EmitSound( filter, entindex(), ep );
			m_bStopLoopingSounds = true;
		}
		return;
	}

	if ( pEvent->event == AE_VORTIGAUNT_DEFEND_BEAMS )
	{
		DefendBeams();
		return;
	}

	if ( pEvent->event == AE_NPC_LEFTFOOT )
	{
		EmitSound( "NPC_Vortigaunt.FootstepLeft", pEvent->eventtime );
		return;
	}

	if ( pEvent->event == AE_NPC_RIGHTFOOT )
	{
		EmitSound( "NPC_Vortigaunt.FootstepRight", pEvent->eventtime );
		return;
	}
	
	BaseClass::HandleAnimEvent( pEvent );
}

//------------------------------------------------------------------------------
// Purpose : Returnst true if entity is stompable
// Input   :
// Output  :
//------------------------------------------------------------------------------
bool CNPC_Vortigaunt::IsStompable(CBaseEntity *pEntity)
{
	if (!pEntity)
		return false;

	float fDist = (pEntity->GetLocalOrigin() - GetLocalOrigin()).Length();
	if (fDist > VORTIGAUNT_STOMP_DIST)
		return false;

	CBaseCombatCharacter* pBCC = (CBaseCombatCharacter *)pEntity;
	
	if ( pBCC && pBCC->DispatchInteraction( g_interactionVortigauntStomp, NULL, this ) )
		return true;

	return false;
}

//------------------------------------------------------------------------------
// Purpose : Translate some activites for the Vortigaunt
// Input   :
// Output  :
//------------------------------------------------------------------------------
Activity CNPC_Vortigaunt::NPC_TranslateActivity( Activity eNewActivity )
{
	if ((eNewActivity == ACT_SIGNAL3)									&& 
		(SelectWeightedSequence ( ACT_SIGNAL3 ) == ACTIVITY_NOT_AVAILABLE)	)
	{
		eNewActivity = ACT_IDLE;
	}

	if (eNewActivity == ACT_IDLE)
	{
		if ( m_NPCState == NPC_STATE_COMBAT || m_NPCState == NPC_STATE_ALERT)
		{
			return ACT_IDLE_ANGRY;
		}
	}
	else if (eNewActivity == ACT_MELEE_ATTACK1)
	{
		// If enemy is low pick ATTACK2 (kick)
		if (GetEnemy() != NULL && (GetEnemy()->EyePosition().z - GetLocalOrigin().z) < 20)
		{
			return ACT_MELEE_ATTACK2;
		}
	}
	return BaseClass::NPC_TranslateActivity( eNewActivity );
}

//------------------------------------------------------------------------------
// Purpose : If I've been in alert for a while and nothing's happened, 
//			 go back to idle
// Input   :
// Output  :
//------------------------------------------------------------------------------
bool CNPC_Vortigaunt::ShouldGoToIdleState( void )
{
	if (m_flLastStateChangeTime + 10 < gpGlobals->curtime)
	{
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::UpdateOnRemove( void)
{
	ClearBeams();
	ClearHandGlow();

	// Chain at end to mimic destructor unwind order
	BaseClass::UpdateOnRemove();
}

//------------------------------------------------------------------------------
// Purpose :
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CNPC_Vortigaunt::Event_Killed( const CTakeDamageInfo &info )
{
	ClearBeams();
	ClearHandGlow();

	BaseClass::Event_Killed( info );
}

bool CNPC_Vortigaunt::CreateBehaviors()
{
	AddBehavior( &m_AssaultBehavior );
	AddBehavior( &m_LeadBehavior );
	//AddBehavior( &m_FollowBehavior );
	
	return BaseClass::CreateBehaviors();
}
//=========================================================
// Spawn
//=========================================================
void CNPC_Vortigaunt::Spawn()
{
	// Allow multiple models (for slaves), but default to vortigaunt.mdl
	char *szModel = (char *)STRING( GetModelName() );
	if (!szModel || !*szModel)
	{
		szModel = "models/vortigaunt.mdl";
		SetModelName( AllocPooledString(szModel) );
	}

	Precache();
	SetModel( szModel );

	SetHullType(HULL_WIDE_HUMAN);
	SetHullSizeNormal();

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );
	SetMoveType( MOVETYPE_STEP );
	m_bloodColor		= BLOOD_COLOR_GREEN;
	m_iHealth			= sk_vortigaunt_health.GetFloat();
	SetViewOffset( Vector ( 0, 0, 64 ) );// position of the eyes relative to monster's origin.
	m_flFieldOfView		= VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello
	m_NPCState			= NPC_STATE_NONE;

	GetExpresser()->SetVoicePitch( random->RandomInt( 85, 110 ) );

	CapabilitiesAdd( bits_CAP_ANIMATEDFACE | bits_CAP_TURN_HEAD | bits_CAP_MOVE_GROUND | bits_CAP_NO_HIT_PLAYER );
	CapabilitiesAdd	( bits_CAP_INNATE_RANGE_ATTACK1 );
	CapabilitiesAdd	( bits_CAP_INNATE_MELEE_ATTACK1 );
	CapabilitiesAdd	( bits_CAP_DOORS_GROUP );
	CapabilitiesAdd( bits_CAP_FRIENDLY_DMG_IMMUNE );
	
	m_flNextHealTime		= 0;		// Next time allowed to heal player
	m_flEyeIntegRate		= 0.6;		// Got a big eyeball so turn it slower
	m_hVictim				= NULL;
	m_bForceArmorRecharge	= false;
	
	m_nCurGlowIndex			= 0;
	m_pLeftHandGlow			= NULL;
	m_pRightHandGlow		= NULL;

	m_bStopLoopingSounds	= false;

	m_iLeftHandAttachment = LookupAttachment( VORTIGAUNT_LEFT_CLAW );
	m_iRightHandAttachment = LookupAttachment( VORTIGAUNT_RIGHT_CLAW );

	NPCInit();

	SetUse( &CNPC_Vortigaunt::Use );
}

//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CNPC_Vortigaunt::Precache()
{
	PrecacheModel( STRING( GetModelName() ) );

	m_nLightningSprite = PrecacheModel("sprites/lgtning.vmt");

	PrecacheModel("sprites/blueglow1.vmt");
	PrecacheModel("sprites/greenglow1.vmt");

	// every new barney must call this, otherwise
	// when a level is loaded, nobody will talk (time is reset to 0)
	TalkInit();

	PrecacheScriptSound( "NPC_Vortigaunt.SuitOn" );
	PrecacheScriptSound( "NPC_Vortigaunt.SuitCharge" );
	PrecacheScriptSound( "NPC_Vortigaunt.Claw" );
	PrecacheScriptSound( "NPC_Vortigaunt.ZapPowerup" );
	PrecacheScriptSound( "NPC_Vortigaunt.Shoot" );
	PrecacheScriptSound( "NPC_Vortigaunt.Kick" );
	PrecacheScriptSound( "NPC_Vortigaunt.StartHealLoop" );
	PrecacheScriptSound( "NPC_Vortigaunt.Swing" );
	PrecacheScriptSound( "NPC_Vortigaunt.StartShootLoop" );
	PrecacheScriptSound( "NPC_Vortigaunt.FootstepLeft" );
	PrecacheScriptSound( "NPC_Vortigaunt.FootstepRight" );
	PrecacheScriptSound( "NPC_Vortigaunt.ClawBeam" );

	BaseClass::Precache();
}	

// Init talk data
void CNPC_Vortigaunt::TalkInit()
{
	
	BaseClass::TalkInit();

	// vortigaunt will try to talk to friends in this order:
	m_szFriends[0] = "npc_vortigaunt";
	m_szFriends[1] = "npc_citizen";

	// get voice for head - just one barney voice for now
	GetExpresser()->SetVoicePitch( 100 );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
	m_OnPlayerUse.FireOutput( pActivator, pCaller );

	if ( !IsInAScript() && m_NPCState != NPC_STATE_SCRIPT )
	{
		// First, try to speak the +USE concept
		if ( IsOkToSpeakInResponseToPlayer() )
		{
			if ( !Speak( TLK_USE ) )
			{
				// If we haven't said hi, say that first
				if ( !GetExpresser()->SpokeConcept( TLK_HELLO ) )
				{
					Speak( TLK_HELLO );
				}
				else
				{
					Speak( TLK_IDLE );
				}
			}
			else
			{
				// Don't say hi after you've said your +USE speech
				GetExpresser()->SetSpokeConcept( TLK_HELLO, NULL );	
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
int	CNPC_Vortigaunt::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
	// make sure friends talk about it if player hurts talkmonsters...
	int ret = BaseClass::OnTakeDamage_Alive( info );
	if (!IsAlive())
	{
		return ret;
	}

	if ( m_NPCState != NPC_STATE_PRONE && (info.GetAttacker()->GetFlags() & FL_CLIENT) )
	{

		// This is a heurstic to determine if the player intended to harm me
		// If I have an enemy, we can't establish intent (may just be crossfire)
		if ( GetEnemy() == NULL )
		{
			// If I'm already suspicious, get mad
			if (m_afMemory & bits_MEMORY_SUSPICIOUS)
			{
				// Alright, now I'm pissed!
				Speak( VORT_MAD );

				Remember( bits_MEMORY_PROVOKED );

				// Allowed to hit the player now!
				CapabilitiesRemove(bits_CAP_NO_HIT_PLAYER);

				StopFollowing();
			}
			else
			{
				// Hey, be careful with that
				Speak( VORT_SHOT );
				Remember( bits_MEMORY_SUSPICIOUS );
			}
		}
		else if ( !(GetEnemy()->IsPlayer()) && (m_lifeState != LIFE_DEAD ))
		{
			Speak( VORT_SHOT );
		}
	}
	return ret;
}

	
//=========================================================
// PainSound
//=========================================================
void CNPC_Vortigaunt::PainSound ( void )
{
	if (gpGlobals->curtime < m_painTime)
		return;
	
	m_painTime = gpGlobals->curtime + random->RandomFloat(0.5, 0.75);

	Speak( VORT_PAIN );
}

//=========================================================
// DeathSound 
//=========================================================
void CNPC_Vortigaunt::DeathSound ( void )
{
	Speak( VORT_DIE );
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr )
{
	CTakeDamageInfo info = inputInfo;

	if ( (info.GetDamageType() & DMG_SHOCK) && FClassnameIs( info.GetAttacker(), GetClassname() ) )
	{
		// mask off damage from other vorts for now
		info.SetDamage( 0.01 );
	}

	switch( ptr->hitgroup)
	{
	case HITGROUP_CHEST:
	case HITGROUP_STOMACH:
		if (info.GetDamageType() & (DMG_BULLET | DMG_SLASH | DMG_BLAST))
		{
			info.ScaleDamage( 0.5f );
		}
		break;
	case 10:
		if (info.GetDamageType() & (DMG_BULLET | DMG_SLASH | DMG_CLUB))
		{
			info.SetDamage( info.GetDamage() - 20 );
			if (info.GetDamage() <= 0)
			{
				g_pEffects->Ricochet( ptr->endpos, (vecDir*-1.0f) );
				info.SetDamage( 0.01 );
			}
		}
		// always a head shot
		ptr->hitgroup = HITGROUP_HEAD;
		break;
	}

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

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
int CNPC_Vortigaunt::TranslateSchedule( int scheduleType )
{
	int baseType;

	switch( scheduleType )
	{
	case SCHED_RANGE_ATTACK1:
		return SCHED_VORTIGAUNT_RANGE_ATTACK;
		break;

	case SCHED_MELEE_ATTACK1:
		if (IsStompable(GetEnemy()))
		{
			return SCHED_VORTIGAUNT_STOMP_ATTACK;
		}
		else
		{
			// Tell my enemy I'm about to punch them so they can do something
			// special if they want to
			if ((GetEnemy() != NULL) && (GetEnemy()->MyNPCPointer() != NULL))
			{
				Vector vFacingDir = BodyDirection2D( );
				Vector vClawPos = EyePosition() + vFacingDir*30;
				GetEnemy()->MyNPCPointer()->DispatchInteraction( g_interactionVortigauntClaw, &vClawPos, this );
			}
			return SCHED_VORTIGAUNT_MELEE_ATTACK;
		}
		break;

	case SCHED_IDLE_STAND:
		{
			// call base class default so that scientist will talk
			// when standing during idle
			baseType = BaseClass::TranslateSchedule(scheduleType);

			if (baseType == SCHED_IDLE_STAND)
			{
				// just look straight ahead
				return SCHED_VORTIGAUNT_STAND;
			}
			else
				return baseType;	
			break;

		}
	case SCHED_FAIL_ESTABLISH_LINE_OF_FIRE:
		{
			// Fail schedule doesn't go through SelectSchedule()
			// So we have to clear beams and glow here
			ClearBeams();
			EndHandGlow();

			return SCHED_COMBAT_FACE;
			break;
		}
	case SCHED_CHASE_ENEMY_FAILED:
		{
			baseType = BaseClass::TranslateSchedule(scheduleType);
			if ( baseType != SCHED_CHASE_ENEMY_FAILED )
				return baseType;

			if (HasMemory(bits_MEMORY_INCOVER))
			{
				// Occasionally run somewhere so I don't just
				// stand around forever if my enemy is stationary
				if (random->RandomInt(0,5) == 5)
				{
					return SCHED_PATROL_RUN;
				}
				else
				{
					return SCHED_VORTIGAUNT_STAND;
				}
			}
			break;
		}
	case SCHED_FAIL_TAKE_COVER:
		{
			// Fail schedule doesn't go through SelectSchedule()
			// So we have to clear beams and glow here
			ClearBeams();
			EndHandGlow();

			return SCHED_RUN_RANDOM;
			break;
		}
	}
	
	return BaseClass::TranslateSchedule( scheduleType );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Vortigaunt::ShouldHealTarget( void )
{
	if ( IsInAScript() )
		return false;

	// Must be allowed to do this
	if ( m_bArmorRechargeEnabled == false )
		return false;

	// Can't be too soon
	if ( m_flNextHealTime > gpGlobals->curtime ) 
		return false;

	// Don't heal when fighting
	if ( m_NPCState == NPC_STATE_COMBAT )
		return false;

	if ( IsCurSchedule( SCHED_VORTIGAUNT_EXTRACT_BUGBAIT ) )
		return false;

	// Can't heal if we're leading the player
	if ( IsLeading() )
		return false;

	// Need LOS to the player
	if ( !HasCondition( COND_SEE_PLAYER ) )
		return false;

	// Can't be saying something
//	if ( GetExpresser()->IsSpeaking() )
//		return false;

	// Find a likely target in range
	CBaseEntity *pEntity = PlayerInRange( GetLocalOrigin(), HEAL_RANGE );

	if ( pEntity != NULL )
	{
		CBasePlayer *pPlayer = ToBasePlayer( pEntity );
		
		// Make sure the player's got a suit
		if ( !pPlayer->IsSuitEquipped() )
			return false;

		if ( pPlayer->GetFlags() & FL_NOTARGET )
			return false;

		// See if the player needs armor, or tau-cannon ammo
		if ( pPlayer->ArmorValue() < sk_vortigaunt_armor_charge.GetInt() )
		{
			m_iCurrentRechargeGoal = pPlayer->ArmorValue() + sk_vortigaunt_armor_charge.GetInt();
			if ( m_iCurrentRechargeGoal > 100 )
				m_iCurrentRechargeGoal = 100;
			SetTarget( pEntity );
			return true;
		}
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : int
//-----------------------------------------------------------------------------
int CNPC_Vortigaunt::SelectHealSchedule( void )
{
	// If our lead behavior has a goal, don't wait around to heal anyone
	if ( m_LeadBehavior.HasGoal() )
		return SCHED_NONE;

	// See if we should heal the player
	if ( ShouldHealTarget() )
		return SCHED_VORTIGAUNT_HEAL;

	return SCHED_NONE;
}

//------------------------------------------------------------------------------
// Purpose :
// Input   :
// Output  :
//------------------------------------------------------------------------------
int CNPC_Vortigaunt::SelectSchedule( void )
{
	ClearBeams();
	EndHandGlow();

	// If we're currently supposed to be doing something scripted, do it immediately.
	if ( m_bExtractingBugbait )
		return SCHED_VORTIGAUNT_EXTRACT_BUGBAIT;

	if ( m_bForceArmorRecharge )
	{
		m_flNextHealTime = 0;
		return SCHED_VORTIGAUNT_HEAL;
	}

	int schedule = SelectHealSchedule();
	if ( schedule != SCHED_NONE )
		return schedule;

	if ( HasCondition( COND_PLAYER_PUSHING ) && GlobalEntity_GetState("gordon_precriminal") != GLOBAL_ON )
	{
		return SCHED_MOVE_AWAY;
	}

	if ( BehaviorSelectSchedule() )
		return BaseClass::SelectSchedule();

	switch( m_NPCState )
	{
	case NPC_STATE_PRONE:
		{
			if (m_bInBarnacleMouth)
			{
				return SCHED_VORTIGAUNT_BARNACLE_CHOMP;
			}
			else
			{
				return SCHED_VORTIGAUNT_BARNACLE_HIT;
			}
		}
	case NPC_STATE_COMBAT:
		{
			// dead enemy
			if ( HasCondition( COND_ENEMY_DEAD ) )
			{
				// call base class, all code to handle dead enemies is centralized there.
				return BaseClass::SelectSchedule();
			}

			if ( HasCondition( COND_HEAVY_DAMAGE ) )
			{
				return SCHED_TAKE_COVER_FROM_ENEMY;
			}

			if ( HasCondition( COND_HEAR_DANGER ) )
			{
				return SCHED_TAKE_COVER_FROM_BEST_SOUND;
			}

			if ( HasCondition( COND_ENEMY_DEAD ) && IsOkToCombatSpeak() )
			{
				Speak( VORT_KILL );
			}

			// If I might hit the player shooting...
			else if ( HasCondition( COND_WEAPON_PLAYER_IN_SPREAD ))
			{
				if ( IsOkToCombatSpeak() && m_nextLineFireTime	< gpGlobals->curtime)
				{
					Speak( VORT_LINE_FIRE );
					m_nextLineFireTime = gpGlobals->curtime + 3.0f;
				}

				// Run to a new location or stand and aim
				if (random->RandomInt(0,2) == 0)
				{
					return SCHED_ESTABLISH_LINE_OF_FIRE;
				}
				else
				{
					return SCHED_COMBAT_FACE;
				}
			}
		}
		break;

	case NPC_STATE_ALERT:	
	case NPC_STATE_IDLE:

		if ( HasCondition( COND_HEAR_DANGER ) )
		{
			return SCHED_TAKE_COVER_FROM_BEST_SOUND;
		}

		if ( HasCondition( COND_ENEMY_DEAD ) && IsOkToCombatSpeak() )
		{
			Speak( VORT_KILL );
		}

		break;
	}
	
	return BaseClass::SelectSchedule();
}

//-----------------------------------------------------------------------------
// Purpose:  This is a generic function (to be implemented by sub-classes) to
//			 handle specific interactions between different types of characters
//			 (For example the barnacle grabbing an NPC)
// Input  :  Constant for the type of interaction
// Output :	 true  - if sub-class has a response for the interaction
//			 false - if sub-class has no response
//-----------------------------------------------------------------------------
bool CNPC_Vortigaunt::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
{
	if (interactionType == g_interactionBarnacleVictimDangle)
	{
		// Force choosing of a new schedule
		ClearSchedule();
		m_bInBarnacleMouth	= true;
		return true;
	}
	else if ( interactionType == g_interactionBarnacleVictimReleased )
	{
		SetIdealState( NPC_STATE_IDLE );

		m_bInBarnacleMouth	= false;
		SetAbsVelocity( vec3_origin );
		SetMoveType( MOVETYPE_STEP );
		return true;
	}
	else if ( interactionType == g_interactionBarnacleVictimGrab )
	{
		if ( GetFlags() & FL_ONGROUND )
		{
			SetGroundEntity( NULL );
		}
		SetIdealState( NPC_STATE_PRONE );
		PainSound();
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::DeclineFollowing( void )
{
	Speak( VORT_POK );
}

//-----------------------------------------------------------------------------
// Purpose: Return true if you're willing to be idly talked to by other friends.
//-----------------------------------------------------------------------------
bool CNPC_Vortigaunt::CanBeUsedAsAFriend( void )
{
	// We don't want to be used if we're busy
	if ( IsCurSchedule( SCHED_VORTIGAUNT_HEAL ) )
		return false;
	if ( IsCurSchedule( SCHED_VORTIGAUNT_EXTRACT_BUGBAIT ) )
		return false;

	return BaseClass::CanBeUsedAsAFriend();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::PrescheduleThink( void )
{
	if ( m_bGlowTurningOn )
	{
		m_bGlowTurningOn = false;

		if ( m_pLeftHandGlow != NULL )
		{
			m_pLeftHandGlow->SetScale( 0.5f, 2.0f );
			m_pLeftHandGlow->SetBrightness( 200, 2.0f );
		}

		if ( m_pRightHandGlow != NULL )
		{
			m_pRightHandGlow->SetScale( 0.5f, 2.0f );
			m_pRightHandGlow->SetBrightness( 200, 2.0f );
		}
	}

	// See if we're able to heal now
	if ( ShouldHealTarget() )
	{
		SetCondition( COND_VORTIGAUNT_CAN_HEAL );
	}
	else
	{
		ClearCondition( COND_VORTIGAUNT_CAN_HEAL );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::BuildScheduleTestBits( void )
{
	BaseClass::BuildScheduleTestBits();

	if ( IsCurSchedule( SCHED_IDLE_STAND ) || IsCurSchedule( SCHED_ALERT_STAND ) )
	{
		SetCustomInterruptCondition( COND_VORTIGAUNT_CAN_HEAL );
	}

	if ( ( ConditionInterruptsCurSchedule( COND_GIVE_WAY ) || 
		   IsCurSchedule(SCHED_HIDE_AND_RELOAD ) || 
		   IsCurSchedule(SCHED_RELOAD ) || 
		   IsCurSchedule(SCHED_STANDOFF ) || 
		   IsCurSchedule(SCHED_TAKE_COVER_FROM_ENEMY ) || 
		   IsCurSchedule(SCHED_COMBAT_FACE ) || 
		   IsCurSchedule(SCHED_ALERT_FACE )  ||
		   IsCurSchedule(SCHED_COMBAT_STAND ) || 
		   IsCurSchedule(SCHED_ALERT_FACE_BESTSOUND) ||
		   IsCurSchedule(SCHED_ALERT_STAND) ) )
	{
		SetCustomInterruptCondition( COND_PLAYER_PUSHING );
	}
}

//=========================================================
// ArmBeam - small beam from arm to nearby geometry
//=========================================================
void CNPC_Vortigaunt::ArmBeam( int side, int beamType )
{
	trace_t tr;
	float flDist = 1.0;
	
	if (m_iBeams >= VORTIGAUNT_MAX_BEAMS)
		return;

	Vector forward, right, up;
	AngleVectors( GetLocalAngles(), &forward, &right, &up );
	Vector vecSrc = GetLocalOrigin() + up * 36 + right * side * 16 + forward * 32;

	for (int i = 0; i < 3; i++)
	{
		Vector vecAim = forward * random->RandomFloat( -1, 1 ) + right * side * random->RandomFloat( 0, 1 ) + up * random->RandomFloat( -1, 1 );
		trace_t tr1;
		AI_TraceLine ( vecSrc, vecSrc + vecAim * 512, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr1);
		if (flDist > tr1.fraction)
		{
			tr = tr1;
			flDist = tr.fraction;
		}
	}

	// Couldn't find anything close enough
	if ( flDist == 1.0 )
		return;

	UTIL_ImpactTrace( &tr, DMG_CLUB );

	m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 3.0 );
	if (!m_pBeam[m_iBeams])
		return;

	m_pBeam[m_iBeams]->PointEntInit( tr.endpos, this );
	m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? m_iLeftHandAttachment : m_iRightHandAttachment );

	if (beamType == VORTIGAUNT_BEAM_HEAL)
	{
		m_pBeam[m_iBeams]->SetColor( 0, 0, 255 );
	}
	else
	{
		m_pBeam[m_iBeams]->SetColor( 96, 128, 16 );
	}
	m_pBeam[m_iBeams]->SetBrightness( 64 );
	m_pBeam[m_iBeams]->SetNoise( 12.5 );
	m_iBeams++;
}

//------------------------------------------------------------------------------
// Purpose : Short beam on had in grenade defend mode
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CNPC_Vortigaunt::DefendBeams( void )
{
	Vector vBeamStart;
	QAngle vBeamAng;

	// -----------
	// Left hand
	// -----------
	int i;
	GetAttachment( 1, vBeamStart, vBeamAng );
	for (i=0;i<4;i++)
	{
		Vector vBeamPos = vBeamStart;
		vBeamPos.x += random->RandomFloat(-8,8);
		vBeamPos.y += random->RandomFloat(-8,8);
		vBeamPos.z += random->RandomFloat(-8,8);

		CBeam  *pBeam = CBeam::BeamCreate( "sprites/lgtning.vmt", 3.0 );
		pBeam->PointEntInit( vBeamPos, this );
		pBeam->SetEndAttachment( 1 );
		pBeam->SetBrightness( 255 );
		pBeam->SetNoise( 2 );
		pBeam->SetColor( 96, 128, 16 );
		pBeam->LiveForTime( 0.1 );
	}

	// -----------
	// Right hand
	// -----------
	GetAttachment( 2, vBeamStart, vBeamAng );
	for (i=4;i<VORTIGAUNT_MAX_BEAMS;i++)
	{
		Vector vBeamPos = vBeamStart;
		vBeamPos.x += random->RandomFloat(-8,8);
		vBeamPos.y += random->RandomFloat(-8,8);
		vBeamPos.z += random->RandomFloat(-8,8);

		CBeam  *pBeam = CBeam::BeamCreate( "sprites/lgtning.vmt", 3.0 );
		pBeam->PointEntInit( vBeamPos, this );
		pBeam->SetEndAttachment( 2 );
		pBeam->SetBrightness( 255 );
		pBeam->SetNoise( 2 );
		pBeam->SetColor( 96, 128, 16 );
		pBeam->LiveForTime( 0.1 );
	}
}

//------------------------------------------------------------------------------
// Purpose : Glow for when vortigaunt punches manhack
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CNPC_Vortigaunt::ClawBeam( CBaseEntity *pHurt, int nNoise, int iAttachment )
{
	for (int i=0;i<2;i++)
	{
		CBeam* pBeam = CBeam::BeamCreate( "sprites/lgtning.vmt", 3.0 );
		pBeam->EntsInit( this, pHurt );
		pBeam->SetStartAttachment( iAttachment);
		pBeam->SetBrightness( 255 );
		pBeam->SetNoise( nNoise );
		pBeam->SetColor( 96, 128, 16 );
		pBeam->LiveForTime( 0.2 );
	}
	EmitSound( "NPC_Vortigaunt.ClawBeam" );
}

//------------------------------------------------------------------------------
// Purpose : Put glowing sprites on hands
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CNPC_Vortigaunt::StartHandGlow( int beamType )
{
	m_bGlowTurningOn	= true;
	m_fGlowAge			= 0;

	if (beamType == VORTIGAUNT_BEAM_HEAL)
	{
		m_fGlowChangeTime = VORTIGAUNT_HEAL_GLOWGROW_TIME;
		m_fGlowScale = 0.6f;
	}
	else 
	{
		m_fGlowChangeTime = VORTIGAUNT_ZAP_GLOWGROW_TIME;
		m_fGlowScale = 0.8f;
	}

	if ( m_pLeftHandGlow == NULL )
	{

		if ( beamType == VORTIGAUNT_BEAM_HEAL )
		{
			m_pLeftHandGlow = CSprite::SpriteCreate( "sprites/blueglow1.vmt", GetLocalOrigin(), FALSE );
			m_pLeftHandGlow->SetAttachment( this, 3 );
		}
		else
		{
			m_pLeftHandGlow = CSprite::SpriteCreate( "sprites/greenglow1.vmt", GetLocalOrigin(), FALSE );
			m_pLeftHandGlow->SetAttachment( this, 3 );
		}
		
		m_pLeftHandGlow->SetTransparency( kRenderGlow, 255, 200, 200, 0, kRenderFxNoDissipation );
		m_pLeftHandGlow->SetBrightness( 0 );
		m_pLeftHandGlow->SetScale( 0 );
	}
	
	if ( beamType != VORTIGAUNT_BEAM_HEAL ) 
	{
		if ( m_pRightHandGlow == NULL )
		{	
			m_pRightHandGlow = CSprite::SpriteCreate( "sprites/greenglow1.vmt", GetLocalOrigin(), FALSE );
			m_pRightHandGlow->SetAttachment( this, 4 );

			m_pRightHandGlow->SetTransparency( kRenderGlow, 255, 200, 200, 0, kRenderFxNoDissipation );
			m_pRightHandGlow->SetBrightness( 0 );
			m_pRightHandGlow->SetScale( 0 );
		}
	}
}


//------------------------------------------------------------------------------
// Purpose : Fade glow from hands
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CNPC_Vortigaunt::EndHandGlow( void )
{
	m_bGlowTurningOn	= false;
	m_fGlowChangeTime = VORTIGAUNT_GLOWFADE_TIME;

	if ( m_pLeftHandGlow )
	{
		m_pLeftHandGlow->SetScale( 0, 0.5f );
		m_pLeftHandGlow->FadeAndDie( 0.5f );
	}

	if ( m_pRightHandGlow )
	{
		m_pRightHandGlow->SetScale( 0, 0.5f );
		m_pRightHandGlow->FadeAndDie( 0.5f );
	}
}

//-----------------------------------------------------------------------------
// Purpose: vortigaunt shoots at noisy body target (fixes problems in cover, etc)
//			NOTE: His weapon doesn't have any error
// Input  :
// Output :
//-----------------------------------------------------------------------------
Vector CNPC_Vortigaunt::GetShootEnemyDir( const Vector &shootOrigin, bool bNoisy )
{
	CBaseEntity *pEnemy = GetEnemy();

	if ( pEnemy )
	{
		return (pEnemy->BodyTarget(shootOrigin, bNoisy)-shootOrigin);
	}
	else
	{
		Vector forward;
		AngleVectors( GetLocalAngles(), &forward );
		return forward;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CNPC_Vortigaunt::IsValidEnemy( CBaseEntity *pEnemy )
{
	if ( IsRoller( pEnemy ) )
	{
		CAI_BaseNPC *pNPC = pEnemy->MyNPCPointer();
		if ( pNPC && pNPC->GetEnemy() != NULL )
			return true;
		return false;
	}

	return BaseClass::IsValidEnemy( pEnemy );
}

//=========================================================
// BeamGlow - brighten all beams
//=========================================================
void CNPC_Vortigaunt::BeamGlow( )
{
	int b = m_iBeams * 32;
	if (b > 255)
		b = 255;

	for (int i = 0; i < m_iBeams; i++)
	{
		if (m_pBeam[i]->GetBrightness() != 255) 
		{
			m_pBeam[i]->SetBrightness( b );
		}
	}
}


//=========================================================
// WackBeam - regenerate dead colleagues
//=========================================================
void CNPC_Vortigaunt::WackBeam( int side, CBaseEntity *pEntity )
{
	Vector vecDest;
	
	if (m_iBeams >= VORTIGAUNT_MAX_BEAMS)
		return;

	if (pEntity == NULL)
		return;

	m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 3.0 );
	if (!m_pBeam[m_iBeams])
		return;

	m_pBeam[m_iBeams]->PointEntInit( pEntity->WorldSpaceCenter(), this );
	m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? m_iLeftHandAttachment : m_iRightHandAttachment );
	m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
	m_pBeam[m_iBeams]->SetBrightness( 255 );
	m_pBeam[m_iBeams]->SetNoise( 12 );
	m_iBeams++;
}

//=========================================================
// ZapBeam - heavy damage directly forward
//=========================================================
void CNPC_Vortigaunt::ZapBeam( int side )
{
	Vector vecSrc, vecAim;
	trace_t tr;
	CBaseEntity *pEntity;

	if (m_iBeams >= VORTIGAUNT_MAX_BEAMS)
		return;

	Vector forward, right, up;
	AngleVectors( GetLocalAngles(), &forward, &right, &up );

	vecSrc = GetLocalOrigin() + up * 36;
	vecAim = GetShootEnemyDir( vecSrc );
	float deflection = 0.01;
	vecAim = vecAim + side * right * random->RandomFloat( 0, deflection ) + up * random->RandomFloat( -deflection, deflection );

	if ( m_bExtractingBugbait == true )
	{
		CRagdollProp *pTest = dynamic_cast< CRagdollProp *>( GetTarget() );

		if ( pTest )
		{
			ragdoll_t *m_ragdoll = pTest->GetRagdoll();

			if ( m_ragdoll )
			{
				Vector vOrigin;
				m_ragdoll->list[0].pObject->GetPosition( &vOrigin, 0 );

				AI_TraceLine ( vecSrc, vOrigin, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
			}

			CRagdollBoogie::Create( pTest, 200, gpGlobals->curtime, 1.0f );
		}
	}
	else
	{
		AI_TraceLine ( vecSrc, vecSrc + vecAim * 1024, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
	}

	m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 5.0 );
	if (!m_pBeam[m_iBeams])
		return;

	m_pBeam[m_iBeams]->PointEntInit( tr.endpos, this );
	m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? m_iLeftHandAttachment : m_iRightHandAttachment );
	m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
	m_pBeam[m_iBeams]->SetBrightness( 255 );
	m_pBeam[m_iBeams]->SetNoise( 3 );
	m_iBeams++;

	pEntity = tr.m_pEnt;
	if (pEntity != NULL && m_takedamage)
	{
		CTakeDamageInfo dmgInfo( this, this, sk_vortigaunt_dmg_zap.GetFloat(), DMG_SHOCK );
		dmgInfo.SetDamagePosition( tr.endpos );
		VectorNormalize( vecAim );// not a unit vec yet
		// hit like a 5kg object flying 400 ft/s
		dmgInfo.SetDamageForce( 5 * 400 * 12 * vecAim );
		pEntity->DispatchTraceAttack( dmgInfo, vecAim, &tr );
	}
}

//------------------------------------------------------------------------------
// Purpose :
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CNPC_Vortigaunt::HealBeam( int side )
{
	if (m_iBeams >= VORTIGAUNT_MAX_BEAMS)
		return;

	Vector forward, right, up;
	AngleVectors( GetLocalAngles(), &forward, &right, &up );

	trace_t tr;
	Vector vecSrc		= GetLocalOrigin() + up * 36;

	m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 5.0 );
	if (!m_pBeam[m_iBeams])
		return;

	m_pBeam[m_iBeams]->EntsInit( GetTarget(), this );
	m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? m_iLeftHandAttachment : m_iRightHandAttachment );
	m_pBeam[m_iBeams]->SetColor( 0, 0, 255 );

	m_pBeam[m_iBeams]->SetBrightness( 255 );
	m_pBeam[m_iBeams]->SetNoise( 6.5 );
	m_iBeams++;
}

//------------------------------------------------------------------------------
// Purpose : Clear Glow
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CNPC_Vortigaunt::ClearHandGlow( )
{
	if (m_pLeftHandGlow)
	{
		UTIL_Remove( m_pLeftHandGlow );
		m_pLeftHandGlow = NULL;
	}
	if (m_pRightHandGlow)
	{
		UTIL_Remove( m_pRightHandGlow );
		m_pRightHandGlow = NULL;
	}
}

//=========================================================
// ClearBeams - remove all beams
//=========================================================
void CNPC_Vortigaunt::ClearBeams( )
{
	for (int i = 0; i < VORTIGAUNT_MAX_BEAMS; i++)
	{
		if (m_pBeam[i])
		{
			UTIL_Remove( m_pBeam[i] );
			m_pBeam[i] = NULL;
		}
	}

	m_iBeams = 0;
	m_nSkin = 0;

	// Stop looping suit charge sound.
	if (m_iSuitSound > 1) 
	{
		StopSound( "NPC_Vortigaunt.SuitCharge" );
		m_iSuitSound = 0;
	}


	if ( m_bStopLoopingSounds )
	{
		StopSound( "NPC_Vortigaunt.StartHealLoop" );
		StopSound( "NPC_Vortigaunt.StartShootLoop" );
		StopSound( "NPC_Vortigaunt.SuitCharge" );
		StopSound( "NPC_Vortigaunt.Shoot" );
		StopSound( "NPC_Vortigaunt.ZapPowerup" );
		m_bStopLoopingSounds = false;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &data - 
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::InputEnableArmorRecharge( inputdata_t &data )
{
	m_bArmorRechargeEnabled = true;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &data - 
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::InputDisableArmorRecharge( inputdata_t &data )
{
	m_bArmorRechargeEnabled = false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &data - 
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::InputChargeTarget( inputdata_t &data )
{
	CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, data.value.String(), data.pActivator );

	// Must be valid
	if ( pTarget == NULL )
	{
		DevMsg( 1, "Unable to charge from unknown entity: %s!\n", data.value.String() );
		return;
	}

	int playerArmor = (pTarget->IsPlayer()) ? ((CBasePlayer *)pTarget)->ArmorValue() : 0;

	if ( playerArmor >= 100 || ( pTarget->GetFlags() & FL_NOTARGET ) )
	{
		m_OnFinishedChargingTarget.FireOutput( this, this );
		return;
	}

	// Keep this as our target
	SetTarget( pTarget );
	m_iCurrentRechargeGoal = playerArmor + sk_vortigaunt_armor_charge.GetInt();
	if ( m_iCurrentRechargeGoal > 100 )
		m_iCurrentRechargeGoal = 100;
	m_bForceArmorRecharge = true;
	SetCondition( COND_PROVOKED );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &data - 
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::InputExtractBugbait( inputdata_t &data )
{
	CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, data.value.String(), data.pActivator );

	// Must be valid
	if ( pTarget == NULL )
	{
		DevMsg( 1, "Unable to extract bugbait from unknown entity %s!\n", data.value.String() );
		return;
	}

	// Keep this as our target
	SetTarget( pTarget );

	// Start to extract
	m_bExtractingBugbait = true;
	SetSchedule( SCHED_VORTIGAUNT_EXTRACT_BUGBAIT );
}

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

AI_BEGIN_CUSTOM_NPC( npc_vortigaunt, CNPC_Vortigaunt )

	DECLARE_USES_SCHEDULE_PROVIDER( CAI_LeadBehavior )

	DECLARE_TASK(TASK_VORTIGAUNT_HEAL_WARMUP)
	DECLARE_TASK(TASK_VORTIGAUNT_HEAL)
	DECLARE_TASK(TASK_VORTIGAUNT_FACE_STOMP)
	DECLARE_TASK(TASK_VORTIGAUNT_STOMP_ATTACK)
	DECLARE_TASK(TASK_VORTIGAUNT_GRENADE_KILL)
	DECLARE_TASK(TASK_VORTIGAUNT_ZAP_GRENADE_OWNER)
	DECLARE_TASK(TASK_VORTIGAUNT_EXTRACT)
	DECLARE_TASK(TASK_VORTIGAUNT_FIRE_EXTRACT_OUTPUT)
	DECLARE_TASK(TASK_VORTIGAUNT_WAIT_FOR_PLAYER)

	DECLARE_TASK( TASK_VORTIGAUNT_EXTRACT_WARMUP )
	DECLARE_TASK( TASK_VORTIGAUNT_EXTRACT_COOLDOWN )

	DECLARE_ACTIVITY(ACT_VORTIGAUNT_AIM)
	DECLARE_ACTIVITY(ACT_VORTIGAUNT_START_HEAL)
	DECLARE_ACTIVITY(ACT_VORTIGAUNT_HEAL_LOOP)
	DECLARE_ACTIVITY(ACT_VORTIGAUNT_END_HEAL)
	DECLARE_ACTIVITY(ACT_VORTIGAUNT_TO_ACTION)
	DECLARE_ACTIVITY(ACT_VORTIGAUNT_TO_IDLE)
	DECLARE_ACTIVITY(ACT_VORTIGAUNT_STOMP)
	DECLARE_ACTIVITY(ACT_VORTIGAUNT_DEFEND)
	DECLARE_ACTIVITY(ACT_VORTIGAUNT_TO_DEFEND)
	DECLARE_ACTIVITY(ACT_VORTIGAUNT_FROM_DEFEND)

	DECLARE_CONDITION(COND_VORTIGAUNT_CAN_HEAL)

	DECLARE_INTERACTION( g_interactionVortigauntStomp )
	DECLARE_INTERACTION( g_interactionVortigauntStompFail )
	DECLARE_INTERACTION( g_interactionVortigauntStompHit )
	DECLARE_INTERACTION( g_interactionVortigauntKick )
	DECLARE_INTERACTION( g_interactionVortigauntClaw )

	DECLARE_ANIMEVENT( AE_VORTIGAUNT_CLAW_LEFT )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_CLAW_RIGHT )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_ZAP_POWERUP )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_ZAP_SHOOT )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_ZAP_DONE )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_HEAL_STARTGLOW )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_HEAL_STARTBEAMS )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_KICK )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_STOMP )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_HEAL_STARTSOUND )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_SWING_SOUND )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_SHOOT_SOUNDSTART )
	DECLARE_ANIMEVENT( AE_VORTIGAUNT_DEFEND_BEAMS )

	//=========================================================
	// > SCHED_VORTIGAUNT_RANGE_ATTACK
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_VORTIGAUNT_RANGE_ATTACK,

		"	Tasks"
		"		TASK_STOP_MOVING				0"
		"		TASK_FACE_IDEAL					0"
		"		TASK_RANGE_ATTACK1				0"
		"		TASK_WAIT						0.2" // Wait a sec before killing beams
		""
		"	Interrupts"
		"		COND_CAN_MELEE_ATTACK1"
		"		COND_HEAVY_DAMAGE"
		"		COND_HEAR_DANGER"
	);

	//=========================================================
	// > SCHED_VORTIGAUNT_MELEE_ATTACK
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_VORTIGAUNT_MELEE_ATTACK,

		"	Tasks"
		"		TASK_STOP_MOVING					0"
		"		TASK_SET_ACTIVITY					ACTIVITY:ACT_IDLE"
		"		TASK_FACE_ENEMY						0"
		"		TASK_MELEE_ATTACK1					0"
		""
		"	Interrupts"
		"		COND_ENEMY_DEAD"
		"		COND_HEAVY_DAMAGE"
		"		COND_ENEMY_OCCLUDED"
	);

	//=========================================================
	// > SCHED_VORTIGAUNT_STOMP_ATTACK
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_VORTIGAUNT_STOMP_ATTACK,

		"	Tasks"
		"		TASK_STOP_MOVING					0"
		"		TASK_SET_ACTIVITY					ACTIVITY:ACT_IDLE"
		"		TASK_VORTIGAUNT_FACE_STOMP			0"
		"		TASK_VORTIGAUNT_STOMP_ATTACK		0"
		""
		"	Interrupts"
				// New_Enemy	Don't interrupt, finish current attack first
		"		COND_ENEMY_DEAD"
		"		COND_HEAVY_DAMAGE"
		"		COND_ENEMY_OCCLUDED"
	);

	//=========================================================
	// > SCHED_VORTIGAUNT_GRENADE_KILL
	//
	// Zap an incoming grenade (GetTarget())
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_VORTIGAUNT_GRENADE_KILL,

		"	Tasks"
		"		TASK_VORTIGAUNT_GRENADE_KILL		0"
		"		TASK_WAIT							0.3"
		"		TASK_VORTIGAUNT_ZAP_GRENADE_OWNER	0"
		""
		"	Interrupts"
		"		COND_LIGHT_DAMAGE"
	);
	//=========================================================
	// > SCHED_VORTIGAUNT_HEAL
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_VORTIGAUNT_HEAL,

		"	Tasks"
		"		TASK_STOP_MOVING				0"
		"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_VORTIGAUNT_STAND"
		"		TASK_GET_PATH_TO_TARGET			0"
		"		TASK_MOVE_TO_TARGET_RANGE		128"				// Move within 128 of target ent (client)
		"		TASK_FACE_PLAYER				0"
		"		TASK_SPEAK_SENTENCE				0"					// VORT_HEAL_SENTENCE
		"		TASK_VORTIGAUNT_HEAL_WARMUP		0"
		"		TASK_VORTIGAUNT_HEAL			0"
		"		TASK_SPEAK_SENTENCE				1"					// VORT_DONE_HEAL_SENTENCE
		""
		"	Interrupts"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
	);

	//=========================================================
	// > SCHED_VORTIGAUNT_STAND
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_VORTIGAUNT_STAND,

		"	Tasks"
		"		TASK_STOP_MOVING					0"
		"		TASK_SET_ACTIVITY					ACTIVITY:ACT_IDLE"
		"		TASK_WAIT							2"					// repick IDLESTAND every two seconds."
		"		TASK_TALKER_HEADRESET				0"					// reset head position"
		""
		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
		"		COND_SMELL"
		"		COND_PROVOKED"
		"		COND_HEAR_COMBAT"
		"		COND_HEAR_DANGER"
	);

	//=========================================================
	// > SCHED_VORTIGAUNT_BARNACLE_HIT
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_VORTIGAUNT_BARNACLE_HIT,

		"	Tasks"
		"		TASK_STOP_MOVING			0"
		"		TASK_PLAY_SEQUENCE			ACTIVITY:ACT_BARNACLE_HIT"
		"		TASK_SET_SCHEDULE			SCHEDULE:SCHED_VORTIGAUNT_BARNACLE_PULL"
		""
		"	Interrupts"
	);

	//=========================================================
	// > SCHED_VORTIGAUNT_BARNACLE_PULL
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_VORTIGAUNT_BARNACLE_PULL,

		"	Tasks"
		"		 TASK_PLAY_SEQUENCE			ACTIVITY:ACT_BARNACLE_PULL"
		""
		"	Interrupts"
	);

	//=========================================================
	// > SCHED_VORTIGAUNT_BARNACLE_CHOMP
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_VORTIGAUNT_BARNACLE_CHOMP,

		"	Tasks"
		"		TASK_PLAY_SEQUENCE			ACTIVITY:ACT_BARNACLE_CHOMP"
		"		TASK_SET_SCHEDULE			SCHEDULE:SCHED_VORTIGAUNT_BARNACLE_CHEW"
		""
		"	Interrupts"
	);

	//=========================================================
	// > SCHED_VORTIGAUNT_BARNACLE_CHEW
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_VORTIGAUNT_BARNACLE_CHEW,

		"	Tasks"
		"		TASK_PLAY_SEQUENCE			ACTIVITY:ACT_BARNACLE_CHEW"
	)

	//=========================================================
	// > SCHED_VORTIGAUNT_EXTRACT_BUGBAIT
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_VORTIGAUNT_EXTRACT_BUGBAIT,

		"	Tasks"
		"		TASK_SET_FAIL_SCHEDULE				SCHEDULE:SCHED_VORTIGAUNT_STAND"
		"		TASK_STOP_MOVING					0"
		"		TASK_GET_PATH_TO_TARGET				0"
		"		TASK_MOVE_TO_TARGET_RANGE			128"				// Move within 128 of target ent (client)
		"		TASK_VORTIGAUNT_WAIT_FOR_PLAYER		0"
		"		TASK_SPEAK_SENTENCE					2"					// Start extracting sentence
		"		TASK_WAIT_FOR_SPEAK_FINISH			1"
		"		TASK_FACE_TARGET					0"
		"		TASK_WAIT_FOR_SPEAK_FINISH			1"
		"		TASK_VORTIGAUNT_EXTRACT_WARMUP		0"
		"		TASK_VORTIGAUNT_EXTRACT				0"
		"		TASK_VORTIGAUNT_EXTRACT_COOLDOWN	0"
		"		TASK_VORTIGAUNT_FIRE_EXTRACT_OUTPUT	0"
		"		TASK_SPEAK_SENTENCE					3"					// Finish extracting sentence
		"		TASK_WAIT_FOR_SPEAK_FINISH			1"
		"		TASK_WAIT							2"
		""
		"	Interrupts"
	)
	
AI_END_CUSTOM_NPC()
