//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
//			events 
//
// $Workfile:     $
// $Date:         $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================

#include	"cbase.h"
#include	"beam_shared.h"
#include	"AI_Default.h"
#include	"AI_Task.h"
#include	"AI_Schedule.h"
#include	"AI_Node.h"
#include	"AI_Hull.h"
#include	"AI_Hint.h"
#include	"AI_Route.h"
#include	"AI_Squad.h"
#include	"AI_SquadSlot.h"
#include	"AI_Motor.h"
#include	"hl1_npc_hgrunt.h"
#include	"soundent.h"
#include	"game.h"
#include	"NPCEvent.h"
#include	"EntityList.h"
#include	"activitylist.h"
#include	"animation.h"
#include	"engine/IEngineSound.h"
#include	"ammodef.h"
#include	"basecombatweapon.h"
#include	"hl1_basegrenade.h"
#include	"customentity.h"
#include	"ai_interactions.h"
#include	"scripted.h"
#include	"hl1_basegrenade.h"
#include	"hl1_grenade_mp5.h"

ConVar	sk_hgrunt_health( "sk_hgrunt_health","0");
ConVar  sk_hgrunt_kick ( "sk_hgrunt_kick", "0" );
ConVar  sk_hgrunt_pellets ( "sk_hgrunt_pellets", "0" );
ConVar  sk_hgrunt_gspeed ( "sk_hgrunt_gspeed", "0" );

extern ConVar sk_plr_dmg_grenade;
extern ConVar sk_plr_dmg_mp5_grenade;

#define SF_GRUNT_LEADER	( 1 << 5  )

int g_fGruntQuestion;				// true if an idle grunt asked a question. Cleared when someone answers.
int g_iSquadIndex = 0;

#define HGRUNT_GUN_SPREAD 0.08716f

//=========================================================
// monster-specific DEFINE's
//=========================================================
#define	GRUNT_CLIP_SIZE					36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
#define GRUNT_VOL						0.35		// volume of grunt sounds
#define GRUNT_SNDLVL					SNDLVL_NORM	// soundlevel of grunt sentences
#define HGRUNT_LIMP_HEALTH				20
#define HGRUNT_DMG_HEADSHOT				( DMG_BULLET | DMG_CLUB )	// damage types that can kill a grunt with a single headshot.
#define HGRUNT_NUM_HEADS				2 // how many grunt heads are there? 
#define HGRUNT_MINIMUM_HEADSHOT_DAMAGE	15 // must do at least this much damage in one shot to head to score a headshot kill
#define	HGRUNT_SENTENCE_VOLUME			(float)0.35 // volume of grunt sentences

#define HGRUNT_9MMAR				( 1 << 0)
#define HGRUNT_HANDGRENADE			( 1 << 1)
#define HGRUNT_GRENADELAUNCHER		( 1 << 2)
#define HGRUNT_SHOTGUN				( 1 << 3)

#define HEAD_GROUP					1
#define HEAD_GRUNT					0
#define HEAD_COMMANDER				1
#define HEAD_SHOTGUN				2
#define HEAD_M203					3
#define GUN_GROUP					2
#define GUN_MP5						0
#define GUN_SHOTGUN					1
#define GUN_NONE					2

//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define		HGRUNT_AE_RELOAD		( 2 )
#define		HGRUNT_AE_KICK			( 3 )
#define		HGRUNT_AE_BURST1		( 4 )
#define		HGRUNT_AE_BURST2		( 5 ) 
#define		HGRUNT_AE_BURST3		( 6 ) 
#define		HGRUNT_AE_GREN_TOSS		( 7 )
#define		HGRUNT_AE_GREN_LAUNCH	( 8 )
#define		HGRUNT_AE_GREN_DROP		( 9 )
#define		HGRUNT_AE_CAUGHT_ENEMY	( 10) // grunt established sight with an enemy (player only) that had previously eluded the squad.
#define		HGRUNT_AE_DROP_GUN		( 11) // grunt (probably dead) is dropping his mp5.


const char *CNPC_HGrunt::pGruntSentences[] = 
{
	"HG_GREN", // grenade scared grunt
	"HG_ALERT", // sees player
	"HG_MONSTER", // sees monster
	"HG_COVER", // running to cover
	"HG_THROW", // about to throw grenade
	"HG_CHARGE",  // running out to get the enemy
	"HG_TAUNT", // say rude things
};

enum
{
	HGRUNT_SENT_NONE = -1,
	HGRUNT_SENT_GREN = 0,
	HGRUNT_SENT_ALERT,
	HGRUNT_SENT_MONSTER,
	HGRUNT_SENT_COVER,
	HGRUNT_SENT_THROW,
	HGRUNT_SENT_CHARGE,
	HGRUNT_SENT_TAUNT,
} HGRUNT_SENTENCE_TYPES;

LINK_ENTITY_TO_CLASS( monster_human_grunt, CNPC_HGrunt );

//=========================================================
// monster-specific schedule types
//=========================================================
enum
{
	SCHED_GRUNT_FAIL = LAST_SHARED_SCHEDULE,
	SCHED_GRUNT_COMBAT_FAIL,
	SCHED_GRUNT_VICTORY_DANCE,
	SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE,
	SCHED_GRUNT_FOUND_ENEMY,
	SCHED_GRUNT_COMBAT_FACE,
	SCHED_GRUNT_SIGNAL_SUPPRESS,
	SCHED_GRUNT_SUPPRESS,
	SCHED_GRUNT_WAIT_IN_COVER,
	SCHED_GRUNT_TAKE_COVER,
	SCHED_GRUNT_GRENADE_COVER,
	SCHED_GRUNT_TOSS_GRENADE_COVER,
	SCHED_GRUNT_HIDE_RELOAD,
	SCHED_GRUNT_SWEEP,
	SCHED_GRUNT_RANGE_ATTACK1A,
	SCHED_GRUNT_RANGE_ATTACK1B,
	SCHED_GRUNT_RANGE_ATTACK2,
	SCHED_GRUNT_REPEL,
	SCHED_GRUNT_REPEL_ATTACK,
	SCHED_GRUNT_REPEL_LAND,
	SCHED_GRUNT_TAKE_COVER_FAILED,
	SCHED_GRUNT_RELOAD,
	SCHED_GRUNT_TAKE_COVER_FROM_ENEMY,
	SCHED_GRUNT_BARNACLE_HIT,
	SCHED_GRUNT_BARNACLE_PULL,
	SCHED_GRUNT_BARNACLE_CHOMP,
	SCHED_GRUNT_BARNACLE_CHEW,
};

//=========================================================
// monster-specific tasks
//=========================================================
enum 
{
	TASK_GRUNT_FACE_TOSS_DIR = LAST_SHARED_TASK + 1,
	TASK_GRUNT_SPEAK_SENTENCE,
	TASK_GRUNT_CHECK_FIRE,
};


//=========================================================
// monster-specific conditions
//=========================================================
enum
{
	COND_GRUNT_NOFIRE = LAST_SHARED_CONDITION + 1,
};

// -----------------------------------------------
//	> Squad slots
// -----------------------------------------------
enum SquadSlot_T
{	
	SQUAD_SLOT_GRENADE1 = LAST_SHARED_SQUADSLOT,
	SQUAD_SLOT_GRENADE2,
	SQUAD_SLOT_ENGAGE1,
	SQUAD_SLOT_ENGAGE2,
};


int ACT_GRUNT_LAUNCH_GRENADE;
int ACT_GRUNT_TOSS_GRENADE;
int ACT_GRUNT_MP5_STANDING;
int ACT_GRUNT_MP5_CROUCHING;
int ACT_GRUNT_SHOTGUN_STANDING;
int ACT_GRUNT_SHOTGUN_CROUCHING;

//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_HGrunt )
	DEFINE_FIELD( CNPC_HGrunt, m_flNextGrenadeCheck, FIELD_TIME ),
	DEFINE_FIELD( CNPC_HGrunt, m_flNextPainTime, FIELD_TIME ),
	DEFINE_FIELD( CNPC_HGrunt, m_flCheckAttackTime, FIELD_FLOAT ),
	DEFINE_FIELD( CNPC_HGrunt, m_vecTossVelocity, FIELD_VECTOR ),
	DEFINE_FIELD( CNPC_HGrunt, m_iLastGrenadeCondition, FIELD_INTEGER ),
	DEFINE_FIELD( CNPC_HGrunt, m_fStanding, FIELD_BOOLEAN ),
	DEFINE_FIELD( CNPC_HGrunt, m_fFirstEncounter, FIELD_BOOLEAN ),
	DEFINE_FIELD( CNPC_HGrunt, m_iClipSize, FIELD_INTEGER ),
	DEFINE_FIELD( CNPC_HGrunt, m_voicePitch, FIELD_INTEGER ),
	DEFINE_FIELD( CNPC_HGrunt, m_iSentence, FIELD_INTEGER ),
	DEFINE_KEYFIELD( CNPC_HGrunt, m_iWeapons, FIELD_INTEGER, "weapons" ),
	DEFINE_KEYFIELD( CNPC_HGrunt, m_SquadName, FIELD_STRING, "netname" ),
END_DATADESC()


//=========================================================
// Spawn
//=========================================================
void CNPC_HGrunt::Spawn()
{
	Precache( );

	SetModel( "models/hgrunt.mdl" );

	SetHullType(HULL_HUMAN);
	SetHullSizeNormal();

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );
	SetMoveType( MOVETYPE_STEP );
	m_bloodColor		= BLOOD_COLOR_RED;
	m_fEffects			= 0;
	m_iHealth			= sk_hgrunt_health.GetFloat();
	m_flFieldOfView		= 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
	m_NPCState			= NPC_STATE_NONE;
	m_flNextGrenadeCheck = gpGlobals->curtime + 1;
	m_flNextPainTime	= gpGlobals->curtime;
	m_iSentence			= HGRUNT_SENT_NONE;

	CapabilitiesClear();
	CapabilitiesAdd ( bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP | bits_CAP_MOVE_GROUND );

	CapabilitiesAdd(bits_CAP_INNATE_RANGE_ATTACK1 );

	// Innate range attack for grenade
	CapabilitiesAdd(bits_CAP_INNATE_RANGE_ATTACK2 );
	// Innate range attack for kicking
	CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1 );
			
	m_fFirstEncounter	= true;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.

	m_HackedGunPos = Vector ( 0, 0, 55 );

	if ( m_iWeapons == 0)
	{
		// initialize to original values
		m_iWeapons = HGRUNT_9MMAR | HGRUNT_HANDGRENADE;
		// pev->weapons = HGRUNT_SHOTGUN;
		// pev->weapons = HGRUNT_9MMAR | HGRUNT_GRENADELAUNCHER;
	}

	if (FBitSet( m_iWeapons, HGRUNT_SHOTGUN ))
	{
		SetBodygroup( GUN_GROUP, GUN_SHOTGUN );
		m_iClipSize		= 8;
	}
	else
	{
		m_iClipSize		= GRUNT_CLIP_SIZE;
	}
	m_cAmmoLoaded		= m_iClipSize;

	if ( random->RandomInt( 0, 99 ) < 80)
		m_nSkin = 0;	// light skin
	else
		m_nSkin = 1;	// dark skin

	if (FBitSet( m_iWeapons, HGRUNT_SHOTGUN ))
	{
		SetBodygroup( HEAD_GROUP, HEAD_SHOTGUN);
	}
	else if (FBitSet( m_iWeapons, HGRUNT_GRENADELAUNCHER ))
	{
		SetBodygroup( HEAD_GROUP, HEAD_M203 );
		m_nSkin = 1; // alway dark skin
	}

	m_flTalkWaitTime = 0;


	//HACK
	g_iSquadIndex = 0;

	BaseClass::Spawn();

	NPCInit();
}

