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.

682 lines
17 KiB

  1. //========= Copyright � 1996-2005, 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_shareddefs.h"
  10. #include "engine/IEngineSound.h"
  11. #include "keyvalues.h"
  12. #include "bot.h"
  13. #include "bot_util.h"
  14. #include "bot_profile.h"
  15. #include "cs_bot.h"
  16. #include <ctype.h>
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. static int s_iBeamSprite = 0;
  20. extern ConVar mp_randomspawn_dist;
  21. extern ConVar mp_randomspawn_los;
  22. //--------------------------------------------------------------------------------------------------------------
  23. /**
  24. * Return true if given name is already in use by another player
  25. */
  26. bool UTIL_IsNameTaken( const char *name, bool ignoreHumans )
  27. {
  28. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  29. {
  30. CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
  31. if (player == NULL)
  32. continue;
  33. if (player->IsPlayer() && player->IsBot())
  34. {
  35. // bots can have prefixes so we need to check the name
  36. // against the profile name instead.
  37. CCSBot *bot = dynamic_cast<CCSBot *>(player);
  38. if ( bot && bot->GetProfile()->GetName() && FStrEq(name, bot->GetProfile()->GetName()))
  39. {
  40. return true;
  41. }
  42. }
  43. else
  44. {
  45. if (!ignoreHumans)
  46. {
  47. if (FStrEq( name, player->GetPlayerName() ))
  48. return true;
  49. }
  50. }
  51. }
  52. return false;
  53. }
  54. //--------------------------------------------------------------------------------------------------------------
  55. int UTIL_ClientsInGame( void )
  56. {
  57. int count = 0;
  58. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  59. {
  60. CBaseEntity *player = UTIL_PlayerByIndex( i );
  61. if (player == NULL)
  62. continue;
  63. count++;
  64. }
  65. return count;
  66. }
  67. //--------------------------------------------------------------------------------------------------------------
  68. /**
  69. * Return the number of non-bots on the given team
  70. */
  71. int UTIL_HumansOnTeam( int teamID, bool isAlive )
  72. {
  73. int count = 0;
  74. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  75. {
  76. CBaseEntity *entity = UTIL_PlayerByIndex( i );
  77. if ( entity == NULL )
  78. continue;
  79. CBasePlayer *player = static_cast<CBasePlayer *>( entity );
  80. if (player->IsBot())
  81. continue;
  82. if (player->GetTeamNumber() != teamID)
  83. continue;
  84. if (isAlive && !player->IsAlive())
  85. continue;
  86. count++;
  87. }
  88. return count;
  89. }
  90. //--------------------------------------------------------------------------------------------------------------
  91. int UTIL_BotsInGame( void )
  92. {
  93. int count = 0;
  94. for (int i = 1; i <= gpGlobals->maxClients; ++i )
  95. {
  96. CBasePlayer *player = static_cast<CBasePlayer *>(UTIL_PlayerByIndex( i ));
  97. if ( player == NULL )
  98. continue;
  99. if ( !player->IsBot() )
  100. continue;
  101. count++;
  102. }
  103. return count;
  104. }
  105. //--------------------------------------------------------------------------------------------------------------
  106. /**
  107. * Kick a bot from the given team. If no bot exists on the team, return false.
  108. */
  109. bool UTIL_KickBotFromTeam( int kickTeam, bool bQueue )
  110. {
  111. int i;
  112. // try to kick a dead bot first
  113. for ( i = 1; i <= gpGlobals->maxClients; ++i )
  114. {
  115. CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
  116. if (player == NULL)
  117. continue;
  118. if (!player->IsBot())
  119. continue;
  120. if ( player->GetPendingTeamNumber() == TEAM_SPECTATOR )
  121. {
  122. // bot has already been flagged for kicking
  123. continue;
  124. }
  125. if ( !player->IsAlive() )
  126. {
  127. // Address issue with bots getting kicked during half-time (this was resulting in bots from the wrong team being kicked)
  128. if ( player->CanKickFromTeam( kickTeam ) )
  129. {
  130. if ( bQueue )
  131. {
  132. // bots flagged as spectators will be kicked at the beginning of the next round restart
  133. player->SetPendingTeamNum( TEAM_SPECTATOR );
  134. }
  135. else
  136. {
  137. // its a bot on the right team - kick it
  138. engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", engine->GetPlayerUserId( player->edict() ) ) );
  139. }
  140. return true;
  141. }
  142. }
  143. }
  144. // no dead bots, kick any bot on the given team
  145. for ( i = 1; i <= gpGlobals->maxClients; ++i )
  146. {
  147. CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
  148. if (player == NULL)
  149. continue;
  150. if (!player->IsBot())
  151. continue;
  152. if ( player->GetPendingTeamNumber() == TEAM_SPECTATOR )
  153. {
  154. // bot has already been flagged for kicking
  155. continue;
  156. }
  157. // Address issue with bots getting kicked during half-time (this was resulting in bots from the wrong team being kicked)
  158. if ( player->CanKickFromTeam( kickTeam ) )
  159. {
  160. if ( bQueue )
  161. {
  162. // bots flagged as spectators will be kicked at the beginning of the next round restart
  163. player->SetPendingTeamNum( TEAM_SPECTATOR );
  164. }
  165. else
  166. {
  167. // its a bot on the right team - kick it
  168. engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", engine->GetPlayerUserId( player->edict() ) ) );
  169. }
  170. return true;
  171. }
  172. }
  173. return false;
  174. }
  175. //--------------------------------------------------------------------------------------------------------------
  176. /**
  177. * Return true if all of the members of the given team are bots
  178. */
  179. bool UTIL_IsTeamAllBots( int team )
  180. {
  181. int botCount = 0;
  182. for( int i=1; i <= gpGlobals->maxClients; ++i )
  183. {
  184. CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
  185. if (player == NULL)
  186. continue;
  187. // skip players on other teams
  188. if (player->GetTeamNumber() != team)
  189. continue;
  190. // if not a bot, fail the test
  191. if (!player->IsBot())
  192. return false;
  193. // is a bot on given team
  194. ++botCount;
  195. }
  196. // if team is empty, there are no bots
  197. return (botCount) ? true : false;
  198. }
  199. //--------------------------------------------------------------------------------------------------------------
  200. /**
  201. * Return the closest active player to the given position.
  202. * If 'distance' is non-NULL, the distance to the closest player is returned in it.
  203. */
  204. extern CBasePlayer *UTIL_GetClosestPlayer( const Vector &pos, float *distance )
  205. {
  206. CBasePlayer *closePlayer = NULL;
  207. float closeDistSq = 999999999999.9f;
  208. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  209. {
  210. CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
  211. if (!IsEntityValid( player ))
  212. continue;
  213. if (!player->IsAlive())
  214. continue;
  215. Vector playerOrigin = GetCentroid( player );
  216. float distSq = (playerOrigin - pos).LengthSqr();
  217. if (distSq < closeDistSq)
  218. {
  219. closeDistSq = distSq;
  220. closePlayer = static_cast<CBasePlayer *>( player );
  221. }
  222. }
  223. if (distance)
  224. *distance = (float)sqrt( closeDistSq );
  225. return closePlayer;
  226. }
  227. //--------------------------------------------------------------------------------------------------------------
  228. /**
  229. * Return the closest active player on the given team to the given position.
  230. * If 'distance' is non-NULL, the distance to the closest player is returned in it.
  231. */
  232. extern CBasePlayer *UTIL_GetClosestPlayer( const Vector &pos, int team, float *distance )
  233. {
  234. CBasePlayer *closePlayer = NULL;
  235. float closeDistSq = 999999999999.9f;
  236. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  237. {
  238. CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
  239. if (!IsEntityValid( player ))
  240. continue;
  241. if (!player->IsAlive())
  242. continue;
  243. if (player->GetTeamNumber() != team)
  244. continue;
  245. Vector playerOrigin = GetCentroid( player );
  246. float distSq = (playerOrigin - pos).LengthSqr();
  247. if (distSq < closeDistSq)
  248. {
  249. closeDistSq = distSq;
  250. closePlayer = static_cast<CBasePlayer *>( player );
  251. }
  252. }
  253. if (distance)
  254. *distance = (float)sqrt( closeDistSq );
  255. return closePlayer;
  256. }
  257. //--------------------------------------------------------------------------------------------------------------
  258. // Takes the bot pointer and constructs the net name using the current bot name prefix.
  259. void UTIL_ConstructBotNetName( char *name, int nameLength, const BotProfile *profile )
  260. {
  261. if (profile == NULL)
  262. {
  263. name[0] = 0;
  264. return;
  265. }
  266. // if there is no bot prefix just use the profile name.
  267. if ((cv_bot_prefix.GetString() == NULL) || (strlen(cv_bot_prefix.GetString()) == 0))
  268. {
  269. Q_strncpy( name, profile->GetName(), nameLength );
  270. return;
  271. }
  272. // find the highest difficulty
  273. const char *diffStr = BotDifficultyName[0];
  274. for ( int i=BOT_EXPERT; i>0; --i )
  275. {
  276. if ( profile->IsDifficulty( (BotDifficultyType)i ) )
  277. {
  278. diffStr = BotDifficultyName[i];
  279. break;
  280. }
  281. }
  282. const char *weaponStr = NULL;
  283. if ( profile->GetWeaponPreferenceCount() )
  284. {
  285. weaponStr = profile->GetWeaponPreferenceAsString( 0 );
  286. const char *translatedAlias = GetTranslatedWeaponAlias( weaponStr );
  287. char wpnName[128];
  288. Q_snprintf( wpnName, sizeof( wpnName ), "weapon_%s", translatedAlias );
  289. WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( wpnName );
  290. if ( hWpnInfo != GetInvalidWeaponInfoHandle() )
  291. {
  292. CCSWeaponInfo *pWeaponInfo = dynamic_cast< CCSWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) );
  293. if ( pWeaponInfo )
  294. {
  295. CSWeaponType weaponType = pWeaponInfo->GetWeaponType();
  296. weaponStr = WeaponClassAsString( weaponType );
  297. }
  298. }
  299. }
  300. if ( !weaponStr )
  301. {
  302. weaponStr = "";
  303. }
  304. char skillStr[16];
  305. Q_snprintf( skillStr, sizeof( skillStr ), "%.0f", profile->GetSkill()*100 );
  306. char temp[MAX_PLAYER_NAME_LENGTH*2];
  307. char prefix[MAX_PLAYER_NAME_LENGTH*2];
  308. Q_strncpy( temp, cv_bot_prefix.GetString(), sizeof( temp ) );
  309. Q_StrSubst( temp, "<difficulty>", diffStr, prefix, sizeof( prefix ) );
  310. Q_StrSubst( prefix, "<weaponclass>", weaponStr, temp, sizeof( temp ) );
  311. Q_StrSubst( temp, "<skill>", skillStr, prefix, sizeof( prefix ) );
  312. Q_snprintf( name, nameLength, "%s %s", prefix, profile->GetName() );
  313. }
  314. //--------------------------------------------------------------------------------------------------------------
  315. /**
  316. * Return true if anyone on the given team can see the given spot
  317. */
  318. bool UTIL_IsVisibleToTeam( const Vector &spot, int team )
  319. {
  320. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  321. {
  322. CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
  323. if (player == NULL)
  324. continue;
  325. if (!player->IsAlive())
  326. continue;
  327. if (player->GetTeamNumber() != team)
  328. continue;
  329. trace_t result;
  330. UTIL_TraceLine( player->EyePosition(), spot, CONTENTS_SOLID, player, COLLISION_GROUP_NONE, &result );
  331. if ( result.fraction == 1.0f )
  332. return true;
  333. }
  334. return false;
  335. }
  336. //--------------------------------------------------------------------------------------------------------------
  337. /**
  338. * Return true if anyone on the given team can see the given spot
  339. */
  340. bool UTIL_IsRandomSpawnFarEnoughAwayFromTeam( const Vector &spot, int team )
  341. {
  342. if ( mp_randomspawn_dist.GetInt() <= 0 )
  343. return true;
  344. if ( mp_randomspawn_los.GetInt() == 0 )
  345. return true;
  346. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  347. {
  348. CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
  349. if (player == NULL)
  350. continue;
  351. if (!player->IsAlive())
  352. continue;
  353. if (player->GetTeamNumber() != team)
  354. continue;
  355. if ( mp_randomspawn_dist.GetInt() > 0 && (player->GetAbsOrigin()).DistTo( spot ) < mp_randomspawn_dist.GetInt() )
  356. {
  357. //NDebugOverlay::Line( player->EyePosition(), spot + Vector( 0, 0, 32 ), 255, 0, 0, true, 4 );
  358. //NDebugOverlay::Line( spot, spot + Vector( 0, 0, 64 ), 255, 128, 0, true, 4 );
  359. continue;
  360. }
  361. else
  362. {
  363. return true;
  364. }
  365. }
  366. return false;
  367. }
  368. //------------------------------------------------------------------------------------------------------------
  369. void UTIL_DrawBeamFromEnt( int i, Vector vecEnd, int iLifetime, byte bRed, byte bGreen, byte bBlue )
  370. {
  371. /* BOTPORT: What is the replacement for MESSAGE_BEGIN?
  372. MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecEnd ); // vecEnd = origin???
  373. WRITE_BYTE( TE_BEAMENTPOINT );
  374. WRITE_SHORT( i );
  375. WRITE_COORD( vecEnd.x );
  376. WRITE_COORD( vecEnd.y );
  377. WRITE_COORD( vecEnd.z );
  378. WRITE_SHORT( s_iBeamSprite );
  379. WRITE_BYTE( 0 ); // startframe
  380. WRITE_BYTE( 0 ); // framerate
  381. WRITE_BYTE( iLifetime ); // life
  382. WRITE_BYTE( 10 ); // width
  383. WRITE_BYTE( 0 ); // noise
  384. WRITE_BYTE( bRed ); // r, g, b
  385. WRITE_BYTE( bGreen ); // r, g, b
  386. WRITE_BYTE( bBlue ); // r, g, b
  387. WRITE_BYTE( 255 ); // brightness
  388. WRITE_BYTE( 0 ); // speed
  389. MESSAGE_END();
  390. */
  391. }
  392. //------------------------------------------------------------------------------------------------------------
  393. void UTIL_DrawBeamPoints( Vector vecStart, Vector vecEnd, int iLifetime, byte bRed, byte bGreen, byte bBlue )
  394. {
  395. NDebugOverlay::Line( vecStart, vecEnd, bRed, bGreen, bBlue, true, 0.1f );
  396. /*
  397. MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecStart );
  398. WRITE_BYTE( TE_BEAMPOINTS );
  399. WRITE_COORD( vecStart.x );
  400. WRITE_COORD( vecStart.y );
  401. WRITE_COORD( vecStart.z );
  402. WRITE_COORD( vecEnd.x );
  403. WRITE_COORD( vecEnd.y );
  404. WRITE_COORD( vecEnd.z );
  405. WRITE_SHORT( s_iBeamSprite );
  406. WRITE_BYTE( 0 ); // startframe
  407. WRITE_BYTE( 0 ); // framerate
  408. WRITE_BYTE( iLifetime ); // life
  409. WRITE_BYTE( 10 ); // width
  410. WRITE_BYTE( 0 ); // noise
  411. WRITE_BYTE( bRed ); // r, g, b
  412. WRITE_BYTE( bGreen ); // r, g, b
  413. WRITE_BYTE( bBlue ); // r, g, b
  414. WRITE_BYTE( 255 ); // brightness
  415. WRITE_BYTE( 0 ); // speed
  416. MESSAGE_END();
  417. */
  418. }
  419. //------------------------------------------------------------------------------------------------------------
  420. void CONSOLE_ECHO( char * pszMsg, ... )
  421. {
  422. va_list argptr;
  423. static char szStr[1024];
  424. va_start( argptr, pszMsg );
  425. vsprintf( szStr, pszMsg, argptr );
  426. va_end( argptr );
  427. Msg( "%s", szStr );
  428. }
  429. //------------------------------------------------------------------------------------------------------------
  430. void BotPrecache( void )
  431. {
  432. s_iBeamSprite = CBaseEntity::PrecacheModel( "sprites/smoke.vmt" );
  433. }
  434. //------------------------------------------------------------------------------------------------------------
  435. #define COS_TABLE_SIZE 256
  436. static float cosTable[ COS_TABLE_SIZE ];
  437. void InitBotTrig( void )
  438. {
  439. for( int i=0; i<COS_TABLE_SIZE; ++i )
  440. {
  441. float angle = (float)(2.0f * M_PI * i / (float)(COS_TABLE_SIZE-1));
  442. cosTable[i] = (float)cos( angle );
  443. }
  444. }
  445. float BotCOS( float angle )
  446. {
  447. angle = AngleNormalizePositive( angle );
  448. int i = (int)( angle * (COS_TABLE_SIZE-1) / 360.0f );
  449. return cosTable[i];
  450. }
  451. float BotSIN( float angle )
  452. {
  453. angle = AngleNormalizePositive( angle - 90 );
  454. int i = (int)( angle * (COS_TABLE_SIZE-1) / 360.0f );
  455. return cosTable[i];
  456. }
  457. //--------------------------------------------------------------------------------------------------------------
  458. /**
  459. * Send a "hint" message to all players, dead or alive.
  460. */
  461. void HintMessageToAllPlayers( const char *message )
  462. {
  463. hudtextparms_t textParms;
  464. textParms.x = -1.0f;
  465. textParms.y = -1.0f;
  466. textParms.fadeinTime = 1.0f;
  467. textParms.fadeoutTime = 5.0f;
  468. textParms.holdTime = 5.0f;
  469. textParms.fxTime = 0.0f;
  470. textParms.r1 = 100;
  471. textParms.g1 = 255;
  472. textParms.b1 = 100;
  473. textParms.r2 = 255;
  474. textParms.g2 = 255;
  475. textParms.b2 = 255;
  476. textParms.effect = 0;
  477. textParms.channel = 0;
  478. UTIL_HudMessageAll( textParms, message );
  479. }
  480. //--------------------------------------------------------------------------------------------------------------------
  481. /**
  482. * Return true if moving from "start" to "finish" will cross a player's line of fire.
  483. * The path from "start" to "finish" is assumed to be a straight line.
  484. * "start" and "finish" are assumed to be points on the ground.
  485. */
  486. bool IsCrossingLineOfFire( const Vector &start, const Vector &finish, CBaseEntity *ignore, int ignoreTeam )
  487. {
  488. for ( int p=1; p <= gpGlobals->maxClients; ++p )
  489. {
  490. CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( p ) );
  491. if (!IsEntityValid( player ))
  492. continue;
  493. if (player == ignore)
  494. continue;
  495. if (!player->IsAlive())
  496. continue;
  497. if (ignoreTeam && player->GetTeamNumber() == ignoreTeam)
  498. continue;
  499. // compute player's unit aiming vector
  500. Vector viewForward;
  501. AngleVectors( player->GetFinalAimAngle(), &viewForward );
  502. const float longRange = 5000.0f;
  503. Vector playerOrigin = GetCentroid( player );
  504. Vector playerTarget = playerOrigin + longRange * viewForward;
  505. Vector result( 0, 0, 0 );
  506. if (IsIntersecting2D( start, finish, playerOrigin, playerTarget, &result ))
  507. {
  508. // simple check to see if intersection lies in the Z range of the path
  509. float loZ, hiZ;
  510. if (start.z < finish.z)
  511. {
  512. loZ = start.z;
  513. hiZ = finish.z;
  514. }
  515. else
  516. {
  517. loZ = finish.z;
  518. hiZ = start.z;
  519. }
  520. if (result.z >= loZ && result.z <= hiZ + HumanHeight)
  521. return true;
  522. }
  523. }
  524. return false;
  525. }
  526. //--------------------------------------------------------------------------------------------------------------
  527. /**
  528. * Performs a simple case-insensitive string comparison, honoring trailing * wildcards
  529. */
  530. bool WildcardMatch( const char *query, const char *test )
  531. {
  532. if ( !query || !test )
  533. return false;
  534. while ( *test && *query )
  535. {
  536. char nameChar = *test;
  537. char queryChar = *query;
  538. if ( tolower(nameChar) != tolower(queryChar) ) // case-insensitive
  539. break;
  540. ++test;
  541. ++query;
  542. }
  543. if ( *query == 0 && *test == 0 )
  544. return true;
  545. // Support trailing *
  546. if ( *query == '*' )
  547. return true;
  548. return false;
  549. }