//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

//
// Author: Michael S. Booth (mike@turtlerockstudios.com), 2003
//
// NOTE: The CS Bot code uses Doxygen-style comments. If you run Doxygen over this code, it will 
// auto-generate documentation.  Visit www.doxygen.org to download the system for free.
//

#ifndef BOT_H
#define BOT_H

#include "cbase.h"
#include "in_buttons.h"
#include "movehelper_server.h"
#include "mathlib/mathlib.h"

#include "bot_manager.h"
#include "bot_util.h"
#include "bot_constants.h"
#include "nav_mesh.h"
#include "gameinterface.h"
#include "weapon_csbase.h"
#include "shared_util.h"
#include "util.h"
#include "shareddefs.h"

#include "tier0/vprof.h"

class BotProfile;


extern bool AreBotsAllowed();


//--------------------------------------------------------------------------------------------------------
// BOTPORT: Convert everything to assume "origin" means "feet"

//
// Utility function to get "centroid" or center of player or player equivalent
//
inline Vector GetCentroid( const CBaseEntity *player )
{
	Vector centroid = player->GetAbsOrigin();

	const Vector &mins = player->WorldAlignMins();
	const Vector &maxs = player->WorldAlignMaxs();

	centroid.z += (maxs.z - mins.z)/2.0f;

	//centroid.z += HalfHumanHeight;

	return centroid;
}


CBasePlayer* ClientPutInServerOverride_Bot( edict_t *pEdict, const char *playername );

/// @todo Remove this nasty hack - CreateFakeClient() calls CBot::Spawn, which needs the profile
extern const BotProfile *g_botInitProfile;
extern int g_botInitTeam;
extern int g_nClientPutInServerOverrides;

//--------------------------------------------------------------------------------------------------------
template < class T > T * CreateBot( const BotProfile *profile, int team )
{
	if ( !AreBotsAllowed() )
		return NULL;

	if ( UTIL_ClientsInGame() >= gpGlobals->maxClients )
	{
		CONSOLE_ECHO( "Unable to create bot: Server is full (%d/%d clients).\n", UTIL_ClientsInGame(), gpGlobals->maxClients );
		return NULL;
	}

	// set the bot's name
	char botName[64];
	UTIL_ConstructBotNetName( botName, 64, profile );

	// This is a backdoor we use so when the engine calls ClientPutInServer (from CreateFakeClient), 
	// expecting the game to make an entity for the fake client, we can make our special bot class
	// instead of a CCSPlayer.
	g_nClientPutInServerOverrides = 0;
	ClientPutInServerOverride( ClientPutInServerOverride_Bot );
	
	// get an edict for the bot
	// NOTE: This will ultimately invoke CBot::Spawn(), so set the profile now
	g_botInitProfile = profile;
	g_botInitTeam = team;
	edict_t *botEdict = engine->CreateFakeClient( botName );

	ClientPutInServerOverride( NULL );
	Assert( g_nClientPutInServerOverrides == 1 );


	if ( botEdict == NULL )
	{
		CONSOLE_ECHO( "Unable to create bot: CreateFakeClient() returned null.\n" );
		return NULL;
	}


	// create an instance of the bot's class and bind it to the edict
	T *bot = dynamic_cast< T * >( CBaseEntity::Instance( botEdict ) );

	if ( bot == NULL )
	{
		Assert( false );
		Error( "Could not allocate and bind entity to bot edict.\n" );
		return NULL;
	}

	bot->ClearFlags();
	bot->AddFlag( FL_CLIENT | FL_FAKECLIENT );

	return bot;
}

//----------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------
/**
 * The base bot class from which bots for specific games are derived
 * A template is needed here because the CBot class must be derived from CBasePlayer, 
 * but also may need to be derived from a more specific player class, such as CCSPlayer
 */
template < class PlayerType >
class CBot : public PlayerType
{
public:
	DECLARE_CLASS( CBot, PlayerType );

	CBot( void );												///< constructor initializes all values to zero
	virtual ~CBot();
	virtual bool Initialize( const BotProfile *profile, int team );		///< (EXTEND) prepare bot for action

	unsigned int GetID( void ) const		{ return m_id; }	///< return bot's unique ID

