//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Base class for humanoid NPCs intended to fight along side player in close
// environments
//
//=============================================================================//

#ifndef NPC_PLAYERCOMPANION_H
#define NPC_PLAYERCOMPANION_H

#include "ai_playerally.h"

#include "ai_behavior_follow.h"
#include "ai_behavior_standoff.h"
#include "ai_behavior_assault.h"
#include "ai_behavior_lead.h"
#include "ai_behavior_actbusy.h"
#include "ai_behavior_fear.h"

#ifdef HL2_EPISODIC
#include "ai_behavior_operator.h"
#include "ai_behavior_passenger_companion.h"
#endif

#if defined( _WIN32 )
#pragma once
#endif

enum AIReadiness_t
{
	AIRL_PANIC = -2,
	AIRL_STEALTH = -1,
	AIRL_RELAXED = 0,
	AIRL_STIMULATED,
	AIRL_AGITATED,
};

enum AIReadinessUse_t
{
	AIRU_NEVER,
	AIRU_ALWAYS,
	AIRU_ONLY_PLAYER_SQUADMATES,
};


class CCompanionActivityRemap : public CActivityRemap
{
public:
	CCompanionActivityRemap( void ) : 
	  m_fUsageBits( 0 ),
	  m_readiness( AIRL_RELAXED ),
	  m_bAiming( false ),
	  m_bWeaponRequired( false ),
	  m_bInVehicle( false ) {} 

	// This bitfield maps which bits of data are being utilized by this data structure, since not all criteria
	// in the parsed file are essential.  You must add corresponding bits to the definitions below and maintain
	// their state in the parsing of the file, as well as check the bitfield before accessing the data.  This
	// could be encapsulated into this class, but we'll probably move away from this model and closer to something
	// more akin to the response rules -- jdw

	int				m_fUsageBits;

	AIReadiness_t	m_readiness;
	bool			m_bAiming;
	bool			m_bWeaponRequired;
	bool			m_bInVehicle;		// For future expansion, this needs to speak more to the exact seat, role, and vehicle
};

// Usage bits for remap "extra" parsing - if these bits are set, the associated data has changed
#define bits_REMAP_READINESS		(1<<0)
#define bits_REMAP_AIMING			(1<<1)
#define bits_REMAP_WEAPON_REQUIRED	(1<<2)
#define bits_REMAP_IN_VEHICLE		(1<<3)

// Readiness modes that only change due to mapmaker scripts
#define READINESS_MIN_VALUE			-2
#define READINESS_MODE_PANIC		-2
#define READINESS_MODE_STEALTH		-1

// Readiness modes that change normally
#define READINESS_VALUE_RELAXED		0.1f
#define READINESS_VALUE_STIMULATED	0.95f
#define READINESS_VALUE_AGITATED	1.0f

class CPhysicsProp;

//-----------------------------------------------------------------------------
//
// CLASS: CNPC_PlayerCompanion
//
//-----------------------------------------------------------------------------

class CNPC_PlayerCompanion : public CAI_PlayerAlly
{
	DECLARE_CLASS( CNPC_PlayerCompanion, CAI_PlayerAlly );

public:
	//---------------------------------
	bool			CreateBehaviors();
	void			Precache();
	void			Spawn();
	virtual void	SelectModel() {};

	virtual int		Restore( IRestore &restore );
	virtual void	DoCustomSpeechAI( void );

	//---------------------------------
	int 			ObjectCaps();
	bool 			ShouldAlwaysThink();

	Disposition_t	IRelationType( CBaseEntity *pTarget );
	
	bool			IsSilentSquadMember() const;

	//---------------------------------
	// Behavior
	//---------------------------------
	void 			GatherConditions();
	virtual void	PredictPlayerPush();
	void			BuildScheduleTestBits();

