//========= Copyright © 1996-2005, 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 _CS_BOT_H_
#define _CS_BOT_H_

#ifdef _GAMECONSOLE
#define OPT_VIS_CSGO
#endif

#include "bot/bot.h"
#include "bot/cs_bot_manager.h"
#include "bot/cs_bot_chatter.h"
#include "cs_gamestate.h"
#include "cs_player.h"
#include "weapon_csbase.h"
#include "cs_nav_pathfind.h"
#include "cs_nav_area.h"

class CBaseDoor;
class CBasePropDoor;
class CCSBot;
class CPushAwayEnumerator;


//--------------------------------------------------------------------------------------------------------------
/**
 * For use with player->m_rgpPlayerItems[]
 */
enum InventorySlotType
{
	PRIMARY_WEAPON_SLOT = 1,
	PISTOL_SLOT,
	KNIFE_SLOT,
	GRENADE_SLOT,
	C4_SLOT
};


//--------------------------------------------------------------------------------------------------------------
/**
 * The definition of a bot's behavior state.  One or more finite state machines 
 * using these states implement a bot's behaviors.
 */
class BotState
{
public:
	virtual void OnEnter( CCSBot *bot ) { }				///< when state is entered
	virtual void OnUpdate( CCSBot *bot ) { }			///< state behavior
	virtual void OnExit( CCSBot *bot ) { }				///< when state exited
	virtual const char *GetName( void ) const = 0;		///< return state name
};


//--------------------------------------------------------------------------------------------------------------
/**
 * The state is invoked when a bot has nothing to do, or has finished what it was doing.
 * A bot never stays in this state - it is the main action selection mechanism.
 */
class IdleState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "Idle"; }

	void UpdateCoop( CCSBot *bot );
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is actively searching for an enemy.
 */
class HuntState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "Hunt"; }

	void ClearHuntArea( void )						{ m_huntArea = NULL; }

private:
	CNavArea *m_huntArea;										///< "far away" area we are moving to
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot has an enemy and is attempting to kill it
 */
class AttackState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "Attack"; }
	
	void SetCrouchAndHold( bool crouch )			{ m_crouchAndHold = crouch; }

protected:
	enum DodgeStateType
	{
		STEADY_ON,
		SLIDE_LEFT,
		SLIDE_RIGHT,
		JUMP,

		NUM_ATTACK_STATES
	};
	DodgeStateType m_dodgeState;
	float m_nextDodgeStateTimestamp;

	CountdownTimer m_repathTimer;
	float m_scopeTimestamp;

	bool m_haveSeenEnemy;										///< false if we haven't yet seen the enemy since we started this attack (told by a friend, etc)
	bool m_isEnemyHidden;										///< true we if we have lost line-of-sight to our enemy
	float m_reacquireTimestamp;									///< time when we can fire again, after losing enemy behind cover
	float m_shieldToggleTimestamp;								///< time to toggle shield deploy state
	bool m_shieldForceOpen;										///< if true, open up and shoot even if in danger

	float m_pinnedDownTimestamp;								///< time when we'll consider ourselves "pinned down" by the enemy

	bool m_crouchAndHold;
	bool m_didAmbushCheck;
	bool m_shouldDodge;
	bool m_firstDodge;

	bool m_isCoward;											///< if true, we'll retreat if outnumbered during this fight
	CountdownTimer m_retreatTimer;

	void StopAttacking( CCSBot *bot );
	void Dodge( CCSBot *bot );									///< do dodge behavior
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot has heard an enemy noise and is moving to find out what it was.
 */
class InvestigateNoiseState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "InvestigateNoise"; }

private:
	void AttendCurrentNoise( CCSBot *bot );						///< move towards currently heard noise
	Vector m_checkNoisePosition;								///< the position of the noise we're investigating
	CountdownTimer m_minTimer;									///< minimum time we will investigate our current noise
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is buying equipment at the start of a round.
 */
class BuyState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "Buy"; }

private:
	bool m_isInitialDelay;
	int m_prefRetries;											///< for retrying buying preferred weapon at current index
	int m_prefIndex;											///< where are we in our list of preferred weapons

	int m_retries;
	bool m_doneBuying;
	bool m_buyDefuseKit;
	bool m_buyGrenade;
	bool m_buyShield;
	bool m_buyPistol;
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is moving to a potentially far away position in the world.
 */
class MoveToState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "MoveTo"; }
	void SetGoalPosition( const Vector &pos )		{ m_goalPosition = pos; }
	void SetRouteType( RouteType route )			{ m_routeType = route; }

private:
	Vector m_goalPosition;										///< goal position of move
	RouteType m_routeType;										///< the kind of route to build
	bool m_radioedPlan;
	bool m_askedForCover;
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a Terrorist bot is moving to pick up a dropped bomb.
 */
class FetchBombState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual const char *GetName( void ) const	{ return "FetchBomb"; }
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a Terrorist bot is actually planting the bomb.
 */
class PlantBombState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const	{ return "PlantBomb"; }
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a CT bot is actually defusing a live bomb.
 */
class DefuseBombState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const	{ return "DefuseBomb"; }
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a CT bot is picking up a hostage
 */
class PickupHostageState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const	{ return "PickupHostage"; }

	void SetEntity( CBaseEntity *entity )				{ m_entity = entity; }

private:
	EHANDLE m_entity;	
};

//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is hiding in a corner.
 * NOTE: This state also includes MOVING TO that hiding spot, which may be all the way
 * across the map!
 */
class HideState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const	{ return "Hide"; }

	void SetHidingSpot( const Vector &pos )		{ m_hidingSpot = pos; }
	const Vector &GetHidingSpot( void ) const	{ return m_hidingSpot; }

	void SetSearchArea( CNavArea *area )		{ m_searchFromArea = area; }
	void SetSearchRange( float range )			{ m_range = range; }
	void SetDuration( float time )				{ m_duration = time; }
	void SetHoldPosition( bool hold )			{ m_isHoldingPosition = hold; }

	bool IsAtSpot( void ) const					{ return m_isAtSpot; }

	float GetHideTime( void ) const
	{
		if (IsAtSpot())
		{
			return m_duration - m_hideTimer.GetRemainingTime();
		}

		return 0.0f;
	}

private:
	CNavArea *m_searchFromArea;
	float m_range;

	Vector m_hidingSpot;
	bool m_isLookingOutward;
	bool m_isAtSpot;
	float m_duration;
	CountdownTimer m_hideTimer;								///< how long to hide

	bool m_isHoldingPosition;
	float m_holdPositionTime;								///< how long to hold our position after we hear nearby enemy noise

	bool m_heardEnemy;										///< set to true when we first hear an enemy
	float m_firstHeardEnemyTime;							///< when we first heard the enemy

	int m_retry;											///< counter for retrying hiding spot

	Vector m_leaderAnchorPos;								///< the position of our follow leader when we decided to hide

	bool m_isPaused;										///< if true, we have paused in our retreat for a moment
	CountdownTimer m_pauseTimer;							///< for stoppping and starting our pauses while we retreat
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is attempting to flee from a bomb that is about to explode.
 */
class EscapeFromBombState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "EscapeFromBomb"; }
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is following another player.
 */
class FollowState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "Follow"; }

	void SetLeader( CCSPlayer *player )				{ m_leader = player; }

private:
	CHandle< CCSPlayer > m_leader;								///< the player we are following
	Vector m_lastLeaderPos;										///< where the leader was when we computed our follow path
	bool m_isStopped;
	float m_stoppedTimestamp;

	enum LeaderMotionStateType
	{
		INVALID,
		STOPPED,
		WALKING,
		RUNNING
	};
	LeaderMotionStateType m_leaderMotionState;
	IntervalTimer m_leaderMotionStateTime;

	bool m_isSneaking;
	float m_lastSawLeaderTime;
	CountdownTimer m_repathInterval;

	IntervalTimer m_walkTime;
	bool m_isAtWalkSpeed;

	float m_waitTime;
	CountdownTimer m_idleTimer;

	void ComputeLeaderMotionState( float leaderSpeed );
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is actually using another entity (ie: facing towards it and pressing the use key)
 */
class UseEntityState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const			{ return "UseEntity"; }

	void SetEntity( CBaseEntity *entity )				{ m_entity = entity; }

private:
	EHANDLE m_entity;											///< the entity we will use
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is opening a door
 */
class OpenDoorState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "OpenDoor"; }

	void SetDoor( CBaseEntity *door );

	bool IsDone( void ) const						{ return m_isDone; }	///< return true if behavior is done

private:
	CHandle< CBaseDoor > m_funcDoor;									///< the func_door we are opening
	CHandle< CBasePropDoor > m_propDoor;								///< the prop_door we are opening
	bool m_isDone;
	CountdownTimer m_timeout;
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is attempting to escape from a field of flames (probably from a Molotov)
 */
class EscapeFromFlamesState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "EscapeFromFlames"; }

private:
	CNavArea *FindNearestNonDamagingArea( CCSBot *bot ) const;
	CountdownTimer m_searchTimer;
	CNavArea *m_safeArea;
};


//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------
/**
 * The Counter-strike Bot
 */
class CCSBot : public CBot< CCSPlayer >
{
public:
	DECLARE_CLASS( CCSBot, CBot< CCSPlayer > );
	DECLARE_DATADESC();

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

	virtual void Spawn( void );									///< (EXTEND) spawn the bot into the game
	virtual void Touch( CBaseEntity *other );					///< (EXTEND) when touched by another entity

	virtual void Upkeep( void );								///< lightweight maintenance, invoked frequently
	virtual void Update( void );								///< heavyweight algorithms, invoked less often
	virtual void BuildUserCmd( CUserCmd& cmd, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse );
	virtual void AvoidPlayers( CUserCmd *pCmd );				///< some game types allow players to pass through each other, this method pushes them apart
	virtual float GetMoveSpeed( void );							///< returns current movement speed (for walk/run)

