diff --git a/code/__HELPERS/logging/dynamic.dm b/code/__HELPERS/logging/dynamic.dm
index 51bba86ca05..488aeaec3be 100644
--- a/code/__HELPERS/logging/dynamic.dm
+++ b/code/__HELPERS/logging/dynamic.dm
@@ -1,5 +1,5 @@
/// Log to dynamic and message admins
-/datum/game_mode/dynamic/proc/log_dynamic_and_announce(text)
+/datum/controller/subsystem/dynamic/proc/log_dynamic_and_announce(text)
message_admins("DYNAMIC: [text]")
log_dynamic("[text]")
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index cf65a14fa09..93ba8bb2f1c 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -242,7 +242,7 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
CHECK_TICK
//Set news report and mode result
- mode.set_round_result()
+ SSdynamic.set_round_result()
to_chat(world, span_infoplain(span_big(span_bold("
The round has ended."))))
log_game("The round has ended.")
@@ -353,17 +353,17 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
//ignore this comment, it fixes the broken sytax parsing caused by the " above
else
parts += "[FOURSPACES]Nobody died this shift!"
- if(istype(SSticker.mode, /datum/game_mode/dynamic))
- var/datum/game_mode/dynamic/mode = SSticker.mode
- parts += "[FOURSPACES]Threat level: [mode.threat_level]"
- parts += "[FOURSPACES]Threat left: [mode.mid_round_budget]"
- if(mode.roundend_threat_log.len)
- parts += "[FOURSPACES]Threat edits:"
- for(var/entry as anything in mode.roundend_threat_log)
- parts += "[FOURSPACES][FOURSPACES][entry]
"
- parts += "[FOURSPACES]Executed rules:"
- for(var/datum/dynamic_ruleset/rule in mode.executed_rules)
- parts += "[FOURSPACES][FOURSPACES][rule.ruletype] - [rule.name]: -[rule.cost + rule.scaled_times * rule.scaling_cost] threat"
+
+ parts += "[FOURSPACES]Threat level: [SSdynamic.threat_level]"
+ parts += "[FOURSPACES]Threat left: [SSdynamic.mid_round_budget]"
+ if(SSdynamic.roundend_threat_log.len)
+ parts += "[FOURSPACES]Threat edits:"
+ for(var/entry as anything in SSdynamic.roundend_threat_log)
+ parts += "[FOURSPACES][FOURSPACES][entry]
"
+ parts += "[FOURSPACES]Executed rules:"
+ for(var/datum/dynamic_ruleset/rule in SSdynamic.executed_rules)
+ parts += "[FOURSPACES][FOURSPACES][rule.ruletype] - [rule.name]: -[rule.cost + rule.scaled_times * rule.scaling_cost] threat"
+
return parts.Join("
")
/client/proc/roundend_report_file()
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/controllers/subsystem/dynamic/dynamic.dm
similarity index 81%
rename from code/game/gamemodes/dynamic/dynamic.dm
rename to code/controllers/subsystem/dynamic/dynamic.dm
index c05d1f38666..8741f75ee67 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/controllers/subsystem/dynamic/dynamic.dm
@@ -20,7 +20,11 @@ GLOBAL_LIST_EMPTY(dynamic_station_traits)
/// Rulesets which have been forcibly enabled or disabled
GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
-/datum/game_mode/dynamic
+SUBSYSTEM_DEF(dynamic)
+ name = "Dynamic"
+ flags = SS_NO_INIT
+ wait = 1 SECONDS
+
// Threat logging vars
/// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
var/threat_level = 0
@@ -193,7 +197,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
VAR_PRIVATE/next_midround_injection
-/datum/game_mode/dynamic/admin_panel()
+/datum/controller/subsystem/dynamic/proc/admin_panel()
var/list/dat = list("
Game Mode PanelGame Mode Panel
")
dat += "Dynamic Mode \[VV\] \[Refresh\]
"
dat += "Threat Level: [threat_level]
"
@@ -231,7 +235,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
usr << browse(dat.Join(), "window=gamemode_panel;size=500x500")
-/datum/game_mode/dynamic/Topic(href, href_list)
+/datum/controller/subsystem/dynamic/Topic(href, href_list)
if (..()) // Sanity, maybe ?
return
if(!check_rights(R_ADMIN))
@@ -289,8 +293,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
admin_panel() // Refreshes the window
-// Checks if there are HIGH_IMPACT_RULESETs and calls the rule's round_result() proc
-/datum/game_mode/dynamic/set_round_result()
+// Set result and news report here
+/datum/controller/subsystem/dynamic/proc/set_round_result()
// If it got to this part, just pick one high impact ruleset if it exists
for(var/datum/dynamic_ruleset/rule in executed_rules)
if(rule.flags & HIGH_IMPACT_RULESET)
@@ -299,9 +303,20 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
if(SSticker.news_report)
return
- return ..()
+ SSticker.mode_result = "undefined"
-/datum/game_mode/dynamic/proc/send_intercept()
+ // Something nuked the station - it wasn't nuke ops (they set their own via their rulset)
+ if(GLOB.station_was_nuked)
+ SSticker.news_report = STATION_NUKED
+
+ if(SSsupermatter_cascade.cascade_initiated)
+ SSticker.news_report = SUPERMATTER_CASCADE
+
+ // Only show this one if we have nothing better to show
+ if(EMERGENCY_ESCAPED_OR_ENDGAMED && !SSticker.news_report)
+ SSticker.news_report = SSshuttle.emergency?.is_hijacked() ? SHUTTLE_HIJACK : STATION_EVACUATED
+
+/datum/controller/subsystem/dynamic/proc/send_intercept()
if(SScommunications.block_command_report) //If we don't want the report to be printed just yet, we put it off until it's ready
addtimer(CALLBACK(src, PROC_REF(send_intercept)), 10 SECONDS)
return
@@ -316,11 +331,33 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
min_threat = min(ruleset.cost, min_threat)
var/greenshift = GLOB.dynamic_forced_extended || (threat_level < min_threat && shown_threat < min_threat) //if both shown and real threat are below any ruleset, its extended time
- generate_station_goals(greenshift)
- . += generate_station_goal_report()
- . += generate_station_trait_report()
+ generate_station_goals(greenshift ? INFINITY : CONFIG_GET(number/station_goal_budget))
+
+ if (GLOB.station_goals.len > 0)
+ var/list/texts = list("
Special Orders for [station_name()]:
")
+ for(var/datum/station_goal/station_goal as anything in GLOB.station_goals)
+ station_goal.on_report()
+ texts += station_goal.get_report()
+
+ . += texts.Join("
")
+
+ var/list/trait_list_strings = list()
+ for(var/datum/station_trait/station_trait as anything in SSstation.station_traits)
+ if(!station_trait.show_in_report)
+ continue
+ trait_list_strings += "[station_trait.get_report()]
"
+ if(trait_list_strings.len > 0)
+ . += "
Identified shift divergencies:
" + trait_list_strings.Join()
+
if(length(SScommunications.command_report_footnotes))
- . += generate_report_footnote()
+ var/footnote_pile = ""
+
+ for(var/datum/command_footnote/footnote in SScommunications.command_report_footnotes)
+ footnote_pile += "[footnote.message]
"
+ footnote_pile += "[footnote.signature]
"
+ footnote_pile += "
"
+
+ . += "
Additional Notes:
" + footnote_pile
print_command_report(., "[command_name()] Status Summary", announce=FALSE)
if(greenshift)
@@ -330,8 +367,10 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
if(SSsecurity_level.get_current_level_as_number() < SEC_LEVEL_BLUE)
SSsecurity_level.set_level(SEC_LEVEL_BLUE)
+ return .
+
/// Generate the advisory level depending on the shown threat level.
-/datum/game_mode/dynamic/proc/generate_advisory_level()
+/datum/controller/subsystem/dynamic/proc/generate_advisory_level()
var/advisory_string = ""
if (prob(PULSAR_REPORT_CHANCE))
if(HAS_TRAIT(SSstation, STATION_TRAIT_BANANIUM_SHIPMENTS))
@@ -376,7 +415,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return advisory_string
-/datum/game_mode/dynamic/proc/show_threatlog(mob/admin)
+/datum/controller/subsystem/dynamic/proc/show_threatlog(mob/admin)
if(!SSticker.HasRoundStarted())
tgui_alert(usr, "The round hasn't started yet!")
return
@@ -395,7 +434,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
usr << browse(out.Join(), "window=threatlog;size=700x500")
/// Generates the threat level using lorentz distribution and assigns peaceful_percentage.
-/datum/game_mode/dynamic/proc/generate_threat()
+/datum/controller/subsystem/dynamic/proc/generate_threat()
// At lower pop levels we run a Liner Interpolation against the max threat based proportionally on the number
// of players ready. This creates a balanced lorentz curve within a smaller range than 0 to max_threat_level.
var/calculated_max_threat = (SSticker.totalPlayersReady < low_pop_player_threshold) ? LERP(low_pop_maximum_threat, max_threat_level, SSticker.totalPlayersReady / low_pop_player_threshold) : max_threat_level
@@ -410,12 +449,12 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
peaceful_percentage = (threat_level/max_threat_level)*100
/// Generates the midround and roundstart budgets
-/datum/game_mode/dynamic/proc/generate_budgets()
+/datum/controller/subsystem/dynamic/proc/generate_budgets()
round_start_budget = lorentz_to_amount(roundstart_split_curve_centre, roundstart_split_curve_width, threat_level, 0.1)
initial_round_start_budget = round_start_budget
mid_round_budget = threat_level - round_start_budget
-/datum/game_mode/dynamic/proc/setup_parameters()
+/datum/controller/subsystem/dynamic/proc/setup_parameters()
log_dynamic("Dynamic mode parameters for the round:")
log_dynamic("Centre is [threat_curve_centre], Width is [threat_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].")
log_dynamic("Stacking limit is [GLOB.dynamic_stacking_limit].")
@@ -448,17 +487,18 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
)
return TRUE
-/datum/game_mode/dynamic/proc/setup_shown_threat()
+/datum/controller/subsystem/dynamic/proc/setup_shown_threat()
if (prob(FAKE_REPORT_CHANCE))
shown_threat = rand(1, 100)
else
shown_threat = clamp(threat_level + rand(REPORT_NEG_DIVERGENCE, REPORT_POS_DIVERGENCE), 0, 100)
-/datum/game_mode/dynamic/proc/set_cooldowns()
+/datum/controller/subsystem/dynamic/proc/set_cooldowns()
var/latejoin_injection_cooldown_middle = 0.5*(latejoin_delay_max + latejoin_delay_min)
latejoin_injection_cooldown = round(clamp(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), latejoin_delay_min, latejoin_delay_max)) + world.time
-/datum/game_mode/dynamic/pre_setup()
+// Called BEFORE everyone is equipped with their job
+/datum/controller/subsystem/dynamic/proc/pre_setup()
if(CONFIG_GET(flag/dynamic_config_enabled))
var/json_file = file("[global.config.directory]/dynamic.json")
if(fexists(json_file))
@@ -517,24 +557,99 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
candidates.Cut()
return TRUE
-/datum/game_mode/dynamic/post_setup(report)
+// Called AFTER everyone is equipped with their job
+/datum/controller/subsystem/dynamic/proc/post_setup(report)
for(var/datum/dynamic_ruleset/roundstart/rule in executed_rules)
rule.candidates.Cut() // The rule should not use candidates at this point as they all are null.
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/game_mode/dynamic/, execute_roundstart_rule), rule), rule.delay)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/controller/subsystem/dynamic/, execute_roundstart_rule), rule), rule.delay)
if (!CONFIG_GET(flag/no_intercept_report))
addtimer(CALLBACK(src, PROC_REF(send_intercept)), rand(waittime_l, waittime_h))
- ..()
+ addtimer(CALLBACK(src, PROC_REF(display_roundstart_logout_report)), ROUNDSTART_LOGOUT_REPORT_TIME)
+
+ if(CONFIG_GET(flag/reopen_roundstart_suicide_roles))
+ var/delay = CONFIG_GET(number/reopen_roundstart_suicide_roles_delay)
+ if(delay)
+ delay *= (1 SECONDS)
+ else
+ delay = (4 MINUTES) //default to 4 minutes if the delay isn't defined.
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(reopen_roundstart_suicide_roles)), delay)
+
+ if(SSdbcore.Connect())
+ var/list/to_set = list()
+ var/arguments = list()
+ if(GLOB.revdata.originmastercommit)
+ to_set += "commit_hash = :commit_hash"
+ arguments["commit_hash"] = GLOB.revdata.originmastercommit
+ if(to_set.len)
+ arguments["round_id"] = GLOB.round_id
+ var/datum/db_query/query_round_game_mode = SSdbcore.NewQuery(
+ "UPDATE [format_table_name("round")] SET [to_set.Join(", ")] WHERE id = :round_id",
+ arguments
+ )
+ query_round_game_mode.Execute()
+ qdel(query_round_game_mode)
+ return TRUE
+
+/datum/controller/subsystem/dynamic/proc/display_roundstart_logout_report()
+ var/list/msg = list("[span_boldnotice("Roundstart logout report")]\n\n")
+ for(var/i in GLOB.mob_living_list)
+ var/mob/living/L = i
+ var/mob/living/carbon/C = L
+ if (istype(C) && !C.last_mind)
+ continue // never had a client
+
+ if(L.ckey && !GLOB.directory[L.ckey])
+ msg += "[L.name] ([L.key]), the [L.job] (Disconnected)\n"
+
+
+ if(L.ckey && L.client)
+ var/failed = FALSE
+ if(L.client.inactivity >= ROUNDSTART_LOGOUT_AFK_THRESHOLD) //Connected, but inactive (alt+tabbed or something)
+ msg += "[L.name] ([L.key]), the [L.job] (Connected, Inactive)\n"
+ failed = TRUE //AFK client
+ if(!failed && L.stat)
+ if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
+ msg += "[L.name] ([L.key]), the [L.job] ([span_boldannounce("Suicide")])\n"
+ failed = TRUE //Disconnected client
+ if(!failed && (L.stat == UNCONSCIOUS || L.stat == HARD_CRIT))
+ msg += "[L.name] ([L.key]), the [L.job] (Dying)\n"
+ failed = TRUE //Unconscious
+ if(!failed && L.stat == DEAD)
+ msg += "[L.name] ([L.key]), the [L.job] (Dead)\n"
+ failed = TRUE //Dead
+
+ continue //Happy connected client
+ for(var/mob/dead/observer/D in GLOB.dead_mob_list)
+ if(D.mind && D.mind.current == L)
+ if(L.stat == DEAD)
+ if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
+ msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Suicide")])\n"
+ continue //Disconnected client
+ else
+ msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n"
+ continue //Dead mob, ghost abandoned
+ else
+ if(D.can_reenter_corpse)
+ continue //Adminghost, or cult/wizard ghost
+ else
+ msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Ghosted")])\n"
+ continue //Ghosted while alive
+
+ var/concatenated_message = msg.Join()
+ log_admin(concatenated_message)
+ to_chat(GLOB.admins, concatenated_message)
+
/// Initializes the internal ruleset variables
-/datum/game_mode/dynamic/proc/setup_rulesets()
+/datum/controller/subsystem/dynamic/proc/setup_rulesets()
midround_rules = init_rulesets(/datum/dynamic_ruleset/midround)
latejoin_rules = init_rulesets(/datum/dynamic_ruleset/latejoin)
/// Returns a list of the provided rulesets.
/// Configures their variables to match config.
-/datum/game_mode/dynamic/proc/init_rulesets(ruleset_subtype)
+/datum/controller/subsystem/dynamic/proc/init_rulesets(ruleset_subtype)
var/list/rulesets = list()
for (var/datum/dynamic_ruleset/ruleset_type as anything in subtypesof(ruleset_subtype))
@@ -551,14 +666,13 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return rulesets
/// A simple roundstart proc used when dynamic_forced_roundstart_ruleset has rules in it.
-/datum/game_mode/dynamic/proc/rigged_roundstart()
+/datum/controller/subsystem/dynamic/proc/rigged_roundstart()
message_admins("[GLOB.dynamic_forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.")
log_dynamic("[GLOB.dynamic_forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.")
for (var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset)
configure_ruleset(rule)
message_admins("Drafting players for forced ruleset [rule.name].")
log_dynamic("Drafting players for forced ruleset [rule.name].")
- rule.mode = src
rule.acceptable(roundstart_pop_ready, threat_level) // Assigns some vars in the modes, running it here for consistency
rule.candidates = candidates.Copy()
rule.trim_candidates()
@@ -572,7 +686,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
spend_roundstart_budget(picking_roundstart_rule(rule, scaled_times, forced = TRUE))
-/datum/game_mode/dynamic/proc/roundstart(list/roundstart_rules)
+/datum/controller/subsystem/dynamic/proc/roundstart(list/roundstart_rules)
if (GLOB.dynamic_forced_extended)
log_dynamic("Starting a round of forced extended.")
return TRUE
@@ -631,7 +745,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
update_log()
/// Initializes the round start ruleset provided to it. Returns how much threat to spend.
-/datum/game_mode/dynamic/proc/picking_roundstart_rule(datum/dynamic_ruleset/roundstart/ruleset, scaled_times = 0, forced = FALSE)
+/datum/controller/subsystem/dynamic/proc/picking_roundstart_rule(datum/dynamic_ruleset/roundstart/ruleset, scaled_times = 0, forced = FALSE)
log_dynamic("Picked a ruleset: [ruleset.name], scaled [scaled_times] times")
ruleset.trim_candidates()
@@ -650,7 +764,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return 0
/// Mainly here to facilitate delayed rulesets. All roundstart rulesets are executed with a timered callback to this proc.
-/datum/game_mode/dynamic/proc/execute_roundstart_rule(sent_rule)
+/datum/controller/subsystem/dynamic/proc/execute_roundstart_rule(sent_rule)
var/datum/dynamic_ruleset/rule = sent_rule
if(rule.execute())
if(rule.persistent)
@@ -665,7 +779,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return FALSE
/// An experimental proc to allow admins to call rules on the fly or have rules call other rules.
-/datum/game_mode/dynamic/proc/picking_specific_rule(ruletype, forced = FALSE, ignore_cost = FALSE)
+/datum/controller/subsystem/dynamic/proc/picking_specific_rule(ruletype, forced = FALSE, ignore_cost = FALSE)
var/datum/dynamic_ruleset/midround/new_rule
if(ispath(ruletype))
new_rule = new ruletype() // You should only use it to call midround rules though.
@@ -714,7 +828,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
new_rule.forget_startup()
return FALSE
-/datum/game_mode/dynamic/process()
+/datum/controller/subsystem/dynamic/fire()
for (var/datum/dynamic_ruleset/rule in current_rules)
if(rule.rule_process() == RULESET_STOP_PROCESSING) // If rule_process() returns 1 (RULESET_STOP_PROCESSING), stop processing.
current_rules -= rule
@@ -722,14 +836,14 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
try_midround_roll()
/// Removes type from the list
-/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
+/datum/controller/subsystem/dynamic/proc/remove_from_list(list/type_list, type)
for(var/I in type_list)
if(istype(I, type))
type_list -= I
return type_list
/// Checks if a type in blocking_list is in rule_list.
-/datum/game_mode/dynamic/proc/check_blocking(list/blocking_list, list/rule_list)
+/datum/controller/subsystem/dynamic/proc/check_blocking(list/blocking_list, list/rule_list)
if(blocking_list.len > 0)
for(var/blocking in blocking_list)
for(var/_executed in rule_list)
@@ -739,7 +853,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return TRUE
return FALSE
-/datum/game_mode/dynamic/make_antag_chance(mob/living/carbon/human/newPlayer)
+/// Handles late-join antag assignments
+/datum/controller/subsystem/dynamic/proc/make_antag_chance(mob/living/carbon/human/newPlayer)
if (GLOB.dynamic_forced_extended)
return
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // No more rules after the shuttle has left
@@ -789,7 +904,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
* This proc handles the execution of a latejoin ruleset, including removing it from latejoin rulesets if not repeatable,
* upping the injection cooldown, and starting a timer to execute the ruleset on delay.
*/
-/datum/game_mode/dynamic/proc/handle_executing_latejoin(datum/dynamic_ruleset/ruleset, mob/living/carbon/human/only_candidate, forced = FALSE)
+/datum/controller/subsystem/dynamic/proc/handle_executing_latejoin(datum/dynamic_ruleset/ruleset, mob/living/carbon/human/only_candidate, forced = FALSE)
ruleset.candidates = list(only_candidate)
ruleset.trim_candidates()
ruleset.load_templates()
@@ -809,7 +924,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return TRUE
/// Apply configurations to rule.
-/datum/game_mode/dynamic/proc/configure_ruleset(datum/dynamic_ruleset/ruleset)
+/datum/controller/subsystem/dynamic/proc/configure_ruleset(datum/dynamic_ruleset/ruleset)
var/rule_conf = LAZYACCESSASSOC(configuration, ruleset.ruletype, ruleset.name)
for(var/variable in rule_conf)
if(!(variable in ruleset.vars))
@@ -822,14 +937,14 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
ruleset.restricted_roles |= JOB_ASSISTANT
/// Get station traits and call for their config
-/datum/game_mode/dynamic/proc/configure_station_trait_costs()
+/datum/controller/subsystem/dynamic/proc/configure_station_trait_costs()
if(!CONFIG_GET(flag/dynamic_config_enabled))
return
for(var/datum/station_trait/station_trait as anything in GLOB.dynamic_station_traits)
configure_station_trait(station_trait)
/// Apply configuration for station trait costs
-/datum/game_mode/dynamic/proc/configure_station_trait(datum/station_trait/station_trait)
+/datum/controller/subsystem/dynamic/proc/configure_station_trait(datum/station_trait/station_trait)
var/list/station_trait_config = LAZYACCESSASSOC(configuration, "Station", station_trait.dynamic_threat_id)
var/cost = station_trait_config["cost"]
@@ -842,31 +957,31 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
GLOB.dynamic_station_traits[station_trait] = cost
/// Refund threat, but no more than threat_level.
-/datum/game_mode/dynamic/proc/refund_threat(regain)
+/datum/controller/subsystem/dynamic/proc/refund_threat(regain)
mid_round_budget = min(threat_level, mid_round_budget + regain)
/// Generate threat and increase the threat_level if it goes beyond, capped at 100
-/datum/game_mode/dynamic/proc/create_threat(gain, list/threat_log, reason)
+/datum/controller/subsystem/dynamic/proc/create_threat(gain, list/threat_log, reason)
mid_round_budget = min(100, mid_round_budget + gain)
if(mid_round_budget > threat_level)
threat_level = mid_round_budget
for(var/list/logs in threat_log)
log_threat(gain, logs, reason)
-/datum/game_mode/dynamic/proc/log_threat(threat_change, list/threat_log, reason)
+/datum/controller/subsystem/dynamic/proc/log_threat(threat_change, list/threat_log, reason)
var/gain_or_loss = "+"
if(threat_change < 0)
gain_or_loss = "-"
threat_log += "Threat [gain_or_loss][abs(threat_change)] - [reason]."
/// Expend round start threat, can't fall under 0.
-/datum/game_mode/dynamic/proc/spend_roundstart_budget(cost, list/threat_log, reason)
+/datum/controller/subsystem/dynamic/proc/spend_roundstart_budget(cost, list/threat_log, reason)
round_start_budget = max(round_start_budget - cost,0)
if (!isnull(threat_log))
log_threat(-cost, threat_log, reason)
/// Expend midround threat, can't fall under 0.
-/datum/game_mode/dynamic/proc/spend_midround_budget(cost, list/threat_log, reason)
+/datum/controller/subsystem/dynamic/proc/spend_midround_budget(cost, list/threat_log, reason)
mid_round_budget = max(mid_round_budget - cost,0)
if (!isnull(threat_log))
log_threat(-cost, threat_log, reason)
@@ -879,7 +994,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
* threat and then rounded to the nearest interval.
* rand() calls without arguments returns a value between 0 and 1, allowing for smaller intervals.
*/
-/datum/game_mode/dynamic/proc/lorentz_to_amount(centre = 0, scale = 1.8, max_threat = 100, interval = 1)
+/datum/controller/subsystem/dynamic/proc/lorentz_to_amount(centre = 0, scale = 1.8, max_threat = 100, interval = 1)
var/location = RANDOM_DECIMAL(-MAXIMUM_DYN_DISTANCE, MAXIMUM_DYN_DISTANCE) * rand()
var/lorentz_result = LORENTZ_CUMULATIVE_DISTRIBUTION(centre, location, scale)
var/std_threat = lorentz_result * max_threat
@@ -888,6 +1003,44 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
var/upper_deviation = max((max_threat - std_threat) * (centre-location)/MAXIMUM_DYN_DISTANCE, 0)
return clamp(round(std_threat + upper_deviation - lower_deviation, interval), 0, 100)
+/proc/reopen_roundstart_suicide_roles()
+ var/include_command = CONFIG_GET(flag/reopen_roundstart_suicide_roles_command_positions)
+ var/list/reopened_jobs = list()
+
+ for(var/mob/living/quitter in GLOB.suicided_mob_list)
+ var/datum/job/job = SSjob.GetJob(quitter.job)
+ if(!job || !(job.job_flags & JOB_REOPEN_ON_ROUNDSTART_LOSS))
+ continue
+ if(!include_command && job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ continue
+ job.current_positions = max(job.current_positions - 1, 0)
+ reopened_jobs += quitter.job
+
+ if(CONFIG_GET(flag/reopen_roundstart_suicide_roles_command_report))
+ if(reopened_jobs.len)
+ var/reopened_job_report_positions
+ for(var/dead_dudes_job in reopened_jobs)
+ reopened_job_report_positions = "[reopened_job_report_positions ? "[reopened_job_report_positions]\n":""][dead_dudes_job]"
+
+ var/suicide_command_report = {"
+ [command_name()] Human Resources Board
+ Notice of Personnel Change
+ To personnel management staff aboard [station_name()]:
+ Our medical staff have detected a series of anomalies in the vital sensors
+ of some of the staff aboard your station.
+ Further investigation into the situation on our end resulted in us discovering
+ a series of rather... unforturnate decisions that were made on the part of said staff.
+ As such, we have taken the liberty to automatically reopen employment opportunities for the positions of the crew members
+ who have decided not to partake in our research. We will be forwarding their cases to our employment review board
+ to determine their eligibility for continued service with the company (and of course the
+ continued storage of cloning records within the central medical backup server.)
+ The following positions have been reopened on our behalf:
+ [reopened_job_report_positions]
+ "}
+
+ print_command_report(suicide_command_report, "Central Command Personnel Update")
+
+
#undef MAXIMUM_DYN_DISTANCE
#undef FAKE_REPORT_CHANCE
diff --git a/code/game/gamemodes/dynamic/dynamic_hijacking.dm b/code/controllers/subsystem/dynamic/dynamic_hijacking.dm
similarity index 86%
rename from code/game/gamemodes/dynamic/dynamic_hijacking.dm
rename to code/controllers/subsystem/dynamic/dynamic_hijacking.dm
index 62c9a5d2124..7577cbcd84f 100644
--- a/code/game/gamemodes/dynamic/dynamic_hijacking.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_hijacking.dm
@@ -1,7 +1,7 @@
-/datum/game_mode/dynamic/proc/setup_hijacking()
+/datum/controller/subsystem/dynamic/proc/setup_hijacking()
RegisterSignal(SSdcs, COMSIG_GLOB_PRE_RANDOM_EVENT, PROC_REF(on_pre_random_event))
-/datum/game_mode/dynamic/proc/on_pre_random_event(datum/source, datum/round_event_control/round_event_control)
+/datum/controller/subsystem/dynamic/proc/on_pre_random_event(datum/source, datum/round_event_control/round_event_control)
SIGNAL_HANDLER
if (!round_event_control.dynamic_should_hijack)
return
diff --git a/code/game/gamemodes/dynamic/dynamic_logging.dm b/code/controllers/subsystem/dynamic/dynamic_logging.dm
similarity index 95%
rename from code/game/gamemodes/dynamic/dynamic_logging.dm
rename to code/controllers/subsystem/dynamic/dynamic_logging.dm
index 7490894b526..16bd56a7303 100644
--- a/code/game/gamemodes/dynamic/dynamic_logging.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_logging.dm
@@ -69,7 +69,7 @@
return serialization
/// Updates the log for the current snapshots.
-/datum/game_mode/dynamic/proc/update_log()
+/datum/controller/subsystem/dynamic/proc/update_log()
var/list/serialized = list()
serialized["threat_level"] = threat_level
serialized["round_start_budget"] = initial_round_start_budget
@@ -84,7 +84,7 @@
rustg_file_write(json_encode(serialized), "[GLOB.log_directory]/dynamic.json")
/// Creates a new snapshot with the given rulesets chosen, and writes to the JSON output.
-/datum/game_mode/dynamic/proc/new_snapshot(datum/dynamic_ruleset/ruleset_chosen)
+/datum/controller/subsystem/dynamic/proc/new_snapshot(datum/dynamic_ruleset/ruleset_chosen)
var/datum/dynamic_snapshot/new_snapshot = new
new_snapshot.remaining_threat = mid_round_budget
diff --git a/code/game/gamemodes/dynamic/dynamic_midround_rolling.dm b/code/controllers/subsystem/dynamic/dynamic_midround_rolling.dm
similarity index 95%
rename from code/game/gamemodes/dynamic/dynamic_midround_rolling.dm
rename to code/controllers/subsystem/dynamic/dynamic_midround_rolling.dm
index c3e295ae875..968037b9fa2 100644
--- a/code/game/gamemodes/dynamic/dynamic_midround_rolling.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_midround_rolling.dm
@@ -1,7 +1,7 @@
/// Returns the world.time of the next midround injection.
/// Will return a cached result from `next_midround_injection`, the variable.
/// If that variable is null, will generate a new one.
-/datum/game_mode/dynamic/proc/next_midround_injection()
+/datum/controller/subsystem/dynamic/proc/next_midround_injection()
if (!isnull(next_midround_injection))
return next_midround_injection
@@ -16,7 +16,7 @@
return last_midround_injection_attempt + distance
-/datum/game_mode/dynamic/proc/try_midround_roll()
+/datum/controller/subsystem/dynamic/proc/try_midround_roll()
if (!forced_injection && next_midround_injection() > world.time)
return
@@ -90,7 +90,7 @@
log_dynamic_and_announce("No midround rulesets could be drafted. ([heavy_light_log_count])")
/// Gets the chance for a heavy ruleset midround injection, the dry_run argument is only used for forced injection.
-/datum/game_mode/dynamic/proc/get_heavy_midround_injection_chance(dry_run)
+/datum/controller/subsystem/dynamic/proc/get_heavy_midround_injection_chance(dry_run)
var/chance_modifier = 1
var/next_midround_roll = next_midround_injection() - SSticker.round_start_time
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets.dm
similarity index 92%
rename from code/game/gamemodes/dynamic/dynamic_rulesets.dm
rename to code/controllers/subsystem/dynamic/dynamic_rulesets.dm
index c9161963f24..b5f84176d59 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets.dm
@@ -7,7 +7,7 @@
var/ruletype = ""
/// If set to TRUE, the rule won't be discarded after being executed, and dynamic will call rule_process() every time it ticks.
var/persistent = FALSE
- /// If set to TRUE, dynamic mode will be able to draft this ruleset again later on. (doesn't apply for roundstart rules)
+ /// If set to TRUE, dynamic will be able to draft this ruleset again later on. (doesn't apply for roundstart rules)
var/repeatable = FALSE
/// If set higher than 0 decreases weight by itself causing the ruleset to appear less often the more it is repeated.
var/repeatable_weight_decrease = 2
@@ -41,7 +41,7 @@
var/required_candidates = 0
/// 0 -> 9, probability for this rule to be picked against other rules. If zero this will effectively disable the rule.
var/weight = 5
- /// Threat cost for this rule, this is decreased from the mode's threat when the rule is executed.
+ /// Threat cost for this rule, this is decreased from the threat level when the rule is executed.
var/cost = 0
/// Cost per level the rule scales up.
var/scaling_cost = 0
@@ -51,13 +51,11 @@
var/total_cost = 0
/// A flag that determines how the ruleset is handled. Check __DEFINES/dynamic.dm for an explanation of the accepted values.
var/flags = NONE
- /// Pop range per requirement. If zero defaults to mode's pop_per_requirement.
+ /// Pop range per requirement. If zero defaults to dynamic's pop_per_requirement.
var/pop_per_requirement = 0
/// Requirements are the threat level requirements per pop range.
/// With the default values, The rule will never get drafted below 10 threat level (aka: "peaceful extended"), and it requires a higher threat level at lower pops.
var/list/requirements = list(40,30,20,10,10,10,10,10,10,10)
- /// Reference to the mode, use this instead of SSticker.mode.
- var/datum/game_mode/dynamic/mode = null
/// If a role is to be considered another for the purpose of banning.
var/antag_flag_override = null
/// If set, will check this preference instead of antag_flag.
@@ -92,8 +90,6 @@
// side effects here. Dynamic rulesets should be stateless anyway.
SHOULD_NOT_OVERRIDE(TRUE)
- mode = SSticker.mode
-
..()
/datum/dynamic_ruleset/roundstart // One or more of those drafted at roundstart
@@ -135,7 +131,7 @@
/// Sets the current threat indices and returns true if we're inside of them
/datum/dynamic_ruleset/proc/is_valid_threat(population, threat_level)
- pop_per_requirement = pop_per_requirement > 0 ? pop_per_requirement : mode.pop_per_requirement
+ pop_per_requirement = pop_per_requirement > 0 ? pop_per_requirement : SSdynamic.pop_per_requirement
indice_pop = min(requirements.len,round(population/pop_per_requirement)+1)
return threat_level >= requirements[indice_pop]
@@ -148,14 +144,14 @@
return 0
var/antag_fraction = 0
- for(var/_ruleset in (mode.executed_rules + list(src))) // we care about the antags we *will* assign, too
+ for(var/_ruleset in (SSdynamic.executed_rules + list(src))) // we care about the antags we *will* assign, too
var/datum/dynamic_ruleset/ruleset = _ruleset
- antag_fraction += ((1 + ruleset.scaled_times) * ruleset.get_antag_cap(population)) / mode.roundstart_pop_ready
+ antag_fraction += ((1 + ruleset.scaled_times) * ruleset.get_antag_cap(population)) / SSdynamic.roundstart_pop_ready
for(var/i in 1 to max_scale)
if(antag_fraction < 0.25)
scaled_times += 1
- antag_fraction += get_antag_cap(population) / mode.roundstart_pop_ready // we added new antags, gotta update the %
+ antag_fraction += get_antag_cap(population) / SSdynamic.roundstart_pop_ready // we added new antags, gotta update the %
return scaled_times * scaling_cost
@@ -170,7 +166,7 @@
/datum/dynamic_ruleset/proc/rule_process()
return
-/// Called on game mode pre_setup for roundstart rulesets.
+/// Called on pre_setup for roundstart rulesets.
/// Do everything you need to do before job is assigned here.
/// IMPORTANT: ASSIGN special_role HERE
/datum/dynamic_ruleset/proc/pre_execute()
@@ -206,15 +202,15 @@
/// Runs from gamemode process() if ruleset fails to start, like delayed rulesets not getting valid candidates.
/// This one only handles refunding the threat, override in ruleset to clean up the rest.
/datum/dynamic_ruleset/proc/clean_up()
- mode.refund_threat(cost + (scaled_times * scaling_cost))
- mode.threat_log += "[worldtime2text()]: [ruletype] [name] refunded [cost + (scaled_times * scaling_cost)]. Failed to execute."
+ SSdynamic.refund_threat(cost + (scaled_times * scaling_cost))
+ SSdynamic.threat_log += "[worldtime2text()]: [ruletype] [name] refunded [cost + (scaled_times * scaling_cost)]. Failed to execute."
/// Gets weight of the ruleset
/// Note that this decreases weight if repeatable is TRUE and repeatable_weight_decrease is higher than 0
/// Note: If you don't want repeatable rulesets to decrease their weight use the weight variable directly
/datum/dynamic_ruleset/proc/get_weight()
if(repeatable && weight > 1 && repeatable_weight_decrease > 0)
- for(var/datum/dynamic_ruleset/DR in mode.executed_rules)
+ for(var/datum/dynamic_ruleset/DR in SSdynamic.executed_rules)
if(istype(DR, type))
weight = max(weight-repeatable_weight_decrease,1)
return weight
@@ -233,7 +229,7 @@
/datum/dynamic_ruleset/proc/trim_candidates()
return
-/// Set mode result and news report here.
+/// Set mode_result and news report here.
/// Only called if ruleset is flagged as HIGH_IMPACT_RULESET
/datum/dynamic_ruleset/proc/round_result()
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_latejoin.dm
similarity index 99%
rename from code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
rename to code/controllers/subsystem/dynamic/dynamic_rulesets_latejoin.dm
index 531be325dc0..683853990b9 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_latejoin.dm
@@ -29,7 +29,7 @@
if (M.mind && (M.mind.assigned_role.title in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role.title in restricted_roles)))
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
- var/threat = round(mode.threat_level/10)
+ var/threat = round(SSdynamic.threat_level/10)
var/ruleset_forced = (GLOB.dynamic_forced_rulesets[type] || RULESET_NOT_FORCED) == RULESET_FORCE_ENABLED
if (!ruleset_forced && job_check < required_enemies[threat])
log_dynamic("FAIL: [src] is not ready, because there are not enough enemies: [required_enemies[threat]] needed, [job_check] found")
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
similarity index 97%
rename from code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
rename to code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
index d39e9011ec3..d643e0a7c02 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
@@ -108,7 +108,7 @@
if (M.mind && (M.mind.assigned_role.title in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role.title in restricted_roles)))
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
- var/threat = round(mode.threat_level/10)
+ var/threat = round(SSdynamic.threat_level/10)
var/ruleset_forced = (GLOB.dynamic_forced_rulesets[type] || RULESET_NOT_FORCED) == RULESET_FORCE_ENABLED
if (!ruleset_forced && job_check < required_enemies[threat])
log_dynamic("FAIL: [src] is not ready, because there are not enough enemies: [required_enemies[threat]] needed, [job_check] found")
@@ -132,23 +132,23 @@
message_admins("Possible volunteers was 0. This shouldn't appear, because of ready(), unless you forced it!")
return
- mode.log_dynamic_and_announce("Polling [possible_volunteers.len] players to apply for the [name] ruleset.")
- candidates = poll_ghost_candidates("The mode is looking for volunteers to become [antag_flag] for [name]", antag_flag_override, antag_flag || antag_flag_override, poll_time = 300)
+ SSdynamic.log_dynamic_and_announce("Polling [possible_volunteers.len] players to apply for the [name] ruleset.")
+ candidates = poll_ghost_candidates("Looking for volunteers to become [antag_flag] for [name]", antag_flag_override, antag_flag || antag_flag_override, poll_time = 300)
if(!candidates || candidates.len <= 0)
- mode.log_dynamic_and_announce("The ruleset [name] received no applications.")
- mode.executed_rules -= src
+ SSdynamic.log_dynamic_and_announce("The ruleset [name] received no applications.")
+ SSdynamic.executed_rules -= src
attempt_replacement()
return
- mode.log_dynamic_and_announce("[candidates.len] players volunteered for [name].")
+ SSdynamic.log_dynamic_and_announce("[candidates.len] players volunteered for [name].")
review_applications()
/// Here is where you can check if your ghost applicants are valid for the ruleset.
/// Called by send_applications().
/datum/dynamic_ruleset/midround/from_ghosts/proc/review_applications()
if(candidates.len < required_applicants)
- mode.executed_rules -= src
+ SSdynamic.executed_rules -= src
return
for (var/i = 1, i <= required_candidates, i++)
if(candidates.len <= 0)
@@ -200,12 +200,12 @@
/datum/dynamic_ruleset/midround/from_ghosts/proc/attempt_replacement()
var/datum/dynamic_ruleset/midround/from_living/autotraitor/sleeper_agent = new
- mode.configure_ruleset(sleeper_agent)
+ SSdynamic.configure_ruleset(sleeper_agent)
- if (!mode.picking_specific_rule(sleeper_agent))
+ if (!SSdynamic.picking_specific_rule(sleeper_agent))
return
- mode.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator)
+ SSdynamic.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator)
///subtype to handle checking players
/datum/dynamic_ruleset/midround/from_living
@@ -378,7 +378,7 @@
var/list/operative_cap = list(2,2,3,3,4,5,5,5,5,5)
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/acceptable(population=0, threat_level=0)
- if (locate(/datum/dynamic_ruleset/roundstart/nuclear) in mode.executed_rules)
+ if (locate(/datum/dynamic_ruleset/roundstart/nuclear) in SSdynamic.executed_rules)
return FALSE // Unavailable if nuke ops were already sent at roundstart
indice_pop = min(operative_cap.len, round(living_players.len/5)+1)
required_candidates = operative_cap[indice_pop]
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
similarity index 99%
rename from code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
rename to code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
index 76708746723..7aa21d5388a 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
@@ -604,9 +604,9 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE)
. = ..()
message_admins("Starting a round of extended.")
log_game("Starting a round of extended.")
- mode.spend_roundstart_budget(mode.round_start_budget)
- mode.spend_midround_budget(mode.mid_round_budget)
- mode.threat_log += "[worldtime2text()]: Extended ruleset set threat to 0."
+ SSdynamic.spend_roundstart_budget(SSdynamic.round_start_budget)
+ SSdynamic.spend_midround_budget(SSdynamic.mid_round_budget)
+ SSdynamic.threat_log += "[worldtime2text()]: Extended ruleset set threat to 0."
return TRUE
//////////////////////////////////////////////
diff --git a/code/game/gamemodes/dynamic/dynamic_unfavorable_situation.dm b/code/controllers/subsystem/dynamic/dynamic_unfavorable_situation.dm
similarity index 90%
rename from code/game/gamemodes/dynamic/dynamic_unfavorable_situation.dm
rename to code/controllers/subsystem/dynamic/dynamic_unfavorable_situation.dm
index 1be6511433c..994f2e3f5de 100644
--- a/code/game/gamemodes/dynamic/dynamic_unfavorable_situation.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_unfavorable_situation.dm
@@ -2,12 +2,12 @@
/// This is used for impactful events like traitors hacking and creating more threat, or a revolutions victory.
/// It tries to spawn a heavy midround if possible, otherwise it will trigger a "bad" random event after a short period.
/// Calling this function will not use up any threat.
-/datum/game_mode/dynamic/proc/unfavorable_situation()
+/datum/controller/subsystem/dynamic/proc/unfavorable_situation()
SHOULD_NOT_SLEEP(TRUE)
INVOKE_ASYNC(src, PROC_REF(_unfavorable_situation))
-/datum/game_mode/dynamic/proc/_unfavorable_situation()
+/datum/controller/subsystem/dynamic/proc/_unfavorable_situation()
var/static/list/unfavorable_random_events = list()
if (!length(unfavorable_random_events))
unfavorable_random_events = generate_unfavourable_events()
@@ -24,7 +24,7 @@
picking_specific_rule(heavy_ruleset, forced = TRUE, ignore_cost = TRUE)
/// Return a valid heavy dynamic ruleset, or an empty list if there's no time to run any rulesets
-/datum/game_mode/dynamic/proc/generate_unfavourable_heavy_rulesets()
+/datum/controller/subsystem/dynamic/proc/generate_unfavourable_heavy_rulesets()
if (EMERGENCY_PAST_POINT_OF_NO_RETURN)
return list()
@@ -58,7 +58,7 @@
return possible_heavies
/// Filter the below list by which events can actually run on this map
-/datum/game_mode/dynamic/proc/generate_unfavourable_events()
+/datum/controller/subsystem/dynamic/proc/generate_unfavourable_events()
var/static/list/unfavorable_random_events = list(
/datum/round_event_control/earthquake,
/datum/round_event_control/immovable_rod,
diff --git a/code/game/gamemodes/dynamic/readme.md b/code/controllers/subsystem/dynamic/readme.md
similarity index 92%
rename from code/game/gamemodes/dynamic/readme.md
rename to code/controllers/subsystem/dynamic/readme.md
index d663e88a239..ec21626945e 100644
--- a/code/game/gamemodes/dynamic/readme.md
+++ b/code/controllers/subsystem/dynamic/readme.md
@@ -12,10 +12,10 @@ This threat is split into two separate budgets--`round_start_budget` and `mid_ro
This split is done with a similar method, known as the ["lorentz distribution"](https://en.wikipedia.org/wiki/Cauchy_distribution), exists to create a bell curve that ensures that while most rounds will have a threat level around ~50, chaotic and tame rounds still exist for variety.
-The process of creating these numbers occurs in `/datum/game_mode/dynamic/proc/generate_threat` (for creating the threat level) and `/datum/game_mode/dynamic/proc/generate_budgets` (for splitting the threat level into budgets).
+The process of creating these numbers occurs in `/datum/controller/subsystem/dynamic/proc/generate_threat` (for creating the threat level) and `/datum/controller/subsystem/dynamic/proc/generate_budgets` (for splitting the threat level into budgets).
## Deciding roundstart threats
-In `/datum/game_mode/dynamic/proc/roundstart()` (called when no admin chooses the rulesets explicitly), Dynamic uses the available roundstart budget to pick threats. This is done through the following system:
+In `/datum/controller/subsystem/dynamic/proc/roundstart()` (called when no admin chooses the rulesets explicitly), Dynamic uses the available roundstart budget to pick threats. This is done through the following system:
- All roundstart rulesets (remember, `/datum/dynamic_ruleset/roundstart`) are put into an associative list with their weight as the values (`drafted_rules`).
- Until there is either no roundstart budget left, or until there is no ruleset we can choose from with the available threat, a `pickweight` is done based on the drafted_rules. If the same threat is picked twice, it will "scale up". The meaning of this depends on the ruleset itself, using the `scaled_times` variable; traitors for instance will create more the higher they scale.
@@ -24,7 +24,7 @@ In `/datum/game_mode/dynamic/proc/roundstart()` (called when no admin chooses th
- After all roundstart threats are chosen, `/datum/dynamic_ruleset/proc/picking_roundstart_rule` is called for each, passing in the ruleset and the number of times it is scaled.
- In this stage, `pre_execute` is called, which is the function that will determine what players get what antagonists. If this function returns FALSE for whatever reason (in the case of an error), then its threat is refunded.
-After this process is done, any leftover roundstart threat will be given to the existing midround budget (done in `/datum/game_mode/dynamic/pre_setup()`).
+After this process is done, any leftover roundstart threat will be given to the existing midround budget (done in `/datum/controller/subsystem/dynamic/pre_setup()`).
## Deciding midround threats
@@ -105,13 +105,13 @@ Midround - Rulesets have additional types
## Configuration and variables
### Configuration
-Configuration can be done through a `config/dynamic.json` file. One is provided as example in the codebase. This config file, loaded in `/datum/game_mode/dynamic/pre_setup()`, directly overrides the values in the codebase, and so is perfect for making some rulesets harder/easier to get, turning them off completely, changing how much they cost, etc.
+Configuration can be done through a `config/dynamic.json` file. One is provided as example in the codebase. This config file, loaded in `/datum/controller/subsystem/dynamic/pre_setup()`, directly overrides the values in the codebase, and so is perfect for making some rulesets harder/easier to get, turning them off completely, changing how much they cost, etc.
The format of this file is:
```json
{
"Dynamic": {
- /* Configuration in here will directly override `/datum/game_mode/dynamic` itself. */
+ /* Configuration in here will directly override `/datum/controller/subsystem/dynamic` itself. */
/* Keys are variable names, values are their new values. */
},
@@ -172,7 +172,7 @@ This is not a complete list--search "configurable" in this README to learn more.
The "Dynamic" key has the following configurable values:
- `pop_per_requirement` - The default value of `pop_per_requirement` for any ruleset that does not explicitly set it. Defaults to 6.
- `latejoin_delay_min`, `latejoin_delay_max` - The time range, in deciseconds (take your seconds, and multiply by 10), for a latejoin to attempt rolling. Once this timer is finished, a new one will be created within the same range.
- - Suppose you have a `latejoin_delay_min` of 600 (60 seconds, 1 minute) and a `latejoin_delay_max` of 1800 (180 seconds, 3 minutes). Once the round starts, a random number in this range will be picked--let's suppose 1.5 minutes. After 1.5 minutes, Dynamic will decide if a latejoin threat should be created (a probability of `/datum/game_mode/dynamic/proc/get_injection_chance()`). Regardless of its decision, a new timer will be started within the range of 1 to 3 minutes, repeatedly.
+ - Suppose you have a `latejoin_delay_min` of 600 (60 seconds, 1 minute) and a `latejoin_delay_max` of 1800 (180 seconds, 3 minutes). Once the round starts, a random number in this range will be picked--let's suppose 1.5 minutes. After 1.5 minutes, Dynamic will decide if a latejoin threat should be created (a probability of `/datum/controller/subsystem/dynamic/proc/get_injection_chance()`). Regardless of its decision, a new timer will be started within the range of 1 to 3 minutes, repeatedly.
- `threat_curve_centre` - A number between -5 and +5. A negative value will give a more peaceful round and a positive value will give a round with higher threat.
- `threat_curve_width` - A number between 0.5 and 4. Higher value will favour extreme rounds and lower value rounds closer to the average.
- `roundstart_split_curve_centre` - A number between -5 and +5. Equivalent to threat_curve_centre, but for the budget split. A negative value will weigh towards midround rulesets, and a positive value will weight towards roundstart ones.
@@ -187,7 +187,7 @@ Random events have the potential to be hijacked by Dynamic to keep the pace of m
`/datum/round_event_control/dynamic_should_hijack` is a variable to random events to allow Dynamic to hijack them, and defaults to FALSE. This is set to TRUE for random events that spawn antagonists.
-In `/datum/game_mode/dynamic/on_pre_random_event` (in `dynamic_hijacking.dm`), Dynamic hooks to random events. If the `dynamic_should_hijack` variable is TRUE, the following sequence of events occurs:
+In `/datum/controller/subsystem/dynamic/on_pre_random_event` (in `dynamic_hijacking.dm`), Dynamic hooks to random events. If the `dynamic_should_hijack` variable is TRUE, the following sequence of events occurs:

diff --git a/code/game/gamemodes/dynamic/ruleset_picking.dm b/code/controllers/subsystem/dynamic/ruleset_picking.dm
similarity index 90%
rename from code/game/gamemodes/dynamic/ruleset_picking.dm
rename to code/controllers/subsystem/dynamic/ruleset_picking.dm
index 0ef6299e159..e3de3289899 100644
--- a/code/game/gamemodes/dynamic/ruleset_picking.dm
+++ b/code/controllers/subsystem/dynamic/ruleset_picking.dm
@@ -8,7 +8,7 @@
*
* * max_allowed_attempts - Allows you to configure how many times the proc will attempt to pick a ruleset before giving up.
*/
-/datum/game_mode/dynamic/proc/pick_ruleset(list/drafted_rules, max_allowed_attempts = INFINITY)
+/datum/controller/subsystem/dynamic/proc/pick_ruleset(list/drafted_rules, max_allowed_attempts = INFINITY)
if (only_ruleset_executed)
log_dynamic("FAIL: only_ruleset_executed")
return null
@@ -52,7 +52,7 @@
return null
/// Executes a random midround ruleset from the list of drafted rules.
-/datum/game_mode/dynamic/proc/pick_midround_rule(list/drafted_rules, description)
+/datum/controller/subsystem/dynamic/proc/pick_midround_rule(list/drafted_rules, description)
log_dynamic("Rolling [drafted_rules.len] [description]")
var/datum/dynamic_ruleset/rule = pick_ruleset(drafted_rules)
@@ -75,7 +75,7 @@
return rule
/// Fired after admins do not cancel a midround injection.
-/datum/game_mode/dynamic/proc/execute_midround_rule(datum/dynamic_ruleset/rule)
+/datum/controller/subsystem/dynamic/proc/execute_midround_rule(datum/dynamic_ruleset/rule)
current_midround_rulesets = null
midround_injection_timer_id = null
if (!rule.repeatable)
@@ -83,7 +83,7 @@
addtimer(CALLBACK(src, PROC_REF(execute_midround_latejoin_rule), rule), rule.delay)
/// Mainly here to facilitate delayed rulesets. All midround/latejoin rulesets are executed with a timered callback to this proc.
-/datum/game_mode/dynamic/proc/execute_midround_latejoin_rule(sent_rule)
+/datum/controller/subsystem/dynamic/proc/execute_midround_latejoin_rule(sent_rule)
var/datum/dynamic_ruleset/rule = sent_rule
spend_midround_budget(rule.cost, threat_log, "[worldtime2text()]: [rule.ruletype] [rule.name]")
rule.pre_execute(GLOB.alive_player_list.len)
@@ -109,7 +109,7 @@
return FALSE
/// Fired when an admin cancels the current midround injection.
-/datum/game_mode/dynamic/proc/admin_cancel_midround(mob/user, timer_id)
+/datum/controller/subsystem/dynamic/proc/admin_cancel_midround(mob/user, timer_id)
if (midround_injection_timer_id != timer_id || !deltimer(midround_injection_timer_id))
to_chat(user, span_notice("Too late!"))
return
@@ -120,7 +120,7 @@
current_midround_rulesets = null
/// Fired when an admin requests a different midround injection.
-/datum/game_mode/dynamic/proc/admin_different_midround(mob/user, timer_id)
+/datum/controller/subsystem/dynamic/proc/admin_different_midround(mob/user, timer_id)
if (midround_injection_timer_id != timer_id || !deltimer(midround_injection_timer_id))
to_chat(user, span_notice("Too late!"))
return
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index b3c449a6bb9..4480db95af9 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -20,8 +20,6 @@ SUBSYSTEM_DEF(ticker)
/// Boolean to track and check if our subsystem setup is done.
var/setup_done = FALSE
- var/datum/game_mode/mode = null
-
var/login_music //music played in pregame lobby
var/round_end_sound //music/jingle played when the world reboots
var/round_end_sound_sent = TRUE //If all clients have loaded it
@@ -206,10 +204,9 @@ SUBSYSTEM_DEF(ticker)
SEND_SIGNAL(src, COMSIG_TICKER_ERROR_SETTING_UP)
if(GAME_STATE_PLAYING)
- mode.process(wait * 0.1)
check_queue()
- if(!roundend_check_paused && (mode.check_finished() || force_ending))
+ if(!roundend_check_paused && (check_finished() || force_ending))
current_state = GAME_STATE_FINISHED
toggle_ooc(TRUE) // Turn it on
toggle_dooc(TRUE)
@@ -217,17 +214,27 @@ SUBSYSTEM_DEF(ticker)
check_maprotate()
Master.SetRunLevel(RUNLEVEL_POSTGAME)
+/// Checks if the round should be ending, called every ticker tick
+/datum/controller/subsystem/ticker/proc/check_finished()
+ if(!setup_done)
+ return FALSE
+ if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME))
+ return TRUE
+ if(GLOB.station_was_nuked)
+ return TRUE
+ if(GLOB.revolutionary_win)
+ return TRUE
+ return FALSE
+
/datum/controller/subsystem/ticker/proc/setup()
to_chat(world, span_boldannounce("Starting game..."))
var/init_start = world.timeofday
- mode = new /datum/game_mode/dynamic
-
CHECK_TICK
- //Configure mode and assign player to special mode stuff
- var/can_continue = 0
- can_continue = src.mode.pre_setup() //Choose antagonists
+ //Configure mode and assign player to antagonists
+ var/can_continue = FALSE
+ can_continue = SSdynamic.pre_setup() //Choose antagonists
CHECK_TICK
can_continue = can_continue && SSjob.DivideOccupations() //Distribute jobs
CHECK_TICK
@@ -235,7 +242,6 @@ SUBSYSTEM_DEF(ticker)
if(!GLOB.Debug2)
if(!can_continue)
log_game("Game failed pre_setup")
- QDEL_NULL(mode)
to_chat(world, "Error setting up game. Reverting to pre-game lobby.")
SSjob.ResetOccupations()
return FALSE
@@ -293,7 +299,7 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/proc/PostSetup()
set waitfor = FALSE
- mode.post_setup()
+ SSdynamic.post_setup()
GLOB.start_state = new /datum/station_state()
GLOB.start_state.count()
@@ -525,7 +531,6 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/Recover()
current_state = SSticker.current_state
force_ending = SSticker.force_ending
- mode = SSticker.mode
login_music = SSticker.login_music
round_end_sound = SSticker.round_end_sound
diff --git a/code/game/gamemodes/dynamic/dynamic_simulations.dm b/code/game/gamemodes/dynamic/dynamic_simulations.dm
deleted file mode 100644
index 63da54becee..00000000000
--- a/code/game/gamemodes/dynamic/dynamic_simulations.dm
+++ /dev/null
@@ -1,122 +0,0 @@
-#ifdef TESTING
-/datum/dynamic_simulation
- var/datum/game_mode/dynamic/gamemode
- var/datum/dynamic_simulation_config/config
- var/list/mock_candidates = list()
-
-/datum/dynamic_simulation/proc/initialize_gamemode(forced_threat)
- gamemode = new
-
- if (forced_threat)
- gamemode.create_threat(forced_threat)
- else
- gamemode.generate_threat()
-
- gamemode.generate_budgets()
- gamemode.set_cooldowns()
-
-/datum/dynamic_simulation/proc/create_candidates(players)
- GLOB.new_player_list.Cut()
-
- for (var/_ in 1 to players)
- var/mob/dead/new_player/mock_new_player = new
- mock_new_player.ready = PLAYER_READY_TO_PLAY
-
- var/datum/mind/mock_mind = new
- mock_new_player.mind = mock_mind
-
- var/datum/client_interface/mock_client = new
-
- var/datum/preferences/prefs = new
- var/list/be_special = list()
- for (var/special_role in GLOB.special_roles)
- be_special += special_role
-
- prefs.be_special = be_special
- mock_client.prefs = prefs
-
- mock_new_player.mock_client = mock_client
-
- mock_candidates += mock_new_player
-
-/datum/dynamic_simulation/proc/simulate(datum/dynamic_simulation_config/config)
- src.config = config
-
- initialize_gamemode(config.forced_threat_level)
- create_candidates(config.roundstart_players)
- gamemode.pre_setup()
-
- var/total_antags = 0
- for (var/_ruleset in gamemode.executed_rules)
- var/datum/dynamic_ruleset/ruleset = _ruleset
- total_antags += ruleset.assigned.len
-
- return list(
- "roundstart_players" = config.roundstart_players,
- "threat_level" = gamemode.threat_level,
- "snapshot" = list(
- "antag_percent" = total_antags / config.roundstart_players,
- "remaining_threat" = gamemode.mid_round_budget,
- "rulesets" = gamemode.executed_rules.Copy(),
- ),
- )
-
-/datum/dynamic_simulation_config
- /// How many players round start should there be?
- var/roundstart_players
-
- /// Optional, force this threat level instead of picking randomly through the lorentz distribution
- var/forced_threat_level
-
-/client/proc/run_dynamic_simulations()
- set name = "Run Dynamic Simulations"
- set category = "Debug"
-
- var/simulations = input(usr, "Enter number of simulations") as num
- var/roundstart_players = input(usr, "Enter number of round start players") as num
- var/forced_threat_level = input(usr, "Enter forced threat level, if you want one") as num | null
-
- SSticker.mode = new /datum/game_mode/dynamic
- message_admins("Running dynamic simulations...")
-
- var/list/outputs = list()
-
- var/datum/dynamic_simulation_config/dynamic_config = new
-
- if (roundstart_players)
- dynamic_config.roundstart_players = roundstart_players
-
- if (forced_threat_level)
- dynamic_config.forced_threat_level = forced_threat_level
-
- for (var/count in 1 to simulations)
- var/datum/dynamic_simulation/simulator = new
- var/output = simulator.simulate(dynamic_config)
- outputs += list(output)
-
- if (CHECK_TICK)
- log_world("[count]/[simulations]")
-
- message_admins("Writing file...")
- WRITE_FILE(file("[GLOB.log_directory]/dynamic_simulations.json"), json_encode(outputs))
- message_admins("Writing complete.")
-
-/proc/export_dynamic_json_of(ruleset_list)
- var/list/export = list()
-
- for (var/_ruleset in ruleset_list)
- var/datum/dynamic_ruleset/ruleset = _ruleset
- export[ruleset.name] = list(
- "repeatable_weight_decrease" = ruleset.repeatable_weight_decrease,
- "weight" = ruleset.weight,
- "cost" = ruleset.cost,
- "scaling_cost" = ruleset.scaling_cost,
- "antag_cap" = ruleset.antag_cap,
- "pop_per_requirement" = ruleset.pop_per_requirement,
- "requirements" = ruleset.requirements,
- "base_prob" = ruleset.base_prob,
- )
-
- return export
-
-#endif
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
deleted file mode 100644
index 39dcb81920a..00000000000
--- a/code/game/gamemodes/game_mode.dm
+++ /dev/null
@@ -1,228 +0,0 @@
-
-
-/*
- * GAMEMODES (by Rastaf0)
- *
- * In the new mode system all special roles are fully supported.
- * You can have proper wizards/traitors/changelings/cultists during any mode.
- * Only two things really depends on gamemode:
- * 1. Starting roles, equipment and preparations
- * 2. Conditions of finishing the round.
- *
- */
-
-
-/datum/game_mode
-
-///Attempts to select players for special roles the mode might have.
-/datum/game_mode/proc/pre_setup()
- return TRUE
-
-///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things
-/datum/game_mode/proc/post_setup(report) //Gamemodes can override the intercept report. Passing TRUE as the argument will force a report.
- if(!report)
- report = !CONFIG_GET(flag/no_intercept_report)
- addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(display_roundstart_logout_report)), ROUNDSTART_LOGOUT_REPORT_TIME)
-
- if(CONFIG_GET(flag/reopen_roundstart_suicide_roles))
- var/delay = CONFIG_GET(number/reopen_roundstart_suicide_roles_delay)
- if(delay)
- delay = (delay SECONDS)
- else
- delay = (4 MINUTES) //default to 4 minutes if the delay isn't defined.
- addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(reopen_roundstart_suicide_roles)), delay)
-
- if(SSdbcore.Connect())
- var/list/to_set = list()
- var/arguments = list()
- if(SSticker.mode)
- to_set += "game_mode = :game_mode"
- arguments["game_mode"] = SSticker.mode
- if(GLOB.revdata.originmastercommit)
- to_set += "commit_hash = :commit_hash"
- arguments["commit_hash"] = GLOB.revdata.originmastercommit
- if(to_set.len)
- arguments["round_id"] = GLOB.round_id
- var/datum/db_query/query_round_game_mode = SSdbcore.NewQuery(
- "UPDATE [format_table_name("round")] SET [to_set.Join(", ")] WHERE id = :round_id",
- arguments
- )
- query_round_game_mode.Execute()
- qdel(query_round_game_mode)
- return TRUE
-
-
-///Handles late-join antag assignments
-/datum/game_mode/proc/make_antag_chance(mob/living/carbon/human/character)
- return
-
-/// 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))
- return TRUE
- if(GLOB.station_was_nuked)
- return TRUE
- if(GLOB.revolutionary_win)
- return TRUE
- return FALSE
-
-/*
- * Generate a list of station goals available to purchase to report to the crew.
- *
- * Returns a formatted string all station goals that are available to the station.
- */
-/datum/game_mode/proc/generate_station_goal_report()
- if(!GLOB.station_goals.len)
- return
- . = "
Special Orders for [station_name()]:
"
- var/list/goal_reports = list()
- for(var/datum/station_goal/station_goal as anything in GLOB.station_goals)
- station_goal.on_report()
- goal_reports += station_goal.get_report()
-
- . += goal_reports.Join("
")
- return
-
-/*
- * Generate a list of active station traits to report to the crew.
- *
- * Returns a formatted string of all station traits (that are shown) affecting the station.
- */
-/datum/game_mode/proc/generate_station_trait_report()
- var/trait_list_string = ""
- for(var/datum/station_trait/station_trait as anything in SSstation.station_traits)
- if(!station_trait.show_in_report)
- continue
- trait_list_string += "[station_trait.get_report()]
"
- if(trait_list_string != "")
- return "
Identified shift divergencies:
" + trait_list_string
- return
-
-/datum/game_mode/proc/generate_report_footnote()
- var/footnote_pile = ""
-
- for(var/datum/command_footnote/footnote in SScommunications.command_report_footnotes)
- footnote_pile += "[footnote.message]
"
- footnote_pile += "[footnote.signature]
"
- footnote_pile += "
"
-
- return "
Additional Notes:
" + footnote_pile
-
-/proc/reopen_roundstart_suicide_roles()
- var/include_command = CONFIG_GET(flag/reopen_roundstart_suicide_roles_command_positions)
- var/list/reopened_jobs = list()
-
- for(var/mob/living/quitter in GLOB.suicided_mob_list)
- var/datum/job/job = SSjob.GetJob(quitter.job)
- if(!job || !(job.job_flags & JOB_REOPEN_ON_ROUNDSTART_LOSS))
- continue
- if(!include_command && job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
- continue
- job.current_positions = max(job.current_positions - 1, 0)
- reopened_jobs += quitter.job
-
- if(CONFIG_GET(flag/reopen_roundstart_suicide_roles_command_report))
- if(reopened_jobs.len)
- var/reopened_job_report_positions
- for(var/dead_dudes_job in reopened_jobs)
- reopened_job_report_positions = "[reopened_job_report_positions ? "[reopened_job_report_positions]\n":""][dead_dudes_job]"
-
- var/suicide_command_report = "[command_name()] Human Resources Board
\
- Notice of Personnel Change
\
- To personnel management staff aboard [station_name()]:
\
- Our medical staff have detected a series of anomalies in the vital sensors \
- of some of the staff aboard your station.
\
- Further investigation into the situation on our end resulted in us discovering \
- a series of rather... unforturnate decisions that were made on the part of said staff.
\
- As such, we have taken the liberty to automatically reopen employment opportunities for the positions of the crew members \
- who have decided not to partake in our research. We will be forwarding their cases to our employment review board \
- to determine their eligibility for continued service with the company (and of course the \
- continued storage of cloning records within the central medical backup server.)
\
- The following positions have been reopened on our behalf:
\
- [reopened_job_report_positions]"
-
- print_command_report(suicide_command_report, "Central Command Personnel Update")
-
-//////////////////////////
-//Reports player logouts//
-//////////////////////////
-/proc/display_roundstart_logout_report()
- var/list/msg = list("[span_boldnotice("Roundstart logout report")]\n\n")
- for(var/i in GLOB.mob_living_list)
- var/mob/living/L = i
- var/mob/living/carbon/C = L
- if (istype(C) && !C.last_mind)
- continue // never had a client
-
- if(L.ckey && !GLOB.directory[L.ckey])
- msg += "[L.name] ([L.key]), the [L.job] (Disconnected)\n"
-
-
- if(L.ckey && L.client)
- var/failed = FALSE
- if(L.client.inactivity >= ROUNDSTART_LOGOUT_AFK_THRESHOLD) //Connected, but inactive (alt+tabbed or something)
- msg += "[L.name] ([L.key]), the [L.job] (Connected, Inactive)\n"
- failed = TRUE //AFK client
- if(!failed && L.stat)
- if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
- msg += "[L.name] ([L.key]), the [L.job] ([span_boldannounce("Suicide")])\n"
- failed = TRUE //Disconnected client
- if(!failed && (L.stat == UNCONSCIOUS || L.stat == HARD_CRIT))
- msg += "[L.name] ([L.key]), the [L.job] (Dying)\n"
- failed = TRUE //Unconscious
- if(!failed && L.stat == DEAD)
- msg += "[L.name] ([L.key]), the [L.job] (Dead)\n"
- failed = TRUE //Dead
-
- continue //Happy connected client
- for(var/mob/dead/observer/D in GLOB.dead_mob_list)
- if(D.mind && D.mind.current == L)
- if(L.stat == DEAD)
- if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Suicide")])\n"
- continue //Disconnected client
- else
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n"
- continue //Dead mob, ghost abandoned
- else
- if(D.can_reenter_corpse)
- continue //Adminghost, or cult/wizard ghost
- else
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Ghosted")])\n"
- continue //Ghosted while alive
-
- var/concatenated_message = msg.Join()
- log_admin(concatenated_message)
- to_chat(GLOB.admins, concatenated_message)
-
-/datum/game_mode/proc/generate_station_goals(greenshift)
- var/goal_budget = greenshift ? INFINITY : CONFIG_GET(number/station_goal_budget)
- var/list/possible = subtypesof(/datum/station_goal)
- // Remove all goals that require space if space is not present
- if(SSmapping.is_planetary())
- for(var/datum/station_goal/goal as anything in possible)
- if(initial(goal.requires_space))
- possible -= goal
- var/goal_weights = 0
- while(possible.len && goal_weights < goal_budget)
- var/datum/station_goal/picked = pick_n_take(possible)
- goal_weights += initial(picked.weight)
- GLOB.station_goals += new picked
-
-//Set result and news report here
-/datum/game_mode/proc/set_round_result()
- SSticker.mode_result = "undefined"
- // Something nuked the station - it wasn't nuke ops (they set their own via their rulset)
- if(GLOB.station_was_nuked)
- SSticker.news_report = STATION_NUKED
- if(SSsupermatter_cascade.cascade_initiated)
- SSticker.news_report = SUPERMATTER_CASCADE
- // Only show this one if we have nothing better to show
- if(EMERGENCY_ESCAPED_OR_ENDGAMED && !SSticker.news_report)
- SSticker.news_report = SSshuttle.emergency?.is_hijacked() ? SHUTTLE_HIJACK : STATION_EVACUATED
-
-/// Mode specific admin panel.
-/datum/game_mode/proc/admin_panel()
- return
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index dd6ca73183e..23d4a38269b 100644
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -861,7 +861,6 @@
hacker.log_message("hacked a communications console, resulting in: [picked_option].", LOG_GAME, log_globally = TRUE)
switch(picked_option)
if(HACK_PIRATE) // Triggers pirates, which the crew may be able to pay off to prevent
- var/datum/game_mode/dynamic/dynamic = SSticker.mode
var/list/pirate_rulesets = list(
/datum/dynamic_ruleset/midround/pirates,
/datum/dynamic_ruleset/midround/dangerous_pirates,
@@ -870,7 +869,7 @@
"Attention crew: sector monitoring reports a massive jump-trace from an enemy vessel destined for your system. Prepare for imminent hostile contact.",
"[command_name()] High-Priority Update",
)
- dynamic.picking_specific_rule(pick(pirate_rulesets), forced = TRUE, ignore_cost = TRUE)
+ SSdynamic.picking_specific_rule(pick(pirate_rulesets), forced = TRUE, ignore_cost = TRUE)
if(HACK_FUGITIVES) // Triggers fugitives, which can cause confusion / chaos as the crew decides which side help
priority_announce(
@@ -891,22 +890,20 @@
continue
shake_camera(crew_member, 15, 1)
- var/datum/game_mode/dynamic/dynamic = SSticker.mode
- dynamic.unfavorable_situation()
+ SSdynamic.unfavorable_situation()
if(HACK_SLEEPER) // Trigger one or multiple sleeper agents with the crew (or for latejoining crew)
var/datum/dynamic_ruleset/midround/sleeper_agent_type = /datum/dynamic_ruleset/midround/from_living/autotraitor
- var/datum/game_mode/dynamic/dynamic = SSticker.mode
var/max_number_of_sleepers = clamp(round(length(GLOB.alive_player_list) / 20), 1, 3)
var/num_agents_created = 0
for(var/num_agents in 1 to rand(1, max_number_of_sleepers))
- if(!dynamic.picking_specific_rule(sleeper_agent_type, forced = TRUE, ignore_cost = TRUE))
+ if(!SSdynamic.picking_specific_rule(sleeper_agent_type, forced = TRUE, ignore_cost = TRUE))
break
num_agents_created++
if(num_agents_created <= 0)
// We failed to run any midround sleeper agents, so let's be patient and run latejoin traitor
- dynamic.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator, forced = TRUE, ignore_cost = TRUE)
+ SSdynamic.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator, forced = TRUE, ignore_cost = TRUE)
else
// We spawned some sleeper agents, nice - give them a report to kickstart the paranoia
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index a1f083f6139..99aa63325ad 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -31,8 +31,7 @@
dat += "
"
if(SSticker.IsRoundInProgress())
dat += "(Game Mode Panel)
"
- if(istype(SSticker.mode, /datum/game_mode/dynamic))
- dat += "(Manage Dynamic Rulesets)
"
+ dat += "(Manage Dynamic Rulesets)
"
dat += {"
Create Object
@@ -147,9 +146,6 @@
user << browse(dat, "window=dyn_mode_options;size=900x650")
/datum/admins/proc/dynamic_ruleset_manager(mob/user)
- if (SSticker.current_state > GAME_STATE_PREGAME && !istype(SSticker.mode, /datum/game_mode/dynamic))
- return // Not running dynamic
-
var/dat = "Dynamic Ruleset Management
\
Change these options to forcibly enable or disable dynamic rulesets.
\
Disabled rulesets will never run, even if they would otherwise be valid.
\
@@ -172,11 +168,10 @@
user << browse(dat, "window=dyn_mode_options;size=900x650")
return
- var/datum/game_mode/dynamic/current_mode = SSticker.mode
var/pop_count = length(GLOB.alive_player_list)
- var/threat_level = current_mode.threat_level
- dat += dynamic_ruleset_category_during_round_display("Latejoin", current_mode.latejoin_rules, pop_count, threat_level)
- dat += dynamic_ruleset_category_during_round_display("Midround", current_mode.midround_rules, pop_count, threat_level)
+ var/threat_level = SSdynamic.threat_level
+ dat += dynamic_ruleset_category_during_round_display("Latejoin", SSdynamic.latejoin_rules, pop_count, threat_level)
+ dat += dynamic_ruleset_category_during_round_display("Midround", SSdynamic.midround_rules, pop_count, threat_level)
user << browse(dat, "window=dyn_mode_options;size=900x650")
/datum/admins/proc/dynamic_ruleset_category_pre_start_display(title, list/rules)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 8330566487c..416b916cb21 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -169,7 +169,6 @@ GLOBAL_PROTECT(admin_verbs_debug)
return list(
#ifdef TESTING /* Keep these at the top to not make the list look fugly */
/client/proc/check_missing_sprites,
- /client/proc/run_dynamic_simulations,
#endif
/proc/machine_upgrade,
/datum/admins/proc/create_or_modify_area,
diff --git a/code/modules/admin/check_antagonists.dm b/code/modules/admin/check_antagonists.dm
index 3b684ed4467..04db519ffb5 100644
--- a/code/modules/admin/check_antagonists.dm
+++ b/code/modules/admin/check_antagonists.dm
@@ -95,8 +95,7 @@
tgui_alert(usr, "The game hasn't started yet!")
return
var/list/dat = list("Round StatusRound Status
")
- if(istype(SSticker.mode, /datum/game_mode/dynamic)) // Currently only used by dynamic. If more start using this, find a better way.
- dat += "Game Mode Panel
"
+ dat += "Game Mode Panel
"
dat += "Round Duration: [DisplayTimeText(world.time - SSticker.round_start_time)]
"
dat += "Emergency shuttle
"
if(EMERGENCY_IDLE_OR_RECALLED)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 50fe0ed1e46..08bfe583d0a 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -69,7 +69,7 @@
else if(href_list["gamemode_panel"])
if(!check_rights(R_ADMIN))
return
- SSticker.mode.admin_panel()
+ SSdynamic.admin_panel()
else if(href_list["call_shuttle"])
if(!check_rights(R_ADMIN))
@@ -400,7 +400,7 @@
else if(href_list["f_dynamic_roundstart"])
if(!check_rights(R_ADMIN))
return
- if(SSticker?.mode)
+ if(SSticker.HasRoundStarted())
return tgui_alert(usr, "The game has already started.")
var/roundstart_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart))
@@ -467,7 +467,7 @@
if(!check_rights(R_ADMIN))
return
- if(SSticker?.mode)
+ if(SSticker.HasRoundStarted())
return tgui_alert(usr, "The game has already started.")
dynamic_mode_options(usr)
@@ -501,7 +501,7 @@
if(!check_rights(R_ADMIN))
return
- if(SSticker?.mode)
+ if(SSticker.HasRoundStarted())
return tgui_alert(usr, "The game has already started.")
var/new_value = input(usr, "Enter the forced threat level for dynamic mode.", "Forced threat level") as num
diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
index 2e66001bfbe..6082567129c 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
@@ -523,7 +523,7 @@ GLOBAL_VAR(station_nuke_source)
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NUKE_DEVICE_DETONATING, src)
- if(SSticker?.mode)
+ if(SSticker.HasRoundStarted())
SSticker.roundend_check_paused = TRUE
addtimer(CALLBACK(src, PROC_REF(actually_explode)), 10 SECONDS)
return TRUE
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index edd2755de75..caacb2bb603 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -498,7 +498,7 @@
SM.Grant(newstruct)
newstruct.key = target.key
var/atom/movable/screen/alert/bloodsense/BS
- if(newstruct.mind && ((stoner && IS_CULTIST(stoner)) || cultoverride) && SSticker?.mode)
+ if(newstruct.mind && ((stoner && IS_CULTIST(stoner)) || cultoverride) && SSticker.HasRoundStarted())
newstruct.mind.add_antag_datum(/datum/antagonist/cult/construct)
if(IS_CULTIST(stoner) || cultoverride)
to_chat(newstruct, "You are still bound to serve the cult[stoner ? " and [stoner]":""], follow [stoner ? stoner.p_their() : "their"] orders and help [stoner ? stoner.p_them() : "them"] complete [stoner ? stoner.p_their() : "their"] goals at all costs.")
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm
index c31bb4ce2ce..fe5f69fd9fa 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm
@@ -21,8 +21,7 @@
/datum/spellbook_entry/summon/guns/can_be_purchased()
// Summon Guns requires 98 threat.
- var/datum/game_mode/dynamic/mode = SSticker.mode
- if(mode.threat_level < MINIMUM_THREAT_FOR_RITUALS)
+ if(SSdynamic.threat_level < MINIMUM_THREAT_FOR_RITUALS)
return FALSE
// Also must be config enabled
return !CONFIG_GET(flag/no_summon_guns)
@@ -39,8 +38,7 @@
/datum/spellbook_entry/summon/magic/can_be_purchased()
// Summon Magic requires 98 threat.
- var/datum/game_mode/dynamic/mode = SSticker.mode
- if(mode.threat_level < MINIMUM_THREAT_FOR_RITUALS)
+ if(SSdynamic.threat_level < MINIMUM_THREAT_FOR_RITUALS)
return FALSE
// Also must be config enabled
return !CONFIG_GET(flag/no_summon_magic)
@@ -60,8 +58,7 @@
/datum/spellbook_entry/summon/events/can_be_purchased()
// Summon Events requires 98 threat.
- var/datum/game_mode/dynamic/mode = SSticker.mode
- if(mode.threat_level < MINIMUM_THREAT_FOR_RITUALS)
+ if(SSdynamic.threat_level < MINIMUM_THREAT_FOR_RITUALS)
return FALSE
// Also, must be config enabled
return !CONFIG_GET(flag/no_summon_events)
@@ -138,8 +135,7 @@
return ..()
/datum/spellbook_entry/summon/specific_spell/can_be_purchased()
- var/datum/game_mode/dynamic/mode = SSticker.mode
- if(mode.threat_level < MINIMUM_THREAT_FOR_RITUALS)
+ if(SSdynamic.threat_level < MINIMUM_THREAT_FOR_RITUALS)
return FALSE
if(GLOB.mass_teaching)
return FALSE
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
index 94b6254d459..a951a5daf42 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
@@ -51,8 +51,7 @@
if (DOOM_METEORS)
var/datum/dynamic_ruleset/roundstart/meteor/meteors = new()
meteors.meteordelay = 0
- var/datum/game_mode/dynamic/mode = SSticker.mode
- mode.execute_roundstart_rule(meteors) // Meteors will continue until morale is crushed.
+ SSdynamic.execute_roundstart_rule(meteors) // Meteors will continue until morale is crushed.
priority_announce("Meteors have been detected on collision course with the station.", "Meteor Alert", ANNOUNCER_METEORS)
#undef DOOM_SINGULARITY
diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm
index 99b708110e1..a90cd6a4d7b 100644
--- a/code/modules/events/_event.dm
+++ b/code/modules/events/_event.dm
@@ -87,8 +87,7 @@
if(ispath(typepath, /datum/round_event/ghost_role) && !(GLOB.ghost_role_flags & GHOSTROLE_MIDROUND_EVENT))
return FALSE
- var/datum/game_mode/dynamic/dynamic = SSticker.mode
- if (istype(dynamic) && dynamic_should_hijack && dynamic.random_event_hijacked != HIJACKED_NOTHING)
+ if (dynamic_should_hijack && SSdynamic.random_event_hijacked != HIJACKED_NOTHING)
return FALSE
return TRUE
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 99897971d18..4324b81c14c 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -235,10 +235,10 @@
if(SSshuttle.emergency)
switch(SSshuttle.emergency.mode)
if(SHUTTLE_RECALL, SHUTTLE_IDLE)
- SSticker.mode.make_antag_chance(humanc)
+ SSdynamic.make_antag_chance(humanc)
if(SHUTTLE_CALL)
if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergency_call_time)*0.5)
- SSticker.mode.make_antag_chance(humanc)
+ SSdynamic.make_antag_chance(humanc)
if((job.job_flags & JOB_ASSIGN_QUIRKS) && humanc && CONFIG_GET(flag/roundstart_traits))
SSquirks.AssignQuirks(humanc, humanc.client)
diff --git a/code/modules/mob/mob_lists.dm b/code/modules/mob/mob_lists.dm
index f0a5275bf71..75904737c3f 100644
--- a/code/modules/mob/mob_lists.dm
+++ b/code/modules/mob/mob_lists.dm
@@ -53,7 +53,7 @@
GLOB.keyloop_list |= src
else if(stat != DEAD || !SSlag_switch?.measures[DISABLE_DEAD_KEYLOOP])
GLOB.keyloop_list |= src
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
if(stat == DEAD)
add_to_current_dead_players()
@@ -65,7 +65,7 @@
SHOULD_CALL_PARENT(TRUE)
GLOB.player_list -= src
GLOB.keyloop_list -= src
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
if(stat == DEAD)
remove_from_current_dead_players()
@@ -75,12 +75,12 @@
///Adds the cliented mob reference to either the list of dead player-mobs or to the list of observers, depending on how they joined the game.
/mob/proc/add_to_current_dead_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
GLOB.dead_player_list |= src
/mob/dead/observer/add_to_current_dead_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
if(started_as_observer)
GLOB.current_observers_list |= src
@@ -92,12 +92,12 @@
///Removes the mob reference from either the list of dead player-mobs or from the list of observers, depending on how they joined the game.
/mob/proc/remove_from_current_dead_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
GLOB.dead_player_list -= src
/mob/dead/observer/remove_from_current_dead_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
if(started_as_observer)
GLOB.current_observers_list -= src
@@ -107,7 +107,7 @@
///Adds the cliented mob reference to the list of living player-mobs. If the mob is an antag, it adds it to the list of living antag player-mobs.
/mob/proc/add_to_current_living_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
GLOB.alive_player_list |= src
if(mind && (mind.special_role || length(mind.antag_datums)))
@@ -115,7 +115,7 @@
///Removes the mob reference from the list of living player-mobs. If the mob is an antag, it removes it from the list of living antag player-mobs.
/mob/proc/remove_from_current_living_players()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
GLOB.alive_player_list -= src
if(LAZYLEN(mind?.antag_datums))
@@ -124,7 +124,7 @@
///Adds the cliented mob reference to the list of living antag player-mobs.
/mob/proc/add_to_current_living_antags()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
if (length(mind.antag_datums) == 0)
@@ -137,6 +137,6 @@
///Removes the mob reference from the list of living antag player-mobs.
/mob/proc/remove_from_current_living_antags()
- if(!SSticker?.mode)
+ if(!SSticker.HasRoundStarted())
return
GLOB.current_living_antags -= src
diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm
index 03f69c2664c..83fb1f0ea7f 100644
--- a/code/modules/station_goals/dna_vault.dm
+++ b/code/modules/station_goals/dna_vault.dm
@@ -101,12 +101,11 @@
F.parent = src
fillers += F
- if(SSticker.mode)
- var/datum/station_goal/dna_vault/dna_vault_goal = locate() in GLOB.station_goals
- if (!isnull(dna_vault_goal))
- animals_max = dna_vault_goal.animal_count
- plants_max = dna_vault_goal.plant_count
- dna_max = dna_vault_goal.human_count
+ var/datum/station_goal/dna_vault/dna_vault_goal = locate() in GLOB.station_goals
+ if (!isnull(dna_vault_goal))
+ animals_max = dna_vault_goal.animal_count
+ plants_max = dna_vault_goal.plant_count
+ dna_max = dna_vault_goal.human_count
return ..()
diff --git a/code/modules/station_goals/generate_goals.dm b/code/modules/station_goals/generate_goals.dm
new file mode 100644
index 00000000000..102212c1212
--- /dev/null
+++ b/code/modules/station_goals/generate_goals.dm
@@ -0,0 +1,13 @@
+/// Creates the initial station goals.
+/proc/generate_station_goals(goal_budget)
+ var/list/possible = subtypesof(/datum/station_goal)
+ // Remove all goals that require space if space is not present
+ if(SSmapping.is_planetary())
+ for(var/datum/station_goal/goal as anything in possible)
+ if(initial(goal.requires_space))
+ possible -= goal
+ var/goal_weights = 0
+ while(possible.len && goal_weights < goal_budget)
+ var/datum/station_goal/picked = pick_n_take(possible)
+ goal_weights += initial(picked.weight)
+ GLOB.station_goals += new picked
diff --git a/tgstation.dme b/tgstation.dme
index 18de2acdc51..50cd7ad5fe6 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -687,6 +687,16 @@
#include "code\controllers\subsystem\wardrobe.dm"
#include "code\controllers\subsystem\weather.dm"
#include "code\controllers\subsystem\wiremod_composite.dm"
+#include "code\controllers\subsystem\dynamic\dynamic.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_hijacking.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_logging.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_midround_rolling.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_rulesets.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_rulesets_latejoin.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_rulesets_midround.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_rulesets_roundstart.dm"
+#include "code\controllers\subsystem\dynamic\dynamic_unfavorable_situation.dm"
+#include "code\controllers\subsystem\dynamic\ruleset_picking.dm"
#include "code\controllers\subsystem\movement\ai_movement.dm"
#include "code\controllers\subsystem\movement\cliff_falling.dm"
#include "code\controllers\subsystem\movement\hyperspace_drift.dm"
@@ -1870,20 +1880,8 @@
#include "code\game\atom\atom_vv.dm"
#include "code\game\atom\atoms_initializing_EXPENSIVE.dm"
#include "code\game\gamemodes\events.dm"
-#include "code\game\gamemodes\game_mode.dm"
#include "code\game\gamemodes\objective.dm"
#include "code\game\gamemodes\objective_items.dm"
-#include "code\game\gamemodes\dynamic\dynamic.dm"
-#include "code\game\gamemodes\dynamic\dynamic_hijacking.dm"
-#include "code\game\gamemodes\dynamic\dynamic_logging.dm"
-#include "code\game\gamemodes\dynamic\dynamic_midround_rolling.dm"
-#include "code\game\gamemodes\dynamic\dynamic_rulesets.dm"
-#include "code\game\gamemodes\dynamic\dynamic_rulesets_latejoin.dm"
-#include "code\game\gamemodes\dynamic\dynamic_rulesets_midround.dm"
-#include "code\game\gamemodes\dynamic\dynamic_rulesets_roundstart.dm"
-#include "code\game\gamemodes\dynamic\dynamic_simulations.dm"
-#include "code\game\gamemodes\dynamic\dynamic_unfavorable_situation.dm"
-#include "code\game\gamemodes\dynamic\ruleset_picking.dm"
#include "code\game\machinery\_machinery.dm"
#include "code\game\machinery\ai_slipper.dm"
#include "code\game\machinery\airlock_control.dm"
@@ -5585,6 +5583,7 @@
#include "code\modules\spells\spell_types\touch\smite.dm"
#include "code\modules\station_goals\bsa.dm"
#include "code\modules\station_goals\dna_vault.dm"
+#include "code\modules\station_goals\generate_goals.dm"
#include "code\modules\station_goals\meteor_shield.dm"
#include "code\modules\station_goals\station_goal.dm"
#include "code\modules\station_goals\vault_mutation.dm"