From e609a7faf77d4c4ad8a926958937fceb67c4130c Mon Sep 17 00:00:00 2001 From: Migratingcocofruit <69551563+Migratingcocofruit@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:29:47 +0200 Subject: [PATCH] Event system refactor and changes (#29420) * meta resolution * emerald resolution * delta resolution * fartgas resolution * box box * stay out stay out * metastation fixes * faragus fix * delta fix * Changes to role impact on event weight * adds disaster level to the event config * weight changes and starts adding disaster level to the ui * ui fixes * adjust disaster level timing * changes disaster timing, weights, disease outbreak role requirements and weights * lots of weight an requirement adjustments * Update tear.dm * some fixes * meta changes * Starting over with farragus * Starting over in faragus * faragus changes * Update cerestation.dmm * Update cerestation.dmm * Update cerestation.dmm * Update cerestation.dmm * Update cerestation.dmm * resolving some faragus conflicts * resolve faragus conflicts * resolves delta conflicts * resolves cyberiad conflicts * resolves emerald conflicts * fixes cables in faragus * fixes meta cables * fixes delta cables * more meta fixes * more farragus cable fixes * more cable fixes * meta and faragus fixes * box and delta fixes * more mapping fixes * hopefully the last fix * Update metastation.dmm * Update metastation.dmm * fixes shorts on cyberiad cerebron and farragus * CI fixes * move new event listing to new system * Adds tracking and cost calculation for blob * finishes setting up blob event detection and costs * minor boolog corrections * fixes events being created at the wrong severity * adds ongoing cost to disease outbreak * moves terror spider event to new system * moves to component based event cost calculation * morph abductor and carps adjusted for new system * various fixes and adjusts rev event * Update revenant_spawn_event.dm * various fixes and adjusts demon incursion to the new system * adjusts some major event weights * correct wrong values of autoend on vendor uprising and rev * Update event_container.dm * more event timing adjustments * adds requirements to traders and incursion * slightly lowers requirements for biohazards * timing changes * temporarily disable loading event times from the config and increases major nothing chance * adjustments to costs and weights of various events * Update event_container.dm * additional adjustments to weights and requirements. moves rev to major * typo fix * ion storm adjustment * Update event_container.dm * more moderate changes * e * Update meteors_event.dm * removes cooldown from nothing event and changes its weight based on the number of remaining enabled events in the container * increases morph event requirements and chance of nothing moderate event * makes morph a major event * Update morph_event.dm * adds viro requirement for disease outbreak and fixes a bug with incorrect resource calculation * allows varediting extra roles for the event system for debug and testing * Adjust weights of traders and abductors and corrects wrong nominal severity for demonic incursion * Adds crew cound to rev weight * makes disasters start rolling later * reduces weight of communications blackout and increases weight of major nothing * adjusts door runtime and prisonbreak weights to make them less frequent. kudzu now scales with total crew * apc overload made less frequent * oopsy * Gives the scrubber clog event a name * fixes a type in a define name * adjusts demonic incursion weight * changes brand intelligence weight an d reduces door runtime weight * more adjustments to demon incursion. Makes additional weight from surplus roles diminish * adjustments to some minor events * Increases event length and requirements of some engineering events and lowers their weight slightly. Also increases cost of ongoing diseases events * fixes a type * Update meteors_event.dm * corrects traders nominal severity and adjusts demon weights * shifts anomaly weights down and increases costs * rebalance rod requirements * Update cerestation.dmm * moves net resource calculation to its own proc and changes the ASSIGNMENT_TOTAL macro to ASSIGNMENT_CREW. * Adds individual first run time for some events and makes majors start rolling slightly later * reduces weight of major nothing * minor reduction to rev and apc overload weight * Changes the way event weight is calculated * end time corrections for anomaly rod and meteors * slightly adjusts down the weights of nothing and some minor events * Adjusts moedrates towards about 3.5 per round and mundanes towards 8 per round * adds first run restrictions on traders and incursion and increases the cooldown time of events * resolve faragus conflicts * resolve emerald conflict * fix some faragus cabling * more cabling correction in faragus * more cable fixes * Update cerestation.dmm * remove doubled scrubber pipe on tile * reduces moderates nothing chance * slightly reduces crew requirements for disasters and also reduces crew weight for disasters * fixes meteor event not removing the alert, and makes it announce a bit later * Adds an event debugging window for real time midround event data * Revert "Adds an event debugging window for real time midround event data" This reverts commit ecd99a6c9e3e0afa5b667e52347e1fda94018a64. * Adds a resource view button to the event manager window and fixes a null in the total resource list * various bug fixes * slightly lowers chance of meteor and similar events * Slightly lowers requirement weights on disaster level events * resolves faragus conflict * changes the default power input of engineering SMES to 200kw and 80kW respectively * connects power monitoring computers to the grid that weren't * meta station elec maints improvements * adds missing power monitoring computers on box and delta * removes dirt from wall * corrects area boundry on meta * adds missing cable on box sci * removes dirt from wall on box * connects cyberiad bridge to the rest of the powernet * connects box brig to powernet * Adds catwalks to box maints * adds catwalks to meta * Adds catwalks to delta * Update deltastation.dmm * adds ability to insulate cables * Moves stations to using extra insulated cables for high power network * Removes catwalks from meta * Removes catwalks from box * Removes catwalks from delta * Delays moderates and majors more and increases disaster chance * resolves box conflicts * Update cable.dm * remove floating request console * corrects position of fire alarm and removes extra intercom on metastation * resolves faragus conflict * reduces meteor variant weights. meaty ores event now has the correct nominal severity * meaty ore weight to 8 * fixes a stray light bulb and light switch in maints as well as a doubled power * Delays meteor type events to 40 minutes at the earliest. Moves the time check to be against round time instead of world time * wires kerberos security checkpoint to the grid * Update cerestation.dmm * Removes stray extinguisher cabinet from meta station * resolves area conflict on faragus * resolves more faragus issues * fixes faragus cabling issues * Update cerestation.dmm * removes another stray fire extinguisher cabinet * insulates the APC in delta PTL room * increases pop requirement and weight for demons and increases medbay and viro weights for diseases * connect delta fore starboard solars to the high power grid instead of the low power * Update deltastation.dmm * deconflict * solves some unconnected cables * connects delta customs to network and finishes resolving conflicts * Update deltastation.dmm * adds the new cables. needs coils and mapping * moves connection perms to the heavy duty cables. toggled with engineering permission ID. box gets new cables * sorts cables on stations * faragus cable connections * cable corrections. Adds heavy duty cables to the autolathe * CI fix * more fixes and makes the new cables show up on t ray scanners * box station mapping fixes * delta mapping fixes * meta fix * Update deltastation.dmm * delta and faragus fixes * Update emeraldstation.dmm * removes APC overloard(major) from event pool * adds diagonal sprites to heavy duty cables. allows sillicons to toggle them and * Makes borg versions of the heavy duty cable coil * palete consolidation * Improves visibility of high power cables * converts shuttle event to new system * makes disaster event last run calc use round time instead of world time * fixes wrong define being used in some event requirements. further increases requirements for demons and meteors. lowers weight of major diseases * Update slaughterevent.dm * reduces cargo requirements for shuttle load event * reduces base weight of major disease event and further increases medical requirements * fixes a wrong wire under the pet store maints door in delta station * resolve mapping conflicts * Revert "resolve mapping conflicts" This reverts commit 129db52501ac61790d1130445537b925dff0f9af. * Revert "Merge branch 'HighPowerMaints' into event_timing" This reverts commit 2594ac89281f46fd1f2a4d739c47e77f170a9fcc, reversing changes made to a738b5f2f27f2635c944174f08196f50762a17a2. * fixes some wonky stuff with xeno code * Adds a crew cost to diseases * makes player contribution towards events variable * changes major event weights and slightly increases demon requirements * slightly increases major disease outbreak weight * comment changes * Adds safeties to protect against null events getting into container or the active list * blob fix * Adds a confirmation to random event toggle * Adjust particulate event weight to fit the new moderate weights * Adds additional security costs to changeling and vamp * Update revenant_spawn_event.dm * uses correct path for rev event cost proc * changes some lists to alists * correctly accesses the event container list and actually uses the config for the event timing --------- Co-authored-by: FunnyMan3595 (Charlie Nolan) Co-authored-by: warriorstar-orion --- code/__DEFINES/blob_defines.dm | 1 + code/__DEFINES/event_defines.dm | 31 ++ code/__DEFINES/misc_defines.dm | 4 - .../sections/event_configuration.dm | 28 +- code/controllers/subsystem/SSevents.dm | 102 +++++-- code/datums/components/event_tracker.dm | 21 ++ code/datums/station_traits/negative_traits.dm | 6 +- code/game/atoms.dm | 10 +- code/game/gamemodes/miniantags/morph/morph.dm | 10 +- .../gamemodes/miniantags/morph/morph_event.dm | 15 + .../miniantags/tourist/tourist_arrivals.dm | 5 +- code/game/objects/effects/spiders.dm | 1 + code/game/objects/structures/aliens.dm | 1 + code/modules/admin/verbs/randomverbs.dm | 3 + .../antagonists/_common/antag_datum.dm | 3 + .../antagonists/abductor/datum_abductor.dm | 3 + .../changeling/datum_changeling.dm | 4 + .../modules/antagonists/vampire/vamp_datum.dm | 4 + .../economy/economy_events/economy_event.dm | 1 + .../economy_events/economy_events_mundane.dm | 2 + code/modules/events/abductor_event.dm | 19 +- code/modules/events/alien_infestation.dm | 20 +- code/modules/events/anomaly_bluespace.dm | 4 +- code/modules/events/anomaly_cryo.dm | 2 +- code/modules/events/anomaly_event.dm | 16 +- code/modules/events/anomaly_flux.dm | 2 +- code/modules/events/anomaly_grav.dm | 2 +- code/modules/events/anomaly_pyro.dm | 2 +- code/modules/events/anomaly_vortex.dm | 4 +- code/modules/events/apc_overload.dm | 6 +- code/modules/events/apc_short.dm | 9 +- code/modules/events/blob/blob_mobs.dm | 11 + .../events/blob/blob_structures/blob_core.dm | 4 + .../events/blob/blob_structures/factory.dm | 5 + .../events/blob/blob_structures/node.dm | 5 + code/modules/events/blob/theblob.dm | 6 + code/modules/events/blob_spawn.dm | 36 ++- code/modules/events/brand_intelligence.dm | 8 +- code/modules/events/bureaucratic_error.dm | 1 + code/modules/events/camera_failure.dm | 4 + code/modules/events/carp_migration.dm | 32 +- code/modules/events/clogged_disposals.dm | 1 + .../modules/events/communications_blackout.dm | 4 + code/modules/events/demon_incursion.dm | 29 +- code/modules/events/disease_outbreak.dm | 19 +- code/modules/events/door_runtime.dm | 3 + code/modules/events/electrical_storm.dm | 4 + code/modules/events/event.dm | 100 +++++-- code/modules/events/event_container.dm | 283 ++++++++++-------- code/modules/events/event_procs.dm | 140 ++++++--- code/modules/events/fake_virus.dm | 3 + code/modules/events/false_alarm.dm | 1 + code/modules/events/immovable_rod.dm | 6 + code/modules/events/infestation.dm | 3 + code/modules/events/ion_storm.dm | 6 +- code/modules/events/koi_mirgration.dm | 1 + code/modules/events/mass_hallucination.dm | 3 + code/modules/events/meaty_ores.dm | 7 + code/modules/events/meteors_event.dm | 27 +- code/modules/events/money_spam.dm | 2 + code/modules/events/prison_break.dm | 4 + code/modules/events/radiation_storm_event.dm | 6 + code/modules/events/revenant_spawn_event.dm | 15 + code/modules/events/rogue_drones.dm | 4 + code/modules/events/sentience.dm | 1 + .../events/shuttle_loan/shuttle_loan_event.dm | 3 + code/modules/events/slaughterevent.dm | 14 + code/modules/events/solarflare.dm | 1 + code/modules/events/spacevine.dm | 6 + code/modules/events/spider_infestation.dm | 2 + code/modules/events/spider_terror.dm | 17 ++ .../events/spontaneous_appendicitis.dm | 5 + code/modules/events/tear.dm | 3 + code/modules/events/traders.dm | 4 + code/modules/events/vent_clog.dm | 2 + code/modules/events/wallrot.dm | 8 + code/modules/events/wormholes.dm | 2 + .../mob/living/basic/friendly/mouse.dm | 2 + code/modules/mob/living/basic/hostile/carp.dm | 6 + .../mob/living/basic/hostile/hellhound.dm | 7 + .../living/basic/hostile/revenant/revenant.dm | 5 + .../mob/living/basic/hostile/skeleton_mob.dm | 7 + .../living/basic/nether_mobs/nether_mobs.dm | 6 + .../living/basic/retaliate/combat_drone.dm | 1 + .../mob/living/carbon/alien/alien_base.dm | 6 + .../carbon/alien/humanoid/caste/hunter.dm | 5 + .../carbon/alien/humanoid/caste/sentinel.dm | 5 + .../living/carbon/alien/humanoid/humanoid.dm | 1 + .../mob/living/carbon/alien/humanoid/queen.dm | 5 + .../mob/living/carbon/alien/larva/larva.dm | 5 + .../simple_animal/hostile/mimic_mobs.dm | 7 + .../hostile/terror_spiders/green.dm | 4 + .../hostile/terror_spiders/prince.dm | 4 + .../hostile/terror_spiders/princess.dm | 3 + .../hostile/terror_spiders/queen_terror.dm | 3 + .../hostile/terror_spiders/reproduction.dm | 2 + .../hostile/terror_spiders/terror_spiders.dm | 5 + .../hostile/terror_spiders/white.dm | 10 + code/modules/mob/mob.dm | 14 + code/modules/station_goals/shield.dm | 2 +- config/example/config.toml | 9 +- paradise.dme | 3 + 102 files changed, 1029 insertions(+), 290 deletions(-) create mode 100644 code/__DEFINES/blob_defines.dm create mode 100644 code/__DEFINES/event_defines.dm create mode 100644 code/datums/components/event_tracker.dm 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 += "" for(var/datum/event_meta/EM in selected_event_container.available_events) + if(!EM.skeleton) + continue html += "" - html += "" + html += "" html += "" html += "" html += "" html += "" html += "" - html += "" + html += "" html += "" html += "" html += "
Name Weight MinWeight MaxWeight OneShot Enabled CurrWeight Remove
[EM.name][EM.skeleton.name][EM.weight][EM.min_weight][EM.max_weight == INFINITY ? "No max" : EM.max_weight][EM.one_shot][EM.enabled][EM.get_weight(number_active_with_role())][EM.get_weight(total_resources)]Remove
" @@ -130,8 +136,8 @@ SUBSYSTEM_DEF(events) html += "" html += "" html += "" - html += "" - html += "" + html += "" + html += "" html += "" html += "" html += "" @@ -145,7 +151,7 @@ SUBSYSTEM_DEF(events) html += "
NameTypeWeightOneShot
[new_event.name ? new_event.name : "Enter Event"][new_event.event_type ? new_event.event_type : "Select Type"][new_event?.skeleton?.name ? new_event?.skeleton?.name : "Enter Event"][new_event?.skeleton?.type ? new_event?.skeleton?.type : "Select Type"][new_event.weight ? new_event.weight : 0][new_event.one_shot]
" html += "" - 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 += "
SeverityStarts AtStarts InAdjust StartPauseInterval Mod
" html += "" - 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 += "" - html += "" + html += "" html += "" html += "" html += "" @@ -190,15 +196,14 @@ SUBSYSTEM_DEF(events) html += "
SeverityNameEvent RotationClear
[GLOB.severity_to_string[severity]][EM ? EM.name : "Random"][EM ? EM.skeleton.name : "Random"]ViewClear
" html += "" 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 += "" - html += "" + html += "" + html += "" html += "" html += "" html += "" @@ -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 += "
SeverityNameEnds AtEnds InStop
[GLOB.severity_to_string[EM.severity]][EM.name] [ADMIN_VV(E, "VV")][GLOB.severity_to_string[E.severity]][E.name] [ADMIN_VV(E, "VV")][no_end ? "N/A" : station_time_timestamp("hh:mm:ss", ends_at)][no_end ? "N/A" : ends_in]Stop
" + html += "" + for(var/resource in total_resources) + html += "" + html += "" + html += "" + html += "" + html += "
Name Weight
[resource][total_resources[resource]]
" + html += "
" + var/list/net_resources = number_active_with_role() + html += "
" + html += "

Net Resources

" + html += "" + html += "" + for(var/resource in net_resources) + html += "" + html += "" + html += "" + html += "" + html += "
Name Weight
[resource][net_resources[resource]]
" + 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"