Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
teamplay.qc File Reference
Include dependency graph for teamplay.qc:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Enumerations

enum  { TEAM_BALANCE_UNINITIALIZED , TEAM_BALANCE_TEAMS_CHECKED , TEAM_BALANCE_TEAM_COUNTS_FILLED }
 Describes a state of team balance data entity. More...

Functions

void _SpawnTeam (string teament_classname, int team_num)
entity Entity_GetTeam (entity this)
 Returns the team entity of the given entity.
int Entity_GetTeamIndex (entity this)
 Returns the team index of the given entity.
bool Entity_HasValidTeam (entity this)
 Returns whether the given entity belongs to a valid team.
void LogTeamChange (float player_id, float team_number, int type)
bool MoveToTeam (entity client, int team_index, int type)
 Moves player to the specified team.
int Player_GetForcedTeamIndex (entity player)
 Returns the index of the forced team of the given player.
bool Player_HasRealForcedTeam (entity player)
 Returns whether player has real forced team.
void Player_SetForcedTeamIndex (entity player, int team_index)
 Sets the index of the forced team of the given player.
bool Player_SetTeamIndex (entity player, int index)
 Sets the team of the player using its index.
bool QueuedPlayersReady (entity this, bool checkspecificteam)
 Returns true when enough players are queued that the next will join directly to the only available team (also triggering the joins of the queued players).
bool QueueNeeded (entity client, int team_index)
void Remove_Countdown (entity this)
void setcolor (entity this, int clr)
void SetPlayerColors (entity player, float _color)
bool SetPlayerTeam (entity player, int team_index, int type)
 Sets the team of the player.
void SV_ChangeTeam (entity player, int new_color)
 Called when the player changes color with the "color" command.
int Team_GetNumberOfAlivePlayers (entity team_ent)
 Returns the number of alive players in a team.
int Team_GetNumberOfAliveTeams ()
 Returns the number of alive teams.
int Team_GetNumberOfOwnedItems (entity team_ent)
 Returns the number of items owned by a team.
int Team_GetNumberOfTeamsWithOwnedItems ()
 Returns the number of teams that own items.
entity Team_GetTeam (int team_num)
 Returns the global team entity that corresponds to the given TEAM_NUM value.
entity Team_GetTeamFromIndex (int index)
 Returns the global team entity at the given index.
float Team_GetTeamScore (entity team_ent)
 Returns the score of the team.
int Team_GetWinnerAliveTeam ()
 Returns the winner team.
int Team_GetWinnerTeam_WithOwnedItems (int min_control_points)
 Returns the winner team.
void Team_InitTeams ()
int Team_MapEnts_FindOrSpawn (string ent_classname, int defaultmask)
 Finds any team map entities and returns their bitmask, else spawns them.
void Team_SetNumberOfAlivePlayers (entity team_ent, int number)
 Sets the number of alive players in a team.
void Team_SetNumberOfOwnedItems (entity team_ent, int number)
 Sets the number of items owned by a team.
void Team_SetTeamScore (entity team_ent, float score)
 Sets the score of the team.
bool TeamBalance_AreEqual (entity ignore, bool would_leave)
void TeamBalance_AutoBalanceBots ()
 Switches a bot from one team to another if teams are not balanced.
void TeamBalance_BanTeamsExcept (entity balance, int index)
 Bans team change to all teams except the given one.
entity TeamBalance_CheckAllowedTeams (entity for_whom)
 Checks whether the player can join teams according to global configuration and mutator settings.
int TeamBalance_CompareTeamsInternal (entity team_a, entity team_b, entity client, bool use_score)
 Compares two teams for the purposes of game balance.
void TeamBalance_Destroy (entity balance)
 Destroy the team balance entity.
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.
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 them.
int TeamBalance_GetAllowedTeams (entity balance)
 Returns the bitmask of allowed teams.
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.
int TeamBalance_GetNumberOfPlayers (entity balance, int index)
 Returns the number of players (both humans and bots) in a 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.
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.
void TeamBalance_GetTeamCounts (entity balance, entity ignore)
 Counts the number of players and various other information about each team.
entity TeamBalance_GetTeamFromIndex (entity balance, int index)
 Returns the team entity of the team balance entity at the given index.
bool TeamBalance_IsTeamAllowed (entity balance, int index)
 Returns whether the team change to the specified team is allowed.
bool TeamBalance_IsTeamAllowedInternal (entity balance, int index)
 Returns whether the team change to the specified team is allowed.
void TeamBalance_JoinBestTeam (entity player)
 Assigns the given player to a team that will make the game most balanced.
bool TeamBalance_QueuedPlayersTagIn (entity ignore)
 Joins queued player(s) to team(s) with a shortage, this should be more robust than only replacing the player that left.
void TeamBalance_RemoveExcessPlayers (entity ignore)
int TeamBalance_SizeDifference (entity ignore)
 Returns the size difference between the largest and smallest team (bots included).
int TeamBalanceTeam_GetNumberOfBots (entity team_ent)
 Returns the number of bots in a team.
int TeamBalanceTeam_GetNumberOfPlayers (entity team_ent)
 Returns the number of players (both humans and bots) in a team.
bool TeamBalanceTeam_IsAllowed (entity team_ent)
 Returns whether the team is allowed.

Variables

string autocvar_g_forced_team_blue
string autocvar_g_forced_team_pink
string autocvar_g_forced_team_red
string autocvar_g_forced_team_yellow
entity g_team_entities [NUM_TEAMS]
 Holds global team entities.
int killindicator_teamchange
int m_num_bots
 Number of bots in a team.
int m_num_owned_items
 Number of items owned by a team.
int m_num_players
 Number of players (both humans and bots) in a team.
int m_num_players_alive
 Number of alive players in a team.
int m_num_players_net
 .m_num_players but excluding bots that would leave if a human joined their team.
int m_team_balance_state
 Holds the state of the team balance data entity.
entity m_team_balance_team [NUM_TEAMS]
 ???
float m_team_score
 The score of the team.
entity remove_countdown
const int TEAM_NOT_ALLOWED = -1
 Indicates that the player is not allowed to join a team.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum

Describes a state of team balance data entity.

Enumerator
TEAM_BALANCE_UNINITIALIZED 

The team balance has not been initialized.

TEAM_BALANCE_TEAMS_CHECKED 

TeamBalance_CheckAllowedTeams has been called.

TEAM_BALANCE_TEAM_COUNTS_FILLED 

TeamBalance_GetTeamCounts has been called.

Definition at line 19 of file teamplay.qc.

20{
26};
@ TEAM_BALANCE_TEAM_COUNTS_FILLED
TeamBalance_GetTeamCounts has been called.
Definition teamplay.qc:25
@ TEAM_BALANCE_TEAMS_CHECKED
TeamBalance_CheckAllowedTeams has been called.
Definition teamplay.qc:23
@ TEAM_BALANCE_UNINITIALIZED
The team balance has not been initialized.
Definition teamplay.qc:21

Function Documentation

◆ _SpawnTeam()

void _SpawnTeam ( string teament_classname,
int team_num )

Definition at line 48 of file teamplay.qc.

49{
50 entity this = new_pure(teament_classname);
51 this.netname = Static_Team_ColorName(team_num);
52 this.cnt = team_num - 1;
53 this.spawnfunc_checked = true;
54 this.team = team_num;
55}
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
string netname
Definition powerups.qc:20
float cnt
Definition powerups.qc:24
int team
Definition main.qh:188
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:66
bool spawnfunc_checked
Definition spawnfunc.qh:8
string Static_Team_ColorName(int teamid)
Definition teams.qh:103

References cnt, entity(), netname, new_pure, spawnfunc_checked, Static_Team_ColorName(), and team.

Referenced by Team_MapEnts_FindOrSpawn().

◆ Entity_GetTeam()

entity Entity_GetTeam ( entity this)

Returns the team entity of the given entity.

Parameters
[in]thisEntity to check.
Returns
Team entity of the given entity or NULL if the entity doesn't belong to any team.

Definition at line 215 of file teamplay.qc.

216{
217 int index = Entity_GetTeamIndex(this);
218 if (!Team_IsValidIndex(index))
219 {
220 return NULL;
221 }
222 return Team_GetTeamFromIndex(index);
223}
#define NULL
Definition post.qh:14
int Entity_GetTeamIndex(entity this)
Returns the team index of the given entity.
Definition teamplay.qc:210
entity Team_GetTeamFromIndex(int index)
Returns the global team entity at the given index.
Definition teamplay.qc:86
bool Team_IsValidIndex(int index)
Returns whether the team index is valid.
Definition teams.qh:151

References entity(), Entity_GetTeamIndex(), NULL, Team_GetTeamFromIndex(), and Team_IsValidIndex().

Referenced by CA_count_alive_players(), Domination_count_controlpoints(), freezetag_count_alive_players(), MUTATOR_HOOKFUNCTION(), MUTATOR_HOOKFUNCTION(), and Onslaught_count_generators().

◆ Entity_GetTeamIndex()

int Entity_GetTeamIndex ( entity this)

Returns the team index of the given entity.

Parameters
[in]thisEntity to check.
Returns
Team index of the entity.

Definition at line 210 of file teamplay.qc.

211{
212 return Team_TeamToIndex(this.team);
213}
int Team_TeamToIndex(int team_num)
Converts team value into team index.
Definition teams.qh:184

References entity(), team, and Team_TeamToIndex().

Referenced by Entity_GetTeam(), SetPlayerTeam(), shuffleteams(), and TeamBalance_GetPlayerForTeamSwitch().

◆ Entity_HasValidTeam()

bool Entity_HasValidTeam ( entity this)

Returns whether the given entity belongs to a valid team.

Parameters
[in]thisEntity to check.
Returns
True if entity belongs to a valid team, false otherwise.

Definition at line 205 of file teamplay.qc.

206{
207 return Team_IsValidTeam(this.team);
208}
bool Team_IsValidTeam(int team_num)
Returns whether team value is valid.
Definition teams.qh:133

References entity(), team, and Team_IsValidTeam().

Referenced by CA_count_alive_players(), CA_PreventStalemate(), Domination_count_controlpoints(), and freezetag_count_alive_players().

◆ LogTeamChange()

void LogTeamChange ( float player_id,
float team_number,
int type )

Definition at line 1148 of file teamplay.qc.

1149{
1151 {
1152 return;
1153 }
1154 if (player_id < 1)
1155 {
1156 return;
1157 }
1158 GameLogEcho(sprintf(":team:%d:%d:%d", player_id, team_number, type));
1159}
void GameLogEcho(string s)
Definition gamelog.qc:15
bool autocvar_sv_eventlog
Definition gamelog.qh:3

References autocvar_sv_eventlog, and GameLogEcho().

Referenced by SetPlayerTeam().

◆ MoveToTeam()

bool MoveToTeam ( entity client,
int team_index,
int type )

Moves player to the specified team.

Parameters
[in,out]clientClient to move.
[in]team_indexIndex of the team.
[in]type???
Returns
True on success, false otherwise.

Definition at line 331 of file teamplay.qc.

