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

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

#include "triggers.h"

#define SF_TANK_ACTIVE						0x0001
#define SF_TANK_PLAYER						0x0002
#define SF_TANK_HUMANS						0x0004
#define SF_TANK_ALIENS						0x0008
#define SF_TANK_LINEOFSIGHT					0x0010
#define SF_TANK_CANCONTROL					0x0020
#define	SF_TANK_DAMAGE_KICK					0x0040	// Kick when take damage
#define	SF_TANK_AIM_AT_POS					0x0080	// Aim at a particular position
#define SF_TANK_AIM_ASSISTANCE				0x0100
#define SF_TANK_NPC							0x0200		
#define SF_TANK_NPC_CONTROLLABLE			0x0400		// 1024
#define SF_TANK_NPC_SET_CONTROLLER			0x0800		// 2048
#define SF_TANK_ALLOW_PLAYER_HITS			0x1000		// 4096		Allow friendly NPCs to fire upon enemies near the player
#define SF_TANK_IGNORE_RANGE_IN_VIEWCONE	0x2000		// 8192		Don't use range as a factor in determining if something is in view cone
#define SF_TANK_NOTSOLID					0x8000		// 32768
#define SF_TANK_SOUNDON						0x10000		// FIXME: This is not really a spawnflag! It holds transient state!!!
#define SF_TANK_HACKPLAYERHIT				0x20000		// 131072	Make this func_tank cheat and hit the player regularly

#define FUNCTANK_DISTANCE_MAX				1200			// 100 ft.
#define FUNCTANK_DISTANCE_MIN_TO_ENEMY		180
#define FUNCTANK_FIREVOLUME					1000
#define FUNCTANK_NPC_ROUTE_TIME				5.0f

// Effect handling
// If the func_tank has a chosen method of handling effects, use that 
// instead of the individual effect settings. (muzzleflash, sound, tracer, etc)
enum FUNCTANK_EFFECT_HANDLING
{
	EH_NONE,			// Use the effect settings
	EH_AR2,				// Use AR2 effects
	EH_COMBINE_CANNON	// Large Combine cannon
};

enum TANKBULLET
{
	TANK_BULLET_NONE	= 0,
	TANK_BULLET_SMALL	= 1,
	TANK_BULLET_MEDIUM	= 2,
	TANK_BULLET_LARGE	= 3,
};

#define MORTAR_BLAST_RADIUS	350


//			Custom damage
//			env_laser (duration is 0.5 rate of fire)
//			rockets
//			explosion?

class CFuncTank : public CBaseEntity
{

	DECLARE_CLASS( CFuncTank, CBaseEntity );

public:

			CFuncTank();
			~CFuncTank();
	void	Spawn( void );
	void	Activate( void );
	void	Precache( void );
	bool	CreateVPhysics( void );
	bool	KeyValue( const char *szKeyName, const char *szValue );
	void	UpdateOnRemove();

	void	SetYawRate( float flYawRate ) { m_yawRate = flYawRate; }
	void	SetPitchRate( float flPitchRate ) { m_pitchRate = flPitchRate; }

	int	ObjectCaps( void ) 
	{ 
		return ( BaseClass::ObjectCaps() | FCAP_IMPULSE_USE | FCAP_USE_IN_RADIUS ); 
	}

	void	Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
	
	virtual	void FuncTankPreThink() { return; }
	void	Think( void );
	virtual	void FuncTankPostThink() { return; }

	int		GetAmmoCount( void )						{ return m_iAmmoCount; }

	// NPC
	bool	NPC_FindManPoint( Vector &vecPos );
	bool	NPC_HasEnemy( void );
	void	NPC_Fire( void );
	void	NPC_InterruptRoute( void );
	void	NPC_JustSawPlayer( CBaseEntity *pTarget );
	void	NPC_SetInRoute( bool bInRoute )				{ m_bNPCInRoute = bInRoute; }
	void	NPC_SetIdleAngle( Vector vecIdle )			{ m_vecNPCIdleTarget = vecIdle; }

	// LOS
	bool	IsEntityInViewCone( CBaseEntity *pEntity );
	bool	HasLOSTo( CBaseEntity *pEntity );

	// Controller
	CBaseCombatCharacter *GetController( void );
	bool StartControl( CBaseCombatCharacter *pController );
	void StopControl( void );
	Vector GetTargetPosition()						{ return m_vTargetPosition; }
	void SetTargetPosition( const Vector &vecPos )	{ m_vTargetPosition = vecPos; }

	const float YawCenter() const { return m_yawCenter; }
	const float YawCenterWorld() const { return m_yawCenterWorld; }
	const float YawRange() const { return m_yawRange; }
	const float PitchCenter() const { return m_pitchCenter; }
	const float PitchCenterWorld() const { return m_pitchCenterWorld; }
	const float PitchRange() const { return m_pitchRange; }