	virtual void Walk( void );
	virtual bool Jump( bool mustJump = false );					///< returns true if jump was started

	//- behavior properties ------------------------------------------------------------------------------------------
	float GetCombatRange( void ) const;
	bool IsRogue( void ) const;									///< return true if we dont listen to teammates or pursue scenario goals
	void SetRogue( bool rogue );
	bool IsHurrying( void ) const;								///< return true if we are in a hurry 
	void Hurry( float duration );								///< force bot to hurry
	bool IsSafe( void ) const;									///< return true if we are in a safe region
	bool IsWellPastSafe( void ) const;							///< return true if it is well past the early, "safe", part of the round
	bool IsEndOfSafeTime( void ) const;							///< return true if we were in the safe time last update, but not now
	float GetSafeTimeRemaining( void ) const;					///< return the amount of "safe time" we have left
	float GetSafeTime( void ) const;							///< return what we think the total "safe time" for this map is
	virtual void Blind( float holdTime, float fadeTime, float startingAlpha = 255 );	// player blinded by a flashbang
	bool IsUnhealthy( void ) const;								///< returns true if bot is low on health
	
	bool IsAlert( void ) const;									///< return true if bot is in heightened "alert" mode
	void BecomeAlert( void );									///< bot becomes "alert" for immediately nearby enemies

	bool IsSneaking( void ) const;								///< return true if bot is sneaking
	void Sneak( float duration );								///< sneak for given duration

	//- behaviors ---------------------------------------------------------------------------------------------------
	void Idle( void );
	bool IsIdling( void ) const;

	void Hide( CNavArea *searchFromArea = NULL, float duration = -1.0f, float hideRange = 750.0f, bool holdPosition = false );	///< DEPRECATED: Use TryToHide() instead
	#define USE_NEAREST true
	bool TryToHide( CNavArea *searchFromArea = NULL, float duration = -1.0f, float hideRange = 750.0f, bool holdPosition = false, bool useNearest = false, const Vector *pStartPosOverride = NULL );	///< try to hide nearby, return false if cannot
	void Hide( const Vector &hidingSpot, float duration = -1.0f, bool holdPosition = false );	///< move to the given hiding place
	bool IsHiding( void ) const;								///< returns true if bot is currently hiding
	bool IsAtHidingSpot( void ) const;							///< return true if we are hiding and at our hiding spot
	float GetHidingTime( void ) const;							///< return number of seconds we have been at our current hiding spot

	bool MoveToInitialEncounter( void );						///< move to a hiding spot and wait for initial encounter with enemy team (return false if no spots are available)

	bool TryToRetreat( float maxRange = 1000.0f, float duration = -1.0f );	///< retreat to a nearby hiding spot, away from enemies

	void Hunt( void );
	bool IsHunting( void ) const;								///< returns true if bot is currently hunting

	void Attack( CCSPlayer *victim );
	void FireWeaponAtEnemy( void );								///< fire our active weapon towards our current enemy
	void StopAttacking( void );
	bool IsAttacking( void ) const;								///< returns true if bot is currently engaging a target

	void MoveTo( const Vector &pos, RouteType route = SAFEST_ROUTE );	///< move to potentially distant position
	bool IsMovingTo( void ) const;								///< return true if we are in the MoveTo state

	void PlantBomb( void );

	void FetchBomb( void );										///< bomb has been dropped - go get it
	bool NoticeLooseBomb( void ) const;							///< return true if we noticed the bomb on the ground or on radar
	bool CanSeeLooseBomb( void ) const;							///< return true if we directly see the loose bomb

	void DefuseBomb( void );

	void PickupHostage( CBaseEntity *entity );
	bool IsPickingupHostage( void ) const;

	bool IsDefusingBomb( void ) const;							///< returns true if bot is currently defusing the bomb
	bool CanSeePlantedBomb( void ) const;						///< return true if we directly see the planted bomb

	void EscapeFromBomb( void );
	bool IsEscapingFromBomb( void ) const;						///< return true if we are escaping from the bomb

	void EscapeFromFlames( void );
	bool IsEscapingFromFlames( void ) const;					///< return true if we are escaping from a field of flames

	void RescueHostages( void );								///< begin process of rescuing hostages

	void UseEntity( CBaseEntity *entity );						///< use the entity

	void OpenDoor( CBaseEntity *door );							///< open the door (assumes we are right in front of it)
	bool IsOpeningDoor( void ) const;							///< return true if we are in the process of opening a door

	void Buy( void );											///< enter the buy state
	bool IsBuying( void ) const;

	void Panic( void );											///< look around in panic
	bool IsPanicking( void ) const;								///< return true if bot is panicked
	void StopPanicking( void );									///< end our panic
	void UpdatePanicLookAround( void );							///< do panic behavior

	void TryToJoinTeam( int team );								///< try to join the given team

	void Follow( CCSPlayer *player );							///< begin following given Player
	void ContinueFollowing( void );								///< continue following our leader after finishing what we were doing
	void StopFollowing( void );									///< stop following
	bool IsFollowing( void ) const;								///< return true if we are following someone (not necessarily in the follow state)
	CCSPlayer *GetFollowLeader( void ) const;					///< return the leader we are following
	float GetFollowDuration( void ) const;						///< return how long we've been following our leader
	bool CanAutoFollow( void ) const;							///< return true if we can auto-follow

	bool IsNotMoving( float minDuration = 0.0f ) const;			///< return true if we are currently standing still and have been for minDuration

	void AimAtEnemy( void );									///< point our weapon towards our enemy
	void StopAiming( void );									///< stop aiming at enemy
	bool IsAimingAtEnemy( void ) const;							///< returns true if we are trying to aim at an enemy

	float GetStateTimestamp( void ) const;						///< get time current state was entered

	bool IsDoingScenario( void ) const;							///< return true if we will do scenario-related tasks

	//- scenario / gamestate -----------------------------------------------------------------------------------------
	CSGameState *GetGameState( void );							///< return an interface to this bot's gamestate
	const CSGameState *GetGameState( void ) const;				///< return an interface to this bot's gamestate

	bool IsAtBombsite( void );									///< return true if we are in a bomb planting zone
	bool GuardRandomZone( float range = 500.0f );				///< pick a random zone and hide near it

	bool IsBusy( void ) const;									///< return true if we are busy doing something important

	//- high-level tasks ---------------------------------------------------------------------------------------------
	enum TaskType
	{
		SEEK_AND_DESTROY,
		PLANT_BOMB,
		FIND_TICKING_BOMB,
		DEFUSE_BOMB,
		GUARD_TICKING_BOMB,
		GUARD_BOMB_DEFUSER,
		GUARD_LOOSE_BOMB,
		GUARD_BOMB_ZONE,
		GUARD_INITIAL_ENCOUNTER,
		ESCAPE_FROM_BOMB,
		HOLD_POSITION,
		FOLLOW,
		VIP_ESCAPE,
		GUARD_VIP_ESCAPE_ZONE,
		COLLECT_HOSTAGES,
		RESCUE_HOSTAGES,
		GUARD_HOSTAGES,
		GUARD_HOSTAGE_RESCUE_ZONE,
		MOVE_TO_LAST_KNOWN_ENEMY_POSITION,
		MOVE_TO_SNIPER_SPOT,
		SNIPING,
		ESCAPE_FROM_FLAMES,

		NUM_TASKS
	};
	void SetTask( TaskType task, CBaseEntity *entity = NULL );	///< set our current "task"
	TaskType GetTask( void ) const;
	CBaseEntity *GetTaskEntity( void );
	const char *GetTaskName( void ) const;						///< return string describing current task

	// You probably never want to call these on CS bots. 
	virtual CBaseEntity		*GetEnemy( void ) OVERRIDE { Assert( 0 ); return NULL; }
	virtual CBaseEntity		*GetEnemy( void ) const OVERRIDE { Assert( 0 ); return NULL; }

	//- behavior modifiers ------------------------------------------------------------------------------------------
	enum DispositionType
	{
		ENGAGE_AND_INVESTIGATE,								///< engage enemies on sight and investigate enemy noises
		OPPORTUNITY_FIRE,									///< engage enemies on sight, but only look towards enemy noises, dont investigate
		SELF_DEFENSE,										///< only engage if fired on, or very close to enemy
		IGNORE_ENEMIES,										///< ignore all enemies - useful for ducking around corners, running away, etc

		NUM_DISPOSITIONS
	};
	void SetDisposition( DispositionType disposition );		///< define how we react to enemies
	DispositionType GetDisposition( void ) const;
	const char *GetDispositionName( void ) const;			///< return string describing current disposition

	void IgnoreEnemies( float duration );					///< ignore enemies for a short duration

	enum MoraleType
	{
		TERRIBLE = -3,
		BAD = -2,
		NEGATIVE = -1,
		NEUTRAL = 0,
		POSITIVE = 1,
		GOOD = 2,
		EXCELLENT = 3,
	};
	MoraleType GetMorale( void ) const;
	const char *GetMoraleName( void ) const;				///< return string describing current morale
	void IncreaseMorale( void );
	void DecreaseMorale( void );

	void Surprise( float duration );						///< become "surprised" - can't attack
	bool IsSurprised( void ) const;							///< return true if we are "surprised"


	//- listening for noises ----------------------------------------------------------------------------------------
	bool IsNoiseHeard( void ) const;							///< return true if we have heard a noise
	bool HeardInterestingNoise( void );							///< return true if we heard an enemy noise worth checking in to

	float GetNoiseInvestigateChance( void ) const;

	void InvestigateNoise( void );								///< investigate recent enemy noise
	bool IsInvestigatingNoise( void ) const;					///< return true if we are investigating a noise
	const Vector *GetNoisePosition( void ) const;				///< return position of last heard noise, or NULL if none heard
	CNavArea *GetNoiseArea( void ) const;						///< return area where noise was heard
	void ForgetNoise( void );									///< clear the last heard noise
	bool CanSeeNoisePosition( void ) const;						///< return true if we directly see where we think the noise came from
	float GetNoiseRange( void ) const;							///< return approximate distance to last noise heard

