]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'z411/timer_bound' into 'master'
authorterencehill <piuntn@gmail.com>
Sun, 2 Jan 2022 18:43:48 +0000 (18:43 +0000)
committerterencehill <piuntn@gmail.com>
Sun, 2 Jan 2022 18:43:48 +0000 (18:43 +0000)
Added upper bound for decreasing timer

See merge request xonotic/xonotic-data.pk3dir!960

23 files changed:
commands.cfg
gamemodes-server.cfg
qcsrc/client/hud/panel/timer.qc
qcsrc/common/gamemodes/gamemode/assault/sv_assault.qc
qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc
qcsrc/common/gamemodes/gamemode/domination/sv_domination.qc
qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qc
qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc
qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc
qcsrc/common/playerstats.qc
qcsrc/common/playerstats.qh
qcsrc/server/command/cmd.qc
qcsrc/server/command/cmd.qh
qcsrc/server/command/sv_cmd.qc
qcsrc/server/command/vote.qc
qcsrc/server/command/vote.qh
qcsrc/server/main.qc
qcsrc/server/round_handler.qc
qcsrc/server/teamplay.qc
qcsrc/server/teamplay.qh
qcsrc/server/world.qc
qcsrc/server/world.qh
xonotic-server.cfg

index 60ffbced2ba90c275e2ea8b0ce0a1a673132d101..a6162badf4037b12ecdc6a15bd90b55b8e3e44d2 100644 (file)
@@ -229,6 +229,7 @@ alias nospectators         "qc_cmd_sv     nospectators         ${* ?}" // Automa
 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
@@ -307,6 +308,7 @@ set sv_vote_command_restriction_endmatch "0"
 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"
@@ -327,7 +329,7 @@ set sv_vote_command_help_gotomap "\nUsage:^3 vcall gotomap mapname\n^7  Where 'm
 // =================================
 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)
@@ -377,4 +379,4 @@ alias vdoend "vdo endmatch"
 //  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\""
index 17b90c624fde8f8af9a50534ad5e69fae6e27ead..62570e5239e86bbd3b749efe7b20b036d67abec2 100644 (file)
@@ -448,6 +448,7 @@ set g_lms_extra_lives 0
 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"
 
 
index 75afc363f0b35fbe460168ca699da6b2de405375..035d21b60d9c889e00209f19076631aafc5251e5 100644 (file)
@@ -51,7 +51,7 @@ void HUD_Timer()
        {
                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);
index f5683051c4694151c9923d4e738cbacbacfd0006..5edfd5ff1863362ecaf53354431793598c11e228 100644 (file)
@@ -191,8 +191,6 @@ void assault_wall_think(entity this)
 // 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;
 
@@ -212,7 +210,8 @@ void assault_new_round(entity this)
 
        // 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;
@@ -251,7 +250,7 @@ int WinningCondition_Assault()
        {
                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));
index d40c04c0b658a63087c801a92d607bf20dd9493d..3d0c292617f9596d0c0ce3c68d9cf374e23de8e5 100644 (file)
@@ -33,42 +33,13 @@ void CA_count_alive_players()
        });
        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()
@@ -86,12 +57,10 @@ 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));
index ca782cedbb87f04f3ee45fa1f6ad5eddb7519b80..80bbf6532a436011640e16ce6546f30172f8f090 100644 (file)
@@ -311,7 +311,7 @@ void Domination_count_controlpoints()
        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,
        {
@@ -321,39 +321,12 @@ void Domination_count_controlpoints()
                        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)
@@ -367,11 +340,9 @@ bool Domination_CheckWinner()
        }
 
        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)
        {
@@ -379,11 +350,6 @@ bool Domination_CheckWinner()
                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);
index 5712c82c980cab2cfa9345f72d284da0301fef6e..58fd90c897f15213ce6b686e9a5b402df00c19d6 100644 (file)
@@ -40,14 +40,10 @@ void freezetag_count_alive_players()
        });
        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;