	virtual bool IsBot( void ) const { return true; }	
	virtual bool IsNetClient( void ) const { return false; }	// Bots should return FALSE for this, they can't receive NET messages

	virtual void Spawn( void );									///< (EXTEND) spawn the bot into the game

	virtual void Upkeep( void ) = 0;							///< lightweight maintenance, invoked frequently
	virtual void Update( void ) = 0;							///< heavyweight algorithms, invoked less often


	virtual void Run( void );
	virtual void Walk( void );
	virtual bool IsRunning( void ) const		{ return m_isRunning; }
	
	virtual void Crouch( void );
	virtual void StandUp( void );
	bool IsCrouching( void ) const	{ return m_isCrouching; }

	void PushPostureContext( void );							///< push the current posture context onto the top of the stack
	void PopPostureContext( void );								///< restore the posture context to the next context on the stack

	virtual void MoveForward( void );
	virtual void MoveBackward( void );
	virtual void StrafeLeft( void );
	virtual void StrafeRight( void );

	#define MUST_JUMP true
	virtual bool Jump( bool mustJump = false );					///< returns true if jump was started
	bool IsJumping( void );										///< returns true if we are in the midst of a jump
	float GetJumpTimestamp( void ) const	{ return m_jumpTimestamp; }	///< return time last jump began

	virtual void ClearMovement( void );						///< zero any MoveForward(), Jump(), etc

	const Vector &GetViewVector( void );						///< return the actual view direction


	//------------------------------------------------------------------------------------
	// Weapon interface
	//
	virtual void UseEnvironment( void );
	virtual void PrimaryAttack( void );
	virtual void ClearPrimaryAttack( void );
	virtual void TogglePrimaryAttack( void );
	virtual void SecondaryAttack( void );
	virtual void Reload( void );

	float GetActiveWeaponAmmoRatio( void ) const;				///< returns ratio of ammo left to max ammo (1 = full clip, 0 = empty)
	bool IsActiveWeaponClipEmpty( void ) const;					///< return true if active weapon has any empty clip
	bool IsActiveWeaponOutOfAmmo( void ) const;					///< return true if active weapon has no ammo at all
	bool IsActiveWeaponRecoilHigh( void ) const;				///< return true if active weapon's bullet spray has become large and inaccurate
	bool IsUsingScope( void );									///< return true if looking thru weapon's scope


	//------------------------------------------------------------------------------------
	// Event hooks
	//

	/// invoked when injured by something (EXTEND) - returns the amount of damage inflicted
	virtual int OnTakeDamage( const CTakeDamageInfo &info )
	{
		return PlayerType::OnTakeDamage( info );
	}

	/// invoked when killed (EXTEND)
	virtual void Event_Killed( const CTakeDamageInfo &info )
	{ 
		PlayerType::Event_Killed( info );
	}

	bool IsEnemy( CBaseEntity *ent ) const;						///< returns TRUE if given entity is our enemy
	int GetEnemiesRemaining( void ) const;						///< return number of enemies left alive
	int GetFriendsRemaining( void ) const;						///< return number of friends left alive

	bool IsPlayerFacingMe( CBasePlayer *enemy ) const;			///< return true if player is facing towards us
	bool IsPlayerLookingAtMe( CBasePlayer *enemy, float cosTolerance = 0.9f ) const;		///< returns true if other player is pointing right at us
	bool IsLookingAtPosition( const Vector &pos, float angleTolerance = 20.0f ) const;	///< returns true if looking (roughly) at given position

	bool IsLocalPlayerWatchingMe( void ) const;					///< return true if local player is observing this bot

	void PrintIfWatched( PRINTF_FORMAT_STRING const char *format, ... ) const;				///< output message to console if we are being watched by the local player

	virtual void UpdatePlayer( void );							///< update player physics, movement, weapon firing commands, etc
	virtual void BuildUserCmd( CUserCmd& cmd, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse );
	virtual void SetModel( const char *modelName );

	int Save( CSave &save )	const						{ return 0; }
	int Restore( CRestore &restore ) const	{ return 0; }
	virtual void Think( void ) { }

	const BotProfile *GetProfile( void ) const		{ return m_profile; }	///< return our personality profile

