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

#ifndef NPC_HUNTER_H
#define NPC_HUNTER_H

#if defined( _WIN32 )
#pragma once
#endif

class CBaseEntity;
class CNPC_Hunter;
class CNPC_Strider;

/// true if given entity pointer is a hunter.
bool Hunter_IsHunter(CBaseEntity *pEnt);

// call throughs for member functions

void Hunter_StriderBusterAttached( CBaseEntity *pHunter, CBaseEntity *pAttached );
void Hunter_StriderBusterDetached( CBaseEntity *pHunter, CBaseEntity *pAttached );
void Hunter_StriderBusterLaunched( CBaseEntity *pBuster );

#include "ai_baseactor.h"
#include "ai_behavior_follow.h"
#include "ai_squadslot.h"

#define	HUNTER_FOLLOW_DISTANCE	2000.0f
#define	HUNTER_FOLLOW_DISTANCE_SQR	(HUNTER_FOLLOW_DISTANCE * HUNTER_FOLLOW_DISTANCE)

//-----------------------------------------------------------------------------
//	Squad slots
//-----------------------------------------------------------------------------
enum SquadSlot_t
{	
	SQUAD_SLOT_HUNTER_CHARGE = LAST_SHARED_SQUADSLOT,
	SQUAD_SLOT_HUNTER_FLANK_FIRST,
	SQUAD_SLOT_HUNTER_FLANK_LAST = SQUAD_SLOT_HUNTER_FLANK_FIRST,
	SQUAD_SLOT_RUN_SHOOT,
};

//-----------------------------------------------------------------------------
// Hunter follow behavior
//-----------------------------------------------------------------------------
class CAI_HunterEscortBehavior : public CAI_FollowBehavior
{
	typedef CAI_FollowBehavior BaseClass;

	DECLARE_CLASS( CAI_HunterEscortBehavior, CAI_FollowBehavior );
	
public:

	CAI_HunterEscortBehavior() :
		BaseClass( AI_FollowParams_t( AIF_HUNTER, true ) ),
		m_flTimeEscortReturn( 0 ),
		m_bEnabled( false )
	{
	}

	CNPC_Hunter *GetOuter() { return (CNPC_Hunter *)( BaseClass::GetOuter() ); }

	void SetEscortTarget( CNPC_Strider *pLeader, bool fFinishCurSchedule = false );
	CNPC_Strider * GetEscortTarget() { return (CNPC_Strider *)GetFollowTarget(); }

	bool FarFromFollowTarget()
	{ 
		return ( GetFollowTarget() && (GetAbsOrigin() - GetFollowTarget()->GetAbsOrigin()).LengthSqr() > HUNTER_FOLLOW_DISTANCE_SQR ); 
	}

	void DrawDebugGeometryOverlays();
	bool ShouldFollow();
	void BuildScheduleTestBits();

	void BeginScheduleSelection();

	void GatherConditions();
	void GatherConditionsNotActive();
	int SelectSchedule();
	int	FollowCallBaseSelectSchedule();
	void StartTask( const Task_t *pTask );
	void RunTask( const Task_t *pTask );

	void CheckBreakEscort();

	void OnDamage( const CTakeDamageInfo &info );
	static void DistributeFreeHunters();
	static void FindFreeHunters( CUtlVector<CNPC_Hunter *> *pFreeHunters );

	float			m_flTimeEscortReturn;
	CSimpleSimTimer	m_FollowAttackTimer;
	bool			m_bEnabled;

	static float	gm_flLastDefendSound; // not saved and loaded, it's okay to yell again after a load

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

	DECLARE_DATADESC();
};

class CNPC_Hunter : public CAI_BaseActor
{
	DECLARE_CLASS( CNPC_Hunter, CAI_BaseActor );

public:
	CNPC_Hunter();
	~CNPC_Hunter();

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

	void			Precache();
	void			Spawn();
	void			PostNPCInit();
	void			Activate();
	void			UpdateOnRemove();
	void			OnRestore();
	bool			CreateBehaviors();
	void			IdleSound();
	bool			ShouldPlayIdleSound();
	bool			CanBecomeRagdoll();
	Activity		GetDeathActivity();
	void			StopLoopingSounds();

