52 this.
cnt = team_num - 1;
69 LOG_TRACEF(
"No \"%s\" entities found on this map, creating them anyway.", ent_classname);
106 return team_ent.m_team_score;
111 team_ent.m_team_score = score;
116 return team_ent.m_num_players_alive;
121 team_ent.m_num_players_alive =
number;
136 return (winner ? winner : -1);
164 return (winner ? winner : -1);
169 return team_ent.m_num_owned_items;
174 team_ent.m_num_owned_items =
number;
195 this.
team = (clr & 15) + 1;
201 builtin_setcolor(
this, clr);
227 float pants = _color & 0x0F;
228 float shirt = _color & 0xF0;
231 setcolor(player, 16 * pants + pants);
242 if (player.team == new_team)
269 int numplayersqueued = 0;
272 && (checkspecificteam ? it.wants_join > 0 : it.wants_join),
274 LOG_DEBUGF(
"Player %s is waiting to join team %d", it.netname, it.wants_join);
276 if (numplayersqueued >= AVAILABLE_TEAMS - 1)
280 LOG_DEBUG(
"No players waiting to join.");
293 if (team_index != old_team_index)
301 if (team_index != -1)
304 player.team_selected = team_index;
311 if (team_index == -1)
317 CS(player).idlekick_lasttimeleft = 0;
349 return player.team_forced;
359 player.team_forced = team_index;
370 player.team_forced = team_index;
395 player.team_forced = 1;
399 player.team_forced = 2;
403 player.team_forced = 3;
407 player.team_forced = 4;
415 player.team_forced = 1;
420 player.team_forced = 2;
425 player.team_forced = 3;
430 player.team_forced = 4;
459 if (player.bot_forced_team)
466 int forced_team_index = player.team_forced;
470 if (!is_team_allowed)
493 entity team_ent = balance.m_team_balance_team[i] =
spawn();
498 balance.nextthink =
time;
586 if (for_whom.team_forced == i &&
605 delete(balance.(m_team_balance_team[i]));
614 LOG_FATAL(
"Team balance entity is NULL.");
618 LOG_FATAL(
"Team balance entity is not initialized.");
659 int prev_size = 0, i = 1;
664 int team_size = would_leave ? team_ent.m_num_players_net : team_ent.m_num_players;
712 if(i == 1 || cur <
min)
719 if(cur > 0 && cur >
min)
728 latest_join =
CS(it).startplaytime;
768 int j, teamplayers_deficit = 0, teamplayers_max = 0;
776 if (it.m_num_players_net > teamplayers_max)
777 teamplayers_max = it.m_num_players_net;
784 for (j = 1; teamplayers_deficit > 0 && j <=
maxclients; ++j)
787 if (it.wants_join <= 0 || it == ignore)
continue;
791 --teamplayers_deficit;
795 for (j = 1; teamplayers_deficit > 0 && j <=
maxclients; ++j)
798 if (it.wants_join >= 0 || it == ignore)
continue;
800 --teamplayers_deficit;
804 return teamplayers_deficit <= 0;
811 LOG_FATAL(
"Team balance entity is NULL.");
815 LOG_FATAL(
"Team balance entity is not initialized.");
830 LOG_FATAL(
"Team balance entity is NULL.");
832 LOG_FATAL(
"Team balance entity is not initialized.");
843 team_ent.m_num_players =
M_ARGV(2,
float);
844 team_ent.m_num_bots =
M_ARGV(3,
float);
850 float skill_weight = 0, skill_weight_sum = 0;
855 if (it.m_skill_mu && it.m_skill_var)
857 skill_weight = 1 / it.m_skill_var;
859 skill_weight_sum += skill_weight;
865 int team_num = it.killindicator_teamchange > 0 ? it.killindicator_teamchange : it.team;
880 team_ent.m_num_players_net = ++team_ent.m_num_players;
882 ++team_ent.m_num_bots;
884 if (it.m_skill_mu && it.m_skill_var)
887 team_ent.m_skill_mu += it.m_skill_mu * skill_weight;
888 team_ent.mass += skill_weight;
899 team_ent.m_num_players_net -= to_remove;
900 bots_todo -= to_remove;
908 float server_skill_average_var = 0; skill_weight = 0;
912 int unranked_clients = team_ent.m_num_players_net - team_ent.count;
913 if (unranked_clients)
915 if (!server_skill_average_var)
918 skill_weight = 1 / server_skill_average_var;
921 team_ent.mass += skill_weight * unranked_clients;
928 team_ent.m_skill_mu /= team_ent.mass;
929 team_ent.m_skill_var = 1 / team_ent.mass;
939 if (team_ent.m_num_players == team_ent.m_num_bots)
940 ++team_ent.m_num_players;
949 LOG_FATAL(
"Team balance entity is NULL.");
953 LOG_FATAL(
"TeamBalance_GetTeamCounts has not been called.");
957 LOG_FATALF(
"Team index is invalid: %f", index);
959 return balance.m_team_balance_team[index - 1].m_num_players;
966 LOG_FATAL(
"Team balance entity is NULL.");
970 LOG_FATAL(
"Team balance entity is not initialized.");
1004 if (balance ==
NULL)
1005 LOG_FATAL(
"Team balance entity is NULL.");
1007 LOG_FATAL(
"TeamBalance_GetTeamCounts has not been called.");
1013 int previous_team = 0;
1014 float player_skill_mu = 0, player_skill_var = 0;
1019 if (previous_team == 0)
1036 if (!player_skill_mu)
1043 float team_i_z = (team_i.m_skill_mu - player_skill_mu) /
sqrt(team_i.m_skill_var + player_skill_var);
1044 float team_p_z = (team_p.m_skill_mu - player_skill_mu) /
sqrt(team_p.m_skill_var + player_skill_var);
1047 team_i_z =
fabs(team_i_z);
1048 team_p_z =
fabs(team_p_z);
1049 if ((!significant && team_i.m_num_players_net < team_p.m_num_players_net)
1050 || ((significant || team_i.m_num_players_net == team_p.m_num_players_net) && team_i_z > team_p_z))
1055 else if (team_i_z == team_p_z)
1088 int smallest_team_index = 0;
1089 int smallest_team_player_count = 0;
1098 if (smallest_team_index == 0)
1100 smallest_team_index = i;
1101 smallest_team_player_count = playercount;
1103 else if (playercount < smallest_team_player_count)
1105 smallest_team_index = i;
1106 smallest_team_player_count = playercount;
1117 if (smallest_team_index == largest_team_index)
1123 largest_team_index);
1126 if (largest_team_player_count - smallest_team_player_count < 2)
1134 smallest_team_index,
true);
1135 if (switchable_bot !=
NULL)
1142 if (switchable_bot ==
NULL)
1152 int largest_team_index = 0;
1153 int largest_team_player_count = 0;
1166 if (largest_team_index == 0)
1168 largest_team_index = i;
1169 largest_team_player_count = playercount;
1171 else if (playercount > largest_team_player_count)
1173 largest_team_index = i;
1174 largest_team_player_count = playercount;
1177 return largest_team_index;
1181 int destination_team_index,
bool is_bot)
1184 destination_team_index, is_bot))
1197 if (temp_score >= lowest_score)
1208 lowest_score = temp_score;
1216 return lowest_player;
1229 GameLogEcho(sprintf(
":team:%d:%d:%d", player_id, team_number, type));
1242 Damage(player, player, player, 100000, DEATH_TEAMCHANGE.m_id,
DMG_NOWEP,
1243 player.origin,
'0 0 0');
1249 && balance.m_team_balance_team[index - 1].m_num_players !=
TEAM_NOT_ALLOWED;
1269 return balance.m_team_balance_team[index - 1];
1284 return team_ent.m_num_players;
1289 return team_ent.m_num_bots;
1294 if (team_a == team_b)
1297 if (team_a.m_num_players_net < team_b.m_num_players_net)
1299 if (team_a.m_num_players_net > team_b.m_num_players_net)
1309 float team_a_strength = use_skill ? team_a.m_skill_mu : 1;
1310 float team_b_strength = use_skill ? team_b.m_skill_mu : 1;
1317 team_a_strength *= (1 + ((team_a.m_team_score ? team_a.m_team_score : 0.96875) / (team_b.m_team_score ? team_b.m_team_score : 0.96875) - 1)
1320 if (team_a_strength < team_b_strength)
1322 if (team_a_strength > team_b_strength)
1338 int human_clients = 0;
1341 if (++human_clients > 1)
1345 if (team_index <= 0)
int bots_would_leave
how many bots would leave so humans can replace them
#define MUTATOR_CALLHOOK(id,...)
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
int killindicator_teamchange
#define autocvar_timelimit
string playername(string thename, int teamid, bool team_colorize)
const int FRAGS_SPECTATOR
int autocvar_bot_vs_human
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
void GameLogEcho(string s)
bool autocvar_sv_eventlog
bool intermission_running
s1 s2 s1 s2 FLAG s1 s2 FLAG spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 CPID_REMOVE
s1 s2 s1 s2 FLAG s1 s2 FLAG spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 CPID_IDLING
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
void Kill_Notification(NOTIF broadcast, entity client, MSG net_type, CPID net_cpid)
#define APP_TEAM_NUM(num, prefix)
#define new_pure(class)
purely logical entities (not linked to the area grid)
ERASEABLE void RandomSelection_Init()
float RandomSelection_chosen_float
#define RandomSelection_AddFloat(f, weight, priority)
bool PlayerScore_Clear(entity player)
Initialize the score of this player if needed.
#define AVAILABLE_TEAMS
Number of teams that exist currently.
float autocvar_g_campaign_forceteam
void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
putting a client as observer in the server
bool PlayerInList(entity player, string list)
void Join(entity this, bool queued_join)
it's assumed this isn't called for bots (campaign_bots_may_start, centreprints)
bool autocvar_sv_teamnagger
float autocvar_sv_maxidle_playertospectator
#define PlayerScore_Get(player, scorefield)
Returns the player's score.
ClientState CS(Client this)
int Team_GetWinnerTeam_WithOwnedItems(int min_control_points)
Returns the winner team.
bool QueuedPlayersReady(entity this, bool checkspecificteam)
Returns true when enough players are queued that the next will join directly to the only available te...
bool TeamBalanceTeam_IsAllowed(entity team_ent)
Returns whether the team is allowed.
string autocvar_g_forced_team_red
void _SpawnTeam(string teament_classname, int team_num)
int Entity_GetTeamIndex(entity this)
Returns the team index of the given entity.
bool TeamBalance_QueuedPlayersTagIn(entity ignore)
Joins queued player(s) to team(s) with a shortage, this should be more robust than only replacing the...
int Team_GetNumberOfAlivePlayers(entity team_ent)
Returns the number of alive players in a team.
entity Team_GetTeam(int team_num)
Returns the global team entity that corresponds to the given TEAM_NUM value.
void TeamBalance_AutoBalanceBots()
Switches a bot from one team to another if teams are not balanced.
void TeamBalance_Destroy(entity balance)
Destroy the team balance entity.
void TeamBalance_BanTeamsExcept(entity balance, int index)
Bans team change to all teams except the given one.
void TeamBalance_RemoveExcessPlayers(entity ignore)
bool QueueNeeded(entity client, int team_index)
entity Entity_GetTeam(entity this)
Returns the team entity of the given entity.
const int TEAM_NOT_ALLOWED
Indicates that the player is not allowed to join a team.
int m_num_players_net
.m_num_players but excluding bots that would leave if a human joined their team.
void Player_DetermineForcedTeam(entity player)
Determines the forced team of the player using current global config.
void TeamBalance_JoinBestTeam(entity player)
Assigns the given player to a team that will make the game most balanced.
bool Player_HasRealForcedTeam(entity player)
Returns whether player has real forced team.
void LogTeamChange(float player_id, float team_number, int type)
int TeamBalance_CompareTeamsInternal(entity team_a, entity team_b, bool use_score)
Compares two teams for the purposes of game balance.
int TeamBalanceTeam_GetNumberOfPlayers(entity team_ent)
Returns the number of players (both humans and bots) in a team.
void Team_SetTeamScore(entity team_ent, float score)
Sets the score of the team.
int m_num_owned_items
Number of items owned by a team.
int Team_MapEnts_FindOrSpawn(string ent_classname, int defaultmask)
Finds any team map entities and returns their bitmask, else spawns them.
int TeamBalance_GetNumberOfPlayers(entity balance, int index)
Returns the number of players (both humans and bots) in a team.
bool SetPlayerTeam(entity player, int team_index, int type)
Sets the team of the player.
entity g_team_entities[NUM_TEAMS]
Holds global team entities.
int TeamBalance_GetLargestTeamIndex(entity balance, int teams)
Returns the index of the team with most players that is contained in the given bitmask of teams.
bool Player_SetTeamIndex(entity player, int index)
Sets the team of the player using its index.
@ TEAM_BALANCE_TEAM_COUNTS_FILLED
TeamBalance_GetTeamCounts has been called.
@ TEAM_BALANCE_TEAMS_CHECKED
TeamBalance_CheckAllowedTeams has been called.
@ TEAM_BALANCE_UNINITIALIZED
The team balance has not been initialized.
int TeamBalanceTeam_GetNumberOfBots(entity team_ent)
Returns the number of bots in a team.
int Team_GetNumberOfTeamsWithOwnedItems()
Returns the number of teams that own items.
void Remove_Countdown(entity this)
int m_num_bots
Number of bots in a team.
int Team_GetWinnerAliveTeam()
Returns the winner team.
void Team_SetNumberOfOwnedItems(entity team_ent, int number)
Sets the number of items owned by a team.
void SetPlayerColors(entity player, float _color)
int m_team_balance_state
Holds the state of the team balance data entity.
void TeamBalance_GetTeamCounts(entity balance, entity ignore)
Counts the number of players and various other information about each team.
string autocvar_g_forced_team_blue
int Team_GetNumberOfOwnedItems(entity team_ent)
Returns the number of items owned by a team.
int Player_GetForcedTeamIndex(entity player)
Returns the index of the forced team of the given player.
bool TeamBalance_IsTeamAllowedInternal(entity balance, int index)
Returns whether the team change to the specified team is allowed.
string autocvar_g_forced_team_yellow
string autocvar_g_forced_team_pink
int m_num_players
Number of players (both humans and bots) in a team.
float Team_GetTeamScore(entity team_ent)
Returns the score of the team.
bool Entity_HasValidTeam(entity this)
Returns whether the given entity belongs to a valid team.
int TeamBalance_GetAllowedTeams(entity balance)
Returns the bitmask of allowed teams.
int m_num_players_alive
Number of alive players in a team.
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings.
entity m_team_balance_team[NUM_TEAMS]
???
bool TeamBalance_IsTeamAllowed(entity balance, int index)
Returns whether the team change to the specified team is allowed.
int TeamBalance_FindBestTeams(entity balance, entity player, bool use_score)
Returns the bitmask of the teams that will make the game most balanced if the player joins any of the...
bool TeamBalance_AreEqual(entity ignore, bool would_leave)
int Team_GetNumberOfAliveTeams()
Returns the number of alive teams.
void KillPlayerForTeamChange(entity player)
Kills player as a result of team change.
void Team_SetNumberOfAlivePlayers(entity team_ent, int number)
Sets the number of alive players in a team.
entity Team_GetTeamFromIndex(int index)
Returns the global team entity at the given index.
int TeamBalance_SizeDifference(entity ignore)
Returns the size difference between the largest and smallest team (bots included).
float m_team_score
The score of the team.
entity TeamBalance_GetPlayerForTeamSwitch(int source_team_index, int destination_team_index, bool is_bot)
Returns the player who is the most suitable for switching between the given teams.
void Player_SetForcedTeamIndex(entity player, int team_index)
Sets the index of the forced team of the given player.
int TeamBalance_FindBestTeam(entity balance, entity player, bool ignore_player)
Finds the team that will make the game most balanced if the player joins it.
entity TeamBalance_GetTeam(entity balance, int team_num)
Returns the team entity of the team balance entity that corresponds to the given TEAM_NUM value.
entity TeamBalance_GetTeamFromIndex(entity balance, int index)
Returns the team entity of the team balance entity at the given index.
bool MoveToTeam(entity client, int team_index, int type)
Moves player to the specified team.
int autocvar_g_balance_teams_remove_wait
int autocvar_g_balance_teams_skill
string autocvar_g_forced_team_otherwise
bool autocvar_g_balance_teams_queue
@ TEAM_FORCE_DEFAULT
Don't force any team.
@ TEAM_FORCE_SPECTATOR
Force the player to spectator team.
float autocvar_g_balance_teams_skill_unranked_factor
int teamplay_bitmask
The set of currently available teams (AVAILABLE_TEAMS is the number of them).
float server_skill_average
Scaled inverse-variance weighted mean of all clients' m_skill_mu, for unranked clients.
@ TEAM_CHANGE_AUTO
The team was selected by autobalance.
float autocvar_g_balance_teams_skill_significance_threshold
@ TEAMS_COMPARE_GREATER
First team the greater than the second one.
@ TEAMS_COMPARE_LESS
First team is less than the second one.
@ TEAMS_COMPARE_EQUAL
Both teams are equal.
string Static_Team_ColorName(int teamid)
bool Team_IsValidTeam(int team_num)
Returns whether team value is valid.
int Team_TeamToBit(int team_num)
Converts team value into bit value that is used in team bitmasks.
bool Team_IsValidIndex(int index)
Returns whether the team index is valid.
int Team_TeamToIndex(int team_num)
Converts team value into team index.
int Team_IndexToTeam(int index)
Converts team index into team value.
const int NUM_TEAMS
Max number of teams that could exist, prefer AVAILABLE_TEAMS.
int Team_IndexToBit(int index)
Converts team index into bit value that is used in team bitmasks.
#define IS_REAL_CLIENT(v)
#define FOREACH_CLIENT(cond, body)
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))