332{
333 //PrintToChatAll(sprintf("MoveToTeam: %s, %f", client.netname, team_index));
334 int lockteams_backup = lockteams; // backup any team lock
335 lockteams = 0; // disable locked teams
336 return = SetPlayerTeam(client, team_index, type);
337 lockteams = lockteams_backup; // restore the team lock
338}
bool SetPlayerTeam(entity player, int team_index, int type)
Sets the team of the player.
Definition teamplay.qc:286
bool lockteams
Definition teamplay.qh:20

References entity(), lockteams, and SetPlayerTeam().

Referenced by GameCommand_moveplayer(), MatchEnd_RestoreSpectatorAndTeamStatus(), and shuffleteams().

◆ Player_GetForcedTeamIndex()

int Player_GetForcedTeamIndex ( entity player)

Returns the index of the forced team of the given player.

Parameters
[in]playerPlayer to check.
Returns
Index of the forced team.

Definition at line 370 of file teamplay.qc.

371{
372 // NB: this is intended to work in !teamplay too
373
374 if (player.team_forced != TEAM_FORCE_DEFAULT)
375 return player.team_forced; // internally forced spec set by minigames etc wins
376
378 {
379 if (IS_REAL_CLIENT(player)) // only players, not bots
380 {
383 else
384 return TEAM_FORCE_DEFAULT;
385 }
386 }
388 return 1;
390 return 2;
392 return 3;
394 return 4;
395 else
396 {
398 {
399 case "red":
400 return 1;
401 case "blue":
402 return 2;
403 case "yellow":
404 return 3;
405 case "pink":
406 return 4;
407 case "spectate":
408 case "spectator":
410 }
411 }
412 return TEAM_FORCE_DEFAULT;
413}
bool autocvar_g_campaign
Definition menu.qc:752
float autocvar_g_campaign_forceteam
Definition campaign.qh:7
bool PlayerInList(entity player, string list)
Definition client.qc:1049
string autocvar_g_forced_team_red
Definition teamplay.qc:41
string autocvar_g_forced_team_blue
Definition teamplay.qc:42
string autocvar_g_forced_team_yellow
Definition teamplay.qc:43
string autocvar_g_forced_team_pink
Definition teamplay.qc:44
string autocvar_g_forced_team_otherwise
Definition teamplay.qh:16
@ TEAM_FORCE_DEFAULT
Don't force any team.
Definition teamplay.qh:153
@ TEAM_FORCE_SPECTATOR
Force the player to spectator team.
Definition teamplay.qh:152
#define IS_REAL_CLIENT(v)
Definition utils.qh:17

References autocvar_g_campaign, autocvar_g_campaign_forceteam, autocvar_g_forced_team_blue, autocvar_g_forced_team_otherwise, autocvar_g_forced_team_pink, autocvar_g_forced_team_red, autocvar_g_forced_team_yellow, entity(), IS_REAL_CLIENT, PlayerInList(), TEAM_FORCE_DEFAULT, TEAM_FORCE_SPECTATOR, and Team_IsValidIndex().

Referenced by ClientCommand_clientversion(), ClientCommand_join(), joinAllowed(), Player_HasRealForcedTeam(), TeamBalance_CheckAllowedTeams(), and TeamBalance_GetTeamCounts().

◆ Player_HasRealForcedTeam()

bool Player_HasRealForcedTeam ( entity player)

Returns whether player has real forced team.

Spectator team is ignored.

Parameters
[in]playerPlayer to check.
Returns
True if player has real forced team, false otherwise.

Definition at line 340 of file teamplay.qc.

341{
343}
int Player_GetForcedTeamIndex(entity player)
Returns the index of the forced team of the given player.
Definition teamplay.qc:370

References entity(), Player_GetForcedTeamIndex(), and TEAM_FORCE_DEFAULT.

Referenced by ShowTeamSelection(), and shuffleteams().

◆ Player_SetForcedTeamIndex()

void Player_SetForcedTeamIndex ( entity player,
int team_index )

Sets the index of the forced team of the given player.

Parameters
[in,out]playerPlayer to adjust.
[in]team_indexIndex of the team to set.

Definition at line 345 of file teamplay.qc.

346{
347 switch (team_index)
348 {
351 {
352 player.team_forced = team_index;
353 break;
354 }
355 default:
356 {
357 if (!Team_IsValidIndex(team_index))
358 {
359 LOG_FATAL("Invalid team index.");
360 }
361 else
362 {
363 player.team_forced = team_index;
364 break;
365 }
366 }
367 }
368}
#define LOG_FATAL(...)
Definition log.qh:50

References entity(), LOG_FATAL, TEAM_FORCE_DEFAULT, TEAM_FORCE_SPECTATOR, and Team_IsValidIndex().

Referenced by MatchEnd_RestoreSpectatorAndTeamStatus(), minigame_addplayer(), and player_clear_minigame().

◆ Player_SetTeamIndex()

bool Player_SetTeamIndex ( entity player,
int index )

Sets the team of the player using its index.

Parameters
[in,out]playerPlayer to adjust.
[in]indexIndex of the team to set.
Returns
True if team switch was successful, false otherwise.

Definition at line 239 of file teamplay.qc.

240{
241 int new_team = Team_IndexToTeam(index);
242 if (player.team == new_team)
243 return true;
244
245 int old_index = Team_TeamToIndex(player.team);
246 if (MUTATOR_CALLHOOK(Player_ChangeTeam, player, old_index, index) == true)
247 {
248 // Mutator has blocked team change.
249 return false;
250 }
251 if (new_team == -1)
252 {
253 if (teamplay) // sanity check, if false this code path should never be reached anyway
254 player.clientcolors = 0;
255 player.team = -1;
256 }
257 else
258 {
259 SetPlayerColors(player, new_team - 1);
260 }
261 MUTATOR_CALLHOOK(Player_ChangedTeam, player, old_index, index);
262 return true;
263}
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
void SetPlayerColors(entity player, float _color)
Definition teamplay.qc:225
int Team_IndexToTeam(int index)
Converts team index into team value.
Definition teams.qh:169
bool teamplay
Definition teams.qh:59

References entity(), MUTATOR_CALLHOOK, SetPlayerColors(), Team_IndexToTeam(), Team_TeamToIndex(), and teamplay.

Referenced by SetPlayerTeam().

◆ QueuedPlayersReady()

bool QueuedPlayersReady ( entity this,
bool checkspecificteam )

Returns true when enough players are queued that the next will join directly to the only available team (also triggering the joins of the queued players).

Optionally only counts players who selected a specific team when joining the queue.

Definition at line 269 of file teamplay.qc.

270{
271 int numplayersqueued = 0;
272
273 FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != this
274 && (checkspecificteam ? it.wants_join > 0 : it.wants_join),
275 {
276 LOG_DEBUGF("Player %s is waiting to join team %d", it.netname, it.wants_join);
277 ++numplayersqueued;
278 if (numplayersqueued >= AVAILABLE_TEAMS - 1)
279 return true;
280 });
281
282 LOG_DEBUG("No players waiting to join.");
283 return false;
284}
#define LOG_DEBUG(...)
Definition log.qh:78
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:52

References entity(), FOREACH_CLIENT, IS_REAL_CLIENT, and LOG_DEBUG.

Referenced by ShowTeamSelection().

◆ QueueNeeded()

bool QueueNeeded ( entity client,
int team_index )

Definition at line 1254 of file teamplay.qc.

1255{
1257 return false;
1258
1259 int human_clients = 0;
1261 {
1262 if (++human_clients > 1)
1263 {
1264 if (TeamBalance_AreEqual(client, true))
1265 return true;
1266 if (team_index <= 0)
1267 return false; // auto-select will join a smaller team
1268
1269 entity balance = TeamBalance_CheckAllowedTeams(client);
1270 TeamBalance_GetTeamCounts(balance, client);
1271 if ((Team_IndexToBit(team_index) & TeamBalance_FindBestTeams(balance, client, false)) == 0)
1272 {
1273 TeamBalance_Destroy(balance);
1274 return true;
1275 }
1276 TeamBalance_Destroy(balance);
1277 return false;
1278 }
1279 });
1280 return false;
1281}
bool warmup_stage
Definition main.qh:120
void TeamBalance_Destroy(entity balance)
Destroy the team balance entity.
Definition teamplay.qc:528
void TeamBalance_GetTeamCounts(entity balance, entity ignore)
Counts the number of players and various other information about each team.
Definition teamplay.qc:758
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings.
Definition teamplay.qc:424
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...
Definition teamplay.qc:928
bool TeamBalance_AreEqual(entity ignore, bool would_leave)
Definition teamplay.qc:585
bool autocvar_g_balance_teams_queue
Definition teamplay.qh:9
int Team_IndexToBit(int index)
Converts team index into bit value that is used in team bitmasks.
Definition teams.qh:211

References autocvar_g_balance_teams_queue, autocvar_g_campaign, entity(), FOREACH_CLIENT, IS_REAL_CLIENT, Team_IndexToBit(), TeamBalance_AreEqual(), TeamBalance_CheckAllowedTeams(), TeamBalance_Destroy(), TeamBalance_FindBestTeams(), TeamBalance_GetTeamCounts(), teamplay, and warmup_stage.

Referenced by joinAllowed().

◆ Remove_Countdown()

void Remove_Countdown ( entity this)

Definition at line 606 of file teamplay.qc.

607{
608 if(this.lifetime <= 0 || TeamBalance_AreEqual(NULL, false))
609 {
610 if(this.lifetime <= 0)
611 {
612 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_REMOVE, playername(remove_countdown.enemy.netname, remove_countdown.enemy.team, true));
613 PutObserverInServer(remove_countdown.enemy, true, true);
614 }
615
616 Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_REMOVE);
617
618 delete(this);
620
621 TeamBalance_RemoveExcessPlayers(NULL); // Check again for excess players in case someone also left while in countdown
622 return;
623 }
624
625 --this.lifetime;
626 this.nextthink = time + 1;
627}
float lifetime
Definition powerups.qc:23
string playername(string thename, int teamid, bool team_colorize)
Definition util.qc:2190
float time
float nextthink
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
Definition all.inc:698
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1500
void Kill_Notification(NOTIF broadcast, entity client, MSG net_type, CPID net_cpid)
Definition all.qc:1464
void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
putting a client as observer in the server
Definition client.qc:261
entity remove_countdown
Definition teamplay.qc:605
void TeamBalance_RemoveExcessPlayers(entity ignore)
Definition teamplay.qc:631

References CPID_REMOVE, entity(), Kill_Notification(), lifetime, nextthink, NULL, playername(), PutObserverInServer(), remove_countdown, Send_Notification(), TeamBalance_AreEqual(), TeamBalance_RemoveExcessPlayers(), and time.

Referenced by TeamBalance_RemoveExcessPlayers().

◆ setcolor()

void setcolor ( entity this,
int clr )

Definition at line 190 of file teamplay.qc.

191{
192#if 1
193 this.clientcolors = clr;
194 if(teamplay)
195 this.team = (clr & 15) + 1;
196 else
197 this.team = -1;
198#else
199 // sets clientcolors and team (even in FFA games)
200 // and sends notification to all clients
201 builtin_setcolor(this, clr);
202#endif
203}
float clientcolors