	virtual void PhysicsSimulate( void );

	virtual void OnStartControlled() {}
	virtual void OnStopControlled() {}

	// SF Tests.
	inline bool IsControllable( void )		{ return ( m_spawnflags & SF_TANK_CANCONTROL ) ? true : false; }
	inline bool IsActive( void )			{ return ( m_spawnflags & SF_TANK_ACTIVE ) ? true : false; }
	inline bool IsNPCControllable( void )	{ return ( m_spawnflags & SF_TANK_NPC_CONTROLLABLE ) ? true : false; }
	inline bool IsNPCSetController( void )	{ return ( m_spawnflags & SF_TANK_NPC_SET_CONTROLLER ) ? true : false; }

	virtual void DoMuzzleFlash( void );
	virtual const char *GetTracerType( void );

protected:
	virtual float GetShotSpeed() { return 0; }

	virtual Vector WorldBarrelPosition( void );
	void		UpdateMatrix( void );

	float GetNextAttack() const { return m_flNextAttack; }
	virtual void SetNextAttack( float flWait ) { m_flNextAttack = flWait; }

	virtual void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker, bool bIgnoreSpread );
	void		TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, trace_t &tr );
	int			GetRandomBurst( void );
	float		GetRandomFireTime( void );

	void	CalcPlayerCrosshairTarget( Vector *pVecTarget );
	void	CalcNPCEnemyTarget( Vector *pVecTarget );

	inline bool IsPlayerManned( void )	{ return m_hController && m_hController->IsPlayer() && ( m_spawnflags & SF_TANK_PLAYER ); }
	inline bool IsNPCManned( void )		{ return m_hController && m_hController->MyNPCPointer() && ( m_spawnflags & SF_TANK_NPC ); }

private:
	void	TrackTarget( void );
	int		DrawDebugTextOverlays(void);
	void	DrawDebugGeometryOverlays(void);

	virtual void FiringSequence( const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker );

	void	StartRotSound( void );
	void	StopRotSound( void );

	// Input handlers.
	void InputActivate( inputdata_t &inputdata );
	void InputDeactivate( inputdata_t &inputdata );
	void InputSetFireRate( inputdata_t &inputdata );
	void InputSetDamage( inputdata_t &inputdata );
	void InputSetTargetDir( inputdata_t &inputdata );
	void InputSetTargetPosition( inputdata_t &inputdata );
	void InputSetTargetEntityName( inputdata_t &inputdata );

protected:
	virtual void InputSetTargetEntity( inputdata_t &inputdata );
	virtual void InputClearTargetEntity( inputdata_t &inputdata );

private:
	void InputFindNPCToManTank( inputdata_t &inputdata );
	void InputStopFindingNPCs( inputdata_t &inputdata );
	void InputStartFindingNPCs( inputdata_t &inputdata );
	void InputForceNPCOff( inputdata_t &inputdata );
	void InputSetMaxRange( inputdata_t &inputdata );

	inline bool CanFire( void );
	bool		InRange( float range );
	bool		InRange2( float flRange2 );

	void		TraceAttack( CBaseEntity *pAttacker, float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType);

	QAngle		AimBarrelAt( const Vector &parentTarget );

	DECLARE_DATADESC();

	bool OnControls( CBaseEntity *pTest );
	bool HasController( void );

	CBaseEntity *FindTarget( string_t targetName, CBaseEntity *pActivator );

	// NPC
	void		NPC_FindController( void );
	bool		NPC_InRoute( void )							{ return m_bNPCInRoute; }
	bool		NPC_InterruptController( void );

	// Aim the tank at the player crosshair 
	void AimBarrelAtPlayerCrosshair( QAngle *pAngles );

	// Aim the tank at the NPC's enemy
	void AimBarrelAtNPCEnemy( QAngle *pAngles );

	// Aim the tank at the func_tank's enemy
	void AimFuncTankAtTarget( void );

	// Returns true if the desired angles are out of range 
	bool RotateTankToAngles( const QAngle &angles, float *pDistX = NULL, float *pDistY = NULL );

	// We lost our target! 
	void LostTarget( void );

	// Purpose: 
	void ComputeLeadingPosition( const Vector &vecShootPosition, CBaseEntity *pTarget, Vector *pLeadPosition );

protected:
	virtual void ControllerPostFrame( void );

	virtual void TankActivate(void);
	virtual void TankDeactivate(void);

	float					m_fireLast;		// Last time I fired
	float					m_fireRate;		// How many rounds/second

	EHANDLE					m_hTarget;

	TANKBULLET				m_bulletType;	// Bullet type
	int						m_iBulletDamage; // 0 means use Bullet type's default damage
	int						m_iBulletDamageVsPlayer; // Damage vs player. 0 means use m_iBulletDamage

