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 KillPlayerForTeamChange (entity player)
 Kills player as a result of team change.
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.
void Player_DetermineForcedTeam (entity player)
 Determines the forced team of the player using current global config.
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, 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().

◆ KillPlayerForTeamChange()

void KillPlayerForTeamChange ( entity player)

Kills player as a result of team change.

Parameters
[in,out]playerPlayer to kill.

Definition at line 1232 of file teamplay.qc.

1233{
1234 if (IS_DEAD(player))
1235 {
1236 return;
1237 }
1238 if (MUTATOR_CALLHOOK(Player_ChangeTeamKill, player) == true)
1239 {
1240 return;
1241 }
1242 Damage(player, player, player, 100000, DEATH_TEAMCHANGE.m_id, DMG_NOWEP,
1243 player.origin, '0 0 0');
1244}
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
#define IS_DEAD(s)
Definition player.qh:244
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition damage.qc:493
#define DMG_NOWEP
Definition damage.qh:104

References Damage(), DMG_NOWEP, entity(), IS_DEAD, and MUTATOR_CALLHOOK.

Referenced by SetPlayerTeam().

◆ LogTeamChange()

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

Definition at line 1219 of file teamplay.qc.

1220{
1222 {
1223 return;
1224 }
1225 if (player_id < 1)
1226 {
1227 return;
1228 }
1229 GameLogEcho(sprintf(":team:%d:%d:%d", player_id, team_number, type));
1230}
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 328 of file teamplay.qc.

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

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

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

◆ Player_DetermineForcedTeam()

void Player_DetermineForcedTeam ( entity player)

Determines the forced team of the player using current global config.

Parameters
[in,out]playerPlayer to adjust.

Definition at line 377 of file teamplay.qc.

378{
380 {
381 if (IS_REAL_CLIENT(player)) // only players, not bots
382 {
384 {
385 player.team_forced = autocvar_g_campaign_forceteam;
386 }
387 else
388 {
389 player.team_forced = TEAM_FORCE_DEFAULT;
390 }
391 }
392 }
394 {
395 player.team_forced = 1;
396 }
398 {
399 player.team_forced = 2;
400 }
402 {
403 player.team_forced = 3;
404 }
406 {
407 player.team_forced = 4;
408 }
409 else
410 {
412 {
413 case "red":
414 {
415 player.team_forced = 1;
416 break;
417 }
418 case "blue":
419 {
420 player.team_forced = 2;
421 break;
422 }
423 case "yellow":
424 {
425 player.team_forced = 3;
426 break;
427 }
428 case "pink":
429 {
430 player.team_forced = 4;
431 break;
432 }
433 case "spectate":
434 case "spectator":
435 {
436 player.team_forced = TEAM_FORCE_SPECTATOR;
437 break;
438 }
439 default:
440 {
441 player.team_forced = TEAM_FORCE_DEFAULT;
442 break;
443 }
444 }
445 }
446 if (!teamplay && Player_HasRealForcedTeam(player))
447 {
448 player.team_forced = TEAM_FORCE_DEFAULT;
449 }
450}
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:1047
string autocvar_g_forced_team_red
Definition teamplay.qc:41
bool Player_HasRealForcedTeam(entity player)
Returns whether player has real forced team.
Definition teamplay.qc:342
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
bool teamplay
Definition teams.qh:59
#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, Player_HasRealForcedTeam(), PlayerInList(), TEAM_FORCE_DEFAULT, TEAM_FORCE_SPECTATOR, Team_IsValidIndex(), and teamplay.

Referenced by ClientConnect().

◆ 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 347 of file teamplay.qc.

348{
349 return player.team_forced;
350}

References entity().

Referenced by ClientCommand_clientversion(), ClientCommand_selectteam(), GameCommand_moveplayer(), joinAllowed(), and PlayerPreThink().

◆ 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 342 of file teamplay.qc.

343{
344 return player.team_forced > TEAM_FORCE_DEFAULT;
345}

References entity(), and TEAM_FORCE_DEFAULT.

Referenced by Player_DetermineForcedTeam(), ShowTeamSelection(), shuffleteams(), TeamBalance_GetTeamCounts(), and TeamBalance_JoinBestTeam().

◆ 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 352 of file teamplay.qc.

353{
354 switch (team_index)
355 {
358 {
359 player.team_forced = team_index;
360 break;
361 }
362 default:
363 {
364 if (!Team_IsValidIndex(team_index))
365 {
366 LOG_FATAL("Invalid team index.");
367 }
368 else
369 {
370 player.team_forced = team_index;
371 break;
372 }
373 }
374 }
375}
#define LOG_FATAL(...)
Definition log.qh:50

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

Referenced by GameCommand_moveplayer(), 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 player.team = -1;
254 }
255 else
256 {
257 SetPlayerColors(player, new_team - 1);
258 }
259 MUTATOR_CALLHOOK(Player_ChangedTeam, player, old_index, index);
260 return true;
261}
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

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

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 267 of file teamplay.qc.