References clientcolors, entity(), team, and teamplay.

◆ SetPlayerColors()

void SetPlayerColors ( entity player,
float _color )

Definition at line 225 of file teamplay.qc.

226{
227 float pants = _color & 0x0F;
228 float shirt = _color & 0xF0;
229 if (teamplay)
230 {
231 setcolor(player, 16 * pants + pants);
232 }
233 else
234 {
235 setcolor(player, shirt + pants);
236 }
237}
#define setcolor
Definition pre.qh:11

References entity(), setcolor, and teamplay.

Referenced by Player_SetTeamIndex(), and SV_ChangeTeam().

◆ SetPlayerTeam()

bool SetPlayerTeam ( entity player,
int team_index,
int type )

Sets the team of the player.

Parameters
[in,out]playerPlayer to adjust.
[in]team_indexIndex of the team to set.
[in]typeType of the team change. See TEAM_CHANGE constants.
Returns
True if team switch was successful, false otherwise.

Definition at line 286 of file teamplay.qc.

287{
288 int old_team_index = Entity_GetTeamIndex(player);
289
290 if (!Player_SetTeamIndex(player, team_index))
291 return false;
292
293 LogTeamChange(player.playerid, player.team, type);
294
295 if (team_index != old_team_index)
296 {
297 if (!IS_DEAD(player))
298 Damage(player, player, player, INFINITY, DEATH_TEAMCHANGE.m_id, DMG_NOWEP, player.origin, '0 0 0');
299 PlayerScore_Clear(player);
300
301 if (!IS_BOT_CLIENT(player))
303
304 if (team_index != -1)
305 {
306 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(player.team, INFO_JOIN_PLAY_TEAM), player.netname);
307 player.team_selected = team_index; // no autoselect in Join()
308
309 if (warmup_stage)
310 ReadyCount(); // teams might be balanced now
311 }
312 }
313
314 if (team_index == -1)
315 {
316 if (autocvar_sv_maxidle_playertospectator > 0 && CS(player).idlekick_lasttimeleft)
317 {
318 // this done here so it happens even when manually speccing during the countdown
319 Kill_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CPID_IDLING);
320 CS(player).idlekick_lasttimeleft = 0;
321 }
322 else if (!CS(player).just_joined && player.frags != FRAGS_SPECTATOR)
323 {
324 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, player.netname);
325 }
326 }
327
328 return true;
329}
#define IS_DEAD(s)
Definition player.qh:244
const int FRAGS_SPECTATOR
engine expects this value
Definition constants.qh:5
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition damage.qc:484
#define DMG_NOWEP
Definition damage.qh:104
#define INFINITY
Definition mathlib.qh:105
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
Definition all.inc:696
#define APP_TEAM_NUM(num, prefix)
Definition all.qh:88
bool PlayerScore_Clear(entity player)
Initialize the score of this player if needed.
Definition scores.qc:286
bool just_joined
Definition client.qh:76
float autocvar_sv_maxidle_playertospectator
Definition client.qh:39
void ReadyCount()
Definition vote.qc:553
ClientState CS(Client this)
Definition state.qh:47
void TeamBalance_AutoBalanceBots()
Switches a bot from one team to another if teams are not balanced.
Definition teamplay.qc:1007
void LogTeamChange(float player_id, float team_number, int type)
Definition teamplay.qc:1148
bool Player_SetTeamIndex(entity player, int index)
Sets the team of the player using its index.
Definition teamplay.qc:239
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition utils.qh:15

References APP_TEAM_NUM, autocvar_sv_maxidle_playertospectator, CPID_IDLING, CS(), Damage(), DMG_NOWEP, entity(), Entity_GetTeamIndex(), FRAGS_SPECTATOR, INFINITY, IS_BOT_CLIENT, IS_DEAD, just_joined, Kill_Notification(), LogTeamChange(), NULL, Player_SetTeamIndex(), PlayerScore_Clear(), ReadyCount(), Send_Notification(), TeamBalance_AutoBalanceBots(), and warmup_stage.

Referenced by ClientKill_Now_TeamChange(), Join(), MoveToTeam(), PutObserverInServer(), PutPlayerInServer(), TeamBalance_AutoBalanceBots(), and TeamBalance_JoinBestTeam().

◆ SV_ChangeTeam()

void SV_ChangeTeam ( entity player,
int new_color )

Called when the player changes color with the "color" command.

Note
the "color" command is always called early on player connection
Parameters
[in,out]playerPlayer that requested a new color.
[in]new_colorRequested color.

Definition at line 1248 of file teamplay.qc.

1249{
1250 if (!teamplay)
1251 SetPlayerColors(player, new_color);
1252}

References entity(), SetPlayerColors(), and teamplay.

◆ Team_GetNumberOfAlivePlayers()

int Team_GetNumberOfAlivePlayers ( entity team_ent)

Returns the number of alive players in a team.

Parameters
[in]team_entTeam entity.
Returns
Number of alive players in a team.

Definition at line 114 of file teamplay.qc.

115{
116 return team_ent.m_num_players_alive;
117}

References entity().

Referenced by CA_CheckTeams(), CA_count_alive_players(), CA_PreventStalemate(), freezetag_CheckTeams(), freezetag_count_alive_players(), MUTATOR_HOOKFUNCTION(), and MUTATOR_HOOKFUNCTION().

◆ Team_GetNumberOfAliveTeams()

int Team_GetNumberOfAliveTeams ( )

Returns the number of alive teams.

Returns
Number of alive teams.

Definition at line 139 of file teamplay.qc.

140{
141 int result = 0;
142 for (int i = 0; i < AVAILABLE_TEAMS; ++i)
143 {
145 {
146 ++result;
147 }
148 }
149 return result;
150}
entity result
Definition promise.qc:45
#define AVAILABLE_TEAMS
Number of teams that exist currently.
entity g_team_entities[NUM_TEAMS]
Holds global team entities.
Definition teamplay.qc:46
int m_num_players_alive
Number of alive players in a team.
Definition teamplay.qc:38

References AVAILABLE_TEAMS, g_team_entities, m_num_players_alive, and result.

Referenced by CA_CheckTeams(), and freezetag_CheckTeams().

◆ Team_GetNumberOfOwnedItems()

int Team_GetNumberOfOwnedItems ( entity team_ent)

Returns the number of items owned by a team.

Parameters
[in]team_entTeam entity.
Returns
Number of items owned by a team.

Definition at line 167 of file teamplay.qc.

168{
169 return team_ent.m_num_owned_items;
170}

References entity().

Referenced by Domination_count_controlpoints(), and Onslaught_count_generators().

◆ Team_GetNumberOfTeamsWithOwnedItems()

int Team_GetNumberOfTeamsWithOwnedItems ( )

Returns the number of teams that own items.

Returns
Number of teams that own items.

Definition at line 177 of file teamplay.qc.

178{
179 int result = 0;
180 for (int i = 0; i < AVAILABLE_TEAMS; ++i)
181 {
183 {
184 ++result;
185 }
186 }
187 return result;
188}
int m_num_owned_items
Number of items owned by a team.
Definition teamplay.qc:39

References AVAILABLE_TEAMS, g_team_entities, m_num_owned_items, and result.

◆ Team_GetTeam()

entity Team_GetTeam ( int team_num)

Returns the global team entity that corresponds to the given TEAM_NUM value.

Parameters
[in]team_numTeam value. See TEAM_NUM constants.
Returns
Global team entity that corresponds to the given TEAM_NUM value.

Definition at line 95 of file teamplay.qc.

96{
97 if (!Team_IsValidTeam(team_num))
98 {
99 LOG_FATALF("Value is invalid: %f", team_num);
100 }
101 return g_team_entities[Team_TeamToIndex(team_num) - 1];
102}
#define LOG_FATALF(...)
Definition log.qh:51

References entity(), g_team_entities, LOG_FATALF, Team_IsValidTeam(), and Team_TeamToIndex().

Referenced by CA_PreventStalemate(), and WinningCondition_RanOutOfSpawns().

◆ Team_GetTeamFromIndex()

entity Team_GetTeamFromIndex ( int index)

Returns the global team entity at the given index.

Parameters
[in]indexIndex of the team.
Returns
Global team entity at the given index.

Definition at line 86 of file teamplay.qc.

87{
88 if (!Team_IsValidIndex(index))
89 {
90 LOG_FATALF("Index is invalid: %f", index);
91 }
92 return g_team_entities[index - 1];
93}

References entity(), g_team_entities, LOG_FATALF, and Team_IsValidIndex().

Referenced by CA_CheckTeams(), CA_count_alive_players(), CA_PreventStalemate(), Domination_count_controlpoints(), Entity_GetTeam(), freezetag_CheckTeams(), freezetag_count_alive_players(), Onslaught_count_generators(), WinningCondition_RanOutOfSpawns(), and WinningCondition_Scores().

◆ Team_GetTeamScore()

float Team_GetTeamScore ( entity team_ent)

Returns the score of the team.

Parameters
[in]team_entTeam entity.
Returns
Score of the team.

Definition at line 104 of file teamplay.qc.

105{
106 return team_ent.m_team_score;
107}

References entity().

Referenced by WinningCondition_RanOutOfSpawns().

◆ Team_GetWinnerAliveTeam()

int Team_GetWinnerAliveTeam ( )

Returns the winner team.

Returns
Winner team or 0 if 2 or more teams have alive players or -1 if no team has any alive players.

Definition at line 124 of file teamplay.qc.

125{
126 int winner = 0;
127 for (int i = 0; i < AVAILABLE_TEAMS; ++i)
128 {
130 {
131 if (winner)
132 return 0;
133 winner = Team_IndexToTeam(i + 1);
134 }
135 }
136 return (winner ? winner : -1);
137}

References AVAILABLE_TEAMS, g_team_entities, m_num_players_alive, and Team_IndexToTeam().

Referenced by CA_CheckWinner(), and freezetag_CheckWinner().

◆ Team_GetWinnerTeam_WithOwnedItems()

int Team_GetWinnerTeam_WithOwnedItems ( int min_owned_items)

Returns the winner team.

Parameters
[in]min_owned_itemsMinimum number of items the winner team must have.
Returns
Winner team or 0 if 2 or more teams own items or -1 if no team own any items.

Definition at line 152 of file teamplay.qc.

153{
154 int winner = 0;
155 for (int i = 0; i < AVAILABLE_TEAMS; ++i)
156 {
157 if (g_team_entities[i].m_num_owned_items >= min_control_points)
158 {
159 if (winner)
160 return 0;
161 winner = Team_IndexToTeam(i + 1);
162 }
163 }
164 return (winner ? winner : -1);
165}

References AVAILABLE_TEAMS, g_team_entities, m_num_owned_items, and Team_IndexToTeam().

Referenced by Domination_CheckWinner(), and Onslaught_CheckWinner().

◆ Team_InitTeams()

void Team_InitTeams ( )

Definition at line 76 of file teamplay.qc.

77{
78 if (g_team_entities[0])
79 return;
80 for (int i = 0; i < AVAILABLE_TEAMS; ++i)
81 {
82 g_team_entities[i] = new_pure(team_entity);
83 }
84}

