Files
Bubberstation/code/controllers/subsystem/events.dm
T
Rhials 40873e0a98 Non-random events now provide a cause in their deadchat broadcast (#74904)
## About The Pull Request

Most calls of runEvent() now provide a cause that is read out to
deadchat. announce_deadchat() has been slightly adjusted to accommodate
this.

Previously, everything that wasn't a truly random event would broadcast
with the same generic "XYZ has just been triggered!" message. Now, you
get a little bit more detail as to why/what triggered the event.

Some helpers in the __HELPERS/events.dm file have been made, for forcing
events normally/async/after a delay (using an addtimer). This also moves
a lot (but not all) instances of events being forced to these helpers.

Some samples:


![image](https://user-images.githubusercontent.com/28870487/233735108-aea2996b-aff4-45e2-ae0c-3e07f86de9b7.png)
Traitors using uplink viruses to turn off the power/comms.


![image](https://user-images.githubusercontent.com/28870487/233734371-18a31dff-198e-4a4a-a43f-15be6cbb545e.png)
Beer nuke!


![image](https://user-images.githubusercontent.com/28870487/233731635-7c93c1ef-641d-40df-975e-a916af4d1129.png)
For when a traitor takes an Space Dragon final objective, which summons
a carp migration event.


![image](https://user-images.githubusercontent.com/28870487/233727323-e2cfc46f-909f-4754-a0f9-a2763360a376.png)
Wizard ritual events!


![image](https://user-images.githubusercontent.com/28870487/233733025-5c8284bc-02e1-41c8-aae4-76a5c2124d97.png)
Even this one!

This also changes runEvent() to run_event(), because I figured I'd be
touching every single instance of the proc anyways.

## Why It's Good For The Game

Better feedback, less confusion amongst deadchat's constituents. 

Some of them may be a bit self-explanatory, but in some cases
(especially the apocalypse rune) it's beneficial to know that an admin
isn't the one behind it.

<!-- Argue for the merits of your changes and how they benefit the game,
especially if they are controversial and/or far reaching. If you can't
actually explain WHY what you are doing will improve the game, then it
probably isn't good for the game in the first place. -->

## Changelog

🆑 Rhials
qol: Deadchat now gets more juicy details on what has triggered a
non-randomly occurring random event.
code: There are now helpers for forcing events in a variety of ways.
More events! More events!!!!!!!
/🆑
2023-04-24 18:45:41 -06:00

172 lines
6.0 KiB
Plaintext

SUBSYSTEM_DEF(events)
name = "Events"
init_order = INIT_ORDER_EVENTS
runlevels = RUNLEVEL_GAME
var/list/control = list() //list of all datum/round_event_control. Used for selecting events based on weight and occurrences.
var/list/running = list() //list of all existing /datum/round_event
var/list/currentrun = list()
var/scheduled = 0 //The next world.time that a naturally occuring random event can be selected.
var/frequency_lower = 1800 //3 minutes lower bound.
var/frequency_upper = 6000 //10 minutes upper bound. Basically an event will happen every 3 to 10 minutes.
var/wizardmode = FALSE
/datum/controller/subsystem/events/Initialize()
for(var/type in typesof(/datum/round_event_control))
var/datum/round_event_control/event = new type()
if(!event.typepath || !event.valid_for_map())
continue //don't want this one! leave it for the garbage collector
control += event //add it to the list of all events (controls)
reschedule()
// Instantiate our holidays list if it hasn't been already
if(isnull(GLOB.holidays))
fill_holidays()
return SS_INIT_SUCCESS
/datum/controller/subsystem/events/fire(resumed = FALSE)
if(!resumed)
checkEvent() //only check these if we aren't resuming a paused fire
src.currentrun = running.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/datum/thing = currentrun[currentrun.len]
currentrun.len--
if(thing)
thing.process(wait * 0.1)
else
running.Remove(thing)
if (MC_TICK_CHECK)
return
//checks if we should select a random event yet, and reschedules if necessary
/datum/controller/subsystem/events/proc/checkEvent()
if(scheduled <= world.time)
spawnEvent()
reschedule()
//decides which world.time we should select another random event at.
/datum/controller/subsystem/events/proc/reschedule()
scheduled = world.time + rand(frequency_lower, max(frequency_lower,frequency_upper))
//selects a random event based on whether it can occur and it's 'weight'(probability)
/datum/controller/subsystem/events/proc/spawnEvent()
set waitfor = FALSE //for the admin prompt
if(!CONFIG_GET(flag/allow_random_events))
return
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.can_spawn_event(players_amt))
continue
if(E.weight < 0) //for round-start events etc.
var/res = TriggerEvent(E)
if(res == EVENT_INTERRUPTED)
continue //like it never happened
if(res == EVENT_CANT_RUN)
return
sum_of_weights += E.weight
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.can_spawn_event(players_amt))
continue
sum_of_weights -= E.weight
if(sum_of_weights <= 0) //we've hit our goal
if(TriggerEvent(E))
return
/datum/controller/subsystem/events/proc/TriggerEvent(datum/round_event_control/E)
. = E.preRunEvent()
if(. == EVENT_CANT_RUN)//we couldn't run this event for some reason, set its max_occurrences to 0
E.max_occurrences = 0
else if(. == EVENT_READY)
E.run_event(random = TRUE)
/datum/controller/subsystem/events/proc/toggleWizardmode()
wizardmode = !wizardmode
message_admins("Summon Events has been [wizardmode ? "enabled, events will occur every [SSevents.frequency_lower / 600] to [SSevents.frequency_upper / 600] minutes" : "disabled"]!")
log_game("Summon Events was [wizardmode ? "enabled" : "disabled"]!")
/datum/controller/subsystem/events/proc/resetFrequency()
frequency_lower = initial(frequency_lower)
frequency_upper = initial(frequency_upper)
/**
* HOLIDAYS
*
* Uncommenting ALLOW_HOLIDAYS in config.txt will enable holidays
*
* It's easy to add stuff. Just add a holiday datum in code/modules/holiday/holidays.dm
* You can then check if it's a special day in any code in the game by calling check_holidays("Groundhog Day")
*
* You can also make holiday random events easily thanks to Pete/Gia's system.
* simply make a random event normally, then assign it a holidayID string which matches the holiday's name.
* Anything with a holidayID, which isn't in the holidays list, will never occur.
*
* Please, Don't spam stuff up with stupid stuff (key example being april-fools Pooh/ERP/etc),
* and don't forget: CHECK YOUR CODE!!!! We don't want any zero-day bugs which happen only on holidays and never get found/fixed!
*/
GLOBAL_LIST(holidays)
/**
* Checks that the passed holiday is located in the global holidays list.
*
* Returns a holiday datum, or null if it's not that holiday.
*/
/proc/check_holidays(holiday_to_find)
if(!CONFIG_GET(flag/allow_holidays))
return // Holiday stuff was not enabled in the config!
if(isnull(GLOB.holidays) && !fill_holidays())
return // Failed to generate holidays, for some reason
return GLOB.holidays[holiday_to_find]
/**
* Fills the holidays list if applicable, or leaves it an empty list.
*/
/proc/fill_holidays()
if(!CONFIG_GET(flag/allow_holidays))
return FALSE // Holiday stuff was not enabled in the config!
GLOB.holidays = list()
for(var/holiday_type in subtypesof(/datum/holiday))
var/datum/holiday/holiday = new holiday_type()
var/delete_holiday = TRUE
for(var/timezone in holiday.timezones)
var/time_in_timezone = world.realtime + timezone HOURS
var/YYYY = text2num(time2text(time_in_timezone, "YYYY")) // get the current year
var/MM = text2num(time2text(time_in_timezone, "MM")) // get the current month
var/DD = text2num(time2text(time_in_timezone, "DD")) // get the current day
var/DDD = time2text(time_in_timezone, "DDD") // get the current weekday
if(holiday.shouldCelebrate(DD, MM, YYYY, DDD))
holiday.celebrate()
GLOB.holidays[holiday.name] = holiday
delete_holiday = FALSE
break
if(delete_holiday)
qdel(holiday)
if(GLOB.holidays.len)
shuffle_inplace(GLOB.holidays)
// regenerate station name because holiday prefixes.
set_station_name(new_station_name())
world.update_status()
return TRUE