123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- /**
- * [CSRD] Bot Team Trading
- *
- * Allows players to switch between balanced teams if a bot is able to switch in their place.
- * Removes the need to switch to Spectators before switching to the desired team.
- *
- * Works with the built-in manager and Doctor McKay's Bot Manager.
- * Intended for use with "fill" quota mode.
- */
- #pragma semicolon 1
- #include <sourcemod>
- #include <tf2_stocks>
- #include <printvalvetranslation>
- #pragma newdecls required
- #define PLUGIN_VERSION "0.0.1"
- public Plugin myinfo = {
- name = "[CSRD] Cross-Team Bot Swap",
- author = "nosoop",
- description = "Allow switching on balanced teams if a bot can be swapped too.",
- version = PLUGIN_VERSION,
- url = "https://git.csrd.science/"
- }
- /**
- * TODO: make it so only replicate disabled balance check if other team has bots?
- */
- public void OnPluginStart() {
- // TODO maybe call on post-round team switch??
- HookEvent("player_team", OnTeamStateChange, EventHookMode_PostNoCopy);
-
- AddCommandListener(OnTeamChangeRequested, "jointeam");
- }
- public void OnPluginEnd() {
- ConVar unbalanceLimit = FindConVar("mp_teams_unbalance_limit");
-
- for (int i = 1; i <= MaxClients; i++) {
- if (IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
- unbalanceLimit.ReplicateToClient(i, "1");
- }
- }
- }
- public void OnConfigsExecuted() {
- OnTeamStateChangePost(true);
- }
- public void OnTeamStateChange(Event event, const char[] name, bool dontBroadcast) {
- // frame delay to ensure players are in correct team
- RequestFrame(OnTeamStateChangePost);
- }
- /**
- * Called when the number of players on a team change.
- *
- * Updates the `mp_teams_unbalance_limit` value tramsmitted to clients, so they can use the team
- * switch UI and freely switch to the other team if a bot is available for swapping.
- */
- public void OnTeamStateChangePost(any data) {
- ConVar unbalanceLimit = FindConVar("mp_teams_unbalance_limit");
-
- // if no unbalance limit, don't bother
- if (!unbalanceLimit.BoolValue) {
- return;
- }
-
- // First pass: set unbalance limit to 1 on all human clients, count bots on teams.
- int nRedBots, nBlueBots;
- for (int i = 1; i <= MaxClients; i++) {
- if (IsClientConnected(i) && IsClientInGame(i)) {
- if (IsFakeClient(i)) {
- switch (TF2_GetClientTeam(i)) {
- case TFTeam_Red: {
- nRedBots++;
- }
- case TFTeam_Blue: {
- nBlueBots++;
- }
- }
- } else {
- unbalanceLimit.ReplicateToClient(i, "1");
- }
- }
- }
-
- // Second pass: If teams are even, allow players to switch if opposite team has bots.
- if (TF2_GetTeamClientCount(TFTeam_Red) == TF2_GetTeamClientCount(TFTeam_Blue)) {
- for (int i = 1; i <= MaxClients; i++) {
- if (IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
- TFTeam team = TF2_GetClientTeam(i);
-
- if (team == TFTeam_Red && nBlueBots) {
- unbalanceLimit.ReplicateToClient(i, "2");
- } else if (team == TFTeam_Blue && nRedBots) {
- unbalanceLimit.ReplicateToClient(i, "2");
- }
- }
- }
- }
- }
- /**
- * Called when a player has requested to switch teams, before the game's logic happens.
- *
- * If the player is on a playing team and wants to switch to the other one, moves a bot from
- * their desired team to their current one. The game then processes the team change command and
- * moves them to their desired team.
- *
- * This won't quite work the way it should if some other plugin blocks the team change further
- * down in their event hook. A detour on CTFPlayer::HandleCommand_JoinTeam() would do its work
- * after most plugins process the command, but this hasn't been an issue on my setup.
- *
- */
- public Action OnTeamChangeRequested(int client, const char[] name, int argc) {
- if (TF2_GetTeamClientCount(TFTeam_Red) == TF2_GetTeamClientCount(TFTeam_Blue)) {
- TFTeam team = TF2_GetClientTeam(client);
- if (team != TFTeam_Red && team != TFTeam_Blue) {
- return Plugin_Continue;
- }
-
- char argstring[64];
- GetCmdArgString(argstring, sizeof(argstring));
-
- if (StrEqual(argstring, "red")) {
- SwapFakeClient(TFTeam_Red);
- } else if (StrEqual(argstring, "blue")) {
- SwapFakeClient(TFTeam_Blue);
- }
- // if switching to spec, let bot manager deal with filling in a player
- }
- // team change command will succeed now that a bot was swapped
- return Plugin_Continue;
- }
- /**
- * Moves a random bot from one playing team to the other.
- */
- void SwapFakeClient(TFTeam sourceTeam) {
- if (sourceTeam != TFTeam_Red && sourceTeam != TFTeam_Blue) {
- ThrowError("Attempting to swap fake client off of a non-playing team.");
- }
-
- int[] bots = new int[TF2_GetTeamClientCount(sourceTeam)];
-
- int nBots;
- for (int i = 1; i <= MaxClients; i++) {
- if (IsClientInGame(i) && IsFakeClient(i) && TF2_GetClientTeam(i) == sourceTeam) {
- bots[nBots++] = i;
- }
- }
-
- if (nBots) {
- int bot = bots[GetRandomInt(0, nBots - 1)];
- TFTeam newTeam = sourceTeam == TFTeam_Red? TFTeam_Blue : TFTeam_Red;
- TF2_ChangeClientTeam(bot, newTeam);
- TF2_RespawnPlayer(bot);
-
- // delay message so it gets displayed after the player switches
- RequestFrame(BotSwapMessage, GetClientUserId(bot));
- }
- }
- /**
- * Displays a "<player> was moved to the other team for game balance" message.
- */
- public void BotSwapMessage(int userid) {
- int client = GetClientOfUserId(userid);
-
- if (client) {
- char name[MAX_NAME_LENGTH];
- GetClientName(client, name, sizeof(name));
-
- PrintValveTranslationToAll(Destination_Chat, "#game_player_was_team_balanced", name);
- }
- }
- //
- stock int TF2_GetTeamClientCount(TFTeam team) {
- return GetTeamClientCount(view_as<int>(team));
- }
|