268{
269 int numplayersqueued = 0;
270
271 FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != this
272 && (checkspecificteam ? it.wants_join > 0 : it.wants_join),
273 {
274 LOG_DEBUGF("Player %s is waiting to join team %d", it.netname, it.wants_join);
275 ++numplayersqueued;
276 if (numplayersqueued >= AVAILABLE_TEAMS - 1)
277 return true;
278 });
279
280 LOG_DEBUG("No players waiting to join.");
281 return false;
282}
#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 1333 of file teamplay.qc.

1334{
1336 return false;
1337
1338 int human_clients = 0;
1340 {
1341 if (++human_clients > 1)
1342 {
1343 if (TeamBalance_AreEqual(client, true))
1344 return true;
1345 if (team_index <= 0)
1346 return false; // auto-select will join a smaller team
1347
1348 entity balance = TeamBalance_CheckAllowedTeams(client);
1349 TeamBalance_GetTeamCounts(balance, client);
1350 if ((Team_IndexToBit(team_index) & TeamBalance_FindBestTeams(balance, client, false)) == 0)
1351 {
1352 TeamBalance_Destroy(balance);
1353 return true;
1354 }
1355 TeamBalance_Destroy(balance);
1356 return false;
1357 }
1358 });
1359 return false;
1360}
bool warmup_stage
Definition main.qh:120
void TeamBalance_Destroy(entity balance)
Destroy the team balance entity.
Definition teamplay.qc:597
void TeamBalance_GetTeamCounts(entity balance, entity ignore)
Counts the number of players and various other information about each team.
Definition teamplay.qc:827
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings.
Definition teamplay.qc:488
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:1002
bool TeamBalance_AreEqual(entity ignore, bool would_leave)
Definition teamplay.qc:654
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 675 of file teamplay.qc.

676{
677 if(this.lifetime <= 0 || TeamBalance_AreEqual(NULL, false))
678 {
679 if(this.lifetime <= 0)
680 {
681 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_REMOVE, playername(remove_countdown.enemy.netname, remove_countdown.enemy.team, true));
682 PutObserverInServer(remove_countdown.enemy, true, true);
683 }
684
685 Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_REMOVE);
686
687 delete(this);
689
690 TeamBalance_RemoveExcessPlayers(NULL); // Check again for excess players in case someone also left while in countdown
691 return;
692 }
693
694 --this.lifetime;
695 this.nextthink = time + 1;
696}
float lifetime
Definition powerups.qc:23
string playername(string thename, int teamid, bool team_colorize)
Definition util.qc:2178
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:695
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:674
void TeamBalance_RemoveExcessPlayers(entity ignore)
Definition teamplay.qc:700

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 284 of file teamplay.qc.

285{
286 int old_team_index = Entity_GetTeamIndex(player);
287
288 if (!Player_SetTeamIndex(player, team_index))
289 return false;
290
291 LogTeamChange(player.playerid, player.team, type);
292
293 if (team_index != old_team_index)
294 {
296 PlayerScore_Clear(player);
297
298 if (!IS_BOT_CLIENT(player))
300
301 if (team_index != -1)
302 {
303 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(player.team, INFO_JOIN_PLAY_TEAM), player.netname);
304 player.team_selected = team_index; // no autoselect in Join()
305
306 if (warmup_stage)
307 ReadyCount(); // teams might be balanced now
308 }
309 }
310
311 if (team_index == -1)
312 {
313 if (autocvar_sv_maxidle_playertospectator > 0 && CS(player).idlekick_lasttimeleft)
314 {
315 // this done here so it happens even when manually speccing during the countdown
316 Kill_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CPID_IDLING);
317 CS(player).idlekick_lasttimeleft = 0;
318 }
319 else if (!CS(player).just_joined && player.frags != FRAGS_SPECTATOR)
320 {
321 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, player.netname);
322 }
323 }
324
325 return true;
326}
const int FRAGS_SPECTATOR
Definition constants.qh:4
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:693
#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:1078
void LogTeamChange(float player_id, float team_number, int type)
Definition teamplay.qc:1219
bool Player_SetTeamIndex(entity player, int index)
Sets the team of the player using its index.
Definition teamplay.qc:239
void KillPlayerForTeamChange(entity player)
Kills player as a result of team change.
Definition teamplay.qc:1232
#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(), entity(), Entity_GetTeamIndex(), FRAGS_SPECTATOR, IS_BOT_CLIENT, just_joined, Kill_Notification(), KillPlayerForTeamChange(), 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 1327 of file teamplay.qc.

1328{
1329 if (!teamplay)
1330 SetPlayerColors(player, new_color);
1331}

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 654 of file teamplay.qc.

655{
656 entity balance = TeamBalance_CheckAllowedTeams(ignore);
657 TeamBalance_GetTeamCounts(balance, ignore);
658
659 int prev_size = 0, i = 1;
660
661 for (; i <= AVAILABLE_TEAMS; ++i)
662 {
663 entity team_ent = TeamBalance_GetTeamFromIndex(balance, i);
664 int team_size = would_leave ? team_ent.m_num_players_net : team_ent.m_num_players;
665 if (i > 1 && team_size != prev_size)
666 break;
667 prev_size = team_size;
668 }
669
670 TeamBalance_Destroy(balance);
671 return i > AVAILABLE_TEAMS;
672}
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:1263

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 1078 of file teamplay.qc.