	const impactdamagetable_t &GetPhysicsImpactDamageTable();

	Class_T			Classify();
	Vector			BodyTarget( const Vector &posSrc, bool bNoisy /*= true*/ );

	int				DrawDebugTextOverlays();
	void			DrawDebugGeometryOverlays();

	void			UpdateEfficiency( bool bInPVS );

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

	virtual Vector	GetNodeViewOffset()					{ return BaseClass::GetDefaultEyeOffset();		}

	int				GetSoundInterests();

	bool			IsInLargeOutdoorMap();

	//---------------------------------
	// CAI_BaseActor
	//---------------------------------
	const char *SelectRandomExpressionForState( NPC_STATE state );
	void PlayExpressionForState( NPC_STATE state );

	//---------------------------------
	// CBaseAnimating
	//---------------------------------
	float			GetIdealAccel() const { return GetIdealSpeed(); }

	//---------------------------------
	// Behavior
	//---------------------------------
	void			NPCThink();
	void			PrescheduleThink();
	void			GatherConditions();
	void			CollectSiegeTargets();
	void			ManageSiegeTargets();
	void			KillCurrentSiegeTarget();
	bool			QueryHearSound( CSound *pSound );
	void			OnSeeEntity( CBaseEntity *pEntity );
	void			CheckFlinches() {} // Hunter handles on own
	void			BuildScheduleTestBits();
	NPC_STATE		SelectIdealState();
	int				SelectSchedule();
	int				SelectCombatSchedule();
	int				SelectSiegeSchedule();
	int				TranslateSchedule( int scheduleType );
	void			StartTask( const Task_t *pTask );
	void			RunTask( const Task_t *pTask );
	Activity		NPC_TranslateActivity( Activity baseAct );
	void			OnChangeActivity( Activity eNewActivity );
	
	void			HandleAnimEvent( animevent_t *pEvent );
	bool			HandleInteraction(int interactionType, void *data, CBaseCombatCharacter *pSourceEnt);

	void			PlayerHasIlluminatedNPC( CBasePlayer *pPlayer, float flDot );

	void			AddEntityRelationship( CBaseEntity *pEntity, Disposition_t nDisposition, int nPriority );
	float			EnemyDistTolerance() { return 100.0f; }

	bool			ScheduledMoveToGoalEntity( int scheduleType, CBaseEntity *pGoalEntity, Activity movementActivity );

	void			OnChangeHintGroup( string_t oldGroup, string_t newGroup );

	bool			IsUsingSiegeTargets()	{ return m_iszSiegeTargetName != NULL_STRING; }
	
	//---------------------------------
	// Inputs
	//---------------------------------
	void			InputDodge( inputdata_t &inputdata );
	void			InputFlankEnemy( inputdata_t &inputdata );
	void			InputDisableShooting( inputdata_t &inputdata );
	void			InputEnableShooting( inputdata_t &inputdata );
	void			InputFollowStrider( inputdata_t &inputdata );
	void			InputUseSiegeTargets( inputdata_t &inputdata );
	void			InputEnableSquadShootDelay( inputdata_t &inputdata );
	void			InputDisableSquadShootDelay( inputdata_t &inputdata );
	void			InputEnableUnplantedShooting( inputdata_t &inputdata );
	void			InputDisableUnplantedShooting( inputdata_t &inputdata );

	//---------------------------------
	// Combat
	//---------------------------------
	bool			FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
	bool			IsValidEnemy( CBaseEntity *pEnemy );
	
	Disposition_t	IRelationType( CBaseEntity *pTarget );
	int				IRelationPriority( CBaseEntity *pTarget );

	void			SetSquad( CAI_Squad *pSquad );

	bool			UpdateEnemyMemory( CBaseEntity *pEnemy, const Vector &position, CBaseEntity *pInformer = NULL );

	int				RangeAttack1Conditions( float flDot, float flDist );
	int				RangeAttack2Conditions( float flDot, float flDist );

	int				MeleeAttack1Conditions ( float flDot, float flDist );
	int				MeleeAttack1ConditionsVsEnemyInVehicle( CBaseCombatCharacter *pEnemy, float flDot );

