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.

359 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. // Author: Michael S. Booth ([email protected]), 2003
  8. #include "cbase.h"
  9. #include "cs_bot.h"
  10. #include "cs_shareddefs.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. #pragma warning( disable : 4355 ) // warning 'this' used in base member initializer list - we're using it safely
  14. //--------------------------------------------------------------------------------------------------------------
  15. static void PrefixChanged( IConVar *c, const char *oldPrefix, float flOldValue )
  16. {
  17. if ( TheCSBots() && TheCSBots()->IsServerActive() )
  18. {
  19. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  20. {
  21. CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
  22. if ( !player )
  23. continue;
  24. if ( !player->IsBot() || !IsEntityValid( player ) )
  25. continue;
  26. CCSBot *bot = dynamic_cast< CCSBot * >( player );
  27. if ( !bot )
  28. continue;
  29. // set the bot's name
  30. char botName[MAX_PLAYER_NAME_LENGTH];
  31. UTIL_ConstructBotNetName( botName, MAX_PLAYER_NAME_LENGTH, bot->GetProfile() );
  32. engine->SetFakeClientConVarValue( bot->edict(), "name", botName );
  33. }
  34. }
  35. }
  36. ConVar cv_bot_traceview( "bot_traceview", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "For internal testing purposes." );
  37. ConVar cv_bot_stop( "bot_stop", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "If nonzero, immediately stops all bot processing." );
  38. ConVar cv_bot_show_nav( "bot_show_nav", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "For internal testing purposes." );
  39. ConVar cv_bot_walk( "bot_walk", "0", FCVAR_REPLICATED, "If nonzero, bots can only walk, not run." );
  40. ConVar cv_bot_difficulty( "bot_difficulty", "1", FCVAR_REPLICATED, "Defines the skill of bots joining the game. Values are: 0=easy, 1=normal, 2=hard, 3=expert." );
  41. ConVar cv_bot_debug( "bot_debug", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "For internal testing purposes." );
  42. ConVar cv_bot_debug_target( "bot_debug_target", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "For internal testing purposes." );
  43. ConVar cv_bot_quota( "bot_quota", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "Determines the total number of bots in the game." );
  44. ConVar cv_bot_quota_mode( "bot_quota_mode", "normal", FCVAR_REPLICATED, "Determines the type of quota.\nAllowed values: 'normal', 'fill', and 'match'.\nIf 'fill', the server will adjust bots to keep N players in the game, where N is bot_quota.\nIf 'match', the server will maintain a 1:N ratio of humans to bots, where N is bot_quota." );
  45. ConVar cv_bot_prefix( "bot_prefix", "", FCVAR_REPLICATED, "This string is prefixed to the name of all bots that join the game.\n<difficulty> will be replaced with the bot's difficulty.\n<weaponclass> will be replaced with the bot's desired weapon class.\n<skill> will be replaced with a 0-100 representation of the bot's skill.", PrefixChanged );
  46. ConVar cv_bot_allow_rogues( "bot_allow_rogues", "1", FCVAR_REPLICATED, "If nonzero, bots may occasionally go 'rogue'. Rogue bots do not obey radio commands, nor pursue scenario goals." );
  47. ConVar cv_bot_allow_pistols( "bot_allow_pistols", "1", FCVAR_REPLICATED, "If nonzero, bots may use pistols." );
  48. ConVar cv_bot_allow_shotguns( "bot_allow_shotguns", "1", FCVAR_REPLICATED, "If nonzero, bots may use shotguns." );
  49. ConVar cv_bot_allow_sub_machine_guns( "bot_allow_sub_machine_guns", "1", FCVAR_REPLICATED, "If nonzero, bots may use sub-machine guns." );
  50. ConVar cv_bot_allow_rifles( "bot_allow_rifles", "1", FCVAR_REPLICATED, "If nonzero, bots may use rifles." );
  51. ConVar cv_bot_allow_machine_guns( "bot_allow_machine_guns", "1", FCVAR_REPLICATED, "If nonzero, bots may use the machine gun." );
  52. ConVar cv_bot_allow_grenades( "bot_allow_grenades", "1", FCVAR_REPLICATED, "If nonzero, bots may use grenades." );
  53. ConVar cv_bot_allow_snipers( "bot_allow_snipers", "1", FCVAR_REPLICATED, "If nonzero, bots may use sniper rifles." );
  54. #ifdef CS_SHIELD_ENABLED
  55. ConVar cv_bot_allow_shield( "bot_allow_shield", "1", FCVAR_REPLICATED );
  56. #endif // CS_SHIELD_ENABLED
  57. ConVar cv_bot_join_team( "bot_join_team", "any", FCVAR_REPLICATED, "Determines the team bots will join into. Allowed values: 'any', 'T', or 'CT'." );
  58. ConVar cv_bot_join_after_player( "bot_join_after_player", "1", FCVAR_REPLICATED, "If nonzero, bots wait until a player joins before entering the game." );
  59. ConVar cv_bot_auto_vacate( "bot_auto_vacate", "1", FCVAR_REPLICATED, "If nonzero, bots will automatically leave to make room for human players." );
  60. ConVar cv_bot_zombie( "bot_zombie", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "If nonzero, bots will stay in idle mode and not attack." );
  61. ConVar cv_bot_defer_to_human( "bot_defer_to_human", "0", FCVAR_REPLICATED, "If nonzero and there is a human on the team, the bots will not do the scenario tasks." );
  62. ConVar cv_bot_chatter( "bot_chatter", "normal", FCVAR_REPLICATED, "Control how bots talk. Allowed values: 'off', 'radio', 'minimal', or 'normal'." );
  63. ConVar cv_bot_profile_db( "bot_profile_db", "BotProfile.db", FCVAR_REPLICATED, "The filename from which bot profiles will be read." );
  64. ConVar cv_bot_dont_shoot( "bot_dont_shoot", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "If nonzero, bots will not fire weapons (for debugging)." );
  65. ConVar cv_bot_eco_limit( "bot_eco_limit", "2000", FCVAR_REPLICATED, "If nonzero, bots will not buy if their money falls below this amount." );
  66. ConVar cv_bot_auto_follow( "bot_auto_follow", "0", FCVAR_REPLICATED, "If nonzero, bots with high co-op may automatically follow a nearby human player." );
  67. ConVar cv_bot_flipout( "bot_flipout", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "If nonzero, bots use no CPU for AI. Instead, they run around randomly." );
  68. extern void FinishClientPutInServer( CCSPlayer *pPlayer );
  69. //--------------------------------------------------------------------------------------------------------------
  70. // Engine callback for custom server commands
  71. void Bot_ServerCommand( void )
  72. {
  73. }
  74. //--------------------------------------------------------------------------------------------------------------
  75. /**
  76. * Constructor
  77. */
  78. CCSBot::CCSBot( void ) : m_chatter( this ), m_gameState( this )
  79. {
  80. m_hasJoined = false;
  81. }
  82. //--------------------------------------------------------------------------------------------------------------
  83. /**
  84. * Destructor
  85. */
  86. CCSBot::~CCSBot()
  87. {
  88. }
  89. //--------------------------------------------------------------------------------------------------------------
  90. /**
  91. * Prepare bot for action
  92. */
  93. bool CCSBot::Initialize( const BotProfile *profile, int team )
  94. {
  95. // extend
  96. BaseClass::Initialize( profile, team );
  97. // CS bot initialization
  98. m_diedLastRound = false;
  99. m_morale = POSITIVE; // starting a new round makes everyone a little happy
  100. m_combatRange = RandomFloat( 325.0f, 425.0f );
  101. // set initial safe time guess for this map
  102. m_safeTime = 15.0f + 5.0f * GetProfile()->GetAggression();
  103. m_name[0] = '\000';
  104. ResetValues();
  105. m_desiredTeam = team;
  106. if (GetTeamNumber() == 0)
  107. {
  108. HandleCommand_JoinTeam( m_desiredTeam );
  109. int desiredClass = GetProfile()->GetSkin();
  110. if ( m_desiredTeam == TEAM_CT && desiredClass )
  111. {
  112. desiredClass = FIRST_CT_CLASS + desiredClass - 1;
  113. }
  114. else if ( m_desiredTeam == TEAM_TERRORIST && desiredClass )
  115. {
  116. desiredClass = FIRST_T_CLASS + desiredClass - 1;
  117. }
  118. HandleCommand_JoinClass( desiredClass );
  119. }
  120. return true;
  121. }
  122. //--------------------------------------------------------------------------------------------------------------
  123. /**
  124. * Reset internal data to initial state
  125. */
  126. void CCSBot::ResetValues( void )
  127. {
  128. m_chatter.Reset();
  129. m_gameState.Reset();
  130. m_avoid = NULL;
  131. m_avoidTimestamp = 0.0f;
  132. m_hurryTimer.Invalidate();
  133. m_alertTimer.Invalidate();
  134. m_sneakTimer.Invalidate();
  135. m_noiseBendTimer.Invalidate();
  136. m_bendNoisePositionValid = false;
  137. m_isStuck = false;
  138. m_stuckTimestamp = 0.0f;
  139. m_wiggleTimer.Invalidate();
  140. m_stuckJumpTimer.Invalidate();
  141. m_pathLength = 0;
  142. m_pathIndex = 0;
  143. m_areaEnteredTimestamp = 0.0f;
  144. m_currentArea = NULL;
  145. m_lastKnownArea = NULL;
  146. m_isStopping = false;
  147. m_avoidFriendTimer.Invalidate();
  148. m_isFriendInTheWay = false;
  149. m_isWaitingBehindFriend = false;
  150. m_isAvoidingGrenade.Invalidate();
  151. StopPanicking();
  152. m_disposition = ENGAGE_AND_INVESTIGATE;
  153. m_enemy = NULL;
  154. m_grenadeTossState = NOT_THROWING;
  155. m_initialEncounterArea = NULL;
  156. m_wasSafe = true;
  157. m_nearbyEnemyCount = 0;
  158. m_enemyPlace = 0;
  159. m_nearbyFriendCount = 0;
  160. m_closestVisibleFriend = NULL;
  161. m_closestVisibleHumanFriend = NULL;
  162. for( int w=0; w<MAX_PLAYERS; ++w )
  163. {
  164. m_watchInfo[w].timestamp = 0.0f;
  165. m_watchInfo[w].isEnemy = false;
  166. m_playerTravelDistance[ w ] = -1.0f;
  167. }
  168. // randomly offset each bot's timer to spread computation out
  169. m_updateTravelDistanceTimer.Start( RandomFloat( 0.0f, 0.9f ) );
  170. m_travelDistancePhase = 0;
  171. m_isEnemyVisible = false;
  172. m_visibleEnemyParts = NONE;
  173. m_lastSawEnemyTimestamp = -999.9f;
  174. m_firstSawEnemyTimestamp = 0.0f;
  175. m_currentEnemyAcquireTimestamp = 0.0f;
  176. m_isLastEnemyDead = true;
  177. m_attacker = NULL;
  178. m_attackedTimestamp = 0.0f;
  179. m_enemyDeathTimestamp = 0.0f;
  180. m_friendDeathTimestamp = 0.0f;
  181. m_lastVictimID = 0;
  182. m_isAimingAtEnemy = false;
  183. m_fireWeaponTimestamp = 0.0f;
  184. m_equipTimer.Invalidate();
  185. m_zoomTimer.Invalidate();
  186. m_isFollowing = false;
  187. m_leader = NULL;
  188. m_followTimestamp = 0.0f;
  189. m_allowAutoFollowTime = 0.0f;
  190. m_enemyQueueIndex = 0;
  191. m_enemyQueueCount = 0;
  192. m_enemyQueueAttendIndex = 0;
  193. m_bomber = NULL;
  194. m_isEnemySniperVisible = false;
  195. m_sawEnemySniperTimer.Invalidate();
  196. m_lookAroundStateTimestamp = 0.0f;
  197. m_inhibitLookAroundTimestamp = 0.0f;
  198. m_lookPitch = 0.0f;
  199. m_lookPitchVel = 0.0f;
  200. m_lookYaw = 0.0f;
  201. m_lookYawVel = 0.0f;
  202. m_aimOffsetTimestamp = 0.0f;
  203. m_aimSpreadTimestamp = 0.0f;
  204. m_lookAtSpotState = NOT_LOOKING_AT_SPOT;
  205. for( int p=0; p<MAX_PLAYERS; ++p )
  206. {
  207. m_partInfo[p].m_validFrame = 0;
  208. }
  209. m_spotEncounter = NULL;
  210. m_spotCheckTimestamp = 0.0f;
  211. m_peripheralTimestamp = 0.0f;
  212. m_avgVelIndex = 0;
  213. m_avgVelCount = 0;
  214. m_lastOrigin = GetCentroid( this );
  215. m_lastRadioCommand = RADIO_INVALID;
  216. m_lastRadioRecievedTimestamp = 0.0f;
  217. m_lastRadioSentTimestamp = 0.0f;
  218. m_radioSubject = NULL;
  219. m_voiceEndTimestamp = 0.0f;
  220. m_hostageEscortCount = 0;
  221. m_hostageEscortCountTimestamp = 0.0f;
  222. m_noisePosition = Vector( 0, 0, 0 );
  223. m_noiseTimestamp = 0.0f;
  224. m_stateTimestamp = 0.0f;
  225. m_task = SEEK_AND_DESTROY;
  226. m_taskEntity = NULL;
  227. m_approachPointCount = 0;
  228. m_approachPointViewPosition.x = 99999999999.9f;
  229. m_approachPointViewPosition.y = 0.0f;
  230. m_approachPointViewPosition.z = 0.0f;
  231. m_checkedHidingSpotCount = 0;
  232. StandUp();
  233. Run();
  234. m_mustRunTimer.Invalidate();
  235. m_waitTimer.Invalidate();
  236. m_pathLadder = NULL;
  237. m_repathTimer.Invalidate();
  238. m_huntState.ClearHuntArea();
  239. m_hasVisitedEnemySpawn = false;
  240. m_stillTimer.Invalidate();
  241. // adjust morale - if we died, our morale decreased,
  242. // but if we live, no adjustement (round win/loss also adjusts morale)
  243. if (m_diedLastRound)
  244. DecreaseMorale();
  245. m_diedLastRound = false;
  246. // IsRogue() randomly changes this
  247. m_isRogue = false;
  248. m_surpriseTimer.Invalidate();
  249. // even though these are EHANDLEs, they need to be NULL-ed
  250. m_goalEntity = NULL;
  251. m_avoid = NULL;
  252. m_enemy = NULL;
  253. for ( int i=0; i<MAX_ENEMY_QUEUE; ++i )
  254. {
  255. m_enemyQueue[i].player = NULL;
  256. m_enemyQueue[i].isReloading = false;
  257. m_enemyQueue[i].isProtectedByShield = false;
  258. }
  259. // start in idle state
  260. m_isOpeningDoor = false;
  261. StopAttacking();
  262. Idle();
  263. }
  264. //--------------------------------------------------------------------------------------------------------------
  265. /**
  266. * Called when bot is placed in map, and when bots are reset after a round ends.
  267. * NOTE: For some reason, this can be called twice when a bot is added.
  268. */
  269. void CCSBot::Spawn( void )
  270. {
  271. // do the normal player spawn process
  272. BaseClass::Spawn();
  273. ResetValues();
  274. V_strcpy_safe( m_name, GetPlayerName() );
  275. Buy();
  276. }