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

#ifndef AI_BEHAVIOR_ACTBUSY_H
#define AI_BEHAVIOR_ACTBUSY_H
#ifdef _WIN32
#pragma once
#endif

#include "ai_behavior.h"
#include "ai_goalentity.h"

//-----------------------------------------------------------------------------
enum
{
	ACTBUSY_TYPE_DEFAULT = 0,
	ACTBUSY_TYPE_COMBAT,
};

enum busyinterrupt_t
{
	BA_INT_NONE,		// Nothing breaks us out of this
	BA_INT_DANGER,		// Only danger signals interrupts this busy anim. The player will be ignored.
	BA_INT_PLAYER,		// The Player's presence interrupts this busy anim
	BA_INT_AMBUSH,		// We're waiting to ambush enemies. Don't break on danger sounds in front of us.
	BA_INT_COMBAT,		// Only break out if we're shot at.
	BA_INT_ZOMBIESLUMP,	// Zombies who are slumped on the ground.
	BA_INT_SIEGE_DEFENSE,
};

enum busyanimparts_t
{
	BA_BUSY,
	BA_ENTRY,
	BA_EXIT,

	BA_MAX_ANIMS,
};

struct busyanim_t
{
	string_t			iszName;
	Activity			iActivities[BA_MAX_ANIMS];
	string_t			iszSequences[BA_MAX_ANIMS];
	string_t			iszSounds[BA_MAX_ANIMS];
	float				flMinTime;		// Min time spent in this busy animation
	float				flMaxTime;		// Max time spent in this busy animation. 0 means continue until interrupted.
	busyinterrupt_t		iBusyInterruptType;
	bool				bUseAutomovement;
};

struct busysafezone_t
{
	Vector	vecMins;
	Vector	vecMaxs;
};

#define NO_MAX_TIME -1

class CAI_ActBusyGoal;

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CAI_ActBusyBehavior : public CAI_SimpleBehavior
{
	DECLARE_CLASS( CAI_ActBusyBehavior, CAI_SimpleBehavior );
public:
	DECLARE_DATADESC();
	CAI_ActBusyBehavior();

	enum
	{
		// Schedules
		SCHED_ACTBUSY_START_BUSYING = BaseClass::NEXT_SCHEDULE,
		SCHED_ACTBUSY_BUSY,
		SCHED_ACTBUSY_STOP_BUSYING,
		SCHED_ACTBUSY_LEAVE,
		SCHED_ACTBUSY_TELEPORT_TO_BUSY,
		NEXT_SCHEDULE,
		
		// Tasks
		TASK_ACTBUSY_PLAY_BUSY_ANIM = BaseClass::NEXT_TASK,
		TASK_ACTBUSY_PLAY_ENTRY,
		TASK_ACTBUSY_PLAY_EXIT,
		TASK_ACTBUSY_TELEPORT_TO_BUSY,
		TASK_ACTBUSY_WALK_PATH_TO_BUSY,
		TASK_ACTBUSY_GET_PATH_TO_ACTBUSY,
		TASK_ACTBUSY_VERIFY_EXIT,
		NEXT_TASK,
		
		// Conditions
		COND_ACTBUSY_LOST_SEE_ENTITY = BaseClass::NEXT_CONDITION,
		COND_ACTBUSY_AWARE_OF_ENEMY_IN_SAFE_ZONE,
		COND_ACTBUSY_ENEMY_TOO_CLOSE,
		NEXT_CONDITION,
	};
	
	virtual const char *GetName() {	return "ActBusy"; }

	void	Enable( CAI_ActBusyGoal *pGoal, float flRange, bool bVisibleOnly );
	void	OnRestore();
	void	SetBusySearchRange( float flRange );
	void	Disable( void );
	void	ForceActBusy( CAI_ActBusyGoal *pGoal, CAI_Hint *pHintNode = NULL, float flMaxTime = NO_MAX_TIME, bool bVisibleOnly = false, bool bTeleportToBusy = false, bool bUseNearestBusy = false, CBaseEntity *pSeeEntity = NULL, Activity activity = ACT_INVALID );
	void	ForceActBusyLeave( bool bVisibleOnly = false );
	void	StopBusying( void );
	bool	IsStopBusying();
	CAI_Hint *FindActBusyHintNode( void );
	CAI_Hint *FindCombatActBusyHintNode( void );
	CAI_Hint *FindCombatActBusyTeleportHintNode( void );
	bool	CanSelectSchedule( void );
	bool	IsCurScheduleOverridable( void );
	bool	ShouldIgnoreSound( CSound *pSound );
	void	OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker );
	int		OnTakeDamage_Alive( const CTakeDamageInfo &info );
	void	GatherConditions( void );
	void	BuildScheduleTestBits( void );
	void	EndScheduleSelection( void );
	Activity NPC_TranslateActivity( Activity nActivity );
	void	HandleAnimEvent( animevent_t *pEvent );
	void	CheckAndCleanupOnExit( void );
	bool	FValidateHintType( CAI_Hint *pHint );
	bool	ActBusyNodeStillActive( void );
	bool	IsMovingToBusy( void ) { return m_bMovingToBusy; }
	bool	IsEnabled( void ) { return m_bEnabled; }
	float	GetReasonableFacingDist( void ) { return 0; }	// Actbusy ignores reasonable facing
	bool	IsInterruptable( void );
	bool	ShouldPlayerAvoid( void );
	void	SetUseRenderBounds( bool bUseBounds ) { m_bUseRenderBoundsForCollision = bUseBounds; }
	void	ComputeAndSetRenderBounds();
	bool	CanFlinch( void );
	bool	CanRunAScriptedNPCInteraction( bool bForced );
	void	OnScheduleChange();
	bool	QueryHearSound( CSound *pSound );
	void	OnSeeEntity( CBaseEntity *pEntity );
	bool	NeedsToPlayExitAnim() { return m_bNeedsToPlayExitAnim; }

	// Returns true if the current NPC is acting busy, or moving to an actbusy
	bool	IsActive( void );
	// Returns true if the current NPC is actually acting busy (i.e. inside an act busy anim)
	bool	IsInsideActBusy( void ) { return m_bBusy; }

	// Combat act busy stuff
	bool	IsCombatActBusy();
	void 	CollectSafeZoneVolumes( CAI_ActBusyGoal *pActBusyGoal );
	bool	IsInSafeZone( CBaseEntity *pEntity );
	int		CountEnemiesInSafeZone();

