Counter Strike : Global Offensive Source Code
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.

1174 lines
36 KiB

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