]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/teamplay.qc
Merge branch 'master' into z411/bai-server
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / teamplay.qc
index 1cfdbf4a0538da331630210cdfbc92a4ef4333ec..8ab200b8bde816d60de4c1e039c9808b2c0fd236 100644 (file)
@@ -31,6 +31,7 @@ const int TEAM_NOT_ALLOWED = -1;
 .int m_team_balance_state; ///< Holds the state of the team balance entity.
 .entity m_team_balance_team[NUM_TEAMS]; ///< ???
 
+.string m_team_name; // z411 team name
 .float m_team_score; ///< The score of the team.
 .int m_num_players; ///< Number of players (both humans and bots) in a team.
 .int m_num_bots; ///< Number of bots in a team.
@@ -72,6 +73,16 @@ entity Team_GetTeam(int team_num)
        return g_team_entities[Team_TeamToIndex(team_num) - 1];
 }
 
+string Team_GetTeamName(entity team_ent)
+{
+       return team_ent.m_team_name;
+}
+
+void Team_SetTeamName(entity team_ent, string name)
+{
+       team_ent.m_team_name = name;
+}
+
 float Team_GetTeamScore(entity team_ent)
 {
        return team_ent.m_team_score;
@@ -92,6 +103,16 @@ void Team_SetNumberOfAlivePlayers(entity team_ent, int number)
        team_ent.m_num_players_alive = number;
 }
 
+int Team_GetNumberOfPlayers(entity team_ent)
+{
+       return team_ent.m_num_players;
+}
+
+void Team_SetNumberOfPlayers(entity team_ent, int number)
+{
+       team_ent.m_num_players = number;
+}
+
 int Team_GetWinnerAliveTeam()
 {
        int winner = 0;
@@ -234,6 +255,21 @@ bool Player_SetTeamIndex(entity player, int index)
        return true;
 }
 
+entity SpectatorWantsJoin(entity this)
+{
+       FOREACH_CLIENT(IS_REAL_CLIENT(it), {
+               if(it == this) continue;
+               if(it.wants_join) {
+                       LOG_DEBUGF("Player is waiting to join: %s", it.netname);
+                       return it;
+               }
+       });
+
+       // No players waiting to join
+       LOG_DEBUG("No players waiting to join.");
+       return NULL;
+}
+
 bool SetPlayerTeam(entity player, int team_index, int type)
 {
        int old_team_index = Entity_GetTeamIndex(player);
@@ -252,7 +288,18 @@ bool SetPlayerTeam(entity player, int team_index, int type)
                        TeamBalance_AutoBalanceBots();
 
                if (team_index != -1)
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(player.team, INFO_JOIN_PLAY_TEAM), player.netname);
+               {
+                       if (!IS_BOT_CLIENT(player) && IS_QUEUE_NEEDED(player) && !SpectatorWantsJoin(player))
+                       {
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(player.team, INFO_JOIN_WANTS_TEAM), player.netname);
+                               Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_JOIN_PREVENT_QUEUE);
+                               player.wants_join = team_index; // Player queued to join
+                       }
+                       else
+                       {
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(player.team, INFO_JOIN_PLAY_TEAM), player.netname);
+                       }
+               }
        }
 
        if (team_index == -1)
@@ -263,6 +310,12 @@ bool SetPlayerTeam(entity player, int team_index, int type)
                        Kill_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CPID_IDLING);
                        CS(player).idlekick_lasttimeleft = 0;
                }
+               else if (player.wants_join)
+               {
+                       player.wants_join = 0;
+                       player.team_selected = false;
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_QUEUE, player.netname);
+               }
                else if (!CS(player).just_joined && player.frags != FRAGS_SPECTATOR)
                {
                        Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, player.netname);
@@ -657,6 +710,130 @@ int TeamBalance_GetAllowedTeams(entity balance)
        return result;
 }
 
