Updates latejoin menu to use TGUI (#17309)

* First work

* Add job icons

* Update FA


2


3

* Add job descriptions

* Fix tooltip color

* Misc changes

* Fix unavailable reason and random job button

* Real fix

* Remove double import

* huh

* Use tgui_alert
This commit is contained in:
Ling
2023-01-05 13:40:50 +01:00
committed by GitHub
parent ae7ae9daec
commit 3c23470219
61 changed files with 601 additions and 123 deletions

View File

@@ -51,6 +51,9 @@
#define JOB_UNAVAILABLE_ACCOUNTAGE 4
#define JOB_UNAVAILABLE_SLOTFULL 5
/// Used when the `get_job_unavailable_error_message` proc can't make sense of a given code.
#define GENERIC_JOB_UNAVAILABLE_ERROR "Error: Unknown job availability."
#define DEFAULT_RELIGION "Christianity"
#define DEFAULT_DEITY "Space Jesus"
@@ -123,3 +126,5 @@
#define IS_SCIENCE(target) (find_job(target) in GLOB.science_positions)
#define IS_CARGO(target) (find_job(target) in GLOB.supply_positions)
#define IS_SECURITY(target) (find_job(target) in GLOB.security_positions)
#define DEPARTMENT_UNASSIGNED "No Department"

View File

@@ -474,17 +474,17 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/proc/check_queue()
if(!queued_players.len)
return
var/hpc = CONFIG_GET(number/hard_popcap)
var/hard_popcap = CONFIG_GET(number/hard_popcap)
//yogs start -- fixes queue when extreme is set but not hard
if(!hpc)
hpc = CONFIG_GET(number/extreme_popcap)
if(!hard_popcap)
hard_popcap = CONFIG_GET(number/extreme_popcap)
//yogs end
if(!hpc)
if(!hard_popcap)
listclearnulls(queued_players)
for (var/mob/dead/new_player/NP in queued_players)
to_chat(NP, span_userdanger("The alive players limit has been released!<br><a href='?src=[REF(NP)];late_join=override'>[html_encode(">>Join Game<<")]</a>"))
SEND_SOUND(NP, sound('sound/misc/notice1.ogg'))
NP.LateChoices()
for (var/mob/dead/new_player/new_player in queued_players)
to_chat(new_player, span_userdanger("The alive players limit has been released!<br><a href='?src=[REF(new_player)];late_join=override'>[html_encode(">>Join Game<<")]</a>"))
SEND_SOUND(new_player, sound('sound/misc/notice1.ogg'))
GLOB.latejoin_menu.ui_interact(new_player)
queued_players.len = 0
queue_delay = 0
return
@@ -495,11 +495,11 @@ SUBSYSTEM_DEF(ticker)
switch(queue_delay)
if(5) //every 5 ticks check if there is a slot available
listclearnulls(queued_players)
if(living_player_count() < hpc)
if(living_player_count() < hard_popcap)
if(next_in_line && next_in_line.client)
to_chat(next_in_line, span_userdanger("A slot has opened! You have approximately 20 seconds to join. <a href='?src=[REF(next_in_line)];late_join=override'>\>\>Join Game\<\<</a>"))
SEND_SOUND(next_in_line, sound('sound/misc/notice1.ogg'))
next_in_line.LateChoices()
next_in_line.ui_interact(next_in_line)
return
queued_players -= next_in_line //Client disconnected, remove he
queue_delay = 0 //No vacancy: restart timer

View File

@@ -158,10 +158,9 @@
/datum/asset/simple/namespaced/fontawesome
legacy = TRUE
assets = list(
"fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
"fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
"fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot',
"fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff',
"fa-regular-400.ttf" = 'html/font-awesome/webfonts/fa-regular-400.ttf',
"fa-solid-900.ttf" = 'html/font-awesome/webfonts/fa-solid-900.ttf',
"fa-v4compatibility.ttf" = 'html/font-awesome/webfonts/fa-v4compatibility.ttf',
"v4shim.css" = 'html/font-awesome/css/v4-shims.min.css'
)
parents = list("font-awesome.css" = 'html/font-awesome/css/all.min.css')

View File

@@ -1,6 +1,9 @@
/datum/job
/// The name of the job used for preferences, bans, etc.
var/title = "NOPE"
/// The description of the job, used for preferences menu.
/// Keep it short and useful. Avoid in-jokes, these are for new players.
var/description
/// This job comes with these accesses by default
var/list/base_access = list()
/// Additional accesses for the job if config.jobs_have_minimal_access is set to false
@@ -68,6 +71,9 @@
///The text a person using olfaction will see for the job of the target's scent
var/smells_like = "a freeloader"
/// Icons to be displayed in the orbit ui. Source: FontAwesome v5.
var/orbit_icon
/*
If you want to change a job on a specific map with this system, you will want to go onto that job datum
and add said map's name to the changed_maps list, like so:

View File

@@ -1,6 +1,8 @@
/datum/job/ai
title = "AI"
description = "Assist the crew, follow your laws, coordinate your cyborgs."
flag = AI_JF
orbit_icon = "eye"
auto_deadmin_role_flags = DEADMIN_POSITION_SILICON|DEADMIN_POSITION_CRITICAL
department_flag = ENGSEC
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/artist
title = "Artist"
description = "Create unique pieces of art for display by the crew around the station."
flag = ARTIST
orbit_icon = "paintbrush"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -3,7 +3,9 @@ Assistant
*/
/datum/job/assistant
title = "Assistant"
description = "Get your space legs, assist people, ask the HoP to give you a job."
flag = ASSISTANT
orbit_icon = "toolbox"
department_flag = CIVILIAN
faction = "Station"
total_positions = 5

View File

@@ -1,6 +1,8 @@
/datum/job/atmos
title = "Atmospheric Technician"
description = "Ensure the air is breathable on the station, fill oxygen tanks, fight fires, purify the air."
flag = ATMOSTECH
orbit_icon = "fire-extinguisher"
department_head = list("Chief Engineer")
department_flag = ENGSEC
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/bartender
title = "Bartender"
description = "Serve booze, mix drinks, keep the crew drunk."
flag = BARTENDER
orbit_icon = "cocktail"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/hydro
title = "Botanist"
description = "Grow plants for the cook, for medicine, and for recreation."
flag = BOTANIST
orbit_icon = "seedling"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,10 @@
/datum/job/captain
title = "Captain"
description = "Be responsible for the station, manage your Heads of Staff, \
keep the crew alive, be prepared to do anything and everything or die \
horribly trying."
flag = CAPTAIN
orbit_icon = "crown"
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD|DEADMIN_POSITION_SECURITY|DEADMIN_POSITION_CRITICAL
department_head = list("CentCom")
department_flag = ENGSEC

View File

@@ -1,6 +1,10 @@
/datum/job/cargo_tech
title = "Cargo Technician"
description = "Distribute supplies to the departments that ordered them, \
collect empty crates, load and unload the supply shuttle, \
ship bounty cubes."
flag = CARGOTECH
orbit_icon = "box"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/chaplain
title = "Chaplain"
description = "Hold services and funerals, cremate people, preach your \
religion, protect the crew against cults."
flag = CHAPLAIN
orbit_icon = "cross"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/chemist
title = "Chemist"
description = "Supply the doctors with chemicals, make medicine, as well as \
less likable substances in the comfort of a fully reinforced room."
flag = CHEMIST
orbit_icon = "prescription-bottle"
department_head = list("Chief Medical Officer")
department_flag = MEDSCI
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/chief_engineer
title = "Chief Engineer"
description = "Coordinate engineering, ensure equipment doesn't get stolen, \
make sure the Supermatter doesn't blow up, maintain telecommunications."
flag = CHIEF
orbit_icon = "user-astronaut"
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
department_head = list("Captain")
department_flag = ENGSEC

View File

@@ -1,6 +1,9 @@
/datum/job/cmo
title = "Chief Medical Officer"
description = "Coordinate doctors and other medbay employees, ensure they \
know how to save lives, check for injuries on the crew monitor."
flag = CMO_JF
orbit_icon = "user-md"
department_head = list("Captain")
department_flag = MEDSCI
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD

View File

@@ -1,6 +1,8 @@
/datum/job/clown
title = "Clown"
description = "Entertain the crew, make bad jokes, go on a holy quest to find bananium, HONK!"
flag = CLOWN
orbit_icon = "face-grin-tears"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/cook
title = "Cook"
description = "Serve food, cook meat, keep the crew fed."
flag = COOK
orbit_icon = "utensils"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/curator
title = "Curator"
description = "Read and write books and hand them to people, stock \
bookshelves, report on station news."
flag = CURATOR
orbit_icon = "book"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/cyborg
title = "Cyborg"
description = "Assist the crew, follow your laws, obey your AI."
flag = CYBORG
orbit_icon = "robot"
auto_deadmin_role_flags = DEADMIN_POSITION_SILICON
department_flag = ENGSEC
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/detective
title = "Detective"
description = "Investigate crimes, gather evidence, perform interrogations, \
look badass, smoke cigarettes."
flag = DETECTIVE
orbit_icon = "user-secret"
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
department_head = list("Head of Security")
department_flag = ENGSEC

View File

@@ -1,6 +1,8 @@
/datum/job/geneticist
title = "Geneticist"
description = "Alter genomes, turn monkeys into humans (and vice-versa), and make DNA backups."
flag = GENETICIST
orbit_icon = "dna"
department_head = list("Chief Medical Officer", "Research Director")
department_flag = MEDSCI
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/hop
title = "Head of Personnel"
description = "Alter access on ID cards, manage civil and supply departments, \
protect Ian, run the station when the captain dies."
flag = HOP
orbit_icon = "dog"
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
department_head = list("Captain")
department_flag = CIVILIAN

View File

@@ -1,6 +1,9 @@
/datum/job/hos
title = "Head of Security"
description = "Coordinate security personnel, ensure they are not corrupt, \
make sure every department is protected."
flag = HOS
orbit_icon = "user-shield"
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD|DEADMIN_POSITION_SECURITY|DEADMIN_POSITION_CRITICAL
department_head = list("Captain")
department_flag = ENGSEC

View File

@@ -1,6 +1,8 @@
/datum/job/janitor
title = "Janitor"
description = "Clean up trash and blood, replace broken lights and slip people over."
flag = JANITOR
orbit_icon = "broom"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/lawyer
title = "Lawyer"
description = "Advocate for prisoners, create law-binding contracts, \
ensure Security is following protocol and Space Law."
flag = LAWYER
orbit_icon = "gavel"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/doctor
title = "Medical Doctor"
description = "Save lives, run around the station looking for victims, \
scan everyone in sight"
flag = DOCTOR
orbit_icon = "staff-snake"
department_head = list("Chief Medical Officer")
department_flag = MEDSCI
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/mime
title = "Mime"
description = "..."
flag = MIME
orbit_icon = "comment-slash"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/qm
title = "Quartermaster"
description = "Coordinate cargo technicians and shaft miners, assist with \
economical purchasing."
flag = QUARTERMASTER
orbit_icon = "sack-dollar"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,10 @@
/datum/job/rd
title = "Research Director"
description = "Supervise research efforts, ensure Robotics is in working \
order, make sure the AI and its Cyborgs aren't rogue, replacing them if \
they are"
flag = RD_JF
orbit_icon = "user-graduate"
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
department_head = list("Captain")
department_flag = MEDSCI

View File

@@ -1,6 +1,8 @@
/datum/job/roboticist
title = "Roboticist"
description = "Build and repair the AI and cyborgs, create mechs."
flag = ROBOTICIST
orbit_icon = "battery-half"
department_head = list("Research Director")
department_flag = MEDSCI
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/scientist
title = "Scientist"
description = "Do experiments, perform research, feed the slimes, make bombs."
flag = SCIENTIST
orbit_icon = "flask"
department_head = list("Research Director")
department_flag = MEDSCI
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/officer
title = "Security Officer"
description = "Protect company assets, follow Space Law\
, eat donuts."
flag = OFFICER
orbit_icon = "shield-halved"
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
department_head = list("Head of Security")
department_flag = ENGSEC

View File

@@ -1,6 +1,9 @@
/datum/job/mining
title = "Shaft Miner"
description = "Travel to strange lands. Mine ores. \
Meet strange creatures. Kill them for their gold."
flag = MINER
orbit_icon = "digging"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/engineer
title = "Station Engineer"
description = "Start the Supermatter, wire the solars, repair station hull \
and wiring damage."
flag = ENGINEER
orbit_icon = "gears"
department_head = list("Chief Engineer")
department_flag = ENGSEC
faction = "Station"

View File

@@ -1,6 +1,9 @@
/datum/job/virologist
title = "Virologist"
description = "Study the effects of various diseases and synthesize a \
vaccine for them. Engineer beneficial viruses."
flag = VIROLOGIST
orbit_icon = "virus"
department_head = list("Chief Medical Officer")
department_flag = MEDSCI
faction = "Station"

View File

@@ -1,6 +1,10 @@
/datum/job/warden
title = "Warden"
description = "Watch over the Brig and Prison Wing, release prisoners when \
their time is up, issue equipment to security, be a security officer when \
they all eventually die."
flag = WARDEN
orbit_icon = "handcuffs"
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
department_head = list("Head of Security")
department_flag = ENGSEC

View File

@@ -0,0 +1,208 @@
#define JOB_CHOICE_YES "Yes"
#define JOB_CHOICE_REROLL "Reroll"
#define JOB_CHOICE_CANCEL "Cancel"
GLOBAL_DATUM_INIT(latejoin_menu, /datum/latejoin_menu, new)
/// Makes a list of jobs and pushes them to a DM list selector. Just in case someone did a special kind of fucky-wucky with TGUI.
/datum/latejoin_menu/proc/fallback_ui(mob/dead/new_player/user)
var/list/jobs = list()
for(var/datum/job/job as anything in SSjob.occupations)
jobs += job.title
var/input_contents = input(user, "Pick a job to join as:", "Latejoin Job Selection") as null|anything in jobs
if(!input_contents)
return
user.AttemptLateSpawn(input_contents)
/datum/latejoin_menu/ui_close(mob/dead/new_player/user)
. = ..()
if(istype(user))
user.jobs_menu_mounted = TRUE // Don't flood a user's chat if they open and close the UI.
/datum/latejoin_menu/ui_interact(mob/dead/new_player/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
// In case they reopen the GUI
// FIXME: this can cause a runtime since user can be a living mob
if(istype(user))
user.jobs_menu_mounted = FALSE
addtimer(CALLBACK(src, PROC_REF(scream_at_player), user), 5 SECONDS)
ui = new(user, src, "JobSelection", "Latejoin Menu")
ui.open()
/datum/latejoin_menu/proc/scream_at_player(mob/dead/new_player/player)
if(!player.jobs_menu_mounted)
to_chat(player, span_notice("If the late join menu isn't showing, hold CTRL while clicking the join button!"))
/datum/latejoin_menu/ui_data(mob/user)
var/mob/dead/new_player/owner = user
var/list/departments = list()
var/list/data = list(
"disable_jobs_for_non_observers" = SSticker.late_join_disabled,
"round_duration" = DisplayTimeText(world.time - SSticker.round_start_time, round_seconds_to = 1),
"departments" = departments,
)
if(SSshuttle.emergency)
switch(SSshuttle.emergency.mode)
if(SHUTTLE_ESCAPE)
data["shuttle_status"] = "The station has been evacuated."
if(SHUTTLE_CALL, SHUTTLE_DOCKED, SHUTTLE_IGNITING, SHUTTLE_ESCAPE)
if(!SSshuttle.canRecall())
data["shuttle_status"] = "The station is currently undergoing evacuation procedures."
for(var/datum/job/prioritized_job in SSjob.prioritized_jobs)
if(prioritized_job.current_positions >= prioritized_job.total_positions)
SSjob.prioritized_jobs -= prioritized_job
for(var/list/category in list(GLOB.command_positions) + list(GLOB.engineering_positions) + list(GLOB.supply_positions) + list(GLOB.nonhuman_positions - "pAI") + list(GLOB.civilian_positions) + list(GLOB.science_positions) + list(GLOB.security_positions) + list(GLOB.medical_positions) )
var/cat_name = SSjob.name_occupations_all[category[1]].exp_type_department
var/list/department_jobs = list()
var/list/department_data = list(
"jobs" = department_jobs,
"open_slots" = 0,
)
departments[cat_name] = department_data
for(var/job in category)
var/datum/job/job_datum = SSjob.name_occupations[job]
if (!job_datum)
continue
var/job_availability = owner.IsJobUnavailable(job_datum.title, latejoin = TRUE)
var/list/job_data = list(
"prioritized" = (job_datum in SSjob.prioritized_jobs),
"used_slots" = job_datum.current_positions,
"open_slots" = job_datum.total_positions < 0 ? "∞" : job_datum.total_positions,
)
if(job_availability != JOB_AVAILABLE)
job_data["unavailable_reason"] = get_job_unavailable_error_message(job_availability, job_datum.title)
if(job_datum.total_positions < 0)
department_data["open_slots"] = "∞"
if(department_data["open_slots"] != "∞")
if(job_datum.total_positions - job_datum.current_positions > 0)
department_data["open_slots"] += job_datum.total_positions - job_datum.current_positions
department_jobs[job_datum.title] = job_data
return data
/datum/latejoin_menu/ui_static_data(mob/user)
var/list/departments = list()
for(var/list/category in list(GLOB.command_positions) + list(GLOB.engineering_positions) + list(GLOB.supply_positions) + list(GLOB.nonhuman_positions - "pAI") + list(GLOB.civilian_positions) + list(GLOB.science_positions) + list(GLOB.security_positions) + list(GLOB.medical_positions) )
var/cat_color = SSjob.name_occupations_all[category[1]].selection_color
var/cat_name = SSjob.name_occupations_all[category[1]].exp_type_department
var/list/department_jobs = list()
var/list/department_data = list(
"jobs" = department_jobs,
"color" = cat_color,
)
departments[cat_name] = department_data
for(var/job in category)
var/datum/job/job_datum = SSjob.name_occupations[job]
if (!job_datum)
continue
var/is_command = (job in GLOB.command_positions)
var/list/job_data = list(
"command" = is_command,
"description" = job_datum.description,
"icon" = job_datum.orbit_icon,
)
department_jobs[job_datum.title] = job_data
return list("departments_static" = departments)
// we can't use GLOB.new_player_state here since it also allows any admin to see the ui, which will cause runtimes
/datum/latejoin_menu/ui_status(mob/user)
return isnewplayer(user) ? UI_INTERACTIVE : UI_CLOSE
/datum/latejoin_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(!ui.user.client || !isnewplayer(ui.user))
return TRUE
var/mob/dead/new_player/owner = ui.user
switch(action)
if("ui_mounted_with_no_bluescreen")
owner.jobs_menu_mounted = TRUE
if("select_job")
if(params["job"] == "Random")
var/job = get_random_job(owner)
if(!job)
tgui_alert(owner, "There is no randomly assignable job at this time. Please manually choose one of the other possible options.")
return TRUE
params["job"] = job
if(!SSticker?.IsRoundInProgress())
tgui_alert(owner, "The round is either not ready, or has already finished...", "Oh No!")
return TRUE
if(!GLOB.enter_allowed || SSticker.late_join_disabled)
tgui_alert(owner, "There is an administrative lock on entering the game for non-observers!", "Oh No!")
return TRUE
//Determines Relevent Population Cap
var/relevant_cap
var/hard_popcap = CONFIG_GET(number/hard_popcap)
var/extreme_popcap = CONFIG_GET(number/extreme_popcap)
if(hard_popcap && extreme_popcap)
relevant_cap = min(hard_popcap, extreme_popcap)
else
relevant_cap = max(hard_popcap, extreme_popcap)
if(SSticker.queued_players.len)
if((living_player_count() >= relevant_cap) || (owner != SSticker.queued_players[1]))
tgui_alert(owner, "The server is full!", "Oh No!")
return TRUE
// SAFETY: AttemptLateSpawn has it's own sanity checks. This is perfectly safe.
owner.AttemptLateSpawn(params["job"])
return TRUE
/// Gives the user a random job that they can join as, and prompts them if they'd actually like to keep it, rerolling if not. Cancellable by the user.
/// WARNING: BLOCKS THREAD!
/datum/latejoin_menu/proc/get_random_job(mob/dead/new_player/owner)
var/attempts = 0
while(TRUE)
if (attempts > 10)
// Give it a few attempts before giving up
return
var/datum/job/random_job = SSjob.GetRandomJob(owner)
if (!random_job)
return
if (owner.IsJobUnavailable(random_job.title, latejoin = TRUE) != JOB_AVAILABLE)
attempts++
continue
attempts = 0
var/list/random_job_options = list(JOB_CHOICE_YES, JOB_CHOICE_REROLL, JOB_CHOICE_CANCEL)
var/choice = tgui_alert(owner, "Do you want to play as \a [random_job.title]?", "Random Job", random_job_options)
if(choice == JOB_CHOICE_CANCEL)
return
if(choice == JOB_CHOICE_YES)
return random_job.title
#undef JOB_CHOICE_YES
#undef JOB_CHOICE_REROLL
#undef JOB_CHOICE_CANCEL

View File

@@ -16,6 +16,9 @@
//Used to make sure someone doesn't get spammed with messages if they're ineligible for roles
var/ineligible_for_roles = FALSE
/// Used to track if the player's jobs menu sent a message saying it successfully mounted.
var/jobs_menu_mounted = FALSE
/mob/dead/new_player/Initialize()
if(client && SSticker.state == GAME_STATE_STARTUP)
var/atom/movable/screen/splash/S = new(client, TRUE, TRUE)
@@ -140,14 +143,14 @@
return
if(href_list["late_join"] == "override")
LateChoices()
GLOB.latejoin_menu.ui_interact(src)
return
if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(key) in GLOB.permissions.admin_datums)))
//yogs start -- donors bypassing the queue
if(ckey(key) in get_donators())
to_chat(usr, span_notice("Because you are a donator, you have bypassed the queue! Thank you for donating!"))
LateChoices()
GLOB.latejoin_menu.ui_interact(src)
return
//yogs end
to_chat(usr, span_danger("[CONFIG_GET(string/hard_popcap_message)]"))
@@ -161,37 +164,14 @@
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
LateChoices()
// TODO: Fallback menu
GLOB.latejoin_menu.ui_interact(usr)
if(href_list["manifest"])
ViewManifest()
if(href_list["SelectedJob"])
if(!SSticker || !SSticker.IsRoundInProgress())
to_chat(usr, span_danger("The round is either not ready, or has already finished..."))
return
if(!GLOB.enter_allowed)
to_chat(usr, span_notice("There is an administrative lock on entering the game!"))
return
if(SSticker.queued_players.len && !(ckey(key) in GLOB.permissions.admin_datums))
if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1]))
to_chat(usr, span_warning("Server is full."))
return
// Check if random role is requested
if(href_list["SelectedJob"] == "Random")
var/datum/job/job = SSjob.GetRandomJob(src)
if(!job)
to_chat(usr, span_warning("There is no randomly assignable Job at this time. Please manually choose one of the other possible options."))
return
href_list["SelectedJob"] = job.title
AttemptLateSpawn(href_list["SelectedJob"])
return
else if(!href_list["late_join"])
new_player_panel()
@@ -332,7 +312,8 @@
return "Your account is not old enough for [jobtitle]."
if(JOB_UNAVAILABLE_SLOTFULL)
return "[jobtitle] is already filled to capacity."
return "Error: Unknown job availability."
return GENERIC_JOB_UNAVAILABLE_ERROR
/mob/dead/new_player/proc/IsJobUnavailable(rank, latejoin = FALSE)
var/datum/job/job = SSjob.GetJob(rank)
@@ -452,60 +433,8 @@
employmentCabinet.addFile(employee)
/mob/dead/new_player/proc/LateChoices()
var/list/dat = list("<div class='notice'>Round Duration: [DisplayTimeText(world.time - SSticker.round_start_time)]</div>")
if(SSshuttle.emergency)
switch(SSshuttle.emergency.mode)
if(SHUTTLE_ESCAPE)
dat += "<div class='notice red'>The station has been evacuated.</div><br>"
if(SHUTTLE_CALL)
if(!SSshuttle.canRecall())
dat += "<div class='notice red'>The station is currently undergoing evacuation procedures.</div><br>"
for(var/datum/job/prioritized_job in SSjob.prioritized_jobs)
if(prioritized_job.current_positions >= prioritized_job.total_positions)
SSjob.prioritized_jobs -= prioritized_job
dat += "<table><tr><td valign='top'>"
var/column_counter = 0
for(var/list/category in list(GLOB.command_positions) + list(GLOB.engineering_positions) + list(GLOB.supply_positions) + list(GLOB.nonhuman_positions - "pAI") + list(GLOB.civilian_positions) + list(GLOB.science_positions) + list(GLOB.security_positions) + list(GLOB.medical_positions) )
var/cat_color = SSjob.name_occupations_all[category[1]].selection_color
dat += "<fieldset style='width: 185px; border: 2px solid [cat_color]; display: inline'>"
dat += "<legend align='center' style='color: [cat_color]'>[SSjob.name_occupations_all[category[1]].exp_type_department]</legend>"
var/list/dept_dat = list()
for(var/job in category)
var/datum/job/job_datum = SSjob.name_occupations[job]
if(job_datum && IsJobUnavailable(job_datum.title, TRUE) == JOB_AVAILABLE)
var/command_bold = ""
if(job in GLOB.command_positions)
command_bold = " command"
if(job_datum in SSjob.prioritized_jobs)
dept_dat += "<a class='job[command_bold]' href='byond://?src=[REF(src)];SelectedJob=[job_datum.title]'>[span_priority("[job_datum.title] ([job_datum.current_positions])")]</a>"
else
dept_dat += "<a class='job[command_bold]' href='byond://?src=[REF(src)];SelectedJob=[job_datum.title]'>[job_datum.title] ([job_datum.current_positions])</a>"
if(!dept_dat.len)
dept_dat += span_nopositions("No positions open.")
dat += jointext(dept_dat, "")
dat += "</fieldset><br>"
column_counter++
if(column_counter > 0 && (column_counter % 3 == 0))
dat += "</td><td valign='top'>"
// Random Job Section
dat += "<fieldset style='width: 185px; border: 2px solid #f0ebe2; display: inline'>"
dat += "<legend align='center' style='color: #f0ebe2'>Random</legend>"
dat += "<a class='job' href='byond://?src=[REF(src)];SelectedJob=Random'>Random Job</a>"
// TODO could add random job selection to be based on player preferences too
dat += "</fieldset><br>"
// Table end
dat += "</td></tr></table></center>"
dat += "</div></div>"
var/datum/browser/popup = new(src, "latechoices", "Choose Profession", 680, 580)
popup.add_stylesheet("playeroptions", 'html/browser/playeroptions.css')
popup.set_content(jointext(dat, ""))
popup.open(FALSE) // 0 is passed to open so that it doesn't use the onclose() proc
/mob/dead/new_player/proc/create_character(transfer_after)
spawning = 1
spawning = TRUE
close_spawn_windows()
var/mob/living/carbon/human/H = new(loc)
@@ -571,12 +500,9 @@
/mob/dead/new_player/proc/close_spawn_windows()
src << browse(null, "window=latechoices") //closes late choices window
src << browse(null, "window=playersetup") //closes the player setup window
src << browse(null, "window=preferences") //closes job selection
src << browse(null, "window=mob_occupation")
src << browse(null, "window=latechoices") //closes late job selection
// Used to make sure that a player has a valid job preference setup, used to knock players out of eligibility for anything if their prefs don't make sense.
// A "valid job preference setup" in this situation means at least having one job set to low, or not having "return to lobby" enabled