@@ -87,31 +83,6 @@ bool freezetag_CheckTeams()
        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);
 
@@ -131,12 +102,10 @@ bool freezetag_CheckWinner()
                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));
index 8f545955caefc20137883d1d15f9c066ec610eb5..51a352a67095f911e0fbe5e4b401fc5b2cf985a7 100644 (file)
@@ -9,6 +9,7 @@
 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
@@ -265,10 +266,10 @@ void lms_RemovePlayer(entity player)
                        });
                        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)
                                {
@@ -414,6 +415,9 @@ MUTATOR_HOOKFUNCTION(lms, ForbidPlayerScore_Clear)
 
 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)
index db39a8a39513a3d80c97816ad3a2bf08d3c1cb94..c071be0e0e63108817b641c71402885dd2d92b67 100644 (file)
@@ -1126,7 +1126,7 @@ void Onslaught_count_generators()
        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)
        {
@@ -1136,37 +1136,12 @@ void Onslaught_count_generators()
                        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()
@@ -1213,13 +1188,9 @@ 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)
        {
index c11efed8b78af9868d79c635bc0761fe483e7c62..e648d29538ce507d0f524b780bc941b5e4e6f09f 100644 (file)
@@ -21,6 +21,47 @@ void PlayerStats_Prematch()
        //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; }
index d41a097d6d642419ecd4f8218e071774ca516f47..39ffab623ef94d5f3790d2e7e9693efa75528ecc 100644 (file)
@@ -56,6 +56,8 @@ const string PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM = "achievement-firstvictim";
 // delay map switch until this is set
 bool PlayerStats_GameReport_DelayMapVote;
 
+void PlayerStats_GameReport_Reset_All();
+
 // call at initialization
 void PlayerStats_GameReport_Init();
 
@@ -88,7 +90,7 @@ void PlayerStats_GameReport_Handler(entity fh, entity pass, float status);
 //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;
index a8a8747e1822558f0b3c572b58a5d105b7d4c773..7241b7415dbb4487035fdca152c73ac2c99bfca8 100644 (file)
@@ -374,32 +374,25 @@ void ClientCommand_ready(entity caller, int request)  // todo: anti-spam for tog
                {
                        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
index f2f72157140916a6e483117cee46eabbab424fc7..802afc8bde6f86e1ed3e145df61f111ea51ec715 100644 (file)
@@ -2,8 +2,6 @@
 
 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;
index 0f97fbe6b5e368b2a1d3952b30d3df776688def6..daa59b5cf7bed78d48d035326644f82061460b0a 100644 (file)
@@ -162,7 +162,13 @@ void GameCommand_allready(int request)
        {
                case CMD_REQUEST_COMMAND:
                {
-                       ReadyRestart();
+                       if(warmup_stage)
+                       {
+                               ReadyRestart(true);
+                       }
+                       else
+                               LOG_INFO("Not in warmup.");
+
                        return;
                }
 
@@ -1308,6 +1314,26 @@ void GameCommand_shuffleteams(int request)
        }
 }
 
+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.
@@ -1629,7 +1655,7 @@ void GameCommand_(int request)
 
 // 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); }
@@ -1652,6 +1678,7 @@ SERVER_COMMAND(nospectators, "Automatically remove spectators from a match") { G
 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); }
index 09e277116a9efcc8c180355a9ee836a0eac9a039..f8345bd83a8ebf783491420d544e50e39cf65272 100644 (file)
@@ -336,12 +336,15 @@ void VoteThink()
 // =======================
 
 // 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);
        }
@@ -394,25 +397,16 @@ void reset_map(bool dorespawn)
        {
                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);
+                       });
                }
        }
 }
@@ -420,19 +414,18 @@ void reset_map(bool dorespawn)
 // 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();
 
@@ -441,23 +434,20 @@ void ReadyRestart_force()
                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; });
@@ -466,13 +456,10 @@ void ReadyRestart_force()
 
        // 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);
@@ -485,11 +472,11 @@ void ReadyRestart_force()
                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");