References AVAILABLE_TEAMS, g_team_entities, and new_pure.

Referenced by ScoreRules_basics().

◆ Team_MapEnts_FindOrSpawn()

int Team_MapEnts_FindOrSpawn ( string ent_classname,
int defaultmask )

Finds any team map entities and returns their bitmask, else spawns them.

Parameters
[in]ent_classnameThe gametype-specific entity classname to search for.
[in]defaultmaskThe bitmask of the team entities to spawn if the map has none.
Returns
The bitmask of the teams found, == defaultmask if there were none.

Definition at line 56 of file teamplay.qc.

57{
58 int teams = 0;
59 entity head = findchain(classname, ent_classname);
60 while (head)
61 {
62 if (Team_IsValidTeam(head.team))
63 teams |= Team_TeamToBit(head.team);
64 head = head.chain;
65 }
66 if (teams)
67 return teams;
68
69 LOG_TRACEF("No \"%s\" entities found on this map, creating them anyway.", ent_classname);
70 for (int i = 1; i <= NUM_TEAMS; ++i)
71 if (defaultmask & Team_IndexToBit(i))
72 _SpawnTeam(ent_classname, Team_IndexToTeam(i));
73 return defaultmask;
74}
entity teams
Definition main.qh:58
string classname
#define LOG_TRACEF(...)
Definition log.qh:75
void _SpawnTeam(string teament_classname, int team_num)
Definition teamplay.qc:48
int Team_TeamToBit(int team_num)
Converts team value into bit value that is used in team bitmasks.
Definition teams.qh:199
const int NUM_TEAMS
Max number of teams that could exist, prefer AVAILABLE_TEAMS.
Definition teams.qh:3

References _SpawnTeam(), classname, entity(), LOG_TRACEF, NUM_TEAMS, Team_IndexToBit(), Team_IndexToTeam(), Team_IsValidTeam(), Team_TeamToBit(), and teams.

Referenced by ctf_DelayedInit(), tdm_DelayedInit(), and tmayhem_DelayedInit().

◆ Team_SetNumberOfAlivePlayers()

void Team_SetNumberOfAlivePlayers ( entity team_ent,
int number )

Sets the number of alive players in a team.

Parameters
[in,out]team_entTeam entity.
[in]numberNumber of players to set.

Definition at line 119 of file teamplay.qc.

120{
121 team_ent.m_num_players_alive = number;
122}
int int number
Definition impulse.qc:89

References entity(), and number.

Referenced by CA_count_alive_players(), and freezetag_count_alive_players().

◆ Team_SetNumberOfOwnedItems()

void Team_SetNumberOfOwnedItems ( entity team_ent,
int number )

Sets the number of items owned by a team.

Parameters
[in,out]team_entTeam entity.
[in]numberNumber of items to set.

Definition at line 172 of file teamplay.qc.

173{
174 team_ent.m_num_owned_items = number;
175}

References entity(), and number.

Referenced by Domination_count_controlpoints(), and Onslaught_count_generators().

◆ Team_SetTeamScore()

void Team_SetTeamScore ( entity team_ent,
float score )

Sets the score of the team.

Parameters
[in,out]team_entTeam entity.
[in]scoreScore to set.

Definition at line 109 of file teamplay.qc.

110{
111 team_ent.m_team_score = score;
112}

References entity().

Referenced by WinningCondition_RanOutOfSpawns(), and WinningCondition_Scores().

◆ TeamBalance_AreEqual()

bool TeamBalance_AreEqual ( entity ignore,
bool would_leave )

Definition at line 585 of file teamplay.qc.

586{
587 entity balance = TeamBalance_CheckAllowedTeams(ignore);
588 TeamBalance_GetTeamCounts(balance, ignore);
589
590 int prev_size = 0, i = 1;
591
592 for (; i <= AVAILABLE_TEAMS; ++i)
593 {
594 entity team_ent = TeamBalance_GetTeamFromIndex(balance, i);
595 int team_size = would_leave ? team_ent.m_num_players_net : team_ent.m_num_players;
596 if (i > 1 && team_size != prev_size)
597 break;
598 prev_size = team_size;
599 }
600
601 TeamBalance_Destroy(balance);
602 return i > AVAILABLE_TEAMS;
603}
int team_size
Definition main.qh:189
entity TeamBalance_GetTeamFromIndex(entity balance, int index)
Returns the team entity of the team balance entity at the given index.
Definition teamplay.qc:1178

References AVAILABLE_TEAMS, entity(), team_size, TeamBalance_CheckAllowedTeams(), TeamBalance_Destroy(), TeamBalance_GetTeamCounts(), and TeamBalance_GetTeamFromIndex().

Referenced by Join(), QueueNeeded(), and Remove_Countdown().

◆ TeamBalance_AutoBalanceBots()

void TeamBalance_AutoBalanceBots ( )

Switches a bot from one team to another if teams are not balanced.

Definition at line 1007 of file teamplay.qc.

1008{
1009 // checks disabled because we always want auto-balanced bots
1010 //if (!autocvar_g_balance_teams_prevent_imbalance)
1011 // return;
1012
1013 if (intermission_running) return;
1014
1017 int smallest_team_index = 0;
1018 int smallest_team_player_count = 0;
1019 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1020 {
1021 entity team_ = TeamBalance_GetTeamFromIndex(balance, i);
1022 if (!TeamBalanceTeam_IsAllowed(team_))
1023 {
1024 continue;
1025 }
1026 int playercount = TeamBalanceTeam_GetNumberOfPlayers(team_);
1027 if (smallest_team_index == 0)
1028 {
1029 smallest_team_index = i;
1030 smallest_team_player_count = playercount;
1031 }
1032 else if (playercount < smallest_team_player_count)
1033 {
1034 smallest_team_index = i;
1035 smallest_team_player_count = playercount;
1036 }
1037 }
1038 //PrintToChatAll(sprintf("Smallest team: %f", smallest_team_index));
1039 //PrintToChatAll(sprintf("Smallest team players: %f", smallest_team_player_count));
1040 entity switchable_bot = NULL;
1041 int teams = BITS(AVAILABLE_TEAMS);
1042 while (teams != 0)
1043 {
1044 int largest_team_index = TeamBalance_GetLargestTeamIndex(balance,
1045 teams);
1046 if (smallest_team_index == largest_team_index)
1047 {
1048 TeamBalance_Destroy(balance);
1049 return;
1050 }
1051 entity largest_team = TeamBalance_GetTeamFromIndex(balance,
1052 largest_team_index);
1053 int largest_team_player_count = TeamBalanceTeam_GetNumberOfPlayers(
1054 largest_team);
1055 if (largest_team_player_count - smallest_team_player_count < 2)
1056 {
1057 TeamBalance_Destroy(balance);
1058 return;
1059 }
1060 //PrintToChatAll(sprintf("Largest team: %f", largest_team_index));
1061 //PrintToChatAll(sprintf("Largest team players: %f", largest_team_player_count));
1062 switchable_bot = TeamBalance_GetPlayerForTeamSwitch(largest_team_index,
1063 smallest_team_index, true);
1064 if (switchable_bot != NULL)
1065 {
1066 break;
1067 }
1068 teams &= ~Team_IndexToBit(largest_team_index);
1069 }
1070 TeamBalance_Destroy(balance);
1071 if (switchable_bot == NULL)
1072 {
1073 //PrintToChatAll("No bot found after searching through all the teams");
1074 return;
1075 }
1076 SetPlayerTeam(switchable_bot, smallest_team_index, TEAM_CHANGE_AUTO);
1077}
#define BITS(n)
Definition bits.qh:9
bool intermission_running
bool TeamBalanceTeam_IsAllowed(entity team_ent)
Returns whether the team is allowed.
Definition teamplay.qc:1192
int TeamBalanceTeam_GetNumberOfPlayers(entity team_ent)
Returns the number of players (both humans and bots) in a team.
Definition teamplay.qc:1197
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.
Definition teamplay.qc:1079
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.
Definition teamplay.qc:1109
@ TEAM_CHANGE_AUTO
The team was selected by autobalance.
Definition teamplay.qh:129

References AVAILABLE_TEAMS, BITS, entity(), intermission_running, NULL, SetPlayerTeam(), TEAM_CHANGE_AUTO, Team_IndexToBit(), TeamBalance_CheckAllowedTeams(), TeamBalance_Destroy(), TeamBalance_GetLargestTeamIndex(), TeamBalance_GetPlayerForTeamSwitch(), TeamBalance_GetTeamCounts(), TeamBalance_GetTeamFromIndex(), TeamBalanceTeam_GetNumberOfPlayers(), TeamBalanceTeam_IsAllowed(), and teams.

Referenced by SetPlayerTeam().

◆ TeamBalance_BanTeamsExcept()

void TeamBalance_BanTeamsExcept ( entity balance,
int index )

Bans team change to all teams except the given one.

Parameters
[in,out]balanceTeam balance entity.
[in]indexIndex of the team.

Definition at line 1167 of file teamplay.qc.

1168{
1169 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1170 {
1171 if (i != index)
1172 {
1173 balance.m_team_balance_team[i - 1].m_num_players = TEAM_NOT_ALLOWED;
1174 }
1175 }
1176}
const int TEAM_NOT_ALLOWED
Indicates that the player is not allowed to join a team.
Definition teamplay.qc:29

References AVAILABLE_TEAMS, entity(), and TEAM_NOT_ALLOWED.

Referenced by TeamBalance_CheckAllowedTeams().

◆ TeamBalance_CheckAllowedTeams()

entity TeamBalance_CheckAllowedTeams ( entity for_whom)

Checks whether the player can join teams according to global configuration and mutator settings.

Parameters
[in]for_whomPlayer to check for. Pass NULL for global rules.
Returns
Team balance entity that holds information about teams. This entity will be automatically destroyed on the next frame but you are encouraged to manually destroy it by calling TeamBalance_Destroy for performance reasons.

Definition at line 424 of file teamplay.qc.