int CNPC_HGrunt::IRelationPriority( CBaseEntity *pTarget )
{
	//I hate alien grunts more than anything.
	if ( pTarget->Classify() == CLASS_ALIEN_MILITARY )
	{
		if ( FClassnameIs( pTarget, "monster_alien_grunt" ) )
		{
			 return ( BaseClass::IRelationPriority ( pTarget ) + 1 );
		}
	}

	return BaseClass::IRelationPriority( pTarget );
}

//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CNPC_HGrunt::Precache()
{
	m_iAmmoType = GetAmmoDef()->Index("9mmRound");

	engine->PrecacheModel("models/hgrunt.mdl");

	enginesound->PrecacheSound( "hgrunt/gr_mgun1.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_mgun2.wav" );
	
	enginesound->PrecacheSound( "hgrunt/gr_die1.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_die2.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_die3.wav" );

	enginesound->PrecacheSound( "hgrunt/gr_pain1.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_pain2.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_pain3.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_pain4.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_pain5.wav" );

	enginesound->PrecacheSound( "hgrunt/gr_reload1.wav" );

	enginesound->PrecacheSound( "weapons/glauncher.wav" );

	enginesound->PrecacheSound( "weapons/sbarrel1.wav" );

	enginesound->PrecacheSound("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event

	// get voice pitch
	if ( random->RandomInt(0,1))
		m_voicePitch = 109 + random->RandomInt(0,7);
	else
		m_voicePitch = 100;

	m_iBrassShell = engine->PrecacheModel ("models/shell.mdl");// brass shell
	m_iShotgunShell = engine->PrecacheModel ("models/shotgunshell.mdl");

	BaseClass::Precache();

	UTIL_PrecacheOther( "grenade_hand" );
	UTIL_PrecacheOther( "grenade_mp5" );
}	

//=========================================================
// someone else is talking - don't speak
//=========================================================
bool CNPC_HGrunt::FOkToSpeak( void )
{
// if someone else is talking, don't speak
	if ( gpGlobals->curtime <= m_flTalkWaitTime )
		 return FALSE;

	if ( m_spawnflags & SF_NPC_GAG )
	{
		if ( m_NPCState != NPC_STATE_COMBAT )
		{
			// no talking outside of combat if gagged.
			return FALSE;
		}
	}

	return TRUE;
}


//=========================================================
// Speak Sentence - say your cued up sentence.
//
// Some grunt sentences (take cover and charge) rely on actually
// being able to execute the intended action. It's really lame
// when a grunt says 'COVER ME' and then doesn't move. The problem
// is that the sentences were played when the decision to TRY
// to move to cover was made. Now the sentence is played after 
// we know for sure that there is a valid path. The schedule
// may still fail but in most cases, well after the grunt has 
// started moving.
//=========================================================
void CNPC_HGrunt::SpeakSentence( void )
{
	if ( m_iSentence == HGRUNT_SENT_NONE )
	{
		// no sentence cued up.
		return; 
	}

	if ( FOkToSpeak() )
	{
		SENTENCEG_PlayRndSz( edict(), pGruntSentences[ m_iSentence ], HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
		JustSpoke();
	}
}

//=========================================================
//=========================================================
void CNPC_HGrunt::JustSpoke( void )
{
	m_flTalkWaitTime = gpGlobals->curtime + random->RandomFloat( 1.5f, 2.0f );
	m_iSentence = HGRUNT_SENT_NONE;
}

//=========================================================
// PrescheduleThink - this function runs after conditions
// are collected and before scheduling code is run.
//=========================================================
void CNPC_HGrunt::PrescheduleThink ( void )
{
	BaseClass::PrescheduleThink();
	
	if ( m_pSquad && GetEnemy() != NULL )
	{
		if ( m_pSquad->GetLeader() == NULL )
			 return;

		CNPC_HGrunt *pSquadLeader = (CNPC_HGrunt*)m_pSquad->GetLeader()->MyNPCPointer();

		if ( pSquadLeader == NULL )
			 return; //Paranoid, so making sure it's ok.		
		
		if ( HasCondition ( COND_SEE_ENEMY ) )
		{
			// update the squad's last enemy sighting time.
			pSquadLeader->m_flLastEnemySightTime = gpGlobals->curtime;
		}
		else
		{
			if ( gpGlobals->curtime - pSquadLeader->m_flLastEnemySightTime > 5 )
			{
				// been a while since we've seen the enemy
				pSquadLeader->GetEnemies()->MarkAsEluded( GetEnemy() );
			}
		}
	}
}

Class_T	CNPC_HGrunt::Classify ( void )
{
	return CLASS_HUMAN_MILITARY;
}

//=========================================================
//
// SquadRecruit(), get some monsters of my classification and
// link them as a group.  returns the group size
//
//=========================================================
int CNPC_HGrunt::SquadRecruit( int searchRadius, int maxMembers )
{
	int squadCount;
	int iMyClass = Classify();// cache this monster's class

	if ( maxMembers < 2 )
		 return 0;

	// I am my own leader
	squadCount = 1;
	
	CBaseEntity *pEntity = NULL;

	if ( m_SquadName != NULL_STRING )
	{
		// I have a netname, so unconditionally recruit everyone else with that name.
		pEntity = gEntList.FindEntityByClassname( pEntity, "monster_human_grunt" );

		while ( pEntity )
		{
			CNPC_HGrunt *pRecruit = (CNPC_HGrunt*)pEntity->MyNPCPointer();

			if ( pRecruit )
			{
				if ( !pRecruit->m_pSquad && pRecruit->Classify() == iMyClass && pRecruit != this )
				{
					// minimum protection here against user error.in worldcraft. 
					if ( pRecruit->m_SquadName != NULL_STRING && FStrEq( STRING( m_SquadName ), STRING( pRecruit->m_SquadName ) ) )
					{
						pRecruit->InitSquad();
						squadCount++;
					}
				}
			}
	
			pEntity = gEntList.FindEntityByClassname( pEntity, "monster_human_grunt" );
		}

		return squadCount;
	}
	else
	{
		char szSquadName[64];
		Q_snprintf( szSquadName, sizeof( szSquadName ), "squad%d\n", g_iSquadIndex );

		m_SquadName = MAKE_STRING( szSquadName );

		while ( ( pEntity = gEntList.FindEntityInSphere( pEntity, GetAbsOrigin(), searchRadius ) ) != NULL )
		{
			if ( !FClassnameIs ( pEntity, "monster_human_grunt" ) )
				  continue;

			CNPC_HGrunt *pRecruit = (CNPC_HGrunt*)pEntity->MyNPCPointer();

			if ( pRecruit && pRecruit != this && pRecruit->IsAlive() && !pRecruit->m_hCine )
			{
				// Can we recruit this guy?
				if ( !pRecruit->m_pSquad && pRecruit->Classify() == iMyClass &&
				   ( (iMyClass != CLASS_ALIEN_MONSTER) || FClassnameIs( this, pRecruit->GetClassname() ) ) &&
					!pRecruit->m_SquadName )
				{
					trace_t tr;
					UTIL_TraceLine( GetAbsOrigin() + GetViewOffset(), pRecruit->GetAbsOrigin() + GetViewOffset(), MASK_NPCSOLID_BRUSHONLY, pRecruit, COLLISION_GROUP_NONE, &tr );// try to hit recruit with a traceline.

					if ( tr.fraction == 1.0 )
					{
						//We're ready to recruit people, so start a squad if I don't have one.
						if ( !m_pSquad )
						{
							InitSquad();
						}

						pRecruit->m_SquadName = m_SquadName;

						pRecruit->CapabilitiesAdd ( bits_CAP_SQUAD );
						pRecruit->InitSquad();

						squadCount++;
					}
				}
			}
		}

		if ( squadCount > 1 )
		{
			 g_iSquadIndex++;
		}
	}

	return squadCount;
}

void CNPC_HGrunt::StartNPC ( void )
{
	if ( !m_pSquad )
	{
		if ( m_SquadName != NULL_STRING )
		{
			// if I have a groupname, I can only recruit if I'm flagged as leader
			if ( GetSpawnFlags() & SF_GRUNT_LEADER )
			{
				InitSquad();

				// try to form squads now.
				int iSquadSize = SquadRecruit( 1024, 4 );

				if ( iSquadSize )
				{
				  Msg ( "Squad of %d %s formed\n", iSquadSize, GetClassname() );
				}
			}
			else
			{

				//Hacky.
				//Revisit me later.
				const char *pSquadName = STRING( m_SquadName );

				m_SquadName = NULL_STRING;

				BaseClass::StartNPC();

				m_SquadName = MAKE_STRING( pSquadName );

				return;
			}
		}
		else
		{
			int iSquadSize = SquadRecruit( 1024, 4 );

			if ( iSquadSize )
			{
			  Msg ( "Squad of %d %s formed\n", iSquadSize, GetClassname() );
			}
		}
	}

	BaseClass::StartNPC();

	if ( m_pSquad && m_pSquad->IsLeader( this ) )
	{
		SetBodygroup( 1, 1 ); // UNDONE: truly ugly hack
		m_nSkin = 0;
	}
}

//=========================================================
// CheckMeleeAttack1
//=========================================================
int CNPC_HGrunt::MeleeAttack1Conditions ( float flDot, float flDist )
{
	if (flDist > 64)
		return COND_TOO_FAR_TO_ATTACK;
	else if (flDot < 0.7)
		return COND_NOT_FACING_ATTACK;
	
	return COND_CAN_MELEE_ATTACK1;
}

//=========================================================
// CheckRangeAttack1 - overridden for HGrunt, cause 
// FCanCheckAttacks() doesn't disqualify all attacks based
// on whether or not the enemy is occluded because unlike
// the base class, the HGrunt can attack when the enemy is
// occluded (throw grenade over wall, etc). We must 
// disqualify the machine gun attack if the enemy is occluded.
//=========================================================
int CNPC_HGrunt::RangeAttack1Conditions ( float flDot, float flDist )
{
	if ( !HasCondition( COND_ENEMY_OCCLUDED ) && flDist <= 2048 && flDot >= 0.5 && NoFriendlyFire() )
	{
		trace_t	tr;

		if ( !GetEnemy()->IsPlayer() && flDist <= 64 )
		{
			// kick nonclients, but don't shoot at them.
			return COND_NONE;
		}

		Vector vecSrc;
		QAngle angAngles;

		GetAttachment( "0", vecSrc, angAngles );

		//NDebugOverlay::Line( GetAbsOrigin() + GetViewOffset(), GetEnemy()->BodyTarget(GetAbsOrigin() + GetViewOffset()), 255, 0, 0, false, 0.1 );
		// verify that a bullet fired from the gun will hit the enemy before the world.
		UTIL_TraceLine( GetAbsOrigin() + GetViewOffset(), GetEnemy()->BodyTarget(GetAbsOrigin() + GetViewOffset()), MASK_SHOT, this/*pentIgnore*/, COLLISION_GROUP_NONE, &tr);

		if ( tr.m_pEnt == GetEnemy() )
		{
			return COND_CAN_RANGE_ATTACK1;
		}
	}

	if ( !NoFriendlyFire() )
		 return COND_WEAPON_BLOCKED_BY_FRIEND; //err =|

	return COND_NONE;
}

int CNPC_HGrunt::RangeAttack2Conditions( float flDot, float flDist  )
{
	m_iLastGrenadeCondition = GetGrenadeConditions( flDot, flDist );
	return m_iLastGrenadeCondition;
}

int CNPC_HGrunt::GetGrenadeConditions( float flDot, float flDist  )
{
	if ( !FBitSet( m_iWeapons, ( HGRUNT_HANDGRENADE | HGRUNT_GRENADELAUNCHER ) ) )
		  return COND_NONE;

	// assume things haven't changed too much since last time
	if (gpGlobals->curtime < m_flNextGrenadeCheck )
		return m_iLastGrenadeCondition;
	
	if ( m_flGroundSpeed != 0 )
		return COND_NONE;

	CBaseEntity *pEnemy = GetEnemy();
	
	if (!pEnemy)
		return COND_NONE;
	
	Vector flEnemyLKP = GetEnemyLKP();
	if ( !(pEnemy->GetFlags() & FL_ONGROUND) && pEnemy->GetWaterLevel() == 0 && flEnemyLKP.z > GetAbsMaxs().z  )
	{
		//!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to 
		// be grenaded.
		// don't throw grenades at anything that isn't on the ground!
		return COND_NONE;
	}
	
	Vector vecTarget;

	if (FBitSet( m_iWeapons, HGRUNT_HANDGRENADE))
	{
		// find feet
		if ( random->RandomInt( 0,1 ) )
		{
			// magically know where they are
			vecTarget = Vector( pEnemy->GetAbsOrigin().x, pEnemy->GetAbsOrigin().y, pEnemy->GetAbsMins().z );
		}
		else
		{
			// toss it to where you last saw them
			vecTarget = flEnemyLKP;
		}
	
	}
	else
	{
		// find target
		// vecTarget = GetEnemy()->BodyTarget( GetAbsOrigin() );
		vecTarget = GetEnemy()->GetAbsOrigin() + (GetEnemy()->BodyTarget( GetAbsOrigin() ) - GetEnemy()->GetAbsOrigin());
		// estimate position
		if ( HasCondition( COND_SEE_ENEMY))
			vecTarget = vecTarget + ((vecTarget - GetAbsOrigin()).Length() / sk_hgrunt_gspeed.GetFloat()) * GetEnemy()->GetAbsVelocity();
	}

	// are any of my squad members near the intended grenade impact area?
	if ( m_pSquad )
	{
		if ( m_pSquad->SquadMemberInRange( vecTarget, 256 ) )
		{
			// crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while.
			m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
			return COND_NONE;
		}
	}
	
	if ( ( vecTarget - GetAbsOrigin() ).Length2D() <= 256 )
	{
		// crap, I don't want to blow myself up
		m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
		return COND_NONE;
	}

		
	if (FBitSet( m_iWeapons, HGRUNT_HANDGRENADE))
	{
		Vector vGunPos;
		QAngle angGunAngles;
		GetAttachment( "0", vGunPos, angGunAngles );


		Vector vecToss = VecCheckToss( this, vGunPos, vecTarget, -1, 0.5, false );

		if ( vecToss != vec3_origin )
		{
			m_vecTossVelocity = vecToss;

			// don't check again for a while.
			m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second.

			return COND_CAN_RANGE_ATTACK2;
		}
		else
		{
			// don't check again for a while.
			m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.

			return COND_NONE;
		}
	}
	else
	{
		Vector vGunPos;
		QAngle angGunAngles;
		GetAttachment( "0", vGunPos, angGunAngles );
		
		Vector vecToss = VecCheckThrow( this, vGunPos, vecTarget, sk_hgrunt_gspeed.GetFloat(), 0.5 );

		if ( vecToss != vec3_origin )
		{
			m_vecTossVelocity = vecToss;

			// don't check again for a while.
			m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second.

			return COND_CAN_RANGE_ATTACK2;
		}
		else
		{
			// don't check again for a while.
			m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.

			return COND_NONE;
		}
	}
}


//=========================================================
// FCanCheckAttacks - this is overridden for human grunts
// because they can throw/shoot grenades when they can't see their
// target and the base class doesn't check attacks if the monster
// cannot see its enemy.
//
// !!!BUGBUG - this gets called before a 3-round burst is fired
// which means that a friendly can still be hit with up to 2 rounds. 
// ALSO, grenades will not be tossed if there is a friendly in front,
// this is a bad bug. Friendly machine gun fire avoidance
// will unecessarily prevent the throwing of a grenade as well.
//=========================================================
bool CNPC_HGrunt::FCanCheckAttacks( void )
{
	// This condition set when too close to a grenade to blow it up
	if ( !HasCondition( COND_TOO_CLOSE_TO_ATTACK ) )
	{
		return true;
	}
	else
	{
		return false;
	}
}

int CNPC_HGrunt::GetSoundInterests( void )
{
	return	SOUND_WORLD	|
			SOUND_COMBAT	|
			SOUND_PLAYER	|
			SOUND_DANGER;
}


//=========================================================
// TraceAttack - make sure we're not taking it in the helmet
//=========================================================
void CNPC_HGrunt::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr )
{
	CTakeDamageInfo info = inputInfo;

	// check for helmet shot
	if (ptr->hitgroup == 11)
	{
		// make sure we're wearing one
		if ( GetBodygroup( 1 ) == HEAD_GRUNT && (info.GetDamageType() & (DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB)))
		{
			// absorb damage
			info.SetDamage( info.GetDamage() - 20 );
			if ( info.GetDamage() <= 0 )
				info.SetDamage( 0.01 );
		}
		// it's head shot anyways
		ptr->hitgroup = HITGROUP_HEAD;
	}
	BaseClass::TraceAttack( info, vecDir, ptr );
}


//=========================================================
// TakeDamage - overridden for the grunt because the grunt
// needs to forget that he is in cover if he's hurt. (Obviously
// not in a safe place anymore).
//=========================================================
int CNPC_HGrunt::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
{
	Forget( bits_MEMORY_INCOVER );

	return BaseClass::OnTakeDamage_Alive ( inputInfo );
}


//=========================================================
// SetYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
float CNPC_HGrunt::MaxYawSpeed( void )
{
	float flYS;

	switch ( GetActivity() )
	{
	case ACT_IDLE:	
		flYS = 150;		
		break;
	case ACT_RUN:	
		flYS = 150;	
		break;
	case ACT_WALK:	
		flYS = 180;		
		break;
	case ACT_RANGE_ATTACK1:	
		flYS = 120;	
		break;
	case ACT_RANGE_ATTACK2:	
		flYS = 120;	
		break;
	case ACT_MELEE_ATTACK1:	
		flYS = 120;	
		break;
	case ACT_MELEE_ATTACK2:	
		flYS = 120;	
		break;
	case ACT_TURN_LEFT:
	case ACT_TURN_RIGHT:	
		flYS = 180;
		break;
	case ACT_GLIDE:
	case ACT_FLY:
		flYS = 30;
		break;
	default:
		flYS = 90;
		break;
	}

	return flYS;
}

void CNPC_HGrunt::IdleSound( void )
{
	if (FOkToSpeak() && ( g_fGruntQuestion || random->RandomInt( 0,1 ) ) )
	{
		if (!g_fGruntQuestion)
		{
			// ask question or make statement
			switch ( random->RandomInt( 0,2 ) )
			{
			case 0: // check in
				SENTENCEG_PlayRndSz( edict(), "HG_CHECK", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
				g_fGruntQuestion = 1;
				break;
			case 1: // question
				SENTENCEG_PlayRndSz( edict(), "HG_QUEST", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
				g_fGruntQuestion = 2;
				break;
			case 2: // statement
				SENTENCEG_PlayRndSz( edict(), "HG_IDLE", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
				break;
			}
		}
		else
		{
			switch (g_fGruntQuestion)
			{
			case 1: // check in
				SENTENCEG_PlayRndSz( edict(), "HG_CLEAR", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
				break;
			case 2: // question 
				SENTENCEG_PlayRndSz( edict(), "HG_ANSWER", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
				break;
			}
			g_fGruntQuestion = 0;
		}
		JustSpoke();
	}
}

bool CNPC_HGrunt::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 )
	{
		SetState ( NPC_STATE_IDLE );
		m_bInBarnacleMouth	= false;
		SetAbsVelocity( vec3_origin );
		SetMoveType( MOVETYPE_STEP );
		return true;
	}
	else if ( interactionType == g_interactionBarnacleVictimGrab )
	{
		if ( GetFlags() & FL_ONGROUND )
		{
			RemoveFlag( FL_ONGROUND );
		}
		
		//Maybe this will break something else.
		if ( GetState() == NPC_STATE_SCRIPT )
		{
			 m_hCine->CancelScript();
			 ClearSchedule();
		}

		SetState( NPC_STATE_PRONE );
		PainSound();
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: Combine needs to check ammo
// Input  :
// Output :
//-----------------------------------------------------------------------------
void CNPC_HGrunt::CheckAmmo ( void )
{
	if ( m_cAmmoLoaded <= 0 )
	 	 SetCondition( COND_NO_PRIMARY_AMMO );

}

//=========================================================
//=========================================================
CBaseEntity *CNPC_HGrunt::Kick( void )
{
	trace_t tr;

	Vector forward;
	AngleVectors( GetAbsAngles(), &forward );
	Vector vecStart = GetAbsOrigin();
	vecStart.z += WorldAlignSize().z * 0.5;
	Vector vecEnd = vecStart + (forward * 70);

	UTIL_TraceHull( vecStart, vecEnd, Vector(-16,-16,-18), Vector(16,16,18), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, &tr );
	
	if ( tr.m_pEnt )
	{
		CBaseEntity *pEntity = tr.m_pEnt;
		return pEntity;
	}

	return NULL;
}

Vector CNPC_HGrunt::Weapon_ShootPosition( void )
{
	if ( m_fStanding )
		return GetAbsOrigin() + Vector( 0, 0, 60 );
	else
		return GetAbsOrigin() + Vector( 0, 0, 48 );
}

void CNPC_HGrunt::Event_Killed( const CTakeDamageInfo &info )
{
	Vector	vecGunPos;
	QAngle	vecGunAngles;

	GetAttachment( "0", vecGunPos, vecGunAngles );

	// switch to body group with no gun.
	SetBodygroup( GUN_GROUP, GUN_NONE );

	// now spawn a gun.
	if (FBitSet( m_iWeapons, HGRUNT_SHOTGUN ))
	{
		 DropItem( "weapon_shotgun", vecGunPos, vecGunAngles );
	}
	else
	{
		 DropItem( "weapon_mp5", vecGunPos, vecGunAngles );
	}

	if (FBitSet( m_iWeapons, HGRUNT_GRENADELAUNCHER ))
	{
		DropItem( "ammo_ARgrenades", BodyTarget( GetAbsOrigin() ), vecGunAngles );
	}

	BaseClass::Event_Killed( info );
}

//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CNPC_HGrunt::HandleAnimEvent( animevent_t *pEvent )
{
	Vector	vecShootDir;
	Vector	vecShootOrigin;

	switch( pEvent->event )
	{
		case HGRUNT_AE_RELOAD:
		{
			CPASAttenuationFilter filter( this );
			enginesound->EmitSound( filter, entindex(), CHAN_WEAPON, "hgrunt/gr_reload1.wav", 1, ATTN_NORM );

			m_cAmmoLoaded = m_iClipSize;
			ClearCondition( COND_NO_PRIMARY_AMMO);
		}
		break;

		case HGRUNT_AE_GREN_TOSS:
		{
			CHandGrenade *pGrenade = (CHandGrenade*)CreateNoSpawn( "grenade_hand", GetAbsOrigin() + Vector(0,0,60), vec3_angle, this );

			// alway explode 3 seconds after the pin was pulled
			pGrenade->m_flDetonateTime = gpGlobals->curtime + 3.5;
			pGrenade->Spawn( );
			pGrenade->SetOwner( this );
			pGrenade->SetOwnerEntity( this );
			pGrenade->SetGravity( 0.5 );
			pGrenade->SetAbsVelocity( m_vecTossVelocity );
			pGrenade->SetDamage( sk_plr_dmg_grenade.GetFloat() );
			pGrenade->SetDamageRadius( pGrenade->GetDamage() * 2.5 );

			m_iLastGrenadeCondition =  COND_NONE;
			m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown.

			Msg( this, "Tossing a grenade to flush you out!\n"	);
		}
		break;

		case HGRUNT_AE_GREN_LAUNCH:
		{
			CPASAttenuationFilter filter2( this );
			enginesound->EmitSound( filter2, entindex(), CHAN_WEAPON, "weapons/glauncher.wav", 0.8, ATTN_NORM );
			
			Vector vecSrc;
			QAngle angAngles;

			GetAttachment( "0", vecSrc, angAngles );
		
			CGrenadeMP5 * m_pMyGrenade = (CGrenadeMP5*)Create( "grenade_mp5", vecSrc, angAngles, this );
			m_pMyGrenade->SetAbsVelocity( m_vecTossVelocity );
			m_pMyGrenade->SetLocalAngularVelocity( QAngle( random->RandomFloat( -100, -500 ), 0, 0 ) );
			m_pMyGrenade->SetMoveType( MOVETYPE_FLYGRAVITY ); 
			m_pMyGrenade->SetOwner( this );
			m_pMyGrenade->SetDamage( sk_plr_dmg_mp5_grenade.GetFloat() );

			if (g_iSkillLevel == SKILL_HARD)
				m_flNextGrenadeCheck = gpGlobals->curtime + random->RandomFloat( 2, 5 );// wait a random amount of time before shooting again
			else
				m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown.

			m_iLastGrenadeCondition =  COND_NONE;

			Msg( this, "Using grenade launcer to flush you out!\n"	);
		}
		break;

		case HGRUNT_AE_GREN_DROP:
		{
			CBaseGrenade *pGrenade = (CBaseGrenade*)CreateNoSpawn( "grenade_hand", Weapon_ShootPosition(), vec3_angle, this );
			pGrenade->SetAbsVelocity( m_vecTossVelocity );
			pGrenade->m_flDetonateTime = gpGlobals->curtime + 3.5;
			pGrenade->Spawn( );
			pGrenade->SetOwner( this );
			pGrenade->SetOwnerEntity( this );		

			m_iLastGrenadeCondition =  COND_NONE;
			Msg( this, "Dropping a grenade!\n"	);
		}
		break;

		case HGRUNT_AE_BURST1:
		{
			if ( FBitSet( m_iWeapons, HGRUNT_9MMAR ) )
			{
				Shoot();

				CPASAttenuationFilter filter3( this );
				// the first round of the three round burst plays the sound and puts a sound in the world sound list.
				if ( random->RandomInt(0,1) )
				{
					enginesound->EmitSound( filter3, entindex(), CHAN_WEAPON, "hgrunt/gr_mgun1.wav", 1, ATTN_NORM );
				}
				else
				{
					enginesound->EmitSound( filter3, entindex(), CHAN_WEAPON, "hgrunt/gr_mgun2.wav", 1, ATTN_NORM );
				}
			}
			else
			{
				Shotgun( );

				CPASAttenuationFilter filter4( this );
				enginesound->EmitSound( filter4, entindex(), CHAN_WEAPON, "weapons/sbarrel1.wav", 1, ATTN_NORM );
			}
		
			CSoundEnt::InsertSound ( SOUND_COMBAT, GetAbsOrigin(), 384, 0.3 );
		}
		break;

		case HGRUNT_AE_BURST2:
		case HGRUNT_AE_BURST3:
			Shoot();
			break;

		case HGRUNT_AE_KICK:
		{
			CBaseEntity *pHurt = Kick();

			if ( pHurt )
			{
				// SOUND HERE!
				Vector forward, up;
				AngleVectors( GetAbsAngles(), &forward, NULL, &up );

				if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
					 pHurt->ViewPunch( QAngle( 15, 0, 0) );

				pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + forward * 100 + up * 50 );

				CTakeDamageInfo info( this, this, sk_hgrunt_kick.GetFloat(), DMG_CLUB );
				CalculateMeleeDamageForce( &info, forward, pHurt->GetAbsOrigin() );
				pHurt->TakeDamage( info );
			}
		}
		break;

		case HGRUNT_AE_CAUGHT_ENEMY:
		{
			if ( FOkToSpeak() )
			{
				SENTENCEG_PlayRndSz( edict(), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
				 JustSpoke();
			}

		}

		default:
			BaseClass::HandleAnimEvent( pEvent );
			break;
	}
}

void CNPC_HGrunt::SetAim( const Vector &aimDir )
{
	QAngle angDir;
	VectorAngles( aimDir, angDir );

	float curPitch = GetPoseParameter( "XR" );
	float newPitch = curPitch + UTIL_AngleDiff( UTIL_ApproachAngle( angDir.x, curPitch, 60 ), curPitch );

	SetPoseParameter( "XR", -newPitch );
}

//=========================================================
// Shoot
//=========================================================
void CNPC_HGrunt::Shoot ( void )
{
	if ( GetEnemy() == NULL )
		return;
	
	Vector vecShootOrigin = Weapon_ShootPosition();
	Vector vecShootDir = GetShootEnemyDir( vecShootOrigin );

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

	Vector	vecShellVelocity = right * random->RandomFloat(40,90) + up * random->RandomFloat( 75,200 ) + forward * random->RandomFloat( -40, 40 );
//	EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, GetAbsAngles().y, m_iBrassShell, TE_BOUNCE_SHELL); 
	FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 2048, m_iAmmoType ); // shoot +-5 degrees
	
	m_fEffects |= EF_MUZZLEFLASH;
	
	m_cAmmoLoaded--;// take away a bullet!

	SetAim( vecShootDir );
}

//=========================================================
// Shoot
//=========================================================
void CNPC_HGrunt :: Shotgun ( void )
{
	if ( GetEnemy() == NULL )
		return;

	Vector vecShootOrigin = Weapon_ShootPosition();
	Vector vecShootDir = GetShootEnemyDir( vecShootOrigin );

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

	Vector	vecShellVelocity = right * random->RandomFloat(40,90) + up * random->RandomFloat( 75,200 ) + forward * random->RandomFloat( -40, 40 );
	//EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, GetAbsAngles().y, m_iShotgunShell, TE_BOUNCE_SHOTSHELL); 
	FireBullets( sk_hgrunt_pellets.GetFloat(), vecShootOrigin, vecShootDir, VECTOR_CONE_15DEGREES, 2048, m_iAmmoType, 0 ); // shoot +-7.5 degrees

	m_fEffects |= EF_MUZZLEFLASH;
	
	m_cAmmoLoaded--;// take away a bullet!

	SetAim( vecShootDir );
}

//=========================================================
// start task
//=========================================================
void CNPC_HGrunt::StartTask ( const Task_t *pTask )
{
	switch ( pTask->iTask )
	{
	case TASK_GRUNT_CHECK_FIRE:
		if ( !NoFriendlyFire() )
		{
			SetCondition( COND_WEAPON_BLOCKED_BY_FRIEND );
		}
		TaskComplete();
		break;

	case TASK_GRUNT_SPEAK_SENTENCE:
		SpeakSentence();
		TaskComplete();
		break;
	
	case TASK_WALK_PATH:
	case TASK_RUN_PATH:
		// grunt no longer assumes he is covered if he moves
		Forget( bits_MEMORY_INCOVER );
		BaseClass ::StartTask( pTask );
		break;

	case TASK_RELOAD:
		SetIdealActivity( ACT_RELOAD );
		break;

	case TASK_GRUNT_FACE_TOSS_DIR:
		break;

	case TASK_FACE_IDEAL:
	case TASK_FACE_ENEMY:
		BaseClass::StartTask( pTask );
		if (GetMoveType() == MOVETYPE_FLY)
		{
			SetIdealActivity( ACT_GLIDE );
		}
		break;

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

//=========================================================
// RunTask
//=========================================================
void CNPC_HGrunt::RunTask ( const Task_t *pTask )
{
	switch ( pTask->iTask )
	{
	case TASK_GRUNT_FACE_TOSS_DIR:
		{
			// project a point along the toss vector and turn to face that point.
			GetMotor()->SetIdealYawToTargetAndUpdate( GetAbsOrigin() + m_vecTossVelocity * 64, AI_KEEP_YAW_SPEED );

			if ( FacingIdeal() )
			{
				TaskComplete();
			}
			break;
		}
	default:
		{
			BaseClass::RunTask( pTask );
			break;
		}
	}
}

//=========================================================
// PainSound
//=========================================================
void CNPC_HGrunt::PainSound ( void )
{
	if ( gpGlobals->curtime > m_flNextPainTime )
	{
		CPASAttenuationFilter filter( this );
		switch ( random->RandomInt( 0,6 ) )
		{
		case 0:	
			enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "hgrunt/gr_pain3.wav", 1, ATTN_NORM );	
			break;
		case 1:
			enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "hgrunt/gr_pain4.wav", 1, ATTN_NORM );	
			break;
		case 2:
			enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "hgrunt/gr_pain5.wav", 1, ATTN_NORM );	
			break;
		case 3:
			enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "hgrunt/gr_pain1.wav", 1, ATTN_NORM );	
			break;
		case 4:
			enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "hgrunt/gr_pain2.wav", 1, ATTN_NORM );	
			break;
		}

		m_flNextPainTime = gpGlobals->curtime + 1;
	}
}

//=========================================================
// DeathSound 
//=========================================================
void CNPC_HGrunt::DeathSound ( void )
{
	CPASAttenuationFilter filter( this, ATTN_IDLE );
	switch ( random->RandomInt(0,2) )
	{
	case 0:	
		enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "hgrunt/gr_die1.wav", 1, ATTN_IDLE );	
		break;
	case 1:
		enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "hgrunt/gr_die2.wav", 1, ATTN_IDLE );	
		break;
	case 2:
		enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "hgrunt/gr_die3.wav", 1, ATTN_IDLE );	
		break;
	}
}

