mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-15 20:22:07 +00:00
<img width="819" height="348" alt="image" src="https://github.com/user-attachments/assets/0424ec76-2648-43d3-8e94-d44558b44bcf" /> ## About The Pull Request Follow up from #92751 - Not to conflict with it but as an idea on how to change it for the long run. Paramedics currently start with broad department access. This proposal replaces that by granting temporary department access only when an emergency is called. When a player presses "Call X" on a Requests Console, responders called receive temporary access to the common work areas of that department.  > [Security] The Automated Announcement System coldly states, "SECURITY EMERGENCY in Research Lab! (Called by Sloan Keppel, Scientist) RETA door access granted to responders." > [Science] The Automated Announcement System coldly states, "RETA activated (Called by Sloan Keppel, Scientist). Security personnel now have temporary access to your areas." They do not receive access to sub rooms or high risk areas. - Access lasts 5 minutes (configurable) - Access is removed when the timer expires or the emergency is resolved - No mapping changes are required (uses existing request consoles) - Removes Paramedics round start access but gives them external access to rescue bodies in space by default - Flashing blue lights on doors affected by temporary access <img width="897" height="837" alt="image" src="https://github.com/user-attachments/assets/97980cb4-3481-44b6-9f96-fc241ca16f57" /> **The full document is here: https://hackmd.io/@NM8HxpG_Toahg5pimrpsKw/Hk0tKq3Yxe** **Wiki documentation for players and admins: https://wiki.tgstation13.org/Guide_To_RETA** ## Why It's Good For The Game - Removes paramedics’ broad “Doctor+” access. - Keeps them effective as emergency responders. - Responders must be called in OR access upgraded. - Keeps sensitive areas secure. - Prevents spam or stacking through cooldown. - Scales across all maps without mapper work. - Gives admins a new tool for temp department wide access - Dedicated logging file and unit tests - Very performant, only affects living players with connected mind - Gives Request Consoles more use as an alarm button and further utility - Imagine later on "Request Janitor" which sorts access and tells Janitor where needed ## Changelog 🆑 add: RETA System - Request Consoles give temporary access to responders when used for some areas. Paramedics lose broad access but get external space access. qol: Request consoles now show name and job role on call message & Cooldown on spamming calls + sound prompt qol: Medibot access no longer based on Paramedic trim ID - Still has original access image: Added "lights_reta" for temporary door access when in effect admin: Gives admins "RETA door access" verb for giving department wide area access on maps. config: New config settings for RETA /🆑
508 lines
16 KiB
Plaintext
508 lines
16 KiB
Plaintext
#define RESTART_COUNTER_PATH "data/round_counter.txt"
|
|
/// Load byond-tracy. If USE_BYOND_TRACY is defined, then this is ignored and byond-tracy is always loaded.
|
|
#define USE_TRACY_PARAMETER "tracy"
|
|
/// Force the log directory to be something specific in the data/logs folder
|
|
#define OVERRIDE_LOG_DIRECTORY_PARAMETER "log-directory"
|
|
/// Prevent the master controller from starting automatically
|
|
#define NO_INIT_PARAMETER "no-init"
|
|
|
|
GLOBAL_VAR(restart_counter)
|
|
|
|
/**
|
|
* WORLD INITIALIZATION
|
|
* THIS IS THE INIT ORDER:
|
|
*
|
|
* BYOND =>
|
|
* - (secret init native) =>
|
|
* - world.Genesis() =>
|
|
* - world.init_byond_tracy()
|
|
* - (Start native profiling)
|
|
* - world.init_debugger()
|
|
* - Master =>
|
|
* - config *unloaded
|
|
* - (all subsystems) PreInit()
|
|
* - GLOB =>
|
|
* - make_datum_reference_lists()
|
|
* - (/static variable inits, reverse declaration order)
|
|
* - (all pre-mapped atoms) /atom/New()
|
|
* - world.New() =>
|
|
* - config.Load()
|
|
* - world.InitTgs() =>
|
|
* - TgsNew() *may sleep
|
|
* - GLOB.rev_data.load_tgs_info()
|
|
* - world.ConfigLoaded() =>
|
|
* - SSdbcore.InitializeRound()
|
|
* - world.SetupLogs()
|
|
* - load_admins()
|
|
* - ...
|
|
* - Master.Initialize() =>
|
|
* - (all subsystems) Initialize()
|
|
* - Master.StartProcessing() =>
|
|
* - Master.Loop() =>
|
|
* - Failsafe
|
|
* - world.RunUnattendedFunctions()
|
|
*
|
|
* Now listen up because I want to make something clear:
|
|
* If something is not in this list it should almost definitely be handled by a subsystem Initialize()ing
|
|
* If whatever it is that needs doing doesn't fit in a subsystem you probably aren't trying hard enough tbhfam
|
|
*
|
|
* GOT IT MEMORIZED?
|
|
* - Dominion/Cyberboss
|
|
*
|
|
* Where to put init shit quick guide:
|
|
* If you need it to happen before the mc is created: world/Genesis.
|
|
* If you need it to happen last: world/New(),
|
|
* Otherwise, in a subsystem preinit or init. Subsystems can set an init priority.
|
|
*/
|
|
|
|
/**
|
|
* THIS !!!SINGLE!!! PROC IS WHERE ANY FORM OF INIITIALIZATION THAT CAN'T BE PERFORMED IN SUBSYSTEMS OR WORLD/NEW IS DONE
|
|
* NOWHERE THE FUCK ELSE
|
|
* I DON'T CARE HOW MANY LAYERS OF DEBUG/PROFILE/TRACE WE HAVE, YOU JUST HAVE TO DEAL WITH THIS PROC EXISTING
|
|
* I'M NOT EVEN GOING TO TELL YOU WHERE IT'S CALLED FROM BECAUSE I'M DECLARING THAT FORBIDDEN KNOWLEDGE
|
|
* SO HELP ME GOD IF I FIND ABSTRACTION LAYERS OVER THIS!
|
|
*/
|
|
/world/proc/Genesis(tracy_initialized = FALSE)
|
|
RETURN_TYPE(/datum/controller/master)
|
|
|
|
if(!tracy_initialized)
|
|
Tracy = new
|
|
#ifdef USE_BYOND_TRACY
|
|
if(Tracy.enable("USE_BYOND_TRACY defined"))
|
|
Genesis(tracy_initialized = TRUE)
|
|
return
|
|
#else
|
|
var/tracy_enable_reason
|
|
if(USE_TRACY_PARAMETER in params)
|
|
tracy_enable_reason = "world.params"
|
|
if(fexists(TRACY_ENABLE_PATH))
|
|
tracy_enable_reason ||= "enabled for round"
|
|
SEND_TEXT(world.log, "[TRACY_ENABLE_PATH] exists, initializing byond-tracy!")
|
|
fdel(TRACY_ENABLE_PATH)
|
|
if(!isnull(tracy_enable_reason) && Tracy.enable(tracy_enable_reason))
|
|
Genesis(tracy_initialized = TRUE)
|
|
return
|
|
#endif
|
|
|
|
Profile(PROFILE_RESTART)
|
|
Profile(PROFILE_RESTART, type = "sendmaps")
|
|
|
|
// Write everything to this log file until we get to SetupLogs() later
|
|
_initialize_log_files("data/logs/config_error.[GUID()].log")
|
|
|
|
// Init the debugger first so we can debug Master
|
|
Debugger = new
|
|
|
|
// Create the logger
|
|
logger = new
|
|
|
|
// THAT'S IT, WE'RE DONE, THE. FUCKING. END.
|
|
Master = new
|
|
|
|
/**
|
|
* World creation
|
|
*
|
|
* Here is where a round itself is actually begun and setup.
|
|
* * db connection setup
|
|
* * config loaded from files
|
|
* * loads admins
|
|
* * Sets up the dynamic menu system
|
|
* * and most importantly, calls initialize on the master subsystem, starting the game loop that causes the rest of the game to begin processing and setting up
|
|
*
|
|
*
|
|
* Nothing happens until something moves. ~Albert Einstein
|
|
*
|
|
* For clarity, this proc gets triggered later in the initialization pipeline, it is not the first thing to happen, as it might seem.
|
|
*
|
|
* Initialization Pipeline:
|
|
* Global vars are new()'ed, (including config, glob, and the master controller will also new and preinit all subsystems when it gets new()ed)
|
|
* Compiled in maps are loaded (mainly centcom). all areas/turfs/objs/mobs(ATOMs) in these maps will be new()ed
|
|
* world/New() (You are here)
|
|
* Once world/New() returns, client's can connect.
|
|
* 1 second sleep
|
|
* Master Controller initialization.
|
|
* Subsystem initialization.
|
|
* Non-compiled-in maps are maploaded, all atoms are new()ed
|
|
* All atoms in both compiled and uncompiled maps are initialized()
|
|
*/
|
|
/world/New()
|
|
log_world("World loaded at [time_stamp()]!")
|
|
|
|
// From a really fucking old commit (91d7150)
|
|
// I wanted to move it but I think this needs to be after /world/New is called but before any sleeps?
|
|
// - Dominion/Cyberboss
|
|
GLOB.timezoneOffset = world.timezone * 36000
|
|
|
|
// First possible sleep()
|
|
InitTgs()
|
|
|
|
config.Load(params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER])
|
|
|
|
ConfigLoaded()
|
|
|
|
if(NO_INIT_PARAMETER in params)
|
|
return
|
|
|
|
Master.Initialize(10, FALSE, TRUE)
|
|
|
|
RunUnattendedFunctions()
|
|
|
|
/// Initializes TGS and loads the returned revising info into GLOB.revdata
|
|
/world/proc/InitTgs()
|
|
TgsNew(new /datum/tgs_event_handler/impl, TGS_SECURITY_TRUSTED)
|
|
GLOB.revdata.load_tgs_info()
|
|
|
|
/// Runs after config is loaded but before Master is initialized
|
|
/world/proc/ConfigLoaded()
|
|
// Everything in here is prioritized in a very specific way.
|
|
// If you need to add to it, ask yourself hard if what your adding is in the right spot
|
|
// (i.e. basically nothing should be added before load_admins() in here)
|
|
|
|
// Try to set round ID
|
|
SSdbcore.InitializeRound()
|
|
|
|
SetupLogs()
|
|
|
|
load_admins(initial = TRUE)
|
|
|
|
load_poll_data()
|
|
|
|
// Initialize RETA system - code/modules/reta/reta_system.dm
|
|
reta_init_config()
|
|
|
|
LoadVerbs(/datum/verbs/menu)
|
|
|
|
if(fexists(RESTART_COUNTER_PATH))
|
|
GLOB.restart_counter = text2num(trim(file2text(RESTART_COUNTER_PATH)))
|
|
fdel(RESTART_COUNTER_PATH)
|
|
|
|
/// Runs after the call to Master.Initialize, but before the delay kicks in. Used to turn the world execution into some single function then exit
|
|
/world/proc/RunUnattendedFunctions()
|
|
#ifdef UNIT_TESTS
|
|
HandleTestRun()
|
|
#endif
|
|
|
|
#ifdef AUTOWIKI
|
|
setup_autowiki()
|
|
#endif
|
|
|
|
/world/proc/HandleTestRun()
|
|
//trigger things to run the whole process
|
|
Master.sleep_offline_after_initializations = FALSE
|
|
SSticker.start_immediately = TRUE
|
|
CONFIG_SET(number/round_end_countdown, 0)
|
|
var/datum/callback/cb
|
|
#ifdef UNIT_TESTS
|
|
cb = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(RunUnitTests))
|
|
#else
|
|
cb = VARSET_CALLBACK(SSticker, force_ending, ADMIN_FORCE_END_ROUND)
|
|
#endif
|
|
SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_addtimer), cb, 10 SECONDS))
|
|
|
|
/// Returns a list of data about the world state, don't clutter
|
|
/world/proc/get_world_state_for_logging()
|
|
var/data = list()
|
|
data["tick_usage"] = world.tick_usage
|
|
data["tick_lag"] = world.tick_lag
|
|
data["time"] = world.time
|
|
data["timestamp"] = rustg_unix_timestamp()
|
|
return data
|
|
|
|
/world/proc/SetupLogs()
|
|
var/override_dir = params[OVERRIDE_LOG_DIRECTORY_PARAMETER]
|
|
if(!override_dir)
|
|
var/realtime = world.realtime
|
|
var/texttime = time2text(realtime, "YYYY/MM/DD", TIMEZONE_UTC)
|
|
GLOB.log_directory = "data/logs/[texttime]/round-"
|
|
GLOB.picture_logging_prefix = "L_[time2text(realtime, "YYYYMMDD", TIMEZONE_UTC)]_"
|
|
GLOB.picture_log_directory = "data/picture_logs/[texttime]/round-"
|
|
if(GLOB.round_id)
|
|
GLOB.log_directory += "[GLOB.round_id]"
|
|
GLOB.picture_logging_prefix += "R_[GLOB.round_id]_"
|
|
GLOB.picture_log_directory += "[GLOB.round_id]"
|
|
else
|
|
var/timestamp = replacetext(time_stamp(), ":", ".")
|
|
GLOB.log_directory += "[timestamp]"
|
|
GLOB.picture_log_directory += "[timestamp]"
|
|
GLOB.picture_logging_prefix += "T_[timestamp]_"
|
|
else
|
|
GLOB.log_directory = "data/logs/[override_dir]"
|
|
GLOB.picture_logging_prefix = "O_[override_dir]_"
|
|
GLOB.picture_log_directory = "data/picture_logs/[override_dir]"
|
|
|
|
logger.init_logging()
|
|
|
|
if(Tracy.trace_path)
|
|
rustg_file_write("[Tracy.trace_path]", "[GLOB.log_directory]/tracy.loc")
|
|
|
|
var/latest_changelog = file("[global.config.directory]/../html/changelogs/archive/" + time2text(world.timeofday, "YYYY-MM", TIMEZONE_UTC) + ".yml")
|
|
GLOB.changelog_hash = fexists(latest_changelog) ? md5(latest_changelog) : 0 //for telling if the changelog has changed recently
|
|
|
|
if(GLOB.round_id)
|
|
log_game("Round ID: [GLOB.round_id]")
|
|
|
|
// This was printed early in startup to the world log and config_error.log,
|
|
// but those are both private, so let's put the commit info in the runtime
|
|
// log which is ultimately public.
|
|
log_runtime(GLOB.revdata.get_log_message())
|
|
|
|
#ifndef USE_CUSTOM_ERROR_HANDLER
|
|
world.log = file("[GLOB.log_directory]/dd.log")
|
|
#else
|
|
if (TgsAvailable()) // why
|
|
world.log = file("[GLOB.log_directory]/dd.log") //not all runtimes trigger world/Error, so this is the only way to ensure we can see all of them.
|
|
#endif
|
|
|
|
/world/Topic(T, addr, master, key)
|
|
TGS_TOPIC //redirect to server tools if necessary
|
|
|
|
var/static/list/topic_handlers = TopicHandlers()
|
|
|
|
var/list/input = params2list(T)
|
|
var/datum/world_topic/handler
|
|
for(var/I in topic_handlers)
|
|
if(I in input)
|
|
handler = topic_handlers[I]
|
|
break
|
|
|
|
if((!handler || initial(handler.log)) && config && CONFIG_GET(flag/log_world_topic))
|
|
log_topic("\"[T]\", from:[addr], master:[master], key:[key]")
|
|
|
|
if(!handler)
|
|
return
|
|
|
|
handler = new handler()
|
|
return handler.TryRun(input)
|
|
|
|
/world/proc/AnnouncePR(announcement, list/payload)
|
|
var/static/list/PRcounts = list() //PR id -> number of times announced this round
|
|
var/id = "[payload["pull_request"]["id"]]"
|
|
if(!PRcounts[id])
|
|
PRcounts[id] = 1
|
|
else
|
|
++PRcounts[id]
|
|
if(PRcounts[id] > CONFIG_GET(number/pr_announcements_per_round))
|
|
return
|
|
|
|
var/final_composed = span_announce("PR: [announcement]")
|
|
for(var/client/C in GLOB.clients)
|
|
C.AnnouncePR(final_composed)
|
|
|
|
/world/proc/FinishTestRun()
|
|
set waitfor = FALSE
|
|
var/list/fail_reasons
|
|
if(GLOB)
|
|
if(GLOB.total_runtimes != 0)
|
|
fail_reasons = list("Total runtimes: [GLOB.total_runtimes]")
|
|
#ifdef UNIT_TESTS
|
|
if(GLOB.failed_any_test)
|
|
LAZYADD(fail_reasons, "Unit Tests failed!")
|
|
#endif
|
|
if(!GLOB.log_directory)
|
|
LAZYADD(fail_reasons, "Missing GLOB.log_directory!")
|
|
else
|
|
fail_reasons = list("Missing GLOB!")
|
|
if(!fail_reasons)
|
|
text2file("Success!", "[GLOB.log_directory]/clean_run.lk")
|
|
else
|
|
log_world("Test run failed!\n[fail_reasons.Join("\n")]")
|
|
sleep(0) //yes, 0, this'll let Reboot finish and prevent byond memes
|
|
qdel(src) //shut it down
|
|
|
|
/// Returns TRUE if the world should do a TGS hard reboot.
|
|
/world/proc/check_hard_reboot()
|
|
if(!TgsAvailable())
|
|
return FALSE
|
|
// byond-tracy can't clean up itself, and thus we should always hard reboot if its enabled, to avoid an infinitely growing trace.
|
|
if(Tracy?.enabled)
|
|
return TRUE
|
|
var/ruhr = CONFIG_GET(number/rounds_until_hard_restart)
|
|
switch(ruhr)
|
|
if(-1)
|
|
return FALSE
|
|
if(0)
|
|
return TRUE
|
|
else
|
|
if(GLOB.restart_counter >= ruhr)
|
|
return TRUE
|
|
else
|
|
text2file("[++GLOB.restart_counter]", RESTART_COUNTER_PATH)
|
|
return FALSE
|
|
|
|
/world/Reboot(reason = 0, fast_track = FALSE)
|
|
if (reason || fast_track) //special reboot, do none of the normal stuff
|
|
if (usr)
|
|
log_admin("[key_name(usr)] Has requested an immediate world restart via client side debugging tools")
|
|
message_admins("[key_name_admin(usr)] Has requested an immediate world restart via client side debugging tools")
|
|
to_chat(world, span_boldannounce("Rebooting World immediately due to host request."))
|
|
else
|
|
to_chat(world, span_boldannounce("Rebooting world..."))
|
|
Master.Shutdown() //run SS shutdowns
|
|
|
|
#ifdef UNIT_TESTS
|
|
FinishTestRun()
|
|
return
|
|
#else
|
|
if(check_hard_reboot())
|
|
log_world("World hard rebooted at [time_stamp()]")
|
|
shutdown_logging() // See comment below.
|
|
QDEL_NULL(Tracy)
|
|
QDEL_NULL(Debugger)
|
|
TgsEndProcess()
|
|
return ..()
|
|
|
|
log_world("World rebooted at [time_stamp()]")
|
|
|
|
shutdown_logging() // Past this point, no logging procs can be used, at risk of data loss.
|
|
QDEL_NULL(Tracy)
|
|
QDEL_NULL(Debugger)
|
|
|
|
TgsReboot() // TGS can decide to kill us right here, so it's important to do it last
|
|
|
|
..()
|
|
#endif
|
|
|
|
/world/Del()
|
|
QDEL_NULL(Tracy)
|
|
QDEL_NULL(Debugger)
|
|
. = ..()
|
|
|
|
/world/proc/update_status()
|
|
|
|
var/list/features = list()
|
|
|
|
if(LAZYACCESS(SSlag_switch.measures, DISABLE_NON_OBSJOBS))
|
|
features += "closed"
|
|
|
|
var/new_status = ""
|
|
var/hostedby
|
|
if(config)
|
|
var/server_name = CONFIG_GET(string/servername)
|
|
if (server_name)
|
|
new_status += "<b>[server_name]</b> "
|
|
if(CONFIG_GET(flag/allow_respawn))
|
|
features += "respawn" // show "respawn" regardless of "respawn as char" or "free respawn"
|
|
if(!CONFIG_GET(flag/allow_ai))
|
|
features += "AI disabled"
|
|
hostedby = CONFIG_GET(string/hostedby)
|
|
|
|
if (CONFIG_GET(flag/station_name_in_hub_entry))
|
|
new_status += " — <b>[station_name()]</b>"
|
|
|
|
var/players = GLOB.clients.len
|
|
|
|
game_state = (CONFIG_GET(number/extreme_popcap) && players >= CONFIG_GET(number/extreme_popcap)) //tells the hub if we are full
|
|
|
|
if (!host && hostedby)
|
|
features += "hosted by <b>[hostedby]</b>"
|
|
|
|
if(length(features))
|
|
new_status += ": [jointext(features, ", ")]"
|
|
|
|
if(!SSticker || SSticker?.current_state == GAME_STATE_STARTUP)
|
|
new_status += "<br><b>STARTING</b>"
|
|
else if(SSticker)
|
|
if(SSticker.current_state == GAME_STATE_PREGAME && SSticker.GetTimeLeft() > 0)
|
|
new_status += "<br>Starting: <b>[round((SSticker.GetTimeLeft())/10)]</b>"
|
|
else if(SSticker.current_state == GAME_STATE_SETTING_UP)
|
|
new_status += "<br>Starting: <b>Now</b>"
|
|
else if(SSticker.IsRoundInProgress())
|
|
new_status += "<br>Time: <b>[time2text(STATION_TIME_PASSED(), "hh:mm", NO_TIMEZONE)]</b>"
|
|
if(SSshuttle?.emergency && SSshuttle?.emergency?.mode != (SHUTTLE_IDLE || SHUTTLE_ENDGAME))
|
|
new_status += " | Shuttle: <b>[SSshuttle.emergency.getModeStr()] [SSshuttle.emergency.getTimerStr()]</b>"
|
|
else if(SSticker.current_state == GAME_STATE_FINISHED)
|
|
new_status += "<br><b>RESTARTING</b>"
|
|
if(SSmapping.current_map)
|
|
new_status += "<br>Map: <b>[SSmapping.current_map.map_path == CUSTOM_MAP_PATH ? "Uncharted Territory" : SSmapping.current_map.map_name]</b>"
|
|
if(SSmap_vote.next_map_config)
|
|
new_status += "[SSmapping.current_map ? " | " : "<br>"]Next: <b>[SSmap_vote.next_map_config.map_path == CUSTOM_MAP_PATH ? "Uncharted Territory" : SSmap_vote.next_map_config.map_name]</b>"
|
|
|
|
status = new_status
|
|
|
|
/world/proc/update_hub_visibility(new_visibility)
|
|
if(new_visibility == GLOB.hub_visibility)
|
|
return
|
|
GLOB.hub_visibility = new_visibility
|
|
if(GLOB.hub_visibility)
|
|
hub_password = "kMZy3U5jJHSiBQjr"
|
|
else
|
|
hub_password = "SORRYNOPASSWORD"
|
|
|
|
/**
|
|
* Handles increasing the world's maxx var and initializing the new turfs and assigning them to the global area.
|
|
* If map_load_z_cutoff is passed in, it will only load turfs up to that z level, inclusive.
|
|
* This is because maploading will handle the turfs it loads itself.
|
|
*/
|
|
/world/proc/increase_max_x(new_maxx, map_load_z_cutoff = maxz)
|
|
if(new_maxx <= maxx)
|
|
return
|
|
var/old_max = world.maxx
|
|
maxx = new_maxx
|
|
if(!map_load_z_cutoff)
|
|
return
|
|
var/area/global_area = GLOB.areas_by_type[world.area] // We're guaranteed to be touching the global area, so we'll just do this
|
|
LISTASSERTLEN(global_area.turfs_by_zlevel, map_load_z_cutoff, list())
|
|
for (var/zlevel in 1 to map_load_z_cutoff)
|
|
var/list/to_add = block(
|
|
old_max + 1, 1, zlevel,
|
|
maxx, maxy, zlevel
|
|
)
|
|
|
|
global_area.turfs_by_zlevel[zlevel] += to_add
|
|
|
|
/world/proc/increase_max_y(new_maxy, map_load_z_cutoff = maxz)
|
|
if(new_maxy <= maxy)
|
|
return
|
|
var/old_maxy = maxy
|
|
maxy = new_maxy
|
|
if(!map_load_z_cutoff)
|
|
return
|
|
var/area/global_area = GLOB.areas_by_type[world.area] // We're guaranteed to be touching the global area, so we'll just do this
|
|
LISTASSERTLEN(global_area.turfs_by_zlevel, map_load_z_cutoff, list())
|
|
for (var/zlevel in 1 to map_load_z_cutoff)
|
|
var/list/to_add = block(
|
|
1, old_maxy + 1, 1,
|
|
maxx, maxy, map_load_z_cutoff
|
|
)
|
|
global_area.turfs_by_zlevel[zlevel] += to_add
|
|
|
|
/world/proc/incrementMaxZ()
|
|
maxz++
|
|
SSmobs.MaxZChanged()
|
|
SSai_controllers.on_max_z_changed()
|
|
|
|
/world/proc/change_fps(new_value = 20)
|
|
if(new_value <= 0)
|
|
CRASH("change_fps() called with [new_value] new_value.")
|
|
if(fps == new_value)
|
|
return //No change required.
|
|
|
|
fps = new_value
|
|
on_tickrate_change()
|
|
|
|
|
|
/world/proc/change_tick_lag(new_value = 0.5)
|
|
if(new_value <= 0)
|
|
CRASH("change_tick_lag() called with [new_value] new_value.")
|
|
if(tick_lag == new_value)
|
|
return //No change required.
|
|
|
|
tick_lag = new_value
|
|
on_tickrate_change()
|
|
|
|
|
|
/world/proc/on_tickrate_change()
|
|
SStimer?.reset_buckets()
|
|
#ifndef DISABLE_DREAMLUAU
|
|
DREAMLUAU_SET_EXECUTION_LIMIT_MILLIS(tick_lag * 100)
|
|
#endif
|
|
|
|
/world/Profile(command, type, format)
|
|
if((command & PROFILE_STOP) || !global.config?.loaded || !CONFIG_GET(flag/forbid_all_profiling))
|
|
. = ..()
|
|
|
|
#undef NO_INIT_PARAMETER
|
|
#undef OVERRIDE_LOG_DIRECTORY_PARAMETER
|
|
#undef USE_TRACY_PARAMETER
|
|
#undef RESTART_COUNTER_PATH
|