425{
426 entity balance = spawn();
427 for (int i = 0; i < AVAILABLE_TEAMS; ++i)
428 {
429 entity team_ent = balance.m_team_balance_team[i] = spawn();
430 team_ent.m_team_score = g_team_entities[i].m_team_score;
431 team_ent.m_num_players = teamplay_bitmask & BIT(i) ? 0 : TEAM_NOT_ALLOWED;
432 }
434 balance.nextthink = time;
435
436 // TODO: Balance quantity of bots across > 2 teams when bot_vs_human is set (and remove next line)
437 if (autocvar_bot_vs_human && AVAILABLE_TEAMS == 2 && for_whom)
438 {
439 if (autocvar_bot_vs_human > 0)
440 {
441 // find last team available
442 if (IS_BOT_CLIENT(for_whom))
443 {
444 if (TeamBalance_IsTeamAllowedInternal(balance, 4))
445 {
446 TeamBalance_BanTeamsExcept(balance, 4);
447 }
448 else if (TeamBalance_IsTeamAllowedInternal(balance, 3))
449 {
450 TeamBalance_BanTeamsExcept(balance, 3);
451 }
452 else
453 {
454 TeamBalance_BanTeamsExcept(balance, 2);
455 }
456 // no further cases, we know at least 2 teams exist
457 }
458 else
459 {
460 if (TeamBalance_IsTeamAllowedInternal(balance, 1))
461 {
462 TeamBalance_BanTeamsExcept(balance, 1);
463 }
464 else if (TeamBalance_IsTeamAllowedInternal(balance, 2))
465 {
466 TeamBalance_BanTeamsExcept(balance, 2);
467 }
468 else
469 {
470 TeamBalance_BanTeamsExcept(balance, 3);
471 }
472 // no further cases, bots have one of the teams
473 }
474 }
475 else
476 {
477 // find first team available
478 if (IS_BOT_CLIENT(for_whom))
479 {
480 if (TeamBalance_IsTeamAllowedInternal(balance, 1))
481 {
482 TeamBalance_BanTeamsExcept(balance, 1);
483 }
484 else if (TeamBalance_IsTeamAllowedInternal(balance, 2))
485 {
486 TeamBalance_BanTeamsExcept(balance, 2);
487 }
488 else
489 {
490 TeamBalance_BanTeamsExcept(balance, 3);
491 }
492 // no further cases, we know at least 2 teams exist
493 }
494 else
495 {
496 if (TeamBalance_IsTeamAllowedInternal(balance, 4))
497 {
498 TeamBalance_BanTeamsExcept(balance, 4);
499 }
500 else if (TeamBalance_IsTeamAllowedInternal(balance, 3))
501 {
502 TeamBalance_BanTeamsExcept(balance, 3);
503 }
504 else
505 {
506 TeamBalance_BanTeamsExcept(balance, 2);
507 }
508 // no further cases, bots have one of the teams
509 }
510 }
511 }
512
513 if (!for_whom)
514 {
515 balance.m_team_balance_state = TEAM_BALANCE_TEAMS_CHECKED;
516 return balance;
517 }
518
519 // if player has a forced team, ONLY allow that one
520 int i = Player_GetForcedTeamIndex(for_whom);
521 if (i != TEAM_FORCE_DEFAULT)
522 TeamBalance_BanTeamsExcept(balance, i);
523
524 balance.m_team_balance_state = TEAM_BALANCE_TEAMS_CHECKED;
525 return balance;
526}
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
#define spawn
int autocvar_bot_vs_human
Definition cvars.qh:67
#define setthink(e, f)
void TeamBalance_BanTeamsExcept(entity balance, int index)
Bans team change to all teams except the given one.
Definition teamplay.qc:1167
bool TeamBalance_IsTeamAllowedInternal(entity balance, int index)
Returns whether the team change to the specified team is allowed.
Definition teamplay.qc:1161
int teamplay_bitmask
The set of currently available teams (AVAILABLE_TEAMS is the number of them).
Definition teamplay.qh:18

References autocvar_bot_vs_human, AVAILABLE_TEAMS, BIT, entity(), g_team_entities, IS_BOT_CLIENT, Player_GetForcedTeamIndex(), setthink, spawn, TEAM_BALANCE_TEAMS_CHECKED, TEAM_FORCE_DEFAULT, TEAM_NOT_ALLOWED, TeamBalance_BanTeamsExcept(), TeamBalance_Destroy(), TeamBalance_IsTeamAllowedInternal(), teamplay_bitmask, and time.

Referenced by adjust_respawntime(), bot_removefromlargestteam(), bot_setnameandstuff(), ClientCommand_join(), ClientConnect(), ClientKill_TeamChange(), GameCommand_moveplayer(), Join(), MUTATOR_HOOKABLE(), QueueNeeded(), shuffleteams(), TeamBalance_AreEqual(), TeamBalance_AutoBalanceBots(), TeamBalance_GetPlayerForTeamSwitch(), TeamBalance_JoinBestTeam(), TeamBalance_QueuedPlayersTagIn(), TeamBalance_RemoveExcessPlayers(), TeamBalance_SizeDifference(), and WinningCondition_RanOutOfSpawns().

◆ TeamBalance_CompareTeamsInternal()

int TeamBalance_CompareTeamsInternal ( entity team_a,
entity team_b,
entity client,
bool use_score )

Compares two teams for the purposes of game balance.

Parameters
[in]team_aFirst team.
[in]team_bSecond team.
[in]clientThe entity trying to join a team.
[in]use_scoreWhether to take into account team scores and skill ratings.
Returns
TEAMS_COMPARE value. See above.
Note
You need to call TeamBalance_GetTeamCounts before calling this function.

Definition at line 1207 of file teamplay.qc.

1208{
1209 if (team_a == team_b)
1210 return TEAMS_COMPARE_EQUAL;
1211
1212 int team_a_size = IS_REAL_CLIENT(client) ? team_a.m_num_players_net : team_a.m_num_players;
1213 int team_b_size = IS_REAL_CLIENT(client) ? team_b.m_num_players_net : team_b.m_num_players;
1214
1215 if (team_a_size < team_b_size)
1216 return TEAMS_COMPARE_LESS;
1217 if (team_a_size > team_b_size)
1218 return TEAMS_COMPARE_GREATER;
1219 if (!use_score || !team_a_size)
1220 return TEAMS_COMPARE_EQUAL;
1221
1222// Compare skill and score together as team strength.
1223 // Teams without players have 0 skill_mu but may have a score which must still apply.
1224 bool use_skill = team_a.m_skill_var && team_b.m_skill_var && autocvar_g_balance_teams_skill
1225 // z-score, <= threshold SDs means difference isn't significant, formula squared for perf
1226 && (((team_a.m_skill_mu - team_b.m_skill_mu)**2) / (team_a.m_skill_var + team_b.m_skill_var) > autocvar_g_balance_teams_skill_significance_threshold**2);
1227 float team_a_strength = use_skill ? team_a.m_skill_mu : 1;
1228 float team_b_strength = use_skill ? team_b.m_skill_mu : 1;
1229 // Early in the match scores are ~random due to too few samples,
1230 // as the match progresses scores become more reliable indicators of team strength.
1231 // 0.96875 (31/32): if at halftime one team has score 0 and the other has 2 its skill is multiplied by 1.376,
1232 // < 1 so that a team with 1 point is stronger than one with 0 (but not by much as sample size is small).
1233 // Applying full score ratio to one team is equivalent here to applying half of it to each.
1235 {
1236 float timelimit_sec = autocvar_timelimit > 0 ? autocvar_timelimit * 60 : 20 * 60;
1237 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)
1238 * min(1, (time - game_starttime) / timelimit_sec)**1.5);
1239 }
1240
1241 if (team_a_strength < team_b_strength)
1242 return TEAMS_COMPARE_LESS;
1243 if (team_a_strength > team_b_strength)
1244 return TEAMS_COMPARE_GREATER;
1245 return TEAMS_COMPARE_EQUAL;
1246}
#define autocvar_timelimit
Definition stats.qh:92
float game_starttime
Definition stats.qh:82
float min(float f,...)
int autocvar_g_balance_teams_skill
Definition teamplay.qh:12
float autocvar_g_balance_teams_skill_significance_threshold
Definition teamplay.qh:14
@ TEAMS_COMPARE_GREATER
First team the greater than the second one.
Definition teamplay.qh:261
@ TEAMS_COMPARE_LESS
First team is less than the second one.
Definition teamplay.qh:259
@ TEAMS_COMPARE_EQUAL
Both teams are equal.
Definition teamplay.qh:260

References autocvar_g_balance_teams_skill, autocvar_g_balance_teams_skill_significance_threshold, autocvar_timelimit, entity(), game_starttime, IS_REAL_CLIENT, min(), TEAMS_COMPARE_EQUAL, TEAMS_COMPARE_GREATER, TEAMS_COMPARE_LESS, time, and warmup_stage.

Referenced by TeamBalance_FindBestTeams().

◆ TeamBalance_Destroy()

void TeamBalance_Destroy ( entity balance)

Destroy the team balance entity.

Parameters
[in,out]balanceTeam balance entity to destroy.
Note
Team balance entity is allowed to be NULL.

Definition at line 528 of file teamplay.qc.

529{
530 if (balance == NULL)
531 {
532 return;
533 }
534 for (int i = 0; i < AVAILABLE_TEAMS; ++i)
535 {
536 delete(balance.(m_team_balance_team[i]));
537 }
538 delete(balance);
539}

References AVAILABLE_TEAMS, entity(), and NULL.

Referenced by adjust_respawntime(), bot_setnameandstuff(), ClientCommand_join(), ClientConnect(), ClientKill_TeamChange(), GameCommand_moveplayer(), Join(), QueueNeeded(), shuffleteams(), TeamBalance_AreEqual(), TeamBalance_AutoBalanceBots(), TeamBalance_CheckAllowedTeams(), TeamBalance_GetPlayerForTeamSwitch(), TeamBalance_JoinBestTeam(), TeamBalance_QueuedPlayersTagIn(), TeamBalance_RemoveExcessPlayers(), and TeamBalance_SizeDifference().

◆ TeamBalance_FindBestTeam()

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.

Parameters
[in]balanceTeam balance entity.
[in]playerPlayer to check.
[in]ignore_player???
Returns
Index of the team that will make the game most balanced if the player joins it. If there are several equally good teams available the function will pick a random one, unless the player is already on one of them in which case that one is returned.

Definition at line 892 of file teamplay.qc.

893{
894 if (balance == NULL)
895 {
896 LOG_FATAL("Team balance entity is NULL.");
897 }
898 if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED)
899 {
900 LOG_FATAL("Team balance entity is not initialized.");
901 }
902
903 // count how many players are in each team
904 TeamBalance_GetTeamCounts(balance, ignore_player ? player : NULL);
905
906 int team_bits = TeamBalance_FindBestTeams(balance, player, true);
907 if (team_bits == 0)
908 {
909 LOG_WARNF("BUG or MISCONFIGURATION: No teams available for %s\n", player.netname);
910 return -1; // only spec
911 }
912
913 // don't punish players for UI mistakes by changing teams randomly when already on a best team
914 if (player.team > 0 && (team_bits & Team_TeamToBit(player.team)))
915 return Team_TeamToIndex(player.team);
916
918 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
919 {
920 if (team_bits & Team_IndexToBit(i))
921 {
923 }
924 }
926}
#define LOG_WARNF(...)
Definition log.qh:59
ERASEABLE void RandomSelection_Init()
Definition random.qc:4
float RandomSelection_chosen_float
Definition random.qh:6
#define RandomSelection_AddFloat(f, weight, priority)
Definition random.qh:15

References AVAILABLE_TEAMS, entity(), LOG_FATAL, LOG_WARNF, NULL, RandomSelection_AddFloat, RandomSelection_chosen_float, RandomSelection_Init(), TEAM_BALANCE_UNINITIALIZED, Team_IndexToBit(), Team_TeamToBit(), Team_TeamToIndex(), TeamBalance_FindBestTeams(), and TeamBalance_GetTeamCounts().

Referenced by ClientKill_TeamChange(), GameCommand_moveplayer(), Join(), and TeamBalance_JoinBestTeam().

◆ TeamBalance_FindBestTeams()

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 them.

