//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: This is the base version of the vortigaunt
//
//=============================================================================//

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

#include "ai_basenpc.h"
#include "ai_behavior.h"
#include "ai_behavior_lead.h"
#include "ai_behavior_standoff.h"
#include "ai_behavior_assault.h"
#include "npc_playercompanion.h"

#define		VORTIGAUNT_MAX_BEAMS				8

#define VORTIGAUNT_BEAM_ALL		-1
#define	VORTIGAUNT_BEAM_ZAP		0
#define	VORTIGAUNT_BEAM_HEAL	1
#define VORTIGAUNT_BEAM_DISPEL	2

class CBeam;
class CSprite;
class CVortigauntChargeToken;
class CVortigauntEffectDispel;

extern ConVar sk_vortigaunt_zap_range;

enum VortigauntHealState_t
{
	HEAL_STATE_NONE,		// Not trying to heal
	HEAL_STATE_WARMUP,		// In the "warm-up" phase of healing
	HEAL_STATE_HEALING,		// In the process of healing
	HEAL_STATE_COOLDOWN,	// in the "cooldown" phase of healing
};

//=========================================================
//	>> CNPC_Vortigaunt
//=========================================================
class CNPC_Vortigaunt : public CNPC_PlayerCompanion
{
	DECLARE_CLASS( CNPC_Vortigaunt, CNPC_PlayerCompanion );

public:
					CNPC_Vortigaunt( void );

	virtual void	Spawn( void );
	virtual void	Precache( void );
	virtual float	MaxYawSpeed( void );

	virtual	Vector  FacingPosition( void );
	virtual Vector	BodyTarget( const Vector &posSrc, bool bNoisy = true );

	virtual void	PrescheduleThink( void );
	virtual void	BuildScheduleTestBits( void );
	virtual void	OnScheduleChange( void );

	virtual int		RangeAttack1Conditions( float flDot, float flDist );	// Primary zap
	virtual int		RangeAttack2Conditions( float flDot, float flDist );	// Concussive zap (larger)
	virtual bool	InnateWeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions );
	virtual int		MeleeAttack1Conditions( float flDot, float flDist );	// Dispel
	virtual float	InnateRange1MinRange( void ) { return 0.0f; }
	virtual float	InnateRange1MaxRange( void ) { return sk_vortigaunt_zap_range.GetFloat()*12; }
	virtual int		OnTakeDamage_Alive( const CTakeDamageInfo &info );
	virtual bool	FInViewCone( CBaseEntity *pEntity );
	virtual bool	ShouldMoveAndShoot( void );

	// vorts have a very long head/neck swing, so debounce heavily
	virtual	float	GetHeadDebounce( void ) { return 0.7; } // how much of previous head turn to use

	virtual void		Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
	virtual void		AlertSound( void );
	virtual Class_T		Classify ( void ) { return IsGameEndAlly() ? CLASS_PLAYER_ALLY_VITAL : CLASS_VORTIGAUNT; }
	virtual void		HandleAnimEvent( animevent_t *pEvent );
	virtual Activity	NPC_TranslateActivity( Activity eNewActivity );

	virtual void	UpdateOnRemove( void );
	virtual void	Event_Killed( const CTakeDamageInfo &info );
	virtual	void	GatherConditions( void );
	virtual void	RunTask( const Task_t *pTask );
	virtual void	StartTask( const Task_t *pTask );
	virtual void	ClearSchedule( const char *szReason );

	virtual void	DeclineFollowing( void );
	virtual bool	CanBeUsedAsAFriend( void );
	virtual bool	IsPlayerAlly( void ) { return true; }

	// Override these to set behavior
	virtual int		TranslateSchedule( int scheduleType );
	virtual int		SelectSchedule( void );
	virtual int		SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
	virtual bool	IsValidEnemy( CBaseEntity *pEnemy );
	bool			IsLeading( void ) { return ( GetRunningBehavior() == &m_LeadBehavior && m_LeadBehavior.HasGoal() ); }

	void			DeathSound( const CTakeDamageInfo &info );
	void			PainSound( const CTakeDamageInfo &info );
	
	virtual void	TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr );
	virtual void	SpeakSentence( int sentType );

	virtual int		IRelationPriority( CBaseEntity *pTarget );
	virtual Disposition_t 	IRelationType( CBaseEntity *pTarget );
	virtual bool	IsReadinessCapable( void ) { return true; }
	virtual float	GetReadinessDecay() { return 30.0f; }
	virtual bool	ShouldRegenerateHealth( void ) { return m_bRegenerateHealth; }
	virtual bool	CanRunAScriptedNPCInteraction( bool bForced = false );
	virtual void	AimGun( void );
	virtual void	OnUpdateShotRegulator( void );

	void	InputEnableArmorRecharge( inputdata_t &data );
	void	InputDisableArmorRecharge( inputdata_t &data );
	void	InputExtractBugbait( inputdata_t &data );
	void	InputChargeTarget( inputdata_t &data );
	void	InputDispel( inputdata_t &data );
	void	InputBeginCarryNPC( inputdata_t &indputdata );
	void	InputEndCarryNPC( inputdata_t &indputdata );

	// Health regeneration
	void	InputEnableHealthRegeneration( inputdata_t &data );
	void	InputDisableHealthRegeneration( inputdata_t &data );

	// color
	void	InputTurnBlue( inputdata_t &data );
	void	InputTurnBlack( inputdata_t &data );

	virtual void	SetScriptedScheduleIgnoreConditions( Interruptability_t interrupt );
	virtual void    OnRestore( void );
	virtual bool	OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval );
	virtual void	OnStartScene( void );
	virtual bool	IsInterruptable( void );
	virtual bool	CanFlinch( void );

	// used so a grub can notify me that I stepped on it. Says a line.
	void	OnSquishedGrub( const CBaseEntity *pGrub );