+bool TeamBalance_AreEqual(entity ignore, bool would_leave)
+{
+       entity balance = TeamBalance_CheckAllowedTeams(ignore);
+       TeamBalance_GetTeamCounts(balance, ignore);
+
+       bool equality = true;
+       int total;
+       int prev_total = 0;
+       int bots = 0;
+
+       for(int i = 1; i <= AVAILABLE_TEAMS; ++i)
+       {
+               total = TeamBalance_GetTeamFromIndex(balance, i).m_num_players;
+               bots += TeamBalance_GetTeamFromIndex(balance, i).m_num_bots;
+               if(i > 1 && total != prev_total)
+               {
+                       equality = false;
+                       break;
+               }
+               prev_total = total;
+       }
+       TeamBalance_Destroy(balance);
+
+       // Ignore if there are "ghost" bots that would leave if anyone joined
+       if (would_leave && bots > autocvar_bot_number)
+               return false;
+
+       return equality;
+}
+
+entity remove_countdown;
+entity remove_player;
+int remove_timeleft;
+
+void Remove_Countdown(entity this)
+{
+       if(remove_timeleft <= 0 || TeamBalance_AreEqual(NULL, false))
+       {
+               if(remove_timeleft <= 0)
+               {
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_REMOVE, playername(remove_player.netname, remove_player.team, true));
+                       PutObserverInServer(remove_player, true, true);
+               }
+
+               Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_REMOVE);
+
+               delete(this);
+               remove_countdown = NULL;
+               remove_player = NULL;
+               remove_timeleft = 0;
+
+               TeamBalance_RemoveExcessPlayers(NULL); // Check again for excess players in case someone also left while in countdown
+               return;
+       }
+
+       --remove_timeleft;
+       this.nextthink = time + 1;
+}
+
+void TeamBalance_RemoveExcessPlayers(entity ignore)
+{
+       if(AVAILABLE_TEAMS != 2 || autocvar_g_campaign) return;
+
+       entity balance = TeamBalance_CheckAllowedTeams(ignore);
+       TeamBalance_GetTeamCounts(balance, ignore);
+
+       int min = 0;
+
+       for(int i = 1; i <= AVAILABLE_TEAMS; ++i)
+       {
+               int cur = TeamBalance_GetTeamFromIndex(balance, i).m_num_players;
+               if(i == 1 || cur < min)
+                       min = cur;
+       }
+
+       for(int tmi = 1; tmi <= AVAILABLE_TEAMS; ++tmi)
+       {
+               int cur = TeamBalance_GetTeamFromIndex(balance, tmi).m_num_players;
+               if(cur > 0 && cur > min) // If this team has excess players
+               {
+                       // Get newest player
+                       int latest_join = 0;
+                       entity latest_join_pl = NULL;
+
+                       FOREACH_CLIENT(IS_REAL_CLIENT(it) || INGAME(it), {
+                               if(it.team == Team_IndexToTeam(tmi) && CS(it).startplaytime > latest_join)
+                               {
+                                       latest_join = CS(it).startplaytime;
+                                       latest_join_pl = it;
+                               }
+                       });
+
+                       // Force player to spectate
+                       if(latest_join_pl)
+                       {
+                               // Send player to spectate
+                               if(autocvar_g_balance_teams_remove_wait)
+                               {
+                                       // Give a warning before moving to spect
+                                       remove_player = latest_join_pl;
+                                       remove_timeleft = autocvar_g_balance_teams_remove_wait;
+
+                                       if (!remove_countdown)
+                                       {
+                                               remove_countdown = new_pure(remove_countdown);
+                                               setthink(remove_countdown, Remove_Countdown);
+                                               remove_countdown.nextthink = time;
+
+                                               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MOVETOSPEC_REMOVE, playername(remove_player.netname, remove_player.team, true), remove_timeleft);
+                                       }
+                               }
+                               else
+                               {
+                                       // Move to spects immediately
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_REMOVE, latest_join_pl.netname);
+                                       PutObserverInServer(latest_join_pl, true, true);
+                               }
+                       }
+               }
+       }
+
+       TeamBalance_Destroy(balance);
+}
+
 bool TeamBalance_IsTeamAllowed(entity balance, int index)
 {
        if (balance == NULL)
@@ -708,6 +885,10 @@ void TeamBalance_GetTeamCounts(entity balance, entity ignore)
                        {
                                continue;
                        }
+                       if (it.wants_join)
+                       {
+                               continue; // Queued players aren't actually in the game.
+                       }
                        int team_num;
                        // TODO: Reconsider when the player is truly on the team.
                        if (IS_CLIENT(it) || INGAME(it))