1079{
1080 // checks disabled because we always want auto-balanced bots
1081 //if (!autocvar_g_balance_teams_prevent_imbalance)
1082 // return;
1083
1084 if (intermission_running) return;
1085
1088 int smallest_team_index = 0;
1089 int smallest_team_player_count = 0;
1090 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1091 {
1092 entity team_ = TeamBalance_GetTeamFromIndex(balance, i);
1093 if (!TeamBalanceTeam_IsAllowed(team_))
1094 {
1095 continue;
1096 }
1097 int playercount = TeamBalanceTeam_GetNumberOfPlayers(team_);
1098 if (smallest_team_index == 0)
1099 {
1100 smallest_team_index = i;
1101 smallest_team_player_count = playercount;
1102 }
1103 else if (playercount < smallest_team_player_count)
1104 {
1105 smallest_team_index = i;
1106 smallest_team_player_count = playercount;
1107 }
1108 }
1109 //PrintToChatAll(sprintf("Smallest team: %f", smallest_team_index));
1110 //PrintToChatAll(sprintf("Smallest team players: %f", smallest_team_player_count));
1111 entity switchable_bot = NULL;
1112 int teams = BITS(AVAILABLE_TEAMS);
1113 while (teams != 0)
1114 {
1115 int largest_team_index = TeamBalance_GetLargestTeamIndex(balance,
1116 teams);
1117 if (smallest_team_index == largest_team_index)
1118 {
1119 TeamBalance_Destroy(balance);
1120 return;
1121 }
1122 entity largest_team = TeamBalance_GetTeamFromIndex(balance,
1123 largest_team_index);
1124 int largest_team_player_count = TeamBalanceTeam_GetNumberOfPlayers(
1125 largest_team);
1126 if (largest_team_player_count - smallest_team_player_count < 2)
1127 {
1128 TeamBalance_Destroy(balance);
1129 return;
1130 }
1131 //PrintToChatAll(sprintf("Largest team: %f", largest_team_index));
1132 //PrintToChatAll(sprintf("Largest team players: %f", largest_team_player_count));
1133 switchable_bot = TeamBalance_GetPlayerForTeamSwitch(largest_team_index,
1134 smallest_team_index, true);
1135 if (switchable_bot != NULL)
1136 {
1137 break;
1138 }
1139 teams &= ~Team_IndexToBit(largest_team_index);
1140 }
1141 TeamBalance_Destroy(balance);
1142 if (switchable_bot == NULL)
1143 {
1144 //PrintToChatAll("No bot found after searching through all the teams");
1145 return;
1146 }
1147 SetPlayerTeam(switchable_bot, smallest_team_index, TEAM_CHANGE_AUTO);
1148}
#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:1277
int TeamBalanceTeam_GetNumberOfPlayers(entity team_ent)
Returns the number of players (both humans and bots) in a team.
Definition teamplay.qc:1282
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:1150
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:1180
@ 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 1252 of file teamplay.qc.

1253{
1254 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1255 {
1256 if (i != index)
1257 {
1258 balance.m_team_balance_team[i - 1].m_num_players = TEAM_NOT_ALLOWED;
1259 }
1260 }
1261}
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 488 of file teamplay.qc.