private:
	virtual int		SelectSchedule( void );
	int				SelectScheduleForLeaving( void );
	int				SelectScheduleWhileNotBusy( int iBase );
	int				SelectScheduleWhileBusy( void );
	virtual void	StartTask( const Task_t *pTask );
	virtual void	RunTask( const Task_t *pTask );
	void			NotifyBusyEnding( void );
	bool			HasAnimForActBusy( int iActBusy, busyanimparts_t AnimPart );
	bool			PlayAnimForActBusy( busyanimparts_t AnimPart );
	void			PlaySoundForActBusy( busyanimparts_t AnimPart ); 

private:
	bool			m_bEnabled;
	bool			m_bForceActBusy;
	Activity		m_ForcedActivity;
	bool			m_bTeleportToBusy;
	bool			m_bUseNearestBusy;
	bool			m_bLeaving;
	bool			m_bVisibleOnly;
	bool			m_bUseRenderBoundsForCollision;
	float			m_flForcedMaxTime;
	bool			m_bBusy;
	bool			m_bMovingToBusy;
	bool			m_bNeedsToPlayExitAnim;
	float			m_flNextBusySearchTime;	
	float			m_flEndBusyAt;
	float			m_flBusySearchRange;
	bool			m_bInQueue;
	int				m_iCurrentBusyAnim;
	CHandle<CAI_ActBusyGoal> m_hActBusyGoal;
	bool			m_bNeedToSetBounds;
	EHANDLE			m_hSeeEntity;
	float			m_fTimeLastSawSeeEntity;
	bool			m_bExitedBusyToDueLostSeeEntity;
	bool			m_bExitedBusyToDueSeeEnemy;

	int				m_iNumConsecutivePathFailures; // Count how many times we failed to find a path to a node, so we can consider teleporting.
	bool			m_bAutoFireWeapon;
	float			m_flDeferUntil;
	int				m_iNumEnemiesInSafeZone;

	CUtlVector<busysafezone_t>m_SafeZones;

	DEFINE_CUSTOM_SCHEDULE_PROVIDER;
};


//-----------------------------------------------------------------------------
// Purpose: A level tool to control the actbusy behavior.
//-----------------------------------------------------------------------------
class CAI_ActBusyGoal : public CAI_GoalEntity
{
	DECLARE_CLASS( CAI_ActBusyGoal, CAI_GoalEntity );
public:
	CAI_ActBusyGoal()
	{
		// Support legacy maps, where this value used to be set from a constant (with a value of 1).
		// Now designers can specify whatever they want in Hammer. Take care of old maps by setting
		// this in the constructor. (sjb)
		m_flSeeEntityTimeout = 1;
	}

	virtual void NPCMovingToBusy( CAI_BaseNPC *pNPC );
	virtual void NPCAbortedMoveTo( CAI_BaseNPC *pNPC );
	virtual void NPCStartedBusy( CAI_BaseNPC *pNPC );
	virtual void NPCStartedLeavingBusy( CAI_BaseNPC *pNPC );
	virtual void NPCFinishedBusy( CAI_BaseNPC *pNPC );
	virtual void NPCLeft( CAI_BaseNPC *pNPC );
	virtual void NPCLostSeeEntity( CAI_BaseNPC *pNPC );
	virtual void NPCSeeEnemy( CAI_BaseNPC *pNPC );