private:

	int		NumAntlionsInRadius( float flRadius );
	void	DispelAntlions( const Vector &vecOrigin, float flRadius, bool bDispel = true );
	bool	HealGestureHasLOS( void );
	bool	PlayerBelowHealthPercentage( CBasePlayer *pPlayer, float flPerc );
	void	StartHealing( void );
	void	StopHealing( bool bInterrupt = false );
	void	MaintainHealSchedule( void );
	bool	ShouldHealTarget( CBaseEntity *pTarget );
	int		SelectHealSchedule( void );

	void	CreateBeamBlast( const Vector &vecOrigin );

private:
	//=========================================================
	// Vortigaunt schedules
	//=========================================================
	enum
	{
		SCHED_VORTIGAUNT_STAND = BaseClass::NEXT_SCHEDULE,
		SCHED_VORTIGAUNT_RANGE_ATTACK,
		SCHED_VORTIGAUNT_HEAL,
		SCHED_VORTIGAUNT_EXTRACT_BUGBAIT,
		SCHED_VORTIGAUNT_FACE_PLAYER,
		SCHED_VORTIGAUNT_RUN_TO_PLAYER,
		SCHED_VORTIGAUNT_DISPEL_ANTLIONS,
		SCHED_VORT_FLEE_FROM_BEST_SOUND,
		SCHED_VORT_ALERT_FACE_BESTSOUND,
	};

	//=========================================================
	// Vortigaunt Tasks 
	//=========================================================
	enum 
	{
		TASK_VORTIGAUNT_HEAL_WARMUP = BaseClass::NEXT_TASK,
		TASK_VORTIGAUNT_HEAL,
		TASK_VORTIGAUNT_EXTRACT_WARMUP,
		TASK_VORTIGAUNT_EXTRACT,
		TASK_VORTIGAUNT_EXTRACT_COOLDOWN,
		TASK_VORTIGAUNT_FIRE_EXTRACT_OUTPUT,
		TASK_VORTIGAUNT_WAIT_FOR_PLAYER,
		TASK_VORTIGAUNT_GET_HEAL_TARGET,
		TASK_VORTIGAUNT_DISPEL_ANTLIONS
	};

	//=========================================================
	// Vortigaunt Conditions
	//=========================================================
	enum
	{
		COND_VORTIGAUNT_CAN_HEAL = BaseClass::NEXT_CONDITION,
		COND_VORTIGAUNT_HEAL_TARGET_TOO_FAR,	// Outside or heal range
		COND_VORTIGAUNT_HEAL_TARGET_BLOCKED,	// Blocked by an obstruction
		COND_VORTIGAUNT_HEAL_TARGET_BEHIND_US,	// Not within our "forward" range
		COND_VORTIGAUNT_HEAL_VALID,				// All conditions satisfied	
		COND_VORTIGAUNT_DISPEL_ANTLIONS,		// Repulse all antlions around us
	};

	// ------------
	// Beams
	// ------------
	inline bool		InAttackSequence( void );
	void			ClearBeams( void );
	void			ArmBeam( int beamType, int nHand );
	void			ZapBeam( int nHand );
	int				m_nLightningSprite;

	// ---------------
	//  Glow
	// ----------------
	void			ClearHandGlow( void );
	float			m_fGlowAge;
	float			m_fGlowChangeTime;
	bool			m_bGlowTurningOn;
	int				m_nCurGlowIndex;
	
	CHandle<CVortigauntEffectDispel>	m_hHandEffect[2];
	
	void			StartHandGlow( int beamType, int nHand );
	void			EndHandGlow( int beamType = VORTIGAUNT_BEAM_ALL );
	void			MaintainGlows( void );

	// ----------------
	//  Healing
	// ----------------
	bool				m_bRegenerateHealth;
	float				m_flNextHealTime;		// Next time allowed to heal player
	EHANDLE				m_hHealTarget;			// The person that I'm going to heal.
	bool				m_bPlayerRequestedHeal;	// This adds some priority to our heal (allows it to happen in combat, etc)
	float				m_flNextHealTokenTime;
	
	VortigauntHealState_t	m_eHealState;
	
	CBaseEntity		*FindHealTarget( void );
	bool			HealBehaviorAvailable( void );
	void			SetHealTarget( CBaseEntity *pTarget, bool bPlayerRequested );
	void			GatherHealConditions( void );

	int				m_nNumTokensToSpawn;
	float			m_flHealHinderedTime;
	float			m_flPainTime;
	float			m_nextLineFireTime;

	bool			m_bArmorRechargeEnabled;
	bool			m_bForceArmorRecharge;
	float			m_flDispelTestTime;
	
	bool			m_bExtractingBugbait;
	
	bool			IsCarryingNPC( void ) const { return m_bCarryingNPC; }
	bool			m_bCarryingNPC;

	COutputEvent	m_OnFinishedExtractingBugbait;
	COutputEvent	m_OnFinishedChargingTarget;
	COutputEvent	m_OnPlayerUse;
	
	//Adrian: Let's do it the right way!
	int				m_iLeftHandAttachment;
	int				m_iRightHandAttachment;
	bool			m_bStopLoopingSounds;
	float			m_flAimDelay;			// Amount of time to suppress aiming

	// used for fading from green vort to blue vort
	CNetworkVar( bool,  m_bIsBlue );
	CNetworkVar( float, m_flBlueEndFadeTime );

	// used for fading to black
	CNetworkVar( bool, m_bIsBlack );