Parameters
[in]balanceTeam balance entity.
[in]playerPlayer to check.
[in]use_scoreWhether to take into account team scores and skill ratings.
Returns
Bitmask of the teams that will make the game most balanced if the player joins any of them.
Note
You need to call TeamBalance_GetTeamCounts before calling this function.

Definition at line 928 of file teamplay.qc.

929{
930 if (balance == NULL)
931 LOG_FATAL("Team balance entity is NULL.");
932 if (balance.m_team_balance_state != TEAM_BALANCE_TEAM_COUNTS_FILLED)
933 LOG_FATAL("TeamBalance_GetTeamCounts has not been called.");
934
935 if (MUTATOR_CALLHOOK(TeamBalance_FindBestTeams, player) == true)
936 return M_ARGV(1, float);
937
938 int team_bits = 0;
939 int previous_team = 0;
940 float player_skill_mu = 0, player_skill_var = 0;
941 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
942 {
943 if (!TeamBalance_IsTeamAllowedInternal(balance, i))
944 continue;
945 if (previous_team == 0)
946 {
947 team_bits = Team_IndexToBit(i);
948 previous_team = i;
949 continue;
950 }
951
952 entity team_i = TeamBalance_GetTeamFromIndex(balance, i);
953 entity team_p = TeamBalance_GetTeamFromIndex(balance, previous_team);
954
955 // During warmup the "best" team isn't the smallest or weakest one,
956 // that method puts all the noobs on the same team when players join in the "wrong" order.
957 // This picks the team(s) whose weighted mean skill differs the most from the joining player's skill,
958 // unless they're all close enough in which case it picks smaller team(s).
959 // Warmup-only as it must unbalance team sizes in extreme cases, swaps needed should be minimal.
961 {
962 if (!player_skill_mu)
963 {
964 player_skill_mu = player.m_skill_mu ? player.m_skill_mu : server_skill_average;
965 player_skill_var = player.m_skill_var ? player.m_skill_var : (server_skill_average * 0.25)**2;
966 }
967 // To determine significance, we'll use a z-score of the inverse-variance weightings
968 // with an adjustable threshold
969 float team_i_z = (team_i.m_skill_mu - player_skill_mu) / sqrt(team_i.m_skill_var + player_skill_var);
970 float team_p_z = (team_p.m_skill_mu - player_skill_mu) / sqrt(team_p.m_skill_var + player_skill_var);
971 bool significant = fabs(team_i_z - team_p_z) > autocvar_g_balance_teams_skill_significance_threshold;
972 // Do fabs afterwards, since the player can be in the middle of the teams (one z +ve, other -ve)
973 team_i_z = fabs(team_i_z);
974 team_p_z = fabs(team_p_z);
975
976 // NB: if bots were to use this code they'd need .m_num_players instead of .m_num_players_net
977 if ((!significant && team_i.m_num_players_net < team_p.m_num_players_net)
978 || ((significant || team_i.m_num_players_net == team_p.m_num_players_net) && team_i_z > team_p_z))
979 {
980 team_bits = Team_IndexToBit(i);
981 previous_team = i;
982 }
983 else if (team_i_z == team_p_z // unlikely with humans, normal with all bots
984 && team_i.m_num_players_net == team_p.m_num_players_net)
985 {
986 team_bits |= Team_IndexToBit(i);
987 previous_team = i;
988 }
989 continue;
990 }
991
992 int compare = TeamBalance_CompareTeamsInternal(team_i, team_p, player, use_score);
993 if (compare == TEAMS_COMPARE_LESS)
994 {
995 team_bits = Team_IndexToBit(i);
996 previous_team = i;
997 }
998 else if (compare == TEAMS_COMPARE_EQUAL)
999 {
1000 team_bits |= Team_IndexToBit(i);
1001 previous_team = i;
1002 }
1003 }
1004 return team_bits;
1005}
#define M_ARGV(x, type)
Definition events.qh:17
float sqrt(float f)
float fabs(float f)
bool autocvar_sv_teamnagger
Definition client.qh:58
int TeamBalance_CompareTeamsInternal(entity team_a, entity team_b, entity client, bool use_score)
Compares two teams for the purposes of game balance.
Definition teamplay.qc:1207
float server_skill_average
Scaled inverse-variance weighted mean of all clients' m_skill_mu, for unranked clients.
Definition teamplay.qh:21

References autocvar_g_balance_teams_skill, autocvar_g_balance_teams_skill_significance_threshold, autocvar_sv_teamnagger, AVAILABLE_TEAMS, entity(), fabs(), IS_REAL_CLIENT, LOG_FATAL, M_ARGV, MUTATOR_CALLHOOK, NULL, server_skill_average, sqrt(), TEAM_BALANCE_TEAM_COUNTS_FILLED, Team_IndexToBit(), TeamBalance_CompareTeamsInternal(), TeamBalance_FindBestTeams(), TeamBalance_GetTeamFromIndex(), TeamBalance_IsTeamAllowedInternal(), TEAMS_COMPARE_EQUAL, TEAMS_COMPARE_LESS, and warmup_stage.

Referenced by ClientCommand_join(), MUTATOR_HOOKABLE(), QueueNeeded(), TeamBalance_FindBestTeam(), and TeamBalance_FindBestTeams().

◆ TeamBalance_GetAllowedTeams()

int TeamBalance_GetAllowedTeams ( entity balance)

Returns the bitmask of allowed teams.

Parameters
[in]balanceTeam balance entity.
Returns
Bitmask of allowed teams.

Definition at line 541 of file teamplay.qc.

542{
543 if (balance == NULL)
544 {
545 LOG_FATAL("Team balance entity is NULL.");
546 }
547 if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED)
548 {
549 LOG_FATAL("Team balance entity is not initialized.");
550 }
551 int result = 0;
552 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
553 {
554 if (TeamBalance_IsTeamAllowedInternal(balance, i))
555 {
557 }
558 }
559 return result;
560}

References AVAILABLE_TEAMS, entity(), LOG_FATAL, NULL, result, TEAM_BALANCE_UNINITIALIZED, Team_IndexToBit(), and TeamBalance_IsTeamAllowedInternal().

Referenced by ClientConnect().

◆ TeamBalance_GetLargestTeamIndex()

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.

Parameters
[in]balanceTeam balance entity.
[in]teamsBitmask of teams to search in.
Returns
Index of the team with most players.

Definition at line 1079 of file teamplay.qc.

1080{
1081 int largest_team_index = 0;
1082 int largest_team_player_count = 0;
1083 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1084 {
1085 if (!(Team_IndexToBit(i) & teams))
1086 {
1087 continue;
1088 }
1089 entity team_ = TeamBalance_GetTeamFromIndex(balance, i);
1090 if (!TeamBalanceTeam_IsAllowed(team_))
1091 {
1092 continue;
1093 }
1094 int playercount = TeamBalanceTeam_GetNumberOfPlayers(team_);
1095 if (largest_team_index == 0)
1096 {
1097 largest_team_index = i;
1098 largest_team_player_count = playercount;
1099 }
1100 else if (playercount > largest_team_player_count)
1101 {
1102 largest_team_index = i;
1103 largest_team_player_count = playercount;
1104 }
1105 }
1106 return largest_team_index;
1107}

References AVAILABLE_TEAMS, entity(), Team_IndexToBit(), TeamBalance_GetTeamFromIndex(), TeamBalanceTeam_GetNumberOfPlayers(), TeamBalanceTeam_IsAllowed(), and teams.

Referenced by TeamBalance_AutoBalanceBots().

◆ TeamBalance_GetNumberOfPlayers()

int TeamBalance_GetNumberOfPlayers ( entity balance,
int index )

Returns the number of players (both humans and bots) in a team.

Parameters
[in]balanceTeam balance entity.
[in]indexIndex of the team.
Returns
Number of player (both humans and bots) in a team.
Note
You need to call TeamBalance_GetTeamCounts before calling this function.

Definition at line 875 of file teamplay.qc.

876{
877 if (balance == NULL)
878 {
879 LOG_FATAL("Team balance entity is NULL.");
880 }
881 if (balance.m_team_balance_state != TEAM_BALANCE_TEAM_COUNTS_FILLED)
882 {
883 LOG_FATAL("TeamBalance_GetTeamCounts has not been called.");
884 }
885 if (!Team_IsValidIndex(index))
886 {
887 LOG_FATALF("Team index is invalid: %f", index);
888 }
889 return balance.m_team_balance_team[index - 1].m_num_players;
890}

References entity(), LOG_FATAL, LOG_FATALF, NULL, TEAM_BALANCE_TEAM_COUNTS_FILLED, and Team_IsValidIndex().

Referenced by adjust_respawntime(), and bot_setnameandstuff().

◆ TeamBalance_GetPlayerForTeamSwitch()

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.

Parameters
[in]source_team_indexIndex of the team to search in.
[in]destination_team_indexIndex of the team to switch to.
[in]is_botTrue to search for bot, false for human.
Returns
Player who is the most suitable for switching between the given teams or NULL if not found.

Definition at line 1109 of file teamplay.qc.

1111{
1113 destination_team_index, is_bot))
1114 {
1115 return M_ARGV(3, entity);
1116 }
1117 entity lowest_player = NULL;
1118 float lowest_score = FLOAT_MAX;
1119 FOREACH_CLIENT(Entity_GetTeamIndex(it) == source_team_index,
1120 {
1121 if (IS_BOT_CLIENT(it) != is_bot)
1122 {
1123 continue;
1124 }
1125 float temp_score = PlayerScore_Get(it, SP_SCORE);
1126 if (temp_score >= lowest_score)
1127 {
1128 continue;
1129 }
1130 //PrintToChatAll(sprintf(
1131 // "Found %s with lowest score, checking allowed teams", it.netname));
1133 if (TeamBalance_IsTeamAllowed(balance, source_team_index))
1134 {
1135 //PrintToChatAll("Allowed");
1136 lowest_player = it;
1137 lowest_score = temp_score;
1138 }
1139 else
1140 {
1141 //PrintToChatAll("Not allowed");
1142 }
1143 TeamBalance_Destroy(balance);
1144 });
1145 return lowest_player;
1146}
const float FLOAT_MAX
Definition float.qh:3
#define PlayerScore_Get(player, scorefield)
Returns the player's score.
Definition scores.qh:42
bool TeamBalance_IsTeamAllowed(entity balance, int index)
Returns whether the team change to the specified team is allowed.
Definition teamplay.qc:738

References entity(), Entity_GetTeamIndex(), FLOAT_MAX, FOREACH_CLIENT, IS_BOT_CLIENT, M_ARGV, MUTATOR_CALLHOOK, NULL, PlayerScore_Get, TeamBalance_CheckAllowedTeams(), TeamBalance_Destroy(), TeamBalance_GetPlayerForTeamSwitch(), and TeamBalance_IsTeamAllowed().

Referenced by MUTATOR_HOOKABLE(), TeamBalance_AutoBalanceBots(), and TeamBalance_GetPlayerForTeamSwitch().

◆ TeamBalance_GetTeam()

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.

