I found an auto team balancer plugin for my new Half-Life TDM server. But plugin does not work correctly on my server. Teams were not balancing when 1v1. I tried different cvars like mp_teamplay 1 and mp_teamplay 2. (I already wrote mp_teamlist "gman;gordon") I hope you can help me.
Code:
/* AMX Mod X script.
http://aghl.ru/forum/ - Russian Half-Life and Adrenaline Gamer Community
AutoTeam Blancer plugin for Half-Life by Lev.
URL: http://aghl.ru/forum/viewtopic.php?f=19&t=638
Info:
Plugin balances teams trying to keep teams equals by players counts.
Takes into account: players counts, spectators, hltv.
Transfers last connected player or player with least score.
Plugin works only if mp_teamplay is 1 and mp_teamlist is set.
Correctly works with any amount of teams and any team names.
CVARs:
teambalancer_exclude "abc" // (default = "c") Flags that excludes player from being transferred by auto team balancing. Flags on bots are ignored.
teambalancer_balancebots "1" // (default = "1") Bots are allowed to be transfered in balancing process.
teambalancer_botsfirst "1" // (default = "1") Bots are preffered before players to transfer.
Installation:
put hl_auto_team_balancer.amxx to plugins folder;
add hl_auto_team_balancer.amxx to config\plugins.ini file.
Thanks to:
koko76 for bug reporting.
Change Log:
v1.1 [2012.01.30]
Initial release.
v1.2 [2012.02.04]
[!] Change: access flags on bots are ignored.
[+] Added: new cvars to regulate bots transferring.
[!] Fixed: possible crash with 32 players.
v1.3 [2012.02.05]
[!] Fixed: bug in balancer code (thanks to koko76).
v1.4 [2012.02.06]
[!] Fixed: minor bug in balancer code.
*/
#pragma semicolon 1
#pragma ctrlchar '\'
#include <amxmodx>
#include <amxmisc>
#include <fakemeta_util>
#define AUTHOR "Lev"
#define PLUGIN "HL Auto Team Balancer"
#define VERSION "1.4"
#define VERSION_CVAR "hlautoteambalancer_version"
// Compilation options
//#define _DEBUG // Enable debug output at server console.
#define MAX_TEAMS 32
#define MAX_TEAMNAME 16
#define MAX_PLAYERS 32
enum PlayerInfo
{
PiId,
PiIsBot,
PiTeamId,
PiFrags,
PiDeaths
}
new _playerInfo[MAX_PLAYERS + 1][PlayerInfo];
new _teamNames[MAX_TEAMS][MAX_TEAMNAME + 1];
new _teams;
new _counts[MAX_TEAMS], _frags[MAX_TEAMS], _deaths[MAX_TEAMS];
new pcvar_teambalancer_exclude, pcvar_teambalancer_balancebots, pcvar_teambalancer_botsfirst;
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR);
register_cvar(VERSION_CVAR, VERSION, FCVAR_SPONLY | FCVAR_SERVER | FCVAR_UNLOGGED);
pcvar_teambalancer_exclude = register_cvar("teambalancer_exclude", "c"); // Flags that excludes player from being transferred by auto team balancing. Flags on bots are ignored.
pcvar_teambalancer_balancebots = register_cvar("teambalancer_balancebots", "1"); // Bots are allowed to be transfered in balancing process.
pcvar_teambalancer_botsfirst = register_cvar("teambalancer_botsfirst", "1"); // Bots are preffered before players to transfer.
#if defined _DEBUG
register_concmd("bal", "BalanceTeamsByCount");
#endif
GetTeamNames();
if (!get_cvar_num("mp_teamplay") || _teams == 0)
{
log_amx("Current mode is not team play or team list is not limited. Plugin is paused...");
pause("ad");
return;
}
}
public client_putinserver(id)
{
set_task(0.2, "BalanceTeamsByCount", id);
}
public client_command(id)
{
new cmd[33];
read_argv(0, cmd, charsmax(cmd));
if (equal(cmd, "spectate"))
set_task(0.2, "BalanceTeamsByCount");
return PLUGIN_CONTINUE;
}
public client_infochanged(id)
{
new teamId = get_user_team(id) - 1;
new i = 0;
// Find previous player team
while(_playerInfo[i][PiId])
{
if (_playerInfo[i][PiId] == id)
{
if (_playerInfo[i][PiTeamId] != teamId)
{
#if defined _DEBUG
server_print("Team change detected: player %i, prev team: %i, new team: %i", id, _playerInfo[i][PiTeamId], teamId);
#endif
BalanceTeamsByCount(id);
}
break;
}
i++;
}
}
public client_disconnect(id)
{
set_task(0.2, "BalanceTeamsByCount", 0);
}
public BalanceTeamsByCount(idLast)
{
#if defined _DEBUG
server_print("idLast: %i", idLast);
#endif
RecountTeams();
static i, id, j, selected, maxPlayers, minPlayers, largestTeamId, smallestTeamId, minFrags, minFragsSet, flagsStr[33], flags, teambalancer_balancebots, teambalancer_botsfirst;
maxPlayers = 0;
minPlayers = MAX_PLAYERS;
largestTeamId = 0;
smallestTeamId = 0;
for (j = 0; j < _teams; j++)
{
if (_counts[j] > maxPlayers)
{
maxPlayers = _counts[j];
largestTeamId = j;
}
if (_counts[j] < minPlayers)
{
minPlayers = _counts[j];
smallestTeamId = j;
}
}
#if defined _DEBUG
server_print("Largest team %i %s: count: %i", largestTeamId, _teamNames[largestTeamId], maxPlayers);
server_print("Smallest team %i %s: count: %i", smallestTeamId, _teamNames[smallestTeamId], minPlayers);
#endif
if (maxPlayers - minPlayers > 1)
{
#if defined _DEBUG
server_print("Going to find player to transfer...");
#endif
get_pcvar_string(pcvar_teambalancer_exclude, flagsStr, charsmax(flagsStr));
flags = read_flags_fixed(flagsStr);
i = 0;
id = 0;
selected = 0;
minFrags = 0;
minFragsSet = 0;
teambalancer_balancebots = get_pcvar_num(pcvar_teambalancer_balancebots);
teambalancer_botsfirst = get_pcvar_num(pcvar_teambalancer_botsfirst);
// Select player from largest team to transfer
while(_playerInfo[i][PiId])
{
if (!teambalancer_balancebots && _playerInfo[i][PiIsBot]) continue; // Skip bots
if (_playerInfo[i][PiTeamId] == largestTeamId)
{
#if defined _DEBUG
server_print("Testing player %i, flags: %i, %i, is bot: %i, selected: %i, minFragsSet: %i, minFrags: %i", _playerInfo[i][PiId], get_user_flags(_playerInfo[i][PiId]), get_user_flags(_playerInfo[i][PiId]) & flags, _playerInfo[i][PiIsBot], selected, minFragsSet, minFrags);
#endif
// If bots are first we will pick bot regardless of the score and who was last
if (teambalancer_botsfirst && _playerInfo[i][PiIsBot])
{
id = _playerInfo[i][PiId];
#if defined _DEBUG
server_print("Selected %i: because bots are first", id);
#endif
break;
}
if (selected) { i++; continue; } // We have found last player within the largest team so we will need to check others only if a bot can be found
// If last player is within largest team we will transfer him
if (_playerInfo[i][PiId] == idLast && get_user_flags(idLast) & flags == 0)
{
id = idLast;
selected = 1;
#if defined _DEBUG
server_print("Selected %i: cos was last", id);
#endif
if (!teambalancer_botsfirst) break; // If don't need to find a bot - break
i++;
continue;
}
// Select player from largest team with lowest frags
if ((!minFragsSet || _playerInfo[i][PiFrags] < minFrags) &&
(_playerInfo[i][PiIsBot] || get_user_flags(_playerInfo[i][PiId]) & flags == 0)) // Ignore access flags on bots
{
minFragsSet = 1;
minFrags = _playerInfo[i][PiFrags];
id = _playerInfo[i][PiId];
#if defined _DEBUG
server_print("Selected %i: with %i frags", id, minFrags);
#endif
}
}
i++;
}
if (id)
{
#if defined _DEBUG
server_print("Transferring %i to %s", id, _teamNames[smallestTeamId]);
#endif
set_user_info(id, "model", _teamNames[smallestTeamId]);
client_cmd(id,"model \"%s\"", _teamNames[smallestTeamId]);
}
}
}
RecountTeams()
{
static players[MAX_PLAYERS], num;
static i, j, id, teamId;
get_players(players, num);
for (j = 0; j < _teams; j++)
{
_counts[j] = 0;
_frags[j] = 0;
_deaths[j] = 0;
}
for (i = 0; i < num; i++)
{
id = players[i];
teamId = get_user_team(id) - 1;
_playerInfo[i][PiId] = id;
_playerInfo[i][PiIsBot] = is_user_bot(id);
_playerInfo[i][PiTeamId] = teamId;
_playerInfo[i][PiFrags] = get_user_frags(id);
_playerInfo[i][PiDeaths] = get_user_deaths(id);
#if defined _DEBUG
server_print("PlayerInfo %i: id: %i, team id: %i, frags: %i, death: %i", i, _playerInfo[i][PiId], _playerInfo[i][PiTeamId], _playerInfo[i][PiFrags], _playerInfo[i][PiDeaths]);
#endif
if (pev(id, pev_iuser1)) continue; // Spectator.
if (teamId < 0 || teamId >= _teams) continue; // Team not set yet.
_counts[teamId]++;
_frags[teamId] += get_user_frags(id);
_deaths[teamId] += get_user_deaths(id);
}
_playerInfo[i][PiId] = 0; // End of the list
#if defined _DEBUG
server_print("Stats:");
for (j = 0; j < _teams; j++)
server_print("Team %i: %s, counts: %i, frags: %i, deaths: %i", j, _teamNames[j], _counts[j], _frags[j], _deaths[j]);
#endif
}
GetTeamNames()
{
new j, teams[MAX_TEAMS * MAX_TEAMNAME + 1], team[MAX_TEAMNAME + 1];
get_cvar_string("mp_teamlist", teams, charsmax(teams));
for (j = 0; j < MAX_TEAMS; j++)
{
strtok(teams, team, charsmax(team), teams, charsmax(teams), ';');
if (team[0] == 0) break;
copy(_teamNames[j], MAX_TEAMNAME, team);
_teams++;
}
#if defined _DEBUG
server_print("Teams count: %i", _teams);
for (j = 0; j < _teams; j++)
server_print("Team %i name: %s", j, _teamNames[j]);
#endif
}
/// Converts string of flags into int.
/// Only chars from 'a' to 'z' are converted, others are skipped.
stock read_flags_fixed(const flags[])
{
new result, i = 0;
while (flags[i] != 0)
{
if (flags[i] >= 'a' && flags[i] <= 'z')
result |= 1 << (flags[i] - 'a');
i++;
}
return result;
}