489{
490 entity balance = spawn();
491 for (int i = 0; i < AVAILABLE_TEAMS; ++i)
492 {
493 entity team_ent = balance.m_team_balance_team[i] = spawn();
494 team_ent.m_team_score = g_team_entities[i].m_team_score;
495 team_ent.m_num_players = teamplay_bitmask & BIT(i) ? 0 : TEAM_NOT_ALLOWED;
496 }
498 balance.nextthink = time;
499
500 // TODO: Balance quantity of bots across > 2 teams when bot_vs_human is set (and remove next line)
501 if (autocvar_bot_vs_human && AVAILABLE_TEAMS == 2 && for_whom)
502 {
503 if (autocvar_bot_vs_human > 0)
504 {
505 // find last team available
506 if (IS_BOT_CLIENT(for_whom))
507 {
508 if (TeamBalance_IsTeamAllowedInternal(balance, 4))
509 {
510 TeamBalance_BanTeamsExcept(balance, 4);
511 }
512 else if (TeamBalance_IsTeamAllowedInternal(balance, 3))
513 {
514 TeamBalance_BanTeamsExcept(balance, 3);
515 }
516 else
517 {
518 TeamBalance_BanTeamsExcept(balance, 2);
519 }
520 // no further cases, we know at least 2 teams exist
521 }
522 else
523 {
524 if (TeamBalance_IsTeamAllowedInternal(balance, 1))
525 {
526 TeamBalance_BanTeamsExcept(balance, 1);
527 }
528 else if (TeamBalance_IsTeamAllowedInternal(balance, 2))
529 {
530 TeamBalance_BanTeamsExcept(balance, 2);
531 }
532 else
533 {
534 TeamBalance_BanTeamsExcept(balance, 3);
535 }
536 // no further cases, bots have one of the teams
537 }
538 }
539 else
540 {
541 // find first team available
542 if (IS_BOT_CLIENT(for_whom))
543 {
544 if (TeamBalance_IsTeamAllowedInternal(balance, 1))
545 {
546 TeamBalance_BanTeamsExcept(balance, 1);
547 }
548 else if (TeamBalance_IsTeamAllowedInternal(balance, 2))
549 {
550 TeamBalance_BanTeamsExcept(balance, 2);
551 }
552 else
553 {
554 TeamBalance_BanTeamsExcept(balance, 3);
555 }
556 // no further cases, we know at least 2 teams exist
557 }
558 else
559 {
560 if (TeamBalance_IsTeamAllowedInternal(balance, 4))
561 {
562 TeamBalance_BanTeamsExcept(balance, 4);
563 }
564 else if (TeamBalance_IsTeamAllowedInternal(balance, 3))
565 {
566 TeamBalance_BanTeamsExcept(balance, 3);
567 }
568 else
569 {
570 TeamBalance_BanTeamsExcept(balance, 2);
571 }
572 // no further cases, bots have one of the teams
573 }
574 }
575 }
576
577 if (!for_whom)
578 {
579 balance.m_team_balance_state = TEAM_BALANCE_TEAMS_CHECKED;
580 return balance;
581 }
582
583 // if player has a forced team, ONLY allow that one
584 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
585 {
586 if (for_whom.team_forced == i &&
588 {
589 TeamBalance_BanTeamsExcept(balance, i);
590 break;
591 }
592 }
593 balance.m_team_balance_state = TEAM_BALANCE_TEAMS_CHECKED;
594 return balance;
595}
#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:1252
bool TeamBalance_IsTeamAllowedInternal(entity balance, int index)
Returns whether the team change to the specified team is allowed.
Definition teamplay.qc:1246
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, setthink, spawn, TEAM_BALANCE_TEAMS_CHECKED, TEAM_NOT_ALLOWED, TeamBalance_BanTeamsExcept(), TeamBalance_Destroy(), TeamBalance_IsTeamAllowedInternal(), teamplay_bitmask, and time.

Referenced by adjust_respawntime(), bot_removefromlargestteam(), bot_setnameandstuff(), ClientCommand_selectteam(), 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_index_b,
bool use_score )

Compares two teams for the purposes of game balance.

Parameters
[in]team_aFirst team.
[in]team_bSecond 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 1292 of file teamplay.qc.

1293{
1294 if (team_a == team_b)
1295 return TEAMS_COMPARE_EQUAL;
1296
1297 if (team_a.m_num_players_net < team_b.m_num_players_net)
1298 return TEAMS_COMPARE_LESS;
1299 if (team_a.m_num_players_net > team_b.m_num_players_net)
1300 return TEAMS_COMPARE_GREATER;
1301 if (!use_score)
1302 return TEAMS_COMPARE_EQUAL;
1303
1304// Compare skill and score together as team strength.
1305 // Teams without players have 0 skill_mu but may have a score which must still apply.
1306 bool use_skill = team_a.m_skill_var && team_b.m_skill_var && autocvar_g_balance_teams_skill
1307 // z-score, <= threshold SDs means difference isn't significant, formula squared for perf
1308 && (((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);
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;
1311 // Early in the match scores are ~random due to too few samples,
1312 // as the match progresses scores become more reliable indicators of team strength.
1313 // 0.96875 (31/32): if at halftime one team has score 0 and the other has 2 its skill is multiplied by 1.376,
1314 // < 1 so that a team with 1 point is better than one with 0 (but not by much as sample size is small).
1315 // Applying full score ratio to one team is equivalent here to applying half of it to each.
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)
1318 * ((time - game_starttime) / (autocvar_timelimit * 60))**1.5);
1319
1320 if (team_a_strength < team_b_strength)
1321 return TEAMS_COMPARE_LESS;
1322 if (team_a_strength > team_b_strength)
1323 return TEAMS_COMPARE_GREATER;
1324 return TEAMS_COMPARE_EQUAL;
1325}
#define autocvar_timelimit
Definition stats.qh:92
float game_starttime
Definition stats.qh:82
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:265
@ TEAMS_COMPARE_LESS
First team is less than the second one.
Definition teamplay.qh:263
@ TEAMS_COMPARE_EQUAL
Both teams are equal.
Definition teamplay.qh:264

References autocvar_g_balance_teams_skill, autocvar_g_balance_teams_skill_significance_threshold, autocvar_timelimit, entity(), game_starttime, 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 597 of file teamplay.qc.

598{
599 if (balance == NULL)
600 {
601 return;
602 }
603 for (int i = 0; i < AVAILABLE_TEAMS; ++i)
604 {
605 delete(balance.(m_team_balance_team[i]));
606 }
607 delete(balance);
608}

References AVAILABLE_TEAMS, entity(), and NULL.

Referenced by adjust_respawntime(), bot_setnameandstuff(), ClientCommand_selectteam(), 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 962 of file teamplay.qc.