View File

@@ -725,7 +725,7 @@ GLOBAL_LIST_EMPTY(vending_products)
.["user"]["department"] = C.registered_account.account_job.paycheck_department
else
.["user"]["job"] = "No Job"
.["user"]["department"] = "No Department"
.["user"]["department"] = DEPARTMENT_UNASSIGNED
.["stock"] = list()
for (var/datum/data/vending_product/R in product_records + coin_records + hidden_records)
.["stock"][R.name] = R.amount

View File

@@ -1,6 +1,6 @@
Due to the fact browse_rsc can't create subdirectories, every time you update font-awesome you'll need to change relative webfont references in all.min.css
eg ../webfonts/fa-regular-400.ttf => fa-regular-400.ttf (or whatever you call it in asset datum)
Second change is ripping out file types other than woff and eot(ie8) from the css
Second change is ripping out file types other than ~~ woff and eot(ie8)~~ ttf from the css
Finally, removing brand related css.
Finally, removing brand related css.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -269,3 +269,25 @@ export const zip = (...arrays) => {
export const zipWith = iterateeFn => (...arrays) => {
return map(values => iterateeFn(...values))(zip(...arrays));
};
const isObject = (obj: unknown) => typeof obj === 'object' && obj !== null;
// Does a deep merge of two objects. DO NOT FEED CIRCULAR OBJECTS!!
export const deepMerge = (...objects: any[]): any => {
const target = {};
for (const object of objects) {
for (const key of Object.keys(object)) {
const targetValue = target[key];
const objectValue = object[key];
if (Array.isArray(targetValue) && Array.isArray(objectValue)) {
target[key] = [...targetValue, ...objectValue];
} else if (isObject(targetValue) && isObject(objectValue)) {
target[key] = deepMerge(targetValue, objectValue);
} else {
target[key] = objectValue;
}
}
}
return target;
};

View File

@@ -17,6 +17,23 @@ export class Color {
toString() {
return `rgba(${this.r | 0}, ${this.g | 0}, ${this.b | 0}, ${this.a | 0})`;
}
// Darkens a color by a given percent. Returns a color, which can have toString called to get it's rgba() css value.
darken(percent) {
percent /= 100;
return new Color(
this.r - this.r * percent,
this.g - this.g * percent,
this.b - this.b * percent,
this.a
);
}
// Brightens a color by a given percent. Returns a color, which can have toString called to get it's rgba() css value.
lighten(percent) {
// No point in rewriting code we already have.
return this.darken(-percent);
}
}
/**

View File

@@ -37,8 +37,8 @@ export { RestrictedInput } from './RestrictedInput';
export { RoundGauge } from './RoundGauge';
export { Section } from './Section';
export { Slider } from './Slider';
export { StyleableSection } from './StyleableSection';
export { Stack } from './Stack';
export { StyleableSection } from './StyleableSection';
export { Table } from './Table';
export { Tabs } from './Tabs';
export { TextArea } from './TextArea';

View File

@@ -0,0 +1,200 @@
import { useBackend } from '../backend';
import { Box, Button, StyleableSection, Icon, Stack, NoticeBox } from '../components';
import { Window } from '../layouts';
import { Color } from 'common/color';
import { SFC } from 'inferno';
import { JobToIcon } from './common/JobToIcon';
import { deepMerge } from 'common/collections';
import { BooleanLike } from 'common/react';
type Job = {
unavailable_reason: string | null;
command: BooleanLike;
open_slots: number;
used_slots: number;
icon: string;
prioritized: BooleanLike;
description: string;
};
type Department = {
color: string;
jobs: Record<string, Job>;
open_slots: number;
};
type Data = {
departments_static: Record<string, Department>;
departments: Record<string, Department>;
alert_state: string;
shuttle_status: string;
disable_jobs_for_non_observers: BooleanLike;
priority: BooleanLike;
round_duration: string;
};
export const JobEntry: SFC<{
jobName: string;
job: Job;
department: Department;
onClick: () => void;
}> = (data) => {
const jobName = data.jobName;
const job = data.job;
const department = data.department;
const jobIcon = job.icon || JobToIcon[jobName] || null;
return (
<Button
fluid
style={{
// Try not to think too hard about this one.
'background-color': job.unavailable_reason
? '#949494' // Grey background
: job.prioritized
? '#16fc0f' // Bright green background
: Color.fromHex(department.color)
.darken(10)
.toString(),
'color': job.unavailable_reason
? '#616161' // Dark grey font
: Color.fromHex(department.color)
.darken(90)
.toString(),
'font-size': '1.1rem',
'cursor': job.unavailable_reason ? 'initial' : 'pointer',
}}
tooltip={
job.unavailable_reason ||
(job.prioritized ? (
<>
<p style={{ 'margin-top': '0px' }}>
<b>The station is looking for more personnel to fill this position!</b>
</p>
{job.description}
</>
) : (
job.description
))
}
onClick={() => {
!job.unavailable_reason && data.onClick();
}}>
<>
{jobIcon && <Icon name={jobIcon} />}
{job.command ? <b>{jobName}</b> : jobName}
<span
style={{
'white-space': 'nowrap',
'position': 'absolute',
'right': '0.5em',
}}>
{job.used_slots} / {job.open_slots}
</span>
</>
</Button>
);
};
export const JobSelection = (props, context) => {
const { act, data } = useBackend<Data>(context);
if (!data?.departments_static) {
return null; // Stop TGUI whitescreens with TGUI-dev!
}
const departments: Record<string, Department> = deepMerge(
data.departments,
data.departments_static
);
return (
<Window
width={1012}
height={data.shuttle_status ? 720 : 700}
onComponentDidMount={() => {
// Send a heartbeat back to DM to let it know the window is alive and well
act('ui_mounted_with_no_bluescreen');
}}>
<Window.Content scrollable>
<StyleableSection
title={
<>
{data.shuttle_status && (
<NoticeBox info>{data.shuttle_status}</NoticeBox>
)}
<span style={{ 'color': 'grey' }}>
It is currently {data.round_duration} into the shift.
</span>
<Button
style={{ 'position': 'absolute', 'right': '1em' }}
onClick={() => act('select_job', { 'job': 'Random' })}
content="Random Job!"
tooltip="Roll target random job. You can re-roll or cancel your random job if you don't like it."
/>
</>
}
titleStyle={{ 'min-height': '3.4em' }}>
<Box wrap="wrap" style={{ 'columns': '20em' }}>
{Object.entries(departments).map((departmentEntry) => {
const departmentName = departmentEntry[0];
const entry = departmentEntry[1];
return (
<Box key={departmentName} minWidth="30%">
<StyleableSection
title={
<>
{departmentName}
<span
style={{
'font-size': '1rem',
'white-space': 'nowrap',
'position': 'absolute',
'right': '1em',
'color': Color.fromHex(entry.color)
.darken(60)
.toString(),
}}>
{entry.open_slots +
(entry.open_slots === 1 ? ' slot' : ' slots') +
' available'}
</span>
</>
}
style={{
'background-color': entry.color,
'margin-bottom': '1em',
'break-inside': 'avoid-column',
}}
titleStyle={{
'border-bottom-color': Color.fromHex(entry.color)
.darken(50)
.toString(),
}}
textStyle={{
'color': Color.fromHex(entry.color)
.darken(80)
.toString(),
}}>
<Stack vertical>
{Object.entries(entry.jobs).map((job) => (
<Stack.Item key={job[0]}>
<JobEntry
key={job[0]}
jobName={job[0]}
job={job[1]}
department={entry}
onClick={() => {
act('select_job', { job: job[0] });
}}
/>
</Stack.Item>
))}
</Stack>
</StyleableSection>
</Box>
);
})}
</Box>
</StyleableSection>
</Window.Content>
</Window>
);
};

View File

@@ -0,0 +1,4 @@
// Used to handle jobs that don't have a trim.
export const JobToIcon = {
'Personal AI': 'mobile-alt',
} as const;

View File

@@ -2367,6 +2367,7 @@
#include "code\modules\mob\update_icons.dm"
#include "code\modules\mob\camera\camera.dm"
#include "code\modules\mob\dead\dead.dm"
#include "code\modules\mob\dead\new_player\latejoin_menu.dm"
#include "code\modules\mob\dead\new_player\login.dm"
#include "code\modules\mob\dead\new_player\logout.dm"
#include "code\modules\mob\dead\new_player\new_player.dm"
@@ -3790,7 +3791,6 @@
#include "yogstation\code\modules\jobs\job_types\network_admin.dm"
#include "yogstation\code\modules\jobs\job_types\paramedic.dm"
#include "yogstation\code\modules\jobs\job_types\psychiatrist.dm"
#include "yogstation\code\modules\jobs\job_types\space_bartender.dm"
#include "yogstation\code\modules\jobs\job_types\tourist.dm"
#include "yogstation\code\modules\language\darkspeak.dm"
#include "yogstation\code\modules\language\japanese.dm"

View File

@@ -33,6 +33,6 @@
for (var/mob/dead/new_player/NP in queue)
to_chat(NP, span_userdanger("The alive players limit has been released!<br><a href='?src=[REF(NP)];late_join=override'>[html_encode(">>Join Game<<")]</a>"))
SEND_SOUND(NP, sound('sound/misc/notice1.ogg'))
NP.LateChoices()
GLOB.latejoin_menu.ui_interact(NP)
queue.len = 0
SSticker.queue_delay = 0

View File

@@ -1,6 +1,8 @@
/datum/job/brigphysician
title = "Brig Physician"
description = "Watch over the Brig and Prison Wing to ensure prisoners receive medical attention when needed."
flag = BRIGPHYS
orbit_icon = "suitcase-medical"
department_head = list("Chief Medical Officer")
department_flag = MEDSCI
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/clerk
title = "Clerk"
description = "Set up shop on the station and unique sell trinkets to the crew for a profit."
flag = CLERK
orbit_icon = "basket-shopping"
department_head = list("Head of Personnel")
department_flag = CIVILIAN
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/miningmedic
title = "Mining Medic"
description = "Watch over the Shaft Miners and they all inevitably die in Lavaland."
flag = MMEDIC
orbit_icon = "kit-medical"
department_head = list("Chief Medical Officer")
department_flag = MEDSCI
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/network_admin
title = "Network Admin"
description = "Maintain and upgrade the AI, try not to break radio communications."
flag = NETWORKADMIN
orbit_icon = "satellite-dish"
department_head = list("Chief Engineer", "Research Director")
department_flag = ENGSEC
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/paramedic
title = "Paramedic"
description = "Constantly reminder the crew about their suit sensor. Come to their aid when they die."
flag = PARAMEDIC
orbit_icon = "truck-medical"
department_head = list("Chief Medical Officer")
department_flag = MEDSCI
faction = "Station"

View File

@@ -1,6 +1,8 @@
/datum/job/psych
title = "Psychiatrist"
description = "Diagnose crew members with psychological issues and aid their treatment."
flag = PSYCH
orbit_icon = "brain"
department_head = list("Chief Medical Officer")
department_flag = MEDSCI
faction = "Station"

View File

@@ -1,13 +0,0 @@
/datum/job/bartender/space
title = "Space Bartender"
flag = null
faction = "A cold beer and the outstreched depths of space"
department_head = null
department_flag = null
base_access = list(ACCESS_BAR, ACCESS_KITCHEN)
total_positions = 0
spawn_positions = 0
department_head = null
department_flag = null
smells_like = "ethanol and isolation"

View File

@@ -1,6 +1,7 @@
/datum/job/tourist
title = "Tourist"
flag = TOUR
orbit_icon = "camera-retro"
department_flag = CIVILIAN
faction = "Station"
total_positions = -1