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

#include "cbase.h"
#include "npcevent.h"
#include "ai_basenpc_physicsflyer.h"
#include "soundenvelope.h"
#include "ai_hint.h"
#include "ai_moveprobe.h"
#include "ai_squad.h"
#include "beam_shared.h"
#include "explode.h"
#include "globalstate.h"
#include "soundent.h"
#include "npc_citizen17.h"
#include "gib.h"
#include "smoke_trail.h"
#include "spotlightend.h"
#include "IEffects.h"
#include "items.h"
#include "ai_route.h"
#include "player_pickup.h"
#include "weapon_physcannon.h"
#include "hl2_player.h"

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

//-----------------------------------------------------------------------------
// Parameters for how the scanner relates to citizens.
//-----------------------------------------------------------------------------
#define SCANNER_CIT_INSPECT_DELAY		10		// Check for citizens this often
#define	SCANNER_CIT_INSPECT_GROUND_DIST	500		// How far to look for citizens to inspect
#define	SCANNER_CIT_INSPECT_FLY_DIST	1500	// How far to look for citizens to inspect

#define SCANNER_CIT_INSPECT_LENGTH		5		// How long does the inspection last
#define SCANNER_HINT_INSPECT_LENGTH		5		// How long does the inspection last
#define SCANNER_SOUND_INSPECT_LENGTH	5		// How long does the inspection last

#define SCANNER_HINT_INSPECT_DELAY		15		// Check for hint nodes this often
	
#define	SCANNER_BANK_RATE				30
#define	SCANNER_MAX_SPEED				250
#define	SCANNER_MAX_DIVE_BOMB_SPEED		2500
#define SCANNER_SQUAD_FLY_DIST			500		// How far to scanners stay apart
#define SCANNER_SQUAD_HELP_DIST			4000	// How far will I fly to help

#define	SPOTLIGHT_WIDTH					32

#define SCANNER_SPOTLIGHT_NEAR_DIST		64
#define SCANNER_SPOTLIGHT_FAR_DIST		256
#define SCANNER_SPOTLIGHT_FLY_HEIGHT	72
#define SCANNER_NOSPOTLIGHT_FLY_HEIGHT	72

#define SCANNER_FLASH_MIN_DIST			900		// How far does flash effect enemy
#define SCANNER_FLASH_MAX_DIST			1200	// How far does flash effect enemy
#define	SCANNER_FLASH_MAX_VALUE			240		// How bright is maximum flash

#define SCANNER_PHOTO_NEAR_DIST			64
#define SCANNER_PHOTO_FAR_DIST			128

#define	SCANNER_FOLLOW_DIST				128

#define SCANNER_ATTACK_NEAR_DIST		150		// Fly attack min distance
#define SCANNER_ATTACK_FAR_DIST			300		// Fly attack max distance
#define SCANNER_ATTACK_RANGE			350		// Attack max distance
#define	SCANNER_ATTACK_MIN_DELAY		8		// Min time between attacks
#define	SCANNER_ATTACK_MAX_DELAY		12		// Max time between attacks
#define	SCANNER_EVADE_TIME				1		// How long to evade after take damage

#define	SCANNER_NUM_GIBS				6		// Number of gibs in gib file

// Sentences
#define SCANNER_SENTENCE_ATTENTION	0
#define SCANNER_SENTENCE_HANDSUP	1
#define SCANNER_SENTENCE_PROCEED	2
#define SCANNER_SENTENCE_CURIOUS	3

// Strider Scout Scanners
#define SCANNER_SCOUT_MAX_SPEED			150

//------------------------------------
// Spawnflags
//------------------------------------
#define SF_CSCANNER_NO_DYNAMIC_LIGHT	(1 << 16)
#define SF_CSCANNER_STRIDER_SCOUT		(1 << 17)

ConVar	sk_scanner_health( "sk_scanner_health","0");
ConVar	sk_scanner_dmg_dive( "sk_scanner_dmg_dive","0");
ConVar	g_debug_cscanner( "g_debug_cscanner", "0" );

//-----------------------------------------------------------------------------
// Think contexts
//-----------------------------------------------------------------------------
static const char *s_pDiveBombSoundThinkContext = "DiveBombSoundThinkContext";


//-----------------------------------------------------------------------------
// Private activities.
//-----------------------------------------------------------------------------
static int ACT_SCANNER_SMALL_FLINCH_ALERT = 0;
static int ACT_SCANNER_SMALL_FLINCH_COMBAT = 0;
static int ACT_SCANNER_INSPECT = 0;
static int ACT_SCANNER_WALK_ALERT = 0;
static int ACT_SCANNER_WALK_COMBAT = 0;
static int ACT_SCANNER_FLARE = 0;
static int ACT_SCANNER_RETRACT = 0;
static int ACT_SCANNER_FLARE_PRONGS = 0;
static int ACT_SCANNER_RETRACT_PRONGS = 0;
static int ACT_SCANNER_FLARE_START = 0;

//-----------------------------------------------------------------------------
// Interactions
//-----------------------------------------------------------------------------
int	g_interactionScannerInspect				= 0;
int	g_interactionScannerInspectBegin		= 0;
int g_interactionScannerInspectHandsUp		= 0;
int g_interactionScannerInspectShowArmband	= 0;//<<TEMP>>still to be completed
int	g_interactionScannerInspectDone			= 0;
int g_interactionScannerSupportEntity		= 0;
int g_interactionScannerSupportPosition		= 0;

//-----------------------------------------------------------------------------
// Animation events
//------------------------------------------------------------------------
int AE_SCANNER_CLOSED;

//-----------------------------------------------------------------------------
// Attachment points
//-----------------------------------------------------------------------------
#define SCANNER_ATTACHMENT_LIGHT	"light"
#define SCANNER_ATTACHMENT_FLASH	1
#define SCANNER_ATTACHMENT_LPRONG	2
#define SCANNER_ATTACHMENT_RPRONG	3

//-----------------------------------------------------------------------------
// Other defines.
//-----------------------------------------------------------------------------
#define SCANNER_MAX_BEAMS		4

//-----------------------------------------------------------------------------
// States for the scanner's sound.
//-----------------------------------------------------------------------------
enum ScannerFlyMode_t
{
	SCANNER_FLY_PHOTO = 0,		// Fly close to photograph entity
	SCANNER_FLY_PATROL,			// Fly slowly around the enviroment
	SCANNER_FLY_FAST,			// Fly quickly around the enviroment
	SCANNER_FLY_CHASE,			// Fly quickly around the enviroment
	SCANNER_FLY_SPOT,			// Fly above enity in spotlight position
	SCANNER_FLY_ATTACK,			// Get in my enemies face for spray or flash
	SCANNER_FLY_DIVE,			// Divebomb - only done when dead
	SCANNER_FLY_FOLLOW,			// Following a target
};

enum ScannerInspectAct_t
{
	SCANNER_INSACT_HANDS_UP,
	SCANNER_INSACT_SHOWARMBAND,
};

class CBeam;
class CSprite;
class SmokeTrail;
class CSpotlightEnd;


class CNPC_CScanner : public CAI_BasePhysicsFlyingBot, public CDefaultPlayerPickupVPhysics
{
DECLARE_CLASS( CNPC_CScanner, CAI_BasePhysicsFlyingBot );

public:
	CNPC_CScanner();

	Class_T			Classify( void ) { return(CLASS_SCANNER); }
	int				GetSoundInterests( void ) { return (SOUND_WORLD|SOUND_COMBAT|SOUND_PLAYER|SOUND_DANGER); }

	void			GatherConditions( void );
	void			Event_Killed( const CTakeDamageInfo &info );
	int				OnTakeDamage_Alive( const CTakeDamageInfo &info );
	int				OnTakeDamage_Dying( const CTakeDamageInfo &info );
	void			TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr );
	void			Gib(void);

	void			OnStateChange( NPC_STATE eOldState, NPC_STATE eNewState );
	void			ClampMotorForces( Vector &linear, AngularImpulse &angular );

	int				DrawDebugTextOverlays(void);

	bool			FValidateHintType(CAI_Hint *pHint);
	float			GetHeadTurnRate( void );

	virtual void	VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
	
	// 	CDefaultPlayerPickupVPhysics
	void			OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason );
	void			OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason );

	virtual int		TranslateSchedule( int scheduleType );
	Disposition_t	IRelationType(CBaseEntity *pTarget);

	void			IdleSound( void );
	void			DeathSound( void );
	void			AlertSound( void );
	void			PainSound( void );

	void			NPCThink( void );
	
	void			PrescheduleThink( void );
	int				MeleeAttack1Conditions ( float flDot, float flDist );
	void			Precache(void);
	void			RunTask( const Task_t *pTask );
	int				SelectSchedule(void);
	bool			ShouldPlayIdleSound( void );
	void			ScannerEmitSound( const char *pszSoundName );
	void			Spawn(void);
	void			Activate();
	void			StartTask( const Task_t *pTask );
	void			OnScheduleChange( void );
	void			UpdateOnRemove( void );
	void			DeployMine();
	float			GetMaxSpeed();

	void			PostRunStopMoving()	{} // scanner can use "movement" activities but not be moving

	Activity		NPC_TranslateActivity( Activity eNewActivity );

	void			HandleAnimEvent( animevent_t *pEvent );

	void			InputDisableSpotlight( inputdata_t &inputdata );
	void			InputInspectTargetPhoto( inputdata_t &inputdata );
	void			InputInspectTargetSpotlight( inputdata_t &inputdata );
	void			InputDeployMine( inputdata_t &inputdata );
	void			InputEquipMine( inputdata_t &inputdata );
	void			InputShouldInspect( inputdata_t &inputdata );
	void			InputSetFlightSpeed( inputdata_t &inputdata );
	void			InputSetFollowTarget( inputdata_t &inputdata );
	void			InputClearFollowTarget( inputdata_t &inputdata );
	void			InputSetDistanceOverride( inputdata_t &inputdata );

	void			InspectTarget( inputdata_t &inputdata, ScannerFlyMode_t eFlyMode );

public:
	bool			HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);
	void			SpeakSentence( int sentenceType );

	// ------------------------------
	//	Inspecting
	// ------------------------------
	Vector			m_vInspectPos;
	float			m_fInspectEndTime;
	float			m_fCheckCitizenTime;	// Time to look for citizens to harass
	float			m_fCheckHintTime;		// Time to look for hints to inspect
	bool			m_bShouldInspect;
	bool			m_bOnlyInspectPlayers;
	bool			m_bNeverInspectPlayers;

	void			SetInspectTargetToEnt(CBaseEntity *pEntity, float fInspectDuration);
	void			SetInspectTargetToPos(const Vector &vInspectPos, float fInspectDuration);
	void			SetInspectTargetToHint(CAI_Hint *pHint, float fInspectDuration);
	void			ClearInspectTarget(void);
	bool			HaveInspectTarget(void);
	Vector			InspectTargetPosition(void);
	bool			IsValidInspectTarget(CBaseEntity *pEntity);
	CBaseEntity*	BestInspectTarget(void);
	void			RequestInspectSupport(void);

	bool			IsStriderScout() { return HasSpawnFlags( SF_CSCANNER_STRIDER_SCOUT ); }

	// ------------------------
	//  Death Cleanup
	// ------------------------
	CTakeDamageInfo m_KilledInfo;

	// ------------------------
	//  Photographing
	// ------------------------
	float			m_fNextPhotographTime;
	CSprite*		m_pEyeFlash;

	void			TakePhoto( void );
	void			BlindFlashTarget( CBaseEntity *pTarget );

	// ------------------------------
	//	Spotlight
	// ------------------------------
	Vector			m_vSpotlightTargetPos;
	Vector			m_vSpotlightCurrentPos;
	CHandle<CBeam>	m_hSpotlight;
	CHandle<CSpotlightEnd> m_hSpotlightTarget;
	Vector			m_vSpotlightDir;
	Vector			m_vSpotlightAngVelocity;
	float			m_flSpotlightCurLength;
	float			m_flSpotlightMaxLength;
	float			m_flSpotlightGoalWidth;
	float			m_fNextSpotlightTime;
	int				m_nHaloSprite;
	
	void			SpotlightUpdate(void);
	Vector			SpotlightTargetPos(void);
	Vector			SpotlightCurrentPos(void);
	void			SpotlightCreate(void);
	void			SpotlightDestroy(void);

private:
	bool	MovingToInspectTarget( void );
	bool	GetGoalDirection( Vector *vOut );
	bool	IsEnemyPlayerInSuit();
	float	GetGoalDistance( void );

	bool	IsHeldByPhyscannon( );
	void	StartSmokeTrail( void );
	Vector	IdealGoalForMovement( const Vector &goalPos, const Vector &startPos, float idealRange, float idealHeight );

	// Take damage from being thrown by a physcannon 
	void TakeDamageFromPhyscannon( CBasePlayer *pPlayer );

	// Take damage from physics impacts
	void TakeDamageFromPhysicsImpact( int index, gamevcollisionevent_t *pEvent );

	// Do we have a physics attacker?
	CBasePlayer *HasPhysicsAttacker( float dt );

	// Purpose:
	void BlendPhyscannonLaunchSpeed();

	virtual void		StopLoopingSounds(void);

	CSoundPatch			*m_pEngineSound;

	// Movement

	float				m_flFlyNoiseBase;
	float				m_flEngineStallTime;
	float				m_fNextFlySoundTime;
	ScannerFlyMode_t	m_nFlyMode;

	Vector m_vecDiveBombDirection;		// The direction we dive bomb. Calculated at the moment of death.
	float m_flDiveBombRollForce;		// Used for roll while dive bombing.

	// physics influence
	CHandle<CBasePlayer>	m_hPhysicsAttacker;
	float					m_flLastPhysicsInfluenceTime;

	float				m_flGoalOverrideDistance;

	// Pose parameters

	int m_nPoseTail;
	int m_nPoseDynamo;
	int m_nPoseFlare;
	int m_nPoseFaceVert;
	int m_nPoseFaceHoriz;

	bool m_bIsClawScanner;	// Formerly the shield scanner.
	bool m_bIsOpen;			// Only for claw scanner

	bool m_bHasSpoken;

	COutputEvent		m_OnPhotographPlayer;
	COutputEvent		m_OnPhotographNPC;

	bool				OverridePathMove( CBaseEntity *pMoveTarget, float flInterval );
	bool				OverrideMove(float flInterval);
	void				MoveToTarget(float flInterval, const Vector &MoveTarget);
	void				MoveToSpotlight(float flInterval);
	void				MoveToPhotograph(float flInterval);
	void				MoveToAttack(float flInterval);
	void				MoveToDivebomb(float flInterval);
	void				MoveExecute_Alive(float flInterval);
	float				MinGroundDist(void);
	Vector				VelocityToEvade(CBaseCombatCharacter *pEnemy);
	void				PlayFlySound(void);

	void				SetBanking( float flInterval );
	void				UpdateHead( float flInterval );
	inline CBaseEntity *EntityToWatch( void );

	void DiveBombSoundThink();

	// Attacks

	SmokeTrail			*m_pSmokeTrail;

	bool				m_bNoLight;
	bool				m_bPhotoTaken;

	void				AttackPreFlash(void);
	void				AttackFlash(void);
	void				AttackFlashBlind(void);
	void				AttackDivebomb(void);
	void				AttackDivebombCollide(float flInterval);

	DEFINE_CUSTOM_AI;

	// Custom interrupt conditions
	enum
	{
		COND_SCANNER_FLY_CLEAR = BaseClass::NEXT_CONDITION,
		COND_SCANNER_FLY_BLOCKED,							
		COND_SCANNER_HAVE_INSPECT_TARGET,							
		COND_SCANNER_INSPECT_DONE,							
		COND_SCANNER_CAN_PHOTOGRAPH,
		COND_SCANNER_SPOT_ON_TARGET,
		COND_SCANNER_GRABBED_BY_PHYSCANNON,
		COND_SCANNER_RELEASED_FROM_PHYSCANNON,
	};

	// Custom schedules
	enum
	{
		SCHED_SCANNER_PATROL = BaseClass::NEXT_SCHEDULE,
		SCHED_SCANNER_SPOTLIGHT_HOVER,
		SCHED_SCANNER_SPOTLIGHT_INSPECT_POS,
		SCHED_SCANNER_SPOTLIGHT_INSPECT_CIT,
		SCHED_SCANNER_PHOTOGRAPH_HOVER,
		SCHED_SCANNER_PHOTOGRAPH,
		SCHED_SCANNER_ATTACK_HOVER,
		SCHED_SCANNER_ATTACK_FLASH,
		SCHED_SCANNER_ATTACK_DIVEBOMB,
		SCHED_SCANNER_CHASE_ENEMY,
		SCHED_SCANNER_CHASE_TARGET,
		SCHED_SCANNER_MOVE_TO_INSPECT,
		SCHED_SCANNER_FOLLOW_HOVER,
		SCHED_SCANNER_HELD_BY_PHYSCANNON,
	};

	// Custom tasks
	enum
	{
		TASK_SCANNER_SET_FLY_PHOTO = BaseClass::NEXT_TASK,
		TASK_SCANNER_SET_FLY_PATROL,
		TASK_SCANNER_SET_FLY_CHASE,
		TASK_SCANNER_SET_FLY_SPOT,
		TASK_SCANNER_SET_FLY_ATTACK,
		TASK_SCANNER_SET_FLY_DIVE,
		TASK_SCANNER_PHOTOGRAPH,
		TASK_SCANNER_ATTACK_PRE_FLASH,
		TASK_SCANNER_ATTACK_FLASH,
		TASK_SCANNER_SPOT_INSPECT_ON,
		TASK_SCANNER_SPOT_INSPECT_WAIT,
		TASK_SCANNER_SPOT_INSPECT_OFF,
		TASK_SCANNER_CLEAR_INSPECT_TARGET,
		TASK_SCANNER_GET_PATH_TO_INSPECT_TARGET,
	};

	DECLARE_DATADESC();
};