	bool CanHearNearbyEnemyGunfire( float range = -1.0f ) const;///< return true if we hear nearby threatening enemy gunfire within given range (-1 == infinite)
	PriorityType GetNoisePriority( void ) const;				///< return priority of last heard noise

	//- radio and chatter--------------------------------------------------------------------------------------------
	void SendRadioMessage( RadioType event );					///< send a radio message
	void SpeakAudio( const char *voiceFilename, float duration, int pitch );	///< send voice chatter
	bool SpeakAudioResponseRules( const char *pConcept, AI_CriteriaSet *criteria, float duration );	///< send voice chatter through response rules system
	BotChatterInterface *GetChatter( void );					///< return an interface to this bot's chatter system
	bool RespondToHelpRequest( CCSPlayer *player, Place place, float maxRange = -1.0f );	///< decide if we should move to help the player, return true if we will
	bool IsUsingVoice() const;									///< new-style "voice" chatter gets voice feedback


	//- enemies ------------------------------------------------------------------------------------------------------
	// BOTPORT: GetEnemy() collides with GetEnemy() in CBaseEntity - need to use different nomenclature
	void SetBotEnemy( CCSPlayer *enemy );						///< set given player as our current enemy
	CCSPlayer *GetBotEnemy( void ) const;
	int GetNearbyEnemyCount( void ) const;						///< return max number of nearby enemies we've seen recently
	unsigned int GetEnemyPlace( void ) const;					///< return location where we see the majority of our enemies
	bool CanSeeBomber( void ) const;							///< return true if we can see the bomb carrier
	CCSPlayer *GetBomber( void ) const;

	int GetNearbyFriendCount( void ) const;						///< return number of nearby teammates
	CCSPlayer *GetClosestVisibleFriend( void ) const;			///< return the closest friend that we can see
	CCSPlayer *GetClosestVisibleHumanFriend( void ) const;		///< return the closest human friend that we can see

	bool IsOutnumbered( void ) const;							///< return true if we are outnumbered by enemies
	int OutnumberedCount( void ) const;							///< return number of enemies we are outnumbered by

	#define ONLY_VISIBLE_ENEMIES true
	CCSPlayer *GetImportantEnemy( bool checkVisibility = false ) const;	///< return the closest "important" enemy for the given scenario (bomb carrier, VIP, hostage escorter)

	void UpdateReactionQueue( void );							///< update our reaction time queue
	CCSPlayer *GetRecognizedEnemy( void );						///< return the most dangerous threat we are "conscious" of
	bool IsRecognizedEnemyReloading( void );					///< return true if the enemy we are "conscious" of is reloading
	bool IsRecognizedEnemyProtectedByShield( void );			///< return true if the enemy we are "conscious" of is hiding behind a shield
	float GetRangeToNearestRecognizedEnemy( void );				///< return distance to closest enemy we are "conscious" of

	CCSPlayer *GetAttacker( void ) const;						///< return last enemy that hurt us
	float GetTimeSinceAttacked( void ) const;					///< return duration since we were last injured by an attacker
	float GetFirstSawEnemyTimestamp( void ) const;				///< time since we saw any enemies
	float GetLastSawEnemyTimestamp( void ) const;
	float GetTimeSinceLastSawEnemy( void ) const;
	float GetTimeSinceAcquiredCurrentEnemy( void ) const;
	float GetTimeSinceBurnedByFlames( void ) const;
	bool HasNotSeenEnemyForLongTime( void ) const;				///< return true if we haven't seen an enemy for "a long time"
	const Vector &GetLastKnownEnemyPosition( void ) const;
	bool IsEnemyVisible( void ) const;							///< is our current enemy visible
	float GetEnemyDeathTimestamp( void ) const;
	bool IsFriendInLineOfFire( void );							///< return true if a friend is in our weapon's way
	bool IsAwareOfEnemyDeath( void ) const;						///< return true if we *noticed* that our enemy died
	int GetLastVictimID( void ) const;							///< return the ID (entindex) of the last victim we killed, or zero

	bool CanSeeSniper( void ) const;							///< return true if we can see an enemy sniper
	bool HasSeenSniperRecently( void ) const;					///< return true if we have seen a sniper recently

	float GetTravelDistanceToPlayer( CCSPlayer *player ) const;	///< return shortest path travel distance to this player	
	bool DidPlayerJustFireWeapon( const CCSPlayer *player ) const;	///< return true if the given player just fired their weapon

	//- navigation --------------------------------------------------------------------------------------------------
	bool HasPath( void ) const;
	void DestroyPath( void );

	float GetFeetZ( void ) const;								///< return Z of bottom of feet

	enum PathResult
	{
		PROGRESSING,		///< we are moving along the path
		END_OF_PATH,		///< we reached the end of the path
		PATH_FAILURE		///< we failed to reach the end of the path
	};
	#define NO_SPEED_CHANGE false
	PathResult UpdatePathMovement( bool allowSpeedChange = true );	///< move along our computed path - if allowSpeedChange is true, bot will walk when near goal to ensure accuracy

	//bool AStarSearch( CNavArea *startArea, CNavArea *goalArea );	///< find shortest path from startArea to goalArea - don't actually buid the path
	bool ComputePath( const Vector &goal, RouteType route = SAFEST_ROUTE );	///< compute path to goal position
	bool StayOnNavMesh( void );
	const Vector &GetPathEndpoint( void ) const;					///< return final position of our current path
	float GetPathDistanceRemaining( void ) const;					///< return estimated distance left to travel along path
	void ResetStuckMonitor( void );
	bool IsAreaVisible( const CNavArea *area ) const;				///< is any portion of the area visible to this bot
	const Vector &GetPathPosition( int index ) const;
	bool GetSimpleGroundHeightWithFloor( const Vector &pos, float *height, Vector *normal = NULL );	///< find "simple" ground height, treating current nav area as part of the floor
	void BreakablesCheck( void );
	void DoorCheck( void );											///< Check for any doors along our path that need opening

	virtual void PushawayTouch( CBaseEntity *pOther );

	Place GetPlace( void ) const;									///< get our current radio chatter place

	bool IsUsingLadder( void ) const;								///< returns true if we are in the process of negotiating a ladder
	void GetOffLadder( void );										///< immediately jump off of our ladder, if we're on one

	void SetGoalEntity( CBaseEntity *entity );
	CBaseEntity *GetGoalEntity( void );

	bool IsNearJump( void ) const;									///< return true if nearing a jump in the path
	float GetApproximateFallDamage( float height ) const;			///< return how much damage will will take from the given fall height

	void ForceRun( float duration );								///< force the bot to run if it moves for the given duration
	virtual bool IsRunning( void ) const;

	void Wait( float duration );									///< wait where we are for the given duration
	bool IsWaiting( void ) const;									///< return true if we are waiting
	void StopWaiting( void );										///< stop waiting

	void Wiggle( void );											///< random movement, for getting un-stuck

	bool IsFriendInTheWay( const Vector &goalPos );					///< return true if a friend is between us and the given position
	void FeelerReflexAdjustment( Vector *goalPosition );			///< do reflex avoidance movements if our "feelers" are touched

	bool HasVisitedEnemySpawn( void ) const;						///< return true if we have visited enemy spawn at least once
	bool IsAtEnemySpawn( void ) const;								///< return true if we are at the/an enemy spawn right now

	//- looking around ----------------------------------------------------------------------------------------------

	// BOTPORT: EVIL VILE HACK - why is EyePosition() not const?!?!?
	const Vector &EyePositionConst( void ) const;
	
	void SetLookAngles( float yaw, float pitch );					///< set our desired look angles
	void UpdateLookAngles( void );									///< move actual view angles towards desired ones
	void UpdateLookAround( bool updateNow = false );				///< update "looking around" mechanism
	void InhibitLookAround( float duration );						///< block all "look at" and "looking around" behavior for given duration - just look ahead

	/// @todo Clean up notion of "forward angle" and "look ahead angle"
	void SetForwardAngle( float angle );							///< define our forward facing
	void SetLookAheadAngle( float angle );							///< define default look ahead angle

	/// look at the given point in space for the given duration (-1 means forever)
	void SetLookAt( const char *desc, const Vector &pos, PriorityType pri, float duration = -1.0f, bool clearIfClose = false, float angleTolerance = 5.0f, bool attack = false );
	void ClearLookAt( void );										///< stop looking at a point in space and just look ahead
	bool IsLookingAtSpot( PriorityType pri = PRIORITY_LOW ) const;	///< return true if we are looking at spot with equal or higher priority
	bool IsViewMoving( float angleVelThreshold = 1.0f ) const;		///< returns true if bot's view angles are rotating (not still)
	bool HasViewBeenSteady( float duration ) const;					///< how long has our view been "steady" (ie: not moving) for given duration

	bool HasLookAtTarget( void ) const;								///< return true if we are in the process of looking at a target

	enum VisiblePartType
	{
		NONE		= 0x00,
		GUT			= 0x01,
		HEAD		= 0x02,
		LEFT_SIDE	= 0x04,			///< the left side of the object from our point of view (not their left side)
		RIGHT_SIDE	= 0x08,			///< the right side of the object from our point of view (not their right side)
		FEET		= 0x10
	};

	#define CHECK_FOV true
	bool IsVisible( const Vector &pos, bool testFOV = false, const CBaseEntity *ignore = NULL ) const;	///< return true if we can see the point
	bool IsVisible( CCSPlayer *player, bool testFOV = false, unsigned char *visParts = NULL ) const;	///< return true if we can see any part of the player

	bool IsBeyondBotMaxVisionDistance( const Vector &vecTargetPosition ) const;

	bool IsNoticable( const CCSPlayer *player, unsigned char visibleParts ) const;	///< return true if we "notice" given player 