//=========================================================
// SetActivity 
//=========================================================
Activity CNPC_HGrunt::NPC_TranslateActivity( Activity eNewActivity )
{
	switch ( eNewActivity)
	{
	case ACT_RANGE_ATTACK1:
		// grunt is either shooting standing or shooting crouched
		if (FBitSet( m_iWeapons, HGRUNT_9MMAR))
		{
			if ( m_fStanding )
			{
				// get aimable sequence
				return (Activity)ACT_GRUNT_MP5_STANDING;
			}
			else
			{
				// get crouching shoot
				return (Activity)ACT_GRUNT_MP5_CROUCHING;
			}
		}
		else
		{
			if ( m_fStanding )
			{
				// get aimable sequence
				return (Activity)ACT_GRUNT_SHOTGUN_STANDING;
			}
			else
			{
				// get crouching shoot
				return (Activity)ACT_GRUNT_SHOTGUN_CROUCHING;
			}
		}
		break;
	case ACT_RANGE_ATTACK2:
		// grunt is going to a secondary long range attack. This may be a thrown 
		// grenade or fired grenade, we must determine which and pick proper sequence
 		if ( m_iWeapons & HGRUNT_HANDGRENADE )
		{
			// get toss anim
			return (Activity)ACT_GRUNT_TOSS_GRENADE;
		}
		else
		{
			// get launch anim
			return (Activity)ACT_GRUNT_LAUNCH_GRENADE;
		}
		break;
	case ACT_RUN:
		if ( m_iHealth <= HGRUNT_LIMP_HEALTH )
		{
			// limp!
			return ACT_RUN_HURT;
		}
		else
		{
			return eNewActivity;
		}
		break;
	case ACT_WALK:
		if ( m_iHealth <= HGRUNT_LIMP_HEALTH )
		{
			// limp!
			return ACT_WALK_HURT;
		}
		else
		{
			return eNewActivity;
		}
		break;
	case ACT_IDLE:
		if ( m_NPCState == NPC_STATE_COMBAT )
		{
			eNewActivity = ACT_IDLE_ANGRY;
		}
		
		break;
	}

	return BaseClass::NPC_TranslateActivity( eNewActivity );
}

