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"