From 79c7ade31c3ce7e62d3c358969e4ac2ce5116ab5 Mon Sep 17 00:00:00 2001 From: Spookerton Date: Fri, 12 Jan 2024 15:36:35 +0000 Subject: [PATCH] transfer controller -> ssroundend - added empty_round_check_interval config option This periodic check ends running rounds with no living players. Defaults off. - added transfer_vote_block_antag_time config option Allows configuration of the previously hardcoded 20 minute block period before each vote for adding new antagonists. Defaults 20 minutes. - adjusted behavior of round end test Rounds can continue indefinitely when no time-based end conditions are set. - corrected admin verb for setting max round length --- baystation12.dme | 2 +- code/controllers/autotransfer.dm | 28 -------- code/controllers/configuration.dm | 45 +++++++++--- .../subsystems/initialization/misc.dm | 1 - code/controllers/subsystems/roundend.dm | 68 +++++++++++++++++++ code/controllers/verbs.dm | 2 - code/datums/vote/add_antag.dm | 4 +- code/game/gamemodes/game_mode_latespawn.dm | 4 +- code/modules/admin/admin.dm | 43 +++++++----- code/modules/admin/admin_verbs.dm | 4 +- code/modules/mob/new_player/new_player.dm | 6 +- config/example/config.txt | 14 ++-- 12 files changed, 150 insertions(+), 71 deletions(-) delete mode 100644 code/controllers/autotransfer.dm create mode 100644 code/controllers/subsystems/roundend.dm diff --git a/baystation12.dme b/baystation12.dme index f86d44798bdce..a1c730aeb1d6c 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -166,7 +166,6 @@ #include "code\_onclick\hud\screen_objects.dm" #include "code\_onclick\hud\skybox.dm" #include "code\controllers\admin.dm" -#include "code\controllers\autotransfer.dm" #include "code\controllers\communications.dm" #include "code\controllers\configuration.dm" #include "code\controllers\controller.dm" @@ -217,6 +216,7 @@ #include "code\controllers\subsystems\plants.dm" #include "code\controllers\subsystems\presence.dm" #include "code\controllers\subsystems\radiation.dm" +#include "code\controllers\subsystems\roundend.dm" #include "code\controllers\subsystems\shuttle.dm" #include "code\controllers\subsystems\skybox.dm" #include "code\controllers\subsystems\spacedrift.dm" diff --git a/code/controllers/autotransfer.dm b/code/controllers/autotransfer.dm deleted file mode 100644 index f5097be3152f8..0000000000000 --- a/code/controllers/autotransfer.dm +++ /dev/null @@ -1,28 +0,0 @@ -var/global/datum/controller/transfer_controller/transfer_controller - -/datum/controller/transfer_controller - var/timerbuffer = 0 //buffer for time check - var/do_continue_vote = TRUE - -/datum/controller/transfer_controller/New() - timerbuffer = config.vote_autotransfer_initial - START_PROCESSING(SSprocessing, src) - -/datum/controller/transfer_controller/Destroy() - STOP_PROCESSING(SSprocessing, src) - . = ..() - -/datum/controller/transfer_controller/Process() - if (time_till_transfer_vote() <= 0) - - if (config.maximum_round_length > 0 && round_duration_in_ticks >= config.maximum_round_length) - init_autotransfer() - else if (do_continue_vote) - SSvote.initiate_vote(/datum/vote/transfer, automatic = 1) - else - init_autotransfer() - - timerbuffer += config.vote_autotransfer_interval - -/datum/controller/transfer_controller/proc/time_till_transfer_vote() - return timerbuffer - round_duration_in_ticks - (1 MINUTE) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 329a3dc0687a5..e37ae97854545 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -81,11 +81,17 @@ /// length of voting period (deciseconds, default 1 minute) var/static/vote_period = 600 - /// Length of time before the first autotransfer vote is called - var/static/vote_autotransfer_initial = 120 MINUTES + /// Time in minutes between checks for ending empty rounds + var/static/empty_round_check_interval = 0 - /// length of time before next sequential autotransfer vote - var/static/vote_autotransfer_interval = 30 MINUTES + /// Time in minutes before the first autotransfer vote + var/static/vote_autotransfer_initial = 120 + + /// Time in minutes before each following autotransfer vote + var/static/vote_autotransfer_interval = 30 + + /// Time in minutes before transfer votes where antagonists cannot be added + var/static/transfer_vote_block_antag_time = 20 /// Length of time before round start when autogamemode vote is called (in seconds, default 100). var/static/vote_autogamemode_timeleft = 100 @@ -552,20 +558,38 @@ var/list/values = splittext(value, ";") var/len = length(values) if (len == 7) - vote_autotransfer_initial = text2num(values[get_weekday_index()]) MINUTES + vote_autotransfer_initial = text2num_or_default(values[get_weekday_index()]) else if (len == 1) - vote_autotransfer_initial = text2num(value) MINUTES + vote_autotransfer_initial = text2num_or_default(value) else log_misc("Invalid vote_autotransfer_initial: [value]") + vote_autotransfer_initial = 0 + if (vote_autotransfer_initial == null || vote_autotransfer_initial < 0) + log_misc("Invalid vote_autotransfer_initial: [value]") + vote_autotransfer_initial = 0 if ("vote_autotransfer_interval") var/list/values = splittext(value, ";") var/len = length(values) if (len == 7) - vote_autotransfer_interval = text2num(values[get_weekday_index()]) MINUTES + vote_autotransfer_interval = text2num_or_default(values[get_weekday_index()]) else if (len == 1) - vote_autotransfer_interval = text2num(value) MINUTES + vote_autotransfer_interval = text2num_or_default(value) else log_misc("Invalid vote_autotransfer_interval: [value]") + vote_autotransfer_interval = 0 + if (vote_autotransfer_interval == null || vote_autotransfer_interval < 0) + log_misc("Invalid vote_autotransfer_interval: [value]") + vote_autotransfer_interval = 0 + if ("transfer_vote_block_antag_time") + transfer_vote_block_antag_time = text2num_or_default(value) + if (transfer_vote_block_antag_time == null || transfer_vote_block_antag_time < 0) + log_misc("Invalid transfer_vote_block_antag_time: [value]") + transfer_vote_block_antag_time = 0 + if ("empty_round_check_interval") + empty_round_check_interval = text2num_or_default(value) + if (empty_round_check_interval == null || empty_round_check_interval < 0) + log_misc("Invalid empty_round_check_interval: [value]") + empty_round_check_interval = 0 if ("vote_autogamemode_timeleft") vote_autogamemode_timeleft = text2num(value) if ("pre_game_time") @@ -834,7 +858,10 @@ if ("log_timers_on_bucket_reset") log_timers_on_bucket_reset = TRUE if ("maximum_round_length") - maximum_round_length = text2num(value) MINUTES + maximum_round_length = text2num_or_default(value) + if (maximum_round_length == null || maximum_round_length < 0) + log_misc("Invalid maximum_round_length: [value]") + maximum_round_length = 0 if ("stat_delay") stat_delay = floor(text2num(value)) if ("warn_autoban_threshold") diff --git a/code/controllers/subsystems/initialization/misc.dm b/code/controllers/subsystems/initialization/misc.dm index 281886f4bfb8c..a92b27103e252 100644 --- a/code/controllers/subsystems/initialization/misc.dm +++ b/code/controllers/subsystems/initialization/misc.dm @@ -18,7 +18,6 @@ SUBSYSTEM_DEF(init_misc) initialize_pipe_datum_category_list() populate_robolimb_list() setupgenetics() - transfer_controller = new /datum/controller/subsystem/init_misc/proc/init_antags() diff --git a/code/controllers/subsystems/roundend.dm b/code/controllers/subsystems/roundend.dm new file mode 100644 index 0000000000000..a692ff318c11b --- /dev/null +++ b/code/controllers/subsystems/roundend.dm @@ -0,0 +1,68 @@ +SUBSYSTEM_DEF(roundend) + name = "Round End" + wait = 30 SECONDS + flags = SS_BACKGROUND + runlevels = RUNLEVEL_GAME + + /// The time in minutes when the round will be ended. + var/static/max_length + + /// The next time in minutes to check whether a round is empty. + var/static/empty_check + + /// The next time in minutes to start a round end vote. + var/static/vote_check + + /// The cached duration to the next vote. + var/static/vote_cache + + +/datum/controller/subsystem/roundend/UpdateStat(time) + if (PreventUpdateStat(time)) + return ..() + if (GAME_STATE < RUNLEVEL_POSTGAME) + var/round_time = round_duration_in_ticks / 600 + var/show_max + if (max_length) + show_max = max(round(max_length - round_time, 0.1), 0) + var/show_empty + if (empty_check) + show_empty = max(round(empty_check - round_time, 0.1), 0) + var/show_vote + if (vote_check) + show_vote = max(round(vote_check - round_time, 0.1), 0) + ..({"\n\ + Max Time: [isnull(show_max) ? "Off" : "[show_max]m"]\n\ + Empty End: [isnull(show_empty) ? "Off" : "[show_empty]m"]\n\ + Next Vote: [isnull(show_vote) ? "Off" : "[show_vote]m"]\ + "}) + else + ..("Game Finished") + + +/datum/controller/subsystem/roundend/Initialize(start_uptime) + max_length = config.maximum_round_length + empty_check = config.empty_round_check_interval + vote_check = config.vote_autotransfer_initial + + +/datum/controller/subsystem/roundend/fire(resumed, no_mc_tick) + var/time = round_duration_in_ticks / 600 + if (max_length && time > max_length) + if (evacuation_controller.is_idle()) + init_autotransfer() + return + if (empty_check && time > empty_check) + empty_check += config.empty_round_check_interval + if (!length(GLOB.living_players)) + SSticker.forced_end = TRUE + return + if (vote_check) + vote_cache = round(max(vote_check - time, 0), 0.1) + if (vote_cache > 0) + return + SSvote.initiate_vote(/datum/vote/transfer, null, TRUE) + if (config.vote_autotransfer_interval) + vote_check += config.vote_autotransfer_interval + else + vote_check = 0 diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index e804a2796e54d..8f2a0c85aa9f9 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -33,8 +33,6 @@ debug_variables(paiController) if("Cameras") debug_variables(cameranet) - if("Transfer Controller") - debug_variables(transfer_controller) if("Gas Data") debug_variables(gas_data) if("Alt Appearance Manager") diff --git a/code/datums/vote/add_antag.dm b/code/datums/vote/add_antag.dm index da0d00b5e1ba7..ab19317c9aa5f 100644 --- a/code/datums/vote/add_antag.dm +++ b/code/datums/vote/add_antag.dm @@ -52,8 +52,8 @@ if(SSticker.attempt_late_antag_spawn(antag_choices)) // This takes a while. antag_add_finished = 1 if(automatic) - // the buffer will already have half an hour added to it, so we'll give it one more - transfer_controller.timerbuffer += config.vote_autotransfer_interval + if (SSroundend.vote_check) + SSroundend.vote_check += config.vote_autotransfer_interval else to_world("No antags were added.") if(automatic) diff --git a/code/game/gamemodes/game_mode_latespawn.dm b/code/game/gamemodes/game_mode_latespawn.dm index e100de1013eed..d403706708dc4 100644 --- a/code/game/gamemodes/game_mode_latespawn.dm +++ b/code/game/gamemodes/game_mode_latespawn.dm @@ -14,10 +14,8 @@ return FALSE if(evacuation_controller.is_evacuating() || evacuation_controller.has_evacuated()) return FALSE - // Don't create auto-antags in the last twenty minutes of the round, but only if the vote interval is longer than 20 minutes - if((config.vote_autotransfer_interval > 20 MINUTES) && (transfer_controller.time_till_transfer_vote() < 20 MINUTES)) + if (SSroundend.vote_check && SSroundend.vote_cache < config.transfer_vote_block_antag_time) return FALSE - return TRUE //This can be overriden in case a game mode needs to do stuff when a player latejoins diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index e9b832f5d3eaa..45398d5a66810 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -1611,34 +1611,45 @@ GLOBAL_VAR_INIT(skip_allow_lists, FALSE) faxreply = null return -/datum/admins/proc/setroundlength() + +/datum/admins/proc/SetRoundLength() set category = "Server" - set desc = "Set the time the round-end vote will start in minutes." set name = "Set Round Length" - - if (GAME_STATE > RUNLEVEL_LOBBY) - to_chat(usr, SPAN_WARNING("You cannot change the round length after the game has started!")) + set desc = "Set the maximum length of a round in minutes." + if (GAME_STATE > RUNLEVEL_GAME) + to_chat(usr, SPAN_WARNING("The game is already ending!")) return - - var/time = input("Set the time until the round-end vote occurs (IN MINUTES). Default is [config.vote_autotransfer_initial / 600]", "Set Round Length", 0) as null | num - - if (!time || !isnum(time) || time < 0) + var/current = round(round_duration_in_ticks / 600, 0.1) + var/response = input(usr, "Time in minutes when the round will end, or 0 to disable.\nCurrent time: [current]m") as null | num + if (!isnum(response)) return + if (!response) + log_and_message_admins("disabled max round length.") + config.maximum_round_length = response + else if (response > current) + log_and_message_admins("set max round length to [response] minutes.") + config.maximum_round_length = response + else + to_chat(usr, SPAN_WARNING("You cannot set a max round length in the past.")) - transfer_controller.timerbuffer = time MINUTES - log_and_message_admins("set the initial round-end vote time to [time] minutes after round-start.") -/datum/admins/proc/toggleroundendvote() +/datum/admins/proc/ToggleContinueVote() set category = "Server" - set desc = "Toggle the continue vote on/off. Toggling off will cause round-end to occur when the next continue vote time would be." set name = "Toggle Continue Vote" - + set desc = "Toggle the continue vote on/off. Toggling off will cause round-end to occur when the next continue vote time would be." if (GAME_STATE > RUNLEVEL_GAME) to_chat(usr, SPAN_WARNING("The game is already ending!")) return + SSroundend.vote_check = !SSroundend.vote_check + if (SSroundend.vote_check) + var/interval = config.vote_autotransfer_interval + if (!interval) + to_chat(usr, SPAN_WARNING("Continue votes not configured.")) + SSroundend.vote_check = 0 + return + SSroundend.vote_check = (round_duration_in_ticks / 600) + interval + log_and_message_admins("toggled continue votes [SSroundend.vote_check ? "ON" : "OFF"]") - transfer_controller.do_continue_vote = !transfer_controller.do_continue_vote - log_and_message_admins("toggled the continue vote [transfer_controller.do_continue_vote ? "ON" : "OFF"]") /datum/admins/proc/togglemoderequirementchecks() set category = "Server" diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 2040dc684b2db..cf82edfa8b209 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -97,8 +97,8 @@ var/global/list/admin_verbs_admin = list( /datum/admins/proc/sendFax, /client/proc/check_fax_history, /client/proc/cmd_admin_notarget, - /datum/admins/proc/setroundlength, - /datum/admins/proc/toggleroundendvote, + /datum/admins/proc/SetRoundLength, + /datum/admins/proc/ToggleContinueVote, /datum/admins/proc/togglemoderequirementchecks, /client/proc/delete_crew_record ) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 6ce1849c9d6f3..e78034a215603 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -76,8 +76,8 @@ stat("Game Mode:", PUBLIC_GAME_MODE) var/extra_antags = list2params(additional_antag_types) stat("Added Antagonists:", extra_antags ? extra_antags : "None") - stat("Initial Continue Vote:", "[round(config.vote_autotransfer_initial / 600, 1)] minutes") - stat("Additional Vote Every:", "[round(config.vote_autotransfer_interval / 600, 1)] minutes") + stat("Initial Continue Vote:", "[config.vote_autotransfer_initial] minutes") + stat("Additional Vote Every:", "[config.vote_autotransfer_interval] minutes") if(GAME_STATE <= RUNLEVEL_LOBBY) stat("Time To Start:", "[round(SSticker.pregame_timeleft/10)][SSticker.round_progressing ? "" : " (DELAYED)"]") @@ -104,7 +104,7 @@ totalPlayers++ if(player.ready)totalPlayersReady++ else - stat("Next Continue Vote:", "[max(round(transfer_controller.time_till_transfer_vote() / 600, 1), 0)] minutes") + stat("Next Continue Vote:", "[SSroundend.vote_cache] minutes") /mob/new_player/Topic(href, href_list) // This is a full override; does not call parent. if (usr != src) diff --git a/config/example/config.txt b/config/example/config.txt index e8a3444e905a8..2e5a99c055377 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -135,6 +135,13 @@ VOTE_DELAY 6000 ## time period (deciseconds) which voting session will last (default 1 minute) VOTE_PERIOD 600 +## Maximum time a round can last for (in minutes). If this time is exceeded, the round +# will autotransfer without a vote at the next continue vote. Leave disabled for no limit. +#MAXIMUM_ROUND_LENGTH 120 + +## If set, the time in minutes between checks for ending empty rounds. Default off. +#EMPTY_ROUND_CHECK_INTERVAL 15 + ## autovote initial delay in minutes before first automatic transfer vote call (default 120) # using seven semicolon (;) separated values allows for different weekday-based values VOTE_AUTOTRANSFER_INITIAL 120 @@ -143,6 +150,9 @@ VOTE_AUTOTRANSFER_INITIAL 120 # using seven semicolon (;) separated values allows for different weekday-based values VOTE_AUTOTRANSFER_INTERVAL 30 +## Time in minutes leading up to the next autotransfer vote in which antagonists cannot +# automatically be created. Default 20. +#TRANSFER_VOTE_BLOCK_ANTAG_TIME 20 ## Time left (seconds) before round start when automatic gamemote vote is called (default 160). VOTE_AUTOGAMEMODE_TIMELEFT 160 @@ -441,10 +451,6 @@ RADIATION_LOWER_LIMIT 0.15 ## DISALLOW_VOTABLE_MODE changeling ## DISALLOW_VOTABLE_MODE malfunction -## Maximum time a round can last for (in minutes). If this time is exceeded, -## the round will autotransfer without a vote at the next continue vote. Leave disabled for no limit. -#MAXIMUM_ROUND_LENGTH 120 - ## The delay in deciseconds between stat() updates. ## Lower can be more responsive to scene changes and updates but has a higher client/server overhead. # STAT_DELAY 5