963{
964 if (balance == NULL)
965 {
966 LOG_FATAL("Team balance entity is NULL.");
967 }
968 if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED)
969 {
970 LOG_FATAL("Team balance entity is not initialized.");
971 }
972 // count how many players are in each team
973 if (ignore_player)
974 {
975 TeamBalance_GetTeamCounts(balance, player);
976 }
977 else
978 {
980 }
981 int team_bits = TeamBalance_FindBestTeams(balance, player, true);
982 if (team_bits == 0)
983 {
984 LOG_FATALF("No teams available for %s\n", GetGametype());
985 }
986
987 // don't punish players for UI mistakes by changing teams randomly when already on a best team
988 if (player.team > 0 && (team_bits & Team_TeamToBit(player.team)))
989 return Team_TeamToIndex(player.team);
990
992 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
993 {
994 if (team_bits & Team_IndexToBit(i))
995 {
997 }
998 }
1000}
string GetGametype()
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(), GetGametype(), LOG_FATAL, LOG_FATALF, 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 1002 of file teamplay.qc.

1003{
1004 if (balance == NULL)
1005 LOG_FATAL("Team balance entity is NULL.");
1006 if (balance.m_team_balance_state != TEAM_BALANCE_TEAM_COUNTS_FILLED)
1007 LOG_FATAL("TeamBalance_GetTeamCounts has not been called.");
1008
1009 if (MUTATOR_CALLHOOK(TeamBalance_FindBestTeams, player) == true)
1010 return M_ARGV(1, float);
1011
1012 int team_bits = 0;
1013 int previous_team = 0;
1014 float player_skill_mu = 0, player_skill_var = 0;
1015 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1016 {
1017 if (!TeamBalance_IsTeamAllowedInternal(balance, i))
1018 continue;
1019 if (previous_team == 0)
1020 {
1021 team_bits = Team_IndexToBit(i);
1022 previous_team = i;
1023 continue;
1024 }
1025
1026 entity team_i = TeamBalance_GetTeamFromIndex(balance, i);
1027 entity team_p = TeamBalance_GetTeamFromIndex(balance, previous_team);
1028
1029 // During warmup the "best" team isn't the smallest or weakest one,
1030 // that method puts all the noobs on the same team when players join in the "wrong" order.
1031 // This picks the team(s) whose weighted mean skill differs the most from the joining player's skill,
1032 // unless they're all close enough in which case it picks smaller team(s).
1033 // Warmup-only as it must unbalance team sizes in extreme cases, swaps needed should be minimal.
1035 {
1036 if (!player_skill_mu)
1037 {
1038 player_skill_mu = player.m_skill_mu ? player.m_skill_mu : server_skill_average;
1039 player_skill_var = player.m_skill_var ? player.m_skill_var : (server_skill_average * 0.25)**2;
1040 }
1041 // To determine significance, we'll use a z-score of the inverse-variance weightings
1042 // with an adjustable threshold
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);
1045 bool significant = fabs(team_i_z - team_p_z) > autocvar_g_balance_teams_skill_significance_threshold;
1046 // Do fabs afterwards, since the player can be in the middle of the teams (one z +ve, other -ve)
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))
1051 {
1052 team_bits = Team_IndexToBit(i);
1053 previous_team = i;
1054 }
1055 else if (team_i_z == team_p_z) // rather unlikely
1056 {
1057 team_bits |= Team_IndexToBit(i);
1058 previous_team = i;
1059 }
1060 continue;
1061 }
1062
1063 int compare = TeamBalance_CompareTeamsInternal(team_i, team_p, use_score);
1064 if (compare == TEAMS_COMPARE_LESS)
1065 {
1066 team_bits = Team_IndexToBit(i);
1067 previous_team = i;
1068 }
1069 else if (compare == TEAMS_COMPARE_EQUAL)
1070 {
1071 team_bits |= Team_IndexToBit(i);
1072 previous_team = i;
1073 }
1074 }
1075 return team_bits;
1076}
#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, bool use_score)
Compares two teams for the purposes of game balance.
Definition teamplay.qc:1292
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(), 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_selectteam(), 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 610 of file teamplay.qc.

611{
612 if (balance == NULL)
613 {
614 LOG_FATAL("Team balance entity is NULL.");
615 }
616 if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED)
617 {
618 LOG_FATAL("Team balance entity is not initialized.");
619 }
620 int result = 0;
621 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
622 {
623 if (TeamBalance_IsTeamAllowedInternal(balance, i))
624 {
626 }
627 }
628 return result;
629}

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 1150 of file teamplay.qc.

1151{
1152 int largest_team_index = 0;
1153 int largest_team_player_count = 0;
1154 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1155 {
1156 if (!(Team_IndexToBit(i) & teams))
1157 {
1158 continue;
1159 }
1160 entity team_ = TeamBalance_GetTeamFromIndex(balance, i);
1161 if (!TeamBalanceTeam_IsAllowed(team_))
1162 {
1163 continue;
1164 }
1165 int playercount = TeamBalanceTeam_GetNumberOfPlayers(team_);
1166 if (largest_team_index == 0)
1167 {
1168 largest_team_index = i;
1169 largest_team_player_count = playercount;
1170 }
1171 else if (playercount > largest_team_player_count)
1172 {
1173 largest_team_index = i;
1174 largest_team_player_count = playercount;
1175 }
1176 }
1177 return largest_team_index;
1178}

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 945 of file teamplay.qc.

