about summary refs log tree commit diff
path: root/src/game
diff options
context:
space:
mode:
authorAlfred Eriksson <somerunce@gmail.com>2008-09-07 08:10:56 +0000
committerAlfred Eriksson <somerunce@gmail.com>2008-09-07 08:10:56 +0000
commit6dcea2c4ca59e0f1ac8450cc31550bdb35bc21b1 (patch)
tree52d63fa64e79123e34e19c15819dbd0fb889abc4 /src/game
parent4fb71c1bca5b5acd86f8ef92441ff687679a95a7 (diff)
downloadzcatch-6dcea2c4ca59e0f1ac8450cc31550bdb35bc21b1.tar.gz
zcatch-6dcea2c4ca59e0f1ac8450cc31550bdb35bc21b1.zip
mermerge from 0.4.3: auto team balancing
Diffstat (limited to 'src/game')
-rw-r--r--src/game/server/entities/character.cpp10
-rw-r--r--src/game/server/gamecontroller.cpp106
-rw-r--r--src/game/server/gamecontroller.hpp6
-rw-r--r--src/game/server/hooks.cpp14
-rw-r--r--src/game/server/player.hpp1
-rw-r--r--src/game/variables.hpp1
6 files changed, 135 insertions, 3 deletions
diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp
index 5c307e62..463b6640 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 7ff69880..4c3c121b 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 1a87034b..2cffc8a8 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 773203bd..8688df28 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 491598d4..55833d11 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 2803915c..5154e8ac 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)