	CSound			*GetBestSound( int validTypes = ALL_SOUNDS );
	bool			QueryHearSound( CSound *pSound );
	bool			QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC = false );
	bool			ShouldIgnoreSound( CSound * );
	
	int 			SelectSchedule();

	virtual int 	SelectScheduleDanger();
	virtual int 	SelectSchedulePriorityAction();
	virtual int 	SelectScheduleNonCombat()			{ return SCHED_NONE; }
	virtual int 	SelectScheduleCombat();
	int 			SelectSchedulePlayerPush();

	virtual bool	CanReload( void );

	virtual bool	ShouldDeferToFollowBehavior();
	bool			ShouldDeferToPassengerBehavior( void );

	bool			IsValidReasonableFacing( const Vector &vecSightDir, float sightDist );
	
	int 			TranslateSchedule( int scheduleType );
	
	void 			StartTask( const Task_t *pTask );
	void 			RunTask( const Task_t *pTask );
	
	Activity		TranslateActivityReadiness( Activity activity );
	Activity		NPC_TranslateActivity( Activity eNewActivity );
	void 			HandleAnimEvent( animevent_t *pEvent );
	bool			HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);

	int				GetSoundInterests();
	
	void 			Touch( CBaseEntity *pOther );

	virtual bool	IgnorePlayerPushing( void );

	void			ModifyOrAppendCriteria( AI_CriteriaSet& set );
	void			Activate( void );

	void			PrepareReadinessRemap( void );
	
	virtual bool	IsNavigationUrgent( void );

	//---------------------------------
	// Readiness
	//---------------------------------

protected:
	virtual bool	IsReadinessCapable();
	bool			IsReadinessLocked() { return gpGlobals->curtime < m_flReadinessLockedUntil; }
	void			AddReadiness( float flAdd, bool bOverrideLock = false );
	void			SubtractReadiness( float flAdd, bool bOverrideLock = false );
	void			SetReadinessValue( float flSet );
	void			SetReadinessSensitivity( float flSensitivity ) { m_flReadinessSensitivity = flSensitivity; }
	virtual void	UpdateReadiness();
	virtual float	GetReadinessDecay();
	bool			IsInScriptedReadinessState( void ) { return (m_flReadiness < 0 ); }

	CUtlVector< CCompanionActivityRemap > m_activityMappings;

public:
	float			GetReadinessValue()	{ return m_flReadiness; }
	int				GetReadinessLevel();
	void			SetReadinessLevel( int iLevel, bool bOverrideLock, bool bSlam );
	void			LockReadiness( float duration = -1.0f ); // Defaults to indefinitely locked
	void			UnlockReadiness( void );

	virtual			void ReadinessLevelChanged( int iPriorLevel ) { 	}

	void			InputGiveWeapon( inputdata_t &inputdata );

#ifdef HL2_EPISODIC
	//---------------------------------
	// Vehicle passenger
	//---------------------------------
	void			InputEnterVehicle( inputdata_t &inputdata );
	void			InputEnterVehicleImmediately( inputdata_t &inputdata );
	void			InputCancelEnterVehicle( inputdata_t &inputdata );
	void			InputExitVehicle( inputdata_t &inputdata );
	bool			CanEnterVehicle( void );
	bool			CanExitVehicle( void );
	void			EnterVehicle( CBaseEntity *pEntityVehicle, bool bImmediately );
	virtual bool	ExitVehicle( void );

	virtual void	UpdateEfficiency( bool bInPVS );
	virtual bool	IsInAVehicle( void ) const;
	virtual	IServerVehicle *GetVehicle( void );
	virtual CBaseEntity *GetVehicleEntity( void );

	virtual bool CanRunAScriptedNPCInteraction( bool bForced = false );
	virtual bool IsAllowedToDodge( void );

#endif // HL2_EPISODIC

