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.

557 lines
14 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Basic BOT handling.
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. //
  8. //-----------------------------------------------------------------------------
  9. // $Log: $
  10. //
  11. // $NoKeywords: $
  12. //=============================================================================//
  13. #include "cbase.h"
  14. #include "cs_player.h"
  15. #include "in_buttons.h"
  16. #include "movehelper_server.h"
  17. #include "team.h"
  18. #include "cs_gamerules.h"
  19. #include "client.h"
  20. void Bot_Think( CCSPlayer *pBot );
  21. ConVar bot_forcefireweapon( "bot_forcefireweapon", "", 0, "Force bots with the specified weapon to fire." );
  22. ConVar bot_forceattack2( "bot_forceattack2", "0", 0, "When firing, use attack2." );
  23. ConVar bot_forceattackon( "bot_forceattackon", "0", 0, "When firing, don't tap fire, hold it down." );
  24. ConVar bot_flipout( "bot_flipout", "0", 0, "When on, all bots fire their guns." );
  25. ConVar bot_mimic( "bot_mimic", "0", 0, "Bot uses usercmd of player by index." );
  26. static ConVar bot_mimic_yaw_offset( "bot_mimic_yaw_offset", "0", 0, "Offsets the bot yaw." );
  27. static int BotNumber = 1;
  28. static int g_iNextBotTeam = -1;
  29. typedef struct
  30. {
  31. bool backwards;
  32. float nextturntime;
  33. bool lastturntoright;
  34. float nextstrafetime;
  35. float sidemove;
  36. QAngle forwardAngle;
  37. QAngle lastAngles;
  38. int m_WantedTeam;
  39. float m_flJoinTeamTime;
  40. bool m_bTempBot; // Is this slot a dump temp bot or a real bot?
  41. } botdata_t;
  42. static botdata_t g_BotData[ MAX_PLAYERS ];
  43. //-----------------------------------------------------------------------------
  44. // Purpose: Create a new Bot and put it in the game.
  45. // Output : Pointer to the new Bot, or NULL if there's no free clients.
  46. //-----------------------------------------------------------------------------
  47. CBasePlayer *BotPutInServer( bool bFrozen, int iTeam )
  48. {
  49. g_iNextBotTeam = iTeam;
  50. char botname[ 64 ];
  51. Q_snprintf( botname, sizeof( botname ), "Bot%02i", BotNumber );
  52. edict_t *pEdict = engine->CreateFakeClient( botname );
  53. if (!pEdict)
  54. {
  55. Msg( "Failed to create Bot.\n");
  56. return NULL;
  57. }
  58. // Allocate a CBasePlayer for the bot, and call spawn
  59. //ClientPutInServer( pEdict, botname );
  60. //ClientActive( pEdict, false );
  61. CCSPlayer *pPlayer = ((CCSPlayer *)CBaseEntity::Instance( pEdict ));
  62. pPlayer->ClearFlags();
  63. pPlayer->AddFlag( FL_CLIENT | FL_FAKECLIENT );
  64. if ( bFrozen )
  65. pPlayer->AddEFlags( EFL_BOT_FROZEN );
  66. if ( iTeam == -1 )
  67. iTeam = ( pPlayer->entindex() & 1 ) ? TEAM_TERRORIST : TEAM_CT;
  68. botdata_t *pData = &g_BotData[pPlayer->entindex()-1];
  69. pData->m_WantedTeam = iTeam;
  70. pData->m_flJoinTeamTime = gpGlobals->curtime + 0.3;
  71. pData->m_bTempBot = true;
  72. BotNumber++;
  73. return pPlayer;
  74. }
  75. bool IsTempBot( CBaseEntity *pEnt )
  76. {
  77. if ( !pEnt )
  78. return false;
  79. if ( !(pEnt->GetFlags() & FL_FAKECLIENT) )
  80. return false;
  81. int i = pEnt->entindex();
  82. if ( i >= 1 && i < MAX_PLAYERS )
  83. return g_BotData[i-1].m_bTempBot;
  84. else
  85. return false;
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Purpose: Run through all the Bots in the game and let them think.
  89. //-----------------------------------------------------------------------------
  90. void Bot_RunAll( void )
  91. {
  92. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  93. {
  94. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  95. if ( IsTempBot( pPlayer ) )
  96. {
  97. Bot_Think( pPlayer );
  98. }
  99. }
  100. }
  101. bool RunMimicCommand( CUserCmd& cmd )
  102. {
  103. if ( bot_mimic.GetInt() <= 0 )
  104. return false;
  105. if ( bot_mimic.GetInt() > gpGlobals->maxClients )
  106. return false;
  107. CBasePlayer *pPlayer = UTIL_PlayerByIndex( bot_mimic.GetInt() );
  108. if ( !pPlayer )
  109. return false;
  110. if ( !pPlayer->GetLastUserCommand() )
  111. return false;
  112. cmd = *pPlayer->GetLastUserCommand();
  113. cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat();
  114. return true;
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose: Simulates a single frame of movement for a player
  118. // Input : *fakeclient -
  119. // *viewangles -
  120. // forwardmove -
  121. // sidemove -
  122. // upmove -
  123. // buttons -
  124. // impulse -
  125. // msec -
  126. // Output : virtual void
  127. //-----------------------------------------------------------------------------
  128. static void RunPlayerMove( CCSPlayer *fakeclient, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime )
  129. {
  130. if ( !fakeclient )
  131. return;
  132. CUserCmd cmd;
  133. // Store off the globals.. they're gonna get whacked
  134. float flOldFrametime = gpGlobals->frametime;
  135. float flOldCurtime = gpGlobals->curtime;
  136. float flTimeBase = gpGlobals->curtime;
  137. fakeclient->SetTimeBase( flTimeBase );
  138. Q_memset( &cmd, 0, sizeof( cmd ) );
  139. if ( !RunMimicCommand( cmd ) )
  140. {
  141. VectorCopy( viewangles, cmd.viewangles );
  142. cmd.forwardmove = forwardmove;
  143. cmd.sidemove = sidemove;
  144. cmd.upmove = upmove;
  145. cmd.buttons = buttons;
  146. cmd.impulse = impulse;
  147. cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
  148. }
  149. MoveHelperServer()->SetHost( fakeclient );
  150. fakeclient->PlayerRunCommand( &cmd, MoveHelperServer() );
  151. // save off the last good usercmd
  152. fakeclient->SetLastUserCommand( cmd );
  153. // Clear out any fixangle that has been set
  154. fakeclient->pl.fixangle = FIXANGLE_NONE;
  155. // Restore the globals..
  156. gpGlobals->frametime = flOldFrametime;
  157. gpGlobals->curtime = flOldCurtime;
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Purpose: Run this Bot's AI for one frame.
  161. //-----------------------------------------------------------------------------
  162. void Bot_Think( CCSPlayer *pBot )
  163. {
  164. // Make sure we stay being a bot
  165. pBot->AddFlag( FL_FAKECLIENT );
  166. botdata_t *botdata = &g_BotData[ pBot->entindex() - 1 ];
  167. float forwardmove = 0.0;
  168. float sidemove = botdata->sidemove;
  169. float upmove = 0.0;
  170. unsigned short buttons = 0;
  171. byte impulse = 0;
  172. float frametime = gpGlobals->frametime;
  173. if ( pBot->GetTeamNumber() == TEAM_UNASSIGNED && gpGlobals->curtime > botdata->m_flJoinTeamTime )
  174. {
  175. pBot->HandleCommand_JoinTeam( botdata->m_WantedTeam );
  176. }
  177. else if ( pBot->GetTeamNumber() != TEAM_UNASSIGNED && pBot->PlayerClass() == CS_CLASS_NONE )
  178. {
  179. // If they're on a team but haven't picked a class, choose a random class..
  180. pBot->HandleCommand_JoinClass( 0 );
  181. }
  182. else
  183. {
  184. QAngle vecViewAngles;
  185. vecViewAngles = pBot->GetLocalAngles();
  186. // Create some random values
  187. if ( pBot->IsAlive() && (pBot->GetSolid() == SOLID_BBOX) )
  188. {
  189. trace_t trace;
  190. // Stop when shot
  191. if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) )
  192. {
  193. if ( pBot->m_iHealth == 100 )
  194. {
  195. forwardmove = 600 * ( botdata->backwards ? -1 : 1 );
  196. if ( botdata->sidemove != 0.0f )
  197. {
  198. forwardmove *= random->RandomFloat( 0.1, 1.0f );
  199. }
  200. }
  201. else
  202. {
  203. forwardmove = 0;
  204. }
  205. }
  206. // Only turn if I haven't been hurt
  207. if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) && pBot->m_iHealth == 100 )
  208. {
  209. Vector vecEnd;
  210. Vector forward;
  211. QAngle angle;
  212. float angledelta = 15.0;
  213. int maxtries = (int)360.0/angledelta;
  214. if ( botdata->lastturntoright )
  215. {
  216. angledelta = -angledelta;
  217. }
  218. angle = pBot->GetLocalAngles();
  219. Vector vecSrc;
  220. while ( --maxtries >= 0 )
  221. {
  222. AngleVectors( angle, &forward, NULL, NULL );
  223. vecSrc = pBot->GetLocalOrigin() + Vector( 0, 0, 36 );
  224. vecEnd = vecSrc + forward * 10;
  225. UTIL_TraceHull( vecSrc, vecEnd, VEC_HULL_MIN, VEC_HULL_MAX,
  226. MASK_PLAYERSOLID, pBot, COLLISION_GROUP_NONE, &trace );
  227. if ( trace.fraction == 1.0 )
  228. {
  229. //if ( gpGlobals->curtime < botdata->nextturntime )
  230. //{
  231. break;
  232. //}
  233. }
  234. angle.y += angledelta;
  235. if ( angle.y > 180 )
  236. angle.y -= 360;
  237. else if ( angle.y < -180 )
  238. angle.y += 360;
  239. botdata->nextturntime = gpGlobals->curtime + 2.0;
  240. botdata->lastturntoright = random->RandomInt( 0, 1 ) == 0 ? true : false;
  241. botdata->forwardAngle = angle;
  242. botdata->lastAngles = angle;
  243. }
  244. /*
  245. if ( gpGlobals->curtime >= botdata->nextstrafetime )
  246. {
  247. botdata->nextstrafetime = gpGlobals->curtime + 1.0f;
  248. if ( random->RandomInt( 0, 5 ) == 0 )
  249. {
  250. botdata->sidemove = -600.0f + 1200.0f * random->RandomFloat( 0, 2 );
  251. }
  252. else
  253. {
  254. botdata->sidemove = 0;
  255. }
  256. sidemove = botdata->sidemove;
  257. if ( random->RandomInt( 0, 20 ) == 0 )
  258. {
  259. botdata->backwards = true;
  260. }
  261. else
  262. {
  263. botdata->backwards = false;
  264. }
  265. }
  266. */
  267. pBot->SetLocalAngles( angle );
  268. vecViewAngles = angle;
  269. }
  270. // If bots are being forced to fire a weapon, see if I have it
  271. else if ( bot_forcefireweapon.GetString() )
  272. {
  273. CBaseCombatWeapon *pWeapon = pBot->Weapon_OwnsThisType( bot_forcefireweapon.GetString() );
  274. if ( pWeapon )
  275. {
  276. // Switch to it if we don't have it out
  277. CBaseCombatWeapon *pActiveWeapon = pBot->GetActiveWeapon();
  278. // Switch?
  279. if ( pActiveWeapon != pWeapon )
  280. {
  281. pBot->Weapon_Switch( pWeapon );
  282. }
  283. else
  284. {
  285. // Start firing
  286. // Some weapons require releases, so randomise firing
  287. if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
  288. {
  289. buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
  290. }
  291. }
  292. }
  293. }
  294. if ( bot_flipout.GetInt() )
  295. {
  296. if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
  297. {
  298. buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
  299. }
  300. }
  301. }
  302. else
  303. {
  304. // Wait for Reinforcement wave
  305. if ( !pBot->IsAlive() )
  306. {
  307. // Try hitting my buttons occasionally
  308. if ( random->RandomInt( 0, 100 ) > 80 )
  309. {
  310. // Respawn the bot
  311. if ( random->RandomInt( 0, 1 ) == 0 )
  312. {
  313. buttons |= IN_JUMP;
  314. }
  315. else
  316. {
  317. buttons = 0;
  318. }
  319. }
  320. }
  321. }
  322. if ( bot_flipout.GetInt() >= 2 )
  323. {
  324. QAngle angOffset = RandomAngle( -1, 1 );
  325. botdata->lastAngles += angOffset;
  326. for ( int i = 0 ; i < 2; i++ )
  327. {
  328. if ( fabs( botdata->lastAngles[ i ] - botdata->forwardAngle[ i ] ) > 15.0f )
  329. {
  330. if ( botdata->lastAngles[ i ] > botdata->forwardAngle[ i ] )
  331. {
  332. botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] + 15;
  333. }
  334. else
  335. {
  336. botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] - 15;
  337. }
  338. }
  339. }
  340. botdata->lastAngles[ 2 ] = 0;
  341. pBot->SetLocalAngles( botdata->lastAngles );
  342. }
  343. }
  344. // Fix up the m_fEffects flags
  345. pBot->PostClientMessagesSent();
  346. pBot->SetPunchAngle( QAngle( 0, 0, 0 ) );
  347. RunPlayerMove( pBot, pBot->GetLocalAngles(), forwardmove, sidemove, upmove, buttons, impulse, frametime );
  348. }
  349. // Handler for the "bot" command.
  350. CON_COMMAND_F( "bot_old", "Add a bot.", FCVAR_CHEAT )
  351. {
  352. // Disable the CS bots, otherwise they'll interfere with the bot code here.
  353. //extern bool g_bEnableCSBots;
  354. //g_bEnableCSBots = false;
  355. CCSPlayer *pPlayer = CCSPlayer::Instance( UTIL_GetCommandClientIndex() );
  356. // The bot command uses switches like command-line switches.
  357. // -count <count> tells how many bots to spawn.
  358. // -team <index> selects the bot's team. Default is -1 which chooses randomly.
  359. // Note: if you do -team !, then it
  360. // -class <index> selects the bot's class. Default is -1 which chooses randomly.
  361. // -frozen prevents the bots from running around when they spawn in.
  362. int count = args.FindArgInt( "-count", 1 );
  363. count = clamp( count, 1, 16 );
  364. int iTeam = -1;
  365. const char *pVal = args.FindArg( "-team" );
  366. if ( pVal )
  367. {
  368. if ( pVal[0] == '!' )
  369. {
  370. if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  371. iTeam = TEAM_CT;
  372. else
  373. iTeam = TEAM_TERRORIST;
  374. }
  375. else if ( pVal[0] == 't' || pVal[0] == 'T' )
  376. {
  377. iTeam = TEAM_TERRORIST;
  378. }
  379. else if ( pVal[0] == 'c' || pVal[0] == 'C' )
  380. {
  381. iTeam = TEAM_CT;
  382. }
  383. else
  384. {
  385. iTeam = atoi( pVal );
  386. if ( iTeam == 1 )
  387. iTeam = TEAM_TERRORIST;
  388. else
  389. iTeam = TEAM_CT;
  390. }
  391. }
  392. // Look at -frozen.
  393. bool bFrozen = !!args.FindArg( "-frozen" );
  394. // Ok, spawn all the bots.
  395. while ( --count >= 0 )
  396. {
  397. extern CBasePlayer *BotPutInServer( bool bFrozen, int iTeam );
  398. BotPutInServer( bFrozen, iTeam );
  399. }
  400. }
  401. // Handle the "PossessBot" command.
  402. void PossessBot_f( const CCommand &args )
  403. {
  404. CCSPlayer *pPlayer = CCSPlayer::Instance( UTIL_GetCommandClientIndex() );
  405. if ( !pPlayer )
  406. return;
  407. // Put the local player in control of this bot.
  408. if ( args.ArgC() != 2 )
  409. {
  410. Warning( "PossessBot <client index>\n" );
  411. return;
  412. }
  413. int iBotClient = atoi( args[1] );
  414. int iBotEnt = iBotClient + 1;
  415. if ( iBotClient < 0 ||
  416. iBotClient >= gpGlobals->maxClients ||
  417. pPlayer->entindex() == iBotEnt )
  418. {
  419. Warning( "PossessBot <client index>\n" );
  420. }
  421. else
  422. {
  423. edict_t *pPlayerData = pPlayer->edict();
  424. edict_t *pBotData = INDEXENT( iBotEnt );
  425. if ( pBotData && pBotData )
  426. {
  427. // SWAP EDICTS
  428. // Backup things we don't want to swap.
  429. edict_t oldPlayerData = *pPlayerData;
  430. edict_t oldBotData = *pBotData;
  431. // Swap edicts.
  432. edict_t tmp = *pPlayerData;
  433. *pPlayerData = *pBotData;
  434. *pBotData = tmp;
  435. // Restore things we didn't want to swap.
  436. //pPlayerData->m_EntitiesTouched = oldPlayerData.m_EntitiesTouched;
  437. //pBotData->m_EntitiesTouched = oldBotData.m_EntitiesTouched;
  438. CBaseEntity *pPlayerBaseEnt = CBaseEntity::Instance( pPlayerData );
  439. CBaseEntity *pBotBaseEnt = CBaseEntity::Instance( pBotData );
  440. // Make the other a bot and make the player not a bot.
  441. pPlayerBaseEnt->RemoveFlag( FL_FAKECLIENT );
  442. pBotBaseEnt->AddFlag( FL_FAKECLIENT );
  443. // Point the CBaseEntities at the right players.
  444. pPlayerBaseEnt->NetworkProp()->SetEdict( pPlayerData );
  445. pBotBaseEnt->NetworkProp()->SetEdict( pBotData );
  446. // Freeze the bot.
  447. pBotBaseEnt->AddEFlags( EFL_BOT_FROZEN );
  448. }
  449. }
  450. }
  451. ConCommand cc_PossessBot( "PossessBot", PossessBot_f, "Toggle. Possess a bot.\n\tArguments: <bot client number>", FCVAR_CHEAT );