From 8b8bcfd67411a65ae347862d3862b2dccd6ef26d Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:06:29 -0500 Subject: [PATCH] Adds two heavy midround rulesets, mass traitors and mass changelings (#93447) ## About The Pull Request - [ ] I tested this pr Adds "Midround Mass Traitors" and "Midround Mass Changelings" heavy rulesets Midround Mass Traitors will turn a number of the crew into sleeper agents Midround Mass Lings will spawn a number of changeling meteors BY DEFAULT: - Traitors will spawn 2-4 sleeper agents - Lings will spawn 2-3 changeling meteors - They won't run without >=2 candidates - They have no config blacklist (so the HoP can roll mass midround traitor, despite not being able to roll normal midround traitor. However default blacklisted roles like security officers are still blocked) - They're weighted impossible on tier 0, same weight as most midrounds on tier 1, and fairly high on tiers 2 and 3 - They're repeatable but very unlikely to repeat - Significantly higher min pop brackets than their solo counter part ## Why It's Good For The Game Adds more options for lategame antags than big midrounds ## Changelog :cl: Melbert add: Two new heavy dynamic rulesets: "Midround Mass Traitors" and "Midround Mass Changelings" /:cl: --- .../subsystem/dynamic/__dynamic_defines.dm | 3 ++ .../subsystem/dynamic/_dynamic_ruleset.dm | 7 ++-- .../dynamic/dynamic_ruleset_midround.dm | 34 ++++++++++++++++++- .../client/preferences/middleware/antags.dm | 7 ++-- .../unit_tests/dynamic_ruleset_sanity.dm | 3 +- config/dynamic.toml | 26 ++++++++++++++ 6 files changed, 73 insertions(+), 7 deletions(-) diff --git a/code/controllers/subsystem/dynamic/__dynamic_defines.dm b/code/controllers/subsystem/dynamic/__dynamic_defines.dm index f24c772fa24..500666dcd14 100644 --- a/code/controllers/subsystem/dynamic/__dynamic_defines.dm +++ b/code/controllers/subsystem/dynamic/__dynamic_defines.dm @@ -43,6 +43,9 @@ /// Ruleset can be configured by admins (implements /proc/configure_ruleset) /// Only implemented for midrounds currently #define RULESET_ADMIN_CONFIGURABLE (1<<2) +/// Applied to rulesets which intentionally share a pref_flag with another rulesets +/// Denotes multiple rulesets which abide by the same preference, but are each a variation of some base ruleset +#define RULESET_VARIATION (1<<3) /// Href for cancelling midround rulesets before execution #define MIDROUND_CANCEL_HREF(...) "(CANCEL)" diff --git a/code/controllers/subsystem/dynamic/_dynamic_ruleset.dm b/code/controllers/subsystem/dynamic/_dynamic_ruleset.dm index 707c0220ca6..e03f733eb7a 100644 --- a/code/controllers/subsystem/dynamic/_dynamic_ruleset.dm +++ b/code/controllers/subsystem/dynamic/_dynamic_ruleset.dm @@ -10,9 +10,12 @@ /// Don't change this unless you know what you're doing. var/config_tag - /// What flag to check for jobbans? Optional, if unset, uses pref_flag + /// What flag to check for jobbans? + /// Optional, if unset, uses pref_flag for jobbans instead var/jobban_flag - /// What flag to check for prefs? Required if the antag has an associated preference + /// What flag to check for prefs? + /// Required if the antag has an associated preference, and must be unique + /// (unless RULESET_VARIATION is set in ruleset_flags) var/pref_flag /// Flags for this ruleset var/ruleset_flags = NONE diff --git a/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm b/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm index b418f1ade42..6df942b5242 100644 --- a/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm +++ b/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm @@ -658,7 +658,7 @@ candidate_role = "Changeling" pref_flag = ROLE_CHANGELING_MIDROUND jobban_flag = ROLE_CHANGELING - ruleset_flags = RULESET_INVADER + ruleset_flags = RULESET_INVADER|RULESET_VARIATION weight = 5 min_pop = 15 max_antag_cap = 1 @@ -670,6 +670,21 @@ /datum/dynamic_ruleset/midround/from_ghosts/space_changeling/assign_role(datum/mind/candidate) generate_changeling_meteor(candidate) +/datum/dynamic_ruleset/midround/from_ghosts/space_changeling/mass + name = "Mass Space Changelings" + config_tag = "Mass Changelings" + midround_type = HEAVY_MIDROUND + min_pop = 25 + min_antag_cap = 2 + max_antag_cap = 3 + repeatable_weight_decrease = 4 + weight = list( + DYNAMIC_TIER_LOW = 0, + DYNAMIC_TIER_LOWMEDIUM = 3, + DYNAMIC_TIER_MEDIUMHIGH = 4, + DYNAMIC_TIER_HIGH = 5, + ) + /datum/dynamic_ruleset/midround/from_ghosts/paradox_clone name = "Paradox Clone" config_tag = "Paradox Clone" @@ -1064,6 +1079,7 @@ false_alarm_able = TRUE pref_flag = ROLE_SLEEPER_AGENT jobban_flag = ROLE_TRAITOR + ruleset_flags = RULESET_VARIATION weight = 10 min_pop = 3 blacklisted_roles = list( @@ -1079,6 +1095,22 @@ "[command_name()] High-Priority Update", ) +/datum/dynamic_ruleset/midround/from_living/traitor/mass + name = "Mass Traitors" + config_tag = "Mass Traitors" + midround_type = HEAVY_MIDROUND + min_pop = 15 + min_antag_cap = 2 + max_antag_cap = 4 + repeatable_weight_decrease = 8 + blacklisted_roles = list() + weight = list( + DYNAMIC_TIER_LOW = 0, + DYNAMIC_TIER_LOWMEDIUM = 3, + DYNAMIC_TIER_MEDIUMHIGH = 8, + DYNAMIC_TIER_HIGH = 10, + ) + /datum/dynamic_ruleset/midround/from_living/malf_ai name = "Malfunctioning AI" config_tag = "Midround Malfunctioning AI" diff --git a/code/modules/client/preferences/middleware/antags.dm b/code/modules/client/preferences/middleware/antags.dm index dd81ac44760..39948078b11 100644 --- a/code/modules/client/preferences/middleware/antags.dm +++ b/code/modules/client/preferences/middleware/antags.dm @@ -149,11 +149,12 @@ GLOBAL_LIST_INIT(non_ruleset_antagonists, list( for (var/datum/dynamic_ruleset/ruleset as anything in subtypesof(/datum/dynamic_ruleset)) var/datum/antagonist/antagonist_type = initial(ruleset.preview_antag_datum) var/antag_flag = initial(ruleset.pref_flag) - if (isnull(antagonist_type) || isnull(antag_flag)) + if(isnull(antagonist_type) || isnull(antag_flag)) continue - // antag_flag is guaranteed to be unique by unit tests. - antagonists[initial(ruleset.pref_flag)] = antagonist_type + // antag_flag is guaranteed to be unique for all non-RULESET_VARIATION rulesets + // the ||= covers specifically those rulesets - prefer the first one over variations + antagonists[initial(ruleset.pref_flag)] ||= antagonist_type var/list/generated_icons = list() diff --git a/code/modules/unit_tests/dynamic_ruleset_sanity.dm b/code/modules/unit_tests/dynamic_ruleset_sanity.dm index b42b34f99df..a639fb0f837 100644 --- a/code/modules/unit_tests/dynamic_ruleset_sanity.dm +++ b/code/modules/unit_tests/dynamic_ruleset_sanity.dm @@ -30,7 +30,8 @@ continue if (antag_flag in known_antag_flags) - TEST_FAIL("[ruleset] has a non-unique antag_flag [antag_flag] (used by [known_antag_flags[antag_flag]])!") + if(!(initial(ruleset.ruleset_flags) & RULESET_VARIATION)) + TEST_FAIL("[ruleset] has a non-unique antag_flag [antag_flag] (used by [known_antag_flags[antag_flag]])!") continue known_antag_flags[antag_flag] = ruleset diff --git a/config/dynamic.toml b/config/dynamic.toml index 3d8b6d29655..e5b7f92b2f9 100644 --- a/config/dynamic.toml +++ b/config/dynamic.toml @@ -354,6 +354,19 @@ repeatable_weight_decrease = 2 repeatable = 1 minimum_required_age = 0 +["Midround Mass Changelings"] +weight.1 = 0 +weight.2 = 3 +weight.3 = 4 +weight.4 = 5 +min_pop = 25 +blacklisted_roles = [] +repeatable_weight_decrease = 4 +repeatable = 1 +minimum_required_age = 0 +min_antag_cap = 2 +max_antag_cap = 3 + ["Paradox Clone"] weight = 5 min_pop = 10 @@ -414,6 +427,19 @@ repeatable_weight_decrease = 2 repeatable = 1 minimum_required_age = 0 +["Midround Mass Traitors"] +weight.1 = 0 +weight.2 = 3 +weight.3 = 8 +weight.4 = 10 +min_pop = 15 +blacklisted_roles = [] +repeatable_weight_decrease = 7 +repeatable = 1 +minimum_required_age = 0 +min_antag_cap = 2 +max_antag_cap = 4 + ["Midround Malfunctioning AI"] weight.1 = 0 weight.2 = 1