X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=blobdiff_plain;f=qcsrc%2Fserver%2Fteamplay.qc;h=163f71a099c7c0cba598d7d4ccc7282ced4f91e1;hp=db01a8f5887d76f1438bc357c5dddd05570ba389;hb=db20e10f5f82423356007ed51deb53c05f964a9a;hpb=b41f7ee415c9cc35d6304ca237986a9e445ffd4c diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index db01a8f58..163f71a09 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -6,22 +6,7 @@ float c1, c2, c3, c4; // # of bots on those teams float cb1, cb2, cb3, cb4; -float audit_teams_time; - -float IsTeamBalanceForced() -{ - if(intermission_running) - return 0; // no rebalancing whatsoever please - if(!teamplay) - return 0; - if(autocvar_g_campaign) - return 0; - if(autocvar_bot_vs_human && (c3==-1 && c4==-1)) - return 0; - if(!autocvar_g_balance_teams_force) - return -1; - return 1; -} +//float audit_teams_time; void TeamchangeFrags(entity e) { @@ -67,8 +52,6 @@ string TeamNoName(float t) return "Neutral Team"; } -void dom_init(); -void ctf_init(); void runematch_init(); void tdm_init(); void entcs_init(); @@ -150,17 +133,16 @@ void InitGameplayMode() ActivateTeamplay(); fraglimit_override = autocvar_g_domination_point_limit; leadlimit_override = autocvar_g_domination_point_leadlimit; - dom_init(); + MUTATOR_ADD(gamemode_domination); have_team_spawns = -1; // request team spawns } if(g_ctf) { ActivateTeamplay(); - g_ctf_ignore_frags = autocvar_g_ctf_ignore_frags; fraglimit_override = autocvar_capturelimit_override; leadlimit_override = autocvar_captureleadlimit_override; - ctf_init(); + MUTATOR_ADD(gamemode_ctf); have_team_spawns = -1; // request team spawns } @@ -228,6 +210,7 @@ void InitGameplayMode() { ActivateTeamplay(); have_team_spawns = -1; // request team spawns + MUTATOR_ADD(gamemode_onslaught); } if(g_race) @@ -534,7 +517,7 @@ void CheckAllowedTeams (entity for_whom) if(autocvar_bot_vs_human > 0) { // bots are all blue - if(clienttype(for_whom) == CLIENTTYPE_BOT) + if(IS_BOT_CLIENT(for_whom)) c1 = c3 = c4 = -1; else c2 = -1; @@ -542,7 +525,7 @@ void CheckAllowedTeams (entity for_whom) else { // bots are all red - if(clienttype(for_whom) == CLIENTTYPE_BOT) + if(IS_BOT_CLIENT(for_whom)) c2 = c3 = c4 = -1; else c1 = -1; @@ -562,8 +545,6 @@ void CheckAllowedTeams (entity for_whom) float PlayerValue(entity p) { - if(IsTeamBalanceForced() == 1) - return 1; return 1; // FIXME: it always returns 1... } @@ -582,7 +563,7 @@ void GetTeamCounts(entity ignore) FOR_EACH_CLIENT(head) { float t; - if(head.classname == "player") + if(IS_PLAYER(head)) t = head.team; else if(head.team_forced > 0) t = head.team_forced; // reserve the spot @@ -591,7 +572,7 @@ void GetTeamCounts(entity ignore) if(head != ignore)// && head.netname != "") { value = PlayerValue(head); - if(clienttype(head) == CLIENTTYPE_BOT) + if(IS_BOT_CLIENT(head)) bvalue = value; else bvalue = 0; @@ -643,11 +624,73 @@ void GetTeamCounts(entity ignore) } } +float TeamSmallerEqThanTeam(float ta, float tb, entity e) +{ + // we assume that CheckAllowedTeams and GetTeamCounts have already been called + float f; + float ca = -1, cb = -1, cba = 0, cbb = 0, sa = 0, sb = 0; + + switch(ta) + { + case 1: ca = c1; cba = cb1; sa = team1_score; break; + case 2: ca = c2; cba = cb2; sa = team2_score; break; + case 3: ca = c3; cba = cb3; sa = team3_score; break; + case 4: ca = c4; cba = cb4; sa = team4_score; break; + } + switch(tb) + { + case 1: cb = c1; cbb = cb1; sb = team1_score; break; + case 2: cb = c2; cbb = cb2; sb = team2_score; break; + case 3: cb = c3; cbb = cb3; sb = team3_score; break; + case 4: cb = c4; cbb = cb4; sb = team4_score; break; + } + + // invalid + if(ca < 0 || cb < 0) + return FALSE; + + // equal + if(ta == tb) + return TRUE; + + if(IS_REAL_CLIENT(e)) + { + if(bots_would_leave) + { + ca -= cba * 0.999; + cb -= cbb * 0.999; + } + } + + // keep teams alive (teams of size 0 always count as smaller, ignoring score) + if(ca < 1) + if(cb >= 1) + return TRUE; + if(ca >= 1) + if(cb < 1) + return FALSE; + + // first, normalize + f = max(ca, cb, 1); + ca /= f; + cb /= f; + f = max(sa, sb, 1); + sa /= f; + sb /= f; + + // the more we're at the end of the match, the more take scores into account + f = bound(0, game_completion_ratio * autocvar_g_balance_teams_scorefactor, 1); + ca += (sa - ca) * f; + cb += (sb - cb) * f; + + return ca <= cb; +} + // returns # of smallest team (1, 2, 3, 4) // NOTE: Assumes CheckAllowedTeams has already been called! float FindSmallestTeam(entity pl, float ignore_pl) { - float totalteams, balance_type, maxc; + float totalteams, t; totalteams = 0; // find out what teams are available @@ -668,7 +711,7 @@ float FindSmallestTeam(entity pl, float ignore_pl) if(totalteams <= 1) { - if(autocvar_g_campaign && pl && clienttype(pl) == CLIENTTYPE_REAL) + if(autocvar_g_campaign && pl && IS_REAL_CLIENT(pl)) return 1; // special case for campaign and player joining else if(g_domination) error("Too few teams available for domination\n"); @@ -688,49 +731,26 @@ float FindSmallestTeam(entity pl, float ignore_pl) else GetTeamCounts(world); - // c1...c4 now have counts of each team - // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker - - // 2 gives priority to what team you're already on, 1 goes in order - // 2 doesn't seem to work though... - balance_type = 1; - - if(bots_would_leave) - //if(pl.classname != "player") - if(clienttype(pl) != CLIENTTYPE_BOT) - { - c1 -= cb1 * 255.0/256.0; - c2 -= cb2 * 255.0/256.0; - c3 -= cb3 * 255.0/256.0; - c4 -= cb4 * 255.0/256.0; - } - maxc = max4(c1, c2, c3, c4); - RandomSelection_Init(); - if(balance_type == 1) - { - // 1: use team count, then score (note: can only use 8 significant bits of score) - if(c1 >= 0) RandomSelection_Add(world, 1, string_null, 1, (maxc - c1) + float2range01(-team1_score) / 256.0); - if(c2 >= 0) RandomSelection_Add(world, 2, string_null, 1, (maxc - c2) + float2range01(-team2_score) / 256.0); - if(c3 >= 0) RandomSelection_Add(world, 3, string_null, 1, (maxc - c3) + float2range01(-team3_score) / 256.0); - if(c4 >= 0) RandomSelection_Add(world, 4, string_null, 1, (maxc - c4) + float2range01(-team4_score) / 256.0); - } - else if(balance_type == 2) - { - // 1: use team count, if equal prefer own team - if(c1 >= 0) RandomSelection_Add(world, 1, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM1) / 512.0); - if(c2 >= 0) RandomSelection_Add(world, 2, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM2) / 512.0); - if(c3 >= 0) RandomSelection_Add(world, 3, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM3) / 512.0); - if(c4 >= 0) RandomSelection_Add(world, 4, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM4) / 512.0); - } - else if(balance_type == 3) - { - // 1: use team count, then score, if equal prefer own team (probably fails due to float accuracy problems) - if(c1 >= 0) RandomSelection_Add(world, 1, string_null, 1, (maxc - c1) + float2range01(-team1_score + 0.5 * (self.team == COLOR_TEAM1)) / 256.0); - if(c2 >= 0) RandomSelection_Add(world, 2, string_null, 1, (maxc - c2) + float2range01(-team2_score + 0.5 * (self.team == COLOR_TEAM2)) / 256.0); - if(c3 >= 0) RandomSelection_Add(world, 3, string_null, 1, (maxc - c3) + float2range01(-team3_score + 0.5 * (self.team == COLOR_TEAM3)) / 256.0); - if(c4 >= 0) RandomSelection_Add(world, 4, string_null, 1, (maxc - c4) + float2range01(-team4_score + 0.5 * (self.team == COLOR_TEAM4)) / 256.0); - } + + t = 1; + if(TeamSmallerEqThanTeam(2, t, pl)) + t = 2; + if(TeamSmallerEqThanTeam(3, t, pl)) + t = 3; + if(TeamSmallerEqThanTeam(4, t, pl)) + t = 4; + + // now t is the minimum, or A minimum! + if(t == 1 || TeamSmallerEqThanTeam(1, t, pl)) + RandomSelection_Add(world, 1, string_null, 1, 1); + if(t == 2 || TeamSmallerEqThanTeam(2, t, pl)) + RandomSelection_Add(world, 2, string_null, 1, 1); + if(t == 3 || TeamSmallerEqThanTeam(3, t, pl)) + RandomSelection_Add(world, 3, string_null, 1, 1); + if(t == 4 || TeamSmallerEqThanTeam(4, t, pl)) + RandomSelection_Add(world, 4, string_null, 1, 1); + return RandomSelection_chosen_float; } @@ -813,7 +833,7 @@ float JoinBestTeam(entity pl, float only_return_best, float forcebestteam) //void() ctf_playerchanged; void SV_ChangeTeam(float _color) { - float scolor, dcolor, steam, dteam, dbotcount, scount, dcount; + float scolor, dcolor, steam, dteam; //, dbotcount, scount, dcount; // in normal deathmatch we can just apply the color and we're done if(!teamplay) { @@ -861,67 +881,20 @@ void SV_ChangeTeam(float _color) return; // changing teams is not allowed } - if(autocvar_g_balance_teams_prevent_imbalance) + // autocvar_g_balance_teams_prevent_imbalance only makes sense if autocvar_g_balance_teams is on, as it makes the team selection dialog pointless + if(autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance) { - // only allow changing to a smaller or equal size team - - // find out what teams are available - //CheckAllowedTeams(); - // count how many players on each team - GetTeamCounts(world); - - // get desired team - if(dteam == 1 && c1 >= 0)//dcolor == COLOR_TEAM1 - 1) - { - dcount = c1; - dbotcount = cb1; - } - else if(dteam == 2 && c2 >= 0)//dcolor == COLOR_TEAM2 - 1) + GetTeamCounts(self); + if(!TeamSmallerEqThanTeam(dteam, steam, self)) { - dcount = c2; - dbotcount = cb2; - } - else if(dteam == 3 && c3 >= 0)//dcolor == COLOR_TEAM3 - 1) - { - dcount = c3; - dbotcount = cb3; - } - else if(dteam == 4 && c4 >= 0)//dcolor == COLOR_TEAM4 - 1) - { - dcount = c4; - dbotcount = cb4; - } - else - { - sprint(self, "Cannot change to an invalid team\n"); - + sprint(self, "Cannot change to a larger/better/shinier team\n"); return; } - - // get starting team - if(steam == 1)//scolor == COLOR_TEAM1 - 1) - scount = c1; - else if(steam == 2)//scolor == COLOR_TEAM2 - 1) - scount = c2; - else if(steam == 3)//scolor == COLOR_TEAM3 - 1) - scount = c3; - else // if(steam == 4)//scolor == COLOR_TEAM4 - 1) - scount = c4; - - if(scount) // started at a valid, nonempty team - { - // check if we're trying to change to a larger team that doens't have bots to swap with - if(dcount >= scount && dbotcount <= 0) - { - sprint(self, "Cannot change to a larger team\n"); - return; // can't change to a larger team - } - } } // bprint("allow change teams from ", ftos(steam), " to ", ftos(dteam), "\n"); - if(self.classname == "player" && steam != dteam) + if(IS_PLAYER(self) && steam != dteam) { // reduce frags during a team change TeamchangeFrags(self); @@ -929,13 +902,12 @@ void SV_ChangeTeam(float _color) SetPlayerTeam(self, dteam, steam, FALSE); - if(self.classname == "player" && steam != dteam) + if(IS_PLAYER(self) && steam != dteam) { // kill player when changing teams if(self.deadflag == DEAD_NO) Damage(self, self, self, 100000, DEATH_TEAMCHANGE, self.origin, '0 0 0'); } - //ctf_playerchanged(); } void ShufflePlayerOutOfTeam (float source_team) @@ -1079,87 +1051,6 @@ void ShufflePlayerOutOfTeam (float source_team) centerprint(selected, strcat("You have been moved into a different team to improve team balance\nYou are now on: ", ColoredTeamName(selected.team))); } -void CauseRebalance(float source_team, float howmany_toomany) -{ - if(IsTeamBalanceForced() == 1) - { - bprint("Rebalancing Teams\n"); - ShufflePlayerOutOfTeam(source_team); - } -} - -// part of g_balance_teams_force -// occasionally perform an audit of the teams to make -// sure they're more or less balanced in player count. -void AuditTeams() -{ - float numplayers, numteams, smallest, toomany; - float balance; - balance = IsTeamBalanceForced(); - if(balance == 0) - return; - - if(audit_teams_time > time) - return; - - audit_teams_time = time + 4 + random(); - -// bprint("Auditing teams\n"); - - CheckAllowedTeams(world); - GetTeamCounts(world); - - - numteams = numplayers = smallest = 0; - if(c1 >= 0) - { - numteams = numteams + 1; - numplayers = numplayers + c1; - smallest = c1; - } - if(c2 >= 0) - { - numteams = numteams + 1; - numplayers = numplayers + c2; - if(c2 < smallest) - smallest = c2; - } - if(c3 >= 0) - { - numteams = numteams + 1; - numplayers = numplayers + c3; - if(c3 < smallest) - smallest = c3; - } - if(c4 >= 0) - { - numteams = numteams + 1; - numplayers = numplayers + c4; - if(c4 < smallest) - smallest = c4; - } - - if(numplayers <= 0) - return; // no players to move around - if(numteams < 2) - return; // don't bother shuffling if for some reason there aren't any teams - - toomany = smallest + 1; - - if(c1 && c1 > toomany) - CauseRebalance(1, c1 - toomany); - if(c2 && c2 > toomany) - CauseRebalance(2, c2 - toomany); - if(c3 && c3 > toomany) - CauseRebalance(3, c3 - toomany); - if(c4 && c4 > toomany) - CauseRebalance(4, c4 - toomany); - - // if teams are still unbalanced, balance them further in the next audit, - // which will happen sooner (keep doing rapid audits until things are in order) - audit_teams_time = time + 0.7 + random()*0.3; -} - // code from here on is just to support maps that don't have team entities void tdm_spawnteam (string teamname, float teamcolor) {