diff --git a/code/__DEFINES/dynamic.dm b/code/__DEFINES/dynamic.dm
index ae5b031c8b..da8a76e71e 100644
--- a/code/__DEFINES/dynamic.dm
+++ b/code/__DEFINES/dynamic.dm
@@ -7,6 +7,7 @@
#define WAROPS_ALWAYS_ALLOWED (1<<1)
#define USE_PREF_WEIGHTS (1<<2)
#define FORCE_IF_WON (1<<3)
+#define USE_PREV_ROUND_WEIGHTS (1<<4)
#define ONLY_RULESET (1<<0)
#define HIGHLANDER_RULESET (1<<1)
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 0a4af736dd..e416396991 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -320,8 +320,10 @@
parts += "[FOURSPACES]Nobody died this shift!"
if(istype(SSticker.mode, /datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
+ mode.update_playercounts()
parts += "[FOURSPACES]Final threat level: [mode.threat_level]"
parts += "[FOURSPACES]Final threat: [mode.threat]"
+ parts += "[FOURSPACES]Average threat: [mode.threat_average]"
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"
diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm
index de2ff17a97..5b350a7917 100644
--- a/code/controllers/subsystem/persistence.dm
+++ b/code/controllers/subsystem/persistence.dm
@@ -14,6 +14,7 @@ SUBSYSTEM_DEF(persistence)
var/list/saved_modes = list(1,2,3)
var/list/saved_dynamic_rules = list(list(),list(),list())
var/list/saved_storytellers = list("foo","bar","baz")
+ var/list/average_dynamic_threat = 50
var/list/saved_maps
var/list/saved_trophies = list()
var/list/spawned_objects = list()
@@ -191,6 +192,7 @@ SUBSYSTEM_DEF(persistence)
if(!json)
return
saved_storytellers = json["data"]
+ average_dynamic_threat = saved_storytellers[4]
saved_storytellers.len = 3
/datum/controller/subsystem/persistence/proc/LoadRecentMaps()
@@ -434,9 +436,10 @@ SUBSYSTEM_DEF(persistence)
saved_storytellers[3] = saved_storytellers[2]
saved_storytellers[2] = saved_storytellers[1]
saved_storytellers[1] = mode.storyteller.name
+ average_dynamic_threat = (mode.threat_average + average_dynamic_threat) / 2
var/json_file = file("data/RecentStorytellers.json")
var/list/file_data = list()
- file_data["data"] = saved_storytellers
+ file_data["data"] = saved_storytellers + average_dynamic_threat
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index b8a5e72d55..c9a776cbc0 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -54,12 +54,18 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
// Current storyteller
var/datum/dynamic_storyteller/storyteller = null
// Threat logging vars
+ /// Starting threat level, for things that increase it but can bring it back down.
+ var/initial_threat_level = 0
/// Target threat level right now. Events and antags will try to keep the round at this level.
var/threat_level = 0
/// The current antag threat. Recalculated every time a ruletype starts or ends.
var/threat = 0
- /// Starting threat level, for things that increase it but can bring it back down.
- var/initial_threat_level = 0
+ /// Threat average over the course of the round, for endgame logs.
+ var/threat_average = 0
+ /// Number of times threat average has been calculated, for calculating above.
+ var/threat_average_weight = 0
+ /// Last time a threat average sample was taken. Used for weighting the rolling average.
+ var/last_threat_sample_time = 0
/// Things that cause a rolling threat adjustment to be displayed at roundend.
var/list/threat_tallies = list()
/// Running information about the threat. Can store text or datum entries.
@@ -715,6 +721,15 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
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() + added_threat
+ if(threat_average_weight)
+ var/cur_sample_weight = world.time - last_threat_sample_time
+ threat_average = ((threat_average * threat_average_weight) + threat) / (threat_average_weight + cur_sample_weight)
+ threat_average_weight += cur_sample_weight
+ else
+ threat_average = threat
+ threat_average_weight++
+ last_threat_sample_time = world.time
+
/// Removes type from the list
/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
for(var/I in type_list)
diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
index 35d5a04488..98e38731b6 100644
--- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm
+++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
@@ -11,6 +11,7 @@
WAROPS_ALWAYS_ALLOWED: Can always do warops, regardless of threat level.
USE_PREF_WEIGHTS: Will use peoples' preferences to change the threat centre.
FORCE_IF_WON: If this mode won the vote, forces it
+ USE_PREV_ROUND_WEIGHTS: Changes its threat centre based on the average chaos of previous rounds.
*/
var/flags = 0
var/dead_player_weight = 1 // How much dead players matter for threat calculation
@@ -92,6 +93,8 @@ Property weights are:
mean += 5
if(voters)
GLOB.dynamic_curve_centre += (mean/voters)
+ if(flags & USE_PREV_ROUND_WEIGHTS)
+ GLOB.dynamic_curve_centre += (50 - SSpersistence.average_dynamic_threat) / 10
GLOB.dynamic_forced_threat_level = forced_threat_level
/datum/dynamic_storyteller/proc/get_midround_cooldown()
@@ -268,9 +271,6 @@ Property weights are:
/datum/dynamic_storyteller/random/get_injection_chance()
return 50 // i would do rand(0,100) but it's actually the same thing when you do the math
-/datum/dynamic_storyteller/random/calculate_threat()
- return 0 // what IS threat
-
/datum/dynamic_storyteller/random/roundstart_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules)
@@ -324,6 +324,7 @@ Property weights are:
desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies. Has a buildup-climax-falling action threat curve."
weight = 2
curve_width = 2
+ flags = USE_PREV_ROUND_WEIGHTS
property_weights = list("story_potential" = 2)
@@ -336,7 +337,7 @@ Property weights are:
name = "Classic"
config_tag = "classic"
desc = "No special antagonist weights. Good variety, but not like random. Uses your chaos preference to weight."
- flags = USE_PREF_WEIGHTS
+ flags = USE_PREF_WEIGHTS | USE_PREV_ROUND_WEIGHTS
/datum/dynamic_storyteller/suspicion
name = "Intrigue"
@@ -345,6 +346,7 @@ Property weights are:
weight = 2
curve_width = 2
dead_player_weight = 2
+ flags = USE_PREV_ROUND_WEIGHTS
property_weights = list("trust" = -3)
/datum/dynamic_storyteller/liteextended