946{
947 if (balance == NULL)
948 {
949 LOG_FATAL("Team balance entity is NULL.");
950 }
951 if (balance.m_team_balance_state != TEAM_BALANCE_TEAM_COUNTS_FILLED)
952 {
953 LOG_FATAL("TeamBalance_GetTeamCounts has not been called.");
954 }
955 if (!Team_IsValidIndex(index))
956 {
957 LOG_FATALF("Team index is invalid: %f", index);
958 }
959 return balance.m_team_balance_team[index - 1].m_num_players;
960}

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 1180 of file teamplay.qc.

1182{
1184 destination_team_index, is_bot))
1185 {
1186 return M_ARGV(3, entity);
1187 }
1188 entity lowest_player = NULL;
1189 float lowest_score = FLOAT_MAX;
1190 FOREACH_CLIENT(Entity_GetTeamIndex(it) == source_team_index,
1191 {
1192 if (IS_BOT_CLIENT(it) != is_bot)
1193 {
1194 continue;
1195 }
1196 float temp_score = PlayerScore_Get(it, SP_SCORE);
1197 if (temp_score >= lowest_score)
1198 {
1199 continue;
1200 }
1201 //PrintToChatAll(sprintf(
1202 // "Found %s with lowest score, checking allowed teams", it.netname));
1204 if (TeamBalance_IsTeamAllowed(balance, source_team_index))
1205 {
1206 //PrintToChatAll("Allowed");
1207 lowest_player = it;
1208 lowest_score = temp_score;
1209 }
1210 else
1211 {
1212 //PrintToChatAll("Not allowed");
1213 }
1214 TeamBalance_Destroy(balance);
1215 });
1216 return lowest_player;
1217}
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:807

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 1272 of file teamplay.qc.

1273{
1274 return TeamBalance_GetTeamFromIndex(balance, Team_TeamToIndex(team_num));
1275}

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 827 of file teamplay.qc.

