#include "teamplay.qh"
-#include "_all.qh"
-#include "cl_client.qh"
+#include "client.qh"
#include "race.qh"
#include "scores.qh"
#include "scores_rules.qh"
+#include "bot/api.qh"
+
#include "command/vote.qh"
-#include "mutators/mutators_include.qh"
+#include "mutators/_mod.qh"
-#include "../common/deathtypes.qh"
+#include "../common/deathtypes/all.qh"
+#include "../common/gamemodes/_mod.qh"
#include "../common/teams.qh"
void TeamchangeFrags(entity e)
GameLogEcho(strcat(":team:", ftos(player_id), ":", ftos(team_number), ":", ftos(type)));
}
-void default_delayedinit()
+void default_delayedinit(entity this)
{
if(!scores_initialized)
ScoreRules_generic();
void InitGameplayMode()
{
- float fraglimit_override, timelimit_override, leadlimit_override, qualifying_override;
-
- qualifying_override = -1;
-
VoteReset();
// find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds
get_mi_min_max(1);
world.mins = mi_min;
world.maxs = mi_max;
+ // currently, NetRadiant's limit is 131072 qu for each side
+ // distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu
+ // set the distance according to map size but don't go over the limit to avoid issues with float precision
+ // in case somebody makes extremely large maps
+ max_shot_distance = min(230000, vlen(world.maxs - world.mins));
MapInfo_LoadMapSettings(mapname);
serverflags &= ~SERVERFLAG_TEAMPLAY;
MapInfo_ClearTemps();
- // set both here, gamemode can override it later
- timelimit_override = autocvar_timelimit_override;
- fraglimit_override = autocvar_fraglimit_override;
- leadlimit_override = autocvar_leadlimit_override;
gamemode_name = MapInfo_Type_ToText(MapInfo_LoadedGametype);
- if(g_dm)
- {
- MUTATOR_ADD(gamemode_deathmatch);
- }
-
- if(g_tdm)
- {
- ActivateTeamplay();
- fraglimit_override = autocvar_g_tdm_point_limit;
- leadlimit_override = autocvar_g_tdm_point_leadlimit;
- if(autocvar_g_tdm_team_spawns)
- have_team_spawns = -1; // request team spawns
- MUTATOR_ADD(gamemode_tdm);
- }
-
- if(g_domination)
- {
- ActivateTeamplay();
- fraglimit_override = autocvar_g_domination_point_limit;
- leadlimit_override = autocvar_g_domination_point_leadlimit;
- if(autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit)
- fraglimit_override = autocvar_g_domination_roundbased_point_limit;
- have_team_spawns = -1; // request team spawns
- MUTATOR_ADD(gamemode_domination);
- }
-
- if(g_ctf)
- {
- ActivateTeamplay();
- fraglimit_override = autocvar_capturelimit_override;
- leadlimit_override = autocvar_captureleadlimit_override;
- have_team_spawns = -1; // request team spawns
- MUTATOR_ADD(gamemode_ctf);
- }
-
- if(g_lms)
- {
- fraglimit_override = autocvar_g_lms_lives_override;
- leadlimit_override = 0; // not supported by LMS
- if(fraglimit_override == 0)
- fraglimit_override = -1;
- MUTATOR_ADD(gamemode_lms);
- }
-
- if(g_ca)
- {
- ActivateTeamplay();
- fraglimit_override = autocvar_g_ca_point_limit;
- leadlimit_override = autocvar_g_ca_point_leadlimit;
- if(autocvar_g_ca_team_spawns)
- have_team_spawns = -1; // request team spawns
- MUTATOR_ADD(gamemode_ca);
- }
-
- if(g_keyhunt)
- {
- ActivateTeamplay();
- fraglimit_override = autocvar_g_keyhunt_point_limit;
- leadlimit_override = autocvar_g_keyhunt_point_leadlimit;
- if(autocvar_g_keyhunt_team_spawns)
- have_team_spawns = -1; // request team spawns
- MUTATOR_ADD(gamemode_keyhunt);
- }
-
- if(g_freezetag)
- {
- ActivateTeamplay();
- fraglimit_override = autocvar_g_freezetag_point_limit;
- leadlimit_override = autocvar_g_freezetag_point_leadlimit;
- if(autocvar_g_freezetag_team_spawns)
- have_team_spawns = -1; // request team spawns
- MUTATOR_ADD(gamemode_freezetag);
- }
-
- if(g_assault)
- {
- ActivateTeamplay();
- have_team_spawns = -1; // request team spawns
- MUTATOR_ADD(gamemode_assault);
- }
-
- if(g_onslaught)
- {
- ActivateTeamplay();
- fraglimit_override = autocvar_g_onslaught_point_limit;
- have_team_spawns = -1; // request team spawns
- MUTATOR_ADD(gamemode_onslaught);
- }
-
- if(g_race)
- {
- if(autocvar_g_race_teams)
- {
- ActivateTeamplay();
- race_teams = bound(2, autocvar_g_race_teams, 4);
- have_team_spawns = -1; // request team spawns
- }
- else
- race_teams = 0;
- qualifying_override = autocvar_g_race_qualifying_timelimit_override;
- fraglimit_override = autocvar_g_race_laps_limit;
- leadlimit_override = 0; // currently not supported by race
-
- // we need to find out the correct value for g_race_qualifying
- float want_qualifying = ((qualifying_override >= 0) ? qualifying_override : autocvar_g_race_qualifying_timelimit) > 0;
-
- if(autocvar_g_campaign)
- {
- g_race_qualifying = 1;
- independent_players = 1;
- }
- else if(!autocvar_g_campaign && want_qualifying)
- {
- g_race_qualifying = 2;
- independent_players = 1;
- race_fraglimit = (race_fraglimit >= 0) ? fraglimit_override : autocvar_fraglimit;
- race_leadlimit = (race_leadlimit >= 0) ? leadlimit_override : autocvar_leadlimit;
- race_timelimit = (race_timelimit >= 0) ? timelimit_override : autocvar_timelimit;
- fraglimit_override = 0;
- leadlimit_override = 0;
- timelimit_override = autocvar_g_race_qualifying_timelimit;
- }
- else
- {
- g_race_qualifying = 0;
- }
-
- MUTATOR_ADD(gamemode_race);
- }
-
- if(g_cts)
- {
- g_race_qualifying = 1;
- fraglimit_override = 0;
- leadlimit_override = 0;
- independent_players = 1;
- MUTATOR_ADD(gamemode_cts);
- }
-
- if(g_nexball)
- {
- fraglimit_override = autocvar_g_nexball_goallimit;
- leadlimit_override = autocvar_g_nexball_goalleadlimit;
- ActivateTeamplay();
- have_team_spawns = -1; // request team spawns
- MUTATOR_ADD(gamemode_nexball);
- }
-
- if(g_keepaway)
- {
- MUTATOR_ADD(gamemode_keepaway);
- }
-
- if(g_invasion)
- {
- fraglimit_override = autocvar_g_invasion_point_limit;
- if(autocvar_g_invasion_teams >= 2)
- {
- ActivateTeamplay();
- if(autocvar_g_invasion_team_spawns)
- have_team_spawns = -1; // request team spawns
- }
- MUTATOR_ADD(gamemode_invasion);
- }
-
cache_mutatormsg = strzone("");
cache_lastmutatormsg = strzone("");
- // enforce the server's universal frag/time limits
- if(!autocvar_g_campaign)
- {
- if(fraglimit_override >= 0)
- cvar_set("fraglimit", ftos(fraglimit_override));
- if(timelimit_override >= 0)
- cvar_set("timelimit", ftos(timelimit_override));
- if(leadlimit_override >= 0)
- cvar_set("leadlimit", ftos(leadlimit_override));
- if(qualifying_override >= 0)
- cvar_set("g_race_qualifying_timelimit", ftos(qualifying_override));
- }
-
- InitializeEntity(world, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK);
+ InitializeEntity(NULL, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK);
}
-string GetClientVersionMessage()
-{SELFPARAM();
- string versionmsg;
- if (self.version_mismatch) {
- if(self.version < autocvar_gameversion) {
- versionmsg = "^3Your client version is outdated.\n\n\n### YOU WON'T BE ABLE TO PLAY ON THIS SERVER ###\n\n\nPlease update!!!^8";
+string GetClientVersionMessage(entity this)
+{
+ if (this.version_mismatch) {
+ if(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 {
- versionmsg = "^3This server is using an outdated Xonotic version.\n\n\n ### THIS SERVER IS INCOMPATIBLE AND THUS YOU CANNOT JOIN ###.^8";
+ return strcat("This is Xonotic ", autocvar_g_xonoticversion,
+ "\n^3This server is using an outdated Xonotic version.\n\n\n ### THIS SERVER IS INCOMPATIBLE AND THUS YOU CANNOT JOIN ###.^8");
}
} else {
- versionmsg = "^2client version and server version are compatible.^8";
+ return strcat("Welcome to Xonotic ", autocvar_g_xonoticversion);
}
- return versionmsg;
}
-string getwelcomemessage(void)
+string getwelcomemessage(entity this)
{
- string s, modifications, motd;
-
MUTATOR_CALLHOOK(BuildMutatorsPrettyString, "");
- modifications = ret_string;
+ string modifications = M_ARGV(0, string);
if(g_weaponarena)
{
else
modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena");
}
- else if(cvar("g_balance_blaster_weaponstart") == 0)
+ else if(cvar("g_balance_blaster_weaponstartoverride") == 0)
modifications = strcat(modifications, ", No start weapons");
if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
modifications = strcat(modifications, ", Low gravity");
- if(g_cloaked && !g_cts)
- modifications = strcat(modifications, ", Cloaked");
if(g_weapon_stay && !g_cts)
modifications = strcat(modifications, ", Weapons stay");
if(g_jetpack)
modifications = strcat(modifications, ", Powerups");
modifications = substring(modifications, 2, strlen(modifications) - 2);
- string versionmessage;
- versionmessage = GetClientVersionMessage();
-
- s = strcat("This is Xonotic ", autocvar_g_xonoticversion, "\n", versionmessage);
- s = strcat(s, "^8\n\nmatch type is ^1", gamemode_name, "^8\n");
+ string versionmessage = GetClientVersionMessage(this);
+ string s = strcat(versionmessage, "^8\n^8\nmatch type is ^1", gamemode_name, "^8\n");
if(modifications != "")
s = strcat(s, "^8\nactive modifications: ^3", modifications, "^8\n");
- if (cvar("g_nades"))
- s = strcat(s, "\n\n^3nades^8 are enabled, press 'g' to use them\n");
-
if(cache_lastmutatormsg != autocvar_g_mutatormsg)
{
if(cache_lastmutatormsg)
string mutator_msg = "";
MUTATOR_CALLHOOK(BuildGameplayTipsString, mutator_msg);
- mutator_msg = ret_string;
+ mutator_msg = M_ARGV(0, string);
s = strcat(s, mutator_msg); // trust that the mutator will do proper formatting
- motd = autocvar_sv_motd;
+ string motd = autocvar_sv_motd;
if (motd != "") {
s = strcat(s, "\n\n^8MOTD: ^7", strreplace("\\n", "\n", motd));
}
return s;
}
+void setcolor(entity this, int clr)
+{
+#if 0
+ this.clientcolors = clr;
+ this.team = (clr & 15) + 1;
+#else
+ builtin_setcolor(this, clr);
+#endif
+}
+
void SetPlayerColors(entity pl, float _color)
{
/*string s;
}
}
-void SetPlayerTeam(entity pl, float t, float s, float noprint)
+bool SetPlayerTeamSimple(entity player, int teamnum)
{
- float _color;
-
- if(t == 4)
- _color = NUM_TEAM_4 - 1;
- else if(t == 3)
- _color = NUM_TEAM_3 - 1;
- else if(t == 2)
- _color = NUM_TEAM_2 - 1;
- else
- _color = NUM_TEAM_1 - 1;
-
- SetPlayerColors(pl,_color);
-
- if(t != s) {
- LogTeamchange(pl.playerid, pl.team, 3); // log manual team join
-
- if(!noprint)
- bprint(pl.netname, "^7 has changed from ", Team_NumberToColoredFullName(s), "^7 to ", Team_NumberToColoredFullName(t), "\n");
+ if (player.team == teamnum)
+ {
+ return true;
}
+ if (MUTATOR_CALLHOOK(Player_ChangeTeam, player, Team_TeamToNumber(
+ player.team), Team_TeamToNumber(teamnum)) == true)
+ {
+ // Mutator has blocked team change.
+ return false;
+ }
+ int oldteam = player.team;
+ SetPlayerColors(player, teamnum - 1);
+ MUTATOR_CALLHOOK(Player_ChangedTeam, player, oldteam, player.team);
+ return true;
+}
+void SetPlayerTeam(entity pl, float t, float s, float noprint)
+{
+ if (t == s)
+ {
+ return;
+ }
+ float teamnum = Team_NumberToTeam(t);
+ SetPlayerTeamSimple(pl, teamnum);
+ LogTeamchange(pl.playerid, pl.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");
}
// set c1...c4 to show what teams are allowed
void CheckAllowedTeams (entity for_whom)
-{SELFPARAM();
- float dm;
- entity head;
- string teament_name;
+{
+ int teams_mask = 0;
c1 = c2 = c3 = c4 = -1;
cb1 = cb2 = cb3 = cb4 = 0;
- teament_name = string_null;
- if(g_onslaught)
- {
- // onslaught is special
- head = findchain(classname, "onslaught_generator");
- while (head)
- {
- if (head.team == NUM_TEAM_1) c1 = 0;
- if (head.team == NUM_TEAM_2) c2 = 0;
- if (head.team == NUM_TEAM_3) c3 = 0;
- if (head.team == NUM_TEAM_4) c4 = 0;
- head = head.chain;
- }
- }
- else if(g_domination)
- teament_name = "dom_team";
- else if(g_ctf)
- teament_name = "ctf_team";
- else if(g_tdm)
- teament_name = "tdm_team";
- else if(g_nexball)
- teament_name = "nexball_team";
- else if(g_assault)
- c1 = c2 = 0; // Assault always has 2 teams
- else
- {
- // cover anything else by treating it like tdm with no teams spawned
- dm = 2;
+ string teament_name = string_null;
- MUTATOR_CALLHOOK(GetTeamCount, dm);
- dm = ret_float;
+ bool mutator_returnvalue = MUTATOR_CALLHOOK(CheckAllowedTeams, teams_mask, teament_name, for_whom);
+ teams_mask = M_ARGV(0, float);
+ teament_name = M_ARGV(1, string);
- if(dm >= 4)
- c1 = c2 = c3 = c4 = 0;
- else if(dm >= 3)
- c1 = c2 = c3 = 0;
- else
- c1 = c2 = 0;
+ if(!mutator_returnvalue)
+ {
+ if(teams_mask & BIT(0)) c1 = 0;
+ if(teams_mask & BIT(1)) c2 = 0;
+ if(teams_mask & BIT(2)) c3 = 0;
+ if(teams_mask & BIT(3)) c4 = 0;
}
// find out what teams are allowed if necessary
if(teament_name)
{
- head = find(world, classname, teament_name);
+ entity head = find(NULL, classname, teament_name);
while(head)
{
- if(!(g_domination && head.netname == ""))
+ switch(head.team)
{
- if(head.team == NUM_TEAM_1)
- c1 = 0;
- else if(head.team == NUM_TEAM_2)
- c2 = 0;
- else if(head.team == NUM_TEAM_3)
- c3 = 0;
- else if(head.team == NUM_TEAM_4)
- c4 = 0;
+ case NUM_TEAM_1: c1 = 0; break;
+ case NUM_TEAM_2: c2 = 0; break;
+ case NUM_TEAM_3: c3 = 0; break;
+ case NUM_TEAM_4: c4 = 0; break;
}
+
head = find(head, classname, teament_name);
}
}
// TODO: Balance quantity of bots across > 2 teams when bot_vs_human is set (and remove next line)
- if(c3==-1 && c4==-1)
+ if(AvailableTeams() == 2)
if(autocvar_bot_vs_human && for_whom)
{
if(autocvar_bot_vs_human > 0)
{
- // bots are all blue
+ // find last team available
+
if(IS_BOT_CLIENT(for_whom))
- c1 = c3 = c4 = -1;
+ {
+ if(c4 >= 0) { c3 = c2 = c1 = -1; }
+ else if(c3 >= 0) { c4 = c2 = c1 = -1; }
+ else { c4 = c3 = c1 = -1; }
+ // no further cases, we know at least 2 teams exist
+ }
else
- c2 = -1;
+ {
+ if(c1 >= 0) { c2 = c3 = c4 = -1; }
+ else if(c2 >= 0) { c1 = c3 = c4 = -1; }
+ else { c1 = c2 = c4 = -1; }
+ // no further cases, bots have one of the teams
+ }
}
else
{
- // bots are all red
+ // find first team available
+
if(IS_BOT_CLIENT(for_whom))
- c2 = c3 = c4 = -1;
+ {
+ if(c1 >= 0) { c2 = c3 = c4 = -1; }
+ else if(c2 >= 0) { c1 = c3 = c4 = -1; }
+ else { c1 = c2 = c4 = -1; }
+ // no further cases, we know at least 2 teams exist
+ }
else
- c1 = -1;
+ {
+ if(c4 >= 0) { c3 = c2 = c1 = -1; }
+ else if(c3 >= 0) { c4 = c2 = c1 = -1; }
+ else { c4 = c3 = c1 = -1; }
+ // no further cases, bots have one of the teams
+ }
}
}
+ if(!for_whom)
+ return;
+
// if player has a forced team, ONLY allow that one
- if(self.team_forced == NUM_TEAM_1 && c1 >= 0)
+ if(for_whom.team_forced == NUM_TEAM_1 && c1 >= 0)
c2 = c3 = c4 = -1;
- else if(self.team_forced == NUM_TEAM_2 && c2 >= 0)
+ else if(for_whom.team_forced == NUM_TEAM_2 && c2 >= 0)
c1 = c3 = c4 = -1;
- else if(self.team_forced == NUM_TEAM_3 && c3 >= 0)
+ else if(for_whom.team_forced == NUM_TEAM_3 && c3 >= 0)
c1 = c2 = c4 = -1;
- else if(self.team_forced == NUM_TEAM_4 && c4 >= 0)
+ else if(for_whom.team_forced == NUM_TEAM_4 && c4 >= 0)
c1 = c2 = c3 = -1;
}
// teams that are allowed will now have their player counts stored in c1...c4
void GetTeamCounts(entity ignore)
{
- entity head;
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
- FOR_EACH_CLIENT(head)
- {
+ FOREACH_CLIENT(true, LAMBDA(
float t;
- if(IS_PLAYER(head) || head.caplayer)
- t = head.team;
- else if(head.team_forced > 0)
- t = head.team_forced; // reserve the spot
+ 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(head != ignore)// && head.netname != "")
+ if(it != ignore)// && it.netname != "")
{
- value = PlayerValue(head);
- if(IS_BOT_CLIENT(head))
+ value = PlayerValue(it);
+ if(IS_BOT_CLIENT(it))
bvalue = value;
else
bvalue = 0;
}
}
}
- }
+ ));
// if the player who has a forced team has not joined yet, reserve the spot
if(autocvar_g_campaign)
// NOTE: Assumes CheckAllowedTeams has already been called!
float FindSmallestTeam(entity pl, float ignore_pl)
{
- float totalteams, t;
- totalteams = 0;
+ 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;
// find out what teams are available
//CheckAllowedTeams();
{
if(autocvar_g_campaign && pl && IS_REAL_CLIENT(pl))
return 1; // special case for campaign and player joining
- else
- error(sprintf("Too few teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype())));
+ 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())));
}
// count how many players are in each team
if(ignore_pl)
GetTeamCounts(pl);
else
- GetTeamCounts(world);
+ GetTeamCounts(NULL);
RandomSelection_Init();
- t = 1;
+ if(TeamSmallerEqThanTeam(1, t, pl))
+ t = 1;
if(TeamSmallerEqThanTeam(2, t, pl))
t = 2;
if(TeamSmallerEqThanTeam(3, t, pl))
// now t is the minimum, or A minimum!
if(t == 1 || TeamSmallerEqThanTeam(1, t, pl))
- RandomSelection_Add(world, 1, string_null, 1, 1);
+ RandomSelection_AddFloat(1, 1, 1);
if(t == 2 || TeamSmallerEqThanTeam(2, t, pl))
- RandomSelection_Add(world, 2, string_null, 1, 1);
+ RandomSelection_AddFloat(2, 1, 1);
if(t == 3 || TeamSmallerEqThanTeam(3, t, pl))
- RandomSelection_Add(world, 3, string_null, 1, 1);
+ RandomSelection_AddFloat(3, 1, 1);
if(t == 4 || TeamSmallerEqThanTeam(4, t, pl))
- RandomSelection_Add(world, 4, string_null, 1, 1);
+ RandomSelection_AddFloat(4, 1, 1);
return RandomSelection_chosen_float;
}
-float JoinBestTeam(entity pl, float only_return_best, float forcebestteam)
-{SELFPARAM();
- float smallest, selectedteam;
-
+int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam)
+{
// 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(pl);
+ 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( c1 >= 0 && pl.team == NUM_TEAM_1)
- selectedteam = pl.team;
- else if(c2 >= 0 && pl.team == NUM_TEAM_2)
- selectedteam = pl.team;
- else if(c3 >= 0 && pl.team == NUM_TEAM_3)
- selectedteam = pl.team;
- else if(c4 >= 0 && pl.team == NUM_TEAM_4)
- selectedteam = pl.team;
+ if (!forcebestteam)
+ {
+ if( c1 >= 0 && this.team == NUM_TEAM_1)
+ selectedteam = this.team;
+ else if(c2 >= 0 && this.team == NUM_TEAM_2)
+ selectedteam = this.team;
+ else if(c3 >= 0 && this.team == NUM_TEAM_3)
+ selectedteam = this.team;
+ else if(c4 >= 0 && this.team == NUM_TEAM_4)
+ selectedteam = this.team;
else
selectedteam = -1;
- if(selectedteam > 0)
+ if (selectedteam > 0)
{
- if(!only_return_best)
+ if (!only_return_best)
{
- SetPlayerColors(pl, selectedteam - 1);
+ SetPlayerTeamSimple(this, selectedteam);
// when JoinBestTeam is called by client.qc/ClientKill_Now_TeamChange the players team is -1 and thus skipped
- // when JoinBestTeam is called by cl_client.qc/ClientConnect the player_id is 0 the log attempt is rejected
- LogTeamchange(pl.playerid, pl.team, 99);
+ // when JoinBestTeam is called by client.qc/ClientConnect the player_id is 0 the log attempt is rejected
+ LogTeamchange(this.playerid, this.team, 99);
}
return selectedteam;
}
// otherwise end up on the smallest team (handled below)
}
- smallest = FindSmallestTeam(pl, true);
+ float bestteam = FindSmallestTeam(this, true);
+ MUTATOR_CALLHOOK(JoinBestTeam, this, bestteam);
+ bestteam = M_ARGV(1, float);
- if(!only_return_best && !pl.bot_forced_team)
+ if (only_return_best || this.bot_forced_team)
{
- TeamchangeFrags(self);
- if(smallest == 1)
- {
- SetPlayerColors(pl, NUM_TEAM_1 - 1);
- }
- else if(smallest == 2)
- {
- SetPlayerColors(pl, NUM_TEAM_2 - 1);
- }
- else if(smallest == 3)
- {
- SetPlayerColors(pl, NUM_TEAM_3 - 1);
- }
- else if(smallest == 4)
- {
- SetPlayerColors(pl, NUM_TEAM_4 - 1);
- }
- else
- {
- error("smallest team: invalid team\n");
- }
-
- LogTeamchange(pl.playerid, pl.team, 2); // log auto join
-
- if(pl.deadflag == DEAD_NO)
- Damage(pl, pl, pl, 100000, DEATH_TEAMCHANGE, pl.origin, '0 0 0');
+ return bestteam;
}
-
- return smallest;
+ bestteam = Team_NumberToTeam(bestteam);
+ if (bestteam != -1)
+ {
+ TeamchangeFrags(this);
+ SetPlayerTeamSimple(this, bestteam);
+ }
+ else
+ {
+ error("JoinBestTeam: invalid team\n");
+ }
+ LogTeamchange(this.playerid, this.team, 2); // log auto join
+ 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(float _color)
-{SELFPARAM();
+void SV_ChangeTeam(entity this, float _color)
+{
float scolor, dcolor, steam, dteam; //, dbotcount, scount, dcount;
// in normal deathmatch we can just apply the color and we're done
- if(!teamplay) {
- SetPlayerColors(self, _color);
+ if(!teamplay)
+ SetPlayerColors(this, _color);
+
+ if(!IS_CLIENT(this))
+ {
+ // since this is an engine function, and gamecode doesn't have any calls earlier than this, do the connecting message here
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_CONNECTING, this.netname);
return;
}
- scolor = self.clientcolors & 0x0F;
+ if(!teamplay)
+ return;
+
+ scolor = this.clientcolors & 0x0F;
dcolor = _color & 0x0F;
if(scolor == NUM_TEAM_1 - 1)
else // if(dcolor == NUM_TEAM_4 - 1)
dteam = 4;
- CheckAllowedTeams(self);
+ CheckAllowedTeams(this);
if(dteam == 1 && c1 < 0) dteam = 4;
if(dteam == 4 && c4 < 0) dteam = 3;
// not changing teams
if(scolor == dcolor)
{
- //bprint("same team change\n");
- SetPlayerTeam(self, dteam, steam, true);
+ SetPlayerTeam(this, dteam, steam, true);
return;
}
- if((autocvar_g_campaign) || (autocvar_g_changeteam_banned && self.wasplayer)) {
- Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_TEAMCHANGE_NOTALLOWED);
+ if((autocvar_g_campaign) || (autocvar_g_changeteam_banned && 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)
{
- GetTeamCounts(self);
- if(!TeamSmallerEqThanTeam(dteam, steam, self))
+ GetTeamCounts(this);
+ if(!TeamSmallerEqThanTeam(dteam, steam, this))
{
- Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM);
+ 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(self) && steam != dteam)
+ if(IS_PLAYER(this) && steam != dteam)
{
// reduce frags during a team change
- TeamchangeFrags(self);
+ TeamchangeFrags(this);
}
- // since this is an engine function, and gamecode doesn't have any calls earlier than this, do the connecting message here
- if(!IS_CLIENT(self))
- Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_CONNECTING, self.netname);
+ SetPlayerTeam(this, dteam, steam, !IS_CLIENT(this));
- SetPlayerTeam(self, dteam, steam, !IS_CLIENT(self));
-
- if(IS_PLAYER(self) && steam != dteam)
+ if(!IS_PLAYER(this) || (steam == dteam))
{
- // kill player when changing teams
- if(self.deadflag == DEAD_NO)
- Damage(self, self, self, 100000, DEATH_TEAMCHANGE, self.origin, '0 0 0');
+ 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');
}
void ShufflePlayerOutOfTeam (float source_team)
{
float smallestteam, smallestteam_count, steam;
float lowest_bot_score, lowest_player_score;
- entity head, lowest_bot, lowest_player, selected;
+ entity lowest_bot, lowest_player, selected;
smallestteam = 0;
smallestteam_count = 999999999;
else // if(source_team == 4)
steam = NUM_TEAM_4;
- lowest_bot = world;
+ lowest_bot = NULL;
lowest_bot_score = 999999999;
- lowest_player = world;
+ lowest_player = NULL;
lowest_player_score = 999999999;
// find the lowest-scoring player & bot of that team
- FOR_EACH_PLAYER(head)
- {
- if(head.team == steam)
+ FOREACH_CLIENT(IS_PLAYER(it) && it.team == steam, LAMBDA(
+ if(it.isbot)
{
- if(head.isbot)
+ if(it.totalfrags < lowest_bot_score)
{
- if(head.totalfrags < lowest_bot_score)
- {
- lowest_bot = head;
- lowest_bot_score = head.totalfrags;
- }
+ lowest_bot = it;
+ lowest_bot_score = it.totalfrags;
}
- else
+ }
+ else
+ {
+ if(it.totalfrags < lowest_player_score)
{
- if(head.totalfrags < lowest_player_score)
- {
- lowest_player = head;
- lowest_player_score = head.totalfrags;
- }
+ lowest_player = it;
+ lowest_player_score = it.totalfrags;
}
}
- }
+ ));
// prefers to move a bot...
- if(lowest_bot != world)
+ if(lowest_bot != NULL)
selected = lowest_bot;
// but it will move a player if it has to
else
TeamchangeFrags(selected);
SetPlayerTeam(selected, smallestteam, source_team, false);
- if(selected.deadflag == DEAD_NO)
- Damage(selected, selected, selected, 100000, DEATH_AUTOTEAMCHANGE, 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);
}