diff --git a/code/__DEFINES/blob_defines.dm b/code/__DEFINES/blob_defines.dm
new file mode 100644
index 00000000000..49713b4a69a
--- /dev/null
+++ b/code/__DEFINES/blob_defines.dm
@@ -0,0 +1 @@
+#define COMSIG_BLOB_MOUSE_BURST "blobmouseburst"
diff --git a/code/__DEFINES/event_defines.dm b/code/__DEFINES/event_defines.dm
new file mode 100644
index 00000000000..5cabcbeaf23
--- /dev/null
+++ b/code/__DEFINES/event_defines.dm
@@ -0,0 +1,31 @@
+#define ASSIGNMENT_CREW "Crew"
+#define ASSIGNMENT_COMMAND "Command"
+#define ASSIGNMENT_ENGINEERING "Engineering"
+#define ASSIGNMENT_MEDICAL "Medical"
+#define ASSIGNMENT_SECURITY "Security"
+#define ASSIGNMENT_SCIENCE "Science"
+#define ASSIGNMENT_AI "AI"
+#define ASSIGNMENT_CYBORG "Cyborg"
+#define ASSIGNMENT_JANITOR "Janitor"
+#define ASSIGNMENT_BOTANIST "Botanist"
+#define ASSIGNMENT_CHAPLAIN "Chaplain"
+#define ASSIGNMENT_CHEMIST "Chemist"
+#define ASSIGNMENT_VIROLOGIST "Virologist"
+#define ASSIGNMENT_CARGO "Cargo Bay"
+
+#define ASSIGNMENT_STAFFING_VALUE 1
+#define EVENT_LEVEL_MUNDANE 1
+#define EVENT_LEVEL_MODERATE 2
+#define EVENT_LEVEL_MAJOR 3
+#define EVENT_LEVEL_DISASTER 4
+
+// Categories for the event tracker component
+#define EVENT_TERROR_SPIDERS "terror_spiders"
+#define EVENT_BLOB "blob"
+#define EVENT_XENOS "xenos"
+#define EVENT_MORPH "morph"
+#define EVENT_CARP "carps"
+#define EVENT_BRAND_INTELLIGENCE "brand_intelligence"
+#define EVENT_DRONE "rogue_drones"
+#define EVENT_DEMONIC "demonic_incursion"
+#define EVENT_REVENTANT "revenant"
diff --git a/code/__DEFINES/misc_defines.dm b/code/__DEFINES/misc_defines.dm
index af350730d81..9b49bf2581c 100644
--- a/code/__DEFINES/misc_defines.dm
+++ b/code/__DEFINES/misc_defines.dm
@@ -62,10 +62,6 @@
#define ZONE_ACTIVE 1
#define ZONE_SLEEPING 0
-#define EVENT_LEVEL_MUNDANE 1
-#define EVENT_LEVEL_MODERATE 2
-#define EVENT_LEVEL_MAJOR 3
-
#define JANUARY 1
#define FEBRUARY 2
#define MARCH 3
diff --git a/code/controllers/configuration/sections/event_configuration.dm b/code/controllers/configuration/sections/event_configuration.dm
index 491e9ba4c0e..cdaacc26d0f 100644
--- a/code/controllers/configuration/sections/event_configuration.dm
+++ b/code/controllers/configuration/sections/event_configuration.dm
@@ -3,23 +3,26 @@
/// Do we want to enable random events at all
var/enable_random_events = TRUE
/// Assoc list of when the first event in a group can run. key: severity | value: assoc list with upper and low bounds (key: "upper"/"lower" | value: time in deciseconds)
- var/list/first_run_times = list(
+ var/list/first_run_times = alist(
EVENT_LEVEL_MUNDANE = null,
- EVENT_LEVEL_MODERATE = null,
- EVENT_LEVEL_MAJOR = list("lower" = 40 MINUTES, "upper" = 50 MINUTES)
+ EVENT_LEVEL_MODERATE = list("lower" = 19 MINUTES, "upper" = 23 MINUTES),
+ EVENT_LEVEL_MAJOR = list("lower" = 29 MINUTES, "upper" = 36 MINUTES),
+ EVENT_LEVEL_DISASTER = list("lower" = 40 MINUTES, "upper" = 50 MINUTES)
) // <---- Whoever designed this needs to be shot
/// Assoc list of lower bounds of event delays. key: severity | value: delay (deciseconds)
- var/list/delay_lower_bound = list(
+ var/list/delay_lower_bound = alist(
EVENT_LEVEL_MUNDANE = 5 MINUTES,
- EVENT_LEVEL_MODERATE = 15 MINUTES,
- EVENT_LEVEL_MAJOR = 25 MINUTES
+ EVENT_LEVEL_MODERATE = 7.5 MINUTES,
+ EVENT_LEVEL_MAJOR = 12 MINUTES,
+ EVENT_LEVEL_DISASTER = 9 MINUTES
)
/// Assoc list of lower bounds of event delays. key: severity | value: delay (deciseconds)
- var/list/delay_upper_bound = list(
+ var/list/delay_upper_bound = alist(
EVENT_LEVEL_MUNDANE = 7.5 MINUTES,
- EVENT_LEVEL_MODERATE = 22.5 MINUTES,
- EVENT_LEVEL_MAJOR = 35 MINUTES
+ EVENT_LEVEL_MODERATE = 12 MINUTES,
+ EVENT_LEVEL_MAJOR = 17 MINUTES,
+ EVENT_LEVEL_DISASTER = 12 MINUTES
)
/// Expected time of a round in deciseconds
var/expected_round_length = 120 MINUTES // This macro is equivilent to 72,000 deciseconds
@@ -36,16 +39,19 @@
expected_round_length = data["expected_round_length"] MINUTES // Convert from minutes to deciseconds
// Load event severities. This is quite awful but needs to be done so we can account for config mistakes. This event system is awful
+
if(islist(data["event_delay_lower_bounds"]))
CONFIG_LOAD_NUM_MULT(delay_lower_bound[EVENT_LEVEL_MUNDANE], data["event_delay_lower_bounds"]["mundane"], MINUTES)
CONFIG_LOAD_NUM_MULT(delay_lower_bound[EVENT_LEVEL_MODERATE], data["event_delay_lower_bounds"]["moderate"], MINUTES)
CONFIG_LOAD_NUM_MULT(delay_lower_bound[EVENT_LEVEL_MAJOR], data["event_delay_lower_bounds"]["major"], MINUTES)
+ CONFIG_LOAD_NUM_MULT(delay_lower_bound[EVENT_LEVEL_DISASTER], data["event_delay_lower_bounds"]["disaster"], MINUTES)
// Same here. I hate this.
if(islist(data["event_delay_upper_bounds"]))
CONFIG_LOAD_NUM_MULT(delay_upper_bound[EVENT_LEVEL_MUNDANE], data["event_delay_upper_bounds"]["mundane"], MINUTES)
CONFIG_LOAD_NUM_MULT(delay_upper_bound[EVENT_LEVEL_MODERATE], data["event_delay_upper_bounds"]["moderate"], MINUTES)
CONFIG_LOAD_NUM_MULT(delay_upper_bound[EVENT_LEVEL_MAJOR], data["event_delay_upper_bounds"]["major"], MINUTES)
+ CONFIG_LOAD_NUM_MULT(delay_upper_bound[EVENT_LEVEL_DISASTER], data["event_delay_upper_bounds"]["disaster"], MINUTES)
// And for the worst, the first run delays. I hate this so much -aa07
if(islist(data["event_initial_delays"]))
@@ -58,7 +64,9 @@
target = EVENT_LEVEL_MODERATE
if("major")
target = EVENT_LEVEL_MAJOR
- ASSERT(target in list(EVENT_LEVEL_MUNDANE, EVENT_LEVEL_MODERATE, EVENT_LEVEL_MAJOR))
+ if("disaster")
+ target = EVENT_LEVEL_DISASTER
+ ASSERT(target in list(EVENT_LEVEL_MUNDANE, EVENT_LEVEL_MODERATE, EVENT_LEVEL_MAJOR, EVENT_LEVEL_DISASTER))
first_run_times[target] = list("lower" = assoclist["lower_bound"] MINUTES, "upper" = assoclist["upper_bound"] MINUTES)
CONFIG_LOAD_NUM(blob_highpop_trigger, data["blob_highpop_trigger"])
diff --git a/code/controllers/subsystem/SSevents.dm b/code/controllers/subsystem/SSevents.dm
index 28ff63dfc37..ccab3509c23 100644
--- a/code/controllers/subsystem/SSevents.dm
+++ b/code/controllers/subsystem/SSevents.dm
@@ -22,15 +22,18 @@ SUBSYSTEM_DEF(events)
var/list/active_events = list()
var/list/finished_events = list()
var/list/allEvents
- var/list/event_containers = list(
+ var/list/event_containers = alist(
EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane,
EVENT_LEVEL_MODERATE = new/datum/event_container/moderate,
- EVENT_LEVEL_MAJOR = new/datum/event_container/major
+ EVENT_LEVEL_MAJOR = new/datum/event_container/major,
+ EVENT_LEVEL_DISASTER = new/datum/event_container/disaster
)
var/datum/event_meta/new_event = new
var/list/biohazards_this_round = list()
+ /// Artificial staffing for debug purpouses
+ var/list/debug_resources = list()
/datum/controller/subsystem/events/Initialize()
allEvents = subtypesof(/datum/event)
@@ -39,8 +42,8 @@ SUBSYSTEM_DEF(events)
for(var/datum/event/E in active_events)
E.process()
- for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
- var/datum/event_container/EC = event_containers[i]
+ for(var/container in event_containers)
+ var/datum/event_container/EC = event_containers[container]
EC.process()
/datum/controller/subsystem/events/proc/event_complete(datum/event/E)
@@ -64,9 +67,9 @@ SUBSYSTEM_DEF(events)
// Add the event back to the list of available events
var/datum/event_container/EC = event_containers[theseverity]
var/datum/event_meta/EM = E.event_meta
- EC.available_events += EM
+ EC.available_events |= EM
- log_debug("Event '[EM.name]' has completed at [station_time_timestamp()].")
+ log_debug("Event '[EM.skeleton.name]' has completed at [station_time_timestamp()].")
/datum/controller/subsystem/events/proc/delay_events(severity, delay)
var/datum/event_container/EC = event_containers[severity]
@@ -87,9 +90,9 @@ SUBSYSTEM_DEF(events)
to_chat(world, "
Random Events This Round:")
for(var/datum/event/E in active_events|finished_events)
var/datum/event_meta/EM = E.event_meta
- if(EM.name == "Nothing")
+ if(EM.skeleton.name == "Nothing")
continue
- var/message = "'[EM.name]' began at [station_time_timestamp("hh:mm:ss", E.startedAt)] "
+ var/message = "'[EM.skeleton.name]' began at [station_time_timestamp("hh:mm:ss", E.startedAt)] "
if(E.isRunning)
message += "and is still running."
else
@@ -102,9 +105,10 @@ SUBSYSTEM_DEF(events)
/datum/controller/subsystem/events/proc/GetInteractWindow()
var/html = "Refresh"
-
+ html += "Resources"
if(selected_event_container)
var/event_time = max(0, selected_event_container.next_event_time - world.time)
+ var/list/total_resources = get_total_resources()
html += "Back
"
html += "Time till start: [round(event_time / 600, 0.1)]
"
html += "
"
@@ -112,14 +116,16 @@ SUBSYSTEM_DEF(events)
html += "
"
html += "| Name | Weight | MinWeight | MaxWeight | OneShot | Enabled | CurrWeight | Remove |
"
for(var/datum/event_meta/EM in selected_event_container.available_events)
+ if(!EM.skeleton)
+ continue
html += ""
- html += "| [EM.name] | "
+ html += "[EM.skeleton.name] | "
html += "[EM.weight] | "
html += "[EM.min_weight] | "
html += "[EM.max_weight == INFINITY ? "No max" : EM.max_weight] | "
html += "[EM.one_shot] | "
html += "[EM.enabled] | "
- html += "[EM.get_weight(number_active_with_role())] | "
+ html += "[EM.get_weight(total_resources)] | "
html += "Remove | "
html += "
"
html += "
"
@@ -130,8 +136,8 @@ SUBSYSTEM_DEF(events)
html += "
"
html += "| Name | Type | Weight | OneShot |
"
html += ""
- html += "| [new_event.name ? new_event.name : "Enter Event"] | "
- html += "[new_event.event_type ? new_event.event_type : "Select Type"] | "
+ html += "[new_event?.skeleton?.name ? new_event?.skeleton?.name : "Enter Event"] | "
+ html += "[new_event?.skeleton?.type ? new_event?.skeleton?.type : "Select Type"] | "
html += "[new_event.weight ? new_event.weight : 0] | "
html += "[new_event.one_shot] | "
html += "
"
@@ -145,7 +151,7 @@ SUBSYSTEM_DEF(events)
html += ""
html += "| Severity | Starts At | Starts In | Adjust Start | Pause | Interval Mod |
"
- for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
+ for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_DISASTER)
var/datum/event_container/EC = event_containers[severity]
var/next_event_at = max(0, EC.next_event_time - world.time)
html += ""
@@ -172,12 +178,12 @@ SUBSYSTEM_DEF(events)
html += "Next Event
"
html += ""
html += "| Severity | Name | Event Rotation | Clear |
"
- for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
+ for(var/severity in EVENT_LEVEL_MUNDANE to EVENT_LEVEL_DISASTER)
var/datum/event_container/EC = event_containers[severity]
var/datum/event_meta/EM = EC.next_event
html += ""
html += "| [GLOB.severity_to_string[severity]] | "
- html += "[EM ? EM.name : "Random"] | "
+ html += "[EM ? EM.skeleton.name : "Random"] | "
html += "View | "
html += "Clear | "
html += "
"
@@ -190,15 +196,14 @@ SUBSYSTEM_DEF(events)
html += ""
html += "| Severity | Name | Ends At | Ends In | Stop |
"
for(var/datum/event/E in active_events)
- if(!E.event_meta)
+ if(!E)
continue
- var/datum/event_meta/EM = E.event_meta
var/ends_at = E.startedAt + (E.lastProcessAt() * 20) // A best estimate, based on how often the manager processes
var/ends_in = max(0, round((ends_at - world.time) / 600, 0.1))
var/no_end = E.noAutoEnd
html += ""
- html += "| [GLOB.severity_to_string[EM.severity]] | "
- html += "[EM.name] [ADMIN_VV(E, "VV")] | "
+ html += "[GLOB.severity_to_string[E.severity]] | "
+ html += "[E.name] [ADMIN_VV(E, "VV")] | "
html += "[no_end ? "N/A" : station_time_timestamp("hh:mm:ss", ends_at)] | "
html += "[no_end ? "N/A" : ends_in] | "
html += "Stop | "
@@ -232,7 +237,7 @@ SUBSYSTEM_DEF(events)
var/datum/event_container/EC = locate(href_list["select_event"])
var/datum/event_meta/EM = EC.SelectEvent()
if(EM)
- log_and_message_admins("has queued the [GLOB.severity_to_string[EC.severity]] event '[EM.name]'.")
+ log_and_message_admins("has queued the [GLOB.severity_to_string[EC.severity]] event '[EM.skeleton.name]'.")
else if(href_list["pause"])
var/datum/event_container/EC = locate(href_list["pause"])
EC.delayed = !EC.delayed
@@ -248,7 +253,7 @@ SUBSYSTEM_DEF(events)
return
var/datum/event/E = locate(href_list["stop"])
var/datum/event_meta/EM = E.event_meta
- log_and_message_admins("has stopped the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
+ log_and_message_admins("has stopped the [GLOB.severity_to_string[EM.skeleton.severity]] event '[EM.skeleton.name]'.")
E.kill()
else if(href_list["view_events"])
selected_event_container = locate(href_list["view_events"])
@@ -258,48 +263,81 @@ SUBSYSTEM_DEF(events)
var/name = clean_input("Enter event name.", "Set Name")
if(name)
var/datum/event_meta/EM = locate(href_list["set_name"])
- EM.name = name
+ EM.skeleton.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
+ EM.change_event(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
if(EM != new_event)
- log_and_message_admins("has changed the weight of the [GLOB.severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].")
+ log_and_message_admins("has changed the weight of the [GLOB.severity_to_string[EM.skeleton.severity]] event '[EM.skeleton.name]' to [EM.weight].")
else if(href_list["toggle_oneshot"])
var/datum/event_meta/EM = locate(href_list["toggle_oneshot"])
EM.one_shot = !EM.one_shot
if(EM != new_event)
- log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
+ log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [GLOB.severity_to_string[EM.skeleton.severity]] event '[EM.skeleton.name]'.")
else if(href_list["toggle_enabled"])
var/datum/event_meta/EM = locate(href_list["toggle_enabled"])
EM.enabled = !EM.enabled
- log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
+ log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [GLOB.severity_to_string[EM.skeleton.severity]] event '[EM.skeleton.name]'.")
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
- log_and_message_admins("has removed the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
+ log_and_message_admins("has removed the [GLOB.severity_to_string[EM.skeleton.severity]] event '[EM.skeleton.name]'.")
else if(href_list["add"])
- if(!new_event.name || !new_event.event_type)
+ if(!new_event.skeleton.name || !new_event.skeleton.type)
return
if(alert("This will add a new event to the rotation. Continue?","Add Event!","Yes","No") != "Yes")
return
- new_event.severity = selected_event_container.severity
+ new_event.skeleton.severity = selected_event_container.severity
selected_event_container.available_events += new_event
- log_and_message_admins("has added \a [GLOB.severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].")
+ log_and_message_admins("has added \a [GLOB.severity_to_string[new_event.skeleton.severity]] event '[new_event.skeleton.name]' of type [new_event.skeleton.type] with weight [new_event.weight].")
new_event = new
else if(href_list["clear"])
var/datum/event_container/EC = locate(href_list["clear"])
if(EC.next_event)
- log_and_message_admins("has dequeued the [GLOB.severity_to_string[EC.severity]] event '[EC.next_event.name]'.")
+ log_and_message_admins("has dequeued the [GLOB.severity_to_string[EC.severity]] event '[EC.next_event.skeleton.name]'.")
EC.next_event = null
+ else if(href_list["resource"])
+ GetResourceWindow()
Interact(usr)
+
+/datum/controller/subsystem/events/proc/GetResourceWindow()
+ var/html = "Refresh"
+ var/list/total_resources = get_total_resources()
+ html += ""
+ html += "
Total Resources
"
+ html += "
"
+ html += "| Name | Weight |
"
+ for(var/resource in total_resources)
+ html += ""
+ html += "| [resource] | "
+ html += "[total_resources[resource]] | "
+ html += "
"
+ html += "
"
+ html += "
"
+ var/list/net_resources = number_active_with_role()
+ html += ""
+ html += "
Net Resources
"
+ html += "
"
+ html += "| Name | Weight |
"
+ for(var/resource in net_resources)
+ html += ""
+ html += "| [resource] | "
+ html += "[net_resources[resource]] | "
+ html += "
"
+ html += "
"
+ html += "
"
+
+ var/datum/browser/popup = new(usr, "resources", "Resources", window_x, window_y)
+ popup.set_content(html)
+ popup.open()
diff --git a/code/datums/components/event_tracker.dm b/code/datums/components/event_tracker.dm
new file mode 100644
index 00000000000..44f21bbce2d
--- /dev/null
+++ b/code/datums/components/event_tracker.dm
@@ -0,0 +1,21 @@
+GLOBAL_LIST_EMPTY(event_trackers)
+
+/datum/component/event_tracker
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+ /// Category to list the event thing under
+ var/category = ""
+
+/datum/component/event_tracker/Initialize(_category)
+ if(!_category)
+ category = "misc"
+ else
+ category = _category
+ GLOB.event_trackers["[category]"] += list(src)
+
+/datum/component/event_tracker/Destroy()
+ GLOB.event_trackers["[category]"] -= list(src)
+ return ..()
+
+/datum/component/event_tracker/proc/event_cost()
+ var/atom/thing = parent
+ return thing.event_cost()
diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm
index c55de30be0b..83f6e150ca3 100644
--- a/code/datums/station_traits/negative_traits.dm
+++ b/code/datums/station_traits/negative_traits.dm
@@ -78,10 +78,10 @@
var/modified_event = FALSE
for(var/datum/event_meta/E in event_severity.available_events)
for(var/i in event_names)
- if(E.name == i)
+ if(E.skeleton.name == i)
E.weight *= weight_multiplier
- for(var/role_weight in E.role_weights)
- E.role_weights[role_weight] *= weight_multiplier
+ for(var/role_weight in E.skeleton.role_weights)
+ E.skeleton.role_weights[role_weight] *= weight_multiplier
if(disable_is_one_shot)
E.one_shot = FALSE
modified_event = TRUE
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 2b92621cd84..acc3ea44fe5 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1514,7 +1514,7 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
if(istype(weapon))
attack_info.last_attacker_weapon = "[weapon] ([weapon.type])"
-// MARK: PTL PROCS
+// MARK: PTL procs
// Called when the target is selected
/atom/proc/on_ptl_target(obj/machinery/power/transmission_laser/ptl)
@@ -1548,3 +1548,11 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
if(ptl)
ptl.target = null
return
+
+// MARK: Event procs
+/// Returns the cost of the atom for the event system as a list of all requirements. By default this is just 1 crew.
+/atom/proc/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z))
+ return list(ASSIGNMENT_CREW = 1)
+
diff --git a/code/game/gamemodes/miniantags/morph/morph.dm b/code/game/gamemodes/miniantags/morph/morph.dm
index dcc7382b7f5..ca570afe7e1 100644
--- a/code/game/gamemodes/miniantags/morph/morph.dm
+++ b/code/game/gamemodes/miniantags/morph/morph.dm
@@ -63,6 +63,12 @@
AddSpell(new /datum/spell/morph_spell/open_vent)
pass_airlock_spell = new
AddSpell(pass_airlock_spell)
+ AddComponent(/datum/component/event_tracker, EVENT_MORPH)
+
+/mob/living/simple_animal/hostile/morph/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 1)
/mob/living/simple_animal/hostile/morph/Destroy()
QDEL_NULL(mimic_spell)
@@ -227,8 +233,8 @@
return ITEM_INTERACT_COMPLETE
playsound(loc, 'sound/weapons/slice.ogg', 50, TRUE, -1)
visible_message(
- "[src] begins to pry open the morph's massive jaws!",
- "You begin to pry open the morph's massive jaws!",
+ "[src] begins to pry open the morph's massive jaws!",
+ "You begin to pry open the morph's massive jaws!",
"You hear wet, meaty tearing nearby!"
)
if(do_after_once(user, 8 SECONDS, target = src))
diff --git a/code/game/gamemodes/miniantags/morph/morph_event.dm b/code/game/gamemodes/miniantags/morph/morph_event.dm
index 2d71c5a55d6..d72a0c9cfa8 100644
--- a/code/game/gamemodes/miniantags/morph/morph_event.dm
+++ b/code/game/gamemodes/miniantags/morph/morph_event.dm
@@ -1,5 +1,19 @@
/datum/event/spawn_morph
+ name = "Morph Spawn"
+ noAutoEnd = TRUE
+ nominal_severity = EVENT_LEVEL_MAJOR
+ role_weights = list(ASSIGNMENT_SECURITY = 3, ASSIGNMENT_CREW = 1)
+ role_requirements = list(ASSIGNMENT_SECURITY = 4, ASSIGNMENT_CREW = 35)
var/key_of_morph
+ var/spawned = FALSE
+
+/datum/event/spawn_morph/tick()
+ if(!length(event_category_cost(EVENT_MORPH)) && spawned)
+ kill()
+
+// Cost is calculated independently of event
+/datum/event/spawn_morph/event_resource_cost()
+ return list()
/datum/event/spawn_morph/proc/get_morph()
spawn()
@@ -30,6 +44,7 @@
S.make_morph_antag()
S.forceMove(vent)
S.add_ventcrawl(vent)
+ spawned = TRUE
dust_if_respawnable(C)
message_admins("[key_of_morph] has been made into morph by an event.")
log_game("[key_of_morph] was spawned as a morph by an event.")
diff --git a/code/game/gamemodes/miniantags/tourist/tourist_arrivals.dm b/code/game/gamemodes/miniantags/tourist/tourist_arrivals.dm
index 545b67f3a9d..f93943c3a54 100644
--- a/code/game/gamemodes/miniantags/tourist/tourist_arrivals.dm
+++ b/code/game/gamemodes/miniantags/tourist/tourist_arrivals.dm
@@ -1,5 +1,8 @@
/datum/event/tourist_arrivals
-
+ name = "Tourist Arrivals"
+ role_weights = list(ASSIGNMENT_SECURITY = 5)
+ role_requirements = list(ASSIGNMENT_SECURITY = 2)
+ nominal_severity = EVENT_LEVEL_MODERATE
/// Maximum number of spawns.
var/max_spawn = 10
/// If the event ran successfully
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index 44afc8309d9..b249101a70d 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -104,6 +104,7 @@
pixel_y = rand(6,-6)
START_PROCESSING(SSobj, src)
AddComponent(/datum/component/swarming)
+ AddComponent(/datum/component/event_tracker, EVENT_TERROR_SPIDERS)
ADD_TRAIT(src, TRAIT_EDIBLE_BUG, "edible_bug") // Normally this is just used for mobs, but spiderlings are kind of that...
/obj/structure/spider/spiderling/Destroy()
diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm
index b479106a8b3..ddb88523019 100644
--- a/code/game/objects/structures/aliens.dm
+++ b/code/game/objects/structures/aliens.dm
@@ -474,6 +474,7 @@
addtimer(CALLBACK(src, PROC_REF(grow)), rand(MIN_GROWTH_TIME, MAX_GROWTH_TIME))
if(status == GROWN)
proximity_monitor = new(src)
+ AddComponent(/datum/component/event_tracker, EVENT_XENOS)
/obj/structure/alien/egg/attack_alien(mob/living/carbon/alien/user)
return attack_hand(user)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index fb5e356cb87..7abe5ce3a77 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -988,6 +988,9 @@ Traitors and the like can also be revived with the previous role mostly intact.
if(!check_rights(R_SERVER|R_EVENT))
return
+ if(tgui_alert(usr, "[GLOB.configuration.event.enable_random_events ? "Disable" : "Enable"] random events?", "Confirm", list("Yes", "No")) != "Yes")
+ return
+
if(!GLOB.configuration.event.enable_random_events)
GLOB.configuration.event.enable_random_events = TRUE
to_chat(usr, "Random events enabled")
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 838b7acccfd..73344d5a30d 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -607,4 +607,7 @@ GLOBAL_LIST_EMPTY(antagonists)
other_team.add_antag_objective(red_team)
red_team.pair_up(blue_team, TRUE)
+/datum/antagonist/proc/antag_event_resource_cost()
+ return list(ASSIGNMENT_SECURITY = 1)
+
#undef SUCCESSFUL_DETACH
diff --git a/code/modules/antagonists/abductor/datum_abductor.dm b/code/modules/antagonists/abductor/datum_abductor.dm
index 5a2365bf161..673b1097f2a 100644
--- a/code/modules/antagonists/abductor/datum_abductor.dm
+++ b/code/modules/antagonists/abductor/datum_abductor.dm
@@ -49,3 +49,6 @@
messages.Add("You are a scientist of [our_team.name]!")
messages.Add("With the help of your teammate, kidnap and experiment on station crew members! Use your tool and ship consoles to support the agent and retrieve humanoid specimens.")
return messages
+
+/datum/antagonist/abductor/antag_event_resource_cost()
+ return list(ASSIGNMENT_SECURITY = 1.5)
diff --git a/code/modules/antagonists/changeling/datum_changeling.dm b/code/modules/antagonists/changeling/datum_changeling.dm
index a790503df67..804756a78be 100644
--- a/code/modules/antagonists/changeling/datum_changeling.dm
+++ b/code/modules/antagonists/changeling/datum_changeling.dm
@@ -473,3 +473,7 @@ RESTRICT_TYPE(/datum/antagonist/changeling)
/datum/antagonist/changeling/custom_blurb()
return "We awaken on the [station_name()], [get_area_name(owner.current, TRUE)]...\nWe have our tasks to attend to..."
+
+// Ling Stronk
+/datum/antagonist/changeling/antag_event_resource_cost()
+ return list(ASSIGNMENT_SECURITY = 3)
diff --git a/code/modules/antagonists/vampire/vamp_datum.dm b/code/modules/antagonists/vampire/vamp_datum.dm
index 74719fd4479..db7cdac8590 100644
--- a/code/modules/antagonists/vampire/vamp_datum.dm
+++ b/code/modules/antagonists/vampire/vamp_datum.dm
@@ -409,3 +409,7 @@ RESTRICT_TYPE(/datum/antagonist/vampire)
/datum/antagonist/vampire/custom_blurb()
return "On the date [GLOB.current_date_string], at [station_time_timestamp()],\n in the [station_name()], [get_area_name(owner.current, TRUE)]...\nThe hunt begins again..."
+
+// Ramps up as you get more powerfull
+/datum/antagonist/vampire/antag_event_resource_cost()
+ return list(ASSIGNMENT_SECURITY = 1 + bloodtotal / 500)
diff --git a/code/modules/economy/economy_events/economy_event.dm b/code/modules/economy/economy_events/economy_event.dm
index 7709c9c94fa..c934d9a56ad 100644
--- a/code/modules/economy/economy_events/economy_event.dm
+++ b/code/modules/economy/economy_events/economy_event.dm
@@ -1,4 +1,5 @@
/datum/event/economic_event
+ name = "Economic News"
endWhen = 50 // this will be set randomly, later
announceWhen = 15
var/datum/event_news/economic/news_story
diff --git a/code/modules/economy/economy_events/economy_events_mundane.dm b/code/modules/economy/economy_events/economy_events_mundane.dm
index 46d84a40cfe..e62bdf4d87e 100644
--- a/code/modules/economy/economy_events/economy_events_mundane.dm
+++ b/code/modules/economy/economy_events/economy_events_mundane.dm
@@ -1,4 +1,5 @@
/datum/event/mundane_news
+ name = "Mundane News"
endWhen = 10
/datum/event/mundane_news/announce()
@@ -19,6 +20,7 @@
NC.alert_news(message.title)
/datum/event/trivial_news
+ name = "Trivial News"
endWhen = 10
/datum/event/trivial_news/announce()
diff --git a/code/modules/events/abductor_event.dm b/code/modules/events/abductor_event.dm
index a1fb43c30a0..95009f28cf4 100644
--- a/code/modules/events/abductor_event.dm
+++ b/code/modules/events/abductor_event.dm
@@ -1,4 +1,20 @@
/datum/event/abductor
+ name = "Abductor Visit"
+ noAutoEnd = TRUE
+ nominal_severity = EVENT_LEVEL_MAJOR
+ role_weights = list(ASSIGNMENT_SECURITY = 5)
+ role_requirements = list(ASSIGNMENT_SECURITY = 3)
+ var/datum/team/abductor/ayys
+ var/spawned = FALSE
+
+/datum/event/abductor/process()
+ if(ayys && length(ayys.members) && spawned)
+ kill()
+ return ..()
+
+/// abductor costs are calculated independently of event
+/datum/event/abductor/event_resource_cost()
+ return list()
/datum/event/abductor/start()
INVOKE_ASYNC(src, PROC_REF(try_makeAbductorTeam))
@@ -23,7 +39,8 @@
mind.active = TRUE
minds += mind
- var/datum/team/abductor/ayys = new /datum/team/abductor(minds)
+ ayys = new /datum/team/abductor(minds)
ayys.create_agent()
ayys.create_scientist()
+ spawned = TRUE
return TRUE
diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm
index 0d8ec05ee2f..f19ae9ea1a5 100644
--- a/code/modules/events/alien_infestation.dm
+++ b/code/modules/events/alien_infestation.dm
@@ -1,9 +1,16 @@
+GLOBAL_LIST_INIT(xeno_things, list("xenos" = list(), "eggs" = list()))
+
/datum/event/alien_infestation
- announceWhen = 400
+ name = "Alien Infestation"
+ announceWhen = 400
+ noAutoEnd = TRUE
var/highpop_trigger = 80
var/spawncount = 2
var/list/playercount
var/successSpawn = FALSE //So we don't make a command report if nothing gets spawned.
+ nominal_severity = EVENT_LEVEL_DISASTER
+ role_weights = list(ASSIGNMENT_SECURITY = 3, ASSIGNMENT_CREW = 0.9, ASSIGNMENT_MEDICAL = 3)
+ role_requirements = list(ASSIGNMENT_SECURITY = 4, ASSIGNMENT_CREW = 50, ASSIGNMENT_MEDICAL = 4)
/datum/event/alien_infestation/setup()
announceWhen = rand(announceWhen, announceWhen + 50)
@@ -14,6 +21,17 @@
else
log_and_message_admins("Warning: Could not spawn any mobs for event Alien Infestation")
+/datum/event/alien_infestation/process()
+ // Check for completion every minute
+ if(!activeFor % 30)
+ if(successSpawn && !length(event_category_cost(EVENT_XENOS)))
+ kill()
+ . = ..()
+
+/// Xeno costs are calculated independently from the event itself
+/datum/event/alien_infestation/event_resource_cost()
+ return list()
+
/datum/event/alien_infestation/start()
playercount = length(GLOB.clients)//grab playercount when event starts not when game starts
if(playercount >= highpop_trigger) //spawn with 4 if highpop
diff --git a/code/modules/events/anomaly_bluespace.dm b/code/modules/events/anomaly_bluespace.dm
index bd66bfe4197..09bb66f2fc4 100644
--- a/code/modules/events/anomaly_bluespace.dm
+++ b/code/modules/events/anomaly_bluespace.dm
@@ -1,7 +1,9 @@
/datum/event/anomaly/anomaly_bluespace
- name = "Anomaly event"
+ name = "Bluespace Anomaly"
startWhen = 3
announceWhen = 10
anomaly_path = /obj/effect/anomaly/bluespace
prefix_message = "Unstable bluespace anomaly detected on long range scanners."
announce_sound = 'sound/AI/anomaly_bluespace.ogg'
+ role_weights = list(ASSIGNMENT_SCIENCE = 1, ASSIGNMENT_ENGINEERING = 1)
+ role_requirements = list(ASSIGNMENT_SCIENCE = 1, ASSIGNMENT_ENGINEERING = 2)
diff --git a/code/modules/events/anomaly_cryo.dm b/code/modules/events/anomaly_cryo.dm
index 892e0835c8d..a4a3d7385c9 100644
--- a/code/modules/events/anomaly_cryo.dm
+++ b/code/modules/events/anomaly_cryo.dm
@@ -1,5 +1,5 @@
/datum/event/anomaly/anomaly_cryo
- name = "Anomaly event"
+ name = "Cryo Anomaly"
startWhen = 3
announceWhen = 10
anomaly_path = /obj/effect/anomaly/cryo
diff --git a/code/modules/events/anomaly_event.dm b/code/modules/events/anomaly_event.dm
index 1f12d8d7d14..8c6bd734232 100644
--- a/code/modules/events/anomaly_event.dm
+++ b/code/modules/events/anomaly_event.dm
@@ -2,6 +2,10 @@
/datum/event/anomaly
name = "Anomaly: Energetic Flux"
+ nominal_severity = EVENT_LEVEL_MODERATE
+ noAutoEnd = TRUE
+ role_weights = list(ASSIGNMENT_SCIENCE = 1, ASSIGNMENT_ENGINEERING = 1)
+ role_requirements = list(ASSIGNMENT_SCIENCE = 1, ASSIGNMENT_ENGINEERING = 1)
var/obj/effect/anomaly/anomaly_path = /obj/effect/anomaly/flux
var/turf/target_turf
announceWhen = 1
@@ -9,8 +13,15 @@
var/prefix_message = "Localized hyper-energetic flux wave detected on long range scanners."
/// Sound effect used
var/announce_sound = 'sound/AI/anomaly_flux.ogg'
+ var/spawned = FALSE
+/datum/event/anomaly/tick()
+ if(spawned && QDELETED(anomaly_path))
+ // Add 15 minutes for cleanup
+ endWhen = activeFor + 450
+ noAutoEnd = FALSE
+
/datum/event/anomaly/setup()
target_turf = find_targets(TRUE)
@@ -45,7 +56,8 @@
GLOB.minor_announcement.Announce("[prefix_message] Expected location: [target.name].", "Anomaly Alert", announce_sound)
/datum/event/anomaly/start()
- var/newAnomaly = new anomaly_path(target_turf)
- announce_to_ghosts(newAnomaly)
+ anomaly_path = new anomaly_path(target_turf)
+ announce_to_ghosts(anomaly_path)
+ spawned = TRUE
#undef TURF_FIND_TRIES
diff --git a/code/modules/events/anomaly_flux.dm b/code/modules/events/anomaly_flux.dm
index 2ed3182998b..f764d71aa9a 100644
--- a/code/modules/events/anomaly_flux.dm
+++ b/code/modules/events/anomaly_flux.dm
@@ -1,4 +1,4 @@
/datum/event/anomaly/anomaly_flux
- name = "Anomaly event"
+ name = "Flux Anomaly"
startWhen = 10
announceWhen = 3
diff --git a/code/modules/events/anomaly_grav.dm b/code/modules/events/anomaly_grav.dm
index e1e4aea4b18..a2ba037d103 100644
--- a/code/modules/events/anomaly_grav.dm
+++ b/code/modules/events/anomaly_grav.dm
@@ -1,5 +1,5 @@
/datum/event/anomaly/anomaly_grav
- name = "Anomaly event"
+ name = "Gravitational Anomaly"
startWhen = 3
announceWhen = 20
anomaly_path = /obj/effect/anomaly/grav
diff --git a/code/modules/events/anomaly_pyro.dm b/code/modules/events/anomaly_pyro.dm
index 050195391c8..7b3109ecfad 100644
--- a/code/modules/events/anomaly_pyro.dm
+++ b/code/modules/events/anomaly_pyro.dm
@@ -1,5 +1,5 @@
/datum/event/anomaly/anomaly_pyro
- name = "Anomaly event"
+ name = "Pyro Anomaly"
startWhen = 3
announceWhen = 10
anomaly_path = /obj/effect/anomaly/pyro
diff --git a/code/modules/events/anomaly_vortex.dm b/code/modules/events/anomaly_vortex.dm
index 37e54930da6..d2073234296 100644
--- a/code/modules/events/anomaly_vortex.dm
+++ b/code/modules/events/anomaly_vortex.dm
@@ -1,7 +1,9 @@
/datum/event/anomaly/anomaly_vortex
- name = "Anomaly event"
+ name = "Vortex Anomaly"
startWhen = 10
announceWhen = 3
anomaly_path = /obj/effect/anomaly/bhole
prefix_message = "Localized high-intensity vortex anomaly detected on long range scanners."
announce_sound = 'sound/AI/anomaly_vortex.ogg'
+ role_weights = list(ASSIGNMENT_SCIENCE = 1, ASSIGNMENT_ENGINEERING = 1)
+ role_requirements = list(ASSIGNMENT_SCIENCE = 1, ASSIGNMENT_ENGINEERING = 3)
diff --git a/code/modules/events/apc_overload.dm b/code/modules/events/apc_overload.dm
index d2b79abacda..49388f36740 100644
--- a/code/modules/events/apc_overload.dm
+++ b/code/modules/events/apc_overload.dm
@@ -1,6 +1,10 @@
#define APC_BREAK_PROBABILITY 25 // the probability that a given APC will be broken
/datum/event/apc_overload
+ name = "APC Overload"
+ nominal_severity = EVENT_LEVEL_MAJOR
+ role_weights = list(ASSIGNMENT_ENGINEERING = 4, ASSIGNMENT_CREW = 0.5)
+ role_requirements = list(ASSIGNMENT_ENGINEERING = 5, ASSIGNMENT_CREW = 30)
var/const/announce_after_mc_ticks = 5
var/const/delayed = FALSE
var/const/event_max_duration_mc_ticks = announce_after_mc_ticks * 2
@@ -9,7 +13,7 @@
announceWhen = announce_after_mc_ticks
/datum/event/apc_overload/setup()
- endWhen = rand(event_min_duration_mc_ticks, event_max_duration_mc_ticks)
+ endWhen = rand(event_min_duration_mc_ticks, event_max_duration_mc_ticks) + 2700
/datum/event/apc_overload/start()
apc_overload_failure(announce=delayed)
diff --git a/code/modules/events/apc_short.dm b/code/modules/events/apc_short.dm
index c241774d4e8..c9fd6bbb11e 100644
--- a/code/modules/events/apc_short.dm
+++ b/code/modules/events/apc_short.dm
@@ -1,14 +1,19 @@
#define APC_BREAK_PROBABILITY 25 // the probability that a given APC will be disabled
/datum/event/apc_short
- var/const/announce_after_mc_ticks = 5
+ name = "APC Short"
+ // Keeps engies busy for a while
+ role_weights = list(ASSIGNMENT_ENGINEERING = 3)
+ role_requirements = list(ASSIGNMENT_ENGINEERING = 3)
+ nominal_severity = EVENT_LEVEL_MODERATE
+ var/const/announce_after_mc_ticks = 5
var/const/event_max_duration_mc_ticks = announce_after_mc_ticks * 2
var/const/event_min_duration_mc_ticks = announce_after_mc_ticks
announceWhen = announce_after_mc_ticks
/datum/event/apc_short/setup()
- endWhen = rand(event_min_duration_mc_ticks, event_max_duration_mc_ticks)
+ endWhen = rand(event_min_duration_mc_ticks, event_max_duration_mc_ticks) + 1800
/datum/event/apc_short/start()
power_failure(announce = FALSE)
diff --git a/code/modules/events/blob/blob_mobs.dm b/code/modules/events/blob/blob_mobs.dm
index c7a720fe54a..50202ca0895 100644
--- a/code/modules/events/blob/blob_mobs.dm
+++ b/code/modules/events/blob/blob_mobs.dm
@@ -25,6 +25,12 @@
/mob/living/basic/blob/Initialize(mapload)
. = ..()
GLOB.blob_minions |= src
+ AddComponent(/datum/component/event_tracker, EVENT_BLOB)
+
+/mob/living/basic/blob/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 3, ASSIGNMENT_MEDICAL = 0.5)
/mob/living/basic/blob/Destroy()
GLOB.blob_minions -= src
@@ -209,6 +215,11 @@
if(name == "blobbernaut")
name = "blobbernaut ([rand(1, 1000)])"
+/mob/living/basic/blob/blobbernaut/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 3, ASSIGNMENT_MEDICAL = 0.5)
+
/datum/action/innate/communicate_overmind_blob
name = "Speak with the overmind"
button_icon = 'icons/mob/guardian.dmi'
diff --git a/code/modules/events/blob/blob_structures/blob_core.dm b/code/modules/events/blob/blob_structures/blob_core.dm
index 242b6fea1f3..2e4ebdcddc9 100644
--- a/code/modules/events/blob/blob_structures/blob_core.dm
+++ b/code/modules/events/blob/blob_structures/blob_core.dm
@@ -34,6 +34,10 @@
var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay")
overlays += C
+/obj/structure/blob/core/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z))
+ return list(ASSIGNMENT_SECURITY = 2, ASSIGNMENT_CREW = 15, ASSIGNMENT_MEDICAL = 2)
/obj/structure/blob/core/Destroy()
if(overmind)
diff --git a/code/modules/events/blob/blob_structures/factory.dm b/code/modules/events/blob/blob_structures/factory.dm
index c2aa113d154..1690bc6bc1d 100644
--- a/code/modules/events/blob/blob_structures/factory.dm
+++ b/code/modules/events/blob/blob_structures/factory.dm
@@ -27,4 +27,9 @@ GLOBAL_VAR_INIT(spores_active, 0)
if(overmind)
overmind.add_mob_to_overmind(BS)
+/obj/structure/blob/factory/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z))
+ return list(ASSIGNMENT_SECURITY = 0.33, ASSIGNMENT_CREW = 1)
+
#undef MAX_GLOBAL_SPORES
diff --git a/code/modules/events/blob/blob_structures/node.dm b/code/modules/events/blob/blob_structures/node.dm
index ce75e5ed44e..359ef3b8777 100644
--- a/code/modules/events/blob/blob_structures/node.dm
+++ b/code/modules/events/blob/blob_structures/node.dm
@@ -33,3 +33,8 @@
Pulse(5, i, color)
obj_integrity = min(max_integrity, obj_integrity + 1)
color = null
+
+/obj/structure/blob/node/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z))
+ return list(ASSIGNMENT_SECURITY = 0.1, ASSIGNMENT_CREW = 1)
diff --git a/code/modules/events/blob/theblob.dm b/code/modules/events/blob/theblob.dm
index ba06e8d9135..c845a48aba6 100644
--- a/code/modules/events/blob/theblob.dm
+++ b/code/modules/events/blob/theblob.dm
@@ -35,6 +35,12 @@ GLOBAL_LIST_EMPTY(blob_minions)
COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered)
)
AddElement(/datum/element/connect_loc, loc_connections)
+ AddComponent(/datum/component/event_tracker, EVENT_BLOB)
+
+/obj/structure/blob/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z))
+ return list(ASSIGNMENT_CREW = 0.07)
/obj/structure/blob/Destroy()
if(atmosblock)
diff --git a/code/modules/events/blob_spawn.dm b/code/modules/events/blob_spawn.dm
index 2bb5b87697c..ff4d71eed61 100644
--- a/code/modules/events/blob_spawn.dm
+++ b/code/modules/events/blob_spawn.dm
@@ -1,7 +1,14 @@
/datum/event/blob
- announceWhen = 180
- endWhen = 240
- var/successSpawn = FALSE //So we don't make a command report if nothing gets spawned.
+ name = "Blob"
+ announceWhen = 180
+ noAutoEnd = TRUE
+ nominal_severity = EVENT_LEVEL_DISASTER
+ role_weights = list(ASSIGNMENT_SECURITY = 2, ASSIGNMENT_CREW = 0.7, ASSIGNMENT_MEDICAL = 2)
+ role_requirements = list(ASSIGNMENT_SECURITY = 3, ASSIGNMENT_CREW = 45, ASSIGNMENT_MEDICAL = 3)
+ /// So we don't make a command report if nothing gets spawned.
+ var/successSpawn = FALSE
+ /// List of all blob cores and blob mice related to this event
+ var/list/blob_things = list("cores" = list(), "mice" = 0)
/datum/event/blob/announce(false_alarm)
if(successSpawn || false_alarm)
@@ -12,6 +19,15 @@
/datum/event/blob/start()
INVOKE_ASYNC(src, PROC_REF(make_blob))
+/datum/event/blob/process()
+ if(!(length(blob_things["cores"]) + blob_things["mice"]) && successSpawn)
+ return kill()
+ return ..()
+
+/// Blob costs are calculated independently from the event itself
+/datum/event/blob/event_resource_cost()
+ return list()
+
/datum/event/blob/proc/make_blob()
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, TRUE, source = /mob/living/basic/mouse/blobinfected)
if(!length(candidates))
@@ -29,7 +45,8 @@
B.mind.special_role = SPECIAL_ROLE_BLOB
B.forceMove(vent)
B.add_ventcrawl(vent)
-
+ RegisterSignal(B, COMSIG_BLOB_MOUSE_BURST, PROC_REF(record_core))
+ blob_things["mice"]++
// Mark it on antag HUD
var/datum/atom_hud/antag/antaghud = GLOB.huds[ANTAG_HUD_BLOB]
antaghud.join_hud(B.mind.current)
@@ -40,3 +57,14 @@
notify_ghosts("Infected Mouse has appeared in [get_area(B)].", source = B, action = NOTIFY_FOLLOW)
successSpawn = TRUE
SSevents.biohazards_this_round += BIOHAZARD_BLOB
+
+/datum/event/blob/proc/record_core(atom/source, obj/structure/blob/core/core)
+ SIGNAL_HANDLER // COMSIG_BLOB_MOUSE_BURST
+ if(core)
+ blob_things["cores"] += list(core)
+ RegisterSignal(core, COMSIG_PARENT_QDELETING, PROC_REF(remove_core))
+ blob_things["mice"]--
+
+/datum/event/blob/proc/remove_core(obj/structure/blob/core/source)
+ if(source)
+ blob_things["cores"] -= source
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index 8b1989f7ad8..692fb8261cf 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -1,6 +1,10 @@
/datum/event/brand_intelligence
+ name = "Brand Intelligence"
+ nominal_severity = EVENT_LEVEL_MODERATE
+ role_weights = list(ASSIGNMENT_ENGINEERING = 1, ASSIGNMENT_CREW = 0.4)
+ role_requirements = list(ASSIGNMENT_ENGINEERING = 2, ASSIGNMENT_CREW = 10)
+ noAutoEnd = TRUE
announceWhen = 21
- endWhen = 1000 //Ends when all vending machines are subverted anyway.
var/list/obj/machinery/economy/vending/vendingMachines = list()
var/list/obj/machinery/economy/vending/infectedMachines = list()
@@ -47,7 +51,7 @@
log_debug("Original brand intelligence machine: [originMachine] ([ADMIN_VV(originMachine,"VV")]) [ADMIN_JMP(originMachine)]")
/datum/event/brand_intelligence/tick()
- if(originMachine.shut_up || originMachine.wires.is_all_cut()) //if the original vending machine is missing or has it's voice switch flipped
+ if(originMachine?.shut_up || originMachine?.wires.is_all_cut()) //if the original vending machine is missing or has it's voice switch flipped
origin_machine_defeated()
return
diff --git a/code/modules/events/bureaucratic_error.dm b/code/modules/events/bureaucratic_error.dm
index 320b563ac32..07e41919f43 100644
--- a/code/modules/events/bureaucratic_error.dm
+++ b/code/modules/events/bureaucratic_error.dm
@@ -1,4 +1,5 @@
/datum/event/bureaucratic_error
+ name = "Bureaucratic Error"
announceWhen = 1
/// Jobs that are not allowed to be picked for the bureaucratic error
var/list/blacklisted_jobs = list(
diff --git a/code/modules/events/camera_failure.dm b/code/modules/events/camera_failure.dm
index 458e0aaad46..dd7e031ab63 100644
--- a/code/modules/events/camera_failure.dm
+++ b/code/modules/events/camera_failure.dm
@@ -1,3 +1,7 @@
+/datum/event/camera_failure
+ name = "Camera Failure"
+ role_weights = list(ASSIGNMENT_ENGINEERING = 10)
+
/datum/event/camera_failure/start()
var/failed_cameras
var/failure_limit = rand(1, 3)
diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm
index 6ecac2de81f..58e8ce26ef1 100644
--- a/code/modules/events/carp_migration.dm
+++ b/code/modules/events/carp_migration.dm
@@ -1,14 +1,35 @@
/datum/event/carp_migration
+ name = "Carp Migration"
announceWhen = 50
- endWhen = 900
-
+ noAutoEnd = TRUE
+ nominal_severity = EVENT_LEVEL_MODERATE
+ role_weights = list(ASSIGNMENT_ENGINEERING = 1, ASSIGNMENT_SECURITY = 2)
+ role_requirements = list(ASSIGNMENT_ENGINEERING = 1, ASSIGNMENT_SECURITY = 1.5)
+ nominal_severity = EVENT_LEVEL_MODERATE
var/list/spawned_mobs = list(
/mob/living/basic/carp = 95,
/mob/living/basic/carp/megacarp = 5)
+ /// Carps spawned by the event
+ var/list/my_carps = list()
+ /// Did we manage to spawn carps
+ var/spawned = FALSE
+
+/datum/event/carp_migration/process()
+ if(!length(my_carps) && spawned)
+ kill()
+ return ..()
+
+// Resource calculation independent of event
+/datum/event/carp_migration/event_resource_cost()
+ return list()
+
+/datum/event/carp_migration/proc/remove_carp(mob/source)
+ SIGNAL_HANDLER // COMSIG_MOB_DEATH
+ my_carps -= source
+ UnregisterSignal(source, COMSIG_MOB_DEATH)
/datum/event/carp_migration/setup()
announceWhen = rand(40, 60)
- endWhen = rand(600, 1200)
/datum/event/carp_migration/announce()
var/announcement = ""
@@ -40,5 +61,8 @@
var/group_size = rand(group_size_min, group_size_max)
for(var/j = 1, j <= group_size, j++)
var/carptype = pickweight(spawned_mobs)
- new carptype(spawn_locations[i])
+ var/mob/carp = new carptype(spawn_locations[i])
+ RegisterSignal(carp, COMSIG_MOB_DEATH, PROC_REF(remove_carp))
+ my_carps += carp
+ spawned = TRUE
i++
diff --git a/code/modules/events/clogged_disposals.dm b/code/modules/events/clogged_disposals.dm
index 0a9d18a0073..8116b861d83 100644
--- a/code/modules/events/clogged_disposals.dm
+++ b/code/modules/events/clogged_disposals.dm
@@ -2,6 +2,7 @@
name = "disposals clog"
startWhen = 10
endWhen = 35
+ nominal_severity = EVENT_LEVEL_MODERATE
var/notify_title = "Clogged Disposals"
var/notify_image = "disposal-flush"
var/list/bins = list()
diff --git a/code/modules/events/communications_blackout.dm b/code/modules/events/communications_blackout.dm
index 12d579b4899..bcf433ed3ab 100644
--- a/code/modules/events/communications_blackout.dm
+++ b/code/modules/events/communications_blackout.dm
@@ -1,3 +1,7 @@
+/datum/event/communications_blackout
+ name = "Communication Blackout"
+ nominal_severity = EVENT_LEVEL_MODERATE
+
/datum/event/communications_blackout/announce(false_alarm)
var/alert = pick( "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \
"Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v?-BZZZT", \
diff --git a/code/modules/events/demon_incursion.dm b/code/modules/events/demon_incursion.dm
index 04146e36b47..bbeba50d97b 100644
--- a/code/modules/events/demon_incursion.dm
+++ b/code/modules/events/demon_incursion.dm
@@ -2,8 +2,10 @@
name = "demon incursion"
/// Corresponds to the number of process() runs the event has lasted for. Roughly 2 minutes here.
announceWhen = 60
- /// Corresponds to the number of process() runs the event has lasted for. Roughly 2 minutes here.
- endWhen = 60
+ noAutoEnd = TRUE
+ role_weights = list(ASSIGNMENT_SECURITY = 4, ASSIGNMENT_CREW = 1, ASSIGNMENT_MEDICAL = 2)
+ role_requirements = list(ASSIGNMENT_SECURITY = 3, ASSIGNMENT_CREW = 35, ASSIGNMENT_MEDICAL = 3)
+ nominal_severity = EVENT_LEVEL_MAJOR
/// The name of the notification for dchat
var/notify_title = "Demonic Incursion"
/// The icon of the notification
@@ -12,6 +14,17 @@
var/list/portal_list = list()
/// The target number of portals
var/target_portals = 100
+ /// Toggled when the first portal is spawned
+ var/spawned = FALSE
+
+
+/datum/event/demon_incursion/tick()
+ if(!length(portal_list) && spawned)
+ kill()
+
+// Costs are calculated independently of event
+/datum/event/demon_incursion/event_resource_cost()
+ return list()
/datum/event/demon_incursion/setup()
impact_area = findEventArea()
@@ -55,6 +68,7 @@
if(length(portal_list) > target_portals)
target_portals *= 2
prepare_spawn_elite()
+ spawned = TRUE
/datum/event/demon_incursion/proc/prepare_spawn_elite()
var/obj/structure/spawner/nether/demon_incursion/elite_portal = pick(portal_list)
@@ -148,6 +162,17 @@
if(turf_to_spread)
spread_turf()
SSticker.mode.incursion_portals += src
+ AddComponent(/datum/component/event_tracker, EVENT_DEMONIC)
+
+/obj/structure/spawner/nether/demon_incursion/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z))
+ return list(ASSIGNMENT_SECURITY = 1, ASSIGNMENT_CREW = 5, ASSIGNMENT_MEDICAL = 1)
+
+/obj/structure/spawner/nether/demon_incursion/Destroy()
+ if(linked_incursion)
+ linked_incursion.portal_list -= src
+ . = ..()
/obj/structure/spawner/nether/demon_incursion/examine(mob/user)
. = ..()
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index 89ab8a71991..caeba785e2d 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -2,8 +2,12 @@ GLOBAL_LIST_EMPTY(current_pending_diseases)
/datum/event/disease_outbreak
// We only want the announcement to happen after the virus has spawned on station
announceWhen = -1
- // Keep the event running until we announce it
+ // Keep the event running for as long as we have infected
noAutoEnd = TRUE
+ name = "Disease Outbreak"
+ role_weights = list(ASSIGNMENT_MEDICAL = 5, ASSIGNMENT_VIROLOGIST = 5, ASSIGNMENT_BOTANIST = 2, ASSIGNMENT_CHEMIST = 2)
+ role_requirements = list(ASSIGNMENT_MEDICAL = 4, ASSIGNMENT_VIROLOGIST = 1, ASSIGNMENT_BOTANIST = 0, ASSIGNMENT_CHEMIST = 1)
+ nominal_severity = EVENT_LEVEL_MODERATE
/// The type of disease that patient zero will be infected with.
var/datum/disease/chosen_disease
@@ -17,6 +21,7 @@ GLOBAL_LIST_EMPTY(current_pending_diseases)
var/static/list/diseases_moderate_major = list()
var/force_disease_time = 300
var/list/infected_players = list()
+ var/first_infection = FALSE
/datum/event/disease_outbreak/setup()
if(isemptylist(diseases_minor) && isemptylist(diseases_moderate_major))
@@ -57,10 +62,10 @@ GLOBAL_LIST_EMPTY(current_pending_diseases)
GLOB.minor_announcement.Announce("Moderate contagion detected aboard [station_name()].", new_sound = 'sound/misc/notice2.ogg', new_title = "Contagion Alert")
if(EVENT_LEVEL_MUNDANE)
GLOB.minor_announcement.Announce("Minor contagion detected aboard [station_name()].", new_sound = 'sound/misc/notice2.ogg', new_title = "Contagion Alert")
- // We did our announcement, the event no longer needs to run
- kill()
/datum/event/disease_outbreak/process()
+ if(length(infected_players) > 0)
+ first_infection = TRUE
if(activeFor == force_disease_time)
for(var/list/disease_event in GLOB.current_pending_diseases)
if(chosen_disease == disease_event["disease"])
@@ -71,8 +76,16 @@ GLOBAL_LIST_EMPTY(current_pending_diseases)
break
if(length(infected_players) > 2 && announceWhen <= 0)
announceWhen = activeFor + 180
+ // The event ends when everyone is cured
+ if(length(infected_players) <= 0 && first_infection)
+ kill()
. = ..()
+/// Cost of ongoing disease outbreak. 1 medical personnal per 2 infected at major severity.
+/datum/event/disease_outbreak/event_resource_cost()
+ return list("[ASSIGNMENT_MEDICAL]" = (severity / nominal_severity) * (length(infected_players) / 3), "[ASSIGNMENT_VIROLOGIST]" = 1)
+
+
//Creates a virus with a harmful effect, guaranteed to be spreadable by contact or airborne
/datum/event/disease_outbreak/proc/create_virus(max_severity = 6)
var/datum/disease/advance/A = new /datum/disease/advance(_event = UID())
diff --git a/code/modules/events/door_runtime.dm b/code/modules/events/door_runtime.dm
index 806e5d063f8..f8b9a605a2b 100644
--- a/code/modules/events/door_runtime.dm
+++ b/code/modules/events/door_runtime.dm
@@ -1,4 +1,7 @@
/datum/event/door_runtime
+ name = "Door Runtime"
+ role_weights = list(ASSIGNMENT_CREW = 0.2)
+ role_requirements = list(ASSIGNMENT_CREW = 0)
/datum/event/door_runtime/announce()
GLOB.minor_announcement.Announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.", "Network Alert", 'sound/AI/door_runtimes.ogg')
diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm
index 8eba69e9802..b55112ee10c 100644
--- a/code/modules/events/electrical_storm.dm
+++ b/code/modules/events/electrical_storm.dm
@@ -1,4 +1,8 @@
/datum/event/electrical_storm
+ name = "Electrical Storm"
+ nominal_severity = EVENT_LEVEL_MODERATE
+ role_weights = list(ASSIGNMENT_ENGINEERING = 4, ASSIGNMENT_JANITOR = 3)
+ role_requirements = list(ASSIGNMENT_ENGINEERING = 3, ASSIGNMENT_JANITOR = 1)
var/lightsoutAmount = 1
var/lightsoutRange = 25
diff --git a/code/modules/events/event.dm b/code/modules/events/event.dm
index 9a3213d75eb..aec9a7f2278 100644
--- a/code/modules/events/event.dm
+++ b/code/modules/events/event.dm
@@ -1,44 +1,50 @@
/datum/event_meta
- var/name = ""
+ var/name = ""
/// Whether or not the event is available for random selection at all.
- var/enabled = TRUE
+ var/enabled = TRUE
/// The base weight of this event. A zero means it may never fire, but see get_weight()
var/weight
/// The minimum weight that this event will have. Only used if non-zero.
var/min_weight
/// The maximum weight that this event will have.
var/max_weight
- /// the current severity of this event
- var/severity
/// If true, then the event will not be re-added to the list of available events
var/one_shot
/// A modifier applied to all event weights (role and base), respects min and max
var/weight_mod = 1
- /// A list of roles that add weight to the event
- var/list/role_weights = list()
- var/datum/event/event_type
+ /// Event held by this meta event. Used to do things like calculate weight.
+ var/datum/event/skeleton
+ /// How early this specific event can run
+ var/first_run_time = 0
-/datum/event_meta/New(event_severity, event_name, datum/event/type, event_weight, list/job_weights, is_one_shot = FALSE, min_event_weight = 0, max_event_weight = INFINITY)
- name = event_name
- severity = event_severity
- event_type = type
+/datum/event_meta/New(event_severity, type, event_weight, is_one_shot = FALSE, min_event_weight = 0, max_event_weight = INFINITY, _first_run_time)
+ if(type)
+ skeleton = new type(EM = src, skeleton = TRUE, _severity = event_severity)
+ name = skeleton.name
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
+ first_run_time = _first_run_time
-/datum/event_meta/proc/get_weight(list/active_with_role)
- if(!enabled)
+/datum/event_meta/proc/change_event(type)
+ var/event_severity = 0
+ if(skeleton)
+ event_severity = skeleton.severity
+ skeleton.event_meta = null
+ qdel(skeleton)
+ skeleton = new type(EM = src, skeleton = TRUE, _severity = event_severity)
+
+/datum/event_meta/proc/get_weight(list/total_resources)
+ if(!enabled || !skeleton)
return 0
-
- var/job_weight = 0
- for(var/role in role_weights)
- if(role in active_with_role)
- job_weight += active_with_role[role] * role_weights[role]
-
- return clamp((weight + job_weight) * weight_mod, min_weight, max_weight)
+ var/resource_effect = skeleton.get_weight(total_resources)
+ var/new_weight = weight
+ if(resource_effect > 0)
+ new_weight *= (1.003 ** resource_effect)
+ if(resource_effect < 0)
+ new_weight *= 0.95 ** -resource_effect
+ return clamp((new_weight) * weight_mod, min_weight, max_weight)
/*/datum/event_meta/ninja/get_weight(var/list/active_with_role)
if(toggle_space_ninja)
@@ -57,6 +63,8 @@
var/endWhen = 0
/// Severity. Lower means less severe, higher means more severe. Does not have to be supported. Is set on New().
var/severity = 0
+ /// The severity the event has normally
+ var/nominal_severity = EVENT_LEVEL_MUNDANE
/// How long the event has existed. You don't need to change this.
var/activeFor = 0
/// If this event is currently running. You should not change this.
@@ -70,8 +78,29 @@
/// The area the event will hit
var/area/impact_area
var/datum/event_meta/event_meta = null
+ /// The base weight of each role on the event for the purpose of calculating event weight and resource cost
+ var/list/role_weights = list()
+ /// A baseline requirement of different roles the event has at it's nominal severity
+ var/list/role_requirements = list()
/datum/event/nothing
+ name = "Nothing"
+
+/datum/event/nothing/has_cooldown()
+ return FALSE
+
+/datum/event/nothing/is_relative()
+ return TRUE
+
+/// Calculate the weight for rolling the event based on round circumstances.
+/datum/event/proc/get_weight(list/total_resources)
+ var/job_weight = 0
+ for(var/role in role_weights)
+ var/role_available = total_resources[role] ? total_resources[role] : 0
+ var/difference = (role_available - role_requirements[role] * (severity / nominal_severity)) * role_weights[role]
+ // We add difference if it's negative, or the square root if it's positive
+ job_weight += difference
+ return job_weight
/**
* Called first before processing.
@@ -177,18 +206,27 @@
SSevents.active_events -= src
SSevents.event_complete(src)
-/datum/event/New(datum/event_meta/EM, skeleton = FALSE)
+// Events have cooldown by default
+/datum/event/proc/has_cooldown()
+ return TRUE
+
+/datum/event/proc/is_relative()
+ return FALSE
+
+/datum/event/New(datum/event_meta/EM, skeleton = FALSE, _severity)
// event needs to be responsible for this, as stuff like APLUs currently make their own events for curious reasons
if(!skeleton)
SSevents.active_events += src
- if(!EM)
- EM = new /datum/event_meta(EVENT_LEVEL_MAJOR, "Unknown, Most likely admin called", src.type)
-
event_meta = EM
- severity = event_meta.severity
- if(severity < EVENT_LEVEL_MUNDANE) severity = EVENT_LEVEL_MUNDANE
- if(severity > EVENT_LEVEL_MAJOR) severity = EVENT_LEVEL_MAJOR
+
+ severity = _severity ? _severity : nominal_severity
+ if(severity < EVENT_LEVEL_MUNDANE)
+ severity = EVENT_LEVEL_MUNDANE
+ log_debug("Event of '[type]' with severity below mundane run has run")
+ if(severity > EVENT_LEVEL_DISASTER)
+ severity = EVENT_LEVEL_DISASTER
+ log_debug("Event of '[type]' with severity above disaster has run")
startedAt = world.time
@@ -208,3 +246,7 @@
/// If this proc returns TRUE, the regular Announce() won't be called.
/datum/event/proc/fake_announce()
return FALSE
+
+/// The amount of people in different roles needed to handle an ongoing event.
+/datum/event/proc/event_resource_cost()
+ return role_requirements
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index f46c37ff75e..07531535679 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -1,16 +1,4 @@
-#define ASSIGNMENT_ANY "Any"
-#define ASSIGNMENT_AI "AI"
-#define ASSIGNMENT_CYBORG "Cyborg"
-#define ASSIGNMENT_ENGINEER "Engineer"
-#define ASSIGNMENT_BOTANIST "Botanist"
-#define ASSIGNMENT_JANITOR "Janitor"
-#define ASSIGNMENT_MEDICAL "Medical"
-#define ASSIGNMENT_SCIENTIST "Scientist"
-#define ASSIGNMENT_SECURITY "Security"
-#define ASSIGNMENT_CHEMIST "Chemist"
-#define ASSIGNMENT_CARGO "Cargo Bay"
-
-GLOBAL_LIST_INIT(severity_to_string, list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT_LEVEL_MODERATE = "Moderate", EVENT_LEVEL_MAJOR = "Major"))
+GLOBAL_LIST_INIT(severity_to_string, alist(EVENT_LEVEL_MUNDANE = "Mundane", EVENT_LEVEL_MODERATE = "Moderate", EVENT_LEVEL_MAJOR = "Major", EVENT_LEVEL_DISASTER = "Disaster"))
GLOBAL_LIST_EMPTY(event_last_fired)
/datum/event_container
@@ -23,6 +11,12 @@ GLOBAL_LIST_EMPTY(event_last_fired)
var/datum/event_meta/next_event = null
var/last_world_time = 0
+ /// Records the initial amount of events in the available event list
+ var/initial_event_count = 0
+
+/datum/event_container/New()
+ . = ..()
+ initial_event_count = length(available_events)
/datum/event_container/process()
if(!next_event_time)
@@ -47,38 +41,48 @@ GLOBAL_LIST_EMPTY(event_last_fired)
set_event_delay()
next_event.enabled = !next_event.one_shot // This event will no longer be available in the random rotation if one shot
- new next_event.event_type(next_event) // Events are added and removed from the processing queue in their New/kill procs
+ new next_event.skeleton.type(next_event, _severity = next_event.skeleton.severity) // Events are added and removed from the processing queue in their New/kill procs
- log_debug("Starting event '[next_event.name]' of severity [GLOB.severity_to_string[severity]].")
- SSblackbox.record_feedback("nested tally", "events", 1, list(GLOB.severity_to_string[severity], next_event.name))
+ log_debug("Starting event '[next_event.skeleton.name]' of severity [GLOB.severity_to_string[severity]].")
+ SSblackbox.record_feedback("nested tally", "events", 1, list(GLOB.severity_to_string[severity], next_event.skeleton.name))
GLOB.event_last_fired[next_event] = world.time
- next_event = null // When set to null, a random event will be selected next time
+ var/datum/event_meta/meta = next_event
+ next_event = null // When set to null, a random event will be selected next time
+ // Used for checks about the event we just ran
+ return meta
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(length(available_events) == 0)
return
- var/active_with_role = number_active_with_role()
+ // A list of the net available resources of each department depending on staffing and active threats/events
+ var/list/total_resources = get_total_resources()
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
+ var/event_weight = EM.get_weight(total_resources)
+ // We use the amount of non disabled events to adjust the value of Nothing, so we count 0 weight events
+ if(EM.enabled && EM.first_run_time < world.time - SSticker.time_game_started)
+ possible_events[EM] = max(event_weight, 0)
+ // For events like nothing we want to have their weight adjusted depending on how many events are left of the original list
+ if(EM.skeleton.is_relative())
+ possible_events[EM] *= (length(available_events) / initial_event_count)
- for(var/event_meta in last_event_time) if(possible_events[event_meta])
- var/time_passed = world.time - GLOB.event_last_fired[event_meta]
- var/half_of_round = GLOB.configuration.event.expected_round_length / 2
- var/weight_modifier = min(1, 1 - ((half_of_round - time_passed) / half_of_round))
- //With this formula, an event ran 30 minutes ago has half weight, and an event ran an hour ago, has 100 % weight. This works better in general for events, as super high weight events are impacted in a meaningful way.
- 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
+ for(var/datum/event_meta/event_meta in last_event_time)
+ if(!event_meta.skeleton)
+ continue
+ if(event_meta.skeleton.has_cooldown() && possible_events[event_meta])
+ var/time_passed = world.time - GLOB.event_last_fired[event_meta]
+ var/cooldown = GLOB.configuration.event.expected_round_length / 2
+ var/weight_modifier = 1 - max(0, 0.5 * ((cooldown - time_passed) / cooldown))
+ // Events that just ran have their base weight reduced by half, tapering to no reduction over half an hour
+ var/new_weight = max(possible_events[event_meta] * weight_modifier, 0)
+ if(new_weight)
+ possible_events[event_meta] = new_weight
if(length(possible_events) == 0)
return null
@@ -88,6 +92,23 @@ GLOBAL_LIST_EMPTY(event_last_fired)
available_events -= picked_event
return picked_event
+/datum/event_container/proc/get_playercount_modifier()
+ switch(length(GLOB.player_list))
+ if(0 to 10)
+ return 1.1
+ if(11 to 15)
+ return 1.05
+ if(16 to 25)
+ return 1
+ if(26 to 35)
+ return 0.95
+ if(36 to 50)
+ return 0.9
+ if(50 to 80)
+ return 0.85
+ if(80 to 10000)
+ return 0.8
+
/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 && GLOB.configuration.event.first_run_times[severity])
@@ -97,30 +118,14 @@ GLOBAL_LIST_EMPTY(event_last_fired)
next_event_time = world.time + event_delay
// Otherwise, follow the standard setup process
else
- var/playercount_modifier = 1
- switch(length(GLOB.player_list))
- 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 50)
- playercount_modifier = 0.8
- if(50 to 80)
- playercount_modifier = 0.7
- if(80 to 10000)
- playercount_modifier = 0.6
-
- playercount_modifier = playercount_modifier * delay_modifier
-
- var/event_delay = rand(GLOB.configuration.event.delay_lower_bound[severity], GLOB.configuration.event.delay_upper_bound[severity]) * playercount_modifier
+ var/event_delay = calculate_event_delay()
next_event_time = world.time + event_delay
log_debug("Next event of severity [GLOB.severity_to_string[severity]] in [(next_event_time - world.time)/600] minutes.")
+/datum/event_container/proc/calculate_event_delay()
+ return rand(GLOB.configuration.event.delay_lower_bound[severity], GLOB.configuration.event.delay_upper_bound[severity]) * delay_modifier * get_playercount_modifier()
+
/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)
@@ -135,98 +140,118 @@ GLOBAL_LIST_EMPTY(event_last_fired)
severity = EVENT_LEVEL_MUNDANE
available_events = list(
// Severity level, event name, event type, base weight, role weights, one shot, min weight, max weight. Last two only used if set.
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Nothing", /datum/event/nothing, 1100),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "PDA Spam", /datum/event/pda_spam, 0, list(ASSIGNMENT_ANY = 4), FALSE, 25, 50),
- 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, "Vermin Infestation",/datum/event/infestation, 100, list(ASSIGNMENT_JANITOR = 100)),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Sentience", /datum/event/sentience, 50),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Wallrot", /datum/event/wallrot, 0, list(ASSIGNMENT_ENGINEER = 30)),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Fungal Growth", /datum/event/wallrot/fungus, 50, list(ASSIGNMENT_CHEMIST = 50)),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Koi School", /datum/event/carp_migration/koi, 80),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Camera Failure", /datum/event/camera_failure, 100, list(ASSIGNMENT_ENGINEER = 10)),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Fake Virus", /datum/event/fake_virus, 50),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Bureaucratic Error",/datum/event/bureaucratic_error, 40, TRUE),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Disease Outbreak", /datum/event/disease_outbreak, 50, list(ASSIGNMENT_MEDICAL = 25), TRUE)
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/nothing, 252),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/pda_spam, 9),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/economic_event, 7),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/trivial_news, 7),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/mundane_news, 7),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/infestation, 11),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/sentience, 15),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/wallrot, 10),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/wallrot/fungus, 10),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/carp_migration/koi, 12),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/camera_failure, 12),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/fake_virus, 12),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/bureaucratic_error, 12, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/disease_outbreak, 12, TRUE)
)
/datum/event_container/moderate
severity = EVENT_LEVEL_MODERATE
available_events = list(
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Nothing", /datum/event/nothing, 1230),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "False Alarm", /datum/event/falsealarm, 200),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Appendicitis", /datum/event/spontaneous_appendicitis, 0, list(ASSIGNMENT_MEDICAL = 10), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Carp School", /datum/event/carp_migration, 200, list(ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_SECURITY = 20), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Rogue Drones", /datum/event/rogue_drone, 0, list(ASSIGNMENT_SECURITY = 20)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Space Vines", /datum/event/spacevine, 250, list(ASSIGNMENT_ENGINEER = 10)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Meteor Shower", /datum/event/meteor_wave, 0, list(ASSIGNMENT_ENGINEER = 25)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Solar Flare", /datum/event/solar_flare, 0, list(ASSIGNMENT_ENGINEER = 25)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Meaty Ores", /datum/event/dust/meaty, 0, list(ASSIGNMENT_ENGINEER = 20)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Communication Blackout", /datum/event/communications_blackout, 500, list(ASSIGNMENT_AI = 150, ASSIGNMENT_SECURITY = 120)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Prison Break", /datum/event/prison_break, 0, list(ASSIGNMENT_SECURITY = 100)),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/nothing, 800),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/falsealarm, 20),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/spontaneous_appendicitis, 5, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/carp_migration, 10, , TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/rogue_drone, 7),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/spacevine, 15),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/meteor_wave, 8, _first_run_time = 40 MINUTES),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/solar_flare, 12),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/dust/meaty, 8, _first_run_time = 40 MINUTES),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/communications_blackout, 10),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/prison_break, 7),
//new /datum/event_meta(EVENT_LEVEL_MODERATE, "Virology Breach", /datum/event/prison_break/virology, 0, list(ASSIGNMENT_MEDICAL = 100)),
//new /datum/event_meta(EVENT_LEVEL_MODERATE, "Xenobiology Breach", /datum/event/prison_break/xenobiology, 0, list(ASSIGNMENT_SCIENCE = 100)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "APC Short", /datum/event/apc_short, 200, list(ASSIGNMENT_ENGINEER = 60)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Electrical Storm", /datum/event/electrical_storm, 250, list(ASSIGNMENT_ENGINEER = 20, ASSIGNMENT_JANITOR = 150)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Radiation Storm", /datum/event/radiation_storm, 25, list(ASSIGNMENT_MEDICAL = 50), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Spider Infestation", /datum/event/spider_infestation, 100, list(ASSIGNMENT_SECURITY = 30), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Ion Storm", /datum/event/ion_storm, 0, list(ASSIGNMENT_AI = 50, ASSIGNMENT_CYBORG = 50, ASSIGNMENT_ENGINEER = 15, ASSIGNMENT_SCIENTIST = 5)),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/apc_short, 12),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/electrical_storm, 12),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/radiation_storm, 10, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/spider_infestation, 10, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/ion_storm, 10),
//new /datum/event_meta/ninja(EVENT_LEVEL_MODERATE, "Space Ninja", /datum/event/space_ninja, 0, list(ASSIGNMENT_SECURITY = 15), TRUE),
// NON-BAY EVENTS
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Mass Hallucination", /datum/event/mass_hallucination, 300),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Brand Intelligence", /datum/event/brand_intelligence, 50, list(ASSIGNMENT_ENGINEER = 25), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Dimensional Tear", /datum/event/tear, 0, list(ASSIGNMENT_SECURITY = 35)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Honknomoly", /datum/event/tear/honk, 0, list(ASSIGNMENT_SECURITY = 15)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Vent Clog", /datum/event/vent_clog, 250),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Disposals Clog", /datum/event/disposals_clog, 250),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Wormholes", /datum/event/wormholes, 150),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Pyro Anomaly", /datum/event/anomaly/anomaly_pyro, 75, list(ASSIGNMENT_ENGINEER = 60)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Cryo Anomaly", /datum/event/anomaly/anomaly_cryo, 75, list(ASSIGNMENT_ENGINEER = 60)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Vortex Anomaly", /datum/event/anomaly/anomaly_vortex, 75, list(ASSIGNMENT_ENGINEER = 25)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Bluespace Anomaly", /datum/event/anomaly/anomaly_bluespace, 75, list(ASSIGNMENT_ENGINEER = 25)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Flux Anomaly", /datum/event/anomaly/anomaly_flux, 75, list(ASSIGNMENT_ENGINEER = 50)),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Gravitational Anomaly", /datum/event/anomaly/anomaly_grav, 200),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Revenant", /datum/event/revenant, 150),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Morph Spawn", /datum/event/spawn_morph, 40, list(ASSIGNMENT_SECURITY = 10), is_one_shot = TRUE),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Disease Outbreak", /datum/event/disease_outbreak, 50, list(ASSIGNMENT_MEDICAL = 30), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Door Runtime", /datum/event/door_runtime, 50, list(ASSIGNMENT_ENGINEER = 25, ASSIGNMENT_AI = 150), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Tourist Arrivals", /datum/event/tourist_arrivals, 100, list(ASSIGNMENT_SECURITY = 15), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Shuttle Loan", /datum/event/shuttle_loan, 100, list(ASSIGNMENT_CARGO = 15, ASSIGNMENT_SECURITY = 10), is_one_shot = TRUE),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Anomalous Particulate", /datum/event/anomalous_particulate_event, 250, is_one_shot = TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/mass_hallucination, 10),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/brand_intelligence, 5, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/tear, 15),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/tear/honk, 10),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/vent_clog, 12),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/disposals_clog, 12),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/wormholes, 15),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/anomaly/anomaly_pyro, 7),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/anomaly/anomaly_cryo, 7),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/anomaly/anomaly_vortex, 7),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/anomaly/anomaly_bluespace, 7),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/anomaly/anomaly_flux, 7),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/anomaly/anomaly_grav, 7),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/disease_outbreak, 15, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/door_runtime, 10, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/tourist_arrivals, 40, TRUE, _first_run_time = 35 MINUTES),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/shuttle_loan, 50, is_one_shot = TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/anomalous_particulate_event, 60, is_one_shot = TRUE),
)
/datum/event_container/major
severity = EVENT_LEVEL_MAJOR
available_events = list(
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Nothing", /datum/event/nothing, 590),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Carp Migration", /datum/event/carp_migration, 10, list(ASSIGNMENT_SECURITY = 3), TRUE),
- //new /datum/event_meta(EVENT_LEVEL_MAJOR, "Containment Breach", /datum/event/prison_break/station, 0, list(ASSIGNMENT_ANY = 5)),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "APC Overload", /datum/event/apc_overload, 0),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Blob", /datum/event/blob, 20, list(ASSIGNMENT_ENGINEER = 4), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Meteor Wave", /datum/event/meteor_wave, 0, list(ASSIGNMENT_ENGINEER = 10), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Abductor Visit", /datum/event/abductor, 20, list(ASSIGNMENT_SECURITY = 3), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Alien Infestation", /datum/event/alien_infestation, 15, list(ASSIGNMENT_SECURITY = 3), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Traders", /datum/event/traders, 85, is_one_shot = TRUE),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Terror Spiders", /datum/event/spider_terror, 15, list(ASSIGNMENT_SECURITY = 3), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Slaughter Demon", /datum/event/spawn_slaughter, 20, is_one_shot = TRUE),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Shadow Demon", /datum/event/spawn_slaughter/shadow, 20, is_one_shot = TRUE),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Demonic Incursion", /datum/event/demon_incursion, 20, list(ASSIGNMENT_SECURITY = 3)),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Immovable Rod", /datum/event/immovable_rod, 0, list(ASSIGNMENT_ENGINEER = 10), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Disease Outbreak", /datum/event/disease_outbreak, 15, list(ASSIGNMENT_MEDICAL = 4), TRUE),
- //new /datum/event_meta(EVENT_LEVEL_MAJOR, "Floor Cluwne", /datum/event/spawn_floor_cluwne, 15, is_one_shot = TRUE)
- //new /datum/event_meta(EVENT_LEVEL_MAJOR, "Pulse Demon Infiltration", /datum/event/spawn_pulsedemon, 20, is_one_shot = TRUE)
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/nothing, 275),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/carp_migration, 13, TRUE),
+ //new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/prison_break/station, 10),
+ //new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/apc_overload, 11),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/meteor_wave, 9, TRUE, _first_run_time = 40 MINUTES),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/abductor, 12, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/traders, 13, is_one_shot = TRUE, _first_run_time = 35 MINUTES),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_slaughter, 8, is_one_shot = TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_slaughter/shadow, 8, is_one_shot = TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/immovable_rod, 9, TRUE, _first_run_time = 40 MINUTES),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/demon_incursion, 10, TRUE, _first_run_time = 35 MINUTES),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/disease_outbreak, 8, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/revenant, 9),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_morph, 16, is_one_shot = TRUE),
+ //new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_floor_cluwne, 15, is_one_shot = TRUE)
+ //new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_pulsedemon, 20, is_one_shot = TRUE)
)
+// The weights here are set up to roll an event about 1 in 3 rounds, assuming you roll as much as possible
+/datum/event_container/disaster
+ severity = EVENT_LEVEL_DISASTER
+ available_events = list(
+ new /datum/event_meta(EVENT_LEVEL_DISASTER, /datum/event/nothing, 5730),
+ new /datum/event_meta(EVENT_LEVEL_DISASTER, /datum/event/blob, 100, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_DISASTER, /datum/event/alien_infestation, 100, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_DISASTER, /datum/event/spider_terror, 100, TRUE)
+ )
+ var/activation_counter = 0
+ var/event_rolls = 0
-#undef ASSIGNMENT_ANY
-#undef ASSIGNMENT_AI
-#undef ASSIGNMENT_CYBORG
-#undef ASSIGNMENT_ENGINEER
-#undef ASSIGNMENT_BOTANIST
-#undef ASSIGNMENT_JANITOR
-#undef ASSIGNMENT_MEDICAL
-#undef ASSIGNMENT_SCIENTIST
-#undef ASSIGNMENT_SECURITY
-#undef ASSIGNMENT_CHEMIST
-#undef ASSIGNMENT_CARGO
+/datum/event_container/disaster/get_playercount_modifier()
+ return 1
+
+/datum/event_container/disaster/acquire_event()
+ // We should only be getting one none nothing disaster roll per round, and doing it this way leaves more room for admins to play around with it.
+ if(activation_counter > 0)
+ for(var/datum/event_meta/meta in available_events)
+ if(istype(meta.skeleton, /datum/event/nothing))
+ return meta
+ event_rolls++
+ . = ..()
+
+/datum/event_container/disaster/start_event()
+ . = ..()
+ var/datum/event_meta/meta = .
+ if(!istype(meta.skeleton, /datum/event/nothing))
+ activation_counter++
+
+/datum/event_container/disaster/calculate_event_delay()
+ . = ..()
+ if(world.time - SSticker.time_game_started + . <= 120 MINUTES && world.time - SSticker.time_game_started + . >= 95 MINUTES)
+ . += 30 MINUTES
+ return
diff --git a/code/modules/events/event_procs.dm b/code/modules/events/event_procs.dm
index 0e95218f193..4e0f17a7bd8 100644
--- a/code/modules/events/event_procs.dm
+++ b/code/modules/events/event_procs.dm
@@ -55,61 +55,66 @@
// Returns how many characters are currently active(not logged out, not AFK for more than 10 minutes)
// with a specific role.
-// Note that this isn't sorted by department, because e.g. having a roboticist shouldn't make meteors spawn.
+// Note that this isn't sorted just by department, because e.g. having a roboticist shouldn't make meteors spawn.
/proc/number_active_with_role()
- var/list/active_with_role = list()
- active_with_role["Engineer"] = 0
- active_with_role["Medical"] = 0
- active_with_role["Security"] = 0
- active_with_role["Scientist"] = 0
- active_with_role["AI"] = 0
- active_with_role["Cyborg"] = 0
- active_with_role["Janitor"] = 0
- active_with_role["Botanist"] = 0
- active_with_role["Chemist"] = 0
- active_with_role["Any"] = length(GLOB.player_list)
- for(var/mob/M in GLOB.player_list)
- if(!M.mind || !M.client || M.client.inactivity > 10 * 10 * 60) // longer than 10 minutes AFK counts them as inactive
+ var/list/active_with_role = list()
+ active_with_role[ASSIGNMENT_CREW] = 0
+ active_with_role[ASSIGNMENT_COMMAND] = 0
+ active_with_role[ASSIGNMENT_ENGINEERING] = 0
+ active_with_role[ASSIGNMENT_MEDICAL] = 0
+ active_with_role[ASSIGNMENT_SECURITY] = 0
+ active_with_role[ASSIGNMENT_SCIENCE] = 0
+ active_with_role[ASSIGNMENT_CARGO] = 0
+
+ if(length(SSevents.debug_resources))
+ active_with_role = SSevents.debug_resources.Copy()
+
+ for(var/mob/player in GLOB.player_list)
+ if(!player.mind?.assigned_role || player.stat == DEAD || !player.client || player.client.inactivity > 10 * 10 * 60) // longer than 10 minutes AFK counts them as inactive
continue
- if(isrobot(M))
- var/mob/living/silicon/robot/R = M
+ var/capacity = player.get_event_capacity()
+
+ if(!capacity)
+ continue
+
+ if(player.mind.assigned_role in (GLOB.exp_jobsmap[EXP_TYPE_CREW]["titles"]))
+ active_with_role[ASSIGNMENT_CREW] += capacity
+
+ if(active_with_role[player.mind.assigned_role])
+ active_with_role[player.mind.assigned_role]+= capacity
+ else
+ active_with_role[player.mind.assigned_role] = capacity
+
+ if(isrobot(player))
+ var/mob/living/silicon/robot/R = player
if(R.module && (R.module.name == "engineering robot module"))
- active_with_role["Engineer"]++
+ active_with_role[ASSIGNMENT_ENGINEERING]+= capacity
if(R.module && (R.module.name == "medical robot module"))
- active_with_role["Medical"]++
+ active_with_role[ASSIGNMENT_MEDICAL]+= capacity
if(R.module && (R.module.name == "security robot module"))
- active_with_role["Security"]++
+ active_with_role[ASSIGNMENT_SECURITY]+= capacity
- if(M.mind.assigned_role in list("Chief Engineer", "Station Engineer"))
- active_with_role["Engineer"]++
+ if(player.mind.assigned_role in GLOB.engineering_positions)
+ active_with_role[ASSIGNMENT_ENGINEERING]+= capacity
- if(M.mind.assigned_role in list("Chief Medical Officer", "Medical Doctor"))
- active_with_role["Medical"]++
+ if(player.mind.assigned_role in GLOB.medical_positions)
+ active_with_role[ASSIGNMENT_MEDICAL]+= capacity
- if(M.mind.assigned_role in GLOB.active_security_positions)
- active_with_role["Security"]++
+ if(player.mind.assigned_role in GLOB.active_security_positions)
+ active_with_role[ASSIGNMENT_SECURITY]+= capacity
- if(M.mind.assigned_role in list("Research Director", "Scientist"))
- active_with_role["Scientist"]++
+ if(player.mind.assigned_role in GLOB.science_positions)
+ active_with_role[ASSIGNMENT_SCIENCE]+= capacity
- if(M.mind.assigned_role == "AI")
- active_with_role["AI"]++
+ if(player.mind.assigned_role in GLOB.supply_positions)
+ active_with_role[ASSIGNMENT_CARGO]+= capacity
- if(M.mind.assigned_role == "Cyborg")
- active_with_role["Cyborg"]++
-
- if(M.mind.assigned_role == "Janitor")
- active_with_role["Janitor"]++
-
- if(M.mind.assigned_role == "Botanist")
- active_with_role["Botanist"]++
-
- if(M.mind.assigned_role == "Chemist")
- active_with_role["Chemist"]++
+ if(player.mind.assigned_role in GLOB.command_positions)
+ active_with_role[ASSIGNMENT_COMMAND]+= capacity
return active_with_role
@@ -119,3 +124,58 @@
if(P.client)
players++
return players
+
+/proc/event_category_cost(category)
+ . = list()
+ if(!category)
+ return
+ for(var/datum/component/event_tracker/tracker in GLOB.event_trackers["[category]"])
+ var/costs = tracker.event_cost()
+ for(var/key in costs)
+ .["[key]"] += costs[key]
+
+/proc/event_total_cost()
+ . = list()
+ for(var/category in GLOB.event_trackers)
+ var/costs = event_category_cost(category)
+ for(var/key in costs)
+ .["[key]"] += costs[key]
+
+/// Returns the net resources available for event rolling
+/proc/get_total_resources()
+ // A list of the amount of active players in each role/department
+ var/active_with_role = number_active_with_role()
+ // A list of the net available resources of each department depending on staffing and active threats/events
+ var/list/total_resources = list()
+
+ // Add resources from staffing
+ for(var/assignment in active_with_role)
+ total_resources[assignment] += active_with_role[assignment] * ASSIGNMENT_STAFFING_VALUE
+
+ // Subtract resources from active antags
+ for(var/datum/antagonist/active in GLOB.antagonists)
+ var/list/antag_costs = active.antag_event_resource_cost()
+ for(var/assignment in antag_costs)
+ if(total_resources[assignment])
+ total_resources[assignment] -= antag_costs[assignment]
+ else
+ total_resources[assignment] = -antag_costs[assignment]
+
+ // Subtract resources from active events
+ for(var/datum/event/active in SSevents.active_events)
+ var/list/event_costs = active.event_resource_cost()
+ for(var/assignment in event_costs)
+ if(total_resources[assignment])
+ total_resources[assignment] -= event_costs[assignment]
+ else
+ total_resources[assignment] = -event_costs[assignment]
+
+ // Subtract resources from various elements
+ var/list/misc_costs = event_total_cost()
+ for(var/assignment in misc_costs)
+ if(total_resources[assignment])
+ total_resources[assignment] -= misc_costs[assignment]
+ else
+ total_resources[assignment] = -misc_costs[assignment]
+
+ return total_resources
diff --git a/code/modules/events/fake_virus.dm b/code/modules/events/fake_virus.dm
index 96d0252503a..f15f5476b7f 100644
--- a/code/modules/events/fake_virus.dm
+++ b/code/modules/events/fake_virus.dm
@@ -1,3 +1,6 @@
+/datum/event/fake_virus
+ name = "Fake Virus"
+
/datum/event/fake_virus/start()
var/list/valid_targets = list()
for(var/mob/living/carbon/human/victim in shuffle(GLOB.player_list))
diff --git a/code/modules/events/false_alarm.dm b/code/modules/events/false_alarm.dm
index d451215a5bf..6c15bd82a3d 100644
--- a/code/modules/events/false_alarm.dm
+++ b/code/modules/events/false_alarm.dm
@@ -1,4 +1,5 @@
/datum/event/falsealarm
+ name = "False Alarm"
endWhen = 1
var/static/list/possible_event_types = list(
/datum/event/alien_infestation,
diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm
index d5c0dd9c201..eb5a3839d8c 100644
--- a/code/modules/events/immovable_rod.dm
+++ b/code/modules/events/immovable_rod.dm
@@ -8,7 +8,13 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
*/
/datum/event/immovable_rod
+ name = "Immovable Rod"
announceWhen = 5
+ // We assume the event keeps engies busy for about 30 minutes
+ endWhen = 900
+ nominal_severity = EVENT_LEVEL_MAJOR
+ role_weights = list(ASSIGNMENT_ENGINEERING = 5)
+ role_requirements = list(ASSIGNMENT_ENGINEERING = 6)
/datum/event/immovable_rod/announce()
GLOB.minor_announcement.Announce("What the fuck was that?!", "General Alert")
diff --git a/code/modules/events/infestation.dm b/code/modules/events/infestation.dm
index 76f45a2ce0b..c908e831a11 100644
--- a/code/modules/events/infestation.dm
+++ b/code/modules/events/infestation.dm
@@ -3,6 +3,9 @@
#define VERM_SPIDERS 2
/datum/event/infestation
+ name = "Vermin Infestation"
+ role_weights = list(ASSIGNMENT_JANITOR = 0.5)
+ role_requirements = list(ASSIGNMENT_JANITOR = 1)
announceWhen = 10
endWhen = 11
/// Which kind of vermin we'll be spawning (one of the three defines)
diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm
index 3bee46a414a..6c3fc438039 100644
--- a/code/modules/events/ion_storm.dm
+++ b/code/modules/events/ion_storm.dm
@@ -3,13 +3,17 @@
#define ION_ANNOUNCE 1
/datum/event/ion_storm
+ name = "Ion Storm"
+ role_weights = list(ASSIGNMENT_COMMAND = 1)
+ role_requirements = list(ASSIGNMENT_COMMAND = 1)
+ nominal_severity = EVENT_LEVEL_MODERATE
var/botEmagChance = 10
var/announceEvent = ION_NOANNOUNCEMENT // -1 means don't announce, 0 means have it randomly announce, 1 means
var/ionMessage = null
var/ionAnnounceChance = 33
announceWhen = 1
-/datum/event/ion_storm/New(datum/event_meta/EM, skeleton = FALSE, botEmagChance = 10, announceEvent = ION_NOANNOUNCEMENT, ionMessage = null, ionAnnounceChance = 33)
+/datum/event/ion_storm/New(datum/event_meta/EM, skeleton = FALSE, _severity, botEmagChance = 10, announceEvent = ION_NOANNOUNCEMENT, ionMessage = null, ionAnnounceChance = 33)
src.botEmagChance = botEmagChance
src.announceEvent = announceEvent
src.ionMessage = ionMessage
diff --git a/code/modules/events/koi_mirgration.dm b/code/modules/events/koi_mirgration.dm
index 6c9031f3a33..eb0bd452e1f 100644
--- a/code/modules/events/koi_mirgration.dm
+++ b/code/modules/events/koi_mirgration.dm
@@ -1,4 +1,5 @@
/datum/event/carp_migration/koi
+ name = "Koi School"
spawned_mobs = list(
/mob/living/simple_animal/hostile/retaliate/carp/koi = 98,
/mob/living/simple_animal/hostile/retaliate/carp/koi/honk = 2)
diff --git a/code/modules/events/mass_hallucination.dm b/code/modules/events/mass_hallucination.dm
index afb6579b5d1..fe1f623d899 100644
--- a/code/modules/events/mass_hallucination.dm
+++ b/code/modules/events/mass_hallucination.dm
@@ -1,6 +1,9 @@
/// at least 75% rad armor is required to be immune to this event
#define RAD_ARMOR_TO_IMMUNITY 150
+/datum/event/mass_hallucination
+ name = "Mass Hallucination"
+
/datum/event/mass_hallucination/setup()
announceWhen = rand(0, 20)
diff --git a/code/modules/events/meaty_ores.dm b/code/modules/events/meaty_ores.dm
index 543e3bd0bfd..0122787e06b 100644
--- a/code/modules/events/meaty_ores.dm
+++ b/code/modules/events/meaty_ores.dm
@@ -1,3 +1,9 @@
+/datum/event/dust/meaty
+ name = "Meaty Ores"
+ nominal_severity = EVENT_LEVEL_MODERATE
+ role_weights = list(ASSIGNMENT_ENGINEERING = 4)
+ role_requirements = list(ASSIGNMENT_ENGINEERING = 4)
+
/datum/event/dust/meaty/announce()
if(prob(16))
GLOB.minor_announcement.Announce("Unknown biological entities have been detected near [station_name()], please stand-by.", "Lifesign Alert")
@@ -6,6 +12,7 @@
/datum/event/dust/meaty/setup()
qnty = rand(45,125)
+ endWhen = 600 // Make it take up engineering staffing for 20 minutes
/datum/event/dust/meaty/start()
INVOKE_ASYNC(src, PROC_REF(spawn_meaty_ores))
diff --git a/code/modules/events/meteors_event.dm b/code/modules/events/meteors_event.dm
index 39a2f79c5a6..035d8eaeb4b 100644
--- a/code/modules/events/meteors_event.dm
+++ b/code/modules/events/meteors_event.dm
@@ -1,9 +1,16 @@
/datum/event/meteor_wave
+ name = "Meteor Wave"
startWhen = 5
- endWhen = 7
+ nominal_severity = EVENT_LEVEL_MODERATE
+ // We set this when the station clears the meteor storm to keep the event ongoing for a bit so it keeps having a cost
+ noAutoEnd = TRUE
+ role_weights = list(ASSIGNMENT_ENGINEERING = 5)
+ role_requirements = list(ASSIGNMENT_ENGINEERING = 4)
var/next_meteor = 6
var/waves = 1
var/atom/movable/screen/alert/augury/meteor/screen_alert
+ /// The time at which we clear the storm. This is set during the event run so we initialize it to an unreachable value
+ var/clear_time = -1
/datum/event/meteor_wave/setup()
for(var/mob/dead/observer/O in GLOB.dead_mob_list)
@@ -22,15 +29,25 @@
//meteor showers are lighter and more common,
/datum/event/meteor_wave/tick()
// keep observers updated with the alert
- for(var/mob/dead/observer/O in GLOB.dead_mob_list)
- O.throw_alert("\ref[src]_augury", /atom/movable/screen/alert/augury/meteor)
+ if(screen_alert)
+ for(var/mob/dead/observer/O in GLOB.dead_mob_list)
+ O.throw_alert("\ref[src]_augury", /atom/movable/screen/alert/augury/meteor)
if(waves && activeFor >= next_meteor)
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(spawn_meteors), get_meteor_count(), get_meteors())
next_meteor += rand(15, 30) / severity
waves--
- endWhen = (waves ? next_meteor + 1 : activeFor + 15)
+ // When the storm is done we set a timer for the event end instead of finishing it.
+ // This is so it still incurs a cost for the event system for a while.
+ if(!waves && noAutoEnd)
+ // Set a 15 cycle(30 second) timer before we announce clearing the storm.
+ // This is to give the last meteor enough time to finish moving
+ clear_time = activeFor + 15
+ endWhen = activeFor + 700
+ noAutoEnd = FALSE
+ if(activeFor == clear_time)
+ announce_clear()
-/datum/event/meteor_wave/end()
+/datum/event/meteor_wave/proc/announce_clear()
for(var/mob/M in GLOB.dead_mob_list)
M.clear_alert("\ref[src]_augury")
QDEL_NULL(screen_alert)
diff --git a/code/modules/events/money_spam.dm b/code/modules/events/money_spam.dm
index 26dd673250f..07cc3959220 100644
--- a/code/modules/events/money_spam.dm
+++ b/code/modules/events/money_spam.dm
@@ -1,4 +1,6 @@
/datum/event/pda_spam
+ name = "PDA Spam"
+ role_weights = list(ASSIGNMENT_CREW = 4)
endWhen = 36000
var/last_spam_time = 0
var/obj/machinery/message_server/useMS
diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm
index 8bfdf4ed4c7..02f82f05a51 100644
--- a/code/modules/events/prison_break.dm
+++ b/code/modules/events/prison_break.dm
@@ -1,4 +1,8 @@
/datum/event/prison_break
+ name = "Prison Break"
+ role_weights = list(ASSIGNMENT_SECURITY = 0.8, ASSIGNMENT_CREW = 0.2)
+ role_requirements = list(ASSIGNMENT_SECURITY = 2, ASSIGNMENT_CREW = 0)
+ nominal_severity = EVENT_LEVEL_MODERATE
startWhen = 40
announceWhen = 75
diff --git a/code/modules/events/radiation_storm_event.dm b/code/modules/events/radiation_storm_event.dm
index b66afda780f..25917ef0a29 100644
--- a/code/modules/events/radiation_storm_event.dm
+++ b/code/modules/events/radiation_storm_event.dm
@@ -1,3 +1,9 @@
+/datum/event/radiation_storm
+ name = "Radiation Storm"
+ nominal_severity = EVENT_LEVEL_MODERATE
+ role_weights = list(ASSIGNMENT_MEDICAL = 5)
+ role_requirements = list(ASSIGNMENT_MEDICAL = 5)
+
/datum/event/radiation_storm/setup()
startWhen = 3
endWhen = startWhen + 1
diff --git a/code/modules/events/revenant_spawn_event.dm b/code/modules/events/revenant_spawn_event.dm
index a7eef1906a0..b333239e82c 100644
--- a/code/modules/events/revenant_spawn_event.dm
+++ b/code/modules/events/revenant_spawn_event.dm
@@ -1,8 +1,21 @@
#define REVENANT_SPAWN_THRESHOLD 10
/datum/event/revenant
+ name = "Revenant"
+ nominal_severity = EVENT_LEVEL_MAJOR
+ noAutoEnd = TRUE
+ role_weights = list(ASSIGNMENT_CHAPLAIN = 5, ASSIGNMENT_CREW = 0.8)
+ role_requirements = list(ASSIGNMENT_CHAPLAIN = 1, ASSIGNMENT_CREW = 30)
var/key_of_revenant
+// Calculated separately from event
+/datum/event/revenant/event_resource_cost()
+ return list()
+
+/datum/event/revenant/proc/on_revenant_death(mob/source)
+ SIGNAL_HANDLER // COMSIG_MOB_DEATH
+ UnregisterSignal(source, COMSIG_MOB_DEATH)
+ kill()
/datum/event/revenant/proc/get_revenant(end_if_fail = 0)
var/deadMobs = 0
@@ -10,6 +23,7 @@
deadMobs++
if(deadMobs < REVENANT_SPAWN_THRESHOLD)
message_admins("Random event attempted to spawn a revenant, but there were only [deadMobs]/[REVENANT_SPAWN_THRESHOLD] dead mobs.")
+ kill()
return
spawn()
@@ -36,6 +50,7 @@
kill()
return
var/mob/living/basic/revenant/revvie = new /mob/living/basic/revenant/(pick(spawn_locs))
+ RegisterSignal(revvie, COMSIG_MOB_DEATH, PROC_REF(on_revenant_death))
player_mind.transfer_to(revvie)
dust_if_respawnable(C)
player_mind.assigned_role = SPECIAL_ROLE_REVENANT
diff --git a/code/modules/events/rogue_drones.dm b/code/modules/events/rogue_drones.dm
index cca4ef28954..40efaa63b66 100644
--- a/code/modules/events/rogue_drones.dm
+++ b/code/modules/events/rogue_drones.dm
@@ -1,6 +1,10 @@
/datum/event/rogue_drone
+ name = "Rogue Drones"
+ role_weights = list(ASSIGNMENT_SECURITY = 2)
+ role_requirements = list(ASSIGNMENT_SECURITY = 1)
startWhen = 10
endWhen = 1000
+ nominal_severity = EVENT_LEVEL_MODERATE
var/list/drones_list = list()
/datum/event/rogue_drone/start()
diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm
index e4a4ae4b39a..0afa60dcea1 100644
--- a/code/modules/events/sentience.dm
+++ b/code/modules/events/sentience.dm
@@ -1,4 +1,5 @@
/datum/event/sentience
+ name = "Sentience"
/datum/event/sentience/start()
INVOKE_ASYNC(src, PROC_REF(make_sentient_mob))
diff --git a/code/modules/events/shuttle_loan/shuttle_loan_event.dm b/code/modules/events/shuttle_loan/shuttle_loan_event.dm
index 74a36821984..a038f874dcc 100644
--- a/code/modules/events/shuttle_loan/shuttle_loan_event.dm
+++ b/code/modules/events/shuttle_loan/shuttle_loan_event.dm
@@ -2,6 +2,9 @@
name = "Shuttle Loan"
announceWhen = 1
endWhen = 500
+ nominal_severity = EVENT_LEVEL_MODERATE
+ role_weights = list(ASSIGNMENT_CARGO = 2, ASSIGNMENT_SECURITY = 2)
+ role_requirements = list(ASSIGNMENT_CARGO = 2, ASSIGNMENT_SECURITY = 2)
/// what type of shuttle loan situation the station faces.
var/datum/shuttle_loan_situation/situation
/// Whether the station has let Centcom commandeer the shuttle yet.
diff --git a/code/modules/events/slaughterevent.dm b/code/modules/events/slaughterevent.dm
index 851ec6aeb48..f26fa697139 100644
--- a/code/modules/events/slaughterevent.dm
+++ b/code/modules/events/slaughterevent.dm
@@ -1,6 +1,15 @@
/datum/event/spawn_slaughter
+ name = "Slaughter Demon"
+ noAutoEnd = TRUE
var/key_of_slaughter
var/mob/living/simple_animal/demon/demon = /mob/living/simple_animal/demon/slaughter/lesser
+ nominal_severity = EVENT_LEVEL_MAJOR
+ role_weights = list(ASSIGNMENT_SECURITY = 5, ASSIGNMENT_JANITOR = 5, ASSIGNMENT_MEDICAL = 3, ASSIGNMENT_CREW = 0.7)
+ role_requirements = list(ASSIGNMENT_SECURITY = 4, ASSIGNMENT_JANITOR = 1, ASSIGNMENT_MEDICAL = 2, ASSIGNMENT_CREW = 25)
+
+/datum/event/spawn_slaughter/tick()
+ if(!demon || demon.stat == DEAD)
+ kill()
/datum/event/spawn_slaughter/proc/get_slaughter()
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [initial(demon.name)]?", ROLE_DEMON, TRUE, source = demon)
@@ -44,10 +53,15 @@
INVOKE_ASYNC(src, PROC_REF(get_slaughter))
/datum/event/spawn_slaughter/greater
+ name = "Greater Slaughter Demon"
demon = /mob/living/simple_animal/demon/slaughter
/datum/event/spawn_slaughter/shadow
+ name = "Shadow Demon"
demon = /mob/living/simple_animal/demon/shadow
+ // Same as slaughter but without Jani
+ role_weights = list(ASSIGNMENT_SECURITY = 5, ASSIGNMENT_MEDICAL = 3, ASSIGNMENT_CREW = 0.7)
+ role_requirements = list(ASSIGNMENT_SECURITY = 4, ASSIGNMENT_MEDICAL = 2, ASSIGNMENT_CREW = 25)
/datum/event/spawn_slaughter/shadow/get_spawn_loc()
var/turf/spawn_center = ..()
diff --git a/code/modules/events/solarflare.dm b/code/modules/events/solarflare.dm
index b289dd7ea56..d4b79cdb8a0 100644
--- a/code/modules/events/solarflare.dm
+++ b/code/modules/events/solarflare.dm
@@ -1,4 +1,5 @@
/datum/event/solar_flare
+ name = "Solar Flare"
startWhen = 2
endWhen = 3
announceWhen = 1
diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm
index 508819fde29..1387fe4acf3 100644
--- a/code/modules/events/spacevine.dm
+++ b/code/modules/events/spacevine.dm
@@ -1,3 +1,9 @@
+/datum/event/spacevine
+ name = "Space Vines"
+ role_weights = list(ASSIGNMENT_CREW = 0.4, ASSIGNMENT_ENGINEERING = 1, ASSIGNMENT_BOTANIST = 2)
+ role_requirements = list(ASSIGNMENT_CREW = 25 , ASSIGNMENT_BOTANIST = 1, ASSIGNMENT_ENGINEERING = 1)
+ nominal_severity = EVENT_LEVEL_MODERATE
+
/datum/event/spacevine/start()
var/list/turfs = list() //list of all the empty floor turfs in the hallway areas
diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm
index 03ef7c95e1a..3e846a8a6b1 100644
--- a/code/modules/events/spider_infestation.dm
+++ b/code/modules/events/spider_infestation.dm
@@ -1,4 +1,6 @@
/datum/event/spider_infestation
+ name = "Spider Infestation"
+ role_weights = list(ASSIGNMENT_SECURITY = 30)
announceWhen = 400
var/spawncount = 1
var/successSpawn = FALSE //So we don't make a command report if nothing gets spawned.
diff --git a/code/modules/events/spider_terror.dm b/code/modules/events/spider_terror.dm
index ead04dc76e9..39f16a5e40f 100644
--- a/code/modules/events/spider_terror.dm
+++ b/code/modules/events/spider_terror.dm
@@ -1,7 +1,12 @@
#define TS_HIGHPOP_TRIGGER 80
/datum/event/spider_terror
+ name = "terror spiders"
announceWhen = 240
+ noAutoEnd = TRUE
+ nominal_severity = EVENT_LEVEL_DISASTER
+ role_weights = list(ASSIGNMENT_SECURITY = 2, ASSIGNMENT_CREW = 0.8, ASSIGNMENT_MEDICAL = 2.5)
+ role_requirements = list(ASSIGNMENT_SECURITY = 3, ASSIGNMENT_CREW = 45, ASSIGNMENT_MEDICAL = 4)
var/spawncount = 1
var/successSpawn = FALSE //So we don't make a command report if nothing gets spawned.
@@ -19,6 +24,16 @@
// It is necessary to wrap this to avoid the event triggering repeatedly.
INVOKE_ASYNC(src, PROC_REF(wrappedstart))
+/// Terror spider costs are calculated independently from the event itself
+/datum/event/spider_terror/event_resource_cost()
+ return list()
+
+/datum/event/spider_terror/process()
+ // End the event once all spiders, eggs and spiderlings are gone from the station Z level.
+ if(!length(event_category_cost(EVENT_TERROR_SPIDERS)) && successSpawn)
+ kill()
+ . = ..()
+
/datum/event/spider_terror/proc/wrappedstart()
var/spider_type
var/infestation_type
@@ -66,6 +81,8 @@
S.give_intro_text()
spawncount--
successSpawn = TRUE
+ if(!successSpawn)
+ kill()
SSticker.record_biohazard_start(infestation_type)
SSevents.biohazards_this_round += infestation_type
diff --git a/code/modules/events/spontaneous_appendicitis.dm b/code/modules/events/spontaneous_appendicitis.dm
index 53923f4d124..679a8cafc20 100644
--- a/code/modules/events/spontaneous_appendicitis.dm
+++ b/code/modules/events/spontaneous_appendicitis.dm
@@ -1,3 +1,8 @@
+/datum/event/spontaneous_appendicitis
+ name = "Appendicitis"
+ role_weights = list(ASSIGNMENT_MEDICAL = 1)
+ role_requirements = list(ASSIGNMENT_MEDICAL = 1)
+
/datum/event/spontaneous_appendicitis/start()
for(var/mob/living/carbon/human/H in shuffle(GLOB.alive_mob_list))
if(issmall(H)) //don't infect monkies; that's a waste.
diff --git a/code/modules/events/tear.dm b/code/modules/events/tear.dm
index a61ecda9b11..a3bfa775013 100644
--- a/code/modules/events/tear.dm
+++ b/code/modules/events/tear.dm
@@ -7,6 +7,9 @@
*/
/datum/event/tear
name = "dimensional tear"
+ nominal_severity = EVENT_LEVEL_MODERATE
+ role_weights = list(ASSIGNMENT_SECURITY = 2)
+ role_requirements = list(ASSIGNMENT_SECURITY = 2)
announceWhen = 6
endWhen = 14
var/notify_title = "Dimensional Rift"
diff --git a/code/modules/events/traders.dm b/code/modules/events/traders.dm
index 94cd3b4050d..ee9d5ccaf6f 100644
--- a/code/modules/events/traders.dm
+++ b/code/modules/events/traders.dm
@@ -4,6 +4,10 @@ GLOBAL_LIST_INIT(unused_trade_stations, list("sol"))
// Heavily copy-pasted from "heist" gamemode.
/datum/event/traders
+ name = "Traders"
+ role_weights = list(ASSIGNMENT_CREW = 0.5)
+ role_requirements = list(ASSIGNMENT_CREW = 30)
+ nominal_severity = EVENT_LEVEL_MAJOR
var/success_spawn = 0
var/station = null
var/spawn_count = 2
diff --git a/code/modules/events/vent_clog.dm b/code/modules/events/vent_clog.dm
index 29b759a2dfa..77063a650c3 100644
--- a/code/modules/events/vent_clog.dm
+++ b/code/modules/events/vent_clog.dm
@@ -1,6 +1,8 @@
/datum/event/vent_clog
+ name = "Scrubber Clog"
startWhen = 5
endWhen = 35
+ nominal_severity = EVENT_LEVEL_MODERATE
var/interval = 2
var/list/vents = list()
diff --git a/code/modules/events/wallrot.dm b/code/modules/events/wallrot.dm
index b9c5729dc80..bcba8bce4af 100644
--- a/code/modules/events/wallrot.dm
+++ b/code/modules/events/wallrot.dm
@@ -1,3 +1,8 @@
+/datum/event/wallrot
+ name = "Wallrot"
+ role_weights = list(ASSIGNMENT_ENGINEERING = 5)
+ role_requirements = list(ASSIGNMENT_ENGINEERING = 1)
+
/datum/event/wallrot/start()
INVOKE_ASYNC(src, PROC_REF(spawn_wallrot))
@@ -36,6 +41,9 @@
break
/datum/event/wallrot/fungus
+ name = "Fungal Growth"
+ role_weights = list(ASSIGNMENT_CHEMIST = 5)
+ role_weights = list(ASSIGNMENT_CHEMIST = 0)
/datum/event/wallrot/fungus/is_valid_candidate(turf/T)
return istype(get_area(T), /area/station/maintenance)
diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm
index 0d045518dba..b3f499bb73e 100644
--- a/code/modules/events/wormholes.dm
+++ b/code/modules/events/wormholes.dm
@@ -1,4 +1,6 @@
/datum/event/wormholes
+ name = "Wormholes"
+ role_weights = "Vent Clog"
announceWhen = 10
endWhen = 60
diff --git a/code/modules/mob/living/basic/friendly/mouse.dm b/code/modules/mob/living/basic/friendly/mouse.dm
index 84601b815fa..b522258735f 100644
--- a/code/modules/mob/living/basic/friendly/mouse.dm
+++ b/code/modules/mob/living/basic/friendly/mouse.dm
@@ -213,6 +213,7 @@
/mob/living/basic/mouse/blobinfected/Initialize(mapload)
. = ..()
+ AddComponent(/datum/component/event_tracker, EVENT_BLOB)
apply_status_effect(STATUS_EFFECT_BLOB_BURST, 120 SECONDS, CALLBACK(src, PROC_REF(burst), FALSE))
/mob/living/basic/mouse/blobinfected/Life()
@@ -252,6 +253,7 @@
if(core)
core.admin_spawned = admin_spawned
+ SEND_SIGNAL(src, COMSIG_BLOB_MOUSE_BURST, core)
SSticker.record_biohazard_start(BIOHAZARD_BLOB)
/mob/living/basic/mouse/blobinfected/get_scooped(mob/living/carbon/grabber)
diff --git a/code/modules/mob/living/basic/hostile/carp.dm b/code/modules/mob/living/basic/hostile/carp.dm
index 5df961d75e3..354fe9510bb 100644
--- a/code/modules/mob/living/basic/hostile/carp.dm
+++ b/code/modules/mob/living/basic/hostile/carp.dm
@@ -68,6 +68,12 @@
. = ..()
carp_randomify(rarechance)
AddComponent(/datum/component/aggro_emote, emote_list = list("gnashes"))
+ AddComponent(/datum/component/event_tracker, EVENT_CARP)
+
+/mob/living/basic/carp/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_CREW = 0.1)
/mob/living/basic/carp/proc/carp_randomify(rarechance)
if(random_color)
diff --git a/code/modules/mob/living/basic/hostile/hellhound.dm b/code/modules/mob/living/basic/hostile/hellhound.dm
index 926ec47684a..24435082f4c 100644
--- a/code/modules/mob/living/basic/hostile/hellhound.dm
+++ b/code/modules/mob/living/basic/hostile/hellhound.dm
@@ -45,6 +45,13 @@
/mob/living/basic/hellhound/Initialize(mapload)
. = ..()
AddElement(/datum/element/ai_retaliate)
+ AddComponent(/datum/component/event_tracker)
+
+/mob/living/basic/hellhound/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 1, ASSIGNMENT_MEDICAL = 0.5)
+
/mob/living/basic/hellhound/examine(mob/user)
. = ..()
diff --git a/code/modules/mob/living/basic/hostile/revenant/revenant.dm b/code/modules/mob/living/basic/hostile/revenant/revenant.dm
index 97dc0500ba6..24c042a0150 100644
--- a/code/modules/mob/living/basic/hostile/revenant/revenant.dm
+++ b/code/modules/mob/living/basic/hostile/revenant/revenant.dm
@@ -156,6 +156,11 @@
remove_from_all_data_huds()
giveSpells()
RegisterSignal(src, COMSIG_BODY_TRANSFER_TO, PROC_REF(make_revenant_antagonist))
+ AddComponent(/datum/component/event_tracker, EVENT_REVENTANT)
+
+/mob/living/basic/revenant/event_cost()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_CHAPLAIN = 1, ASSIGNMENT_CREW = 10, ASSIGNMENT_MEDICAL = 2)
/mob/living/basic/revenant/proc/make_revenant_antagonist(revenant)
SIGNAL_HANDLER // COMSIG_BODY_TRANSFER_TO
diff --git a/code/modules/mob/living/basic/hostile/skeleton_mob.dm b/code/modules/mob/living/basic/hostile/skeleton_mob.dm
index 17aa73081ef..655cdfcf149 100644
--- a/code/modules/mob/living/basic/hostile/skeleton_mob.dm
+++ b/code/modules/mob/living/basic/hostile/skeleton_mob.dm
@@ -37,6 +37,13 @@
/mob/living/basic/skeleton/Initialize(mapload)
. = ..()
AddElement(/datum/element/ai_retaliate)
+ AddComponent(/datum/component/event_tracker)
+
+/mob/living/basic/skeleton/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 1, ASSIGNMENT_MEDICAL = 0.5)
+
/mob/living/basic/skeleton/arctic
name = "undead arctic explorer"
diff --git a/code/modules/mob/living/basic/nether_mobs/nether_mobs.dm b/code/modules/mob/living/basic/nether_mobs/nether_mobs.dm
index 211fd482c92..8b72354c24c 100644
--- a/code/modules/mob/living/basic/nether_mobs/nether_mobs.dm
+++ b/code/modules/mob/living/basic/nether_mobs/nether_mobs.dm
@@ -36,3 +36,9 @@
update_appearance(UPDATE_NAME)
color = "#5494DA"
AddElement(/datum/element/ai_retaliate)
+ AddComponent(/datum/component/event_tracker, EVENT_DEMONIC)
+
+/mob/living/basic/netherworld/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 1, ASSIGNMENT_MEDICAL = 0.5)
diff --git a/code/modules/mob/living/basic/retaliate/combat_drone.dm b/code/modules/mob/living/basic/retaliate/combat_drone.dm
index 60ac532c7f8..b0b08cd82df 100644
--- a/code/modules/mob/living/basic/retaliate/combat_drone.dm
+++ b/code/modules/mob/living/basic/retaliate/combat_drone.dm
@@ -34,6 +34,7 @@
set_default_language(GLOB.all_languages["Galactic Common"])
AddElement(/datum/element/ai_retaliate)
update_icons()
+ AddComponent(/datum/component/event_tracker, EVENT_DRONE)
/mob/living/basic/malf_drone/proc/create_trail(datum/source, atom/oldloc, _dir, forced)
var/turf/T = get_turf(oldloc)
diff --git a/code/modules/mob/living/carbon/alien/alien_base.dm b/code/modules/mob/living/carbon/alien/alien_base.dm
index b1455fc31b9..feb234c5e58 100644
--- a/code/modules/mob/living/carbon/alien/alien_base.dm
+++ b/code/modules/mob/living/carbon/alien/alien_base.dm
@@ -33,6 +33,12 @@
for(var/organ_path in get_caste_organs())
var/obj/item/organ/internal/organ = new organ_path()
organ.insert(src)
+ AddComponent(/datum/component/event_tracker, EVENT_XENOS)
+
+/mob/living/carbon/alien/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z))
+ return list(ASSIGNMENT_SECURITY = 1, ASSIGNMENT_CREW = 3, ASSIGNMENT_MEDICAL = 1)
/// returns the list of type paths of the organs that we need to insert into
/// this particular xeno upon its creation
diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
index 8e6f90c28ae..35fa500bfb4 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
@@ -15,6 +15,11 @@
name = "alien hunter ([rand(1, 1000)])"
real_name = name
+/mob/living/carbon/alien/humanoid/hunter/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z))
+ return list(ASSIGNMENT_SECURITY = 0.8, ASSIGNMENT_CREW = 4, ASSIGNMENT_MEDICAL = 0.4)
+
/mob/living/carbon/alien/humanoid/hunter/get_caste_organs()
. = ..()
. += /obj/item/organ/internal/alien/plasmavessel/hunter
diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm
index 9e20672738e..91bf252a9a8 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm
@@ -31,6 +31,11 @@
name = "alien sentinel ([rand(1, 1000)])"
real_name = name
+/mob/living/carbon/alien/humenoid/sentinel/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z))
+ return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 3, ASSIGNMENT_MEDICAL = 0.2)
+
/mob/living/carbon/alien/humanoid/sentinel/get_caste_organs()
. = ..()
. += list(
diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
index 6781fda1c29..996618c6e0f 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
@@ -32,6 +32,7 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list(
AddSpell(new /datum/spell/alien_spell/regurgitate)
. = ..()
AddComponent(/datum/component/footstep, FOOTSTEP_MOB_CLAW, 0.5, -11)
+ AddComponent(/datum/component/event_tracker, EVENT_XENOS)
/mob/living/carbon/alien/humanoid/get_strippable_items(datum/source, list/items)
items |= GLOB.strippable_alien_humanoid_items
diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm
index 789444d4c43..aae2a6ebae0 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm
@@ -19,6 +19,11 @@
ADD_TRAIT(src, TRAIT_FORCE_DOORS, UNIQUE_TRAIT_SOURCE(src))
AddSpell(new /datum/spell/alien_spell/tail_lash)
+/mob/living/carbon/alien/humenoid/queen/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z))
+ return list(ASSIGNMENT_SECURITY = 2, ASSIGNMENT_CREW = 12, ASSIGNMENT_MEDICAL = 1)
+
/mob/living/carbon/alien/humanoid/queen/get_caste_organs()
. = ..()
. += list(
diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm
index b56e3392ceb..c51e3f2c64a 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva.dm
@@ -31,6 +31,11 @@
var/datum/action/innate/hide/alien_larva_hide/hide = new()
hide.Grant(src)
+/mob/living/carbon/alien/larva/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z))
+ return list(ASSIGNMENT_SECURITY = 0.2, ASSIGNMENT_CREW = 1)
+
/mob/living/carbon/alien/larva/Destroy()
for(var/datum/action/innate/hide/alien_larva_hide/hide in actions)
hide.Remove(src)
diff --git a/code/modules/mob/living/simple_animal/hostile/mimic_mobs.dm b/code/modules/mob/living/simple_animal/hostile/mimic_mobs.dm
index 02ad0664a34..4149036554d 100644
--- a/code/modules/mob/living/simple_animal/hostile/mimic_mobs.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mimic_mobs.dm
@@ -235,8 +235,15 @@ GLOBAL_LIST_INIT(protected_objects, list(/obj/structure/table, /obj/structure/ca
orig_vendor.forceMove(src)
orig_vendor.aggressive = FALSE // just to be safe, in case this was converted
+ AddComponent(/datum/component/event_tracker, EVENT_BRAND_INTELLIGENCE)
+
return ..(mapload, base, creator, destroy_original = FALSE)
+/mob/living/simple_animal/hostile/mimic/copy/vendor/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 1)
+
/mob/living/simple_animal/hostile/mimic/copy/vendor/AttackingTarget()
. = ..()
if(. && target && Adjacent(target))
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm
index 3191e2dd7c3..3b50711640d 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm
@@ -97,6 +97,10 @@
else
visible_message("[src] bites [target], but cannot inject venom into [target.p_their()] [inject_target]!")
+/mob/living/simple_animal/hostile/poison/terror_spider/green/event_cost()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 2, ASSIGNMENT_MEDICAL = 0.2)
+
/obj/structure/spider/terrorweb/green
name = "slimy web"
desc = "This web is partly composed of strands of green slime."
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm
index fdd2f2116de..151e4383eef 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm
@@ -109,3 +109,7 @@
if(hit_something)
visible_message("[src] slams into [hit_atom]!", "You slam into [hit_atom]!")
playsound(get_turf(src), 'sound/effects/meteorimpact.ogg', 100, TRUE)
+
+/mob/living/simple_animal/hostile/poison/terror_spider/prince/event_cost()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 2, ASSIGNMENT_CREW = 20, ASSIGNMENT_MEDICAL = 3)
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm
index c9448373d16..eba3e0c2a18 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm
@@ -123,3 +123,6 @@
return TRUE
return FALSE
+/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/event_cost()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 2, ASSIGNMENT_CREW = 15, ASSIGNMENT_MEDICAL = 2)
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen_terror.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen_terror.dm
index b24824e4a4c..d359fa58e56 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen_terror.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen_terror.dm
@@ -348,6 +348,9 @@
. += "[p_they(TRUE)] has laid [eggslaid] egg[eggslaid != 1 ? "s" : ""]."
. += "[p_they(TRUE)] has lived for [MinutesAlive()] minutes."
+/mob/living/simple_animal/hostile/poison/terror_spider/queen/event_cost()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 3, ASSIGNMENT_CREW = 30, ASSIGNMENT_MEDICAL = 2)
/obj/item/projectile/terrorqueenspit
name = "acid spit"
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm
index 89c2ae0d8c0..718874daf44 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm
@@ -22,6 +22,7 @@
GLOB.ts_spiderling_list += src
if(is_away_level(z))
spider_awaymission = TRUE
+ AddComponent(/datum/component/event_tracker, EVENT_TERROR_SPIDERS)
/obj/structure/spider/spiderling/terror_spiderling/Destroy()
GLOB.ts_spiderling_list -= src
@@ -216,6 +217,7 @@
name = "prince of terror eggs"
if(/mob/living/simple_animal/hostile/poison/terror_spider/queen)
name = "queen of terror eggs"
+ AddComponent(/datum/component/event_tracker, EVENT_TERROR_SPIDERS)
/obj/structure/spider/eggcluster/terror_eggcluster/Destroy()
GLOB.ts_egg_list -= src
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm
index 657f5e64b8f..62e77046917 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm
@@ -238,6 +238,10 @@ GLOBAL_LIST_EMPTY(ts_infected_list)
fed++
qdel(J)
+/mob/living/simple_animal/hostile/poison/terror_spider/event_cost()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 0.2, ASSIGNMENT_CREW = 1.5, ASSIGNMENT_MEDICAL = 0.2)
+
// --------------------------------------------------------------------------------
// --------------------- TERROR SPIDERS: PROC OVERRIDES ---------------------------
// --------------------------------------------------------------------------------
@@ -287,6 +291,7 @@ GLOBAL_LIST_EMPTY(ts_infected_list)
var/datum/atom_hud/U = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
U.add_hud_to(src)
spider_creation_time = world.time
+ AddComponent(/datum/component/event_tracker, EVENT_TERROR_SPIDERS)
/mob/living/simple_animal/hostile/poison/terror_spider/proc/announcetoghosts()
if(stat == DEAD)
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm
index 7187d64eede..4ba84b3bdde 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm
@@ -51,6 +51,10 @@
LoseTarget()
GLOB.move_manager.move_away(src,L,2,1)
+/mob/living/simple_animal/hostile/poison/terror_spider/white/event_cost()
+ if(is_station_level((get_turf(src)).z) && stat != DEAD)
+ return list(ASSIGNMENT_SECURITY = 1, ASSIGNMENT_CREW = 5, ASSIGNMENT_MEDICAL = 2)
+
/proc/IsTSInfected(mob/living/carbon/C) // Terror AI requires this
if(C.get_int_organ(/obj/item/organ/internal/body_egg))
return 1
@@ -59,6 +63,12 @@
/obj/item/organ/internal/body_egg/terror_eggs/Initialize(mapload)
. = ..()
GLOB.ts_infected_list += src
+ AddComponent(/datum/component/event_tracker, EVENT_TERROR_SPIDERS)
+
+/obj/item/organ/internal/body_egg/terror_eggs/event_cost()
+ . = list()
+ if(is_station_level((get_turf(src)).z) && owner)
+ return list(ASSIGNMENT_MEDICAL = 1)
/obj/item/organ/internal/body_egg/terror_eggs/insert(mob/living/carbon/M, special)
. = ..()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index cf29d23642a..37623e95500 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1642,3 +1642,17 @@ GLOBAL_LIST_INIT(holy_areas, typecacheof(list(
client.mouse_pointer_icon = null
return
client.mouse_pointer_icon = mousepointers["[lowest_prio]"]
+
+/mob/proc/get_event_capacity()
+ . = 1
+ var/max_symptom_severity = 0
+ for(var/datum/disease/advance/virus in viruses)
+ for(var/datum/symptom/curr_symptom in virus.symptoms)
+ if(curr_symptom.severity > max_symptom_severity)
+ max_symptom_severity = curr_symptom.severity
+ switch(max_symptom_severity)
+ if(4)
+ . *= 0.5
+ if(5 to 6)
+ return 0
+
diff --git a/code/modules/station_goals/shield.dm b/code/modules/station_goals/shield.dm
index ab9f4ffd65f..e4d197e172e 100644
--- a/code/modules/station_goals/shield.dm
+++ b/code/modules/station_goals/shield.dm
@@ -294,7 +294,7 @@
/obj/machinery/satellite/meteor_shield/proc/change_meteor_chance(mod)
for(var/datum/event_container/container in SSevents.event_containers)
for(var/datum/event_meta/M in container.available_events)
- if(M.event_type == /datum/event/meteor_wave)
+ if(M.skeleton.type == /datum/event/meteor_wave)
M.weight_mod *= mod
/obj/machinery/satellite/meteor_shield/Destroy()
diff --git a/config/example/config.toml b/config/example/config.toml
index 598bee10690..dd8a6bf9c74 100644
--- a/config/example/config.toml
+++ b/config/example/config.toml
@@ -229,9 +229,9 @@ forward_all_ahelps = true
# Disable this to disable all random events
allow_random_events = true
# Map of lower bounds for event delays. In minutes.
-event_delay_lower_bounds = {mundane = 10, moderate = 30, major = 50}
+event_delay_lower_bounds = {mundane = 5, moderate = 7.5, major = 12, disaster = 9}
# Map of upper bounds for event delays. In minutes.
-event_delay_upper_bounds = {mundane = 15, moderate = 45, major = 70}
+event_delay_upper_bounds = {mundane = 7.5, moderate = 12, major = 17, disaster = 12}
# Expected round length in minutes. Changes event weights
expected_round_length = 120
# Initial delays for the first events firing off. If any of these are commented, a random value based on above thresholds is used.
@@ -239,8 +239,9 @@ expected_round_length = 120
# Values are in minutes
event_initial_delays = [
#{severity = "mundane", lower_bound = 10, upper_bound = 15},
- {severity = "moderate", lower_bound = 25, upper_bound = 40},
- {severity = "major", lower_bound = 55, upper_bound = 75},
+ {severity = "moderate", lower_bound = 19, upper_bound = 23},
+ {severity = "major", lower_bound = 29, upper_bound = 36},
+ {severity = "disaster", lower_bound = 40, upper_bound = 50},
]
# The population needed to allow blobs to split consciousness
blob_highpop_trigger = 60
diff --git a/paradise.dme b/paradise.dme
index 3c12a303e98..de3806e5489 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -43,6 +43,7 @@
#include "code\__DEFINES\basic_mob_defines.dm"
#include "code\__DEFINES\basic_mob_flags.dm"
#include "code\__DEFINES\bio_chip_defines.dm"
+#include "code\__DEFINES\blob_defines.dm"
#include "code\__DEFINES\bots.dm"
#include "code\__DEFINES\cable_defines.dm"
#include "code\__DEFINES\callbacks.dm"
@@ -66,6 +67,7 @@
#include "code\__DEFINES\economy_event_defines.dm"
#include "code\__DEFINES\emotes_defines.dm"
#include "code\__DEFINES\error_handler_defines.dm"
+#include "code\__DEFINES\event_defines.dm"
#include "code\__DEFINES\flags.dm"
#include "code\__DEFINES\food_defines.dm"
#include "code\__DEFINES\footstep_defines.dm"
@@ -549,6 +551,7 @@
#include "code\datums\components\ducttape.dm"
#include "code\datums\components\edit_complainer.dm"
#include "code\datums\components\emissive_blocker.dm"
+#include "code\datums\components\event_tracker.dm"
#include "code\datums\components\footstep.dm"
#include "code\datums\components\forces_doors_open.dm"
#include "code\datums\components\fullauto.dm"