diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 6a1e0b1d851..f3db9bb3159 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -378,3 +378,5 @@
/datum/config_entry/number/monkeycap
config_entry_value = 64
min_val = 0
+
+/datum/config_entry/flag/dynamic_config_enabled
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index cb7250f8109..ee23ba9464e 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -107,6 +107,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
var/highlander_executed = FALSE
/// If a only ruleset has been executed.
var/only_ruleset_executed = FALSE
+ /// Dynamic configuration, loaded on pre_setup
+ var/list/configuration = null
/datum/game_mode/dynamic/admin_panel()
var/list/dat = list("
Game Mode PanelGame Mode Panel
")
@@ -287,6 +289,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
return TRUE
/datum/game_mode/dynamic/pre_setup()
+ if(CONFIG_GET(flag/dynamic_config_enabled))
+ var/json_file = file("config/dynamic.json")
+ if(fexists(json_file))
+ configuration = json_decode(file2text(json_file))
for (var/rule in subtypesof(/datum/dynamic_ruleset))
var/datum/dynamic_ruleset/ruleset = new rule()
// Simple check if the ruleset should be added to the lists.
@@ -300,6 +306,17 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if ("Midround")
if (ruleset.weight)
midround_rules += ruleset
+ if(configuration)
+ if(!configuration[ruleset.ruletype])
+ continue
+ if(!configuration[ruleset.ruletype][ruleset.name])
+ continue
+ var/rule_conf = configuration[ruleset.ruletype][ruleset.name]
+ for(var/variable in rule_conf)
+ if(!ruleset.vars[variable])
+ stack_trace("Invalid dynamic configuration variable [variable] in [ruleset.ruletype] [ruleset.name]")
+ ruleset.vars[variable] = rule_conf[variable]
+
for(var/mob/dead/new_player/player in GLOB.player_list)
if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
roundstart_pop_ready++
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
index 2e0a25f09dc..fe4aa64304c 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
@@ -92,7 +92,7 @@
return (threat_level >= high_population_requirement)
else
pop_per_requirement = pop_per_requirement > 0 ? pop_per_requirement : mode.pop_per_requirement
- var/indice_pop = min(10,round(population/pop_per_requirement)+1)
+ var/indice_pop = min(requirements.len,round(population/pop_per_requirement)+1)
return (threat_level >= requirements[indice_pop])
/// This is called if persistent variable is true everytime SSTicker ticks.
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 9e4a82a1a8f..e2c4f5ca8cd 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -313,14 +313,14 @@
cost = 35
requirements = list(90,90,90,80,60,40,30,20,10,10)
high_population_requirement = 10
- var/operative_cap = list(2,2,3,3,4,5,5,5,5,5)
+ var/list/operative_cap = list(2,2,3,3,4,5,5,5,5,5)
var/datum/team/nuclear/nuke_team
flags = HIGHLANDER_RULESET
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/acceptable(population=0, threat=0)
if (locate(/datum/dynamic_ruleset/roundstart/nuclear) in mode.executed_rules)
return FALSE // Unavailable if nuke ops were already sent at roundstart
- var/indice_pop = min(10,round(living_players.len/5)+1)
+ var/indice_pop = min(operative_cap.len, round(living_players.len/5)+1)
required_candidates = operative_cap[indice_pop]
return ..()
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
index 56532ef5146..cd839214057 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
@@ -200,16 +200,16 @@
requirements = list(100,90,80,60,40,30,10,10,10,10)
high_population_requirement = 10
flags = HIGHLANDER_RULESET
- var/cultist_cap = list(2,2,2,3,3,4,4,4,4,4)
+ var/list/cultist_cap = list(2,2,2,3,3,4,4,4,4,4)
var/datum/team/cult/main_cult
/datum/dynamic_ruleset/roundstart/bloodcult/ready(forced = FALSE)
- var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1)
+ var/indice_pop = min(cultist_cap.len, round(mode.roundstart_pop_ready/pop_per_requirement)+1)
required_candidates = cultist_cap[indice_pop]
. = ..()
/datum/dynamic_ruleset/roundstart/bloodcult/pre_execute()
- var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1)
+ var/indice_pop = min(cultist_cap.len, round(mode.roundstart_pop_ready/pop_per_requirement)+1)
var/cultists = cultist_cap[indice_pop]
for(var/cultists_number = 1 to cultists)
if(candidates.len <= 0)
@@ -258,18 +258,18 @@
requirements = list(90,90,90,80,60,40,30,20,10,10)
high_population_requirement = 10
flags = HIGHLANDER_RULESET
- var/operative_cap = list(2,2,2,3,3,3,4,4,5,5)
+ var/list/operative_cap = list(2,2,2,3,3,3,4,4,5,5)
var/datum/team/nuclear/nuke_team
/datum/dynamic_ruleset/roundstart/nuclear/ready(forced = FALSE)
- var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1)
+ var/indice_pop = min(operative_cap.len,round(mode.roundstart_pop_ready/pop_per_requirement)+1)
required_candidates = operative_cap[indice_pop]
. = ..()
/datum/dynamic_ruleset/roundstart/nuclear/pre_execute()
// If ready() did its job, candidates should have 5 or more members in it
- var/indice_pop = min(10,round(mode.roundstart_pop_ready/5)+1)
+ var/indice_pop = min(operative_cap.len, round(mode.roundstart_pop_ready/5)+1)
var/operatives = operative_cap[indice_pop]
for(var/operatives_number = 1 to operatives)
if(candidates.len <= 0)
diff --git a/config/dynamic.json b/config/dynamic.json
new file mode 100644
index 00000000000..f396d16403b
--- /dev/null
+++ b/config/dynamic.json
@@ -0,0 +1,36 @@
+{
+ "Roundstart": {
+ "Traitors": {
+ "cost": 10,
+ "weight": 5,
+ "required_candidates": 1,
+ "high_population_requirement": 10,
+ "minimum_required_aged": 0,
+ "requirements": [
+ 10,
+ 10,
+ 10,
+ 10,
+ 10,
+ 10,
+ 10,
+ 10,
+ 10,
+ 10
+ ],
+ "protected_roles": [
+ "Security Officer",
+ "Warden",
+ "Detective",
+ "Head of Security",
+ "Captain"
+ ],
+ "restricted_roles": [
+ "Cyborg"
+ ],
+ "autotraitor_cooldown": 450
+ }
+ },
+ "Midround": {},
+ "Latejoin": {}
+}
diff --git a/config/game_options.txt b/config/game_options.txt
index 409f3599da6..82d6781708c 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -254,6 +254,9 @@ MAX_TICKETS_PER_ROLL 100
## Uncomment to allow players to see the set odds of different rounds in secret/random in the get server revision screen. This will NOT tell the current roundtype.
#SHOW_GAME_TYPE_ODDS
+## Uncomment to enable dynamic ruleset config file.
+DYNAMIC_CONFIG_ENABLED
+
## RANDOM EVENTS ###
## Comment this out to disable random events during the round.
ALLOW_RANDOM_EVENTS