Parameters
[in]balanceTeam balance entity.
[in]team_numTeam value. See TEAM_NUM constants.
Returns
Team entity of the team balance entity that corresponds to the given TEAM_NUM value.
Note
The team entities are temporary so you may write to their fields.

Definition at line 1187 of file teamplay.qc.

1188{
1189 return TeamBalance_GetTeamFromIndex(balance, Team_TeamToIndex(team_num));
1190}

References entity(), Team_TeamToIndex(), and TeamBalance_GetTeamFromIndex().

Referenced by TeamBalance_GetTeamCounts(), and TeamBalance_QueuedPlayersTagIn().

◆ TeamBalance_GetTeamCounts()

void TeamBalance_GetTeamCounts ( entity balance,
entity ignore )

Counts the number of players and various other information about each team.

Parameters
[in,out]balanceTeam balance entity.
[in]ignorePlayer to ignore. This is useful if you plan to switch the player's team. Pass NULL for global information.
Note
This function updates the internal state of the team balance entity.

Definition at line 758 of file teamplay.qc.

759{
760 if (balance == NULL)
761 LOG_FATAL("Team balance entity is NULL.");
762 if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED)
763 LOG_FATAL("Team balance entity is not initialized.");
764
766 {
767 // Mutator has overriden the configuration.
768 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
769 {
770 entity team_ent = TeamBalance_GetTeamFromIndex(balance, i);
771 if (TeamBalanceTeam_IsAllowed(team_ent))
772 {
773 MUTATOR_CALLHOOK(TeamBalance_GetTeamCount, i, ignore);
774 team_ent.m_num_players = M_ARGV(2, float);
775 team_ent.m_num_bots = M_ARGV(3, float);
776 }
777 }
778 }
779 else
780 {
781 float skill_weight = 0, skill_weight_sum = 0;
783 // Manually count all players.
784 FOREACH_CLIENT(true,
785 {
786 if (it.m_skill_mu && it.m_skill_var)
787 {
788 skill_weight = 1 / it.m_skill_var;
789 server_skill_average += it.m_skill_mu * skill_weight;
790 skill_weight_sum += skill_weight;
791 }
792
793 if (it == ignore)
794 continue;
795
796 int team_num = it.killindicator_teamchange > 0 ? it.killindicator_teamchange : it.team;
797 if (team_num <= 0)
798 {
799 int forced_team_index = Player_GetForcedTeamIndex(it);
800 if (forced_team_index > TEAM_FORCE_DEFAULT) // Do we really need this? Probably not.
801 team_num = Team_IndexToTeam(forced_team_index); // reserve the spot
802 else
803 continue;
804 }
805 if (!Team_IsValidTeam(team_num))
806 continue;
807
808 entity team_ent = TeamBalance_GetTeam(balance, team_num);
809 if (!TeamBalanceTeam_IsAllowed(team_ent))
810 continue;
811
812 team_ent.m_num_players_net = ++team_ent.m_num_players;
813 if (IS_BOT_CLIENT(it))
814 ++team_ent.m_num_bots;
815
816 if (it.m_skill_mu && it.m_skill_var)
817 {
818 // skill_weight calculated above
819 team_ent.m_skill_mu += it.m_skill_mu * skill_weight;
820 team_ent.mass += skill_weight;
821 ++team_ent.count;
822 }
823 });
824
825 // Deduct bots that would leave if a player joined their team;
826 // to ensure a player can join we aim to deduct at least one from each team which has some.
827 for (int i = 1, bots_maxperteam = ceil(bots_would_leave / AVAILABLE_TEAMS); i <= AVAILABLE_TEAMS; ++i)
828 {
829 entity team_ent = TeamBalance_GetTeamFromIndex(balance, i);
830 team_ent.m_num_players_net -= min(team_ent.m_num_bots, bots_maxperteam);
831 }
832
833 // Calculate inverse-variance weighted mean skill ratings,
834 // for each unranked client use a fraction of the mean of all clients' skill ratings.
837 : 1000;
838 float server_skill_average_var = 0; skill_weight = 0; // will only be calculated once, if needed
839 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
840 {
841 entity team_ent = TeamBalance_GetTeamFromIndex(balance, i);
842 int unranked_clients = team_ent.m_num_players - team_ent.count; // noobs and bots
843 if (unranked_clients)
844 {
845 if (!server_skill_average_var)
846 {
847 server_skill_average_var = (server_skill_average * 0.25)**2;
848 skill_weight = 1 / server_skill_average_var;
849 }
850 team_ent.m_skill_mu += (server_skill_average * skill_weight) * unranked_clients;
851 team_ent.mass += skill_weight * unranked_clients;
852 }
853 if (team_ent.mass)
854 {
855 // .mass is storing the arithmetic sum of members' inverse-variance weightings
856 // Hence the team's mean is: team_ent.m_skill_mu / team_ent.mass
857 // Hence the team's variance is: 1 / team_ent.mass
858 team_ent.m_skill_mu /= team_ent.mass;
859 team_ent.m_skill_var = 1 / team_ent.mass;
860 }
861 }
862 }
863
864 // if the player who has a forced team has not joined yet, reserve the spot
867 {
869 if (team_ent.m_num_players == team_ent.m_num_bots)
870 ++team_ent.m_num_players;
871 }
872 balance.m_team_balance_state = TEAM_BALANCE_TEAM_COUNTS_FILLED;
873}
int bots_would_leave
how many bots would leave so humans can replace them
Definition api.qh:101
float ceil(float f)
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.
Definition teamplay.qc:1187
float autocvar_g_balance_teams_skill_unranked_factor
Definition teamplay.qh:13

References autocvar_g_balance_teams_skill_unranked_factor, autocvar_g_campaign, autocvar_g_campaign_forceteam, AVAILABLE_TEAMS, bots_would_leave, ceil(), entity(), FOREACH_CLIENT, IS_BOT_CLIENT, LOG_FATAL, M_ARGV, min(), MUTATOR_CALLHOOK, NULL, Player_GetForcedTeamIndex(), server_skill_average, TEAM_BALANCE_TEAM_COUNTS_FILLED, TEAM_BALANCE_UNINITIALIZED, TEAM_FORCE_DEFAULT, Team_IndexToTeam(), Team_IsValidIndex(), Team_IsValidTeam(), TeamBalance_GetTeam(), TeamBalance_GetTeamCounts(), TeamBalance_GetTeamFromIndex(), and TeamBalanceTeam_IsAllowed().

Referenced by adjust_respawntime(), bot_removefromlargestteam(), bot_setnameandstuff(), ClientCommand_join(), MUTATOR_HOOKABLE(), QueueNeeded(), TeamBalance_AreEqual(), TeamBalance_AutoBalanceBots(), TeamBalance_FindBestTeam(), TeamBalance_GetTeamCounts(), TeamBalance_QueuedPlayersTagIn(), TeamBalance_RemoveExcessPlayers(), and TeamBalance_SizeDifference().

◆ TeamBalance_GetTeamFromIndex()

entity TeamBalance_GetTeamFromIndex ( entity balance,
int index )

Returns the team entity of the team balance entity at the given index.

Parameters
[in]balanceTeam balance entity.
[in]indexIndex of the team.
Returns
Team entity of the team balance entity at the given index.
Note
The team entities are temporary so you may write to their fields.

Definition at line 1178 of file teamplay.qc.

1179{
1180 if (!Team_IsValidIndex(index))
1181 {
1182 LOG_FATALF("Index is invalid: %f", index);
1183 }
1184 return balance.m_team_balance_team[index - 1];
1185}

References entity(), LOG_FATALF, and Team_IsValidIndex().

Referenced by TeamBalance_AreEqual(), TeamBalance_AutoBalanceBots(), TeamBalance_FindBestTeams(), TeamBalance_GetLargestTeamIndex(), TeamBalance_GetTeam(), TeamBalance_GetTeamCounts(), TeamBalance_QueuedPlayersTagIn(), TeamBalance_RemoveExcessPlayers(), and TeamBalance_SizeDifference().

◆ TeamBalance_IsTeamAllowed()

bool TeamBalance_IsTeamAllowed ( entity balance,
int index )

Returns whether the team change to the specified team is allowed.

Parameters
[in]balanceTeam balance entity.
[in]indexIndex of the team.
Returns
True if team change to the specified team is allowed, false otherwise.

Definition at line 738 of file teamplay.qc.

739{
740 if (balance == NULL)
741 {
742 LOG_FATAL("Team balance entity is NULL.");
743 }
744 if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED)
745 {
746 LOG_FATAL("Team balance entity is not initialized.");
747 }
748 if (!Team_IsValidIndex(index))
749 {
750 LOG_FATALF("Team index is invalid: %f",
751 index);
752 }
753 return TeamBalance_IsTeamAllowedInternal(balance, index);
754}

References entity(), LOG_FATAL, LOG_FATALF, NULL, TEAM_BALANCE_UNINITIALIZED, Team_IsValidIndex(), and TeamBalance_IsTeamAllowedInternal().

Referenced by adjust_respawntime(), GameCommand_moveplayer(), shuffleteams(), TeamBalance_GetPlayerForTeamSwitch(), and WinningCondition_RanOutOfSpawns().

◆ TeamBalance_IsTeamAllowedInternal()

bool TeamBalance_IsTeamAllowedInternal ( entity balance,
int index )

Returns whether the team change to the specified team is allowed.

Parameters
[in]balanceTeam balance entity.
[in]indexIndex of the team.
Returns
True if team change to the specified team is allowed, false otherwise.
Note
This function bypasses all the sanity checks.

Definition at line 1161 of file teamplay.qc.

1162{
1163 return index <= AVAILABLE_TEAMS
1164 && balance.m_team_balance_team[index - 1].m_num_players != TEAM_NOT_ALLOWED;
1165}

References AVAILABLE_TEAMS, entity(), and TEAM_NOT_ALLOWED.

Referenced by TeamBalance_CheckAllowedTeams(), TeamBalance_FindBestTeams(), TeamBalance_GetAllowedTeams(), and TeamBalance_IsTeamAllowed().

◆ TeamBalance_JoinBestTeam()

void TeamBalance_JoinBestTeam ( entity player)

Assigns the given player to a team that will make the game most balanced.

Parameters
[in,out]playerPlayer to assign.

Definition at line 415 of file teamplay.qc.

416{
417 //PrintToChatAll(sprintf("TeamBalance_JoinBestTeam: %s", player.netname));
418 entity balance = TeamBalance_CheckAllowedTeams(player);
419 int best_team_index = TeamBalance_FindBestTeam(balance, player, true);
420 TeamBalance_Destroy(balance);
421 SetPlayerTeam(player, best_team_index, TEAM_CHANGE_AUTO);
422}
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.
Definition teamplay.qc:892

References entity(), SetPlayerTeam(), TEAM_CHANGE_AUTO, TeamBalance_CheckAllowedTeams(), TeamBalance_Destroy(), and TeamBalance_FindBestTeam().

Referenced by PutPlayerInServer().

◆ TeamBalance_QueuedPlayersTagIn()

bool TeamBalance_QueuedPlayersTagIn ( entity ignore)

Joins queued player(s) to team(s) with a shortage, this should be more robust than only replacing the player that left.

