.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.
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;
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;
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);
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)
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);
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)
{
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))