From 70fe7d1f861ccccb7a6c9a2c18e178a72e5ee233 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sat, 27 Feb 2021 13:35:25 +0100 Subject: [PATCH] [MIRROR] Dynamic now plays a part in controlling random event antagonists (#3685) * Dynamic now plays a part in controlling random event antagonists * Update _event.dm * Update spider_infestation.dm Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Co-authored-by: KathrinBailey <53862927+KathrinBailey@users.noreply.github.com> --- code/__DEFINES/dcs/signals.dm | 5 ++ code/__DEFINES/dynamic.dm | 15 ++++-- code/game/gamemodes/dynamic/dynamic.dm | 48 +++++++++++++++++-- .../gamemodes/dynamic/dynamic_hijacking.dm | 26 ++++++++++ code/game/gamemodes/dynamic/readme.md | 14 ++++++ .../revenant/revenant_spawn_event.dm | 1 + .../antagonists/slaughter/slaughterevent.dm | 1 + code/modules/events/_event.dm | 21 ++++++-- code/modules/events/abductor.dm | 1 + code/modules/events/alien_infestation.dm | 2 + code/modules/events/blob.dm | 2 + code/modules/events/nightmare.dm | 1 + code/modules/events/operative.dm | 1 + code/modules/events/pirates.dm | 1 + code/modules/events/space_dragon.dm | 1 + code/modules/events/space_ninja.dm | 1 + code/modules/events/spider_infestation.dm | 1 + code/modules/events/swarmer.dm | 1 + tgstation.dme | 1 + 19 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 code/game/gamemodes/dynamic/dynamic_hijacking.dm diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index a9c9d34892b..07c2e10f0fd 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -27,6 +27,11 @@ #define COMSIG_GLOB_JOB_AFTER_SPAWN "!job_after_spawn" /// crewmember joined the game (mob/living, rank) #define COMSIG_GLOB_CREWMEMBER_JOINED "!crewmember_joined" +/// Random event is trying to roll. (/datum/round_event_control/random_event) +/// Called by (/datum/round_event_control/preRunEvent). +#define COMSIG_GLOB_PRE_RANDOM_EVENT "!pre_random_event" + /// Do not allow this random event to continue. + #define CANCEL_PRE_RANDOM_EVENT (1<<0) /// signals from globally accessible objects diff --git a/code/__DEFINES/dynamic.dm b/code/__DEFINES/dynamic.dm index a5b1c15be5f..dedf05396c5 100644 --- a/code/__DEFINES/dynamic.dm +++ b/code/__DEFINES/dynamic.dm @@ -1,8 +1,17 @@ /// This is the only ruleset that should be picked this round, used by admins and should not be on rulesets in code. -#define ONLY_RULESET 1 +#define ONLY_RULESET (1 << 0) /// Only one ruleset with this flag will be picked. -#define HIGH_IMPACT_RULESET 2 +#define HIGH_IMPACT_RULESET (1 << 1) /// This ruleset can only be picked once. Anything that does not have a scaling_cost MUST have this. -#define LONE_RULESET 4 +#define LONE_RULESET (1 << 2) + +/// 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" diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 00a4c592c08..d2b5994c6e2 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -129,9 +129,24 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) /// lower value rounds closer to the average. var/roundstart_split_curve_width = 1.8 + /// The minimum amount of time for antag random events to be hijacked. + var/random_event_hijack_minimum = 10 MINUTES + + /// The maximum amount of time for antag random events to be hijacked. + var/random_event_hijack_maximum = 18 MINUTES + /// 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 + /datum/game_mode/dynamic/admin_panel() var/list/dat = list("Game Mode Panel

Game Mode Panel

") dat += "Dynamic Mode \[VV\] \[Refresh\]
" @@ -159,7 +174,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) dat += "[DR.ruletype] - [DR.name]
" else dat += "none.
" - dat += "
Injection Timers: ([get_injection_chance(TRUE)]% chance)
" + dat += "
Injection Timers: ([get_injection_chance(dry_run = TRUE)]% latejoin chance, [get_midround_injection_chance(dry_run = TRUE)]% midround chance)
" dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)] seconds"] \[Now!\]
" dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)] seconds"] \[Now!\]
" usr << browse(dat.Join(), "window=gamemode_panel;size=500x500") @@ -359,6 +374,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) vars[variable] = configuration["Dynamic"][variable] setup_parameters() + setup_hijacking() var/valid_roundstart_ruleset = 0 for (var/rule in subtypesof(/datum/dynamic_ruleset)) @@ -647,7 +663,9 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) message_admins("DYNAMIC: Checking for midround injection.") log_game("DYNAMIC: Checking for midround injection.") - if (prob(get_injection_chance())) + 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) @@ -661,8 +679,17 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) drafted_rules[rule] = rule.get_weight() if (drafted_rules.len > 0) picking_midround_latejoin_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.") -/// Gets the chance for latejoin and midround injection, the dry_run argument is only used for forced injection. + // 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 @@ -685,6 +712,16 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) 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 + /// Removes type from the list /datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type) for(var/I in type_list) @@ -801,6 +838,11 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) if (20 to INFINITY) return rand(90, 100) +/// Log to messages and to the game +/datum/game_mode/dynamic/proc/dynamic_log(text) + message_admins("DYNAMIC: [text]") + log_game("DYNAMIC: [text]") + #undef FAKE_REPORT_CHANCE #undef REPORT_NEG_DIVERGENCE #undef REPORT_POS_DIVERGENCE diff --git a/code/game/gamemodes/dynamic/dynamic_hijacking.dm b/code/game/gamemodes/dynamic/dynamic_hijacking.dm new file mode 100644 index 00000000000..20c2e1d903e --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_hijacking.dm @@ -0,0 +1,26 @@ +/datum/game_mode/dynamic/proc/setup_hijacking() + RegisterSignal(SSdcs, COMSIG_GLOB_PRE_RANDOM_EVENT, .proc/on_pre_random_event) + +/datum/game_mode/dynamic/proc/on_pre_random_event(datum/source, datum/round_event_control/round_event_control) + if (!round_event_control.dynamic_should_hijack) + return + + if (random_event_hijacked != HIJACKED_NOTHING) + dynamic_log("Random event [round_event_control.name] tried to roll, but Dynamic vetoed it (random event has already ran).") + SSevents.spawnEvent() + SSevents.reschedule() + return CANCEL_PRE_RANDOM_EVENT + + var/time_range = rand(random_event_hijack_minimum, random_event_hijack_maximum) + + 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)]%.") + 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)]%.") + return CANCEL_PRE_RANDOM_EVENT diff --git a/code/game/gamemodes/dynamic/readme.md b/code/game/gamemodes/dynamic/readme.md index 223be8654d5..3a8d41f230b 100644 --- a/code/game/gamemodes/dynamic/readme.md +++ b/code/game/gamemodes/dynamic/readme.md @@ -168,3 +168,17 @@ The "Dynamic" key has the following configurable values: - `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. - `roundstart_split_curve_width` - A number between 0.5 and 4. Equivalent to threat_curve_width, but for the budget split. Higher value will favour more variance in splits and lower value rounds closer to the average. +- `random_event_hijack_minimum` - The minimum amount of time for antag random events to be hijacked. (See [Random Event Hijacking](#random-event-hijacking)) +- `random_event_hijack_maximum` - The maximum amount of time for antag random events to be hijacked. (See [Random Event Hijacking](#random-event-hijacking)) +- `hijacked_random_event_injection_chance` - The amount of injection chance to give to Dynamic when a random event is hijacked. (See [Random Event Hijacking](#random-event-hijacking)) + +## Random Event "Hijacking" +Random events have the potential to be hijacked by Dynamic to keep the pace of midround injections, while also allowing greenshifts to contain some antagonists. + +`/datum/round_event_control/dynamic_should_hijack` is a variable to random events to allow Dynamic to hijack them, and defaults to FALSE. This is set to TRUE for random events that spawn antagonists. + +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) + +`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`. diff --git a/code/modules/antagonists/revenant/revenant_spawn_event.dm b/code/modules/antagonists/revenant/revenant_spawn_event.dm index c9a892cd64b..10ee621a9b8 100644 --- a/code/modules/antagonists/revenant/revenant_spawn_event.dm +++ b/code/modules/antagonists/revenant/revenant_spawn_event.dm @@ -6,6 +6,7 @@ weight = 7 max_occurrences = 1 min_players = 5 + dynamic_should_hijack = TRUE /datum/round_event/ghost_role/revenant diff --git a/code/modules/antagonists/slaughter/slaughterevent.dm b/code/modules/antagonists/slaughter/slaughterevent.dm index 64963abf343..ddde384a4e4 100644 --- a/code/modules/antagonists/slaughter/slaughterevent.dm +++ b/code/modules/antagonists/slaughter/slaughterevent.dm @@ -5,6 +5,7 @@ max_occurrences = 1 earliest_start = 1 HOURS min_players = 20 + dynamic_should_hijack = TRUE diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm index fff0ffadbef..47ec75e9331 100644 --- a/code/modules/events/_event.dm +++ b/code/modules/events/_event.dm @@ -1,3 +1,5 @@ +#define RANDOM_EVENT_ADMIN_INTERVENTION_TIME 60 // SKYRAT EDIT: Original value (10) + //this singleton datum is used by the events controller to dictate how it selects events /datum/round_event_control var/name //The human-readable name of the event @@ -26,6 +28,9 @@ var/triggering //admin cancellation + /// Whether or not dynamic should hijack this event + var/dynamic_should_hijack = FALSE + /datum/round_event_control/New() if(config && !wizardevent) // Magic is unaffected by configs earliest_start = CEILING(earliest_start * CONFIG_GET(number/events_min_time_mul), 1) @@ -55,18 +60,24 @@ return FALSE if(ispath(typepath, /datum/round_event/ghost_role) && !(GLOB.ghost_role_flags & GHOSTROLE_MIDROUND_EVENT)) return FALSE + + var/datum/game_mode/dynamic/dynamic = SSticker.mode + if (istype(dynamic) && dynamic_should_hijack && dynamic.random_event_hijacked != HIJACKED_NOTHING) + return FALSE + return TRUE /datum/round_event_control/proc/preRunEvent() if(!ispath(typepath, /datum/round_event)) return EVENT_CANT_RUN + if (SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PRE_RANDOM_EVENT, src) & CANCEL_PRE_RANDOM_EVENT) + return EVENT_INTERRUPTED + triggering = TRUE if (alert_observers) - //message_admins("Random Event triggering in 10 seconds: [name] (CANCEL)") //ORIGINAL - //sleep(100) //ORIGINAL - message_admins("Random Event triggering in 60 seconds: [name] (CANCEL)") //SKYRAT EDIT CHANGE - EVENTS - sleep(600) //SKYRAT EDIT CHANGE - EVENTS + message_admins("Random Event triggering in [RANDOM_EVENT_ADMIN_INTERVENTION_TIME] seconds: [name] (CANCEL)") + sleep(RANDOM_EVENT_ADMIN_INTERVENTION_TIME SECONDS) var/gamemode = SSticker.mode.config_tag var/players_amt = get_active_player_count(alive_check = TRUE, afk_check = TRUE, human_check = TRUE) if(!canSpawnEvent(players_amt, gamemode)) @@ -217,3 +228,5 @@ processing = my_processing SSevents.running += src return ..() + +#undef RANDOM_EVENT_ADMIN_INTERVENTION_TIME diff --git a/code/modules/events/abductor.dm b/code/modules/events/abductor.dm index 6f239f7aa22..2ba534d42a0 100755 --- a/code/modules/events/abductor.dm +++ b/code/modules/events/abductor.dm @@ -4,6 +4,7 @@ weight = 10 max_occurrences = 1 min_players = 20 + dynamic_should_hijack = TRUE gamemode_blacklist = list("nuclear","wizard","revolution") /datum/round_event/ghost_role/abductor diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm index e34d2cece49..df51f1af81e 100644 --- a/code/modules/events/alien_infestation.dm +++ b/code/modules/events/alien_infestation.dm @@ -8,6 +8,8 @@ min_players = 10 + dynamic_should_hijack = TRUE + /datum/round_event_control/alien_infestation/canSpawnEvent() . = ..() if(!.) diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm index d4d8328013b..e7da374af9f 100644 --- a/code/modules/events/blob.dm +++ b/code/modules/events/blob.dm @@ -7,6 +7,8 @@ // min_players = 20 // SKYRAT EDIT -- Original min_players = 60 // SKYRAT EDIT CHANGE -- Requires 60 alive non-afk players for blob + dynamic_should_hijack = TRUE + gamemode_blacklist = list("blob") //Just in case a blob survives that long /datum/round_event_control/blob/canSpawnEvent(players, gamemode) diff --git a/code/modules/events/nightmare.dm b/code/modules/events/nightmare.dm index ac3a1da8b80..9d8694e8548 100644 --- a/code/modules/events/nightmare.dm +++ b/code/modules/events/nightmare.dm @@ -3,6 +3,7 @@ typepath = /datum/round_event/ghost_role/nightmare max_occurrences = 1 min_players = 20 + dynamic_should_hijack = TRUE /datum/round_event/ghost_role/nightmare minimum_required = 1 diff --git a/code/modules/events/operative.dm b/code/modules/events/operative.dm index 9e92364b89d..b0eab57f6c9 100644 --- a/code/modules/events/operative.dm +++ b/code/modules/events/operative.dm @@ -3,6 +3,7 @@ typepath = /datum/round_event/ghost_role/operative weight = 0 //Admin only max_occurrences = 1 + dynamic_should_hijack = TRUE /datum/round_event/ghost_role/operative minimum_required = 1 diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm index 339f7ca20cc..e451d78567f 100644 --- a/code/modules/events/pirates.dm +++ b/code/modules/events/pirates.dm @@ -6,6 +6,7 @@ max_occurrences = 1 min_players = 10 earliest_start = 30 MINUTES + dynamic_should_hijack = TRUE gamemode_blacklist = list("nuclear") #define PIRATES_ROGUES "Rogues" diff --git a/code/modules/events/space_dragon.dm b/code/modules/events/space_dragon.dm index ac2db2cf84c..0bf18ede2d2 100644 --- a/code/modules/events/space_dragon.dm +++ b/code/modules/events/space_dragon.dm @@ -6,6 +6,7 @@ weight = 10 max_occurrences = 1 min_players = 20 + dynamic_should_hijack = TRUE /datum/round_event/ghost_role/space_dragon minimum_required = 1 diff --git a/code/modules/events/space_ninja.dm b/code/modules/events/space_ninja.dm index ceef16a28d0..f82e087b2e6 100644 --- a/code/modules/events/space_ninja.dm +++ b/code/modules/events/space_ninja.dm @@ -6,6 +6,7 @@ weight = 10 earliest_start = 20 MINUTES min_players = 20 + dynamic_should_hijack = TRUE /datum/round_event/ghost_role/space_ninja minimum_required = 1 diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm index 700019903e0..56cb91f589e 100644 --- a/code/modules/events/spider_infestation.dm +++ b/code/modules/events/spider_infestation.dm @@ -5,6 +5,7 @@ //max_occurrences = 1 //ORIGINAL max_occurrences = 0 //SKYRAT EDIT CHANGE //min_players = 20 - SKYRAT EDIT - moved to modular_skyrat/master_files + dynamic_should_hijack = TRUE /datum/round_event/spider_infestation announceWhen = 400 diff --git a/code/modules/events/swarmer.dm b/code/modules/events/swarmer.dm index 2212b271019..2599cc92d79 100644 --- a/code/modules/events/swarmer.dm +++ b/code/modules/events/swarmer.dm @@ -5,6 +5,7 @@ weight = 3 //SKYRAT EDIT CHANGE - EVENTS - same as the dynamic ruleset max_occurrences = 1 //Only once okay fam min_players = 20 + dynamic_should_hijack = TRUE /datum/round_event/spawn_swarmer/announce(fake) priority_announce("Our long-range sensors have detected that your station's defenses have been breached by some sort of alien device. We suggest searching for and destroying it as soon as possible.", "[command_name()] High-Priority Update") diff --git a/tgstation.dme b/tgstation.dme index 5d575bc138e..712011fd8f7 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -840,6 +840,7 @@ #include "code\game\gamemodes\clown_ops\clown_weapons.dm" #include "code\game\gamemodes\cult\cult.dm" #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_rulesets.dm" #include "code\game\gamemodes\dynamic\dynamic_rulesets_latejoin.dm"