	bool IsEnemyPartVisible( VisiblePartType part ) const;			///< if enemy is visible, return the part we see for our current enemy
	const Vector &GetPartPosition( CCSPlayer *player, VisiblePartType part ) const;	///< return world space position of given part on player

	float ComputeWeaponSightRange( void );							///< return line-of-sight distance to obstacle along weapon fire ray

	bool IsAnyVisibleEnemyLookingAtMe( bool testFOV = false ) const;///< return true if any enemy I have LOS to is looking directly at me

	bool IsSignificantlyCloser( const CCSPlayer *testPlayer, const CCSPlayer *referencePlayer ) const;	///< return true if testPlayer is significantly closer than referencePlayer

	//- approach points ---------------------------------------------------------------------------------------------
	void ComputeApproachPoints( void );								///< determine the set of "approach points" representing where the enemy can enter this region
	void UpdateApproachPoints( void );								///< recompute the approach point set if we have moved far enough to invalidate the current ones
	void ClearApproachPoints( void );
	void DrawApproachPoints( void ) const;							///< for debugging
	float GetHidingSpotCheckTimestamp( HidingSpot *spot ) const;	///< return time when given spot was last checked
	void SetHidingSpotCheckTimestamp( HidingSpot *spot );			///< set the timestamp of the given spot to now

	const CNavArea *GetInitialEncounterArea( void ) const;			///< return area where we think we will first meet the enemy
	void SetInitialEncounterArea( const CNavArea *area );

	//- weapon query and equip --------------------------------------------------------------------------------------
	#define MUST_EQUIP true
	void EquipBestWeapon( bool mustEquip = false );					///< equip the best weapon we are carrying that has ammo
	void EquipPistol( void );										///< equip our pistol
	void EquipKnife( void );										///< equip the knife

	#define DONT_USE_SMOKE_GRENADE true
	bool EquipGrenade( bool noSmoke = false );						///< equip a grenade, return false if we cant

	bool IsUsingKnife( void ) const;								///< returns true if we have knife equipped
	bool IsUsingPistol( void ) const;								///< returns true if we have pistol equipped
	bool IsUsingGrenade( void ) const;								///< returns true if we have grenade equipped
	bool IsUsingSniperRifle( void ) const;							///< returns true if using a "sniper" rifle
	bool IsUsing( CSWeaponID weapon ) const;						///< returns true if using the specific weapon
	bool IsSniper( void ) const;									///< return true if we have a sniper rifle in our inventory
	bool IsSniping( void ) const;									///< return true if we are actively sniping (moving to sniper spot or settled in)
	bool IsUsingShotgun( void ) const;								///< returns true if using a shotgun
	bool IsUsingMachinegun( void ) const;							///< returns true if using the big 'ol machinegun
	void ThrowGrenade( const Vector &target );						///< begin the process of throwing the grenade
	bool IsThrowingGrenade( void ) const;							///< return true if we are in the process of throwing a grenade
	bool HasGrenade( void ) const;									///< return true if we have a grenade in our inventory
	void AvoidEnemyGrenades( void );								///< react to enemy grenades we see
	bool IsAvoidingGrenade( void ) const;							///< return true if we are in the act of avoiding a grenade
	bool DoesActiveWeaponHaveRemoveableSilencer( void ) const;		///< returns true if we are using a weapon with a removable silencer
	bool CanActiveWeaponFire( void ) const;							///< returns true if our current weapon can attack
	CWeaponCSBase *GetActiveCSWeapon( void ) const;					///< get our current Counter-Strike weapon

	void GiveWeapon( const char *weaponAlias );						///< Debug command to give a named weapon

	virtual void PrimaryAttack( void );								///< presses the fire button, unless we're holding a pistol that can't fire yet (so we can just always call PrimaryAttack())

	enum ZoomType { NO_ZOOM, LOW_ZOOM, HIGH_ZOOM };
	ZoomType GetZoomLevel( void );									///< return the current zoom level of our weapon

	bool AdjustZoom( float range );									///< change our zoom level to be appropriate for the given range
	bool IsWaitingForZoom( void ) const;							///< return true if we are reacquiring after our zoom

	bool IsPrimaryWeaponEmpty( void ) const;						///< return true if primary weapon doesn't exist or is totally out of ammo
	bool IsPistolEmpty( void ) const;								///< return true if pistol doesn't exist or is totally out of ammo

	int GetHostageEscortCount( void ) const;						///< return the number of hostages following me
	void IncreaseHostageEscortCount( void );
	float GetRangeToFarthestEscortedHostage( void ) const;			///< return euclidean distance to farthest escorted hostage
	void ResetWaitForHostagePatience( void );

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

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

	/// invoked when killed (EXTEND)
	virtual void Event_Killed( const CTakeDamageInfo &info );

	virtual bool BumpWeapon( CBaseCombatWeapon *pWeapon );		///< invoked when in contact with a CWeaponBox


	/// invoked when event occurs in the game (some events have NULL entity)
	void OnPlayerFootstep( IGameEvent *event );
	void OnPlayerRadio( IGameEvent *event );
	void OnPlayerDeath( IGameEvent *event );
	void OnPlayerFallDamage( IGameEvent *event );

	void OnBombPickedUp( IGameEvent *event );
	void OnBombPlanted( IGameEvent *event );
	void OnBombBeep( IGameEvent *event );
	void OnBombDefuseBegin( IGameEvent *event );
	void OnBombDefused( IGameEvent *event );
	void OnBombDefuseAbort( IGameEvent *event );
	void OnBombExploded( IGameEvent *event );

	void OnRoundEnd( IGameEvent *event );
	void OnRoundStart( IGameEvent *event );

	void OnDoorMoving( IGameEvent *event );

	void OnBreakProp( IGameEvent *event );
	void OnBreakBreakable( IGameEvent *event );

	void OnHostageFollows( IGameEvent *event );
	void OnHostageRescuedAll( IGameEvent *event );

	void OnWeaponFire( IGameEvent *event );
	void OnWeaponFireOnEmpty( IGameEvent *event );
	void OnWeaponReload( IGameEvent *event );
	void OnWeaponZoom( IGameEvent *event );

	void OnBulletImpact( IGameEvent *event );

	void OnHEGrenadeDetonate( IGameEvent *event );
	void OnFlashbangDetonate( IGameEvent *event );
	void OnSmokeGrenadeDetonate( IGameEvent *event );
	void OnMolotovDetonate( IGameEvent *event );
	void OnDecoyDetonate( IGameEvent *event );
	void OnDecoyFiring( IGameEvent *event );
	void OnGrenadeBounce( IGameEvent *event );

	void OnNavBlocked( IGameEvent *event );

	void OnEnteredNavArea( CNavArea *newArea );						///< invoked when bot enters a nav area

	#define IS_FOOTSTEP true
	void OnAudibleEvent( IGameEvent *event, CBasePlayer *player, float range, PriorityType priority, bool isHostile, bool isFootstep = false, const Vector *actualOrigin = NULL );	///< Checks if the bot can hear the event

	void SetLastCoopSpawnPoint( SpawnPointCoopEnemy *spawn );
	SpawnPointCoopEnemy *GetLastCoopSpawnPoint( void ) const;
	CHandle< SpawnPointCoopEnemy > m_lastCoopSpawnPoint;				/// the last spawn point entity that we spawned from

protected:

	float SlowNoise( float fTau ) const;

	BotProfile *m_pLocalProfile;										///< local profile of the bot - can change over time due to dynamic bot difficulty changes

private:
	friend class CCSBotManager;

	/// @todo Get rid of these
	friend class AttackState;
	friend class BuyState;

	// BOTPORT: Remove this vile hack
	Vector m_eyePosition;

	void ResetValues( void );										///< reset internal data to initial state
	void BotDeathThink( void );

	char m_name[64];												///< copied from STRING(pev->netname) for debugging
	void DebugDisplay( void ) const;								///< render bot debug info


	//- behavior properties ------------------------------------------------------------------------------------------
	float m_combatRange;											///< desired distance between us and them during gunplay
	mutable bool m_isRogue;											///< if true, the bot is a "rogue" and listens to no-one
	mutable CountdownTimer m_rogueTimer;
	MoraleType m_morale;											///< our current morale, based on our win/loss history
	bool m_diedLastRound;											///< true if we died last round
	float m_safeTime;												///< duration at the beginning of the round where we feel "safe"
	bool m_wasSafe;													///< true if we were in the safe time last update
	void AdjustSafeTime( void );									///< called when enemy seen to adjust safe time for this round
	NavRelativeDirType m_blindMoveDir;								///< which way to move when we're blind
	bool m_blindFire;												///< if true, fire weapon while blinded
	CountdownTimer m_surpriseTimer;									///< when we were surprised

	bool m_isFollowing;												///< true if we are following someone
	CHandle< CCSPlayer > m_leader;									///< the ID of who we are following
	float m_followTimestamp;										///< when we started following
	float m_allowAutoFollowTime;									///< time when we can auto follow

	CountdownTimer m_hurryTimer;									///< if valid, bot is in a hurry
	CountdownTimer m_alertTimer;									///< if valid, bot is alert
	CountdownTimer m_sneakTimer;									///< if valid, bot is sneaking
	CountdownTimer m_panicTimer;									///< if valid, bot is panicking


	// instances of each possible behavior state, to avoid dynamic memory allocation during runtime
	IdleState				m_idleState;
	HuntState				m_huntState;
	AttackState				m_attackState;
	InvestigateNoiseState	m_investigateNoiseState;
	BuyState				m_buyState;
	MoveToState				m_moveToState;
	FetchBombState			m_fetchBombState;
	PlantBombState			m_plantBombState;
	DefuseBombState			m_defuseBombState;
	PickupHostageState		m_pickupHostageState;
	HideState				m_hideState;
	EscapeFromBombState		m_escapeFromBombState;
	FollowState				m_followState;
	UseEntityState			m_useEntityState;
	OpenDoorState			m_openDoorState;
	EscapeFromFlamesState	m_escapeFromFlamesState;

