diff --git a/code/__DEFINES/dynamic.dm b/code/__DEFINES/dynamic.dm
index 0d57961f48..ae5b031c8b 100644
--- a/code/__DEFINES/dynamic.dm
+++ b/code/__DEFINES/dynamic.dm
@@ -6,6 +6,7 @@
#define NO_ASSASSIN (1<<0)
#define WAROPS_ALWAYS_ALLOWED (1<<1)
#define USE_PREF_WEIGHTS (1<<2)
+#define FORCE_IF_WON (1<<3)
#define ONLY_RULESET (1<<0)
#define HIGHLANDER_RULESET (1<<1)
diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm
index 6d263473b0..29f26e4500 100644
--- a/code/__DEFINES/maths.dm
+++ b/code/__DEFINES/maths.dm
@@ -203,5 +203,7 @@
#define MANHATTAN_DISTANCE(a, b) (abs(a.x - b.x) + abs(a.y - b.y))
+#define LOGISTIC_FUNCTION(L,k,x,x_0) (L/(1+(NUM_E**(-k*(x-x_0)))))
+
/// Make sure something is a boolean TRUE/FALSE 1/0 value, since things like bitfield & bitflag doesn't always give 1s and 0s.
#define FORCE_BOOLEAN(x) ((x)? TRUE : FALSE)
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index bc290bedb9..2b4822a013 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -396,6 +396,10 @@
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_NUM
+/datum/config_entry/keyed_list/job_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/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 31e436f206..91d0db85d1 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -66,6 +66,7 @@ SUBSYSTEM_DEF(job)
/datum/controller/subsystem/job/proc/GetJob(rank)
+ RETURN_TYPE(/datum/job)
if(!occupations.len)
SetupOccupations()
return name_occupations[rank]
@@ -738,4 +739,4 @@ SUBSYSTEM_DEF(job)
. |= player.mind
/datum/controller/subsystem/job/proc/JobDebug(message)
- log_job_debug(message)
\ No newline at end of file
+ log_job_debug(message)
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index e7e5754f6b..e16c6d5fb3 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -354,11 +354,15 @@ SUBSYSTEM_DEF(vote)
return message_admins("A vote has tried to change the gamemode, but the game has already started. Aborting.")
GLOB.master_mode = "dynamic"
var/list/runnable_storytellers = config.get_runnable_storytellers()
+ var/datum/dynamic_storyteller/picked
for(var/T in runnable_storytellers)
var/datum/dynamic_storyteller/S = T
+ if(stored_gamemode_votes[initial(S.name)] == 1 && CHECK_BITFIELD(initial(S.flags), FORCE_IF_WON))
+ picked = S
runnable_storytellers[S] *= round(stored_gamemode_votes[initial(S.name)]*100000,1)
- var/datum/dynamic_storyteller/S = pickweightAllowZero(runnable_storytellers)
- GLOB.dynamic_storyteller_type = S
+ if(!picked)
+ picked = pickweightAllowZero(runnable_storytellers)
+ GLOB.dynamic_storyteller_type = picked
if("map")
var/datum/map_config/VM = config.maplist[.]
message_admins("The map has been voted for and will change to: [VM.map_name]")
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 4c3ef824b9..58dfd6d814 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -54,9 +54,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
// Current storyteller
var/datum/dynamic_storyteller/storyteller = null
// Threat logging vars
- /// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
+ /// Target threat level right now. Events and antags will try to keep the round at this level.
var/threat_level = 0
- /// Set at the beginning of the round. Spent by the mode to "purchase" rules.
+ /// 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
@@ -261,11 +261,11 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
. += "Uncharted Space
"
. += "Congratulations and thank you for participating in the NT 'Frontier' space program! Your station is actively orbiting a high value system far from the nearest support stations. Little is known about your region of space, and the opportunity to encounter the unknown invites greater glory. You are encouraged to elevate security as necessary to protect Nanotrasen assets."
set_security_level(SEC_LEVEL_BLUE)
- if(80 to 99)
+ if(80 to 95)
. += "Black Orbit
"
. += "As part of a mandatory security protocol, we are required to inform you that as a result of your orbital pattern directly behind an astrological body (oriented from our nearest observatory), your station will be under decreased monitoring and support. It is anticipated that your extreme location and decreased surveillance could pose security risks. Avoid unnecessary risks and attempt to keep your station in one piece."
set_security_level(SEC_LEVEL_AMBER)
- if(100)
+ if(96 to 100)
. += "Impending Doom
"
. += "Your station is somehow in the middle of hostile territory, in clear view of any enemy of the corporation. Your likelihood to survive is low, and station destruction is expected and almost inevitable. Secure any sensitive material and neutralize any enemy you will come across. It is important that you at least try to maintain the station.
"
. += "Good luck."
@@ -330,7 +330,6 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
peaceful_percentage = round(LORENTZ_CUMULATIVE_DISTRIBUTION(relative_threat, GLOB.dynamic_curve_centre, GLOB.dynamic_curve_width), 0.01)*100
- threat = threat_level
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Initial threat level")
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_centre,"Curve centre")
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_width,"Curve width")
@@ -463,7 +462,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
log_game("DYNAMIC: Additional ruleset picked successfully, now [executed_rules.len] picked. [extra_rulesets_amount] remaining.")
else
- if(threat >= 50)
+ if(threat_level >= 50)
message_admins("DYNAMIC: Picking first roundstart ruleset failed. You should report this.")
log_game("DYNAMIC: Picking first roundstart ruleset failed. drafted_rules.len = [drafted_rules.len] and threat = [threat]/[threat_level]")
return FALSE
@@ -509,9 +508,8 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
drafted_rules -= starting_rule
starting_rule.trim_candidates()
- var/added_threat = starting_rule.scale_up(extra_rulesets_amount, threat)
+ starting_rule.scale_up(extra_rulesets_amount, threat)
if (starting_rule.pre_execute())
- spend_threat(starting_rule.cost + added_threat)
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
@@ -534,8 +532,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
/datum/game_mode/dynamic/proc/execute_roundstart_rule(sent_rule)
var/datum/dynamic_ruleset/rule = sent_rule
if(rule.execute())
- if(rule.persistent)
- current_rules += rule
+ current_rules += rule
SSblackbox.record_feedback("associative","dynamic_rulesets",1,rule.get_blackbox_info())
return TRUE
rule.clean_up() // Refund threat, delete teams and so on.
@@ -607,7 +604,6 @@ 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))
- spend_threat(new_rule.cost)
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)
@@ -617,8 +613,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
log_game("DYNAMIC: Making a call to a specific ruleset...[new_rule.name]!")
SSblackbox.record_feedback("associative","dynamic_rulesets",1,new_rule.get_blackbox_info())
executed_rules += new_rule
- if (new_rule.persistent)
- current_rules += new_rule
+ current_rules += new_rule
return TRUE
else if (forced)
log_game("DYNAMIC: The ruleset [new_rule.name] couldn't be executed due to lack of eligible players.")
@@ -629,7 +624,6 @@ 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].")
- spend_threat(rule.cost)
log_threat("[rule.ruletype] [rule.name] spent [rule.cost]", verbose = TRUE)
if(rule.flags & HIGHLANDER_RULESET)
highlander_executed = TRUE
@@ -647,9 +641,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
return TRUE
stack_trace("The [rule.ruletype] rule \"[rule.name]\" failed to execute.")
return FALSE
-
+
/datum/game_mode/dynamic/process()
- if (pop_last_updated < world.time - (60 SECONDS))
+ if (pop_last_updated < world.time - (120 SECONDS))
pop_last_updated = world.time
update_playercounts()
@@ -658,7 +652,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
current_rules -= rule
SSblackbox.record_feedback("tally","dynamic",1,"Rulesets finished")
SSblackbox.record_feedback("associative","dynamic_rulesets_finished",1,rule.get_blackbox_info())
-
+
storyteller.do_process()
if (midround_injection_cooldown < world.time)
@@ -717,6 +711,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
/// Removes type from the list
/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
@@ -767,23 +762,21 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
SSblackbox.record_feedback("tally","dynamic",1,"Successful latejoin injections")
latejoin_injection_cooldown = storyteller.get_latejoin_cooldown() + world.time
-/// Refund threat, but no more than threat_level.
-/datum/game_mode/dynamic/proc/refund_threat(regain)
- threat = min(threat_level,threat+regain)
- SSblackbox.record_feedback("tally","dynamic_threat",regain,"Refunded threat")
- log_threat("[regain] refunded. Threat is now [threat].", verbose = TRUE)
-
-/// Generate threat and increase the threat_level if it goes beyond, capped at 100
+/// Increase the threat level.
/datum/game_mode/dynamic/proc/create_threat(gain)
- threat = min(100, threat+gain)
- if(threat > threat_level)
- threat_level = threat
+ threat_level += gain
SSblackbox.record_feedback("tally","dynamic_threat",gain,"Created threat")
- log_threat("[gain] created. Threat is now [threat] and threat level is now [threat_level].", verbose = TRUE)
+ log_threat("[gain] created. Threat level is now [threat_level].", verbose = TRUE)
-/// Expend threat, can't fall under 0.
+/// Decrease the threat level.
+/datum/game_mode/dynamic/proc/remove_threat(loss)
+ threat_level -= loss
+ SSblackbox.record_feedback("tally","dynamic_threat",loss,"Removed threat")
+ 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 = max(threat-cost,0)
+ threat += cost
SSblackbox.record_feedback("tally","dynamic_threat",cost,"Threat spent")
log_threat("[cost] spent. Threat is now [threat].", verbose = TRUE)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
index 2f143f7233..eb7da2144f 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
@@ -174,8 +174,8 @@
/// This is called if persistent variable is true everytime SSTicker ticks.
/datum/dynamic_ruleset/proc/rule_process()
- return
-
+ return TRUE
+
/// Called on game mode pre_setup for roundstart rulesets.
/// Do everything you need to do before job is assigned here.
/// IMPORTANT: ASSIGN special_role HERE
@@ -201,8 +201,7 @@
/// Runs from gamemode process() if ruleset fails to start, like delayed rulesets not getting valid candidates.
/// This one only handles refunding the threat, override in ruleset to clean up the rest.
/datum/dynamic_ruleset/proc/clean_up()
- mode.refund_threat(cost + (scaled_times * scaling_cost))
- mode.log_threat("[ruletype] [name] refunded [cost + (scaled_times * scaling_cost)]",verbose=TRUE)
+ return
/// Gets weight of the ruleset
/// Note that this decreases weight if repeatable is TRUE and repeatable_weight_decrease is higher than 0
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 638d8b53b6..15c531b15d 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -315,7 +315,6 @@
/datum/dynamic_ruleset/midround/from_ghosts/wizard
name = "Wizard"
config_tag = "midround_wizard"
- persistent = TRUE
antag_datum = /datum/antagonist/wizard
antag_flag = ROLE_WIZARD
enemy_roles = list("Security Officer","Detective","Head of Security", "Captain")
@@ -344,7 +343,6 @@
/datum/dynamic_ruleset/midround/from_ghosts/wizard/rule_process() // i can literally copy this from are_special_antags_dead it's great
if(isliving(wizard.current) && wizard.current.stat!=DEAD)
return FALSE
-
for(var/obj/item/phylactery/P in GLOB.poi_list) //TODO : IsProperlyDead()
if(P.mind && P.mind.has_antag_datum(/datum/antagonist/wizard))
return FALSE
@@ -666,9 +664,6 @@
Mind.transfer_to(Ninja)
var/datum/antagonist/ninja/ninjadatum = new
ninjadatum.helping_station = pick(TRUE,FALSE)
- if(ninjadatum.helping_station)
- mode.refund_threat(cost+5)
- mode.log_threat("Ninja was helping station; [cost+5] cost refunded.")
Mind.add_antag_datum(ninjadatum)
if(Ninja.mind != Mind) //something has gone wrong!
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
index 2527c83077..8b44abbe03 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
@@ -802,7 +802,6 @@
/datum/dynamic_ruleset/roundstart/bloodsucker
name = "Bloodsuckers"
config_tag = "bloodsucker"
- persistent = TRUE
antag_flag = ROLE_BLOODSUCKER
antag_datum = ANTAG_DATUM_BLOODSUCKER
minimum_required_age = 0
diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
index d25416a4d1..63fd12f47a 100644
--- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm
+++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
@@ -5,17 +5,18 @@
var/list/property_weights = list() // See below.
var/curve_centre = 0 // As GLOB.dynamic_curve_centre.
var/curve_width = 1.8 // As GLOB.dynamic_curve_width.
- var/forced_threat_level = -1
+ var/forced_threat_level = -1 // As GLOB.dynamic_forced_threat_level
/*
NO_ASSASSIN: Will not have permanent assassination targets.
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
*/
var/flags = 0
+ var/dead_player_weight = 1 // How much dead players matter for threat calculation
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/pop_antag_ratio = 5 // How many non-antags there should be vs antags.
var/datum/game_mode/dynamic/mode = null // Cached as soon as it's made, by dynamic.
/**
@@ -39,6 +40,27 @@ Property weights are:
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
mode.event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
+/datum/dynamic_storyteller/proc/calculate_threat()
+ var/threat = 0
+ for(var/datum/antagonist/A in GLOB.antagonists)
+ if(A?.owner?.current && A.owner.current.stat != DEAD)
+ threat += A.threat()
+ for(var/r in SSevents.running)
+ var/datum/round_event/R = r
+ threat += R.threat()
+ for(var/mob/living/simple_animal/hostile/H in GLOB.mob_living_list)
+ var/turf/T = get_turf(H)
+ 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()
+ threat += (mode.current_players[CURRENT_DEAD_PLAYERS].len)*dead_player_weight
+ return round(threat,0.1)
+
/datum/dynamic_storyteller/proc/do_process()
return
@@ -83,25 +105,9 @@ Property weights are:
if(mode.forced_injection)
mode.forced_injection = !dry_run
return 100
- var/chance = 0
- // If the high pop override is in effect, we reduce the impact of population on the antag injection chance
- var/high_pop_factor = (mode.current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit)
- var/max_pop_per_antag = max(pop_antag_ratio,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
- if (!mode.current_players[CURRENT_LIVING_ANTAGS].len)
- chance += 80 // No antags at all? let's boost those odds!
- else
- var/current_pop_per_antag = mode.current_players[CURRENT_LIVING_PLAYERS].len / mode.current_players[CURRENT_LIVING_ANTAGS].len
- if (current_pop_per_antag > max_pop_per_antag)
- chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag))
- else
- chance += 25-10*(max_pop_per_antag-current_pop_per_antag)
- if (mode.current_players[CURRENT_DEAD_PLAYERS].len > mode.current_players[CURRENT_LIVING_PLAYERS].len)
- chance -= 30 // More than half the crew died? ew, let's calm down on antags
- if (mode.threat > 70)
- chance += 15
- if (mode.threat < 30)
- chance -= 15
- return round(max(0,chance))
+ var/threat_perc = mode.threat/mode.threat_level
+
+ return round(max(0,100*(1-(threat_perc*threat_perc*threat_perc))))
/datum/dynamic_storyteller/proc/roundstart_draft()
var/list/drafted_rules = list()
@@ -114,32 +120,41 @@ Property weights are:
for(var/property in property_weights)
if(property in rule.property_weights) // just treat it as 0 if it's not in there
property_weight += rule.property_weights[property] * property_weights[property]
- drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
+ drafted_rules[rule] = (rule.get_weight() * property_weight)*rule.weight_mult
return drafted_rules
/datum/dynamic_storyteller/proc/midround_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules)
// if there are antags OR the rule is an antag rule, antag_acceptable will be true.
- if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
+ if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
rule.trim_candidates()
+ var/cost_difference = abs(rule.cost-(mode.threat_level-mode.threat))
+ /* Basically, the closer the cost is to the current threat-level-away-from-threat, the more likely it is to
+ pick this particular ruleset.
+ Let's use a toy example: there's 60 threat level and 10 threat spent.
+ We want to pick a ruleset that's close to that, so we run the below equation, on two rulesets.
+ Ruleset 1 has 30 cost, ruleset 2 has 5 cost.
+ When we do the math, ruleset 1's threat_weight is 0.538, and ruleset 2's is 0.238, meaning ruleset 1
+ is 2.26 times as likely to be picked, all other things considered.
+ Of course, we don't want it to GUARANTEE the closest, that's no fun, so it's just a weight.
+ */
+ var/threat_weight = 1-abs(1-LOGISTIC_FUNCTION(2,0.05,cost_difference,0))
if (rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
- drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
- else if(mode.threat < rule.cost)
- SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
+ drafted_rules[rule] = round(((rule.get_weight() * property_weight)*rule.weight_mult*threat_weight)*1000,1)
return drafted_rules
/datum/dynamic_storyteller/proc/latejoin_draft(mob/living/carbon/human/newPlayer)
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/latejoin/rule in mode.latejoin_rules)
- if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
+ if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level - mode.threat))
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
@@ -150,56 +165,53 @@ Property weights are:
rule.candidates = list(newPlayer)
rule.trim_candidates()
+ var/cost_difference = abs(rule.cost-(mode.threat_level-mode.threat))
+ var/threat_weight = 1-abs(1-(LOGISTIC_FUNCTION(2,0.05,cost_difference,0)))
if (rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
- drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
- else if(mode.threat < rule.cost)
- SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
+ drafted_rules[rule] = round(((rule.get_weight() * property_weight)*rule.weight_mult*threat_weight)*1000,1)
return drafted_rules
/datum/dynamic_storyteller/proc/event_draft()
var/list/drafted_rules = list()
for(var/datum/dynamic_ruleset/event/rule in mode.events)
- if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
+ if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && (mode.threat_level - mode.threat) >= rule.cost)
if(rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
- else if(mode.threat < rule.cost)
- SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
return drafted_rules
-/datum/dynamic_storyteller/cowabunga
+/datum/dynamic_storyteller/chaotic
name = "Chaotic"
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" = 10)
+ property_weights = list("extended" = -1, "chaos" = 2)
weight = 1
event_frequency_lower = 2 MINUTES
event_frequency_upper = 10 MINUTES
- flags = WAROPS_ALWAYS_ALLOWED
- pop_antag_ratio = 4
+ flags = WAROPS_ALWAYS_ALLOWED | FORCE_IF_WON
var/refund_cooldown = 0
-/datum/dynamic_storyteller/cowabunga/get_midround_cooldown()
+/datum/dynamic_storyteller/chaotic/do_process()
+ if(refund_cooldown < world.time)
+ mode.create_threat(20)
+ mode.log_threat("Chaotic storyteller ramped up the chaos. Threat level is now [mode.threat_level].")
+ refund_cooldown = world.time + 20 MINUTES
+
+/datum/dynamic_storyteller/chaotic/get_midround_cooldown()
return ..() / 4
-/datum/dynamic_storyteller/cowabunga/get_latejoin_cooldown()
+/datum/dynamic_storyteller/chaotic/get_latejoin_cooldown()
return ..() / 4
-/datum/dynamic_storyteller/cowabunga/do_process()
- if(refund_cooldown < world.time)
- mode.refund_threat(40)
- mode.log_threat("Chaotic storyteller refunded 40 threat. Threat is now [mode.threat].")
- refund_cooldown = world.time + 1200 SECONDS
-
/datum/dynamic_storyteller/team
name = "Teamwork"
config_tag = "teamwork"
@@ -223,23 +235,93 @@ Property weights are:
flags = WAROPS_ALWAYS_ALLOWED
property_weights = list("valid" = 1, "conversion" = 20)
-/datum/dynamic_storyteller/classic
+/datum/dynamic_storyteller/random
name = "Random"
config_tag = "random"
- desc = "No special weights attached. Anything goes."
- weight = 4
- curve_width = 4
- pop_antag_ratio = 7
- flags = USE_PREF_WEIGHTS
+ weight = 1
+ desc = "No weighting at all; every ruleset has the same chance of happening. Cooldowns vary wildly. As random as it gets."
+ forced_threat_level = 100
+
+/datum/dynamic_storyteller/random/get_midround_cooldown()
+ return rand(GLOB.dynamic_midround_delay_min/2, GLOB.dynamic_midround_delay_max*2)
+
+/datum/dynamic_storyteller/random/get_event_cooldown()
+ return rand(GLOB.dynamic_event_delay_min/2, GLOB.dynamic_event_delay_max*2)
+
+/datum/dynamic_storyteller/random/get_latejoin_cooldown()
+ return rand(GLOB.dynamic_latejoin_delay_min/2, GLOB.dynamic_latejoin_delay_max*2)
+
+/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)
+ 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)
+ drafted_rules[rule] = 1
+ return drafted_rules
+
+/datum/dynamic_storyteller/random/midround_draft()
+ var/list/drafted_rules = list()
+ for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules)
+ if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
+ // Classic secret : only autotraitor/minor roles
+ if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
+ continue
+ rule.trim_candidates()
+ if (rule.ready())
+ drafted_rules[rule] = 1
+ return drafted_rules
+
+/datum/dynamic_storyteller/random/latejoin_draft(mob/living/carbon/human/newPlayer)
+ var/list/drafted_rules = list()
+ for (var/datum/dynamic_ruleset/latejoin/rule in mode.latejoin_rules)
+ if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
+ // Classic secret : only autotraitor/minor roles
+ if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
+ continue
+ // No stacking : only one round-ender, unless threat level > stacking_limit.
+ if (mode.threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
+ if(rule.flags & HIGHLANDER_RULESET && mode.highlander_executed)
+ continue
+ rule.candidates = list(newPlayer)
+ rule.trim_candidates()
+ if (rule.ready())
+ drafted_rules[rule] = 1
+ return drafted_rules
+
+/datum/dynamic_storyteller/random/event_draft()
+ var/list/drafted_rules = list()
+ for(var/datum/dynamic_ruleset/event/rule in mode.events)
+ if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
+ if(rule.ready())
+ drafted_rules[rule] = 1
+ return drafted_rules
/datum/dynamic_storyteller/story
name = "Story"
config_tag = "story"
- desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies."
+ desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies. Has a buildup-climax-falling action threat curve."
weight = 2
curve_width = 2
- pop_antag_ratio = 7
- property_weights = list("story_potential" = 10)
+ property_weights = list("story_potential" = 2)
+
+
+/datum/dynamic_storyteller/story/do_process()
+ var/current_time = (world.time / SSautotransfer.targettime)*180
+ mode.threat_level = round(mode.initial_threat_level*(sin(current_time)+0.5),0.1)
+
+/datum/dynamic_storyteller/classic
+ 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
/datum/dynamic_storyteller/suspicion
name = "Intrigue"
@@ -247,8 +329,8 @@ Property weights are:
desc = "Antags that instill distrust in the crew. Traitors, bloodsuckers."
weight = 2
curve_width = 2
- pop_antag_ratio = 7
- property_weights = list("trust" = -5)
+ dead_player_weight = 2
+ property_weights = list("trust" = -3)
/datum/dynamic_storyteller/liteextended
name = "Calm"
@@ -256,10 +338,10 @@ Property weights are:
desc = "Low-chaos round. Few antags. No conversion."
curve_centre = -3
curve_width = 0.5
- flags = NO_ASSASSIN
+ flags = NO_ASSASSIN | FORCE_IF_WON
weight = 1
- pop_antag_ratio = 10
- property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
+ dead_player_weight = 5
+ property_weights = list("extended" = 2, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
/datum/dynamic_storyteller/no_antag
name = "Extended"
@@ -267,7 +349,7 @@ Property weights are:
desc = "No standard antags. Threatening events may still spawn."
curve_centre = -5
curve_width = 0.5
- flags = NO_ASSASSIN
+ flags = NO_ASSASSIN | FORCE_IF_WON
weight = 1
property_weights = list("extended" = 2)
diff --git a/code/game/gamemodes/gangs/gang.dm b/code/game/gamemodes/gangs/gang.dm
index fa25701ac4..df94d68688 100644
--- a/code/game/gamemodes/gangs/gang.dm
+++ b/code/game/gamemodes/gangs/gang.dm
@@ -4,6 +4,7 @@
can_coexist_with_others = FALSE
job_rank = ROLE_GANG
antagpanel_category = "Gang"
+ threat = 2
var/hud_type = "gangster"
var/message_name = "Gangster"
var/datum/team/gang/gang
@@ -167,6 +168,7 @@
name = "Gang boss"
hud_type = "gang_boss"
message_name = "Leader"
+ threat = 10
/datum/antagonist/gang/boss/on_gain()
..()
@@ -474,4 +476,4 @@
#undef MAXIMUM_RECALLS
-#undef INFLUENCE_INTERVAL
\ No newline at end of file
+#undef INFLUENCE_INTERVAL
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 2e3626dd4c..30ca8955d2 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -22,6 +22,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED
var/show_name_in_check_antagonists = FALSE //Will append antagonist name in admin listings - use for categories that share more than one antag type
var/list/blacklisted_quirks = list(/datum/quirk/nonviolent,/datum/quirk/mute) // Quirks that will be removed upon gaining this antag. Pacifist and mute are default.
+ var/threat = 0 // Amount of threat this antag poses, for dynamic mode
/datum/antagonist/New()
GLOB.antagonists += src
@@ -237,6 +238,10 @@ GLOBAL_LIST_EMPTY(antagonists)
return H.hijack_speed_override
return hijack_speed
+/// Gets our threat level. Defaults to threat var, override for custom stuff like different traitor goals having different threats.
+/datum/antagonist/proc/threat()
+ return threat
+
//This one is created by admin tools for custom objectives
/datum/antagonist/custom
antagpanel_category = "Custom"
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 564e9a8617..44116dbe36 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -6,6 +6,7 @@
antagpanel_category = "Abductor"
job_rank = ROLE_ABDUCTOR
show_in_antagpanel = FALSE //should only show subtypes
+ threat = 5
var/datum/team/abductor_team/team
var/sub_role
var/outfit
diff --git a/code/modules/antagonists/blob/blob.dm b/code/modules/antagonists/blob/blob.dm
index 3d6db983dc..1b076c9302 100644
--- a/code/modules/antagonists/blob/blob.dm
+++ b/code/modules/antagonists/blob/blob.dm
@@ -3,7 +3,7 @@
roundend_category = "blobs"
antagpanel_category = "Blob"
job_rank = ROLE_BLOB
-
+ threat = 20
var/datum/action/innate/blobpop/pop_action
var/starting_points_human_blob = 60
var/point_rate_human_blob = 2
@@ -63,4 +63,4 @@
if(owner && owner.current)
var/mob/camera/blob/B = owner.current
if(istype(B))
- . += "(Progress: [B.blobs_legit.len]/[B.blobwincount])"
\ No newline at end of file
+ . += "(Progress: [B.blobs_legit.len]/[B.blobwincount])"
diff --git a/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm b/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm
index b720d3590e..3757aecd02 100644
--- a/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm
+++ b/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm
@@ -75,6 +75,7 @@
desc = "A floating, fragile spore."
icon_state = "blobpod"
icon_living = "blobpod"
+ threat = 0.2
health = 30
maxHealth = 30
verb_say = "psychically pulses"
diff --git a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm
index 755d12ae09..d40942f43d 100644
--- a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm
+++ b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm
@@ -6,7 +6,7 @@
roundend_category = "bloodsuckers"
antagpanel_category = "Bloodsucker"
job_rank = ROLE_BLOODSUCKER
-
+ threat = 5
// NAME
var/vampname // My Dracula name
var/vamptitle // My Dracula title
@@ -91,10 +91,8 @@
// Refill with Blood
owner.current.blood_volume = max(owner.current.blood_volume,BLOOD_VOLUME_SAFE)
-
-
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/datum/antagonist/bloodsucker/threat()
+ return threat+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 5fc6716a7d..84b9d4e828 100644
--- a/code/modules/antagonists/bloodsucker/datum_hunter.dm
+++ b/code/modules/antagonists/bloodsucker/datum_hunter.dm
@@ -13,7 +13,10 @@
var/list/datum/objective/objectives_given = list() // For removal if needed.
var/datum/martial_art/my_kungfu // Hunters know a lil kung fu.
var/bad_dude = FALSE // Every first hunter spawned is a SHIT LORD.
+ threat = -3
+/datum/antagonist/vamphunter/threat()
+ return bad_dude ? -threat : threat
/datum/antagonist/vamphunter/on_gain()
diff --git a/code/modules/antagonists/bloodsucker/datum_vassal.dm b/code/modules/antagonists/bloodsucker/datum_vassal.dm
index 345c6f3c10..3d5ed1369b 100644
--- a/code/modules/antagonists/bloodsucker/datum_vassal.dm
+++ b/code/modules/antagonists/bloodsucker/datum_vassal.dm
@@ -19,6 +19,7 @@
var/datum/antagonist/bloodsucker/master // Who made me?
var/list/datum/action/powers = list()// Purchased powers
var/list/datum/objective/objectives_given = list() // For removal if needed.
+ threat = 1
/datum/antagonist/vassal/can_be_owned(datum/mind/new_owner)
// If we weren't created by a bloodsucker, then we cannot be a vassal (assigned from antag panel)
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index efe21f38fa..a48e080a89 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -5,6 +5,7 @@
var/special_role = ROLE_BROTHER
var/datum/team/brother_team/team
antag_moodlet = /datum/mood_event/focused
+ threat = 3
/datum/antagonist/brother/create_team(datum/team/brother_team/new_team)
if(!new_team)
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index a81c409233..cc15147312 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -8,6 +8,7 @@
antagpanel_category = "Changeling"
job_rank = ROLE_CHANGELING
antag_moodlet = /datum/mood_event/focused
+ threat = 10
var/you_are_greet = TRUE
var/give_objectives = TRUE
diff --git a/code/modules/antagonists/clockcult/clock_mobs.dm b/code/modules/antagonists/clockcult/clock_mobs.dm
index 2f00fd4e4a..87466d65f2 100644
--- a/code/modules/antagonists/clockcult/clock_mobs.dm
+++ b/code/modules/antagonists/clockcult/clock_mobs.dm
@@ -6,6 +6,7 @@
unique_name = 1
minbodytemp = 0
unsuitable_atmos_damage = 0
+ threat = 1
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) //Robotic
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
healable = FALSE
diff --git a/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm b/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm
index 897bff779d..f43f2814d8 100644
--- a/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm
+++ b/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm
@@ -9,6 +9,7 @@
desc = "The stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield."
icon_state = "clockwork_marauder"
mob_biotypes = MOB_HUMANOID
+ threat = 3
health = 120
maxHealth = 120
force_threshold = 8
diff --git a/code/modules/antagonists/clockcult/clockcult.dm b/code/modules/antagonists/clockcult/clockcult.dm
index 1869414ab6..cf3d88983c 100644
--- a/code/modules/antagonists/clockcult/clockcult.dm
+++ b/code/modules/antagonists/clockcult/clockcult.dm
@@ -5,6 +5,7 @@
antagpanel_category = "Clockcult"
job_rank = ROLE_SERVANT_OF_RATVAR
antag_moodlet = /datum/mood_event/cult
+ threat = 3
var/datum/action/innate/hierophant/hierophant_network = new()
var/datum/team/clockcult/clock_team
var/make_team = TRUE //This should be only false for tutorial scarabs
diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm
index 4bab621674..1746fd135c 100644
--- a/code/modules/antagonists/cult/cult.dm
+++ b/code/modules/antagonists/cult/cult.dm
@@ -5,6 +5,7 @@
roundend_category = "cultists"
antagpanel_category = "Cult"
antag_moodlet = /datum/mood_event/cult
+ threat = 3
var/datum/action/innate/cult/comm/communion = new
var/datum/action/innate/cult/mastervote/vote = new
var/datum/action/innate/cult/blood_magic/magic = new
diff --git a/code/modules/antagonists/devil/devil.dm b/code/modules/antagonists/devil/devil.dm
index 1d0bfde322..5d9ced0aae 100644
--- a/code/modules/antagonists/devil/devil.dm
+++ b/code/modules/antagonists/devil/devil.dm
@@ -91,6 +91,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
job_rank = ROLE_DEVIL
//Don't delete upon mind destruction, otherwise soul re-selling will break.
delete_on_mind_deletion = FALSE
+ threat = 5
var/obligation
var/ban
var/bane
@@ -112,6 +113,9 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
/obj/effect/proc_holder/spell/targeted/summon_dancefloor))
var/ascendable = FALSE
+/datum/antagonist/devil/threat()
+ return threat + form * 10
+
/datum/antagonist/devil/can_be_owned(datum/mind/new_owner)
. = ..()
return . && (ishuman(new_owner.current) || iscyborg(new_owner.current))
@@ -120,7 +124,6 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
. = ..()
.["Toggle ascendable"] = CALLBACK(src,.proc/admin_toggle_ascendable)
-
/datum/antagonist/devil/proc/admin_toggle_ascendable(mob/admin)
ascendable = !ascendable
message_admins("[key_name_admin(admin)] set [owner.current] devil ascendable to [ascendable]")
diff --git a/code/modules/antagonists/devil/imp/imp.dm b/code/modules/antagonists/devil/imp/imp.dm
index c2f636959b..1539bc384b 100644
--- a/code/modules/antagonists/devil/imp/imp.dm
+++ b/code/modules/antagonists/devil/imp/imp.dm
@@ -62,6 +62,7 @@
name = "Imp"
antagpanel_category = "Devil"
show_in_roundend = FALSE
+ threat = 10
/datum/antagonist/imp/on_gain()
. = ..()
@@ -71,4 +72,4 @@
var/datum/objective/newobjective = new
newobjective.explanation_text = "Try to get a promotion to a higher devilic rank."
newobjective.owner = owner
- objectives += newobjective
\ No newline at end of file
+ objectives += newobjective
diff --git a/code/modules/antagonists/devil/sintouched/sintouched.dm b/code/modules/antagonists/devil/sintouched/sintouched.dm
index 9983e5f599..c9bf474567 100644
--- a/code/modules/antagonists/devil/sintouched/sintouched.dm
+++ b/code/modules/antagonists/devil/sintouched/sintouched.dm
@@ -14,6 +14,13 @@
var/static/list/sins = list(SIN_ACEDIA,SIN_GLUTTONY,SIN_GREED,SIN_SLOTH,SIN_WRATH,SIN_ENVY,SIN_PRIDE)
+/datum/antagonist/sintouched/threat()
+ switch(sin)
+ if(SIN_GLUTTONY,SIN_ENVY)
+ return 1
+ else
+ return 0
+
/datum/antagonist/sintouched/New()
. = ..()
sin = pick(sins)
@@ -80,4 +87,4 @@
#undef SIN_GREED
#undef SIN_PRIDE
#undef SIN_SLOTH
-#undef SIN_WRATH
\ No newline at end of file
+#undef SIN_WRATH
diff --git a/code/modules/antagonists/disease/disease_abilities.dm b/code/modules/antagonists/disease/disease_abilities.dm
index e8dcaf9cc7..496d11bcbc 100644
--- a/code/modules/antagonists/disease/disease_abilities.dm
+++ b/code/modules/antagonists/disease/disease_abilities.dm
@@ -59,7 +59,7 @@ new /datum/disease_ability/symptom/powerful/youth
var/stat_block = ""
var/threshold_block = ""
var/category = ""
-
+ var/malefit = 0 // used for threat calculation
var/list/symptoms
var/list/actions
@@ -282,6 +282,7 @@ new /datum/disease_ability/symptom/powerful/youth
/datum/disease_ability/symptom/medium/heal
cost = 5
+ malefit = -1
category = "Symptom (+)"
/datum/disease_ability/symptom/powerful
@@ -291,6 +292,7 @@ new /datum/disease_ability/symptom/powerful/youth
/datum/disease_ability/symptom/powerful/heal
cost = 8
+ malefit = -1
category = "Symptom (Strong+)"
/******MILD******/
@@ -319,50 +321,61 @@ new /datum/disease_ability/symptom/powerful/youth
/datum/disease_ability/symptom/medium/hallucigen
symptoms = list(/datum/symptom/hallucigen)
+ malefit = 1
short_desc = "Cause victims to hallucinate."
long_desc = "Cause victims to hallucinate. Decreases stats, especially resistance."
/datum/disease_ability/symptom/medium/choking
symptoms = list(/datum/symptom/choking)
+ malefit = 1
short_desc = "Cause victims to choke."
long_desc = "Cause victims to choke, threatening asphyxiation. Decreases stats, especially transmissibility."
/datum/disease_ability/symptom/medium/confusion
symptoms = list(/datum/symptom/confusion)
+ malefit = 1
short_desc = "Cause victims to become confused."
long_desc = "Cause victims to become confused intermittently."
/datum/disease_ability/symptom/medium/vomit
symptoms = list(/datum/symptom/vomit)
+ malefit = 1
short_desc = "Cause victims to vomit."
long_desc = "Cause victims to vomit. Slightly increases transmissibility. Vomiting also also causes the victims to lose nutrition and removes some toxin damage."
/datum/disease_ability/symptom/medium/voice_change
symptoms = list(/datum/symptom/voice_change)
+ malefit = 1
short_desc = "Change the voice of victims."
long_desc = "Change the voice of victims, causing confusion in communications."
/datum/disease_ability/symptom/medium/visionloss
symptoms = list(/datum/symptom/visionloss)
+ malefit = 1
short_desc = "Damage the eyes of victims, eventually causing blindness."
long_desc = "Damage the eyes of victims, eventually causing blindness. Decreases all stats."
/datum/disease_ability/symptom/medium/deafness
+ malefit = 1
symptoms = list(/datum/symptom/deafness)
/datum/disease_ability/symptom/medium/fever
+ malefit = 1
symptoms = list(/datum/symptom/fever)
/datum/disease_ability/symptom/medium/shivering
+ malefit = 1
symptoms = list(/datum/symptom/shivering)
/datum/disease_ability/symptom/medium/headache
symptoms = list(/datum/symptom/headache)
/datum/disease_ability/symptom/medium/nano_boost
+ malefit = -1
symptoms = list(/datum/symptom/nano_boost)
/datum/disease_ability/symptom/medium/nano_destroy
+ malefit = 1
symptoms = list(/datum/symptom/nano_destroy)
/datum/disease_ability/symptom/medium/viraladaptation
@@ -374,18 +387,22 @@ new /datum/disease_ability/symptom/powerful/youth
symptoms = list(/datum/symptom/viralevolution)
/datum/disease_ability/symptom/medium/polyvitiligo
+ malefit = 1
symptoms = list(/datum/symptom/polyvitiligo)
/datum/disease_ability/symptom/medium/disfiguration
+ malefit = 1
symptoms = list(/datum/symptom/disfiguration)
/datum/disease_ability/symptom/medium/itching
symptoms = list(/datum/symptom/itching)
+ malefit = 1
short_desc = "Cause victims to itch."
long_desc = "Cause victims to itch, increasing all stats except stealth."
/datum/disease_ability/symptom/medium/heal/weight_loss
symptoms = list(/datum/symptom/weight_loss)
+ malefit = 1
short_desc = "Cause victims to lose weight."
long_desc = "Cause victims to lose weight, and make it almost impossible for them to gain nutrition from food. Reduced nutrition allows your infection to spread more easily from hosts, especially by sneezing."
@@ -400,12 +417,15 @@ new /datum/disease_ability/symptom/powerful/youth
/******POWERFUL******/
/datum/disease_ability/symptom/powerful/fire
+ malefit = 1
symptoms = list(/datum/symptom/fire)
/datum/disease_ability/symptom/powerful/flesh_eating
+ malefit = 1
symptoms = list(/datum/symptom/flesh_eating)
/datum/disease_ability/symptom/powerful/genetic_mutation
+ malefit = 1
symptoms = list(/datum/symptom/genetic_mutation)
cost = 8
@@ -413,6 +433,7 @@ new /datum/disease_ability/symptom/powerful/youth
symptoms = list(/datum/symptom/inorganic_adaptation)
/datum/disease_ability/symptom/powerful/narcolepsy
+ malefit = 1
symptoms = list(/datum/symptom/narcolepsy)
/datum/disease_ability/symptom/powerful/youth
@@ -451,4 +472,4 @@ new /datum/disease_ability/symptom/powerful/youth
/datum/disease_ability/symptom/powerful/heal/coma
symptoms = list(/datum/symptom/heal/coma)
short_desc = "Cause victims to fall into a healing coma when hurt."
- long_desc = "Cause victims to fall into a healing coma when hurt."
\ No newline at end of file
+ long_desc = "Cause victims to fall into a healing coma when hurt."
diff --git a/code/modules/antagonists/disease/disease_datum.dm b/code/modules/antagonists/disease/disease_datum.dm
index 3c7de589d9..c827179006 100644
--- a/code/modules/antagonists/disease/disease_datum.dm
+++ b/code/modules/antagonists/disease/disease_datum.dm
@@ -17,6 +17,14 @@
. = ..()
+/datum/antagonist/disease/threat()
+ var/mob/camera/disease/D = owner.current
+ var/final_threat = 0
+ for(var/V in D.purchased_abilities)
+ var/datum/disease_ability/A = V
+ final_threat += (A.cost/8)*A.malefit
+ return final_threat*D.hosts
+
/datum/antagonist/disease/greet()
to_chat(owner.current, "You are the [owner.special_role]!")
to_chat(owner.current, "Infect members of the crew to gain adaptation points, and spread your infection further.")
diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm
index 5c878bcc55..bda1fbabe6 100644
--- a/code/modules/antagonists/ert/ert.dm
+++ b/code/modules/antagonists/ert/ert.dm
@@ -10,6 +10,7 @@
var/datum/outfit/outfit = /datum/outfit/ert/security
var/role = "Security Officer"
var/list/name_source
+ threat = -5
show_in_antagpanel = FALSE
antag_moodlet = /datum/mood_event/focused
diff --git a/code/modules/antagonists/greybois/greybois.dm b/code/modules/antagonists/greybois/greybois.dm
index b5e18045e8..458ebad9cd 100644
--- a/code/modules/antagonists/greybois/greybois.dm
+++ b/code/modules/antagonists/greybois/greybois.dm
@@ -2,6 +2,7 @@
name = "Emergency Assistant"
show_name_in_check_antagonists = TRUE
show_in_antagpanel = FALSE
+ threat = -1
var/mission = "Assist the station."
var/datum/outfit/outfit = /datum/outfit/ert/greybois
diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm
index bfd7b50cf8..ffca67b882 100644
--- a/code/modules/antagonists/highlander/highlander.dm
+++ b/code/modules/antagonists/highlander/highlander.dm
@@ -73,4 +73,4 @@
antiwelder.name = "compulsion of honor"
antiwelder.desc = "You are unable to hold anything in this hand until you're the last one left!"
antiwelder.icon_state = "bloodhand_right"
- H.put_in_hands(antiwelder)
\ No newline at end of file
+ H.put_in_hands(antiwelder)
diff --git a/code/modules/antagonists/monkey/monkey.dm b/code/modules/antagonists/monkey/monkey.dm
index e480eb8674..ebb39c814e 100644
--- a/code/modules/antagonists/monkey/monkey.dm
+++ b/code/modules/antagonists/monkey/monkey.dm
@@ -8,6 +8,7 @@
job_rank = ROLE_MONKEY
roundend_category = "monkeys"
antagpanel_category = "Monkey"
+ threat = 3
var/datum/team/monkey/monkey_team
var/monkey_only = TRUE
@@ -81,6 +82,7 @@
/datum/antagonist/monkey/leader
name = "Monkey Leader"
+ threat = 5
monkey_only = FALSE
/datum/antagonist/monkey/leader/admin_add(datum/mind/new_owner,mob/admin)
diff --git a/code/modules/antagonists/morph/morph_antag.dm b/code/modules/antagonists/morph/morph_antag.dm
index e01751946d..07781ce60a 100644
--- a/code/modules/antagonists/morph/morph_antag.dm
+++ b/code/modules/antagonists/morph/morph_antag.dm
@@ -2,5 +2,6 @@
name = "Morph"
show_name_in_check_antagonists = TRUE
show_in_antagpanel = FALSE
+ threat = 2
-//It does nothing! (Besides tracking)
\ No newline at end of file
+//It does nothing! (Besides tracking)
diff --git a/code/modules/antagonists/nightmare/nightmare.dm b/code/modules/antagonists/nightmare/nightmare.dm
index 41a3f181d0..837b6e4216 100644
--- a/code/modules/antagonists/nightmare/nightmare.dm
+++ b/code/modules/antagonists/nightmare/nightmare.dm
@@ -1,4 +1,5 @@
/datum/antagonist/nightmare
name = "Nightmare"
show_in_antagpanel = FALSE
- show_name_in_check_antagonists = TRUE
\ No newline at end of file
+ show_name_in_check_antagonists = TRUE
+ threat = 5
diff --git a/code/modules/antagonists/ninja/ninja.dm b/code/modules/antagonists/ninja/ninja.dm
index 133bd5ab6a..bfd028aec2 100644
--- a/code/modules/antagonists/ninja/ninja.dm
+++ b/code/modules/antagonists/ninja/ninja.dm
@@ -8,6 +8,9 @@
var/give_objectives = TRUE
var/give_equipment = TRUE
+/datum/antagonist/ninja/threat()
+ return helping_station ? -8 : 8
+
/datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_ninja_icons_added(M)
diff --git a/code/modules/antagonists/nukeop/clownop.dm b/code/modules/antagonists/nukeop/clownop.dm
index a3ccdee2b5..0ff2980939 100644
--- a/code/modules/antagonists/nukeop/clownop.dm
+++ b/code/modules/antagonists/nukeop/clownop.dm
@@ -3,6 +3,7 @@
name = "Clown Operative"
roundend_category = "clown operatives"
antagpanel_category = "ClownOp"
+ threat = 7
nukeop_outfit = /datum/outfit/syndicate/clownop
/datum/antagonist/nukeop/clownop/on_gain()
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index 4a63ba65bd..7510ad6997 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -4,6 +4,7 @@
antagpanel_category = "NukeOp"
job_rank = ROLE_OPERATIVE
antag_moodlet = /datum/mood_event/focused
+ threat = 10
var/datum/team/nuclear/nuke_team
var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint.
diff --git a/code/modules/antagonists/overthrow/overthrow.dm b/code/modules/antagonists/overthrow/overthrow.dm
index c8b253793e..0e8c4a35e0 100644
--- a/code/modules/antagonists/overthrow/overthrow.dm
+++ b/code/modules/antagonists/overthrow/overthrow.dm
@@ -10,6 +10,7 @@
roundend_category = "syndicate mutineers"
antagpanel_category = "Syndicate Mutineers"
job_rank = ROLE_TRAITOR // simply use the traitor preference & jobban settings
+ threat = 5
var/datum/team/overthrow/team
var/static/list/possible_useful_items
diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm
index ff33477909..01f3c6068e 100644
--- a/code/modules/antagonists/pirate/pirate.dm
+++ b/code/modules/antagonists/pirate/pirate.dm
@@ -3,6 +3,7 @@
job_rank = ROLE_TRAITOR
roundend_category = "space pirates"
antagpanel_category = "Pirate"
+ threat = 5
var/datum/team/pirate/crew
/datum/antagonist/pirate/greet()
@@ -104,4 +105,4 @@
else
parts += "The pirate crew has failed."
- return "
[parts.Join("
")]
"
\ No newline at end of file
+ return "[parts.Join("
")]
"
diff --git a/code/modules/antagonists/revenant/revenant_antag.dm b/code/modules/antagonists/revenant/revenant_antag.dm
index 8d99edf26e..46c1176533 100644
--- a/code/modules/antagonists/revenant/revenant_antag.dm
+++ b/code/modules/antagonists/revenant/revenant_antag.dm
@@ -2,6 +2,7 @@
name = "Revenant"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
+ threat = 5
/datum/antagonist/revenant/greet()
owner.announce_objectives()
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index c4190d8cd9..4f308cc4c0 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -7,6 +7,7 @@
antagpanel_category = "Revolution"
job_rank = ROLE_REV
antag_moodlet = /datum/mood_event/revolution
+ threat = 2
var/hud_type = "rev"
var/datum/team/revolution/rev_team
@@ -148,6 +149,7 @@
/datum/antagonist/rev/head
name = "Head Revolutionary"
hud_type = "rev_head"
+ threat = 8
var/remove_clumsy = FALSE
var/give_flash = FALSE
var/give_hud = TRUE
diff --git a/code/modules/antagonists/slaughter/slaughter_antag.dm b/code/modules/antagonists/slaughter/slaughter_antag.dm
index a018603cf1..04f7167fa5 100644
--- a/code/modules/antagonists/slaughter/slaughter_antag.dm
+++ b/code/modules/antagonists/slaughter/slaughter_antag.dm
@@ -3,6 +3,7 @@
show_name_in_check_antagonists = TRUE
var/objective_verb = "Kill"
var/datum/mind/summoner
+ threat = 10
job_rank = ROLE_ALIEN
show_in_antagpanel = FALSE
@@ -28,4 +29,4 @@
/datum/antagonist/slaughter/laughter
name = "Laughter demon"
- objective_verb = "Hug and Tickle"
\ No newline at end of file
+ objective_verb = "Hug and Tickle"
diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm
index 0b66e8cb23..04ad53f65b 100644
--- a/code/modules/antagonists/survivalist/survivalist.dm
+++ b/code/modules/antagonists/survivalist/survivalist.dm
@@ -3,6 +3,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
blacklisted_quirks = list(/datum/quirk/nonviolent) // mutes are allowed
+ threat = 1
var/greet_message = ""
/datum/antagonist/survivalist/proc/forge_objectives()
diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm
index 292fb10202..724c4e6f76 100644
--- a/code/modules/antagonists/swarmer/swarmer.dm
+++ b/code/modules/antagonists/swarmer/swarmer.dm
@@ -70,6 +70,7 @@
icon_living = "swarmer"
icon_dead = "swarmer_unactivated"
icon_gib = null
+ threat = 0.5
wander = 0
harm_intent_damage = 5
minbodytemp = 0
diff --git a/code/modules/antagonists/traitor/classes/ai.dm b/code/modules/antagonists/traitor/classes/ai.dm
index fac1efeb49..eaa1137d51 100644
--- a/code/modules/antagonists/traitor/classes/ai.dm
+++ b/code/modules/antagonists/traitor/classes/ai.dm
@@ -1,5 +1,6 @@
/datum/traitor_class/ai // this one is special, so has no weight
name = "Malfunctioning AI"
+ threat = 20
/datum/traitor_class/ai/forge_objectives(datum/antagonist/traitor/T)
var/objective_count = 0
diff --git a/code/modules/antagonists/traitor/classes/assassin.dm b/code/modules/antagonists/traitor/classes/assassin.dm
index 0c1d950f57..6b709aac59 100644
--- a/code/modules/antagonists/traitor/classes/assassin.dm
+++ b/code/modules/antagonists/traitor/classes/assassin.dm
@@ -3,7 +3,7 @@
employer = "Donk Corporation"
weight = 0
chaos = 1
- cost = 2
+ threat = 2
/datum/traitor_class/human/assassin/forge_single_objective(datum/antagonist/traitor/T)
.=1
diff --git a/code/modules/antagonists/traitor/classes/hijack.dm b/code/modules/antagonists/traitor/classes/hijack.dm
index e89eda1dcf..59e0591600 100644
--- a/code/modules/antagonists/traitor/classes/hijack.dm
+++ b/code/modules/antagonists/traitor/classes/hijack.dm
@@ -3,7 +3,7 @@
employer = "The Gorlex Marauders"
weight = 3
chaos = 5
- cost = 5
+ threat = 3
uplink_filters = list(/datum/uplink_item/stealthy_weapons/romerol_kit)
/datum/traitor_class/human/hijack/forge_objectives(datum/antagonist/traitor/T)
diff --git a/code/modules/antagonists/traitor/classes/martyr.dm b/code/modules/antagonists/traitor/classes/martyr.dm
index 78f8bf9b0c..72f9ac86fc 100644
--- a/code/modules/antagonists/traitor/classes/martyr.dm
+++ b/code/modules/antagonists/traitor/classes/martyr.dm
@@ -3,7 +3,7 @@
employer = "The Tiger Cooperative"
weight = 2
chaos = 5
- cost = 5
+ threat = 5
uplink_filters = list(/datum/uplink_item/stealthy_weapons/romerol_kit,/datum/uplink_item/bundles_TC/contract_kit)
/datum/traitor_class/human/martyr/forge_objectives(datum/antagonist/traitor/T)
diff --git a/code/modules/antagonists/traitor/classes/traitor_class.dm b/code/modules/antagonists/traitor/classes/traitor_class.dm
index 3df9dec929..012a269572 100644
--- a/code/modules/antagonists/traitor/classes/traitor_class.dm
+++ b/code/modules/antagonists/traitor/classes/traitor_class.dm
@@ -5,7 +5,7 @@ GLOBAL_LIST_EMPTY(traitor_classes)
var/employer = "The Syndicate"
var/weight = 0
var/chaos = 0
- var/cost = 0
+ var/threat = 0
var/TC = 20
var/list/uplink_filters
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index a9dee81a6c..d3aae4b75a 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -12,6 +12,7 @@
var/datum/traitor_class/traitor_kind
var/datum/contractor_hub/contractor_hub
hijack_speed = 0.5 //10 seconds per hijack stage by default
+ threat = 5
/datum/antagonist/traitor/New()
..()
@@ -21,11 +22,6 @@
/datum/antagonist/traitor/proc/set_traitor_kind(var/kind)
traitor_kind = GLOB.traitor_classes[kind]
- if(istype(SSticker.mode, /datum/game_mode/dynamic))
- var/datum/game_mode/dynamic/mode = SSticker.mode
- if(traitor_kind.cost)
- mode.spend_threat(traitor_kind.cost)
- mode.log_threat("[traitor_kind.cost] was spent due to [owner.name] being a [traitor_kind.name].")
/datum/antagonist/traitor/on_gain()
if(owner.current && isAI(owner.current))
@@ -38,7 +34,7 @@
var/list/weights = list()
for(var/C in GLOB.traitor_classes)
var/datum/traitor_class/class = GLOB.traitor_classes[C]
- var/weight = (1.5*class.weight)/(0.5+NUM_E**(-chaos_weight*class.chaos)) // just a logistic function
+ var/weight = LOGISTIC_FUNCTION(1.5*class.weight,chaos_weight,class.chaos,0)
weights[C] = weight
var/choice = pickweightAllowZero(weights)
if(!choice)
@@ -294,3 +290,6 @@
/datum/antagonist/traitor/is_gamemode_hero()
return SSticker.mode.name == "traitor"
+
+/datum/antagonist/traitor/threat()
+ return threat+traitor_kind.threat
diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm
index 15ec78692f..46b5edffd3 100644
--- a/code/modules/antagonists/wishgranter/wishgranter.dm
+++ b/code/modules/antagonists/wishgranter/wishgranter.dm
@@ -2,6 +2,7 @@
name = "Wishgranter Avatar"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
+ threat = 20
/datum/antagonist/wishgranter/proc/forge_objectives()
var/datum/objective/hijack/hijack = new
@@ -25,4 +26,4 @@
H.dna.add_mutation(HULK)
H.dna.add_mutation(XRAY)
H.dna.add_mutation(SPACEMUT)
- H.dna.add_mutation(TK)
\ No newline at end of file
+ H.dna.add_mutation(TK)
diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm
index 5ef3f24bd6..0e4e26e3f8 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook.dm
@@ -11,7 +11,6 @@
var/buy_word = "Learn"
var/limit //used to prevent a spellbook_entry from being bought more than X times with one wizard spellbook
var/list/no_coexistance_typecache //Used so you can't have specific spells together
- var/dynamic_cost = 0 // How much threat the spell costs to purchase for dynamic.
var/dynamic_requirement = 0 // How high the threat level needs to be for purchasing in dynamic.
/datum/spellbook_entry/New()
@@ -31,10 +30,6 @@
for(var/spell in user.mind.spell_list)
if(is_type_in_typecache(spell, no_coexistance_typecache))
return 0
- if(dynamic_cost>0 && istype(SSticker.mode,/datum/game_mode/dynamic))
- var/datum/game_mode/dynamic/mode = SSticker.mode
- if(mode.threat < dynamic_cost)
- return 0
return 1
/datum/spellbook_entry/proc/Buy(mob/living/carbon/human/user,obj/item/spellbook/book) //return 1 on success
@@ -70,10 +65,6 @@
SSblackbox.record_feedback("nested tally", "wizard_spell_improved", 1, list("[name]", "[aspell.spell_level]"))
return 1
//No same spell found - just learn it
- if(dynamic_cost > 0 && istype(SSticker.mode,/datum/game_mode/dynamic))
- var/datum/game_mode/dynamic/mode = SSticker.mode
- mode.spend_threat(dynamic_cost)
- mode.log_threat("Wizard spent [dynamic_cost] on [name].")
SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
user.mind.AddSpell(S)
to_chat(user, "You have learned [S.name].")
@@ -97,10 +88,6 @@
if(!S)
S = new spell_type()
var/spell_levels = 0
- if(dynamic_cost > 0 && istype(SSticker.mode,/datum/game_mode/dynamic))
- var/datum/game_mode/dynamic/mode = SSticker.mode
- mode.refund_threat(dynamic_cost)
- mode.log_threat("Wizard refunded [dynamic_cost] on [name].")
for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list)
if(initial(S.name) == initial(aspell.name))
spell_levels = aspell.spell_level
@@ -308,7 +295,6 @@
desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself."
item_path = /obj/item/gun/magic/staff/change
dynamic_requirement = 60
- dynamic_cost = 20
/datum/spellbook_entry/item/staffanimation
name = "Staff of Animation"
@@ -376,7 +362,6 @@
item_path = /obj/item/storage/belt/wands/full
category = "Defensive"
dynamic_requirement = 60
- dynamic_cost = 10
/datum/spellbook_entry/item/armor
name = "Mastercrafted Armor Set"
@@ -396,7 +381,6 @@
item_path = /obj/item/antag_spawner/contract
category = "Assistance"
dynamic_requirement = 50
- dynamic_cost = 10
/datum/spellbook_entry/item/plasmafist
name = "Plasma Fist"
@@ -424,10 +408,6 @@
category = "Assistance"
dynamic_requirement = 60
-/datum/spellbook_entry/item/bloodbottle/New()
- ..()
- dynamic_cost = CONFIG_GET(keyed_list/dynamic_cost)["slaughter_demon"]
-
/datum/spellbook_entry/item/hugbottle
name = "Bottle of Tickles"
desc = "A bottle of magically infused fun, the smell of which will \
@@ -443,10 +423,6 @@
category = "Assistance"
dynamic_requirement = 40
-/datum/spellbook_entry/item/hugbottle/New()
- ..()
- dynamic_cost = CONFIG_GET(keyed_list/dynamic_cost)["slaughter_demon"]/3
-
/datum/spellbook_entry/item/mjolnir
name = "Mjolnir"
desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power."
@@ -522,7 +498,6 @@
/datum/spellbook_entry/summon/guns
name = "Summon Guns"
desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. Just be careful not to stand still too long!"
- dynamic_cost = 10
dynamic_requirement = 60
/datum/spellbook_entry/summon/guns/IsAvailible()
@@ -536,17 +511,11 @@
active = 1
playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, 1)
to_chat(user, "You have cast summon guns!")
- if(istype(SSticker.mode,/datum/game_mode/dynamic))
- var/datum/game_mode/dynamic/mode = SSticker.mode
- var/threat_spent = dynamic_cost
- mode.spend_threat(threat_spent)
- mode.log_threat("Wizard spent [threat_spent] on summon guns.")
return 1
/datum/spellbook_entry/summon/magic
name = "Summon Magic"
desc = "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time."
- dynamic_cost = 10
dynamic_requirement = 60
/datum/spellbook_entry/summon/magic/IsAvailible()
@@ -560,17 +529,11 @@
active = 1
playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, 1)
to_chat(user, "You have cast summon magic!")
- if(istype(SSticker.mode,/datum/game_mode/dynamic))
- var/datum/game_mode/dynamic/mode = SSticker.mode
- var/threat_spent = dynamic_cost
- mode.spend_threat(threat_spent)
- mode.log_threat("Wizard spent [threat_spent] on summon magic.")
return 1
/datum/spellbook_entry/summon/events
name = "Summon Events"
desc = "Give Murphy's law a little push and replace all events with special wizard ones that will confound and confuse everyone. Multiple castings increase the rate of these events."
- dynamic_cost = 20
dynamic_requirement = 60
var/times = 0
@@ -582,11 +545,6 @@
/datum/spellbook_entry/summon/events/Buy(mob/living/carbon/human/user,obj/item/spellbook/book)
SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
summonevents()
- if(istype(SSticker.mode,/datum/game_mode/dynamic) && times == 0)
- var/datum/game_mode/dynamic/mode = SSticker.mode
- var/threat_spent = dynamic_cost
- mode.spend_threat(threat_spent)
- mode.log_threat("Wizard spent [threat_spent] on summon events.")
times++
playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, 1)
to_chat(user, "You have cast summon events.")
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 234bb3d1a1..2d32f8f00a 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -4,6 +4,7 @@
antagpanel_category = "Wizard"
job_rank = ROLE_WIZARD
antag_moodlet = /datum/mood_event/focused
+ threat = 30
var/give_objectives = TRUE
var/strip = TRUE //strip before equipping
var/allow_rename = TRUE
diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm
index b8f9927bc4..7c4c5351df 100644
--- a/code/modules/antagonists/xeno/xeno.dm
+++ b/code/modules/antagonists/xeno/xeno.dm
@@ -13,6 +13,7 @@
job_rank = ROLE_ALIEN
show_in_antagpanel = FALSE
var/datum/team/xeno/xeno_team
+ threat = 3
/datum/antagonist/xeno/create_team(datum/team/xeno/new_team)
if(!new_team)
@@ -34,4 +35,4 @@
/mob/living/carbon/alien/mind_initialize()
..()
if(!mind.has_antag_datum(/datum/antagonist/xeno))
- mind.add_antag_datum(/datum/antagonist/xeno)
\ No newline at end of file
+ mind.add_antag_datum(/datum/antagonist/xeno)
diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm
index e2125964e4..a8ab470d5d 100644
--- a/code/modules/events/_event.dm
+++ b/code/modules/events/_event.dm
@@ -121,7 +121,8 @@
var/activeFor = 0 //How long the event has existed. You don't need to change this.
var/current_players = 0 //Amount of of alive, non-AFK human players on server at the time of event start
- var/fakeable = TRUE //Can be faked by fake news event.
+ var/threat = 0
+ var/fakeable = TRUE //Can be faked by fake news event.
//Called first before processing.
//Allows you to setup your event, such as randomly
@@ -161,7 +162,9 @@
/datum/round_event/proc/end()
return
-
+// Returns threat; used for dynamic. Used for custom stuff, just returns the threat var by default.
+/datum/round_event/proc/threat()
+ return threat
//Do not override this proc, instead use the appropiate procs.
//This proc will handle the calls to the appropiate procs.
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index 75114af902..e61af1368d 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -27,6 +27,7 @@
"How do I vore people?",
"ERP?",
"Not epic bros...")
+ threat = 5
/datum/round_event/brand_intelligence/announce(fake)
diff --git a/code/modules/events/meteor_wave.dm b/code/modules/events/meteor_wave.dm
index e24ce13034..7763f9950d 100644
--- a/code/modules/events/meteor_wave.dm
+++ b/code/modules/events/meteor_wave.dm
@@ -16,6 +16,7 @@
startWhen = 6
endWhen = 66
announceWhen = 1
+ threat = 15
var/list/wave_type
var/wave_name = "normal"
var/direction
@@ -88,8 +89,10 @@
max_occurrences = 3
earliest_start = 35 MINUTES
+
/datum/round_event/meteor_wave/threatening
wave_name = "threatening"
+ threat = 25
/datum/round_event_control/meteor_wave/catastrophic
name = "Meteor Wave: Catastrophic"
@@ -101,6 +104,7 @@
/datum/round_event/meteor_wave/catastrophic
wave_name = "catastrophic"
+ threat = 35
#undef SINGULO_BEACON_DISTURBANCE
#undef SINGULO_BEACON_MAX_DISTURBANCE
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index 47cde0899b..86ff1ed0da 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -15,7 +15,7 @@
/datum/round_event/pirates
startWhen = 60 //2 minutes to answer
- var/datum/comm_message/threat
+ var/datum/comm_message/threat_message
var/payoff = 0
var/paid_off = FALSE
var/ship_name = "Space Privateers Association"
@@ -28,16 +28,16 @@
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", "commandreport") // CITADEL EDIT metabreak
if(fake)
return
- threat = new
+ threat_message = new
payoff = round(SSshuttle.points * 0.80)
- threat.title = "Business proposition"
- threat.content = "This is [ship_name]. Pay up [payoff] credits or you'll walk the plank."
- threat.possible_answers = list("We'll pay.","No way.")
- threat.answer_callback = CALLBACK(src,.proc/answered)
- SScommunications.send_message(threat,unique = TRUE)
+ threat_message.title = "Business proposition"
+ threat_message.content = "This is [ship_name]. Pay up [payoff] credits or you'll walk the plank."
+ threat_message.possible_answers = list("We'll pay.","No way.")
+ threat_message.answer_callback = CALLBACK(src,.proc/answered)
+ SScommunications.send_message(threat_message,unique = TRUE)
/datum/round_event/pirates/proc/answered()
- if(threat && threat.answered == 1)
+ if(threat_message && threat_message.answered == 1)
if(SSshuttle.points >= payoff)
SSshuttle.points -= payoff
priority_announce("Thanks for the credits, landlubbers.",sender_override = ship_name)
diff --git a/code/modules/events/wizard/magicarp.dm b/code/modules/events/wizard/magicarp.dm
index 57e2a2a051..4d2e8e624c 100644
--- a/code/modules/events/wizard/magicarp.dm
+++ b/code/modules/events/wizard/magicarp.dm
@@ -30,6 +30,7 @@
icon_dead = "magicarp_dead"
icon_gib = "magicarp_gib"
ranged = 1
+ threat = 4
retreat_distance = 2
minimum_distance = 0 //Between shots they can and will close in to nash
projectiletype = /obj/item/projectile/magic
@@ -51,6 +52,7 @@
color = "#00FFFF"
maxHealth = 75
health = 75
+ threat = 7
/mob/living/simple_animal/hostile/carp/ranged/chaos/Shoot()
projectiletype = pick(allowed_projectile_types)
diff --git a/code/modules/events/wizard/race.dm b/code/modules/events/wizard/race.dm
index 2aeb200c88..5c3b8432c1 100644
--- a/code/modules/events/wizard/race.dm
+++ b/code/modules/events/wizard/race.dm
@@ -10,6 +10,7 @@
var/list/stored_name
var/list/stored_species
var/list/stored_dna
+ threat = 10
/datum/round_event/wizard/race/setup()
stored_name = list()
@@ -56,4 +57,4 @@
H.set_species(stored_species[H])
H.real_name = stored_name[H]
H.dna.unique_enzymes = stored_dna[H]
- to_chat(H, "You feel back to your normal self again.")
\ No newline at end of file
+ to_chat(H, "You feel back to your normal self again.")
diff --git a/code/modules/holiday/halloween/halloween.dm b/code/modules/holiday/halloween/halloween.dm
index d3b9b92b4f..6c9e527f38 100644
--- a/code/modules/holiday/halloween/halloween.dm
+++ b/code/modules/holiday/halloween/halloween.dm
@@ -190,6 +190,7 @@
icon_dead = "scary_clown"
icon_gib = "scary_clown"
speak = list("...", ". . .")
+ threat = 3
maxHealth = 120
health = 120
emote_see = list("silently stares")
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index 507c8a5026..ee441c21f7 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -63,6 +63,8 @@
//If a job complies with dresscodes, loadout items will not be equipped instead of the job's outfit, instead placing the items into the player's backpack.
var/dresscodecompliant = TRUE
+ // How much threat this job is worth in dynamic. Is subtracted if the player's not an antag, added if they are.
+ var/threat = 0
//Only override this proc
//H is usually a human unless an /equip override transformed it
@@ -88,6 +90,11 @@
if(. == null)
return antag_rep
+/datum/job/proc/GetThreat()
+ . = CONFIG_GET(keyed_list/job_threat)[lowertext(title)]
+ if(. == null)
+ return threat
+
//Don't override this unless the job transforms into a non-human (Silicons do this for example)
/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null, client/preference_source)
if(!H)
diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm
index 4bcfab5836..efe574ab66 100644
--- a/code/modules/jobs/job_types/ai.dm
+++ b/code/modules/jobs/job_types/ai.dm
@@ -15,6 +15,7 @@
exp_type_department = EXP_TYPE_SILICON
display_order = JOB_DISPLAY_ORDER_AI
var/do_special_check = TRUE
+ threat = 5
/datum/job/ai/equip(mob/living/carbon/human/H, visualsOnly, announce, latejoin, datum/outfit/outfit_override, client/preference_source = null)
if(visualsOnly)
diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm
index eec6e77578..c4fa213b61 100644
--- a/code/modules/jobs/job_types/assistant.dm
+++ b/code/modules/jobs/job_types/assistant.dm
@@ -16,6 +16,7 @@ Assistant
antag_rep = 7
display_order = JOB_DISPLAY_ORDER_ASSISTANT
dresscodecompliant = FALSE
+ threat = 0.2
/datum/job/assistant/get_access()
if(CONFIG_GET(flag/assistants_have_maint_access) || !CONFIG_GET(flag/jobs_have_minimal_access)) //Config has assistant maint access set
diff --git a/code/modules/jobs/job_types/atmospheric_technician.dm b/code/modules/jobs/job_types/atmospheric_technician.dm
index 97d73db5ce..019e50799e 100644
--- a/code/modules/jobs/job_types/atmospheric_technician.dm
+++ b/code/modules/jobs/job_types/atmospheric_technician.dm
@@ -18,6 +18,7 @@
minimal_access = list(ACCESS_ATMOSPHERICS, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ENGINE,
ACCESS_ENGINE_EQUIP, ACCESS_EMERGENCY_STORAGE, ACCESS_CONSTRUCTION, ACCESS_MINERAL_STOREROOM)
display_order = JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN
+ threat = 0.5
/datum/outfit/job/atmos
name = "Atmospheric Technician"
diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm
index f542e96d50..709c53d51d 100644
--- a/code/modules/jobs/job_types/bartender.dm
+++ b/code/modules/jobs/job_types/bartender.dm
@@ -15,6 +15,7 @@
access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_BAR, ACCESS_MINERAL_STOREROOM)
display_order = JOB_DISPLAY_ORDER_BARTENDER
+ threat = 0.5
/datum/outfit/job/bartender
name = "Bartender"
diff --git a/code/modules/jobs/job_types/botanist.dm b/code/modules/jobs/job_types/botanist.dm
index 916783fe60..4cf106ea99 100644
--- a/code/modules/jobs/job_types/botanist.dm
+++ b/code/modules/jobs/job_types/botanist.dm
@@ -14,6 +14,7 @@
access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_HYDROPONICS, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM)
display_order = JOB_DISPLAY_ORDER_BOTANIST
+ threat = 1.5 // lol powergame
/datum/outfit/job/botanist
name = "Botanist"
diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm
index 40bfa0bbfa..d38a2a3ddf 100644
--- a/code/modules/jobs/job_types/captain.dm
+++ b/code/modules/jobs/job_types/captain.dm
@@ -25,6 +25,7 @@
display_order = JOB_DISPLAY_ORDER_CAPTAIN
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity)
+ threat = 5
/datum/job/captain/get_access()
return get_all_accesses()
@@ -62,4 +63,4 @@
mask = /obj/item/clothing/mask/gas/sechailer
suit = /obj/item/clothing/suit/space/hardsuit/captain
- suit_store = /obj/item/tank/internals/oxygen
\ No newline at end of file
+ suit_store = /obj/item/tank/internals/oxygen
diff --git a/code/modules/jobs/job_types/cargo_technician.dm b/code/modules/jobs/job_types/cargo_technician.dm
index 74b06adff7..d574482444 100644
--- a/code/modules/jobs/job_types/cargo_technician.dm
+++ b/code/modules/jobs/job_types/cargo_technician.dm
@@ -16,6 +16,7 @@
minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM)
display_order = JOB_DISPLAY_ORDER_CARGO_TECHNICIAN
+ threat = 0.2
/datum/outfit/job/cargo_tech
name = "Cargo Technician"
diff --git a/code/modules/jobs/job_types/chaplain.dm b/code/modules/jobs/job_types/chaplain.dm
index 70d8af317e..4aed218cf9 100644
--- a/code/modules/jobs/job_types/chaplain.dm
+++ b/code/modules/jobs/job_types/chaplain.dm
@@ -15,6 +15,7 @@
minimal_access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE)
display_order = JOB_DISPLAY_ORDER_CHAPLAIN
+ threat = 0.5
/datum/job/chaplain/after_spawn(mob/living/H, mob/M)
diff --git a/code/modules/jobs/job_types/chemist.dm b/code/modules/jobs/job_types/chemist.dm
index 66167a3827..44b0e90ec8 100644
--- a/code/modules/jobs/job_types/chemist.dm
+++ b/code/modules/jobs/job_types/chemist.dm
@@ -17,6 +17,7 @@
minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_MINERAL_STOREROOM)
display_order = JOB_DISPLAY_ORDER_CHEMIST
+ threat = 1.5
/datum/outfit/job/chemist
name = "Chemist"
diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm
index d031653a31..c5065cd169 100644
--- a/code/modules/jobs/job_types/chief_engineer.dm
+++ b/code/modules/jobs/job_types/chief_engineer.dm
@@ -29,6 +29,7 @@
display_order = JOB_DISPLAY_ORDER_CHIEF_ENGINEER
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/paraplegic, /datum/quirk/insanity)
+ threat = 2
/datum/outfit/job/ce
name = "Chief Engineer"
diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm
index 4a80975bb9..8dd7fafab3 100644
--- a/code/modules/jobs/job_types/chief_medical_officer.dm
+++ b/code/modules/jobs/job_types/chief_medical_officer.dm
@@ -27,6 +27,7 @@
display_order = JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity)
+ threat = 2
/datum/outfit/job/cmo
name = "Chief Medical Officer"
diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm
index a18aee474c..c65c062bce 100644
--- a/code/modules/jobs/job_types/clown.dm
+++ b/code/modules/jobs/job_types/clown.dm
@@ -17,6 +17,7 @@
mind_traits = list(TRAIT_CLOWN_MENTALITY)
display_order = JOB_DISPLAY_ORDER_CLOWN
+ threat = 0 // honk
/datum/outfit/job/clown
name = "Clown"
diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm
index b41c0d8505..20969bf1d9 100644
--- a/code/modules/jobs/job_types/cook.dm
+++ b/code/modules/jobs/job_types/cook.dm
@@ -16,6 +16,7 @@
minimal_access = list(ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM)
display_order = JOB_DISPLAY_ORDER_COOK
+ threat = 0.2
/datum/outfit/job/cook
name = "Cook"
diff --git a/code/modules/jobs/job_types/curator.dm b/code/modules/jobs/job_types/curator.dm
index 9e0a91e6aa..d73d909f4f 100644
--- a/code/modules/jobs/job_types/curator.dm
+++ b/code/modules/jobs/job_types/curator.dm
@@ -15,6 +15,7 @@
minimal_access = list(ACCESS_LIBRARY, ACCESS_CONSTRUCTION, ACCESS_MINING_STATION)
display_order = JOB_DISPLAY_ORDER_CURATOR
+ threat = 0.3
/datum/outfit/job/curator
name = "Curator"
diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm
index 0c586d47a6..55c13df7a2 100644
--- a/code/modules/jobs/job_types/detective.dm
+++ b/code/modules/jobs/job_types/detective.dm
@@ -22,6 +22,7 @@
display_order = JOB_DISPLAY_ORDER_DETECTIVE
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/nonviolent, /datum/quirk/paraplegic)
+ threat = 1
/datum/outfit/job/detective
name = "Detective"
diff --git a/code/modules/jobs/job_types/geneticist.dm b/code/modules/jobs/job_types/geneticist.dm
index 35586c0bc8..2e8b18d772 100644
--- a/code/modules/jobs/job_types/geneticist.dm
+++ b/code/modules/jobs/job_types/geneticist.dm
@@ -17,6 +17,7 @@
minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM)
display_order = JOB_DISPLAY_ORDER_GENETICIST
+ threat = 1.5
/datum/outfit/job/geneticist
name = "Geneticist"
diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm
index cf8310504d..b1fe3471f1 100644
--- a/code/modules/jobs/job_types/head_of_personnel.dm
+++ b/code/modules/jobs/job_types/head_of_personnel.dm
@@ -34,6 +34,8 @@
display_order = JOB_DISPLAY_ORDER_HEAD_OF_PERSONNEL
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/prosopagnosia, /datum/quirk/insanity)
+ threat = 2
+
/datum/outfit/job/hop
name = "Head of Personnel"
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index 926eb0b068..811dff87e7 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -30,6 +30,7 @@
display_order = JOB_DISPLAY_ORDER_HEAD_OF_SECURITY
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/nonviolent, /datum/quirk/paraplegic, /datum/quirk/insanity)
+ threat = 3
/datum/outfit/job/hos
name = "Head of Security"
diff --git a/code/modules/jobs/job_types/janitor.dm b/code/modules/jobs/job_types/janitor.dm
index bc3d2b8abd..73028255be 100644
--- a/code/modules/jobs/job_types/janitor.dm
+++ b/code/modules/jobs/job_types/janitor.dm
@@ -15,6 +15,7 @@
minimal_access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM)
display_order = JOB_DISPLAY_ORDER_JANITOR
+ threat = 0.2
/datum/outfit/job/janitor
name = "Janitor"
diff --git a/code/modules/jobs/job_types/lawyer.dm b/code/modules/jobs/job_types/lawyer.dm
index 7bf9c39d45..3f80e44492 100644
--- a/code/modules/jobs/job_types/lawyer.dm
+++ b/code/modules/jobs/job_types/lawyer.dm
@@ -18,6 +18,7 @@
mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM)
display_order = JOB_DISPLAY_ORDER_LAWYER
+ threat = 0.3
/datum/outfit/job/lawyer
name = "Lawyer"
diff --git a/code/modules/jobs/job_types/medical_doctor.dm b/code/modules/jobs/job_types/medical_doctor.dm
index 605727cf80..3eaa2ae10a 100644
--- a/code/modules/jobs/job_types/medical_doctor.dm
+++ b/code/modules/jobs/job_types/medical_doctor.dm
@@ -15,6 +15,7 @@
minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CLONING, ACCESS_MINERAL_STOREROOM)
display_order = JOB_DISPLAY_ORDER_MEDICAL_DOCTOR
+ threat = 0.5
/datum/outfit/job/doctor
name = "Medical Doctor"
diff --git a/code/modules/jobs/job_types/mime.dm b/code/modules/jobs/job_types/mime.dm
index f38b4edf52..72a6ac8e13 100644
--- a/code/modules/jobs/job_types/mime.dm
+++ b/code/modules/jobs/job_types/mime.dm
@@ -16,6 +16,8 @@
display_order = JOB_DISPLAY_ORDER_MIME
+ threat = 0
+
/datum/job/mime/after_spawn(mob/living/carbon/human/H, mob/M)
. = ..()
H.apply_pref_name("mime", M.client)
diff --git a/code/modules/jobs/job_types/paramedic.dm b/code/modules/jobs/job_types/paramedic.dm
index b74f1a1c18..df6e1431ae 100644
--- a/code/modules/jobs/job_types/paramedic.dm
+++ b/code/modules/jobs/job_types/paramedic.dm
@@ -17,6 +17,8 @@
display_order = JOB_DISPLAY_ORDER_PARAMEDIC
+ threat = 0.5
+
/datum/outfit/job/paramedic
name = "Paramedic"
jobtype = /datum/job/paramedic
diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm
index 04460936af..897defb0ea 100644
--- a/code/modules/jobs/job_types/quartermaster.dm
+++ b/code/modules/jobs/job_types/quartermaster.dm
@@ -27,6 +27,7 @@
display_order = JOB_DISPLAY_ORDER_QUARTERMASTER
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity)
+ threat = 0.5
/datum/outfit/job/quartermaster
name = "Quartermaster"
diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm
index 9cc2f4b8bf..15e5c64654 100644
--- a/code/modules/jobs/job_types/research_director.dm
+++ b/code/modules/jobs/job_types/research_director.dm
@@ -31,6 +31,7 @@
display_order = JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity)
+ threat = 5
/datum/outfit/job/rd
name = "Research Director"
diff --git a/code/modules/jobs/job_types/roboticist.dm b/code/modules/jobs/job_types/roboticist.dm
index 345b1ac3d3..f9f1d20d3b 100644
--- a/code/modules/jobs/job_types/roboticist.dm
+++ b/code/modules/jobs/job_types/roboticist.dm
@@ -17,6 +17,7 @@
minimal_access = list(ACCESS_ROBOTICS, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM)
display_order = JOB_DISPLAY_ORDER_ROBOTICIST
+ threat = 1
/datum/outfit/job/roboticist
name = "Roboticist"
diff --git a/code/modules/jobs/job_types/scientist.dm b/code/modules/jobs/job_types/scientist.dm
index ce9a628ae0..9809e7b4dc 100644
--- a/code/modules/jobs/job_types/scientist.dm
+++ b/code/modules/jobs/job_types/scientist.dm
@@ -17,6 +17,7 @@
minimal_access = list(ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_MINERAL_STOREROOM)
display_order = JOB_DISPLAY_ORDER_SCIENTIST
+ threat = 1.2
/datum/outfit/job/scientist
name = "Scientist"
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index 92cc15673f..decb334423 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -22,6 +22,7 @@
display_order = JOB_DISPLAY_ORDER_SECURITY_OFFICER
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/nonviolent, /datum/quirk/paraplegic)
+ threat = 2
/datum/job/officer/get_access()
var/list/L = list()
diff --git a/code/modules/jobs/job_types/shaft_miner.dm b/code/modules/jobs/job_types/shaft_miner.dm
index 4ba1fc9ac1..9eb268895e 100644
--- a/code/modules/jobs/job_types/shaft_miner.dm
+++ b/code/modules/jobs/job_types/shaft_miner.dm
@@ -19,6 +19,8 @@
display_order = JOB_DISPLAY_ORDER_SHAFT_MINER
+ threat = 1.5
+
/datum/outfit/job/miner
name = "Shaft Miner (Lavaland)"
jobtype = /datum/job/mining
diff --git a/code/modules/jobs/job_types/station_engineer.dm b/code/modules/jobs/job_types/station_engineer.dm
index 144693aba1..66ddc90e9b 100644
--- a/code/modules/jobs/job_types/station_engineer.dm
+++ b/code/modules/jobs/job_types/station_engineer.dm
@@ -20,6 +20,8 @@
display_order = JOB_DISPLAY_ORDER_STATION_ENGINEER
+ threat = 1
+
/datum/outfit/job/engineer
name = "Station Engineer"
jobtype = /datum/job/engineer
diff --git a/code/modules/jobs/job_types/virologist.dm b/code/modules/jobs/job_types/virologist.dm
index c5a9c9e9fb..4ba65dff59 100644
--- a/code/modules/jobs/job_types/virologist.dm
+++ b/code/modules/jobs/job_types/virologist.dm
@@ -18,6 +18,8 @@
display_order = JOB_DISPLAY_ORDER_VIROLOGIST
+ threat = 1.5
+
/datum/outfit/job/virologist
name = "Virologist"
jobtype = /datum/job/virologist
diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm
index 8ddcf1ad91..15d839ac48 100644
--- a/code/modules/jobs/job_types/warden.dm
+++ b/code/modules/jobs/job_types/warden.dm
@@ -22,6 +22,7 @@
display_order = JOB_DISPLAY_ORDER_WARDEN
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/nonviolent, /datum/quirk/paraplegic)
+ threat = 2
/datum/job/warden/get_access()
var/list/L = list()
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 708c9ea2cd..a6366c03fe 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -8,6 +8,7 @@
response_help = "thinks better of touching"
response_disarm = "flails at"
response_harm = "punches"
+ threat = 1
speak_chance = 1
icon = 'icons/mob/mob.dmi'
speed = 0
@@ -118,6 +119,7 @@
desc = "A massive, armored construct built to spearhead attacks and soak up enemy fire."
icon_state = "behemoth"
icon_living = "behemoth"
+ threat = 3
maxHealth = 150
health = 150
response_harm = "harmlessly punches"
@@ -180,6 +182,7 @@
desc = "A wicked, clawed shell constructed to assassinate enemies and sow chaos behind enemy lines."
icon_state = "floating"
icon_living = "floating"
+ threat = 3
maxHealth = 65
health = 65
melee_damage_lower = 20
diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm
index ff42512666..f7ebde4758 100644
--- a/code/modules/mob/living/simple_animal/guardian/guardian.dm
+++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm
@@ -8,6 +8,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
name = "Guardian Spirit"
real_name = "Guardian Spirit"
desc = "A mysterious being that stands by its charge, ever vigilant."
+ threat = 5
speak_emote = list("hisses")
gender = NEUTER
mob_biotypes = NONE
diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm
index bb51358192..3b9700bb58 100644
--- a/code/modules/mob/living/simple_animal/hostile/alien.dm
+++ b/code/modules/mob/living/simple_animal/hostile/alien.dm
@@ -7,6 +7,7 @@
icon_dead = "alienh_dead"
icon_gib = "syndicate_gib"
gender = FEMALE
+ threat = 1
response_help = "pokes"
response_disarm = "shoves"
response_harm = "hits"
@@ -62,6 +63,7 @@
icon_state = "aliens"
icon_living = "aliens"
icon_dead = "aliens_dead"
+ threat = 3
health = 150
maxHealth = 150
melee_damage_lower = 15
@@ -78,6 +80,7 @@
icon_state = "alienq"
icon_living = "alienq"
icon_dead = "alienq_dead"
+ threat = 8
health = 250
maxHealth = 250
melee_damage_lower = 15
@@ -157,6 +160,7 @@
name = "lusty xenomorph maid"
melee_damage_lower = 0
melee_damage_upper = 0
+ threat = -1
a_intent = INTENT_HELP
friendly = "caresses"
obj_damage = 0
diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm
index dca7bc7006..a77098cb80 100644
--- a/code/modules/mob/living/simple_animal/hostile/bear.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bear.dm
@@ -2,6 +2,7 @@
/mob/living/simple_animal/hostile/bear
name = "space bear"
desc = "You don't need to be faster than a space bear, you just need to outrun your crewmates."
+ threat = 1
icon_state = "bear"
icon_living = "bear"
icon_dead = "bear_dead"
diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm
index 524c220fa4..ce7ab92e0c 100644
--- a/code/modules/mob/living/simple_animal/hostile/bees.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bees.dm
@@ -16,6 +16,7 @@
icon_state = ""
icon_living = ""
icon = 'icons/mob/bees.dmi'
+ threat = 0.3
gender = FEMALE
speak_emote = list("buzzes")
emote_hear = list("buzzes")
diff --git a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm
index be88394692..dbb957a96b 100644
--- a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm
@@ -1,6 +1,7 @@
/mob/living/simple_animal/hostile/boss
name = "A Perfectly Generic Boss Placeholder"
desc = ""
+ threat = 10
robust_searching = 1
stat_attack = UNCONSCIOUS
status_flags = 0
@@ -134,4 +135,4 @@
/datum/boss_active_timed_battle/Destroy()
abilities = null
SSobj.processing.Remove(src)
- return ..()
\ No newline at end of file
+ return ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm
index 36968a1ab0..9c89ff4a4f 100644
--- a/code/modules/mob/living/simple_animal/hostile/carp.dm
+++ b/code/modules/mob/living/simple_animal/hostile/carp.dm
@@ -7,6 +7,7 @@
icon_living = "carp"
icon_dead = "carp_dead"
icon_gib = "carp_gib"
+ threat = 0.2
mob_biotypes = MOB_ORGANIC|MOB_BEAST
speak_chance = 0
turns_per_move = 5
@@ -70,7 +71,7 @@
icon_living = "megacarp"
icon_dead = "megacarp_dead"
icon_gib = "megacarp_gib"
-
+ threat = 3
regen_amount = 6
maxHealth = 30
@@ -94,7 +95,7 @@
name = "Cayenne"
desc = "A failed Syndicate experiment in weaponized space carp technology, it now serves as a lovable mascot."
gender = FEMALE
-
+ threat = 5
regen_amount = 8
speak_emote = list("squeaks")
diff --git a/code/modules/mob/living/simple_animal/hostile/dark_wizard.dm b/code/modules/mob/living/simple_animal/hostile/dark_wizard.dm
index aec136af7d..6ca6eac0cc 100644
--- a/code/modules/mob/living/simple_animal/hostile/dark_wizard.dm
+++ b/code/modules/mob/living/simple_animal/hostile/dark_wizard.dm
@@ -1,6 +1,7 @@
/mob/living/simple_animal/hostile/dark_wizard
name = "Dark Wizard"
desc = "Killing amateurs since the dawn of times."
+ threat = 3
icon = 'icons/mob/simple_human.dmi'
icon_state = "dark_wizard"
icon_living = "dark_wizard"
@@ -36,4 +37,4 @@
icon_state = "declone"
damage = 4
damage_type = BURN
- flag = "energy"
\ No newline at end of file
+ flag = "energy"
diff --git a/code/modules/mob/living/simple_animal/hostile/eyeballs.dm b/code/modules/mob/living/simple_animal/hostile/eyeballs.dm
index 2d438dbf1c..dbb9048ca4 100644
--- a/code/modules/mob/living/simple_animal/hostile/eyeballs.dm
+++ b/code/modules/mob/living/simple_animal/hostile/eyeballs.dm
@@ -26,4 +26,4 @@
movement_type = FLYING
faction = list("spooky")
- del_on_death = 1
\ No newline at end of file
+ del_on_death = 1
diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm
index 69956c5d0d..7ac0ea2b44 100644
--- a/code/modules/mob/living/simple_animal/hostile/faithless.dm
+++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm
@@ -4,6 +4,7 @@
icon_state = "faithless"
icon_living = "faithless"
icon_dead = "faithless_dead"
+ threat = 1
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
gender = MALE
speak_chance = 0
diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
index cc7b1e19d1..3c3612f434 100644
--- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
+++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
@@ -17,6 +17,7 @@
//basic spider mob, these generally guard nests
/mob/living/simple_animal/hostile/poison/giant_spider
+ threat = 1
name = "giant spider"
desc = "Furry and black, it makes you shudder to look at it. This one has deep red eyes."
icon_state = "guard"
diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
index 9b5b428f44..7c6f9e9c7b 100644
--- a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
+++ b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
@@ -9,6 +9,7 @@
icon_state = "crawling"
icon_living = "crawling"
icon_dead = "dead"
+ threat = 0.5
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
speak_chance = 80
maxHealth = 220
@@ -114,4 +115,4 @@
unique_name = FALSE
AIStatus = AI_OFF
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- minbodytemp = 0
\ No newline at end of file
+ minbodytemp = 0
diff --git a/code/modules/mob/living/simple_animal/hostile/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm
index cc225f4bac..9d64b76198 100644
--- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm
+++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm
@@ -6,6 +6,7 @@
icon_state = "headcrab"
icon_living = "headcrab"
icon_dead = "headcrab_dead"
+ threat = 1
gender = NEUTER
health = 50
maxHealth = 50
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index bd4f334476..e496d007a2 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -3,6 +3,7 @@
stop_automated_movement_when_pulled = 0
obj_damage = 40
environment_smash = ENVIRONMENT_SMASH_STRUCTURES //Bitflags. Set to ENVIRONMENT_SMASH_STRUCTURES to break closets,tables,racks, etc; ENVIRONMENT_SMASH_WALLS for walls; ENVIRONMENT_SMASH_RWALLS for rwalls
+ var/threat = 0 // for dynamic
var/atom/target
var/ranged = FALSE
var/rapid = 0 //How many shots per volley.
@@ -594,3 +595,6 @@ mob/living/simple_animal/hostile/proc/DestroySurroundings() // for use with mega
. += M
else if (M.loc.type in hostile_machines)
. += M.loc
+
+/mob/living/simple_animal/hostile/proc/threat()
+ return threat
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
index e558982fbb..05c4007b6f 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
@@ -11,6 +11,7 @@
icon_living = "leaper"
icon_dead = "leaper_dead"
mob_biotypes = MOB_ORGANIC|MOB_BEAST
+ threat = 2
maxHealth = 300
health = 300
ranged = TRUE
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm b/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm
index 91e17e8e57..ae161d596d 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm
@@ -8,6 +8,7 @@
icon_living = "arachnid"
icon_dead = "arachnid_dead"
mob_biotypes = MOB_ORGANIC|MOB_BUG
+ threat = 2
melee_damage_lower = 30
melee_damage_upper = 30
maxHealth = 300
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm b/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
index b2e6fa9704..c05d14965e 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
@@ -14,6 +14,7 @@
icon_living = "mook"
icon_dead = "mook_dead"
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
+ threat = 0.5
pixel_x = -16
maxHealth = 45
health = 45
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
index 7565a686bf..0521afa9e9 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
@@ -13,6 +13,7 @@
icon_state = "seedling"
icon_living = "seedling"
icon_dead = "seedling_dead"
+ threat = 0.5
maxHealth = 100
health = 100
melee_damage_lower = 30
diff --git a/code/modules/mob/living/simple_animal/hostile/killertomato.dm b/code/modules/mob/living/simple_animal/hostile/killertomato.dm
index 17658cc7c1..e418cc4ab0 100644
--- a/code/modules/mob/living/simple_animal/hostile/killertomato.dm
+++ b/code/modules/mob/living/simple_animal/hostile/killertomato.dm
@@ -5,6 +5,7 @@
icon_living = "tomato"
icon_dead = "tomato_dead"
gender = NEUTER
+ threat = 0.3
speak_chance = 0
turns_per_move = 5
maxHealth = 30
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
index a0e6561c68..c90ebe8a5c 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
@@ -23,6 +23,7 @@ Difficulty: Medium
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner
name = "blood-drunk miner"
desc = "A miner destined to wander forever, engaged in an endless hunt."
+ threat = 15
health = 900
maxHealth = 900
icon_state = "miner"
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
index b50ebfe160..0d4b7d1b1f 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
@@ -26,6 +26,7 @@ Difficulty: Hard
/mob/living/simple_animal/hostile/megafauna/bubblegum
name = "bubblegum"
desc = "In what passes for a hierarchy among slaughter demons, this one is king."
+ threat = 35
health = 2500
maxHealth = 2500
attacktext = "rends"
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index f8ba050864..08bd1cedbc 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -24,6 +24,7 @@ Difficulty: Very Hard
/mob/living/simple_animal/hostile/megafauna/colossus
name = "colossus"
desc = "A monstrous creature protected by heavy shielding."
+ threat = 40
health = 2500
maxHealth = 2500
attacktext = "judges"
@@ -603,6 +604,7 @@ Difficulty: Very Hard
icon_state = "lightgeist"
icon_living = "lightgeist"
icon_dead = "butterfly_dead"
+ threat = -0.7
turns_per_move = 1
response_help = "waves away"
response_disarm = "brushes aside"
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
index 0e3cde5628..4644992ad0 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
@@ -33,6 +33,7 @@ Difficulty: Medium
/mob/living/simple_animal/hostile/megafauna/dragon
name = "ash drake"
desc = "Guardians of the necropolis."
+ threat = 30
health = 2500
maxHealth = 2500
spacewalk = TRUE
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
index 7cfae9310a..a9d42373a2 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
@@ -37,6 +37,7 @@ Difficulty: Normal
/mob/living/simple_animal/hostile/megafauna/hierophant
name = "hierophant"
desc = "A massive metal club that hangs in the air as though waiting. It'll make you dance to its beat."
+ threat = 30
health = 2500
maxHealth = 2500
attacktext = "clubs"
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
index 43ce940d57..62a09dff65 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
@@ -18,6 +18,7 @@ Difficulty: Medium
/mob/living/simple_animal/hostile/megafauna/legion
name = "Legion"
+ threat = 30
health = 800
maxHealth = 800
spacewalk = TRUE
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm
index 1cbabd41dd..4d20d4c7fb 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm
@@ -8,6 +8,7 @@
icon_aggro = "Basilisk_alert"
icon_dead = "Basilisk_dead"
icon_gib = "syndicate_gib"
+ threat = 4
mob_biotypes = MOB_ORGANIC|MOB_BEAST
move_to_delay = 20
projectiletype = /obj/item/projectile/temp/basilisk
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
index 8687b2d14c..652840f7e3 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
@@ -10,6 +10,7 @@
move_to_delay = 5
vision_range = 20
aggro_vision_range = 20
+ threat = 1
maxHealth = 40 //easy to kill, but oh, will you be seeing a lot of them.
health = 40
melee_damage_lower = 10
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
index 1156b11db3..2114612fce 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
@@ -11,6 +11,7 @@
robust_searching = TRUE
ranged_ignores_vision = TRUE
ranged = TRUE
+ threat = 5
obj_damage = 5
vision_range = 6
aggro_vision_range = 18
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
index fdc3e2e9b7..68a0e81cc7 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
@@ -25,6 +25,7 @@
icon_aggro = "broodmother"
icon_dead = "egg_sac"
icon_gib = "syndicate_gib"
+ threat = 10
maxHealth = 800
health = 800
melee_damage_lower = 30
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm
index 0d62eb260d..95fafbabfd 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm
@@ -24,6 +24,7 @@
icon_aggro = "herald"
icon_dead = "herald_dying"
icon_gib = "syndicate_gib"
+ threat = 10
maxHealth = 800
health = 800
melee_damage_lower = 20
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
index 1bc9ea1e4e..d83a38ae24 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
@@ -24,6 +24,7 @@
icon_aggro = "legionnaire"
icon_dead = "legionnaire_dead"
icon_gib = "syndicate_gib"
+ threat = 10
maxHealth = 800
health = 800
melee_damage_lower = 30
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
index e65c4f5b20..3276965fa2 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
@@ -24,6 +24,7 @@
icon_aggro = "pandora"
icon_dead = "pandora_dead"
icon_gib = "syndicate_gib"
+ threat = 10
maxHealth = 800
health = 800
melee_damage_lower = 15
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goldgrub.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goldgrub.dm
index 25e38652e1..c43e9a7461 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goldgrub.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goldgrub.dm
@@ -8,6 +8,7 @@
icon_aggro = "Goldgrub_alert"
icon_dead = "Goldgrub_dead"
icon_gib = "syndicate_gib"
+ threat = 0.2
mob_biotypes = MOB_ORGANIC|MOB_BEAST
vision_range = 2
aggro_vision_range = 9
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
index 87abc1ea14..3c8cf9025f 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
@@ -10,6 +10,7 @@
icon_gib = "syndicate_gib"
mob_biotypes = MOB_ORGANIC|MOB_BEAST
mouse_opacity = MOUSE_OPACITY_OPAQUE
+ threat = 2
move_to_delay = 10
ranged = 1
ranged_cooldown_time = 60
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
index 800c35493b..b41ca15948 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
@@ -9,6 +9,7 @@
icon_gib = "syndicate_gib"
mob_biotypes = MOB_ORGANIC
mouse_opacity = MOUSE_OPACITY_OPAQUE
+ threat = 4
move_to_delay = 14
ranged = 1
vision_range = 4
@@ -233,6 +234,7 @@
icon_state = "legion"
icon_living = "legion"
icon_dead = "legion"
+ threat = 5
health = 450
maxHealth = 450
melee_damage_lower = 20
diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
index 363e3ec573..1c8b171738 100644
--- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm
+++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
@@ -10,6 +10,7 @@
obj_damage = 100
melee_damage_lower = 25
melee_damage_upper = 50
+ threat = 2
attacktext = "slashes"
attack_sound = 'sound/weapons/bladeslice.ogg'
faction = list("creature")
diff --git a/code/modules/mob/living/simple_animal/hostile/pirate.dm b/code/modules/mob/living/simple_animal/hostile/pirate.dm
index 1b2212bf55..d8737d1edc 100644
--- a/code/modules/mob/living/simple_animal/hostile/pirate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/pirate.dm
@@ -11,6 +11,7 @@
response_help = "pushes"
response_disarm = "shoves"
response_harm = "hits"
+ threat = 3
speed = 0
maxHealth = 115
health = 115
diff --git a/code/modules/mob/living/simple_animal/hostile/russian.dm b/code/modules/mob/living/simple_animal/hostile/russian.dm
index c99c7a04b5..9c3e3e0b92 100644
--- a/code/modules/mob/living/simple_animal/hostile/russian.dm
+++ b/code/modules/mob/living/simple_animal/hostile/russian.dm
@@ -13,6 +13,7 @@
response_disarm = "shoves"
response_harm = "hits"
speed = 0
+ threat = 1
maxHealth = 100
health = 100
harm_intent_damage = 5
diff --git a/code/modules/mob/living/simple_animal/hostile/sharks.dm b/code/modules/mob/living/simple_animal/hostile/sharks.dm
index 4e16a1f7bd..4008334a57 100644
--- a/code/modules/mob/living/simple_animal/hostile/sharks.dm
+++ b/code/modules/mob/living/simple_animal/hostile/sharks.dm
@@ -16,6 +16,7 @@
response_disarm = "gently pushes aside"
response_harm = "hits"
speed = 0
+ threat = 1
maxHealth = 75
health = 75
harm_intent_damage = 18
diff --git a/code/modules/mob/living/simple_animal/hostile/skeleton.dm b/code/modules/mob/living/simple_animal/hostile/skeleton.dm
index 51c55cbf65..df71701bb7 100644
--- a/code/modules/mob/living/simple_animal/hostile/skeleton.dm
+++ b/code/modules/mob/living/simple_animal/hostile/skeleton.dm
@@ -11,6 +11,7 @@
speak_emote = list("rattles")
emote_see = list("rattles")
a_intent = INTENT_HARM
+ threat = 0.5
maxHealth = 40
blood_volume = 0
health = 40
@@ -62,6 +63,7 @@
icon_state = "templar"
icon_living = "templar"
icon_dead = "templar_dead"
+ threat = 1.5
maxHealth = 150
health = 150
weather_immunities = list("snow")
@@ -95,6 +97,7 @@
icon_state = "plasma_miner"
icon_living = "plasma_miner"
icon_dead = "plasma_miner"
+ threat = 2
maxHealth = 150
health = 150
harm_intent_damage = 10
@@ -111,6 +114,7 @@
icon_state = "plasma_miner_tool"
icon_living = "plasma_miner_tool"
icon_dead = "plasma_miner_tool"
+ threat = 3
maxHealth = 185
health = 185
harm_intent_damage = 15
diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm
index 2bab332cd0..fb31e48d90 100644
--- a/code/modules/mob/living/simple_animal/hostile/statue.dm
+++ b/code/modules/mob/living/simple_animal/hostile/statue.dm
@@ -10,7 +10,7 @@
gender = NEUTER
a_intent = INTENT_HARM
mob_biotypes = MOB_HUMANOID
-
+ threat = 3
response_help = "touches"
response_disarm = "pushes"
diff --git a/code/modules/mob/living/simple_animal/hostile/stickman.dm b/code/modules/mob/living/simple_animal/hostile/stickman.dm
index 855d55938d..24bf5e9ea9 100644
--- a/code/modules/mob/living/simple_animal/hostile/stickman.dm
+++ b/code/modules/mob/living/simple_animal/hostile/stickman.dm
@@ -5,6 +5,7 @@
icon_living = "stickman"
icon_dead = "stickman_dead"
icon_gib = "syndicate_gib"
+ threat = 0.5
mob_biotypes = MOB_HUMANOID
gender = MALE
speak_chance = 0
diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
index 22bc968f84..8c4e19774d 100644
--- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
@@ -28,6 +28,7 @@
response_help = "pokes"
response_disarm = "shoves"
response_harm = "hits"
+ threat = 1
speed = 0
stat_attack = UNCONSCIOUS
robust_searching = 1
diff --git a/code/modules/mob/living/simple_animal/hostile/tree.dm b/code/modules/mob/living/simple_animal/hostile/tree.dm
index a915ede835..bc932666f6 100644
--- a/code/modules/mob/living/simple_animal/hostile/tree.dm
+++ b/code/modules/mob/living/simple_animal/hostile/tree.dm
@@ -7,6 +7,7 @@
icon_dead = "pine_1"
icon_gib = "pine_1"
gender = NEUTER
+ threat = 1
speak_chance = 0
turns_per_move = 5
response_help = "brushes"
diff --git a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm
index 976f8df229..13a4d1793e 100644
--- a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm
+++ b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm
@@ -50,6 +50,7 @@
name = "venus human trap"
desc = "Now you know how the fly feels."
icon_state = "venus_human_trap"
+ threat = 1
layer = SPACEVINE_MOB_LAYER
health = 50
maxHealth = 50
diff --git a/code/modules/mob/living/simple_animal/hostile/wizard.dm b/code/modules/mob/living/simple_animal/hostile/wizard.dm
index f97613fe8f..7265540679 100644
--- a/code/modules/mob/living/simple_animal/hostile/wizard.dm
+++ b/code/modules/mob/living/simple_animal/hostile/wizard.dm
@@ -11,6 +11,7 @@
response_help = "pokes"
response_disarm = "shoves"
response_harm = "hits"
+ threat = 3
speed = 0
maxHealth = 100
health = 100
diff --git a/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm b/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm
index 88b5bcc373..8878f17893 100644
--- a/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm
+++ b/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm
@@ -11,6 +11,7 @@
mob_biotypes = MOB_ORGANIC|MOB_BEAST
mouse_opacity = MOUSE_OPACITY_ICON
move_to_delay = 5
+ threat = 1
friendly = "floats near"
speak_emote = list("puffs")
vision_range = 5
diff --git a/code/modules/mob/living/simple_animal/hostile/zombie.dm b/code/modules/mob/living/simple_animal/hostile/zombie.dm
index e926a5d332..503f5c121e 100644
--- a/code/modules/mob/living/simple_animal/hostile/zombie.dm
+++ b/code/modules/mob/living/simple_animal/hostile/zombie.dm
@@ -7,6 +7,7 @@
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
speak_chance = 0
stat_attack = UNCONSCIOUS //braains
+ threat = 1
maxHealth = 100
health = 100
harm_intent_damage = 5
diff --git a/code/modules/ruins/spaceruin_code/clericsden.dm b/code/modules/ruins/spaceruin_code/clericsden.dm
index 46f8f806e4..67e071b274 100644
--- a/code/modules/ruins/spaceruin_code/clericsden.dm
+++ b/code/modules/ruins/spaceruin_code/clericsden.dm
@@ -21,6 +21,7 @@
desc = "A weaker construct meant to scour ruins for objects of Nar'Sie's affection. Those barbed claws are no joke."
icon_state = "proteon"
icon_living = "proteon"
+ threat = 0.4
maxHealth = 35
health = 35
melee_damage_lower = 8
diff --git a/config/config.txt b/config/config.txt
index 306d1d9a6d..fd5dc968fb 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -7,6 +7,7 @@ $include antag_rep.txt
$include donator_groupings.txt
$include dynamic_config.txt
$include plushies/defines.txt
+$include job_threats.txt
# You can use the @ character at the beginning of a config option to lock it from being edited in-game
# Example usage:
diff --git a/config/job_threats.txt b/config/job_threats.txt
new file mode 100644
index 0000000000..b3f0e2162d
--- /dev/null
+++ b/config/job_threats.txt
@@ -0,0 +1,5 @@
+## Custom job threat values
+## 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