public:

	virtual void	OnPlayerKilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info );

	//---------------------------------
	//---------------------------------
	bool PickTacticalLookTarget( AILookTargetArgs_t *pArgs );

	//---------------------------------
	// Aiming
	//---------------------------------
	CBaseEntity		*GetAimTarget() { return m_hAimTarget; }
	void			SetAimTarget( CBaseEntity *pTarget );
	void			StopAiming( char *pszReason = NULL );
	bool			FindNewAimTarget();
	void			OnNewLookTarget();
	bool			ShouldBeAiming();
	virtual bool	IsAllowedToAim();
	bool			HasAimLOS( CBaseEntity *pAimTarget );
	void			AimGun();
	CBaseEntity		*GetAlternateMoveShootTarget();

	//---------------------------------
	// Combat
	//---------------------------------
	virtual void 	LocateEnemySound() {};

	bool			IsValidEnemy( CBaseEntity *pEnemy );

	bool 			IsSafeFromFloorTurret( const Vector &vecLocation, CBaseEntity *pTurret );

	bool			ShouldMoveAndShoot( void );
	void			OnUpdateShotRegulator();

	void			DecalTrace( trace_t *pTrace, char const *decalName );
	bool 			FCanCheckAttacks();
	Vector 			GetActualShootPosition( const Vector &shootOrigin );
	WeaponProficiency_t CalcWeaponProficiency( CBaseCombatWeapon *pWeapon );
	bool			ShouldLookForBetterWeapon();
	bool			Weapon_CanUse( CBaseCombatWeapon *pWeapon );
	void			Weapon_Equip( CBaseCombatWeapon *pWeapon );
	void			PickupWeapon( CBaseCombatWeapon *pWeapon );
	
	bool 			FindCoverPos( CBaseEntity *pEntity, Vector *pResult);
	bool			FindCoverPosInRadius( CBaseEntity *pEntity, const Vector &goalPos, float coverRadius, Vector *pResult );
	bool			FindCoverPos( CSound *pSound, Vector *pResult );
	bool			FindMortarCoverPos( CSound *pSound, Vector *pResult );
	bool 			IsCoverPosition( const Vector &vecThreat, const Vector &vecPosition );

	bool			IsEnemyTurret() { return ( GetEnemy() && IsTurret(GetEnemy()) ); }
	
	static bool		IsMortar( CBaseEntity *pEntity );
	static bool		IsSniper( CBaseEntity *pEntity );
	static bool		IsTurret(  CBaseEntity *pEntity );
	static bool		IsGunship( CBaseEntity *pEntity );
	
	//---------------------------------
	// Damage handling
	//---------------------------------
	int 			OnTakeDamage_Alive( const CTakeDamageInfo &info );
	void 			OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker );

	//---------------------------------
	// Hints
	//---------------------------------
	bool			FValidateHintType ( CAI_Hint *pHint );

	//---------------------------------
	// Navigation
	//---------------------------------
	bool			IsValidMoveAwayDest( const Vector &vecDest );
	bool 			ValidateNavGoal();
	bool 			OverrideMove( float flInterval );				// Override to take total control of movement (return true if done so)
	bool			MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost );
	float			GetIdealSpeed() const;
	float			GetIdealAccel() const;
	bool			OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult );

	//---------------------------------
	// Inputs
	//---------------------------------
	void 			InputOutsideTransition( inputdata_t &inputdata );
	void			InputSetReadinessPanic( inputdata_t &inputdata );
	void			InputSetReadinessStealth( inputdata_t &inputdata );
	void			InputSetReadinessLow( inputdata_t &inputdata );
	void			InputSetReadinessMedium( inputdata_t &inputdata );
	void			InputSetReadinessHigh( inputdata_t &inputdata );
	void			InputLockReadiness( inputdata_t &inputdata );
#if HL2_EPISODIC
	void			InputClearAllOuputs( inputdata_t &inputdata ); ///< annihilate every output on this npc
#endif

	bool			AllowReadinessValueChange( void );

protected:
	//-----------------------------------------------------
	// Conditions, Schedules, Tasks
	//-----------------------------------------------------
	enum
	{
		COND_PC_HURTBYFIRE = BaseClass::NEXT_CONDITION,
		COND_PC_SAFE_FROM_MORTAR,
		COND_PC_BECOMING_PASSENGER,
		NEXT_CONDITION,

		SCHED_PC_COWER = BaseClass::NEXT_SCHEDULE,
		SCHED_PC_MOVE_TOWARDS_COVER_FROM_BEST_SOUND,
		SCHED_PC_TAKE_COVER_FROM_BEST_SOUND,
		SCHED_PC_FLEE_FROM_BEST_SOUND,
		SCHED_PC_FAIL_TAKE_COVER_TURRET,
		SCHED_PC_FAKEOUT_MORTAR,
		SCHED_PC_GET_OFF_COMPANION,
		NEXT_SCHEDULE,

		TASK_PC_WAITOUT_MORTAR = BaseClass::NEXT_TASK,
		TASK_PC_GET_PATH_OFF_COMPANION,
		NEXT_TASK,
	};

