From 23fb45ba6e1b93fb0e7283a4783313a79d60a148 Mon Sep 17 00:00:00 2001 From: c0 Date: Tue, 29 Mar 2016 08:49:17 +0300 Subject: [PATCH 1/2] Adds minimal players requirements for random event --- code/__HELPERS/game.dm | 10 +++++-- code/controllers/subsystem/events.dm | 26 +++++-------------- code/modules/events/abductor.dm | 1 + code/modules/events/alien_infestation.dm | 2 ++ code/modules/events/anomaly.dm | 2 ++ code/modules/events/anomaly_flux.dm | 2 ++ code/modules/events/anomaly_vortex.dm | 2 ++ code/modules/events/blob.dm | 1 + code/modules/events/brand_intelligence.dm | 2 ++ code/modules/events/carp_migration.dm | 1 + code/modules/events/disease_outbreak.dm | 1 + code/modules/events/electrical_storm.dm | 1 + code/modules/events/event.dm | 24 +++++++++++++++-- code/modules/events/false_alarm.dm | 9 ++++--- code/modules/events/immovable_rod.dm | 1 + code/modules/events/ion_storm.dm | 1 + code/modules/events/mass_hallucination.dm | 1 + code/modules/events/meteor_wave.dm | 1 + code/modules/events/portal_storm.dm | 1 + code/modules/events/prison_break.dm | 1 + code/modules/events/spacevine.dm | 1 + code/modules/events/spider_infestation.dm | 1 + .../events/spontaneous_appendicitis.dm | 1 + code/modules/events/wormholes.dm | 1 + 24 files changed, 67 insertions(+), 27 deletions(-) diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index ff88b0a70e0..371ba01bb99 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -332,13 +332,19 @@ for(var/client/C in show_to) C.images -= I -/proc/get_active_player_count() +/proc/get_active_player_count(var/alive_check = 0, var/afk_check = 0, var/human_check = 0) // Get active players who are playing in the round var/active_players = 0 for(var/i = 1; i <= player_list.len; i++) var/mob/M = player_list[i] if(M && M.client) - if(istype(M, /mob/new_player)) // exclude people in the lobby + if(alive_check && M.stat) + continue + else if(afk_check && M.client.is_afk()) + continue + else if(human_check && !istype(M, /mob/living/carbon/human)) + continue + else if(istype(M, /mob/new_player)) // exclude people in the lobby continue else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers) var/mob/dead/observer/O = M diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm index 198c6d18300..c4fcf0ab933 100644 --- a/code/controllers/subsystem/events.dm +++ b/code/controllers/subsystem/events.dm @@ -70,19 +70,14 @@ var/datum/subsystem/events/SSevent // if(E) E.runEvent() return + var/gamemode = ticker.mode.config_tag + var/players_amt = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1) + // Only alive, non-AFK human players count towards this. + var/sum_of_weights = 0 for(var/datum/round_event_control/E in control) - if(E.occurrences >= E.max_occurrences) + if(!E.canSpawnEvent(players_amt, gamemode)) continue - if(E.earliest_start >= world.time) - continue - if(E.gamemode_blacklist.len && (ticker.mode.config_tag in E.gamemode_blacklist)) - continue - if(E.gamemode_whitelist.len && !(ticker.mode.config_tag in E.gamemode_whitelist)) - continue - if(E.holidayID) - if(!holidays || !holidays[E.holidayID]) - continue if(E.weight < 0) //for round-start events etc. if(E.runEvent() == PROCESS_KILL) E.max_occurrences = 0 @@ -96,17 +91,8 @@ var/datum/subsystem/events/SSevent sum_of_weights = rand(0,sum_of_weights) //reusing this variable. It now represents the 'weight' we want to select for(var/datum/round_event_control/E in control) - if(E.occurrences >= E.max_occurrences) + if(!E.canSpawnEvent(players_amt, gamemode)) continue - if(E.earliest_start >= world.time) - continue - if(E.gamemode_blacklist.len && (ticker.mode.config_tag in E.gamemode_blacklist)) - continue - if(E.gamemode_whitelist.len && !(ticker.mode.config_tag in E.gamemode_whitelist)) - continue - if(E.holidayID) - if(!holidays || !holidays[E.holidayID]) - continue sum_of_weights -= E.weight if(sum_of_weights <= 0) //we've hit our goal diff --git a/code/modules/events/abductor.dm b/code/modules/events/abductor.dm index 3eaf7e3c0c7..65ec0a7aae2 100644 --- a/code/modules/events/abductor.dm +++ b/code/modules/events/abductor.dm @@ -4,6 +4,7 @@ weight = 5 max_occurrences = 1 + min_players = 5 earliest_start = 18000 // 30 min gamemode_blacklist = list("nuclear","wizard","revolution","abduction") diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm index b57118957f1..0082e3d62fa 100644 --- a/code/modules/events/alien_infestation.dm +++ b/code/modules/events/alien_infestation.dm @@ -2,6 +2,8 @@ name = "Alien Infestation" typepath = /datum/round_event/alien_infestation weight = 5 + + min_players = 10 max_occurrences = 1 /datum/round_event/alien_infestation diff --git a/code/modules/events/anomaly.dm b/code/modules/events/anomaly.dm index 547618e63de..0b98ee4ec7f 100644 --- a/code/modules/events/anomaly.dm +++ b/code/modules/events/anomaly.dm @@ -1,6 +1,8 @@ /datum/round_event_control/anomaly name = "Anomaly: Energetic Flux" typepath = /datum/round_event/anomaly + + min_players = 1 max_occurrences = 0 //This one probably shouldn't occur! It'd work, but it wouldn't be very fun. weight = 15 diff --git a/code/modules/events/anomaly_flux.dm b/code/modules/events/anomaly_flux.dm index 7d8da5021df..10fdb119ff2 100644 --- a/code/modules/events/anomaly_flux.dm +++ b/code/modules/events/anomaly_flux.dm @@ -1,6 +1,8 @@ /datum/round_event_control/anomaly/anomaly_flux name = "Anomaly: Hyper-Energetic Flux" typepath = /datum/round_event/anomaly/anomaly_flux + + min_players = 10 max_occurrences = 5 weight = 20 diff --git a/code/modules/events/anomaly_vortex.dm b/code/modules/events/anomaly_vortex.dm index 72684b1a8bd..9d7b40cf6e1 100644 --- a/code/modules/events/anomaly_vortex.dm +++ b/code/modules/events/anomaly_vortex.dm @@ -1,6 +1,8 @@ /datum/round_event_control/anomaly/anomaly_vortex name = "Anomaly: Vortex" typepath = /datum/round_event/anomaly/anomaly_vortex + + min_players = 20 max_occurrences = 2 weight = 5 diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm index 343c4bd5758..ab361a56b39 100644 --- a/code/modules/events/blob.dm +++ b/code/modules/events/blob.dm @@ -4,6 +4,7 @@ weight = 5 max_occurrences = 1 + min_players = 20 earliest_start = 18000 //30 minutes gamemode_blacklist = list("blob") //Just in case a blob survives that long diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm index 7edf5a9f482..5a4fbcd9c5d 100644 --- a/code/modules/events/brand_intelligence.dm +++ b/code/modules/events/brand_intelligence.dm @@ -2,6 +2,8 @@ name = "Brand Intelligence" typepath = /datum/round_event/brand_intelligence weight = 5 + + min_players = 15 max_occurrences = 1 /datum/round_event/brand_intelligence diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm index 0b65480c830..a7af3fe9a31 100644 --- a/code/modules/events/carp_migration.dm +++ b/code/modules/events/carp_migration.dm @@ -2,6 +2,7 @@ name = "Carp Migration" typepath = /datum/round_event/carp_migration weight = 15 + min_players = 2 earliest_start = 6000 max_occurrences = 6 diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm index bf704135455..d19c54d5080 100644 --- a/code/modules/events/disease_outbreak.dm +++ b/code/modules/events/disease_outbreak.dm @@ -2,6 +2,7 @@ name = "Disease Outbreak" typepath = /datum/round_event/disease_outbreak max_occurrences = 1 + min_players = 10 weight = 5 /datum/round_event/disease_outbreak diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm index 302586925c5..1568e409207 100644 --- a/code/modules/events/electrical_storm.dm +++ b/code/modules/events/electrical_storm.dm @@ -2,6 +2,7 @@ name = "Electrical Storm" typepath = /datum/round_event/electrical_storm earliest_start = 6000 + min_players = 5 weight = 40 alertadmins = 0 diff --git a/code/modules/events/event.dm b/code/modules/events/event.dm index 722f6556868..26f2a3f568f 100644 --- a/code/modules/events/event.dm +++ b/code/modules/events/event.dm @@ -1,6 +1,6 @@ //this datum is used by the events controller to dictate how it selects events /datum/round_event_control - var/name //The name human-readable name of the event + var/name //The human-readable name of the event var/typepath //The typepath of the event datum /datum/round_event var/weight = 10 //The weight this event has in the random-selection process. @@ -8,9 +8,10 @@ //10 is the default weight. 20 is twice more likely; 5 is half as likely as this default. var/earliest_start = 12000 //The earliest world.time that an event can start (round-duration in deciseconds) default: 20 mins + var/min_players = 0 //The minimum amount of alive, non-AFK human players on server required to start the event. var/occurrences = 0 //How many times this event has occured - var/max_occurrences = 20 //The maximum number of times this event can occur (naturally), it can still be forced. + var/max_occurrences = 20 //The maximum number of times this event can occur (naturally), it can still be forced. //By setting this to 0 you can effectively disable an event. var/holidayID = "" //string which should be in the SSevents.holidays list if you wish this event to be holiday-specific @@ -26,10 +27,28 @@ /datum/round_event_control/wizard wizardevent = 1 +// Checks if the event can be spawned. Used by event controller and "false alarm" event. +// Admin-created events override this. +/datum/round_event_control/proc/canSpawnEvent(var/players_amt, var/gamemode) + if(occurrences >= max_occurrences) + return FALSE + if(earliest_start >= world.time) + return FALSE + if(players_amt < min_players) + return FALSE + if(gamemode_blacklist.len && (gamemode in gamemode_blacklist)) + return FALSE + if(gamemode_whitelist.len && !(gamemode in gamemode_whitelist)) + return FALSE + if(holidayID && (!SSevent.holidays || !SSevent.holidays[holidayID])) + return FALSE + return TRUE + /datum/round_event_control/proc/runEvent() if(!ispath(typepath,/datum/round_event)) return PROCESS_KILL var/datum/round_event/E = new typepath() + E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1) E.control = src feedback_add_details("event_ran","[E]") occurrences++ @@ -47,6 +66,7 @@ var/endWhen = 0 //When in the lifetime the event should end. var/activeFor = 0 //How long the event has existed. You don't need to change this. + var/current_players = 0 //Amount of of alive, non-AFK human players on server at the time of event start //Called first before processing. //Allows you to setup your event, such as randomly diff --git a/code/modules/events/false_alarm.dm b/code/modules/events/false_alarm.dm index 1379d44d3e9..e34c532dad0 100644 --- a/code/modules/events/false_alarm.dm +++ b/code/modules/events/false_alarm.dm @@ -10,11 +10,14 @@ /datum/round_event/falsealarm/announce() var/list/events_list = list() + + var/players_amt = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1) + var/gamemode = ticker.mode.config_tag + for(var/datum/round_event_control/E in SSevent.control) - if(E.holidayID || E.wizardevent) - continue - if(E.earliest_start >= world.time) + if(!E.canSpawnEvent(players_amt, gamemode)) continue + var/datum/round_event/event = E.typepath if(initial(event.announceWhen) <= 0) continue diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index 4ff5bd2519b..79199bc33a0 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -10,6 +10,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 /datum/round_event_control/immovable_rod name = "Immovable Rod" typepath = /datum/round_event/immovable_rod + min_players = 15 max_occurrences = 5 /datum/round_event/immovable_rod diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index 177ee3dece6..b2e140ac548 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -5,6 +5,7 @@ name = "Ion Storm" typepath = /datum/round_event/ion_storm weight = 15 + min_players = 2 /datum/round_event/ion_storm var/botEmagChance = 10 diff --git a/code/modules/events/mass_hallucination.dm b/code/modules/events/mass_hallucination.dm index 17888250c20..1705598c36e 100644 --- a/code/modules/events/mass_hallucination.dm +++ b/code/modules/events/mass_hallucination.dm @@ -3,6 +3,7 @@ typepath = /datum/round_event/mass_hallucination weight = 7 max_occurrences = 2 + min_players = 1 /datum/round_event/mass_hallucination/start() for(var/mob/living/carbon/C in living_mob_list) diff --git a/code/modules/events/meteor_wave.dm b/code/modules/events/meteor_wave.dm index 9a48f2ccca6..b670df5bdf2 100644 --- a/code/modules/events/meteor_wave.dm +++ b/code/modules/events/meteor_wave.dm @@ -2,6 +2,7 @@ name = "Meteor Wave" typepath = /datum/round_event/meteor_wave weight = 5 + min_players = 5 max_occurrences = 3 /datum/round_event/meteor_wave diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm index e9152f15168..fb4f68e7fba 100644 --- a/code/modules/events/portal_storm.dm +++ b/code/modules/events/portal_storm.dm @@ -2,6 +2,7 @@ name = "Portal Storm: Syndicate Shocktroops" typepath = /datum/round_event/portal_storm/syndicate_shocktroop weight = 2 + min_players = 15 earliest_start = 18000 /datum/round_event/portal_storm/syndicate_shocktroop diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm index f81e7f47cc4..f4d1ae65437 100644 --- a/code/modules/events/prison_break.dm +++ b/code/modules/events/prison_break.dm @@ -2,6 +2,7 @@ name = "Prison Break" typepath = /datum/round_event/prison_break max_occurrences = 2 + min_players = 5 /datum/round_event/prison_break announceWhen = 50 diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 67ae062313c..a75c6503b44 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -3,6 +3,7 @@ typepath = /datum/round_event/spacevine weight = 15 max_occurrences = 3 + min_players = 10 /datum/round_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 32641e5125d..a25f60366ee 100644 --- a/code/modules/events/spider_infestation.dm +++ b/code/modules/events/spider_infestation.dm @@ -3,6 +3,7 @@ typepath = /datum/round_event/spider_infestation weight = 5 max_occurrences = 1 + min_players = 15 /datum/round_event/spider_infestation announceWhen = 400 diff --git a/code/modules/events/spontaneous_appendicitis.dm b/code/modules/events/spontaneous_appendicitis.dm index 11e8d74923e..491da24ba6c 100644 --- a/code/modules/events/spontaneous_appendicitis.dm +++ b/code/modules/events/spontaneous_appendicitis.dm @@ -4,6 +4,7 @@ weight = 20 max_occurrences = 4 earliest_start = 6000 + min_players = 5 // To make your chance of getting help a bit higher. /datum/round_event/spontaneous_appendicitis/start() for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm index 7ae77f055d6..213f654c4e4 100644 --- a/code/modules/events/wormholes.dm +++ b/code/modules/events/wormholes.dm @@ -3,6 +3,7 @@ typepath = /datum/round_event/wormholes max_occurrences = 3 weight = 2 + min_players = 2 /datum/round_event/wormholes From 9f4e6c6c426506395fedc1d24d91f5ffc07db3c6 Mon Sep 17 00:00:00 2001 From: c0 Date: Wed, 30 Mar 2016 02:16:04 +0300 Subject: [PATCH 2/2] Adds two new events-related config options --- code/controllers/configuration.dm | 14 +- code/modules/events/{event.dm => _event.dm} | 298 ++++++++++---------- config/game_options.txt | 9 +- tgstation.dme | 2 +- 4 files changed, 174 insertions(+), 149 deletions(-) rename code/modules/events/{event.dm => _event.dm} (95%) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 55679b7138c..03a72667267 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -90,7 +90,6 @@ var/list/probabilities = list() // relative probability of each mode var/humans_need_surnames = 0 - var/allow_random_events = 0 // enables random events mid-round when set to 1 var/allow_ai = 0 // allow ai job var/forbid_secborg = 0 // disallow secborg module to be chosen. var/panic_bunker = 0 // prevents new people it hasn't seen before from connecting @@ -187,6 +186,13 @@ var/maprotation = 1 var/maprotatechancedelta = 0.75 + // Enables random events mid-round when set to 1 + var/allow_random_events = 0 + + // Multipliers for random events minimal starting time and minimal players amounts + var/events_min_time_mul = 1 + var/events_min_players_mul = 1 + // The object used for the clickable stat() button. var/obj/effect/statclick/statclick @@ -510,6 +516,12 @@ config.allow_latejoin_antagonists = 1 if("allow_random_events") config.allow_random_events = 1 + + if("events_min_time_mul") + config.events_min_time_mul = text2num(value) + if("events_min_players_mul") + config.events_min_players_mul = text2num(value) + if("minimal_access_threshold") config.minimal_access_threshold = text2num(value) if("jobs_have_minimal_access") diff --git a/code/modules/events/event.dm b/code/modules/events/_event.dm similarity index 95% rename from code/modules/events/event.dm rename to code/modules/events/_event.dm index 26f2a3f568f..2a10c13bfbf 100644 --- a/code/modules/events/event.dm +++ b/code/modules/events/_event.dm @@ -1,147 +1,153 @@ -//this datum is used by the events controller to dictate how it selects events -/datum/round_event_control - var/name //The human-readable name of the event - var/typepath //The typepath of the event datum /datum/round_event - - var/weight = 10 //The weight this event has in the random-selection process. - //Higher weights are more likely to be picked. - //10 is the default weight. 20 is twice more likely; 5 is half as likely as this default. - - var/earliest_start = 12000 //The earliest world.time that an event can start (round-duration in deciseconds) default: 20 mins - var/min_players = 0 //The minimum amount of alive, non-AFK human players on server required to start the event. - - var/occurrences = 0 //How many times this event has occured - var/max_occurrences = 20 //The maximum number of times this event can occur (naturally), it can still be forced. - //By setting this to 0 you can effectively disable an event. - - var/holidayID = "" //string which should be in the SSevents.holidays list if you wish this event to be holiday-specific - //anything with a (non-null) holidayID which does not match holiday, cannot run. - var/wizardevent = 0 - - var/alertadmins = 1 //should we let the admins know this event is firing - //should be disabled on events that fire a lot - - var/list/gamemode_blacklist = list() // Event won't happen in these gamemodes - var/list/gamemode_whitelist = list() // Event will happen ONLY in these gamemodes if not empty - -/datum/round_event_control/wizard - wizardevent = 1 - -// Checks if the event can be spawned. Used by event controller and "false alarm" event. -// Admin-created events override this. -/datum/round_event_control/proc/canSpawnEvent(var/players_amt, var/gamemode) - if(occurrences >= max_occurrences) - return FALSE - if(earliest_start >= world.time) - return FALSE - if(players_amt < min_players) - return FALSE - if(gamemode_blacklist.len && (gamemode in gamemode_blacklist)) - return FALSE - if(gamemode_whitelist.len && !(gamemode in gamemode_whitelist)) - return FALSE - if(holidayID && (!SSevent.holidays || !SSevent.holidays[holidayID])) - return FALSE - return TRUE - -/datum/round_event_control/proc/runEvent() - if(!ispath(typepath,/datum/round_event)) - return PROCESS_KILL - var/datum/round_event/E = new typepath() - E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1) - E.control = src - feedback_add_details("event_ran","[E]") - occurrences++ - - testing("[time2text(world.time, "hh:mm:ss")] [E.type]") - - return E - -/datum/round_event //NOTE: Times are measured in master controller ticks! - var/processing = 1 - var/datum/round_event_control/control - - var/startWhen = 0 //When in the lifetime to call start(). - var/announceWhen = 0 //When in the lifetime to call announce(). Set an event's announceWhen to >0 if there is an announcement. - var/endWhen = 0 //When in the lifetime the event should end. - - var/activeFor = 0 //How long the event has existed. You don't need to change this. - var/current_players = 0 //Amount of of alive, non-AFK human players on server at the time of event start - -//Called first before processing. -//Allows you to setup your event, such as randomly -//setting the startWhen and or announceWhen variables. -//Only called once. -//EDIT: if there's anything you want to override within the new() call, it will not be overridden by the time this proc is called. -//It will only have been overridden by the time we get to announce() start() tick() or end() (anything but setup basically). -//This is really only for setting defaults which can be overridden later when New() finishes. -/datum/round_event/proc/setup() - return - -//Called when the tick is equal to the startWhen variable. -//Allows you to start before announcing or vice versa. -//Only called once. -/datum/round_event/proc/start() - return - -//Called when the tick is equal to the announceWhen variable. -//Allows you to announce before starting or vice versa. -//Only called once. -/datum/round_event/proc/announce() - return - -//Called on or after the tick counter is equal to startWhen. -//You can include code related to your event or add your own -//time stamped events. -//Called more than once. -/datum/round_event/proc/tick() - return - -//Called on or after the tick is equal or more than endWhen -//You can include code related to the event ending. -//Do not place spawn() in here, instead use tick() to check for -//the activeFor variable. -//For example: if(activeFor == myOwnVariable + 30) doStuff() -//Only called once. -/datum/round_event/proc/end() - return - - - -//Do not override this proc, instead use the appropiate procs. -//This proc will handle the calls to the appropiate procs. -/datum/round_event/process() - if(!processing) - return - - if(activeFor == startWhen) - start() - - if(activeFor == announceWhen) - announce() - - if(startWhen < activeFor && activeFor < endWhen) - tick() - - if(activeFor == endWhen) - end() - - // Everything is done, let's clean up. - if(activeFor >= endWhen && activeFor >= announceWhen && activeFor >= startWhen) - kill() - - 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/round_event/proc/kill() - SSevent.running -= src - - -//Sets up the event then adds the event to the the list of running events -/datum/round_event/New() - setup() - SSevent.running += src +//this datum is used by the events controller to dictate how it selects events +/datum/round_event_control + var/name //The human-readable name of the event + var/typepath //The typepath of the event datum /datum/round_event + + var/weight = 10 //The weight this event has in the random-selection process. + //Higher weights are more likely to be picked. + //10 is the default weight. 20 is twice more likely; 5 is half as likely as this default. + + var/earliest_start = 12000 //The earliest world.time that an event can start (round-duration in deciseconds) default: 20 mins + var/min_players = 0 //The minimum amount of alive, non-AFK human players on server required to start the event. + + var/occurrences = 0 //How many times this event has occured + var/max_occurrences = 20 //The maximum number of times this event can occur (naturally), it can still be forced. + //By setting this to 0 you can effectively disable an event. + + var/holidayID = "" //string which should be in the SSevents.holidays list if you wish this event to be holiday-specific + //anything with a (non-null) holidayID which does not match holiday, cannot run. + var/wizardevent = 0 + + var/alertadmins = 1 //should we let the admins know this event is firing + //should be disabled on events that fire a lot + + var/list/gamemode_blacklist = list() // Event won't happen in these gamemodes + var/list/gamemode_whitelist = list() // Event will happen ONLY in these gamemodes if not empty + +/datum/round_event_control/New() + ..() + if(config && !wizardevent) // Magic is unaffected by configs + earliest_start = Ceiling(earliest_start * config.events_min_time_mul) + min_players = Ceiling(min_players * config.events_min_players_mul) + +/datum/round_event_control/wizard + wizardevent = 1 + +// Checks if the event can be spawned. Used by event controller and "false alarm" event. +// Admin-created events override this. +/datum/round_event_control/proc/canSpawnEvent(var/players_amt, var/gamemode) + if(occurrences >= max_occurrences) + return FALSE + if(earliest_start >= world.time) + return FALSE + if(players_amt < min_players) + return FALSE + if(gamemode_blacklist.len && (gamemode in gamemode_blacklist)) + return FALSE + if(gamemode_whitelist.len && !(gamemode in gamemode_whitelist)) + return FALSE + if(holidayID && (!SSevent.holidays || !SSevent.holidays[holidayID])) + return FALSE + return TRUE + +/datum/round_event_control/proc/runEvent() + if(!ispath(typepath,/datum/round_event)) + return PROCESS_KILL + var/datum/round_event/E = new typepath() + E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1) + E.control = src + feedback_add_details("event_ran","[E]") + occurrences++ + + testing("[time2text(world.time, "hh:mm:ss")] [E.type]") + + return E + +/datum/round_event //NOTE: Times are measured in master controller ticks! + var/processing = 1 + var/datum/round_event_control/control + + var/startWhen = 0 //When in the lifetime to call start(). + var/announceWhen = 0 //When in the lifetime to call announce(). Set an event's announceWhen to >0 if there is an announcement. + var/endWhen = 0 //When in the lifetime the event should end. + + var/activeFor = 0 //How long the event has existed. You don't need to change this. + var/current_players = 0 //Amount of of alive, non-AFK human players on server at the time of event start + +//Called first before processing. +//Allows you to setup your event, such as randomly +//setting the startWhen and or announceWhen variables. +//Only called once. +//EDIT: if there's anything you want to override within the new() call, it will not be overridden by the time this proc is called. +//It will only have been overridden by the time we get to announce() start() tick() or end() (anything but setup basically). +//This is really only for setting defaults which can be overridden later when New() finishes. +/datum/round_event/proc/setup() + return + +//Called when the tick is equal to the startWhen variable. +//Allows you to start before announcing or vice versa. +//Only called once. +/datum/round_event/proc/start() + return + +//Called when the tick is equal to the announceWhen variable. +//Allows you to announce before starting or vice versa. +//Only called once. +/datum/round_event/proc/announce() + return + +//Called on or after the tick counter is equal to startWhen. +//You can include code related to your event or add your own +//time stamped events. +//Called more than once. +/datum/round_event/proc/tick() + return + +//Called on or after the tick is equal or more than endWhen +//You can include code related to the event ending. +//Do not place spawn() in here, instead use tick() to check for +//the activeFor variable. +//For example: if(activeFor == myOwnVariable + 30) doStuff() +//Only called once. +/datum/round_event/proc/end() + return + + + +//Do not override this proc, instead use the appropiate procs. +//This proc will handle the calls to the appropiate procs. +/datum/round_event/process() + if(!processing) + return + + if(activeFor == startWhen) + start() + + if(activeFor == announceWhen) + announce() + + if(startWhen < activeFor && activeFor < endWhen) + tick() + + if(activeFor == endWhen) + end() + + // Everything is done, let's clean up. + if(activeFor >= endWhen && activeFor >= announceWhen && activeFor >= startWhen) + kill() + + 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/round_event/proc/kill() + SSevent.running -= src + + +//Sets up the event then adds the event to the the list of running events +/datum/round_event/New() + setup() + SSevent.running += src return ..() \ No newline at end of file diff --git a/config/game_options.txt b/config/game_options.txt index 956f8169c03..767aff019fc 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -179,10 +179,17 @@ ALLOW_LATEJOIN_ANTAGONISTS #SHOW_GAME_TYPE_ODDS ### RANDOM EVENTS ### - ## Comment this to disable random events during the round. ALLOW_RANDOM_EVENTS +## Multiplier for earliest start time of dangerous events. +## Set to 0 to make dangerous events avaliable from round start. +EVENTS_MIN_TIME_MUL 1 + +## Multiplier for minimal player count (players = alive non-AFK humans) for dangerous events to start. +## Set to 0 to make dangerous events avaliable for all populations. +EVENTS_MIN_PLAYERS_MUL 1 + ### AI ### diff --git a/tgstation.dme b/tgstation.dme index 2a442e42c7b..aa685097200 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -974,6 +974,7 @@ #include "code\modules\detectivework\footprints_and_rag.dm" #include "code\modules\detectivework\scanner.dm" #include "code\modules\emoji\emoji_parse.dm" +#include "code\modules\events\_event.dm" #include "code\modules\events\abductor.dm" #include "code\modules\events\alien_infestation.dm" #include "code\modules\events\anomaly.dm" @@ -990,7 +991,6 @@ #include "code\modules\events\disease_outbreak.dm" #include "code\modules\events\dust.dm" #include "code\modules\events\electrical_storm.dm" -#include "code\modules\events\event.dm" #include "code\modules\events\false_alarm.dm" #include "code\modules\events\immovable_rod.dm" #include "code\modules\events\ion_storm.dm"