828{
829 if (balance == NULL)
830 LOG_FATAL("Team balance entity is NULL.");
831 if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED)
832 LOG_FATAL("Team balance entity is not initialized.");
833
835 {
836 // Mutator has overriden the configuration.
837 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
838 {
839 entity team_ent = TeamBalance_GetTeamFromIndex(balance, i);
840 if (TeamBalanceTeam_IsAllowed(team_ent))
841 {
842 MUTATOR_CALLHOOK(TeamBalance_GetTeamCount, i, ignore);
843 team_ent.m_num_players = M_ARGV(2, float);
844 team_ent.m_num_bots = M_ARGV(3, float);
845 }
846 }
847 }
848 else
849 {
850 float skill_weight = 0, skill_weight_sum = 0;
852 // Manually count all players.
853 FOREACH_CLIENT(true,
854 {
855 if (it.m_skill_mu && it.m_skill_var)
856 {
857 skill_weight = 1 / it.m_skill_var;
858 server_skill_average += it.m_skill_mu * skill_weight;
859 skill_weight_sum += skill_weight;
860 }
861
862 if (it == ignore)
863 continue;
864
865 int team_num = it.killindicator_teamchange > 0 ? it.killindicator_teamchange : it.team;
866 if (team_num <= 0)
867 {
868 if (Player_HasRealForcedTeam(it)) // Do we really need this? Probably not.
869 team_num = Team_IndexToTeam(it.team_forced); // reserve the spot
870 else
871 continue;
872 }
873 if (!Team_IsValidTeam(team_num))
874 continue;
875
876 entity team_ent = TeamBalance_GetTeam(balance, team_num);
877 if (!TeamBalanceTeam_IsAllowed(team_ent))
878 continue;
879
880 team_ent.m_num_players_net = ++team_ent.m_num_players;
881 if (IS_BOT_CLIENT(it))
882 ++team_ent.m_num_bots;
883
884 if (it.m_skill_mu && it.m_skill_var)
885 {
886 // skill_weight calculated above
887 team_ent.m_skill_mu += it.m_skill_mu * skill_weight;
888 team_ent.mass += skill_weight;
889 ++team_ent.count;
890 }
891 });
892
893 // deduct bots that would leave if a player joined their team, distributing the deductions between teams
894 // bones_was_here: not sure if it's safe to apply this to .m_num_players so using .m_num_players_net
895 for (int i = 1, bots_todo = bots_would_leave; bots_todo > 0 && i <= AVAILABLE_TEAMS; ++i)
896 {
897 entity team_ent = TeamBalance_GetTeamFromIndex(balance, i);
898 int to_remove = min(team_ent.m_num_bots, ceil(bots_todo / AVAILABLE_TEAMS));
899 team_ent.m_num_players_net -= to_remove;
900 bots_todo -= to_remove;
901 }
902
903 // Calculate inverse-variance weighted mean skill ratings,
904 // for each unranked client use a fraction of the mean of all clients' skill ratings.
907 : 1000;
908 float server_skill_average_var = 0; skill_weight = 0; // will only be calculated once, if needed
909 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
910 {
911 entity team_ent = TeamBalance_GetTeamFromIndex(balance, i);
912 int unranked_clients = team_ent.m_num_players_net - team_ent.count;
913 if (unranked_clients)
914 {
915 if (!server_skill_average_var)
916 {
917 server_skill_average_var = (server_skill_average * 0.25)**2;
918 skill_weight = 1 / server_skill_average_var;
919 }
920 team_ent.m_skill_mu += (server_skill_average * skill_weight) * unranked_clients;
921 team_ent.mass += skill_weight * unranked_clients;
922 }
923 if (team_ent.mass)
924 {
925 // .mass is storing the arithmetic sum of members' inverse-variance weightings
926 // Hence the team's mean is: team_ent.m_skill_mu / team_ent.mass
927 // Hence the team's variance is: 1 / team_ent.mass
928 team_ent.m_skill_mu /= team_ent.mass;
929 team_ent.m_skill_var = 1 / team_ent.mass;
930 }
931 }
932 }
933
934 // if the player who has a forced team has not joined yet, reserve the spot
937 {
939 if (team_ent.m_num_players == team_ent.m_num_bots)
940 ++team_ent.m_num_players;
941 }
942 balance.m_team_balance_state = TEAM_BALANCE_TEAM_COUNTS_FILLED;
943}
int bots_would_leave
how many bots would leave so humans can replace them
Definition api.qh:101
float ceil(float f)
float min(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:1272
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_HasRealForcedTeam(), server_skill_average, TEAM_BALANCE_TEAM_COUNTS_FILLED, TEAM_BALANCE_UNINITIALIZED, Team_IndexToTeam(), Team_IsValidIndex(), Team_IsValidTeam(), TeamBalance_GetTeam(), TeamBalance_GetTeamCounts(), TeamBalance_GetTeamFromIndex(), and TeamBalanceTeam_IsAllowed().

Referenced by adjust_respawntime(), bot_removefromlargestteam(), bot_setnameandstuff(), ClientCommand_selectteam(), 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 1263 of file teamplay.qc.

1264{
1265 if (!Team_IsValidIndex(index))
1266 {
1267 LOG_FATALF("Index is invalid: %f", index);
1268 }
1269 return balance.m_team_balance_team[index - 1];
1270}

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 807 of file teamplay.qc.

808{
809 if (balance == NULL)
810 {
811 LOG_FATAL("Team balance entity is NULL.");
812 }
813 if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED)
814 {
815 LOG_FATAL("Team balance entity is not initialized.");
816 }
817 if (!Team_IsValidIndex(index))
818 {
819 LOG_FATALF("Team index is invalid: %f",
820 index);
821 }
822 return TeamBalance_IsTeamAllowedInternal(balance, index);
823}

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

Referenced by adjust_respawntime(), ClientCommand_selectteam(), 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 1246 of file teamplay.qc.

1247{
1248 return index <= AVAILABLE_TEAMS
1249 && balance.m_team_balance_team[index - 1].m_num_players != TEAM_NOT_ALLOWED;
1250}

References AVAILABLE_TEAMS, entity(), and TEAM_NOT_ALLOWED.

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

◆ 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 452 of file teamplay.qc.

453{
454 //PrintToChatAll(sprintf("TeamBalance_JoinBestTeam: %s", player.netname));
455 if (!teamplay)
456 {
457 return;
458 }
459 if (player.bot_forced_team)
460 {
461 return;
462 }
463 entity balance = TeamBalance_CheckAllowedTeams(player);
464 if (Player_HasRealForcedTeam(player))
465 {
466 int forced_team_index = player.team_forced;
467 bool is_team_allowed = TeamBalance_IsTeamAllowedInternal(balance,
468 forced_team_index);
469 TeamBalance_Destroy(balance);
470 if (!is_team_allowed)
471 {
472 return;
473 }
474 if (!SetPlayerTeam(player, forced_team_index, TEAM_CHANGE_AUTO))
475 {
476 return;
477 }
478 return;
479 }
480 int best_team_index = TeamBalance_FindBestTeam(balance, player, true);
481 TeamBalance_Destroy(balance);
482 if (!SetPlayerTeam(player, best_team_index, TEAM_CHANGE_AUTO))
483 {
484 return;
485 }
486}
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:962

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

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 763 of file teamplay.qc.