	virtual bool ClientCommand( const CCommand &args );			///< Do a "client command" - useful for invoking menu choices, etc.
	virtual int Cmd_Argc( void );								///< Returns the number of tokens in the command string
	virtual char *Cmd_Argv( int argc );							///< Retrieves a specified token

private:
	CUtlVector< char * > m_args;

protected:
	const BotProfile *m_profile;								///< the "personality" profile of this bot

private:
	friend class CBotManager;

	unsigned int m_id;											///< unique bot ID

	CUserCmd m_userCmd;
	bool m_isRunning;											///< run/walk mode
	bool m_isCrouching;											///< true if crouching (ducking)
	float m_forwardSpeed;
	float m_strafeSpeed;
	float m_verticalSpeed;
	int m_buttonFlags;											///< bitfield of movement buttons

	float m_jumpTimestamp;										///< time when we last began a jump

	Vector m_viewForward;										///< forward view direction (only valid when GetViewVector() is used)

	/// the PostureContext represents the current settings of walking and crouching
	struct PostureContext
	{
		bool isRunning;
		bool isCrouching;
	};
	enum { MAX_POSTURE_STACK = 8 };
	PostureContext m_postureStack[ MAX_POSTURE_STACK ];
	int m_postureStackIndex;									///< index of top of stack

	void ResetCommand( void );
	//byte ThrottledMsec( void ) const;

protected:
	virtual float GetMoveSpeed( void );									///< returns current movement speed (for walk/run)
};


//-----------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------
//
// Inlines
//

//--------------------------------------------------------------------------------------------------------------
template < class T >
inline void CBot<T>::SetModel( const char *modelName )
{
	BaseClass::SetModel( modelName );
}

//-----------------------------------------------------------------------------------------------------------
template < class T >
inline float CBot<T>::GetMoveSpeed( void )
{
	return this->MaxSpeed();
}

//-----------------------------------------------------------------------------------------------------------
template < class T >
inline void CBot<T>::Run( void )
{
	m_isRunning = true;
}

//-----------------------------------------------------------------------------------------------------------
template < class T >
inline void CBot<T>::Walk( void )
{
	m_isRunning = false;
}

//-----------------------------------------------------------------------------------------------------------
template < class T >
inline bool CBot<T>::IsActiveWeaponRecoilHigh( void ) const
{
	const QAngle &angles = const_cast< CBot<T> * >( this )->GetPunchAngle();
	const float highRecoil = -1.5f;
	return (angles.x < highRecoil);
}

//-----------------------------------------------------------------------------------------------------------
template < class T >
inline void CBot<T>::PushPostureContext( void )
{
	if (m_postureStackIndex == MAX_POSTURE_STACK)
	{
		PrintIfWatched( "PushPostureContext() overflow error!\n" );
		return;
	}

	m_postureStack[ m_postureStackIndex ].isRunning = m_isRunning;
	m_postureStack[ m_postureStackIndex ].isCrouching = m_isCrouching;
	++m_postureStackIndex;
}

//-----------------------------------------------------------------------------------------------------------
template < class T >
inline void CBot<T>::PopPostureContext( void )
{
	if (m_postureStackIndex == 0)
	{
		PrintIfWatched( "PopPostureContext() underflow error!\n" );
		m_isRunning = true;
		m_isCrouching = false;
		return;
	}

	--m_postureStackIndex;
	m_isRunning = m_postureStack[ m_postureStackIndex ].isRunning;
	m_isCrouching = m_postureStack[ m_postureStackIndex ].isCrouching;
}

//-----------------------------------------------------------------------------------------------------------
template < class T >
inline bool CBot<T>::IsPlayerFacingMe( CBasePlayer *other ) const
{
	Vector toOther = other->GetAbsOrigin() - this->GetAbsOrigin();

	Vector otherForward;
	AngleVectors( other->EyeAngles() + other->GetPunchAngle(), &otherForward );

	if (DotProduct( otherForward, toOther ) < 0.0f)
		return true;

	return false;
}

//-----------------------------------------------------------------------------------------------------------
template < class T >
inline bool CBot<T>::IsPlayerLookingAtMe( CBasePlayer *other, float cosTolerance ) const
{
	Vector toOther = other->GetAbsOrigin() - this->GetAbsOrigin();
	toOther.NormalizeInPlace();

	Vector otherForward;
	AngleVectors( other->EyeAngles() + other->GetPunchAngle(), &otherForward );

	// other player must be pointing nearly right at us to be "looking at" us
	if (DotProduct( otherForward, toOther ) < -cosTolerance)
		return true;

	return false;
}

