diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index c94c0a489c9..2c13b7eb07d 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -259,6 +259,14 @@ /// Game has round finished #define GAME_STATE_FINISHED 4 +// Used for SSticker.force_ending +/// Default, round is not being forced to end. +#define END_ROUND_AS_NORMAL 0 +/// End the round now as normal +#define FORCE_END_ROUND 1 +/// For admin forcing roundend, can be used to distinguish the two +#define ADMIN_FORCE_END_ROUND 2 + /** Create a new timer and add it to the queue. * Arguments: diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 7294795ba20..9e14c1c1212 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -201,22 +201,20 @@ player_client.give_award(/datum/award/score/hardcore_random, human_mob, round(human_mob.hardcore_survival_score)) -/datum/controller/subsystem/ticker/proc/declare_completion() +/datum/controller/subsystem/ticker/proc/declare_completion(was_forced = END_ROUND_AS_NORMAL) set waitfor = FALSE for(var/datum/callback/roundend_callbacks as anything in round_end_events) roundend_callbacks.InvokeAsync() LAZYCLEARLIST(round_end_events) - var/speed_round = FALSE - if(world.time - SSticker.round_start_time <= 300 SECONDS) - speed_round = TRUE + var/speed_round = (STATION_TIME_PASSED() <= 10 MINUTES) for(var/client/C in GLOB.clients) if(!C?.credits) C?.RollCredits() C?.playtitlemusic(40) - if(speed_round) + if(speed_round && was_forced != ADMIN_FORCE_END_ROUND) C?.give_award(/datum/award/achievement/misc/speed_round, C?.mob) HandleRandomHardcoreScore(C) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index c78ff138a3d..b9260e51312 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -10,8 +10,10 @@ SUBSYSTEM_DEF(ticker) /// state of current round (used by process()) Use the defines GAME_STATE_* ! var/current_state = GAME_STATE_STARTUP - /// Boolean to track if round was ended by admin intervention or a "round-ending" event, like summoning Nar'Sie, a blob victory, the nuke going off, etc. - var/force_ending = FALSE + /// Boolean to track if round should be forcibly ended next ticker tick. + /// Set by admin intervention ([ADMIN_FORCE_END_ROUND]) + /// or a "round-ending" event, like summoning Nar'Sie, a blob victory, the nuke going off, etc. ([FORCE_END_ROUND]) + var/force_ending = END_ROUND_AS_NORMAL /// If TRUE, there is no lobby phase, the game starts immediately. var/start_immediately = FALSE /// Boolean to track and check if our subsystem setup is done. @@ -220,7 +222,7 @@ SUBSYSTEM_DEF(ticker) mode.process(wait * 0.1) check_queue() - if(!roundend_check_paused && mode.check_finished(force_ending) || force_ending) + if(!roundend_check_paused && (mode.check_finished() || force_ending)) current_state = GAME_STATE_FINISHED toggle_ooc(TRUE) // Turn it on toggle_dooc(TRUE) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 0383bf89bab..61037fe4c77 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -56,7 +56,8 @@ /datum/game_mode/proc/make_antag_chance(mob/living/carbon/human/character) return -/datum/game_mode/proc/check_finished(force_ending) //to be called by SSticker +/// Checks if the round should be ending, called every ticker tick +/datum/game_mode/proc/check_finished() if(!SSticker.setup_done) return FALSE if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME)) @@ -65,8 +66,7 @@ return TRUE if(GLOB.revolutionary_win) return TRUE - if(force_ending) - return TRUE + return FALSE /* * Generate a list of station goals available to purchase to report to the crew. diff --git a/code/game/world.dm b/code/game/world.dm index 07fe533b474..380448d9fcf 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -49,8 +49,8 @@ GLOBAL_VAR(restart_counter) * - Dominion/Cyberboss * * Where to put init shit quick guide: - * If you need it to happen before the mc is created: world/Genesis. - * If you need it to happen last: world/New(), + * If you need it to happen before the mc is created: world/Genesis. + * If you need it to happen last: world/New(), * Otherwise, in a subsystem preinit or init. Subsystems can set an init priority. */ @@ -180,7 +180,7 @@ GLOBAL_VAR(restart_counter) #ifdef UNIT_TESTS cb = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(RunUnitTests)) #else - cb = VARSET_CALLBACK(SSticker, force_ending, TRUE) + cb = VARSET_CALLBACK(SSticker, force_ending, ADMIN_FORCE_END_ROUND) #endif SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_addtimer), cb, 10 SECONDS)) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index b7aea102b69..4dc402e0063 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -197,7 +197,7 @@ if(tgui_alert(usr, "This will end the round, are you SURE you want to do this?", "Confirmation", list("Yes", "No")) == "Yes") if(tgui_alert(usr, "Final Confirmation: End the round NOW?", "Confirmation", list("Yes", "No")) == "Yes") message_admins(span_adminnotice("[key_name_admin(usr)] has ended the round.")) - SSticker.force_ending = TRUE //Yeah there we go APC destroyed mission accomplished + SSticker.force_ending = ADMIN_FORCE_END_ROUND //Yeah there we go APC destroyed mission accomplished return else message_admins(span_adminnotice("[key_name_admin(usr)] decided against ending the round.")) diff --git a/code/modules/admin/verbs/server.dm b/code/modules/admin/verbs/server.dm index 8f0a112da16..811b2a3506d 100644 --- a/code/modules/admin/verbs/server.dm +++ b/code/modules/admin/verbs/server.dm @@ -77,7 +77,7 @@ if(confirm == "Cancel") return if(confirm == "Yes") - SSticker.force_ending = TRUE + SSticker.force_ending = FORCE_END_ROUND SSblackbox.record_feedback("tally", "admin_verb", 1, "End Round") // If you are copy-pasting this, ensure the 4th parameter is unique to the new proc! /datum/admins/proc/toggleooc() diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index 021c8bb12d5..70a1e69e1e3 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -207,7 +207,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) main_objective.completed = TRUE to_chat(world, span_blob("[real_name] consumed the station in an unstoppable tide!")) SSticker.news_report = BLOB_WIN - SSticker.force_ending = TRUE + SSticker.force_ending = FORCE_END_ROUND /mob/camera/blob/Destroy() QDEL_NULL(blobstrain) diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm index 2f628fd66b4..a6c6616ad48 100644 --- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm +++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm @@ -358,7 +358,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module)) /obj/machinery/doomsday_device/proc/trigger_doomsday() callback_on_everyone_on_z(SSmapping.levels_by_trait(ZTRAIT_STATION), CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(bring_doomsday)), src) to_chat(world, span_bold("The AI cleansed the station of life with [src]!")) - SSticker.force_ending = TRUE + SSticker.force_ending = FORCE_END_ROUND /proc/bring_doomsday(mob/living/victim, atom/source) if(issilicon(victim)) diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 2c0ae6f7705..1c83ca58e1d 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -263,7 +263,7 @@ ///Helper to set the round to end asap. Current usage Cult round end code /proc/ending_helper() - SSticker.force_ending = TRUE + SSticker.force_ending = FORCE_END_ROUND /** * Selects cinematic to play as part of the cult end depending on the outcome then ends the round afterward diff --git a/code/modules/power/supermatter/supermatter_delamination/delamination_effects.dm b/code/modules/power/supermatter/supermatter_delamination/delamination_effects.dm index 1c3a0402ebe..49415d65dec 100644 --- a/code/modules/power/supermatter/supermatter_delamination/delamination_effects.dm +++ b/code/modules/power/supermatter/supermatter_delamination/delamination_effects.dm @@ -205,7 +205,7 @@ sleep(10 SECONDS) SSticker.news_report = SUPERMATTER_CASCADE - SSticker.force_ending = TRUE + SSticker.force_ending = FORCE_END_ROUND /// Scatters crystal mass over the event spawns as long as they are at least 30 tiles away from whatever we want to avoid. /datum/sm_delam/proc/effect_crystal_mass(obj/machinery/power/supermatter_crystal/sm, avoid) diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm index 1709bafb144..3fef3d985f7 100644 --- a/code/modules/unit_tests/unit_test.dm +++ b/code/modules/unit_tests/unit_test.dm @@ -236,7 +236,7 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests()) fdel(file_name) file(file_name) << json_encode(test_results) - SSticker.force_ending = TRUE + SSticker.force_ending = ADMIN_FORCE_END_ROUND //We have to call this manually because del_text can preceed us, and SSticker doesn't fire in the post game SSticker.declare_completion()