mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-25 16:45:42 +00:00
Basically, they key difference between StonedMC and CarnMC is that when multiple ticks want to run at the same byond tick, we divvy up the tick between the subsystems, rather then allow one subsystem to hog it all. The key difference between StonedMC and GoonPS is that we allow the subsystems to tell us how to divvy up the tick using flags and priority. The new SS_ flags allows us to select behaviors that used to be piggybacked as side effects of dynamic wait or default but sometimes unneeded behavior. Dynamic wait is 100% gone, lower priority and SS_BACKGROUND are better more refined ways of doing this when combined with MC_TICK_CHECK I have by design never looked at the inners of goonPS, so this is all original code but I know it uses two loops because of comments by goon devs on reddit threads, that design didn't make sense before, but when I can tell a SS how much of a byond tick it is allowed to have, knowing how many need to run this tick is helpful I also know a bit more about how it works from piecing together comments in #vgstation. Detailed list of changes: Subsystems now have flags, allowing fine grain control over things like rather or not it processes, inits, rather it's wait is how long between runs (post run timing) or how long between starts, and rather or not late fires should cause the next fire to be earlier. Mc now has two loops One loop handles queuing shit, one loop handles running shit. MC now splits up tick allotment rather than first come first serve Subsystems can even request a bigger share using higher priorities. (It will even resume subsystems it paused if other subsystems hadn't used as much as it predicted they might need) Default fps is now 20 This is related enough to the MC and it's a change that's really long since over due All code oddities are most likely to be necessities to lower overhead on the mc since it runs every tick
205 lines
6.9 KiB
Plaintext
205 lines
6.9 KiB
Plaintext
var/datum/subsystem/events/SSevent
|
|
|
|
/datum/subsystem/events
|
|
name = "Events"
|
|
init_order = 6
|
|
|
|
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/list/holidays //List of all holidays occuring today or null if no holidays
|
|
var/wizardmode = 0
|
|
|
|
|
|
/datum/subsystem/events/New()
|
|
NEW_SS_GLOBAL(SSevent)
|
|
|
|
|
|
/datum/subsystem/events/Initialize(time, zlevel)
|
|
for(var/type in typesof(/datum/round_event_control))
|
|
var/datum/round_event_control/E = new type()
|
|
if(!E.typepath)
|
|
continue //don't want this one! leave it for the garbage collector
|
|
control += E //add it to the list of all events (controls)
|
|
reschedule()
|
|
getHoliday()
|
|
..()
|
|
|
|
|
|
/datum/subsystem/events/fire(resumed = 0)
|
|
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()
|
|
else
|
|
running.Remove(thing)
|
|
if (MC_TICK_CHECK)
|
|
return
|
|
|
|
//checks if we should select a random event yet, and reschedules if necessary
|
|
/datum/subsystem/events/proc/checkEvent()
|
|
if(scheduled <= world.time)
|
|
spawnEvent()
|
|
reschedule()
|
|
|
|
//decides which world.time we should select another random event at.
|
|
/datum/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/subsystem/events/proc/spawnEvent()
|
|
if(!config.allow_random_events)
|
|
// var/datum/round_event_control/E = locate(/datum/round_event_control/dust) in control
|
|
// 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.canSpawnEvent(players_amt, gamemode))
|
|
continue
|
|
if(E.weight < 0) //for round-start events etc.
|
|
if(E.runEvent() == PROCESS_KILL)
|
|
E.max_occurrences = 0
|
|
continue
|
|
if (E.alertadmins)
|
|
message_admins("Random Event triggering: [E.name] ([E.typepath])")
|
|
log_game("Random Event triggering: [E.name] ([E.typepath])")
|
|
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.canSpawnEvent(players_amt, gamemode))
|
|
continue
|
|
sum_of_weights -= E.weight
|
|
|
|
if(sum_of_weights <= 0) //we've hit our goal
|
|
if(E.runEvent() == PROCESS_KILL)//we couldn't run this event for some reason, set its max_occurrences to 0
|
|
E.max_occurrences = 0
|
|
continue
|
|
if (E.alertadmins)
|
|
message_admins("Random Event triggering: [E.name] ([E.typepath])")
|
|
log_game("Random Event triggering: [E.name] ([E.typepath])")
|
|
return
|
|
|
|
/datum/round_event/proc/findEventArea() //Here's a nice proc to use to find an area for your event to land in!
|
|
var/list/safe_areas = list(
|
|
/area/turret_protected/ai,
|
|
/area/turret_protected/ai_upload,
|
|
/area/engine,
|
|
/area/solar,
|
|
/area/holodeck,
|
|
/area/shuttle
|
|
)
|
|
|
|
//These are needed because /area/engine has to be removed from the list, but we still want these areas to get fucked up.
|
|
var/list/danger_areas = list(
|
|
/area/engine/break_room,
|
|
/area/engine/chiefs_office)
|
|
|
|
//Need to locate() as it's just a list of paths.
|
|
return locate(pick((the_station_areas - safe_areas) + danger_areas))
|
|
|
|
|
|
//allows a client to trigger an event
|
|
//aka Badmin Central
|
|
/client/proc/forceEvent()
|
|
set name = "Trigger Event"
|
|
set category = "Fun"
|
|
|
|
if(!holder ||!check_rights(R_FUN))
|
|
return
|
|
|
|
holder.forceEvent()
|
|
|
|
/datum/admins/proc/forceEvent()
|
|
var/dat = ""
|
|
var/normal = ""
|
|
var/magic = ""
|
|
var/holiday = ""
|
|
for(var/datum/round_event_control/E in SSevent.control)
|
|
dat = "<BR><A href='?src=\ref[src];forceevent=\ref[E]'>[E]</A>"
|
|
if(E.holidayID)
|
|
holiday += dat
|
|
else if(E.wizardevent)
|
|
magic += dat
|
|
else
|
|
normal += dat
|
|
|
|
dat = normal + "<BR>" + magic + "<BR>" + holiday
|
|
|
|
var/datum/browser/popup = new(usr, "forceevent", "Force Random Event", 300, 750)
|
|
popup.set_content(dat)
|
|
popup.open()
|
|
|
|
|
|
/*
|
|
//////////////
|
|
// 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 doing if(SSevent.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!
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//ALSO, MOST IMPORTANTLY: Don't add stupid stuff! Discuss bonus content with Project-Heads first please!//
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
*/
|
|
|
|
//sets up the holidays and holidays list
|
|
/datum/subsystem/events/proc/getHoliday()
|
|
if(!config.allow_holidays)
|
|
return // Holiday stuff was not enabled in the config!
|
|
|
|
var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year
|
|
var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month
|
|
var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
|
|
|
|
for(var/H in subtypesof(/datum/holiday))
|
|
var/datum/holiday/holiday = new H()
|
|
if(holiday.shouldCelebrate(DD, MM, YY))
|
|
holiday.celebrate()
|
|
if(!holidays)
|
|
holidays = list()
|
|
holidays[holiday.name] = holiday
|
|
|
|
if(holidays)
|
|
holidays = shuffle(holidays)
|
|
world.update_status()
|
|
|
|
/datum/subsystem/events/proc/toggleWizardmode()
|
|
wizardmode = !wizardmode
|
|
message_admins("Summon Events has been [wizardmode ? "enabled, events will occur every [SSevent.frequency_lower / 600] to [SSevent.frequency_upper / 600] minutes" : "disabled"]!")
|
|
log_game("Summon Events was [wizardmode ? "enabled" : "disabled"]!")
|
|
|
|
|
|
/datum/subsystem/events/proc/resetFrequency()
|
|
frequency_lower = initial(frequency_lower)
|
|
frequency_upper = initial(frequency_upper)
|