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.

432 lines
11 KiB

  1. //========= Copyright 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 "player.h"
  15. #include "hl1mp_player.h"
  16. #include "in_buttons.h"
  17. #include "movehelper_server.h"
  18. void ClientPutInServer( edict_t *pEdict, const char *playername );
  19. void Bot_Think( CHL1MP_Player *pBot );
  20. #ifdef DEBUG
  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_defend( "bot_defend", "0", 0, "Set to a team number, and that team will all keep their combat shields raised." );
  26. ConVar bot_changeclass( "bot_changeclass", "0", 0, "Force all bots to change to the specified class." );
  27. ConVar bot_zombie( "bot_zombie", "0", 0, "Brraaaaaiiiins." );
  28. static ConVar bot_mimic( "bot_mimic", "0", 0, "Bot uses usercmd of player by index." );
  29. static ConVar bot_mimic_yaw_offset( "bot_mimic_yaw_offset", "0", 0, "Offsets the bot yaw." );
  30. ConVar bot_attack( "bot_attack", "1", 0, "Shoot!" );
  31. ConVar bot_sendcmd( "bot_sendcmd", "", 0, "Forces bots to send the specified command." );
  32. ConVar bot_crouch( "bot_crouch", "0", 0, "Bot crouches" );
  33. static int BotNumber = 1;
  34. static int g_iNextBotTeam = -1;
  35. static int g_iNextBotClass = -1;
  36. typedef struct
  37. {
  38. bool backwards;
  39. float nextturntime;
  40. bool lastturntoright;
  41. float nextstrafetime;
  42. float sidemove;
  43. QAngle forwardAngle;
  44. QAngle lastAngles;
  45. float m_flJoinTeamTime;
  46. int m_WantedTeam;
  47. int m_WantedClass;
  48. } botdata_t;
  49. static botdata_t g_BotData[ MAX_PLAYERS ];
  50. //-----------------------------------------------------------------------------
  51. // Purpose: Create a new Bot and put it in the game.
  52. // Output : Pointer to the new Bot, or NULL if there's no free clients.
  53. //-----------------------------------------------------------------------------
  54. CBasePlayer *BotPutInServer( bool bFrozen, int iTeam )
  55. {
  56. g_iNextBotTeam = iTeam;
  57. char botname[ 64 ];
  58. Q_snprintf( botname, sizeof( botname ), "Bot%02i", BotNumber );
  59. // This is an evil hack, but we use it to prevent sv_autojointeam from kicking in.
  60. edict_t *pEdict = engine->CreateFakeClient( botname );
  61. if (!pEdict)
  62. {
  63. Msg( "Failed to create Bot.\n");
  64. return NULL;
  65. }
  66. // Allocate a CBasePlayer for the bot, and call spawn
  67. //ClientPutInServer( pEdict, botname );
  68. CHL1MP_Player *pPlayer = ((CHL1MP_Player *)CBaseEntity::Instance( pEdict ));
  69. pPlayer->ClearFlags();
  70. pPlayer->AddFlag( FL_CLIENT | FL_FAKECLIENT );
  71. if ( bFrozen )
  72. pPlayer->AddEFlags( EFL_BOT_FROZEN );
  73. char szReturnString[512];
  74. Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", "gman" );
  75. engine->ClientCommand ( pPlayer->edict(), szReturnString );
  76. BotNumber++;
  77. g_BotData[pPlayer->entindex()-1].m_WantedTeam = iTeam;
  78. g_BotData[pPlayer->entindex()-1].m_flJoinTeamTime = gpGlobals->curtime + 0.3;
  79. return pPlayer;
  80. }
  81. //-----------------------------------------------------------------------------
  82. // Purpose: Run through all the Bots in the game and let them think.
  83. //-----------------------------------------------------------------------------
  84. void Bot_RunAll( void )
  85. {
  86. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  87. {
  88. CHL1MP_Player *pPlayer = ToHL1MPPlayer( UTIL_PlayerByIndex( i ) );
  89. if ( pPlayer && (pPlayer->GetFlags() & FL_FAKECLIENT) )
  90. {
  91. Bot_Think( pPlayer );
  92. }
  93. }
  94. }
  95. bool RunMimicCommand( CUserCmd& cmd )
  96. {
  97. if ( bot_mimic.GetInt() <= 0 )
  98. return false;
  99. if ( bot_mimic.GetInt() > gpGlobals->maxClients )
  100. return false;
  101. CBasePlayer *pPlayer = UTIL_PlayerByIndex( bot_mimic.GetInt() );
  102. if ( !pPlayer )
  103. return false;
  104. if ( !pPlayer->GetLastUserCommand() )
  105. return false;
  106. cmd = *pPlayer->GetLastUserCommand();
  107. cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat();
  108. return true;
  109. }
  110. //-----------------------------------------------------------------------------
  111. // Purpose: Simulates a single frame of movement for a player
  112. // Input : *fakeclient -
  113. // *viewangles -
  114. // forwardmove -
  115. // sidemove -
  116. // upmove -
  117. // buttons -
  118. // impulse -
  119. // msec -
  120. // Output : virtual void
  121. //-----------------------------------------------------------------------------
  122. static void RunPlayerMove( CHL1MP_Player *fakeclient, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime )
  123. {
  124. if ( !fakeclient )
  125. return;
  126. CUserCmd cmd;
  127. // Store off the globals.. they're gonna get whacked
  128. float flOldFrametime = gpGlobals->frametime;
  129. float flOldCurtime = gpGlobals->curtime;
  130. float flTimeBase = gpGlobals->curtime + gpGlobals->frametime - frametime;
  131. fakeclient->SetTimeBase( flTimeBase );
  132. Q_memset( &cmd, 0, sizeof( cmd ) );
  133. if ( !RunMimicCommand( cmd ) && !bot_zombie.GetBool() )
  134. {
  135. VectorCopy( viewangles, cmd.viewangles );
  136. cmd.forwardmove = forwardmove;
  137. cmd.sidemove = sidemove;
  138. cmd.upmove = upmove;
  139. cmd.buttons = buttons;
  140. cmd.impulse = impulse;
  141. cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
  142. }
  143. if( bot_crouch.GetInt() )
  144. cmd.buttons |= IN_DUCK;
  145. if ( bot_attack.GetBool() )
  146. cmd.buttons |= IN_ATTACK;
  147. MoveHelperServer()->SetHost( fakeclient );
  148. fakeclient->PlayerRunCommand( &cmd, MoveHelperServer() );
  149. // save off the last good usercmd
  150. fakeclient->SetLastUserCommand( cmd );
  151. // Clear out any fixangle that has been set
  152. fakeclient->pl.fixangle = FIXANGLE_NONE;
  153. // Restore the globals..
  154. gpGlobals->frametime = flOldFrametime;
  155. gpGlobals->curtime = flOldCurtime;
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose: Run this Bot's AI for one frame.
  159. //-----------------------------------------------------------------------------
  160. void Bot_Think( CHL1MP_Player *pBot )
  161. {
  162. // Make sure we stay being a bot
  163. pBot->AddFlag( FL_FAKECLIENT );
  164. botdata_t *botdata = &g_BotData[ ENTINDEX( pBot->edict() ) - 1 ];
  165. QAngle vecViewAngles;
  166. float forwardmove = 0.0;
  167. float sidemove = botdata->sidemove;
  168. float upmove = 0.0;
  169. unsigned short buttons = 0;
  170. byte impulse = 0;
  171. float frametime = gpGlobals->frametime;
  172. vecViewAngles = pBot->GetLocalAngles();
  173. // Create some random values
  174. if ( pBot->IsAlive() && (pBot->GetSolid() == SOLID_BBOX) )
  175. {
  176. trace_t trace;
  177. // Stop when shot
  178. if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) )
  179. {
  180. if ( pBot->m_iHealth == 100 )
  181. {
  182. forwardmove = 600 * ( botdata->backwards ? -1 : 1 );
  183. if ( botdata->sidemove != 0.0f )
  184. {
  185. forwardmove *= random->RandomFloat( 0.1, 1.0f );
  186. }
  187. }
  188. else
  189. {
  190. forwardmove = 0;
  191. }
  192. }
  193. // Only turn if I haven't been hurt
  194. if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) && pBot->m_iHealth == 100 )
  195. {
  196. Vector vecEnd;
  197. Vector forward;
  198. QAngle angle;
  199. float angledelta = 15.0;
  200. int maxtries = (int)360.0/angledelta;
  201. if ( botdata->lastturntoright )
  202. {
  203. angledelta = -angledelta;
  204. }
  205. angle = pBot->GetLocalAngles();
  206. Vector vecSrc;
  207. while ( --maxtries >= 0 )
  208. {
  209. AngleVectors( angle, &forward );
  210. vecSrc = pBot->GetLocalOrigin() + Vector( 0, 0, 36 );
  211. vecEnd = vecSrc + forward * 10;
  212. UTIL_TraceHull( vecSrc, vecEnd, VEC_HULL_MIN_SCALED( pBot ), VEC_HULL_MAX_SCALED( pBot ),
  213. MASK_PLAYERSOLID, pBot, COLLISION_GROUP_NONE, &trace );
  214. if ( trace.fraction == 1.0 )
  215. {
  216. if ( gpGlobals->curtime < botdata->nextturntime )
  217. {
  218. break;
  219. }
  220. }
  221. angle.y += angledelta;
  222. if ( angle.y > 180 )
  223. angle.y -= 360;
  224. else if ( angle.y < -180 )
  225. angle.y += 360;
  226. botdata->nextturntime = gpGlobals->curtime + 2.0;
  227. botdata->lastturntoright = random->RandomInt( 0, 1 ) == 0 ? true : false;
  228. botdata->forwardAngle = angle;
  229. botdata->lastAngles = angle;
  230. }
  231. if ( gpGlobals->curtime >= botdata->nextstrafetime )
  232. {
  233. botdata->nextstrafetime = gpGlobals->curtime + 1.0f;
  234. if ( random->RandomInt( 0, 5 ) == 0 )
  235. {
  236. botdata->sidemove = -600.0f + 1200.0f * random->RandomFloat( 0, 2 );
  237. }
  238. else
  239. {
  240. botdata->sidemove = 0;
  241. }
  242. sidemove = botdata->sidemove;
  243. if ( random->RandomInt( 0, 20 ) == 0 )
  244. {
  245. botdata->backwards = true;
  246. }
  247. else
  248. {
  249. botdata->backwards = false;
  250. }
  251. }
  252. pBot->SetLocalAngles( angle );
  253. vecViewAngles = angle;
  254. }
  255. // Is my team being forced to defend?
  256. if ( bot_defend.GetInt() == pBot->GetTeamNumber() )
  257. {
  258. buttons |= IN_ATTACK2;
  259. }
  260. // If bots are being forced to fire a weapon, see if I have it
  261. else if ( bot_forcefireweapon.GetString() )
  262. {
  263. CBaseCombatWeapon *pWeapon = pBot->Weapon_OwnsThisType( bot_forcefireweapon.GetString() );
  264. if ( pWeapon )
  265. {
  266. // Switch to it if we don't have it out
  267. CBaseCombatWeapon *pActiveWeapon = pBot->GetActiveWeapon();
  268. // Switch?
  269. if ( pActiveWeapon != pWeapon )
  270. {
  271. pBot->Weapon_Switch( pWeapon );
  272. }
  273. else
  274. {
  275. // Start firing
  276. // Some weapons require releases, so randomise firing
  277. if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
  278. {
  279. buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
  280. }
  281. }
  282. }
  283. }
  284. if ( bot_flipout.GetInt() )
  285. {
  286. if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
  287. {
  288. buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
  289. }
  290. }
  291. if ( strlen( bot_sendcmd.GetString() ) > 0 )
  292. {
  293. //send the cmd from this bot
  294. CCommand args;
  295. args.Tokenize( bot_sendcmd.GetString() );
  296. pBot->ClientCommand( args );
  297. bot_sendcmd.SetValue("");
  298. }
  299. }
  300. else
  301. {
  302. // Wait for Reinforcement wave
  303. if ( !pBot->IsAlive() )
  304. {
  305. // Try hitting my buttons occasionally
  306. if ( random->RandomInt( 0, 100 ) > 80 )
  307. {
  308. // Respawn the bot
  309. if ( random->RandomInt( 0, 1 ) == 0 )
  310. {
  311. buttons |= IN_JUMP;
  312. }
  313. else
  314. {
  315. buttons = 0;
  316. }
  317. }
  318. }
  319. }
  320. if ( bot_flipout.GetInt() >= 2 )
  321. {
  322. QAngle angOffset = RandomAngle( -1, 1 );
  323. botdata->lastAngles += angOffset;
  324. for ( int i = 0 ; i < 2; i++ )
  325. {
  326. if ( fabs( botdata->lastAngles[ i ] - botdata->forwardAngle[ i ] ) > 15.0f )
  327. {
  328. if ( botdata->lastAngles[ i ] > botdata->forwardAngle[ i ] )
  329. {
  330. botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] + 15;
  331. }
  332. else
  333. {
  334. botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] - 15;
  335. }
  336. }
  337. }
  338. botdata->lastAngles[ 2 ] = 0;
  339. pBot->SetLocalAngles( botdata->lastAngles );
  340. }
  341. RunPlayerMove( pBot, pBot->GetLocalAngles(), forwardmove, sidemove, upmove, buttons, impulse, frametime );
  342. }
  343. #endif