Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1053 lines
31 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. //
  8. // Author: Michael S. Booth ([email protected]), 2003
  9. //
  10. // NOTE: The CS Bot code uses Doxygen-style comments. If you run Doxygen over this code, it will
  11. // auto-generate documentation. Visit www.doxygen.org to download the system for free.
  12. //
  13. #ifndef BOT_H
  14. #define BOT_H
  15. #include "cbase.h"
  16. #include "in_buttons.h"
  17. #include "movehelper_server.h"
  18. #include "mathlib/mathlib.h"
  19. #include "bot_manager.h"
  20. #include "bot_util.h"
  21. #include "bot_constants.h"
  22. #include "nav_mesh.h"
  23. #include "gameinterface.h"
  24. #include "weapon_csbase.h"
  25. #include "shared_util.h"
  26. #include "util.h"
  27. #include "shareddefs.h"
  28. #include "tier0/vprof.h"
  29. class BotProfile;
  30. extern bool AreBotsAllowed();
  31. //--------------------------------------------------------------------------------------------------------
  32. // BOTPORT: Convert everything to assume "origin" means "feet"
  33. //
  34. // Utility function to get "centroid" or center of player or player equivalent
  35. //
  36. inline Vector GetCentroid( const CBaseEntity *player )
  37. {
  38. Vector centroid = player->GetAbsOrigin();
  39. const Vector &mins = player->WorldAlignMins();
  40. const Vector &maxs = player->WorldAlignMaxs();
  41. centroid.z += (maxs.z - mins.z)/2.0f;
  42. //centroid.z += HalfHumanHeight;
  43. return centroid;
  44. }
  45. CBasePlayer* ClientPutInServerOverride_Bot( edict_t *pEdict, const char *playername );
  46. /// @todo Remove this nasty hack - CreateFakeClient() calls CBot::Spawn, which needs the profile
  47. extern const BotProfile *g_botInitProfile;
  48. extern int g_botInitTeam;
  49. extern int g_nClientPutInServerOverrides;
  50. //--------------------------------------------------------------------------------------------------------
  51. template < class T > T * CreateBot( const BotProfile *profile, int team )
  52. {
  53. if ( !AreBotsAllowed() )
  54. return NULL;
  55. if ( UTIL_ClientsInGame() >= gpGlobals->maxClients )
  56. {
  57. CONSOLE_ECHO( "Unable to create bot: Server is full (%d/%d clients).\n", UTIL_ClientsInGame(), gpGlobals->maxClients );
  58. return NULL;
  59. }
  60. // set the bot's name
  61. char botName[64];
  62. UTIL_ConstructBotNetName( botName, 64, profile );
  63. // This is a backdoor we use so when the engine calls ClientPutInServer (from CreateFakeClient),
  64. // expecting the game to make an entity for the fake client, we can make our special bot class
  65. // instead of a CCSPlayer.
  66. g_nClientPutInServerOverrides = 0;
  67. ClientPutInServerOverride( ClientPutInServerOverride_Bot );
  68. // get an edict for the bot
  69. // NOTE: This will ultimately invoke CBot::Spawn(), so set the profile now
  70. g_botInitProfile = profile;
  71. g_botInitTeam = team;
  72. edict_t *botEdict = engine->CreateFakeClient( botName );
  73. ClientPutInServerOverride( NULL );
  74. Assert( g_nClientPutInServerOverrides == 1 );
  75. if ( botEdict == NULL )
  76. {
  77. CONSOLE_ECHO( "Unable to create bot: CreateFakeClient() returned null.\n" );
  78. return NULL;
  79. }
  80. // create an instance of the bot's class and bind it to the edict
  81. T *bot = dynamic_cast< T * >( CBaseEntity::Instance( botEdict ) );
  82. if ( bot == NULL )
  83. {
  84. Assert( false );
  85. Error( "Could not allocate and bind entity to bot edict.\n" );
  86. return NULL;
  87. }
  88. bot->ClearFlags();
  89. bot->AddFlag( FL_CLIENT | FL_FAKECLIENT );
  90. return bot;
  91. }
  92. //----------------------------------------------------------------------------------------------------------------
  93. //----------------------------------------------------------------------------------------------------------------
  94. /**
  95. * The base bot class from which bots for specific games are derived
  96. * A template is needed here because the CBot class must be derived from CBasePlayer,
  97. * but also may need to be derived from a more specific player class, such as CCSPlayer
  98. */
  99. template < class PlayerType >
  100. class CBot : public PlayerType
  101. {
  102. public:
  103. DECLARE_CLASS( CBot, PlayerType );
  104. CBot( void ); ///< constructor initializes all values to zero
  105. virtual ~CBot();
  106. virtual bool Initialize( const BotProfile *profile, int team ); ///< (EXTEND) prepare bot for action
  107. unsigned int GetID( void ) const { return m_id; } ///< return bot's unique ID
  108. virtual bool IsBot( void ) const { return true; }
  109. virtual bool IsNetClient( void ) const { return false; } // Bots should return FALSE for this, they can't receive NET messages
  110. virtual void Spawn( void ); ///< (EXTEND) spawn the bot into the game
  111. virtual void Upkeep( void ) = 0; ///< lightweight maintenance, invoked frequently
  112. virtual void Update( void ) = 0; ///< heavyweight algorithms, invoked less often
  113. virtual void Run( void );
  114. virtual void Walk( void );
  115. virtual bool IsRunning( void ) const { return m_isRunning; }
  116. virtual void Crouch( void );
  117. virtual void StandUp( void );
  118. bool IsCrouching( void ) const { return m_isCrouching; }
  119. void PushPostureContext( void ); ///< push the current posture context onto the top of the stack
  120. void PopPostureContext( void ); ///< restore the posture context to the next context on the stack
  121. virtual void MoveForward( void );
  122. virtual void MoveBackward( void );
  123. virtual void StrafeLeft( void );
  124. virtual void StrafeRight( void );
  125. #define MUST_JUMP true
  126. virtual bool Jump( bool mustJump = false ); ///< returns true if jump was started
  127. bool IsJumping( void ); ///< returns true if we are in the midst of a jump
  128. float GetJumpTimestamp( void ) const { return m_jumpTimestamp; } ///< return time last jump began
  129. virtual void ClearMovement( void ); ///< zero any MoveForward(), Jump(), etc
  130. const Vector &GetViewVector( void ); ///< return the actual view direction
  131. //------------------------------------------------------------------------------------
  132. // Weapon interface
  133. //
  134. virtual void UseEnvironment( void );
  135. virtual void PrimaryAttack( void );
  136. virtual void ClearPrimaryAttack( void );
  137. virtual void TogglePrimaryAttack( void );
  138. virtual void SecondaryAttack( void );
  139. virtual void Reload( void );
  140. float GetActiveWeaponAmmoRatio( void ) const; ///< returns ratio of ammo left to max ammo (1 = full clip, 0 = empty)
  141. bool IsActiveWeaponClipEmpty( void ) const; ///< return true if active weapon has any empty clip
  142. bool IsActiveWeaponOutOfAmmo( void ) const; ///< return true if active weapon has no ammo at all
  143. bool IsActiveWeaponRecoilHigh( void ) const; ///< return true if active weapon's bullet spray has become large and inaccurate
  144. bool IsUsingScope( void ); ///< return true if looking thru weapon's scope
  145. //------------------------------------------------------------------------------------
  146. // Event hooks
  147. //
  148. /// invoked when injured by something (EXTEND) - returns the amount of damage inflicted
  149. virtual int OnTakeDamage( const CTakeDamageInfo &info )
  150. {
  151. return PlayerType::OnTakeDamage( info );
  152. }
  153. /// invoked when killed (EXTEND)
  154. virtual void Event_Killed( const CTakeDamageInfo &info )
  155. {
  156. PlayerType::Event_Killed( info );
  157. }
  158. bool IsEnemy( CBaseEntity *ent ) const; ///< returns TRUE if given entity is our enemy
  159. int GetEnemiesRemaining( void ) const; ///< return number of enemies left alive
  160. int GetFriendsRemaining( void ) const; ///< return number of friends left alive
  161. bool IsPlayerFacingMe( CBasePlayer *enemy ) const; ///< return true if player is facing towards us
  162. bool IsPlayerLookingAtMe( CBasePlayer *enemy, float cosTolerance = 0.9f ) const; ///< returns true if other player is pointing right at us
  163. bool IsLookingAtPosition( const Vector &pos, float angleTolerance = 20.0f ) const; ///< returns true if looking (roughly) at given position
  164. bool IsLocalPlayerWatchingMe( void ) const; ///< return true if local player is observing this bot
  165. void PrintIfWatched( PRINTF_FORMAT_STRING const char *format, ... ) const; ///< output message to console if we are being watched by the local player
  166. virtual void UpdatePlayer( void ); ///< update player physics, movement, weapon firing commands, etc
  167. virtual void BuildUserCmd( CUserCmd& cmd, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse );
  168. virtual void SetModel( const char *modelName );
  169. int Save( CSave &save ) const { return 0; }
  170. int Restore( CRestore &restore ) const { return 0; }
  171. virtual void Think( void ) { }
  172. const BotProfile *GetProfile( void ) const { return m_profile; } ///< return our personality profile
  173. virtual bool ClientCommand( const CCommand &args ); ///< Do a "client command" - useful for invoking menu choices, etc.
  174. virtual int Cmd_Argc( void ); ///< Returns the number of tokens in the command string
  175. virtual char *Cmd_Argv( int argc ); ///< Retrieves a specified token
  176. private:
  177. CUtlVector< char * > m_args;
  178. protected:
  179. const BotProfile *m_profile; ///< the "personality" profile of this bot
  180. private:
  181. friend class CBotManager;
  182. unsigned int m_id; ///< unique bot ID
  183. CUserCmd m_userCmd;
  184. bool m_isRunning; ///< run/walk mode
  185. bool m_isCrouching; ///< true if crouching (ducking)
  186. float m_forwardSpeed;
  187. float m_strafeSpeed;
  188. float m_verticalSpeed;
  189. int m_buttonFlags; ///< bitfield of movement buttons
  190. float m_jumpTimestamp; ///< time when we last began a jump
  191. Vector m_viewForward; ///< forward view direction (only valid when GetViewVector() is used)
  192. /// the PostureContext represents the current settings of walking and crouching
  193. struct PostureContext
  194. {
  195. bool isRunning;
  196. bool isCrouching;
  197. };
  198. enum { MAX_POSTURE_STACK = 8 };
  199. PostureContext m_postureStack[ MAX_POSTURE_STACK ];
  200. int m_postureStackIndex; ///< index of top of stack
  201. void ResetCommand( void );
  202. //byte ThrottledMsec( void ) const;
  203. protected:
  204. virtual float GetMoveSpeed( void ); ///< returns current movement speed (for walk/run)
  205. };
  206. //-----------------------------------------------------------------------------------------------------------
  207. //-----------------------------------------------------------------------------------------------------------
  208. //
  209. // Inlines
  210. //
  211. //--------------------------------------------------------------------------------------------------------------
  212. template < class T >
  213. inline void CBot<T>::SetModel( const char *modelName )
  214. {
  215. BaseClass::SetModel( modelName );
  216. }
  217. //-----------------------------------------------------------------------------------------------------------
  218. template < class T >
  219. inline float CBot<T>::GetMoveSpeed( void )
  220. {
  221. return this->MaxSpeed();
  222. }
  223. //-----------------------------------------------------------------------------------------------------------
  224. template < class T >
  225. inline void CBot<T>::Run( void )
  226. {
  227. m_isRunning = true;
  228. }
  229. //-----------------------------------------------------------------------------------------------------------
  230. template < class T >
  231. inline void CBot<T>::Walk( void )
  232. {
  233. m_isRunning = false;
  234. }
  235. //-----------------------------------------------------------------------------------------------------------
  236. template < class T >
  237. inline bool CBot<T>::IsActiveWeaponRecoilHigh( void ) const
  238. {
  239. const QAngle &angles = const_cast< CBot<T> * >( this )->GetPunchAngle();
  240. const float highRecoil = -1.5f;
  241. return (angles.x < highRecoil);
  242. }
  243. //-----------------------------------------------------------------------------------------------------------
  244. template < class T >
  245. inline void CBot<T>::PushPostureContext( void )
  246. {
  247. if (m_postureStackIndex == MAX_POSTURE_STACK)
  248. {
  249. PrintIfWatched( "PushPostureContext() overflow error!\n" );
  250. return;
  251. }
  252. m_postureStack[ m_postureStackIndex ].isRunning = m_isRunning;
  253. m_postureStack[ m_postureStackIndex ].isCrouching = m_isCrouching;
  254. ++m_postureStackIndex;
  255. }
  256. //-----------------------------------------------------------------------------------------------------------
  257. template < class T >
  258. inline void CBot<T>::PopPostureContext( void )
  259. {
  260. if (m_postureStackIndex == 0)
  261. {
  262. PrintIfWatched( "PopPostureContext() underflow error!\n" );
  263. m_isRunning = true;
  264. m_isCrouching = false;
  265. return;
  266. }
  267. --m_postureStackIndex;
  268. m_isRunning = m_postureStack[ m_postureStackIndex ].isRunning;
  269. m_isCrouching = m_postureStack[ m_postureStackIndex ].isCrouching;
  270. }
  271. //-----------------------------------------------------------------------------------------------------------
  272. template < class T >
  273. inline bool CBot<T>::IsPlayerFacingMe( CBasePlayer *other ) const
  274. {
  275. Vector toOther = other->GetAbsOrigin() - this->GetAbsOrigin();
  276. Vector otherForward;
  277. AngleVectors( other->EyeAngles() + other->GetPunchAngle(), &otherForward );
  278. if (DotProduct( otherForward, toOther ) < 0.0f)
  279. return true;
  280. return false;
  281. }
  282. //-----------------------------------------------------------------------------------------------------------
  283. template < class T >
  284. inline bool CBot<T>::IsPlayerLookingAtMe( CBasePlayer *other, float cosTolerance ) const
  285. {
  286. Vector toOther = other->GetAbsOrigin() - this->GetAbsOrigin();
  287. toOther.NormalizeInPlace();
  288. Vector otherForward;
  289. AngleVectors( other->EyeAngles() + other->GetPunchAngle(), &otherForward );
  290. // other player must be pointing nearly right at us to be "looking at" us
  291. if (DotProduct( otherForward, toOther ) < -cosTolerance)
  292. return true;
  293. return false;
  294. }
  295. //-----------------------------------------------------------------------------------------------------------
  296. template < class T >
  297. inline const Vector &CBot<T>::GetViewVector( void )
  298. {
  299. AngleVectors( this->EyeAngles() + this->GetPunchAngle(), &m_viewForward );
  300. return m_viewForward;
  301. }
  302. //-----------------------------------------------------------------------------------------------------------
  303. template < class T >
  304. inline bool CBot<T>::IsLookingAtPosition( const Vector &pos, float angleTolerance ) const
  305. {
  306. // forced to do this since many methods in CBaseEntity are not const, but should be
  307. CBot< T > *me = const_cast< CBot< T > * >( this );
  308. Vector to = pos - me->EyePosition();
  309. QAngle idealAngles;
  310. VectorAngles( to, idealAngles );
  311. QAngle viewAngles = me->EyeAngles();
  312. float deltaYaw = AngleNormalize( idealAngles.y - viewAngles.y );
  313. float deltaPitch = AngleNormalize( idealAngles.x - viewAngles.x );
  314. if (fabs( deltaYaw ) < angleTolerance && abs( deltaPitch ) < angleTolerance)
  315. return true;
  316. return false;
  317. }
  318. //--------------------------------------------------------------------------------------------------------------
  319. template < class PlayerType >
  320. inline CBot< PlayerType >::CBot( void )
  321. {
  322. // the profile will be attached after this instance is constructed
  323. m_profile = NULL;
  324. // assign this bot a unique ID
  325. static unsigned int nextID = 1;
  326. // wraparound (highly unlikely)
  327. if (nextID == 0)
  328. ++nextID;
  329. m_id = nextID;
  330. ++nextID;
  331. m_postureStackIndex = 0;
  332. }
  333. //--------------------------------------------------------------------------------------------------------------
  334. template < class PlayerType >
  335. inline CBot< PlayerType >::~CBot( void )
  336. {
  337. }
  338. //--------------------------------------------------------------------------------------------------------------
  339. /**
  340. * Prepare bot for action
  341. */
  342. template < class PlayerType >
  343. inline bool CBot< PlayerType >::Initialize( const BotProfile *profile, int team )
  344. {
  345. m_profile = profile;
  346. return true;
  347. }
  348. //--------------------------------------------------------------------------------------------------------------
  349. template < class PlayerType >
  350. inline void CBot< PlayerType >::Spawn( void )
  351. {
  352. // initialize the bot (thus setting its profile)
  353. if (m_profile == NULL)
  354. Initialize( g_botInitProfile, g_botInitTeam );
  355. // let the base class set some things up
  356. PlayerType::Spawn();
  357. // Make sure everyone knows we are a bot
  358. this->AddFlag( FL_CLIENT | FL_FAKECLIENT );
  359. // Bots use their own thinking mechanism
  360. this->SetThink( NULL );
  361. m_isRunning = true;
  362. m_isCrouching = false;
  363. m_postureStackIndex = 0;
  364. m_jumpTimestamp = 0.0f;
  365. // Command interface variable initialization
  366. ResetCommand();
  367. }
  368. /*
  369. //--------------------------------------------------------------------------------------------------------------
  370. template < class PlayerType >
  371. inline void CBot< PlayerType >::BotThink( void )
  372. {
  373. float g_flBotFullThinkInterval = 1.0 / 15.0; // full AI at lower frequency (was 10 in GoldSrc)
  374. Upkeep();
  375. if (gpGlobals->curtime >= m_flNextFullBotThink)
  376. {
  377. m_flNextFullBotThink = gpGlobals->curtime + g_flBotFullThinkInterval;
  378. ResetCommand();
  379. Update();
  380. }
  381. UpdatePlayer();
  382. }
  383. */
  384. //--------------------------------------------------------------------------------------------------------------
  385. template < class PlayerType >
  386. inline void CBot< PlayerType >::MoveForward( void )
  387. {
  388. m_forwardSpeed = GetMoveSpeed();
  389. SETBITS( m_buttonFlags, IN_FORWARD );
  390. // make mutually exclusive
  391. CLEARBITS( m_buttonFlags, IN_BACK );
  392. }
  393. //--------------------------------------------------------------------------------------------------------------
  394. template < class PlayerType >
  395. inline void CBot< PlayerType >::MoveBackward( void )
  396. {
  397. m_forwardSpeed = -GetMoveSpeed();
  398. SETBITS( m_buttonFlags, IN_BACK );
  399. // make mutually exclusive
  400. CLEARBITS( m_buttonFlags, IN_FORWARD );
  401. }
  402. //--------------------------------------------------------------------------------------------------------------
  403. template < class PlayerType >
  404. inline void CBot< PlayerType >::StrafeLeft( void )
  405. {
  406. m_strafeSpeed = -GetMoveSpeed();
  407. SETBITS( m_buttonFlags, IN_MOVELEFT );
  408. // make mutually exclusive
  409. CLEARBITS( m_buttonFlags, IN_MOVERIGHT );
  410. }
  411. //--------------------------------------------------------------------------------------------------------------
  412. template < class PlayerType >
  413. inline void CBot< PlayerType >::StrafeRight( void )
  414. {
  415. m_strafeSpeed = GetMoveSpeed();
  416. SETBITS( m_buttonFlags, IN_MOVERIGHT );
  417. // make mutually exclusive
  418. CLEARBITS( m_buttonFlags, IN_MOVELEFT );
  419. }
  420. //--------------------------------------------------------------------------------------------------------------
  421. template < class PlayerType >
  422. inline bool CBot< PlayerType >::Jump( bool mustJump )
  423. {
  424. if (IsJumping() || IsCrouching())
  425. return false;
  426. if (!mustJump)
  427. {
  428. const float minJumpInterval = 0.9f; // 1.5f;
  429. if (gpGlobals->curtime - m_jumpTimestamp < minJumpInterval)
  430. return false;
  431. }
  432. // still need sanity check for jumping frequency
  433. const float sanityInterval = 0.3f;
  434. if (gpGlobals->curtime - m_jumpTimestamp < sanityInterval)
  435. return false;
  436. // jump
  437. SETBITS( m_buttonFlags, IN_JUMP );
  438. m_jumpTimestamp = gpGlobals->curtime;
  439. return true;
  440. }
  441. //--------------------------------------------------------------------------------------------------------------
  442. /**
  443. * Zero any MoveForward(), Jump(), etc
  444. */
  445. template < class PlayerType >
  446. void CBot< PlayerType >::ClearMovement( void )
  447. {
  448. m_forwardSpeed = 0.0;
  449. m_strafeSpeed = 0.0;
  450. m_verticalSpeed = 100.0; // stay at the top of water, so we don't drown. TODO: swim logic
  451. m_buttonFlags &= ~(IN_FORWARD | IN_BACK | IN_LEFT | IN_RIGHT | IN_JUMP);
  452. }
  453. //--------------------------------------------------------------------------------------------------------------
  454. /**
  455. * Returns true if we are in the midst of a jump
  456. */
  457. template < class PlayerType >
  458. inline bool CBot< PlayerType >::IsJumping( void )
  459. {
  460. // if long time after last jump, we can't be jumping
  461. if (gpGlobals->curtime - m_jumpTimestamp > 3.0f)
  462. return false;
  463. // if we just jumped, we're still jumping
  464. if (gpGlobals->curtime - m_jumpTimestamp < 0.9f) // 1.0f
  465. return true;
  466. // a little after our jump, we're jumping until we hit the ground
  467. if (FBitSet( this->GetFlags(), FL_ONGROUND ))
  468. return false;
  469. return true;
  470. }
  471. //--------------------------------------------------------------------------------------------------------------
  472. template < class PlayerType >
  473. inline void CBot< PlayerType >::Crouch( void )
  474. {
  475. m_isCrouching = true;
  476. }
  477. //--------------------------------------------------------------------------------------------------------------
  478. template < class PlayerType >
  479. inline void CBot< PlayerType >::StandUp( void )
  480. {
  481. m_isCrouching = false;
  482. }
  483. //--------------------------------------------------------------------------------------------------------------
  484. template < class PlayerType >
  485. inline void CBot< PlayerType >::UseEnvironment( void )
  486. {
  487. SETBITS( m_buttonFlags, IN_USE );
  488. }
  489. //--------------------------------------------------------------------------------------------------------------
  490. template < class PlayerType >
  491. inline void CBot< PlayerType >::PrimaryAttack( void )
  492. {
  493. SETBITS( m_buttonFlags, IN_ATTACK );
  494. }
  495. //--------------------------------------------------------------------------------------------------------------
  496. template < class PlayerType >
  497. inline void CBot< PlayerType >::ClearPrimaryAttack( void )
  498. {
  499. CLEARBITS( m_buttonFlags, IN_ATTACK );
  500. }
  501. //--------------------------------------------------------------------------------------------------------------
  502. template < class PlayerType >
  503. inline void CBot< PlayerType >::TogglePrimaryAttack( void )
  504. {
  505. if (FBitSet( m_buttonFlags, IN_ATTACK ))
  506. {
  507. CLEARBITS( m_buttonFlags, IN_ATTACK );
  508. }
  509. else
  510. {
  511. SETBITS( m_buttonFlags, IN_ATTACK );
  512. }
  513. }
  514. //--------------------------------------------------------------------------------------------------------------
  515. template < class PlayerType >
  516. inline void CBot< PlayerType >::SecondaryAttack( void )
  517. {
  518. SETBITS( m_buttonFlags, IN_ATTACK2 );
  519. }
  520. //--------------------------------------------------------------------------------------------------------------
  521. template < class PlayerType >
  522. inline void CBot< PlayerType >::Reload( void )
  523. {
  524. SETBITS( m_buttonFlags, IN_RELOAD );
  525. }
  526. //--------------------------------------------------------------------------------------------------------------
  527. /**
  528. * Returns ratio of ammo left to max ammo (1 = full clip, 0 = empty)
  529. */
  530. template < class PlayerType >
  531. inline float CBot< PlayerType >::GetActiveWeaponAmmoRatio( void ) const
  532. {
  533. CWeaponCSBase *weapon = this->GetActiveCSWeapon();
  534. if (weapon == NULL)
  535. return 0.0f;
  536. // weapons with no ammo are always full
  537. if (weapon->Clip1() < 0)
  538. return 1.0f;
  539. return (float)weapon->Clip1() / (float)weapon->GetMaxClip1();
  540. }
  541. //--------------------------------------------------------------------------------------------------------------
  542. /**
  543. * Return true if active weapon has an empty clip
  544. */
  545. template < class PlayerType >
  546. inline bool CBot< PlayerType >::IsActiveWeaponClipEmpty( void ) const
  547. {
  548. CWeaponCSBase *gun = this->GetActiveCSWeapon();
  549. if (gun && gun->Clip1() == 0)
  550. return true;
  551. return false;
  552. }
  553. //--------------------------------------------------------------------------------------------------------------
  554. /**
  555. * Return true if active weapon has no ammo at all
  556. */
  557. template < class PlayerType >
  558. inline bool CBot< PlayerType >::IsActiveWeaponOutOfAmmo( void ) const
  559. {
  560. CWeaponCSBase *weapon = this->GetActiveCSWeapon();
  561. if (weapon == NULL)
  562. return true;
  563. return !weapon->HasAnyAmmo();
  564. }
  565. //--------------------------------------------------------------------------------------------------------------
  566. /**
  567. * Return true if looking thru weapon's scope
  568. */
  569. template < class PlayerType >
  570. inline bool CBot< PlayerType >::IsUsingScope( void )
  571. {
  572. // if our field of view is less than 90, we're looking thru a scope (maybe only true for CS...)
  573. if (this->GetFOV() < this->GetDefaultFOV())
  574. return true;
  575. return false;
  576. }
  577. //--------------------------------------------------------------------------------------------------------------
  578. /**
  579. * Fill in a CUserCmd with our data
  580. */
  581. template < class PlayerType >
  582. inline void CBot< PlayerType >::BuildUserCmd( CUserCmd& cmd, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse )
  583. {
  584. Q_memset( &cmd, 0, sizeof( cmd ) );
  585. cmd.command_number = gpGlobals->tickcount;
  586. cmd.forwardmove = forwardmove;
  587. cmd.sidemove = sidemove;
  588. cmd.upmove = upmove;
  589. cmd.buttons = buttons;
  590. cmd.impulse = impulse;
  591. VectorCopy( viewangles, cmd.viewangles );
  592. cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
  593. }
  594. //--------------------------------------------------------------------------------------------------------------
  595. /**
  596. * Update player physics, movement, weapon firing commands, etc
  597. */
  598. template < class PlayerType >
  599. inline void CBot< PlayerType >::UpdatePlayer( void )
  600. {
  601. if (m_isCrouching)
  602. {
  603. SETBITS( m_buttonFlags, IN_DUCK );
  604. }
  605. else if (!m_isRunning)
  606. {
  607. SETBITS( m_buttonFlags, IN_SPEED );
  608. }
  609. if ( this->IsEFlagSet(EFL_BOT_FROZEN) )
  610. {
  611. m_buttonFlags = 0; // Freeze.
  612. m_forwardSpeed = 0;
  613. m_strafeSpeed = 0;
  614. m_verticalSpeed = 0;
  615. }
  616. // Fill in a CUserCmd with our data
  617. this->BuildUserCmd( m_userCmd, this->EyeAngles(), m_forwardSpeed, m_strafeSpeed, m_verticalSpeed, m_buttonFlags, 0 );
  618. // Save off the CUserCmd to execute later
  619. this->ProcessUsercmds( &m_userCmd, 1, 1, 0, false );
  620. }
  621. //--------------------------------------------------------------------------------------------------------------
  622. template < class PlayerType >
  623. inline void CBot< PlayerType >::ResetCommand( void )
  624. {
  625. m_forwardSpeed = 0.0;
  626. m_strafeSpeed = 0.0;
  627. m_verticalSpeed = 100.0; // stay at the top of water, so we don't drown. TODO: swim logic
  628. m_buttonFlags = 0;
  629. }
  630. //--------------------------------------------------------------------------------------------------------------
  631. /*
  632. template < class PlayerType >
  633. inline byte CBot< PlayerType >::ThrottledMsec( void ) const
  634. {
  635. int iNewMsec;
  636. // Estimate Msec to use for this command based on time passed from the previous command
  637. iNewMsec = (int)( (gpGlobals->curtime - m_flPreviousCommandTime) * 1000 );
  638. if (iNewMsec > 255) // Doh, bots are going to be slower than they should if this happens.
  639. iNewMsec = 255; // Upgrade that CPU or use less bots!
  640. return (byte)iNewMsec;
  641. }
  642. */
  643. //--------------------------------------------------------------------------------------------------------------
  644. /**
  645. * Do a "client command" - useful for invoking menu choices, etc.
  646. */
  647. template < class PlayerType >
  648. inline bool CBot< PlayerType >::ClientCommand( const CCommand &args )
  649. {
  650. // Remove old args
  651. int i;
  652. for ( i=0; i<m_args.Count(); ++i )
  653. {
  654. delete[] m_args[i];
  655. }
  656. m_args.RemoveAll();
  657. // parse individual args
  658. const char *cmd = args.GetCommandString();
  659. while (1)
  660. {
  661. // skip whitespace up to a /n
  662. while (*cmd && *cmd <= ' ' && *cmd != '\n')
  663. {
  664. cmd++;
  665. }
  666. if (*cmd == '\n')
  667. { // a newline seperates commands in the buffer
  668. cmd++;
  669. break;
  670. }
  671. if (!*cmd)
  672. break;
  673. cmd = SharedParse (cmd);
  674. if (!cmd)
  675. break;
  676. m_args.AddToTail( CloneString( SharedGetToken() ) );
  677. }
  678. // and pass to the base class
  679. return PlayerType::ClientCommand( args );
  680. }
  681. //--------------------------------------------------------------------------------------------------------------
  682. /**
  683. * Returns the number of tokens in the command string
  684. */
  685. template < class PlayerType >
  686. inline int CBot< PlayerType >::Cmd_Argc()
  687. {
  688. return m_args.Count();
  689. }
  690. //--------------------------------------------------------------------------------------------------------------
  691. /**
  692. * Retrieves a specified token
  693. */
  694. template < class PlayerType >
  695. inline char * CBot< PlayerType >::Cmd_Argv( int argc )
  696. {
  697. if ( argc < 0 || argc >= m_args.Count() )
  698. return NULL;
  699. return m_args[argc];
  700. }
  701. //--------------------------------------------------------------------------------------------------------------
  702. /**
  703. * Returns TRUE if given entity is our enemy
  704. */
  705. template < class PlayerType >
  706. inline bool CBot< PlayerType >::IsEnemy( CBaseEntity *ent ) const
  707. {
  708. // only Players (real and AI) can be enemies
  709. if (!ent->IsPlayer())
  710. return false;
  711. // corpses are no threat
  712. if (!ent->IsAlive())
  713. return false;
  714. CBasePlayer *player = static_cast<CBasePlayer *>( ent );
  715. // if they are on our team, they are our friends
  716. if (player->GetTeamNumber() == this->GetTeamNumber())
  717. return false;
  718. // yep, we hate 'em
  719. return true;
  720. }
  721. //--------------------------------------------------------------------------------------------------------------
  722. /**
  723. * Return number of enemies left alive
  724. */
  725. template < class PlayerType >
  726. inline int CBot< PlayerType >::GetEnemiesRemaining( void ) const
  727. {
  728. int count = 0;
  729. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  730. {
  731. CBaseEntity *player = UTIL_PlayerByIndex( i );
  732. if (player == NULL)
  733. continue;
  734. if (!IsEnemy( player ))
  735. continue;
  736. if (!player->IsAlive())
  737. continue;
  738. count++;
  739. }
  740. return count;
  741. }
  742. //--------------------------------------------------------------------------------------------------------------
  743. /**
  744. * Return number of friends left alive
  745. */
  746. template < class PlayerType >
  747. inline int CBot< PlayerType >::GetFriendsRemaining( void ) const
  748. {
  749. int count = 0;
  750. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  751. {
  752. CBaseEntity *player = UTIL_PlayerByIndex( i );
  753. if (player == NULL)
  754. continue;
  755. if (IsEnemy( player ))
  756. continue;
  757. if (!player->IsAlive())
  758. continue;
  759. if (player == static_cast<CBaseEntity *>( const_cast<CBot *>( this ) ))
  760. continue;
  761. count++;
  762. }
  763. return count;
  764. }
  765. //--------------------------------------------------------------------------------------------------------------
  766. /**
  767. * Return true if the local player is currently in observer mode watching this bot.
  768. */
  769. template < class PlayerType >
  770. inline bool CBot< PlayerType >::IsLocalPlayerWatchingMe( void ) const
  771. {
  772. if ( engine->IsDedicatedServer() )
  773. return false;
  774. CBasePlayer *player = UTIL_GetListenServerHost();
  775. if ( player == NULL )
  776. return false;
  777. if ( cv_bot_debug_target.GetInt() > 0 )
  778. {
  779. return this->entindex() == cv_bot_debug_target.GetInt();
  780. }
  781. if ( player->IsObserver() || !player->IsAlive() )
  782. {
  783. if ( const_cast< CBot< PlayerType > * >(this) == player->GetObserverTarget() )
  784. {
  785. switch( player->GetObserverMode() )
  786. {
  787. case OBS_MODE_IN_EYE:
  788. case OBS_MODE_CHASE:
  789. return true;
  790. }
  791. }
  792. }
  793. return false;
  794. }
  795. //--------------------------------------------------------------------------------------------------------------
  796. /**
  797. * Output message to console if we are being watched by the local player
  798. */
  799. template < class PlayerType >
  800. inline void CBot< PlayerType >::PrintIfWatched( PRINTF_FORMAT_STRING const char *format, ... ) const
  801. {
  802. if (cv_bot_debug.GetInt() == 0)
  803. {
  804. return;
  805. }
  806. if ((IsLocalPlayerWatchingMe() && (cv_bot_debug.GetInt() == 1 || cv_bot_debug.GetInt() == 3)) ||
  807. (cv_bot_debug.GetInt() == 2 || cv_bot_debug.GetInt() == 4))
  808. {
  809. va_list varg;
  810. char buffer[ CBotManager::MAX_DBG_MSG_SIZE ];
  811. const char *name = const_cast< CBot< PlayerType > * >( this )->GetPlayerName();
  812. va_start( varg, format );
  813. vsprintf( buffer, format, varg );
  814. va_end( varg );
  815. // prefix the console message with the bot's name (this can be NULL if bot was just added)
  816. ClientPrint( UTIL_GetListenServerHost(),
  817. HUD_PRINTCONSOLE,
  818. UTIL_VarArgs( "%s: %s",
  819. (name) ? name : "(NULL netname)", buffer ) );
  820. TheBots->AddDebugMessage( buffer );
  821. }
  822. }
  823. //-----------------------------------------------------------------------------------------------------------
  824. //-----------------------------------------------------------------------------------------------------------
  825. extern void InstallBotControl( void );
  826. extern void RemoveBotControl( void );
  827. extern void Bot_ServerCommand( void );
  828. extern void Bot_RegisterCvars( void );
  829. extern bool IsSpotOccupied( CBaseEntity *me, const Vector &pos ); // if a player is at the given spot, return true
  830. extern const Vector *FindNearbyHidingSpot( CBaseEntity *me, const Vector &pos, float maxRange = 1000.0f, bool isSniper = false, bool useNearest = false );
  831. extern const Vector *FindRandomHidingSpot( CBaseEntity *me, Place place, bool isSniper = false );
  832. extern const Vector *FindNearbyRetreatSpot( CBaseEntity *me, const Vector &start, float maxRange = 1000.0f, int avoidTeam = 0 );
  833. #endif // BOT_H