csrd_bot_swap.sp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /**
  2. * [CSRD] Bot Team Trading
  3. *
  4. * Allows players to switch between balanced teams if a bot is able to switch in their place.
  5. * Removes the need to switch to Spectators before switching to the desired team.
  6. *
  7. * Works with the built-in manager and Doctor McKay's Bot Manager.
  8. * Intended for use with "fill" quota mode.
  9. */
  10. #pragma semicolon 1
  11. #include <sourcemod>
  12. #include <tf2_stocks>
  13. #include <printvalvetranslation>
  14. #pragma newdecls required
  15. #define PLUGIN_VERSION "0.0.1"
  16. public Plugin myinfo = {
  17. name = "[CSRD] Cross-Team Bot Swap",
  18. author = "nosoop",
  19. description = "Allow switching on balanced teams if a bot can be swapped too.",
  20. version = PLUGIN_VERSION,
  21. url = "https://git.csrd.science/"
  22. }
  23. /**
  24. * TODO: make it so only replicate disabled balance check if other team has bots?
  25. */
  26. public void OnPluginStart() {
  27. // TODO maybe call on post-round team switch??
  28. HookEvent("player_team", OnTeamStateChange, EventHookMode_PostNoCopy);
  29. AddCommandListener(OnTeamChangeRequested, "jointeam");
  30. }
  31. public void OnPluginEnd() {
  32. ConVar unbalanceLimit = FindConVar("mp_teams_unbalance_limit");
  33. for (int i = 1; i <= MaxClients; i++) {
  34. if (IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
  35. unbalanceLimit.ReplicateToClient(i, "1");
  36. }
  37. }
  38. }
  39. public void OnConfigsExecuted() {
  40. OnTeamStateChangePost(true);
  41. }
  42. public void OnTeamStateChange(Event event, const char[] name, bool dontBroadcast) {
  43. // frame delay to ensure players are in correct team
  44. RequestFrame(OnTeamStateChangePost);
  45. }
  46. /**
  47. * Called when the number of players on a team change.
  48. */
  49. public void OnTeamStateChangePost(any data) {
  50. ConVar unbalanceLimit = FindConVar("mp_teams_unbalance_limit");
  51. // if no unbalance limit, don't bother
  52. if (!unbalanceLimit.BoolValue) {
  53. return;
  54. }
  55. // First pass: set unbalance limit to 1 on all human clients, count bots on teams.
  56. int nRedBots, nBlueBots;
  57. for (int i = 1; i <= MaxClients; i++) {
  58. if (IsClientConnected(i) && IsClientInGame(i)) {
  59. if (IsFakeClient(i)) {
  60. switch (TF2_GetClientTeam(i)) {
  61. case TFTeam_Red: {
  62. nRedBots++;
  63. }
  64. case TFTeam_Blue: {
  65. nBlueBots++;
  66. }
  67. }
  68. } else {
  69. unbalanceLimit.ReplicateToClient(i, "1");
  70. }
  71. }
  72. }
  73. // Second pass: If teams are even, allow players to switch if opposite team has bots.
  74. if (TF2_GetTeamClientCount(TFTeam_Red) == TF2_GetTeamClientCount(TFTeam_Blue)) {
  75. for (int i = 1; i <= MaxClients; i++) {
  76. if (IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
  77. TFTeam team = TF2_GetClientTeam(i);
  78. if (team == TFTeam_Red && nBlueBots) {
  79. unbalanceLimit.ReplicateToClient(i, "2");
  80. } else if (team == TFTeam_Blue && nRedBots) {
  81. unbalanceLimit.ReplicateToClient(i, "2");
  82. }
  83. }
  84. }
  85. }
  86. }
  87. public Action OnTeamChangeRequested(int client, const char[] name, int argc) {
  88. if (TF2_GetTeamClientCount(TFTeam_Red) == TF2_GetTeamClientCount(TFTeam_Blue)) {
  89. TFTeam team = TF2_GetClientTeam(client);
  90. if (team != TFTeam_Red && team != TFTeam_Blue) {
  91. return Plugin_Continue;
  92. }
  93. char argstring[64];
  94. GetCmdArgString(argstring, sizeof(argstring));
  95. if (StrEqual(argstring, "red")) {
  96. SwapFakeClient(TFTeam_Red);
  97. } else if (StrEqual(argstring, "blue")) {
  98. SwapFakeClient(TFTeam_Blue);
  99. }
  100. // if switching to spec, let bot manager deal with filling in a player
  101. }
  102. // team change command will succeed now that a bot was swapped
  103. return Plugin_Continue;
  104. }
  105. /**
  106. * Moves a bot from one playing team to the other.
  107. */
  108. void SwapFakeClient(TFTeam sourceTeam) {
  109. if (sourceTeam != TFTeam_Red && sourceTeam != TFTeam_Blue) {
  110. ThrowError("Attempting to swap fake client off of a non-playing team.");
  111. }
  112. int[] bots = new int[TF2_GetTeamClientCount(sourceTeam)];
  113. int nBots;
  114. for (int i = 1; i <= MaxClients; i++) {
  115. if (IsClientInGame(i) && IsFakeClient(i) && TF2_GetClientTeam(i) == sourceTeam) {
  116. bots[nBots++] = i;
  117. }
  118. }
  119. if (nBots) {
  120. int bot = bots[GetRandomInt(0, nBots - 1)];
  121. TFTeam newTeam = sourceTeam == TFTeam_Red? TFTeam_Blue : TFTeam_Red;
  122. TF2_ChangeClientTeam(bot, newTeam);
  123. TF2_RespawnPlayer(bot);
  124. // delay message so it gets displayed after the player switches
  125. RequestFrame(BotSwapMessage, GetClientUserId(bot));
  126. }
  127. }
  128. /**
  129. * Displays a "<player> was moved to the other team for game balance" message.
  130. */
  131. public void BotSwapMessage(int userid) {
  132. int client = GetClientOfUserId(userid);
  133. if (client) {
  134. char name[MAX_NAME_LENGTH];
  135. GetClientName(client, name, sizeof(name));
  136. PrintValveTranslationToAll(Destination_Chat, "#game_player_was_team_balanced", name);
  137. }
  138. }
  139. //
  140. stock int TF2_GetTeamClientCount(TFTeam team) {
  141. return GetTeamClientCount(view_as<int>(team));
  142. }