string GetClientVersionMessage(entity this)
{
- if (this.version_mismatch) {
- if(this.version < autocvar_gameversion) {
+ if (CS(this).version_mismatch) {
+ if(CS(this).version < autocvar_gameversion) {
return strcat("This is Xonotic ", autocvar_g_xonoticversion,
"\n^3Your client version is outdated.\n\n\n### YOU WON'T BE ABLE TO PLAY ON THIS SERVER ###\n\n\nPlease update!!!^8");
} else {
#endif
}
-void SetPlayerColors(entity pl, float _color)
+void SetPlayerColors(entity player, float _color)
{
- /*string s;
- s = ftos(cl);
- stuffcmd(pl, strcat("color ", s, " ", s, "\n") );
- pl.team = cl + 1;
- //pl.clientcolors = pl.clientcolors - (pl.clientcolors & 15) + cl;
- pl.clientcolors = 16*cl + cl;*/
-
- float pants, shirt;
- pants = _color & 0x0F;
- shirt = _color & 0xF0;
-
-
- if(teamplay) {
- setcolor(pl, 16*pants + pants);
- } else {
- setcolor(pl, shirt + pants);
+ float pants = _color & 0x0F;
+ float shirt = _color & 0xF0;
+ if (teamplay)
+ {
+ setcolor(player, 16 * pants + pants);
+ }
+ else
+ {
+ setcolor(player, shirt + pants);
}
}
{
if (player.team == teamnum)
{
+ // This is important when players join the game and one of their color
+ // matches the team color while other doesn't. For example [BOT]Lion.
+ SetPlayerColors(player, teamnum - 1);
return true;
}
if (MUTATOR_CALLHOOK(Player_ChangeTeam, player, Team_TeamToNumber(
return true;
}
-void SetPlayerTeam(entity pl, float t, float s, float noprint)
+void SetPlayerTeam(entity player, int destinationteam, int sourceteam, bool noprint)
{
- if (t == s)
+ int teamnum = Team_NumberToTeam(destinationteam);
+ if (!SetPlayerTeamSimple(player, teamnum))
{
return;
}
- float teamnum = Team_NumberToTeam(t);
- SetPlayerTeamSimple(pl, teamnum);
- LogTeamchange(pl.playerid, pl.team, 3); // log manual team join
+ LogTeamchange(player.playerid, player.team, 3); // log manual team join
if (noprint)
{
return;
}
- bprint(playername(pl, false), "^7 has changed from ", Team_NumberToColoredFullName(s), "^7 to ", Team_NumberToColoredFullName(t), "\n");
+ bprint(playername(player, false), "^7 has changed from ", Team_NumberToColoredFullName(sourceteam), "^7 to ", Team_NumberToColoredFullName(destinationteam), "\n");
}
// set c1...c4 to show what teams are allowed
int teams_mask = 0;
c1 = c2 = c3 = c4 = -1;
- cb1 = cb2 = cb3 = cb4 = 0;
+ numbotsteam1 = numbotsteam2 = numbotsteam3 = numbotsteam4 = 0;
string teament_name = string_null;
// teams that are allowed will now have their player counts stored in c1...c4
void GetTeamCounts(entity ignore)
{
- float value, bvalue;
- // now count how many players are on each team already
-
- // FIXME: also find and memorize the lowest-scoring bot on each team (in case players must be shuffled around)
- // also remember the lowest-scoring player
-
- FOREACH_CLIENT(true, LAMBDA(
- float t;
- if(IS_PLAYER(it) || it.caplayer)
- t = it.team;
- else if(it.team_forced > 0)
- t = it.team_forced; // reserve the spot
- else
- continue;
- if(it != ignore)// && it.netname != "")
+ if (MUTATOR_CALLHOOK(GetTeamCounts) == true)
+ {
+ if (c1 >= 0)
{
+ MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_1, ignore, c1, numbotsteam1,
+ lowestplayerteam1, lowestbotteam1);
+ c1 = M_ARGV(2, float);
+ numbotsteam1 = M_ARGV(3, float);
+ lowestplayerteam1 = M_ARGV(4, entity);
+ lowestbotteam1 = M_ARGV(5, entity);
+ }
+ if (c2 >= 0)
+ {
+ MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_2, ignore, c2, numbotsteam2,
+ lowestplayerteam2, lowestbotteam2);
+ c2 = M_ARGV(2, float);
+ numbotsteam2 = M_ARGV(3, float);
+ lowestplayerteam2 = M_ARGV(4, entity);
+ lowestbotteam2 = M_ARGV(5, entity);
+ }
+ if (c3 >= 0)
+ {
+ MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_3, ignore, c3, numbotsteam3,
+ lowestplayerteam3, lowestbotteam3);
+ c3 = M_ARGV(2, float);
+ numbotsteam3 = M_ARGV(3, float);
+ lowestplayerteam3 = M_ARGV(4, entity);
+ lowestbotteam3 = M_ARGV(5, entity);
+ }
+ if (c4 >= 0)
+ {
+ MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_4, ignore, c4, numbotsteam4,
+ lowestplayerteam4, lowestbotteam4);
+ c4 = M_ARGV(2, float);
+ numbotsteam4 = M_ARGV(3, float);
+ lowestplayerteam4 = M_ARGV(4, entity);
+ lowestbotteam4 = M_ARGV(5, entity);
+ }
+ }
+ else
+ {
+ float value, bvalue;
+ // now count how many players are on each team already
+ float lowestplayerscore1 = FLOAT_MAX;
+ float lowestbotscore1 = FLOAT_MAX;
+ float lowestplayerscore2 = FLOAT_MAX;
+ float lowestbotscore2 = FLOAT_MAX;
+ float lowestplayerscore3 = FLOAT_MAX;
+ float lowestbotscore3 = FLOAT_MAX;
+ float lowestplayerscore4 = FLOAT_MAX;
+ float lowestbotscore4 = FLOAT_MAX;
+ FOREACH_CLIENT(true, LAMBDA(
+ float t;
+ if (IS_PLAYER(it) || it.caplayer)
+ {
+ t = it.team;
+ }
+ else if (it.team_forced > 0)
+ {
+ t = it.team_forced; // reserve the spot
+ }
+ else
+ {
+ continue;
+ }
+ if (it == ignore)
+ {
+ continue;
+ }
value = PlayerValue(it);
- if(IS_BOT_CLIENT(it))
+ if (IS_BOT_CLIENT(it))
+ {
bvalue = value;
+ }
else
+ {
bvalue = 0;
- if(t == NUM_TEAM_1)
+ }
+ if (value == 0)
{
- if(c1 >= 0)
- {
- c1 = c1 + value;
- cb1 = cb1 + bvalue;
- }
+ continue;
}
- if(t == NUM_TEAM_2)
+ switch (t)
{
- if(c2 >= 0)
+ case NUM_TEAM_1:
{
- c2 = c2 + value;
- cb2 = cb2 + bvalue;
+ if (c1 < 0)
+ {
+ break;
+ }
+ c1 += value;
+ numbotsteam1 += bvalue;
+ float tempscore = PlayerScore_Get(it, SP_SCORE);
+ if (!bvalue)
+ {
+ if (tempscore < lowestplayerscore1)
+ {
+ lowestplayerteam1 = it;
+ lowestplayerscore1 = tempscore;
+ }
+ break;
+ }
+ if (tempscore < lowestbotscore1)
+ {
+ lowestbotteam1 = it;
+ lowestbotscore1 = tempscore;
+ }
+ break;
}
- }
- if(t == NUM_TEAM_3)
- {
- if(c3 >= 0)
+ case NUM_TEAM_2:
{
- c3 = c3 + value;
- cb3 = cb3 + bvalue;
+ if (c2 < 0)
+ {
+ break;
+ }
+ c2 += value;
+ numbotsteam2 += bvalue;
+ float tempscore = PlayerScore_Get(it, SP_SCORE);
+ if (!bvalue)
+ {
+ if (tempscore < lowestplayerscore2)
+ {
+ lowestplayerteam2 = it;
+ lowestplayerscore2 = tempscore;
+ }
+ break;
+ }
+ if (tempscore < lowestbotscore2)
+ {
+ lowestbotteam2 = it;
+ lowestbotscore2 = tempscore;
+ }
+ break;
}
- }
- if(t == NUM_TEAM_4)
- {
- if(c4 >= 0)
+ case NUM_TEAM_3:
+ {
+ if (c3 < 0)
+ {
+ break;
+ }
+ c3 += value;
+ numbotsteam3 += bvalue;
+ float tempscore = PlayerScore_Get(it, SP_SCORE);
+ if (!bvalue)
+ {
+ if (tempscore < lowestplayerscore3)
+ {
+ lowestplayerteam3 = it;
+ lowestplayerscore3 = tempscore;
+ }
+ break;
+ }
+ if (tempscore < lowestbotscore3)
+ {
+ lowestbotteam3 = it;
+ lowestbotscore3 = tempscore;
+ }
+ break;
+ }
+ case NUM_TEAM_4:
{
- c4 = c4 + value;
- cb4 = cb4 + bvalue;
+ if (c4 < 0)
+ {
+ break;
+ }
+ c4 += value;
+ numbotsteam4 += bvalue;
+ float tempscore = PlayerScore_Get(it, SP_SCORE);
+ if (!bvalue)
+ {
+ if (tempscore < lowestplayerscore4)
+ {
+ lowestplayerteam4 = it;
+ lowestplayerscore4 = tempscore;
+ }
+ break;
+ }
+ if (tempscore < lowestbotscore4)
+ {
+ lowestbotteam4 = it;
+ lowestbotscore4 = tempscore;
+ }
+ break;
}
}
- }
- ));
+ ));
+ }
// if the player who has a forced team has not joined yet, reserve the spot
if(autocvar_g_campaign)
{
switch(autocvar_g_campaign_forceteam)
{
- case 1: if(c1 == cb1) ++c1; break;
- case 2: if(c2 == cb2) ++c2; break;
- case 3: if(c3 == cb3) ++c3; break;
- case 4: if(c4 == cb4) ++c4; break;
+ case 1: if(c1 == numbotsteam1) ++c1; break;
+ case 2: if(c2 == numbotsteam2) ++c2; break;
+ case 3: if(c3 == numbotsteam3) ++c3; break;
+ case 4: if(c4 == numbotsteam4) ++c4; break;
}
}
}
-float TeamSmallerEqThanTeam(float ta, float tb, entity e)
+bool IsTeamSmallerThanTeam(int teama, int teamb, entity e, bool usescore)
{
+ // equal
+ if (teama == teamb)
+ {
+ return false;
+ }
// we assume that CheckAllowedTeams and GetTeamCounts have already been called
- float f;
- float ca = -1, cb = -1, cba = 0, cbb = 0, sa = 0, sb = 0;
+ float numplayersteama = -1, numplayersteamb = -1;
+ float numbotsteama = 0, numbotsteamb = 0;
+ float scoreteama = 0, scoreteamb = 0;
- switch(ta)
+ switch (teama)
{
- case 1: ca = c1; cba = cb1; sa = team1_score; break;
- case 2: ca = c2; cba = cb2; sa = team2_score; break;
- case 3: ca = c3; cba = cb3; sa = team3_score; break;
- case 4: ca = c4; cba = cb4; sa = team4_score; break;
+ case 1: numplayersteama = c1; numbotsteama = numbotsteam1; scoreteama = team1_score; break;
+ case 2: numplayersteama = c2; numbotsteama = numbotsteam2; scoreteama = team2_score; break;
+ case 3: numplayersteama = c3; numbotsteama = numbotsteam3; scoreteama = team3_score; break;
+ case 4: numplayersteama = c4; numbotsteama = numbotsteam4; scoreteama = team4_score; break;
}
- switch(tb)
+ switch (teamb)
{
- case 1: cb = c1; cbb = cb1; sb = team1_score; break;
- case 2: cb = c2; cbb = cb2; sb = team2_score; break;
- case 3: cb = c3; cbb = cb3; sb = team3_score; break;
- case 4: cb = c4; cbb = cb4; sb = team4_score; break;
+ case 1: numplayersteamb = c1; numbotsteamb = numbotsteam1; scoreteamb = team1_score; break;
+ case 2: numplayersteamb = c2; numbotsteamb = numbotsteam2; scoreteamb = team2_score; break;
+ case 3: numplayersteamb = c3; numbotsteamb = numbotsteam3; scoreteamb = team3_score; break;
+ case 4: numplayersteamb = c4; numbotsteamb = numbotsteam4; scoreteamb = team4_score; break;
}
// invalid
- if(ca < 0 || cb < 0)
+ if (numplayersteama < 0 || numplayersteamb < 0)
return false;
- // equal
- if(ta == tb)
+ if ((IS_REAL_CLIENT(e) && bots_would_leave))
+ {
+ numplayersteama -= numbotsteama;
+ numplayersteamb -= numbotsteamb;
+ }
+ if (!usescore)
+ {
+ return numplayersteama < numplayersteamb;
+ }
+ if (numplayersteama < numplayersteamb)
+ {
return true;
-
- if(IS_REAL_CLIENT(e))
+ }
+ if (numplayersteama > numplayersteamb)
{
- if(bots_would_leave)
- {
- ca -= cba * 0.999;
- cb -= cbb * 0.999;
- }
+ return false;
}
-
- // keep teams alive (teams of size 0 always count as smaller, ignoring score)
- if(ca < 1)
- if(cb >= 1)
- return true;
- if(ca >= 1)
- if(cb < 1)
- return false;
-
- // first, normalize
- f = max(ca, cb, 1);
- ca /= f;
- cb /= f;
- f = max(sa, sb, 1);
- sa /= f;
- sb /= f;
-
- // the more we're at the end of the match, the more take scores into account
- f = bound(0, game_completion_ratio * autocvar_g_balance_teams_scorefactor, 1);
- ca += (sa - ca) * f;
- cb += (sb - cb) * f;
-
- return ca <= cb;
+ return scoreteama < scoreteamb;
}
-// returns # of smallest team (1, 2, 3, 4)
-// NOTE: Assumes CheckAllowedTeams has already been called!
-float FindSmallestTeam(entity pl, float ignore_pl)
+bool IsTeamEqualToTeam(int teama, int teamb, entity e, bool usescore)
{
- int totalteams = 0;
- int t = 1; // initialize with a random team?
- if(c4 >= 0) t = 4;
- if(c3 >= 0) t = 3;
- if(c2 >= 0) t = 2;
- if(c1 >= 0) t = 1;
+ // equal
+ if (teama == teamb)
+ {
+ return true;
+ }
+ // we assume that CheckAllowedTeams and GetTeamCounts have already been called
+ float numplayersteama = -1, numplayersteamb = -1;
+ float numbotsteama = 0, numbotsteamb = 0;
+ float scoreteama = 0, scoreteamb = 0;
- // find out what teams are available
- //CheckAllowedTeams();
+ switch (teama)
+ {
+ case 1: numplayersteama = c1; numbotsteama = numbotsteam1; scoreteama = team1_score; break;
+ case 2: numplayersteama = c2; numbotsteama = numbotsteam2; scoreteama = team2_score; break;
+ case 3: numplayersteama = c3; numbotsteama = numbotsteam3; scoreteama = team3_score; break;
+ case 4: numplayersteama = c4; numbotsteama = numbotsteam4; scoreteama = team4_score; break;
+ }
+ switch (teamb)
+ {
+ case 1: numplayersteamb = c1; numbotsteamb = numbotsteam1; scoreteamb = team1_score; break;
+ case 2: numplayersteamb = c2; numbotsteamb = numbotsteam2; scoreteamb = team2_score; break;
+ case 3: numplayersteamb = c3; numbotsteamb = numbotsteam3; scoreteamb = team3_score; break;
+ case 4: numplayersteamb = c4; numbotsteamb = numbotsteam4; scoreteamb = team4_score; break;
+ }
- // make sure there are at least 2 teams to join
- if(c1 >= 0)
- totalteams = totalteams + 1;
- if(c2 >= 0)
- totalteams = totalteams + 1;
- if(c3 >= 0)
- totalteams = totalteams + 1;
- if(c4 >= 0)
- totalteams = totalteams + 1;
+ // invalid
+ if (numplayersteama < 0 || numplayersteamb < 0)
+ return false;
- if((autocvar_bot_vs_human || pl.team_forced > 0) && totalteams == 1)
- totalteams += 1;
+ if ((IS_REAL_CLIENT(e) && bots_would_leave))
+ {
+ numplayersteama -= numbotsteama;
+ numplayersteamb -= numbotsteamb;
+ }
+ if (!usescore)
+ {
+ return numplayersteama == numplayersteamb;
+ }
+ if (numplayersteama < numplayersteamb)
+ {
+ return false;
+ }
+ if (numplayersteama > numplayersteamb)
+ {
+ return false;
+ }
+ return scoreteama == scoreteamb;
+}
- if(totalteams <= 1)
+int FindBestTeams(entity player, bool usescore)
+{
+ if (MUTATOR_CALLHOOK(FindBestTeams, player) == true)
{
- if(autocvar_g_campaign && pl && IS_REAL_CLIENT(pl))
- return 1; // special case for campaign and player joining
- else if(totalteams == 1) // single team
- LOG_TRACEF("Only 1 team available for %s, you may need to fix your map", MapInfo_Type_ToString(MapInfo_CurrentGametype()));
- else // no teams, major no no
- error(sprintf("No teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype())));
+ return M_ARGV(1, float);
}
+ int teambits = 0;
+ int previousteam = 0;
+ if (c1 >= 0)
+ {
+ teambits = BIT(0);
+ previousteam = 1;
+ }
+ if (c2 >= 0)
+ {
+ if (previousteam == 0)
+ {
+ teambits = BIT(1);
+ previousteam = 2;
+ }
+ else if (IsTeamSmallerThanTeam(2, previousteam, player, usescore))
+ {
+ teambits = BIT(1);
+ previousteam = 2;
+ }
+ else if (IsTeamEqualToTeam(2, previousteam, player, usescore))
+ {
+ teambits |= BIT(1);
+ previousteam = 2;
+ }
+ }
+ if (c3 >= 0)
+ {
+ if (previousteam == 0)
+ {
+ teambits = BIT(2);
+ previousteam = 3;
+ }
+ else if (IsTeamSmallerThanTeam(3, previousteam, player, usescore))
+ {
+ teambits = BIT(2);
+ previousteam = 3;
+ }
+ else if (IsTeamEqualToTeam(3, previousteam, player, usescore))
+ {
+ teambits |= BIT(2);
+ previousteam = 3;
+ }
+ }
+ if (c4 >= 0)
+ {
+ if (previousteam == 0)
+ {
+ teambits = BIT(3);
+ }
+ else if (IsTeamSmallerThanTeam(4, previousteam, player, usescore))
+ {
+ teambits = BIT(3);
+ }
+ else if (IsTeamEqualToTeam(4, previousteam, player, usescore))
+ {
+ teambits |= BIT(3);
+ }
+ }
+ return teambits;
+}
+// returns # of smallest team (1, 2, 3, 4)
+// NOTE: Assumes CheckAllowedTeams has already been called!
+float FindSmallestTeam(entity pl, float ignore_pl)
+{
// count how many players are in each team
- if(ignore_pl)
+ if (ignore_pl)
+ {
GetTeamCounts(pl);
+ }
else
+ {
GetTeamCounts(NULL);
-
+ }
+ int teambits = FindBestTeams(pl, true);
+ if (teambits == 0)
+ {
+ error(sprintf("No teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype())));
+ }
RandomSelection_Init();
-
- if(TeamSmallerEqThanTeam(1, t, pl))
- t = 1;
- if(TeamSmallerEqThanTeam(2, t, pl))
- t = 2;
- if(TeamSmallerEqThanTeam(3, t, pl))
- t = 3;
- if(TeamSmallerEqThanTeam(4, t, pl))
- t = 4;
-
- // now t is the minimum, or A minimum!
- if(t == 1 || TeamSmallerEqThanTeam(1, t, pl))
+ if ((teambits & BIT(0)) != 0)
+ {
RandomSelection_AddFloat(1, 1, 1);
- if(t == 2 || TeamSmallerEqThanTeam(2, t, pl))
+ }
+ if ((teambits & BIT(1)) != 0)
+ {
RandomSelection_AddFloat(2, 1, 1);
- if(t == 3 || TeamSmallerEqThanTeam(3, t, pl))
+ }
+ if ((teambits & BIT(2)) != 0)
+ {
RandomSelection_AddFloat(3, 1, 1);
- if(t == 4 || TeamSmallerEqThanTeam(4, t, pl))
+ }
+ if ((teambits & BIT(3)) != 0)
+ {
RandomSelection_AddFloat(4, 1, 1);
-
+ }
return RandomSelection_chosen_float;
}
int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam)
{
- float bestteam, selectedteam;
-
// don't join a team if we're not playing a team game
- if(!teamplay)
+ if (!teamplay)
+ {
return 0;
+ }
// find out what teams are available
CheckAllowedTeams(this);
+ float selectedteam;
+
// if we don't care what team he ends up on, put him on whatever team he entered as.
// if he's not on a valid team, then let other code put him on the smallest team
- if(!forcebestteam)
+ if (!forcebestteam)
{
if( c1 >= 0 && this.team == NUM_TEAM_1)
selectedteam = this.team;
else
selectedteam = -1;
- if(selectedteam > 0)
+ if (selectedteam > 0)
{
- if(!only_return_best)
+ if (!only_return_best)
{
SetPlayerTeamSimple(this, selectedteam);
// otherwise end up on the smallest team (handled below)
}
- bestteam = FindSmallestTeam(this, true);
- MUTATOR_CALLHOOK(JoinBestTeam, this, bestteam);
- bestteam = M_ARGV(1, float);
-
- if(!only_return_best && !this.bot_forced_team)
+ float bestteam = FindSmallestTeam(this, true);
+ if (only_return_best || this.bot_forced_team)
{
- bestteam = Team_NumberToTeam(bestteam);
- if (bestteam != -1)
- {
- TeamchangeFrags(this);
- SetPlayerTeamSimple(this, bestteam);
- }
- else
- {
- error("smallest team: invalid team\n");
- }
-
- LogTeamchange(this.playerid, this.team, 2); // log auto join
-
- if(!IS_DEAD(this))
- Damage(this, this, this, 100000, DEATH_TEAMCHANGE.m_id, this.origin, '0 0 0');
+ return bestteam;
+ }
+ bestteam = Team_NumberToTeam(bestteam);
+ if (bestteam == -1)
+ {
+ error("JoinBestTeam: invalid team\n");
+ }
+ int oldteam = Team_TeamToNumber(this.team);
+ TeamchangeFrags(this);
+ SetPlayerTeamSimple(this, bestteam);
+ LogTeamchange(this.playerid, this.team, 2); // log auto join
+ if (!IS_BOT_CLIENT(this))
+ {
+ AutoBalanceBots(oldteam, Team_TeamToNumber(bestteam));
+ }
+ if (!IS_DEAD(this) && (MUTATOR_CALLHOOK(Player_ChangeTeamKill, this) ==
+ false))
+ {
+ Damage(this, this, this, 100000, DEATH_TEAMCHANGE.m_id, this.origin, '0 0 0');
}
-
return bestteam;
}
-//void() ctf_playerchanged;
void SV_ChangeTeam(entity this, float _color)
{
- float scolor, dcolor, steam, dteam; //, dbotcount, scount, dcount;
+ float sourcecolor, destinationcolor, sourceteam, destinationteam;
// in normal deathmatch we can just apply the color and we're done
if(!teamplay)
if(!teamplay)
return;
- scolor = this.clientcolors & 0x0F;
- dcolor = _color & 0x0F;
-
- if(scolor == NUM_TEAM_1 - 1)
- steam = 1;
- else if(scolor == NUM_TEAM_2 - 1)
- steam = 2;
- else if(scolor == NUM_TEAM_3 - 1)
- steam = 3;
- else // if(scolor == NUM_TEAM_4 - 1)
- steam = 4;
- if(dcolor == NUM_TEAM_1 - 1)
- dteam = 1;
- else if(dcolor == NUM_TEAM_2 - 1)
- dteam = 2;
- else if(dcolor == NUM_TEAM_3 - 1)
- dteam = 3;
- else // if(dcolor == NUM_TEAM_4 - 1)
- dteam = 4;
+ sourcecolor = this.clientcolors & 0x0F;
+ destinationcolor = _color & 0x0F;
+ sourceteam = Team_TeamToNumber(sourcecolor + 1);
+ destinationteam = Team_TeamToNumber(destinationcolor + 1);
+
CheckAllowedTeams(this);
- if(dteam == 1 && c1 < 0) dteam = 4;
- if(dteam == 4 && c4 < 0) dteam = 3;
- if(dteam == 3 && c3 < 0) dteam = 2;
- if(dteam == 2 && c2 < 0) dteam = 1;
+ if (destinationteam == 1 && c1 < 0) destinationteam = 4;
+ if (destinationteam == 4 && c4 < 0) destinationteam = 3;
+ if (destinationteam == 3 && c3 < 0) destinationteam = 2;
+ if (destinationteam == 2 && c2 < 0) destinationteam = 1;
// not changing teams
- if(scolor == dcolor)
+ if (sourcecolor == destinationcolor)
{
- SetPlayerTeam(this, dteam, steam, true);
+ SetPlayerTeam(this, destinationteam, sourceteam, true);
return;
}
- if((autocvar_g_campaign) || (autocvar_g_changeteam_banned && this.wasplayer)) {
+ if((autocvar_g_campaign) || (autocvar_g_changeteam_banned && CS(this).wasplayer)) {
Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_TEAMCHANGE_NOTALLOWED);
return; // changing teams is not allowed
}
// autocvar_g_balance_teams_prevent_imbalance only makes sense if autocvar_g_balance_teams is on, as it makes the team selection dialog pointless
- if(autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance)
+ if (autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance)
{
GetTeamCounts(this);
- if(!TeamSmallerEqThanTeam(dteam, steam, this))
+ if ((BIT(destinationteam - 1) & FindBestTeams(this, false)) == 0)
{
Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM);
return;
}
}
-
-// bprint("allow change teams from ", ftos(steam), " to ", ftos(dteam), "\n");
-
- if(IS_PLAYER(this) && steam != dteam)
+ if(IS_PLAYER(this) && sourceteam != destinationteam)
{
// reduce frags during a team change
TeamchangeFrags(this);
}
+ SetPlayerTeam(this, destinationteam, sourceteam, !IS_CLIENT(this));
+ AutoBalanceBots(sourceteam, destinationteam);
+ if (!IS_PLAYER(this) || (sourceteam == destinationteam))
+ {
+ return;
+ }
+ // kill player when changing teams
+ if (IS_DEAD(this) || (MUTATOR_CALLHOOK(Player_ChangeTeamKill, this) == true))
+ {
+ return;
+ }
+ Damage(this, this, this, 100000, DEATH_TEAMCHANGE.m_id, this.origin, '0 0 0');
+}
- SetPlayerTeam(this, dteam, steam, !IS_CLIENT(this));
-
- if(IS_PLAYER(this) && steam != dteam)
+void AutoBalanceBots(int sourceteam, int destinationteam)
+{
+ if ((sourceteam == -1) || (destinationteam == -1))
+ {
+ return;
+ }
+ if (!autocvar_g_balance_teams ||
+ !autocvar_g_balance_teams_prevent_imbalance)
{
- // kill player when changing teams
- if(!IS_DEAD(this))
- Damage(this, this, this, 100000, DEATH_TEAMCHANGE.m_id, this.origin, '0 0 0');
+ return;
}
+ int numplayerssourceteam = 0;
+ int numplayersdestinationteam = 0;
+ entity lowestbotdestinationteam = NULL;
+ switch (sourceteam)
+ {
+ case 1:
+ {
+ numplayerssourceteam = c1;
+ break;
+ }
+ case 2:
+ {
+ numplayerssourceteam = c2;
+ break;
+ }
+ case 3:
+ {
+ numplayerssourceteam = c3;
+ break;
+ }
+ case 4:
+ {
+ numplayerssourceteam = c4;
+ break;
+ }
+ }
+ switch (destinationteam)
+ {
+ case 1:
+ {
+ numplayersdestinationteam = c1;
+ lowestbotdestinationteam = lowestbotteam1;
+ break;
+ }
+ case 2:
+ {
+ numplayersdestinationteam = c2;
+ lowestbotdestinationteam = lowestbotteam2;
+ break;
+ }
+ case 3:
+ {
+ numplayersdestinationteam = c3;
+ lowestbotdestinationteam = lowestbotteam3;
+ break;
+ }
+ case 4:
+ {
+ numplayersdestinationteam = c4;
+ lowestbotdestinationteam = lowestbotteam4;
+ break;
+ }
+ }
+ if ((numplayersdestinationteam <= numplayerssourceteam) ||
+ (lowestbotdestinationteam == NULL))
+ {
+ return;
+ }
+ SetPlayerTeamSimple(lowestbotdestinationteam, Team_NumberToTeam(sourceteam));
+ if (IS_DEAD(lowestbotdestinationteam) || (MUTATOR_CALLHOOK(
+ Player_ChangeTeamKill, lowestbotdestinationteam) == true))
+ {
+ return;
+ }
+ Damage(lowestbotdestinationteam, lowestbotdestinationteam,
+ lowestbotdestinationteam, 100000, DEATH_TEAMCHANGE.m_id,
+ lowestbotdestinationteam.origin, '0 0 0');
}
void ShufflePlayerOutOfTeam (float source_team)
TeamchangeFrags(selected);
SetPlayerTeam(selected, smallestteam, source_team, false);
- if(!IS_DEAD(selected))
- Damage(selected, selected, selected, 100000, DEATH_AUTOTEAMCHANGE.m_id, selected.origin, '0 0 0');
+ if (IS_DEAD(selected) || MUTATOR_CALLHOOK(Player_ChangeTeamKill, selected) == true)
+ {
+ return;
+ }
+ Damage(selected, selected, selected, 100000, DEATH_AUTOTEAMCHANGE.m_id, selected.origin, '0 0 0');
Send_Notification(NOTIF_ONE, selected, MSG_CENTER, CENTER_DEATH_SELF_AUTOTEAMCHANGE, selected.team);
}