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

1235{
1236 if (IS_DEAD(player))
1237 {
1238 return;
1239 }
1240 if (MUTATOR_CALLHOOK(Player_ChangeTeamKill, player) == true)
1241 {
1242 return;
1243 }
1244 Damage(player, player, player, 100000, DEATH_TEAMCHANGE.m_id, DMG_NOWEP,
1245 player.origin, '0 0 0');
1246}
#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:483
#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 1221 of file teamplay.qc.

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

331{
332 //PrintToChatAll(sprintf("MoveToTeam: %s, %f", client.netname, team_index));
333 int lockteams_backup = lockteams; // backup any team lock
334 lockteams = 0; // disable locked teams
335 if (!SetPlayerTeam(client, team_index, type))
336 {
337 lockteams = lockteams_backup; // restore the team lock
338 return false;
339 }
340 lockteams = lockteams_backup; // restore the team lock
341 return true;
342}
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_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 379 of file teamplay.qc.

380{
382 {
383 if (IS_REAL_CLIENT(player)) // only players, not bots
384 {
386 {
387 player.team_forced = autocvar_g_campaign_forceteam;
388 }
389 else
390 {
391 player.team_forced = TEAM_FORCE_DEFAULT;
392 }
393 }
394 }
396 {
397 player.team_forced = 1;
398 }
400 {
401 player.team_forced = 2;
402 }
404 {
405 player.team_forced = 3;
406 }
408 {
409 player.team_forced = 4;
410 }
411 else
412 {
414 {
415 case "red":
416 {
417 player.team_forced = 1;
418 break;
419 }
420 case "blue":
421 {
422 player.team_forced = 2;
423 break;
424 }
425 case "yellow":
426 {
427 player.team_forced = 3;
428 break;
429 }
430 case "pink":
431 {
432 player.team_forced = 4;
433 break;
434 }
435 case "spectate":
436 case "spectator":
437 {
438 player.team_forced = TEAM_FORCE_SPECTATOR;
439 break;
440 }
441 default:
442 {
443 player.team_forced = TEAM_FORCE_DEFAULT;
444 break;
445 }
446 }
447 }
448 if (!teamplay && Player_HasRealForcedTeam(player))
449 {
450 player.team_forced = TEAM_FORCE_DEFAULT;
451 }
452}
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:344
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 349 of file teamplay.qc.

350{
351 return player.team_forced;
352}

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

345{
346 return player.team_forced > TEAM_FORCE_DEFAULT;
347}

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

355{
356 switch (team_index)
357 {
360 {
361 player.team_forced = team_index;
362 break;
363 }
364 default:
365 {
366 if (!Team_IsValidIndex(team_index))
367 {
368 LOG_FATAL("Invalid team index.");
369 }
370 else
371 {
372 player.team_forced = team_index;
373 break;
374 }
375 }
376 }
377}
#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 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}
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(), 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 1335 of file teamplay.qc.

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

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

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 {
298 PlayerScore_Clear(player);
299
300 if (!IS_BOT_CLIENT(player))
302
303 if (team_index != -1)
304 {
305 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(player.team, INFO_JOIN_PLAY_TEAM), player.netname);
306 player.team_selected = team_index; // no autoselect in Join()
307
308 if (warmup_stage)
309 ReadyCount(); // teams might be balanced now
310 }
311 }
312
313 if (team_index == -1)
314 {
315 if (autocvar_sv_maxidle_playertospectator > 0 && CS(player).idlekick_lasttimeleft)
316 {
317 // this done here so it happens even when manually speccing during the countdown
318 Kill_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CPID_IDLING);
319 CS(player).idlekick_lasttimeleft = 0;
320 }
321 else if (!CS(player).just_joined && player.frags != FRAGS_SPECTATOR)
322 {
323 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, player.netname);
324 }
325 }
326
327 return true;
328}
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:694
#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:1080
void LogTeamChange(float player_id, float team_number, int type)
Definition teamplay.qc:1221
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:1234
#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 1329 of file teamplay.qc.

1330{
1331 if (!teamplay)
1332 SetPlayerColors(player, new_color);
1333}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1275{
1276 return TeamBalance_GetTeamFromIndex(balance, Team_TeamToIndex(team_num));
1277}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1290{
1291 return team_ent.m_num_bots;
1292}

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

1285{
1286 return team_ent.m_num_players;
1287}

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

1280{
1281 return team_ent && team_ent.m_num_players != TEAM_NOT_ALLOWED;
1282}

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 828 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 676 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().