]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/teamplay.qc
fix a few syntax errors
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / teamplay.qc
index 003ca27427cf81de8baad925733b58759e5ccfaf..b8f2f3ac8746ea4e02daae0936833d2086954699 100644 (file)
@@ -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(!teams_matter)
-               return 0;
-       if(cvar("g_campaign"))
-               return 0;
-       if(cvar("bot_vs_human") && (c3==-1 && c4==-1))
-               return 0;
-       if(!cvar("g_balance_teams_force"))
-               return -1;
-       return 1;
-}
+//float audit_teams_time;
 
 void TeamchangeFrags(entity e)
 {
@@ -68,15 +53,13 @@ string TeamNoName(float t)
 }
 
 void dom_init();
-void ctf_init();
 void runematch_init();
 void tdm_init();
-void nb_init();
 void entcs_init();
 
 void LogTeamchange(float player_id, float team_number, float type)
 {
-       if(!cvar("sv_eventlog"))
+       if(!autocvar_sv_eventlog)
                return;
 
        if(player_id < 1)
@@ -85,69 +68,6 @@ void LogTeamchange(float player_id, float team_number, float type)
        GameLogEcho(strcat(":team:", ftos(player_id), ":", ftos(team_number), ":", ftos(type)));
 }
 
-void WriteGameCvars()
-{
-       cvar_set("g_dm", ftos(g_dm));
-       cvar_set("g_tdm", ftos(g_tdm));
-       cvar_set("g_domination", ftos(g_domination));
-       cvar_set("g_ctf", ftos(g_ctf));
-       cvar_set("g_runematch", ftos(g_runematch));
-       cvar_set("g_lms", ftos(g_lms));
-       cvar_set("g_arena", ftos(g_arena));
-       cvar_set("g_ca", ftos(g_ca));
-       cvar_set("g_keyhunt", ftos(g_keyhunt));
-       cvar_set("g_assault", ftos(g_assault));
-       cvar_set("g_onslaught", ftos(g_onslaught));
-       cvar_set("g_race", ftos(g_race));
-       cvar_set("g_nexball", ftos(g_nexball));
-       cvar_set("g_cts", ftos(g_cts));
-       cvar_set("g_freezetag", ftos(g_freezetag));
-}
-
-void ReadGameCvars()
-{
-       float found;
-       float prev;
-       float i;
-
-       found = 0;
-       prev = cvar("gamecfg");
-       for(i = 0; i < 2; ++i)
-       {
-               found += (g_dm = (!found && (prev != GAME_DEATHMATCH) && cvar("g_dm")));
-               found += (g_tdm = (!found && (prev != GAME_TEAM_DEATHMATCH) && cvar("g_tdm")));
-               found += (g_domination = (!found && (prev != GAME_DOMINATION) && cvar("g_domination")));
-               found += (g_ctf = (!found && (prev != GAME_CTF) && cvar("g_ctf")));
-               found += (g_runematch = (!found && (prev != GAME_RUNEMATCH) && cvar("g_runematch")));
-               found += (g_lms = (!found && (prev != GAME_LMS) && cvar("g_lms")));
-               found += (g_arena = (!found && (prev != GAME_ARENA) && cvar("g_arena")));
-               found += (g_ca = (!found && (prev != GAME_CA) && cvar("g_ca")));
-               found += (g_keyhunt = (!found && (prev != GAME_KEYHUNT) && cvar("g_keyhunt")));
-               found += (g_assault = (!found && (prev != GAME_ASSAULT) && cvar("g_assault")));
-               found += (g_onslaught = (!found && (prev != GAME_ONSLAUGHT) && cvar("g_onslaught")));
-               found += (g_race = (!found && (prev != GAME_RACE) && cvar("g_race")));
-               found += (g_nexball = (!found && (prev != GAME_NEXBALL) && cvar("g_nexball")));
-               found += (g_cts = (!found && (prev != GAME_CTS) && cvar("g_cts")));
-               found += (g_freezetag = (!found && (prev != GAME_FREEZETAG) && cvar("g_freezetag")));
-
-               if(found)
-                       break;
-
-               prev = -1; // second attempt takes place WITHOUT prev set
-       }
-
-       if(!found)
-               g_dm = 1;
-
-       if(g_dm && cvar("deathmatch_force_teamplay"))
-       {
-               g_dm = 0;
-               g_tdm = 1;
-       }
-
-       teams_matter = 0;
-}
-
 void default_delayedinit()
 {
        if(!scores_initialized)
@@ -156,16 +76,8 @@ void default_delayedinit()
 
 void ActivateTeamplay()
 {
-       float teamplay_default;
-       teamplay_default = cvar("teamplay_default");
-
-       if(teamplay_default)
-               teamplay = teamplay_default;
-       else
-               teamplay = 3;
-       cvar_set("teamplay", ftos(teamplay));
-
-       teams_matter = 1;
+       serverflags |= SERVERFLAG_TEAMPLAY;
+       teamplay = 1;
 }
 
 void InitGameplayMode()
@@ -176,19 +88,14 @@ void InitGameplayMode()
 
        VoteReset();
 
-       teams_matter = 0;
-       cvar_set("teamplay", "0");
-
-       // make sure only ONE type is selected
-       ReadGameCvars();
-       WriteGameCvars();
-
        // find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds
        get_mi_min_max(1);
        world.mins = mi_min;
        world.maxs = mi_max;
 
        MapInfo_LoadMapSettings(mapname);
+       teamplay = 0;
+       serverflags &~= SERVERFLAG_TEAMPLAY;
 
        if not(cvar_value_issafe(world.fog))
        {
@@ -204,81 +111,53 @@ void InitGameplayMode()
 
        MapInfo_ClearTemps();
 
-       // in case mapinfo switched the type
-       ReadGameCvars();
-
        // set both here, gamemode can override it later
-       timelimit_override = cvar("timelimit_override");
-       fraglimit_override = cvar("fraglimit_override");
-       leadlimit_override = cvar("leadlimit_override");
+       timelimit_override = autocvar_timelimit_override;
+       fraglimit_override = autocvar_fraglimit_override;
+       leadlimit_override = autocvar_leadlimit_override;
+       gamemode_name = MapInfo_Type_ToText(MapInfo_LoadedGametype);
 
        if(g_dm)
        {
-               game = GAME_DEATHMATCH;
-               gamemode_name = "Deathmatch";
        }
 
        if(g_tdm)
        {
-               game = GAME_TEAM_DEATHMATCH;
-               gamemode_name = "Team Deathmatch";
                ActivateTeamplay();
                tdm_init();
-               if(cvar("g_tdm_team_spawns"))
+               if(autocvar_g_tdm_team_spawns)
                        have_team_spawns = -1; // request team spawns
        }
 
        if(g_domination)
        {
-               game = GAME_DOMINATION;
-               gamemode_name = "Domination";
                ActivateTeamplay();
-               fraglimit_override = cvar("g_domination_point_limit");
-               leadlimit_override = cvar("g_domination_point_leadlimit");
+               fraglimit_override = autocvar_g_domination_point_limit;
+               leadlimit_override = autocvar_g_domination_point_leadlimit;
                dom_init();
                have_team_spawns = -1; // request team spawns
        }
 
        if(g_ctf)
        {
-               game = GAME_CTF;
-               gamemode_name = "Capture the Flag";
                ActivateTeamplay();
-               if(cvar("g_campaign"))
-                       g_ctf_win_mode = 2;
-               else
-                       g_ctf_win_mode = cvar("g_ctf_win_mode");
-               g_ctf_ignore_frags = cvar("g_ctf_ignore_frags");
-               if(g_ctf_win_mode == 2)
-               {
-                       fraglimit_override = cvar("g_ctf_capture_limit");
-                       leadlimit_override = cvar("g_ctf_capture_leadlimit");
-               }
-               else
-               {
-                       fraglimit_override = cvar("capturelimit_override");
-                       leadlimit_override = cvar("captureleadlimit_override");
-               }
-               ctf_init();
+               fraglimit_override = autocvar_capturelimit_override;
+               leadlimit_override = autocvar_captureleadlimit_override;
+               MUTATOR_ADD(gamemode_ctf);
                have_team_spawns = -1; // request team spawns
        }
 
        if(g_runematch)
        {
-               game = GAME_RUNEMATCH;
-               gamemode_name = "Rune Match";
-               if(cvar("deathmatch_force_teamplay"))
-                       ActivateTeamplay();
-               fraglimit_override = cvar("g_runematch_point_limit");
-               leadlimit_override = cvar("g_runematch_point_leadlimit");
+               // ActivateTeamplay();
+               fraglimit_override = autocvar_g_runematch_point_limit;
+               leadlimit_override = autocvar_g_runematch_point_leadlimit;
                runematch_init();
        }
 
        if(g_lms)
        {
-               game = GAME_LMS;
-               gamemode_name = "Last Man Standing";
-               fraglimit_override = cvar("g_lms_lives_override");
+               fraglimit_override = autocvar_g_lms_lives_override;
                leadlimit_override = 0; // not supported by LMS
                if(fraglimit_override == 0)
                        fraglimit_override = -1;
@@ -289,50 +168,40 @@ void InitGameplayMode()
 
        if(g_arena)
        {
-               game = GAME_ARENA;
-               gamemode_name = "Arena";
-               fraglimit_override = cvar("g_arena_point_limit");
-               leadlimit_override = cvar("g_arena_point_leadlimit");
-               maxspawned = cvar("g_arena_maxspawned");
+               fraglimit_override = autocvar_g_arena_point_limit;
+               leadlimit_override = autocvar_g_arena_point_leadlimit;
+               maxspawned = autocvar_g_arena_maxspawned;
                if(maxspawned < 2)
                        maxspawned = 2;
-               arena_roundbased = cvar("g_arena_roundbased");
+               arena_roundbased = autocvar_g_arena_roundbased;
        }
 
        if(g_ca)
        {
-               game = GAME_CA;
-               gamemode_name = "Clan Arena";
                ActivateTeamplay();
-               fraglimit_override = cvar("g_ca_point_limit");
-               leadlimit_override = cvar("g_ca_point_leadlimit");
+               fraglimit_override = autocvar_g_ca_point_limit;
+               leadlimit_override = autocvar_g_ca_point_leadlimit;
                precache_sound("ctf/red_capture.wav");
                precache_sound("ctf/blue_capture.wav");
        }
        if(g_keyhunt)
        {
-               game = GAME_KEYHUNT;
-               gamemode_name = "Key Hunt";
                ActivateTeamplay();
-               fraglimit_override = cvar("g_keyhunt_point_limit");
-               leadlimit_override = cvar("g_keyhunt_point_leadlimit");
+               fraglimit_override = autocvar_g_keyhunt_point_limit;
+               leadlimit_override = autocvar_g_keyhunt_point_leadlimit;
                MUTATOR_ADD(gamemode_keyhunt);
        }
 
        if(g_freezetag)
        {
-               game = GAME_FREEZETAG;
-               gamemode_name = "Freeze Tag";
                ActivateTeamplay();
-               fraglimit_override = cvar("g_freezetag_point_limit");
-               leadlimit_override = cvar("g_freezetag_point_leadlimit");
+               fraglimit_override = autocvar_g_freezetag_point_limit;
+               leadlimit_override = autocvar_g_freezetag_point_leadlimit;
                MUTATOR_ADD(gamemode_freezetag);
        }
 
        if(g_assault)
        {
-               game = GAME_ASSAULT;
-               gamemode_name = "Assault";
                ActivateTeamplay();
                ScoreRules_assault();
                have_team_spawns = -1; // request team spawns
@@ -340,35 +209,30 @@ void InitGameplayMode()
 
        if(g_onslaught)
        {
-               game = GAME_ONSLAUGHT;
-               gamemode_name = "Onslaught";
                ActivateTeamplay();
                have_team_spawns = -1; // request team spawns
+               MUTATOR_ADD(gamemode_onslaught);
        }
 
        if(g_race)
        {
-               game = GAME_RACE;
-               gamemode_name = "Race";
 
-               if(cvar("g_race_teams"))
+               if(autocvar_g_race_teams)
                {
                        ActivateTeamplay();
-                       race_teams = bound(2, cvar("g_race_teams"), 4);
+                       race_teams = bound(2, autocvar_g_race_teams, 4);
                        have_team_spawns = -1; // request team spawns
                }
                else
                        race_teams = 0;
 
-               qualifying_override = cvar("g_race_qualifying_timelimit_override");
-               fraglimit_override = cvar("g_race_laps_limit");
+               qualifying_override = autocvar_g_race_qualifying_timelimit_override;
+               fraglimit_override = autocvar_g_race_laps_limit;
                leadlimit_override = 0; // currently not supported by race
        }
 
        if(g_cts)
        {
-               game = GAME_CTS;
-               gamemode_name = "CTS";
                g_race_qualifying = 1;
                fraglimit_override = 0;
                leadlimit_override = 0;
@@ -376,26 +240,26 @@ void InitGameplayMode()
 
        if(g_nexball)
        {
-               game = GAME_NEXBALL;
-               gamemode_name = "Nexball";
-               fraglimit_override = cvar("g_nexball_goallimit");
-               leadlimit_override = cvar("g_nexball_goalleadlimit");
-               ActivateTeamplay();
-               nb_init();
-               have_team_spawns = -1; // request team spawns
+        fraglimit_override = autocvar_g_nexball_goallimit;
+        leadlimit_override = autocvar_g_nexball_goalleadlimit;
+        ActivateTeamplay();
+        have_team_spawns = -1; // request team spawns
+           MUTATOR_ADD(gamemode_nexball);
+       }
+        
+       if(g_keepaway)
+       {
+               MUTATOR_ADD(gamemode_keepaway);
        }
 
-       if(teams_matter)
+       if(teamplay)
                entcs_init();
 
-       // save it (for the next startup)
-       cvar_set("gamecfg", ftos(game));
-
        cache_mutatormsg = strzone("");
        cache_lastmutatormsg = strzone("");
 
        // enforce the server's universal frag/time limits
-       if(!cvar("g_campaign"))
+       if(!autocvar_g_campaign)
        {
                if(fraglimit_override >= 0)
                        cvar_set("fraglimit", ftos(fraglimit_override));
@@ -410,19 +274,19 @@ void InitGameplayMode()
        if(g_race)
        {
                // we need to find out the correct value for g_race_qualifying
-               if(cvar("g_campaign"))
+               if(autocvar_g_campaign)
                {
                        g_race_qualifying = 1;
                }
-               else if(!cvar("g_campaign") && cvar("g_race_qualifying_timelimit") > 0)
+               else if(!autocvar_g_campaign && autocvar_g_race_qualifying_timelimit > 0)
                {
                        g_race_qualifying = 2;
-                       race_fraglimit = cvar("fraglimit");
-                       race_leadlimit = cvar("leadlimit");
-                       race_timelimit = cvar("timelimit");
+                       race_fraglimit = autocvar_fraglimit;
+                       race_leadlimit = autocvar_leadlimit;
+                       race_timelimit = autocvar_timelimit;
                        cvar_set("fraglimit", "0");
                        cvar_set("leadlimit", "0");
-                       cvar_set("timelimit", cvar_string("g_race_qualifying_timelimit"));
+                       cvar_set("timelimit", ftos(autocvar_g_race_qualifying_timelimit));
                }
                else
                        g_race_qualifying = 0;
@@ -440,9 +304,9 @@ void InitGameplayMode()
 }
 
 string GetClientVersionMessage() {
-       local string versionmsg;
+       string versionmsg;
        if (self.version_mismatch) {
-               if(self.version < cvar("gameversion")) {
+               if(self.version < autocvar_gameversion) {
                        versionmsg = "^3Your client version is outdated.\n\n\n### YOU WON'T BE ABLE TO PLAY ON THIS SERVER ###\n\n\nPlease update!!!^8";
                } else {
                        versionmsg = "^3This server is using an outdated Xonotic version.\n\n\n ### THIS SERVER IS INCOMPATIBLE AND THUS YOU CANNOT JOIN ###.^8";
@@ -453,56 +317,10 @@ string GetClientVersionMessage() {
        return versionmsg;
 }
 
-
-void PrintWelcomeMessage(entity pl)
+string getwelcomemessage(void)
 {
        string s, modifications, motd;
 
-       if(self.cvar_scr_centertime == 0) return;
-
-       if(cvar("g_campaign"))
-       {
-               if(self.classname == "player" && !self.BUTTON_INFO)
-                       return;
-       }
-       else
-       {
-               if((time - self.jointime) > cvar("welcome_message_time") && !self.BUTTON_INFO)
-                       return;
-       }
-
-       if( !(timeoutStatus >= 1) ) { //really print the WelcomeMessage to the player every frame when timeout-seconds are shown or the game is restarted, to make sure that the shown number is accurate
-               if(self.welcomemessage_time > time) return;
-               self.welcomemessage_time = time + max(0.5, self.cvar_scr_centertime * 0.6);
-       }
-
-       if(cvar("g_campaign"))
-       {
-               centerprint(pl, campaign_message);
-               return;
-       }
-
-//TODO GreEn`mArine: make the timeout-messages clientside as well (just like the ready restart countdown)!
-       if(!self.BUTTON_INFO)
-       {
-               // TODO get rid of this too
-               local string specString;
-               specString = NEWLINES;
-               //if(time < game_starttime) //also show the countdown when being a spectator
-               //      specString = strcat(specString, "\n\n^1Game starts in ", ftos(ceil(game_starttime - time)), " seconds^7");
-               //else
-               if (timeoutStatus != 0)
-                       specString = strcat(specString, "\n\n", getTimeoutText(1));
-               else
-               {
-                       if(self.classname == "player")
-                               return;
-                       goto normal;
-               }
-               return centerprint_atprio(self, CENTERPRIO_SPAM, specString);
-       }
-
-:normal
        ret_string = "";
        MUTATOR_CALLHOOK(BuildMutatorsPrettyString);
        modifications = ret_string;
@@ -516,48 +334,49 @@ void PrintWelcomeMessage(entity pl)
                else
                        modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena");
        }
-       if(cvar("g_start_weapon_laser") == 0)
+       if(autocvar_g_start_weapon_laser == 0)
                modifications = strcat(modifications, ", No start weapons");
-       if(cvar("sv_gravity") < 800)
+       if(autocvar_sv_gravity < 800)
                modifications = strcat(modifications, ", Low gravity");
-       if(g_cloaked)
+       if(g_cloaked && !g_cts)
                modifications = strcat(modifications, ", Cloaked");
        if(g_grappling_hook)
                modifications = strcat(modifications, ", Hook");
        if(g_midair)
                modifications = strcat(modifications, ", Midair");
        if(g_pinata)
-               modifications = strcat(modifications, ", Pinata");
-       if(g_weapon_stay)
+               modifications = strcat(modifications, ", Piñata");
+       if(g_weapon_stay && !g_cts)
                modifications = strcat(modifications, ", Weapons stay");
        if(g_bloodloss > 0)
-               modifications = strcat(modifications, ", Bloodloss");
+               modifications = strcat(modifications, ", Blood loss");
        if(g_jetpack)
                modifications = strcat(modifications, ", Jet pack");
+       if(autocvar_g_powerups == 0)
+               modifications = strcat(modifications, ", No powerups");
+       if(autocvar_g_powerups > 0)
+               modifications = strcat(modifications, ", Powerups");
        modifications = substring(modifications, 2, strlen(modifications) - 2);
 
-       local string versionmessage;
+       string versionmessage;
        versionmessage = GetClientVersionMessage();
 
-       s = strcat(s, NEWLINES, "This is Xonotic ", cvar_string("g_xonoticversion"), "\n", versionmessage);
+       s = strcat("This is Xonotic ", autocvar_g_xonoticversion, "\n", versionmessage);
        s = strcat(s, "^8\n\nmatch type is ^1", gamemode_name, "^8\n");
 
        if(modifications != "")
                s = strcat(s, "^8\nactive modifications: ^3", modifications, "^8\n");
 
-       if(timeoutStatus != 0)
-               s = strcat(s, "\n\n", getTimeoutText(1));
-
        if (g_grappling_hook)
                s = strcat(s, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n");
 
-       if(cache_lastmutatormsg != cvar_string("g_mutatormsg"))
+       if(cache_lastmutatormsg != autocvar_g_mutatormsg)
        {
                if(cache_lastmutatormsg)
                        strunzone(cache_lastmutatormsg);
                if(cache_mutatormsg)
                        strunzone(cache_mutatormsg);
-               cache_lastmutatormsg = strzone(cvar_string("g_mutatormsg"));
+               cache_lastmutatormsg = strzone(autocvar_g_mutatormsg);
                cache_mutatormsg = strzone(cache_lastmutatormsg);
        }
 
@@ -565,16 +384,13 @@ void PrintWelcomeMessage(entity pl)
                s = strcat(s, "\n\n^8special gameplay tips: ^7", cache_mutatormsg);
        }
 
-       motd = cvar_string("sv_motd");
+       motd = autocvar_sv_motd;
        if (motd != "") {
                s = strcat(s, "\n\n^8MOTD: ^7", strreplace("\\n", "\n", motd));
        }
-       s = strcat(s, "\n");
-
-       centerprint(pl, s);
+       return s;
 }
 
-
 void SetPlayerColors(entity pl, float _color)
 {
        /*string s;
@@ -589,7 +405,7 @@ void SetPlayerColors(entity pl, float _color)
        shirt = _color & 0xF0;
 
 
-       if(teams_matter) {
+       if(teamplay) {
                setcolor(pl, 16*pants + pants);
        } else {
                setcolor(pl, shirt + pants);
@@ -630,11 +446,8 @@ void CheckAllowedTeams (entity for_whom)
        c1 = c2 = c3 = c4 = -1;
        cb1 = cb2 = cb3 = cb4 = 0;
 
-       if(cvar("g_campaign") && for_whom && clienttype(for_whom) == CLIENTTYPE_REAL)
-       {
-               c1 = 0; // only allow RED team for player joining
-       }
-       else if(g_onslaught)
+       teament_name = string_null;
+       if(g_onslaught)
        {
                // onslaught is special
                head = findchain(classname, "onslaught_generator");
@@ -700,9 +513,9 @@ void CheckAllowedTeams (entity for_whom)
 
        // TODO: Balance quantity of bots across > 2 teams when bot_vs_human is set (and remove next line)
        if(c3==-1 && c4==-1)
-       if(cvar("bot_vs_human") && for_whom)
+       if(autocvar_bot_vs_human && for_whom)
        {
-               if(cvar("bot_vs_human") > 0)
+               if(autocvar_bot_vs_human > 0)
                {
                        // bots are all blue
                        if(clienttype(for_whom) == CLIENTTYPE_BOT)
@@ -733,8 +546,6 @@ void CheckAllowedTeams (entity for_whom)
 
 float PlayerValue(entity p)
 {
-       if(IsTeamBalanceForced() == 1)
-               return 1;
        return 1;
        // FIXME: it always returns 1...
 }
@@ -750,8 +561,15 @@ void GetTeamCounts(entity ignore)
        // FIXME: also find and memorize the lowest-scoring bot on each team (in case players must be shuffled around)
        // also remember the lowest-scoring player
 
-       FOR_EACH_PLAYER(head)
+       FOR_EACH_CLIENT(head)
        {
+               float t;
+               if(head.classname == "player")
+                       t = head.team;
+               else if(head.team_forced > 0)
+                       t = head.team_forced; // reserve the spot
+               else
+                       continue;
                if(head != ignore)// && head.netname != "")
                {
                        value = PlayerValue(head);
@@ -759,7 +577,7 @@ void GetTeamCounts(entity ignore)
                                bvalue = value;
                        else
                                bvalue = 0;
-                       if(head.team == COLOR_TEAM1)
+                       if(t == COLOR_TEAM1)
                        {
                                if(c1 >= 0)
                                {
@@ -767,7 +585,7 @@ void GetTeamCounts(entity ignore)
                                        cb1 = cb1 + bvalue;
                                }
                        }
-                       if(head.team == COLOR_TEAM2)
+                       if(t == COLOR_TEAM2)
                        {
                                if(c2 >= 0)
                                {
@@ -775,7 +593,7 @@ void GetTeamCounts(entity ignore)
                                        cb2 = cb2 + bvalue;
                                }
                        }
-                       if(head.team == COLOR_TEAM3)
+                       if(t == COLOR_TEAM3)
                        {
                                if(c3 >= 0)
                                {
@@ -783,7 +601,7 @@ void GetTeamCounts(entity ignore)
                                        cb3 = cb3 + bvalue;
                                }
                        }
-                       if(head.team == COLOR_TEAM4)
+                       if(t == COLOR_TEAM4)
                        {
                                if(c4 >= 0)
                                {
@@ -793,13 +611,87 @@ void GetTeamCounts(entity ignore)
                        }
                }
        }
+
+       // 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 == cb1) ++c1; break;
+                       case 2: if(c2 == cb2) ++c2; break;
+                       case 3: if(c3 == cb3) ++c3; break;
+                       case 4: if(c4 == cb4) ++c4; break;
+               }
+       }
+}
+
+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(clienttype(e) == CLIENTTYPE_REAL)
+       {
+               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
@@ -815,12 +707,12 @@ float FindSmallestTeam(entity pl, float ignore_pl)
        if(c4 >= 0)
                totalteams = totalteams + 1;
 
-       if((cvar("bot_vs_human") || pl.team_forced > 0) && totalteams == 1)
+       if((autocvar_bot_vs_human || pl.team_forced > 0) && totalteams == 1)
                totalteams += 1;
 
        if(totalteams <= 1)
        {
-               if(cvar("g_campaign") && pl && clienttype(pl) == CLIENTTYPE_REAL)
+               if(autocvar_g_campaign && pl && clienttype(pl) == CLIENTTYPE_REAL)
                        return 1; // special case for campaign and player joining
                else if(g_domination)
                        error("Too few teams available for domination\n");
@@ -840,49 +732,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;
 }
 
@@ -891,39 +760,12 @@ float JoinBestTeam(entity pl, float only_return_best, float forcebestteam)
        float smallest, selectedteam;
 
        // don't join a team if we're not playing a team game
-       if(!teams_matter)
+       if(!teamplay)
                return 0;
 
        // find out what teams are available
        CheckAllowedTeams(pl);
 
-       // if we want the player in a certain team for campaign, force him there
-       if(cvar("g_campaign"))
-       if(clienttype(pl) == CLIENTTYPE_REAL) // only players, not bots
-       {
-               switch(cvar("g_campaign_forceteam"))
-               {
-                       case 1:
-                               SetPlayerColors(pl, COLOR_TEAM1 - 1);
-                               LogTeamchange(pl.playerid, pl.team, 2);
-                               return COLOR_TEAM1;
-                       case 2:
-                               SetPlayerColors(pl, COLOR_TEAM2 - 1);
-                               LogTeamchange(pl.playerid, pl.team, 2);
-                               return COLOR_TEAM2;
-                       case 3:
-                               SetPlayerColors(pl, COLOR_TEAM3 - 1);
-                               LogTeamchange(pl.playerid, pl.team, 2);
-                               return COLOR_TEAM3;
-                       case 4:
-                               SetPlayerColors(pl, COLOR_TEAM4 - 1);
-                               LogTeamchange(pl.playerid, pl.team, 2);
-                               return COLOR_TEAM4;
-                       default:
-                               break;
-               }
-       }
-
        // if we don't care what team he ends up on, put him on whatever team he entered as.
        // if he's not on a valid team, then let other code put him on the smallest team
        if(!forcebestteam)
@@ -992,10 +834,10 @@ 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(!teams_matter) {
+       if(!teamplay) {
                SetPlayerColors(self, _color);
                return;
        }
@@ -1035,67 +877,20 @@ void SV_ChangeTeam(float _color)
                return;
        }
 
-       if((cvar("g_campaign")) || (cvar("g_changeteam_banned") && self.wasplayer)) {
+       if((autocvar_g_campaign) || (autocvar_g_changeteam_banned && self.wasplayer)) {
                sprint(self, "Team changes not allowed\n");
                return; // changing teams is not allowed
        }
 
-       if(cvar("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)
+               GetTeamCounts(self);
+               if(!TeamSmallerEqThanTeam(dteam, steam, self))
                {
-                       dcount = c1;
-                       dbotcount = cb1;
-               }
-               else if(dteam == 2 && c2 >= 0)//dcolor == COLOR_TEAM2 - 1)
-               {
-                       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");
@@ -1114,7 +909,6 @@ void SV_ChangeTeam(float _color)
                if(self.deadflag == DEAD_NO)
                        Damage(self, self, self, 100000, DEATH_TEAMCHANGE, self.origin, '0 0 0');
        }
-       //ctf_playerchanged();
 }
 
 void ShufflePlayerOutOfTeam (float source_team)
@@ -1159,7 +953,7 @@ void ShufflePlayerOutOfTeam (float source_team)
                steam = COLOR_TEAM2;
        else if(source_team == 3)
                steam = COLOR_TEAM3;
-       else if(source_team == 4)
+       else // if(source_team == 4)
                steam = COLOR_TEAM4;
 
        lowest_bot = world;
@@ -1258,106 +1052,25 @@ 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)
 {
-       local entity e;
+       entity e;
        e = spawn();
        e.classname = "tdm_team";
        e.netname = teamname;
        e.cnt = teamcolor;
        e.team = e.cnt + 1;
-};
+}
 
 // spawn some default teams if the map is not set up for tdm
 void tdm_spawnteams()
 {
        float numteams;
 
-       numteams = cvar("g_tdm_teams_override");
+       numteams = autocvar_g_tdm_teams_override;
        if(numteams < 2)
-               numteams = cvar("g_tdm_teams");
+               numteams = autocvar_g_tdm_teams;
        numteams = bound(2, numteams, 4);
 
        tdm_spawnteam("Red", COLOR_TEAM1-1);
@@ -1366,16 +1079,16 @@ void tdm_spawnteams()
                tdm_spawnteam("Yellow", COLOR_TEAM3-1);
        if(numteams >= 4)
                tdm_spawnteam("Pink", COLOR_TEAM4-1);
-};
+}
 
 void tdm_delayedinit()
 {
        // if no teams are found, spawn defaults
        if (find(world, classname, "tdm_team") == world)
                tdm_spawnteams();
-};
+}
 
 void tdm_init()
 {
        InitializeEntity(world, tdm_delayedinit, INITPRIO_GAMETYPE);
-};
+}