	int				MeleeAttack2Conditions( float flDot, float flDist );

	bool			WeaponLOSCondition(const Vector &ownerPos, const Vector &targetPos, bool bSetConditions);
	bool			TestShootPosition(const Vector &vecShootPos, const Vector &targetPos );

	Vector			Weapon_ShootPosition();

	CBaseEntity *	MeleeAttack( float flDist, int iDamage, QAngle &qaViewPunch, Vector &vecVelocityPunch, int BloodOrigin );

	void			MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType );
	void			DoMuzzleFlash( int nAttachment );

	bool			CanShootThrough( const trace_t &tr, const Vector &vecTarget );

	int				CountRangedAttackers();
	void 			DelayRangedAttackers( float minDelay, float maxDelay, bool bForced = false );

	//---------------------------------
	//	Sounds & speech
	//---------------------------------
	void			AlertSound();
	void			PainSound( const CTakeDamageInfo &info );
	void			DeathSound( const CTakeDamageInfo &info );

	//---------------------------------
	// Damage handling
	//---------------------------------
	void			TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr );
	bool			IsHeavyDamage( const CTakeDamageInfo &info );
	int				OnTakeDamage( const CTakeDamageInfo &info );
	int				OnTakeDamage_Alive( const CTakeDamageInfo &info );
	void			Event_Killed( const CTakeDamageInfo &info );

	void			StartBleeding();
	inline bool		IsBleeding() { return m_bIsBleeding; }
	void			Explode();

	void			SetupGlobalModelData();
	
	//---------------------------------
	// Navigation & Movement
	//---------------------------------
	bool			OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval );
	float			MaxYawSpeed();
	bool			IsJumpLegal(const Vector &startPos, const Vector &apex, const Vector &endPos) const;
	float			GetJumpGravity() const		{ return 3.0f; }
	bool			ShouldProbeCollideAgainstEntity( CBaseEntity *pEntity );
	void            TaskFail( AI_TaskFailureCode_t code );
	void			TaskFail( const char *pszGeneralFailText )	{ TaskFail( MakeFailCode( pszGeneralFailText ) ); }

	CAI_BaseNPC *	GetEntity() { return this; }

	//---------------------------------
	// Magnade
	//---------------------------------	
	void	StriderBusterAttached( CBaseEntity *pAttached );
	void	StriderBusterDetached( CBaseEntity *pAttached );

