diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 66912fa787..33683d20ef 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -21,6 +21,10 @@ #define COMSIG_GLOB_PLAY_CINEMATIC "!play_cinematic" #define COMPONENT_GLOB_BLOCK_CINEMATIC 1 +#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 /// from SSsun when the sun changes position : (primary_sun, suns) #define COMSIG_SUN_MOVED "sun_moved" diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 2d0601d839..69dd54e810 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -26,6 +26,7 @@ #define reverseList(L) reverseRange(L.Copy()) #define LAZYADDASSOC(L, K, V) if(!L) { L = list(); } L[K] += list(V); #define LAZYREMOVEASSOC(L, K, V) if(L) { if(L[K]) { L[K] -= V; if(!length(L[K])) L -= K; } if(!length(L)) L = null; } +#define LAZYACCESSASSOC(L, I, K) L ? L[I] ? L[I][K] ? L[I][K] : null : null : null /// Passed into BINARY_INSERT to compare keys #define COMPARE_KEY __BIN_LIST[__BIN_MID] @@ -294,6 +295,22 @@ if (total <= 0) return item +/proc/pickweightAllowZero(list/L) //The original pickweight proc will sometimes pick entries with zero weight. I'm not sure if changing the original will break anything, so I left it be. + var/total = 0 + var/item + for (item in L) + if (!L[item]) + L[item] = 0 + total += L[item] + + total = rand(0, total) + for (item in L) + total -=L [item] + if (total <= 0 && L[item]) + return item + + return null + //Picks a number of elements from a list based on weight. //This is highly optimised and good for things like grabbing 200 items from a list of 40,000 //Much more efficient than many pickweight calls diff --git a/code/game/gamemodes/dynamic/dynamic_hijacking.dm b/code/game/gamemodes/dynamic/dynamic_hijacking.dm new file mode 100644 index 0000000000..04892ad153 --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_hijacking.dm @@ -0,0 +1,27 @@ +/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) + SIGNAL_HANDLER + 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/modules/events/_event.dm b/code/modules/events/_event.dm index 10f8c60386..b8226704cd 100644 --- a/code/modules/events/_event.dm +++ b/code/modules/events/_event.dm @@ -1,3 +1,5 @@ +#define RANDOM_EVENT_ADMIN_INTERVENTION_TIME 10 + //this 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