764{
765 if (!teamplay)
766 return true;
767
768 int j, teamplayers_deficit = 0, teamplayers_max = 0;
769 entity it, balance = TeamBalance_CheckAllowedTeams(ignore);
770 TeamBalance_GetTeamCounts(balance, ignore);
771
772 for (int j = 1; j <= AVAILABLE_TEAMS; ++j)
773 {
774 it = TeamBalance_GetTeamFromIndex(balance, j);
775 // find the largest team size
776 if (it.m_num_players_net > teamplayers_max)
777 teamplayers_max = it.m_num_players_net;
778 }
779 // find how many players we'd need to join to achieve balanced numbers that way
780 for (j = 1; j <= AVAILABLE_TEAMS; ++j)
781 teamplayers_deficit += teamplayers_max - TeamBalance_GetTeamFromIndex(balance, j).m_num_players_net;
782
783 // first pass: find clients(s) who want to play on a specific team
784 for (j = 1; teamplayers_deficit > 0 && j <= maxclients; ++j)
785 {
786 it = ftoe(j);
787 if (it.wants_join <= 0 || it == ignore) continue;
788 if (TeamBalance_GetTeamFromIndex(balance, it.wants_join).m_num_players_net >= teamplayers_max) continue;
789 Join(it, false);
790 ++TeamBalance_GetTeam(balance, it.team).m_num_players_net;
791 --teamplayers_deficit;
792 }
793
794 // second pass: find clients(s) who want to play on any team
795 for (j = 1; teamplayers_deficit > 0 && j <= maxclients; ++j)
796 {
797 it = ftoe(j);
798 if (it.wants_join >= 0 || it == ignore) continue;
799 Join(it, false);
800 --teamplayers_deficit; // don't need to update .m_num_players_net as we won't read it again
801 }
802
803 TeamBalance_Destroy(balance);
804 return teamplayers_deficit <= 0; // return true if teams are now balanced
805}
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:2069

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 700 of file teamplay.qc.

701{
702 if(AVAILABLE_TEAMS != 2 || autocvar_g_campaign) return;
703
704 entity balance = TeamBalance_CheckAllowedTeams(ignore);
705 TeamBalance_GetTeamCounts(balance, ignore);
706
707 int min = 0;
708
709 for(int i = 1; i <= AVAILABLE_TEAMS; ++i)
710 {
711 int cur = TeamBalance_GetTeamFromIndex(balance, i).m_num_players;
712 if(i == 1 || cur < min)
713 min = cur;
714 }
715
716 for(int tmi = 1; tmi <= AVAILABLE_TEAMS; ++tmi)
717 {
718 int cur = TeamBalance_GetTeamFromIndex(balance, tmi).m_num_players;
719 if(cur > 0 && cur > min) // If this team has excess players
720 {
721 // Get newest player
722 int latest_join = 0;
723 entity latest_join_pl = NULL;
724
726 if(it.team == Team_IndexToTeam(tmi) && CS(it).startplaytime > latest_join)
727 {
728 latest_join = CS(it).startplaytime;
729 latest_join_pl = it;
730 }
731 });
732
733 // Force player to spectate
734 if(latest_join_pl)
735 {
736 // Send player to spectate
738 {
739 // Give a warning before moving to spect
740 if (!remove_countdown)
741 {
744 remove_countdown.nextthink = time;
745 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);
746 }
747 remove_countdown.enemy = latest_join_pl;
749 }
750 else
751 {
752 // Move to spects immediately
753 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_REMOVE, latest_join_pl.netname);
754 PutObserverInServer(latest_join_pl, true, true);
755 }
756 }
757 }
758 }
759
760 TeamBalance_Destroy(balance);
761}
float startplaytime
Definition client.qh:67
#define INGAME(it)
Definition sv_rules.qh:24
void Remove_Countdown(entity this)
Definition teamplay.qc:675
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 631 of file teamplay.qc.

632{
633 if (!teamplay)
634 return 0;
635
636 entity balance = TeamBalance_CheckAllowedTeams(ignore);
637 TeamBalance_GetTeamCounts(balance, ignore);
638
639 int ts_min = 255, ts_max = 0;
640 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
641 {
642 int ts = TeamBalance_GetTeamFromIndex(balance, i).m_num_players;
643 if (ts_min > ts)
644 ts_min = ts;
645 if (ts_max < ts)
646 ts_max = ts;
647 }
648
649 TeamBalance_Destroy(balance);
650
651 return ts_max - ts_min;
652}
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 1287 of file teamplay.qc.

1288{
1289 return team_ent.m_num_bots;
1290}

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 1282 of file teamplay.qc.

1283{
1284 return team_ent.m_num_players;
1285}

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 1277 of file teamplay.qc.

1278{
1279 return team_ent && team_ent.m_num_players != TEAM_NOT_ALLOWED;
1280}

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_DetermineForcedTeam().

◆ autocvar_g_forced_team_pink

string autocvar_g_forced_team_pink

Definition at line 44 of file teamplay.qc.

Referenced by Player_DetermineForcedTeam().

◆ autocvar_g_forced_team_red

string autocvar_g_forced_team_red

Definition at line 41 of file teamplay.qc.

Referenced by Player_DetermineForcedTeam().

◆ autocvar_g_forced_team_yellow

string autocvar_g_forced_team_yellow

Definition at line 43 of file teamplay.qc.

Referenced by Player_DetermineForcedTeam().

◆ g_team_entities

◆ killindicator_teamchange

int killindicator_teamchange

Definition at line 826 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 674 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().