text: //========= Copyright Valve Corporation, All rights reserved. ============//
  //
  // Purpose: Houndeye - a spooky sonic dog.
  //
  // $NoKeywords: $
  //=============================================================================//
  
  #include "cbase.h"
  #include "npc_houndeye.h"
  #include "ai_default.h"
  #include "ai_node.h"
  #include "ai_route.h"
  #include "AI_Navigator.h"
  #include "AI_Motor.h"
  #include "ai_squad.h"
  #include "AI_TacticalServices.h"
  #include "soundent.h"
  #include "EntityList.h"
  #include "game.h"
  #include "activitylist.h"
  #include "hl2_shareddefs.h"
  #include "grenade_energy.h"
  #include "energy_wave.h"
  #include "ai_interactions.h"
  #include "ndebugoverlay.h"
  #include "npcevent.h"
  #include "player.h"
  #include "vstdlib/random.h"
  #include "engine/IEngineSound.h"
  #include "movevars_shared.h"
  #ifdef HOE_DLL
  #include "beam_flags.h"
  #include "explode.h"
  #endif
  
  // memdbgon must be the last include file in a .cpp file!!!
  #include "tier0/memdbgon.h"
  
  #define	HOUNDEYE_MAX_ATTACK_RADIUS		500
  #define	HOUNDEYE_MIN_ATTACK_RADIUS		100
  
  #ifdef HOE_DLL
  #define HOUNDEYE_EYE_FRAMES 3 // how many different switchable maps for the eye
  #else
  #define HOUNDEYE_EYE_FRAMES 4 // how many different switchable maps for the eye
  #endif
  
  ConVar	sk_Houndeye_health( "sk_Houndeye_health","0");
  ConVar	sk_Houndeye_dmg_blast( "sk_Houndeye_dmg_blast","0");
  
  //=========================================================
  // Interactions
  //=========================================================
  int	g_interactionHoundeyeGroupAttack					= 0;
  int	g_interactionHoundeyeGroupRetreat					= 0;
  int	g_interactionHoundeyeGroupRalley					= 0;
  
  //=========================================================
  // Specialized Tasks 
  //=========================================================
  enum 
  {
  	TASK_HOUND_CLOSE_EYE = LAST_SHARED_TASK,
  	TASK_HOUND_OPEN_EYE,
  	TASK_HOUND_THREAT_DISPLAY,
  	TASK_HOUND_FALL_ASLEEP,
  	TASK_HOUND_WAKE_UP,
  	TASK_HOUND_HOP_BACK,
  
  	TASK_HOUND_GET_PATH_TO_CIRCLE,
  	TASK_HOUND_REVERSE_STRAFE_DIR,
  };
  
  //-----------------------------------------------------------------------------
  // Custom Conditions
  //-----------------------------------------------------------------------------
  enum Houndeye_Conds
  {
  	COND_HOUND_GROUP_ATTACK = LAST_SHARED_CONDITION,
  	COND_HOUND_GROUP_RETREAT,
  	COND_HOUND_GROUP_RALLEY,
  #ifdef HOE_DLL
  	COND_HOUND_ENEMY_DISTANT,
  #endif
  };
  
  //=========================================================
  // Specialized Shedules
  //=========================================================
  enum
  {
  	SCHED_HOUND_AGITATED = LAST_SHARED_SCHEDULE,
  	SCHED_HOUND_HOP_RETREAT,
  	SCHED_HOUND_RANGE_ATTACK1,
  
  	SCHED_HOUND_ATTACK_STRAFE,
  	SCHED_HOUND_ATTACK_STRAFE_REVERSE,
  	SCHED_HOUND_GROUP_ATTACK,
  	SCHED_HOUND_GROUP_RETREAT,
  	SCHED_HOUND_CHASE_ENEMY,
  	SCHED_HOUND_COVER_WAIT,
  	SCHED_HOUND_GROUP_RALLEY,
  #ifdef HOE_DLL
  	SCHED_HOUND_SLEEP,
  	SCHED_HOUND_WAKE_URGENT,
  	SCHED_HOUND_WAKE_LAZY,
  #endif
  };
  
  //=========================================================
  // Specialized activities
  //=========================================================
  int	ACT_HOUND_GUARD;
  
  //=========================================================
  // Monster's Anim Events Go Here
  //=========================================================
  #define		HOUND_AE_WARN			1
  #define		HOUND_AE_STARTATTACK	2
  #define		HOUND_AE_THUMP			3
  #define		HOUND_AE_ANGERSOUND1	4
  #define		HOUND_AE_ANGERSOUND2	5
  #define		HOUND_AE_HOPBACK		6
  #define		HOUND_AE_CLOSE_EYE		7
  #define		HOUND_AE_LEAP_HIT		8
  
  #ifdef HOE_DLL
  #else
  //-----------------------------------------------------------------------------
  // Purpose: Initialize the custom schedules
  // Input  :
  // Output :
  //-----------------------------------------------------------------------------
  void CNPC_Houndeye::InitCustomSchedules(void) 
  {
  	INIT_CUSTOM_AI(CNPC_Houndeye);
  
  	ADD_CUSTOM_TASK(CNPC_Houndeye,	TASK_HOUND_CLOSE_EYE);
  	ADD_CUSTOM_TASK(CNPC_Houndeye,	TASK_HOUND_OPEN_EYE);
  	ADD_CUSTOM_TASK(CNPC_Houndeye,	TASK_HOUND_THREAT_DISPLAY);
  	ADD_CUSTOM_TASK(CNPC_Houndeye,	TASK_HOUND_FALL_ASLEEP);
  	ADD_CUSTOM_TASK(CNPC_Houndeye,	TASK_HOUND_WAKE_UP);
  	ADD_CUSTOM_TASK(CNPC_Houndeye,	TASK_HOUND_HOP_BACK);
  
  	ADD_CUSTOM_TASK(CNPC_Houndeye,	TASK_HOUND_GET_PATH_TO_CIRCLE);
  	ADD_CUSTOM_TASK(CNPC_Houndeye,	TASK_HOUND_REVERSE_STRAFE_DIR);
  	
  	ADD_CUSTOM_CONDITION(CNPC_Houndeye,	COND_HOUND_GROUP_ATTACK);
  	ADD_CUSTOM_CONDITION(CNPC_Houndeye,	COND_HOUND_GROUP_RETREAT);
  
  	ADD_CUSTOM_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_HOP_RETREAT);
  	ADD_CUSTOM_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_RANGE_ATTACK1);
  	ADD_CUSTOM_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_ATTACK_STRAFE);
  	ADD_CUSTOM_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_ATTACK_STRAFE_REVERSE);
  	ADD_CUSTOM_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_GROUP_ATTACK);
  	ADD_CUSTOM_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_GROUP_RETREAT);
  	ADD_CUSTOM_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_CHASE_ENEMY);
  	ADD_CUSTOM_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_COVER_WAIT);
  	ADD_CUSTOM_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_GROUP_RALLEY);
  	
  	ADD_CUSTOM_ACTIVITY(CNPC_Houndeye,	ACT_HOUND_GUARD);
  
  	g_interactionHoundeyeGroupAttack				= CBaseCombatCharacter::GetInteractionID();
  	g_interactionHoundeyeGroupRetreat				= CBaseCombatCharacter::GetInteractionID();
  	g_interactionHoundeyeGroupRalley				= CBaseCombatCharacter::GetInteractionID();
  
  	AI_LOAD_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_HOP_RETREAT);
  	AI_LOAD_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_RANGE_ATTACK1);
  	AI_LOAD_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_ATTACK_STRAFE);
  	AI_LOAD_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_ATTACK_STRAFE_REVERSE);
  	AI_LOAD_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_GROUP_ATTACK);
  	AI_LOAD_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_GROUP_RETREAT);
  	AI_LOAD_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_CHASE_ENEMY);
  	AI_LOAD_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_COVER_WAIT);
  	AI_LOAD_SCHEDULE(CNPC_Houndeye,	SCHED_HOUND_GROUP_RALLEY);
  }
  
  #endif
  LINK_ENTITY_TO_CLASS( npc_houndeye, CNPC_Houndeye );
  #ifdef HOE_DLL
  #else
  IMPLEMENT_CUSTOM_AI( npc_houndeye, CNPC_Houndeye );
  #endif
  BEGIN_DATADESC( CNPC_Houndeye )
  
  	DEFINE_FIELD( m_fAsleep,					FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_fDontBlink,				FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_flNextSecondaryAttack,	FIELD_TIME ),
  	DEFINE_FIELD( m_bLoopClockwise,			FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_pEnergyWave,				FIELD_CLASSPTR ),
  	DEFINE_FIELD( m_flEndEnergyWaveTime,		FIELD_TIME ),	
  #ifdef HOE_DLL
  	DEFINE_FIELD( m_bShouldGib,				FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_flTimeStartIdle,		FIELD_TIME ),
  #endif
  
  END_DATADESC()
  
  //=========================================================
  // Classify - indicates this monster's place in the 
  // relationship table.
  //=========================================================
  Class_T	CNPC_Houndeye::Classify ( void )
  {
  	return	CLASS_HOUNDEYE;
  }
  
  #ifdef HOE_DLL
  //-----------------------------------------------------------------------------
  void CNPC_Houndeye::OnStateChange( NPC_STATE OldState, NPC_STATE NewState )
  {
  	if ( NewState == NPC_STATE_IDLE )
  	{
  		m_flTimeStartIdle = gpGlobals->curtime;
  	}
  }
  
  //-----------------------------------------------------------------------------
  void CNPC_Houndeye::GatherConditions( void )
  {
  	BaseClass::GatherConditions();
  
  	// Abort the blast if no enemies are in range
  	SetCondition( COND_HOUND_ENEMY_DISTANT );
  	if ( IsCurSchedule( SCHED_RANGE_ATTACK1 ) )
  	{
  		AIEnemiesIter_t iter;
  		for ( AI_EnemyInfo_t *pEnemyInfo = GetEnemies()->GetFirst(&iter); pEnemyInfo != NULL; pEnemyInfo = GetEnemies()->GetNext(&iter) )
  		{
  			CBaseEntity *pEnemy = pEnemyInfo->hEnemy;
  			if ( pEnemy && (pEnemy->GetAbsOrigin() - GetAbsOrigin()).Length() < HOUNDEYE_MAX_ATTACK_RADIUS * 0.75)
  			{
  				ClearCondition( COND_HOUND_ENEMY_DISTANT );
  				break;
  			}
  		}
  	}
  }
  #endif // HOE_DLL
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  // Input  :
  // Output :
  //-----------------------------------------------------------------------------
  int CNPC_Houndeye::RangeAttack1Conditions ( float flDot, float flDist )
  {
  	// I'm not allowed to attack if standing in another hound eye 
  	// (note houndeyes allowed to interpenetrate)
  	trace_t tr;
  	AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,0.1), 
  					GetHullMins(), GetHullMaxs(),
  					MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  	if (tr.startsolid)
  	{
  		CBaseEntity *pEntity = tr.m_pEnt;
  #ifdef HOE_DLL
  		if ( FClassnameIs(pEntity, "npc_houndeye") )
  #else
  		if (pEntity->Classify() == CLASS_HOUNDEYE)
  #endif
  		{
  			return( COND_NONE );
  		}
  	}
  
  	// If I'm really close to my enemy allow me to attack if 
  	// I'm facing regardless of next attack time
  	if (flDist < 100 && flDot >= 0.3)
  	{
  		return COND_CAN_RANGE_ATTACK1;
  	}
  	if ( gpGlobals->curtime < m_flNextAttack )
  	{
  		return( COND_NONE );
  	}
  	if (flDist > ( HOUNDEYE_MAX_ATTACK_RADIUS * 0.5 ))
  	{
  		return COND_TOO_FAR_TO_ATTACK;
  	}
  	if (flDot < 0.3)
  	{
  		return COND_NOT_FACING_ATTACK;
  	}
  	return COND_CAN_RANGE_ATTACK1;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Overidden for human grunts because they  hear the DANGER sound
  // Input  :
  // Output :
  //-----------------------------------------------------------------------------
  int CNPC_Houndeye::GetSoundInterests( void )
  {
  	return	SOUND_WORLD		|
  			SOUND_COMBAT	|
  			SOUND_PLAYER	|
  			SOUND_DANGER;
  }
  
  //=========================================================
  // MaxYawSpeed - allows each sequence to have a different
  // turn rate associated with it.
  //=========================================================
  float CNPC_Houndeye::MaxYawSpeed ( void )
  {
  	int ys = 90;
  
  	switch ( GetActivity() )
  	{
  	case ACT_CROUCHIDLE://sleeping!
  		ys = 0;
  		break;
  	case ACT_IDLE:	
  		ys = 60;
  		break;
  	case ACT_WALK:
  		ys = 90;
  		break;
  	case ACT_RUN:	
  		ys = 90;
  		break;
  	case ACT_TURN_LEFT:
  	case ACT_TURN_RIGHT:
  		ys = 90;
  		break;
  	}
  	return ys;
  }
  
  //=========================================================
  // HandleAnimEvent - catches the monster-specific messages
  // that occur when tagged animation frames are played.
  //=========================================================
  void CNPC_Houndeye::HandleAnimEvent( animevent_t *pEvent )
  {
  	switch ( pEvent->event )
  	{
  		case HOUND_AE_WARN:
  			// do stuff for this event.
  			WarnSound();
  			break;
  
  		case HOUND_AE_STARTATTACK:
  #ifdef HOE_DLL
  			CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(),
  				/*HOUNDEYE_MAX_ATTACK_RADIUS + NAI_Hull::Width( HULL_HUMAN )*/
  				HOUNDEYE_MAX_ATTACK_RADIUS * 0.75,
  				3.5, this );
  #endif
  			WarmUpSound();
  			break;
  
  		case HOUND_AE_HOPBACK:
  			{
  				float flGravity = GetCurrentGravity();
  
  				SetGroundEntity( NULL );
  
  				Vector forward;
  				AngleVectors( GetLocalAngles(), &forward );
  				Vector vecNewVelocity = forward * -200;
  				//jump up 36 inches
  				vecNewVelocity.z += sqrt( 2 * flGravity * 36 );
  				SetAbsVelocity( vecNewVelocity );
  				break;
  			}
  
  		case HOUND_AE_THUMP:
  			{
  			// emit the shockwaves
  			SonicAttack();
  #ifdef HOE_DLL
  			m_bShouldGib = true;
  			TakeDamage( CTakeDamageInfo( this, this, GetHealth(), DMG_PREVENT_PHYSICS_FORCE | DMG_GENERIC | DMG_ALWAYSGIB ) );
  #else
  			m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 5.0, 8.0 );
  #endif
  			}
  			break;
  
  		case HOUND_AE_ANGERSOUND1:
  			{
  				EmitSound( "NPC_Houndeye.Anger1" );
  			}
  			break;
  
  		case HOUND_AE_ANGERSOUND2:
  			{
  			EmitSound( "NPC_Houndeye.Anger2" );
  			}
  			break;
  
  		case HOUND_AE_CLOSE_EYE:
  			if ( !m_fDontBlink )
  			{
  #ifdef HOE_DLL
  				SetSkin( HOUNDEYE_EYE_FRAMES - 1 );
  #else // HOE_DLL
  			//<<TEMP>>	pev->skin = HOUNDEYE_EYE_FRAMES - 1;
  #endif // HOE_DLL
  			}
  			break;
  
  		case HOUND_AE_LEAP_HIT:
  			{
  				//<<TEMP>>return;//<<TEMP>>
  				SetGroundEntity( NULL );
  
  				//
  				// Take him off ground so engine doesn't instantly reset FL_ONGROUND.
  				//
  				UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
  				Vector vecJumpDir;
  				if ( GetEnemy() != NULL )
  				{
  					Vector vecEnemyEyePos = GetEnemy()->EyePosition();
  
  					float gravity = GetCurrentGravity();
  					if ( gravity <= 1 )
  					{
  						gravity = 1;
  					}
  
  					//
  					// How fast does the houndeye need to travel to reach my enemy's eyes given gravity?
  					//
  					float height = ( vecEnemyEyePos.z - GetAbsOrigin().z );
  					if ( height < 16 )
  					{
  						height = 16;
  					}
  					else if ( height > 120 )
  					{
  						height = 120;
  					}
  					float speed = sqrt( 2 * gravity * height );
  					float time = speed / gravity;
  
  					//
  					// Scale the sideways velocity to get there at the right time
  					//
  					vecJumpDir = vecEnemyEyePos - GetAbsOrigin();
  					vecJumpDir = vecJumpDir / time;
  
  					//
  					// Speed to offset gravity at the desired height.
  					//
  					vecJumpDir.z = speed;
  
  					//
  					// Don't jump too far/fast.
  					//
  					float distance = vecJumpDir.Length();
  					if ( distance > 650 )
  					{
  						vecJumpDir = vecJumpDir * ( 650.0 / distance );
  					}
  				}
  				else
  				{
  					Vector forward, up;
  					AngleVectors( GetLocalAngles(), &forward, NULL, &up );
  					//
  					// Jump hop, don't care where.
  					//
  					vecJumpDir = Vector( forward.x, forward.y, up.z ) * 350;
  				}
  
  				SetAbsVelocity( vecJumpDir );
  				m_flNextAttack = gpGlobals->curtime + 2;
  				break;
  			}
  		default:
  			BaseClass::HandleAnimEvent( pEvent );
  			break;
  	}
  }
  
  //=========================================================
  // Spawn
  //=========================================================
  void CNPC_Houndeye::Spawn()
  {
  	Precache( );
  
  	SetModel("models/houndeye.mdl");
  #ifdef HOE_DLL
  	// This is too wide (30x30) and too tall (72), but need 25x25
  	SetHullType( HULL_LARGE );
  #else
  	SetHullType(HULL_WIDE_SHORT);
  #endif
  	SetHullSizeNormal();
  #ifdef HOE_DLL
  	UTIL_SetSize( this, Vector(-25,-25,0), Vector(25,25,50) );
  #endif
  
  	SetSolid( SOLID_BBOX );
  	AddSolidFlags( FSOLID_NOT_STANDABLE );
  	SetMoveType( MOVETYPE_STEP );
  	SetBloodColor( BLOOD_COLOR_YELLOW );
  	m_iHealth			= sk_Houndeye_health.GetFloat();
  	m_flFieldOfView		= 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
  	m_NPCState			= NPC_STATE_NONE;
  	m_fAsleep			= false; // everyone spawns awake
  	m_fDontBlink		= false;
  	CapabilitiesAdd( bits_CAP_SQUAD );
  	CapabilitiesAdd( bits_CAP_MOVE_GROUND );
  	CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 );
  	CapabilitiesAdd( bits_CAP_TURN_HEAD );
  
  	m_flNextSecondaryAttack = 0;
  	m_bLoopClockwise		= random->RandomInt(0,1) ? true : false;
  
  	m_pEnergyWave			= NULL;
  	m_flEndEnergyWaveTime	= 0;
  
  	SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE ); 
  
  	NPCInit();
  }
  
  //=========================================================
  // Precache - precaches all resources this monster needs
  //=========================================================
  void CNPC_Houndeye::Precache()
  {
  	PrecacheModel("models/houndeye.mdl");
  
  	PrecacheScriptSound( "NPC_Houndeye.Anger1" );
  	PrecacheScriptSound( "NPC_Houndeye.Anger2" );
  	PrecacheScriptSound( "NPC_Houndeye.SpeakSentence" );
  	PrecacheScriptSound( "NPC_Houndeye.Idle" );
  	PrecacheScriptSound( "NPC_Houndeye.WarmUp" );
  	PrecacheScriptSound( "NPC_Houndeye.Warn" );
  	PrecacheScriptSound( "NPC_Houndeye.Alert" );
  	PrecacheScriptSound( "NPC_Houndeye.Die" );
  	PrecacheScriptSound( "NPC_Houndeye.Pain" );
  	PrecacheScriptSound( "NPC_Houndeye.Retreat" );
  	PrecacheScriptSound( "NPC_Houndeye.SonicAttack" );
  
  	PrecacheScriptSound( "NPC_Houndeye.GroupAttack" );
  	PrecacheScriptSound( "NPC_Houndeye.GroupFollow" );
  
  #ifdef HOE_DLL
  	m_iBeamRingEffectIndex = PrecacheModel( "sprites/physbeam.vmt" );
  #else
  	UTIL_PrecacheOther("grenade_energy");
  #endif
  	BaseClass::Precache();
  }	
  
  //------------------------------------------------------------------------------
  // Purpose :
  // Input   :
  // Output  :
  //------------------------------------------------------------------------------
  void CNPC_Houndeye::SpeakSentence( int sentenceType )
  {
  	if (gpGlobals->curtime > m_flSoundWaitTime)
  	{
  		EmitSound( "NPC_Houndeye.SpeakSentence" );
  		m_flSoundWaitTime = gpGlobals->curtime + 1.0;
  	}
  }
  
  //=========================================================
  // IdleSound
  //=========================================================
  void CNPC_Houndeye::IdleSound ( void )
  {
  	if (!FOkToMakeSound())
  	{
  		return;
  	}
  	CPASAttenuationFilter filter( this,"NPC_Houndeye.Idle" );
  	EmitSound( filter, entindex(),"NPC_Houndeye.Idle" );
  }
  
  //=========================================================
  // IdleSound
  //=========================================================
  void CNPC_Houndeye::WarmUpSound ( void )
  {
  	EmitSound( "NPC_Houndeye.WarmUp" );
  }
  
  //=========================================================
  // WarnSound 
  //=========================================================
  void CNPC_Houndeye::WarnSound ( void )
  {
  	EmitSound( "NPC_Houndeye.Warn" );
  }
  
  //=========================================================
  // AlertSound 
  //=========================================================
  void CNPC_Houndeye::AlertSound ( void )
  {
  	// only first squad member makes ALERT sound.
  	if ( m_pSquad && !m_pSquad->IsLeader( this ) )
  	{
  		return; 
  	}
  
  	EmitSound( "NPC_Houndeye.Alert" );
  }
  
  //=========================================================
  // DeathSound 
  //=========================================================
  #ifdef HOE_DLL
  void CNPC_Houndeye::DeathSound ( const CTakeDamageInfo &info )
  #else
  void CNPC_Houndeye::DeathSound ( void )
  #endif
  {
  	EmitSound( "NPC_Houndeye.Die" );
  }
  
  //=========================================================
  // PainSound 
  //=========================================================
  #ifdef HOE_DLL
  void CNPC_Houndeye::PainSound ( const CTakeDamageInfo &info )
  #else
  void CNPC_Houndeye::PainSound ( void )
  #endif
  {
  	EmitSound( "NPC_Houndeye.Pain" );
  }
  
  //=========================================================
  // WriteBeamColor - writes a color vector to the network 
  // based on the size of the group. 
  //=========================================================
  void CNPC_Houndeye::WriteBeamColor ( void )
  {
  	BYTE	bRed, bGreen, bBlue;
  
  	if ( m_pSquad )
  	{
  		switch ( m_pSquad->NumMembers() )
  		{
  		case 2:
  			// no case for 0 or 1, cause those are impossible for monsters in Squads.
  			bRed	= 101;
  			bGreen	= 133;
  			bBlue	= 221;
  			break;
  		case 3:
  			bRed	= 67;
  			bGreen	= 85;
  			bBlue	= 255;
  			break;
  		case 4:
  			bRed	= 62;
  			bGreen	= 33;
  			bBlue	= 211;
  			break;
  		default:
  			DevWarning( 2, "Unsupported Houndeye SquadSize!\n" );
  			bRed	= 188;
  			bGreen	= 220;
  			bBlue	= 255;
  			break;
  		}
  	}
  	else
  	{
  		// solo houndeye - weakest beam
  		bRed	= 188;
  		bGreen	= 220;
  		bBlue	= 255;
  	}
  	
  	WRITE_BYTE( bRed   );
  	WRITE_BYTE( bGreen );
  	WRITE_BYTE( bBlue  );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Plays the engine sound.
  //-----------------------------------------------------------------------------
  void CNPC_Houndeye::NPCThink(void)
  {
  	if (m_pEnergyWave)
  	{
  		if (gpGlobals->curtime > m_flEndEnergyWaveTime)
  		{
  			UTIL_Remove(m_pEnergyWave);
  			m_pEnergyWave = NULL;
  		}
  	}
  
  	// -----------------------------------------------------
  	//  Update collision group
  	//		While I'm running I'm allowed to penetrate
  	//		other houndeyes
  	// -----------------------------------------------------
  	Vector vVelocity;
  	GetVelocity( &vVelocity, NULL );
  	if (vVelocity.Length() > 10)
  	{
  		SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );
  	}
  	else 
  	{
  		// Don't go solid if resting in another houndeye 
  		trace_t tr;
  		AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,1), 
  						GetHullMins(), GetHullMaxs(),
  						MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  		if (!tr.startsolid)
  		{
  			SetCollisionGroup( COLLISION_GROUP_NONE );
  		}
  		else
  		{
  			SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );
  		}
  	}
  /*
  	if (GetCollisionGroup() == HL2COLLISION_GROUP_HOUNDEYE)
  	{
  		NDebugOverlay::Box(GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 0);
  	}
  	else
  	{
  		NDebugOverlay::Box(GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 0);
  	}
  */
  	BaseClass::NPCThink();
  }
  
  //------------------------------------------------------------------------------
  // Purpose : Broadcast retreat occasionally when hurt
  // Input   :
  // Output  :
  //------------------------------------------------------------------------------
  int CNPC_Houndeye::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  {
  	if (m_pSquad && random->RandomInt(0,10) == 10)
  	{
  		EmitSound( "NPC_Houndeye.Retreat" );
  		m_flSoundWaitTime = gpGlobals->curtime + 1.0;
  
  		m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRetreat, NULL, this );
  	}
  	
  	return BaseClass::OnTakeDamage_Alive( info );
  }
  
  //------------------------------------------------------------------------------
  // Purpose : Broadcast retreat when member of squad killed
  // Input   :
  // Output  :
  //------------------------------------------------------------------------------
  void CNPC_Houndeye::Event_Killed( const CTakeDamageInfo &info )
  {
  	EmitSound( "NPC_Houndeye.Retreat" );
  	m_flSoundWaitTime = gpGlobals->curtime + 1.0;
  
  	if (m_pSquad)
  	{
  		m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRetreat, NULL, this );
  	}
  
  #ifdef HOE_DLL
  	// Almost shut the eye
  	SetSkin( HOUNDEYE_EYE_FRAMES - 2 );
  #endif // HOE_DLL
  
  	BaseClass::Event_Killed( info );
  }
  
  //=========================================================
  // SonicAttack
  //=========================================================
  void CNPC_Houndeye::SonicAttack ( void )
  {
  	EmitSound( "NPC_Houndeye.SonicAttack" );
  
  	if (m_pEnergyWave)
  	{
  		UTIL_Remove(m_pEnergyWave);
  	}
  	Vector vFacingDir = EyeDirection3D( );
  #ifdef HOE_DLL
  		// blast circle
  		CBroadcastRecipientFilter filter;
  		te->BeamRingPoint( filter, 0.0, // FIXME: want TE_BEAMCYLINDER
  			GetAbsOrigin(),						//origin
  			0,									//start radius
  			HOUNDEYE_MAX_ATTACK_RADIUS * 2,		//end radius
  			m_iBeamRingEffectIndex,				//texture
  			0,									//halo index
  			0,									//start frame
  			0,									//framerate
  			0.5,								//life
  			32,									//width
  			0,									//spread
  			0,									//amplitude
  			255,								//r
  			255,								//g
  			255,								//b
  			50,									//a
  			0,									//speed
  			FBEAM_FADEOUT
  			);
  #else
  	m_pEnergyWave = (CEnergyWave*)Create( "energy_wave", EyePosition(), GetLocalAngles() );
  	m_flEndEnergyWaveTime = gpGlobals->curtime + 1; //<<TEMP>> magic
  	m_pEnergyWave->SetAbsVelocity( 100*vFacingDir );
  #endif
  	CBaseEntity *pEntity = NULL;
  	// iterate on all entities in the vicinity.
  	for ( CEntitySphereQuery sphere( GetAbsOrigin(), HOUNDEYE_MAX_ATTACK_RADIUS ); pEntity = sphere.GetCurrentEntity(); sphere.NextEntity() )
  	{
  #ifdef HOE_DLL
  		if ( FClassnameIs(pEntity, "npc_houndeye") )
  #else
  		if (pEntity->Classify()	== CLASS_HOUNDEYE)
  #endif
  		{
  			continue;
  		}
  
  		if (pEntity->GetFlags() & FL_NOTARGET)
  		{
  			continue;
  		}
  
  		IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
  
  		if ( pEntity->m_takedamage != DAMAGE_NO || pPhysicsObject)
  		{
  			// --------------------------
  			// Adjust damage by distance
  			// --------------------------
  			float flDist = (pEntity->WorldSpaceCenter() - GetAbsOrigin()).Length();
  			float flDamageAdjuster = 1-( flDist / HOUNDEYE_MAX_ATTACK_RADIUS );
  
  			// --------------------------
  			// Adjust damage by direction
  			// --------------------------
  			Vector forward;
  			AngleVectors( GetAbsAngles(), &forward );
  			Vector vEntDir		= (pEntity->GetAbsOrigin() - GetAbsOrigin());
  			VectorNormalize(vEntDir);
  			float flDotPr		= DotProduct(forward,vEntDir);
  			flDamageAdjuster   *= flDotPr;
  
  #ifdef HOE_DLL
  			if (flDamageAdjuster <= 0)
  #else
  			if (flDamageAdjuster < 0)
  #endif
  			{
  				continue;
  			}
  
  			// --------------------------
  			// Adjust damage by visibility
  			// --------------------------
  			if ( !FVisible( pEntity ) )
  			{
  				if ( pEntity->IsPlayer() )
  				{
  					// if this entity is a client, and is not in full view, inflict half damage. We do this so that players still 
  					// take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients
  					// so that monsters in other parts of the level don't take the damage and get pissed.
  					flDamageAdjuster *= 0.5;
  				}
  				else if ( !FClassnameIs( pEntity, "func_breakable" ) && !FClassnameIs( pEntity, "func_pushable" ) ) 
  				{
  					// do not hurt nonclients through walls, but allow damage to be done to breakables
  					continue;
  				}
  			}
  
  			// ------------------------------
  			//  Apply the damage
  			// ------------------------------
  			if (pEntity->m_takedamage != DAMAGE_NO)
  			{
  #ifdef HOE_DLL
  				// Only gib if entity is close to the blast
  				int damageType = DMG_SONIC;
  				if ( flDist < HOUNDEYE_MAX_ATTACK_RADIUS / 3 )
  					damageType |= DMG_ALWAYSGIB;
  				CTakeDamageInfo info( this, this, flDamageAdjuster * sk_Houndeye_dmg_blast.GetFloat(), damageType );
  #else
  				CTakeDamageInfo info( this, this, flDamageAdjuster * sk_Houndeye_dmg_blast.GetFloat(), DMG_SONIC | DMG_ALWAYSGIB );
  #endif
  				CalculateExplosiveDamageForce( &info, (pEntity->GetAbsOrigin() - GetAbsOrigin()), pEntity->GetAbsOrigin() );
  
  				pEntity->TakeDamage( info );
  
  				// Throw the player
  				if ( pEntity->IsPlayer() )
  				{
  					Vector forward;
  					AngleVectors( GetLocalAngles(), &forward );
  
  					Vector vecVelocity = pEntity->GetAbsVelocity();
  					vecVelocity	+= forward * 250 * flDamageAdjuster;
  					vecVelocity.z = 300 * flDamageAdjuster;
  					pEntity->SetAbsVelocity( vecVelocity );
  					pEntity->ViewPunch( QAngle(random->RandomInt(-20,20), 0, random->RandomInt(-20,20)) );
  				}
  			}
  			// ------------------------------
  			//  Apply physics foces
  			// ------------------------------
  			IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
  			if (pPhysicsObject)
  			{
  				float flForce	= flDamageAdjuster * 8000;
  				pPhysicsObject->ApplyForceCenter( (vEntDir+Vector(0,0,0.2)) * flForce );
  				pPhysicsObject->ApplyTorqueCenter( vEntDir * flForce );
  			}
  		}
  	}
  #ifdef HOE_DLL
  	ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), this, 100, 100, false );
  #endif
  }
  		
  //=========================================================
  // start task
  //=========================================================
  void CNPC_Houndeye::StartTask( const Task_t *pTask )
  {
  	switch ( pTask->iTask )
  	{
  	case TASK_HOUND_GET_PATH_TO_CIRCLE:
  	{
  		if (GetEnemy() == NULL)
  		{
  			TaskFail(FAIL_NO_ENEMY);
  		}
  		else 
  		{
  			Vector vTargetPos = GetEnemyLKP();
  			vTargetPos.z	= GetFloorZ(vTargetPos);
  
  #ifdef HOE_DLL
  			float flRadius = 150.0; // distance from enemy
  			float flArc = 90.0;
  			float flStepDist = 10.0;
  			Vector vDestPos = vTargetPos;
  			vDestPos.x += flRadius * cos( DEG2RAD(flArc) );
  			vDestPos.y += flRadius * sin( DEG2RAD(flArc) );
  			if (GetNavigator()->SetRadialGoal(vDestPos, vTargetPos, flRadius, flArc, flStepDist, m_bLoopClockwise))
  #else
  
  			if (GetNavigator()->SetRadialGoal(vTargetPos, random->RandomInt(50,500), 90, 175, m_bLoopClockwise))
  #endif
  			{
  				TaskComplete();
  				return;
  			}
  			TaskFail(FAIL_NO_ROUTE);
  		}
  		break;
  	}
  	case TASK_HOUND_REVERSE_STRAFE_DIR:
  	{
  		// Try the other direction
  		m_bLoopClockwise = (m_bLoopClockwise) ? false : true;
  		TaskComplete();
  		break;
  	}
  
  	// Override to set appropriate distances
  	case TASK_GET_PATH_TO_ENEMY_LOS:
  	{
  		float			flMaxRange	= HOUNDEYE_MAX_ATTACK_RADIUS * 0.9;
  		float			flMinRange	= HOUNDEYE_MIN_ATTACK_RADIUS;
  		Vector 			posLos;
  		bool			foundLos	= false;
  		
  		if (GetEnemy() != NULL)
  		{
  			foundLos = GetTacticalServices()->FindLos(GetEnemyLKP(),GetEnemy()->EyePosition(), flMinRange, flMaxRange, 0.0, &posLos);
  		}
  		else
  		{
  			TaskFail(FAIL_NO_TARGET);
  			return;
  		}
  
  		if (foundLos)
  		{
  			GetNavigator()->SetGoal( AI_NavGoal_t( posLos, ACT_RUN, AIN_HULL_TOLERANCE ) );
  		}
  		else
  		{
  			TaskFail(FAIL_NO_SHOOT);
  		}
  		break;
  	}
  
  	case TASK_HOUND_FALL_ASLEEP:
  		{
  			m_fAsleep = true; // signal that hound is lying down (must stand again before doing anything else!)
  			TaskComplete( true );
  			break;
  		}
  	case TASK_HOUND_WAKE_UP:
  		{
  			m_fAsleep = false; // signal that hound is standing again
  			TaskComplete( true );
  			break;
  		}
  	case TASK_HOUND_OPEN_EYE:
  		{
  			m_fDontBlink = false; // turn blinking back on and that code will automatically open the eye
  			TaskComplete( true );
  			break;
  		}
  	case TASK_HOUND_CLOSE_EYE:
  		{
  #ifdef HOE_DLL
  			SetSkin( 0 );
  #else
  //<<TEMP>>			pev->skin = 0;
  #endif
  			m_fDontBlink = true; // tell blink code to leave the eye alone.
  			break;
  		}
  	case TASK_HOUND_THREAT_DISPLAY:
  		{
  			SetIdealActivity( ACT_IDLE_ANGRY );
  			break;
  		}
  	case TASK_HOUND_HOP_BACK:
  		{
  			SetIdealActivity( ACT_LEAP );
  			break;
  		}
  	case TASK_RANGE_ATTACK1:
  		{
  			SetIdealActivity( ACT_RANGE_ATTACK1 );
  			break;
  		}
  	default: 
  		{
  			BaseClass::StartTask(pTask);
  			break;
  		}
  	}
  }
  
  //=========================================================
  // RunTask 
  //=========================================================
  void CNPC_Houndeye::RunTask( const Task_t *pTask )
  {
  	switch ( pTask->iTask )
  	{
  	case TASK_HOUND_THREAT_DISPLAY:
  		{
  			GetMotor()->SetIdealYawToTargetAndUpdate( GetEnemyLKP(), AI_KEEP_YAW_SPEED );
  
  			if ( IsActivityFinished() )
  			{
  				TaskComplete();
  			}
  			
  			break;
  		}
  	case TASK_HOUND_CLOSE_EYE:
  		{
  #ifdef HOE_DLL
  			if ( GetSkin() < HOUNDEYE_EYE_FRAMES - 1 )
  			{
  				SetSkin( GetSkin() + 1 );
  			}
  #else
  			/*//<<TEMP>>
  			if ( pev->skin < HOUNDEYE_EYE_FRAMES - 1 )
  			{
  				pev->skin++;
  			}
  			*/
  #endif
  			break;
  		}
  	case TASK_HOUND_HOP_BACK:
  		{
  			if ( IsActivityFinished() )
  			{
  				TaskComplete();
  			}
  			break;
  		}
  	default:
  		{
  			BaseClass::RunTask(pTask);
  			break;
  		}
  	}
  }
  
  
  //------------------------------------------------------------------------------
  // Purpose :
  // Input   :
  // Output  :
  //------------------------------------------------------------------------------
  void CNPC_Houndeye::PrescheduleThink ( void )
  {
  	BaseClass::PrescheduleThink();
  	
  	// if the hound is mad and is running, make hunt noises.
  	if ( m_NPCState == NPC_STATE_COMBAT && ( GetActivity() == ACT_RUN ) && random->RandomFloat( 0, 1 ) < 0.2 )
  	{
  		WarnSound();
  	}
  
  	// at random, initiate a blink if not already blinking or sleeping
  	if ( !m_fDontBlink )
  	{
  #ifdef HOE_DLL
  		if ( ( GetSkin() == 0 ) && random->RandomInt(0,0x7F) == 0 )
  		{
  			SetSkin( HOUNDEYE_EYE_FRAMES - 1 );
  		}
  		else if ( GetSkin() != 0 )
  		{
  			SetSkin( GetSkin() - 1 );
  		}
  #else
  		/*//<<TEMP>>//<<TEMP>>
  		if ( ( pev->skin == 0 ) && random->RandomInt(0,0x7F) == 0 )
  		{// start blinking!
  			pev->skin = HOUNDEYE_EYE_FRAMES - 1;
  		}
  		else if ( pev->skin != 0 )
  		{// already blinking
  			pev->skin--;
  		}
  		*/
  #endif
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Override base class activiites
  // Input  :
  // Output :
  //-----------------------------------------------------------------------------
  Activity CNPC_Houndeye::NPC_TranslateActivity( Activity eNewActivity )
  {
  	if ( eNewActivity == ACT_IDLE && m_NPCState == NPC_STATE_COMBAT )
  	{
  		return ACT_IDLE_ANGRY;
  	}
  	return eNewActivity;
  }
  
  //------------------------------------------------------------------------------
  // Purpose :
  // Input   :
  // Output  :
  //------------------------------------------------------------------------------
  int CNPC_Houndeye::TranslateSchedule( int scheduleType )
  {
  	switch( scheduleType )
  	{
  	case SCHED_IDLE_STAND:
  		{
  #ifdef HOE_DLL
  			if ( IsInSquad() && !GetSquad()->IsLeader( this ) && !m_fAsleep &&
  				 ( m_flTimeStartIdle < gpGlobals->curtime - random->RandomInt(15,25) ) )
  			{
  				return SCHED_HOUND_SLEEP;
  			}
  #endif
  			return BaseClass::TranslateSchedule( scheduleType );
  		}
  	case SCHED_RANGE_ATTACK1:
  		{
  			return SCHED_HOUND_RANGE_ATTACK1;
  		}
  	case SCHED_CHASE_ENEMY_FAILED:
  		{
  			return SCHED_COMBAT_FACE;
  		}
  	default:
  		{
  			return BaseClass::TranslateSchedule ( scheduleType );
  		}
  	}
  }
  
  //------------------------------------------------------------------------------
  // Purpose : Is anyone in the squad currently attacking
  // Input   :
  // Output  :
  //------------------------------------------------------------------------------
  bool CNPC_Houndeye::IsAnyoneInSquadAttacking( void )
  {
  	if (!m_pSquad)
  	{
  		return false;
  	}
  
  	AISquadIter_t iter;
  	for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
  	{
  		if (pSquadMember->IsCurSchedule(SCHED_HOUND_RANGE_ATTACK1))
  		{
  			return true;
  		}
  	}
  	return false;
  }
  
  //=========================================================
  // SelectSchedule
  //=========================================================
  int CNPC_Houndeye::SelectSchedule( void )
  {
  #ifdef HOE_DLL
  	if ( m_fAsleep )
  	{
  		// Check interrupts to the SCHED_HOUND_SLEEP to see how to react
  		if ( HasCondition( COND_NEW_ENEMY ) ||
  			 HasCondition( COND_LIGHT_DAMAGE ) ||
  			 HasCondition( COND_HEAVY_DAMAGE ) )
  		{
  			return SCHED_HOUND_WAKE_URGENT;
  		}
  		else if ( HasCondition( COND_HEAR_COMBAT ) ||
  			 HasCondition( COND_HEAR_PLAYER ) ||
  			 HasCondition( COND_HEAR_WORLD ) )
  		{
  #define HOUNDEYE_SOUND_STARTLE_VOLUME	128 // how loud a sound has to be to badly scare a sleeping houndeye
  			CSound *pSound = GetBestSound();
  			if ( pSound &&
  				 pSound->GetSoundOrigin().DistToSqr( EarPosition() ) < HOUNDEYE_SOUND_STARTLE_VOLUME * HOUNDEYE_SOUND_STARTLE_VOLUME )
  			{
  				return SCHED_HOUND_WAKE_URGENT;
  			}
  		}
  		else
  		{
  			return SCHED_HOUND_WAKE_LAZY;
  		}
  	}
  #endif
  
  	switch	( m_NPCState )
  	{
  	case NPC_STATE_IDLE:
  	case NPC_STATE_ALERT:
  		{
  			if ( HasCondition(COND_LIGHT_DAMAGE) ||
  				 HasCondition(COND_HEAVY_DAMAGE) )
  			{
  				return SCHED_TAKE_COVER_FROM_ORIGIN;
  			}
  			break;
  		}
  	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 a group attack was requested attack even if attack conditions not met
  			if ( HasCondition( COND_HOUND_GROUP_ATTACK ))
  			{
  				// Check that I'm not standing in another hound eye 
  				// before attacking
  				trace_t tr;
  				AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,1), 
  								GetHullMins(), GetHullMaxs(),
  								MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  				if (!tr.startsolid)
  				{
  					return SCHED_HOUND_GROUP_ATTACK;
  				}
  
  				// Otherwise attack as soon as I can
  				else
  				{
  					m_flNextAttack = gpGlobals->curtime;
  					SCHED_HOUND_ATTACK_STRAFE;
  				}
  			}
  
  			// If a group retread was requested 
  			if ( HasCondition( COND_HOUND_GROUP_RETREAT ))
  			{
  				return SCHED_HOUND_GROUP_RETREAT;
  			}
  
  			if ( HasCondition( COND_LIGHT_DAMAGE ) | 
  				 HasCondition( COND_HEAVY_DAMAGE ) )
  			{
  				if ( random->RandomFloat( 0 , 1 ) <= 0.4 )
  				{
  					trace_t tr;
  					Vector forward;
  					AngleVectors( GetAbsAngles(), &forward );
  					AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + forward * -128, 
  						GetHullMins(), GetHullMaxs(),
  						MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  
  					if ( tr.fraction == 1.0 )
  					{
  						// it's clear behind, so the hound will jump
  						return SCHED_HOUND_HOP_RETREAT;
  					}
  				}
  
  				return SCHED_TAKE_COVER_FROM_ENEMY;
  			}
  
  			// If a group rally was requested 
  			if ( HasCondition( COND_HOUND_GROUP_RALLEY ))
  			{
  				return SCHED_HOUND_GROUP_RALLEY;
  			}
  
  			if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  			{
  				if (m_pSquad && random->RandomInt(0,4) == 0)
  				{
  					if (!IsAnyoneInSquadAttacking())
  					{
  						EmitSound( "NPC_Houndeye.GroupAttack" );
  						
  						m_flSoundWaitTime = gpGlobals->curtime + 1.0;
  
  						m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupAttack, NULL, this );
  						return SCHED_HOUND_GROUP_ATTACK;
  					}
  				}
  				//<<TEMP>>comment
  				SetCollisionGroup( COLLISION_GROUP_NONE );
  				return SCHED_RANGE_ATTACK1;
  			}
  			else
  			{
  				if (m_pSquad && random->RandomInt(0,5) == 0)
  				{
  					if (!IsAnyoneInSquadAttacking())
  					{
  						EmitSound( "NPC_Houndeye.GroupFollow" );
  
  						m_flSoundWaitTime = gpGlobals->curtime + 1.0;
  
  						m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRalley, NULL, this );
  						return SCHED_HOUND_ATTACK_STRAFE;
  					}
  				}
  				return SCHED_HOUND_ATTACK_STRAFE;
  			}
  			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_Houndeye::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
  {
  	if (interactionType == g_interactionHoundeyeGroupAttack)
  	{
  		SetCondition(COND_HOUND_GROUP_ATTACK);
  		return true;
  	}
  	else if (interactionType == g_interactionHoundeyeGroupRetreat)
  	{
  		SetCondition(COND_HOUND_GROUP_RETREAT);
  		return true;
  	}
  	else if (interactionType == g_interactionHoundeyeGroupRalley)
  	{
  		SetCondition(COND_HOUND_GROUP_RALLEY);
  		SetTarget(sourceEnt);
  		m_bLoopClockwise = false;
  		return true;
  	}
  
  	return false;
  }
  
  
  //-----------------------------------------------------------------------------
  //
  // Schedules
  //
  //-----------------------------------------------------------------------------
  
  #ifdef HOE_DLL
  
  AI_BEGIN_CUSTOM_NPC( npc_houndeye, CNPC_Houndeye )
  
  	DECLARE_TASK( TASK_HOUND_CLOSE_EYE )
  	DECLARE_TASK( TASK_HOUND_OPEN_EYE )
  	DECLARE_TASK( TASK_HOUND_THREAT_DISPLAY )
  	DECLARE_TASK( TASK_HOUND_FALL_ASLEEP )
  	DECLARE_TASK( TASK_HOUND_WAKE_UP )
  	DECLARE_TASK( TASK_HOUND_HOP_BACK )
  
  	DECLARE_TASK( TASK_HOUND_GET_PATH_TO_CIRCLE )
  	DECLARE_TASK( TASK_HOUND_REVERSE_STRAFE_DIR )
  	
  	DECLARE_CONDITION( COND_HOUND_GROUP_ATTACK )
  	DECLARE_CONDITION( COND_HOUND_GROUP_RETREAT )
  	DECLARE_CONDITION( COND_HOUND_GROUP_RALLEY )
  	DECLARE_CONDITION( COND_HOUND_ENEMY_DISTANT )
  
  	DECLARE_ACTIVITY( ACT_HOUND_GUARD )
  
  	DECLARE_INTERACTION( g_interactionHoundeyeGroupAttack )
  	DECLARE_INTERACTION( g_interactionHoundeyeGroupRetreat )
  	DECLARE_INTERACTION( g_interactionHoundeyeGroupRalley )
  
  	//=========================================================
  	// SCHED_HOUND_ATTACK_STRAFE
  	//
  	//  Run a cirle around my enemy
  	//=========================================================
  	DEFINE_SCHEDULE 
  	(
  		SCHED_HOUND_ATTACK_STRAFE  ,
  
  		"	Tasks "
  		"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_HOUND_ATTACK_STRAFE_REVERSE"
  		"		TASK_HOUND_GET_PATH_TO_CIRCLE	0"
  		"		TASK_RUN_PATH					0"
  		"		TASK_WAIT_FOR_MOVEMENT			0"
  		""
  		"	Interrupts "
  		"		COND_WEAPON_SIGHT_OCCLUDED"
  		"		COND_NEW_ENEMY"
  		"		COND_ENEMY_DEAD"
  		"		COND_HEAVY_DAMAGE"
  		"		COND_CAN_RANGE_ATTACK1"
  		"		COND_HOUND_GROUP_ATTACK"
  		"		COND_HOUND_GROUP_RETREAT"
  	)
  
  	//=========================================================
  	// SCHED_HOUND_ATTACK_STRAFE_REVERSE
  	//
  	//  Run a cirle around my enemy
  	//=========================================================
  	DEFINE_SCHEDULE 
  	(
  		SCHED_HOUND_ATTACK_STRAFE_REVERSE  ,
  
  		"	Tasks "
  		"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_HOUND_CHASE_ENEMY"
  		"		TASK_HOUND_REVERSE_STRAFE_DIR	0"
  		"		TASK_HOUND_GET_PATH_TO_CIRCLE	0"
  		"		TASK_RUN_PATH					0"
  		"		TASK_WAIT_FOR_MOVEMENT			0"
  		""
  		"	Interrupts "
  		"		COND_WEAPON_SIGHT_OCCLUDED"
  		"		COND_NEW_ENEMY"
  		"		COND_ENEMY_DEAD"
  		"		COND_HEAVY_DAMAGE"
  		"		COND_CAN_RANGE_ATTACK1"
  		"		COND_HOUND_GROUP_ATTACK"
  		"		COND_HOUND_GROUP_RETREAT"
  	)
  
  	//========================================================
  	// SCHED_HOUND_CHASE_ENEMY
  	//=========================================================
  	DEFINE_SCHEDULE
  	(
  		SCHED_HOUND_CHASE_ENEMY,
  
  		"	Tasks"
  		"		TASK_SET_TOLERANCE_DISTANCE		30	 "
  		"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  		"		TASK_GET_PATH_TO_ENEMY			0"
  		"		TASK_RUN_PATH					0"
  		"		TASK_WAIT_FOR_MOVEMENT			0"
  		""
  		"	Interrupts"
  		"		COND_NEW_ENEMY"
  		"		COND_ENEMY_DEAD"
  		"		COND_CAN_RANGE_ATTACK1"
  		"		COND_HOUND_GROUP_ATTACK"
  		"		COND_HOUND_GROUP_RETREAT"
  	)
  
  	//=========================================================
  	// SCHED_HOUND_GROUP_ATTACK
  	//
  	//  Face enemy, pause, then attack
  	//=========================================================
  	DEFINE_SCHEDULE 
  	(
  		SCHED_HOUND_GROUP_ATTACK  ,
  
  		"	Tasks "
  		"		TASK_STOP_MOVING			0"
  		"		TASK_FACE_ENEMY				0"
  		"		TASK_SET_ACTIVITY			ACTIVITY:ACT_IDLE_ANGRY"
  		"		TASK_SPEAK_SENTENCE			0"
  		"		TASK_WAIT					1"
  		"		TASK_SET_SCHEDULE			SCHEDULE:SCHED_HOUND_RANGE_ATTACK1"
  		""
  		"	Interrupts "
  		"		COND_NEW_ENEMY"
  		"		COND_ENEMY_DEAD"
  		"		COND_HEAVY_DAMAGE"
  	)	
  		
  	//=========================================================
  	// > SCHED_HOUND_GROUP_RETREAT
  	//
  	//		Take cover from enemy! 
  	//=========================================================
  	DEFINE_SCHEDULE
  	(
  		SCHED_HOUND_GROUP_RETREAT,
  
  		"	Tasks"
  		"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_HOUND_ATTACK_STRAFE"
  		"		TASK_STOP_MOVING				0"
  		"		TASK_WAIT						0.2"
  		"		TASK_SET_TOLERANCE_DISTANCE		24"
  		"		TASK_FIND_COVER_FROM_ENEMY		0"
  		"		TASK_RUN_PATH					0"
  		"		TASK_WAIT_FOR_MOVEMENT			0"
  		"		TASK_REMEMBER					MEMORY:INCOVER"
  		"		TASK_FACE_ENEMY					0"
  		"		TASK_SET_ACTIVITY				ACTIVITY:ACT_IDLE"	// Translated to cover
  		"		TASK_SET_SCHEDULE				SCHEDULE:SCHED_HOUND_COVER_WAIT"
  		""
  		"	Interrupts"
  		"		COND_NEW_ENEMY"
  	)
  
  	//=========================================================
  	// > SCHED_HOUND_GROUP_RALLEY
  	//
  	//		Run to rally hound! 
  	//=========================================================
  	DEFINE_SCHEDULE
  	(
  		SCHED_HOUND_GROUP_RALLEY,
  
  		"	Tasks"
  		"		TASK_SET_TOLERANCE_DISTANCE		30"
  		"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_HOUND_ATTACK_STRAFE"
  		"		TASK_GET_PATH_TO_TARGET			0"
  		"		TASK_RUN_PATH					0"
  		"		TASK_WAIT_FOR_MOVEMENT			0"
  		""
  		"	Interrupts"
  		"		COND_NEW_ENEMY"
  		"		COND_ENEMY_DEAD"
  		"		COND_HEAVY_DAMAGE"
  		"		COND_HOUND_GROUP_ATTACK"
  		"		COND_HOUND_GROUP_RETREAT"
  	)
  
  	//=========================================================
  	// > SCHED_HOUND_COVER_WAIT
  	//
  	//		Wait in cover till enemy see's me or I take damage
  	//=========================================================
  	DEFINE_SCHEDULE
  	(
  		SCHED_HOUND_COVER_WAIT,
  
  		"	Tasks"
  		"		TASK_WAIT						2"
  		""
  		"	Interrupts"
  		"		COND_SEE_ENEMY"
  		"		COND_LIGHT_DAMAGE"
  	)			
  
  	//=========================================================
  	// > SCHED_HOUND_RANGE_ATTACK1
  	//=========================================================
  	DEFINE_SCHEDULE
  	(
  		SCHED_HOUND_RANGE_ATTACK1,
  
  		"	Tasks"
  		"		 TASK_STOP_MOVING			0"
  		"		 TASK_SET_ACTIVITY			ACTIVITY:ACT_IDLE_ANGRY"
  		"		 TASK_FACE_IDEAL			0"
  		"		 TASK_RANGE_ATTACK1			0"
  		""
  		"	Interrupts"
  		//"		COND_LIGHT_DAMAGE"	// don't interupt on small damage
  		"		COND_HEAVY_DAMAGE"
  		"		COND_HOUND_ENEMY_DISTANT" // HOE_DLL
  	)
  
  	//=========================================================
  	// > SCHED_HOUND_HOP_RETREAT
  	//=========================================================
  	DEFINE_SCHEDULE
  	(
  		SCHED_HOUND_HOP_RETREAT,
  
  		"	Tasks"
  		"		 TASK_STOP_MOVING				0"
  		"		 TASK_HOUND_HOP_BACK			0"
  		"		 TASK_SET_SCHEDULE				SCHEDULE:SCHED_TAKE_COVER_FROM_ENEMY"
  		""
  		"	Interrupts"
  	)
  
  	//=========================================================
  	// > SCHED_HOUND_SLEEP
  	//=========================================================
  	DEFINE_SCHEDULE
  	(
  		SCHED_HOUND_SLEEP,
  
  		"	Tasks"
  		"		TASK_STOP_MOVING			0"
  		"		TASK_SET_ACTIVITY			ACTIVITY:ACT_IDLE"
  		"		TASK_WAIT_RANDOM			5"
  		"		TASK_PLAY_SEQUENCE			ACTIVITY:ACT_CROUCH"
  		"		TASK_SET_ACTIVITY			ACTIVITY:ACT_CROUCHIDLE"
  		"		TASK_HOUND_FALL_ASLEEP		0"
  		"		TASK_WAIT_RANDOM			25"
  		"		TASK_HOUND_CLOSE_EYE		0"
  		""
  		"	Interrupts"
  		"		COND_LIGHT_DAMAGE"
  		"		COND_HEAVY_DAMAGE"
  		"		COND_NEW_ENEMY"
  		"		COND_HEAR_COMBAT"
  		"		COND_HEAR_PLAYER"
  		"		COND_HEAR_WORLD"
  	)
  
  	//=========================================================
  	// > SCHED_HOUND_WAKE_URGENT
  	//=========================================================
  	DEFINE_SCHEDULE
  	(
  		SCHED_HOUND_WAKE_URGENT,
  
  		"	Tasks"
  		"		TASK_HOUND_OPEN_EYE			0"
  		"		TASK_PLAY_SEQUENCE			ACTIVITY:ACT_HOP"
  		"		TASK_FACE_IDEAL				0"
  		"		TASK_HOUND_WAKE_UP			0"
  		""
  		"	Interrupts"
  	)
  
  	//=========================================================
  	// > SCHED_HOUND_WAKE_URGENT
  	//=========================================================
  	DEFINE_SCHEDULE
  	(
  		SCHED_HOUND_WAKE_LAZY,
  
  		"	Tasks"
  		"		TASK_HOUND_OPEN_EYE			0"
  		"		TASK_WAIT_RANDOM			2.5"
  		"		TASK_PLAY_SEQUENCE			ACTIVITY:ACT_STAND"
  		"		TASK_HOUND_WAKE_UP			0"
  		""
  		"	Interrupts"
  	)
  
  AI_END_CUSTOM_NPC()
  
  #else // HOE_DLL
  
  //=========================================================
  // SCHED_HOUND_ATTACK_STRAFE
  //
  //  Run a cirle around my enemy
  //=========================================================
  AI_DEFINE_SCHEDULE 
  (
  	SCHED_HOUND_ATTACK_STRAFE  ,
  
  	"	Tasks "
  	"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_HOUND_ATTACK_STRAFE_REVERSE"
  	"		TASK_HOUND_GET_PATH_TO_CIRCLE	0"
  	"		TASK_RUN_PATH					0"
  	"		TASK_WAIT_FOR_MOVEMENT			0"
  	""
  	"	Interrupts "
  	"		COND_WEAPON_SIGHT_OCCLUDED"
  	"		COND_NEW_ENEMY"
  	"		COND_ENEMY_DEAD"
  	"		COND_HEAVY_DAMAGE"
  	"		COND_CAN_RANGE_ATTACK1"
  	"		COND_HOUND_GROUP_ATTACK"
  	"		COND_HOUND_GROUP_RETREAT"
  );
  
  //=========================================================
  // SCHED_HOUND_ATTACK_STRAFE_REVERSE
  //
  //  Run a cirle around my enemy
  //=========================================================
  AI_DEFINE_SCHEDULE 
  (
  	SCHED_HOUND_ATTACK_STRAFE_REVERSE  ,
  
  	"	Tasks "
  	"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_HOUND_CHASE_ENEMY"
  	"		TASK_HOUND_REVERSE_STRAFE_DIR	0"
  	"		TASK_HOUND_GET_PATH_TO_CIRCLE	0"
  	"		TASK_RUN_PATH					0"
  	"		TASK_WAIT_FOR_MOVEMENT			0"
  	""
  	"	Interrupts "
  	"		COND_WEAPON_SIGHT_OCCLUDED"
  	"		COND_NEW_ENEMY"
  	"		COND_ENEMY_DEAD"
  	"		COND_HEAVY_DAMAGE"
  	"		COND_CAN_RANGE_ATTACK1"
  	"		COND_HOUND_GROUP_ATTACK"
  	"		COND_HOUND_GROUP_RETREAT"
  );
  
  //========================================================
  // SCHED_HOUND_CHASE_ENEMY
  //=========================================================
  AI_DEFINE_SCHEDULE
  (
  	SCHED_HOUND_CHASE_ENEMY,
  
  	"	Tasks"
  	"		TASK_SET_TOLERANCE_DISTANCE		30	 "
  	"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  	"		TASK_GET_PATH_TO_ENEMY			0"
  	"		TASK_RUN_PATH					0"
  	"		TASK_WAIT_FOR_MOVEMENT			0"
  	""
  	"	Interrupts"
  	"		COND_NEW_ENEMY"
  	"		COND_ENEMY_DEAD"
  	"		COND_CAN_RANGE_ATTACK1"
  	"		COND_HOUND_GROUP_ATTACK"
  	"		COND_HOUND_GROUP_RETREAT"
  );
  
  //=========================================================
  // SCHED_HOUND_GROUP_ATTACK
  //
  //  Face enemy, pause, then attack
  //=========================================================
  AI_DEFINE_SCHEDULE 
  (
  	SCHED_HOUND_GROUP_ATTACK  ,
  
  	"	Tasks "
  	"		TASK_STOP_MOVING			0"
  	"		TASK_FACE_ENEMY				0"
  	"		TASK_SET_ACTIVITY			ACTIVITY:ACT_IDLE_ANGRY"
  	"		TASK_SPEAK_SENTENCE			0"
  	"		TASK_WAIT					1"
  	"		TASK_SET_SCHEDULE			SCHEDULE:SCHED_HOUND_RANGE_ATTACK1"
  	""
  	"	Interrupts "
  	"		COND_NEW_ENEMY"
  	"		COND_ENEMY_DEAD"
  	"		COND_HEAVY_DAMAGE"
  );	
  	
  //=========================================================
  // > SCHED_HOUND_GROUP_RETREAT
  //
  //		Take cover from enemy! 
  //=========================================================
  AI_DEFINE_SCHEDULE
  (
  	SCHED_HOUND_GROUP_RETREAT,
  
  	"	Tasks"
  	"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_HOUND_ATTACK_STRAFE"
  	"		TASK_STOP_MOVING				0"
  	"		TASK_WAIT						0.2"
  	"		TASK_SET_TOLERANCE_DISTANCE		24"
  	"		TASK_FIND_COVER_FROM_ENEMY		0"
  	"		TASK_RUN_PATH					0"
  	"		TASK_WAIT_FOR_MOVEMENT			0"
  	"		TASK_REMEMBER					MEMORY:INCOVER"
  	"		TASK_FACE_ENEMY					0"
  	"		TASK_SET_ACTIVITY				ACTIVITY:ACT_IDLE"	// Translated to cover
  	"		TASK_SET_SCHEDULE				SCHEDULE:SCHED_HOUND_COVER_WAIT"
  	""
  	"	Interrupts"
  	"		COND_NEW_ENEMY"
  );
  
  //=========================================================
  // > SCHED_HOUND_GROUP_RALLEY
  //
  //		Run to rally hound! 
  //=========================================================
  AI_DEFINE_SCHEDULE
  (
  	SCHED_HOUND_GROUP_RALLEY,
  
  	"	Tasks"
  	"		TASK_SET_TOLERANCE_DISTANCE		30"
  	"		TASK_SET_FAIL_SCHEDULE			SCHEDULE:SCHED_HOUND_ATTACK_STRAFE"
  	"		TASK_GET_PATH_TO_TARGET			0"
  	"		TASK_RUN_PATH					0"
  	"		TASK_WAIT_FOR_MOVEMENT			0"
  	""
  	"	Interrupts"
  	"		COND_NEW_ENEMY"
  	"		COND_ENEMY_DEAD"
  	"		COND_HEAVY_DAMAGE"
  	"		COND_HOUND_GROUP_ATTACK"
  	"		COND_HOUND_GROUP_RETREAT"
  );
  
  //=========================================================
  // > SCHED_HOUND_COVER_WAIT
  //
  //		Wait in cover till enemy see's me or I take damage
  //=========================================================
  AI_DEFINE_SCHEDULE
  (
  	SCHED_HOUND_COVER_WAIT,
  
  	"	Tasks"
  	"		TASK_WAIT						2"
  	""
  	"	Interrupts"
  	"		COND_SEE_ENEMY"
  	"		COND_LIGHT_DAMAGE"
  );				
  
  //=========================================================
  // > SCHED_HOUND_RANGE_ATTACK1
  //=========================================================
  AI_DEFINE_SCHEDULE
  (
  	SCHED_HOUND_RANGE_ATTACK1,
  
  	"	Tasks"
  	"		 TASK_STOP_MOVING			0"
  	"		 TASK_SET_ACTIVITY			ACTIVITY:ACT_IDLE_ANGRY"
  	"		 TASK_FACE_IDEAL			0"
  	"		 TASK_RANGE_ATTACK1			0"
  	""
  	"	Interrupts"
  	//"		COND_LIGHT_DAMAGE"	// don't interupt on small damage
  	"		COND_HEAVY_DAMAGE"
  );
  
  //=========================================================
  // > SCHED_HOUND_HOP_RETREAT
  //=========================================================
  AI_DEFINE_SCHEDULE
  (
  	SCHED_HOUND_HOP_RETREAT,
  
  	"	Tasks"
  	"		 TASK_STOP_MOVING				0"
  	"		 TASK_HOUND_HOP_BACK			0"
  	"		 TASK_SET_SCHEDULE				SCHEDULE:SCHED_TAKE_COVER_FROM_ENEMY"
  	""
  	"	Interrupts"
  );
  
  #endif // HOE_DLL
  