BEGIN_DATADESC( CNPC_CScanner )

	DEFINE_SOUNDPATCH( m_pEngineSound ),

	DEFINE_EMBEDDED( m_KilledInfo ),
	DEFINE_FIELD( m_flGoalOverrideDistance,	FIELD_FLOAT ),
	DEFINE_FIELD( m_bPhotoTaken,			FIELD_BOOLEAN ),
	DEFINE_FIELD( m_vInspectPos,			FIELD_VECTOR ),
	DEFINE_FIELD( m_fInspectEndTime,		FIELD_TIME ),
	DEFINE_FIELD( m_fCheckCitizenTime,		FIELD_TIME ),
	DEFINE_FIELD( m_fCheckHintTime,			FIELD_TIME ),
	DEFINE_KEYFIELD( m_bShouldInspect,		FIELD_BOOLEAN,	"ShouldInspect" ),
	DEFINE_KEYFIELD( m_bOnlyInspectPlayers, FIELD_BOOLEAN,  "OnlyInspectPlayers" ),
	DEFINE_KEYFIELD( m_bNeverInspectPlayers,FIELD_BOOLEAN,  "NeverInspectPlayers" ),
	DEFINE_FIELD( m_fNextPhotographTime,	FIELD_TIME ),
//	DEFINE_FIELD( m_pEyeFlash,				FIELD_CLASSPTR ),
	DEFINE_FIELD( m_vSpotlightTargetPos,	FIELD_POSITION_VECTOR ),
	DEFINE_FIELD( m_vSpotlightCurrentPos,	FIELD_POSITION_VECTOR ),
// don't save (recreated after restore/transition)
//	DEFINE_FIELD( m_hSpotlight,				FIELD_EHANDLE ),
//	DEFINE_FIELD( m_hSpotlightTarget,		FIELD_EHANDLE ),
	DEFINE_FIELD( m_vSpotlightDir,			FIELD_VECTOR ),
	DEFINE_FIELD( m_vSpotlightAngVelocity,	FIELD_VECTOR ),
	DEFINE_FIELD( m_flSpotlightCurLength,	FIELD_FLOAT ),
	DEFINE_FIELD( m_fNextSpotlightTime,		FIELD_TIME ),
	DEFINE_FIELD( m_nHaloSprite,			FIELD_INTEGER ),
	DEFINE_FIELD( m_fNextFlySoundTime,		FIELD_TIME ),
	DEFINE_FIELD( m_nFlyMode,				FIELD_INTEGER ),
	DEFINE_FIELD( m_nPoseTail,				FIELD_INTEGER ),
	DEFINE_FIELD( m_nPoseDynamo,			FIELD_INTEGER ),
	DEFINE_FIELD( m_nPoseFlare,				FIELD_INTEGER ),
	DEFINE_FIELD( m_nPoseFaceVert,			FIELD_INTEGER ),
	DEFINE_FIELD( m_nPoseFaceHoriz,			FIELD_INTEGER ),

	DEFINE_FIELD( m_bIsClawScanner,			FIELD_BOOLEAN ),
	DEFINE_FIELD( m_bIsOpen,				FIELD_BOOLEAN ),

	// DEFINE_FIELD( m_bHasSpoken,			FIELD_BOOLEAN ),

	DEFINE_FIELD( m_pSmokeTrail,			FIELD_CLASSPTR ),
	DEFINE_FIELD( m_flFlyNoiseBase,			FIELD_FLOAT ),
	DEFINE_FIELD( m_flEngineStallTime,		FIELD_TIME ),

	DEFINE_FIELD( m_vecDiveBombDirection,	FIELD_VECTOR ),
	DEFINE_FIELD( m_flDiveBombRollForce,	FIELD_FLOAT ),

	DEFINE_KEYFIELD( m_flSpotlightMaxLength,	FIELD_FLOAT,	"SpotlightLength"),
	DEFINE_KEYFIELD( m_flSpotlightGoalWidth,	FIELD_FLOAT,	"SpotlightWidth"),

	// Physics Influence
	DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ),
	DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ),

	DEFINE_KEYFIELD( m_bNoLight, FIELD_BOOLEAN, "SpotlightDisabled" ),

	DEFINE_INPUTFUNC( FIELD_VOID, "DisableSpotlight", InputDisableSpotlight ),
	DEFINE_INPUTFUNC( FIELD_STRING, "InspectTargetPhoto", InputInspectTargetPhoto ),
	DEFINE_INPUTFUNC( FIELD_STRING, "InspectTargetSpotlight", InputInspectTargetSpotlight ),
	DEFINE_INPUTFUNC( FIELD_INTEGER, "InputShouldInspect", InputShouldInspect ),
	DEFINE_INPUTFUNC( FIELD_STRING, "SetFollowTarget", InputSetFollowTarget ),
	DEFINE_INPUTFUNC( FIELD_VOID, "ClearFollowTarget", InputClearFollowTarget ),
	DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDistanceOverride", InputSetDistanceOverride ),

	DEFINE_INPUTFUNC( FIELD_INTEGER, "SetFlightSpeed", InputSetFlightSpeed ),
	DEFINE_INPUTFUNC( FIELD_STRING, "DeployMine", InputDeployMine ),
	DEFINE_INPUTFUNC( FIELD_STRING, "EquipMine", InputEquipMine ),

	DEFINE_OUTPUT( m_OnPhotographPlayer, "OnPhotographPlayer" ),
	DEFINE_OUTPUT( m_OnPhotographNPC, "OnPhotographNPC" ),

	DEFINE_THINKFUNC( DiveBombSoundThink ),

END_DATADESC()