	int GetType() { return m_iType; }
	bool IsCombatActBusyTeleportAllowed()	{ return m_bAllowCombatActBusyTeleport; }

protected:
	CAI_ActBusyBehavior *GetBusyBehaviorForNPC( const char *pszActorName, CBaseEntity *pActivator, CBaseEntity *pCaller, const char *sInputName );
	CAI_ActBusyBehavior *GetBusyBehaviorForNPC( CBaseEntity *pEntity, const char *sInputName );

	void		 EnableGoal( CAI_BaseNPC *pAI );

	// Inputs
	virtual void InputActivate( inputdata_t &inputdata );
	virtual void InputDeactivate( inputdata_t &inputdata );
	void		 InputSetBusySearchRange( inputdata_t &inputdata );
	void		 InputForceNPCToActBusy( inputdata_t &inputdata );
	void		 InputForceThisNPCToActBusy( inputdata_t &inputdata );
	void		 InputForceThisNPCToLeave( inputdata_t &inputdata );

	DECLARE_DATADESC();

protected:
	float			m_flBusySearchRange;
	bool			m_bVisibleOnly;
	int				m_iType;
	bool			m_bAllowCombatActBusyTeleport;

public:
	// Let the actbusy behavior query these so we don't have to duplicate the data.
	string_t		m_iszSeeEntityName;
	float			m_flSeeEntityTimeout;
	string_t		m_iszSafeZoneVolume;
	int				m_iSightMethod;

protected:
	COutputEHANDLE	m_OnNPCStartedBusy;
	COutputEHANDLE	m_OnNPCFinishedBusy;
	COutputEHANDLE	m_OnNPCLeft;
	COutputEHANDLE	m_OnNPCLostSeeEntity;
	COutputEHANDLE	m_OnNPCSeeEnemy;
};

// Maximum number of nodes allowed in an actbusy queue
#define MAX_QUEUE_NODES		20

//-----------------------------------------------------------------------------
// Purpose: A level tool to control the actbusy behavior to create NPC queues 
//-----------------------------------------------------------------------------
class CAI_ActBusyQueueGoal : public CAI_ActBusyGoal
{
	DECLARE_CLASS( CAI_ActBusyQueueGoal, CAI_ActBusyGoal );
public:
	virtual void Spawn( void );
	virtual void DrawDebugGeometryOverlays( void );
	virtual void NPCMovingToBusy( CAI_BaseNPC *pNPC );
	virtual void NPCStartedBusy( CAI_BaseNPC *pNPC );
	virtual void NPCAbortedMoveTo( CAI_BaseNPC *pNPC );
	virtual void NPCFinishedBusy( CAI_BaseNPC *pNPC );
	virtual void NPCStartedLeavingBusy( CAI_BaseNPC *pNPC );

	virtual void InputActivate( inputdata_t &inputdata );
	void		 InputPlayerStartedBlocking( inputdata_t &inputdata );
	void		 InputPlayerStoppedBlocking( inputdata_t &inputdata );
	void		 InputMoveQueueUp( inputdata_t &inputdata );

	void		 PushNPCBackInQueue( CAI_BaseNPC *pNPC, int iStartingNode );
	void		 RemoveNPCFromQueue( CAI_BaseNPC *pNPC );
	void		 RecalculateQueueCount( void );
	void		 QueueThink( void );
  	void		 MoveQueueUp( void );
  	void		 MoveQueueUpThink( void );
	bool		 NodeIsOccupied( int i );
	CAI_BaseNPC			*GetNPCOnNode( int iNode );
	CAI_ActBusyBehavior *GetQueueBehaviorForNPC( CAI_BaseNPC	*pNPC );

	DECLARE_DATADESC();

private:
	int						m_iCurrentQueueCount;
	CHandle<CAI_Hint>		m_hNodes[ MAX_QUEUE_NODES ];
	bool					m_bPlayerBlockedNodes[ MAX_QUEUE_NODES ];
	EHANDLE					m_hExitNode;
	EHANDLE					m_hExitingNPC;
	bool					m_bForceReachFront;

	// Read from mapdata
	string_t		m_iszNodes[ MAX_QUEUE_NODES ];
	string_t		m_iszExitNode;

	// Outputs
	COutputInt		m_OnQueueMoved;
	COutputEHANDLE	m_OnNPCLeftQueue;
	COutputEHANDLE	m_OnNPCStartedLeavingQueue;
};

#endif // AI_BEHAVIOR_ACTBUSY_H
