//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: The downtrodden citizens of City 17. Timid when unarmed, they will
//			rise up against their Combine oppressors when given a weapon.
//
//=============================================================================//

#ifndef	NPC_CITIZEN_H
#define	NPC_CITIZEN_H

#include "npc_playercompanion.h"

#include "ai_behavior_functank.h"

struct SquadCandidate_t;

//-----------------------------------------------------------------------------
//
// CLASS: CNPC_Citizen
//
//-----------------------------------------------------------------------------

//-------------------------------------
// Spawnflags
//-------------------------------------

#define SF_CITIZEN_FOLLOW			( 1 << 16 )	//65536 follow the player as soon as I spawn.
#define	SF_CITIZEN_MEDIC			( 1 << 17 )	//131072
#define SF_CITIZEN_RANDOM_HEAD		( 1 << 18 )	//262144
#define SF_CITIZEN_AMMORESUPPLIER	( 1 << 19 )	//524288
#define SF_CITIZEN_NOT_COMMANDABLE	( 1 << 20 ) //1048576
#define SF_CITIZEN_IGNORE_SEMAPHORE ( 1 << 21 ) //2097152		Work outside the speech semaphore system
#define SF_CITIZEN_RANDOM_HEAD_MALE	( 1 << 22 )	//4194304
#define SF_CITIZEN_RANDOM_HEAD_FEMALE ( 1 << 23 )//8388608
#define SF_CITIZEN_USE_RENDER_BOUNDS ( 1 << 24 )//16777216

//-------------------------------------
// Animation events
//-------------------------------------

enum CitizenType_t
{
	CT_DEFAULT,
	CT_DOWNTRODDEN,
	CT_REFUGEE,
	CT_REBEL,
	CT_UNIQUE
};

//-----------------------------------------------------------------------------
// Citizen expression types
//-----------------------------------------------------------------------------
enum CitizenExpressionTypes_t
{
	CIT_EXP_UNASSIGNED,	// Defaults to this, selects other in spawn.

	CIT_EXP_SCARED,
	CIT_EXP_NORMAL,
	CIT_EXP_ANGRY,

	CIT_EXP_LAST_TYPE,
};

#define CITIZEN_AE_HEAL 1

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

class CNPC_Citizen : public CNPC_PlayerCompanion
{
	DECLARE_CLASS( CNPC_Citizen, CNPC_PlayerCompanion );
public:
	CNPC_Citizen()
	 :	m_iHead( -1 )
	{
	}

	//---------------------------------
	bool			CreateBehaviors();
	void			Precache();
	void			PrecacheAllOfType( CitizenType_t );
	void			Spawn();
	void			PostNPCInit();
	virtual void	SelectModel();
	void			SelectExpressionType();
	void			Activate();
	void			FixupMattWeapon();

	void			OnRestore();
	
	//---------------------------------
	string_t 		GetModelName() const;
	
	Class_T 		Classify();

	bool 			ShouldAlwaysThink();

	//---------------------------------
	// Behavior
	//---------------------------------
	bool			ShouldBehaviorSelectSchedule( CAI_BehaviorBase *pBehavior );
	void 			GatherConditions();
	void			PredictPlayerPush();
	void 			PrescheduleThink();
	void			BuildScheduleTestBits();

	bool			FInViewCone( CBaseEntity *pEntity );

	int				SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
	int				SelectSchedule();

	int 			SelectSchedulePriorityAction();
	int 			SelectScheduleHeal();
	int 			SelectScheduleRetrieveItem();
	int 			SelectScheduleNonCombat();
	int 			SelectScheduleManhackCombat();
	int 			SelectScheduleCombat();
	bool			ShouldDeferToFollowBehavior();
	int 			TranslateSchedule( int scheduleType );

	bool			ShouldAcceptGoal( CAI_BehaviorBase *pBehavior, CAI_GoalEntity *pGoal );
	void			OnClearGoal( CAI_BehaviorBase *pBehavior, CAI_GoalEntity *pGoal );
	
	void 			StartTask( const Task_t *pTask );
	void 			RunTask( const Task_t *pTask );
	
	Activity		NPC_TranslateActivity( Activity eNewActivity );
	void 			HandleAnimEvent( animevent_t *pEvent );
	void			TaskFail( AI_TaskFailureCode_t code );

	void			PickupWeapon( CBaseCombatWeapon *pWeapon );
	void 			PickupItem( CBaseEntity *pItem );