Chooses players with a specific team preference first to increase chances of everyone getting what they want. Returns true if the teams are now balanced.

Definition at line 694 of file teamplay.qc.

695{
696 if (!teamplay)
697 return true;
698
699 int j, teamplayers_deficit = 0, teamplayers_max = 0;
700 entity it, balance = TeamBalance_CheckAllowedTeams(ignore);
701 TeamBalance_GetTeamCounts(balance, ignore);
702
703 for (int j = 1; j <= AVAILABLE_TEAMS; ++j)
704 {
705 it = TeamBalance_GetTeamFromIndex(balance, j);
706 // find the largest team size
707 if (it.m_num_players_net > teamplayers_max)
708 teamplayers_max = it.m_num_players_net;
709 }
710 // find how many players we'd need to join to achieve balanced numbers that way
711 for (j = 1; j <= AVAILABLE_TEAMS; ++j)
712 teamplayers_deficit += teamplayers_max - TeamBalance_GetTeamFromIndex(balance, j).m_num_players_net;
713
714 // first pass: find clients(s) who want to play on a specific team
715 for (j = 1; teamplayers_deficit > 0 && j <= maxclients; ++j)
716 {
717 it = ftoe(j);
718 if (it.wants_join <= 0 || it == ignore) continue;
719 if (TeamBalance_GetTeamFromIndex(balance, it.wants_join).m_num_players_net >= teamplayers_max) continue;
720 Join(it, false);
721 ++TeamBalance_GetTeam(balance, it.team).m_num_players_net;
722 --teamplayers_deficit;
723 }
724
725 // second pass: find clients(s) who want to play on any team
726 for (j = 1; teamplayers_deficit > 0 && j <= maxclients; ++j)
727 {
728 it = ftoe(j);
729 if (it.wants_join >= 0 || it == ignore) continue;
730 Join(it, false);
731 --teamplayers_deficit; // don't need to update .m_num_players_net as we won't read it again
732 }
733
734 TeamBalance_Destroy(balance);
735 return teamplayers_deficit <= 0; // return true if teams are now balanced
736}
float maxclients
#define ftoe(i)
Definition misc.qh:26
void Join(entity this, bool queued_join)
it's assumed this isn't called for bots (campaign_bots_may_start, centreprints)
Definition client.qc:2075

References AVAILABLE_TEAMS, entity(), ftoe, Join(), maxclients, TeamBalance_CheckAllowedTeams(), TeamBalance_Destroy(), TeamBalance_GetTeam(), TeamBalance_GetTeamCounts(), TeamBalance_GetTeamFromIndex(), and teamplay.

Referenced by ClientDisconnect(), and ClientKill_Now_TeamChange().

◆ TeamBalance_RemoveExcessPlayers()

void TeamBalance_RemoveExcessPlayers ( entity ignore)

Definition at line 631 of file teamplay.qc.

632{
633 if(AVAILABLE_TEAMS != 2 || autocvar_g_campaign) return;
634
635 entity balance = TeamBalance_CheckAllowedTeams(ignore);
636 TeamBalance_GetTeamCounts(balance, ignore);
637
638 int min = 0;
639
640 for(int i = 1; i <= AVAILABLE_TEAMS; ++i)
641 {
642 int cur = TeamBalance_GetTeamFromIndex(balance, i).m_num_players;
643 if(i == 1 || cur < min)
644 min = cur;
645 }
646
647 for(int tmi = 1; tmi <= AVAILABLE_TEAMS; ++tmi)
648 {
649 int cur = TeamBalance_GetTeamFromIndex(balance, tmi).m_num_players;
650 if(cur > 0 && cur > min) // If this team has excess players
651 {
652 // Get newest player
653 int latest_join = 0;
654 entity latest_join_pl = NULL;
655
657 if(it.team == Team_IndexToTeam(tmi) && CS(it).startplaytime > latest_join)
658 {
659 latest_join = CS(it).startplaytime;
660 latest_join_pl = it;
661 }
662 });
663
664 // Force player to spectate
665 if(latest_join_pl)
666 {
667 // Send player to spectate
669 {
670 // Give a warning before moving to spect
671 if (!remove_countdown)
672 {
675 remove_countdown.nextthink = time;
676 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MOVETOSPEC_REMOVE, playername(latest_join_pl.netname, latest_join_pl.team, true), autocvar_g_balance_teams_remove_wait);
677 }
678 remove_countdown.enemy = latest_join_pl;
680 }
681 else
682 {
683 // Move to spects immediately
684 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_REMOVE, latest_join_pl.netname);
685 PutObserverInServer(latest_join_pl, true, true);
686 }
687 }
688 }
689 }
690
691 TeamBalance_Destroy(balance);
692}
float startplaytime
Definition client.qh:67
#define INGAME(it)
Definition sv_rules.qh:24
void Remove_Countdown(entity this)
Definition teamplay.qc:606
int autocvar_g_balance_teams_remove_wait
Definition teamplay.qh:11

References autocvar_g_balance_teams_remove_wait, autocvar_g_campaign, AVAILABLE_TEAMS, CS(), entity(), FOREACH_CLIENT, INGAME, IS_REAL_CLIENT, min(), new_pure, NULL, playername(), PutObserverInServer(), Remove_Countdown(), remove_countdown, Send_Notification(), setthink, startplaytime, Team_IndexToTeam(), TeamBalance_CheckAllowedTeams(), TeamBalance_Destroy(), TeamBalance_GetTeamCounts(), TeamBalance_GetTeamFromIndex(), and time.

Referenced by ClientDisconnect(), ClientKill_Now_TeamChange(), and Remove_Countdown().

◆ TeamBalance_SizeDifference()

int TeamBalance_SizeDifference ( entity ignore)

Returns the size difference between the largest and smallest team (bots included).

Definition at line 562 of file teamplay.qc.

563{
564 if (!teamplay)
565 return 0;
566
567 entity balance = TeamBalance_CheckAllowedTeams(ignore);
568 TeamBalance_GetTeamCounts(balance, ignore);
569
570 int ts_min = 255, ts_max = 0;
571 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
572 {
573 int ts = TeamBalance_GetTeamFromIndex(balance, i).m_num_players;
574 if (ts_min > ts)
575 ts_min = ts;
576 if (ts_max < ts)
577 ts_max = ts;
578 }
579
580 TeamBalance_Destroy(balance);
581
582 return ts_max - ts_min;
583}
int ts_min
Definition scoreboard.qh:27
int ts_max
team size
Definition scoreboard.qh:27

References AVAILABLE_TEAMS, entity(), TeamBalance_CheckAllowedTeams(), TeamBalance_Destroy(), TeamBalance_GetTeamCounts(), TeamBalance_GetTeamFromIndex(), teamplay, ts_max, and ts_min.

Referenced by ReadyCount().

◆ TeamBalanceTeam_GetNumberOfBots()

int TeamBalanceTeam_GetNumberOfBots ( entity team_ent)

Returns the number of bots in a team.

Parameters
[in]team_entTeam entity.
Returns
Number of bots in a team.
Note
You need to call TeamBalance_GetTeamCounts before calling this function.

Definition at line 1202 of file teamplay.qc.

1203{
1204 return team_ent.m_num_bots;
1205}

References entity().

◆ TeamBalanceTeam_GetNumberOfPlayers()

int TeamBalanceTeam_GetNumberOfPlayers ( entity team_ent)

Returns the number of players (both humans and bots) in a team.

Parameters
[in]team_entTeam entity.
Returns
Number of player (both humans and bots) in a team.
Note
You need to call TeamBalance_GetTeamCounts before calling this function.

Definition at line 1197 of file teamplay.qc.

1198{
1199 return team_ent.m_num_players;
1200}

References entity().

Referenced by TeamBalance_AutoBalanceBots(), and TeamBalance_GetLargestTeamIndex().

◆ TeamBalanceTeam_IsAllowed()

bool TeamBalanceTeam_IsAllowed ( entity team_ent)

Returns whether the team is allowed.

Parameters
[in]team_entTeam entity.
Returns
True if team is allowed, false otherwise.

Definition at line 1192 of file teamplay.qc.

1193{
1194 return team_ent && team_ent.m_num_players != TEAM_NOT_ALLOWED;
1195}

References entity(), and TEAM_NOT_ALLOWED.

Referenced by TeamBalance_AutoBalanceBots(), TeamBalance_GetLargestTeamIndex(), and TeamBalance_GetTeamCounts().

Variable Documentation

◆ autocvar_g_forced_team_blue

string autocvar_g_forced_team_blue

Definition at line 42 of file teamplay.qc.

Referenced by Player_GetForcedTeamIndex().

◆ autocvar_g_forced_team_pink

string autocvar_g_forced_team_pink

Definition at line 44 of file teamplay.qc.

Referenced by Player_GetForcedTeamIndex().

◆ autocvar_g_forced_team_red

string autocvar_g_forced_team_red

Definition at line 41 of file teamplay.qc.

Referenced by Player_GetForcedTeamIndex().

◆ autocvar_g_forced_team_yellow

string autocvar_g_forced_team_yellow

Definition at line 43 of file teamplay.qc.

Referenced by Player_GetForcedTeamIndex().

◆ g_team_entities

◆ killindicator_teamchange

int killindicator_teamchange

Definition at line 757 of file teamplay.qc.

◆ m_num_bots

int m_num_bots

Number of bots in a team.

Definition at line 37 of file teamplay.qc.

◆ m_num_owned_items

int m_num_owned_items

Number of items owned by a team.

Definition at line 39 of file teamplay.qc.

Referenced by Team_GetNumberOfTeamsWithOwnedItems(), and Team_GetWinnerTeam_WithOwnedItems().

◆ m_num_players

int m_num_players

Number of players (both humans and bots) in a team.

Definition at line 35 of file teamplay.qc.

◆ m_num_players_alive

int m_num_players_alive

Number of alive players in a team.

Definition at line 38 of file teamplay.qc.

Referenced by Team_GetNumberOfAliveTeams(), and Team_GetWinnerAliveTeam().

◆ m_num_players_net

int m_num_players_net

.m_num_players but excluding bots that would leave if a human joined their team.

Definition at line 36 of file teamplay.qc.

◆ m_team_balance_state

int m_team_balance_state

Holds the state of the team balance data entity.

Definition at line 31 of file teamplay.qc.

◆ m_team_balance_team

entity m_team_balance_team[NUM_TEAMS]

???

Definition at line 32 of file teamplay.qc.

◆ m_team_score

float m_team_score

The score of the team.

Definition at line 34 of file teamplay.qc.

◆ remove_countdown

entity remove_countdown

Definition at line 605 of file teamplay.qc.

Referenced by Remove_Countdown(), and TeamBalance_RemoveExcessPlayers().

◆ TEAM_NOT_ALLOWED

const int TEAM_NOT_ALLOWED = -1

Indicates that the player is not allowed to join a team.

Definition at line 29 of file teamplay.qc.

Referenced by TeamBalance_BanTeamsExcept(), TeamBalance_CheckAllowedTeams(), TeamBalance_IsTeamAllowedInternal(), and TeamBalanceTeam_IsAllowed().