@@ -497,7 +484,13 @@ void ReadyRestart()
        // 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
@@ -518,7 +511,7 @@ void ReadyCount()
        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);
 }
 
 
@@ -768,12 +761,23 @@ int VoteCommand_parse(entity caller, string vote_command, string vote_list, floa
                }
 
                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;
                }
 
index 4dfd9e8575f2abf22c9cdc1e639171ca64988e0e..63c9e8e453765892bff4823cc41de0e7865e1453 100644 (file)
@@ -63,15 +63,13 @@ void VoteCommand(int request, entity caller, int argc, string vote_command);
 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();
 
index cf91b9de4bafd7d6b250a860732702a82222f409..2b29422b8969362356b6a552106158c9b881d0e7 100644 (file)
@@ -331,8 +331,8 @@ void StartFrame()
        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;
        }
 
index 93c3835271a049b796eaab6a2ff4911ebfe7b6fd..5307eca782a827398f66eed801fdd73682d458b4 100644 (file)
@@ -28,7 +28,7 @@ void round_handler_Think(entity this)
                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
index c26a0b16550ac51062a40c742468d5f93f214076..7257156a9693b44b28b4b2ed7698d11702f507eb 100644 (file)
@@ -35,7 +35,7 @@ const int TEAM_NOT_ALLOWED = -1;
 .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;
@@ -92,6 +92,21 @@ void Team_SetNumberOfAlivePlayers(entity team_ent, int number)
        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;
@@ -105,22 +120,37 @@ int Team_GetNumberOfAliveTeams()
        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;
                }
index 06787c6ffa06c72ec593b4429560949c99e74996..a94b247a8a64150d78ba6c1ca52200ff16b70350 100644 (file)
@@ -51,23 +51,32 @@ int Team_GetNumberOfAlivePlayers(entity team_ent);
 /// \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 ================================
 
index 728c9d757e6ffa77ad2f1edfc0637492dc2c8c0f..580073830f81f48f682669399eb4f8bb6c51387b 100644 (file)
@@ -494,7 +494,6 @@ void cvar_changes_init()
                BADCVAR("sv_maxrate");
                BADCVAR("sv_motd");
                BADCVAR("sv_public");
-               BADCVAR("sv_ready_restart");
                BADCVAR("sv_showfps");
                BADCVAR("sv_status_privacy");
                BADCVAR("sv_taunt");
@@ -1300,10 +1299,18 @@ void NextLevel()
 
        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();
@@ -1383,13 +1390,13 @@ float GetWinningCode(float fraglimitreached, float equality)
 // 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;
        });
@@ -1398,7 +1405,7 @@ void AddWinners(.float field, float value)
 // 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;
@@ -1645,7 +1652,7 @@ void CheckRules_World()
                                if(readyplayers || playerswithlaps >= 2)
                                {
                                        checkrules_suddendeathend = 0;
-                                       ReadyRestart(); // go to race
+                                       ReadyRestart(true); // go to race
                                        return;
                                }
                                else
index bdaac8c271e5ae08f79af6ba5f32e02fa8fb717c..3f6b9b6d22d676abd06947c077560d28e6924307 100644 (file)
@@ -134,7 +134,7 @@ const int WINNING_STARTSUDDENDEATHOVERTIME = 3; // no winner, enter suddendeath
 
 float WinningCondition_Scores(float limit, float leadlimit);
 void SetWinners(.float field, float value);
-void ReadyRestart();
+void ReadyRestart(bool forceWarmupEnd);
 
 void DumpStats(float final);
 
index 1f7a0c088e22c01b973d94758a54bdbeb1c3829e..0f374902a0b86be3fceab9e67128f7e2afd18479 100644 (file)
@@ -15,9 +15,7 @@ set minplayers 0 "fill server with bots to reach this number of players in teaml
 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
 
@@ -548,7 +546,7 @@ set g_frozen_damage_trigger 1 "if 1, frozen players falling into the void/lava/s
 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"