	/// @todo Allow multiple simultaneous state machines (look around, etc)	
	void SetState( BotState *state );								///< set the current behavior state
	BotState *m_state;												///< current behavior state
	float m_stateTimestamp;											///< time state was entered
	bool m_isAttacking;												///< if true, special Attack state is overriding the state machine
	bool m_isOpeningDoor;											///< if true, special OpenDoor state is overriding the state machine

	TaskType m_task;												///< our current task
	EHANDLE m_taskEntity;											///< an entity used for our task

	//- navigation ---------------------------------------------------------------------------------------------------
	Vector m_goalPosition;
	EHANDLE m_goalEntity;
	void MoveTowardsPosition( const Vector &pos );					///< move towards position, independant of view angle
	void MoveAwayFromPosition( const Vector &pos );					///< move away from position, independant of view angle
	void StrafeAwayFromPosition( const Vector &pos );				///< strafe (sidestep) away from position, independant of view angle
	void StuckCheck( void );										///< check if we have become stuck
	CCSNavArea *m_currentArea;										///< the nav area we are standing on
	CCSNavArea *m_lastKnownArea;										///< the last area we were in
	EHANDLE m_avoid;												///< higher priority player we need to make way for
	float m_avoidTimestamp;
	bool m_isStopping;												///< true if we're trying to stop because we entered a 'stop' nav area
	bool m_hasVisitedEnemySpawn;									///< true if we have been at the enemy spawn
	IntervalTimer m_stillTimer;										///< how long we have been not moving
	void CoopUpdateChecks( void );									///< checks needed for coop

	//- path navigation data ----------------------------------------------------------------------------------------
	enum { MAX_PATH_LENGTH = 256 };
	struct ConnectInfo
	{
		CNavArea *area;												///< the area along the path
		NavTraverseType how;										///< how to enter this area from the previous one
		Vector pos;													///< our movement goal position at this point in the path
		const CNavLadder *ladder;									///< if "how" refers to a ladder, this is it
	}
	m_path[ MAX_PATH_LENGTH ];
	int m_pathLength;
	int m_pathIndex;												///< index of next area on path
	float m_areaEnteredTimestamp;
	void BuildTrivialPath( const Vector &goal );					///< build trivial path to goal, assuming we are already in the same area

	CountdownTimer m_repathTimer;									///< must have elapsed before bot can pathfind again

	bool ComputePathPositions( void );								///< determine actual path positions bot will move between along the path
	void SetupLadderMovement( void );
	void SetPathIndex( int index );									///< set the current index along the path
	void DrawPath( void );
	int FindOurPositionOnPath( Vector *close, bool local = false ) const;	///< compute the closest point to our current position on our path
	int FindPathPoint( float aheadRange, Vector *point, int *prevIndex = NULL );	///< compute a point a fixed distance ahead along our path.
	bool FindClosestPointOnPath( const Vector &pos, int startIndex, int endIndex, Vector *close ) const;	///< compute closest point on path to given point
	bool IsStraightLinePathWalkable( const Vector &goal ) const;	///< test for un-jumpable height change, or unrecoverable fall
	void ComputeLadderAngles( float *yaw, float *pitch );			///< computes ideal yaw/pitch for traversing the current ladder on our path

	mutable CountdownTimer m_avoidFriendTimer;						///< used to throttle how often we check for friends in our path
	mutable bool m_isFriendInTheWay;								///< true if a friend is blocking our path
	CountdownTimer m_politeTimer;									///< we'll wait for friend to move until this runs out
	bool m_isWaitingBehindFriend;									///< true if we are waiting for a friend to move

	#define ONLY_JUMP_DOWN true
	bool DiscontinuityJump( float ground, bool onlyJumpDown = false, bool mustJump = false ); ///< check if we need to jump due to height change

	enum LadderNavState
	{
		APPROACH_ASCENDING_LADDER,									///< prepare to scale a ladder
		APPROACH_DESCENDING_LADDER,									///< prepare to go down ladder 
		FACE_ASCENDING_LADDER,
		FACE_DESCENDING_LADDER,
		MOUNT_ASCENDING_LADDER,										///< move toward ladder until "on" it
		MOUNT_DESCENDING_LADDER,									///< move toward ladder until "on" it
		ASCEND_LADDER,												///< go up the ladder
		DESCEND_LADDER,												///< go down the ladder
		DISMOUNT_ASCENDING_LADDER,									///< get off of the ladder
		DISMOUNT_DESCENDING_LADDER,									///< get off of the ladder
		MOVE_TO_DESTINATION,										///< dismount ladder and move to destination area
	}
	m_pathLadderState;
	bool m_pathLadderFaceIn;										///< if true, face towards ladder, otherwise face away
	const CNavLadder *m_pathLadder;									///< the ladder we need to use to reach the next area
	bool UpdateLadderMovement( void );								///< called by UpdatePathMovement()
	NavRelativeDirType m_pathLadderDismountDir;						///< which way to dismount
	float m_pathLadderDismountTimestamp;							///< time when dismount started
	float m_pathLadderEnd;											///< if ascending, z of top, if descending z of bottom
	void ComputeLadderEndpoint( bool ascending );
	float m_pathLadderTimestamp;									///< time when we started using ladder - for timeout check

	CountdownTimer m_mustRunTimer;									///< if nonzero, bot cannot walk
	CountdownTimer m_waitTimer;										///< if nonzero, we are waiting where we are

	void UpdateTravelDistanceToAllPlayers( void );					///< periodically compute shortest path distance to each player
	CountdownTimer m_updateTravelDistanceTimer;						///< for throttling travel distance computations
	float m_playerTravelDistance[ MAX_PLAYERS ];					///< current distance from this bot to each player
	unsigned char m_travelDistancePhase;							///< a counter for optimizing when to compute travel distance

	//- game scenario mechanisms -------------------------------------------------------------------------------------
	CSGameState m_gameState;										///< our current knowledge about the state of the scenario

	byte m_hostageEscortCount;										///< the number of hostages we're currently escorting
	void UpdateHostageEscortCount( void );							///< periodic check of hostage count in case we lost some
	float m_hostageEscortCountTimestamp;

	int m_desiredTeam;												///< the team we want to be on
	bool m_hasJoined;												///< true if bot has actually joined the game

	bool m_isWaitingForHostage;
	CountdownTimer m_inhibitWaitingForHostageTimer;					///< if active, inhibits us waiting for lagging hostages
	CountdownTimer m_waitForHostageTimer;							///< stops us waiting too long

	//- listening mechanism ------------------------------------------------------------------------------------------
	Vector m_noisePosition;											///< position we last heard non-friendly noise
	float m_noiseTravelDistance;									///< the travel distance to the noise
	float m_noiseTimestamp;											///< when we heard it (can get zeroed)
	CNavArea *m_noiseArea;											///< the nav area containing the noise
	PriorityType m_noisePriority;									///< priority of currently heard noise
	bool UpdateLookAtNoise( void );									///< return true if we decided to look towards the most recent noise source
	CountdownTimer m_noiseBendTimer;								///< for throttling how often we bend our line of sight to the noise location
	Vector m_bentNoisePosition;										///< the last computed bent line of sight
	bool m_bendNoisePositionValid;

	//- "looking around" mechanism -----------------------------------------------------------------------------------
	float m_lookAroundStateTimestamp;								///< time of next state change
	float m_lookAheadAngle;											///< our desired forward look angle
	float m_forwardAngle;											///< our current forward facing direction
	float m_inhibitLookAroundTimestamp;								///< time when we can look around again

	enum LookAtSpotState
	{
		NOT_LOOKING_AT_SPOT,			///< not currently looking at a point in space
		LOOK_TOWARDS_SPOT,				///< in the process of aiming at m_lookAtSpot
		LOOK_AT_SPOT,					///< looking at m_lookAtSpot
		NUM_LOOK_AT_SPOT_STATES
	}
	m_lookAtSpotState;
	Vector m_lookAtSpot;											///< the spot we're currently looking at
	PriorityType m_lookAtSpotPriority;
	float m_lookAtSpotDuration;										///< how long we need to look at the spot
	float m_lookAtSpotTimestamp;									///< when we actually began looking at the spot
	float m_lookAtSpotAngleTolerance;								///< how exactly we must look at the spot
	bool m_lookAtSpotClearIfClose;									///< if true, the look at spot is cleared if it gets close to us
	bool m_lookAtSpotAttack;										///< if true, the look at spot should be attacked
	const char *m_lookAtDesc;										///< for debugging
	void UpdateLookAt( void );
	void UpdatePeripheralVision();									///< update enounter spot timestamps, etc
	float m_peripheralTimestamp;

	enum { MAX_APPROACH_POINTS = 16 };
	struct ApproachPoint
	{
		Vector m_pos;
		CNavArea *m_area;
	};

	ApproachPoint m_approachPoint[ MAX_APPROACH_POINTS ];
	unsigned char m_approachPointCount;
	Vector m_approachPointViewPosition;								///< the position used when computing current approachPoint set

	CBaseEntity * FindEntitiesOnPath( float distance, CPushAwayEnumerator *enumerator, bool checkStuck );

	IntervalTimer m_viewSteadyTimer;								///< how long has our view been "steady" (ie: not moving)

	bool BendLineOfSight( const Vector &eye, const Vector &target, Vector *bend, float angleLimit = 135.0f ) const;		///< "bend" our line of sight until we can see the target point. Return bend point, false if cant bend.
	bool FindApproachPointNearestPath( Vector *pos );				///< find the approach point that is nearest to our current path, ahead of us
	bool FindGrenadeTossPathTarget( Vector *pos );					///< find spot to throw grenade ahead of us and "around the corner" along our path
	enum GrenadeTossState
	{
		NOT_THROWING,				///< not yet throwing
		START_THROW,				///< lining up throw
		THROW_LINED_UP,				///< pause for a moment when on-line
		FINISH_THROW,				///< throwing
	};
	GrenadeTossState m_grenadeTossState;
	CountdownTimer m_tossGrenadeTimer;								///< timeout timer for grenade tossing
	const CNavArea *m_initialEncounterArea;							///< area where we think we will initially encounter the enemy
	void LookForGrenadeTargets( void );								///< look for grenade throw targets and throw our grenade at them
	void UpdateGrenadeThrow( void );								///< process grenade throwing
	CountdownTimer m_isAvoidingGrenade;								///< if nonzero we are in the act of avoiding a grenade