	void 			SimpleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );

	bool			IgnorePlayerPushing( void );

	int				DrawDebugTextOverlays( void );

	virtual const char *SelectRandomExpressionForState( NPC_STATE state );

	//---------------------------------
	// Combat
	//---------------------------------
	bool 			OnBeginMoveAndShoot();
	void 			OnEndMoveAndShoot();
	
	virtual bool	UseAttackSquadSlots()	{ return false; }
	void 			LocateEnemySound();

	bool			IsManhackMeleeCombatant();
	
	Vector 			GetActualShootPosition( const Vector &shootOrigin );
	void 			OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon );

	bool			ShouldLookForBetterWeapon();


	//---------------------------------
	// Damage handling
	//---------------------------------
	int 			OnTakeDamage_Alive( const CTakeDamageInfo &info );
	
	//---------------------------------
	// Commander mode
	//---------------------------------
	bool 			IsCommandable();
	bool			IsPlayerAlly( CBasePlayer *pPlayer = NULL );
	bool			CanJoinPlayerSquad();
	bool			WasInPlayerSquad();
	bool			HaveCommandGoal() const;
	bool			IsCommandMoving();
	bool			ShouldAutoSummon();
	bool 			IsValidCommandTarget( CBaseEntity *pTarget );
	bool 			NearCommandGoal();
	bool 			VeryFarFromCommandGoal();
	bool 			TargetOrder( CBaseEntity *pTarget, CAI_BaseNPC **Allies, int numAllies );
	void 			MoveOrder( const Vector &vecDest, CAI_BaseNPC **Allies, int numAllies );
	void			OnMoveOrder();
	void 			CommanderUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
	bool			ShouldSpeakRadio( CBaseEntity *pListener );
	void			OnMoveToCommandGoalFailed();
	void			AddToPlayerSquad();
	void			RemoveFromPlayerSquad();
	void 			TogglePlayerSquadState();
	void			UpdatePlayerSquad();
	static int		PlayerSquadCandidateSortFunc( const SquadCandidate_t *, const SquadCandidate_t * );
	void 			FixupPlayerSquad();
	void 			ClearFollowTarget();
	void 			UpdateFollowCommandPoint();
	bool			IsFollowingCommandPoint();
	CAI_BaseNPC *	GetSquadCommandRepresentative();
	void			SetSquad( CAI_Squad *pSquad );
	void			AddInsignia();
	void			RemoveInsignia();
	bool			SpeakCommandResponse( AIConcept_t concept, const char *modifiers = NULL );
	
	//---------------------------------
	// Scanner interaction
	//---------------------------------
	float 			GetNextScannerInspectTime() { return m_fNextInspectTime; }
	void			SetNextScannerInspectTime( float flTime ) { m_fNextInspectTime = flTime; }
	bool			HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);
	
	//---------------------------------
	// Hints
	//---------------------------------
	Activity		GetHintActivity( short sHintType, Activity HintsActivity );
	bool			FValidateHintType ( CAI_Hint *pHint );

	//---------------------------------
	// Special abilities
	//---------------------------------
	bool 			IsMedic() 			{ return HasSpawnFlags(SF_CITIZEN_MEDIC); }
	bool 			IsAmmoResupplier() 	{ return HasSpawnFlags(SF_CITIZEN_AMMORESUPPLIER); }
	
	bool 			CanHeal();
	bool 			ShouldHealTarget( CBaseEntity *pTarget, bool bActiveUse = false );
	void 			Heal();

	bool			ShouldLookForHealthItem();
	
	//---------------------------------
	// Inputs
	//---------------------------------
	void 			InputStartPatrolling( inputdata_t &inputdata );
	void 			InputStopPatrolling( inputdata_t &inputdata );
	void			InputGiveWeapon( inputdata_t &inputdata );
	void			InputSetCommandable( inputdata_t &inputdata );
	void			InputSetMedicOn( inputdata_t &inputdata );
	void			InputSetMedicOff( inputdata_t &inputdata );
	void			InputSetAmmoResupplierOn( inputdata_t &inputdata );
	void			InputSetAmmoResupplierOff( inputdata_t &inputdata );
	void			InputSpeakIdleResponse( inputdata_t &inputdata );

	//---------------------------------
	//	Sounds & speech
	//---------------------------------
	void			FearSound(void);
	void			DeathSound(void);
	bool			UseSemaphore( void );

	// Citizen responses
	bool 			RespondedTo( const char *ResponseConcept );

	virtual void	OnChangeRunningBehavior( CAI_BehaviorBase *pOldBehavior,  CAI_BehaviorBase *pNewBehavior );
