[MIRROR] Dynamic 2022, Part 1 - Redesigning midround rolls (Midrounds happen far more frequently now, and scale their power over time) [MDB IGNORE] (#13718)

* Dynamic 2022, Part 1 - Redesigning midround rolls (Midrounds happen far more frequently now, and scale their power over time)

* Update dynamic_rulesets_midround.dm

* wew

Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com>
Co-authored-by: Gandalf <9026500+Gandalf2k15@users.noreply.github.com>
This commit is contained in:
SkyratBot
2022-05-23 17:06:51 +02:00
committed by GitHub
parent 1bc520d5a8
commit ea6f252e81
27 changed files with 286 additions and 209 deletions
+6 -3
View File
@@ -7,11 +7,14 @@
/// This ruleset can only be picked once. Anything that does not have a scaling_cost MUST have this.
#define LONE_RULESET (1 << 2)
/// This is a "heavy" midround ruleset, and should be run later into the round
#define MIDROUND_RULESET_STYLE_HEAVY "Heavy"
/// This is a "light" midround ruleset, and should be run early into the round
#define MIDROUND_RULESET_STYLE_LIGHT "Light"
/// No round event was hijacked this cycle
#define HIJACKED_NOTHING "HIJACKED_NOTHING"
/// This cycle, a round event was hijacked when the last midround event was too recent.
#define HIJACKED_TOO_RECENT "HIJACKED_TOO_RECENT"
/// This cycle, a round event was hijacked when the next midround event is too soon.
#define HIJACKED_TOO_SOON "HIJACKED_TOO_SOON"
+78 -113
View File
@@ -78,26 +78,29 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
/// The maximum time the recurring latejoin ruleset timer is allowed to be.
var/latejoin_delay_max = (25 MINUTES)
/// When world.time is over this number the mode tries to inject a midround ruleset.
var/midround_injection_cooldown = 0
/// The low bound for the midround roll time splits.
/// This number influences where to place midround rolls, making this smaller
/// will make midround rolls more frequent, and vice versa.
/// A midround will never be able to roll before this.
var/midround_lower_bound = 10 MINUTES
/// The minimum time the recurring midround ruleset timer is allowed to be.
var/midround_delay_min = (15 MINUTES)
/// The upper bound for the midround roll time splits.
/// This number influences where to place midround rolls, making this larger
/// will make midround rolls less frequent, and vice versa.
/// A midround will never be able to roll farther than this.
var/midround_upper_bound = 100 MINUTES
/// The maximum time the recurring midround ruleset timer is allowed to be.
var/midround_delay_max = (35 MINUTES)
/// The distance between the chosen midround roll point (which is deterministic),
/// and when it can actually roll.
/// Basically, if this is set to 5 minutes, and a midround roll point is decided to be at 20 minutes,
/// then it can roll anywhere between 15 and 25 minutes.
var/midround_roll_distance = 3 MINUTES
/// If above this threat, increase the chance of injection
var/higher_injection_chance_minimum_threat = 70
/// The chance of injection increase when above higher_injection_chance_minimum_threat
var/higher_injection_chance = 15
/// If below this threat, decrease the chance of injection
var/lower_injection_chance_minimum_threat = 10
/// The chance of injection decrease when above lower_injection_chance_minimum_threat
var/lower_injection_chance = 15
/// The amount of threat per midround roll.
/// Basically, if this is set to 5, then for every 5 threat, one midround roll will be added.
/// The equation this is used in rounds up, meaning that if this is set to 5, and you have 6
/// threat, then you will get 2 midround rolls.
var/threat_per_midround_roll = 6.5
/// A number between -5 and +5.
/// A negative value will give a more peaceful round and
@@ -127,15 +130,49 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
/// The maximum amount of time for antag random events to be hijacked.
var/random_event_hijack_maximum = 18 MINUTES
/// What is the lower bound of when the roundstart annoucement is sent out?
var/waittime_l = 600
/// What is the higher bound of when the roundstart annoucement is sent out?
var/waittime_h = 1800
/// Maximum amount of threat allowed to generate.
var/max_threat_level = 100
/// The extra chance multiplier that a heavy impact midround ruleset will run next time.
/// For example, if this is set to 50, then the next heavy roll will be about 50% more likely to happen.
var/hijacked_random_event_injection_chance_modifier = 50
/// Any midround before this point is guaranteed to be light
var/midround_light_upper_bound = 25 MINUTES
/// Any midround after this point is guaranteed to be heavy
var/midround_heavy_lower_bound = 55 MINUTES
/// If there are less than this many players readied, threat level will be lowered.
/// This number should be kept fairly low, as there are other measures that population
/// impacts Dynamic, such as the requirements variable on rulesets.
var/low_pop_player_threshold = 20
/// The maximum threat that can roll with *zero* players.
/// As the number of players approaches `low_pop_player_threshold`, the maximum
/// threat level will increase.
/// For example, if `low_pop_minimum_threat` is 50, `low_pop_player_threshold` is 20,
/// and the number of readied players is 10, then the highest threat that can roll is
/// lerp(50, 100, 10 / 20), AKA 75.
var/low_pop_minimum_threat = 50
/// The chance for latejoins to roll when ready
var/latejoin_roll_chance = 50
// == EVERYTHING BELOW THIS POINT SHOULD NOT BE CONFIGURED ==
/// A list of recorded "snapshots" of the round, stored in the dynamic.json log
var/list/datum/dynamic_snapshot/snapshots
/// The time when the last midround injection was attempted, whether or not it was successful
var/last_midround_injection_attempt = 0
/// The amount to inject when a round event is hijacked
var/hijacked_random_event_injection_chance = 50
/// Whether or not a random event has been hijacked this midround cycle
var/random_event_hijacked = HIJACKED_NOTHING
@@ -150,14 +187,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
/// Can differ from the actual threat amount.
var/shown_threat
/// What is the lower bound of when the roundstart annoucement is sent out?
var/waittime_l = 600
/// What is the higher bound of when the roundstart annoucement is sent out?
var/waittime_h = 1800
/// Maximum amount of threat allowed to generate.
var/max_threat_level = 100
VAR_PRIVATE/next_midround_injection
/datum/game_mode/dynamic/admin_panel()
var/list/dat = list("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Game Mode Panel</title></head><body><h1><B>Game Mode Panel</B></h1>")
@@ -186,9 +216,15 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
dat += "[DR.ruletype] - <b>[DR.name]</b><br>"
else
dat += "none.<br>"
dat += "<br>Injection Timers: (<b>[get_injection_chance(dry_run = TRUE)]%</b> latejoin chance, <b>[get_midround_injection_chance(dry_run = TRUE)]%</b> midround chance)<BR>"
dat += "<br>Injection Timers: (<b>[get_heavy_midround_injection_chance(dry_run = TRUE)]%</b> heavy midround chance)<BR>"
dat += "Latejoin: [DisplayTimeText(latejoin_injection_cooldown-world.time)] <a href='?src=\ref[src];[HrefToken()];injectlate=1'>\[Now!\]</a><BR>"
dat += "Midround: [DisplayTimeText(midround_injection_cooldown-world.time)] <a href='?src=\ref[src];[HrefToken()];injectmid=1'>\[Now!\]</a><BR>"
var/next_injection = next_midround_injection()
if (next_injection == INFINITY)
dat += "All midrounds have been exhausted."
else
dat += "Midround: [DisplayTimeText(next_injection - world.time)] <a href='?src=\ref[src];[HrefToken()];injectmid=1'>\[Now!\]</a><BR>"
usr << browse(dat.Join(), "window=gamemode_panel;size=500x500")
/datum/game_mode/dynamic/Topic(href, href_list)
@@ -215,9 +251,9 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
forced_injection = TRUE
message_admins("[key_name(usr)] forced a latejoin injection.")
else if (href_list["injectmid"])
midround_injection_cooldown = 0
forced_injection = TRUE
message_admins("[key_name(usr)] forced a midround injection.")
try_midround_roll()
else if (href_list["threatlog"])
show_threatlog(usr)
else if (href_list["stacking_limit"])
@@ -330,6 +366,9 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
var/relative_threat = LORENTZ_DISTRIBUTION(threat_curve_centre, threat_curve_width)
threat_level = clamp(round(lorentz_to_amount(relative_threat), 0.1), 0, max_threat_level)
if (SSticker.totalPlayersReady < low_pop_player_threshold)
threat_level = min(threat_level, LERP(low_pop_minimum_threat, max_threat_level, SSticker.totalPlayersReady / low_pop_player_threshold))
peaceful_percentage = round(LORENTZ_CUMULATIVE_DISTRIBUTION(relative_threat, threat_curve_centre, threat_curve_width), 0.01)*100
/// Generates the midround and roundstart budgets
@@ -362,9 +401,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
var/latejoin_injection_cooldown_middle = 0.5*(latejoin_delay_max + latejoin_delay_min)
latejoin_injection_cooldown = round(clamp(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), latejoin_delay_min, latejoin_delay_max)) + world.time
var/midround_injection_cooldown_middle = 0.5*(midround_delay_max + midround_delay_min)
midround_injection_cooldown = round(clamp(EXP_DISTRIBUTION(midround_injection_cooldown_middle), midround_delay_min, midround_delay_max)) + world.time
/datum/game_mode/dynamic/pre_setup()
if(CONFIG_GET(flag/dynamic_config_enabled))
var/json_file = file("[global.config.directory]/dynamic.json")
@@ -611,80 +647,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if(rule.rule_process() == RULESET_STOP_PROCESSING) // If rule_process() returns 1 (RULESET_STOP_PROCESSING), stop processing.
current_rules -= rule
if (midround_injection_cooldown < world.time)
if (GLOB.dynamic_forced_extended)
return
// Somehow it managed to trigger midround multiple times so this was moved here.
// There is no way this should be able to trigger an injection twice now.
var/midround_injection_cooldown_middle = 0.5*(midround_delay_max + midround_delay_min)
midround_injection_cooldown = (round(clamp(EXP_DISTRIBUTION(midround_injection_cooldown_middle), midround_delay_min, midround_delay_max)) + world.time)
// Time to inject some threat into the round
if(EMERGENCY_PAST_POINT_OF_NO_RETURN) // Unless the shuttle is past the point of no return
return
message_admins("DYNAMIC: Checking for midround injection.")
log_game("DYNAMIC: Checking for midround injection.")
last_midround_injection_attempt = world.time
if (prob(get_midround_injection_chance()))
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/midround/rule in midround_rules)
if (!rule.weight)
continue
if (rule.acceptable(GLOB.alive_player_list.len, threat_level) && mid_round_budget >= rule.cost)
// If admins have disabled dynamic from picking from the ghost pool
if(rule.ruletype == "Latejoin" && !(GLOB.ghost_role_flags & GHOSTROLE_MIDROUND_EVENT))
continue
rule.trim_candidates()
if (rule.ready())
drafted_rules[rule] = rule.get_weight()
if (drafted_rules.len > 0)
pick_midround_rule(drafted_rules)
else if (random_event_hijacked == HIJACKED_TOO_SOON)
log_game("DYNAMIC: Midround injection failed when random event was hijacked. Spawning another random event in its place.")
// A random event antag would have rolled had this injection check passed.
// As a refund, spawn a non-ghost-role random event.
SSevents.spawnEvent()
SSevents.reschedule()
random_event_hijacked = HIJACKED_NOTHING
/// Gets the chance for latejoin injection, the dry_run argument is only used for forced injection.
/datum/game_mode/dynamic/proc/get_injection_chance(dry_run = FALSE)
if(forced_injection)
forced_injection = dry_run
return 100
var/chance = 0
var/max_pop_per_antag = max(5,15 - round(threat_level/10) - round(GLOB.alive_player_list.len/5))
if (!GLOB.current_living_antags.len)
chance += 50 // No antags at all? let's boost those odds!
else
var/current_pop_per_antag = GLOB.alive_player_list.len / GLOB.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 (GLOB.dead_player_list.len > GLOB.alive_player_list.len)
chance -= 30 // More than half the crew died? ew, let's calm down on antags
if (mid_round_budget > higher_injection_chance_minimum_threat)
chance += higher_injection_chance
if (mid_round_budget < lower_injection_chance_minimum_threat)
chance -= lower_injection_chance
return round(max(0,chance))
/// Gets the chance for midround injection, the dry_run argument is only used for forced injection.
/// Usually defers to the latejoin injection chance.
/datum/game_mode/dynamic/proc/get_midround_injection_chance(dry_run)
var/chance = get_injection_chance(dry_run)
if (random_event_hijacked != HIJACKED_NOTHING)
chance += hijacked_random_event_injection_chance
return chance
try_midround_roll()
/// Removes type from the list
/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
@@ -719,7 +682,9 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/execute_midround_latejoin_rule, forced_latejoin_rule), forced_latejoin_rule.delay)
forced_latejoin_rule = null
else if (latejoin_injection_cooldown < world.time && prob(get_injection_chance()))
else if (latejoin_injection_cooldown < world.time && (forced_injection || prob(latejoin_roll_chance)))
forced_injection = FALSE
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules)
if (!rule.weight)
@@ -785,14 +750,14 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
/// Expend round start threat, can't fall under 0.
/datum/game_mode/dynamic/proc/spend_roundstart_budget(cost, list/threat_log, reason)
round_start_budget = max(round_start_budget - cost,0)
for(var/list/logs in threat_log)
log_threat(cost, logs, reason)
if (!isnull(threat_log))
log_threat(-cost, threat_log, reason)
/// Expend midround threat, can't fall under 0.
/datum/game_mode/dynamic/proc/spend_midround_budget(cost, list/threat_log, reason)
mid_round_budget = max(mid_round_budget - cost,0)
for(var/list/logs in threat_log)
log_threat(cost, logs, reason)
if (!isnull(threat_log))
log_threat(-cost, threat_log, reason)
/// Turns the value generated by lorentz distribution to number between 0 and 100.
/// Used for threat level and splitting the budgets.
@@ -17,11 +17,9 @@
if (world.time - last_midround_injection_attempt < time_range)
random_event_hijacked = HIJACKED_TOO_RECENT
dynamic_log("Random event [round_event_control.name] tried to roll, but the last midround injection \
was too recent. Injection chance has been raised to [get_midround_injection_chance(dry_run = TRUE)]%.")
was too recent. Heavy injection chance has been raised to [get_heavy_midround_injection_chance(dry_run = TRUE)]%.")
return CANCEL_PRE_RANDOM_EVENT
if (midround_injection_cooldown - world.time < time_range)
random_event_hijacked = HIJACKED_TOO_SOON
dynamic_log("Random event [round_event_control.name] tried to roll, but the next midround injection \
is too soon. Injection chance has been raised to [get_midround_injection_chance(dry_run = TRUE)]%.")
if (next_midround_injection() - world.time < time_range)
dynamic_log("Random event [round_event_control.name] tried to roll, but the next midround injection is too soon.")
return CANCEL_PRE_RANDOM_EVENT
@@ -0,0 +1,90 @@
/// Returns the world.time of the next midround injection.
/// Will return a cached result from `next_midround_injection`, the variable.
/// If that variable is null, will generate a new one.
/datum/game_mode/dynamic/proc/next_midround_injection()
if (!isnull(next_midround_injection))
return next_midround_injection
// Admins can futz around with the midround threat, and we want to be able to react to that
var/midround_threat = threat_level - round_start_budget
var/rolls = CEILING(midround_threat / threat_per_midround_roll, 1)
var/distance = ((1 / (rolls + 1)) * midround_upper_bound) + midround_lower_bound
if (last_midround_injection_attempt == 0)
last_midround_injection_attempt = SSticker.round_start_time
return last_midround_injection_attempt + distance
/datum/game_mode/dynamic/proc/try_midround_roll()
if (!forced_injection && next_midround_injection() > world.time)
return
if (GLOB.dynamic_forced_extended)
return
if (EMERGENCY_PAST_POINT_OF_NO_RETURN)
return
last_midround_injection_attempt = world.time
next_midround_injection = null
forced_injection = FALSE
var/spawn_heavy = prob(get_heavy_midround_injection_chance())
dynamic_log("A midround ruleset is rolling, and will be [spawn_heavy ? "HEAVY" : "LIGHT"].")
random_event_hijacked = HIJACKED_NOTHING
var/list/drafted_heavies = list()
var/list/drafted_lights = list()
for (var/datum/dynamic_ruleset/midround/ruleset in midround_rules)
if (ruleset.weight == 0)
continue
if (!ruleset.acceptable(GLOB.alive_player_list.len, threat_level))
continue
if (mid_round_budget < ruleset.cost)
continue
if (ruleset.minimum_round_time > world.time - SSticker.round_start_time)
continue
// If admins have disabled dynamic from picking from the ghost pool
if(istype(ruleset, /datum/dynamic_ruleset/midround/from_ghosts) && !(GLOB.ghost_role_flags & GHOSTROLE_MIDROUND_EVENT))
continue
ruleset.trim_candidates()
if (ruleset.ready())
var/ruleset_is_heavy = (ruleset.midround_ruleset_style == MIDROUND_RULESET_STYLE_HEAVY)
if (ruleset_is_heavy)
drafted_heavies[ruleset] = ruleset.get_weight()
else
drafted_lights[ruleset] = ruleset.get_weight()
if (spawn_heavy && drafted_heavies.len > 0 && pick_midround_rule(drafted_heavies))
return
else if (drafted_lights.len > 0 && pick_midround_rule(drafted_lights))
if (spawn_heavy)
dynamic_log("A heavy ruleset was intended to roll, but there weren't any available.")
else
dynamic_log("No midround rulesets could be drafted.")
/// Gets the chance for a heavy ruleset midround injection, the dry_run argument is only used for forced injection.
/datum/game_mode/dynamic/proc/get_heavy_midround_injection_chance(dry_run)
var/chance_modifier = 1
var/next_midround_roll = next_midround_injection() - SSticker.round_start_time
if (random_event_hijacked != HIJACKED_NOTHING)
chance_modifier += (hijacked_random_event_injection_chance_modifier / 100)
if (GLOB.current_living_antags.len == 0)
chance_modifier += 0.5
if (GLOB.dead_player_list.len > GLOB.alive_player_list.len)
chance_modifier -= 0.3
var/heavy_coefficient = CLAMP01((next_midround_roll - midround_light_upper_bound) / (midround_heavy_lower_bound - midround_light_upper_bound))
return 100 * (heavy_coefficient * max(1, chance_modifier))
@@ -115,7 +115,7 @@
required_candidates = 1
weight = 2
delay = 1 MINUTES // Prevents rule start while head is offstation.
cost = 20
cost = 10
requirements = list(101,101,70,40,30,20,20,20,20,20)
flags = HIGH_IMPACT_RULESET
blocking_rules = list(/datum/dynamic_ruleset/roundstart/revs)
@@ -200,7 +200,7 @@
)
required_candidates = 1
weight = 4
cost = 10
cost = 7
requirements = list(101,101,101,10,10,10,10,10,10,10)
repeatable = TRUE
@@ -11,6 +11,7 @@
/datum/dynamic_ruleset/midround // Can be drafted once in a while during a round
ruletype = "Midround"
var/midround_ruleset_style
/// If the ruleset should be restricted from ghost roles.
var/restrict_ghost_roles = TRUE
/// What mob type the ruleset is restricted to.
@@ -20,6 +21,9 @@
var/list/dead_players = list()
var/list/list_observers = list()
/// The minimum round time before this ruleset will show up
var/minimum_round_time = 0
/datum/dynamic_ruleset/midround/from_ghosts
weight = 0
required_type = /mob/dead/observer
@@ -171,9 +175,6 @@
/datum/dynamic_ruleset/midround/from_ghosts/proc/attempt_replacement()
var/datum/dynamic_ruleset/midround/autotraitor/sleeper_agent = new
// Otherwise, it has a chance to fail. We don't want that, since this is already pretty unlikely.
sleeper_agent.has_failure_chance = FALSE
mode.configure_ruleset(sleeper_agent)
if (!mode.picking_specific_rule(sleeper_agent))
@@ -189,6 +190,7 @@
/datum/dynamic_ruleset/midround/autotraitor
name = "Syndicate Sleeper Agent"
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
antag_datum = /datum/antagonist/traitor
antag_flag = ROLE_SLEEPER_AGENT
antag_flag_override = ROLE_TRAITOR
@@ -207,32 +209,11 @@
ROLE_POSITRONIC_BRAIN,
)
required_candidates = 1
weight = 7
cost = 10
requirements = list(10,10,10,10,10,10,10,10,10,10)
weight = 35
cost = 3
requirements = list(3,3,3,3,3,3,3,3,3,3)
repeatable = TRUE
/// Whether or not this instance of sleeper agent should be randomly acceptable.
/// If TRUE, then this has a threat level% chance to succeed.
var/has_failure_chance = TRUE
/datum/dynamic_ruleset/midround/autotraitor/acceptable(population = 0, threat = 0)
var/player_count = GLOB.alive_player_list.len
var/antag_count = GLOB.current_living_antags.len
var/max_traitors = round(player_count / 10) + 1
// adding traitors if the antag population is getting low
var/too_little_antags = antag_count < max_traitors
if (!too_little_antags)
log_game("DYNAMIC: Too many living antags compared to living players ([antag_count] living antags, [player_count] living players, [max_traitors] max traitors)")
return FALSE
if (has_failure_chance && !prob(mode.threat_level))
log_game("DYNAMIC: Random chance to roll autotraitor failed, it was a [mode.threat_level]% chance.")
return FALSE
return ..()
/datum/dynamic_ruleset/midround/autotraitor/trim_candidates()
..()
for(var/mob/living/player in living_players)
@@ -266,6 +247,7 @@
/datum/dynamic_ruleset/midround/families
name = "Family Head Aspirants"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
persistent = TRUE
antag_datum = /datum/antagonist/gang
antag_flag = ROLE_FAMILY_HEAD_ASPIRANT
@@ -343,6 +325,7 @@
/datum/dynamic_ruleset/midround/malf
name = "Malfunctioning AI"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/malf_ai
antag_flag = ROLE_MALF_MIDROUND
antag_flag_override = ROLE_MALF
@@ -359,7 +342,7 @@
required_enemies = list(4,4,4,4,4,4,2,2,2,0)
required_candidates = 1
weight = 3
cost = 22
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
required_type = /mob/living/silicon/ai
blocking_rules = list(/datum/dynamic_ruleset/roundstart/malf_ai)
@@ -403,6 +386,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/wizard
name = "Wizard"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/wizard
antag_flag = ROLE_WIZARD_MIDROUND
antag_flag_override = ROLE_WIZARD
@@ -415,8 +399,8 @@
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 1
cost = 20
requirements = list(90,90,90,80,60,40,30,20,10,10)
cost = 10
requirements = list(90,90,90,80,60,50,40,40,40,40)
flags = HIGH_IMPACT_RULESET
/datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE)
@@ -440,6 +424,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/nuclear
name = "Nuclear Assault"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_flag = ROLE_OPERATIVE_MIDROUND
antag_flag_override = ROLE_OPERATIVE
antag_datum = /datum/antagonist/nukeop
@@ -454,7 +439,8 @@
required_enemies = list(3,3,3,3,3,2,1,1,0,0)
required_candidates = 5
weight = 5
cost = 35
cost = 7
minimum_round_time = 70 MINUTES
requirements = list(90,90,90,80,60,40,30,20,10,10)
var/list/operative_cap = list(2,2,3,3,4,5,5,5,5,5)
var/datum/team/nuclear/nuke_team
@@ -490,6 +476,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/blob
name = "Blob"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/blob
antag_flag = ROLE_BLOB
enemy_roles = list(
@@ -500,8 +487,9 @@
)
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 2
cost = 10
minimum_round_time = 35 MINUTES
weight = 3
cost = 8
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
@@ -512,6 +500,7 @@
/// Infects a random player, making them explode into a blob.
/datum/dynamic_ruleset/midround/blob_infection
name = "Blob Infection"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/blob/infection
antag_flag = ROLE_BLOB_INFECTION
antag_flag_override = ROLE_BLOB
@@ -536,7 +525,8 @@
)
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 2
minimum_round_time = 35 MINUTES
weight = 3
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
@@ -569,6 +559,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph
name = "Alien Infestation"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/xeno
antag_flag = ROLE_ALIEN
enemy_roles = list(
@@ -579,7 +570,8 @@
)
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 0 //SKYRAT EDIT CHANGE
minimum_round_time = 40 MINUTES
weight = 5
cost = 10
requirements = list(101,101,101,70,50,40,20,15,10,10)
repeatable = TRUE
@@ -620,6 +612,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/nightmare
name = "Nightmare"
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
antag_datum = /datum/antagonist/nightmare
antag_flag = ROLE_NIGHTMARE
antag_flag_override = ROLE_ALIEN
@@ -632,7 +625,7 @@
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 3
cost = 10
cost = 5
requirements = list(101,101,101,70,50,40,20,15,10,10)
repeatable = TRUE
var/list/spawn_locs = list()
@@ -671,6 +664,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/space_dragon
name = "Space Dragon"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/space_dragon
antag_flag = ROLE_SPACE_DRAGON
antag_flag_override = ROLE_SPACE_DRAGON
@@ -683,7 +677,7 @@
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 4
cost = 10
cost = 7
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
var/list/spawn_locs = list()
@@ -721,6 +715,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/abductors
name = "Abductors"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/abductor
antag_flag = ROLE_ABDUCTOR
enemy_roles = list(
@@ -733,7 +728,7 @@
required_candidates = 2
required_applicants = 2
weight = 4
cost = 10
cost = 7
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
var/datum/team/abductor_team/new_team
@@ -764,6 +759,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/space_ninja
name = "Space Ninja"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/ninja
antag_flag = ROLE_NINJA
enemy_roles = list(
@@ -775,11 +771,10 @@
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 4
cost = 10
cost = 8
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
var/list/spawn_locs = list()
minimum_players = 999 //SKYRAT EDIT ADDITION - EVENTS
/datum/dynamic_ruleset/midround/from_ghosts/space_ninja/execute()
for(var/obj/effect/landmark/carpspawn/carp_spawn in GLOB.landmarks_list)
@@ -809,6 +804,7 @@
/datum/dynamic_ruleset/midround/spiders
name = "Spiders"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_flag = ROLE_SPIDER
required_type = /mob/dead/observer
enemy_roles = list(
@@ -820,7 +816,7 @@
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 0
weight = 3
cost = 10
cost = 8
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
var/spawncount = 2
@@ -832,6 +828,7 @@
/// Revenant ruleset
/datum/dynamic_ruleset/midround/from_ghosts/revenant
name = "Revenant"
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
antag_datum = /datum/antagonist/revenant
antag_flag = ROLE_REVENANT
enemy_roles = list(
@@ -843,7 +840,7 @@
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 4
cost = 10
cost = 5
requirements = list(101,101,101,70,50,40,20,15,10,10)
repeatable = TRUE
var/dead_mobs_required = 20
@@ -883,11 +880,12 @@
/// Sentient Disease ruleset
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease
name = "Sentient Disease"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/disease
antag_flag = ROLE_SENTIENT_DISEASE
required_candidates = 1
weight = 4
cost = 10
cost = 8
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
@@ -900,8 +898,9 @@
return virus
/// Space Pirates ruleset
/datum/dynamic_ruleset/midround/from_ghosts/pirates
/datum/dynamic_ruleset/midround/pirates
name = "Space Pirates"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_flag = "Space Pirates"
required_type = /mob/dead/observer
enemy_roles = list(
@@ -911,13 +910,13 @@
JOB_SECURITY_OFFICER,
)
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 3
required_candidates = 0
weight = 4
cost = 10
cost = 8
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
/datum/dynamic_ruleset/midround/from_ghosts/pirates/acceptable(population=0, threat=0)
/datum/dynamic_ruleset/midround/pirates/acceptable(population=0, threat=0)
if (!SSmapping.empty_space)
return FALSE
return ..()
@@ -929,6 +928,7 @@
/// Obsessed ruleset
/datum/dynamic_ruleset/midround/obsessed
name = "Obsessed"
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
antag_datum = /datum/antagonist/obsessed
antag_flag = ROLE_OBSESSED
restricted_roles = list(
@@ -974,6 +974,7 @@
/// Thief ruleset
/datum/dynamic_ruleset/midround/opportunist
name = "Opportunist"
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
antag_datum = /datum/antagonist/thief
antag_flag = ROLE_OPPORTUNIST
antag_flag_override = ROLE_THIEF
@@ -991,7 +992,7 @@
ROLE_POSITRONIC_BRAIN,
)
required_candidates = 1
weight = 0 // Disabled until Dynamic midround rolling handles minor threats better
weight = 2
cost = 3 //Worth less than obsessed, but there's more of them.
requirements = list(10,10,10,10,10,10,10,10,10,10)
repeatable = TRUE
@@ -7,7 +7,6 @@
/datum/dynamic_ruleset/roundstart/traitor
name = "Traitors"
persistent = TRUE
antag_flag = ROLE_TRAITOR
antag_datum = /datum/antagonist/traitor
minimum_required_age = 0
@@ -30,11 +29,9 @@
requirements = list(8,8,8,8,8,8,8,8,8,8)
antag_cap = list("denominator" = 24)
var/autotraitor_cooldown = (15 MINUTES)
COOLDOWN_DECLARE(autotraitor_cooldown_check)
/datum/dynamic_ruleset/roundstart/traitor/pre_execute(population)
. = ..()
COOLDOWN_START(src, autotraitor_cooldown_check, autotraitor_cooldown)
var/num_traitors = get_antag_cap(population) * (scaled_times + 1)
for (var/i = 1 to num_traitors)
if(candidates.len <= 0)
@@ -46,12 +43,6 @@
GLOB.pre_setup_antags += M.mind
return TRUE
/datum/dynamic_ruleset/roundstart/traitor/rule_process()
if (COOLDOWN_FINISHED(src, autotraitor_cooldown_check))
COOLDOWN_START(src, autotraitor_cooldown_check, autotraitor_cooldown)
log_game("DYNAMIC: Checking if we can turn someone into a traitor.")
mode.picking_specific_rule(/datum/dynamic_ruleset/midround/autotraitor)
//////////////////////////////////////////////
// //
// MALFUNCTIONING AI //
+21 -17
View File
@@ -28,20 +28,27 @@ After this process is done, any leftover roundstart threat will be given to the
## Deciding midround threats
Latejoin and midround injection cooldowns are set using exponential distribution between
### Frequency
- 5 minutes and 25 for latejoin (configurable as latejoin_delay_min and latejoin_delay_max)
- 15 minutes and 35 for midround (configurable as midround_delay_min and midround_delay_max)
The frequency of midround threats is based on the midround threat of the round. The number of midround threats that will roll is `threat_level` / `threat_per_midround_roll` (configurable), rounded up. For example, if `threat_per_midround_roll` is set to 5, then for every 5 threat, one midround roll will be added. If you have 6 threat, with this configuration, you will get 2 midround rolls.
this value is then added to `world.time` and assigned to the injection cooldown variables.
These midround roll points are then equidistantly spaced across the round, starting from `midround_lower_bound` (configurable) to `midround_upper_bound` (configurable), with a +/- of `midround_roll_distance` (configurable).
[rigged_roundstart][/datum/game_mode/dynamic/proc/rigged_roundstart] is called instead if there are forced rules (an admin set the mode)
For example, if:
1. `midround_lower_bound` is `10 MINUTES`
2. `midround_upper_bound` is `100 MINUTES`
3. `midround_roll_distance` is `3 MINUTES`
4. You have 5 midround rolls for the round
1. [setup_parameters][/datum/game_mode/proc/setup_parameters]\()
2. [pre_setup][/datum/game_mode/proc/pre_setup]\()
3. [roundstart][/datum/game_mode/dynamic/proc/roundstart]\() OR [rigged_roundstart][/datum/game_mode/dynamic/proc/rigged_roundstart]\()
4. [picking_roundstart_rule][/datum/game_mode/dynamic/proc/picking_roundstart_rule]\(drafted_rules)
5. [post_setup][/datum/game_mode/proc/post_setup]\()
...then those 5 midround rolls will be placed equidistantly (meaning equally apart) across the first 10-100 minutes of the round. Every individual roll will then be adjusted to either be 3 minutes earlier, or 3 minutes later.
### Threat variety
Threats are split between **heavy** rulesets and **light** rulesets. A heavy ruleset includes major threats like space dragons or blobs, while light rulesets are ones that don't often cause shuttle calls when rolled, such as revenants or traitors (sleeper agents).
When a midround roll occurs, the decision to choose between light or heavy depends on the current round time. If it is less than `midround_light_upper_bound` (configurable), then it is guaranteed to be a light ruleset. If it is more than `midround_heavy_lower_bound`, then it is guaranteed to be a heavy ruleset. If it is any point in between, it will interpolate the value between those. This means that the longer the round goes on, the more likely you are to get a heavy ruleset.
If no heavy ruleset can run, such as not having enough threat, then a light ruleset is guaranteed to run.
## Rule Processing
@@ -153,17 +160,14 @@ Rulesets have the following variables notable to developers and those interested
- `protected_roles` - Serves the same purpose of `restricted_roles`, except it can be turned off through configuration (`protect_roles_from_antagonist`). For example, security officers *shouldn't* be made traitor, so they are in Traitor's `protected_roles`.
- When considering putting a role in `protected_roles` or `restricted_roles`, the rule of thumb is if it is *technically infeasible* to support that job in that role. There's no *technical* reason a security officer can't be a traitor, and so they are simply in `protected_roles`. There *are* technical reasons a cyborg can't be a changeling, so they are in `restricted_roles` instead.
This is not a complete list--search "configurable" in this README to learn more.
### Dynamic
The "Dynamic" key has the following configurable values:
- `pop_per_requirement` - The default value of `pop_per_requirement` for any ruleset that does not explicitly set it. Defaults to 6.
- `latejoin_delay_min`, `latejoin_delay_max` - The time range, in deciseconds (take your seconds, and multiply by 10), for a latejoin to attempt rolling. Once this timer is finished, a new one will be created within the same range.
- Suppose you have a `latejoin_delay_min` of 600 (60 seconds, 1 minute) and a `latejoin_delay_max` of 1800 (180 seconds, 3 minutes). Once the round starts, a random number in this range will be picked--let's suppose 1.5 minutes. After 1.5 minutes, Dynamic will decide if a latejoin threat should be created (a probability of `/datum/game_mode/dynamic/proc/get_injection_chance()`). Regardless of its decision, a new timer will be started within the range of 1 to 3 minutes, repeatedly.
- `midround_delay_min`, `midround_delay_max` - Same as `latejoin_delay_min` and `latejoin_delay_max`, except for midround threats instead of latejoin ones.
- `higher_injection_chance`, `higher_injection_chance_minimum_threat` - Manipulates the injection chance (`/datum/game_mode/dynamic/proc/get_injection_chance()`). If the *current midround budget* is above `higher_injection_chance_minimum_threat`, then this chance will be increased by `higher_injection_chance`.
- For example: suppose you have a `higher_injection_chance_minimum_threat` of 70, and a `higher_injection_chance` of 15. This means that, if when a midround threat is trying to roll, there is 75 midround budget left, then the injection chance will go up 15%.
- `lower_injection_chance`, `lower_injection_chance_minimum_threat` - The inverse of the `higher_injection_chance` variables. If the *current midround budget* is *below* `lower_injection_chance`, then the chance is lowered by `lower_injection_chance_minimum_threat`.
- For example: suppose you have a `lower_injection_chance_minimum_threat` of 30, and a `lower_injection_chance` of 15. This means if there is 20 midround budget left, then the chance will lower by 15%.
- `threat_curve_centre` - A number between -5 and +5. A negative value will give a more peaceful round and a positive value will give a round with higher threat.
- `threat_curve_width` - A number between 0.5 and 4. Higher value will favour extreme rounds and lower value rounds closer to the average.
- `roundstart_split_curve_centre` - A number between -5 and +5. Equivalent to threat_curve_centre, but for the budget split. A negative value will weigh towards midround rulesets, and a positive value will weight towards roundstart ones.
@@ -180,6 +184,6 @@ Random events have the potential to be hijacked by Dynamic to keep the pace of m
In `/datum/game_mode/dynamic/on_pre_random_event` (in `dynamic_hijacking.dm`), Dynamic hooks to random events. If the `dynamic_should_hijack` variable is TRUE, the following sequence of events occurs:
![Flow chart to describe the chain of events for Dynamic 2021 to take](https://user-images.githubusercontent.com/35135081/109071468-9cab7e00-76a8-11eb-8f9f-2b920c602ef4.png)
![Flow chart to describe the chain of events for Dynamic 2021 to take](https://github.com/tgstation/documentation-assets/blob/main/dynamic/random_event_hijacking.png)
`n` is a random value between `random_event_hijack_minimum` and `random_event_hijack_maximum`. Injection chance, should it need to be raised, is increased by `hijacked_random_event_injection_chance`.
`n` is a random value between `random_event_hijack_minimum` and `random_event_hijack_maximum`. Heavy injection chance, should it need to be raised, is increased by `hijacked_random_event_injection_chance_modifier`.
@@ -33,7 +33,7 @@
/datum/game_mode/dynamic/proc/pick_midround_rule(list/drafted_rules)
var/datum/dynamic_ruleset/rule = pick_ruleset(drafted_rules)
if (isnull(rule))
return
return null
current_midround_rulesets = drafted_rules - rule
midround_injection_timer_id = addtimer(
@@ -47,6 +47,8 @@
<a href='?src=[REF(src)];cancelmidround=[midround_injection_timer_id]'>CANCEL</a> | \
<a href='?src=[REF(src)];differentmidround=[midround_injection_timer_id]'>SOMETHING ELSE</a>")
return rule
/// Fired after admins do not cancel a midround injection.
/datum/game_mode/dynamic/proc/execute_midround_rule(datum/dynamic_ruleset/rule)
current_midround_rulesets = null
@@ -36,7 +36,7 @@ GLOBAL_LIST_EMPTY(antagonists)
///Name of the antag hud we provide to this mob.
var/antag_hud_name
/// If set to true, the antag will not be added to the living antag list.
var/soft_antag = FALSE
var/count_against_dynamic_roll_chance = TRUE
/// The battlecry this antagonist shouts when suiciding with C4/X4.
var/suicide_cry = ""
//Antag panel properties
@@ -92,14 +92,14 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/on_body_transfer(mob/living/old_body, mob/living/new_body)
SHOULD_CALL_PARENT(TRUE)
remove_innate_effects(old_body)
if(!soft_antag && old_body && old_body.stat != DEAD && !LAZYLEN(old_body.mind?.antag_datums))
if(old_body && old_body.stat != DEAD && !LAZYLEN(old_body.mind?.antag_datums))
old_body.remove_from_current_living_antags()
var/datum/action/antag_info/info_button = info_button_ref?.resolve()
if(info_button)
info_button.Remove(old_body)
info_button.Grant(new_body)
apply_innate_effects(new_body)
if(!soft_antag && new_body.stat != DEAD)
if(count_against_dynamic_roll_chance && new_body.stat != DEAD)
new_body.add_to_current_living_antags()
//This handles the application of antag huds/special abilities
@@ -162,7 +162,7 @@ GLOBAL_LIST_EMPTY(antagonists)
replace_banned_player()
else if(owner.current.client?.holder && (CONFIG_GET(flag/auto_deadmin_antagonists) || owner.current.client.prefs?.toggles & DEADMIN_ANTAGONIST))
owner.current.client.holder.auto_deadmin()
if(!soft_antag && owner.current.stat != DEAD && owner.current.client)
if(count_against_dynamic_roll_chance && owner.current.stat != DEAD && owner.current.client)
owner.current.add_to_current_living_antags()
SEND_SIGNAL(owner, COMSIG_ANTAGONIST_GAINED, src)
@@ -203,7 +203,7 @@ GLOBAL_LIST_EMPTY(antagonists)
remove_innate_effects()
clear_antag_moodies()
LAZYREMOVE(owner.antag_datums, src)
if(!LAZYLEN(owner.antag_datums) && !soft_antag)
if(!LAZYLEN(owner.antag_datums))
owner.current.remove_from_current_living_antags()
if(info_button_ref)
QDEL_NULL(info_button_ref)
@@ -11,6 +11,7 @@
prevent_roundtype_conversion = FALSE
antagpanel_category = "Ash Walkers"
suicide_cry = "I HAVE NO IDEA WHAT THIS THING DOES!!"
count_against_dynamic_roll_chance = FALSE
var/datum/team/ashwalkers/ashie_team
//SKYRAT EDIT: Recipes for Tribals
///The list of recipes that will be learned on inheriting the antag datum
@@ -33,6 +33,7 @@
antag_hud_name = "brainwashed"
antagpanel_category = "Other"
show_name_in_check_antagonists = TRUE
count_against_dynamic_roll_chance = FALSE
ui_name = "AntagInfoBrainwashed"
suicide_cry = "FOR... SOMEONE!!"
@@ -910,7 +910,7 @@
name = "\improper Headslug Changeling"
show_in_antagpanel = FALSE
give_objectives = FALSE
soft_antag = TRUE
count_against_dynamic_roll_chance = FALSE
genetic_points = 5
total_genetic_points = 5
+1
View File
@@ -6,6 +6,7 @@
antag_hud_name = "obsessed"
show_name_in_check_antagonists = TRUE
roundend_category = "obsessed"
count_against_dynamic_roll_chance = FALSE
silent = TRUE //not actually silent, because greet will be called by the trauma anyway.
suicide_cry = "FOR MY LOVE!!"
preview_outfit = /datum/outfit/obsessed
+1
View File
@@ -10,6 +10,7 @@
show_to_ghosts = TRUE
antag_moodlet = /datum/mood_event/focused
suicide_cry = "FOR NANOTRASEN!!"
count_against_dynamic_roll_chance = FALSE
var/datum/team/ert/ert_team
var/leader = FALSE
var/datum/outfit/outfit = /datum/outfit/centcom/ert/security
@@ -9,6 +9,7 @@
antag_hud_name = "fugitive"
suicide_cry = "FOR FREEDOM!!"
preview_outfit = /datum/outfit/prisoner
count_against_dynamic_roll_chance = FALSE
var/datum/team/fugitive/fugitive_team
var/is_captured = FALSE
var/backstory = "error"
@@ -7,6 +7,7 @@
prevent_roundtype_conversion = FALSE
antag_hud_name = "fugitive_hunter"
suicide_cry = "FOR GLORY!!"
count_against_dynamic_roll_chance = FALSE
var/datum/team/fugitive_hunters/hunter_team
var/backstory = "error"
@@ -3,6 +3,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE //Not that it will be there for long
suicide_cry = "FOR THE GREENTEXT!!" // This can never actually show up, but not including it is a missed opportunity
count_against_dynamic_roll_chance = FALSE
/datum/antagonist/greentext/proc/forge_objectives()
var/datum/objective/succeed_objective = new /datum/objective("Succeed")
@@ -5,6 +5,7 @@
show_name_in_check_antagonists = TRUE
can_elimination_hijack = ELIMINATION_ENABLED
suicide_cry = "FOR SCOTLAND!!" // If they manage to lose their no-drop stuff somehow
count_against_dynamic_roll_chance = FALSE
/datum/antagonist/highlander/apply_innate_effects(mob/living/mob_override)
var/mob/living/L = owner.current || mob_override
@@ -8,6 +8,7 @@
show_in_antagpanel = TRUE
antagpanel_category = "Other"
show_name_in_check_antagonists = TRUE
count_against_dynamic_roll_chance = FALSE
silent = TRUE //not actually silent, because greet will be called by the trauma anyway.
/// Brain trauma associated with this antag datum
+1
View File
@@ -9,6 +9,7 @@
preview_outfit = /datum/outfit/thief
antag_hud_name = "thief"
ui_name = "AntagInfoThief"
count_against_dynamic_roll_chance = FALSE
///assoc list of strings set up for the flavor of the thief.
var/list/thief_flavor
///if added by an admin, they can choose a thief flavor
@@ -5,7 +5,7 @@
prevent_roundtype_conversion = FALSE
suicide_cry = "FOR MY LOVE!!"
var/datum/mind/date
soft_antag = TRUE
count_against_dynamic_roll_chance = FALSE
/datum/antagonist/valentine/proc/forge_objectives()
var/datum/objective/protect/protect_objective = new /datum/objective/protect
+8 -1
View File
@@ -126,7 +126,14 @@
/mob/proc/add_to_current_living_antags()
if(!SSticker?.mode)
return
GLOB.current_living_antags |= src
if (length(mind.antag_datums) == 0)
return
for (var/datum/antagonist/antagonist in mind.antag_datums)
if (antagonist.count_against_dynamic_roll_chance)
GLOB.current_living_antags |= src
return
///Removes the mob reference from the list of living antag player-mobs.
/mob/proc/remove_from_current_living_antags()
@@ -2,9 +2,7 @@
/datum/unit_test/dynamic_roundstart_ruleset_sanity
/datum/unit_test/dynamic_roundstart_ruleset_sanity/Run()
for (var/_ruleset in subtypesof(/datum/dynamic_ruleset/roundstart))
var/datum/dynamic_ruleset/roundstart/ruleset = _ruleset
for (var/datum/dynamic_ruleset/roundstart/ruleset as anything in subtypesof(/datum/dynamic_ruleset/roundstart))
var/has_scaling_cost = initial(ruleset.scaling_cost)
var/is_lone = initial(ruleset.flags) & (LONE_RULESET | HIGH_IMPACT_RULESET)
@@ -13,6 +11,11 @@
else if (!has_scaling_cost && !is_lone)
TEST_FAIL("[ruleset] has no scaling cost, but is also not a lone/highlander ruleset.")
for (var/datum/dynamic_ruleset/midround/ruleset as anything in subtypesof(/datum/dynamic_ruleset/midround) - /datum/dynamic_ruleset/midround/from_ghosts)
var/midround_ruleset_style = initial(ruleset.midround_ruleset_style)
if (midround_ruleset_style != MIDROUND_RULESET_STYLE_HEAVY && midround_ruleset_style != MIDROUND_RULESET_STYLE_LIGHT)
TEST_FAIL("[ruleset] has an invalid midround_ruleset_style, it should be MIDROUND_RULESET_STYLE_HEAVY or MIDROUND_RULESET_STYLE_LIGHT")
/// Verifies that dynamic rulesets have unique antag_flag.
/datum/unit_test/dynamic_unique_antag_flags
@@ -1,6 +1,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/lone_infiltrator
name = "Lone Infiltrator"
antag_datum = /datum/antagonist/traitor/infiltrator
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_flag = ROLE_LONE_INFILTRATOR
restricted_roles = list(JOB_CYBORG, JOB_AI, JOB_SECURITY_OFFICER, JOB_WARDEN, JOB_DETECTIVE, JOB_HEAD_OF_SECURITY, JOB_CAPTAIN, JOB_CORRECTIONS_OFFICER, JOB_VANGUARD_OPERATIVE, JOB_NT_REP, JOB_BLUESHIELD, JOB_ORDERLY, JOB_BOUNCER, JOB_CUSTOMS_AGENT, JOB_ENGINEERING_GUARD, JOB_SCIENCE_GUARD) //SKYRAT EDIT - Sec_haul
required_candidates = 1
@@ -135,6 +135,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/cortical_borer
name = "Cortical Borer Infestation"
antag_datum = /datum/antagonist/cortical_borer
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
antag_flag = ROLE_BORER
enemy_roles = list(
JOB_CAPTAIN,
+1
View File
@@ -1291,6 +1291,7 @@
#include "code\game\gamemodes\dynamic\dynamic.dm"
#include "code\game\gamemodes\dynamic\dynamic_hijacking.dm"
#include "code\game\gamemodes\dynamic\dynamic_logging.dm"
#include "code\game\gamemodes\dynamic\dynamic_midround_rolling.dm"
#include "code\game\gamemodes\dynamic\dynamic_rulesets.dm"
#include "code\game\gamemodes\dynamic\dynamic_rulesets_latejoin.dm"
#include "code\game\gamemodes\dynamic\dynamic_rulesets_midround.dm"