diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 8a26b1f44b..0a4af736dd 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -320,8 +320,8 @@
parts += "[FOURSPACES]Nobody died this shift!"
if(istype(SSticker.mode, /datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
- parts += "[FOURSPACES]Threat level: [mode.threat_level]"
- parts += "[FOURSPACES]Threat left: [mode.threat]"
+ parts += "[FOURSPACES]Final threat level: [mode.threat_level]"
+ parts += "[FOURSPACES]Final threat: [mode.threat]"
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"
@@ -331,7 +331,7 @@
for(var/entry in mode.threat_tallies)
parts += "[FOURSPACES][FOURSPACES][entry] added [mode.threat_tallies[entry]]"
SSblackbox.record_feedback("tally","dynamic_threat",mode.threat_level,"Final threat level")
- SSblackbox.record_feedback("tally","dynamic_threat",mode.threat,"Threat left")
+ SSblackbox.record_feedback("tally","dynamic_threat",mode.threat,"Final Threat")
return parts.Join("
")
/client/proc/roundend_report_file()
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 8b08366c50..f253bc543d 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -400,6 +400,10 @@
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_NUM
+/datum/config_entry/keyed_list/antag_threat
+ key_mode = KEY_MODE_TEXT
+ value_mode = VALUE_MODE_NUM
+
/datum/config_entry/number/monkeycap
config_entry_value = 64
min_val = 0
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 58dfd6d814..d1e8a87997 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -152,9 +152,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
/datum/game_mode/dynamic/admin_panel()
var/list/dat = list("
Game Mode PanelGame Mode Panel
")
dat += "Dynamic Mode \[VV\]\[Refresh\]
"
- dat += "Threat Level: [threat_level]
"
+ dat += "Target threat: [threat_level]
"
- dat += "Threat to Spend: [threat] \[Adjust\] \[View Log\]
"
+ dat += "Current threat: [threat] \[Adjust\] \[View Log\]
"
dat += "
"
dat += "Storyteller: [storyteller.name]
"
dat += "Parameters: centre = [GLOB.dynamic_curve_centre] ; width = [GLOB.dynamic_curve_width].
"
@@ -339,9 +339,10 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
storyteller = new GLOB.dynamic_storyteller_type // this is where all the initialization happens
storyteller.on_start()
SSblackbox.record_feedback("text","dynamic_storyteller",1,storyteller.name)
- message_admins("Dynamic mode parameters for the round:")
- message_admins("Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].")
- message_admins("Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].")
+ message_admins("Dynamic mode parameters for the round:\n\
+ Storyteller is [storyteller.name].\n\
+ Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].\n\
+ Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].")
log_game("DYNAMIC: Dynamic mode parameters for the round:")
log_game("DYNAMIC: Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].")
log_game("DYNAMIC: Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].")
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 15c531b15d..bc2aa288c2 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -106,7 +106,7 @@
if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles)))
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
- var/threat = round(mode.threat_level/10)
+ var/threat = CLAMP(round(mode.threat_level/10),1,10)
if (job_check < required_enemies[threat])
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles")
return FALSE
@@ -216,7 +216,7 @@
var/player_count = mode.current_players[CURRENT_LIVING_PLAYERS].len
var/antag_count = mode.current_players[CURRENT_LIVING_ANTAGS].len
var/max_traitors = round(player_count / 10) + 1
- if ((antag_count < max_traitors) && prob(mode.threat_level))//adding traitors if the antag population is getting low
+ if ((antag_count < max_traitors) && prob(min(100,mode.threat_level)))//adding traitors if the antag population is getting low
return ..()
else
return FALSE
diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
index 63fd12f47a..4d847a90ae 100644
--- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm
+++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
@@ -53,11 +53,13 @@ Property weights are:
if(H.stat != DEAD && is_station_level(T.z) && !("Station" in H.faction))
threat += H.threat()
for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS])
- if (M.stat != DEAD && M.mind && M.mind.assigned_role)
- if(length(M.mind.antag_datums))
- threat += SSjob.GetJob(M.mind.assigned_role).GetThreat()
- else
- threat -= SSjob.GetJob(M.mind.assigned_role).GetThreat()
+ if (M?.mind?.assigned_role && M.stat != DEAD)
+ var/datum/job/J = SSjob.GetJob(M.mind.assigned_role)
+ if(J)
+ if(length(M.mind.antag_datums))
+ threat += J.GetThreat()
+ else
+ threat -= J.GetThreat()
threat += (mode.current_players[CURRENT_DEAD_PLAYERS].len)*dead_player_weight
return round(threat,0.1)
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 2706eac405..4a7abda53c 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -241,6 +241,9 @@ GLOBAL_LIST_EMPTY(antagonists)
/// Gets our threat level. Defaults to threat var, override for custom stuff like different traitor goals having different threats.
/datum/antagonist/proc/threat()
+ . = CONFIG_GET(keyed_list/antag_threat)[lowertext(name)]
+ if(. == null)
+ return threat
return threat
//This one is created by admin tools for custom objectives
diff --git a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm
index d40942f43d..94c62df696 100644
--- a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm
+++ b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm
@@ -92,7 +92,7 @@
owner.current.blood_volume = max(owner.current.blood_volume,BLOOD_VOLUME_SAFE)
/datum/antagonist/bloodsucker/threat()
- return threat+3*vamplevel
+ return ..()+3*vamplevel
/datum/antagonist/bloodsucker/proc/SelectFirstName()
diff --git a/code/modules/antagonists/bloodsucker/datum_hunter.dm b/code/modules/antagonists/bloodsucker/datum_hunter.dm
index 84b9d4e828..c0933d8a3c 100644
--- a/code/modules/antagonists/bloodsucker/datum_hunter.dm
+++ b/code/modules/antagonists/bloodsucker/datum_hunter.dm
@@ -16,7 +16,7 @@
threat = -3
/datum/antagonist/vamphunter/threat()
- return bad_dude ? -threat : threat
+ return bad_dude ? -(..()) : ..()
/datum/antagonist/vamphunter/on_gain()
diff --git a/code/modules/antagonists/devil/devil.dm b/code/modules/antagonists/devil/devil.dm
index dec39a2aa6..a7071276cc 100644
--- a/code/modules/antagonists/devil/devil.dm
+++ b/code/modules/antagonists/devil/devil.dm
@@ -114,7 +114,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
var/ascendable = FALSE
/datum/antagonist/devil/threat()
- return threat + form * 10
+ return ..() + form * 10
/datum/antagonist/devil/can_be_owned(datum/mind/new_owner)
. = ..()
diff --git a/code/modules/antagonists/ninja/ninja.dm b/code/modules/antagonists/ninja/ninja.dm
index bfd028aec2..2615822dd8 100644
--- a/code/modules/antagonists/ninja/ninja.dm
+++ b/code/modules/antagonists/ninja/ninja.dm
@@ -4,12 +4,13 @@
job_rank = ROLE_NINJA
show_name_in_check_antagonists = TRUE
antag_moodlet = /datum/mood_event/focused
+ threat = 8
var/helping_station = FALSE
var/give_objectives = TRUE
var/give_equipment = TRUE
/datum/antagonist/ninja/threat()
- return helping_station ? -8 : 8
+ return helping_station ? -(..()) : ..()
/datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index d3aae4b75a..4aed79a6dc 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -292,4 +292,4 @@
return SSticker.mode.name == "traitor"
/datum/antagonist/traitor/threat()
- return threat+traitor_kind.threat
+ return (..())+traitor_kind.threat
diff --git a/config/job_threats.txt b/config/job_threats.txt
index b3f0e2162d..e57edb66e4 100644
--- a/config/job_threats.txt
+++ b/config/job_threats.txt
@@ -2,4 +2,8 @@
## List of job titles followed by dynamic threat value, all prefixed with JOB_THREAT. See code/modules/jobs/job_types for titles
## e.g.
## JOB_THREAT Captain 5
-## JOB_THREAT Assistant 0.2
\ No newline at end of file
+## JOB_THREAT Assistant 0.2
+
+## Custom antag threat values, see above
+## e.g.
+## ANTAG_THREAT Traitor 5
\ No newline at end of file