diff --git a/code/controllers/configuration/config_entry.dm b/code/controllers/configuration/config_entry.dm
index 3ac103affc..4406655375 100644
--- a/code/controllers/configuration/config_entry.dm
+++ b/code/controllers/configuration/config_entry.dm
@@ -1,6 +1,7 @@
#define VALUE_MODE_NUM 0
#define VALUE_MODE_TEXT 1
#define VALUE_MODE_FLAG 2
+#define VALUE_MODE_NUM_LIST 3
#define KEY_MODE_TEXT 0
#define KEY_MODE_TYPE 1
@@ -191,6 +192,18 @@
if(VALUE_MODE_TEXT)
new_value = key_value
continue_check_value = new_value
+ if(VALUE_MODE_NUM_LIST)
+ // this is all copy+pasted from number list up there, but it's super basic so I don't see it being changed soon
+ var/list/new_list = list()
+ var/list/values = splittext(key_value," ")
+ for(var/I in values)
+ var/temp = text2num(I)
+ if(isnull(temp))
+ log_admin("invalid number list entry in [key_name]: [I]")
+ continue_check_value = FALSE
+ new_list += temp
+ new_value = new_list
+ continue_check_value = new_list.len
if(continue_check_value && continue_check_key && ValidateListEntry(new_key, new_value))
config_entry_value[new_key] = new_value
return TRUE
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index cf0caebb61..fb974c85bc 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -367,3 +367,27 @@
/datum/config_entry/number/auto_transfer_delay
config_entry_value = 72000
min_val = 0
+
+/datum/config_entry/number/dynamic_high_pop_limit
+ config_entry_value = 55
+ min_val = 1
+
+/datum/config_entry/number/dynamic_pop_per_requirement
+ config_entry_value = 6
+ min_val = 1
+
+/datum/config_entry/keyed_list/dynamic_cost
+ key_mode = KEY_MODE_TEXT
+ value_mode = VALUE_MODE_NUM
+
+/datum/config_entry/keyed_list/dynamic_weight
+ key_mode = KEY_MODE_TEXT
+ value_mode = VALUE_MODE_NUM
+
+/datum/config_entry/keyed_list/dynamic_requirements
+ key_mode = KEY_MODE_TEXT
+ value_mode = VALUE_MODE_NUM_LIST
+
+/datum/config_entry/keyed_list/dynamic_high_population_requirement
+ key_mode = KEY_MODE_TEXT
+ value_mode = VALUE_MODE_NUM
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index e22b785670..24cfddd14f 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -107,6 +107,11 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
/// If a only ruleset has been executed.
var/only_ruleset_executed = FALSE
+/datum/game_mode/dynamic/New() // i have NO IDEA if this is the proper way to do this.
+ ..()
+ pop_per_requirement = CONFIG_GET(number/dynamic_pop_per_requirement)
+ GLOB.dynamic_high_pop_limit = CONFIG_GET(number/dynamic_high_pop_limit)
+
/datum/game_mode/dynamic/admin_panel()
var/list/dat = list("
Game Mode PanelGame Mode Panel
")
dat += "Dynamic Mode \[VV\]
"
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
index 66afcbfb92..011b6ec251 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
@@ -3,6 +3,8 @@
var/name = ""
/// For admin logging and round end screen, do not change this unless making a new rule type.
var/ruletype = ""
+ /// For config purposes, similar to config_tag for secret game modes.
+ var/config_tag = null
/// 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)
@@ -65,7 +67,18 @@
restricted_roles += protected_roles
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
restricted_roles += "Assistant"
-
+ var/weights = CONFIG_GET(keyed_list/dynamic_weight)
+ var/costs = CONFIG_GET(keyed_list/dynamic_cost)
+ var/requirementses = CONFIG_GET(keyed_list/dynamic_requirements) // can't damn well use requirements
+ var/high_population_requirements = CONFIG_GET(keyed_list/dynamic_high_population_requirement)
+ if(config_tag in weights)
+ weight = weights[config_tag]
+ if(config_tag in costs)
+ cost = costs[config_tag]
+ if(config_tag in requirementses)
+ requirements = requirementses[config_tag]
+ if(config_tag in high_population_requirements)
+ high_population_requirement = high_population_requirements[config_tag]
if (istype(SSticker.mode, /datum/game_mode/dynamic))
mode = SSticker.mode
else if (GLOB.master_mode != "dynamic") // This is here to make roundstart forced ruleset function.
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
index 24b4c67357..fde020a3ae 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
@@ -52,6 +52,7 @@
/datum/dynamic_ruleset/latejoin/infiltrator
name = "Syndicate Infiltrator"
+ config_tag = "latejoin_traitor"
antag_datum = /datum/antagonist/traitor
antag_flag = ROLE_TRAITOR
restricted_roles = list("AI", "Cyborg")
@@ -72,6 +73,7 @@
/datum/dynamic_ruleset/latejoin/provocateur
name = "Provocateur"
+ config_tag = "latejoin_revolution"
antag_datum = /datum/antagonist/rev/head
antag_flag = ROLE_REV_HEAD
antag_flag_override = ROLE_REV
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 4da085d5a9..56b6e57c4d 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -169,6 +169,7 @@
/datum/dynamic_ruleset/midround/autotraitor
name = "Syndicate Sleeper Agent"
+ config_tag = "midround_traitor"
antag_datum = /datum/antagonist/traitor
antag_flag = ROLE_TRAITOR
restricted_roles = list("AI", "Cyborg", "Positronic Brain")
@@ -224,6 +225,7 @@
/datum/dynamic_ruleset/midround/malf
name = "Malfunctioning AI"
+ config_tag = "malf_ai"
antag_datum = /datum/antagonist/traitor
antag_flag = ROLE_MALF
enemy_roles = list("Security Officer", "Warden","Detective","Head of Security", "Captain", "Scientist", "Chemist", "Research Director", "Chief Engineer")
@@ -276,6 +278,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/wizard
name = "Wizard"
+ config_tag = "midround_wizard"
antag_datum = /datum/antagonist/wizard
antag_flag = ROLE_WIZARD
enemy_roles = list("Security Officer","Detective","Head of Security", "Captain")
@@ -308,6 +311,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/nuclear
name = "Nuclear Assault"
+ config_tag = "midround_nuclear"
antag_flag = ROLE_OPERATIVE
antag_datum = /datum/antagonist/nukeop
enemy_roles = list("AI", "Cyborg", "Security Officer", "Warden","Detective","Head of Security", "Captain")
@@ -351,6 +355,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/blob
name = "Blob"
+ config_tag = "blob"
antag_datum = /datum/antagonist/blob
antag_flag = ROLE_BLOB
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
@@ -374,6 +379,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph
name = "Alien Infestation"
+ config_tag = "xenos"
antag_datum = /datum/antagonist/xeno
antag_flag = ROLE_ALIEN
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
@@ -420,6 +426,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/nightmare
name = "Nightmare"
+ config_tag = "nightmare"
antag_datum = /datum/antagonist/nightmare
antag_flag = "Nightmare"
antag_flag_override = ROLE_ALIEN
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
index 38ce6f68d0..6a9aefa996 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
@@ -7,6 +7,7 @@
/datum/dynamic_ruleset/roundstart/traitor
name = "Traitors"
+ config_tag = "traitor"
persistent = TRUE
antag_flag = ROLE_TRAITOR
antag_datum = /datum/antagonist/traitor/
@@ -48,6 +49,7 @@
/datum/dynamic_ruleset/roundstart/traitorbro
name = "Blood Brothers"
+ config_tag = "traitorbro"
antag_flag = ROLE_BROTHER
antag_datum = /datum/antagonist/brother/
restricted_roles = list("AI", "Cyborg")
@@ -100,6 +102,7 @@
/datum/dynamic_ruleset/roundstart/changeling
name = "Changelings"
+ config_tag = "changeling"
antag_flag = ROLE_CHANGELING
antag_datum = /datum/antagonist/changeling
restricted_roles = list("AI", "Cyborg")
@@ -149,6 +152,7 @@
// Dynamic is a wonderful thing that adds wizards to every round and then adds even more wizards during the round.
/datum/dynamic_ruleset/roundstart/wizard
name = "Wizard"
+ config_tag = "wizard"
antag_flag = ROLE_WIZARD
antag_datum = /datum/antagonist/wizard
minimum_required_age = 14
@@ -194,6 +198,7 @@
/datum/dynamic_ruleset/roundstart/bloodcult
name = "Blood Cult"
+ config_tag = "cult"
antag_flag = ROLE_CULTIST
antag_datum = /datum/antagonist/cult
minimum_required_age = 14
@@ -254,6 +259,7 @@
/datum/dynamic_ruleset/roundstart/nuclear
name = "Nuclear Emergency"
+ config_tag = "nuclear"
antag_flag = ROLE_OPERATIVE
antag_datum = /datum/antagonist/nukeop
var/datum/antagonist/antag_leader_datum = /datum/antagonist/nukeop/leader
@@ -343,6 +349,7 @@
/datum/dynamic_ruleset/roundstart/delayed/revs
name = "Revolution"
+ config_tag = "revolution"
persistent = TRUE
antag_flag = ROLE_REV_HEAD
antag_flag_override = ROLE_REV
@@ -433,6 +440,7 @@
/datum/dynamic_ruleset/roundstart/extended
name = "Extended"
+ config_tag = "extended"
antag_flag = null
antag_datum = null
restricted_roles = list()
@@ -456,6 +464,7 @@
/datum/dynamic_ruleset/roundstart/clockcult
name = "Clockcult"
+ config_tag = "clockwork_cult"
antag_flag = ROLE_SERVANT_OF_RATVAR
antag_datum = /datum/antagonist/clockcult
restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
@@ -559,6 +568,7 @@
/datum/dynamic_ruleset/roundstart/nuclear/clown_ops
name = "Clown Ops"
+ config_tag = "clownops"
antag_datum = /datum/antagonist/nukeop/clownop
antag_leader_datum = /datum/antagonist/nukeop/leader/clownop
requirements = list(101,101,101,101,101,101,101,101,101,101)
@@ -584,6 +594,7 @@
/datum/dynamic_ruleset/roundstart/devil
name = "Devil"
+ config_tag = "devil"
antag_flag = ROLE_DEVIL
antag_datum = /datum/antagonist/devil
restricted_roles = list("Lawyer", "Curator", "Chaplain", "Head of Security", "Captain", "AI")
@@ -642,6 +653,7 @@
/datum/dynamic_ruleset/roundstart/monkey
name = "Monkey"
+ config_tag = "monkey"
antag_flag = ROLE_MONKEY
antag_datum = /datum/antagonist/monkey/leader
restricted_roles = list("Cyborg", "AI")
@@ -704,6 +716,7 @@
/datum/dynamic_ruleset/roundstart/meteor
name = "Meteor"
+ config_tag = "meteor"
persistent = TRUE
required_candidates = 0
weight = 3
diff --git a/config/game_options.txt b/config/game_options.txt
index 02d620eb31..ca58e58a5b 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -255,6 +255,141 @@ EVENTS_MIN_TIME_MUL 1
EVENTS_MIN_PLAYERS_MUL 1
+
+### DYNAMIC MODE ###
+
+## How many roundstart players required for high population override to take effect.
+DYNAMIC_HIGH_POP_LIMIT 55
+
+## Pop range per requirement.
+## If the value is five the range is:
+## 0-4, 5-9, 10-14, 15-19, 20-24, 25-29, 30-34, 35-39, 40-54, 45+
+## If it is six the range is:
+## 0-5, 6-11, 12-17, 18-23, 24-29, 30-35, 36-41, 42-47, 48-53, 54+
+## If it is seven the range is:
+## 0-6, 7-13, 14-20, 21-27, 28-34, 35-41, 42-48, 49-55, 56-62, 63+
+## Options outside this range can be used, of course.
+DYNAMIC_POP_PER_REQUIREMENT 6
+
+## 1 -> 9, probability for this rule to be picked against other rules.
+## Note that requirements must also be met, and some requirements are impossible to meet.
+DYNAMIC_WEIGHT TRAITOR 5
+DYNAMIC_WEIGHT TRAITORBRO 4
+DYNAMIC_WEIGHT CHANGELING 3
+DYNAMIC_WEIGHT WIZARD 1
+DYNAMIC_WEIGHT CULT 3
+DYNAMIC_WEIGHT NUCLEAR 3
+DYNAMIC_WEIGHT REVOLUTION 2
+# All below are impossible-by-default
+DYNAMIC_WEIGHT EXTENDED 3
+DYNAMIC_WEIGHT CLOCKWORK_CULT 3
+DYNAMIC_WEIGHT CLOWNOPS 3
+DYNAMIC_WEIGHT DEVIL 3
+DYNAMIC_WEIGHT MONKEY 3
+DYNAMIC_WEIGHT METEOR 3
+
+## Midround antags
+DYNAMIC_WEIGHT MIDROUND_TRAITOR 7
+DYNAMIC_WEIGHT MALF_AI 3
+DYNAMIC_WEIGHT MIDROUND_WIZARD 1
+DYNAMIC_WEIGHT MIDROUND_NUCLEAR 5
+DYNAMIC_WEIGHT BLOB 4
+DYNAMIC_WEIGHT XENOS 3
+DYNAMIC_WEIGHT NIGHTMARE 3
+
+## Latejoin antags
+DYNAMIC_WEIGHT LATEJOIN_TRAITOR 7
+DYNAMIC_WEIGHT LATEJOIN_REVOLUTION 2
+
+## Threat cost. This is decreased from the mode's threat when the rule is executed.
+DYNAMIC_COST TRAITOR 10
+DYNAMIC_COST TRAITORBRO 10
+DYNAMIC_COST CHANGELING 30
+DYNAMIC_COST WIZARD 30
+DYNAMIC_COST CULT 30
+DYNAMIC_COST NUCLEAR 40
+DYNAMIC_COST REVOLUTION 35
+# All below are impossible-by-default
+DYNAMIC_COST EXTENDED 0
+DYNAMIC_COST CLOCKWORK_CULT 0
+DYNAMIC_COST CLOWNOPS 40
+DYNAMIC_COST DEVIL 0
+DYNAMIC_COST MONKEY 0
+DYNAMIC_COST METEOR 0
+
+## Midround antags
+DYNAMIC_COST MIDROUND_TRAITOR 10
+DYNAMIC_COST MALF_AI 35
+DYNAMIC_COST MIDROUND_WIZARD 20
+DYNAMIC_COST MIDROUND_NUCLEAR 35
+DYNAMIC_COST BLOB 10
+DYNAMIC_COST XENOS 10
+DYNAMIC_COST NIGHTMARE 10
+
+## Latejoin antags
+DYNAMIC_COST LATEJOIN_TRAITOR 5
+DYNAMIC_COST LATEJOIN_REVOLUTION 20
+
+## Rule will not be generated with threat levels below requirement at a pop value. Pop values are determined by dynamic's pop-per-requirement.
+## By default it's 0-5, 6-11, 12-17, 18-23, 24-29, 30-35, 36-41, 42-47, 48-53, 54+.
+## This means that 40 30 30 20 20 20 15 15 15 10 will not generate below 40 at 0-5, 30 at 6-11 etc.
+DYNAMIC_REQUIREMENTS TRAITOR 10 10 10 10 10 10 10 10 10 10
+DYNAMIC_REQUIREMENTS TRAITORBRO 40 30 30 20 20 15 15 15 10 10
+DYNAMIC_REQUIREMENTS CHANGELING 80 70 60 50 40 20 20 10 10 10
+DYNAMIC_REQUIREMENTS WIZARD 90 90 70 40 30 20 10 10 10 10
+DYNAMIC_REQUIREMENTS CULT 100 90 80 60 40 30 10 10 10 10
+DYNAMIC_REQUIREMENTS NUCLEAR 90 90 90 80 60 40 30 20 10 10
+DYNAMIC_REQUIREMENTS REVOLUTION 101 101 70 40 30 20 10 10 10 10
+# All below are impossible-by-default
+DYNAMIC_REQUIREMENTS EXTENDED 101 101 101 101 101 101 101 101 101 101
+DYNAMIC_REQUIREMENTS CLOCKWORK_CULT 101 101 101 101 101 101 101 101 101 101
+DYNAMIC_REQUIREMENTS CLOWNOPS 101 101 101 101 101 101 101 101 101 101
+DYNAMIC_REQUIREMENTS DEVIL 101 101 101 101 101 101 101 101 101 101
+DYNAMIC_REQUIREMENTS MONKEY 101 101 101 101 101 101 101 101 101 101
+DYNAMIC_REQUIREMENTS METEOR 101 101 101 101 101 101 101 101 101 101
+
+## Midround antags
+DYNAMIC_REQUIREMENTS MIDROUND_TRAITOR 50 40 30 20 10 10 10 10 10 10
+DYNAMIC_REQUIREMENTS MALF_AI 101 101 80 70 60 60 50 50 40 40
+DYNAMIC_REQUIREMENTS MIDROUND_WIZARD 90 90 70 40 30 20 10 10 10 10
+DYNAMIC_REQUIREMENTS MIDROUND_NUCLEAR 90 90 90 80 60 40 30 20 10 10
+DYNAMIC_REQUIREMENTS BLOB 101 101 101 80 60 50 30 20 10 10
+DYNAMIC_REQUIREMENTS XENOS 101 101 101 70 50 40 20 15 10 10
+DYNAMIC_REQUIREMENTS NIGHTMARE 101 101 101 70 50 40 20 15 10 10
+
+## Latejoin antags
+DYNAMIC_REQUIREMENTS LATEJOIN_TRAITOR 40 30 20 10 10 10 10 10 10 10
+DYNAMIC_REQUIREMENTS LATEJOIN_REVOLUTION 101 101 70 40 30 20 20 20 20 20
+
+## An alternative, static requirement used instead when pop is over mode's high_pop_limit.
+DYNAMIC_HIGH_POPULATION_REQUIREMENT TRAITOR 10
+DYNAMIC_HIGH_POPULATION_REQUIREMENT TRAITORBRO 15
+DYNAMIC_HIGH_POPULATION_REQUIREMENT CHANGELING 10
+DYNAMIC_HIGH_POPULATION_REQUIREMENT WIZARD 10
+DYNAMIC_HIGH_POPULATION_REQUIREMENT CULT 10
+DYNAMIC_HIGH_POPULATION_REQUIREMENT NUCLEAR 10
+DYNAMIC_HIGH_POPULATION_REQUIREMENT REVOLUTION 10
+# All below are impossible-by-default
+DYNAMIC_HIGH_POPULATION_REQUIREMENT EXTENDED 101
+DYNAMIC_HIGH_POPULATION_REQUIREMENT CLOCKWORK_CULT 101
+DYNAMIC_HIGH_POPULATION_REQUIREMENT CLOWNOPS 101
+DYNAMIC_HIGH_POPULATION_REQUIREMENT DEVIL 101
+DYNAMIC_HIGH_POPULATION_REQUIREMENT MONKEY 101
+DYNAMIC_HIGH_POPULATION_REQUIREMENT METEOR 101
+
+## Midround antags
+DYNAMIC_HIGH_POPULATION_REQUIREMENT MIDROUND_TRAITOR 10
+DYNAMIC_HIGH_POPULATION_REQUIREMENT MALF_AI 35
+DYNAMIC_HIGH_POPULATION_REQUIREMENT MIDROUND_WIZARD 50
+DYNAMIC_HIGH_POPULATION_REQUIREMENT MIDROUND_NUCLEAR 10
+DYNAMIC_HIGH_POPULATION_REQUIREMENT BLOB 50
+DYNAMIC_HIGH_POPULATION_REQUIREMENT XENOS 50
+DYNAMIC_HIGH_POPULATION_REQUIREMENT NIGHTMARE 50
+
+## Latejoin antags
+DYNAMIC_HIGH_POPULATION_REQUIREMENT LATEJOIN_TRAITOR 10
+DYNAMIC_HIGH_POPULATION_REQUIREMENT LATEJOIN_REVOLUTION 50
+
## AI ###
## Allow the AI job to be picked.