	SpotEncounter *m_spotEncounter;									///< the spots we will encounter as we move thru our current area
	float m_spotCheckTimestamp;										///< when to check next encounter spot

	/// @todo Add timestamp for each possible client to hiding spots
	enum { MAX_CHECKED_SPOTS = 64 };
	struct HidingSpotCheckInfo
	{
		HidingSpot *spot;
		float timestamp;
	}
	m_checkedHidingSpot[ MAX_CHECKED_SPOTS ];
	int m_checkedHidingSpotCount;

	//- view angle mechanism -----------------------------------------------------------------------------------------
	float m_lookPitch;												///< our desired look pitch angle
	float m_lookPitchVel;
	float m_lookYaw;												///< our desired look yaw angle
	float m_lookYawVel;

	//- aim angle mechanism -----------------------------------------------------------------------------------------
	void PickNewAimSpot();											///< set the current aim offset
	void UpdateAimPrediction( void );								///< wiggle aim based on aim error term
	Vector m_targetSpot;											///< the spot we currently wish to fire at
	Vector m_targetSpotVelocity;									///< the spot we currently wish to fire at
	Vector m_targetSpotPredicted;									///< the spot we currently wish to fire at
	QAngle m_aimError;
	QAngle m_aimGoal;
	float m_targetSpotTime;
	float m_aimFocus;												///< radius of aim focus
	float m_aimFocusInterval;										///< time interval of current offset adjustment (can be random)
	float m_aimFocusNextUpdate;										///< time of next offset adjustment

	struct PartInfo
	{
		Vector m_headPos;											///< current head position
		Vector m_gutPos;											///< current gut position
		Vector m_feetPos;											///< current feet position
		Vector m_leftSidePos;										///< current left side position
		Vector m_rightSidePos;										///< current right side position
		int m_validFrame;											///< frame of last computation (for lazy evaluation)
	};
	static PartInfo m_partInfo[ MAX_PLAYERS ];						///< part positions for each player
	void ComputePartPositions( CCSPlayer *player );					///< compute part positions from bone location

	//- attack state data --------------------------------------------------------------------------------------------
	DispositionType m_disposition;									///< how we will react to enemies
	CountdownTimer m_ignoreEnemiesTimer;							///< how long will we ignore enemies
	mutable CHandle< CCSPlayer > m_enemy;							///< our current enemy
	bool m_isEnemyVisible;											///< result of last visibility test on enemy
	unsigned char m_visibleEnemyParts;								///< which parts of the visible enemy do we see
	Vector m_lastEnemyPosition;										///< last place we saw the enemy
	float m_lastSawEnemyTimestamp;
	float m_firstSawEnemyTimestamp;
	float m_currentEnemyAcquireTimestamp;
	float m_enemyDeathTimestamp;									///< if m_enemy is dead, this is when he died
	float m_friendDeathTimestamp;									///< time since we saw a friend die
	bool m_isLastEnemyDead;											///< true if we killed or saw our last enemy die
	int m_nearbyEnemyCount;											///< max number of enemies we've seen recently
	unsigned int m_enemyPlace;										///< the location where we saw most of our enemies

	struct WatchInfo
	{
		float timestamp;											///< time we last saw this player, zero if never seen
		bool isEnemy;
	}
	m_watchInfo[ MAX_PLAYERS ];
	mutable CHandle< CCSPlayer > m_bomber;							///< points to bomber if we can see him

	int m_nearbyFriendCount;										///< number of nearby teammates
	mutable CHandle< CCSPlayer > m_closestVisibleFriend;			///< the closest friend we can see
	mutable CHandle< CCSPlayer > m_closestVisibleHumanFriend;		///< the closest human friend we can see

	IntervalTimer m_attentionInterval;								///< time between attention checks

	mutable CHandle< CCSPlayer > m_attacker;						///< last enemy that hurt us (may not be same as m_enemy)
	float m_attackedTimestamp;										///< when we were hurt by the m_attacker
	IntervalTimer m_burnedByFlamesTimer;

	int m_lastVictimID;												///< the entindex of the last victim we killed, or zero
	bool m_isAimingAtEnemy;											///< if true, we are trying to aim at our enemy
	bool m_isRapidFiring;											///< if true, RunUpkeep() will toggle our primary attack as fast as it can
	IntervalTimer m_equipTimer;										///< how long have we had our current weapon equipped
	CountdownTimer m_zoomTimer;										///< for delaying firing immediately after zoom
	bool DoEquip( CWeaponCSBase *gun );								///< equip the given item

	void ReloadCheck( void );										///< reload our weapon if we must
	void SilencerCheck( void );										///< use silencer

	float m_fireWeaponTimestamp;

	// sleeping
	bool m_bIsSleeping;

	bool m_isEnemySniperVisible;									///< do we see an enemy sniper right now
	CountdownTimer m_sawEnemySniperTimer;							///< tracking time since saw enemy sniper
	
	//- reaction time system -----------------------------------------------------------------------------------------
	enum { MAX_ENEMY_QUEUE = 20 };
	struct ReactionState
	{
		// NOTE: player position & orientation is not currently stored separately
		CHandle<CCSPlayer> player;
		bool isReloading;
		bool isProtectedByShield;
	}
	m_enemyQueue[ MAX_ENEMY_QUEUE ];								///< round-robin queue for simulating reaction times
	byte m_enemyQueueIndex;
	byte m_enemyQueueCount;
	byte m_enemyQueueAttendIndex;									///< index of the timeframe we are "conscious" of

	CCSPlayer *FindMostDangerousThreat( void );						///< return most dangerous threat in my field of view (feeds into reaction time queue)


	//- stuck detection ---------------------------------------------------------------------------------------------
	bool m_isStuck;
	float m_stuckTimestamp;											///< time when we got stuck
	Vector m_stuckSpot;												///< the location where we became stuck
	NavRelativeDirType m_wiggleDirection;
	CountdownTimer m_wiggleTimer;
	CountdownTimer m_stuckJumpTimer;								///< time for next jump when stuck
	float m_nextCleanupCheckTimestamp;

	enum { MAX_VEL_SAMPLES = 10 };	
	float m_avgVel[ MAX_VEL_SAMPLES ];
	int m_avgVelIndex;
	int m_avgVelCount;
	Vector m_lastOrigin;

	//- radio --------------------------------------------------------------------------------------------------------
	RadioType m_lastRadioCommand;									///< last radio command we recieved
	float m_lastRadioRecievedTimestamp;								///< time we recieved a radio message
	float m_lastRadioSentTimestamp;									///< time when we send a radio message
	CHandle< CCSPlayer > m_radioSubject;							///< who issued the radio message
	Vector m_radioPosition;											///< position referred to in radio message
	void RespondToRadioCommands( void );
	bool IsRadioCommand( RadioType event ) const;					///< returns true if the radio message is an order to do something

	/// new-style "voice" chatter gets voice feedback
	float m_voiceEndTimestamp;

	BotChatterInterface *m_pChatter;									///< chatter mechanism

	int ObjectCaps( void ) { return ( BaseClass::ObjectCaps() | FCAP_IMPULSE_USE ); } //allow +use

#ifdef OPT_VIS_CSGO
	//--------------------------------------------------------------------------------------------------
	// PS3 vis cache
	//--------------------------------------------------------------------------------------------------

	bool m_bVis[5];
	char m_aVisParts[5];
#endif

};


//
// Inlines
//

inline float CCSBot::GetFeetZ( void ) const
{
	return GetAbsOrigin().z;
}

inline const Vector *CCSBot::GetNoisePosition( void ) const
{
	if (m_noiseTimestamp > 0.0f)
		return &m_noisePosition;

	return NULL;
}

inline bool CCSBot::IsAwareOfEnemyDeath( void ) const
{
	if (GetEnemyDeathTimestamp() == 0.0f)
		return false;

	if (m_enemy == NULL)
		return true;

	if (!m_enemy->IsAlive() && gpGlobals->curtime - GetEnemyDeathTimestamp() > (1.0f - 0.8f * GetProfile()->GetSkill()))
		return true;

	return false;
}

inline void CCSBot::Panic( void )
{
	// we are stunned for a moment
	Surprise( RandomFloat( 0.2f, 0.3f ) );

	const float panicTime = 3.0f;
	m_panicTimer.Start( panicTime );

	const float panicRetreatRange = 300.0f;
	TryToRetreat( panicRetreatRange, 0.0f );

	PrintIfWatched( "*** PANIC ***\n" );
}

inline bool CCSBot::IsPanicking( void ) const
{
	return !m_panicTimer.IsElapsed();
}

inline void CCSBot::StopPanicking( void )
{
	m_panicTimer.Invalidate();
}

inline bool CCSBot::IsNotMoving( float minDuration ) const
{
	return (m_stillTimer.HasStarted() && m_stillTimer.GetElapsedTime() >= minDuration);
}

inline CWeaponCSBase *CCSBot::GetActiveCSWeapon( void ) const
{
	return reinterpret_cast<CWeaponCSBase *>( GetActiveWeapon() );
}

inline void CCSBot::SetLastCoopSpawnPoint( SpawnPointCoopEnemy *spawn )
{
	m_lastCoopSpawnPoint = spawn;
}

inline SpawnPointCoopEnemy *CCSBot::GetLastCoopSpawnPoint( void ) const
{
	return m_lastCoopSpawnPoint;
}

