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.

383 lines
9.2 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 "interface.h"
  14. #include "filesystem.h"
  15. #undef VECTOR_NO_SLOW_OPERATIONS
  16. #include "mathlib/vector.h"
  17. #include "eiface.h"
  18. #include "edict.h"
  19. #include "game/server/iplayerinfo.h"
  20. #include "igameevents.h"
  21. #include "convar.h"
  22. #include "vstdlib/random.h"
  23. #include "../../game/shared/in_buttons.h"
  24. #include "../../game/shared/shareddefs.h"
  25. //#include "../../game_shared/util_shared.h"
  26. #include "engine/IEngineTrace.h"
  27. extern IBotManager *botmanager;
  28. extern IUniformRandomStream *randomStr;
  29. extern IPlayerInfoManager *playerinfomanager;
  30. extern IVEngineServer *engine;
  31. extern IEngineTrace *enginetrace;
  32. extern IPlayerInfoManager *playerinfomanager; // game dll interface to interact with players
  33. extern IServerPluginHelpers *helpers; // special 3rd party plugin helpers from the engine
  34. extern CGlobalVars *gpGlobals;
  35. ConVar bot_forcefireweapon( "plugin_bot_forcefireweapon", "", 0, "Force bots with the specified weapon to fire." );
  36. ConVar bot_forceattack2( "plugin_bot_forceattack2", "0", 0, "When firing, use attack2." );
  37. ConVar bot_forceattackon( "plugin_bot_forceattackon", "0", 0, "When firing, don't tap fire, hold it down." );
  38. ConVar bot_flipout( "plugin_bot_flipout", "0", 0, "When on, all bots fire their guns." );
  39. ConVar bot_changeclass( "plugin_bot_changeclass", "0", 0, "Force all bots to change to the specified class." );
  40. static ConVar bot_mimic( "plugin_bot_mimic", "0", 0, "Bot uses usercmd of player by index." );
  41. static ConVar bot_mimic_yaw_offset( "plugin_bot_mimic_yaw_offset", "0", 0, "Offsets the bot yaw." );
  42. ConVar bot_sendcmd( "plugin_bot_sendcmd", "", 0, "Forces bots to send the specified command." );
  43. ConVar bot_crouch( "plugin_bot_crouch", "0", 0, "Bot crouches" );
  44. // This is our bot class.
  45. class CPluginBot
  46. {
  47. public:
  48. CPluginBot() :
  49. m_bBackwards(0),
  50. m_flNextTurnTime(0),
  51. m_bLastTurnToRight(0),
  52. m_flNextStrafeTime(0),
  53. m_flSideMove(0),
  54. m_ForwardAngle(),
  55. m_LastAngles()
  56. {
  57. }
  58. bool m_bBackwards;
  59. float m_flNextTurnTime;
  60. bool m_bLastTurnToRight;
  61. float m_flNextStrafeTime;
  62. float m_flSideMove;
  63. QAngle m_ForwardAngle;
  64. QAngle m_LastAngles;
  65. IBotController *m_BotInterface;
  66. IPlayerInfo *m_PlayerInfo;
  67. edict_t *m_BotEdict;
  68. };
  69. CUtlVector<CPluginBot> s_Bots;
  70. void Bot_Think( CPluginBot *pBot );
  71. // Handler for the "bot" command.
  72. void BotAdd_f()
  73. {
  74. if ( !botmanager )
  75. return;
  76. static int s_BotNum = 0;
  77. char botName[64];
  78. Q_snprintf( botName, sizeof(botName), "Bot_%i", s_BotNum );
  79. s_BotNum++;
  80. edict_t *botEdict = botmanager->CreateBot( botName );
  81. if ( botEdict )
  82. {
  83. int botIndex = s_Bots.AddToTail();
  84. CPluginBot & bot = s_Bots[ botIndex ];
  85. bot.m_BotInterface = botmanager->GetBotController( botEdict );
  86. bot.m_PlayerInfo = playerinfomanager->GetPlayerInfo( botEdict );
  87. bot.m_BotEdict = botEdict;
  88. Assert( bot.m_BotInterface );
  89. }
  90. }
  91. ConCommand cc_Bot( "plugin_bot_add", BotAdd_f, "Add a bot." );
  92. //-----------------------------------------------------------------------------
  93. // Purpose: Run through all the Bots in the game and let them think.
  94. //-----------------------------------------------------------------------------
  95. void Bot_RunAll( void )
  96. {
  97. if ( !botmanager )
  98. return;
  99. for ( int i = 0; i < s_Bots.Count(); i++ )
  100. {
  101. CPluginBot & bot = s_Bots[i];
  102. if ( bot.m_BotEdict->IsFree() || !bot.m_BotEdict->GetUnknown()|| !bot.m_PlayerInfo->IsConnected() )
  103. {
  104. s_Bots.Remove(i);
  105. --i;
  106. }
  107. else
  108. {
  109. Bot_Think( &bot );
  110. }
  111. }
  112. }
  113. bool Bot_RunMimicCommand( CBotCmd& cmd )
  114. {
  115. if ( bot_mimic.GetInt() <= 0 )
  116. return false;
  117. if ( bot_mimic.GetInt() > gpGlobals->maxClients )
  118. return false;
  119. IPlayerInfo *playerInfo = playerinfomanager->GetPlayerInfo( engine->PEntityOfEntIndex( bot_mimic.GetInt() ) );
  120. if ( !playerInfo )
  121. return false;
  122. cmd = playerInfo->GetLastUserCommand();
  123. cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat();
  124. if( bot_crouch.GetInt() )
  125. cmd.buttons |= IN_DUCK;
  126. return true;
  127. }
  128. void Bot_UpdateStrafing( CPluginBot *pBot, CBotCmd &cmd )
  129. {
  130. if ( gpGlobals->curtime >= pBot->m_flNextStrafeTime )
  131. {
  132. pBot->m_flNextStrafeTime = gpGlobals->curtime + 1.0f;
  133. if ( randomStr->RandomInt( 0, 5 ) == 0 )
  134. {
  135. pBot->m_flSideMove = -600.0f + 1200.0f * randomStr->RandomFloat( 0, 2 );
  136. }
  137. else
  138. {
  139. pBot->m_flSideMove = 0;
  140. }
  141. cmd.sidemove = pBot->m_flSideMove;
  142. if ( randomStr->RandomInt( 0, 20 ) == 0 )
  143. {
  144. pBot->m_bBackwards = true;
  145. }
  146. else
  147. {
  148. pBot->m_bBackwards = false;
  149. }
  150. }
  151. }
  152. void Bot_UpdateDirection( CPluginBot *pBot )
  153. {
  154. float angledelta = 15.0;
  155. int maxtries = (int)360.0/angledelta;
  156. if ( pBot->m_bLastTurnToRight )
  157. {
  158. angledelta = -angledelta;
  159. }
  160. QAngle angle( pBot->m_BotInterface->GetLocalAngles() );
  161. trace_t trace;
  162. Vector vecSrc, vecEnd, forward;
  163. while ( --maxtries >= 0 )
  164. {
  165. AngleVectors( angle, &forward );
  166. vecSrc = pBot->m_BotInterface->GetLocalOrigin() + Vector( 0, 0, 36 );
  167. vecEnd = vecSrc + forward * 10;
  168. Ray_t ray;
  169. ray.Init( vecSrc, vecEnd, Vector(-16, -16, 0 ), Vector( 16, 16, 72 ) );
  170. CTraceFilterWorldAndPropsOnly traceFilter;
  171. enginetrace->TraceRay( ray, MASK_PLAYERSOLID, &traceFilter, &trace );
  172. if ( trace.fraction == 1.0 )
  173. {
  174. if ( gpGlobals->curtime < pBot->m_flNextTurnTime )
  175. {
  176. break;
  177. }
  178. }
  179. angle.y += angledelta;
  180. if ( angle.y > 180 )
  181. angle.y -= 360;
  182. else if ( angle.y < -180 )
  183. angle.y += 360;
  184. pBot->m_flNextTurnTime = gpGlobals->curtime + 2.0;
  185. pBot->m_bLastTurnToRight = randomStr->RandomInt( 0, 1 ) == 0 ? true : false;
  186. pBot->m_ForwardAngle = angle;
  187. pBot->m_LastAngles = angle;
  188. }
  189. pBot->m_BotInterface->SetLocalAngles( angle );
  190. }
  191. void Bot_FlipOut( CPluginBot *pBot, CBotCmd &cmd )
  192. {
  193. if ( bot_flipout.GetInt() > 0 && !pBot->m_PlayerInfo->IsDead() )
  194. {
  195. if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
  196. {
  197. cmd.buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
  198. }
  199. if ( bot_flipout.GetInt() >= 2 )
  200. {
  201. QAngle angOffset = RandomAngle( -1, 1 );
  202. pBot->m_LastAngles += angOffset;
  203. for ( int i = 0 ; i < 2; i++ )
  204. {
  205. if ( fabs( pBot->m_LastAngles[ i ] - pBot->m_ForwardAngle[ i ] ) > 15.0f )
  206. {
  207. if ( pBot->m_LastAngles[ i ] > pBot->m_ForwardAngle[ i ] )
  208. {
  209. pBot->m_LastAngles[ i ] = pBot->m_ForwardAngle[ i ] + 15;
  210. }
  211. else
  212. {
  213. pBot->m_LastAngles[ i ] = pBot->m_ForwardAngle[ i ] - 15;
  214. }
  215. }
  216. }
  217. pBot->m_LastAngles[ 2 ] = 0;
  218. pBot->m_BotInterface->SetLocalAngles( pBot->m_LastAngles );
  219. }
  220. }
  221. }
  222. void Bot_HandleSendCmd( CPluginBot *pBot )
  223. {
  224. if ( strlen( bot_sendcmd.GetString() ) > 0 )
  225. {
  226. //send the cmd from this bot
  227. helpers->ClientCommand( pBot->m_BotEdict, bot_sendcmd.GetString() );
  228. bot_sendcmd.SetValue("");
  229. }
  230. }
  231. // If bots are being forced to fire a weapon, see if I have it
  232. void Bot_ForceFireWeapon( CPluginBot *pBot, CBotCmd &cmd )
  233. {
  234. if ( Q_strlen( bot_forcefireweapon.GetString() ) > 0 )
  235. {
  236. pBot->m_BotInterface->SetActiveWeapon( bot_forcefireweapon.GetString() );
  237. bot_forcefireweapon.SetValue( "" );
  238. // Start firing
  239. // Some weapons require releases, so randomise firing
  240. if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
  241. {
  242. cmd.buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
  243. }
  244. }
  245. }
  246. void Bot_SetForwardMovement( CPluginBot *pBot, CBotCmd &cmd )
  247. {
  248. if ( !pBot->m_BotInterface->IsEFlagSet(EFL_BOT_FROZEN) )
  249. {
  250. if ( pBot->m_PlayerInfo->GetHealth() == 100 )
  251. {
  252. cmd.forwardmove = 600 * ( pBot->m_bBackwards ? -1 : 1 );
  253. if ( pBot->m_flSideMove != 0.0f )
  254. {
  255. cmd.forwardmove *= randomStr->RandomFloat( 0.1, 1.0f );
  256. }
  257. }
  258. else
  259. {
  260. // Stop when shot
  261. cmd.forwardmove = 0;
  262. }
  263. }
  264. }
  265. void Bot_HandleRespawn( CPluginBot *pBot, CBotCmd &cmd )
  266. {
  267. // Wait for Reinforcement wave
  268. if ( pBot->m_PlayerInfo->IsDead() )
  269. {
  270. if ( pBot->m_PlayerInfo->GetTeamIndex() == 0 )
  271. {
  272. helpers->ClientCommand( pBot->m_BotEdict, "joingame" );
  273. helpers->ClientCommand( pBot->m_BotEdict, "jointeam 3" );
  274. helpers->ClientCommand( pBot->m_BotEdict, "joinclass 0" );
  275. }
  276. }
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Run this Bot's AI for one frame.
  280. //-----------------------------------------------------------------------------
  281. void Bot_Think( CPluginBot *pBot )
  282. {
  283. CBotCmd cmd;
  284. Q_memset( &cmd, 0, sizeof( cmd ) );
  285. // Finally, override all this stuff if the bot is being forced to mimic a player.
  286. if ( !Bot_RunMimicCommand( cmd ) )
  287. {
  288. cmd.sidemove = pBot->m_flSideMove;
  289. if ( !pBot->m_PlayerInfo->IsDead() )
  290. {
  291. Bot_SetForwardMovement( pBot, cmd );
  292. // Only turn if I haven't been hurt
  293. if ( !pBot->m_BotInterface->IsEFlagSet(EFL_BOT_FROZEN) && pBot->m_PlayerInfo->GetHealth() == 100 )
  294. {
  295. Bot_UpdateDirection( pBot );
  296. Bot_UpdateStrafing( pBot, cmd );
  297. }
  298. // Handle console settings.
  299. Bot_ForceFireWeapon( pBot, cmd );
  300. Bot_HandleSendCmd( pBot );
  301. }
  302. else
  303. {
  304. Bot_HandleRespawn( pBot, cmd );
  305. }
  306. Bot_FlipOut( pBot, cmd );
  307. cmd.viewangles = pBot->m_BotInterface->GetLocalAngles();
  308. cmd.upmove = 0;
  309. cmd.impulse = 0;
  310. }
  311. pBot->m_BotInterface->RunPlayerMove( &cmd );
  312. }