protected:

	void ConsiderFlinching( const CTakeDamageInfo &info );

	void TaskFindDodgeActivity();

	void GatherChargeConditions();
	void GatherIndoorOutdoorConditions();

	// Charge attack.
	bool ShouldCharge( const Vector &startPos, const Vector &endPos, bool useTime, bool bCheckForCancel );
	void ChargeLookAhead();
	float ChargeSteer();
	bool EnemyIsRightInFrontOfMe( CBaseEntity **pEntity );
	void ChargeDamage( CBaseEntity *pTarget );
	bool HandleChargeImpact( Vector vecImpact, CBaseEntity *pEntity );

	void BeginVolley( int nNum, float flStartTime );
	bool ShootFlechette( CBaseEntity *pTargetEntity, bool bSingleShot );
	bool ShouldSeekTarget( CBaseEntity *pTargetEntity, bool bStriderBuster );
	void GetShootDir( Vector &vecDir, const Vector &vecSrc, CBaseEntity *pTargetEntity, bool bStriderbuster, int nShotNum, bool bSingleShot );
	bool ClampShootDir( Vector &vecDir );

	void SetAim( const Vector &aimDir, float flInterval );
	void RelaxAim( float flInterval );
	void UpdateAim();
	void UpdateEyes();
	void LockBothEyes( float flDuration );
	void UnlockBothEyes( float flDuration );

	void TeslaThink();
	void BleedThink();
	void JostleVehicleThink();

	void FollowStrider( const char *szStrider );
	void FollowStrider( CNPC_Strider * pStrider );
	int NumHuntersInMySquad();

	bool CanPlantHere( const Vector &vecPos );

	//---------------------------------
	// Foot handling
	//---------------------------------
	Vector LeftFootHit( float eventtime );
	Vector RightFootHit( float eventtime );
	Vector BackFootHit( float eventtime );

	void FootFX( const Vector &origin );

	CBaseEntity *GetEnemyVehicle();
	bool IsCorporealEnemy( CBaseEntity *pEnemy );

	void PhysicsDamageEffect( const Vector &vecPos, const Vector &vecDir );
	bool PlayerFlashlightOnMyEyes( CBasePlayer *pPlayer );

	//-----------------------------------------------------
	// Conditions, Schedules, Tasks
	//-----------------------------------------------------
	enum 
	{
		SCHED_HUNTER_RANGE_ATTACK1 = BaseClass::NEXT_SCHEDULE,
		SCHED_HUNTER_RANGE_ATTACK2,
		SCHED_HUNTER_MELEE_ATTACK1,
		SCHED_HUNTER_DODGE,
		SCHED_HUNTER_CHASE_ENEMY,
		SCHED_HUNTER_CHASE_ENEMY_MELEE,
		SCHED_HUNTER_COMBAT_FACE,
		SCHED_HUNTER_FLANK_ENEMY,
		SCHED_HUNTER_CHANGE_POSITION,
		SCHED_HUNTER_CHANGE_POSITION_FINISH,
		SCHED_HUNTER_SIDESTEP,
		SCHED_HUNTER_PATROL,
		SCHED_HUNTER_FLINCH_STICKYBOMB,
		SCHED_HUNTER_STAGGER,
		SCHED_HUNTER_PATROL_RUN,
		SCHED_HUNTER_TAKE_COVER_FROM_ENEMY,
		SCHED_HUNTER_HIDE_UNDER_COVER,
		SCHED_HUNTER_FAIL_IMMEDIATE, // instant fail without waiting
		SCHED_HUNTER_CHARGE_ENEMY,
		SCHED_HUNTER_FAIL_CHARGE_ENEMY,
		SCHED_HUNTER_FOUND_ENEMY,
		SCHED_HUNTER_FOUND_ENEMY_ACK,
		SCHED_HUNTER_RANGE_ATTACK2_VS_STRIDERBUSTER,
		SCHED_HUNTER_RANGE_ATTACK2_VS_STRIDERBUSTER_LATENT,
		SCHED_HUNTER_GOTO_HINT,
		SCHED_HUNTER_CLEAR_HINTNODE,
		SCHED_HUNTER_FAIL_DODGE,
		SCHED_HUNTER_SIEGE_STAND,
		SCHED_HUNTER_CHANGE_POSITION_SIEGE,

		TASK_HUNTER_AIM = BaseClass::NEXT_TASK,
		TASK_HUNTER_FIND_DODGE_POSITION,
		TASK_HUNTER_DODGE,
		TASK_HUNTER_PRE_RANGE_ATTACK2,
		TASK_HUNTER_SHOOT_COMMIT,
		TASK_HUNTER_BEGIN_FLANK,
		TASK_HUNTER_ANNOUNCE_FLANK,
		TASK_HUNTER_STAGGER,
		TASK_HUNTER_CORNERED_TIMER,
		TASK_HUNTER_FIND_SIDESTEP_POSITION,
		TASK_HUNTER_CHARGE,
		TASK_HUNTER_CHARGE_DELAY,
		TASK_HUNTER_FINISH_RANGE_ATTACK,
		TASK_HUNTER_WAIT_FOR_MOVEMENT_FACING_ENEMY,

		COND_HUNTER_SHOULD_PATROL = BaseClass::NEXT_CONDITION,
		COND_HUNTER_FORCED_FLANK_ENEMY,
		COND_HUNTER_FORCED_DODGE,
		COND_HUNTER_CAN_CHARGE_ENEMY,
		COND_HUNTER_HIT_BY_STICKYBOMB,
		COND_HUNTER_STAGGERED,
		COND_HUNTER_IS_INDOORS,
		COND_HUNTER_SEE_STRIDERBUSTER,
		COND_HUNTER_INCOMING_VEHICLE,
		COND_HUNTER_NEW_HINTGROUP,
		COND_HUNTER_CANT_PLANT,
		COND_HUNTER_SQUADMATE_FOUND_ENEMY,
	};

	enum HunterEyeStates_t
	{
		HUNTER_EYE_STATE_TOP_LOCKED = 0,
		HUNTER_EYE_STATE_BOTTOM_LOCKED,
		HUNTER_EYE_STATE_BOTH_LOCKED,
		HUNTER_EYE_STATE_BOTH_UNLOCKED,
	};

	string_t		m_iszFollowTarget;		// Name of the strider we should follow.
	CSimpleStopwatch m_BeginFollowDelay;

	int				m_nKillingDamageType;
	HunterEyeStates_t m_eEyeState;

	float			m_aimYaw;
	float			m_aimPitch;

	float			m_flShootAllowInterruptTime;
	float			m_flNextChargeTime;				// Prevents us from doing our threat display too often.
	float			m_flNextDamageTime;
	float			m_flNextSideStepTime;

	CSimpleSimTimer m_HeavyDamageDelay;
	CSimpleSimTimer m_FlinchTimer;
	CSimpleSimTimer m_EyeSwitchTimer;		// Controls how often we switch which eye is focusing on our enemy.

	bool			m_bTopMuzzle;	// Used to alternate between top muzzle FX and bottom muzzle FX.
	bool			m_bEnableSquadShootDelay;
	bool			m_bIsBleeding;

	Activity		m_eDodgeActivity;
	CSimpleSimTimer m_RundownDelay;
	CSimpleSimTimer m_IgnoreVehicleTimer;

	bool m_bDisableShooting;	// Range attack disabled via an input. Used for scripting melee attacks.
	
	bool m_bFlashlightInEyes;	// The player is shining the flashlight on our eyes.
	float m_flPupilDilateTime;	// When to dilate our pupils if the flashlight is no longer on our eyes.

	Vector m_vecEnemyLastSeen;
	Vector m_vecLastCanPlantHerePos;
	Vector m_vecStaggerDir;

	bool m_bPlanted;
	bool m_bLastCanPlantHere;
	bool m_bMissLeft;
	bool m_bEnableUnplantedShooting;

	static float	gm_flMinigunDistZ;
	static Vector	gm_vecLocalRelativePositionMinigun;

	static int gm_nTopGunAttachment;
	static int gm_nBottomGunAttachment;
	static int gm_nAimYawPoseParam;
	static int gm_nAimPitchPoseParam;
	static int gm_nBodyYawPoseParam;
	static int gm_nBodyPitchPoseParam;
	static int gm_nStaggerYawPoseParam;
	static int gm_nHeadCenterAttachment;
	static int gm_nHeadBottomAttachment;
	static float gm_flHeadRadius;

	static int gm_nUnplantedNode;
	static int gm_nPlantedNode;

	CAI_HunterEscortBehavior m_EscortBehavior;

	int m_nFlechettesQueued;
	int m_nClampedShots;				// The number of consecutive shots fired at an out-of-max yaw target.

	float m_flNextRangeAttack2Time;		// Time when we can fire another volley of flechettes.
	float m_flNextFlechetteTime;		// Time to fire the next flechette in this volley.
	
	float m_flNextMeleeTime;
	float m_flTeslaStopTime;

	string_t m_iszCurrentExpression;

	// buster fu	
	CUtlVector< EHANDLE >	m_hAttachedBusters;		// List of busters attached to us
	float m_fCorneredTimer; ///< hunter was cornered when fleeing player; it won't flee again until this time

	CSimpleSimTimer m_CheckHintGroupTimer;

	DEFINE_CUSTOM_AI;

	DECLARE_DATADESC();

	friend class CAI_HunterEscortBehavior;
	friend class CHunterMaker;

	bool	m_bInLargeOutdoorMap;
	float	m_flTimeSawEnemyAgain;

	// Sounds
	//CSoundPatch	*m_pGunFiringSound;

	CUtlVector<EHANDLE> m_pSiegeTargets;
	string_t	m_iszSiegeTargetName;
	float		m_flTimeNextSiegeTargetAttack;
	EHANDLE		m_hCurrentSiegeTarget;
	
	EHANDLE		m_hHitByVehicle;
};


#endif