//-----------------------------------------------------------------------------------------------------------
template < class T >
inline const Vector &CBot<T>::GetViewVector( void )
{
	AngleVectors( this->EyeAngles() + this->GetPunchAngle(), &m_viewForward );
	return m_viewForward;
}

//-----------------------------------------------------------------------------------------------------------
template < class T >
inline bool CBot<T>::IsLookingAtPosition( const Vector &pos, float angleTolerance ) const
{
	// forced to do this since many methods in CBaseEntity are not const, but should be
	CBot< T > *me = const_cast< CBot< T > * >( this );

	Vector to = pos - me->EyePosition();

	QAngle idealAngles;
	VectorAngles( to, idealAngles );

	QAngle viewAngles = me->EyeAngles();

	float deltaYaw = AngleNormalize( idealAngles.y - viewAngles.y );
	float deltaPitch = AngleNormalize( idealAngles.x - viewAngles.x );

	if (fabs( deltaYaw ) < angleTolerance && abs( deltaPitch ) < angleTolerance)
		return true;

	return false;
}

//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline CBot< PlayerType >::CBot( void )
{
	// the profile will be attached after this instance is constructed
	m_profile = NULL;

	// assign this bot a unique ID
	static unsigned int nextID = 1;

	// wraparound (highly unlikely)
	if (nextID == 0)
		++nextID;

	m_id = nextID;
	++nextID;

	m_postureStackIndex = 0;
}

