diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 87aee98471..602b37e493 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -155,6 +155,20 @@ var/list/contact_levels = list(1, 5) // Defines which Z-levels which, for example, a Code Red announcement may affect var/list/player_levels = list(1, 3, 4, 5, 6) // Defines all Z-levels a character can typically reach + var/const/minutes_to_ticks = 60 * 10 + // Event settings + var/expected_round_length = 60 * 3 * minutes_to_ticks // 3 hours + // If the first delay has a custom start time + // No custom time, no custom time, between 80 to 100 minutes respectively. + var/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000)) + // The lowest delay until next event + // 10, 30, 50 minutes respectively + var/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000) + // The upper delay until next event + // 15, 45, 70 minutes respectively + var/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000) + + /datum/configuration/New() var/list/L = typesof(/datum/game_mode) - /datum/game_mode for (var/T in L) @@ -524,6 +538,33 @@ if("player_levels") config.player_levels = text2numlist(value, ";") + if("expected_round_length") + config.expected_round_length = text2num(value) * minutes_to_ticks + + if("event_custom_start_mundane") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = values[1] * minutes_to_ticks, "upper" = values[2] * minutes_to_ticks) + + if("event_custom_start_moderate") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = values[1] * minutes_to_ticks, "upper" = values[2] * minutes_to_ticks) + + if("event_custom_start_major") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = values[1] * minutes_to_ticks, "upper" = values[2] * minutes_to_ticks) + + if("event_delay_lower") + var/values = text2numlist(value, ";") + config.event_delay_lower[EVENT_LEVEL_MUNDANE] = values[1] * minutes_to_ticks + config.event_delay_lower[EVENT_LEVEL_MODERATE] = values[2] * minutes_to_ticks + config.event_delay_lower[EVENT_LEVEL_MAJOR] = values[3] * minutes_to_ticks + + if("event_delay_upper") + var/values = text2numlist(value, ";") + config.event_delay_upper[EVENT_LEVEL_MUNDANE] = values[1] * minutes_to_ticks + config.event_delay_upper[EVENT_LEVEL_MODERATE] = values[2] * minutes_to_ticks + config.event_delay_upper[EVENT_LEVEL_MAJOR] = values[3] * minutes_to_ticks + else log_misc("Unknown setting in configuration: '[name]'") diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index 49684fdc91..3afc89f421 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -360,6 +360,7 @@ datum/controller/game_controller/proc/process_pipenets() Powernet.reset() datum/controller/game_controller/proc/process_nano() + last_thing_processed = /datum/nanoui var/i = 1 while(i<=nanomanager.processing_uis.len) var/datum/nanoui/ui = nanomanager.processing_uis[i] @@ -371,15 +372,7 @@ datum/controller/game_controller/proc/process_nano() datum/controller/game_controller/proc/process_events() last_thing_processed = /datum/event - var/i = 1 - while(i<=events.len) - var/datum/event/Event = events[i] - if(Event) - Event.process() - i++ - continue - events.Cut(i,i+1) - checkEvent() + event_manager.process() datum/controller/game_controller/proc/Recover() //Mostly a placeholder for now. var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n" diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 4ab6742b83..263a91769b 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -48,7 +48,7 @@ message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") return -/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data")) +/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event")) set category = "Debug" set name = "Debug Controller" set desc = "Debug the various periodic loop controllers for the game (be careful!)" @@ -102,6 +102,9 @@ feedback_add_details("admin_verb","DAutovoter") if("Gas Data") debug_variables(gas_data) - feedback_add_details("admin_verv","DGasdata") + feedback_add_details("admin_verb","DGasdata") + if("Event") + debug_variables(event_manager) + feedback_add_details("admin_verb", "DEvent") message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") return diff --git a/code/game/gamemodes/calamity/calamity.dm b/code/game/gamemodes/calamity/calamity.dm index be0d9abdb2..65eb970f12 100644 --- a/code/game/gamemodes/calamity/calamity.dm +++ b/code/game/gamemodes/calamity/calamity.dm @@ -82,6 +82,14 @@ return 1 /datum/game_mode/calamity/post_setup() + // Reduce the interval between moderate/major events + event_manager.delay_modifier[EVENT_LEVEL_MODERATE] = 0.5 + event_manager.delay_modifier[EVENT_LEVEL_MAJOR] = 0.75 + + // Add the cortical borer event + var/list/moderate_event_list = event_manager.available_events[EVENT_LEVEL_MODERATE] + var/event = new /datum/event_meta(/datum/event/borer_infestation, 400) + moderate_event_list.Add(event) if(chosen_atypes) for(var/atype in chosen_atypes) diff --git a/code/global.dm b/code/global.dm index cf8bc91b54..73c1507382 100644 --- a/code/global.dm +++ b/code/global.dm @@ -9,7 +9,6 @@ var/global/list/all_areas = list() var/global/list/machines = list() var/global/list/processing_objects = list() var/global/list/active_diseases = list() -var/global/list/events = list() var/global/list/med_hud_users = list() //list of all entities using a medical HUD. var/global/list/sec_hud_users = list() //list of all entities using a security HUD. //items that ask to be called every cycle @@ -179,6 +178,9 @@ var/forceblob = 0 // nanomanager, the manager for Nano UIs var/datum/nanomanager/nanomanager = new() +// event manager, the manager for events +var/datum/event_manager/event_manager = new() + //airlockWireColorToIndex takes a number representing the wire color, e.g. the orange wire is always 1, the dark red wire is always 2, etc. It returns the index for whatever that wire does. //airlockIndexToWireColor does the opposite thing - it takes the index for what the wire does, for example AIRLOCK_WIRE_IDSCAN is 1, AIRLOCK_WIRE_POWER1 is 2, etc. It returns the wire color number. //airlockWireColorToFlag takes the wire color number and returns the flag for it (1, 2, 4, 8, 16, etc) @@ -213,6 +215,10 @@ var/list/AAlarmWireColorToIndex #define shuttle_time_in_station 1800 // 3 minutes in the station #define shuttle_time_to_arrive 6000 // 10 minutes to arrive +#define EVENT_LEVEL_MUNDANE 1 +#define EVENT_LEVEL_MODERATE 2 +#define EVENT_LEVEL_MAJOR 3 + //away missions var/list/awaydestinations = list() //a list of landmarks that the warpgate can take you to diff --git a/code/modules/economy/Events.dm b/code/modules/economy/Events.dm index 5770e95026..9bc6a3d0bc 100644 --- a/code/modules/economy/Events.dm +++ b/code/modules/economy/Events.dm @@ -1,5 +1,6 @@ /datum/event/economic_event + name = "Economic event" endWhen = 50 //this will be set randomly, later announceWhen = 15 var/event_type = 0 diff --git a/code/modules/economy/Events_Mundane.dm b/code/modules/economy/Events_Mundane.dm index df2a92e01c..e4c4253d70 100644 --- a/code/modules/economy/Events_Mundane.dm +++ b/code/modules/economy/Events_Mundane.dm @@ -1,5 +1,6 @@ /datum/event/mundane_news + name = "Mundande news" endWhen = 10 /datum/event/mundane_news/announce() @@ -127,6 +128,7 @@ news_network.add_news("Nyx Daily", newMsg) /datum/event/trivial_news + name = "Trivial news" endWhen = 10 /datum/event/trivial_news/announce() diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm index 2feeecbb1a..94ca69c9a5 100644 --- a/code/modules/events/carp_migration.dm +++ b/code/modules/events/carp_migration.dm @@ -1,7 +1,9 @@ /datum/event/carp_migration + name = "Carp Migration" announceWhen = 50 oneShot = 1 - endWhen = 900 + endWhen = 900 + var/list/spawned_carp = list() /datum/event/carp_migration/setup() @@ -9,12 +11,28 @@ endWhen = rand(600,1200) /datum/event/carp_migration/announce() - command_announcement.Announce("Unknown biological entities have been detected near [station_name()], please stand-by.", "Lifesign Alert") + var/announcement = "" + if(severity == EVENT_LEVEL_MAJOR) + announcement = "Massive migration of unknown biological entities has been detected near [station_name()], please stand-by." + else + announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [station_name()], please stand-by." + command_announcement.Announce(announcement, "Lifesign Alert") /datum/event/carp_migration/start() + if(severity == EVENT_LEVEL_MAJOR) + for(var/i = 1 to rand(3,5)) + spawn_fish(landmarks_list.len) + else if(severity == EVENT_LEVEL_MODERATE) + spawn_fish(landmarks_list.len) + else + spawn_fish(rand(1, 5)) + +/datum/event/carp_migration/proc/spawn_fish(var/limit) for(var/obj/effect/landmark/C in landmarks_list) if(C.name == "carpspawn") spawned_carp.Add(new /mob/living/simple_animal/hostile/carp(C.loc)) + if(spawned_carp.len >= limit) + return /datum/event/carp_migration/end() for(var/mob/living/simple_animal/hostile/carp/C in spawned_carp) diff --git a/code/modules/events/event.dm b/code/modules/events/event.dm index 059317070e..e7d823e0c6 100644 --- a/code/modules/events/event.dm +++ b/code/modules/events/event.dm @@ -1,10 +1,56 @@ +/datum/event_meta + var/weight = 1 + var/min_weight = 1 + var/max_weight = 1 + var/severity = 0 + var/has_fired = 0 + var/list/role_weights = list() + var/datum/event/event_type + +/datum/event_meta/New(var/event_severity, var/datum/event/type, var/event_weight, var/list/job_weights, var/min_event_weight, var/max_event_weight) + severity = event_severity + event_type = type + weight = event_weight + if(job_weights) + role_weights = job_weights + +/datum/event_meta/proc/get_weight(var/list/active_with_role) + var/job_weight = 0 + for(var/role in role_weights) + job_weight = active_with_role[role] * role_weights[role] + + var/total_weight = weight + job_weight + + // Only min/max the weight if the values are set + if(min_weight && total_weight < min_weight) total_weight = min_weight + if(max_weight && total_weight > max_weight) total_weight = max_weight + + return total_weight + +/datum/event_meta/alien/get_weight(var/list/active_with_role) + if(aliens_allowed) + return ..(active_with_role) + return 0 + +/datum/event_meta/ninja/get_weight(var/list/active_with_role) + if(toggle_space_ninja) + return ..(active_with_role) + return 0 + /datum/event //NOTE: Times are measured in master controller ticks! + var/name = ""//Name of the event var/startWhen = 0 //When in the lifetime to call start(). var/announceWhen = 0 //When in the lifetime to call announce(). var/endWhen = 0 //When in the lifetime the event should end. var/oneShot = 0 //If true, then the event removes itself from the list of potential events on creation. + var/severity = 0 //Severity. Lower means less severe, higher means more severe. Does not have to be supported. Is set on New(). var/activeFor = 0 //How long the event has existed. You don't need to change this. + var/isRunning = 1 //If this event is currently running. You should not change this. + var/datum/event_meta/event_meta = null + +/datum/event/nothing + name = "Nothing" //Called first before processing. //Allows you to setup your event, such as randomly @@ -42,21 +88,21 @@ return - //Do not override this proc, instead use the appropiate procs. //This proc will handle the calls to the appropiate procs. /datum/event/proc/process() - if(activeFor > startWhen && activeFor < endWhen) tick() if(activeFor == startWhen) + isRunning = 1 start() if(activeFor == announceWhen) announce() if(activeFor == endWhen) + isRunning = 0 end() // Everything is done, let's clean up. @@ -65,19 +111,24 @@ activeFor++ - -//Garbage collects the event by removing it from the global events list, -//which should be the only place it's referenced. //Called when start(), announce() and end() has all been called. /datum/event/proc/kill() - events.Remove(src) + // If this event was forcefully killed run end() for individual cleanup + if(isRunning) + isRunning = 0 + end() + event_manager.active_events -= src + event_manager.event_complete(src) + +/datum/event/New(var/datum/event_meta/EM) + // event needs to be responsible for this, as stuff like APLUs currently make their own events for curious reasons + event_manager.active_events += src + + 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 -//Adds the event to the global events list, and removes it from the list -//of potential events. -/datum/event/New() setup() - events.Add(src) - /*if(oneShot) - potentialRandomEvents.Remove(type)*/ ..() diff --git a/code/modules/events/event_dynamic.dm b/code/modules/events/event_dynamic.dm index fc96c21821..e24fd00e8a 100644 --- a/code/modules/events/event_dynamic.dm +++ b/code/modules/events/event_dynamic.dm @@ -187,7 +187,7 @@ var/list/event_last_fired = list() // 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. -/proc/number_active_with_role(role) +/proc/number_active_with_role() var/list/active_with_role = list() active_with_role["Engineer"] = 0 active_with_role["Medical"] = 0 @@ -197,6 +197,7 @@ var/list/event_last_fired = list() active_with_role["Cyborg"] = 0 active_with_role["Janitor"] = 0 active_with_role["Gardener"] = 0 + active_with_role["Any"] = player_list.len for(var/mob/M in player_list) if(!M.mind || !M.client || M.client.inactivity > 10 * 10 * 60) // longer than 10 minutes AFK counts them as inactive diff --git a/code/modules/events/event_manager.dm b/code/modules/events/event_manager.dm index 12efae2fac..204f66f151 100644 --- a/code/modules/events/event_manager.dm +++ b/code/modules/events/event_manager.dm @@ -1,23 +1,130 @@ -var/list/allEvents = typesof(/datum/event) - /datum/event -var/list/potentialRandomEvents = typesof(/datum/event) - /datum/event -//var/list/potentialRandomEvents = typesof(/datum/event) - /datum/event - /datum/event/spider_infestation - /datum/event/alien_infestation +#define ASSIGNMENT_ANY "Any" +#define ASSIGNMENT_AI "AI" +#define ASSIGNMENT_CYBORG "Cyborg" +#define ASSIGNMENT_ENGINEER "Engineer" +#define ASSIGNMENT_GARDENER "Gardener" +#define ASSIGNMENT_JANITOR "Janitor" +#define ASSIGNMENT_MEDICAL "Medical" +#define ASSIGNMENT_SCIENTIST "Scientist" +#define ASSIGNMENT_SECURITY "Security" -var/eventTimeLower = 12000 //20 minutes -var/eventTimeUpper = 24000 //40 minutes -var/scheduledEvent = null +/datum/event_manager + var/list/available_events = list( + EVENT_LEVEL_MUNDANE = list( + // Severity level, even type, base weight, role weights, min weight, max weight. Last two only used if set and non-zero + new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/pda_spam, 0, list(ASSIGNMENT_ANY = 4), 25, 200), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/money_lotto, 0, list(ASSIGNMENT_ANY = 1), 5, 50), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/money_hacker, 0, list(ASSIGNMENT_ANY = 4), 25, 200), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/economic_event, 300), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/trivial_news, 400), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/mundane_news, 300), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/carp_migration, 20, list(ASSIGNMENT_SECURITY = 10)), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/brand_intelligence, 20, list(ASSIGNMENT_JANITOR = 25)), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/infestation, 100, list(ASSIGNMENT_JANITOR = 100)), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, /datum/event/wallrot, 0, list(ASSIGNMENT_ENGINEER = 30, ASSIGNMENT_GARDENER = 50)), + ), + EVENT_LEVEL_MODERATE = list( + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/nothing, 10), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/carp_migration, 20, list(ASSIGNMENT_SECURITY = 10)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/rogue_drone, 5, list(ASSIGNMENT_ENGINEER = 25, ASSIGNMENT_SECURITY = 25)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/spacevine, 10, list(ASSIGNMENT_ENGINEER = 5)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/meteor_shower, 0, list(ASSIGNMENT_ENGINEER = 10)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/communications_blackout, 50, list(ASSIGNMENT_AI = 25, ASSIGNMENT_SECURITY = 25)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/grid_check, 25, list(ASSIGNMENT_ENGINEER = 10)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/electrical_storm, 15, list(ASSIGNMENT_ENGINEER = 5, ASSIGNMENT_JANITOR = 15)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/radiation_storm, 0, list(ASSIGNMENT_MEDICAL = 10)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/spontaneous_appendicitis, 0, list(ASSIGNMENT_MEDICAL = 10)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/viral_infection, 0, list(ASSIGNMENT_MEDICAL = 10)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/spider_infestation, 5, list(ASSIGNMENT_SECURITY = 5)), + new /datum/event_meta/alien(EVENT_LEVEL_MODERATE, /datum/event/alien_infestation, 2.5,list(ASSIGNMENT_SECURITY = 1), max_event_weight = 5), + new /datum/event_meta/ninja(EVENT_LEVEL_MODERATE, /datum/event/space_ninja, 0, list(ASSIGNMENT_SECURITY = 1), max_event_weight = 5), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/ionstorm, 0, list(ASSIGNMENT_AI = 25, ASSIGNMENT_CYBORG = 25, ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_SCIENTIST = 5)), + ), + EVENT_LEVEL_MAJOR = list( + new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/nothing, 100), + new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/carp_migration, 0, list(ASSIGNMENT_SECURITY = 10)), + new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/viral_infection, 0, list(ASSIGNMENT_MEDICAL = 10)), + new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/blob, 0, list(ASSIGNMENT_ENGINEER = 10)), + new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/meteor_wave, 0, list(ASSIGNMENT_ENGINEER = 10)), + ) + ) + var/list/datum/event/active_events = list() + var/list/datum/event/finished_events = list() -//Currently unused. Needs an admin panel for messing with events. -/*/proc/addPotentialEvent(var/type) - potentialRandomEvents |= type + var/list/datum/event/allEvents -/proc/removePotentialEvent(var/type) - potentialRandomEvents -= type*/ + var/list/last_event_time = list() + var/list/next_event_time = list(EVENT_LEVEL_MUNDANE = 1, EVENT_LEVEL_MODERATE = 0, EVENT_LEVEL_MAJOR = 0) + var/list/delay_modifier = list(EVENT_LEVEL_MUNDANE = 1, EVENT_LEVEL_MODERATE = 1, EVENT_LEVEL_MAJOR = 1) +/datum/event_manager/New() + allEvents = typesof(/datum/event) - /datum/event -/proc/checkEvent() - if(!scheduledEvent) - //more players = more time between events, less players = less time between events +/datum/event_manager/proc/process() + for(var/datum/event/E in event_manager.active_events) + E.process() + + for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) + if(next_event_time[i] == 0) + // Our first time running, setup start times + set_event_delay(i) + else + // Is it time to fire a new event of this severity level? + if(world.timeofday > next_event_time[i]) + start_event(i) + +/datum/event_manager/proc/start_event(var/severity) + var/datum/event_meta/EM = acquire_event(available_events[severity]) + if(!EM) + // If no event was available now, check again in one minute (rather than next process tick) + next_event_time[severity] = next_event_time[severity] + (60 * 10) + return + + log_debug("Starting event of severity [severity].") + new EM.event_type(EM) + + // Remove the event meta data from the list of available events + available_events[severity] -= EM + +/datum/event_manager/proc/acquire_event(var/list/events) + if(events.len == 0) + return + var/active_with_role = number_active_with_role() + + var/list/possible_events = list() + for(var/datum/event_meta/EM in events) + var/event_weight = EM.get_weight(active_with_role) + if(event_weight) + possible_events[EM] = event_weight + + for(var/event_meta in last_event_time) if(possible_events[event_meta]) + var/time_passed = world.time - event_last_fired[event_meta] + var/weight_modifier = max(0, (config.expected_round_length - time_passed) / 300) + var/new_weight = max(possible_events[event_meta] - weight_modifier, 0) + + if(new_weight) + possible_events[event_meta] = new_weight + else + possible_events -= event_meta + + if(possible_events.len == 0) + return null + + var/picked_event = pickweight(possible_events) + last_event_time[picked_event] = world.time + + return picked_event + +/datum/event_manager/proc/set_event_delay(var/severity) + // If the next event time has not yet been set and we have a custom first time start + if(next_event_time[severity] == 0 && config.event_first_run[severity]) + var/lower = config.event_first_run[severity]["lower"] + var/upper = config.event_first_run[severity]["upper"] + var/event_delay = rand(lower, upper) + next_event_time[severity] = world.timeofday + event_delay + // Otherwise, follow the standard setup process + else var/playercount_modifier = 1 switch(player_list.len) if(0 to 10) @@ -30,36 +137,30 @@ var/scheduledEvent = null playercount_modifier = 0.9 if(36 to 100000) playercount_modifier = 0.8 + playercount_modifier = playercount_modifier * delay_modifier[severity] - if(ticker.mode && ticker.mode.name == "calamity") //Calamity mode lowers the time required between events drastically. - playercount_modifier = playercount_modifier * 0.5 + var/event_delay = rand(config.event_delay_lower[severity], config.event_delay_upper[severity]) * playercount_modifier + next_event_time[severity] = world.timeofday + event_delay - var/next_event_delay = rand(eventTimeLower, eventTimeUpper) * playercount_modifier - scheduledEvent = world.timeofday + next_event_delay - log_debug("Next event in [next_event_delay/600] minutes.") + log_debug("Next event of severity [severity] in [(next_event_time[severity] - world.timeofday)/600] minutes.") - else if(world.timeofday > scheduledEvent) - spawn_dynamic_event() - - scheduledEvent = null - checkEvent() - -//unused, see proc/dynamic_event() -/* -/proc/spawnEvent() - if(!config.allow_random_events) +/datum/event_manager/proc/event_complete(var/datum/event/E) + if(!E.event_meta) // datum/event is used here and there for random reasons + log_debug("Event of '[E.type]' with missing meta-data has completed.") return - var/Type = pick(potentialRandomEvents) - if(!Type) - return + finished_events += E + // Add the event back to the list of available events, unless it's a oneShot + if(!E.oneShot) + var/list/datum/event_meta/AE = available_events[E.event_meta.severity] + AE.Add(E.event_meta) - //The event will add itself to the MC's event list - //and start working via the constructor. - new Type -*/ + log_debug("Event '[E.name]' has completed.") -/client/proc/forceEvent(var/type in allEvents) +/proc/debugStartEvent(var/severity) + event_manager.start_event(severity) + +/client/proc/forceEvent(var/type in event_manager.allEvents) set name = "Trigger Event (Debug Only)" set category = "Debug" @@ -67,5 +168,15 @@ var/scheduledEvent = null return if(ispath(type)) - new type + new type(new /datum/event_meta(EVENT_LEVEL_MAJOR)) message_admins("[key_name_admin(usr)] has triggered an event. ([type])", 1) + +#undef ASSIGNMENT_ANY +#undef ASSIGNMENT_AI +#undef ASSIGNMENT_CYBORG +#undef ASSIGNMENT_ENGINEER +#undef ASSIGNMENT_GARDENER +#undef ASSIGNMENT_JANITOR +#undef ASSIGNMENT_MEDICAL +#undef ASSIGNMENT_SCIENTIST +#undef ASSIGNMENT_SECURITY \ No newline at end of file diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm index 6a6c0f7320..4d402e2c04 100644 --- a/code/modules/events/radiation_storm.dm +++ b/code/modules/events/radiation_storm.dm @@ -1,58 +1,57 @@ /datum/event/radiation_storm - announceWhen = 1 - oneShot = 1 + var/const/enterBelt = 60 + var/const/leaveBelt = 170 + var/const/revokeAccess = 230 + endWhen = revokeAccess + oneShot = 1 + + var/postStartTicks /datum/event/radiation_storm/announce() - // Don't do anything, we want to pack the announcement with the actual event + command_announcement.Announce("High levels of radiation detected near the station. Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') /datum/event/radiation_storm/start() - spawn() - command_announcement.Announce("High levels of radiation detected near the station. Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') - make_maint_all_access() - - - sleep(600) - + make_maint_all_access() +/datum/event/radiation_storm/tick() + if(activeFor == enterBelt) command_announcement.Announce("The station has entered the radiation belt. Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert") + radiate() - for(var/i = 0, i < 10, i++) - for(var/mob/living/carbon/human/H in living_mob_list) - var/turf/T = get_turf(H) - if(!T) - continue - if(T.z != 1) - continue - if(istype(T.loc, /area/maintenance) || istype(T.loc, /area/crew_quarters)) - continue - - if(istype(H,/mob/living/carbon/human)) - H.apply_effect((rand(15,35)),IRRADIATE,0) - if(prob(5)) - H.apply_effect((rand(40,70)),IRRADIATE,0) - if (prob(75)) - randmutb(H) // Applies bad mutation - domutcheck(H,null,MUTCHK_FORCED) - else - randmutg(H) // Applies good mutation - domutcheck(H,null,MUTCHK_FORCED) - - - for(var/mob/living/carbon/monkey/M in living_mob_list) - var/turf/T = get_turf(M) - if(!T) - continue - if(T.z != 1) - continue - M.apply_effect((rand(5,25)),IRRADIATE,0) - sleep(100) + if(activeFor > enterBelt && activeFor < leaveBelt) + postStartTicks++ + if(postStartTicks == 10) + postStartTicks = 0 + radiate() + else if(activeFor == leaveBelt) command_announcement.Announce("The station has passed the radiation belt. Please report to medbay if you experience any unusual symptoms. Maintenance will lose all access again shortly.", "Anomaly Alert") +/datum/event/radiation_storm/proc/radiate() + for(var/mob/living/carbon/C in living_mob_list) + var/turf/T = get_turf(C) + if(!T) + continue + if(!(T.z in config.station_levels)) + continue + if(istype(T.loc, /area/maintenance) || istype(T.loc, /area/crew_quarters)) + continue - sleep(600) // Want to give them time to get out of maintenance. + if(istype(C,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = C + H.apply_effect((rand(15,35)),IRRADIATE,0) + if(prob(5)) + H.apply_effect((rand(40,70)),IRRADIATE,0) + if (prob(75)) + randmutb(H) // Applies bad mutation + domutcheck(H,null,MUTCHK_FORCED) + else + randmutg(H) // Applies good mutation + domutcheck(H,null,MUTCHK_FORCED) + else if(istype(C,/mob/living/carbon/monkey)) + C.apply_effect((rand(5,25)),IRRADIATE,0) - - revoke_maint_all_access() +/datum/event/radiation_storm/end() + revoke_maint_all_access() diff --git a/code/modules/events/viral_infection.dm b/code/modules/events/viral_infection.dm index 1be584b8e3..6e40d38d33 100644 --- a/code/modules/events/viral_infection.dm +++ b/code/modules/events/viral_infection.dm @@ -1,11 +1,6 @@ - -datum/event/viral_infection - var/severity = 1 - datum/event/viral_infection/setup() announceWhen = rand(0, 3000) endWhen = announceWhen + 1 - severity = rand(1, 3) datum/event/viral_infection/announce() command_announcement.Announce("Confirmed outbreak of level five biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak5.ogg') @@ -18,7 +13,9 @@ datum/event/viral_infection/start() if(!candidates.len) return candidates = shuffle(candidates)//Incorporating Donkie's list shuffle - while(severity > 0 && candidates.len) + severity = severity == 1 ? 1 : severity - 1 + var/actual_severity = severity * rand(1, 3) + while(actual_severity > 0 && candidates.len) infect_mob_random_lesser(candidates[1]) candidates.Remove(candidates[1]) - severity-- + actual_severity-- diff --git a/code/modules/events/wallrot.dm b/code/modules/events/wallrot.dm index 3911b4f1fa..b36f7f9376 100644 --- a/code/modules/events/wallrot.dm +++ b/code/modules/events/wallrot.dm @@ -1,20 +1,13 @@ -/turf/simulated/wall - - -datum/event/wallrot - var/severity = 1 - datum/event/wallrot/setup() announceWhen = rand(0, 300) endWhen = announceWhen + 1 - severity = rand(5, 10) datum/event/wallrot/announce() command_announcement.Announce("Harmful fungi detected on station. Station structures may be contaminated.", "Biohazard Alert") datum/event/wallrot/start() spawn() - var/turf/center = null + var/turf/simulated/wall/center = null // 100 attempts for(var/i=0, i<100, i++) @@ -24,14 +17,15 @@ datum/event/wallrot/start() if(center) // Make sure at least one piece of wall rots! - center:rot() + center.rot() // Have a chance to rot lots of other walls. var/rotcount = 0 + var/actual_severity = severity * rand(5, 10) for(var/turf/simulated/wall/W in range(5, center)) if(prob(50)) - W:rot() + W.rot() rotcount++ // Only rot up to severity walls - if(rotcount >= severity) + if(rotcount >= actual_severity) break \ No newline at end of file diff --git a/config/example/config.txt b/config/example/config.txt index cfc417f1c8..4d1c1a0c56 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -272,3 +272,20 @@ CONTACT_LEVELS 1;5 ## Defines all Z-levels a character can typically reach PLAYER_LEVELS 1;3;4;5;6 + +## Expected round length in minutes +EXPECTED_ROUND_LENGTH 180 + +## The lower delay between events in minutes. +## Affect mundane, moderate, and major events respectively +EVENT_DELAY_LOWER 10;30;50 + +## The upper delay between events in minutes. +## Affect mundane, moderate, and major events respectively +EVENT_DELAY_UPPER 15;45;70 + +## The delay until the first time an event of the given severity runs in minutes. +## Unset setting use the EVENT_DELAY_LOWER and EVENT_DELAY_UPPER values instead. +# EVENT_CUSTOM_START_MINOR 10;15 +# EVENT_CUSTOM_START_MODERATE 30;45 +EVENT_CUSTOM_START_MAJOR 80;100 \ No newline at end of file