private:
	void SetupCoverSearch( CBaseEntity *pEntity );
	void CleanupCoverSearch();

	//-----------------------------------------------------
	
	bool			m_bMovingAwayFromPlayer;
	bool			m_bWeightPathsInCover;

	enum eCoverType
	{
		CT_NORMAL,
		CT_TURRET,
		CT_MORTAR
	};

	static eCoverType	gm_fCoverSearchType;
	static bool 		gm_bFindingCoverFromAllEnemies;

	CSimpleSimTimer		m_FakeOutMortarTimer;

	// Derived classes should not use the expresser directly
	virtual CAI_Expresser *GetExpresser()	{ return BaseClass::GetExpresser(); }

protected:
	//-----------------------------------------------------

	virtual CAI_FollowBehavior &GetFollowBehavior( void ) { return m_FollowBehavior; }

	CAI_AssaultBehavior				m_AssaultBehavior;
	CAI_FollowBehavior				m_FollowBehavior;
	CAI_StandoffBehavior			m_StandoffBehavior;
	CAI_LeadBehavior				m_LeadBehavior;
	CAI_ActBusyBehavior				m_ActBusyBehavior;
#ifdef HL2_EPISODIC
	CAI_OperatorBehavior			m_OperatorBehavior;
	CAI_PassengerBehaviorCompanion	m_PassengerBehavior;
	CAI_FearBehavior				m_FearBehavior;
#endif
	//-----------------------------------------------------

	bool	ShouldAlwaysTransition( void );

	// Readiness is a value that's fed by various events in the NPC's AI. It is used
	// to make decisions about what type of posture the NPC should be in (relaxed, agitated).
	// It is not used to make decisions about what to do in the AI. 
	float m_flReadiness;
	float m_flReadinessSensitivity;
	bool m_bReadinessCapable;
	float m_flReadinessLockedUntil;
	float	m_fLastBarrelExploded;
	float	m_fLastPlayerKill;
	int		m_iNumConsecutiveBarrelsExploded;  // Companions keep track of the # of consecutive barrels exploded by the player and speaks a response as it increases
	int		m_iNumConsecutivePlayerKills;  // Alyx keeps track of the # of consecutive kills by the player and speaks a response as it increases

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

	float m_flBoostSpeed;

	//-----------------------------------------------------
	
	CSimpleSimTimer m_AnnounceAttackTimer;

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

	EHANDLE m_hAimTarget;

#ifdef HL2_EPISODIC
	CHandle<CPhysicsProp>	m_hFlare;
#endif // HL2_EPISODIC

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

	static string_t gm_iszMortarClassname;
	static string_t gm_iszFloorTurretClassname;
	static string_t gm_iszGroundTurretClassname;
	static string_t gm_iszShotgunClassname;
	static string_t	gm_iszRollerMineClassname;

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

	void	InputEnableAlwaysTransition( inputdata_t &inputdata );
	void	InputDisableAlwaysTransition( inputdata_t &inputdata );
	bool	m_bAlwaysTransition;
	bool	m_bDontPickupWeapons;

	void	InputEnableWeaponPickup( inputdata_t &inputdata );
	void	InputDisableWeaponPickup( inputdata_t &inputdata );

	COutputEvent	m_OnWeaponPickup;

	CStopwatch		m_SpeechWatch_PlayerLooking;

	DECLARE_DATADESC();
	DEFINE_CUSTOM_AI;
};

// Used for quick override move searches against certain types of entities
void OverrideMoveCache_ForceRepopulateList( void );
CBaseEntity *OverrideMoveCache_FindTargetsInRadius( CBaseEntity *pFirstEntity, const Vector &vecOrigin, float flRadius );
void OverrideMoveCache_LevelInitPreEntity( void );
void OverrideMoveCache_LevelShutdownPostEntity( void );

#endif // NPC_PLAYERCOMPANION_H