public:
	DECLARE_SERVERCLASS();
	DECLARE_DATADESC();
	DEFINE_CUSTOM_AI;
};

//=============================================================================
// 
//  Charge Token 
//	
//=============================================================================

class CVortigauntChargeToken : public CBaseEntity
{
	DECLARE_CLASS( CVortigauntChargeToken, CBaseEntity );

public:

	static CVortigauntChargeToken *CreateChargeToken( const Vector &vecOrigin, CBaseEntity *pOwner, CBaseEntity *pTarget );

	CVortigauntChargeToken( void );	

	virtual void	Spawn( void );
	virtual void	Precache( void );
	virtual unsigned int PhysicsSolidMaskForEntity( void ) const;

	void	FadeAndDie( void );
	void	SeekThink( void );
	void	SeekTouch( CBaseEntity	*pOther );
	void	SetTargetEntity( CBaseEntity *pTarget ) { m_hTarget = pTarget; }

private:

	Vector	GetSteerVector( const Vector &vecForward );

	float				m_flLifetime;
	EHANDLE				m_hTarget;

	CNetworkVar( bool, m_bFadeOut );

	DECLARE_SERVERCLASS();
	DECLARE_DATADESC();
};

//=============================================================================
// 
//  Dispel Effect
//	
//=============================================================================

class CVortigauntEffectDispel : public CBaseEntity
{
	DECLARE_CLASS( CVortigauntEffectDispel, CBaseEntity );

public:

	static CVortigauntEffectDispel *CreateEffectDispel( const Vector &vecOrigin, CBaseEntity *pOwner, CBaseEntity *pTarget );

	CVortigauntEffectDispel( void );	

	virtual void	Spawn( void );

	void	FadeAndDie( void );

private:

	CNetworkVar( bool, m_bFadeOut );

	DECLARE_SERVERCLASS();
	DECLARE_DATADESC();
};

#endif // NPC_VORTIGAUNT_H
