diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 5c307e62e..463b66409 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -62,6 +62,7 @@ bool CHARACTER::spawn(PLAYER *player, vec2 pos, int team) game.world.insert_entity(this); alive = true; + player->force_balanced = false; game.controller->on_character_spawn(this); @@ -563,6 +564,15 @@ void CHARACTER::tick() return; } * */ + + if(player->force_balanced) + { + char buf[128]; + str_format(buf, sizeof(buf), "You were moved to %s due to team balancing", game.controller->get_team_name(team)); + game.send_broadcast(buf, player->client_id); + + player->force_balanced = false; + } //player_core core; //core.pos = pos; diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index 7ff698801..4c3c121b9 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -26,6 +26,8 @@ GAMECONTROLLER::GAMECONTROLLER() teamscore[0] = 0; teamscore[1] = 0; + unbalanced_tick = -1; + num_spawn_points[0] = 0; num_spawn_points[1] = 0; num_spawn_points[2] = 0; @@ -188,6 +190,7 @@ void GAMECONTROLLER::startround() game.world.paused = false; teamscore[0] = 0; teamscore[1] = 0; + unbalanced_tick = -1; round_count++; } @@ -341,6 +344,49 @@ void GAMECONTROLLER::tick() } } + if (is_teamplay() && unbalanced_tick != -1 && server_tick() > unbalanced_tick+config.sv_teambalance_time*server_tickspeed()*60) + { + dbg_msg("game", "Balancing teams"); + + int t[2] = {0,0}; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(game.players[i].client_id != -1 && game.players[i].team != -1) + t[game.players[i].team]++; + } + + int m = (t[0] > t[1]) ? 0 : 1; + int num_balance = abs(t[0]-t[1]) / 2; + int scorediff = abs(teamscore[0]-teamscore[1]); + + do + { + // move player who is closest to team-scorediff + PLAYER *p = 0; + int pd = teamscore[m]; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(game.players[i].client_id == -1) + continue; + + if(game.players[i].team == m && (!p || abs(scorediff - game.players[i].score) < pd)) + { + p = &(game.players[i]); + pd = abs(scorediff - game.players[i].score); + } + } + + // change in player::set_team needed: player won't lose score on team-change + int score_before = p->score; + p->set_team(m^1); + p->score = score_before; + + p->respawn(); + p->force_balanced = true; + } while (--num_balance); + + unbalanced_tick = -1; + } // update browse info int prog = -1; @@ -431,6 +477,66 @@ bool GAMECONTROLLER::can_join_team(int team, int notthisid) return (numplayers[0] + numplayers[1]) < config.sv_max_clients-config.sv_spectator_slots; } +bool GAMECONTROLLER::check_team_balance() +{ + if(!is_teamplay() || !config.sv_teambalance_time) + return true; + + int t[2] = {0, 0}; + for(int i = 0; i < MAX_CLIENTS; i++) + { + PLAYER *p = &(game.players[i]); + if(p->client_id != -1 && p->team != -1) + t[p->team]++; + } + + if(abs(t[0]-t[1]) >= 2) + { + dbg_msg("game", "Team is NOT balanced (red=%d blue=%d)", t[0], t[1]); + if (game.controller->unbalanced_tick == -1) + game.controller->unbalanced_tick = server_tick(); + return false; + } + else + { + dbg_msg("game", "Team is balanced (red=%d blue=%d)", t[0], t[1]); + game.controller->unbalanced_tick = -1; + return true; + } +} + +bool GAMECONTROLLER::can_change_team(PLAYER *pplayer, int jointeam) +{ + int t[2] = {0, 0}; + + if (!is_teamplay() || jointeam == -1 || !config.sv_teambalance_time) + return true; + + for(int i = 0; i < MAX_CLIENTS; i++) + { + PLAYER *p = &(game.players[i]); + if(p->client_id != -1 && p->team != -1) + t[p->team]++; + } + + // simulate what would happen if changed team + t[jointeam]++; + if (pplayer->team != -1) + t[jointeam^1]--; + + // there is a player-difference of at least 2 + if(abs(t[0]-t[1]) >= 2) + { + // player wants to join team with less players + if ((t[0] < t[1] && jointeam == 0) || (t[0] > t[1] && jointeam == 1)) + return true; + else + return false; + } + else + return true; +} + void GAMECONTROLLER::do_player_score_wincheck() { if(game_over_tick == -1 && !warmup) diff --git a/src/game/server/gamecontroller.hpp b/src/game/server/gamecontroller.hpp index 1a87034b1..2cffc8a85 100644 --- a/src/game/server/gamecontroller.hpp +++ b/src/game/server/gamecontroller.hpp @@ -34,7 +34,7 @@ protected: void cyclemap(); void resetgame(); - + const char *gametype; int round_start_tick; @@ -52,6 +52,8 @@ protected: public: bool is_teamplay() const; + int unbalanced_tick; + GAMECONTROLLER(); void do_team_score_wincheck(); @@ -118,6 +120,8 @@ public: virtual const char *get_team_name(int team); virtual int get_auto_team(int notthisid); virtual bool can_join_team(int team, int notthisid); + bool check_team_balance(); + bool can_change_team(PLAYER *pplayer, int jointeam); int clampteam(int team); virtual void post_reset(); diff --git a/src/game/server/hooks.cpp b/src/game/server/hooks.cpp index 773203bde..8688df282 100644 --- a/src/game/server/hooks.cpp +++ b/src/game/server/hooks.cpp @@ -104,6 +104,8 @@ void mods_connected(int client_id) game.players[client_id].team = -1; else game.players[client_id].team = game.controller->get_auto_team(client_id); + + (void) game.controller->check_team_balance(); // send motd NETMSG_SV_MOTD msg; @@ -115,7 +117,7 @@ void mods_connected(int client_id) void mods_client_drop(int client_id) { game.players[client_id].on_disconnect(); - + (void) game.controller->check_team_balance(); } void mods_message(int msgtype, int client_id) @@ -156,7 +158,15 @@ void mods_message(int msgtype, int client_id) // Switch team on given client and kill/respawn him if(game.controller->can_join_team(msg->team, client_id)) - p->set_team(msg->team); + { + if(game.controller->can_change_team(p, msg->team)) + { + p->set_team(msg->team); + (void) game.controller->check_team_balance(); + } + else + game.send_broadcast("Teams must be balanced, please join other team", client_id); + } else { char buf[128]; diff --git a/src/game/server/player.hpp b/src/game/server/player.hpp index 491598d4f..55833d11e 100644 --- a/src/game/server/player.hpp +++ b/src/game/server/player.hpp @@ -21,6 +21,7 @@ public: int client_id; int team; int score; + bool force_balanced; // int64 last_chat; diff --git a/src/game/variables.hpp b/src/game/variables.hpp index 2803915c7..5154e8ac9 100644 --- a/src/game/variables.hpp +++ b/src/game/variables.hpp @@ -61,3 +61,4 @@ MACRO_CONFIG_INT(sv_tournament_mode, 0, 0, 1) MACRO_CONFIG_INT(sv_spamprotection, 1, 0, 1) MACRO_CONFIG_INT(sv_spectator_slots, 0, 0, 12) +MACRO_CONFIG_INT(sv_teambalance_time, 1, 0, 1000)