inline float CCSBot::GetCombatRange( void ) const
{ 
	return m_combatRange; 
}

inline void CCSBot::SetRogue( bool rogue )
{ 
	m_isRogue = rogue;
}

inline void CCSBot::Hurry( float duration )
{ 
	m_hurryTimer.Start( duration ); 
}

inline float CCSBot::GetSafeTime( void ) const
{
	return m_safeTime;
}

inline bool CCSBot::IsUnhealthy( void ) const
{
	return (GetHealth() <= 40);
}

inline bool CCSBot::IsAlert( void ) const
{
	return !m_alertTimer.IsElapsed();
}

inline void CCSBot::BecomeAlert( void )
{
	const float alertCooldownTime = 10.0f;
	m_alertTimer.Start( alertCooldownTime );
}

inline bool CCSBot::IsSneaking( void ) const
{
	return !m_sneakTimer.IsElapsed();
}

inline void CCSBot::Sneak( float duration )
{
	m_sneakTimer.Start( duration );
}

inline bool CCSBot::IsFollowing( void ) const
{ 
	return m_isFollowing;
}

inline CCSPlayer *CCSBot::GetFollowLeader( void ) const
{ 
	return m_leader;
}

inline float CCSBot::GetFollowDuration( void ) const
{
	return gpGlobals->curtime - m_followTimestamp;
}

inline bool CCSBot::CanAutoFollow( void ) const
{ 
	return (gpGlobals->curtime > m_allowAutoFollowTime);
}

inline void CCSBot::AimAtEnemy( void )
{ 
	m_isAimingAtEnemy = true;
}

inline void CCSBot::StopAiming( void )
{ 
	m_isAimingAtEnemy = false;
}

inline bool CCSBot::IsAimingAtEnemy( void ) const
{
	return m_isAimingAtEnemy;
}

inline float CCSBot::GetStateTimestamp( void ) const
{
	return m_stateTimestamp;
}

inline CSGameState *CCSBot::GetGameState( void )
{
	return &m_gameState;
}

inline const CSGameState *CCSBot::GetGameState( void ) const
{
	return &m_gameState;
}

inline bool CCSBot::IsAtBombsite( void )
{
	return m_bInBombZone;
}

inline void CCSBot::SetTask( TaskType task, CBaseEntity *entity )
{
	m_task = task;
	m_taskEntity = entity;

	if ( task == CCSBot::PLANT_BOMB )
	{
		// don't stop to attack - get the bomb there
		SetDisposition( CCSBot::SELF_DEFENSE );		
	}
}

inline CCSBot::TaskType CCSBot::GetTask( void ) const
{
	return m_task;
}

inline CBaseEntity *CCSBot::GetTaskEntity( void )
{
	return static_cast<CBaseEntity *>( m_taskEntity );
}

inline CCSBot::MoraleType CCSBot::GetMorale( void ) const
{
	return m_morale;
}

inline void CCSBot::Surprise( float duration )
{
	m_surpriseTimer.Start( duration );
}

inline bool CCSBot::IsSurprised( void ) const
{
	return !m_surpriseTimer.IsElapsed();
}

inline CNavArea *CCSBot::GetNoiseArea( void ) const
{
	return m_noiseArea;
}

inline void CCSBot::ForgetNoise( void )
{
	m_noiseTimestamp = 0.0f;
}

inline float CCSBot::GetNoiseRange( void ) const
{
	if (IsNoiseHeard())
		return m_noiseTravelDistance;

	return 999999999.9f;
}

inline PriorityType CCSBot::GetNoisePriority( void ) const
{ 
	return m_noisePriority;
}

inline BotChatterInterface *CCSBot::GetChatter( void )
{
	return m_pChatter;
}

inline CCSPlayer *CCSBot::GetBotEnemy( void ) const
{
	return m_enemy;
}

inline int CCSBot::GetNearbyEnemyCount( void ) const
{ 
	return MIN( GetEnemiesRemaining(), m_nearbyEnemyCount );
}

inline unsigned int CCSBot::GetEnemyPlace( void ) const
{
	return m_enemyPlace;
}

inline bool CCSBot::CanSeeBomber( void ) const
{
	return (m_bomber == NULL) ? false : true;
}

inline CCSPlayer *CCSBot::GetBomber( void ) const
{
	return m_bomber;
}

inline int CCSBot::GetNearbyFriendCount( void ) const
{
	return MIN( GetFriendsRemaining(), m_nearbyFriendCount );
}

inline CCSPlayer *CCSBot::GetClosestVisibleFriend( void ) const
{
	return m_closestVisibleFriend;
}

inline CCSPlayer *CCSBot::GetClosestVisibleHumanFriend( void ) const
{
	return m_closestVisibleHumanFriend;
}

inline float CCSBot::GetTimeSinceAttacked( void ) const
{
	return gpGlobals->curtime - m_attackedTimestamp;
}

inline float CCSBot::GetFirstSawEnemyTimestamp( void ) const
{
	return m_firstSawEnemyTimestamp;
}

inline float CCSBot::GetLastSawEnemyTimestamp( void ) const
{
	return m_lastSawEnemyTimestamp;
}

inline float CCSBot::GetTimeSinceLastSawEnemy( void ) const
{
	return gpGlobals->curtime - m_lastSawEnemyTimestamp;
}

inline float CCSBot::GetTimeSinceAcquiredCurrentEnemy( void ) const
{
	return gpGlobals->curtime - m_currentEnemyAcquireTimestamp;
}

inline float CCSBot::GetTimeSinceBurnedByFlames( void ) const
{
	return m_burnedByFlamesTimer.GetElapsedTime();
}

inline const Vector &CCSBot::GetLastKnownEnemyPosition( void ) const
{
	return m_lastEnemyPosition;
}

inline bool CCSBot::IsEnemyVisible( void ) const			
{
	return m_isEnemyVisible;
}

inline float CCSBot::GetEnemyDeathTimestamp( void ) const	
{
	return m_enemyDeathTimestamp;
}

inline int CCSBot::GetLastVictimID( void ) const
{
	return m_lastVictimID;
}

inline bool CCSBot::CanSeeSniper( void ) const
{
	return m_isEnemySniperVisible;
}

inline bool CCSBot::HasSeenSniperRecently( void ) const
{
	return !m_sawEnemySniperTimer.IsElapsed();
}

inline float CCSBot::GetTravelDistanceToPlayer( CCSPlayer *player ) const
{
	if (player == NULL)
		return -1.0f;

	if (!player->IsAlive())
		return -1.0f;

	return m_playerTravelDistance[ player->entindex() % MAX_PLAYERS ];
}

inline bool CCSBot::HasPath( void ) const
{
	return (m_pathLength) ? true : false;
}

inline void CCSBot::DestroyPath( void )		
{
	m_isStopping = false;
	m_pathLength = 0;
	m_pathLadder = NULL;
}

inline const Vector &CCSBot::GetPathEndpoint( void ) const		
{
	return m_path[ m_pathLength-1 ].pos;
}

inline const Vector &CCSBot::GetPathPosition( int index ) const
{
	return m_path[ index ].pos;
}

inline bool CCSBot::IsUsingLadder( void ) const	
{
	return (m_pathLadder) ? true : false;
}

inline void CCSBot::SetGoalEntity( CBaseEntity *entity )	
{
	m_goalEntity = entity;
}

inline CBaseEntity *CCSBot::GetGoalEntity( void )
{
	return m_goalEntity;
}

inline void CCSBot::ForceRun( float duration )
{
	Run();
	m_mustRunTimer.Start( duration );
}

inline void CCSBot::Wait( float duration )			
{
	m_waitTimer.Start( duration );
}

inline bool CCSBot::IsWaiting( void ) const		
{
	return !m_waitTimer.IsElapsed();
}

inline void CCSBot::StopWaiting( void )			
{
	m_waitTimer.Invalidate();
}

inline bool CCSBot::HasVisitedEnemySpawn( void ) const		
{
	return m_hasVisitedEnemySpawn;
}

inline const Vector &CCSBot::EyePositionConst( void ) const		
{
	return m_eyePosition;
}
	
inline void CCSBot::SetLookAngles( float yaw, float pitch )
{
	m_lookYaw = yaw;
	m_lookPitch = pitch;
}

inline void CCSBot::SetForwardAngle( float angle ) 
{
	m_forwardAngle = angle;
}

inline void CCSBot::SetLookAheadAngle( float angle ) 
{
	m_lookAheadAngle = angle;
}

inline void CCSBot::ClearLookAt( void )
{ 
	//PrintIfWatched( "ClearLookAt()\n" );
	m_lookAtSpotState = NOT_LOOKING_AT_SPOT; 
	m_lookAtDesc = NULL; 
}

inline bool CCSBot::IsLookingAtSpot( PriorityType pri ) const
{ 
	if (m_lookAtSpotState != NOT_LOOKING_AT_SPOT && m_lookAtSpotPriority >= pri)
		return true;

	return false;
}

inline bool CCSBot::IsViewMoving( float angleVelThreshold ) const
{
	if (m_lookYawVel < angleVelThreshold && m_lookYawVel > -angleVelThreshold &&
		m_lookPitchVel < angleVelThreshold && m_lookPitchVel > -angleVelThreshold)
	{
		return false;
	}

	return true;
}

inline bool CCSBot::HasViewBeenSteady( float duration ) const
{
	return (m_viewSteadyTimer.GetElapsedTime() > duration);
}

inline bool CCSBot::HasLookAtTarget( void ) const
{
	return (m_lookAtSpotState != NOT_LOOKING_AT_SPOT);
}

inline bool CCSBot::IsEnemyPartVisible( VisiblePartType part ) const
{ 
	VPROF_BUDGET( "CCSBot::IsEnemyPartVisible", VPROF_BUDGETGROUP_NPCS );

	if (!IsEnemyVisible())
		return false;

	return (m_visibleEnemyParts & part) ? true : false;
}

