Files
siliconsandGitHub c07901dbc7 Fixes early transfers, round time is now not going to pass until the round starts (#7525)
we never checked if the round began before calling round time

closes #7524 
closes #7523
2026-02-18 12:40:43 +00:00

259 lines
7.7 KiB
Plaintext

GLOBAL_DATUM_INIT(join_menu, /datum/join_menu, new)
/**
* Global singleton for holding TGUI data for players joining.
*/
/datum/join_menu
/datum/join_menu/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "JoinMenu", "Join Menu")
ui.open()
/datum/join_menu/proc/queue_update()
addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, update_static_data)), 0, TIMER_UNIQUE | TIMER_OVERRIDE)
/datum/join_menu/ui_state()
return GLOB.explicit_new_player_state
/datum/join_menu/ui_static_data(mob/user, datum/tgui/ui)
var/list/data = ..()
// every entry will have:
// - faction
// - department
// - title, and a description
// faction dropdown --> department dropdown --> title (slots left) with join button and dropdown for description
// ghostroles will be considered a ""faction"" but will use a special list.
var/mob/new_player/N = user
if(!istype(N))
return // nice one lmao
// generate job list
var/list/jobs = list()
data["jobs"] = jobs
// collect
var/list/datum/prototype/role/job/eligible = list()
for(var/title in RSroles.legacy_all_job_titles())
var/datum/prototype/role/job/J = RSroles.legacy_job_by_title(title)
if(!(J.join_types & JOB_LATEJOIN))
continue
if(!IsJobAvailable(J, N))
continue
eligible += J
// make
for(var/datum/prototype/role/job/J as anything in eligible) // already type filtered
// faction
var/list/faction
if(!jobs[J.faction])
jobs[J.faction] = faction = list()
else
faction = jobs[J.faction]
// department
var/list/department
// todo: this is awful
var/department_name = LAZYACCESS(J.departments, 1)
department_name = capitalize(department_name)
if(!jobs[J.faction][department_name])
jobs[J.faction][department_name] = department = list()
else
department = jobs[J.faction][department_name]
// finally, add job data
var/slots = J.slots_remaining(TRUE)
var/list/job_data = list(
"id" = J.id,
"name" = EffectiveTitle(J, N),
"desc" = EffectiveDesc(J, N),
"slots" = slots == INFINITY? -1 : slots,
"real_name" = J.title
)
department += list(job_data) // wrap list
// generate ghostrole list
var/list/ghostroles = list()
data["ghostroles"] = ghostroles
for(var/id in GLOB.ghostroles)
var/datum/prototype/role/ghostrole/R = GLOB.ghostroles[id]
// can't afford runtime here
if(!istype(R) || !IsGhostroleAvailable(R, N))
continue
var/slots = R.SpawnsLeft(user.client)
var/list/ghostrole_data = list(
"id" = id,
"name" = R.name,
"desc" = R.desc,
"slots" = slots == INFINITY? -1 : slots
)
ghostroles += list(ghostrole_data) // wrap list
return data
/datum/join_menu/ui_data(mob/user, datum/tgui/ui)
var/list/data = ..()
// common info goes into ui data
var/level = "green"
switch(GLOB.security_level)
if(SEC_LEVEL_BLUE)
level = "blue";
if(SEC_LEVEL_ORANGE)
level = "orange";
if(SEC_LEVEL_VIOLET)
level = "violet";
if(SEC_LEVEL_YELLOW)
level = "yellow";
if(SEC_LEVEL_GREEN)
level = "green";
if(SEC_LEVEL_RED)
level = "red";
else
level = "delta";
data["security_level"] = level
// todo: have js render this, not us
data["duration"] = DisplayTimeText(round(STATION_TIME_PASSED(), 10))
// 0 = not evaccing, 1 = evacuating, 2 = crew transfer, 3 = evacuated
var/evac = 0
if(SSemergencyshuttle.going_to_centcom())
evac = 3
else if(SSemergencyshuttle.online())
if(SSemergencyshuttle.evac)
evac = 1
else
evac = 2
data["evacuated"] = evac
data["charname"] = user.client?.prefs?.real_name || "Unknown User"
// position in queue, -1 for not queued, null for no queue active, otherwise number
// if -1, ui should present option to queue
// if null, ui shouldn't show queue at all or say "there is no queue" etc etc
data["queue"] = QueueStatus(user)
return data
/**
* checks if job is available
* if not, it shouldn't even show
*/
/datum/join_menu/proc/IsJobAvailable(datum/prototype/role/job/J, mob/new_player/N)
return J.check_client_availability_one(N.client, TRUE, TRUE) == ROLE_AVAILABLE
/**
* checks if ghostrole is available
* if not, it shouldn't even show
*/
/datum/join_menu/proc/IsGhostroleAvailable(datum/prototype/role/ghostrole/G, mob/new_player/N)
return G.AllowSpawn(N.client)
/**
* return effective title - used for alt titles - JOBS ONLY, not ghostroles
*/
/datum/join_menu/proc/EffectiveTitle(datum/prototype/role/job/J, mob/new_player/N)
return N.client.prefs.get_job_alt_title_name(J) || J.title
/**
* returns effective desc - used for alt titles - JOBS ONLY, not ghostroles
*/
/datum/join_menu/proc/EffectiveDesc(datum/prototype/role/job/J, mob/new_player/N)
var/title = N.client.prefs.get_job_alt_title_name(J)
var/datum/prototype/struct/alt_title/T = J.alt_titles?[title]
return isnull(T)? J.desc : (initial(T.title_blurb) || J.desc)
/datum/join_menu/proc/QueueStatus(mob/new_player/N)
return null
/*
/datum/join_menu/proc/QueueStatus(mob/new_player/N)
QueueActive()? (SSticker.queued_players.Find(N) || -1) : null
*/
/datum/join_menu/proc/QueueActive()
return FALSE
/*
/datum/join_menu/proc/QueueActive()
var/relevant_cap = PopCap()
return length(SSticker.queued_players) || (relevant_cap && living_player_count() > relevant_cap)
*/
/datum/join_menu/proc/PopCap()
return INFINITY
/*
/datum/join_menu/proc/PopCap()
. = null
var/hpc = CONFIG_GET(number/hard_popcap)
var/epc = CONFIG_GET(number/extreme_popcap)
if(hpc && epc)
. = min(hpc, epc)
else
. = max(hpc, epc)
*/
/datum/join_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
var/mob/new_player/N = usr
if(!istype(N))
to_chat(usr, SPAN_DANGER("You are not in the lobby."))
return
switch(action)
if("join")
if(!SSticker || !SSticker.IsRoundInProgress())
to_chat(usr, SPAN_DANGER("The round is either not ready, or has already finished..."))
return
if(!AttemptQueue(usr))
return
var/id = params["id"]
if(!id)
return
switch(params["type"])
if("job")
if(!config_legacy.enter_allowed)
to_chat(usr, SPAN_NOTICE("There is an administrative lock on entering the game."))
return
var/datum/prototype/role/job/J = RSroles.legacy_job_by_id(id)
if(!J)
to_chat(usr, SPAN_WARNING("Failed to find job [id]."))
return
to_chat(usr, SPAN_NOTICE("Attempting to latespawn as [id] ([J.title])."))
N.AttemptLateSpawn(J.title) // todo: remove shim
if("ghostrole")
var/datum/prototype/role/ghostrole/R = get_ghostrole_datum(id)
if(!R)
to_chat(usr, SPAN_WARNING("Failed to find ghostrole [R]"))
return
to_chat(usr, SPAN_WARNING("Attempting to join as ghostrole [id] ([R.name])."))
N.close_spawn_windows()
var/client/C = N.client
var/error = R.AttemptSpawn(C)
if(istext(error))
to_chat(C, SPAN_DANGER("[error]"))
if("queue")
return
// AttemptQueue(usr)
/**
* Return FALSE to block joining.
*/
/datum/join_menu/proc/AttemptQueue(mob/new_player/N)
. = TRUE
/*
if(QueueActive() && !(ckey(N.key) in GLOB.admin_datums))
var/queue_position = SSticker.queued_players.Find(usr)
if(queue_position == 1)
if(living_player_count() < CONFIG_GET(number/hard_popcap))
return TRUE
to_chat(usr, SPAN_NOTICE("You are next in line to join the game. You will be notified when a slot opens up."))
return FALSE
else
to_chat(usr, SPAN_DANGER("[CONFIG_GET(string/hard_popcap_message)]"))
if(queue_position)
to_chat(usr, SPAN_NOTICE("There are [queue_position-1] players in front of you in the queue to join the game."))
return FALSE
else
SSticker.queued_players += usr
to_chat(usr, SPAN_NOTICE("You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len]."))
return FALSE
*/
/mob/new_player/proc/LateChoices()
GLOB.join_menu.ui_interact(src)