LINK_ENTITY_TO_CLASS(npc_cscanner, CNPC_CScanner);


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CNPC_CScanner::CNPC_CScanner()
{
#ifdef _DEBUG
	m_vInspectPos.Init();
	m_vSpotlightTargetPos.Init();
	m_vSpotlightCurrentPos.Init();
	m_vSpotlightDir.Init();
	m_vSpotlightAngVelocity.Init();
	m_vCurrentBanking.Init();
#endif
	m_bShouldInspect = true;
	m_bOnlyInspectPlayers = false;
	m_bNeverInspectPlayers = false;
	m_pEngineSound = NULL;
	m_bHasSpoken = false;

	char szMapName[256];
	Q_strncpy(szMapName, STRING(gpGlobals->mapname), sizeof(szMapName) );
	Q_strlower(szMapName);

	if( !Q_strnicmp( szMapName, "d3_c17", 6 ) )
	{
		// Streetwar scanners are claw scanners
		m_bIsClawScanner = true;
	}
	else
	{
		m_bIsClawScanner = false;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_CScanner::Spawn(void)
{
	// Check for user error
	if (m_flSpotlightMaxLength <= 0)
	{
		DevMsg("CNPC_CScanner::Spawn: Invalid spotlight length <= 0, setting to 500\n");
		m_flSpotlightMaxLength = 500;
	}
	
	if (m_flSpotlightGoalWidth <= 0)
	{
		DevMsg("CNPC_CScanner::Spawn: Invalid spotlight width <= 0, setting to 100\n");
		m_flSpotlightGoalWidth = 100;
	}

	if (m_flSpotlightGoalWidth > MAX_BEAM_WIDTH )
	{
		DevMsg("CNPC_CScanner::Spawn: Invalid spotlight width %.1f (max %.1f).\n", m_flSpotlightGoalWidth, MAX_BEAM_WIDTH );
		m_flSpotlightGoalWidth = MAX_BEAM_WIDTH; 
	}

	Precache();

	if( m_bIsClawScanner )
	{
		SetModel( "models/shield_scanner.mdl");
	}
	else
	{
		SetModel( "models/combine_scanner.mdl");
	}

	SetHullType( HULL_TINY_CENTERED );
	SetHullSizeNormal();

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );

	SetMoveType( MOVETYPE_VPHYSICS );

	m_bloodColor		= DONT_BLEED;
	m_iHealth			= sk_scanner_health.GetFloat();
	SetViewOffset( Vector(0, 0, 10) );		// Position of the eyes relative to NPC's origin.
	m_flFieldOfView		= 0.2;
	m_NPCState			= NPC_STATE_NONE;
	
	SetNavType( NAV_FLY );
	
	AddFlag( FL_FLY );

	// This entity cannot be dissolved by the combine balls,
	// nor does it get killed by the mega physcannon.
	AddEFlags( EFL_NO_DISSOLVE | EFL_NO_MEGAPHYSCANNON_RAGDOLL );

	m_flGoalOverrideDistance = 0.0f;

	// ------------------------------------
	//	Init all class vars 
	// ------------------------------------
	m_vInspectPos			= vec3_origin;
	m_fInspectEndTime		= 0;
	m_fCheckCitizenTime		= gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY;
	m_fCheckHintTime		= gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY;
	m_fNextPhotographTime	= 0;

	m_vSpotlightTargetPos	= vec3_origin;
	m_vSpotlightCurrentPos	= vec3_origin;

	m_hSpotlight			= NULL;
	m_hSpotlightTarget		= NULL;

	AngleVectors( GetLocalAngles(), &m_vSpotlightDir );
	m_vSpotlightAngVelocity = vec3_origin;

	m_pEyeFlash				= 0;
	m_fNextSpotlightTime	= 0;
	m_fNextFlySoundTime		= 0;
	m_nFlyMode				= SCANNER_FLY_PATROL;
	m_vCurrentBanking		= m_vSpotlightDir;
	m_fHeadYaw				= 0;
	m_pSmokeTrail			= NULL;
	m_flSpotlightCurLength	= m_flSpotlightMaxLength;

	SetCurrentVelocity( vec3_origin );

	Vector	bobAmount;
	
	bobAmount.x = random->RandomFloat( -2.0f, 2.0f );
	bobAmount.y = random->RandomFloat( -2.0f, 2.0f );
	
	bobAmount.z = random->RandomFloat( 2.0f, 4.0f );

	if ( random->RandomInt( 0, 1 ) )
	{
		bobAmount.z *= -1.0f;
	}
	
	SetNoiseMod( bobAmount );

	// set flight speed
	m_flSpeed = GetMaxSpeed();

	m_nPoseTail = LookupPoseParameter( "tail_control" );
	m_nPoseDynamo = LookupPoseParameter( "dynamo_wheel" );
	m_nPoseFlare = LookupPoseParameter( "alert_control" );
	m_nPoseFaceVert = LookupPoseParameter( "flex_vert" );
	m_nPoseFaceHoriz = LookupPoseParameter( "flex_horz" );

	// --------------------------------------------

	CapabilitiesAdd( bits_CAP_MOVE_FLY | bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_SKIP_NAV_GROUND_CHECK );
	CapabilitiesAdd( bits_CAP_INNATE_MELEE_ATTACK1 );

	m_bPhotoTaken = false;

	NPCInit();

	// Watch for this error state
	if ( m_bOnlyInspectPlayers && m_bNeverInspectPlayers )
	{
		Assert( 0 );
		Warning( "ERROR: Scanner set to never and always inspect players!\n" );
	}

	m_flFlyNoiseBase = random->RandomFloat( 0, M_PI );

	m_flNextAttack = gpGlobals->curtime;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CNPC_CScanner::Activate()
{
	BaseClass::Activate();

	// Have to do this here because sprites do not go across level transitions
	m_pEyeFlash = CSprite::SpriteCreate( "sprites/blueflare1.vmt", GetLocalOrigin(), FALSE );
	m_pEyeFlash->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation );
	m_pEyeFlash->SetAttachment( this, LookupAttachment( SCANNER_ATTACHMENT_LIGHT ) );
	m_pEyeFlash->SetBrightness( 0 );
	m_pEyeFlash->SetScale( 1.4 );
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : eOldState - 
//			eNewState - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::OnStateChange( NPC_STATE eOldState, NPC_STATE eNewState )
{
	if (( eNewState == NPC_STATE_ALERT ) || ( eNewState == NPC_STATE_COMBAT ))
	{
		SetPoseParameter(m_nPoseFlare, 1.0f);
	}
	else
	{
		SetPoseParameter(m_nPoseFlare, 0);
	}
}


//------------------------------------------------------------------------------
// Purpose: Override to split in two when attacked
//------------------------------------------------------------------------------
int CNPC_CScanner::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
	// Turn off my spotlight when shot
	SpotlightDestroy();
	m_fNextSpotlightTime = gpGlobals->curtime + 2.0f;

	// Start smoking when we're nearly dead
	if ( m_iHealth < ( m_iMaxHealth - ( m_iMaxHealth / 4 ) ) )
	{
		StartSmokeTrail();
	}

	return (BaseClass::OnTakeDamage_Alive( info ));
}


//------------------------------------------------------------------------------
// Purpose: Override to split in two when attacked
//------------------------------------------------------------------------------
int CNPC_CScanner::OnTakeDamage_Dying( const CTakeDamageInfo &info )
{
	// do the damage
	m_iHealth -= info.GetDamage();
	
	if ( m_iHealth < -40 )
	{
		Gib();
		return 1;
	}

	return VPhysicsTakeDamage( info );
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_CScanner::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
{
	if ( info.GetDamageType() & DMG_BULLET)
	{
		g_pEffects->Ricochet(ptr->endpos,ptr->plane.normal);
	}
	
	BaseClass::TraceAttack( info, vecDir, ptr );
}


//-----------------------------------------------------------------------------
// Take damage from being thrown by a physcannon 
//-----------------------------------------------------------------------------
#define SCANNER_SMASH_SPEED 250.0	// How fast a scanner must slam into something to take full damage
void CNPC_CScanner::TakeDamageFromPhyscannon( CBasePlayer *pPlayer )
{
	CTakeDamageInfo info;
	info.SetDamageType( DMG_GENERIC );
	info.SetInflictor( this );
	info.SetAttacker( pPlayer );
	info.SetDamagePosition( GetAbsOrigin() );
	info.SetDamageForce( Vector( 1.0, 1.0, 1.0 ) );

	// Convert velocity into damage.
	Vector vel;
	VPhysicsGetObject()->GetVelocity( &vel, NULL );
	float flSpeed = vel.Length();

	float flFactor = flSpeed / SCANNER_SMASH_SPEED;

	// Clamp. Don't inflict negative damage or massive damage!
	flFactor = clamp( flFactor, 0.0f, 2.0f );
	float flDamage = m_iMaxHealth * flFactor;

#if 0
	Msg("Doing %f damage for %f speed!\n", flDamage, flSpeed );
#endif

	info.SetDamage( flDamage );
	TakeDamage( info );
}


//-----------------------------------------------------------------------------
// Take damage from physics impacts
//-----------------------------------------------------------------------------
void CNPC_CScanner::TakeDamageFromPhysicsImpact( int index, gamevcollisionevent_t *pEvent )
{
	CBaseEntity *pHitEntity = pEvent->pEntities[!index];

	// NOTE: Augment the normal impact energy scale here.
	float flDamageScale = PlayerHasMegaPhysCannon() ? 10.0f : 5.0f;

	// Scale by the mapmaker's energyscale
	flDamageScale *= m_impactEnergyScale;

	int damageType = 0;
	float damage = CalculateDefaultPhysicsDamage( index, pEvent, flDamageScale, true, damageType );
	if ( damage == 0 )
		return;

	Vector damagePos;
	pEvent->pInternalData->GetContactPoint( damagePos );
	Vector damageForce = pEvent->postVelocity[index] * pEvent->pObjects[index]->GetMass();
	if ( damageForce == vec3_origin )
	{
		// This can happen if this entity is motion disabled, and can't move.
		// Use the velocity of the entity that hit us instead.
		damageForce = pEvent->postVelocity[!index] * pEvent->pObjects[!index]->GetMass();
	}

	// FIXME: this doesn't pass in who is responsible if some other entity "caused" this collision
	PhysCallbackDamage( this, CTakeDamageInfo( pHitEntity, pHitEntity, damageForce, damagePos, damage, damageType ), *pEvent, index );
}


//-----------------------------------------------------------------------------
// Is the scanner being held?
//-----------------------------------------------------------------------------
bool CNPC_CScanner::IsHeldByPhyscannon( )
{
	return VPhysicsGetObject() && (VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD);
}

	
//------------------------------------------------------------------------------
// Physics impact
//------------------------------------------------------------------------------
#define SCANNER_SMASH_TIME	0.75		// How long after being thrown from a physcannon that a manhack is eligible to die from impact
void CNPC_CScanner::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
{
	BaseClass::VPhysicsCollision( index, pEvent );

	// Take no impact damage while being carried.
	if ( IsHeldByPhyscannon( ) )
		return;

	CBasePlayer *pPlayer = HasPhysicsAttacker( SCANNER_SMASH_TIME );
	if( pPlayer )
	{
		TakeDamageFromPhyscannon( pPlayer );
		return;
	}

	// It also can take physics damage from things thrown by the player.
	int otherIndex = !index;
	CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex];
	if ( pHitEntity )
	{
		if ( pHitEntity->HasPhysicsAttacker( 0.5f ) )
		{
			// It can take physics damage from things thrown by the player.
			TakeDamageFromPhysicsImpact( index, pEvent );
		}
		else if ( FClassnameIs( pHitEntity, "prop_combine_ball" ) )
		{
			// It also can take physics damage from a combine ball.
			TakeDamageFromPhysicsImpact( index, pEvent );
		}
	}
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::Gib( void )
{
	if ( IsMarkedForDeletion() )
		return;

	// Sparks
	for ( int i = 0; i < 4; i++ )
	{
		Vector sparkPos = GetAbsOrigin();
		sparkPos.x += random->RandomFloat(-12,12);
		sparkPos.y += random->RandomFloat(-12,12);
		sparkPos.z += random->RandomFloat(-12,12);
		g_pEffects->Sparks(sparkPos);
	}
	
	// Light
	CBroadcastRecipientFilter filter;
	te->DynamicLight( filter, 0.0, &WorldSpaceCenter(), 255, 180, 100, 0, 100, 0.1, 0 );

	// Spawn all gibs
	if( m_bIsClawScanner )
	{
		CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib1.mdl");
		CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib2.mdl");
		CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib3.mdl");
		CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib4.mdl");
		CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib5.mdl");
		CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib6.mdl");
	}
	else
	{
		CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib01.mdl" );
		CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib02.mdl" );
		CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib04.mdl" );
		CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib05.mdl" );
	}

	// Cover the gib spawn
	ExplosionCreate( WorldSpaceCenter(), GetAbsAngles(), this, 64, 64, false );

	// Turn off any smoke trail
	if ( m_pSmokeTrail )
	{
		m_pSmokeTrail->m_ParticleLifetime = 0;
		UTIL_Remove(m_pSmokeTrail);
		m_pSmokeTrail = NULL;
	}

	// FIXME: This is because we couldn't save/load the CTakeDamageInfo.
	// because it's midnight before the teamwide playtest. Real solution
	// is to add a datadesc to CTakeDamageInfo
	if ( m_KilledInfo.GetInflictor() )
	{
		BaseClass::Event_Killed( m_KilledInfo );
	}

	DeployMine();

	UTIL_Remove(this);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pPhysGunUser - 
//			bPunting - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
{
	m_hPhysicsAttacker = pPhysGunUser;
	m_flLastPhysicsInfluenceTime = gpGlobals->curtime;

	if ( reason == PUNTED_BY_CANNON )
	{
		// There's about to be a massive change in velocity. 
		// Think immediately to handle changes in m_vCurrentVelocity;
		SetNextThink( gpGlobals->curtime + 0.01f );
		
		m_flEngineStallTime = gpGlobals->curtime + 2.0f;
		ScannerEmitSound( "DiveBomb" );
	}
	else
	{
		SetCondition( COND_SCANNER_GRABBED_BY_PHYSCANNON );
		ClearCondition( COND_SCANNER_RELEASED_FROM_PHYSCANNON );
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pPhysGunUser - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
{
	m_hPhysicsAttacker = pPhysGunUser;
	m_flLastPhysicsInfluenceTime = gpGlobals->curtime;
	
	ClearCondition( COND_SCANNER_GRABBED_BY_PHYSCANNON );
	SetCondition( COND_SCANNER_RELEASED_FROM_PHYSCANNON );
	
	if ( Reason == LAUNCHED_BY_CANNON )
	{
		m_flEngineStallTime = gpGlobals->curtime + 2.0f;

		// There's about to be a massive change in velocity. 
		// Think immediately to handle changes in m_vCurrentVelocity;
		SetNextThink( gpGlobals->curtime + 0.01f );
		ScannerEmitSound( "DiveBomb" );
	}
}


//------------------------------------------------------------------------------
// Do we have a physics attacker?
//------------------------------------------------------------------------------
CBasePlayer *CNPC_CScanner::HasPhysicsAttacker( float dt )
{
	// If the player is holding me now, or I've been recently thrown
	// then return a pointer to that player
	if ( IsHeldByPhyscannon( ) || (gpGlobals->curtime - dt <= m_flLastPhysicsInfluenceTime) )
	{
		return m_hPhysicsAttacker;
	}
	return NULL;
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::StopLoopingSounds(void)
{
	CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
	controller.SoundDestroy( m_pEngineSound );
	m_pEngineSound = NULL;

	BaseClass::StopLoopingSounds();
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pInflictor - 
//			pAttacker - 
//			flDamage - 
//			bitsDamageType - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::Event_Killed( const CTakeDamageInfo &info )
{
	// Copy off the takedamage info that killed me, since we're not going to call
	// up into the base class's Event_Killed() until we gib. (gibbing is ultimate death)
	m_KilledInfo = info;	

	DeployMine();

	m_OnDeath.FireOutput( info.GetAttacker(), this );

	ClearInspectTarget();

	// Interrupt whatever schedule I'm on
	SetCondition(COND_SCHEDULE_DONE);

	// Remove spotlight
	SpotlightDestroy();

	// Add a random chance of spawning a battery...
	if ( !HasSpawnFlags(SF_NPC_NO_WEAPON_DROP) && random->RandomFloat( 0.0f, 1.0f) < 0.3f )
	{
		CItem *pBattery = (CItem*)CreateEntityByName("item_battery");
		if ( pBattery )
		{
			pBattery->SetAbsOrigin( GetAbsOrigin() );
			pBattery->SetAbsVelocity( GetAbsVelocity() );
			pBattery->SetLocalAngularVelocity( GetLocalAngularVelocity() );
			pBattery->ActivateWhenAtRest();
			pBattery->Spawn();
		}
	}

	// Remove sprite
	UTIL_Remove(m_pEyeFlash);
	m_pEyeFlash = NULL;

	// If I have an enemy and I'm up high, do a dive bomb (unless dissolved)
	if ( !m_bIsClawScanner && GetEnemy() != NULL && (info.GetDamageType() & DMG_DISSOLVE) == false )
	{
		Vector vecDelta = GetLocalOrigin() - GetEnemy()->GetLocalOrigin();
		if ( ( vecDelta.z > 120 ) && ( vecDelta.Length() > 360 ) )
		{	
			AttackDivebomb();
			return;
		}
	}

	Gib();
}


//------------------------------------------------------------------------------
// Purpose: Checks to see if we hit anything while dive bombing.
//------------------------------------------------------------------------------
void CNPC_CScanner::AttackDivebombCollide(float flInterval)
{
	//
	// Trace forward to see if I hit anything
	//
	Vector			checkPos = GetAbsOrigin() + (GetCurrentVelocity() * flInterval);
	trace_t			tr;
	CBaseEntity*	pHitEntity = NULL;
	AI_TraceHull( GetAbsOrigin(), checkPos, GetHullMins(), GetHullMaxs(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );

	if (tr.m_pEnt)
	{
		pHitEntity = tr.m_pEnt;

		// Did I hit an entity that isn't another scanner?
		if (pHitEntity && pHitEntity->Classify()!=CLASS_SCANNER)
		{
			if ( !pHitEntity->ClassMatches("item_battery") )
			{
				if ( !pHitEntity->IsWorld() )
				{
					CTakeDamageInfo info( this, this, sk_scanner_dmg_dive.GetFloat(), DMG_CLUB );
					CalculateMeleeDamageForce( &info, (tr.endpos - tr.startpos), tr.endpos );
					pHitEntity->TakeDamage( info );
				}
				Gib();
			}
		}
	}

	if (tr.fraction != 1.0)
	{
		// We've hit something so deflect our velocity based on the surface
		// norm of what we've hit
		if (flInterval > 0)
		{
			float moveLen	= (1.0 - tr.fraction)*(GetAbsOrigin() - checkPos).Length();
			Vector vBounceVel	= moveLen*tr.plane.normal/flInterval;

			// If I'm right over the ground don't push down
			if (vBounceVel.z < 0)
			{
				float floorZ = GetFloorZ(GetAbsOrigin());
				if (abs(GetAbsOrigin().z - floorZ) < 36)
				{
					vBounceVel.z = 0;
				}
			}
			SetCurrentVelocity( GetCurrentVelocity() + vBounceVel );
		}
		CBaseCombatCharacter* pBCC = ToBaseCombatCharacter( pHitEntity );

		if (pBCC)
		{
			// Spawn some extra blood where we hit
			SpawnBlood(tr.endpos, g_vecAttackDir, pBCC->BloodColor(), sk_scanner_dmg_dive.GetFloat());
		}
		else
		{
			if (!(m_spawnflags	& SF_NPC_GAG))
			{
				// <<TEMP>> need better sound here...
				ScannerEmitSound( "Shoot" );
			}
			// For sparks we must trace a line in the direction of the surface norm
			// that we hit.
			checkPos = GetAbsOrigin() - (tr.plane.normal * 24);

			AI_TraceLine( GetAbsOrigin(), checkPos,MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
			if (tr.fraction != 1.0)
			{
				g_pEffects->Sparks( tr.endpos );

				CBroadcastRecipientFilter filter;
				te->DynamicLight( filter, 0.0,
					&GetAbsOrigin(), 255, 180, 100, 0, 50, 0.1, 0 );
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: Tells use whether or not the NPC cares about a given type of hint node.
// Input  : sHint - 
// Output : TRUE if the NPC is interested in this hint type, FALSE if not.
//-----------------------------------------------------------------------------
bool CNPC_CScanner::FValidateHintType(CAI_Hint *pHint)
{
	return( pHint->HintType() == HINT_WORLD_WINDOW );
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : Type - 
//-----------------------------------------------------------------------------
int CNPC_CScanner::TranslateSchedule( int scheduleType ) 
{
	switch ( scheduleType )
	{
		case SCHED_IDLE_STAND:
		{
			return SCHED_SCANNER_PATROL;
		}
	}
	return BaseClass::TranslateSchedule(scheduleType);
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : idealActivity - 
//			*pIdealWeaponActivity - 
// Output : int
//-----------------------------------------------------------------------------
Activity CNPC_CScanner::NPC_TranslateActivity( Activity eNewActivity )
{
	if( !m_bIsClawScanner )
	{
		return BaseClass::NPC_TranslateActivity( eNewActivity );
	}

	// The claw scanner came along a little late and doesn't have the activities
	// of the city scanner. So Just pick between these three
	if( eNewActivity == ACT_DISARM )
	{
		// Closing up.
		return eNewActivity;
	}

	if( m_bIsOpen )
	{
		return ACT_IDLE_ANGRY;
	}
	else
	{
		return ACT_IDLE;
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CNPC_CScanner::HandleAnimEvent( animevent_t *pEvent )
{
	if( pEvent->event == AE_SCANNER_CLOSED )
	{
		m_bIsOpen = false;
		SetActivity( ACT_IDLE );
		return;
	}

	BaseClass::HandleAnimEvent( pEvent );
}

//-----------------------------------------------------------------------------
// Purpose: Emit sounds specific to the NPC's state.
//-----------------------------------------------------------------------------
void CNPC_CScanner::AlertSound(void)
{
	ScannerEmitSound( "Alert" );
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::DeathSound(void)
{
	ScannerEmitSound( "Die" );
}


//-----------------------------------------------------------------------------
// Purpose: Plays sounds while idle or in combat.
//-----------------------------------------------------------------------------
void CNPC_CScanner::IdleSound(void)
{
	if ( m_NPCState == NPC_STATE_COMBAT )
	{
		// dvs: the combat sounds should be related to what is happening, rather than random
		ScannerEmitSound( "Combat" );
	}
	else
	{
		ScannerEmitSound( "Idle" );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Plays a sound when hurt.
//-----------------------------------------------------------------------------
void CNPC_CScanner::PainSound(void)
{
	ScannerEmitSound( "Pain" );
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_CScanner::PlayFlySound(void)
{
	if ( IsMarkedForDeletion() )
	{
		return;
	}

	CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();

	//Setup the sound if we're not already
	if ( m_pEngineSound == NULL )
	{
		// Create the sound
		CPASAttenuationFilter filter( this );

		if( m_bIsClawScanner )
		{
			m_pEngineSound = controller.SoundCreate( filter, entindex(), CHAN_STATIC, "NPC_SScanner.FlyLoop", ATTN_NORM );
		}
		else
		{
			m_pEngineSound = controller.SoundCreate( filter, entindex(), CHAN_STATIC, "NPC_CScanner.FlyLoop", ATTN_NORM );
		}

		Assert(m_pEngineSound);

		// Start the engine sound
		controller.Play( m_pEngineSound, 0.0f, 100.0f );
		controller.SoundChangeVolume( m_pEngineSound, 1.0f, 2.0f );
	}

	float	speed	 = GetCurrentVelocity().Length();
	float	flVolume = 0.25f + (0.75f*(speed/GetMaxSpeed()));
	int		iPitch	 = min( 255, 80 + (20*(speed/GetMaxSpeed())) );

	//Update our pitch and volume based on our speed
	controller.SoundChangePitch( m_pEngineSound, iPitch, 0.1f );
	controller.SoundChangeVolume( m_pEngineSound, flVolume, 0.1f );
}


//-----------------------------------------------------------------------------
// Purpose: Plays the engine sound.
//-----------------------------------------------------------------------------
void CNPC_CScanner::NPCThink(void)
{
	if (!IsAlive())
	{
		SetActivity((Activity)ACT_SCANNER_RETRACT_PRONGS);
		StudioFrameAdvance( );
		SetNextThink( gpGlobals->curtime + 0.1f );
	}
	else
	{
		BaseClass::NPCThink();
		SpotlightUpdate();
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_CScanner::Precache(void)
{
	// Model
	if( m_bIsClawScanner )
	{
		PrecacheModel("models/shield_scanner.mdl");

		PrecacheModel("models/gibs/Shield_Scanner_Gib1.mdl");
		PrecacheModel("models/gibs/Shield_Scanner_Gib2.mdl");
		PrecacheModel("models/gibs/Shield_Scanner_Gib3.mdl");
		PrecacheModel("models/gibs/Shield_Scanner_Gib4.mdl");
		PrecacheModel("models/gibs/Shield_Scanner_Gib5.mdl");
		PrecacheModel("models/gibs/Shield_Scanner_Gib6.mdl");

		PrecacheScriptSound( "NPC_SScanner.Shoot");
		PrecacheScriptSound( "NPC_SScanner.Alert" );
		PrecacheScriptSound( "NPC_SScanner.Die" );
		PrecacheScriptSound( "NPC_SScanner.Combat" );
		PrecacheScriptSound( "NPC_SScanner.Idle" );
		PrecacheScriptSound( "NPC_SScanner.Pain" );
		PrecacheScriptSound( "NPC_SScanner.TakePhoto" );
		PrecacheScriptSound( "NPC_SScanner.AttackFlash" );
		PrecacheScriptSound( "NPC_SScanner.DiveBombFlyby" );
		PrecacheScriptSound( "NPC_SScanner.DiveBomb" );
		PrecacheScriptSound( "NPC_SScanner.DeployMine" );

		PrecacheScriptSound( "NPC_SScanner.FlyLoop" );
	}
	else
	{
		PrecacheModel("models/combine_scanner.mdl");

		PrecacheModel("models/gibs/scanner_gib01.mdl" );
		PrecacheModel("models/gibs/scanner_gib02.mdl" );	
		PrecacheModel("models/gibs/scanner_gib02.mdl" );
		PrecacheModel("models/gibs/scanner_gib04.mdl" );
		PrecacheModel("models/gibs/scanner_gib05.mdl" );

		PrecacheScriptSound( "NPC_CScanner.Shoot");
		PrecacheScriptSound( "NPC_CScanner.Alert" );
		PrecacheScriptSound( "NPC_CScanner.Die" );
		PrecacheScriptSound( "NPC_CScanner.Combat" );
		PrecacheScriptSound( "NPC_CScanner.Idle" );
		PrecacheScriptSound( "NPC_CScanner.Pain" );
		PrecacheScriptSound( "NPC_CScanner.TakePhoto" );
		PrecacheScriptSound( "NPC_CScanner.AttackFlash" );
		PrecacheScriptSound( "NPC_CScanner.DiveBombFlyby" );
		PrecacheScriptSound( "NPC_CScanner.DiveBomb" );
		PrecacheScriptSound( "NPC_CScanner.DeployMine" );

		PrecacheScriptSound( "NPC_CScanner.FlyLoop" );
	}

	// Sprites
	m_nHaloSprite = PrecacheModel("sprites/light_glow03.vmt");
	PrecacheModel( "sprites/glow_test02.vmt" );

	BaseClass::Precache();
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::SpeakSentence( int sentenceType )
{
	if (sentenceType == SCANNER_SENTENCE_ATTENTION)
	{
		ScannerEmitSound( "Attention" );
	}
	else if (sentenceType == SCANNER_SENTENCE_HANDSUP)
	{
		ScannerEmitSound( "Scan" );
	}
	else if (sentenceType == SCANNER_SENTENCE_PROCEED)
	{
		ScannerEmitSound( "Proceed" );
	}
	else if (sentenceType == SCANNER_SENTENCE_CURIOUS)
	{
		ScannerEmitSound( "Curious" );
	}
}


//------------------------------------------------------------------------------
// Purpose: Request help inspecting from other squad members
//------------------------------------------------------------------------------
void CNPC_CScanner::RequestInspectSupport(void)
{
	if (m_pSquad)
	{
		AISquadIter_t iter;
		for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
		{
			if (pSquadMember != this)
			{
				if (GetTarget())
				{
					pSquadMember->DispatchInteraction(g_interactionScannerSupportEntity,((void *)((CBaseEntity*)GetTarget())),this);
				}
				else
				{
					pSquadMember->DispatchInteraction(g_interactionScannerSupportPosition,((void *)m_vInspectPos.Base()),this);
				}
			}
		}
	}
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
bool CNPC_CScanner::IsValidInspectTarget(CBaseEntity *pEntity)
{
	// If a citizen, make sure he can be inspected again
	if (pEntity->Classify() == CLASS_CITIZEN_PASSIVE)
	{
		if (((CNPC_Citizen*)pEntity)->GetNextScannerInspectTime() > gpGlobals->curtime)
		{
			return false;
		}
	}

	// Make sure no other squad member has already chosen to 
	// inspect this entity
	if (m_pSquad)
	{
		AISquadIter_t iter;
		for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
		{
			if (pSquadMember->GetTarget() == pEntity)
			{
				return false;
			}
		}
	}

	// Do not inspect friendly targets
	if ( IRelationType( pEntity ) == D_LI )
		return false;

	return true;
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
CBaseEntity* CNPC_CScanner::BestInspectTarget(void)
{
	if ( !m_bShouldInspect )
		return NULL;

	CBaseEntity*	pBestEntity = NULL;
	float			fBestDist	= MAX_COORD_RANGE;
	float			fTestDist;

	CBaseEntity *pEntity = NULL;

	// If I have a spotlight, search from the spotlight position
	// otherwise search from my position
	Vector	vSearchOrigin;
	float	fSearchDist;
	if (m_hSpotlightTarget != NULL)
	{
		vSearchOrigin	= m_hSpotlightTarget->GetAbsOrigin();
		fSearchDist		= SCANNER_CIT_INSPECT_GROUND_DIST;
	}
	else
	{
		vSearchOrigin	= WorldSpaceCenter();
		fSearchDist		= SCANNER_CIT_INSPECT_FLY_DIST;
	}

	if ( m_bOnlyInspectPlayers )
	{
		CBasePlayer *pPlayer = AI_GetSinglePlayer();
		if ( !pPlayer )
			return NULL;

		if ( !pPlayer->IsAlive() || (pPlayer->GetFlags() & FL_NOTARGET) )
			return NULL;

		return WorldSpaceCenter().DistToSqr( pPlayer->EyePosition() ) <= (fSearchDist * fSearchDist) ? pPlayer : NULL;
	}

	CUtlVector<CBaseEntity *> candidates;
	float fSearchDistSq = fSearchDist * fSearchDist;
	int i;

	// Inspect players unless told otherwise
	if ( m_bNeverInspectPlayers == false )
	{
		// Players
		for ( i = 1; i <= gpGlobals->maxClients; i++ )
		{
			CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );

			if ( pPlayer )
			{
				if ( vSearchOrigin.DistToSqr(pPlayer->GetAbsOrigin()) < fSearchDistSq )
				{
					candidates.AddToTail( pPlayer );
				}
			}
		}
	}
	
	// NPCs
	CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
	
	for ( i = 0; i < g_AI_Manager.NumAIs(); i++ )
	{
		if ( ppAIs[i] != this && vSearchOrigin.DistToSqr(ppAIs[i]->GetAbsOrigin()) < fSearchDistSq )
		{
			candidates.AddToTail( ppAIs[i] );
		}
	}

	for ( i = 0; i < candidates.Count(); i++ )
	{
		pEntity = candidates[i];
		Assert( pEntity != this && (pEntity->MyNPCPointer() || pEntity->IsPlayer() ) );

		CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
		if ( ( pNPC && pNPC->Classify() == CLASS_CITIZEN_PASSIVE ) || pEntity->IsPlayer() )
		{
			if ( pEntity->GetFlags() & FL_NOTARGET )
				continue;

			if ( pEntity->IsAlive() == false )
				continue;

			// Ensure it's within line of sight
			if ( !FVisible( pEntity ) )
				continue;

			fTestDist = ( GetAbsOrigin() - pEntity->EyePosition() ).Length();
			if ( fTestDist < fBestDist )
			{
				if ( IsValidInspectTarget( pEntity ) )
				{
					fBestDist	= fTestDist;
					pBestEntity	= pEntity; 
				}
			}
		}
	}
	return pBestEntity;
}


//------------------------------------------------------------------------------
// Purpose: Clears any previous inspect target and set inspect target to
//			 the given entity and set the durection of the inspection
//------------------------------------------------------------------------------
void CNPC_CScanner::SetInspectTargetToEnt(CBaseEntity *pEntity, float fInspectDuration)
{
	ClearInspectTarget();
	SetTarget(pEntity);
	
	m_fInspectEndTime = gpGlobals->curtime + fInspectDuration;
}


//------------------------------------------------------------------------------
// Purpose: Clears any previous inspect target and set inspect target to
//			 the given hint node and set the durection of the inspection
//------------------------------------------------------------------------------
void CNPC_CScanner::SetInspectTargetToHint(CAI_Hint *pHint, float fInspectDuration)
{
	ClearInspectTarget();

	float yaw = pHint->Yaw();
	// --------------------------------------------
	// Figure out the location that the hint hits
	// --------------------------------------------
	Vector vHintDir	= UTIL_YawToVector( yaw );

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

	Vector vHintEnd	= vHintOrigin + (vHintDir * 512);
	
	trace_t tr;
	AI_TraceLine ( vHintOrigin, vHintEnd, MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr);
	
	if ( g_debug_cscanner.GetBool() )
	{
		NDebugOverlay::Line( vHintOrigin, tr.endpos, 255, 0, 0, true, 4.0f );
		NDebugOverlay::Cross3D( tr.endpos, -Vector(8,8,8), Vector(8,8,8), 255, 0, 0, true, 4.0f );
	}

	if (tr.fraction == 1.0f )
	{
		DevMsg("ERROR: Scanner hint node not facing a surface!\n");
	}
	else
	{
		SetHintNode( pHint );
		m_vInspectPos = tr.endpos;
		pHint->Lock( this );

		m_fInspectEndTime = gpGlobals->curtime + fInspectDuration;
	}
}


//------------------------------------------------------------------------------
// Purpose: Clears any previous inspect target and set inspect target to
//			 the given position and set the durection of the inspection
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CNPC_CScanner::SetInspectTargetToPos(const Vector &vInspectPos, float fInspectDuration)
{
	ClearInspectTarget();
	m_vInspectPos		= vInspectPos;

	m_fInspectEndTime	= gpGlobals->curtime + fInspectDuration;
}


//------------------------------------------------------------------------------
// Purpose: Clears out any previous inspection targets
//------------------------------------------------------------------------------
void CNPC_CScanner::ClearInspectTarget(void)
{
	if ( GetIdealState() != NPC_STATE_SCRIPT )
	{
		SetTarget( NULL );
	}

	ClearHintNode( SCANNER_HINT_INSPECT_LENGTH );
	m_vInspectPos	= vec3_origin;
}


//------------------------------------------------------------------------------
// Purpose: Returns true if there is a position to be inspected.
//------------------------------------------------------------------------------
bool CNPC_CScanner::HaveInspectTarget( void )
{
	if ( GetTarget() != NULL )
		return true;

	if ( m_vInspectPos != vec3_origin )
		return true;

	return false;
}


//------------------------------------------------------------------------------
// Purpose: 
//------------------------------------------------------------------------------
Vector CNPC_CScanner::InspectTargetPosition(void)
{
	// If we have a target, return an adjust position
	if ( GetTarget() != NULL )
	{
		Vector	vEyePos = GetTarget()->EyePosition();

		// If in spotlight mode, aim for ground below target unless is client
		if ( m_nFlyMode == SCANNER_FLY_SPOT && !(GetTarget()->GetFlags() & FL_CLIENT) )
		{
			Vector vInspectPos;
			vInspectPos.x	= vEyePos.x;
			vInspectPos.y	= vEyePos.y;
			vInspectPos.z	= GetFloorZ( vEyePos );

			// Let's take three-quarters between eyes and ground
			vInspectPos.z	+= ( vEyePos.z - vInspectPos.z ) * 0.75f;

			return vInspectPos;
		}
		else
		{
			// Otherwise aim for eyes
			return vEyePos;
		}
	}
	else if ( m_vInspectPos != vec3_origin )
	{
		return m_vInspectPos;
	}
	else
	{
		DevMsg("InspectTargetPosition called with no target!\n");
		
		return m_vInspectPos;
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_CScanner::InputShouldInspect( inputdata_t &inputdata )
{
	m_bShouldInspect = ( inputdata.value.Int() != 0 );
	
	if ( !m_bShouldInspect )
	{
		if ( GetEnemy() == GetTarget() )
			SetEnemy(NULL);
		ClearInspectTarget();
		SetTarget(NULL);
		SpotlightDestroy();
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_CScanner::InputSetFlightSpeed(inputdata_t &inputdata)
{
	//FIXME: Currently unsupported

	/*
	m_flFlightSpeed = inputdata.value.Int();
	m_bFlightSpeedOverridden = (m_flFlightSpeed > 0);
	*/
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CNPC_CScanner::DeployMine()
{
	CBaseEntity *child;
	// iterate through all children
	for ( child = FirstMoveChild(); child != NULL; child = child->NextMovePeer() )
	{
		if( FClassnameIs( child, "combine_mine" ) )
		{
			child->SetParent( NULL );
			child->SetAbsVelocity( GetAbsVelocity() );
			child->SetOwnerEntity( this );

			ScannerEmitSound( "DeployMine" );

			IPhysicsObject *pPhysObj = child->VPhysicsGetObject();
			if( pPhysObj )
			{
				// Make sure the mine's awake
				pPhysObj->Wake();
			}

			if( m_bIsClawScanner )
			{
				// Fold up.
				SetActivity( ACT_DISARM );
			}

			return;
		}
	}
}

float CNPC_CScanner::GetMaxSpeed()
{
	if( IsStriderScout() )
	{
		return SCANNER_SCOUT_MAX_SPEED;
	}

	return SCANNER_MAX_SPEED;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_CScanner::InputDeployMine(inputdata_t &inputdata)
{
	DeployMine();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_CScanner::InputEquipMine(inputdata_t &inputdata)
{
	CBaseEntity *child;
	// iterate through all children
	for ( child = FirstMoveChild(); child != NULL; child = child->NextMovePeer() )
	{
		if( FClassnameIs( child, "combine_mine" ) )
		{
			// Already have a mine!
			return;
		}
	}

	CBaseEntity *pEnt;

	pEnt = CreateEntityByName( "combine_mine" );
	bool bPlacedMine = false;

	if( m_bIsClawScanner )
	{
		Vector	vecOrigin;
		QAngle	angles;
		int		attachment;

		attachment = LookupAttachment( "claw" );

		if( attachment > -1 )
		{
			GetAttachment( attachment, vecOrigin, angles );
			
			pEnt->SetAbsOrigin( vecOrigin );
			pEnt->SetAbsAngles( angles );
			pEnt->SetOwnerEntity( this );
			pEnt->SetParent( this, attachment );

			m_bIsOpen = true;
			SetActivity( ACT_IDLE_ANGRY );
			bPlacedMine = true;
		}
	}


	if( !bPlacedMine )
	{
		Vector vecMineLocation = GetAbsOrigin();
		vecMineLocation.z -= 32.0;

		pEnt->SetAbsOrigin( vecMineLocation );
		pEnt->SetAbsAngles( GetAbsAngles() );
		pEnt->SetOwnerEntity( this );
		pEnt->SetParent( this );
	}

	pEnt->Spawn();
}


//-----------------------------------------------------------------------------
// Purpose: Tells the scanner to go photograph an entity.
// Input  : String name or classname of the entity to inspect.
//-----------------------------------------------------------------------------
void CNPC_CScanner::InputInspectTargetPhoto(inputdata_t &inputdata)
{
	m_vLastPatrolDir = vec3_origin;
	m_bPhotoTaken = false;
	InspectTarget( inputdata, SCANNER_FLY_PHOTO );
}


//-----------------------------------------------------------------------------
// Purpose: Tells the scanner to go spotlight an entity.
// Input  : String name or classname of the entity to inspect.
//-----------------------------------------------------------------------------
void CNPC_CScanner::InputInspectTargetSpotlight(inputdata_t &inputdata)
{
	InspectTarget( inputdata, SCANNER_FLY_SPOT );
}


//-----------------------------------------------------------------------------
// Purpose: Tells the scanner to go photo or spotlight an entity.
// Input  : String name or classname of the entity to inspect.
//-----------------------------------------------------------------------------
void CNPC_CScanner::InspectTarget( inputdata_t &inputdata, ScannerFlyMode_t eFlyMode )
{
	CBaseEntity *pEnt = gEntList.FindEntityGeneric( NULL, inputdata.value.String(), this, inputdata.pActivator );
	
	if ( pEnt != NULL )
	{
		// Set and begin to inspect our target
		SetInspectTargetToEnt( pEnt, SCANNER_CIT_INSPECT_LENGTH );
		
		m_nFlyMode = eFlyMode;
		SetCondition( COND_SCANNER_HAVE_INSPECT_TARGET );
		
		// Stop us from any other navigation we were doing
		GetNavigator()->ClearGoal();
	}
	else
	{
		DevMsg( "InspectTarget: target %s not found!\n", inputdata.value.String() );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_CScanner::MovingToInspectTarget( void )
{
	// If we're flying to a photograph target and the photo isn't yet taken, we're still moving to it
	if ( m_nFlyMode == SCANNER_FLY_PHOTO && m_bPhotoTaken == false )
		return true;

	// If we're still on a path, then we're still moving
	if ( HaveInspectTarget() && GetNavigator()->IsGoalActive() )
		return true;

	return false;
}

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

	// Clear out our old conditions
	ClearCondition( COND_SCANNER_INSPECT_DONE );
	ClearCondition( COND_SCANNER_HAVE_INSPECT_TARGET );
	ClearCondition( COND_SCANNER_SPOT_ON_TARGET );
	ClearCondition( COND_SCANNER_CAN_PHOTOGRAPH );

	// We don't do any of these checks if we have an enemy
	if ( GetEnemy() )
		return;

	// --------------------------------------
	//  COND_SCANNER_INSPECT_DONE
	//
	// If my inspection over 
	// ---------------------------------------------------------

	// Refresh our timing if we're still moving to our inspection target
	if ( MovingToInspectTarget() )
	{
		m_fInspectEndTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_LENGTH;
	}

	// Update our follow times
	if ( HaveInspectTarget() && gpGlobals->curtime > m_fInspectEndTime && m_nFlyMode != SCANNER_FLY_FOLLOW )
	{
		SetCondition ( COND_SCANNER_INSPECT_DONE );

		m_fCheckCitizenTime	= gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY;
		m_fCheckHintTime	= gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY;
		ClearInspectTarget();
	}

	// ----------------------------------------------------------
	//  If I heard a sound and I don't have an enemy, inspect it
	// ----------------------------------------------------------
	if ( ( HasCondition( COND_HEAR_COMBAT ) || HasCondition( COND_HEAR_DANGER ) ) && m_nFlyMode != SCANNER_FLY_FOLLOW )
	{
		CSound *pSound = GetBestSound();
		
		if ( pSound )
		{
			// Chase an owner if we can
			if ( pSound->m_hOwner != NULL )
			{
				// Don't inspect sounds of things we like
				if ( IRelationType( pSound->m_hOwner ) != D_LI )
				{
					// Only bother if we can see it
					if ( FVisible( pSound->m_hOwner ) )
					{
						SetInspectTargetToEnt( pSound->m_hOwner, SCANNER_SOUND_INSPECT_LENGTH );
					}
				}
			}
			else
			{
				// Otherwise chase the specific sound
				Vector vSoundPos = pSound->GetSoundOrigin();
				SetInspectTargetToPos( vSoundPos, SCANNER_SOUND_INSPECT_LENGTH );
			}

			m_nFlyMode = (random->RandomInt(0,2)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;
		}
	}

	// --------------------------------------
	//  COND_SCANNER_HAVE_INSPECT_TARGET
	//
	// Look for a nearby citizen or player to hassle. 
	// ---------------------------------------------------------

	// Check for citizens to inspect
	if ( gpGlobals->curtime	> m_fCheckCitizenTime && HaveInspectTarget() == false )
	{
		CBaseEntity *pBestEntity = BestInspectTarget();
		
		if ( pBestEntity != NULL )
		{
			SetInspectTargetToEnt( pBestEntity, SCANNER_CIT_INSPECT_LENGTH );
			m_nFlyMode = (random->RandomInt(0,3)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;
			SetCondition ( COND_SCANNER_HAVE_INSPECT_TARGET );
		}
	}

	// Check for hints to inspect
	if ( gpGlobals->curtime > m_fCheckHintTime && HaveInspectTarget() == false )
	{
		SetHintNode( CAI_HintManager::FindHint( this, HINT_WORLD_WINDOW, 0, SCANNER_CIT_INSPECT_FLY_DIST ) );

		if ( GetHintNode() )
		{
			m_fCheckHintTime = gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY;

			m_nFlyMode = (random->RandomInt(0,2)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;

			SetInspectTargetToHint( GetHintNode(), SCANNER_HINT_INSPECT_LENGTH );

			SetCondition ( COND_SCANNER_HAVE_INSPECT_TARGET );
		}
	}

	// --------------------------------------
	//  COND_SCANNER_SPOT_ON_TARGET
	//
	//  True when spotlight is on target ent
	// --------------------------------------

	if ( m_hSpotlightTarget != NULL	&& HaveInspectTarget() && m_hSpotlightTarget->GetSmoothedVelocity().Length() < 25 )
	{
		// If I have a target entity, check my spotlight against the
		// actual position of the entity
		if (GetTarget())
		{
			float fInspectDist = (m_vSpotlightTargetPos - m_vSpotlightCurrentPos).Length();
			if ( fInspectDist < 100 )
			{
				SetCondition( COND_SCANNER_SPOT_ON_TARGET );
			}
		}
		// Otherwise just check by beam direction
		else
		{
			Vector vTargetDir = SpotlightTargetPos() - GetLocalOrigin();
			VectorNormalize(vTargetDir);
			float dotpr = DotProduct(vTargetDir, m_vSpotlightDir);
			if (dotpr > 0.95)
			{
				SetCondition( COND_SCANNER_SPOT_ON_TARGET );
			}
		}
	}

	// --------------------------------------------
	//  COND_SCANNER_CAN_PHOTOGRAPH
	//
	//  True when can photograph target ent
	// --------------------------------------------

	ClearCondition( COND_SCANNER_CAN_PHOTOGRAPH );

	if ( m_nFlyMode == SCANNER_FLY_PHOTO )
	{
		// Make sure I have something to photograph and I'm ready to photograph and I'm not moving to fast
		if ( gpGlobals->curtime > m_fNextPhotographTime && HaveInspectTarget() && GetCurrentVelocity().LengthSqr() < (64*64) )
		{
			// Check that I'm in the right distance range
			float  fInspectDist = (InspectTargetPosition() - GetAbsOrigin()).Length2D();
			
			// See if we're within range
			if ( fInspectDist > SCANNER_PHOTO_NEAR_DIST && fInspectDist < SCANNER_PHOTO_FAR_DIST )
			{
				// Make sure we're looking at the target
				if ( UTIL_AngleDiff( GetAbsAngles().y, VecToYaw( InspectTargetPosition() - GetAbsOrigin() ) ) < 4.0f )
				{
					trace_t tr;
					AI_TraceLine ( GetAbsOrigin(), InspectTargetPosition(), MASK_OPAQUE, GetTarget(), COLLISION_GROUP_NONE, &tr);
					
					if ( tr.fraction == 1.0f )
					{
						SetCondition( COND_SCANNER_CAN_PHOTOGRAPH );
					}
				}
			}
		}
	}
}

//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::PrescheduleThink(void)
{
	BaseClass::PrescheduleThink();

	// Go back to idling if we're done
	if ( GetIdealActivity() == ACT_SCANNER_FLARE_START )
	{
		if ( IsSequenceFinished() )
		{
			SetIdealActivity( (Activity) ACT_IDLE );
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: For innate melee attack
//-----------------------------------------------------------------------------
int CNPC_CScanner::MeleeAttack1Conditions( float flDot, float flDist )
{
	if (GetEnemy() == NULL)
	{
		return COND_NONE;
	}

	// Check too far to attack with 2D distance
	float vEnemyDist2D = (GetEnemy()->GetLocalOrigin() - GetLocalOrigin()).Length2D();

	if (m_flNextAttack > gpGlobals->curtime)
	{
		return COND_NONE;
	}
	else if (vEnemyDist2D > SCANNER_ATTACK_RANGE)
	{
		return COND_TOO_FAR_TO_ATTACK;
	}
	else if (flDot < 0.7)
	{
		return COND_NOT_FACING_ATTACK;
	}
	return COND_CAN_MELEE_ATTACK1;
}


//-----------------------------------------------------------------------------
// Purpose: Overridden because if the player is a criminal, we hate them.
// Input  : pTarget - Entity with which to determine relationship.
// Output : Returns relationship value.
//-----------------------------------------------------------------------------
Disposition_t CNPC_CScanner::IRelationType(CBaseEntity *pTarget)
{
	// If it's the player and they are a criminal, we hates them
	if ( pTarget->Classify() == CLASS_PLAYER )
	{
		if ( GlobalEntity_GetState("gordon_precriminal") == GLOBAL_ON )
			return D_NU;
	}

	return BaseClass::IRelationType( pTarget );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pTask - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::RunTask( const Task_t *pTask )
{
	switch ( pTask->iTask )
	{
		case TASK_SCANNER_PHOTOGRAPH:
		{
			if ( IsWaitFinished() )
			{	
				// If light was on turn it off
				if ( m_pEyeFlash->GetBrightness() > 0 )
				{
					m_pEyeFlash->SetBrightness( 0 );

					// I'm done with this target
					if ( gpGlobals->curtime > m_fInspectEndTime )
					{
						ClearInspectTarget();
						TaskComplete();
					}
					// Otherwise take another picture
					else
					{
						SetWait( 5.0f, 10.0f );
					}
				}
				// If light was off, take another picture
				else
				{
					TakePhoto();
					SetWait( 0.1f );
				}
			}
			break;
		}
		case TASK_SCANNER_ATTACK_PRE_FLASH:
		{
			AttackPreFlash();
			
			if ( IsWaitFinished() )
			{
				TaskComplete();
			}
			break;
		}
		case TASK_SCANNER_ATTACK_FLASH:
		{
			if (IsWaitFinished())
			{
				AttackFlashBlind();
				TaskComplete();
			}
			break;
		}
		default:
		{
			BaseClass::RunTask(pTask);
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: Gets the appropriate next schedule based on current condition
//			bits.
//-----------------------------------------------------------------------------
int CNPC_CScanner::SelectSchedule(void)
{
	// Turn our flash off in case we were interrupted while it was on.
	if ( m_pEyeFlash )
	{
		m_pEyeFlash->SetBrightness( 0 );
	}

	// ----------------------------------------------------
	//  If I'm dead, go into a dive bomb
	// ----------------------------------------------------
	if ( m_iHealth <= 0 )
	{
		m_flSpeed = SCANNER_MAX_DIVE_BOMB_SPEED;
		return SCHED_SCANNER_ATTACK_DIVEBOMB;
	}

	// -------------------------------
	// If I'm in a script sequence
	// -------------------------------
	if ( m_NPCState == NPC_STATE_SCRIPT )
		return(BaseClass::SelectSchedule());

	// -------------------------------
	// Flinch
	// -------------------------------
	if ( HasCondition(COND_LIGHT_DAMAGE) || HasCondition(COND_HEAVY_DAMAGE) )
	{
		if ( IsHeldByPhyscannon( ) ) 
 			return SCHED_SMALL_FLINCH;

		if ( m_NPCState == NPC_STATE_IDLE )
			return SCHED_SMALL_FLINCH;

		if ( m_NPCState == NPC_STATE_ALERT )
		{
			if ( m_iHealth < ( 3 * sk_scanner_health.GetFloat() / 4 ))
				return SCHED_TAKE_COVER_FROM_ORIGIN;

			if ( SelectWeightedSequence( ACT_SMALL_FLINCH ) != -1 )
				return SCHED_SMALL_FLINCH;
		}
		else
		{
			if ( random->RandomInt( 0, 10 ) < 4 )
				return SCHED_SMALL_FLINCH;
		}
	}

	// I'm being held by the physcannon... struggle!
	if ( IsHeldByPhyscannon( ) ) 
		return SCHED_SCANNER_HELD_BY_PHYSCANNON;

	// ----------------------------------------------------------
	//  If I have an enemy
	// ----------------------------------------------------------
	if ( GetEnemy() != NULL && GetEnemy()->IsAlive() && m_bShouldInspect )
	{
		// Always chase the enemy
		SetInspectTargetToEnt( GetEnemy(), 9999 );

		// Patrol if the enemy has vanished
		if ( HasCondition( COND_LOST_ENEMY ) )
			return SCHED_SCANNER_PATROL;
		
		// Chase via route if we're directly blocked
		if ( HasCondition( COND_SCANNER_FLY_BLOCKED ) )
			return SCHED_SCANNER_CHASE_ENEMY;
		
		// Attack if it's time
		if ( gpGlobals->curtime < m_flNextAttack )
			return SCHED_SCANNER_SPOTLIGHT_HOVER;

		// Melee attack if possible
		if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
		{ 
			if ( random->RandomInt(0,1) )
				return SCHED_SCANNER_ATTACK_FLASH;

			// TODO: a schedule where he makes an alarm sound?
			return SCHED_SCANNER_CHASE_ENEMY;
		}

		// If I'm far from the enemy, stay up high and approach in spotlight mode
		float fAttack2DDist = ( GetEnemyLKP() - GetAbsOrigin() ).Length2D();

		if ( fAttack2DDist > SCANNER_ATTACK_FAR_DIST )
			return SCHED_SCANNER_SPOTLIGHT_HOVER;

		// Otherwise fly in low for attack
		return SCHED_SCANNER_ATTACK_HOVER;
	}

	// ----------------------------------------------------------
	//  If I have something to inspect
	// ----------------------------------------------------------
	if ( HaveInspectTarget() )
	{
		// Pathfind to our goal
		if ( HasCondition( COND_SCANNER_FLY_BLOCKED ) )
			return SCHED_SCANNER_MOVE_TO_INSPECT;

		// If I was chasing, pick with photographing or spotlighting 
		if ( m_nFlyMode == SCANNER_FLY_CHASE )
		{
			m_nFlyMode = (random->RandomInt(0,1)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;
		}

		// Handle spotlight
		if ( m_nFlyMode == SCANNER_FLY_SPOT )
		{
			if (HasCondition( COND_SCANNER_SPOT_ON_TARGET ))
			{
				if (GetTarget())
				{
					RequestInspectSupport();

					CAI_BaseNPC *pNPC = GetTarget()->MyNPCPointer();
					// If I'm leading the inspection, so verbal inspection
					if (pNPC && pNPC->GetTarget() == this)
					{
						return SCHED_SCANNER_SPOTLIGHT_INSPECT_CIT;
					}

					return SCHED_SCANNER_SPOTLIGHT_HOVER;
				}

				return SCHED_SCANNER_SPOTLIGHT_INSPECT_POS;
			}

			return SCHED_SCANNER_SPOTLIGHT_HOVER;
		}
		
		// Handle photographing
		if ( m_nFlyMode == SCANNER_FLY_PHOTO )
		{
			if ( HasCondition( COND_SCANNER_CAN_PHOTOGRAPH ))
				return SCHED_SCANNER_PHOTOGRAPH;

			return SCHED_SCANNER_PHOTOGRAPH_HOVER;
		}
		
		// Handle following after a target
		if ( m_nFlyMode == SCANNER_FLY_FOLLOW )
		{
			//TODO: Randomly make noise, photograph, etc
			return SCHED_SCANNER_FOLLOW_HOVER;
		}

		// Handle patrolling
		if ( ( m_nFlyMode == SCANNER_FLY_PATROL ) || ( m_nFlyMode == SCANNER_FLY_FAST ) )
			return SCHED_SCANNER_PATROL;
	}

	// Default to patrolling around
	return SCHED_SCANNER_PATROL;
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::SpotlightDestroy(void)
{
	if ( m_hSpotlight )
	{
		UTIL_Remove(m_hSpotlight);
		m_hSpotlight = NULL;
		
		UTIL_Remove(m_hSpotlightTarget);
		m_hSpotlightTarget = NULL;
	}
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::SpotlightCreate(void)
{
	// Make sure we don't already have one
	if ( m_hSpotlight != NULL )
		return;

	// Can we create a spotlight yet?
	if ( gpGlobals->curtime < m_fNextSpotlightTime )
		return;

	// If I have an enemy, start spotlight on my enemy
	if (GetEnemy() != NULL)
	{
		Vector vEnemyPos	= GetEnemyLKP();
		Vector vTargetPos	= vEnemyPos;
		vTargetPos.z		= GetFloorZ(vEnemyPos);
		m_vSpotlightDir = vTargetPos - GetLocalOrigin();
		VectorNormalize(m_vSpotlightDir);
	}
	// If I have an target, start spotlight on my target
	else if (GetTarget() != NULL)
	{
		Vector vTargetPos	= GetTarget()->GetLocalOrigin();
		vTargetPos.z		= GetFloorZ(GetTarget()->GetLocalOrigin());
		m_vSpotlightDir = vTargetPos - GetLocalOrigin();
		VectorNormalize(m_vSpotlightDir);
	}
	// Other wise just start looking down
	else
	{
		m_vSpotlightDir	= Vector(0,0,-1); 
	}

	trace_t tr;
	AI_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + m_vSpotlightDir * 2024, MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr );

	m_hSpotlightTarget = (CSpotlightEnd*)CreateEntityByName( "spotlight_end" );
	m_hSpotlightTarget->Spawn();
	m_hSpotlightTarget->SetLocalOrigin( tr.endpos );
	m_hSpotlightTarget->SetOwnerEntity( this );
	// YWB:  Because the scanner only moves the target during think, make sure we interpolate over 0.1 sec instead of every tick!!!
	m_hSpotlightTarget->SetSimulatedEveryTick( false );

	// Using the same color as the beam...
	m_hSpotlightTarget->SetRenderColor( 255, 255, 255 );
	m_hSpotlightTarget->m_Radius = m_flSpotlightMaxLength;

	m_hSpotlight = CBeam::BeamCreate( "sprites/glow_test02.vmt", SPOTLIGHT_WIDTH );
	// Set the temporary spawnflag on the beam so it doesn't save (we'll recreate it on restore)
	m_hSpotlight->AddSpawnFlags( SF_BEAM_TEMPORARY );
	m_hSpotlight->SetColor( 255, 255, 255 ); 
	m_hSpotlight->SetHaloTexture( m_nHaloSprite );
	m_hSpotlight->SetHaloScale( 32 );
	m_hSpotlight->SetEndWidth( m_hSpotlight->GetWidth() );
	m_hSpotlight->SetBeamFlags( (FBEAM_SHADEOUT|FBEAM_NOTILE) );
	m_hSpotlight->SetBrightness( 32 );
	m_hSpotlight->SetNoise( 0 );
	m_hSpotlight->EntsInit( this, m_hSpotlightTarget );

	// attach to light
	m_hSpotlight->SetStartAttachment( LookupAttachment( SCANNER_ATTACHMENT_LIGHT ) );

	m_vSpotlightAngVelocity = vec3_origin;
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
Vector CNPC_CScanner::SpotlightTargetPos(void)
{
	// ----------------------------------------------
	//  If I have an enemy 
	// ----------------------------------------------
	if (GetEnemy() != NULL)
	{
		// If I can see my enemy aim for him
		if (HasCondition(COND_SEE_ENEMY))
		{
			// If its client aim for his eyes
			if (GetEnemy()->GetFlags() & FL_CLIENT)
			{
				m_vSpotlightTargetPos = GetEnemy()->EyePosition();
			}
			// Otherwise same for his feet
			else
			{
				m_vSpotlightTargetPos	= GetEnemy()->GetLocalOrigin();
				m_vSpotlightTargetPos.z	= GetFloorZ(GetEnemy()->GetLocalOrigin());
			}
		}
		// Otherwise aim for last known position if I can see LKP
		else
		{
			Vector vLKP				= GetEnemyLKP();
			m_vSpotlightTargetPos.x	= vLKP.x;
			m_vSpotlightTargetPos.y	= vLKP.y;
			m_vSpotlightTargetPos.z	= GetFloorZ(vLKP);
		}
	}
	// ----------------------------------------------
	//  If I have an inspect target
	// ----------------------------------------------
	else if (HaveInspectTarget())
	{
		m_vSpotlightTargetPos = InspectTargetPosition();
	}
	else
	{
		// This creates a nice patrol spotlight sweep
		// in the direction that I'm travelling
		m_vSpotlightTargetPos	= GetCurrentVelocity();
		m_vSpotlightTargetPos.z = 0;
		VectorNormalize( m_vSpotlightTargetPos );
		m_vSpotlightTargetPos   *= 5;

		float noiseScale = 2.5;
		const Vector &noiseMod = GetNoiseMod();
		m_vSpotlightTargetPos.x += noiseScale*sin(noiseMod.x * gpGlobals->curtime + noiseMod.x);
		m_vSpotlightTargetPos.y += noiseScale*cos(noiseMod.y* gpGlobals->curtime + noiseMod.y);
		m_vSpotlightTargetPos.z -= fabs(noiseScale*cos(noiseMod.z* gpGlobals->curtime + noiseMod.z) );
		m_vSpotlightTargetPos   = GetLocalOrigin()+m_vSpotlightTargetPos * 2024;
	}

	return m_vSpotlightTargetPos;
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
Vector CNPC_CScanner::SpotlightCurrentPos(void)
{
	Vector vTargetDir		= SpotlightTargetPos() - GetLocalOrigin();
	VectorNormalize(vTargetDir);

	if (!m_hSpotlight)
	{
		DevMsg("Spotlight pos. called w/o spotlight!\n");
		return vec3_origin;
	}
	// -------------------------------------------------
	//  Beam has momentum relative to it's ground speed
	//  so sclae the turn rate based on its distance
	//  from the beam source
	// -------------------------------------------------
	float	fBeamDist		= (m_hSpotlightTarget->GetLocalOrigin() - GetLocalOrigin()).Length();

	float	fBeamTurnRate	= atan(50/fBeamDist);
	Vector  vNewAngVelocity = fBeamTurnRate * (vTargetDir - m_vSpotlightDir);

	float	myDecay	 = 0.4;
	m_vSpotlightAngVelocity = (myDecay * m_vSpotlightAngVelocity + (1-myDecay) * vNewAngVelocity);

	// ------------------------------
	//  Limit overall angular speed
	// -----------------------------
	if (m_vSpotlightAngVelocity.Length() > 1)
	{

		Vector velDir = m_vSpotlightAngVelocity;
		VectorNormalize(velDir);
		m_vSpotlightAngVelocity = velDir * 1;
	}

	// ------------------------------
	//  Calculate new beam direction
	// ------------------------------
	m_vSpotlightDir = m_vSpotlightDir + m_vSpotlightAngVelocity;
	m_vSpotlightDir = m_vSpotlightDir;
	VectorNormalize(m_vSpotlightDir);


	// ---------------------------------------------
	//	Get beam end point.  Only collide with
	//  solid objects, not npcs
	// ---------------------------------------------
	trace_t tr;
	Vector vTraceEnd = GetAbsOrigin() + (m_vSpotlightDir * 2 * m_flSpotlightMaxLength);
	AI_TraceLine ( GetAbsOrigin(), vTraceEnd, MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr);

	return (tr.endpos);
}


//------------------------------------------------------------------------------
// Purpose: Update the direction and position of my spotlight
//------------------------------------------------------------------------------
void CNPC_CScanner::SpotlightUpdate(void)
{
	//FIXME: JDW - E3 Hack
	if ( m_bNoLight )
	{
		if ( m_hSpotlight )
		{
			SpotlightDestroy();
		}

		return;
	}

	if ((m_nFlyMode != SCANNER_FLY_SPOT) &&
		(m_nFlyMode != SCANNER_FLY_PATROL) && 
		(m_nFlyMode != SCANNER_FLY_FAST))
	{
		if ( m_hSpotlight )
		{	
			SpotlightDestroy();
		}
		return;
	}
	
	// If I don't have a spotlight attempt to create one

	if ( m_hSpotlight == NULL )
	{
		SpotlightCreate();
		
		if ( m_hSpotlight== NULL )
			return;
	}

	// Calculate the new homing target position
	m_vSpotlightCurrentPos = SpotlightCurrentPos();

	// ------------------------------------------------------------------
	//  If I'm not facing the spotlight turn it off 
	// ------------------------------------------------------------------
	Vector vSpotDir = m_vSpotlightCurrentPos - GetAbsOrigin();
	VectorNormalize(vSpotDir);
	
	Vector	vForward;
	AngleVectors( GetAbsAngles(), &vForward );

	float dotpr = DotProduct( vForward, vSpotDir );
	
	if ( dotpr < 0.0 )
	{
		// Leave spotlight off for a while
		m_fNextSpotlightTime = gpGlobals->curtime + 3.0f;

		SpotlightDestroy();
		return;
	}

	// --------------------------------------------------------------
	//  Update spotlight target velocity
	// --------------------------------------------------------------
	Vector vTargetDir  = (m_vSpotlightCurrentPos - m_hSpotlightTarget->GetLocalOrigin());
	float  vTargetDist = vTargetDir.Length();

	Vector vecNewVelocity = vTargetDir;
	VectorNormalize(vecNewVelocity);
	vecNewVelocity *= (10 * vTargetDist);

	// If a large move is requested, just jump to final spot as we
	// probably hit a discontinuity
	if (vecNewVelocity.Length() > 200)
	{
		VectorNormalize(vecNewVelocity);
		vecNewVelocity *= 200;
		m_hSpotlightTarget->SetLocalOrigin( m_vSpotlightCurrentPos );
	}
	m_hSpotlightTarget->SetAbsVelocity( vecNewVelocity );

	m_hSpotlightTarget->m_vSpotlightOrg = GetAbsOrigin();

	// Avoid sudden change in where beam fades out when cross disconinuities
	m_hSpotlightTarget->m_vSpotlightDir = m_hSpotlightTarget->GetLocalOrigin() - m_hSpotlightTarget->m_vSpotlightOrg;
	float flBeamLength	= VectorNormalize( m_hSpotlightTarget->m_vSpotlightDir );
	m_flSpotlightCurLength = (0.80*m_flSpotlightCurLength) + (0.2*flBeamLength);

	// Fade out spotlight end if past max length.  
	if (m_flSpotlightCurLength > 2*m_flSpotlightMaxLength)
	{
		m_hSpotlightTarget->SetRenderColorA( 0 );
		m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength);
	}
	else if (m_flSpotlightCurLength > m_flSpotlightMaxLength)		
	{
		m_hSpotlightTarget->SetRenderColorA( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) );
		m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength);
	}
	else
	{
		m_hSpotlightTarget->SetRenderColorA( 1.0 );
		m_hSpotlight->SetFadeLength(m_flSpotlightCurLength);
	}

	// Adjust end width to keep beam width constant
	float flNewWidth = SPOTLIGHT_WIDTH * ( flBeamLength/m_flSpotlightMaxLength);
	
	m_hSpotlight->SetWidth(flNewWidth);
	m_hSpotlight->SetEndWidth(flNewWidth);

	m_hSpotlightTarget->m_flLightScale = 0.0;
}

//-----------------------------------------------------------------------------
// Purpose: Called just before we are deleted.
//-----------------------------------------------------------------------------
void CNPC_CScanner::UpdateOnRemove( void )
{
	// Stop combat loops if I'm alive. If I'm dead, the die sound will already have stopped it.
	if ( IsAlive() && m_bHasSpoken )
	{
		SentenceStop();
	}
	SpotlightDestroy();
	BaseClass::UpdateOnRemove();
}

//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::TakePhoto(void)
{
	ScannerEmitSound( "TakePhoto" );
	
	m_pEyeFlash->SetScale( 1.4 );
	m_pEyeFlash->SetBrightness( 255 );
	m_pEyeFlash->SetColor(255,255,255);

	Vector vRawPos		= InspectTargetPosition();
	Vector vLightPos	= vRawPos;

	// If taking picture of entity, aim at feet
	if ( GetTarget() )
	{
		if ( GetTarget()->IsPlayer() )
		{
			m_OnPhotographPlayer.FireOutput( GetTarget(), this );
			BlindFlashTarget( GetTarget() );
		}
		
		if ( GetTarget()->MyNPCPointer() != NULL )
		{
			m_OnPhotographNPC.FireOutput( GetTarget(), this );
			GetTarget()->MyNPCPointer()->DispatchInteraction( g_interactionScannerInspectBegin, NULL, this );
		}
	}

	SetIdealActivity( (Activity) ACT_SCANNER_FLARE_START );

	m_bPhotoTaken = true;
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::AttackPreFlash(void)
{
	ScannerEmitSound( "TakePhoto" );

	// If off turn on, if on turn off
	if (m_pEyeFlash->GetBrightness() == 0)
	{
		m_pEyeFlash->SetScale( 0.5 );
		m_pEyeFlash->SetBrightness( 255 );
		m_pEyeFlash->SetColor(255,0,0);
	}
	else
	{
		m_pEyeFlash->SetBrightness( 0 );
	}
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::AttackFlash(void)
{
	ScannerEmitSound( "AttackFlash" );
	m_pEyeFlash->SetScale( 1.8 );
	m_pEyeFlash->SetBrightness( 255 );
	m_pEyeFlash->SetColor(255,255,255);

	if (GetEnemy() != NULL)
	{
		Vector pos = GetEnemyLKP();
		CBroadcastRecipientFilter filter;
		te->DynamicLight( filter, 0.0, &pos, 200, 200, 255, 0, 300, 0.2, 50 );

		if (GetEnemy()->IsPlayer())
		{
			m_OnPhotographPlayer.FireOutput(GetTarget(), this);
		}
		else if( GetEnemy()->MyNPCPointer() )
		{
			m_OnPhotographNPC.FireOutput(GetTarget(), this);
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pTarget - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::BlindFlashTarget( CBaseEntity *pTarget )
{
	// Tell all the striders this person is here!
	CAI_BaseNPC **	ppAIs 	= g_AI_Manager.AccessAIs();
	int 			nAIs 	= g_AI_Manager.NumAIs();
	
	if( IsStriderScout() )
	{
		for ( int i = 0; i < nAIs; i++ )
		{
			if( FClassnameIs( ppAIs[ i ], "npc_strider" ) )
			{
				ppAIs[ i ]->UpdateEnemyMemory( pTarget, pTarget->GetAbsOrigin(), this );
			}
		}
	}

	// Only bother with player
	if ( pTarget->IsPlayer() == false )
		return;

	// Scale the flash value by how closely the player is looking at me
	Vector vFlashDir = GetAbsOrigin() - pTarget->EyePosition();
	VectorNormalize(vFlashDir);
	
	Vector vFacing;
	AngleVectors( pTarget->EyeAngles(), &vFacing );

	float dotPr	= DotProduct( vFlashDir, vFacing );

	// Not if behind us
	if ( dotPr > 0.5f )
	{
		// Make sure nothing in the way
		trace_t tr;
		AI_TraceLine ( GetAbsOrigin(), pTarget->EyePosition(), MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr );

		if ( tr.startsolid == false && tr.fraction == 1.0)
		{
			color32 white = { 255, 255, 255, SCANNER_FLASH_MAX_VALUE * dotPr };
			UTIL_ScreenFade( pTarget, white, 3.0, 0.5, FFADE_IN );
		}
	}
}

//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::AttackFlashBlind(void)
{
	if( GetEnemy() )
	{
		BlindFlashTarget( GetEnemy() );
	}

	m_pEyeFlash->SetBrightness( 0 );

	float fAttackDelay = random->RandomFloat(SCANNER_ATTACK_MIN_DELAY,SCANNER_ATTACK_MAX_DELAY);

	if( IsStriderScout() )
	{
		// Make strider scouts more snappy.
		fAttackDelay *= 0.5;
	}

	m_flNextAttack	= gpGlobals->curtime + fAttackDelay;
	m_fNextSpotlightTime = gpGlobals->curtime + 1.0f;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_CScanner::StartSmokeTrail( void )
{
	if ( m_pSmokeTrail != NULL )
		return;

	m_pSmokeTrail = SmokeTrail::CreateSmokeTrail();
	
	if ( m_pSmokeTrail )
	{
		m_pSmokeTrail->m_SpawnRate = 10;
		m_pSmokeTrail->m_ParticleLifetime = 1;
		m_pSmokeTrail->m_StartSize		= 8;
		m_pSmokeTrail->m_EndSize		= 50;
		m_pSmokeTrail->m_SpawnRadius	= 10;
		m_pSmokeTrail->m_MinSpeed		= 15;
		m_pSmokeTrail->m_MaxSpeed		= 25;
		
		m_pSmokeTrail->m_StartColor.Init( 0.5f, 0.5f, 0.5f );
		m_pSmokeTrail->m_EndColor.Init( 0, 0, 0 );
		m_pSmokeTrail->SetLifetime( 500.0f );
		m_pSmokeTrail->FollowEntity( this );
	}
}

//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CNPC_CScanner::AttackDivebomb( void )
{
	ScannerEmitSound( "DiveBomb" );

	if (m_hSpotlight)
	{
		SpotlightDestroy();
	}

	StartSmokeTrail();
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pTask - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::StartTask( const Task_t *pTask )
{
	switch (pTask->iTask)
	{
	case TASK_SCANNER_GET_PATH_TO_INSPECT_TARGET:
		{
			// Must have somewhere to fly to
			if ( HaveInspectTarget() == false )
			{
				TaskFail( "No inspection target to fly to!\n" );
				return;
			}

			if ( GetTarget() )
			{	
				//FIXME: Tweak
				//Vector idealPos = IdealGoalForMovement( InspectTargetPosition(), GetAbsOrigin(), 128.0f, 128.0f );
				
				AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin );
			
				if ( GetNavigator()->SetGoal( goal ) )
				{
					TaskComplete();
					return;
				}
			}
			else
			{
				AI_NavGoal_t goal( GOALTYPE_LOCATION, InspectTargetPosition() );
			
				if ( GetNavigator()->SetGoal( goal ) )
				{
					TaskComplete();
					return;
				}
			}

			// Don't try and inspect this target again for a few seconds
			CNPC_Citizen *pCitizen = dynamic_cast<CNPC_Citizen *>( GetTarget() );
			if ( pCitizen )
			{
				pCitizen->SetNextScannerInspectTime( gpGlobals->curtime + 5.0 );
			}

			TaskFail("No route to inspection target!\n");
		}
		break;

	case TASK_SCANNER_SPOT_INSPECT_ON:
	{
		if (GetTarget() == NULL)
		{
			TaskFail(FAIL_NO_TARGET);
		}
		else
		{
			CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer();
			if (!pNPC)
			{
				TaskFail(FAIL_NO_TARGET);
			}
			else
			{
				pNPC->DispatchInteraction(g_interactionScannerInspectBegin,NULL,this);
				
				// Now we need some time to inspect
				m_fInspectEndTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_LENGTH;
				TaskComplete();
			}
		}
		break;
	}
	case TASK_SCANNER_SPOT_INSPECT_WAIT:
	{
		if (GetTarget() == NULL)
		{
			TaskFail(FAIL_NO_TARGET);
		}
		else
		{
			CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer();
			if (!pNPC)
			{
				SetTarget( NULL );
				TaskFail(FAIL_NO_TARGET);
			}
			else
			{
				//<<TEMP>>//<<TEMP>> armband too!
				pNPC->DispatchInteraction(g_interactionScannerInspectHandsUp,NULL,this);
			}
			TaskComplete();
		}
		break;
	}
	case TASK_SCANNER_SPOT_INSPECT_OFF:
	{
		if (GetTarget() == NULL)
		{
			TaskFail(FAIL_NO_TARGET);
		}
		else
		{
			CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer();
			if (!pNPC)
			{
				TaskFail(FAIL_NO_TARGET);
			}
			else
			{
				pNPC->DispatchInteraction(g_interactionScannerInspectDone,NULL,this);

				// Clear target entity and don't inspect again for a while
				SetTarget( NULL );
				m_fCheckCitizenTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY;
				TaskComplete();
			}
		}
		break;
	}
	case TASK_SCANNER_CLEAR_INSPECT_TARGET:
	{
		ClearInspectTarget();

		TaskComplete();
		break;
	}
	case TASK_SCANNER_SET_FLY_PHOTO:
	{
		m_nFlyMode = SCANNER_FLY_PHOTO;
		m_bPhotoTaken = false;

		// Leave spotlight off for a while
		m_fNextSpotlightTime = gpGlobals->curtime + 2.0;

		TaskComplete();
		break;
	}
	case TASK_SCANNER_SET_FLY_PATROL:
	{
		// Fly in partol mode and clear any
		// remaining target entity
		m_nFlyMode = SCANNER_FLY_PATROL;
		TaskComplete();
		break;
	}
	case TASK_SCANNER_SET_FLY_CHASE:
	{
		m_nFlyMode = SCANNER_FLY_CHASE;
		TaskComplete();
		break;
	}
	case TASK_SCANNER_SET_FLY_SPOT:
	{
		m_nFlyMode = SCANNER_FLY_SPOT;
		TaskComplete();
		break;
	}
	case TASK_SCANNER_SET_FLY_ATTACK:
	{
		m_nFlyMode = SCANNER_FLY_ATTACK;
		TaskComplete();
		break;
	}

	case TASK_SCANNER_SET_FLY_DIVE:
	{
		// Pick a direction to divebomb.
		if ( GetEnemy() != NULL )
		{
			// Fly towards my enemy
			Vector vEnemyPos = GetEnemyLKP();
			m_vecDiveBombDirection = vEnemyPos - GetLocalOrigin();
		}
		else
		{
			// Pick a random forward and down direction.
			Vector forward;
			GetVectors( &forward, NULL, NULL );
			m_vecDiveBombDirection = forward + Vector( random->RandomFloat( -10, 10 ), random->RandomFloat( -10, 10 ), random->RandomFloat( -20, -10 ) );
		}
		VectorNormalize( m_vecDiveBombDirection );

		// Calculate a roll force.
		m_flDiveBombRollForce = random->RandomFloat( 20.0, 420.0 );
		if ( random->RandomInt( 0, 1 ) )
		{
			m_flDiveBombRollForce *= -1;
		}

		DiveBombSoundThink();

		m_nFlyMode = SCANNER_FLY_DIVE;
		TaskComplete();
		break;
	}

	case TASK_SCANNER_PHOTOGRAPH:
	{
		TakePhoto();
		SetWait( 0.1 );
		break;
	}

	case TASK_SCANNER_ATTACK_PRE_FLASH:
	{
		if( IsStriderScout() )
		{
			Vector vecScare = GetEnemy()->EarPosition();
			Vector vecDir = WorldSpaceCenter() - vecScare;
			VectorNormalize( vecDir );
			vecScare += vecDir * 64.0f;

			CSoundEnt::InsertSound( SOUND_DANGER, vecScare, 256, 1.0, this );
		}

		if (m_pEyeFlash)
		{
			AttackPreFlash();
			// Flash red for a while
			SetWait( 1.0f );
		}
		else
		{
			TaskFail("No Flash");
		}
		break;
	}

	case TASK_SCANNER_ATTACK_FLASH:
	{
		AttackFlash();
		// Blinding occurs slightly later
		SetWait( 0.05 );
		break;
	}

	// Override to go to inspect target position whether or not is an entity
	case TASK_GET_PATH_TO_TARGET:
	{
		if (!HaveInspectTarget())
		{
			TaskFail(FAIL_NO_TARGET);
		}
		else if (GetHintNode())
		{
			Vector vNodePos;
			GetHintNode()->GetPosition(this,&vNodePos);

			GetNavigator()->SetGoal( vNodePos );
		}
		else 
		{
			AI_NavGoal_t goal( (const Vector &)InspectTargetPosition() );
			goal.pTarget = GetTarget();
			GetNavigator()->SetGoal( goal );
		}
		break;
	}
	default:
		BaseClass::StartTask(pTask);
		break;
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_CScanner::OnScheduleChange( void )
{
	m_flSpeed = GetMaxSpeed();

	BaseClass::OnScheduleChange();
}


//-----------------------------------------------------------------------------
// Purpose: Overridden so that scanners play battle sounds while fighting.
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
bool CNPC_CScanner::ShouldPlayIdleSound( void )
{
	if ( HasSpawnFlags( SF_NPC_GAG ) )
		return false;

	if ( random->RandomInt( 0, 25 ) != 0 )
		return false;

	return true;
}


//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CNPC_CScanner::ScannerEmitSound( const char *pszSoundName )
{
	CFmtStr snd;
	
	const char szCityScanner[] = "NPC_CScanner";
	const char szShieldScanner[] = "NPC_SScanner";

	if( m_bIsClawScanner )
	{
		snd.sprintf("%s.%s", szShieldScanner, pszSoundName );
	}
	else
	{
		snd.sprintf("%s.%s", szCityScanner, pszSoundName );
	}

	m_bHasSpoken = true;

	EmitSound( snd.Access() );
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
float CNPC_CScanner::MinGroundDist( void )
{
	if ( m_nFlyMode == SCANNER_FLY_SPOT && !GetHintNode() )
	{
		return SCANNER_SPOTLIGHT_FLY_HEIGHT;
	}

	return SCANNER_NOSPOTLIGHT_FLY_HEIGHT;
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_CScanner::BlendPhyscannonLaunchSpeed()
{
	// Blend out desired velocity when launched by the physcannon
	if (!VPhysicsGetObject())
		return;

	if ( HasPhysicsAttacker( SCANNER_SMASH_TIME ) && !IsHeldByPhyscannon( ) )
	{
		Vector vecCurrentVelocity;
		VPhysicsGetObject()->GetVelocity( &vecCurrentVelocity, NULL );
		float flLerpFactor = (gpGlobals->curtime - m_flLastPhysicsInfluenceTime) / SCANNER_SMASH_TIME;
		flLerpFactor = clamp( flLerpFactor, 0.0f, 1.0f );
		flLerpFactor = SimpleSplineRemapVal( flLerpFactor, 0.0f, 1.0f, 0.0f, 1.0f );
		flLerpFactor *= flLerpFactor;
		VectorLerp( vecCurrentVelocity, m_vCurrentVelocity, flLerpFactor, m_vCurrentVelocity );
	}
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_CScanner::MoveExecute_Alive(float flInterval)
{
	// Amount of noise to add to flying
	float noiseScale = 3.0f;

	// -------------------------------------------
	//  Avoid obstacles, unless I'm dive bombing
	// -------------------------------------------
	if (m_nFlyMode != SCANNER_FLY_DIVE)
	{
		SetCurrentVelocity( GetCurrentVelocity() + VelocityToAvoidObstacles(flInterval) );
	}
	// If I am dive bombing add more noise to my flying
	else
	{
		AttackDivebombCollide(flInterval);
		noiseScale *= 4;
	}

	IPhysicsObject *pPhysics = VPhysicsGetObject();

	if ( pPhysics && pPhysics->IsAsleep() )
	{
		pPhysics->Wake();
	}

	// Add time-coherent noise to the current velocity so that it never looks bolted in place.
	AddNoiseToVelocity( noiseScale );

	if ( m_bIsClawScanner )
	{
		m_vCurrentVelocity *= ( 1 + sin( ( gpGlobals->curtime + m_flFlyNoiseBase ) * 2.5f ) * .1 );
	}

	float maxSpeed = GetEnemy() ? ( GetMaxSpeed() * 2.0f ) : GetMaxSpeed();
	if ( m_nFlyMode == SCANNER_FLY_DIVE )
	{
		maxSpeed = -1;
	}

	// Limit fall speed
	LimitSpeed( maxSpeed );

	// Blend out desired velocity when launched by the physcannon
	BlendPhyscannonLaunchSpeed();

	// Update what we're looking at
	UpdateHead( flInterval );

	// Control the tail based on our vertical travel
	float tailPerc = clamp( GetCurrentVelocity().z, -150, 250 );
	tailPerc = SimpleSplineRemapVal( tailPerc, -150, 250, -25, 80 );

	SetPoseParameter( m_nPoseTail, tailPerc );

	// Spin the dynamo based upon our speed
	float flCurrentDynamo = GetPoseParameter( m_nPoseDynamo );
	float speed	= GetCurrentVelocity().Length();
	float flDynamoSpeed = (maxSpeed > 0 ? speed / maxSpeed : 1.0) * 60;
	flCurrentDynamo -= flDynamoSpeed;
	if ( flCurrentDynamo < -180.0 )
	{
		flCurrentDynamo += 360.0;
	}
	SetPoseParameter( m_nPoseDynamo, flCurrentDynamo );

	PlayFlySound();
}

//-----------------------------------------------------------------------------
// Purpose: Handles movement towards the last move target.
// Input  : flInterval - 
//-----------------------------------------------------------------------------
bool CNPC_CScanner::OverridePathMove( CBaseEntity *pMoveTarget, float flInterval )
{
	// Save our last patrolling direction
	Vector lastPatrolDir = GetNavigator()->GetCurWaypointPos() - GetAbsOrigin();
	
	// Continue on our path
	if ( ProgressFlyPath( flInterval, pMoveTarget, (MASK_NPCSOLID|CONTENTS_WATER), false, 64 ) == AINPP_COMPLETE )
	{
		if ( IsCurSchedule( SCHED_SCANNER_PATROL ) )
		{
			m_vLastPatrolDir = lastPatrolDir;
			VectorNormalize(m_vLastPatrolDir);
		}

		return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flInterval - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_CScanner::OverrideMove( float flInterval )
{
	// ----------------------------------------------
	//	If dive bombing
	// ----------------------------------------------
	if (m_nFlyMode == SCANNER_FLY_DIVE)
	{
		MoveToDivebomb( flInterval );
	}
	else
	{
		Vector vMoveTargetPos(0,0,0);
		CBaseEntity *pMoveTarget = NULL;
		
		if ( !GetNavigator()->IsGoalActive() || ( GetNavigator()->GetCurWaypointFlags() | bits_WP_TO_PATHCORNER ) )
		{
			// Select move target 
			if ( GetTarget() != NULL )
			{
				pMoveTarget = GetTarget();
			}
			else if ( GetEnemy() != NULL )
			{
				pMoveTarget = GetEnemy();
			}
			
			// Select move target position 
			if ( HaveInspectTarget() )
			{
				vMoveTargetPos = InspectTargetPosition(); 
			}
			else if ( GetEnemy() != NULL )
			{
				vMoveTargetPos = GetEnemy()->GetAbsOrigin();
			}
		}
		else
		{
			vMoveTargetPos = GetNavigator()->GetCurWaypointPos();
		}

		ClearCondition( COND_SCANNER_FLY_CLEAR );
		ClearCondition( COND_SCANNER_FLY_BLOCKED );

		// See if we can fly there directly
		if ( pMoveTarget || HaveInspectTarget() )
		{
			trace_t tr;
			AI_TraceHull( GetAbsOrigin(), vMoveTargetPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );

			float fTargetDist = (1.0f-tr.fraction)*(GetAbsOrigin() - vMoveTargetPos).Length();
			
			if ( ( tr.m_pEnt == pMoveTarget ) || ( fTargetDist < 50 ) )
			{
				if ( g_debug_cscanner.GetBool() )
				{
					NDebugOverlay::Line(GetLocalOrigin(), vMoveTargetPos, 0,255,0, true, 0);
					NDebugOverlay::Cross3D(tr.endpos,Vector(-5,-5,-5),Vector(5,5,5),0,255,0,true,0.1);
				}

				SetCondition( COND_SCANNER_FLY_CLEAR );
			}
			else		
			{
				//HANDY DEBUG TOOL	
				if ( g_debug_cscanner.GetBool() )
				{
					NDebugOverlay::Line(GetLocalOrigin(), vMoveTargetPos, 255,0,0, true, 0);
					NDebugOverlay::Cross3D(tr.endpos,Vector(-5,-5,-5),Vector(5,5,5),255,0,0,true,0.1);
				}

				SetCondition( COND_SCANNER_FLY_BLOCKED );
			}
		}

		// If I have a route, keep it updated and move toward target
		if ( GetNavigator()->IsGoalActive() )
		{
			if ( OverridePathMove( pMoveTarget, flInterval ) )
			{
				BlendPhyscannonLaunchSpeed();
				return true;
			}
		}	
		else if (m_nFlyMode == SCANNER_FLY_SPOT)
		{
			MoveToSpotlight( flInterval );
		}
		// If photographing
		else if ( m_nFlyMode == SCANNER_FLY_PHOTO )
		{
			MoveToPhotograph( flInterval );
		}
		else if ( m_nFlyMode == SCANNER_FLY_FOLLOW )
		{
			MoveToSpotlight( flInterval );
		}
		// ----------------------------------------------
		//	If attacking
		// ----------------------------------------------
		else if (m_nFlyMode == SCANNER_FLY_ATTACK)
		{
			if ( m_hSpotlight )
			{
				SpotlightDestroy();
			}
			
			MoveToAttack( flInterval );
		}
		// -----------------------------------------------------------------
		// If I don't have a route, just decelerate
		// -----------------------------------------------------------------
		else if (!GetNavigator()->IsGoalActive())
		{
			float	myDecay	 = 9.5;
			Decelerate( flInterval, myDecay);
		}
	}
		
	MoveExecute_Alive( flInterval );

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Accelerates toward a given position.
// Input  : flInterval - Time interval over which to move.
//			vecMoveTarget - Position to move toward.
//-----------------------------------------------------------------------------
void CNPC_CScanner::MoveToTarget( float flInterval, const Vector &vecMoveTarget )
{
	// Don't move if stalling
	if ( m_flEngineStallTime > gpGlobals->curtime )
		return;
	
	// Look at our inspection target if we have one
	if ( GetEnemy() != NULL )
	{
		// Otherwise at our enemy
		TurnHeadToTarget( flInterval, GetEnemy()->EyePosition() );
	}
	else if ( HaveInspectTarget() )
	{
		TurnHeadToTarget( flInterval, InspectTargetPosition() );
	}
	else
	{
		// Otherwise face our motion direction
		TurnHeadToTarget( flInterval, vecMoveTarget );
	}

	// -------------------------------------
	// Move towards our target
	// -------------------------------------
	float myAccel;
	float myZAccel = 400.0f;
	float myDecay  = 0.15f;

	Vector vecCurrentDir;

	// Get the relationship between my current velocity and the way I want to be going.
	vecCurrentDir = GetCurrentVelocity();
	VectorNormalize( vecCurrentDir );

	Vector targetDir = vecMoveTarget - GetAbsOrigin();
	float flDist = VectorNormalize(targetDir);

	float flDot;
	flDot = DotProduct( targetDir, vecCurrentDir );

	if( flDot > 0.25 )
	{
		// If my target is in front of me, my flight model is a bit more accurate.
		myAccel = 250;
	}
	else
	{
		// Have a harder time correcting my course if I'm currently flying away from my target.
		myAccel = 128;
	}

	if ( myAccel > flDist / flInterval )
	{
		myAccel = flDist / flInterval;
	}

	if ( myZAccel > flDist / flInterval )
	{
		myZAccel = flDist / flInterval;
	}

	MoveInDirection( flInterval, targetDir, myAccel, myZAccel, myDecay );

	// calc relative banking targets
	Vector forward, right, up;
	GetVectors( &forward, &right, &up );

	m_vCurrentBanking.x	= targetDir.x;
	m_vCurrentBanking.z	= 120.0f * DotProduct( right, targetDir );
	m_vCurrentBanking.y	= 0;

	float speedPerc = SimpleSplineRemapVal( GetCurrentVelocity().Length(), 0.0f, GetMaxSpeed(), 0.0f, 1.0f );

	speedPerc = clamp( speedPerc, 0.0f, 1.0f );

	m_vCurrentBanking *= speedPerc;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flInterval - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::MoveToSpotlight( float flInterval )
{
	if ( flInterval <= 0 )
		return;

	Vector vTargetPos;

	if ( HaveInspectTarget() )
	{
		vTargetPos = InspectTargetPosition();
	}
	else if ( GetEnemy() != NULL )
	{
		vTargetPos = GetEnemyLKP();
	}
	else
	{
		return;
	}

	//float flDesiredDist = SCANNER_SPOTLIGHT_NEAR_DIST + ( ( SCANNER_SPOTLIGHT_FAR_DIST - SCANNER_SPOTLIGHT_NEAR_DIST ) / 2 );
	
	float flIdealHeightDiff = SCANNER_SPOTLIGHT_NEAR_DIST;
	if( IsEnemyPlayerInSuit() )
	{
		flIdealHeightDiff *= 0.5;
	}

	Vector idealPos = IdealGoalForMovement( vTargetPos, GetAbsOrigin(), GetGoalDistance(), flIdealHeightDiff );

	MoveToTarget( flInterval, idealPos );

	//TODO: Re-implement?

	/*
	// ------------------------------------------------
	//  Also keep my distance from other squad members
	//  unless I'm inspecting
	// ------------------------------------------------
	if (m_pSquad &&
		gpGlobals->curtime > m_fInspectEndTime)
	{
		CBaseEntity*	pNearest	= m_pSquad->NearestSquadMember(this);
		if (pNearest)
		{
			Vector			vNearestDir = (pNearest->GetLocalOrigin() - GetLocalOrigin());
			if (vNearestDir.Length() < SCANNER_SQUAD_FLY_DIST) 
			{
				vNearestDir		= pNearest->GetLocalOrigin() - GetLocalOrigin();
				VectorNormalize(vNearestDir);
				vFlyDirection  -= 0.5*vNearestDir;
			}
		}
	}

	// ---------------------------------------------------------
	//  Add evasion if I have taken damage recently
	// ---------------------------------------------------------
	if ((m_flLastDamageTime + SCANNER_EVADE_TIME) > gpGlobals->curtime)
	{
		vFlyDirection = vFlyDirection + VelocityToEvade(GetEnemyCombatCharacterPointer());
	}
	*/
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flInterval - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::MoveToAttack(float flInterval)
{
	if (GetEnemy() == NULL)
		return;

	if ( flInterval <= 0 )
		return;

	Vector vTargetPos = GetEnemyLKP();

	//float flDesiredDist = SCANNER_ATTACK_NEAR_DIST + ( ( SCANNER_ATTACK_FAR_DIST - SCANNER_ATTACK_NEAR_DIST ) / 2 );
	
	Vector idealPos = IdealGoalForMovement( vTargetPos, GetAbsOrigin(), GetGoalDistance(), SCANNER_ATTACK_NEAR_DIST );

	MoveToTarget( flInterval, idealPos );

	//FIXME: Re-implement?

	/*
	// ---------------------------------------------------------
	//  Add evasion if I have taken damage recently
	// ---------------------------------------------------------
	if ((m_flLastDamageTime + SCANNER_EVADE_TIME) > gpGlobals->curtime)
	{
		vFlyDirection = vFlyDirection + VelocityToEvade(GetEnemyCombatCharacterPointer());
	}
	*/
}


//-----------------------------------------------------------------------------
// Danger sounds. 
//-----------------------------------------------------------------------------
void CNPC_CScanner::DiveBombSoundThink()
{
	Vector vecPosition, vecVelocity;
	IPhysicsObject *pPhysicsObject = VPhysicsGetObject();

	if ( pPhysicsObject == NULL )
		return;

	pPhysicsObject->GetPosition( &vecPosition, NULL );
	pPhysicsObject->GetVelocity( &vecVelocity, NULL );
	
	CBasePlayer *pPlayer = AI_GetSinglePlayer();
	if ( pPlayer )
	{
		Vector vecDelta;
		VectorSubtract( pPlayer->GetAbsOrigin(), vecPosition, vecDelta );
		VectorNormalize( vecDelta );
		if ( DotProduct( vecDelta, vecVelocity ) > 0.5f )
		{
			Vector vecEndPoint;
			VectorMA( vecPosition, 2.0f * TICK_INTERVAL, vecVelocity, vecEndPoint );
			float flDist = CalcDistanceToLineSegment( pPlayer->GetAbsOrigin(), vecPosition, vecEndPoint );
			if ( flDist < 200.0f )
			{
				ScannerEmitSound( "DiveBombFlyby" );
				SetContextThink( &CNPC_CScanner::DiveBombSoundThink, gpGlobals->curtime + 0.5f, s_pDiveBombSoundThinkContext );
				return;
			}
		}
	}

	SetContextThink( &CNPC_CScanner::DiveBombSoundThink, gpGlobals->curtime + 2.0f * TICK_INTERVAL, s_pDiveBombSoundThinkContext );
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flInterval - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::MoveToDivebomb(float flInterval)
{
	float myAccel = 1600;
	float myDecay = 0.05f; // decay current velocity to 10% in 1 second

	// Fly towards my enemy
	Vector vEnemyPos = GetEnemyLKP();
	Vector vFlyDirection  = vEnemyPos - GetLocalOrigin();
	VectorNormalize( vFlyDirection );

	// Set net velocity 
	MoveInDirection( flInterval, m_vecDiveBombDirection, myAccel, myAccel, myDecay);

	// Spin out of control.
	Vector forward;
	VPhysicsGetObject()->LocalToWorldVector( &forward, Vector( 1.0, 0.0, 0.0 ) );
	AngularImpulse torque = forward * m_flDiveBombRollForce;
	VPhysicsGetObject()->ApplyTorqueCenter( torque );

	// BUGBUG: why Y axis and not Z?
	Vector up;
	VPhysicsGetObject()->LocalToWorldVector( &up, Vector( 0.0, 1.0, 0.0 ) );
	VPhysicsGetObject()->ApplyForceCenter( up * 2000 );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CNPC_CScanner::IsEnemyPlayerInSuit()
{
	if( GetEnemy() && GetEnemy()->IsPlayer() )
	{
		CHL2_Player *pPlayer = NULL;
		pPlayer = (CHL2_Player *)GetEnemy();

		if( pPlayer && pPlayer->IsSuitEquipped() )
		{
			return true;
		}
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : float
//-----------------------------------------------------------------------------
float CNPC_CScanner::GetGoalDistance( void )
{
	if ( m_flGoalOverrideDistance != 0.0f )
		return m_flGoalOverrideDistance;

	switch ( m_nFlyMode )
	{
	case SCANNER_FLY_PHOTO:
		return ( SCANNER_PHOTO_NEAR_DIST + ( ( SCANNER_PHOTO_FAR_DIST - SCANNER_PHOTO_NEAR_DIST ) / 2 ) );
		break;

	case SCANNER_FLY_SPOT:
		{
			float goalDist = ( SCANNER_SPOTLIGHT_NEAR_DIST + ( ( SCANNER_SPOTLIGHT_FAR_DIST - SCANNER_SPOTLIGHT_NEAR_DIST ) / 2 ) );
			if( IsEnemyPlayerInSuit() )
			{
				goalDist *= 0.5;
			}
			return goalDist;
		}
		break;
	
	case SCANNER_FLY_ATTACK:
		{
			float goalDist = ( SCANNER_ATTACK_NEAR_DIST + ( ( SCANNER_ATTACK_FAR_DIST - SCANNER_ATTACK_NEAR_DIST ) / 2 ) );
			if( IsEnemyPlayerInSuit() )
			{
				goalDist *= 0.5;
			}
			return goalDist;
		}
		break;

	case SCANNER_FLY_FOLLOW:
		return ( SCANNER_FOLLOW_DIST );
		break;
	}

	return 128.0f;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &vOut - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_CScanner::GetGoalDirection( Vector *vOut )
{
	CBaseEntity *pTarget = GetTarget();

	if ( pTarget == NULL )
		return false;

	if ( FClassnameIs( pTarget, "info_hint_air" ) || FClassnameIs( pTarget, "info_target" ) )
	{
		AngleVectors( pTarget->GetAbsAngles(), vOut );
		return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &goalPos - 
//			&startPos - 
//			idealRange - 
//			idealHeight - 
// Output : Vector
//-----------------------------------------------------------------------------
Vector CNPC_CScanner::IdealGoalForMovement( const Vector &goalPos, const Vector &startPos, float idealRange, float idealHeightDiff )
{
	Vector	vMoveDir;
	
	if ( GetGoalDirection( &vMoveDir ) == false )
	{
		vMoveDir = ( goalPos - startPos );
		vMoveDir.z = 0;
		VectorNormalize( vMoveDir );
	}

	// Move up from the position by the desired amount
	Vector vIdealPos = goalPos + Vector( 0, 0, idealHeightDiff ) + ( vMoveDir * -idealRange );

	// Trace down and make sure we can fit here
	trace_t	tr;
	AI_TraceHull( vIdealPos, vIdealPos - Vector( 0, 0, MinGroundDist() ), GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );

	// Move up otherwise
	if ( tr.fraction < 1.0f )
	{
		vIdealPos.z += ( MinGroundDist() * ( 1.0f - tr.fraction ) );
	}

	//FIXME: We also need to make sure that we fit here at all, and if not, chose a new spot

	// Debug tools
	if ( g_debug_cscanner.GetBool() )
	{
		NDebugOverlay::Cross3D( goalPos, -Vector(8,8,8), Vector(8,8,8), 255, 255, 0, true, 0.1f );
		NDebugOverlay::Cross3D( startPos, -Vector(8,8,8), Vector(8,8,8), 255, 0, 255, true, 0.1f );
		NDebugOverlay::Cross3D( vIdealPos, -Vector(8,8,8), Vector(8,8,8), 255, 255, 255, true, 0.1f );
		NDebugOverlay::Line( startPos, goalPos, 0, 255, 0, true, 0.1f );

		NDebugOverlay::Cross3D( goalPos + ( vMoveDir * -idealRange ), -Vector(8,8,8), Vector(8,8,8), 255, 255, 255, true, 0.1f );
		NDebugOverlay::Line( goalPos, goalPos + ( vMoveDir * -idealRange ), 255, 255, 0, true, 0.1f );
		NDebugOverlay::Line( goalPos + ( vMoveDir * -idealRange ), vIdealPos, 255, 255, 0, true, 0.1f );
	}

	return vIdealPos;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_CScanner::MoveToPhotograph(float flInterval)
{
	if ( HaveInspectTarget() == false )
		return;

	//float flDesiredDist = SCANNER_PHOTO_NEAR_DIST + ( ( SCANNER_PHOTO_FAR_DIST - SCANNER_PHOTO_NEAR_DIST ) / 2 );
	
	Vector idealPos = IdealGoalForMovement( InspectTargetPosition(), GetAbsOrigin(), GetGoalDistance(), 32.0f );

	MoveToTarget( flInterval, idealPos );

	//FIXME: Re-implement?

	/*
	// ------------------------------------------------
	//  Also keep my distance from other squad members
	//  unless I'm inspecting
	// ------------------------------------------------
	if (m_pSquad &&
		gpGlobals->curtime > m_fInspectEndTime)
	{
		CBaseEntity*	pNearest	= m_pSquad->NearestSquadMember(this);
		if (pNearest)
		{
			Vector			vNearestDir = (pNearest->GetLocalOrigin() - GetLocalOrigin());
			if (vNearestDir.Length() < SCANNER_SQUAD_FLY_DIST) 
			{
				vNearestDir		= pNearest->GetLocalOrigin() - GetLocalOrigin();
				VectorNormalize(vNearestDir);
				vFlyDirection  -= 0.5*vNearestDir;
			}
		}
	}
	*/
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Vector CNPC_CScanner::VelocityToEvade(CBaseCombatCharacter *pEnemy)
{
	if (pEnemy)
	{
		// -----------------------------------------
		//  Keep out of enemy's shooting position
		// -----------------------------------------
		Vector vEnemyFacing = pEnemy->BodyDirection2D( );
		Vector	vEnemyDir   = pEnemy->EyePosition() - GetLocalOrigin();
		VectorNormalize(vEnemyDir);
		float  fDotPr		= DotProduct(vEnemyFacing,vEnemyDir);

		if (fDotPr < -0.9)
		{
			Vector vDirUp(0,0,1);
			Vector vDir;
			CrossProduct( vEnemyFacing, vDirUp, vDir);

			Vector crossProduct;
			CrossProduct(vEnemyFacing, vEnemyDir, crossProduct);
			if (crossProduct.y < 0)
			{
				vDir = vDir * -1;
			}
			return (vDir);
		}
		else if (fDotPr < -0.85)
		{
			Vector vDirUp(0,0,1);
			Vector vDir;
			CrossProduct( vEnemyFacing, vDirUp, vDir);

			Vector crossProduct;
			CrossProduct(vEnemyFacing, vEnemyDir, crossProduct);
			if (random->RandomInt(0,1))
			{
				vDir = vDir * -1;
			}
			return (vDir);
		}
	}
	return vec3_origin;
}


//-----------------------------------------------------------------------------
// 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_CScanner::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* pSourceEnt)
{
	//	TODO:: - doing this by just an interrupt contition would be a lot better!
	if (interactionType ==	g_interactionScannerSupportEntity)
	{
		// Only accept help request if I'm not already busy
		if (GetEnemy() == NULL && !HaveInspectTarget())
		{
			// Only accept if target is a reasonable distance away
			CBaseEntity* pTarget = (CBaseEntity*)data;
			float fTargetDist = (pTarget->GetLocalOrigin() - GetLocalOrigin()).Length();

			if (fTargetDist < SCANNER_SQUAD_HELP_DIST)
			{
				float fInspectTime = (((CNPC_CScanner*)pSourceEnt)->m_fInspectEndTime - gpGlobals->curtime);
				SetInspectTargetToEnt(pTarget,fInspectTime);

				if (random->RandomInt(0,2)==0)
				{
					SetSchedule(SCHED_SCANNER_PHOTOGRAPH_HOVER);
				}
				else
				{
					SetSchedule(SCHED_SCANNER_SPOTLIGHT_HOVER);
				}
				return true;
			}
		}
	}
	else if (interactionType ==	g_interactionScannerSupportPosition)
	{
		// Only accept help request if I'm not already busy
		if (GetEnemy() == NULL && !HaveInspectTarget())
		{
			// Only accept if target is a reasonable distance away
			Vector vInspectPos;
			vInspectPos.x = ((Vector *)data)->x;
			vInspectPos.y = ((Vector *)data)->y;
			vInspectPos.z = ((Vector *)data)->z;

			float fTargetDist = (vInspectPos - GetLocalOrigin()).Length();

			if (fTargetDist < SCANNER_SQUAD_HELP_DIST)
			{
				float fInspectTime = (((CNPC_CScanner*)pSourceEnt)->m_fInspectEndTime - gpGlobals->curtime);
				SetInspectTargetToPos(vInspectPos,fInspectTime);

				if (random->RandomInt(0,2)==0)
				{
					SetSchedule(SCHED_SCANNER_PHOTOGRAPH_HOVER);
				}
				else
				{
					SetSchedule(SCHED_SCANNER_SPOTLIGHT_HOVER);
				}
				return true;
			}
		}
	}
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::InputDisableSpotlight( inputdata_t &inputdata )
{
	m_bNoLight = true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CNPC_CScanner::DrawDebugTextOverlays(void)
{
	int nOffset = BaseClass::DrawDebugTextOverlays();

	if ( m_debugOverlays & OVERLAY_TEXT_BIT ) 
	{
		Vector vel;
		GetVelocity( &vel, NULL );

		char tempstr[512];
		Q_snprintf( tempstr, sizeof(tempstr), "speed (max): %.2f (%.2f)", vel.Length(), m_flSpeed );
		NDebugOverlay::EntityText( entindex(), nOffset, tempstr, 0 );
		nOffset++;
	}

	return nOffset;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Output : float
//-----------------------------------------------------------------------------
float CNPC_CScanner::GetHeadTurnRate( void ) 
{ 
	if ( GetEnemy() )
		return 800.0f;

	if ( HaveInspectTarget() )
		return 500.0f;

	return 350.0f;
}

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

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flInterval - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::UpdateHead( float flInterval )
{
	float yaw = GetPoseParameter( m_nPoseFaceHoriz );
	float pitch = GetPoseParameter( m_nPoseFaceVert );

	CBaseEntity *pTarget = EntityToWatch();

	Vector	vLookPos;
	QAngle	vLookAngle;

	if ( GetAttachment( "eyes", vLookPos, vLookAngle ) == false )
	{
		vLookPos = EyePosition();
	}

	if ( pTarget != NULL )
	{
		Vector	lookDir = pTarget->EyePosition() - vLookPos;
		VectorNormalize( lookDir );
		
		if ( DotProduct( lookDir, BodyDirection3D() ) < 0.0f )
		{
			SetPoseParameter( m_nPoseFaceHoriz,	UTIL_Approach( 0, yaw, 10 ) );
			SetPoseParameter( m_nPoseFaceVert, UTIL_Approach( 0, pitch, 10 ) );
			
			return;
		}

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

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

		SetPoseParameter( m_nPoseFaceHoriz, UTIL_Approach( yaw + yawDiff, yaw, 50 ) );
		SetPoseParameter( m_nPoseFaceVert, UTIL_Approach( pitch + pitchDiff, pitch, 50 ) );
	}
	else
	{
		SetPoseParameter( m_nPoseFaceHoriz,	UTIL_Approach( 0, yaw, 10 ) );
		SetPoseParameter( m_nPoseFaceVert, UTIL_Approach( 0, pitch, 10 ) );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &linear - 
//			&angular - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::ClampMotorForces( Vector &linear, AngularImpulse &angular )
{ 
	// limit reaction forces
	if ( m_nFlyMode != SCANNER_FLY_DIVE )
	{
		linear.x = clamp( linear.x, -500, 500 );
		linear.y = clamp( linear.y, -500, 500 );
		linear.z = clamp( linear.z, -500, 500 );
	}

	// If we're dive bombing, we need to drop faster than normal
	if ( m_nFlyMode != SCANNER_FLY_DIVE )
	{
		// Add in weightlessness
		linear.z += 800;
	}

	angular.z = clamp( angular.z, -GetHeadTurnRate(), GetHeadTurnRate() );
	if ( m_nFlyMode == SCANNER_FLY_DIVE )
	{
		// Disable pitch and roll motors while crashing.
		angular.x = 0;
		angular.y = 0;
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::InputSetFollowTarget( inputdata_t &inputdata )
{
	InspectTarget( inputdata, SCANNER_FLY_FOLLOW );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::InputClearFollowTarget( inputdata_t &inputdata )
{
	SetInspectTargetToEnt( NULL, 0 );
	
	m_nFlyMode = SCANNER_FLY_PATROL;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &inputdata - 
//-----------------------------------------------------------------------------
void CNPC_CScanner::InputSetDistanceOverride( inputdata_t &inputdata )
{
	m_flGoalOverrideDistance = inputdata.value.Float();
}

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

AI_BEGIN_CUSTOM_NPC( npc_cscanner, CNPC_CScanner )

	DECLARE_TASK(TASK_SCANNER_SET_FLY_PHOTO)
	DECLARE_TASK(TASK_SCANNER_SET_FLY_PATROL)
	DECLARE_TASK(TASK_SCANNER_SET_FLY_CHASE)
	DECLARE_TASK(TASK_SCANNER_SET_FLY_SPOT)
	DECLARE_TASK(TASK_SCANNER_SET_FLY_ATTACK)
	DECLARE_TASK(TASK_SCANNER_SET_FLY_DIVE)
	DECLARE_TASK(TASK_SCANNER_PHOTOGRAPH)
	DECLARE_TASK(TASK_SCANNER_ATTACK_PRE_FLASH)
	DECLARE_TASK(TASK_SCANNER_ATTACK_FLASH)
	DECLARE_TASK(TASK_SCANNER_SPOT_INSPECT_ON)
	DECLARE_TASK(TASK_SCANNER_SPOT_INSPECT_WAIT)
	DECLARE_TASK(TASK_SCANNER_SPOT_INSPECT_OFF)
	DECLARE_TASK(TASK_SCANNER_CLEAR_INSPECT_TARGET)
	DECLARE_TASK(TASK_SCANNER_GET_PATH_TO_INSPECT_TARGET)

	DECLARE_CONDITION(COND_SCANNER_FLY_CLEAR)
	DECLARE_CONDITION(COND_SCANNER_FLY_BLOCKED)
	DECLARE_CONDITION(COND_SCANNER_HAVE_INSPECT_TARGET)
	DECLARE_CONDITION(COND_SCANNER_INSPECT_DONE)
	DECLARE_CONDITION(COND_SCANNER_CAN_PHOTOGRAPH)
	DECLARE_CONDITION(COND_SCANNER_SPOT_ON_TARGET)
	DECLARE_CONDITION(COND_SCANNER_RELEASED_FROM_PHYSCANNON)
	DECLARE_CONDITION(COND_SCANNER_GRABBED_BY_PHYSCANNON)

	DECLARE_ACTIVITY(ACT_SCANNER_SMALL_FLINCH_ALERT)
	DECLARE_ACTIVITY(ACT_SCANNER_SMALL_FLINCH_COMBAT)
	DECLARE_ACTIVITY(ACT_SCANNER_INSPECT)
	DECLARE_ACTIVITY(ACT_SCANNER_WALK_ALERT)
	DECLARE_ACTIVITY(ACT_SCANNER_WALK_COMBAT)
	DECLARE_ACTIVITY(ACT_SCANNER_FLARE)
	DECLARE_ACTIVITY(ACT_SCANNER_RETRACT)
	DECLARE_ACTIVITY(ACT_SCANNER_FLARE_PRONGS)
	DECLARE_ACTIVITY(ACT_SCANNER_RETRACT_PRONGS)
	DECLARE_ACTIVITY(ACT_SCANNER_FLARE_START)

	DECLARE_ANIMEVENT( AE_SCANNER_CLOSED )

	DECLARE_INTERACTION(g_interactionScannerInspect)
	DECLARE_INTERACTION(g_interactionScannerInspectBegin)
	DECLARE_INTERACTION(g_interactionScannerInspectDone)
	DECLARE_INTERACTION(g_interactionScannerInspectHandsUp)
	DECLARE_INTERACTION(g_interactionScannerInspectShowArmband)
	DECLARE_INTERACTION(g_interactionScannerSupportEntity)
	DECLARE_INTERACTION(g_interactionScannerSupportPosition)

	//=========================================================
	// > SCHED_SCANNER_PATROL
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_PATROL,

		"	Tasks"
		"		TASK_SCANNER_CLEAR_INSPECT_TARGET	0"
		"		TASK_SCANNER_SET_FLY_PATROL			0"
		"		TASK_SET_TOLERANCE_DISTANCE			32"
		"		TASK_SET_ROUTE_SEARCH_TIME			5"	// Spend 5 seconds trying to build a path if stuck
		"		TASK_GET_PATH_TO_RANDOM_NODE		2000"
		"		TASK_RUN_PATH						0"
		"		TASK_WAIT_FOR_MOVEMENT				0"
		""
		"	Interrupts"
		"		COND_GIVE_WAY"
		"		COND_NEW_ENEMY"
		"		COND_SEE_ENEMY"
		"		COND_SEE_FEAR"
		"		COND_HEAR_COMBAT"
		"		COND_HEAR_DANGER"
		"		COND_HEAR_PLAYER"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
		"		COND_PROVOKED"
		"		COND_SCANNER_HAVE_INSPECT_TARGET"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_SPOTLIGHT_HOVER
	//
	// Hover above target entity, trying to get spotlight
	// on my target
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_SPOTLIGHT_HOVER,

		"	Tasks"
		"		TASK_SCANNER_SET_FLY_SPOT			0"
		"		TASK_SET_ACTIVITY					ACTIVITY:ACT_WALK  "
		"		TASK_WAIT							1"
		""
		"	Interrupts"
		"		COND_SCANNER_SPOT_ON_TARGET"
		"		COND_SCANNER_INSPECT_DONE"
		"		COND_SCANNER_FLY_BLOCKED"
		"		COND_NEW_ENEMY"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_SPOTLIGHT_INSPECT_POS
	//
	// Inspect a position once spotlight is on it
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_SPOTLIGHT_INSPECT_POS,

		"	Tasks"
		"		TASK_SCANNER_SET_FLY_SPOT			0"
		"		TASK_SET_ACTIVITY					ACTIVITY:ACT_SCANNER_INSPECT"
		"		TASK_SPEAK_SENTENCE					3"	// Curious sound
		"		TASK_WAIT							5"
		"		TASK_SCANNER_CLEAR_INSPECT_TARGET	0"
		""
		"	Interrupts"
		"		COND_SCANNER_INSPECT_DONE"
		"		COND_HEAR_DANGER"
		"		COND_HEAR_COMBAT"
		"		COND_NEW_ENEMY"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_SPOTLIGHT_INSPECT_CIT
	//
	// Inspect a citizen once spotlight is on it
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_SPOTLIGHT_INSPECT_CIT,

		"	Tasks"
		"		TASK_SCANNER_SET_FLY_SPOT			0"
		"		TASK_SET_ACTIVITY					ACTIVITY:ACT_SCANNER_INSPECT"
		"		TASK_SPEAK_SENTENCE					0"	// Stop!
		"		TASK_WAIT							1"
		"		TASK_SCANNER_SPOT_INSPECT_ON		0"
		"		TASK_WAIT							2"
		"		TASK_SPEAK_SENTENCE					1"	// Hands on head or Show Armband!
		"		TASK_WAIT							1"
		"		TASK_SCANNER_SPOT_INSPECT_WAIT		0"
		"		TASK_WAIT							5"
		"		TASK_SPEAK_SENTENCE					2"	// Free to go!
		"		TASK_WAIT							1"
		"		TASK_SCANNER_SPOT_INSPECT_OFF		0"
		"		TASK_SCANNER_CLEAR_INSPECT_TARGET	0"
		""
		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_PHOTOGRAPH_HOVER
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_PHOTOGRAPH_HOVER,

		"	Tasks"
		"		TASK_SCANNER_SET_FLY_PHOTO			0"
		"		TASK_WAIT							2"
		""
		"	Interrupts"
		"		COND_SCANNER_INSPECT_DONE"
		"		COND_SCANNER_CAN_PHOTOGRAPH"
		"		COND_SCANNER_FLY_BLOCKED"
		"		COND_NEW_ENEMY"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_ATTACK_HOVER
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_ATTACK_HOVER,

		"	Tasks"
		"		TASK_SCANNER_SET_FLY_ATTACK			0"
		"		TASK_SET_ACTIVITY					ACTIVITY:ACT_IDLE"
		"		TASK_WAIT							0.1"
		""
		"	Interrupts"
		"		COND_TOO_FAR_TO_ATTACK"
		"		COND_SCANNER_FLY_BLOCKED"
		"		COND_NEW_ENEMY"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_PHOTOGRAPH
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_PHOTOGRAPH,

		"	Tasks"
		"		TASK_SCANNER_SET_FLY_PHOTO			0"
		"		TASK_SCANNER_PHOTOGRAPH				0"
		""
		"	Interrupts"
		"		COND_SCANNER_INSPECT_DONE"
		"		COND_NEW_ENEMY"
		"		COND_ENEMY_DEAD"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_ATTACK_FLASH
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_ATTACK_FLASH,

		"	Tasks"
		"		TASK_SCANNER_SET_FLY_ATTACK			0"
		"		TASK_SET_ACTIVITY					ACTIVITY:ACT_IDLE"
		"		TASK_SCANNER_ATTACK_PRE_FLASH		0 "
		"		TASK_SCANNER_ATTACK_FLASH			0"
		"		TASK_WAIT							0.5"
		""
		"	Interrupts"
		"		COND_NEW_ENEMY"
		"		COND_ENEMY_DEAD"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_ATTACK_DIVEBOMB
	//
	// Only done when scanner is dead
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_ATTACK_DIVEBOMB,

		"	Tasks"
		"		TASK_SCANNER_SET_FLY_DIVE			0"
		"		TASK_SET_ACTIVITY					ACTIVITY:ACT_IDLE"
		"		TASK_WAIT							10"
		""
		"	Interrupts"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_CHASE_ENEMY
	//
	//  Different interrupts than normal chase enemy.  
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_CHASE_ENEMY,

		"	Tasks"
		"		 TASK_SCANNER_SET_FLY_CHASE			0"
		"		 TASK_SET_FAIL_SCHEDULE				SCHEDULE:SCHED_SCANNER_PATROL"
		"		 TASK_SET_TOLERANCE_DISTANCE		120"
		"		 TASK_GET_PATH_TO_ENEMY				0"
		"		 TASK_RUN_PATH						0"
		"		 TASK_WAIT_FOR_MOVEMENT				0"
		""
		""
		"	Interrupts"
		"		COND_SCANNER_FLY_CLEAR"
		"		COND_NEW_ENEMY"
		"		COND_ENEMY_DEAD"
		"		COND_LOST_ENEMY"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_CHASE_TARGET
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_CHASE_TARGET,

		"	Tasks"
		"		 TASK_SCANNER_SET_FLY_CHASE			0"
		"		 TASK_SET_TOLERANCE_DISTANCE		64"
		"		 TASK_GET_PATH_TO_TARGET			0"	//FIXME: This is wrong!
		"		 TASK_RUN_PATH						0"
		"		 TASK_WAIT_FOR_MOVEMENT				0"
		""
		"	Interrupts"
		"		COND_SCANNER_FLY_CLEAR"
		"		COND_NEW_ENEMY"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_MOVE_TO_INSPECT
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_MOVE_TO_INSPECT,

		"	Tasks"
		"		 TASK_SET_FAIL_SCHEDULE						SCHEDULE:SCHED_SCANNER_PATROL"
		"		 TASK_SET_TOLERANCE_DISTANCE				128"
		"		 TASK_SCANNER_GET_PATH_TO_INSPECT_TARGET	0"
		"		 TASK_RUN_PATH								0"
		"		 TASK_WAIT_FOR_MOVEMENT						0"
		""
		"	Interrupts"
		"		COND_SCANNER_FLY_CLEAR"
		"		COND_NEW_ENEMY"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)
	
	//=========================================================
	// > SCHED_SCANNER_FOLLOW_HOVER
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_FOLLOW_HOVER,

		"	Tasks"
		"		TASK_SET_ACTIVITY					ACTIVITY:ACT_IDLE"
		"		TASK_WAIT							0.1"
		""
		"	Interrupts"
		"		COND_SCANNER_FLY_BLOCKED"
		"		COND_SCANNER_GRABBED_BY_PHYSCANNON"
	)

	//=========================================================
	// > SCHED_SCANNER_HELD_BY_PHYSCANNON
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_SCANNER_HELD_BY_PHYSCANNON,

		"	Tasks"
		"		TASK_SET_ACTIVITY					ACTIVITY:ACT_IDLE"
		"		TASK_WAIT							5.0"
		""
		"	Interrupts"
		"		COND_LIGHT_DAMAGE"
		"		COND_HEAVY_DAMAGE"
		"		COND_SCANNER_RELEASED_FROM_PHYSCANNON"
	)

	
AI_END_CUSTOM_NPC()