void CNPC_HGrunt::ClearAttackConditions( void )
{
	bool fCanRangeAttack2 = HasCondition( COND_CAN_RANGE_ATTACK2 );

	// Call the base class.
	BaseClass::ClearAttackConditions();

	if( fCanRangeAttack2 )
	{
		// We don't allow the base class to clear this condition because we
		// don't sense for it every frame.
		SetCondition( COND_CAN_RANGE_ATTACK2 );
	}
}

int CNPC_HGrunt::SelectSchedule( void )
{
		// clear old sentence
	m_iSentence = HGRUNT_SENT_NONE;

	// flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling. 
	if ( GetMoveType() == MOVETYPE_FLY && m_NPCState != NPC_STATE_PRONE )
	{
		if (GetFlags() & FL_ONGROUND)
		{
			// just landed
			SetMoveType( MOVETYPE_STEP );
			return SCHED_GRUNT_REPEL_LAND;
		}
		else
		{
			// repel down a rope, 
			if ( m_NPCState == NPC_STATE_COMBAT )
				return SCHED_GRUNT_REPEL_ATTACK;
			else
				return SCHED_GRUNT_REPEL;
		}
	}

	// grunts place HIGH priority on running away from danger sounds.
	if ( HasCondition ( COND_HEAR_DANGER ) )
	{
		// dangerous sound nearby!
				
		//!!!KELLY - currently, this is the grunt's signal that a grenade has landed nearby,
		// and the grunt should find cover from the blast
		// good place for "SHIT!" or some other colorful verbal indicator of dismay.
		// It's not safe to play a verbal order here "Scatter", etc cause 
		// this may only affect a single individual in a squad. 
				
		if (FOkToSpeak())
		{
			SENTENCEG_PlayRndSz( edict(), "HG_GREN", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
			JustSpoke();
		}

		return SCHED_TAKE_COVER_FROM_BEST_SOUND;
	}

	switch	( m_NPCState )
	{

	case NPC_STATE_PRONE:
	{
		if (m_bInBarnacleMouth)
		{
			return SCHED_GRUNT_BARNACLE_CHOMP;
		}
		else
		{
			return SCHED_GRUNT_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();
			}

// new enemy
			if ( HasCondition( COND_NEW_ENEMY) )
			{
				if ( m_pSquad )
				{
					if ( !m_pSquad->IsLeader( this ) )
					{
						return SCHED_TAKE_COVER_FROM_ENEMY;
					}
					else 
					{
						//!!!KELLY - the leader of a squad of grunts has just seen the player or a 
						// monster and has made it the squad's enemy. You
						// can check pev->flags for FL_CLIENT to determine whether this is the player
						// or a monster. He's going to immediately start
						// firing, though. If you'd like, we can make an alternate "first sight" 
						// schedule where the leader plays a handsign anim
						// that gives us enough time to hear a short sentence or spoken command
						// before he starts pluggin away.
						if (FOkToSpeak())// && RANDOM_LONG(0,1))
						{
							if ((GetEnemy() != NULL) && GetEnemy()->IsPlayer())
								// player
								SENTENCEG_PlayRndSz( edict(), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
							else if ((GetEnemy() != NULL) &&
									(GetEnemy()->Classify() != CLASS_PLAYER_ALLY) && 
									(GetEnemy()->Classify() != CLASS_HUMAN_PASSIVE) && 
									(GetEnemy()->Classify() != CLASS_MACHINE) )
								// monster
								SENTENCEG_PlayRndSz( edict(), "HG_MONST", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);

							JustSpoke();
						}
						
						if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
						{
							return SCHED_GRUNT_SUPPRESS;
						}
						else
						{
							return SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE;
						}
					}
				}
			}
// no ammo
			else if ( HasCondition ( COND_NO_PRIMARY_AMMO ) )
			{
				//!!!KELLY - this individual just realized he's out of bullet ammo. 
				// He's going to try to find cover to run to and reload, but rarely, if 
				// none is available, he'll drop and reload in the open here. 
				return SCHED_GRUNT_HIDE_RELOAD;
			}
			
// damaged just a little
			else if ( HasCondition( COND_LIGHT_DAMAGE ) )
			{
				// if hurt:
				// 90% chance of taking cover
				// 10% chance of flinch.
				int iPercent = random->RandomInt(0,99);

				if ( iPercent <= 90 && GetEnemy() != NULL )
				{
					// only try to take cover if we actually have an enemy!

					//!!!KELLY - this grunt was hit and is going to run to cover.
					if (FOkToSpeak()) // && RANDOM_LONG(0,1))
					{
						//SENTENCEG_PlayRndSz( ENT(pev), "HG_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
						m_iSentence = HGRUNT_SENT_COVER;
						//JustSpoke();
					}
					return SCHED_TAKE_COVER_FROM_ENEMY;
				}
				else
				{
					return SCHED_SMALL_FLINCH;
				}
			}
// can kick
			else if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
			{
				return SCHED_MELEE_ATTACK1;
			}
// can grenade launch

			else if ( FBitSet( m_iWeapons, HGRUNT_GRENADELAUNCHER) && HasCondition ( COND_CAN_RANGE_ATTACK2 ) && OccupyStrategySlotRange( SQUAD_SLOT_GRENADE1, SQUAD_SLOT_GRENADE2 ) )
			{
				// shoot a grenade if you can
				return SCHED_RANGE_ATTACK2;
			}
// can shoot
			else if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
			{
				if ( m_pSquad )
				{
					if ( m_pSquad->GetLeader() != NULL )
					{
					
						CAI_BaseNPC *pSquadLeader = m_pSquad->GetLeader()->MyNPCPointer();
						
						// if the enemy has eluded the squad and a squad member has just located the enemy
						// and the enemy does not see the squad member, issue a call to the squad to waste a 
						// little time and give the player a chance to turn.
						if ( pSquadLeader && pSquadLeader->EnemyHasEludedMe() && !HasCondition ( COND_ENEMY_FACING_ME ) )
						{
							return SCHED_GRUNT_FOUND_ENEMY;
						}
					}
				}

				if ( OccupyStrategySlotRange ( SQUAD_SLOT_ENGAGE1, SQUAD_SLOT_ENGAGE2 ) )
				{
					// try to take an available ENGAGE slot
					return SCHED_RANGE_ATTACK1;
				}
				else if ( HasCondition ( COND_CAN_RANGE_ATTACK2 ) && OccupyStrategySlotRange( SQUAD_SLOT_GRENADE1, SQUAD_SLOT_GRENADE2 ) )
				{
					// throw a grenade if can and no engage slots are available
					return SCHED_RANGE_ATTACK2;
				}
				else
				{
					// hide!
					return SCHED_TAKE_COVER_FROM_ENEMY;
				}
			}
// can't see enemy
			else if ( HasCondition( COND_ENEMY_OCCLUDED ) )
			{
				if ( HasCondition( COND_CAN_RANGE_ATTACK2 ) && OccupyStrategySlotRange( SQUAD_SLOT_GRENADE1, SQUAD_SLOT_GRENADE2 ) )
				{
					//!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole"  "frag out" etc
					if (FOkToSpeak())
					{
						SENTENCEG_PlayRndSz( edict(), "HG_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
						JustSpoke();
					}
					return SCHED_RANGE_ATTACK2;
				}
				else if ( OccupyStrategySlotRange ( SQUAD_SLOT_ENGAGE1, SQUAD_SLOT_ENGAGE2 ) )
				{
					//!!!KELLY - grunt cannot see the enemy and has just decided to 
					// charge the enemy's position. 
					if (FOkToSpeak())// && RANDOM_LONG(0,1))
					{
						//SENTENCEG_PlayRndSz( ENT(pev), "HG_CHARGE", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
						m_iSentence = HGRUNT_SENT_CHARGE;
						//JustSpoke();
					}

					return SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE;
				}
				else
				{
					//!!!KELLY - grunt is going to stay put for a couple seconds to see if
					// the enemy wanders back out into the open, or approaches the
					// grunt's covered position. Good place for a taunt, I guess?
					if (FOkToSpeak() && random->RandomInt(0,1))
					{
						SENTENCEG_PlayRndSz( edict(), "HG_TAUNT", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
						JustSpoke();
					}
					return SCHED_STANDOFF;
				}
			}
			
			if ( HasCondition( COND_SEE_ENEMY ) && !HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
			{
				return SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE;
			}
		}
		case NPC_STATE_ALERT:
			if ( HasCondition( COND_ENEMY_DEAD ) && SelectWeightedSequence( ACT_VICTORY_DANCE ) != ACTIVITY_NOT_AVAILABLE )
			{
				// Scan around for new enemies
				return SCHED_VICTORY_DANCE;
			}
			break;
	}

	return BaseClass::SelectSchedule();
}

int CNPC_HGrunt::TranslateSchedule( int scheduleType )
{

	if ( scheduleType == SCHED_CHASE_ENEMY_FAILED )
	{
		 return SCHED_ESTABLISH_LINE_OF_FIRE;
	}
	switch	( scheduleType )
	{
	case SCHED_TAKE_COVER_FROM_ENEMY:
		{
			if ( m_pSquad )
			{
				if ( g_iSkillLevel == SKILL_HARD && HasCondition( COND_CAN_RANGE_ATTACK2 ) && OccupyStrategySlotRange( SQUAD_SLOT_GRENADE1, SQUAD_SLOT_GRENADE2 ) )
				{
					if (FOkToSpeak())
					{
						SENTENCEG_PlayRndSz( edict(), "HG_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
						JustSpoke();
					}
					return SCHED_GRUNT_TOSS_GRENADE_COVER;
				}
				else
				{
			  	    return SCHED_GRUNT_TAKE_COVER;
				}
			}
			else
			{
				if ( random->RandomInt(0,1) )
				{
					return SCHED_GRUNT_TAKE_COVER;
				}
				else
				{
					return SCHED_GRUNT_GRENADE_COVER;
				}
			}
		}
	case SCHED_GRUNT_TAKE_COVER_FAILED:
		{
			if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) && OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
			{
				return SCHED_RANGE_ATTACK1;
			}

			return SCHED_FAIL;
		}
		break;
	
	case SCHED_RANGE_ATTACK1:
		{
			// randomly stand or crouch
			if ( random->RandomInt( 0,9 ) == 0)
			{
				m_fStanding = random->RandomInt( 0, 1 ) != 0;
			}
		 
			if ( m_fStanding )
				return SCHED_GRUNT_RANGE_ATTACK1B;
			else
				return SCHED_GRUNT_RANGE_ATTACK1A;
		}

	case SCHED_RANGE_ATTACK2:
		{
			return SCHED_GRUNT_RANGE_ATTACK2;
		}
	case SCHED_VICTORY_DANCE:
		{
			if ( m_pSquad )
			{
				if ( !m_pSquad->IsLeader( this ) )
				{
					return SCHED_GRUNT_FAIL;
				}
			}

			return SCHED_GRUNT_VICTORY_DANCE;
		}
	case SCHED_GRUNT_SUPPRESS:
		{
			if ( GetEnemy()->IsPlayer() && m_fFirstEncounter )
			{
				m_fFirstEncounter = FALSE;// after first encounter, leader won't issue handsigns anymore when he has a new enemy
				return SCHED_GRUNT_SIGNAL_SUPPRESS;
			}
			else
			{
				return SCHED_GRUNT_SUPPRESS;
			}
		}
	case SCHED_FAIL:
		{
			if ( GetEnemy() != NULL )
			{
				// grunt has an enemy, so pick a different default fail schedule most likely to help recover.
				return SCHED_GRUNT_COMBAT_FAIL;
			}

			return SCHED_GRUNT_FAIL;
		}
	case SCHED_GRUNT_REPEL:
		{
			Vector vecVel = GetAbsVelocity();
			if ( vecVel.z > -128 )
			{
				vecVel.z -= 32;
				SetAbsVelocity( vecVel );
			}

			return SCHED_GRUNT_REPEL;
		}
	case SCHED_GRUNT_REPEL_ATTACK:
		{
			Vector vecVel = GetAbsVelocity();
			if ( vecVel.z > -128 )
			{
				vecVel.z -= 32;
				SetAbsVelocity( vecVel );
			}

			return SCHED_GRUNT_REPEL_ATTACK;
		}
	default:
		{
			return BaseClass::TranslateSchedule( scheduleType );
		}
	}
}

//=========================================================
// CHGruntRepel - when triggered, spawns a monster_human_grunt
// repelling down a line.
//=========================================================

class CNPC_HGruntRepel:public CAI_BaseNPC
{
	DECLARE_CLASS( CNPC_HGruntRepel, CAI_BaseNPC );
public:
	void Spawn( void );
	void Precache( void );
	void RepelUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
	int m_iSpriteTexture;	// Don't save, precache

	DECLARE_DATADESC();
};

LINK_ENTITY_TO_CLASS( monster_grunt_repel, CNPC_HGruntRepel );



//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_HGruntRepel )
	DEFINE_USEFUNC( CNPC_HGruntRepel, RepelUse ),
END_DATADESC()

void CNPC_HGruntRepel::Spawn( void )
{
	Precache( );
	SetSolid( SOLID_NONE );

	SetUse( RepelUse );
}

void CNPC_HGruntRepel::Precache( void )
{
	UTIL_PrecacheOther( "monster_human_grunt" );
	m_iSpriteTexture = engine->PrecacheModel( "sprites/rope.vmt" );
}

void CNPC_HGruntRepel::RepelUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
	trace_t tr;
	UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -4096.0), MASK_NPCSOLID, this,COLLISION_GROUP_NONE, &tr);
	
	CBaseEntity *pEntity = Create( "monster_human_grunt", GetAbsOrigin(), GetAbsAngles() );
	CAI_BaseNPC *pGrunt = pEntity->MyNPCPointer( );
	pGrunt->SetMoveType( MOVETYPE_FLY );
	pGrunt->SetAbsVelocity( Vector( 0, 0, random->RandomFloat( -196, -128 ) ) );
	pGrunt->SetActivity( ACT_GLIDE );
	// UNDONE: position?
	pGrunt->m_vecLastPosition = tr.endpos;

	CBeam *pBeam = CBeam::BeamCreate( "sprites/rope.vmt", 10 );
	pBeam->PointEntInit( GetAbsOrigin() + Vector(0,0,112), pGrunt );
	pBeam->SetBeamFlags( FBEAM_SOLID );
	pBeam->SetColor( 255, 255, 255 );
	pBeam->SetThink( SUB_Remove );
	SetNextThink( gpGlobals->curtime + -4096.0 * tr.fraction / pGrunt->GetAbsVelocity().z + 0.5 );

	UTIL_Remove( this );
}


//------------------------------------------------------------------------------
//
// Schedules
//
//------------------------------------------------------------------------------
AI_BEGIN_CUSTOM_NPC( monster_human_grunt, CNPC_HGrunt )

	DECLARE_ACTIVITY( ACT_GRUNT_LAUNCH_GRENADE )
	DECLARE_ACTIVITY( ACT_GRUNT_TOSS_GRENADE )
	DECLARE_ACTIVITY( ACT_GRUNT_MP5_STANDING );
	DECLARE_ACTIVITY( ACT_GRUNT_MP5_CROUCHING );
	DECLARE_ACTIVITY( ACT_GRUNT_SHOTGUN_STANDING );
	DECLARE_ACTIVITY( ACT_GRUNT_SHOTGUN_CROUCHING );

	DECLARE_CONDITION( COND_GRUNT_NOFIRE )
	
	DECLARE_TASK( TASK_GRUNT_FACE_TOSS_DIR )
	DECLARE_TASK( TASK_GRUNT_SPEAK_SENTENCE )
	DECLARE_TASK( TASK_GRUNT_CHECK_FIRE )
		
	DECLARE_SQUADSLOT( SQUAD_SLOT_GRENADE1 )
	DECLARE_SQUADSLOT( SQUAD_SLOT_GRENADE2 )
	DECLARE_SQUADSLOT( SQUAD_SLOT_ENGAGE1 )
	DECLARE_SQUADSLOT( SQUAD_SLOT_ENGAGE2 )

	//=========================================================
	// > SCHED_GRUNT_FAIL
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_FAIL,
	
		"	Tasks"
		"		TASK_STOP_MOVING		0"
		"		TASK_SET_ACTIVITY		ACTIVITY:ACT_IDLE"
		"		TASK_WAIT				0"
		"		TASK_WAIT_PVS			0"
		"	"
		"	Interrupts"
		"		COND_CAN_RANGE_ATTACK1"
		"		COND_CAN_MELEE_ATTACK1"
	)
	
	//=========================================================
	// > SCHED_GRUNT_COMBAT_FAIL
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_COMBAT_FAIL,
	
		"	Tasks"
		"		TASK_STOP_MOVING		0"
		"		TASK_SET_ACTIVITY		ACTIVITY:ACT_IDLE"
		"		TASK_WAIT_FACE_ENEMY	2"
		"		TASK_WAIT_PVS			0"
		"	"
		"	Interrupts"
		"		COND_CAN_RANGE_ATTACK1"
		)
	
	//=========================================================
	// > SCHED_GRUNT_VICTORY_DANCE
	// Victory dance!
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_VICTORY_DANCE,
	
		"	Tasks"
		"		TASK_STOP_MOVING				0"
		"		TASK_FACE_ENEMY					0"
		"		TASK_WAIT						1.5"
		"		TASK_GET_PATH_TO_ENEMY_CORPSE	0"
		"		TASK_WALK_PATH					0"
		"		TASK_WAIT_FOR_MOVEMENT			0"
		"		TASK_FACE_ENEMY					0"
		"		TASK_PLAY_SEQUENCE				ACTIVITY:ACT_VICTORY_DANCE"
		"	"
		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
	)
	
	//=========================================================
	// > SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE
	// Establish line of fire - move to a position that allows
	// the grunt to attack.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE,
	
		"	Tasks"
		"		TASK_SET_FAIL_SCHEDULE		SCHEDULE:SCHED_GRUNT_TAKE_COVER_FROM_ENEMY"
		"		TASK_GET_PATH_TO_ENEMY		0"
		"		TASK_GRUNT_SPEAK_SENTENCE	0"
		"		TASK_RUN_PATH				0"
		"		TASK_WAIT_FOR_MOVEMENT		0"
		"	"
		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_ENEMY_DEAD"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
		"		COND_CAN_RANGE_ATTACK1"
		"		COND_CAN_MELEE_ATTACK1"
		"		COND_CAN_RANGE_ATTACK2"
		"		COND_CAN_MELEE_ATTACK2"
		"		COND_HEAR_DANGER"
	)
	
	//=========================================================
	// > SCHED_GRUNT_FOUND_ENEMY
	// Grunt established sight with an enemy
	// that was hiding from the squad.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_FOUND_ENEMY,
	
		"	Tasks"
		"		TASK_STOP_MOVING				0"
		"		TASK_FACE_ENEMY					0"
		"		TASK_PLAY_SEQUENCE_FACE_ENEMY	ACTIVITY:ACT_SIGNAL1"
		"	"
		"	Interrupts"
		"		COND_HEAR_DANGER"
	)
	
	
	//=========================================================
	// > SCHED_GRUNT_COMBAT_FACE
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_COMBAT_FACE,
	
		"	Tasks"
		"		TASK_STOP_MOVING		0"
		"		TASK_SET_ACTIVITY		ACTIVITY:ACT_IDLE"
		"		TASK_FACE_ENEMY			0"
		"		TASK_WAIT				1.5"
		"		TASK_SET_SCHEDULE		SCHEDULE:SCHED_GRUNT_SWEEP"
		"	"
		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_ENEMY_DEAD"
		"		COND_CAN_RANGE_ATTACK1"
		"		COND_CAN_RANGE_ATTACK2"
	)
	
	
	//=========================================================
	// > SCHED_GRUNT_SIGNAL_SUPPRESS
	// Suppressing fire - don't stop shooting until the clip is
	// empty or grunt gets hurt.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_SIGNAL_SUPPRESS,
	
		"	Tasks"
		"		TASK_STOP_MOVING				0"
		"		TASK_FACE_IDEAL					0"
		"		TASK_PLAY_SEQUENCE_FACE_ENEMY	ACTIVITY:ACT_SIGNAL2"
		"		TASK_FACE_ENEMY					0"
		"		TASK_GRUNT_CHECK_FIRE			0"
		"		TASK_RANGE_ATTACK1				0"
		"		TASK_FACE_ENEMY					0"
		"		TASK_GRUNT_CHECK_FIRE			0"
		"		TASK_RANGE_ATTACK1				0"
		"		TASK_FACE_ENEMY					0"
		"		TASK_GRUNT_CHECK_FIRE			0"
		"		TASK_RANGE_ATTACK1				0"
		"		TASK_FACE_ENEMY					0"
		"		TASK_GRUNT_CHECK_FIRE			0"
		"		TASK_RANGE_ATTACK1				0"
		"		TASK_FACE_ENEMY					0"
		"		TASK_GRUNT_CHECK_FIRE			0"
		"		TASK_RANGE_ATTACK1				0"
		"	"
		"	Interrupts"
		"		COND_ENEMY_DEAD"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
		"		COND_GRUNT_NOFIRE"
		"		COND_NO_PRIMARY_AMMO"
		"		COND_HEAR_DANGER"
	)
	
	//=========================================================
	// > SCHED_GRUNT_SUPPRESS
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_SUPPRESS,
	
		"	Tasks"
		"		TASK_STOP_MOVING			0"
		"		TASK_FACE_ENEMY				0"
		"		TASK_GRUNT_CHECK_FIRE		0"
		"		TASK_RANGE_ATTACK1			0"
		"		TASK_FACE_ENEMY				0"
		"		TASK_GRUNT_CHECK_FIRE		0"
		"		TASK_RANGE_ATTACK1			0"
		"		TASK_FACE_ENEMY				0"
		"		TASK_GRUNT_CHECK_FIRE		0"
		"		TASK_RANGE_ATTACK1			0"
		"		TASK_FACE_ENEMY				0"
		"		TASK_GRUNT_CHECK_FIRE		0"
		"		TASK_RANGE_ATTACK1			0"
		"		TASK_FACE_ENEMY				0"
		"		TASK_GRUNT_CHECK_FIRE		0"
		"		TASK_RANGE_ATTACK1			0"
		"	"
		"	Interrupts"
		"		COND_ENEMY_DEAD"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
		"		COND_GRUNT_NOFIRE"
		"		COND_NO_PRIMARY_AMMO"
		"		COND_HEAR_DANGER"
	)
	
	//=========================================================
	// > SCHED_GRUNT_WAIT_IN_COVER
	// grunt wait in cover - we don't allow danger or the ability
	// to attack to break a grunt's run to cover schedule, but
	// when a grunt is in cover, we do want them to attack if they can.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_WAIT_IN_COVER,
	
		"	Tasks"
		"		TASK_STOP_MOVING		0"
		"		TASK_SET_ACTIVITY		ACTIVITY:ACT_IDLE"
		"		TASK_WAIT_FACE_ENEMY	1"
		"	"
		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_HEAR_DANGER"
		"		COND_CAN_RANGE_ATTACK1"
		"		COND_CAN_RANGE_ATTACK2"
		"		COND_CAN_MELEE_ATTACK1"
		"		COND_CAN_MELEE_ATTACK2"
	)
	
	
	//=========================================================
	// > SCHED_GRUNT_TAKE_COVER
	// !!!BUGBUG - set a decent fail schedule here.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_TAKE_COVER,
	
		"	Tasks"
		"		TASK_STOP_MOVING			0"
		"		TASK_SET_FAIL_SCHEDULE		SCHEDULE:SCHED_GRUNT_TAKE_COVER_FAILED"
		"		TASK_WAIT					0.2"
		"		TASK_FIND_COVER_FROM_ENEMY	0"
		"		TASK_GRUNT_SPEAK_SENTENCE	0"
		"		TASK_RUN_PATH				0"
		"		TASK_WAIT_FOR_MOVEMENT		0"
		"		TASK_REMEMBER				MEMORY:INCOVER"
		"		TASK_SET_SCHEDULE			SCHEDULE:SCHED_GRUNT_WAIT_IN_COVER"
		"	"
		"	Interrupts"
	)
	
	
	//=========================================================
	// > SCHED_GRUNT_GRENADE_COVER
	// drop grenade then run to cover.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_GRENADE_COVER,
	
		"	Tasks"
		"		TASK_STOP_MOVING						0"
		"		TASK_FIND_COVER_FROM_ENEMY				99"
		"		TASK_FIND_FAR_NODE_COVER_FROM_ENEMY		384"
		"		TASK_PLAY_SEQUENCE						ACTIVITY:ACT_SPECIAL_ATTACK1"
		"		TASK_CLEAR_MOVE_WAIT					0"
		"		TASK_RUN_PATH							0"
		"		TASK_WAIT_FOR_MOVEMENT					0"
		"		TASK_SET_SCHEDULE						SCHEDULE:SCHED_GRUNT_WAIT_IN_COVER"
		"	"
		"	Interrupts"
	)
		
	//=========================================================
	// > SCHED_GRUNT_TOSS_GRENADE_COVER
	// drop grenade then run to cover.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_TOSS_GRENADE_COVER,
	
		"	Tasks"
		"		TASK_FACE_ENEMY				0"
		"		TASK_RANGE_ATTACK2			0"
		"		TASK_SET_SCHEDULE			SCHEDULE:SCHED_GRUNT_TAKE_COVER_FROM_ENEMY"
		"	"
		"	Interrupts"
	)
	
	//=========================================================
	// > SCHED_GRUNT_HIDE_RELOAD
	// Grunt reload schedule
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_HIDE_RELOAD,
	
		"	Tasks"
		"		TASK_STOP_MOVING				0"
		"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_GRUNT_RELOAD"
		"		TASK_FIND_COVER_FROM_ENEMY		0"
		"		TASK_RUN_PATH					0"
		"		TASK_WAIT_FOR_MOVEMENT			0"
		"		TASK_REMEMBER					MEMORY:INCOVER"
		"		TASK_FACE_ENEMY					0"
		"		TASK_PLAY_SEQUENCE				ACTIVITY:ACT_RELOAD"
		"	"
		"	Interrupts"
		"		COND_HEAVY_DAMAGE"
		"		COND_HEAR_DANGER"
	)
	
	//=========================================================
	// > SCHED_GRUNT_SWEEP
	// Do a turning sweep of the area
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_SWEEP,
	
		"	Tasks"
		"		TASK_TURN_LEFT			179"
		"		TASK_WAIT				1"
		"		TASK_TURN_LEFT			179"
		"		TASK_WAIT				1"
		"	"
		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
		"		COND_CAN_RANGE_ATTACK1"
		"		COND_CAN_RANGE_ATTACK2"
		"		COND_HEAR_WORLD"
		"		COND_HEAR_DANGER"
		"		COND_HEAR_PLAYER"
	)
	
	//=========================================================
	// > SCHED_GRUNT_RANGE_ATTACK1A
	// primary range attack. Overriden because base class stops attacking when the enemy is occluded.
	// grunt's grenade toss requires the enemy be occluded.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_RANGE_ATTACK1A,
	
		"	Tasks"
		"		TASK_STOP_MOVING					0"
		"		TASK_PLAY_SEQUENCE_FACE_ENEMY		ACTIVITY:ACT_CROUCH"
		"		TASK_GRUNT_CHECK_FIRE				0"
		"		TASK_RANGE_ATTACK1					0"
		"		TASK_FACE_ENEMY						0"
		"		TASK_GRUNT_CHECK_FIRE				0"
		"		TASK_RANGE_ATTACK1					0"
		"		TASK_FACE_ENEMY						0"
		"		TASK_GRUNT_CHECK_FIRE				0"
		"		TASK_RANGE_ATTACK1					0"
		"		TASK_FACE_ENEMY						0"
		"		TASK_GRUNT_CHECK_FIRE				0"
		"		TASK_RANGE_ATTACK1					0"
		"	"
		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_ENEMY_DEAD"
		"		COND_HEAVY_DAMAGE"
		"		COND_ENEMY_OCCLUDED"
		"		COND_HEAR_DANGER"
		"		COND_GRUNT_NOFIRE"
		"		COND_NO_PRIMARY_AMMO"
	)
	
	//=========================================================
	// > SCHED_GRUNT_RANGE_ATTACK1B
	// primary range attack. Overriden because base class stops attacking when the enemy is occluded.
	// grunt's grenade toss requires the enemy be occluded.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_RANGE_ATTACK1B,
	
		"	Tasks"
		"		TASK_STOP_MOVING					0"
		"		TASK_PLAY_SEQUENCE_FACE_ENEMY		ACTIVITY:ACT_IDLE_ANGRY"
		"		TASK_GRUNT_CHECK_FIRE				0"
		"		TASK_RANGE_ATTACK1					0"
		"		TASK_FACE_ENEMY						0"
		"		TASK_GRUNT_CHECK_FIRE				0"
		"		TASK_RANGE_ATTACK1					0"
		"		TASK_FACE_ENEMY						0"
		"		TASK_GRUNT_CHECK_FIRE				0"
		"		TASK_RANGE_ATTACK1					0"
		"		TASK_FACE_ENEMY						0"
		"		TASK_GRUNT_CHECK_FIRE				0"
		"		TASK_RANGE_ATTACK1					0"
		"	"
		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_ENEMY_DEAD"
		"		COND_HEAVY_DAMAGE"
		"		COND_ENEMY_OCCLUDED"
		"		COND_HEAR_DANGER"
		"		COND_GRUNT_NOFIRE"
		"		COND_NO_PRIMARY_AMMO"
	)
	
	//=========================================================
	// > SCHED_GRUNT_RANGE_ATTACK2
	// secondary range attack. Overriden because base class stops attacking when the enemy is occluded.
	// grunt's grenade toss requires the enemy be occluded.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_RANGE_ATTACK2,
	
		"	Tasks"
		"		TASK_STOP_MOVING			0"
		"		TASK_GRUNT_FACE_TOSS_DIR	0"
		"		TASK_PLAY_SEQUENCE			ACTIVITY:ACT_RANGE_ATTACK2"
		"		TASK_SET_SCHEDULE			SCHEDULE:SCHED_GRUNT_WAIT_IN_COVER" // don't run immediately after throwing grenade.
		"	"
		"	Interrupts"
	)
	
	//=========================================================
	// > SCHED_GRUNT_REPEL
	// secondary range attack. Overriden because base class stops attacking when the enemy is occluded.
	// grunt's grenade toss requires the enemy be occluded.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_REPEL,
	
		"	Tasks"
		"		TASK_STOP_MOVING			0"
		"		TASK_FACE_IDEAL				0"
		"		TASK_PLAY_SEQUENCE			ACTIVITY:ACT_GLIDE"
		"	"
		"	Interrupts"
		"		COND_SEE_ENEMY"
		"		COND_NEW_ENEMY"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
		"		COND_HEAR_DANGER"
		"		COND_HEAR_PLAYER"
		"		COND_HEAR_COMBAT"
	)
	
	//=========================================================
	// > SCHED_GRUNT_REPEL_ATTACK
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_REPEL_ATTACK,
	
		"	Tasks"
		"		TASK_STOP_MOVING			0"
		"		TASK_FACE_ENEMY				0"
		"		TASK_PLAY_SEQUENCE			ACTIVITY:ACT_FLY"
		"	"
		"	Interrupts"
		"		COND_ENEMY_OCCLUDED"
	)
	
	//=========================================================
	// > SCHED_GRUNT_REPEL_LAND
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_REPEL_LAND,
	
		"	Tasks"
		"		TASK_STOP_MOVING					0"
		"		TASK_PLAY_SEQUENCE					ACTIVITY:ACT_LAND"
		"		TASK_GET_PATH_TO_LASTPOSITION		0"
		"		TASK_RUN_PATH						0"
		"		TASK_WAIT_FOR_MOVEMENT				0"
		"		TASK_CLEAR_LASTPOSITION				0"
		"	"
		"	Interrupts"
		"		COND_SEE_ENEMY"
		"		COND_NEW_ENEMY"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
		"		COND_HEAR_DANGER"
		"		COND_HEAR_COMBAT"
		"		COND_HEAR_PLAYER"
	)
	
	//=========================================================
	// > SCHED_GRUNT_RELOAD
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_RELOAD,
	
		"	Tasks"
		"		TASK_STOP_MOVING			0"
		"		TASK_PLAY_SEQUENCE			ACTIVITY:ACT_RELOAD"
		"	"
		"	Interrupts"
		"		COND_HEAVY_DAMAGE"
	)
	
	//=========================================================
	// > SCHED_GRUNT_TAKE_COVER_FROM_ENEMY
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_TAKE_COVER_FROM_ENEMY,
	
		"	Tasks"
		"		TASK_STOP_MOVING				0"
		"		TASK_WAIT						0.2"
		"		TASK_FIND_COVER_FROM_ENEMY		0"
		"		TASK_RUN_PATH					0"
		"		TASK_WAIT_FOR_MOVEMENT			0"
		"		TASK_REMEMBER					MEMORY:INCOVER"
		"		TASK_FACE_ENEMY					0"
		"		TASK_WAIT						1"
		"	"
		"	Interrupts"
		"		COND_NEW_ENEMY"
	)
	
	//=========================================================
	// > SCHED_GRUNT_TAKE_COVER_FAILED
	// special schedule type that forces analysis of conditions and picks
	// the best possible schedule to recover from this type of failure.
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_TAKE_COVER_FAILED,
		"	Tasks"
		"	Interrupts"
	)

	//=========================================================
	// > SCHED_GRUNT_BARNACLE_HIT
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_BARNACLE_HIT,

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

	//=========================================================
	// > SCHED_GRUNT_BARNACLE_PULL
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_BARNACLE_PULL,

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

	//=========================================================
	// > SCHED_GRUNT_BARNACLE_CHOMP
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_BARNACLE_CHOMP,

		"	Tasks"
		"		 TASK_PLAY_SEQUENCE			ACTIVITY:ACT_BARNACLE_CHOMP"
		"		 TASK_SET_SCHEDULE			SCHEDULE:SCHED_GRUNT_BARNACLE_CHEW"
		""
		"	Interrupts"
	)

	//=========================================================
	// > SCHED_GRUNT_BARNACLE_CHEW
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_GRUNT_BARNACLE_CHEW,

		"	Tasks"
		"		 TASK_PLAY_SEQUENCE			ACTIVITY:ACT_BARNACLE_CHEW"
	)

