Skip to content

Commit d7382d0

Browse files
authored
Dynamic now plays a part in controlling random event antagonists (tgstation#57175)
Dynamic now plays a part in controlling random event antagonists Dynamic 2021's carefully tuned pacing is demolished by random event antags spawning new antags right after Dynamic spawns a midround. While I do in some respect believe this is simply the roll of the dice, it's just too volatile for me to feel justified ignoring it.
1 parent 981c32d commit d7382d0

File tree

19 files changed

+134
-8
lines changed

19 files changed

+134
-8
lines changed

code/__DEFINES/dcs/signals.dm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
#define COMSIG_GLOB_JOB_AFTER_SPAWN "!job_after_spawn"
2828
/// crewmember joined the game (mob/living, rank)
2929
#define COMSIG_GLOB_CREWMEMBER_JOINED "!crewmember_joined"
30+
/// Random event is trying to roll. (/datum/round_event_control/random_event)
31+
/// Called by (/datum/round_event_control/preRunEvent).
32+
#define COMSIG_GLOB_PRE_RANDOM_EVENT "!pre_random_event"
33+
/// Do not allow this random event to continue.
34+
#define CANCEL_PRE_RANDOM_EVENT (1<<0)
3035

3136
/// signals from globally accessible objects
3237

code/__DEFINES/dynamic.dm

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
/// This is the only ruleset that should be picked this round, used by admins and should not be on rulesets in code.
2-
#define ONLY_RULESET 1
2+
#define ONLY_RULESET (1 << 0)
33

44
/// Only one ruleset with this flag will be picked.
5-
#define HIGH_IMPACT_RULESET 2
5+
#define HIGH_IMPACT_RULESET (1 << 1)
66

77
/// This ruleset can only be picked once. Anything that does not have a scaling_cost MUST have this.
8-
#define LONE_RULESET 4
8+
#define LONE_RULESET (1 << 2)
9+
10+
/// No round event was hijacked this cycle
11+
#define HIJACKED_NOTHING "HIJACKED_NOTHING"
12+
13+
/// This cycle, a round event was hijacked when the last midround event was too recent.
14+
#define HIJACKED_TOO_RECENT "HIJACKED_TOO_RECENT"
15+
16+
/// This cycle, a round event was hijacked when the next midround event is too soon.
17+
#define HIJACKED_TOO_SOON "HIJACKED_TOO_SOON"

code/game/gamemodes/dynamic/dynamic.dm

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,24 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
129129
/// lower value rounds closer to the average.
130130
var/roundstart_split_curve_width = 1.8
131131

132+
/// The minimum amount of time for antag random events to be hijacked.
133+
var/random_event_hijack_minimum = 10 MINUTES
134+
135+
/// The maximum amount of time for antag random events to be hijacked.
136+
var/random_event_hijack_maximum = 18 MINUTES
137+
132138
/// A list of recorded "snapshots" of the round, stored in the dynamic.json log
133139
var/list/datum/dynamic_snapshot/snapshots
134140

141+
/// The time when the last midround injection was attempted, whether or not it was successful
142+
var/last_midround_injection_attempt = 0
143+
144+
/// The amount to inject when a round event is hijacked
145+
var/hijacked_random_event_injection_chance = 50
146+
147+
/// Whether or not a random event has been hijacked this midround cycle
148+
var/random_event_hijacked = HIJACKED_NOTHING
149+
135150
/datum/game_mode/dynamic/admin_panel()
136151
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>")
137152
dat += "Dynamic Mode <a href='?_src_=vars;[HrefToken()];Vars=[REF(src)]'>\[VV\]</a> <a href='?src=\ref[src];[HrefToken()]'>\[Refresh\]</a><BR>"
@@ -159,7 +174,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
159174
dat += "[DR.ruletype] - <b>[DR.name]</b><br>"
160175
else
161176
dat += "none.<br>"
162-
dat += "<br>Injection Timers: (<b>[get_injection_chance(TRUE)]%</b> chance)<BR>"
177+
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>"
163178
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"] <a href='?src=\ref[src];[HrefToken()];injectlate=1'>\[Now!\]</a><BR>"
164179
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"] <a href='?src=\ref[src];[HrefToken()];injectmid=1'>\[Now!\]</a><BR>"
165180
usr << browse(dat.Join(), "window=gamemode_panel;size=500x500")
@@ -359,6 +374,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
359374
vars[variable] = configuration["Dynamic"][variable]
360375

361376
setup_parameters()
377+
setup_hijacking()
362378

363379
var/valid_roundstart_ruleset = 0
364380
for (var/rule in subtypesof(/datum/dynamic_ruleset))
@@ -647,7 +663,9 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
647663
message_admins("DYNAMIC: Checking for midround injection.")
648664
log_game("DYNAMIC: Checking for midround injection.")
649665

650-
if (prob(get_injection_chance()))
666+
last_midround_injection_attempt = world.time
667+
668+
if (prob(get_midround_injection_chance()))
651669
var/list/drafted_rules = list()
652670
for (var/datum/dynamic_ruleset/midround/rule in midround_rules)
653671
if (!rule.weight)
@@ -661,8 +679,17 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
661679
drafted_rules[rule] = rule.get_weight()
662680
if (drafted_rules.len > 0)
663681
picking_midround_latejoin_rule(drafted_rules)
682+
else if (random_event_hijacked == HIJACKED_TOO_SOON)
683+
log_game("DYNAMIC: Midround injection failed when random event was hijacked. Spawning another random event in its place.")
664684

665-
/// Gets the chance for latejoin and midround injection, the dry_run argument is only used for forced injection.
685+
// A random event antag would have rolled had this injection check passed.
686+
// As a refund, spawn a non-ghost-role random event.
687+
SSevents.spawnEvent()
688+
SSevents.reschedule()
689+
690+
random_event_hijacked = HIJACKED_NOTHING
691+
692+
/// Gets the chance for latejoin injection, the dry_run argument is only used for forced injection.
666693
/datum/game_mode/dynamic/proc/get_injection_chance(dry_run = FALSE)
667694
if(forced_injection)
668695
forced_injection = !dry_run
@@ -685,6 +712,16 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
685712
chance -= lower_injection_chance
686713
return round(max(0,chance))
687714

715+
/// Gets the chance for midround injection, the dry_run argument is only used for forced injection.
716+
/// Usually defers to the latejoin injection chance.
717+
/datum/game_mode/dynamic/proc/get_midround_injection_chance(dry_run)
718+
var/chance = get_injection_chance(dry_run)
719+
720+
if (random_event_hijacked != HIJACKED_NOTHING)
721+
chance += hijacked_random_event_injection_chance
722+
723+
return chance
724+
688725
/// Removes type from the list
689726
/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
690727
for(var/I in type_list)
@@ -801,6 +838,11 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
801838
if (20 to INFINITY)
802839
return rand(90, 100)
803840

841+
/// Log to messages and to the game
842+
/datum/game_mode/dynamic/proc/dynamic_log(text)
843+
message_admins("DYNAMIC: [text]")
844+
log_game("DYNAMIC: [text]")
845+
804846
#undef FAKE_REPORT_CHANCE
805847
#undef REPORT_NEG_DIVERGENCE
806848
#undef REPORT_POS_DIVERGENCE
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/datum/game_mode/dynamic/proc/setup_hijacking()
2+
RegisterSignal(SSdcs, COMSIG_GLOB_PRE_RANDOM_EVENT, .proc/on_pre_random_event)
3+
4+
/datum/game_mode/dynamic/proc/on_pre_random_event(datum/source, datum/round_event_control/round_event_control)
5+
if (!round_event_control.dynamic_should_hijack)
6+
return
7+
8+
if (random_event_hijacked != HIJACKED_NOTHING)
9+
dynamic_log("Random event [round_event_control.name] tried to roll, but Dynamic vetoed it (random event has already ran).")
10+
SSevents.spawnEvent()
11+
SSevents.reschedule()
12+
return CANCEL_PRE_RANDOM_EVENT
13+
14+
var/time_range = rand(random_event_hijack_minimum, random_event_hijack_maximum)
15+
16+
if (world.time - last_midround_injection_attempt < time_range)
17+
random_event_hijacked = HIJACKED_TOO_RECENT
18+
dynamic_log("Random event [round_event_control.name] tried to roll, but the last midround injection \
19+
was too recent. Injection chance has been raised to [get_midround_injection_chance(dry_run = TRUE)]%.")
20+
return CANCEL_PRE_RANDOM_EVENT
21+
22+
if (midround_injection_cooldown - world.time < time_range)
23+
random_event_hijacked = HIJACKED_TOO_SOON
24+
dynamic_log("Random event [round_event_control.name] tried to roll, but the next midround injection \
25+
is too soon. Injection chance has been raised to [get_midround_injection_chance(dry_run = TRUE)]%.")
26+
return CANCEL_PRE_RANDOM_EVENT

code/game/gamemodes/dynamic/readme.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,17 @@ The "Dynamic" key has the following configurable values:
168168
- `threat_curve_width` - A number between 0.5 and 4. Higher value will favour extreme rounds and lower value rounds closer to the average.
169169
- `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.
170170
- `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.
171+
- `random_event_hijack_minimum` - The minimum amount of time for antag random events to be hijacked. (See [Random Event Hijacking](#random-event-hijacking))
172+
- `random_event_hijack_maximum` - The maximum amount of time for antag random events to be hijacked. (See [Random Event Hijacking](#random-event-hijacking))
173+
- `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))
174+
175+
## Random Event "Hijacking"
176+
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.
177+
178+
`/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.
179+
180+
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:
181+
182+
![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)
183+
184+
`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`.

code/modules/antagonists/revenant/revenant_spawn_event.dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
weight = 7
77
max_occurrences = 1
88
min_players = 5
9+
dynamic_should_hijack = TRUE
910

1011

1112
/datum/round_event/ghost_role/revenant

code/modules/antagonists/slaughter/slaughterevent.dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
max_occurrences = 1
66
earliest_start = 1 HOURS
77
min_players = 20
8+
dynamic_should_hijack = TRUE
89

910

1011

code/modules/events/_event.dm

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#define RANDOM_EVENT_ADMIN_INTERVENTION_TIME 10
2+
13
//this singleton datum is used by the events controller to dictate how it selects events
24
/datum/round_event_control
35
var/name //The human-readable name of the event
@@ -26,6 +28,9 @@
2628

2729
var/triggering //admin cancellation
2830

31+
/// Whether or not dynamic should hijack this event
32+
var/dynamic_should_hijack = FALSE
33+
2934
/datum/round_event_control/New()
3035
if(config && !wizardevent) // Magic is unaffected by configs
3136
earliest_start = CEILING(earliest_start * CONFIG_GET(number/events_min_time_mul), 1)
@@ -55,16 +60,24 @@
5560
return FALSE
5661
if(ispath(typepath, /datum/round_event/ghost_role) && !(GLOB.ghost_role_flags & GHOSTROLE_MIDROUND_EVENT))
5762
return FALSE
63+
64+
var/datum/game_mode/dynamic/dynamic = SSticker.mode
65+
if (istype(dynamic) && dynamic_should_hijack && dynamic.random_event_hijacked != HIJACKED_NOTHING)
66+
return FALSE
67+
5868
return TRUE
5969

6070
/datum/round_event_control/proc/preRunEvent()
6171
if(!ispath(typepath, /datum/round_event))
6272
return EVENT_CANT_RUN
6373

74+
if (SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PRE_RANDOM_EVENT, src) & CANCEL_PRE_RANDOM_EVENT)
75+
return EVENT_INTERRUPTED
76+
6477
triggering = TRUE
6578
if (alert_observers)
66-
message_admins("Random Event triggering in 10 seconds: [name] (<a href='?src=[REF(src)];cancel=1'>CANCEL</a>)")
67-
sleep(100)
79+
message_admins("Random Event triggering in [RANDOM_EVENT_ADMIN_INTERVENTION_TIME] seconds: [name] (<a href='?src=[REF(src)];cancel=1'>CANCEL</a>)")
80+
sleep(RANDOM_EVENT_ADMIN_INTERVENTION_TIME SECONDS)
6881
var/gamemode = SSticker.mode.config_tag
6982
var/players_amt = get_active_player_count(alive_check = TRUE, afk_check = TRUE, human_check = TRUE)
7083
if(!canSpawnEvent(players_amt, gamemode))
@@ -215,3 +228,5 @@
215228
processing = my_processing
216229
SSevents.running += src
217230
return ..()
231+
232+
#undef RANDOM_EVENT_ADMIN_INTERVENTION_TIME

code/modules/events/abductor.dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
weight = 10
55
max_occurrences = 1
66
min_players = 20
7+
dynamic_should_hijack = TRUE
78
gamemode_blacklist = list("nuclear","wizard","revolution")
89

910
/datum/round_event/ghost_role/abductor

code/modules/events/alien_infestation.dm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
min_players = 10
77

8+
dynamic_should_hijack = TRUE
9+
810
/datum/round_event_control/alien_infestation/canSpawnEvent()
911
. = ..()
1012
if(!.)

code/modules/events/blob.dm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
min_players = 20
88

9+
dynamic_should_hijack = TRUE
10+
911
gamemode_blacklist = list("blob") //Just in case a blob survives that long
1012

1113
/datum/round_event_control/blob/canSpawnEvent(players, gamemode)

code/modules/events/nightmare.dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
typepath = /datum/round_event/ghost_role/nightmare
44
max_occurrences = 1
55
min_players = 20
6+
dynamic_should_hijack = TRUE
67

78
/datum/round_event/ghost_role/nightmare
89
minimum_required = 1

code/modules/events/operative.dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
typepath = /datum/round_event/ghost_role/operative
44
weight = 0 //Admin only
55
max_occurrences = 1
6+
dynamic_should_hijack = TRUE
67

78
/datum/round_event/ghost_role/operative
89
minimum_required = 1

code/modules/events/pirates.dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
max_occurrences = 1
66
min_players = 10
77
earliest_start = 30 MINUTES
8+
dynamic_should_hijack = TRUE
89
gamemode_blacklist = list("nuclear")
910

1011
#define PIRATES_ROGUES "Rogues"

code/modules/events/space_dragon.dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
weight = 10
55
max_occurrences = 1
66
min_players = 20
7+
dynamic_should_hijack = TRUE
78

89
/datum/round_event/ghost_role/space_dragon
910
minimum_required = 1

code/modules/events/space_ninja.dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
weight = 10
66
earliest_start = 20 MINUTES
77
min_players = 20
8+
dynamic_should_hijack = TRUE
89

910
/datum/round_event/ghost_role/space_ninja
1011
minimum_required = 1

code/modules/events/spider_infestation.dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
weight = 10
55
max_occurrences = 1
66
min_players = 20
7+
dynamic_should_hijack = TRUE
78

89
/datum/round_event/spider_infestation
910
announceWhen = 400

code/modules/events/swarmer.dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
weight = 10
55
max_occurrences = 1 //Only once okay fam
66
min_players = 20
7+
dynamic_should_hijack = TRUE
78

89
/datum/round_event/spawn_swarmer/announce(fake)
910
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")

tgstation.dme

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,7 @@
815815
#include "code\game\gamemodes\clown_ops\clown_weapons.dm"
816816
#include "code\game\gamemodes\cult\cult.dm"
817817
#include "code\game\gamemodes\dynamic\dynamic.dm"
818+
#include "code\game\gamemodes\dynamic\dynamic_hijacking.dm"
818819
#include "code\game\gamemodes\dynamic\dynamic_logging.dm"
819820
#include "code\game\gamemodes\dynamic\dynamic_rulesets.dm"
820821
#include "code\game\gamemodes\dynamic\dynamic_rulesets_latejoin.dm"

0 commit comments

Comments
 (0)