#ifdef HL2_EPISODIC
	string_t				m_iszAmmoType;		// The name of the ammodef that we use when we fire. Bullet damage still comes from keyvalues.
	int						m_iAmmoType;		// The cached index of the ammodef that we use when we fire.
#else
	int						m_iSmallAmmoType;
	int						m_iMediumAmmoType;
	int						m_iLargeAmmoType;
#endif // HL2_EPISODIC

	int						m_spread;		// firing spread

	EntityMatrix			m_parentMatrix;

	Vector					m_sightOrigin;	// Last sight of target
	EHANDLE					m_hFuncTankTarget;

	int						m_nBulletCount;

private:

	// This is either the player manning the func_tank, or an NPC. The NPC is either manning the tank, or running
	// to the man point. If he's en-route, m_bNPCInRoute will be true. 
	CHandle<CBaseCombatCharacter> m_hController;

	float					m_flNextAttack;
	Vector					m_vecControllerUsePos;
	
	float					m_yawCenter;	// "Center" yaw
	float					m_yawCenterWorld;	// "Center" yaw in world space
	float					m_yawRate;		// Max turn rate to track targets
	float					m_yawRange;		// Range of turning motion (one-sided: 30 is +/- 30 degress from center)
											// Zero is full rotation
	float					m_yawTolerance;	// Tolerance angle

	float					m_pitchCenter;	// "Center" pitch
	float					m_pitchCenterWorld;	// "Center" pitch in world space
	float					m_pitchRate;	// Max turn rate on pitch
	float					m_pitchRange;	// Range of pitch motion as above
	float					m_pitchTolerance;	// Tolerance angle

	float					m_fireTime;		// How much time has been used to fire the weapon so far.
	float					m_lastSightTime;// Last time I saw target
	float					m_persist;		// Persistence of firing (how long do I shoot when I can't see)
	float					m_persist2;		// Secondary persistence of firing (randomly shooting when I can't see)
	float					m_persist2burst;// How long secondary persistence burst lasts
	float					m_minRange;		// Minimum range to aim/track
	float					m_maxRange;		// Max range to aim/track
	float					m_flMinRange2;
	float					m_flMaxRange2;
	int						m_iAmmoCount;	// ammo 

	Vector					m_barrelPos;	// Length of the freakin barrel
	float					m_spriteScale;	// Scale of any sprites we shoot
	string_t				m_iszSpriteSmoke;
	string_t				m_iszSpriteFlash;

	string_t				m_iszMaster;	// Master entity (game_team_master or multisource)

	string_t				m_soundStartRotate;
	string_t				m_soundStopRotate;
	string_t				m_soundLoopRotate;

	float					m_flPlayerGracePeriod;
	float					m_flIgnoreGraceUpto;
	float					m_flPlayerLockTimeBeforeFire;
	float					m_flLastSawNonPlayer;

	string_t				m_targetEntityName;
	Vector					m_vTargetPosition;
	Vector					m_vecNPCIdleTarget;

	// Used for when the gun is attached to another entity
	string_t				m_iszBarrelAttachment;
	int						m_nBarrelAttachment;
	string_t				m_iszBaseAttachment;

	// Used when the gun is actually a part of the parent entity, and pose params aim it
	string_t				m_iszYawPoseParam;
	string_t				m_iszPitchPoseParam;
	float					m_flYawPoseCenter;
	float					m_flPitchPoseCenter;
	bool					m_bUsePoseParameters;

	// Lead the target?
	bool					m_bPerformLeading;
	float					m_flStartLeadFactor;
	float					m_flStartLeadFactorTime;
	float					m_flNextLeadFactor;
	float					m_flNextLeadFactorTime;

	COutputEvent			m_OnFire;
	COutputEvent			m_OnLoseTarget;
	COutputEvent			m_OnAquireTarget;
	COutputEvent			m_OnAmmoDepleted;
	COutputEvent			m_OnGotController;
	COutputEvent			m_OnLostController;
	COutputEvent			m_OnGotPlayerController;
	COutputEvent			m_OnLostPlayerController;
	COutputEvent			m_OnReadyToFire;

	CHandle<CBaseTrigger>	m_hControlVolume;
	string_t				m_iszControlVolume;

	float					m_flNextControllerSearch;
	bool					m_bShouldFindNPCs;
	bool					m_bNPCInRoute;
	string_t				m_iszNPCManPoint; 

	bool					m_bReadyToFire;

	int						m_iEffectHandling;
};

#endif // FUNC_TANK_H