AI_END_CUSTOM_NPC()


//=========================================================
// DEAD HGRUNT PROP
//=========================================================
class CNPC_DeadHGrunt : public CAI_BaseNPC
{
	DECLARE_CLASS( CNPC_DeadHGrunt, CAI_BaseNPC );
public:
	void Spawn( void );
	Class_T	Classify ( void ) { return	CLASS_HUMAN_MILITARY; }
	float MaxYawSpeed( void ) { return 8.0f; }

	bool KeyValue( const char *szKeyName, const char *szValue );

	int	m_iPose;// which sequence to display	-- temporary, don't need to save
	static char *m_szPoses[3];
};

char *CNPC_DeadHGrunt::m_szPoses[] = { "deadstomach", "deadside", "deadsitting" };

bool CNPC_DeadHGrunt::KeyValue( const char *szKeyName, const char *szValue )
{
	if ( FStrEq( szKeyName, "pose" ) )
		m_iPose = atoi( szValue );
	else 
		CAI_BaseNPC::KeyValue( szKeyName, szValue );

	return true;
}

LINK_ENTITY_TO_CLASS( monster_hgrunt_dead, CNPC_DeadHGrunt );

//=========================================================
// ********** DeadHGrunt SPAWN **********
//=========================================================
void CNPC_DeadHGrunt::Spawn( void )
{
	engine->PrecacheModel("models/hgrunt.mdl");
	SetModel( "models/hgrunt.mdl" );

	m_fEffects			= 0;
	SetSequence( 0 );
	m_bloodColor		= BLOOD_COLOR_RED;

	SetSequence( LookupSequence( m_szPoses[m_iPose] ) );

	if ( GetSequence() == -1 )
	{
		Msg ( "Dead hgrunt with bad pose\n" );
	}

	// Corpses have less health
	m_iHealth			= 8;

	// map old bodies onto new bodies
	switch( m_nBody )
	{
	case 0: // Grunt with Gun
		m_nBody = 0;
		m_nSkin = 0;
		SetBodygroup( HEAD_GROUP, HEAD_GRUNT );
		SetBodygroup( GUN_GROUP, GUN_MP5 );
		break;
	case 1: // Commander with Gun
		m_nBody = 0;
		m_nSkin = 0;
		SetBodygroup( HEAD_GROUP, HEAD_COMMANDER );
		SetBodygroup( GUN_GROUP, GUN_MP5 );
		break;
	case 2: // Grunt no Gun
		m_nBody = 0;
		m_nSkin = 0;
		SetBodygroup( HEAD_GROUP, HEAD_GRUNT );
		SetBodygroup( GUN_GROUP, GUN_NONE );
		break;
	case 3: // Commander no Gun
		m_nBody = 0;
		m_nSkin = 0;
		SetBodygroup( HEAD_GROUP, HEAD_COMMANDER );
		SetBodygroup( GUN_GROUP, GUN_NONE );
		break;
	}

	NPCInitDead();
}
