alias printstats "qc_cmd_sv printstats ${* ?}" // Dump eventlog player stats and other score information
alias radarmap "qc_cmd_sv radarmap ${* ?}" // Generate a radar image of the map
alias reducematchtime "qc_cmd_sv reducematchtime ${* ?}" // Decrease the timelimit value incrementally
+alias resetmatch "qc_cmd_sv resetmatch ${* ?}" // Soft restart the map
alias setbots "qc_cmd_sv setbots ${* ?}" // Adjust how many bots are in the match
alias shuffleteams "qc_cmd_sv shuffleteams ${* ?}" // Randomly move players to different teams
alias stuffto "qc_cmd_sv stuffto ${* ?}" // Send a command to be executed on a client
set sv_vote_command_restriction_reducematchtime "0"
set sv_vote_command_restriction_extendmatchtime "0"
set sv_vote_command_restriction_allready "0"
+set sv_vote_command_restriction_resetmatch "0"
set sv_vote_command_restriction_kick "1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" // enough space for ban reason
set sv_vote_command_restriction_kickban "1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" // enough space for ban reason
set sv_vote_command_restriction_cointoss "0"
// =================================
set sv_vote_call 1 "Allow users to call a vote for the commands in sv_vote_commands"
set sv_vote_change 1 "Allow voters to change their mind after already voting"
-set sv_vote_commands "restart fraglimit gotomap nextmap endmatch reducematchtime extendmatchtime allready kick cointoss movetoauto shuffleteams bots nobots" "these commands can be voted by players or used directly by masters (vdo) in addition to sv_vote_master_commands"
+set sv_vote_commands "restart fraglimit gotomap nextmap endmatch reducematchtime extendmatchtime allready resetmatch kick cointoss movetoauto shuffleteams bots nobots" "these commands can be voted by players or used directly by masters (vdo) in addition to sv_vote_master_commands"
set sv_vote_only_commands ""
set sv_vote_limit 160 "Maximum allowed length of a vote command"
set sv_vote_master_commands "movetored movetoblue movetoyellow movetopink movetospec" "Extra commands which vote masters can execute by themselves, along with the normal sv_vote_commands." // maybe add kickban here (but then sv_vote_master 0)
// rcon server commands
// ======================
rcon_secure 1
-set rcon_restricted_commands "restart fraglimit chmap gotomap nextmap endmatch reducematchtime extendmatchtime allready kick cointoss movetoauto shuffleteams bots nobots movetored movetoblue movetoyellow movetopink movetospec kickban \"sv_cmd bans\" \"sv_cmd unban *\" status \"sv_cmd teamstatus\""
+set rcon_restricted_commands "restart fraglimit chmap gotomap nextmap endmatch reducematchtime extendmatchtime allready resetmatch kick cointoss movetoauto shuffleteams bots nobots movetored movetoblue movetoyellow movetopink movetospec kickban \"sv_cmd bans\" \"sv_cmd unban *\" status \"sv_cmd teamstatus\""
set g_lms_regenerate 0
set g_lms_last_join 3 "if g_lms_join_anytime is 0, new players can only join if the worst active player has (fraglimit - g_lms_last_join) or more lives; in other words, new players can no longer join once the worst player loses more than g_lms_last_join lives"
set g_lms_join_anytime 1 "1: new players can join, but get same amount of lives as the worst player; 0: new players can only join if the worst active player has (fraglimit - g_lms_last_join) or more lives"
+set g_lms_items 0 "enables items to spawn, weaponarena still disables weapons and ammo (to force all items to spawn, use g_pickup_items 1 instead)"
set g_lms_weaponarena "most_available" "starting weapons - takes the same options as g_weaponarena"
{
float warmup_timelimit = STAT(WARMUP_TIMELIMIT);
if(warmup_timelimit > 0)
- warmup_timeleft = max(0, warmup_timelimit - time);
+ warmup_timeleft = max(0, warmup_timelimit - time + STAT(GAMESTARTTIME));
else if(warmup_timelimit == 0)
warmup_timeleft = timeleft;
warmup_timeleft = ceil(warmup_timeleft);
// reset objectives, toggle spawnpoints, reset triggers, ...
void assault_new_round(entity this)
{
- //bprint("ASSAULT: new round\n");
-
// up round counter
this.winning = this.winning + 1;
// reset the level with a countdown
cvar_set("timelimit", ftos(ceil(time - AS_ROUND_DELAY - game_starttime) / 60));
- ReadyRestart_force(); // sets game_starttime
+ bprint("Starting second round...\n");
+ ReadyRestart_force(true); // sets game_starttime
}
entity as_round;
{
if(ent.winning) // round end has been triggered by attacking team
{
- bprint("Assault: round completed.\n");
+ bprint(Team_ColoredFullName(assault_attacker_team), " destroyed the objective in ", process_time(2, ceil(time - game_starttime)), "\n");
SetWinners(team, assault_attacker_team);
TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 666 - TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 0));
});
FOREACH_CLIENT(IS_REAL_CLIENT(it),
{
- STAT(REDALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(
- 1));
- STAT(BLUEALIVE, it) = Team_GetNumberOfAlivePlayers(
- Team_GetTeamFromIndex(2));
- STAT(YELLOWALIVE, it) = Team_GetNumberOfAlivePlayers(
- Team_GetTeamFromIndex(3));
- STAT(PINKALIVE, it) = Team_GetNumberOfAlivePlayers(
- Team_GetTeamFromIndex(4));
+ STAT(REDALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(1));
+ STAT(BLUEALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(2));
+ STAT(YELLOWALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(3));
+ STAT(PINKALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(4));
});
}
-int CA_GetWinnerTeam()
-{
- int winner_team = 0;
- if (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(1)) >= 1)
- {
- winner_team = NUM_TEAM_1;
- }
- for (int i = 2; i <= NUM_TEAMS; ++i)
- {
- if (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(i)) >= 1)
- {
- if (winner_team != 0)
- {
- return 0;
- }
- winner_team = Team_IndexToTeam(i);
- }
- }
- if (winner_team)
- {
- return winner_team;
- }
- return -1; // no player left
-}
-
void nades_Clear(entity player);
float CA_CheckWinner()
}
CA_count_alive_players();
- if (Team_GetNumberOfAliveTeams() > 1)
- {
+ int winner_team = Team_GetWinnerAliveTeam();
+ if (!winner_team)
return 0;
- }
- int winner_team = CA_GetWinnerTeam();
if(winner_team > 0)
{
Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
total_control_points = 0;
for (int i = 1; i <= NUM_TEAMS; ++i)
{
- Team_SetNumberOfControlPoints(Team_GetTeamFromIndex(i), 0);
+ Team_SetNumberOfOwnedItems(Team_GetTeamFromIndex(i), 0);
}
IL_EACH(g_dompoints, true,
{
continue;
}
entity team_ = Entity_GetTeam(it.goalentity);
- int num_control_points = Team_GetNumberOfControlPoints(team_);
+ int num_control_points = Team_GetNumberOfOwnedItems(team_);
++num_control_points;
- Team_SetNumberOfControlPoints(team_, num_control_points);
+ Team_SetNumberOfOwnedItems(team_, num_control_points);
});
}
-int Domination_GetWinnerTeam()
-{
- int winner_team = 0;
- if (Team_GetNumberOfControlPoints(Team_GetTeamFromIndex(1)) ==
- total_control_points)
- {
- winner_team = NUM_TEAM_1;
- }
- for (int i = 2; i <= NUM_TEAMS; ++i)
- {
- if (Team_GetNumberOfControlPoints(Team_GetTeamFromIndex(i)) ==
- total_control_points)
- {
- if (winner_team != 0)
- {
- return 0;
- }
- winner_team = Team_IndexToTeam(i);
- }
- }
- if (winner_team)
- {
- return winner_team;
- }
- return -1; // no control points left?
-}
-
bool Domination_CheckWinner()
{
if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
}
Domination_count_controlpoints();
-
- float winner_team = Domination_GetWinnerTeam();
-
- if(winner_team == -1)
- return false;
+ int winner_team = Team_GetWinnerTeam_WIthOwnedItems(total_control_points);
+ if (winner_team == -1)
+ return 0;
if(winner_team > 0)
{
Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN));
TeamScore_AddToTeam(winner_team, ST_DOM_CAPS, +1);
}
- else if(winner_team == -1)
- {
- Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED);
- Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED);
- }
game_stopped = true;
round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit);
});
FOREACH_CLIENT(IS_REAL_CLIENT(it),
{
- STAT(REDALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(
- 1));
- STAT(BLUEALIVE, it) = Team_GetNumberOfAlivePlayers(
- Team_GetTeamFromIndex(2));
- STAT(YELLOWALIVE, it) = Team_GetNumberOfAlivePlayers(
- Team_GetTeamFromIndex(3));
- STAT(PINKALIVE, it) = Team_GetNumberOfAlivePlayers(
- Team_GetTeamFromIndex(4));
+ STAT(REDALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(1));
+ STAT(BLUEALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(2));
+ STAT(YELLOWALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(3));
+ STAT(PINKALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(4));
});
eliminatedPlayers.SendFlags |= 1;
return false;
}
-int freezetag_getWinnerTeam()
-{
- int winner_team = 0;
- if (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(1)) >= 1)
- {
- winner_team = NUM_TEAM_1;
- }
- for (int i = 2; i <= NUM_TEAMS; ++i)
- {
- if (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(i)) >= 1)
- {
- if (winner_team != 0)
- {
- return 0;
- }
- winner_team = Team_IndexToTeam(i);
- }
- }
- if (winner_team)
- {
- return winner_team;
- }
- return -1; // no player left
-}
-
void nades_Clear(entity);
void nades_GiveBonus(entity player, float score);
return true;
}
- if (Team_GetNumberOfAliveTeams() > 1)
- {
+ int winner_team = Team_GetWinnerAliveTeam();
+ if (!winner_team)
return false;
- }
- int winner_team = freezetag_getWinnerTeam();
if(winner_team > 0)
{
Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
int autocvar_g_lms_extra_lives;
bool autocvar_g_lms_join_anytime;
int autocvar_g_lms_last_join;
+bool autocvar_g_lms_items;
bool autocvar_g_lms_regenerate;
// main functions
});
GameRules_scoring_add(player, LMS_RANK, pl_cnt + 1);
}
- else
+ else if (player.lmsplayer)
{
int min_forfeiter_rank = 665; // different from 666
- FOREACH_CLIENT(true, {
+ FOREACH_CLIENT(it != player, {
// update rank of other players that were eliminated
if (it.frags == FRAGS_PLAYER_OUT_OF_GAME)
{
MUTATOR_HOOKFUNCTION(lms, FilterItemDefinition)
{
+ if (autocvar_g_lms_items)
+ return false;
+
entity definition = M_ARGV(0, entity);
if (autocvar_g_lms_extra_lives && definition == ITEM_ExtraLife)
total_generators = 0;
for (int i = 1; i <= NUM_TEAMS; ++i)
{
- Team_SetNumberOfControlPoints(Team_GetTeamFromIndex(i), 0);
+ Team_SetNumberOfOwnedItems(Team_GetTeamFromIndex(i), 0);
}
for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext)
{
continue;
}
entity team_ = Entity_GetTeam(e);
- int num_control_points = Team_GetNumberOfControlPoints(team_);
- ++num_control_points;
- Team_SetNumberOfControlPoints(team_, num_control_points);
+ int num_generators = Team_GetNumberOfOwnedItems(team_);
+ ++num_generators;
+ Team_SetNumberOfOwnedItems(team_, num_generators);
}
}
-int Onslaught_GetWinnerTeam()
-{
- int winner_team = 0;
- if (Team_GetNumberOfControlPoints(Team_GetTeamFromIndex(1)) >= 1)
- {
- winner_team = NUM_TEAM_1;
- }
- for (int i = 2; i <= NUM_TEAMS; ++i)
- {
- if (Team_GetNumberOfControlPoints(Team_GetTeamFromIndex(i)) >= 1)
- {
- if (winner_team != 0)
- {
- return 0;
- }
- winner_team = Team_IndexToTeam(i);
- }
- }
- if (winner_team)
- {
- return winner_team;
- }
- return -1; // no generators left?
-}
-
void nades_Clear(entity e);
bool Onslaught_CheckWinner()
else { wpforenemy_announced = false; ons_stalemate = false; }
Onslaught_count_generators();
-
- if (Team_GetNumberOfTeamsWithControlPoints() > 1)
- {
+ int winner_team = Team_GetWinnerTeam_WIthOwnedItems(1);
+ if (!winner_team)
return 0;
- }
-
- int winner_team = Onslaught_GetWinnerTeam();
if(winner_team > 0)
{
//foobar
}
+// Deletes current playerstats DB, creates a new one and fully initializes it
+void PlayerStats_GameReport_Reset_All()
+{
+ strfree(PS_GR_OUT_TL);
+ strfree(PS_GR_OUT_PL);
+ strfree(PS_GR_OUT_EVL);
+
+ if (PS_GR_OUT_DB >= 0)
+ db_close(PS_GR_OUT_DB);
+ PlayerStats_GameReport_Init();
+ if(PS_GR_OUT_DB < 0)
+ return;
+
+ for (int i = 0; i < 16; i++)
+ if (teamscorekeepers[i])
+ PlayerStats_GameReport_AddTeam(i + 1);
+ FOREACH_CLIENT(true, {
+ // NOTE Adding back a player we are applying any cl_allow_uidtracking change
+ // usually only possible by reconnecting to the server
+ strfree(it.playerstats_id);
+ PlayerStats_GameReport_AddEvent(sprintf("kills-%d", it.playerid));
+ if (IS_BOT_CLIENT(it) || CS_CVAR(it).cvar_cl_allow_uidtracking)
+ PlayerStats_GameReport_AddPlayer(it);
+ });
+ FOREACH(Scores, true, {
+ string label = scores_label(it);
+ if (label == "")
+ continue;
+ PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_TOTAL, label));
+ PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_SCOREBOARD, label));
+ });
+ for(int i = 0; i < MAX_TEAMSCORE; ++i)
+ {
+ string label = teamscores_label(i);
+ if (label == "")
+ continue;
+ PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_TOTAL, label));
+ PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_SCOREBOARD, label));
+ }
+}
+
void PlayerStats_GameReport_AddPlayer(entity e)
{
if((PS_GR_OUT_DB < 0) || (e.playerstats_id)) { return; }
// delay map switch until this is set
bool PlayerStats_GameReport_DelayMapVote;
+void PlayerStats_GameReport_Reset_All();
+
// call at initialization
void PlayerStats_GameReport_Init();
//string autocvar_g_playerstats_uri;
string autocvar_g_playerstats_gamereport_ladder;
-string autocvar_g_playerstats_gamereport_uri = "http://stats.xonotic.org/stats/submit";
+string autocvar_g_playerstats_gamereport_uri = "https://stats.xonotic.org/stats/submit";
const float PS_B_STATUS_ERROR = -2;
const float PS_B_STATUS_IDLE = -1;
{
if (IS_CLIENT(caller))
{
- if (warmup_stage || autocvar_sv_ready_restart || g_race_qualifying == 2)
+ if (warmup_stage || g_race_qualifying == 2)
{
- if (!readyrestart_happened || autocvar_sv_ready_restart_repeatable)
+ if (time < game_starttime) // game is already restarting
+ return;
+ if (caller.ready) // toggle
{
- if (time < game_starttime) // game is already restarting
- return;
- if (caller.ready) // toggle
- {
- caller.ready = false;
- if(IS_PLAYER(caller) || caller.caplayer == 1)
- bprint(playername(caller.netname, caller.team, false), "^2 is ^1NOT^2 ready\n");
- }
- else
- {
- caller.ready = true;
- if(IS_PLAYER(caller) || caller.caplayer == 1)
- bprint(playername(caller.netname, caller.team, false), "^2 is ready\n");
- }
-
- // cannot reset the game while a timeout is active!
- if (!timeout_status) ReadyCount();
+ caller.ready = false;
+ if(IS_PLAYER(caller) || caller.caplayer == 1)
+ bprint(playername(caller.netname, caller.team, false), "^2 is ^1NOT^2 ready\n");
}
else
{
- sprint(caller, "^1Game has already been restarted\n");
+ caller.ready = true;
+ if(IS_PLAYER(caller) || caller.caplayer == 1)
+ bprint(playername(caller.netname, caller.team, false), "^2 is ready\n");
}
+
+ // cannot reset the game while a timeout is active!
+ if (!timeout_status) ReadyCount();
}
}
return; // never fall through to usage
float autocvar_sv_clientcommand_antispam_time;
int autocvar_sv_clientcommand_antispam_count;
-bool autocvar_sv_ready_restart;
-bool autocvar_sv_ready_restart_repeatable;
.float cmd_floodtime;
.float cmd_floodcount;
{
case CMD_REQUEST_COMMAND:
{
- ReadyRestart();
+ if(warmup_stage)
+ {
+ ReadyRestart(true);
+ }
+ else
+ LOG_INFO("Not in warmup.");
+
return;
}
}
}
+void GameCommand_resetmatch(int request)
+{
+ switch (request)
+ {
+ case CMD_REQUEST_COMMAND:
+ {
+ ReadyRestart(false);
+ return;
+ }
+
+ default:
+ case CMD_REQUEST_USAGE:
+ {
+ LOG_HELP("Usage:^3 sv_cmd resetmatch");
+ LOG_HELP(" No arguments required.");
+ return;
+ }
+ }
+}
+
void GameCommand_stuffto(int request, int argc)
{
// This... is a fairly dangerous and powerful command... - It allows any arguments to be sent to a client via rcon.
// Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
SERVER_COMMAND(adminmsg, "Send an admin message to a client directly") { GameCommand_adminmsg(request, arguments); }
-SERVER_COMMAND(allready, "Restart the server and reset the players") { GameCommand_allready(request); }
+SERVER_COMMAND(allready, "Ends warmup and starts the match") { GameCommand_allready(request); }
SERVER_COMMAND(allspec, "Force all players to spectate") { GameCommand_allspec(request, arguments); }
SERVER_COMMAND(anticheat, "Create an anticheat report for a client") { GameCommand_anticheat(request, arguments); }
SERVER_COMMAND(animbench, "Benchmark model animation (LAGS)") { GameCommand_animbench(request, arguments); }
SERVER_COMMAND(printstats, "Dump eventlog player stats and other score information") { GameCommand_printstats(request); }
SERVER_COMMAND(radarmap, "Generate a radar image of the map") { GameCommand_radarmap(request, arguments); }
SERVER_COMMAND(reducematchtime, "Decrease the timelimit value incrementally") { GameCommand_reducematchtime(request); }
+SERVER_COMMAND(resetmatch, "Soft restart the game without changing teams; goes back to warmup if enabled") { GameCommand_resetmatch(request); }
SERVER_COMMAND(setbots, "Adjust how many bots are in the match") { GameCommand_setbots(request, arguments); }
SERVER_COMMAND(shuffleteams, "Randomly move players to different teams") { GameCommand_shuffleteams(request); }
SERVER_COMMAND(stuffto, "Send a command to be executed on a client") { GameCommand_stuffto(request, arguments); }
// =======================
// Resets the state of all clients, items, weapons, waypoints, ... of the map.
-void reset_map(bool dorespawn)
+void reset_map(bool dorespawn, bool is_fake_round_start)
{
if (time <= game_starttime)
{
if (game_stopped)
return;
+
+ if (!is_fake_round_start)
+ PlayerStats_GameReport_Reset_All();
if (round_handler_IsActive())
round_handler_Reset(game_starttime);
}
{
if (!MUTATOR_CALLHOOK(reset_map_players))
{
- if (restart_mapalreadyrestarted || (time < game_starttime))
+ FOREACH_CLIENT(IS_PLAYER(it),
{
- FOREACH_CLIENT(IS_PLAYER(it),
- {
- /*
- only reset players if a restart countdown is active
- this can either be due to cvar sv_ready_restart_after_countdown having set
- restart_mapalreadyrestarted to 1 after the countdown ended or when
- sv_ready_restart_after_countdown is not used and countdown is still running
- */
- // PlayerScore_Clear(it);
- CS(it).killcount = 0;
- // stop the player from moving so that he stands still once he gets respawned
- it.velocity = '0 0 0';
- it.avelocity = '0 0 0';
- CS(it).movement = '0 0 0';
- PutClientInServer(it);
- });
- }
+ // PlayerScore_Clear(it);
+ CS(it).killcount = 0;
+ // stop the player from moving so that he stands still once he gets respawned
+ it.velocity = '0 0 0';
+ it.avelocity = '0 0 0';
+ CS(it).movement = '0 0 0';
+ PutClientInServer(it);
+ });
}
}
}
// Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
void ReadyRestart_think(entity this)
{
- restart_mapalreadyrestarted = true;
- reset_map(true);
+ reset_map(true, false);
Score_ClearAll();
delete(this);
}
// Forces a restart of the game without actually reloading the map // this is a mess...
-void ReadyRestart_force()
+void ReadyRestart_force(bool is_fake_round_start)
{
if (time <= game_starttime && game_stopped)
return;
-
- bprint("^1Server is restarting...\n");
+ if (!is_fake_round_start)
+ bprint("^1Match is restarting...\n");
VoteReset();
cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime)));
checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
- readyrestart_happened = true;
- game_starttime = time + RESTART_COUNTDOWN;
+ if(warmup_stage)
+ game_starttime = time; // Warmup: No countdown in warmup
+ else
+ game_starttime = time + RESTART_COUNTDOWN; // Go into match mode
// clear player attributes
FOREACH_CLIENT(IS_PLAYER(it), {
it.alivetime = 0;
CS(it).killcount = 0;
- float val = PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, 0);
- PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, -val);
});
- restart_mapalreadyrestarted = false; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
-
- // disable the warmup global for the server
- if(warmup_stage)
+ // if we're ending the warmup stage call the corresponding hook
+ if(!is_fake_round_start && !warmup_stage)
localcmd("\nsv_hook_warmupend\n");
- warmup_stage = 0; // once the game is restarted the game is in match stage
// reset the .ready status of all players (also spectators)
FOREACH_CLIENT(IS_REAL_CLIENT(it), { it.ready = false; });
// lock teams with lockonrestart
if (autocvar_teamplay_lockonrestart && teamplay)
- {
- lockteams = true;
- bprint("^1The teams are now locked.\n");
- }
+ lockteams = !warmup_stage;
// initiate the restart-countdown-announcer entity
- if (sv_ready_restart_after_countdown)
+ if (!is_fake_round_start && sv_ready_restart_after_countdown && !warmup_stage)
{
entity restart_timer = new_pure(restart_timer);
setthink(restart_timer, ReadyRestart_think);
FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
}
- if (!sv_ready_restart_after_countdown) reset_map(true);
+ if (!sv_ready_restart_after_countdown || warmup_stage) reset_map(true, is_fake_round_start);
if (autocvar_sv_eventlog) GameLogEcho(":restart");
}
-void ReadyRestart()
+void ReadyRestart(bool forceWarmupEnd)
{
if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || intermission_running || race_completing) localcmd("restart\n");
else localcmd("\nsv_hook_readyrestart\n");
// Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
// Otherwise scores could be manipulated during the countdown.
if (!sv_ready_restart_after_countdown) Score_ClearAll();
- ReadyRestart_force();
+
+ if(forceWarmupEnd)
+ warmup_stage = 0; // forcefully end warmup and go to match stage
+ else
+ warmup_stage = cvar("g_warmup"); // go into warmup if it's enabled, otherwise restart into match stage
+
+ ReadyRestart_force(false);
}
// Count the players who are ready and determine whether or not to restart the match
ready_needed_factor = bound(0.5, cvar("g_warmup_majority_factor"), 0.999);
ready_needed_count = floor(t_players * ready_needed_factor) + 1;
- if (readycount >= ready_needed_count) ReadyRestart();
+ if (readycount >= ready_needed_count) ReadyRestart(true);
}
}
case "restart":
+ case "resetmatch": // re-direct all match restarting to resetmatch
{
- // add a delay so that vote result can be seen and announcer can be heard
- // if the vote is accepted
- vote_parsed_command = strcat("defer 1 ", vote_command);
- vote_parsed_display = strzone(strcat("^1", vote_command));
+ vote_parsed_command = "resetmatch";
+ vote_parsed_display = strzone("^1resetmatch");
+
+ break;
+ }
+ case "allready":
+ {
+ if(!warmup_stage) {
+ print_to(caller, "Game already started. Use the resetmatch command to restart the match.");
+ return -1;
+ }
+
+ vote_parsed_command = vote_command;
+ vote_parsed_display = strzone(strcat("^1", vote_command));
break;
}
const float RESTART_COUNTDOWN = 10;
entity nagger;
float readycount; // amount of players who are ready
-float readyrestart_happened; // keeps track of whether a restart has already happened
-float restart_mapalreadyrestarted; // bool, indicates whether reset_map() was already executed
.float ready; // flag for if a player is ready
.int team_saved; // team number to restore upon map reset
.void(entity this) reset; // if set, an entity is reset using this
.void(entity this) reset2; // if set, an entity is reset using this (after calling ALL the reset functions for other entities)
-void reset_map(float dorespawn);
+void reset_map(float dorespawn, bool is_fake_round_start);
void ReadyCount();
-void ReadyRestart_force();
+void ReadyRestart_force(bool is_fake_round_start);
void VoteCount(float first_count);
void Nagger_Init();
CreatureFrame_All();
CheckRules_World();
- if (warmup_stage && !game_stopped && warmup_limit > 0 && time >= warmup_limit) {
- ReadyRestart();
+ if (warmup_stage && !game_stopped && warmup_limit > 0 && time - game_starttime >= warmup_limit) {
+ ReadyRestart(true);
return;
}
this.wait = false;
this.cnt = this.count + 1; // init countdown
round_starttime = time + this.count;
- reset_map(true);
+ reset_map(true, false);
}
if (this.cnt > 0) // countdown running
.int m_num_players; ///< Number of players (both humans and bots) in a team.
.int m_num_bots; ///< Number of bots in a team.
.int m_num_players_alive; ///< Number of alive players in a team.
-.int m_num_control_points; ///< Number of control points owned by a team.
+.int m_num_owned_items; ///< Number of items owned by a team.
string autocvar_g_forced_team_red;
string autocvar_g_forced_team_blue;
team_ent.m_num_players_alive = number;
}
+int Team_GetWinnerAliveTeam()
+{
+ int winner = 0;
+ for (int i = 0; i < NUM_TEAMS; ++i)
+ {
+ if (g_team_entities[i].m_num_players_alive > 0)
+ {
+ if (winner)
+ return 0;
+ winner = Team_IndexToTeam(i + 1);
+ }
+ }
+ return (winner ? winner : -1);
+}
+
int Team_GetNumberOfAliveTeams()
{
int result = 0;
return result;
}
-int Team_GetNumberOfControlPoints(entity team_ent)
+int Team_GetWinnerTeam_WIthOwnedItems(int min_control_points)
+{
+ int winner = 0;
+ for (int i = 0; i < NUM_TEAMS; ++i)
+ {
+ if (g_team_entities[i].m_num_owned_items >= min_control_points)
+ {
+ if (winner)
+ return 0;
+ winner = Team_IndexToTeam(i + 1);
+ }
+ }
+ return (winner ? winner : -1);
+}
+
+int Team_GetNumberOfOwnedItems(entity team_ent)
{
- return team_ent.m_num_control_points;
+ return team_ent.m_num_owned_items;
}
-void Team_SetNumberOfControlPoints(entity team_ent, int number)
+void Team_SetNumberOfOwnedItems(entity team_ent, int number)
{
- team_ent.m_num_control_points = number;
+ team_ent.m_num_owned_items = number;
}
-int Team_GetNumberOfTeamsWithControlPoints()
+int Team_GetNumberOfTeamsWithOwnedItems()
{
int result = 0;
for (int i = 0; i < NUM_TEAMS; ++i)
{
- if (g_team_entities[i].m_num_control_points > 0)
+ if (g_team_entities[i].m_num_owned_items > 0)
{
++result;
}
/// \param[in] number Number of players to set.
void Team_SetNumberOfAlivePlayers(entity team_ent, int number);
+/// \brief Returns the winner team.
+/// \return Winner team or 0 if 2 or more teams have alive players or -1 if no team has any alive players.
+int Team_GetWinnerAliveTeam();
+
/// \brief Returns the number of alive teams.
/// \return Number of alive teams.
int Team_GetNumberOfAliveTeams();
-/// \brief Returns the number of control points owned by a team.
+/// \brief Returns the winner team.
+/// \param[in] min_owned_items Minimum number of items the winner team must have.
+/// \return Winner team or 0 if 2 or more teams own items or -1 if no team own any items.
+int Team_GetWinnerTeam_WIthOwnedItems(int min_owned_items);
+
+/// \brief Returns the number of items owned by a team.
/// \param[in] team_ent Team entity.
-/// \return Number of control points owned by a team.
-int Team_GetNumberOfControlPoints(entity team_ent);
+/// \return Number of items owned by a team.
+int Team_GetNumberOfOwnedItems(entity team_ent);
-/// \brief Sets the number of control points owned by a team.
+/// \brief Sets the number of items owned by a team.
/// \param[in,out] team_ent Team entity.
-/// \param[in] number Number of control points to set.
-void Team_SetNumberOfControlPoints(entity team_ent, int number);
+/// \param[in] number Number of items to set.
+void Team_SetNumberOfOwnedItems(entity team_ent, int number);
-/// \brief Returns the number of teams that own control points.
-/// \return Number of teams that own control points.
-int Team_GetNumberOfTeamsWithControlPoints();
+/// \brief Returns the number of teams that own items.
+/// \return Number of teams that own items.
+int Team_GetNumberOfTeamsWithOwnedItems();
// ======================= Entity specific API ================================
BADCVAR("sv_maxrate");
BADCVAR("sv_motd");
BADCVAR("sv_public");
- BADCVAR("sv_ready_restart");
BADCVAR("sv_showfps");
BADCVAR("sv_status_privacy");
BADCVAR("sv_taunt");
GameLogClose();
- FOREACH_CLIENT(IS_PLAYER(it), {
+ int winner_team = 0;
+ FOREACH_CLIENT(IS_PLAYER(it) || it.caplayer, {
FixIntermissionClient(it);
if(it.winning)
- bprint(playername(it.netname, it.team, false), " ^7wins.\n");
+ {
+ if (teamplay && !winner_team)
+ {
+ winner_team = it.team;
+ bprint(Team_ColorCode(winner_team), Team_ColorName_Upper(winner_team), "^7 team wins the match\n");
+ }
+ bprint(playername(it.netname, it.team, false), " ^7wins\n");
+ }
});
target_music_kill();
// set the .winning flag for exactly those players with a given field value
void SetWinners(.float field, float value)
{
- FOREACH_CLIENT(IS_PLAYER(it), { it.winning = (it.(field) == value); });
+ FOREACH_CLIENT(IS_PLAYER(it) || it.caplayer, { it.winning = (it.(field) == value); });
}
// set the .winning flag for those players with a given field value
void AddWinners(.float field, float value)
{
- FOREACH_CLIENT(IS_PLAYER(it), {
+ FOREACH_CLIENT(IS_PLAYER(it) || it.caplayer, {
if(it.(field) == value)
it.winning = 1;
});
// clear the .winning flags
void ClearWinners()
{
- FOREACH_CLIENT(IS_PLAYER(it), { it.winning = 0; });
+ FOREACH_CLIENT(IS_PLAYER(it) || it.caplayer, { it.winning = 0; });
}
int fragsleft_last;
if(readyplayers || playerswithlaps >= 2)
{
checkrules_suddendeathend = 0;
- ReadyRestart(); // go to race
+ ReadyRestart(true); // go to race
return;
}
else
float WinningCondition_Scores(float limit, float leadlimit);
void SetWinners(.float field, float value);
-void ReadyRestart();
+void ReadyRestart(bool forceWarmupEnd);
void DumpStats(float final);
set minplayers_per_team 0 "fill server with bots to reach this number of players per team (if bot_number is not enough)"
// restart server if all players hit "ready"-button
-set sv_ready_restart 0 "allow a map to be restarted once all players pressed the \"ready\" button"
set sv_ready_restart_after_countdown 0 "reset players and map items after the countdown ended, instead of at the beginning of the countdown"
-set sv_ready_restart_repeatable 0 "allows the players to restart the game as often as needed"
alias sv_hook_readyrestart
set g_frozen_force 0.6 "How much to multiply the force on a frozen player with"
// player statistics
-set g_playerstats_gamereport_uri "http://stats.xonotic.org/stats/submit" "Output player statistics information to either: URL (with ://), console (with a dash like this: -), or supply a filename to output to data directory."
+set g_playerstats_gamereport_uri "https://stats.xonotic.org/stats/submit" "Output player statistics information to either: URL (with ://), console (with a dash like this: -), or supply a filename to output to data directory."
set g_playerstats_gamereport_ladder ""
set g_playerstats_playerbasic_uri "http://stats.xonotic.org"
set g_playerstats_playerdetail_uri "http://stats.xonotic.org/player/me"