diff --git a/baystation12.dme b/baystation12.dme
index 7c4e4f1554..d13c553649 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -848,6 +848,7 @@
#include "code\modules\events\disease_outbreak.dm"
#include "code\modules\events\electrical_storm.dm"
#include "code\modules\events\event.dm"
+#include "code\modules\events\event_container.dm"
#include "code\modules\events\event_dynamic.dm"
#include "code\modules\events\event_manager.dm"
#include "code\modules\events\grid_check.dm"
diff --git a/code/game/gamemodes/calamity/calamity.dm b/code/game/gamemodes/calamity/calamity.dm
index d22b8fad01..8120153b56 100644
--- a/code/game/gamemodes/calamity/calamity.dm
+++ b/code/game/gamemodes/calamity/calamity.dm
@@ -83,12 +83,14 @@
/datum/game_mode/calamity/post_setup()
// Reduce the interval between moderate/major events
- event_manager.delay_modifier[EVENT_LEVEL_MODERATE] = 0.5
- event_manager.delay_modifier[EVENT_LEVEL_MAJOR] = 0.75
+ var/datum/event_container/EModerate = event_manager.event_containers[EVENT_LEVEL_MODERATE]
+ var/datum/event_container/EMajor = event_manager.event_containers[EVENT_LEVEL_MAJOR]
+ EModerate.delay_modifier = 0.5
+ EMajor.delay_modifier = 0.75
// Add the cortical borer event
- var/list/moderate_event_list = event_manager.available_events[EVENT_LEVEL_MODERATE]
- var/event = new /datum/event_meta(EVENT_LEVEL_MODERATE, "Borer Infestation", /datum/event/borer_infestation, 400)
+ var/list/moderate_event_list = EModerate.available_events
+ var/event = new /datum/event_meta(EVENT_LEVEL_MODERATE, "Borer Infestation", /datum/event/borer_infestation, 400, one_shot = 1)
moderate_event_list.Add(event)
if(chosen_atypes)
diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm
index a84a4fefda..813becdccf 100644
--- a/code/modules/events/alien_infestation.dm
+++ b/code/modules/events/alien_infestation.dm
@@ -3,7 +3,6 @@
/datum/event/alien_infestation
name = "Alien Infestation"
announceWhen = 400
- oneShot = 1
var/spawncount = 1
var/successSpawn = 0 //So we don't make a command report if nothing gets spawned.
diff --git a/code/modules/events/borers.dm b/code/modules/events/borers.dm
index 54c16deaed..38d560175e 100644
--- a/code/modules/events/borers.dm
+++ b/code/modules/events/borers.dm
@@ -2,7 +2,6 @@
/datum/event/borer_infestation
name = "Borer Infestation"
- oneShot = 1
/datum/event/borer_infestation
announceWhen = 400
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index 0f9412527b..3ea8e0e023 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -2,7 +2,6 @@
name = "Brand Intelligence"
announceWhen = 21
endWhen = 1000 //Ends when all vending machines are subverted anyway.
- oneShot = 1
var/list/obj/machinery/vending/vendingMachines = list()
var/list/obj/machinery/vending/infectedVendingMachines = list()
diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm
index 94ca69c9a5..ceb4bac0bf 100644
--- a/code/modules/events/carp_migration.dm
+++ b/code/modules/events/carp_migration.dm
@@ -1,7 +1,6 @@
/datum/event/carp_migration
name = "Carp Migration"
announceWhen = 50
- oneShot = 1
endWhen = 900
var/list/spawned_carp = list()
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index 65f56391c3..67409a9ea0 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -1,7 +1,6 @@
/datum/event/disease_outbreak
name = "Disease Outbreak"
announceWhen = 15
- oneShot = 1
/datum/event/disease_outbreak/announce()
diff --git a/code/modules/events/event.dm b/code/modules/events/event.dm
index 4fc288ab6d..f2e0e890f3 100644
--- a/code/modules/events/event.dm
+++ b/code/modules/events/event.dm
@@ -1,29 +1,37 @@
/datum/event_meta
- var/name = ""
- var/weight = 1
- var/min_weight = 1
- var/max_weight = 1
- var/severity = 0
- var/has_fired = 0
+ var/name = ""
+ var/enabled = 1 // Whether or not the event is available for random selection at all
+ var/weight = 0 // The base weight of this event. A zero means it may never fire, but see get_weight()
+ var/min_weight = 0 // The minimum weight that this event will have. Only used if non-zero.
+ var/max_weight = 0 // The maximum weight that this event will have. Only use if non-zero.
+ var/severity = 0 // The current severity of this event
+ var/one_shot = 0 //If true, then the event will not be re-added to the list of available events
var/list/role_weights = list()
var/datum/event/event_type
-/datum/event_meta/New(var/event_severity, var/event_name, var/datum/event/type, var/event_weight, var/list/job_weights, var/min_event_weight, var/max_event_weight)
+/datum/event_meta/New(var/event_severity, var/event_name, var/datum/event/type, var/event_weight, var/list/job_weights, var/is_one_shot = 0, var/min_event_weight = 0, var/max_event_weight = 0)
name = event_name
severity = event_severity
event_type = type
+ one_shot = is_one_shot
weight = event_weight
+ min_weight = min_event_weight
+ max_weight = max_event_weight
if(job_weights)
role_weights = job_weights
/datum/event_meta/proc/get_weight(var/list/active_with_role)
+ if(!enabled)
+ return 0
+
var/job_weight = 0
for(var/role in role_weights)
- job_weight = active_with_role[role] * role_weights[role]
+ if(role in active_with_role)
+ job_weight += active_with_role[role] * role_weights[role]
var/total_weight = weight + job_weight
- // Only min/max the weight if the values are set
+ // Only min/max the weight if the values are non-zero
if(min_weight && total_weight < min_weight) total_weight = min_weight
if(max_weight && total_weight > max_weight) total_weight = max_weight
@@ -44,7 +52,6 @@
var/startWhen = 0 //When in the lifetime to call start().
var/announceWhen = 0 //When in the lifetime to call announce().
var/endWhen = 0 //When in the lifetime the event should end.
- var/oneShot = 0 //If true, then the event removes itself from the list of potential events on creation.
var/severity = 0 //Severity. Lower means less severe, higher means more severe. Does not have to be supported. Is set on New().
var/activeFor = 0 //How long the event has existed. You don't need to change this.
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
new file mode 100644
index 0000000000..0220ea17cb
--- /dev/null
+++ b/code/modules/events/event_container.dm
@@ -0,0 +1,178 @@
+#define ASSIGNMENT_ANY "Any"
+#define ASSIGNMENT_AI "AI"
+#define ASSIGNMENT_CYBORG "Cyborg"
+#define ASSIGNMENT_ENGINEER "Engineer"
+#define ASSIGNMENT_GARDENER "Gardener"
+#define ASSIGNMENT_JANITOR "Janitor"
+#define ASSIGNMENT_MEDICAL "Medical"
+#define ASSIGNMENT_SCIENTIST "Scientist"
+#define ASSIGNMENT_SECURITY "Security"
+
+var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT_LEVEL_MODERATE = "Moderate", EVENT_LEVEL_MAJOR = "Major")
+
+/datum/event_container
+ var/severity = -1
+ var/delayed = 0
+ var/delay_modifier = 1
+ var/next_event_time = 0
+ var/list/available_events
+ var/list/last_event_time = list()
+ var/datum/event_meta/next_event = null
+
+ var/last_world_time = 0
+
+/datum/event_container/proc/process()
+ if(!next_event_time)
+ set_event_delay()
+
+ if(delayed)
+ next_event_time += (world.timeofday - last_world_time)
+ else if(world.timeofday > next_event_time)
+ start_event()
+
+ last_world_time = world.timeofday
+
+/datum/event_container/proc/start_event()
+ if(!next_event) // If non-one has explicitly set an event, randomly pick one
+ next_event = acquire_event()
+
+ // Has an event been acquired?
+ if(next_event)
+ // Set when the event of this type was last fired, and prepare the next event start
+ last_event_time[next_event] = world.timeofday
+ set_event_delay()
+ next_event.enabled = !next_event.one_shot // This event will no longer be available in the random rotation if one shot
+
+ var/datum/event/E = new next_event.event_type(next_event) // Events are added and removed from the processing queue in their New/kill procs
+
+ log_debug("Starting event '[E.name]' of severity [severity_to_string[severity]].")
+ next_event = null // When set to null, a random event will be selected next time
+ else
+ // If not, wait for one minute, instead of one tick, before checking again.
+ next_event_time += (60 * 10)
+
+
+/datum/event_container/proc/acquire_event()
+ if(available_events.len == 0)
+ return
+ var/active_with_role = number_active_with_role()
+
+ var/list/possible_events = list()
+ for(var/datum/event_meta/EM in available_events)
+ var/event_weight = EM.get_weight(active_with_role)
+ if(EM.enabled && event_weight)
+ possible_events[EM] = event_weight
+
+ for(var/event_meta in last_event_time) if(possible_events[event_meta])
+ var/time_passed = world.timeofday - event_last_fired[event_meta]
+ var/weight_modifier = max(0, (config.expected_round_length - time_passed) / 300)
+ var/new_weight = max(possible_events[event_meta] - weight_modifier, 0)
+
+ if(new_weight)
+ possible_events[event_meta] = new_weight
+ else
+ possible_events -= event_meta
+
+ if(possible_events.len == 0)
+ return null
+
+ // Select an event and remove it from the pool of available events
+ var/picked_event = pickweight(possible_events)
+ available_events -= picked_event
+ return picked_event
+
+/datum/event_container/proc/set_event_delay()
+ // If the next event time has not yet been set and we have a custom first time start
+ if(next_event_time == 0 && config.event_first_run[severity])
+ var/lower = config.event_first_run[severity]["lower"]
+ var/upper = config.event_first_run[severity]["upper"]
+ var/event_delay = rand(lower, upper)
+ next_event_time = world.timeofday + event_delay
+ // Otherwise, follow the standard setup process
+ else
+ var/playercount_modifier = 1
+ switch(player_list.len)
+ if(0 to 10)
+ playercount_modifier = 1.2
+ if(11 to 15)
+ playercount_modifier = 1.1
+ if(16 to 25)
+ playercount_modifier = 1
+ if(26 to 35)
+ playercount_modifier = 0.9
+ if(36 to 100000)
+ playercount_modifier = 0.8
+ playercount_modifier = playercount_modifier * delay_modifier
+
+ var/event_delay = rand(config.event_delay_lower[severity], config.event_delay_upper[severity]) * playercount_modifier
+ next_event_time = world.timeofday + event_delay
+
+ log_debug("Next event of severity [severity_to_string[severity]] in [(next_event_time - world.timeofday)/600] minutes.")
+
+/datum/event_container/proc/SelectEvent()
+ var/datum/event_meta/EM = input("Select an event to queue up.", "Event Selection", null) as null|anything in available_events
+ if(!EM)
+ return
+ if(next_event)
+ available_events += next_event
+ available_events -= EM
+ next_event = EM
+
+/datum/event_container/mundane
+ severity = EVENT_LEVEL_MUNDANE
+ available_events = list(
+ // Severity level, event name, even type, base weight, role weights, one shot, min weight, max weight. Last two only used if set and non-zero
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Nothing", /datum/event/nothing, 100),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "PDA Spam", /datum/event/pda_spam, 0, list(ASSIGNMENT_ANY = 4), 0, 25, 50),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Lotto", /datum/event/money_lotto, 0, list(ASSIGNMENT_ANY = 1), 1, 5, 15),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Hacker", /datum/event/money_hacker, 0, list(ASSIGNMENT_ANY = 4), 1, 10, 25),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Economic News", /datum/event/economic_event, 300),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Trivial News", /datum/event/trivial_news, 400),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Mundane News", /datum/event/mundane_news, 300),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Lost Carp", /datum/event/carp_migration, 20, list(ASSIGNMENT_SECURITY = 10), 1),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Brand Intelligence",/datum/event/brand_intelligence,20, list(ASSIGNMENT_JANITOR = 25), 1),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Vermin Infestation",/datum/event/infestation, 100, list(ASSIGNMENT_JANITOR = 100)),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Wall root", /datum/event/wallrot, 0, list(ASSIGNMENT_ENGINEER = 30, ASSIGNMENT_GARDENER = 50)),
+ )
+
+/datum/event_container/moderate
+ severity = EVENT_LEVEL_MODERATE
+ available_events = list(
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Nothing", /datum/event/nothing, 10),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Carp School", /datum/event/carp_migration, 20, list(ASSIGNMENT_SECURITY = 10), 1),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Rogue Drones", /datum/event/rogue_drone, 5, list(ASSIGNMENT_ENGINEER = 25, ASSIGNMENT_SECURITY = 25)),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Space vines", /datum/event/spacevine, 10, list(ASSIGNMENT_ENGINEER = 5)),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Meteor Shower", /datum/event/meteor_shower, 0, list(ASSIGNMENT_ENGINEER = 10)),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Communication Blackout", /datum/event/communications_blackout, 50, list(ASSIGNMENT_AI = 25, ASSIGNMENT_SECURITY = 25)),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Prison Break", /datum/event/prison_break, 0, list(ASSIGNMENT_SECURITY = 50)),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Grid Check", /datum/event/grid_check, 25, list(ASSIGNMENT_ENGINEER = 10)),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Electrical Storm", /datum/event/electrical_storm, 15, list(ASSIGNMENT_ENGINEER = 5, ASSIGNMENT_JANITOR = 15)),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Radiation Storm", /datum/event/radiation_storm, 0, list(ASSIGNMENT_MEDICAL = 10), 1),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Appendicitis", /datum/event/spontaneous_appendicitis, 0, list(ASSIGNMENT_MEDICAL = 10), 1),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Viral Infection", /datum/event/viral_infection, 0, list(ASSIGNMENT_MEDICAL = 10)),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Spider Infestation", /datum/event/spider_infestation, 5, list(ASSIGNMENT_SECURITY = 5), 1),
+ new /datum/event_meta/alien(EVENT_LEVEL_MODERATE, "Alien Infestation", /datum/event/alien_infestation, 2.5,list(ASSIGNMENT_SECURITY = 1), 1, 0, 5),
+ new /datum/event_meta/ninja(EVENT_LEVEL_MODERATE, "Space Ninja", /datum/event/space_ninja, 0, list(ASSIGNMENT_SECURITY = 1), 1, 0, 5),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Ion Storm", /datum/event/ionstorm, 0, list(ASSIGNMENT_AI = 25, ASSIGNMENT_CYBORG = 25, ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_SCIENTIST = 5)),
+ )
+
+/datum/event_container/major
+ severity = EVENT_LEVEL_MAJOR
+ available_events = list(
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Nothing", /datum/event/nothing, 50),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Carp Migration", /datum/event/carp_migration, 0, list(ASSIGNMENT_SECURITY = 10), 1),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Viral Infection", /datum/event/viral_infection, 0, list(ASSIGNMENT_MEDICAL = 10), 1),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Blob", /datum/event/blob, 0, list(ASSIGNMENT_ENGINEER = 10), 1),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Meteor Wave", /datum/event/meteor_wave, 0, list(ASSIGNMENT_ENGINEER = 10), 1),
+ )
+
+
+#undef ASSIGNMENT_ANY
+#undef ASSIGNMENT_AI
+#undef ASSIGNMENT_CYBORG
+#undef ASSIGNMENT_ENGINEER
+#undef ASSIGNMENT_GARDENER
+#undef ASSIGNMENT_JANITOR
+#undef ASSIGNMENT_MEDICAL
+#undef ASSIGNMENT_SCIENTIST
+#undef ASSIGNMENT_SECURITY
diff --git a/code/modules/events/event_manager.dm b/code/modules/events/event_manager.dm
index c74e302b46..e01b835352 100644
--- a/code/modules/events/event_manager.dm
+++ b/code/modules/events/event_manager.dm
@@ -1,72 +1,23 @@
-#define ASSIGNMENT_ANY "Any"
-#define ASSIGNMENT_AI "AI"
-#define ASSIGNMENT_CYBORG "Cyborg"
-#define ASSIGNMENT_ENGINEER "Engineer"
-#define ASSIGNMENT_GARDENER "Gardener"
-#define ASSIGNMENT_JANITOR "Janitor"
-#define ASSIGNMENT_MEDICAL "Medical"
-#define ASSIGNMENT_SCIENTIST "Scientist"
-#define ASSIGNMENT_SECURITY "Security"
-
-var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT_LEVEL_MODERATE = "Moderate", EVENT_LEVEL_MAJOR = "Major")
-
/datum/event_manager
- var/list/available_events = list(
- EVENT_LEVEL_MUNDANE = list(
- // Severity level, event name, even type, base weight, role weights, min weight, max weight. Last two only used if set and non-zero
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Nothing", /datum/event/nothing, 100),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "PDA Spam", /datum/event/pda_spam, 0, list(ASSIGNMENT_ANY = 4), 25, 200),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Lotto", /datum/event/money_lotto, 0, list(ASSIGNMENT_ANY = 1), 5, 50),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Hacker", /datum/event/money_hacker, 0, list(ASSIGNMENT_ANY = 4), 25, 200),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Economic News", /datum/event/economic_event, 300),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Trivial News", /datum/event/trivial_news, 400),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Mundane News", /datum/event/mundane_news, 300),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Carp", /datum/event/carp_migration, 20, list(ASSIGNMENT_SECURITY = 10)),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Wall root", /datum/event/wallrot, 0, list(ASSIGNMENT_ENGINEER = 30, ASSIGNMENT_GARDENER = 50)),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Brand Intelligence", /datum/event/brand_intelligence, 20, list(ASSIGNMENT_JANITOR = 25)),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Vermin Infestation", /datum/event/infestation, 100, list(ASSIGNMENT_JANITOR = 100)),
- ),
- EVENT_LEVEL_MODERATE = list(
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Nothing", /datum/event/nothing, 10),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Carp Infestation", /datum/event/carp_migration, 20, list(ASSIGNMENT_SECURITY = 10)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Rogue Drones", /datum/event/rogue_drone, 5, list(ASSIGNMENT_ENGINEER = 25, ASSIGNMENT_SECURITY = 25)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Space vines", /datum/event/spacevine, 10, list(ASSIGNMENT_ENGINEER = 5)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Meteor Shower", /datum/event/meteor_shower, 0, list(ASSIGNMENT_ENGINEER = 10)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Communication Blackout", /datum/event/communications_blackout, 50, list(ASSIGNMENT_AI = 25, ASSIGNMENT_SECURITY = 25)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Grid Check", /datum/event/grid_check, 25, list(ASSIGNMENT_ENGINEER = 10)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Electrical Storm", /datum/event/electrical_storm, 15, list(ASSIGNMENT_ENGINEER = 5, ASSIGNMENT_JANITOR = 15)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Radiation Storm", /datum/event/radiation_storm, 0, list(ASSIGNMENT_MEDICAL = 10)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Appendicitis", /datum/event/spontaneous_appendicitis, 0, list(ASSIGNMENT_MEDICAL = 10)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Viral Infection", /datum/event/viral_infection, 0, list(ASSIGNMENT_MEDICAL = 10)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Spider Infestation",/datum/event/spider_infestation, 5, list(ASSIGNMENT_SECURITY = 5)),
- new /datum/event_meta/alien(EVENT_LEVEL_MODERATE, "Alien Infestation", /datum/event/alien_infestation, 2.5,list(ASSIGNMENT_SECURITY = 1), max_event_weight = 5),
- new /datum/event_meta/ninja(EVENT_LEVEL_MODERATE, "Space Ninja", /datum/event/space_ninja, 0, list(ASSIGNMENT_SECURITY = 1), max_event_weight = 5),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Ion Storm", /datum/event/ionstorm, 0, list(ASSIGNMENT_AI = 25, ASSIGNMENT_CYBORG = 25, ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_SCIENTIST = 5)),
- ),
- EVENT_LEVEL_MAJOR = list(
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Nothing", /datum/event/nothing, 100),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Carp Migration", /datum/event/carp_migration, 0, list(ASSIGNMENT_SECURITY = 10)),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Viral Infection", /datum/event/viral_infection, 0, list(ASSIGNMENT_MEDICAL = 10)),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Blob", /datum/event/blob, 0, list(ASSIGNMENT_ENGINEER = 10)),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Meteor Wave", /datum/event/meteor_wave, 0, list(ASSIGNMENT_ENGINEER = 10)),
- )
- )
-
- var/window_x = 370
- var/window_y = 530
+ var/window_x = 700
+ var/window_y = 600
var/table_options = " align='center'"
var/row_options1 = " width='85px'"
var/row_options2 = " width='260px'"
+ var/row_options3 = " width='150px'"
+ var/datum/event_container/selected_event_container = null
var/list/datum/event/active_events = list()
var/list/datum/event/finished_events = list()
var/list/datum/event/allEvents
+ var/list/datum/event_container/event_containers = list(
+ EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane,
+ EVENT_LEVEL_MODERATE = new/datum/event_container/moderate,
+ EVENT_LEVEL_MAJOR = new/datum/event_container/major
+ )
- var/list/last_event_time = list()
- var/list/next_event = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = null)
- var/list/next_event_time = list(EVENT_LEVEL_MUNDANE = 0, EVENT_LEVEL_MODERATE = 0, EVENT_LEVEL_MAJOR = 0)
- var/list/delay_modifier = list(EVENT_LEVEL_MUNDANE = 1, EVENT_LEVEL_MODERATE = 1, EVENT_LEVEL_MAJOR = 1)
+ var/datum/event_meta/new_event = new
/datum/event_manager/New()
allEvents = typesof(/datum/event) - /datum/event
@@ -76,85 +27,8 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
E.process()
for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
- // Is it time to fire a new event of this severity level?
- if(world.timeofday > next_event_time[i])
- process_event_start(i)
-
-/datum/event_manager/proc/process_event_start(var/severity)
- var/event = next_event[severity]
- if(event)
- start_event(event)
-
- // Attempt to select a new event
- next_event[severity] = acquire_event(available_events[severity])
- if(next_event[severity]) // If we got an event, set the next delay proper
- set_event_delay(severity)
- else // Otherwise, wait for one minute (rather than next process tick) before checking again
- next_event_time[severity] += (60 * 10)
-
-/datum/event_manager/proc/start_event(var/datum/event_meta/EM)
- // Set when the event of this type was last fired
- last_event_time[EM] = world.timeofday
-
- log_debug("Starting event of severity [EM.severity].")
- new EM.event_type(EM) // Events are added and removed from the processing queue in New/Del
-
-/datum/event_manager/proc/acquire_event(var/list/events)
- if(events.len == 0)
- return
- var/active_with_role = number_active_with_role()
-
- var/list/possible_events = list()
- for(var/datum/event_meta/EM in events)
- var/event_weight = EM.get_weight(active_with_role)
- if(event_weight)
- possible_events[EM] = event_weight
-
- for(var/event_meta in last_event_time) if(possible_events[event_meta])
- var/time_passed = world.timeofday - event_last_fired[event_meta]
- var/weight_modifier = max(0, (config.expected_round_length - time_passed) / 300)
- var/new_weight = max(possible_events[event_meta] - weight_modifier, 0)
-
- if(new_weight)
- possible_events[event_meta] = new_weight
- else
- possible_events -= event_meta
-
- if(possible_events.len == 0)
- return null
-
- // Select an event and remove it from the pool of available events
- var/picked_event = pickweight(possible_events)
- events -= picked_event
- return picked_event
-
-/datum/event_manager/proc/set_event_delay(var/severity)
- // If the next event time has not yet been set and we have a custom first time start
- if(next_event_time[severity] == 0 && config.event_first_run[severity])
- var/lower = config.event_first_run[severity]["lower"]
- var/upper = config.event_first_run[severity]["upper"]
- var/event_delay = rand(lower, upper)
- next_event_time[severity] = world.timeofday + event_delay
- // Otherwise, follow the standard setup process
- else
- var/playercount_modifier = 1
- switch(player_list.len)
- if(0 to 10)
- playercount_modifier = 1.2
- if(11 to 15)
- playercount_modifier = 1.1
- if(16 to 25)
- playercount_modifier = 1
- if(26 to 35)
- playercount_modifier = 0.9
- if(36 to 100000)
- playercount_modifier = 0.8
- playercount_modifier = playercount_modifier * delay_modifier[severity]
-
- var/event_delay = rand(config.event_delay_lower[severity], config.event_delay_upper[severity]) * playercount_modifier
- next_event_time[severity] = world.timeofday + event_delay
-
- log_debug("Next event of severity [severity] in [(next_event_time[severity] - world.timeofday)/600] minutes.")
+ var/list/datum/event_container/EC = event_containers[i]
+ EC.process()
/datum/event_manager/proc/event_complete(var/datum/event/E)
if(!E.event_meta) // datum/event is used here and there for random reasons
@@ -162,10 +36,10 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
return
finished_events += E
- // Add the event back to the list of available events, unless it's a oneShot
- if(!E.oneShot)
- var/list/datum/event_meta/AE = available_events[E.event_meta.severity]
- AE.Add(E.event_meta)
+
+ // Add the event back to the list of available events
+ var/datum/event_container/EC = event_containers[E.severity]
+ EC.available_events += E.event_meta
log_debug("Event '[E.name]' has completed.")
@@ -179,39 +53,103 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
popup.open()
/datum/event_manager/proc/GetInteractWindow()
- var html = "Refresh
"
+ var/html = "Refresh"
- html += "
"
- html += "
Event Start
"
- html += "
"
- html += "| Severity | Until start | Adjust start |
"
- for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
- var/event_time = max(0, next_event_time[severity] - world.timeofday)
- html += ""
- html += "| [severity_to_string[severity]] | "
- html += "[event_time / 600] | "
- html += ""
- html += "--"
- html += "-"
- html += "+"
- html += "++"
- html += " | "
- html += "
"
- html += "
"
- html += "
"
+ if(selected_event_container)
+ var/event_time = max(0, selected_event_container.next_event_time - world.timeofday)
+ html += "Back
"
+ html += "Time till start: [round(event_time / 600, 0.1)]
"
+ html += ""
+ html += "
Available [severity_to_string[selected_event_container.severity]] Events (queued events will not be displayed)
"
+ html += "
"
+ html += "| Name | Weight | MinWeight | MaxWeight | OneShot | Enabled | CurrWeight | Remove |
"
+ for(var/datum/event_meta/EM in selected_event_container.available_events)
+ html += ""
+ html += "| [EM.name] | "
+ html += "[EM.weight] | "
+ html += "[EM.min_weight] | "
+ html += "[EM.max_weight] | "
+ html += "[EM.one_shot] | "
+ html += "[EM.enabled] | "
+ html += "[EM.get_weight()] | "
+ html += "Remove | "
+ html += "
"
+ html += "
"
+ html += "
"
- html += ""
- html += "
Next Event
"
- html += "
"
- html += "| Severity | Name |
"
- for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
- var/datum/event_meta/EM = next_event[severity]
+ html += ""
+ html += "
Add Event
"
+ html += "
"
- html += "
"
+ html += "
"
+ html += "
Add"
+ html += "
"
+ else
+ html += "
"
+ html += ""
+ html += "
Event Start
"
+
+ html += "
"
+ html += "| Severity | Until start | Adjust start | Pause | Interval Mod |
"
+ for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
+ var/datum/event_container/EC = event_containers[severity]
+ var/event_time = max(0, EC.next_event_time - world.timeofday)
+ html += ""
+ html += "| [severity_to_string[severity]] | "
+ html += "[round(event_time / 600, 0.1)] | "
+ html += ""
+ html += "--"
+ html += "-"
+ html += "+"
+ html += "++"
+ html += " | "
+ html += ""
+ html += "[EC.delayed ? "Resume" : "Pause"]"
+ html += " | "
+ html += ""
+ html += "[EC.delay_modifier]"
+ html += " | "
+ html += "
"
+ html += "
"
+ html += "
"
+
+ html += ""
+ html += "
Next Event
"
+ html += "
"
+ html += "| Severity | Name | Event Rotation | Clear |
"
+ for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
+ var/datum/event_container/EC = event_containers[severity]
+ var/datum/event_meta/EM = EC.next_event
+ html += ""
+ html += "| [severity_to_string[severity]] | "
+ html += "[EM ? EM.name : "Random"] | "
+ html += "View | "
+ html += "Clear | "
+ html += "
"
+ html += "
"
+ html += "
"
+
+ html += ""
+ html += "
Running Events
"
+ html += "
"
+ html += "| Severity | Name | Stop |
"
+ for(var/datum/event/E in active_events)
+ if(!E.event_meta)
+ continue
+ var/datum/event_meta/EM = E.event_meta
+ html += ""
+ html += "| [severity_to_string[EM.severity]] | "
+ html += "[EM.name] | "
+ html += "Stop | "
+ html += "
"
+ html += "
"
+ html += "
"
return html
@@ -219,27 +157,75 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
if(..())
return
- var/severity = text2num(href_list["severity"])
if(href_list["dec_timer"])
- next_event_time[severity] -= (60 * RaiseToPower(10, text2num(href_list["dec_timer"])))
+ var/datum/event_container/EC = locate(href_list["event"])
+ EC.next_event_time -= (60 * RaiseToPower(10, text2num(href_list["dec_timer"])))
else if(href_list["inc_timer"])
- next_event_time[severity] += (60 * RaiseToPower(10, text2num(href_list["inc_timer"])))
+ var/datum/event_container/EC = locate(href_list["event"])
+ EC.next_event_time += (60 * RaiseToPower(10, text2num(href_list["inc_timer"])))
else if(href_list["select_event"])
- SelectEvent(severity)
+ var/datum/event_container/EC = locate(href_list["select_event"])
+ EC.SelectEvent()
+ else if(href_list["pause"])
+ var/datum/event_container/EC = locate(href_list["pause"])
+ EC.delayed = !EC.delayed
+ else if(href_list["interval"])
+ var/delay = input("Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") as num|null
+ if(delay && delay > 0)
+ var/datum/event_container/EC = locate(href_list["interval"])
+ EC.delay_modifier = delay
+ else if(href_list["stop"])
+ if(alert("Stopping an event may have unintended side-effects. Continue?","Stopping Event!","Yes","No") != "Yes")
+ return
+ var/datum/event/E = locate(href_list["stop"])
+ E.kill()
+ else if(href_list["view_events"])
+ selected_event_container = locate(href_list["view_events"])
+ else if(href_list["back"])
+ selected_event_container = null
+ else if(href_list["set_name"])
+ var/name = input("Enter event name.", "Set Name") as text|null
+ if(name)
+ var/datum/event_meta/EM = locate(href_list["set_name"])
+ EM.name = name
+ else if(href_list["set_type"])
+ var/type = input("Select event type.", "Select") as null|anything in allEvents
+ if(type)
+ var/datum/event_meta/EM = locate(href_list["set_type"])
+ EM.event_type = type
+ else if(href_list["set_weight"])
+ var/weight = input("Enter weight. A higher value means higher chance for the event of being selected.", "Set Weight") as num|null
+ if(weight && weight > 0)
+ var/datum/event_meta/EM = locate(href_list["set_weight"])
+ EM.weight = weight
+ else if(href_list["set_oneshot"])
+ var/datum/event_meta/EM = locate(href_list["set_oneshot"])
+ EM.one_shot = !EM.one_shot
+ else if(href_list["set_enabled"])
+ var/datum/event_meta/EM = locate(href_list["set_enabled"])
+ EM.enabled = !EM.enabled
+ else if(href_list["remove"])
+ if(alert("This will remove the event from rotation. Continue?","Removing Event!","Yes","No") != "Yes")
+ return
+ var/datum/event_meta/EM = locate(href_list["remove"])
+ var/datum/event_container/EC = locate(href_list["EC"])
+ EC.available_events -= EM
+ else if(href_list["add"])
+ if(!new_event.name || !new_event.event_type)
+ return
+ if(alert("This will add a new event to the rotation. Continue?","Add Event!","Yes","No") != "Yes")
+ return
+ selected_event_container.available_events += new_event
+ new_event = new
+ else if(href_list["clear"])
+ var/datum/event_container/EC = locate(href_list["clear"])
+ EC.next_event = null
Interact(usr)
-/datum/event_manager/proc/SelectEvent(var/severity)
- var/datum/event_meta/EM = input("Select an event to queue up.", "Event Selection", null) as null|anything in available_events[severity]
- if(!EM)
- return
- if(next_event[severity])
- available_events[severity] += next_event[severity]
- available_events[severity] -= EM
- next_event[severity] = EM
-
/proc/debugStartEvent(var/severity)
- event_manager.start_event(severity)
+ var/datum/event_container/EC = event_manager.event_containers[severity]
+ EC.start_event()
/client/proc/forceEvent(var/type in event_manager.allEvents)
set name = "Trigger Event (Debug Only)"
@@ -259,13 +245,3 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
event_manager.Interact(usr)
feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
-
-#undef ASSIGNMENT_ANY
-#undef ASSIGNMENT_AI
-#undef ASSIGNMENT_CYBORG
-#undef ASSIGNMENT_ENGINEER
-#undef ASSIGNMENT_GARDENER
-#undef ASSIGNMENT_JANITOR
-#undef ASSIGNMENT_MEDICAL
-#undef ASSIGNMENT_SCIENTIST
-#undef ASSIGNMENT_SECURITY
\ No newline at end of file
diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm
index b1eed0fc03..9c3008e4d0 100644
--- a/code/modules/events/prison_break.dm
+++ b/code/modules/events/prison_break.dm
@@ -1,7 +1,6 @@
/datum/event/prison_break
name = "Prison Break"
announceWhen = 50
- oneShot = 1
var/releaseWhen = 25
var/list/area/prisonAreas = list()
diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm
index 3e7249b368..b88fe7f1b6 100644
--- a/code/modules/events/radiation_storm.dm
+++ b/code/modules/events/radiation_storm.dm
@@ -5,7 +5,6 @@
var/const/revokeAccess = 230
endWhen = revokeAccess
- oneShot = 1
var/postStartTicks
diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm
index c8f270fe13..d6af6f7b5b 100644
--- a/code/modules/events/spacevine.dm
+++ b/code/modules/events/spacevine.dm
@@ -2,7 +2,6 @@
/datum/event/spacevine
name = "Space Vines"
- oneShot = 1
/datum/event/spacevine/start()
//biomass is basically just a resprited version of space vines
diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm
index 6f256e0b8c..6e38f78715 100644
--- a/code/modules/events/spider_infestation.dm
+++ b/code/modules/events/spider_infestation.dm
@@ -3,7 +3,6 @@
/datum/event/spider_infestation
name = "Large Spider Infestation"
announceWhen = 400
- oneShot = 1
var/spawncount = 1