From: Lyberta Date: Thu, 22 Feb 2018 14:55:50 +0000 (+0300) Subject: Teamplay: Removed most global variables. X-Git-Tag: xonotic-v0.8.5~1953^2~44 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=cfed0689e0277396cc2058d14dabee2c93834dbe;p=xonotic%2Fxonotic-data.pk3dir.git Teamplay: Removed most global variables. --- diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc index 2637aeef2..04425c24b 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc @@ -1927,12 +1927,9 @@ MUTATOR_HOOKFUNCTION(ons, CheckAllowedTeams) // onslaught is special for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) { - switch(tmp_entity.team) + if (Team_IsValidTeam(tmp_entity.team)) { - case NUM_TEAM_1: c1 = 0; break; - case NUM_TEAM_2: c2 = 0; break; - case NUM_TEAM_3: c3 = 0; break; - case NUM_TEAM_4: c4 = 0; break; + M_ARGV(0, float) |= BIT(Team_TeamToNumber(tmp_entity.team) - 1); } } @@ -2163,11 +2160,7 @@ spawnfunc(onslaught_generator) void ons_ScoreRules() { CheckAllowedTeams(NULL); - int teams = 0; - if(c1 >= 0) teams |= BIT(0); - if(c2 >= 0) teams |= BIT(1); - if(c3 >= 0) teams |= BIT(2); - if(c4 >= 0) teams |= BIT(3); + int teams = GetAllowedTeams(); GameRules_scoring(teams, SFL_SORT_PRIO_PRIMARY, 0, { field_team(ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY); field(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); diff --git a/qcsrc/common/t_items.qc b/qcsrc/common/t_items.qc index 249615d2d..183c38d3b 100644 --- a/qcsrc/common/t_items.qc +++ b/qcsrc/common/t_items.qc @@ -631,11 +631,15 @@ float adjust_respawntime(float normal_respawntime) { CheckAllowedTeams(NULL); GetTeamCounts(NULL); int players = 0; - if (c1 != -1) players += c1; - if (c2 != -1) players += c2; - if (c3 != -1) players += c3; - if (c4 != -1) players += c4; - + for (int i = 1; i < 5; ++i) + { + entity team_ = Team_GetTeamFromIndex(i); + if (Team_IsAllowed(team_)) + { + players += Team_GetNumberOfPlayers(team_); + } + } + if (players >= 2) { return normal_respawntime * (r / (players + o) + l); } else { diff --git a/qcsrc/common/teams.qh b/qcsrc/common/teams.qh index 57d644c04..032aa38d6 100644 --- a/qcsrc/common/teams.qh +++ b/qcsrc/common/teams.qh @@ -143,12 +143,12 @@ bool Team_IsValidTeam(int team_) return false; } -/// \brief Returns whether team number is valid. -/// \param[in] number Team number to check. -/// \return True if team number is valid, false otherwise. -bool Team_IsValidNumber(int number) +/// \brief Returns whether the team index is valid. +/// \param[in] index Team index to check. +/// \return True if team index is valid, false otherwise. +bool Team_IsValidIndex(int index) { - switch (number) + switch (index) { case 1: case 2: diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc index a605fc068..2f579aaf8 100644 --- a/qcsrc/server/bot/default/bot.qc +++ b/qcsrc/server/bot/default/bot.qc @@ -465,12 +465,9 @@ void bot_removefromlargestteam() int thiscount = 0; - switch(it.team) + if (Team_IsValidTeam(it.team)) { - case NUM_TEAM_1: thiscount = c1; break; - case NUM_TEAM_2: thiscount = c2; break; - case NUM_TEAM_3: thiscount = c3; break; - case NUM_TEAM_4: thiscount = c4; break; + thiscount = Team_GetNumberOfPlayers(Team_GetTeam(it.team)); } if(thiscount > bestcount) diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index f64935330..4b9c70b26 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -1256,11 +1256,7 @@ void ClientConnect(entity this) if (teamplay) { CheckAllowedTeams(this); - int t = 0; - if (c1 >= 0) t |= BIT(0); - if (c2 >= 0) t |= BIT(1); - if (c3 >= 0) t |= BIT(2); - if (c4 >= 0) t |= BIT(3); + int t = GetAllowedTeams(); stuffcmd(this, sprintf("set _teams_available %d\n", t)); } else diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index 6de4507b1..0e2d92557 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -1073,7 +1073,7 @@ void GameCommand_moveplayer(float request, float argc) else if (team_id == 0) // auto team { CheckAllowedTeams(client); - team_id = Team_NumberToTeam(FindSmallestTeam(client, false)); + team_id = Team_NumberToTeam(FindBestTeam(client, false)); } else { @@ -1084,10 +1084,10 @@ void GameCommand_moveplayer(float request, float argc) // Check to see if the destination team is even available switch (team_id) { - case NUM_TEAM_1: if (c1 == -1) { LOG_INFO("Sorry, can't move player to red team if it doesn't exist."); return; } break; - case NUM_TEAM_2: if (c2 == -1) { LOG_INFO("Sorry, can't move player to blue team if it doesn't exist."); return; } break; - case NUM_TEAM_3: if (c3 == -1) { LOG_INFO("Sorry, can't move player to yellow team if it doesn't exist."); return; } break; - case NUM_TEAM_4: if (c4 == -1) { LOG_INFO("Sorry, can't move player to pink team if it doesn't exist."); return; } break; + case NUM_TEAM_1: if (Team_IsAllowed(Team_GetTeamFromIndex(1))) { LOG_INFO("Sorry, can't move player to red team if it doesn't exist."); return; } break; + case NUM_TEAM_2: if (Team_IsAllowed(Team_GetTeamFromIndex(2))) { LOG_INFO("Sorry, can't move player to blue team if it doesn't exist."); return; } break; + case NUM_TEAM_3: if (Team_IsAllowed(Team_GetTeamFromIndex(3))) { LOG_INFO("Sorry, can't move player to yellow team if it doesn't exist."); return; } break; + case NUM_TEAM_4: if (Team_IsAllowed(Team_GetTeamFromIndex(4))) { LOG_INFO("Sorry, can't move player to pink team if it doesn't exist."); return; } break; default: LOG_INFO("Sorry, can't move player here if team ", destination, " doesn't exist."); return; @@ -1367,10 +1367,13 @@ void GameCommand_shuffleteams(float request) int number_of_teams = 0; CheckAllowedTeams(NULL); - if (c1 >= 0) number_of_teams = max(1, number_of_teams); - if (c2 >= 0) number_of_teams = max(2, number_of_teams); - if (c3 >= 0) number_of_teams = max(3, number_of_teams); - if (c4 >= 0) number_of_teams = max(4, number_of_teams); + for (int i = 1; i < 5; ++i) + { + if (Team_IsAllowed(Team_GetTeamFromIndex(i))) + { + number_of_teams = max(i, number_of_teams); + } + } int team_index = 0; FOREACH_CLIENT_RANDOM(IS_PLAYER(it) || it.caplayer, { diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 060e1ce55..a25c16be4 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -27,8 +27,6 @@ float bots_would_leave; void UpdateFrags(entity player, int f); .float totalfrags; -float team1_score, team2_score, team3_score, team4_score; - // flag set on worldspawn so that the code knows if it is dedicated or not float server_is_dedicated; diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index a3bb6eb21..5c1724ce3 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -1690,10 +1690,11 @@ float WinningCondition_Scores(float limit, float leadlimit) if(teamplay) { - team1_score = TeamScore_GetCompareValue(NUM_TEAM_1); - team2_score = TeamScore_GetCompareValue(NUM_TEAM_2); - team3_score = TeamScore_GetCompareValue(NUM_TEAM_3); - team4_score = TeamScore_GetCompareValue(NUM_TEAM_4); + for (int i = 1; i < 5; ++i) + { + Team_SetTeamScore(Team_GetTeamFromIndex(i), + TeamScore_GetCompareValue(Team_NumberToTeam(i))); + } } ClearWinners(); @@ -1763,30 +1764,32 @@ float WinningCondition_RanOutOfSpawns() if(!some_spawn_has_been_used) return WINNING_NO; - team1_score = team2_score = team3_score = team4_score = 0; - - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { - switch(it.team) + for (int i = 1; i < 5; ++i) + { + Team_SetTeamScore(Team_GetTeamFromIndex(i), 0); + } + + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), + { + if (Team_IsValidTeam(it.team)) { - case NUM_TEAM_1: team1_score = 1; break; - case NUM_TEAM_2: team2_score = 1; break; - case NUM_TEAM_3: team3_score = 1; break; - case NUM_TEAM_4: team4_score = 1; break; + Team_SetTeamScore(Team_GetTeam(it.team), 1); } }); IL_EACH(g_spawnpoints, true, { - switch(it.team) + if (Team_IsValidTeam(it.team)) { - case NUM_TEAM_1: team1_score = 1; break; - case NUM_TEAM_2: team2_score = 1; break; - case NUM_TEAM_3: team3_score = 1; break; - case NUM_TEAM_4: team4_score = 1; break; + Team_SetTeamScore(Team_GetTeam(it.team), 1); } }); ClearWinners(); + float team1_score = Team_GetTeamScore(Team_GetTeamFromIndex(1)); + float team2_score = Team_GetTeamScore(Team_GetTeamFromIndex(2)); + float team3_score = Team_GetTeamScore(Team_GetTeamFromIndex(3)); + float team4_score = Team_GetTeamScore(Team_GetTeamFromIndex(4)); if(team1_score + team2_score + team3_score + team4_score == 0) { checkrules_equality = true; @@ -1796,20 +1799,28 @@ float WinningCondition_RanOutOfSpawns() { float t, i; if(team1_score) - t = NUM_TEAM_1; + t = 1; else if(team2_score) - t = NUM_TEAM_2; + t = 2; else if(team3_score) - t = NUM_TEAM_3; + t = 3; else // if(team4_score) - t = NUM_TEAM_4; + t = 4; CheckAllowedTeams(NULL); for(i = 0; i < MAX_TEAMSCORE; ++i) { - if(t != NUM_TEAM_1) if(c1 >= 0) TeamScore_AddToTeam(NUM_TEAM_1, i, -1000); - if(t != NUM_TEAM_2) if(c2 >= 0) TeamScore_AddToTeam(NUM_TEAM_2, i, -1000); - if(t != NUM_TEAM_3) if(c3 >= 0) TeamScore_AddToTeam(NUM_TEAM_3, i, -1000); - if(t != NUM_TEAM_4) if(c4 >= 0) TeamScore_AddToTeam(NUM_TEAM_4, i, -1000); + for (int j = 1; j < 5; ++j) + { + if (t == j) + { + continue; + } + if (!Team_IsAllowed(Team_GetTeamFromIndex(j))) + { + continue; + } + TeamScore_AddToTeam(Team_NumberToTeam(j), i, -1000); + } } AddWinners(team, t); diff --git a/qcsrc/server/mutators/mutator/gamemode_assault.qc b/qcsrc/server/mutators/mutator/gamemode_assault.qc index 70e266918..aa7137a26 100644 --- a/qcsrc/server/mutators/mutator/gamemode_assault.qc +++ b/qcsrc/server/mutators/mutator/gamemode_assault.qc @@ -579,7 +579,7 @@ MUTATOR_HOOKFUNCTION(as, PlayHitsound) MUTATOR_HOOKFUNCTION(as, CheckAllowedTeams) { // assault always has 2 teams - c1 = c2 = 0; + M_ARGV(0, float) = BIT(0) | BIT(1); return true; } diff --git a/qcsrc/server/mutators/mutator/gamemode_domination.qc b/qcsrc/server/mutators/mutator/gamemode_domination.qc index 38ef58b6c..c72d5fbfd 100644 --- a/qcsrc/server/mutators/mutator/gamemode_domination.qc +++ b/qcsrc/server/mutators/mutator/gamemode_domination.qc @@ -426,12 +426,9 @@ MUTATOR_HOOKFUNCTION(dom, CheckAllowedTeams) { if(head.netname != "") { - switch(head.team) + if (Team_IsValidTeam(head.team)) { - case NUM_TEAM_1: c1 = 0; break; - case NUM_TEAM_2: c2 = 0; break; - case NUM_TEAM_3: c3 = 0; break; - case NUM_TEAM_4: c4 = 0; break; + M_ARGV(0, float) |= BIT(Team_TeamToNumber(head.team) - 1); } } @@ -643,13 +640,7 @@ void dom_DelayedInit(entity this) // Do this check with a delay so we can wait f } CheckAllowedTeams(NULL); - //domination_teams = ((c4>=0) ? 4 : (c3>=0) ? 3 : 2); - - int teams = 0; - if(c1 >= 0) teams |= BIT(0); - if(c2 >= 0) teams |= BIT(1); - if(c3 >= 0) teams |= BIT(2); - if(c4 >= 0) teams |= BIT(3); + int teams = GetAllowedTeams(); domination_teams = teams; domination_roundbased = autocvar_g_domination_roundbased; diff --git a/qcsrc/server/scores_rules.qc b/qcsrc/server/scores_rules.qc index 8d87407e6..17f5de90a 100644 --- a/qcsrc/server/scores_rules.qc +++ b/qcsrc/server/scores_rules.qc @@ -65,10 +65,7 @@ void ScoreRules_generic() int teams = 0; if (teamplay) { CheckAllowedTeams(NULL); - if (c1 >= 0) teams |= BIT(0); - if (c2 >= 0) teams |= BIT(1); - if (c3 >= 0) teams |= BIT(2); - if (c4 >= 0) teams |= BIT(3); + teams = GetAllowedTeams(); } GameRules_scoring(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, {}); } diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index cf21de4bf..8b92f86b2 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -15,6 +15,367 @@ #include "../common/gamemodes/_mod.qh" #include "../common/teams.qh" +/// \brief Indicates that the player is not allowed to join a team. +const int TEAM_NOT_ALLOWED = -1; + +.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. +.entity m_lowest_human; ///< Human with the lowest score in a team. +.entity m_lowest_bot; ///< Bot with the lowest score in a team. + +entity g_team_entities[4]; ///< Holds global team entities. + +STATIC_INIT(g_team_entities) +{ + g_team_entities[0] = spawn(); + g_team_entities[1] = spawn(); + g_team_entities[2] = spawn(); + g_team_entities[3] = spawn(); +} + +entity Team_GetTeamFromIndex(int index) +{ + if (!Team_IsValidIndex(index)) + { + LOG_FATALF("Team_GetTeamFromIndex: Index is invalid: %f", index); + } + return g_team_entities[index - 1]; +} + +entity Team_GetTeam(int team_num) +{ + if (!Team_IsValidTeam(team_num)) + { + LOG_FATALF("Team_GetTeam: Value is invalid: %f", team_num); + } + return g_team_entities[Team_TeamToNumber(team_num) - 1]; +} + +float Team_GetTeamScore(entity team_) +{ + return team_.m_team_score; +} + +void Team_SetTeamScore(entity team_, float score) +{ + team_.m_team_score = score; +} + +void CheckAllowedTeams(entity for_whom) +{ + for (int i = 0; i < 4; ++i) + { + g_team_entities[i].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[i].m_num_bots = 0; + g_team_entities[i].m_lowest_human = NULL; + g_team_entities[i].m_lowest_bot = NULL; + } + + int teams_mask = 0; + string teament_name = string_null; + bool mutator_returnvalue = MUTATOR_CALLHOOK(CheckAllowedTeams, teams_mask, + teament_name, for_whom); + teams_mask = M_ARGV(0, float); + teament_name = M_ARGV(1, string); + if (mutator_returnvalue) + { + for (int i = 0; i < 4; ++i) + { + if (teams_mask & BIT(i)) + { + g_team_entities[i].m_num_players = 0; + } + } + } + + // find out what teams are allowed if necessary + if (teament_name) + { + entity head = find(NULL, classname, teament_name); + while (head) + { + if (Team_IsValidTeam(head.team)) + { + Team_GetTeam(head.team).m_num_players = 0; + } + head = find(head, classname, teament_name); + } + } + + // TODO: Balance quantity of bots across > 2 teams when bot_vs_human is set (and remove next line) + if (AvailableTeams() == 2) + if (autocvar_bot_vs_human && for_whom) + { + if (autocvar_bot_vs_human > 0) + { + // find last team available + if (IS_BOT_CLIENT(for_whom)) + { + if (Team_IsAllowed(g_team_entities[3])) + { + g_team_entities[2].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[1].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + } + else if (Team_IsAllowed(g_team_entities[2])) + { + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[1].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + } + else + { + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[2].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + } + // no further cases, we know at least 2 teams exist + } + else + { + if (Team_IsAllowed(g_team_entities[0])) + { + g_team_entities[1].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[2].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + } + else if (Team_IsAllowed(g_team_entities[1])) + { + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[2].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + } + else + { + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[1].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + } + // no further cases, bots have one of the teams + } + } + else + { + // find first team available + if (IS_BOT_CLIENT(for_whom)) + { + if (Team_IsAllowed(g_team_entities[0])) + { + g_team_entities[1].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[2].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + } + else if (Team_IsAllowed(g_team_entities[1])) + { + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[2].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + } + else + { + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[1].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + } + // no further cases, we know at least 2 teams exist + } + else + { + if (Team_IsAllowed(g_team_entities[3])) + { + g_team_entities[2].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[1].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + } + else if (Team_IsAllowed(g_team_entities[2])) + { + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[1].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + } + else + { + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[2].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + } + // no further cases, bots have one of the teams + } + } + } + + if (!for_whom) + { + return; + } + + // if player has a forced team, ONLY allow that one + if (for_whom.team_forced == NUM_TEAM_1 && Team_IsAllowed( + g_team_entities[0])) + { + g_team_entities[1].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[2].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + } + else if (for_whom.team_forced == NUM_TEAM_2 && Team_IsAllowed( + g_team_entities[1])) + { + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[2].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + } + else if (for_whom.team_forced == NUM_TEAM_3 && Team_IsAllowed( + g_team_entities[2])) + { + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[1].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[3].m_num_players = TEAM_NOT_ALLOWED; + } + else if (for_whom.team_forced == NUM_TEAM_4 && Team_IsAllowed( + g_team_entities[3])) + { + g_team_entities[0].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[1].m_num_players = TEAM_NOT_ALLOWED; + g_team_entities[2].m_num_players = TEAM_NOT_ALLOWED; + } +} + +int GetAllowedTeams() +{ + int result = 0; + for (int i = 0; i < 4; ++i) + { + if (Team_IsAllowed(g_team_entities[i])) + { + result |= BIT(i); + } + } + return result; +} + +bool Team_IsAllowed(entity team_) +{ + return team_.m_num_players != TEAM_NOT_ALLOWED; +} + +void GetTeamCounts(entity ignore) +{ + if (MUTATOR_CALLHOOK(GetTeamCounts) == true) + { + // Mutator has overriden the configuration. + for (int i = 0; i < 4; ++i) + { + entity team_ = g_team_entities[i]; + if (Team_IsAllowed(team_)) + { + MUTATOR_CALLHOOK(GetTeamCount, Team_NumberToTeam(i + 1), ignore, + team_.m_num_players, team_.m_num_bots, team_.m_lowest_human, + team_.m_lowest_bot); + team_.m_num_players = M_ARGV(2, float); + team_.m_num_bots = M_ARGV(3, float); + team_.m_lowest_human = M_ARGV(4, entity); + team_.m_lowest_bot = M_ARGV(5, entity); + } + } + } + else + { + // Manually count all players. + FOREACH_CLIENT(true, + { + if (it == ignore) + { + continue; + } + int team_num; + if (IS_PLAYER(it) || it.caplayer) + { + team_num = it.team; + } + else if (it.team_forced > 0) + { + team_num = it.team_forced; // reserve the spot + } + else + { + continue; + } + if (!Team_IsValidTeam(team_num)) + { + continue; + } + entity team_ = Team_GetTeam(team_num); + if (!Team_IsAllowed(team_)) + { + continue; + } + ++team_.m_num_players; + if (IS_BOT_CLIENT(it)) + { + ++team_.m_num_bots; + } + float temp_score = PlayerScore_Get(it, SP_SCORE); + if (!IS_BOT_CLIENT(it)) + { + if (team_.m_lowest_human == NULL) + { + team_.m_lowest_human = it; + continue; + } + if (temp_score < PlayerScore_Get(team_.m_lowest_human, + SP_SCORE)) + { + team_.m_lowest_human = it; + } + continue; + } + if (team_.m_lowest_bot == NULL) + { + team_.m_lowest_bot = it; + continue; + } + if (temp_score < PlayerScore_Get(team_.m_lowest_bot, SP_SCORE)) + { + team_.m_lowest_bot = it; + } + }); + } + + // if the player who has a forced team has not joined yet, reserve the spot + if (autocvar_g_campaign) + { + if (Team_IsValidIndex(autocvar_g_campaign_forceteam)) + { + entity team_ = Team_GetTeamFromIndex(autocvar_g_campaign_forceteam); + if (team_.m_num_players == team_.m_num_bots) + { + ++team_.m_num_players; + } + } + } +} + +int Team_GetNumberOfPlayers(entity team_) +{ + return team_.m_num_players; +} + +int Team_GetNumberOfBots(entity team_) +{ + return team_.m_num_bots; +} + +entity Team_GetLowestHuman(entity team_) +{ + return team_.m_lowest_human; +} + +entity Team_GetLowestBot(entity team_) +{ + return team_.m_lowest_bot; +} + void TeamchangeFrags(entity e) { PlayerScore_Clear(e); @@ -190,8 +551,8 @@ void KillPlayerForTeamChange(entity player) { return; } - Damage(player, player, player, 100000, DEATH_TEAMCHANGE.m_id, DMG_NOWEP, player.origin, - '0 0 0'); + Damage(player, player, player, 100000, DEATH_TEAMCHANGE.m_id, DMG_NOWEP, + player.origin, '0 0 0'); } bool SetPlayerTeamSimple(entity player, int team_num) @@ -209,16 +570,16 @@ bool SetPlayerTeamSimple(entity player, int team_num) // Mutator has blocked team change. return false; } - int old_team = player.team; + int old_team_num = player.team; SetPlayerColors(player, team_num - 1); - MUTATOR_CALLHOOK(Player_ChangedTeam, player, old_team, player.team); + MUTATOR_CALLHOOK(Player_ChangedTeam, player, old_team_num, player.team); return true; } -bool SetPlayerTeam(entity player, int destination_team, int source_team, - bool no_print) +bool SetPlayerTeam(entity player, int destination_team_index, + int source_team_index, bool no_print) { - int team_num = Team_NumberToTeam(destination_team); + int team_num = Team_NumberToTeam(destination_team_index); if (!SetPlayerTeamSimple(player, team_num)) { return false; @@ -228,410 +589,41 @@ bool SetPlayerTeam(entity player, int destination_team, int source_team, { return true; } - bprint(playername(player, false), "^7 has changed from ", Team_NumberToColoredFullName(source_team), "^7 to ", Team_NumberToColoredFullName(destination_team), "\n"); + bprint(playername(player, false), "^7 has changed from ", + Team_NumberToColoredFullName(source_team_index), "^7 to ", + Team_NumberToColoredFullName(destination_team_index), "\n"); return true; } -// set c1...c4 to show what teams are allowed -void CheckAllowedTeams(entity for_whom) -{ - int teams_mask = 0; - - c1 = c2 = c3 = c4 = -1; - num_bots_team1 = num_bots_team2 = num_bots_team3 = num_bots_team4 = 0; - - string teament_name = string_null; - - bool mutator_returnvalue = MUTATOR_CALLHOOK(CheckAllowedTeams, teams_mask, teament_name, for_whom); - teams_mask = M_ARGV(0, float); - teament_name = M_ARGV(1, string); - - if(!mutator_returnvalue) - { - if(teams_mask & BIT(0)) c1 = 0; - if(teams_mask & BIT(1)) c2 = 0; - if(teams_mask & BIT(2)) c3 = 0; - if(teams_mask & BIT(3)) c4 = 0; - } - - // find out what teams are allowed if necessary - if(teament_name) - { - entity head = find(NULL, classname, teament_name); - while(head) - { - switch(head.team) - { - case NUM_TEAM_1: c1 = 0; break; - case NUM_TEAM_2: c2 = 0; break; - case NUM_TEAM_3: c3 = 0; break; - case NUM_TEAM_4: c4 = 0; break; - } - - head = find(head, classname, teament_name); - } - } - - // TODO: Balance quantity of bots across > 2 teams when bot_vs_human is set (and remove next line) - if(AvailableTeams() == 2) - if(autocvar_bot_vs_human && for_whom) - { - if(autocvar_bot_vs_human > 0) - { - // find last team available - - if(IS_BOT_CLIENT(for_whom)) - { - if(c4 >= 0) { c3 = c2 = c1 = -1; } - else if(c3 >= 0) { c4 = c2 = c1 = -1; } - else { c4 = c3 = c1 = -1; } - // no further cases, we know at least 2 teams exist - } - else - { - if(c1 >= 0) { c2 = c3 = c4 = -1; } - else if(c2 >= 0) { c1 = c3 = c4 = -1; } - else { c1 = c2 = c4 = -1; } - // no further cases, bots have one of the teams - } - } - else - { - // find first team available - - if(IS_BOT_CLIENT(for_whom)) - { - if(c1 >= 0) { c2 = c3 = c4 = -1; } - else if(c2 >= 0) { c1 = c3 = c4 = -1; } - else { c1 = c2 = c4 = -1; } - // no further cases, we know at least 2 teams exist - } - else - { - if(c4 >= 0) { c3 = c2 = c1 = -1; } - else if(c3 >= 0) { c4 = c2 = c1 = -1; } - else { c4 = c3 = c1 = -1; } - // no further cases, bots have one of the teams - } - } - } - - if(!for_whom) - return; - - // if player has a forced team, ONLY allow that one - if(for_whom.team_forced == NUM_TEAM_1 && c1 >= 0) - c2 = c3 = c4 = -1; - else if(for_whom.team_forced == NUM_TEAM_2 && c2 >= 0) - c1 = c3 = c4 = -1; - else if(for_whom.team_forced == NUM_TEAM_3 && c3 >= 0) - c1 = c2 = c4 = -1; - else if(for_whom.team_forced == NUM_TEAM_4 && c4 >= 0) - c1 = c2 = c3 = -1; -} - -float PlayerValue(entity p) -{ - return 1; - // FIXME: it always returns 1... -} - -// c1...c4 should be set to -1 (not allowed) or 0 (allowed). -// teams that are allowed will now have their player counts stored in c1...c4 -void GetTeamCounts(entity ignore) -{ - if (MUTATOR_CALLHOOK(GetTeamCounts) == true) - { - if (c1 >= 0) - { - MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_1, ignore, c1, - num_bots_team1, lowest_human_team1, lowest_bot_team1); - c1 = M_ARGV(2, float); - num_bots_team1 = M_ARGV(3, float); - lowest_human_team1 = M_ARGV(4, entity); - lowest_bot_team1 = M_ARGV(5, entity); - } - if (c2 >= 0) - { - MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_2, ignore, c2, - num_bots_team2, lowest_human_team2, lowest_bot_team2); - c2 = M_ARGV(2, float); - num_bots_team2 = M_ARGV(3, float); - lowest_human_team2 = M_ARGV(4, entity); - lowest_bot_team2 = M_ARGV(5, entity); - } - if (c3 >= 0) - { - MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_3, ignore, c3, - num_bots_team3, lowest_human_team3, lowest_bot_team3); - c3 = M_ARGV(2, float); - num_bots_team3 = M_ARGV(3, float); - lowest_human_team3 = M_ARGV(4, entity); - lowest_bot_team3 = M_ARGV(5, entity); - } - if (c4 >= 0) - { - MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_4, ignore, - c4, num_bots_team4, lowest_human_team4, lowest_bot_team4); - c4 = M_ARGV(2, float); - num_bots_team4 = M_ARGV(3, float); - lowest_human_team4 = M_ARGV(4, entity); - lowest_bot_team4 = M_ARGV(5, entity); - } - } - else - { - float value, bvalue; - // now count how many players are on each team already - float lowest_human_score1 = FLOAT_MAX; - float lowest_bot_score1 = FLOAT_MAX; - float lowest_human_score2 = FLOAT_MAX; - float lowest_bot_score2 = FLOAT_MAX; - float lowest_human_score3 = FLOAT_MAX; - float lowest_bot_score3 = FLOAT_MAX; - float lowest_human_score4 = FLOAT_MAX; - float lowest_bot_score4 = FLOAT_MAX; - FOREACH_CLIENT(true, - { - float t; - if (IS_PLAYER(it) || it.caplayer) - { - t = it.team; - } - else if (it.team_forced > 0) - { - t = it.team_forced; // reserve the spot - } - else - { - continue; - } - if (it == ignore) - { - continue; - } - value = PlayerValue(it); - if (IS_BOT_CLIENT(it)) - { - bvalue = value; - } - else - { - bvalue = 0; - } - if (value == 0) - { - continue; - } - switch (t) - { - case NUM_TEAM_1: - { - if (c1 < 0) - { - break; - } - c1 += value; - num_bots_team1 += bvalue; - float temp_score = PlayerScore_Get(it, SP_SCORE); - if (!bvalue) - { - if (temp_score < lowest_human_score1) - { - lowest_human_team1 = it; - lowest_human_score1 = temp_score; - } - break; - } - if (temp_score < lowest_bot_score1) - { - lowest_bot_team1 = it; - lowest_bot_score1 = temp_score; - } - break; - } - case NUM_TEAM_2: - { - if (c2 < 0) - { - break; - } - c2 += value; - num_bots_team2 += bvalue; - float temp_score = PlayerScore_Get(it, SP_SCORE); - if (!bvalue) - { - if (temp_score < lowest_human_score2) - { - lowest_human_team2 = it; - lowest_human_score2 = temp_score; - } - break; - } - if (temp_score < lowest_bot_score2) - { - lowest_bot_team2 = it; - lowest_bot_score2 = temp_score; - } - break; - } - case NUM_TEAM_3: - { - if (c3 < 0) - { - break; - } - c3 += value; - num_bots_team3 += bvalue; - float temp_score = PlayerScore_Get(it, SP_SCORE); - if (!bvalue) - { - if (temp_score < lowest_human_score3) - { - lowest_human_team3 = it; - lowest_human_score3 = temp_score; - } - break; - } - if (temp_score < lowest_bot_score3) - { - lowest_bot_team3 = it; - lowest_bot_score3 = temp_score; - } - break; - } - case NUM_TEAM_4: - { - if (c4 < 0) - { - break; - } - c4 += value; - num_bots_team4 += bvalue; - float temp_score = PlayerScore_Get(it, SP_SCORE); - if (!bvalue) - { - if (temp_score < lowest_human_score4) - { - lowest_human_team4 = it; - lowest_human_score4 = temp_score; - } - break; - } - if (temp_score < lowest_bot_score4) - { - lowest_bot_team4 = it; - lowest_bot_score4 = temp_score; - } - break; - } - } - }); - } - - // if the player who has a forced team has not joined yet, reserve the spot - if(autocvar_g_campaign) - { - switch(autocvar_g_campaign_forceteam) - { - case 1: if(c1 == num_bots_team1) ++c1; break; - case 2: if(c2 == num_bots_team2) ++c2; break; - case 3: if(c3 == num_bots_team3) ++c3; break; - case 4: if(c4 == num_bots_team4) ++c4; break; - } - } -} - -bool IsTeamSmallerThanTeam(int team_a, int team_b, entity player, +bool IsTeamSmallerThanTeam(int team_index_a, int team_index_b, entity player, bool use_score) { - if (!Team_IsValidNumber(team_a)) + if (!Team_IsValidIndex(team_index_a)) { - LOG_FATALF("IsTeamSmallerThanTeam: team_a is invalid: %f", team_a); + LOG_FATALF("IsTeamSmallerThanTeam: team_index_a is invalid: %f", + team_index_a); } - if (!Team_IsValidNumber(team_b)) + if (!Team_IsValidIndex(team_index_b)) { - LOG_FATALF("IsTeamSmallerThanTeam: team_b is invalid: %f", team_b); + LOG_FATALF("IsTeamSmallerThanTeam: team_index_b is invalid: %f", + team_index_b); } - if (team_a == team_b) + if (team_index_a == team_index_b) { return false; } - // we assume that CheckAllowedTeams and GetTeamCounts have already been called - int num_players_team_a = -1, num_players_team_b = -1; - int num_bots_team_a = 0, num_bots_team_b = 0; - float score_team_a = 0, score_team_b = 0; - switch (team_a) - { - case 1: - { - num_players_team_a = c1; - num_bots_team_a = num_bots_team1; - score_team_a = team1_score; - break; - } - case 2: - { - num_players_team_a = c2; - num_bots_team_a = num_bots_team2; - score_team_a = team2_score; - break; - } - case 3: - { - num_players_team_a = c3; - num_bots_team_a = num_bots_team3; - score_team_a = team3_score; - break; - } - case 4: - { - num_players_team_a = c4; - num_bots_team_a = num_bots_team4; - score_team_a = team4_score; - break; - } - } - switch (team_b) - { - case 1: - { - num_players_team_b = c1; - num_bots_team_b = num_bots_team1; - score_team_b = team1_score; - break; - } - case 2: - { - num_players_team_b = c2; - num_bots_team_b = num_bots_team2; - score_team_b = team2_score; - break; - } - case 3: - { - num_players_team_b = c3; - num_bots_team_b = num_bots_team3; - score_team_b = team3_score; - break; - } - case 4: - { - num_players_team_b = c4; - num_bots_team_b = num_bots_team4; - score_team_b = team4_score; - break; - } - } - // invalid - if (num_players_team_a < 0 || num_players_team_b < 0) + entity team_a = Team_GetTeamFromIndex(team_index_a); + entity team_b = Team_GetTeamFromIndex(team_index_b); + if (!Team_IsAllowed(team_a) || !Team_IsAllowed(team_b)) { return false; } + int num_players_team_a = team_a.m_num_players; + int num_players_team_b = team_b.m_num_players; if (IS_REAL_CLIENT(player) && bots_would_leave) { - num_players_team_a -= num_bots_team_a; - num_players_team_b -= num_bots_team_b; + num_players_team_a -= team_a.m_num_bots; + num_players_team_b -= team_b.m_num_bots; } if (!use_score) { @@ -645,97 +637,38 @@ bool IsTeamSmallerThanTeam(int team_a, int team_b, entity player, { return false; } - return score_team_a < score_team_b; + return team_a.m_team_score < team_b.m_team_score; } -bool IsTeamEqualToTeam(int team_a, int team_b, entity player, bool use_score) +bool IsTeamEqualToTeam(int team_index_a, int team_index_b, entity player, + bool use_score) { - if (!Team_IsValidNumber(team_a)) + if (!Team_IsValidIndex(team_index_a)) { - LOG_FATALF("IsTeamEqualToTeam: team_a is invalid: %f", team_a); + LOG_FATALF("IsTeamEqualToTeam: team_index_a is invalid: %f", + team_index_a); } - if (!Team_IsValidNumber(team_b)) + if (!Team_IsValidIndex(team_index_b)) { - LOG_FATALF("IsTeamEqualToTeam: team_b is invalid: %f", team_b); + LOG_FATALF("IsTeamEqualToTeam: team_index_b is invalid: %f", + team_index_b); } - if (team_a == team_b) + if (team_index_a == team_index_b) { return true; } - // we assume that CheckAllowedTeams and GetTeamCounts have already been called - int num_players_team_a = -1, num_players_team_b = -1; - int num_bots_team_a = 0, num_bots_team_b = 0; - float score_team_a = 0, score_team_b = 0; - switch (team_a) - { - case 1: - { - num_players_team_a = c1; - num_bots_team_a = num_bots_team1; - score_team_a = team1_score; - break; - } - case 2: - { - num_players_team_a = c2; - num_bots_team_a = num_bots_team2; - score_team_a = team2_score; - break; - } - case 3: - { - num_players_team_a = c3; - num_bots_team_a = num_bots_team3; - score_team_a = team3_score; - break; - } - case 4: - { - num_players_team_a = c4; - num_bots_team_a = num_bots_team4; - score_team_a = team4_score; - break; - } - } - switch (team_b) + entity team_a = Team_GetTeamFromIndex(team_index_a); + entity team_b = Team_GetTeamFromIndex(team_index_b); + if (!Team_IsAllowed(team_a) || !Team_IsAllowed(team_b)) { - case 1: - { - num_players_team_b = c1; - num_bots_team_b = num_bots_team1; - score_team_b = team1_score; - break; - } - case 2: - { - num_players_team_b = c2; - num_bots_team_b = num_bots_team2; - score_team_b = team2_score; - break; - } - case 3: - { - num_players_team_b = c3; - num_bots_team_b = num_bots_team3; - score_team_b = team3_score; - break; - } - case 4: - { - num_players_team_b = c4; - num_bots_team_b = num_bots_team4; - score_team_b = team4_score; - break; - } - } - // invalid - if (num_players_team_a < 0 || num_players_team_b < 0) return false; - + } + int num_players_team_a = team_a.m_num_players; + int num_players_team_b = team_b.m_num_players; if (IS_REAL_CLIENT(player) && bots_would_leave) { - num_players_team_a -= num_bots_team_a; - num_players_team_b -= num_bots_team_b; + num_players_team_a -= team_a.m_num_bots; + num_players_team_b -= team_b.m_num_bots; } if (!use_score) { @@ -745,7 +678,7 @@ bool IsTeamEqualToTeam(int team_a, int team_b, entity player, bool use_score) { return false; } - return score_team_a == score_team_b; + return team_a.m_team_score == team_b.m_team_score; } int FindBestTeams(entity player, bool use_score) @@ -756,12 +689,12 @@ int FindBestTeams(entity player, bool use_score) } int team_bits = 0; int previous_team = 0; - if (c1 >= 0) + if (Team_IsAllowed(g_team_entities[0])) { team_bits = BIT(0); previous_team = 1; } - if (c2 >= 0) + if (Team_IsAllowed(g_team_entities[1])) { if (previous_team == 0) { @@ -779,7 +712,7 @@ int FindBestTeams(entity player, bool use_score) previous_team = 2; } } - if (c3 >= 0) + if (Team_IsAllowed(g_team_entities[2])) { if (previous_team == 0) { @@ -797,7 +730,7 @@ int FindBestTeams(entity player, bool use_score) previous_team = 3; } } - if (c4 >= 0) + if (Team_IsAllowed(g_team_entities[3])) { if (previous_team == 0) { @@ -815,9 +748,7 @@ int FindBestTeams(entity player, bool use_score) return team_bits; } -// returns # of smallest team (1, 2, 3, 4) -// NOTE: Assumes CheckAllowedTeams has already been called! -int FindSmallestTeam(entity player, float ignore_player) +int FindBestTeam(entity player, float ignore_player) { // count how many players are in each team if (ignore_player) @@ -831,7 +762,8 @@ int FindSmallestTeam(entity player, float ignore_player) int team_bits = FindBestTeams(player, true); if (team_bits == 0) { - error(sprintf("No teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype()))); + LOG_FATALF("FindBestTeam: No teams available for %s\n", + MapInfo_Type_ToString(MapInfo_CurrentGametype())); } RandomSelection_Init(); if ((team_bits & BIT(0)) != 0) @@ -868,31 +800,20 @@ void JoinBestTeam(entity this, bool force_best_team) // if they're not on a valid team, then let other code put them on the smallest team if (!force_best_team) { - int selected_team; - if ((c1 >= 0) && (this.team == NUM_TEAM_1)) - { - selected_team = this.team; - } - else if ((c2 >= 0) && (this.team == NUM_TEAM_2)) - { - selected_team = this.team; - } - else if ((c3 >= 0) && (this.team == NUM_TEAM_3)) + int selected_team_num = -1; + for (int i = 0; i < 4; ++i) { - selected_team = this.team; - } - else if ((c4 >= 0) && (this.team == NUM_TEAM_4)) - { - selected_team = this.team; - } - else - { - selected_team = -1; + if (Team_IsAllowed(g_team_entities[i]) && (this.team == + Team_NumberToTeam(i))) + { + selected_team_num = this.team; + break; + } } - - if (selected_team > 0) + + if (Team_IsValidTeam(selected_team_num)) { - SetPlayerTeamSimple(this, selected_team); + SetPlayerTeamSimple(this, selected_team_num); LogTeamchange(this.playerid, this.team, 99); return; } @@ -902,26 +823,23 @@ void JoinBestTeam(entity this, bool force_best_team) { return; } - int best_team = FindSmallestTeam(this, true); - best_team = Team_NumberToTeam(best_team); - if (best_team == -1) - { - error("JoinBestTeam: invalid team\n"); - } - int old_team = Team_TeamToNumber(this.team); + int best_team_index = FindBestTeam(this, true); + int best_team_num = Team_NumberToTeam(best_team_index); + int old_team_index = Team_TeamToNumber(this.team); TeamchangeFrags(this); - SetPlayerTeamSimple(this, best_team); + SetPlayerTeamSimple(this, best_team_num); LogTeamchange(this.playerid, this.team, 2); // log auto join - if ((old_team != -1) && !IS_BOT_CLIENT(this)) + if ((old_team_index != -1) && !IS_BOT_CLIENT(this)) { - AutoBalanceBots(old_team, Team_TeamToNumber(best_team)); + AutoBalanceBots(old_team_index, best_team_index); } KillPlayerForTeamChange(this); } void SV_ChangeTeam(entity this, float _color) { - float source_color, destination_color, source_team, destination_team; + int source_color, destination_color; + int source_team_index, destination_team_index; // in normal deathmatch we can just apply the color and we're done if(!teamplay) @@ -940,25 +858,25 @@ void SV_ChangeTeam(entity this, float _color) source_color = this.clientcolors & 0x0F; destination_color = _color & 0x0F; - source_team = Team_TeamToNumber(source_color + 1); - destination_team = Team_TeamToNumber(destination_color + 1); + source_team_index = Team_TeamToNumber(source_color + 1); + destination_team_index = Team_TeamToNumber(destination_color + 1); - if (destination_team == -1) + if (destination_team_index == -1) { return; } CheckAllowedTeams(this); - if (destination_team == 1 && c1 < 0) destination_team = 4; - if (destination_team == 4 && c4 < 0) destination_team = 3; - if (destination_team == 3 && c3 < 0) destination_team = 2; - if (destination_team == 2 && c2 < 0) destination_team = 1; + if (destination_team_index == 1 && !Team_IsAllowed(g_team_entities[0])) destination_team_index = 4; + if (destination_team_index == 4 && !Team_IsAllowed(g_team_entities[3])) destination_team_index = 3; + if (destination_team_index == 3 && !Team_IsAllowed(g_team_entities[2])) destination_team_index = 2; + if (destination_team_index == 2 && !Team_IsAllowed(g_team_entities[1])) destination_team_index = 1; // not changing teams if (source_color == destination_color) { - SetPlayerTeam(this, destination_team, source_team, true); + SetPlayerTeam(this, destination_team_index, source_team_index, true); return; } @@ -971,40 +889,42 @@ void SV_ChangeTeam(entity this, float _color) if (autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance) { GetTeamCounts(this); - if ((BIT(destination_team - 1) & FindBestTeams(this, false)) == 0) + if ((BIT(destination_team_index - 1) & FindBestTeams(this, false)) == 0) { Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM); return; } } - if(IS_PLAYER(this) && source_team != destination_team) + if (IS_PLAYER(this) && source_team_index != destination_team_index) { // reduce frags during a team change TeamchangeFrags(this); } - if (!SetPlayerTeam(this, destination_team, source_team, !IS_CLIENT(this))) + if (!SetPlayerTeam(this, destination_team_index, source_team_index, + !IS_CLIENT(this))) { return; } - AutoBalanceBots(source_team, destination_team); - if (!IS_PLAYER(this) || (source_team == destination_team)) + AutoBalanceBots(source_team_index, destination_team_index); + if (!IS_PLAYER(this) || (source_team_index == destination_team_index)) { return; } KillPlayerForTeamChange(this); } -void AutoBalanceBots(int source_team, int destination_team) +void AutoBalanceBots(int source_team_index, int destination_team_index) { - if (!Team_IsValidNumber(source_team)) + if (!Team_IsValidIndex(source_team_index)) { - LOG_WARNF("AutoBalanceBots: Source team is invalid: %f", source_team); + LOG_WARNF("AutoBalanceBots: Source team index is invalid: %f", + source_team_index); return; } - if (!Team_IsValidNumber(destination_team)) + if (!Team_IsValidIndex(destination_team_index)) { - LOG_WARNF("AutoBalanceBots: Destination team is invalid: %f", - destination_team); + LOG_WARNF("AutoBalanceBots: Destination team index is invalid: %f", + destination_team_index); return; } if (!autocvar_g_balance_teams || @@ -1012,69 +932,18 @@ void AutoBalanceBots(int source_team, int destination_team) { return; } - int num_players_source_team = 0; - int num_players_destination_team = 0; - entity lowest_bot_destination_team = NULL; - switch (source_team) - { - case 1: - { - num_players_source_team = c1; - break; - } - case 2: - { - num_players_source_team = c2; - break; - } - case 3: - { - num_players_source_team = c3; - break; - } - case 4: - { - num_players_source_team = c4; - break; - } - } - if (num_players_source_team < 0) + entity source_team = Team_GetTeamFromIndex(source_team_index); + if (!Team_IsAllowed(source_team)) { return; } - switch (destination_team) - { - case 1: - { - num_players_destination_team = c1; - lowest_bot_destination_team = lowest_bot_team1; - break; - } - case 2: - { - num_players_destination_team = c2; - lowest_bot_destination_team = lowest_bot_team2; - break; - } - case 3: - { - num_players_destination_team = c3; - lowest_bot_destination_team = lowest_bot_team3; - break; - } - case 4: - { - num_players_destination_team = c4; - lowest_bot_destination_team = lowest_bot_team4; - break; - } - } - if ((num_players_destination_team <= num_players_source_team) || - (lowest_bot_destination_team == NULL)) + entity destination_team = Team_GetTeamFromIndex(destination_team_index); + if ((destination_team.m_num_players <= source_team.m_num_players) || + (destination_team.m_lowest_bot == NULL)) { return; } - SetPlayerTeamSimple(lowest_bot_destination_team, - Team_NumberToTeam(source_team)); - KillPlayerForTeamChange(lowest_bot_destination_team); + SetPlayerTeamSimple(destination_team.m_lowest_bot, + Team_NumberToTeam(source_team_index)); + KillPlayerForTeamChange(destination_team.m_lowest_bot); } diff --git a/qcsrc/server/teamplay.qh b/qcsrc/server/teamplay.qh index 7c4ebe77b..85dca1ac4 100644 --- a/qcsrc/server/teamplay.qh +++ b/qcsrc/server/teamplay.qh @@ -3,33 +3,85 @@ string cache_mutatormsg; string cache_lastmutatormsg; -// The following variables are used for balancing. They are not updated -// automatically. You need to call CheckAllowedTeams and GetTeamCounts to get -// proper values. - -// These four have 2 different states. If they are equal to -1, it means that -// the player can't join the team. Zero or positive value means that player can -// join the team and means the number of players on that team. -float c1; -float c2; -float c3; -float c4; -float num_bots_team1; ///< Number of bots in the first team. -float num_bots_team2; ///< Number of bots in the second team. -float num_bots_team3; ///< Number of bots in the third team. -float num_bots_team4; ///< Number of bots in the fourth team. -entity lowest_human_team1; ///< Human with the lowest score in the first team. -entity lowest_human_team2; ///< Human with the lowest score in the second team. -entity lowest_human_team3; ///< Human with the lowest score in the third team. -entity lowest_human_team4; ///< Human with the lowest score in the fourth team. -entity lowest_bot_team1; ///< Bot with the lowest score in the first team. -entity lowest_bot_team2; ///< Bot with the lowest score in the second team. -entity lowest_bot_team3; ///< Bot with the lowest score in the third team. -entity lowest_bot_team4; ///< Bot with the lowest score in the fourth team. +/// \brief Returns the global team entity at the given index. +/// \param[in] index Index of the team. +/// \return Global team entity at the given index. +entity Team_GetTeamFromIndex(int index); + +/// \brief Returns the global team entity that corresponds to the given TEAM_NUM +/// value. +/// \param[in] team_num Team value. See TEAM_NUM constants. +/// \return Global team entity that corresponds to the given TEAM_NUM value. +entity Team_GetTeam(int team_num); + +/// \brief Returns the score of the team. +/// \param[in] team_ Team entity. +/// \return Score of the team. +float Team_GetTeamScore(entity team_); + +/// \brief Sets the score of the team. +/// \param[in,out] team_ Team entity. +/// \param[in] score Score to set. +void Team_SetTeamScore(entity team_, float score); + +/// \brief Checks whether the player can join teams according to global +/// configuration and mutator settings. +/// \param[in] for_whom Player to check for. Pass NULL for global rules. +/// \note This function sets various internal variables and is required to be +/// called before several other functions. +void CheckAllowedTeams(entity for_whom); -int redowned, blueowned, yellowowned, pinkowned; +/// \brief Returns the bitmask of allowed teams. +/// \return Bitmask of allowed teams. +/// \note You need to call CheckAllowedTeams before calling this function. +int GetAllowedTeams(); + +/// \brief Returns whether the team is allowed. +/// \param[in] team_ Team entity. +/// \return True if team is allowed, false otherwise. +/// \note You need to call CheckAllowedTeams before calling this function. +bool Team_IsAllowed(entity team_); + +/// \brief Counts the number of players and various other information about +/// each team. +/// \param[in] ignore Player to ignore. This is useful if you plan to switch the +/// player's team. Pass NULL for global information. +/// \note You need to call CheckAllowedTeams before calling this function. +/// \note This function sets many internal variables and is required to be +/// called before several other functions. +void GetTeamCounts(entity ignore); + +/// \brief Returns the number of players (both humans and bots) in a team. +/// \param[in] team_ Team entity. +/// \return Number of player (both humans and bots) in a team. +/// \note You need to call CheckAllowedTeams and GetTeamCounts before calling +/// this function. +int Team_GetNumberOfPlayers(entity team_); + +/// \brief Returns the number of bots in a team. +/// \param[in] team_ Team entity. +/// \return Number of bots in a team. +/// \note You need to call CheckAllowedTeams and GetTeamCounts before calling +/// this function. +int Team_GetNumberOfBots(entity team_); + +/// \brief Returns the human with the lowest score in a team or NULL if there is +/// none. +/// \param[in] team_ Team entity. +/// \return Human with the lowest score in a team or NULL if there is none. +/// \note You need to call CheckAllowedTeams and GetTeamCounts before calling +/// this function. +entity Team_GetLowestHuman(entity team_); + +/// \brief Returns the bot with the lowest score in a team or NULL if there is +/// none. +/// \param[in] team_ Team entity. +/// \return Bot with the lowest score in a team or NULL if there is none. +/// \note You need to call CheckAllowedTeams and GetTeamCounts before calling +/// this function. +entity Team_GetLowestBot(entity team_); -//float audit_teams_time; +int redowned, blueowned, yellowowned, pinkowned; void TeamchangeFrags(entity e); @@ -43,11 +95,12 @@ string GetClientVersionMessage(entity this); string getwelcomemessage(entity this); +void setcolor(entity this, int clr); + void SetPlayerColors(entity player, float _color); /// \brief Kills player as a result of team change. /// \param[in,out] player Player to kill. -/// \return No return. void KillPlayerForTeamChange(entity player); /// \brief Sets the team of the player. @@ -58,63 +111,55 @@ bool SetPlayerTeamSimple(entity player, int team_num); /// \brief Sets the team of the player. /// \param[in,out] player Player to adjust. -/// \param[in] destination_team Team to set. -/// \param[in] source_team Previous team of the player. +/// \param[in] destination_team_index Index of the team to set. +/// \param[in] source_team_index Previous index of the team of the player. /// \param[in] no_print Whether to print this event to players' console. /// \return True if team switch was successful, false otherwise. -bool SetPlayerTeam(entity player, int destination_team, int source_team, - bool no_print); - -// set c1...c4 to show what teams are allowed -void CheckAllowedTeams(entity for_whom); - -float PlayerValue(entity p); - -// c1...c4 should be set to -1 (not allowed) or 0 (allowed). -// teams that are allowed will now have their player counts stored in c1...c4 -void GetTeamCounts(entity ignore); +bool SetPlayerTeam(entity player, int destination_team_index, + int source_team_index, bool no_print); /// \brief Returns whether one team is smaller than the other. -/// \param[in] team_a First team. -/// \param[in] team_b Second team. +/// \param[in] team_index_a Index of the first team. +/// \param[in] team_index_b Index of the second team. /// \param[in] player Player to check. /// \param[in] use_score Whether to take into account team scores. /// \return True if first team is smaller than the second one, false otherwise. -/// \note This function assumes that CheckAllowedTeams and GetTeamCounts have -/// been called. -bool IsTeamSmallerThanTeam(int team_a, int team_b, entity player, +/// \note You need to call CheckAllowedTeams and GetTeamCounts before calling +/// this function. +bool IsTeamSmallerThanTeam(int team_index_a, int team_index_b, entity player, bool use_score); /// \brief Returns whether one team is equal to the other. -/// \param[in] team_a First team. -/// \param[in] team_b Second team. +/// \param[in] team_index_a Index of the first team. +/// \param[in] team_index_b Index of the second team. /// \param[in] player Player to check. /// \param[in] use_score Whether to take into account team scores. /// \return True if first team is equal to the second one, false otherwise. -/// \note This function assumes that CheckAllowedTeams and GetTeamCounts have -/// been called. -bool IsTeamEqualToTeam(int team_a, int team_b, entity player, bool use_score); +/// \note You need to call CheckAllowedTeams and GetTeamCounts before calling +/// this function. +bool IsTeamEqualToTeam(int team_index_a, int team_index_b, entity player, + bool use_score); /// \brief Returns the bitmask of the best teams for the player to join. /// \param[in] player Player to check. /// \param[in] use_score Whether to take into account team scores. /// \return Bitmask of the best teams for the player to join. -/// \note This function assumes that CheckAllowedTeams and GetTeamCounts have -/// been called. +/// \note You need to call CheckAllowedTeams and GetTeamCounts before calling +/// this function. int FindBestTeams(entity player, bool use_score); -// returns # of smallest team (1, 2, 3, 4) -// NOTE: Assumes CheckAllowedTeams has already been called! -int FindSmallestTeam(entity player, float ignore_player); +/// \brief Finds the best team for the player. +/// \param[in] player Player to check. +/// \param[in] ignore_player ??? +/// \return Index of the best team for the player. If there are several equally +/// good teams available, the function will pick a random one. +int FindBestTeam(entity player, float ignore_player); void JoinBestTeam(entity this, bool force_best_team); /// \brief Auto balances bots in teams after the player has changed team. -/// \param[in] source_team Previous team of the player (1, 2, 3, 4). -/// \param[in] destination_team Current team of the player (1, 2, 3, 4). -/// \return No return. -/// \note This function assumes that CheckAllowedTeams and GetTeamCounts have -/// been called. -void AutoBalanceBots(int source_team, int destination_team); - -void setcolor(entity this, int clr); +/// \param[in] source_team_index Previous index of the team of the player. +/// \param[in] destination_team_index Current index of the team of the player. +/// \note You need to call CheckAllowedTeams and GetTeamCounts before calling +/// this function. +void AutoBalanceBots(int source_team_index, int destination_team_index);