X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fcommand%2Fvote.qc;h=3367ef310c2e1eb5d97852b34964f1aa5188ab02;hb=613663cc624d93b575bbb2e1402ab673d94d02c7;hp=097b7e1331e8a392d81156d5ef321e2e30fee6a6;hpb=3c29496e2c6d820a8f7d4af19b9567b9f4ba487c;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index 097b7e133..3367ef310 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include // ============================================= @@ -335,12 +337,19 @@ 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) + { + Score_ClearAll(); + PlayerStats_GameReport_Reset_All(); + } + if (round_handler_IsActive()) round_handler_Reset(game_starttime); } @@ -351,8 +360,11 @@ void reset_map(bool dorespawn) shuffleteams_on_reset_map = false; } - FOREACH_CLIENT(IS_PLAYER(it), - { + FOREACH_CLIENT(true, { + if (time <= game_starttime) + accuracy_reset(it); // for spectators too because weapon accuracy is persistent + if (!IS_PLAYER(it)) + continue; if (STAT(FROZEN, it)) Unfreeze(it, false); player_powerups_remove_all(it); @@ -407,28 +419,29 @@ 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) { - reset_map(true); - Score_ClearAll(); + reset_map(true, false); 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(); // clear overtime, we have to decrease timelimit to its original value again. if (checkrules_overtimesadded > 0 && g_race_qualifying != 2) cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime))); - checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0; - + checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = overtimes = 0; + if(warmup_stage) game_starttime = time; // Warmup: No countdown in warmup + else if (autocvar_g_campaign) + game_starttime = time + 3; else game_starttime = time + RESTART_COUNTDOWN; // Go into match mode @@ -436,12 +449,10 @@ void ReadyRestart_force() 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); }); - // 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"); // reset the .ready status of all players (also spectators) @@ -454,7 +465,7 @@ void ReadyRestart_force() lockteams = !warmup_stage; // initiate the restart-countdown-announcer entity - if (sv_ready_restart_after_countdown && !warmup_stage) + if (!is_fake_round_start && sv_ready_restart_after_countdown && !warmup_stage) { entity restart_timer = new_pure(restart_timer); setthink(restart_timer, ReadyRestart_think); @@ -467,42 +478,71 @@ 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 || warmup_stage) 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(bool endWarmup) +void ReadyRestart(bool forceWarmupEnd) { - if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || intermission_running || race_completing) localcmd("restart\n"); + if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || intermission_running || race_completing) + { + // NOTE: ReadyRestart support is mandatory in campaign + if (autocvar_g_campaign) + error("ReadyRestart must be supported in campaign mode!"); + localcmd("restart\n"); // if ReadyRestart is denied, restart the server + } 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(); - - if(endWarmup) + if(forceWarmupEnd || autocvar_g_campaign) 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 anyway + warmup_stage = cvar("g_warmup"); // go into warmup if it's enabled, otherwise restart into match stage - ReadyRestart_force(); + ReadyRestart_force(false); } -// Count the players who are ready and determine whether or not to restart the match +/* Count the players who are ready and determine whether or not to restart the match when: + * a player presses F4 server/command/cmd.qc ClientCommand_ready() + * a player switches from players to specs server/client.qc PutObserverInServer() + * a player joins (from specs or directly) server/client.qc PutPlayerInServer() + * a player disconnects server/client.qc ClientDisconnect() */ void ReadyCount() { + // cannot reset the game while a timeout is active or pending + if (timeout_status) return; + float ready_needed_factor, ready_needed_count; - float t_ready = 0, t_players = 0; + float t_players = 0; + readycount = 0; - FOREACH_CLIENT(IS_REAL_CLIENT(it) && (IS_PLAYER(it) || it.caplayer == 1), { + FOREACH_CLIENT(IS_REAL_CLIENT(it) && (IS_PLAYER(it) || INGAME_JOINED(it)), { ++t_players; - if (it.ready) ++t_ready; + if (it.ready) ++readycount; }); - readycount = t_ready; - Nagger_ReadyCounted(); + if (t_players < map_minplayers) // map_minplayers will only be set if g_warmup -1 at worldspawn + { + // TODO: handle player spectating/disconnecting during countdown + if (warmup_limit > 0) + warmup_limit = -1; + return; // don't ReadyRestart if players are ready but too few + } + else if (map_minplayers && warmup_limit <= 0) + { + // there's enough players now but we're still in infinite warmup + warmup_limit = cvar("g_warmup_limit"); + if (warmup_limit == 0) + warmup_limit = autocvar_timelimit * 60; + if (warmup_limit > 0) + game_starttime = time; + // implicit else: g_warmup -1 && g_warmup_limit -1 means + // warmup continues until enough players AND enough RUPs (no time limit) + } + ready_needed_factor = bound(0.5, cvar("g_warmup_majority_factor"), 0.999); ready_needed_count = floor(t_players * ready_needed_factor) + 1; @@ -768,7 +808,7 @@ int VoteCommand_parse(entity caller, string vote_command, string vote_list, floa case "allready": { if(!warmup_stage) { - print_to(caller, "Game already started. Use the reset command to restart the match."); + print_to(caller, "Game already started. Use the resetmatch command to restart the match."); return -1; }