Files
+37 21b4095dfd [MDB IGNORE] [IDB IGNORE] Upstream Sync - 04/17/2026 (#5453)
Upstream 04/17/2026

fixes https://github.com/Bubberstation/Bubberstation/issues/5549

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>
Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com>
Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com>
Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com>
Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com>
Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com>
Co-authored-by: loganuk <fakeemail123@aol.com>
Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com>
Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com>
Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com>
Co-authored-by: Lucy <lucy@absolucy.moe>
Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com>
Co-authored-by: Isratosh <Isratosh@hotmail.com>
Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com>
Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com>
Co-authored-by: Alexander V. <volas@ya.ru>
Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com>
Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Tim <timothymtorres@gmail.com>
Co-authored-by: Iamgoofball <iamgoofball@gmail.com>
Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com>
Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com>
Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com>
Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com>
Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com>
Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com>
Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com>
Co-authored-by: Josh <josh.adam.powell@gmail.com>
Co-authored-by: Josh Powell <josh.powell@softwire.com>
Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com>
Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com>
Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com>
Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
2026-05-16 00:56:00 +02:00

232 lines
7.9 KiB
Plaintext

SUBSYSTEM_DEF(title)
name = "Title Screen"
ss_flags = SS_NO_FIRE
init_stage = INITSTAGE_EARLY
var/file_path
var/icon/startup_splash
/// The current title screen being displayed, as a file path text.
var/current_title_screen
/// The current notice text, or null.
var/current_notice
/// The preamble html that includes all styling and layout.
var/title_html
/// The list of possible title screens to rotate through, as file path texts.
var/title_screens = list()
/// average realtime seconds it takes to load the map we're currently running
var/average_completion_time = DEFAULT_TITLE_MAP_LOADTIME
/// a given startup message => average timestamp in realtime seconds
var/list/startup_message_timings = list()
/// Raw data to update later
var/list/progress_json = list()
/// The reference realtime that we're treating as 0 for this run
var/progress_reference_time = 0
/// A list of station traits that have lobby buttons
var/list/available_lobby_station_traits = list()
/datum/controller/subsystem/title/Initialize()
var/dat
if(!fexists("[global.config.directory]/bubbers/bubbers_title.txt"))
to_chat(world, span_boldwarning("CRITICAL ERROR: Unable to read bubbers_title.txt, reverting to backup title html, please check your server config and ensure this file exists."))
dat = DEFAULT_TITLE_HTML
else
dat = file2text("[global.config.directory]/bubbers/bubbers_title.txt")
title_html = dat
var/list/provisional_title_screens = flist("[global.config.directory]/title_screens/images/")
var/list/local_title_screens = list()
var/list/current_map_file = splittext(SSmapping.current_map.map_file, ".")
for(var/screen in provisional_title_screens)
var/list/formatted_list = splittext(screen, "+")
if((LOWER_TEXT(formatted_list[1]) == LOWER_TEXT(current_map_file[1])))
local_title_screens += screen
continue
if(LAZYLEN(formatted_list) > 1 && LOWER_TEXT(formatted_list[1]) == "startup_splash")
var/file_path = "[global.config.directory]/title_screens/images/[screen]"
ASSERT(fexists(file_path))
startup_splash = new(fcopy_rsc(file_path))
if(local_title_screens.len == 0)
for(var/screen in provisional_title_screens)
var/list/formatted_list = splittext(screen, "+")
if((LAZYLEN(formatted_list) == 1 && (formatted_list[1] != "exclude" && formatted_list[1] != "blank.png" && formatted_list[1] != "startup_splash")))
local_title_screens += screen
// Progress stuff
check_progress_reference_time()
load_progress_json()
if(startup_splash)
change_title_screen(startup_splash)
else
change_title_screen(DEFAULT_TITLE_LOADING_SCREEN)
if(length(local_title_screens))
for(var/i in local_title_screens)
var/file_path = "[global.config.directory]/title_screens/images/[i]"
ASSERT(fexists(file_path))
var/icon/title2use = new(fcopy_rsc(file_path))
title_screens += title2use
return SS_INIT_SUCCESS
/**
* Make sure reference time is set up. If not, this is now time 0.
*/
/datum/controller/subsystem/title/proc/check_progress_reference_time()
if(!progress_reference_time)
progress_reference_time = world.timeofday
/**
* Handle and clean up leaving startup
*/
/datum/controller/subsystem/title/proc/check_finish_progress()
//It's the first time we're firing out of startup -> pregame
if(progress_json && SSticker.current_state == GAME_STATE_PREGAME)
save_progress_json()
/**
* Load the progress info json and setup that part of the SS.
*/
/datum/controller/subsystem/title/proc/load_progress_json()
var/json_file = file(TITLE_PROGRESS_CACHE_FILE)
if(!fexists(json_file))
return
// Load map progress cache info
progress_json = json_decode(file2text(json_file))
// Different format. Purge everything.
if(progress_json["_version"] != TITLE_PROGRESS_CACHE_VERSION)
progress_json.Cut()
return
// If there's no info about the current map, use the defaults.
var/list/map_info = progress_json[SSmapping.current_map.map_name]
if(!islist(map_info))
return
// Get expected total time and subpart time
average_completion_time = map_info["total"] || DEFAULT_TITLE_MAP_LOADTIME
startup_message_timings = map_info["messages"] || list()
/datum/controller/subsystem/title/proc/save_progress_json()
var/json_file = file(TITLE_PROGRESS_CACHE_FILE)
var/list/map_info = list()
progress_json["_version"] = TITLE_PROGRESS_CACHE_VERSION
if(progress_json[SSmapping.current_map.map_name])
// Save total time and updated message timings. Latest time is worth 1/4 the "average"
map_info["total"] = 0.75 * average_completion_time + 0.25 * (world.timeofday - progress_reference_time)
else
// New. Just save the time it took.
map_info["total"] = world.timeofday - progress_reference_time
map_info["messages"] = startup_message_timings
progress_json[SSmapping.current_map.map_name] = map_info
fdel(json_file)
WRITE_FILE(json_file, json_encode(progress_json))
// We're done, don't touch it again this round.
progress_json = null
/datum/controller/subsystem/title/Recover()
startup_splash = SStitle.startup_splash
file_path = SStitle.file_path
current_title_screen = SStitle.current_title_screen
current_notice = SStitle.current_notice
title_html = SStitle.title_html
title_screens = SStitle.title_screens
average_completion_time = SStitle.average_completion_time
startup_message_timings = SStitle.startup_message_timings
progress_json = SStitle.progress_json
progress_reference_time = SStitle.progress_reference_time
/**
* Show the title screen to all new players.
*/
/datum/controller/subsystem/title/proc/show_title_screen()
for(var/mob/dead/new_player/new_player in GLOB.new_player_list)
INVOKE_ASYNC(new_player, TYPE_PROC_REF(/mob/dead/new_player, show_title_screen))
/**
* Adds a notice to the main title screen in the form of big red text!
*/
/datum/controller/subsystem/title/proc/set_notice(new_title)
current_notice = new_title ? sanitize_text(new_title) : null
show_title_screen()
/**
* Changes the title screen to a new image.
*/
/datum/controller/subsystem/title/proc/change_title_screen(new_screen)
if(new_screen)
current_title_screen = new_screen
else
if(LAZYLEN(title_screens))
current_title_screen = pick(title_screens)
else
current_title_screen = DEFAULT_TITLE_SCREEN_IMAGE
check_finish_progress()
show_title_screen()
/**
* Update a user's character setup name.
* Arguments:
* * user - The user being updated
* * name - the real name of the current slot.
*/
/datum/controller/subsystem/title/proc/update_character_name(mob/dead/new_player/user, name)
if(!(istype(user) && user.title_screen_is_ready))
return
user.client << output(name, "title_browser:update_current_character")
/**
* Adds a startup message to the splashscreen.
*
* Arguments:
* * msg - the message to show users.
* * warning - optional: TRUE to indicate this is an error/warning
*/
/proc/add_startup_message(msg, warning)
// Remove the # second(s) / #s part of the message.
var/static/regex/msg_key_regex = new(@"[0-9.]+( second)?s?!", "ig")
// HTML displayed to user
var/msg_html = {"<p class="terminal_text">[warning ? "☒ " : ""][msg]</p>"}
// Key used to cache the timing info
var/msg_key = msg_key_regex.Replace(msg, "#")
GLOB.startup_messages += msg_html
// If we ran before SStitle initialized, set the ref time now.
SStitle.check_progress_reference_time()
// Add or update message history info.
var/old_timing = SStitle.startup_message_timings[msg_key]
var/new_timing
if(!old_timing)
// new message
new_timing = world.timeofday - SStitle.progress_reference_time
else
// old message. Latest time is worth 1/4 the "average"
new_timing = 0.75 * old_timing + 0.25 * (world.timeofday - SStitle.progress_reference_time)
SStitle.startup_message_timings[msg_key] = new_timing
for(var/mob/dead/new_player/new_player in GLOB.new_player_list)
if(!new_player.title_screen_is_ready)
continue
new_player.client << output(msg_html, "title_browser:append_terminal_text")
new_player.client << output(list2params(list(new_timing, SStitle.average_completion_time)), "title_browser:update_loading_progress")