//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline CBot< PlayerType >::~CBot( void )
{
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Prepare bot for action
 */
template < class PlayerType >
inline bool CBot< PlayerType >::Initialize( const BotProfile *profile, int team )
{
	m_profile = profile;
	return true;
}

//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::Spawn( void )
{
	// initialize the bot (thus setting its profile)
	if (m_profile == NULL)
		Initialize( g_botInitProfile, g_botInitTeam );

	// let the base class set some things up
	PlayerType::Spawn();

	// Make sure everyone knows we are a bot
	this->AddFlag( FL_CLIENT | FL_FAKECLIENT );

	// Bots use their own thinking mechanism
	this->SetThink( NULL );

	m_isRunning = true;
	m_isCrouching = false;
	m_postureStackIndex = 0;

	m_jumpTimestamp = 0.0f;

	// Command interface variable initialization
	ResetCommand();
}


/*
//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::BotThink( void )
{
float g_flBotFullThinkInterval	= 1.0 / 15.0;	// full AI at lower frequency (was 10 in GoldSrc)
	
	
	Upkeep();

	if (gpGlobals->curtime >= m_flNextFullBotThink)
	{
		m_flNextFullBotThink = gpGlobals->curtime + g_flBotFullThinkInterval;

		ResetCommand();
		Update();
	}

	UpdatePlayer();
}
*/


//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::MoveForward( void )
{
	m_forwardSpeed = GetMoveSpeed();
	SETBITS( m_buttonFlags, IN_FORWARD );

	// make mutually exclusive
	CLEARBITS( m_buttonFlags, IN_BACK );
}


//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::MoveBackward( void )
{
	m_forwardSpeed = -GetMoveSpeed();
	SETBITS( m_buttonFlags, IN_BACK );

	// make mutually exclusive
	CLEARBITS( m_buttonFlags, IN_FORWARD );
}

//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::StrafeLeft( void )
{
	m_strafeSpeed = -GetMoveSpeed();
	SETBITS( m_buttonFlags, IN_MOVELEFT );

	// make mutually exclusive
	CLEARBITS( m_buttonFlags, IN_MOVERIGHT );
}

//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::StrafeRight( void )
{
	m_strafeSpeed = GetMoveSpeed();
	SETBITS( m_buttonFlags, IN_MOVERIGHT );

	// make mutually exclusive
	CLEARBITS( m_buttonFlags, IN_MOVELEFT );
}

//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline bool CBot< PlayerType >::Jump( bool mustJump )
{
	if (IsJumping() || IsCrouching())
		return false;

	if (!mustJump)
	{
		const float minJumpInterval = 0.9f; // 1.5f;
		if (gpGlobals->curtime - m_jumpTimestamp < minJumpInterval)
			return false;
	}

	// still need sanity check for jumping frequency
	const float sanityInterval = 0.3f;
	if (gpGlobals->curtime - m_jumpTimestamp < sanityInterval)
		return false;

	// jump
	SETBITS( m_buttonFlags, IN_JUMP );
	m_jumpTimestamp = gpGlobals->curtime;
	return true;
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Zero any MoveForward(), Jump(), etc
 */
template < class PlayerType >
void CBot< PlayerType >::ClearMovement( void )
{
	m_forwardSpeed = 0.0;
	m_strafeSpeed = 0.0;
	m_verticalSpeed	= 100.0; // stay at the top of water, so we don't drown.  TODO: swim logic
	m_buttonFlags &= ~(IN_FORWARD | IN_BACK | IN_LEFT | IN_RIGHT | IN_JUMP);
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Returns true if we are in the midst of a jump
 */
template < class PlayerType >
inline bool CBot< PlayerType >::IsJumping( void )
{
	// if long time after last jump, we can't be jumping
	if (gpGlobals->curtime - m_jumpTimestamp > 3.0f)
		return false;

	// if we just jumped, we're still jumping
	if (gpGlobals->curtime - m_jumpTimestamp < 0.9f) // 1.0f
		return true;

	// a little after our jump, we're jumping until we hit the ground
	if (FBitSet( this->GetFlags(), FL_ONGROUND ))
		return false;

	return true;
}

//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::Crouch( void )
{
	m_isCrouching = true;
}

//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::StandUp( void )
{
	m_isCrouching = false;
}


//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::UseEnvironment( void )
{
	SETBITS( m_buttonFlags, IN_USE );
}


//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::PrimaryAttack( void )
{
	SETBITS( m_buttonFlags, IN_ATTACK );
}

//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::ClearPrimaryAttack( void )
{
	CLEARBITS( m_buttonFlags, IN_ATTACK );
}

//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::TogglePrimaryAttack( void )
{
	if (FBitSet( m_buttonFlags, IN_ATTACK ))
	{
		CLEARBITS( m_buttonFlags, IN_ATTACK );
	}
	else
	{
		SETBITS( m_buttonFlags, IN_ATTACK );
	}
}


//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::SecondaryAttack( void )
{
	SETBITS( m_buttonFlags, IN_ATTACK2 );
}

//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::Reload( void )
{
	SETBITS( m_buttonFlags, IN_RELOAD );
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Returns ratio of ammo left to max ammo (1 = full clip, 0 = empty)
 */
template < class PlayerType >
inline float CBot< PlayerType >::GetActiveWeaponAmmoRatio( void ) const
{
	CWeaponCSBase *weapon = this->GetActiveCSWeapon();

	if (weapon == NULL)
		return 0.0f;

	// weapons with no ammo are always full
	if (weapon->Clip1() < 0)
		return 1.0f;

	return (float)weapon->Clip1() / (float)weapon->GetMaxClip1();
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Return true if active weapon has an empty clip
 */
template < class PlayerType >
inline bool CBot< PlayerType >::IsActiveWeaponClipEmpty( void ) const
{
	CWeaponCSBase *gun = this->GetActiveCSWeapon();

	if (gun && gun->Clip1() == 0)
		return true;

	return false;
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Return true if active weapon has no ammo at all
 */
template < class PlayerType >
inline bool CBot< PlayerType >::IsActiveWeaponOutOfAmmo( void ) const
{
	CWeaponCSBase *weapon = this->GetActiveCSWeapon();

	if (weapon == NULL)
		return true;

	return !weapon->HasAnyAmmo();
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Return true if looking thru weapon's scope
 */
template < class PlayerType >
inline bool CBot< PlayerType >::IsUsingScope( void )
{
	// if our field of view is less than 90, we're looking thru a scope (maybe only true for CS...)
	if (this->GetFOV() < this->GetDefaultFOV())
		return true;

	return false;
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Fill in a CUserCmd with our data
 */
template < class PlayerType >
inline void CBot< PlayerType >::BuildUserCmd( CUserCmd& cmd, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse )
{
	Q_memset( &cmd, 0, sizeof( cmd ) );
	cmd.command_number = gpGlobals->tickcount;
	cmd.forwardmove = forwardmove;
	cmd.sidemove = sidemove;
	cmd.upmove = upmove;
	cmd.buttons = buttons;
	cmd.impulse = impulse;

	VectorCopy( viewangles, cmd.viewangles );
	cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Update player physics, movement, weapon firing commands, etc
 */
template < class PlayerType >
inline void CBot< PlayerType >::UpdatePlayer( void )
{
	if (m_isCrouching)
	{
		SETBITS( m_buttonFlags, IN_DUCK );
	}
	else if (!m_isRunning)
	{
		SETBITS( m_buttonFlags, IN_SPEED );
	}

	if ( this->IsEFlagSet(EFL_BOT_FROZEN) )
	{
		m_buttonFlags = 0; // Freeze.
		m_forwardSpeed = 0;
		m_strafeSpeed = 0;
		m_verticalSpeed = 0;
	}

	// Fill in a CUserCmd with our data
	this->BuildUserCmd( m_userCmd, this->EyeAngles(), m_forwardSpeed, m_strafeSpeed, m_verticalSpeed, m_buttonFlags, 0 );

	// Save off the CUserCmd to execute later
	this->ProcessUsercmds( &m_userCmd, 1, 1, 0, false );
}


//--------------------------------------------------------------------------------------------------------------
template < class PlayerType >
inline void CBot< PlayerType >::ResetCommand( void )
{
	m_forwardSpeed = 0.0;
	m_strafeSpeed = 0.0;
	m_verticalSpeed	= 100.0; // stay at the top of water, so we don't drown.  TODO: swim logic
	m_buttonFlags = 0;
}


//--------------------------------------------------------------------------------------------------------------
/*
template < class PlayerType >
inline byte CBot< PlayerType >::ThrottledMsec( void ) const
{
	int iNewMsec;

	// Estimate Msec to use for this command based on time passed from the previous command
	iNewMsec = (int)( (gpGlobals->curtime - m_flPreviousCommandTime) * 1000 );
	if (iNewMsec > 255)		// Doh, bots are going to be slower than they should if this happens.
		iNewMsec = 255;		// Upgrade that CPU or use less bots!

	return (byte)iNewMsec;
}
*/

//--------------------------------------------------------------------------------------------------------------
/**
 * Do a "client command" - useful for invoking menu choices, etc.
 */
template < class PlayerType >
inline bool CBot< PlayerType >::ClientCommand( const CCommand &args )
{
	// Remove old args
	int i;
	for ( i=0; i<m_args.Count(); ++i )
	{
		delete[] m_args[i];
	}
	m_args.RemoveAll();

	// parse individual args
	const char *cmd = args.GetCommandString();
	while (1)
	{
		// skip whitespace up to a /n
		while (*cmd && *cmd <= ' ' && *cmd != '\n')
		{
			cmd++;
		}
		
		if (*cmd == '\n')
		{	// a newline seperates commands in the buffer
			cmd++;
			break;
		}

		if (!*cmd)
			break;
	
		cmd = SharedParse (cmd);
		if (!cmd)
			break;

		m_args.AddToTail( CloneString( SharedGetToken() ) );
	}

	// and pass to the base class
	return PlayerType::ClientCommand( args );
}


//--------------------------------------------------------------------------------------------------------------
/**
 * Returns the number of tokens in the command string
 */
template < class PlayerType >
inline int CBot< PlayerType >::Cmd_Argc()
{
	return m_args.Count();
}


//--------------------------------------------------------------------------------------------------------------
/**
 * Retrieves a specified token
 */
template < class PlayerType >
inline char * CBot< PlayerType >::Cmd_Argv( int argc )
{
	if ( argc < 0 || argc >= m_args.Count() )
		return NULL;
	return m_args[argc];
}


//--------------------------------------------------------------------------------------------------------------
/**
 * Returns TRUE if given entity is our enemy
 */
template < class PlayerType >
inline bool CBot< PlayerType >::IsEnemy( CBaseEntity *ent ) const
{
	// only Players (real and AI) can be enemies
	if (!ent->IsPlayer())
		return false;

	// corpses are no threat
	if (!ent->IsAlive())
		return false;	

	CBasePlayer *player = static_cast<CBasePlayer *>( ent );

	// if they are on our team, they are our friends
	if (player->GetTeamNumber() == this->GetTeamNumber())
		return false;

	// yep, we hate 'em
	return true;
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Return number of enemies left alive
 */
template < class PlayerType >
inline int CBot< PlayerType >::GetEnemiesRemaining( void ) const
{
	int count = 0;

	for ( int i = 1; i <= gpGlobals->maxClients; ++i )
	{
		CBaseEntity *player = UTIL_PlayerByIndex( i );

		if (player == NULL)
			continue;

		if (!IsEnemy( player ))
			continue;

		if (!player->IsAlive())
			continue;

		count++;
	}

	return count;
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Return number of friends left alive
 */
template < class PlayerType >
inline int CBot< PlayerType >::GetFriendsRemaining( void ) const
{
	int count = 0;

	for ( int i = 1; i <= gpGlobals->maxClients; ++i )
	{
		CBaseEntity *player = UTIL_PlayerByIndex( i );

		if (player == NULL)
			continue;

		if (IsEnemy( player ))
			continue;

		if (!player->IsAlive())
			continue;

		if (player == static_cast<CBaseEntity *>( const_cast<CBot *>( this ) ))
			continue;

		count++;
	}

	return count;
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Return true if the local player is currently in observer mode watching this bot.
 */
template < class PlayerType >
inline bool CBot< PlayerType >::IsLocalPlayerWatchingMe( void ) const
{
	if ( engine->IsDedicatedServer() )
		return false;

	CBasePlayer *player = UTIL_GetListenServerHost();
	if ( player == NULL )
		return false;

	if ( cv_bot_debug_target.GetInt() > 0 )
	{
		return this->entindex() == cv_bot_debug_target.GetInt();
	}

	if ( player->IsObserver() || !player->IsAlive() )
	{
		if ( const_cast< CBot< PlayerType > * >(this) == player->GetObserverTarget() )
		{
			switch( player->GetObserverMode() )
			{
				case OBS_MODE_IN_EYE:
				case OBS_MODE_CHASE:
					return true;
			}
		}
	}

	return false;
}


//--------------------------------------------------------------------------------------------------------------
/**
 * Output message to console if we are being watched by the local player
 */
template < class PlayerType >
inline void CBot< PlayerType >::PrintIfWatched( PRINTF_FORMAT_STRING const char *format, ... ) const
{
	if (cv_bot_debug.GetInt() == 0)
	{
		return;
	}

	if ((IsLocalPlayerWatchingMe() && (cv_bot_debug.GetInt() == 1 || cv_bot_debug.GetInt() == 3)) ||
		(cv_bot_debug.GetInt() == 2 || cv_bot_debug.GetInt() == 4))
	{
		va_list varg;
		char buffer[ CBotManager::MAX_DBG_MSG_SIZE ];
		const char *name = const_cast< CBot< PlayerType > * >( this )->GetPlayerName();

		va_start( varg, format );
		vsprintf( buffer, format, varg );
		va_end( varg );

		// prefix the console message with the bot's name (this can be NULL if bot was just added)
		ClientPrint( UTIL_GetListenServerHost(),
			HUD_PRINTCONSOLE,
			UTIL_VarArgs( "%s: %s",
			(name) ? name : "(NULL netname)", buffer ) );

		TheBots->AddDebugMessage( buffer );
	}
}

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

extern void InstallBotControl( void );
extern void RemoveBotControl( void );
extern void Bot_ServerCommand( void );
extern void Bot_RegisterCvars( void );

extern bool IsSpotOccupied( CBaseEntity *me, const Vector &pos );	// if a player is at the given spot, return true
extern const Vector *FindNearbyHidingSpot( CBaseEntity *me, const Vector &pos, float maxRange = 1000.0f, bool isSniper = false, bool useNearest = false );
extern const Vector *FindRandomHidingSpot( CBaseEntity *me, Place place, bool isSniper = false );
extern const Vector *FindNearbyRetreatSpot( CBaseEntity *me, const Vector &start, float maxRange = 1000.0f, int avoidTeam = 0 );


#endif // BOT_H