private:
	//-----------------------------------------------------
	// Conditions, Schedules, Tasks
	//-----------------------------------------------------
	enum
	{
		COND_CIT_PLAYERHEALREQUEST = BaseClass::NEXT_CONDITION,
		COND_CIT_COMMANDHEAL,
		COND_CIT_HURTBYFIRE,
		COND_CIT_START_INSPECTION,
		
		SCHED_CITIZEN_PLAY_INSPECT_ACTIVITY = BaseClass::NEXT_SCHEDULE,
		SCHED_CITIZEN_HEAL,
		SCHED_CITIZEN_RANGE_ATTACK1_RPG,
		SCHED_CITIZEN_PATROL,
		SCHED_CITIZEN_MOURN_PLAYER,
		SCHED_CITIZEN_SIT_ON_TRAIN,
		SCHED_CITIZEN_STRIDER_RANGE_ATTACK1_RPG,
		
		TASK_CIT_HEAL = BaseClass::NEXT_TASK,
		TASK_CIT_RPG_AUGER,
		TASK_CIT_PLAY_INSPECT_SEQUENCE,
		TASK_CIT_SIT_ON_TRAIN,
		TASK_CIT_LEAVE_TRAIN,
		TASK_CIT_SPEAK_MOURNING,
	};

	//-----------------------------------------------------
	
	int				m_nInspectActivity;
	float			m_flNextFearSoundTime;
	float			m_flStopManhackFlinch;
	float			m_fNextInspectTime;		// Next time I'm allowed to get inspected by a scanner
	float			m_flPlayerHealTime;
	float			m_flNextHealthSearchTime; // Next time I'm allowed to look for a healthkit
	float			m_flAllyHealTime;
	float			m_flPlayerGiveAmmoTime;
	string_t		m_iszAmmoSupply;
	int				m_iAmmoAmount;
	bool			m_bShouldPatrol;
	string_t		m_iszOriginalSquad;
	float			m_flTimeJoinedPlayerSquad;
	bool			m_bWasInPlayerSquad;
	float			m_flTimeLastCloseToPlayer;

	CSimpleSimTimer	m_AutoSummonTimer;
	Vector			m_vAutoSummonAnchor;

	CitizenType_t	m_Type;
	CitizenExpressionTypes_t	m_ExpressionType;

	int				m_iHead;

	static CSimpleSimTimer gm_PlayerSquadEvaluateTimer;

	float			m_flTimePlayerStare;	// The game time at which the player started staring at me.
	float			m_flTimeNextHealStare;	// Next time I'm allowed to heal a player who is staring at me.

	//-----------------------------------------------------
	//	Outputs
	//-----------------------------------------------------
	COutputEvent		m_OnJoinedPlayerSquad;
	COutputEvent		m_OnLeftPlayerSquad;
	COutputEvent		m_OnFollowOrder;
	COutputEvent		m_OnStationOrder; 
	COutputEvent		m_OnPlayerUse;

	//-----------------------------------------------------
	CAI_FuncTankBehavior	m_FuncTankBehavior;

	CHandle<CAI_FollowGoal>	m_hSavedFollowGoalEnt;

	bool					m_bDontUseSemaphore;
	
	//-----------------------------------------------------
	
	DECLARE_DATADESC();
	DEFINE_CUSTOM_AI;
};

//---------------------------------------------------------
//---------------------------------------------------------
inline bool CNPC_Citizen::NearCommandGoal()
{
	const float flDistSqr = COMMAND_GOAL_TOLERANCE * COMMAND_GOAL_TOLERANCE;
	return ( ( GetAbsOrigin() - GetCommandGoal() ).LengthSqr() <= flDistSqr );
}

//---------------------------------------------------------
//---------------------------------------------------------
inline bool CNPC_Citizen::VeryFarFromCommandGoal()
{
	const float flDistSqr = (12*50) * (12*50);
	return ( ( GetAbsOrigin() - GetCommandGoal() ).LengthSqr() > flDistSqr );
}



//==============================================================================
// CITIZEN PLAYER-RESPONSE SYSTEM
//==============================================================================
#define CITIZEN_RESPONSE_DISTANCE			768			// Maximum distance for responding citizens
#define CITIZEN_RESPONSE_REFIRE_TIME		15.0		// Time after giving a response before giving any more
#define CITIZEN_RESPONSE_GIVEUP_TIME		4.0			// Time after a response trigger was fired before discarding it without responding

enum citizenresponses_t
{
	CR_PLAYER_SHOT_GUNSHIP,		// Player has shot the gunship with a bullet weapon
	CR_PLAYER_KILLED_GUNSHIP,	// Player has destroyed the gunship
	CR_VITALNPC_DIED,			// Mapmaker specified that an NPC that was vital has died

	// Add new responses here

	MAX_CITIZEN_RESPONSES,
};

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

class CCitizenResponseSystem : public CBaseEntity
{
	DECLARE_CLASS( CCitizenResponseSystem, CBaseEntity );
public:
	DECLARE_DATADESC();

	void	Spawn();
	void	OnRestore();

	void	AddResponseTrigger( citizenresponses_t	iTrigger );

	void	ResponseThink();

	//---------------------------------
	// Inputs
	//---------------------------------
	void 	InputResponseVitalNPC( inputdata_t &inputdata );

private:
	float	m_flResponseAddedTime[ MAX_CITIZEN_RESPONSES ];		// Time at which the response was added. 0 if we have no response.
	float	m_flNextResponseTime;
};

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

class CSquadInsignia : public CBaseAnimating
{
	DECLARE_CLASS( CSquadInsignia, CBaseAnimating );
	void Spawn();
};

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

CCitizenResponseSystem	*GetCitizenResponse();

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

#endif	//NPC_CITIZEN_H
