diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm
index f1db37d9fa..8f34901d46 100644
--- a/code/controllers/configuration/configuration.dm
+++ b/code/controllers/configuration/configuration.dm
@@ -368,12 +368,16 @@
var/list/datum/dynamic_storyteller/runnable_storytellers = new
var/list/probabilities = Get(/datum/config_entry/keyed_list/storyteller_weight)
var/list/repeated_mode_adjust = Get(/datum/config_entry/number_list/repeated_mode_adjust)
+ var/list/min_player_counts = Get(/datum/config_entry/keyed_list/storyteller_min_players)
for(var/T in storyteller_cache)
var/datum/dynamic_storyteller/S = T
var/config_tag = initial(S.config_tag)
var/probability = (config_tag in probabilities) ? probabilities[config_tag] : initial(S.weight)
+ var/min_players = (config_tag in min_player_counts) ? min_player_counts[config_tag] : initial(S.min_players)
if(probability <= 0)
continue
+ if(length(GLOB.player_list) < min_players)
+ continue
if(SSpersistence.saved_storytellers.len == repeated_mode_adjust.len)
var/name = initial(S.name)
var/recent_round = min(SSpersistence.saved_storytellers.Find(name),3)
diff --git a/code/controllers/configuration/entries/dynamic.dm b/code/controllers/configuration/entries/dynamic.dm
index 4c03de4678..44d9a041a6 100644
--- a/code/controllers/configuration/entries/dynamic.dm
+++ b/code/controllers/configuration/entries/dynamic.dm
@@ -92,3 +92,7 @@
/datum/config_entry/keyed_list/storyteller_weight
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_NUM
+
+/datum/config_entry/keyed_list/storyteller_min_players
+ 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 3143ef2ee5..de57bf0402 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -127,6 +127,8 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
var/only_ruleset_executed = FALSE
/// Antags rolled by rules so far, to keep track of and discourage scaling past a certain ratio of crew/antags especially on lowpop.
var/antags_rolled = 0
+ // Arbitrary threat addition, for fudging purposes.
+ var/added_threat = 50
/datum/game_mode/dynamic/New() // i have NO IDEA if this is the proper way to do this.
..()
@@ -172,9 +174,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
else
dat += "none.
"
dat += "
Injection Timers: ([storyteller.get_injection_chance(TRUE)]% chance)
"
- dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)] seconds"] \[Now!\]
"
- dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)] seconds"] \[Now!\]
"
- dat += "Event: [(event_injection_cooldown-world.time)>60*10 ? "[round((event_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(event_injection_cooldown-world.time)] seconds"] \[Now!\]
"
+ dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)/10] seconds"] \[Now!\]
"
+ dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)/10] seconds"] \[Now!\]
"
+ dat += "Event: [(event_injection_cooldown-world.time)>60*10 ? "[round((event_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(event_injection_cooldown-world.time)/10] seconds"] \[Now!\]
"
usr << browse(dat.Join(), "window=gamemode_panel;size=500x500")
/datum/game_mode/dynamic/Topic(href, href_list)
@@ -511,7 +513,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
starting_rule.trim_candidates()
starting_rule.scale_up(extra_rulesets_amount, threat)
if (starting_rule.pre_execute())
- log_threat("[starting_rule.ruletype] - [starting_rule.name] -[starting_rule.cost + starting_rule.scaled_times * starting_rule.scaling_cost] threat", verbose = TRUE)
+ log_threat("[starting_rule.ruletype] - [starting_rule.name] [starting_rule.cost + starting_rule.scaled_times * starting_rule.scaling_cost] threat", verbose = TRUE)
if(starting_rule.flags & HIGHLANDER_RULESET)
highlander_executed = TRUE
else if(starting_rule.flags & ONLY_RULESET)
@@ -605,7 +607,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
if ((forced || (new_rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && new_rule.cost <= threat)))
new_rule.trim_candidates()
if (new_rule.ready(forced))
- log_threat("[new_rule.ruletype] - [new_rule.name] -[new_rule.cost] threat", verbose = TRUE)
+ log_threat("[new_rule.ruletype] - [new_rule.name] [new_rule.cost] threat", verbose = TRUE)
if (new_rule.execute()) // This should never fail since ready() returned 1
if(new_rule.flags & HIGHLANDER_RULESET)
highlander_executed = TRUE
@@ -625,7 +627,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
var/datum/dynamic_ruleset/rule = sent_rule
if (rule.execute())
log_game("DYNAMIC: Injected a [rule.ruletype == "latejoin" ? "latejoin" : "midround"] ruleset [rule.name].")
- log_threat("[rule.ruletype] [rule.name] spent [rule.cost]", verbose = TRUE)
+ log_threat("[rule.ruletype] [rule.name] added [rule.cost]", verbose = TRUE)
if(rule.flags & HIGHLANDER_RULESET)
highlander_executed = TRUE
else if(rule.flags & ONLY_RULESET)
@@ -712,8 +714,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
current_players[CURRENT_OBSERVERS].Add(M)
continue
current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow)
- threat = storyteller.calculate_threat() + 50 // 50 is the centerpoint, so we want it to be around 50
-
+ threat = storyteller.calculate_threat() + added_threat
/// Removes type from the list
/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
for(var/I in type_list)
@@ -766,20 +767,20 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
/// Increase the threat level.
/datum/game_mode/dynamic/proc/create_threat(gain)
threat_level += gain
- SSblackbox.record_feedback("tally","dynamic_threat",gain,"Created threat")
+ SSblackbox.record_feedback("tally","dynamic_threat",gain,"Created threat level")
log_threat("[gain] created. Threat level is now [threat_level].", verbose = TRUE)
/// Decrease the threat level.
/datum/game_mode/dynamic/proc/remove_threat(loss)
threat_level -= loss
- SSblackbox.record_feedback("tally","dynamic_threat",loss,"Removed threat")
+ SSblackbox.record_feedback("tally","dynamic_threat",loss,"Removed threat level")
log_threat("[loss] removed. Threat level is now [threat_level].", verbose = TRUE)
/// Fill up more of the threat level.
/datum/game_mode/dynamic/proc/spend_threat(cost)
- threat += cost
- SSblackbox.record_feedback("tally","dynamic_threat",cost,"Threat spent")
- log_threat("[cost] spent. Threat is now [threat].", verbose = TRUE)
+ added_threat += cost
+ SSblackbox.record_feedback("tally","dynamic_threat",cost,"Threat added")
+ log_threat("[cost] added. Threat is now [threat].", verbose = TRUE)
/// Turns the value generated by lorentz distribution to threat value between 0 and 100.
/datum/game_mode/dynamic/proc/lorentz_to_threat(x)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index bc2aa288c2..b746100f27 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -415,7 +415,7 @@
required_candidates = 1
blocking_rules = list(/datum/dynamic_ruleset/roundstart/clockcult)
weight = 4
- cost = 10
+ cost = 20
requirements = list(101,101,101,80,60,50,50,50,50,50)
high_population_requirement = 50
repeatable = TRUE
diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
index 4d847a90ae..71eb7ec533 100644
--- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm
+++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
@@ -17,6 +17,7 @@
var/weight = 3 // Weights for randomly picking storyteller. Multiplied by score after voting.
var/event_frequency_lower = 6 MINUTES // How rare events will be, at least.
var/event_frequency_upper = 20 MINUTES // How rare events will be, at most.
+ var/min_players = -1 // How many players are required for this one to start.
var/datum/game_mode/dynamic/mode = null // Cached as soon as it's made, by dynamic.
/**
@@ -114,7 +115,7 @@ Property weights are:
/datum/dynamic_storyteller/proc/roundstart_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules)
- if (rule.acceptable(mode.roundstart_pop_ready, mode.threat_level) && mode.threat >= rule.cost) // If we got the population and threat required
+ if (rule.acceptable(mode.roundstart_pop_ready, mode.threat_level)) // If we got the population and threat required
rule.candidates = mode.candidates.Copy()
rule.trim_candidates()
if (rule.ready() && rule.candidates.len > 0)
@@ -195,11 +196,12 @@ Property weights are:
config_tag = "chaotic"
curve_centre = 10
desc = "High chaos modes. Revs, wizard, clock cult. Multiple antags at once. Chaos is kept up all round."
- property_weights = list("extended" = -1, "chaos" = 2)
+ property_weights = list("extended" = -1, "chaos" = 1)
weight = 1
event_frequency_lower = 2 MINUTES
event_frequency_upper = 10 MINUTES
flags = WAROPS_ALWAYS_ALLOWED | FORCE_IF_WON
+ min_players = 40
var/refund_cooldown = 0
/datum/dynamic_storyteller/chaotic/do_process()
@@ -221,6 +223,7 @@ Property weights are:
curve_centre = 2
curve_width = 1.5
weight = 2
+ min_players = 30
flags = WAROPS_ALWAYS_ALLOWED
property_weights = list("valid" = 3, "trust" = 5)
diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm
index b5ce5538e9..39dc4a1ed2 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm
@@ -74,12 +74,6 @@ GLOBAL_VAR_INIT(war_declared, FALSE)
new uplink_type(get_turf(user), user.key, CHALLENGE_TELECRYSTALS - tc_malus + CEILING(PLAYER_SCALING * actual_players, 1))
CONFIG_SET(number/shuttle_refuel_delay, max(CONFIG_GET(number/shuttle_refuel_delay), CHALLENGE_SHUTTLE_DELAY))
- if(istype(SSticker.mode, /datum/game_mode/dynamic))
- var/datum/game_mode/dynamic/mode = SSticker.mode
- if(!(mode.storyteller.flags & WAROPS_ALWAYS_ALLOWED))
- var/threat_spent = CONFIG_GET(number/dynamic_warops_cost)
- mode.spend_threat(threat_spent)
- mode.log_threat("Nuke ops spent [threat_spent] on war ops.")
SSblackbox.record_feedback("amount", "nuclear_challenge_mode", 1)
qdel(src)
diff --git a/code/modules/antagonists/traitor/classes/human.dm b/code/modules/antagonists/traitor/classes/human.dm
index 4e096390c0..30aa11e39d 100644
--- a/code/modules/antagonists/traitor/classes/human.dm
+++ b/code/modules/antagonists/traitor/classes/human.dm
@@ -34,7 +34,7 @@
if(is_dynamic)
var/threat_spent = CONFIG_GET(number/dynamic_assassinate_cost)
mode.spend_threat(threat_spent)
- mode.log_threat("[T.owner.name] spent [threat_spent] on an assassination target.")
+ mode.log_threat("[T.owner.name] added [threat_spent] on an assassination target.")
var/list/active_ais = active_ais()
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
var/datum/objective/destroy/destroy_objective = new
diff --git a/config/dynamic_config.txt b/config/dynamic_config.txt
index 25bc763d0b..c08b316e2e 100644
--- a/config/dynamic_config.txt
+++ b/config/dynamic_config.txt
@@ -1,13 +1,18 @@
## Dynamic storytellers weights: how likely each storyteller is to show up. This is adjusted by voting and recency.
STORYTELLER_WEIGHT CHAOTIC 2
STORYTELLER_WEIGHT TEAMWORK 4
-STORYTELLER_WEIGHT CONVERSION 2
-STORYTELLER_WEIGHT RANDOM 6
+STORYTELLER_WEIGHT CONVERSION 0
+STORYTELLER_WEIGHT RANDOM 2
+STORYTELLER_WEIGHT CLASSIC 6
STORYTELLER_WEIGHT INTRIGUE 6
STORYTELLER_WEIGHT STORY 6
STORYTELLER_WEIGHT CALM 6
STORYTELLER_WEIGHT EXTENDED 0
+## Dynamic storyteller minimum player coutns: how many players before this storyteller can be rolled.
+STORYTELLER_MIN_PLAYERS CHAOTIC 30
+STORYTELLER_MIN_PLAYERS TEAMWORK 30
+
## Injection delays: how long (in minutes) will pass before a midround or latejoin antag is injected.
DYNAMIC_MIDROUND_DELAY_MIN 5
DYNAMIC_MIDROUND_DELAY_MAX 15