From 97c040cee265a366cf51c2391409ec802b40f438 Mon Sep 17 00:00:00 2001 From: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Date: Sun, 19 Jun 2022 01:40:36 -0700 Subject: [PATCH] Replace adding threat (the traitor objective, post-revs victory) with unfavorable situations (#67538) Hacking the command console and winning revs no longer adds midround threat. Instead, it'll force a heavy ruleset to spawn, and barring that, will spawn a dangerous random event. Not really tested, you know how it is with stuff like this. This change will slightly decrease the total number of midrounds in the round. Why It's Good For The Game Before D2022P1, midround threat effectively did not work. Adding midround threat did not guarantee anything would actually happen, whether that be the "obsessed spawn and doesn't do anything" kind of nothing happens, or the "absolutely no ruleset spawns" literally kind of nothing happens. Sometimes, though, it might have! This PR makes that a guarantee. After D2022P1, midround threat works and is also extremely dangerous in the quantities that are being added. 15 threat means 2-3 extra midround antagonists are spawned, and this late into the round are often going to force heavy rulesets anyway, but at a disproportionate cost. Furthermore, adding midround threat decreases the time it takes for the next midround, as midround rolls are rolled equidistantly from one another. It is possible, for instance, for a midround ruleset to run, and then another to run seconds later as the distance changes. This keeps things direct--the crew gets a disadvantage, but is not completely overwhelmed by several antagonists disproportionately. --- code/game/gamemodes/dynamic/dynamic.dm | 7 +-- .../dynamic/dynamic_unfavorable_situation.dm | 52 +++++++++++++++++++ .../game/machinery/computer/communications.dm | 30 +++++------ .../antagonists/revolution/revolution.dm | 2 +- tgstation.dme | 1 + 5 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 code/game/gamemodes/dynamic/dynamic_unfavorable_situation.dm diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index b05802b5362..f3f74152bd4 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -592,7 +592,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) return FALSE /// An experimental proc to allow admins to call rules on the fly or have rules call other rules. -/datum/game_mode/dynamic/proc/picking_specific_rule(ruletype, forced = FALSE) +/datum/game_mode/dynamic/proc/picking_specific_rule(ruletype, forced = FALSE, ignore_cost = FALSE) var/datum/dynamic_ruleset/midround/new_rule if(ispath(ruletype)) new_rule = new ruletype() // You should only use it to call midround rules though. @@ -618,10 +618,11 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) return FALSE var/population = GLOB.alive_player_list.len - if((new_rule.acceptable(population, threat_level) && new_rule.cost <= mid_round_budget) || forced) + if((new_rule.acceptable(population, threat_level) && (ignore_cost || new_rule.cost <= mid_round_budget)) || forced) new_rule.trim_candidates() if (new_rule.ready(forced)) - spend_midround_budget(new_rule.cost, threat_log, "[worldtime2text()]: Forced rule [new_rule.name]") + if (!ignore_cost) + spend_midround_budget(new_rule.cost, threat_log, "[worldtime2text()]: Forced rule [new_rule.name]") new_rule.pre_execute(population) if (new_rule.execute()) // This should never fail since ready() returned 1 if(new_rule.flags & HIGH_IMPACT_RULESET) diff --git a/code/game/gamemodes/dynamic/dynamic_unfavorable_situation.dm b/code/game/gamemodes/dynamic/dynamic_unfavorable_situation.dm new file mode 100644 index 00000000000..0f9baf876a0 --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_unfavorable_situation.dm @@ -0,0 +1,52 @@ +/// An easy interface to make...*waves hands* bad things happen. +/// This is used for impactful events like traitors hacking and creating more threat, or a revolutions victory. +/// It tries to spawn a heavy midround if possible, otherwise it will trigger a "bad" random event after a short period. +/// Calling this function will not use up any threat. +/datum/game_mode/dynamic/proc/unfavorable_situation() + var/static/list/unfavorable_random_events = list( + /datum/round_event_control/immovable_rod, + /datum/round_event_control/meteor_wave, + /datum/round_event_control/portal_storm_syndicate, + ) + + var/list/possible_heavies = list() + + // Ignored factors: threat cost, minimum round time + for (var/datum/dynamic_ruleset/midround/ruleset as anything in midround_rules) + if (ruleset.midround_ruleset_style != MIDROUND_RULESET_STYLE_HEAVY) + continue + + if (ruleset.weight == 0) + continue + + if (ruleset.cost > max_threat_level) + continue + + if (!ruleset.acceptable(GLOB.alive_player_list.len, threat_level)) + continue + + if (ruleset.minimum_round_time > world.time - SSticker.round_start_time) + continue + + if(istype(ruleset, /datum/dynamic_ruleset/midround/from_ghosts) && !(GLOB.ghost_role_flags & GHOSTROLE_MIDROUND_EVENT)) + continue + + ruleset.trim_candidates() + + if (!ruleset.ready()) + continue + + possible_heavies[ruleset] = ruleset.get_weight() + + if (possible_heavies.len == 0) + var/datum/round_event_control/round_event_control_type = pick(unfavorable_random_events) + var/delay = rand(20 SECONDS, 1 MINUTES) + + dynamic_log("An unfavorable situation was requested, but no heavy rulesets could be drafted. Spawning [initial(round_event_control_type.name)] in [DisplayTimeText(delay)] instead.") + + var/datum/round_event_control/round_event_control = new round_event_control_type + addtimer(CALLBACK(round_event_control, /datum/round_event_control.proc/runEvent), delay) + else + var/datum/dynamic_ruleset/midround/heavy_ruleset = pick_weight(possible_heavies) + dynamic_log("An unfavorable situation was requested, spawning [initial(heavy_ruleset.name)]") + picking_specific_rule(heavy_ruleset, forced = TRUE, ignore_cost = TRUE) diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index a37b7cf9772..9e7b15eff08 100755 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -776,8 +776,6 @@ #define MIN_GHOSTS_FOR_FUGITIVES 6 /// The maximum percentage of the population to be ghosts before we no longer have the chance of spawning Sleeper Agents. #define MAX_PERCENT_GHOSTS_FOR_SLEEPER 0.2 -/// The amount of threat injected by a hack, if chosen. -#define HACK_THREAT_INJECTION_AMOUNT 15 /* * The communications console hack, @@ -811,8 +809,8 @@ if(HACK_PIRATE) // Triggers pirates, which the crew may be able to pay off to prevent priority_announce( "Attention crew, it appears that someone on your station has made unexpected communication with a Syndicate ship in nearby space.", - "[command_name()] High-Priority Update" - ) + "[command_name()] High-Priority Update", + ) var/datum/round_event_control/pirates/pirate_event = locate() in SSevents.control if(!pirate_event) @@ -822,19 +820,19 @@ if(HACK_FUGITIVES) // Triggers fugitives, which can cause confusion / chaos as the crew decides which side help priority_announce( "Attention crew, it appears that someone on your station has made unexpected communication with an unmarked ship in nearby space.", - "[command_name()] High-Priority Update" - ) + "[command_name()] High-Priority Update", + ) var/datum/round_event_control/fugitives/fugitive_event = locate() in SSevents.control if(!fugitive_event) CRASH("hack_console() attempted to run fugitives, but could not find an event controller!") addtimer(CALLBACK(fugitive_event, /datum/round_event_control.proc/runEvent), rand(20 SECONDS, 1 MINUTES)) - if(HACK_THREAT) // Adds a flat amount of threat to buy a (probably) more dangerous antag later + if(HACK_THREAT) // Force an unfavorable situation on the crew priority_announce( SSmapping.config.orbit_shift_replacement, - "[command_name()] High-Priority Update" - ) + "[command_name()] High-Priority Update", + ) for(var/mob/crew_member as anything in GLOB.player_list) if(!is_station_level(crew_member.z)) @@ -842,7 +840,7 @@ shake_camera(crew_member, 15, 1) var/datum/game_mode/dynamic/dynamic = SSticker.mode - dynamic.create_threat(HACK_THREAT_INJECTION_AMOUNT, list(dynamic.threat_log, dynamic.roundend_threat_log), "[worldtime2text()]: Communications console hacked by [hacker]") + dynamic.unfavorable_situation() if(HACK_SLEEPER) // Trigger one or multiple sleeper agents with the crew (or for latejoining crew) var/datum/dynamic_ruleset/midround/sleeper_agent_type = /datum/dynamic_ruleset/midround/autotraitor @@ -850,23 +848,20 @@ var/max_number_of_sleepers = clamp(round(length(GLOB.alive_player_list) / 20), 1, 3) var/num_agents_created = 0 for(var/num_agents in 1 to rand(1, max_number_of_sleepers)) - // Offset the threat cost of the sleeper agent(s) we're about to run... - dynamic.create_threat(initial(sleeper_agent_type.cost)) - // ...Then try to actually trigger a sleeper agent. - if(!dynamic.picking_specific_rule(sleeper_agent_type, TRUE)) + if(!dynamic.picking_specific_rule(sleeper_agent_type, forced = TRUE, ignore_cost = TRUE)) break num_agents_created++ if(num_agents_created <= 0) // We failed to run any midround sleeper agents, so let's be patient and run latejoin traitor - dynamic.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator, TRUE) + dynamic.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator, forced = TRUE, ignore_cost = TRUE) else // We spawned some sleeper agents, nice - give them a report to kickstart the paranoia priority_announce( "Attention crew, it appears that someone on your station has hijacked your telecommunications, broadcasting a Syndicate radio signal to your fellow employees.", - "[command_name()] High-Priority Update" - ) + "[command_name()] High-Priority Update", + ) #undef HACK_PIRATE #undef HACK_FUGITIVES @@ -876,7 +871,6 @@ #undef MIN_GHOSTS_FOR_PIRATES #undef MIN_GHOSTS_FOR_FUGITIVES #undef MAX_PERCENT_GHOSTS_FOR_SLEEPER -#undef HACK_THREAT_INJECTION_AMOUNT /datum/comm_message var/title diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm index 0d97f21ce8e..1824771ce21 100644 --- a/code/modules/antagonists/revolution/revolution.dm +++ b/code/modules/antagonists/revolution/revolution.dm @@ -486,7 +486,7 @@ if (revs_win_injection_amount) var/datum/game_mode/dynamic/dynamic = SSticker.mode - dynamic.create_threat(revs_win_injection_amount, list(dynamic.threat_log, dynamic.roundend_threat_log), "[worldtime2text()]: Revolution victory") + dynamic.unfavorable_situation() priority_announce("A recent assessment of your station has marked your station as a severe risk area for high ranking Nanotrasen officials. \ For the safety of our staff, we have blacklisted your station for new employment of security and command. \ diff --git a/tgstation.dme b/tgstation.dme index 7a66930706e..2de3a0ccbca 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1250,6 +1250,7 @@ #include "code\game\gamemodes\dynamic\dynamic_rulesets_midround.dm" #include "code\game\gamemodes\dynamic\dynamic_rulesets_roundstart.dm" #include "code\game\gamemodes\dynamic\dynamic_simulations.dm" +#include "code\game\gamemodes\dynamic\dynamic_unfavorable_situation.dm" #include "code\game\gamemodes\dynamic\ruleset_picking.dm" #include "code\game\machinery\_machinery.dm" #include "code\game\machinery\ai_slipper.dm"