inline bool CCSBot::IsSignificantlyCloser( const CCSPlayer *testPlayer, const CCSPlayer *referencePlayer ) const
{
	if ( !referencePlayer )
		return true;

	if ( !testPlayer )
		return false;

	float testDist = ( GetAbsOrigin() - testPlayer->GetAbsOrigin() ).Length();
	float referenceDist = ( GetAbsOrigin() - referencePlayer->GetAbsOrigin() ).Length();

	const float significantRangeFraction = 0.7f;
	if ( testDist < referenceDist * significantRangeFraction )
		return true;

	return false;
}

inline void CCSBot::ClearApproachPoints( void )	
{
	m_approachPointCount = 0;
}

inline const CNavArea *CCSBot::GetInitialEncounterArea( void ) const
{
	return m_initialEncounterArea;
}

inline void CCSBot::SetInitialEncounterArea( const CNavArea *area )		
{
	m_initialEncounterArea = area;
}

inline bool CCSBot::IsThrowingGrenade( void ) const		
{
	return m_grenadeTossState != NOT_THROWING;
}

inline bool CCSBot::IsAvoidingGrenade( void ) const
{
	return !m_isAvoidingGrenade.IsElapsed();
}

inline void CCSBot::PrimaryAttack( void )
{

	// for now the bots only secondary attack with the revolver until I teach them to hold the fire button
	CWeaponCSBase *weapon = GetActiveCSWeapon();
	if ( weapon && weapon->IsRevolver() && CanActiveWeaponFire() )
	{
		BaseClass::SecondaryAttack();
		return;
	}

	if ( IsUsingPistol() && !CanActiveWeaponFire() )
		return;

	BaseClass::PrimaryAttack();
}

inline CCSBot::ZoomType CCSBot::GetZoomLevel( void )
{
	if (GetFOV() > 60.0f)
		return NO_ZOOM;
	if (GetFOV() > 25.0f)
		return LOW_ZOOM;
	return HIGH_ZOOM;
}

inline bool CCSBot::IsWaitingForZoom( void ) const		
{
	return !m_zoomTimer.IsElapsed();
}

inline int CCSBot::GetHostageEscortCount( void ) const		
{
	return m_hostageEscortCount;
}

inline void CCSBot::IncreaseHostageEscortCount( void )		
{
	++m_hostageEscortCount;
}

inline void CCSBot::ResetWaitForHostagePatience( void )
{
	m_isWaitingForHostage = false;
	m_inhibitWaitingForHostageTimer.Invalidate();
}


inline bool CCSBot::IsUsingVoice() const
{
	return m_voiceEndTimestamp > gpGlobals->curtime; 
}

inline bool CCSBot::IsOpeningDoor( void ) const
{
	return m_isOpeningDoor;
}


//--------------------------------------------------------------------------------------------------------------
/**
 * Return true if the given weapon is a sniper rifle
 */
inline bool IsSniperRifle( CWeaponCSBase *weapon )
{
	if (weapon == NULL)
		return false;

	return weapon->IsKindOf(WEAPONTYPE_SNIPER_RIFLE);
}


//--------------------------------------------------------------------------------------------------------------
/**
 * Functor used with NavAreaBuildPath()
 */
class PathCost
{
public:
	PathCost( CCSBot *bot, RouteType route = SAFEST_ROUTE )
	{
		m_bot = bot;
		m_route = route;

		float baseDangerFactor = CSGameRules()->IsPlayingGunGameTRBomb() ? 0.25f : 100.0f;
		m_dangerFactor = (1.0f - (0.95f * m_bot->GetProfile()->GetAggression( ))) * baseDangerFactor;
	}

	// HPE_TODO[pmf]: check that these new parameters are okay to be ignored
	float operator() ( CNavArea *area, CNavArea *fromArea, const CNavLadder *ladder, const CFuncElevator *elevator, float length )
	{
        float dangerFactor = m_dangerFactor;

		if (fromArea == NULL)
		{
			if (m_route == FASTEST_ROUTE)
				return 0.0f;

			// first area in path, cost is just danger
			return dangerFactor * area->GetDanger( m_bot->GetTeamNumber() );
		}
		else if ((fromArea->GetAttributes() & NAV_MESH_JUMP) && (area->GetAttributes() & NAV_MESH_JUMP))
		{
			// cannot actually walk in jump areas - disallow moving from jump area to jump area
			return -1.0f;
		}
		if ( area->GetAttributes() & NAV_MESH_NO_HOSTAGES && m_bot->GetHostageEscortCount() )
		{
			// if we're leading hostages, don't try to go where they can't
			return -1.0f;
		}
		else
		{
			// compute distance from previous area to this area
			float dist;
			if (ladder)
			{
				// ladders are slow to use
				const float ladderPenalty = 1.0f; // 3.0f;
				dist = ladderPenalty * ladder->m_length;

				// if we are currently escorting hostages, avoid ladders (hostages are confused by them)
				//if (m_bot->GetHostageEscortCount())
				//	dist *= 100.0f;
			}
			else
			{
				dist = (area->GetCenter() - fromArea->GetCenter()).Length();
			}

			// compute distance travelled along path so far
			float cost = dist + fromArea->GetCostSoFar();

			// add cost of "jump down" pain unless we're jumping into water
			if (!area->IsUnderwater() && area->IsConnected( fromArea, NUM_DIRECTIONS ) == false)
			{
				// this is a "jump down" (one way drop) transition - estimate damage we will take to traverse it
				float fallDistance = -fromArea->ComputeGroundHeightChange( area );

				// if it's a drop-down ladder, estimate height from the bottom of the ladder to the lower area
				if ( ladder && ladder->m_bottom.z < fromArea->GetCenter().z && ladder->m_bottom.z > area->GetCenter().z )
				{
					fallDistance = ladder->m_bottom.z - area->GetCenter().z;
				}

				float fallDamage = m_bot->GetApproximateFallDamage( fallDistance );

				if (fallDamage > 0.0f)
				{
					// if the fall would kill us, don't use it
					const float deathFallMargin = 10.0f;
					if (fallDamage + deathFallMargin >= m_bot->GetHealth())
						return -1.0f;

					// if we need to get there in a hurry, ignore minor pain
					const float painTolerance = 15.0f * m_bot->GetProfile()->GetAggression() + 10.0f;
					if (m_route != FASTEST_ROUTE || fallDamage > painTolerance)
					{
						// cost is proportional to how much it hurts when we fall
						// 10 points - not a big deal, 50 points - ouch!
						cost += 100.0f * fallDamage * fallDamage;
					}
				}
			}

			// if this is a "crouch" or "walk" area, add penalty
			if (area->GetAttributes() & (NAV_MESH_CROUCH | NAV_MESH_WALK))
			{
				// these areas are very slow to move through
				float penalty = (m_route == FASTEST_ROUTE) ? 20.0f : 5.0f;

				// avoid crouch areas if we are rescuing hostages 
				if ((area->GetAttributes() & NAV_MESH_CROUCH) && m_bot->GetHostageEscortCount())
				{
					penalty *= 3.0f;
				}

				cost += penalty * dist;
			}

			// if this is a "jump" area, add penalty
			if (area->GetAttributes() & NAV_MESH_JUMP)
			{
				// jumping can slow you down
				//const float jumpPenalty = (m_route == FASTEST_ROUTE) ? 100.0f : 0.5f;
				const float jumpPenalty = 1.0f;
				cost += jumpPenalty * dist;
			}

			// if this is an area to avoid, add penalty
			if ( area->IsDamaging() )
			{
				const float damagingPenalty = 100.0f;
				cost += damagingPenalty * dist;
			}

			// if this is an area to avoid, add penalty
			if (area->GetAttributes() & NAV_MESH_AVOID)
			{
				const float avoidPenalty = 20.0f;
				cost += avoidPenalty * dist;
			}

			if (m_route == SAFEST_ROUTE)
			{
				// add in the danger of this path - danger is per unit length traveled
				cost += dist + ( dist * dangerFactor * area->GetDanger( m_bot->GetTeamNumber() ) );
			}

			// this term causes the same bot to choose different routes over time,
			// but keep the same route for a period in case of repaths
			int timeMod = (int)( gpGlobals->curtime / 10.0f ) + 1;
			
			int uniqueID = ((size_t)area) >> 7; // areas are 128-byte aligned, so shift address over
			// We just need a unique number approximately between 1 and 300, so take the mod 293 because it's prime
			unsigned int nRandomCost = ( unsigned int )( m_bot->entindex() * uniqueID * timeMod ) % 293;
			cost += 1.0f + (float)nRandomCost;

			if (!m_bot->IsAttacking())
			{
				// add in cost of teammates in the way

				// approximate density of teammates based on area
				float size = (area->GetSizeX() + area->GetSizeY())/2.0f;

				// degenerate check
				if (size >= 1.0f)
				{
					// cost is proportional to the density of teammates in this area
					const float costPerFriendPerUnit = 50000.0f;
					cost += costPerFriendPerUnit * (float)area->GetPlayerCount( m_bot->GetTeamNumber() ) / size;
				}
			}

			return cost;
		}
	}

private:
	CCSBot *	m_bot;
	RouteType	m_route;
	float		m_dangerFactor;
};

inline CCSBot *ToCSBot( CBaseEntity *pEntity )
{
	CCSPlayer* pPlayer = ToCSPlayer( pEntity );
	if ( !pPlayer || !pPlayer->IsBot() )
		return NULL;

	return dynamic_cast<CCSBot*>( pPlayer );
}


//--------------------------------------------------------------------------------------------------------------
//
// Prototypes
//
extern int GetBotFollowCount( CCSPlayer *leader );
extern const Vector *FindNearbyRetreatSpot( CCSBot *me, float maxRange = 250.0f );
extern const HidingSpot *FindInitialEncounterSpot( CBaseEntity *me, const Vector &searchOrigin, float enemyArriveTime, float maxRange, bool isSniper );


#endif	// _CS_BOT_H_