From 699e9f8150db8b7173bec75f9a9dfeef71881c05 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Thu, 15 Dec 2022 22:18:28 +0100 Subject: [PATCH] [MIRROR] TGUI Latejoin Menu! [MDB IGNORE] (#18025) * TGUI Latejoin Menu! * [PR PR] Fixes up the TGUI Latejoin Menu! (#18044) * Makes Angel, Dragon and Robotic wings available in the preferences menu again (#18013) Makes Angel, Dragon and Robotic wings available in the prefs menu again * Automatic changelog for PR #18013 [ci skip] * [MIRROR] Petrified human statues now drop a (stone) brain on destruction [MDB IGNORE] (#18026) * Petrified human statues now drop a (stone) brain on destruction (#71816) ## About The Pull Request Destroying a petrified human statue will now drop their brain, albeit a little more statue-like. ![image](https://user-images.githubusercontent.com/41448081/206316168-e902d6e4-1cb8-4498-80c8-891c59ae638b.png) Changed from original PR after talking w/ Fikou about it ## Why It's Good For The Game While the combo *does* step on the toes of Smite, the statue+rod combo is capable of fully removing you from the round without a way back very quickly. This keeps the strength of the combo while not making it entirely impossible to get back into the round. ## Changelog :cl: balance: Destroying a petrified human statue will now drop their brain. /:cl: * Petrified human statues now drop a (stone) brain on destruction Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com> * Automatic changelog for PR #18026 [ci skip] * [MIRROR] Chaplain armor beacon now uses radial + previews possible armor sets, plus some choice beacon code cleanup. [MDB IGNORE] (#18019) * Chaplain armor beacon now uses radial + previews possible armor sets, plus some choice beacon code cleanup. (#71674) ## About The Pull Request - The chaplain choice beacon now uses a radial to select the armor set, instead of a list, giving the user a preview of what each looks like. ![image](https://user-images.githubusercontent.com/51863163/205417930-f5ceab11-6974-48a9-a871-abcb8228bcf2.png) - Lots of additional cleanup to choice beacon code in general. Less copy pasted code. - All beacons now speak from the beacon with their message, instead of some going by "headset message". Soul removed ## Why It's Good For The Game I always forgot when selecting my armor which looks like what, and choosing an ugly one is a pain since you only get one choice. This should help chaplains get the armor they actually want without needing to check the wiki. ## Changelog :cl: Melbert qol: The chaplain's armament beacon now displays a radial instead of a text list, showing previews of what all the armor sets look like qol: (Almost) all choice beacons now use a pod to send their item, instead of just magicking it under your feet code: Cleaned up some choice beacon code. /:cl: Co-authored-by: Fikou <23585223+Fikou@ users.noreply.github.com> * Chaplain armor beacon now uses radial + previews possible armor sets, plus some choice beacon code cleanup. * update modular Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: Fikou <23585223+Fikou@ users.noreply.github.com> Co-authored-by: Tom <8881105+tf-4@users.noreply.github.com> * Automatic changelog for PR #18019 [ci skip] * [MIRROR] Fixed an improper proc ref. [MDB IGNORE] (#18031) Fixed an improper proc ref. Co-authored-by: lizardqueenlexi <105025397+lizardqueenlexi@users.noreply.github.com> Co-authored-by: Tom <8881105+tf-4@users.noreply.github.com> * Automatic changelog for PR #18031 [ci skip] * Fiiix! * Fuck * [MIRROR] Windoors are vulnerable to a jaws of life [MDB IGNORE] (#18040) * Windoors are vulnerable to a jaws of life (#71733) ## About The Pull Request Windoors can be pried by a jaws of life now ## Why It's Good For The Game Windoors are already immune to any kind of deconstruction outside of bashing it, so making it immune to the jaws of life as well seems excessive ## Changelog :cl: balance: jaws of life can pry windoors /:cl: Co-authored-by: Candycaneannihalator * Windoors are vulnerable to a jaws of life Co-authored-by: texan-down-under <73374039+etherware-novice@users.noreply.github.com> Co-authored-by: Candycaneannihalator * Automatic changelog for PR #18040 [ci skip] * Automatic changelog compile [ci skip] * [fix] fixes det revolver icons (#18049) * Delete guns.dmi * Add files via upload * Automatic changelog for PR #18049 [ci skip] * [MIRROR] Fixes fake links in Adminwho that led to "no_feedback_link" [MDB IGNORE] (#18027) * Fixes fake links in Adminwho that led to "no_feedback_link" (#71804) ## About The Pull Request Basically, sometimes, very rarely, it was possible for this to happen, when it shouldn't be happening. I was wondering for a little while what it was about, and then I finally downloaded my chat logs for a round and realized what was going on. It should no longer happen. ## Why It's Good For The Game Clickable links that lead nowhere (and thus don't do anything) aren't very good for the game. ## Changelog :cl: GoldenAlpharex fix: There shouldn't be clickable names in Adminwho that don't do anything anymore. /:cl: * Fixes fake links in Adminwho that led to "no_feedback_link" Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> * Fix: Self-Actualization Device Doubling Damage (#18015) * Change adjustOrganLoss/BruteLoss/FireLoss to set * Reverts my naive soluton * Automatic changelog for PR #18027 [ci skip] * Automatic changelog for PR #18015 [ci skip] * Fix TGUI latejoin menu not closing properly, properly this time * Oh yea, flavourtext requirement exists. Not that we actually have it enabled upstream. * Revert "Merge remote-tracking branch 'upstream/upstream-merge-71883' into fixlatejoinmenu" This reverts commit 3279e73418f95637162a2923b0431e9e26b43403, reversing changes made to d46aab7b421fee94d49b57e863bfd8e0917fc43f. * Fucking stupid code merge shit * Cool newline bro * Dumb stupid commented code * Also this Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> Co-authored-by: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com> Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: Fikou <23585223+Fikou@ users.noreply.github.com> Co-authored-by: Tom <8881105+tf-4@users.noreply.github.com> Co-authored-by: lizardqueenlexi <105025397+lizardqueenlexi@users.noreply.github.com> Co-authored-by: texan-down-under <73374039+etherware-novice@users.noreply.github.com> Co-authored-by: Candycaneannihalator Co-authored-by: Changelogs Co-authored-by: RatFromTheJungle <62520989+RatFromTheJungle@users.noreply.github.com> Co-authored-by: Dani Glore Co-authored-by: distributivgesetz * fix Co-authored-by: Rimi Nosha Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com> Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: Fikou <23585223+Fikou@ users.noreply.github.com> Co-authored-by: Tom <8881105+tf-4@users.noreply.github.com> Co-authored-by: lizardqueenlexi <105025397+lizardqueenlexi@users.noreply.github.com> Co-authored-by: texan-down-under <73374039+etherware-novice@users.noreply.github.com> Co-authored-by: Candycaneannihalator Co-authored-by: Changelogs Co-authored-by: RatFromTheJungle <62520989+RatFromTheJungle@users.noreply.github.com> Co-authored-by: Dani Glore Co-authored-by: distributivgesetz Co-authored-by: tastyfish --- code/__DEFINES/jobs.dm | 6 +- code/_onclick/hud/new_player.dm | 17 +- code/controllers/subsystem/ticker.dm | 16 +- .../newscaster/newscaster_machine.dm | 2 +- .../antagonists/separatist/separatist.dm | 2 +- code/modules/jobs/departments/departments.dm | 22 +- .../mob/dead/new_player/latejoin_menu.dm | 221 ++++++++++++++++++ .../modules/mob/dead/new_player/new_player.dm | 153 +----------- .../file_system/programs/bounty_board.dm | 2 +- code/modules/vending/_vending.dm | 2 +- .../modules/jobs/departments/departments.dm | 5 +- .../mob/dead/new_player/latejoin_menu.dm | 8 + .../modules/title_screen/code/new_player.dm | 99 +------- tgstation.dme | 2 + tgui/packages/common/collections.ts | 21 ++ tgui/packages/common/color.js | 17 ++ .../tgui/components/StyleableSection.tsx | 26 +++ tgui/packages/tgui/components/index.js | 1 + .../packages/tgui/interfaces/JobSelection.tsx | 211 +++++++++++++++++ .../tgui/interfaces/Orbit/constants.ts | 6 - tgui/packages/tgui/interfaces/Orbit/index.tsx | 5 +- .../tgui/interfaces/common/JobToIcon.ts | 6 + 22 files changed, 568 insertions(+), 282 deletions(-) create mode 100644 code/modules/mob/dead/new_player/latejoin_menu.dm create mode 100644 modular_skyrat/master_files/code/modules/mob/dead/new_player/latejoin_menu.dm create mode 100644 tgui/packages/tgui/components/StyleableSection.tsx create mode 100644 tgui/packages/tgui/interfaces/JobSelection.tsx create mode 100644 tgui/packages/tgui/interfaces/common/JobToIcon.ts diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index e0a0d80e7f3..6923787b638 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -6,6 +6,10 @@ #define JOB_UNAVAILABLE_SLOTFULL 5 /// Job unavailable due to incompatibility with an antag role. #define JOB_UNAVAILABLE_ANTAG_INCOMPAT 6 + +/// 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" #define DEFAULT_BIBLE "Default Bible Name" @@ -170,7 +174,7 @@ #define JOB_DISPLAY_ORDER_ENGINEER_GUARD 44 //SKYRAT EDIT ADDITION #define JOB_DISPLAY_ORDER_CUSTOMS_AGENT 45 //SKYRAT EDIT ADDITION -#define DEPARTMENT_UNASSIGNED "No department assigned" +#define DEPARTMENT_UNASSIGNED "No Department" #define DEPARTMENT_BITFLAG_SECURITY (1<<0) #define DEPARTMENT_SECURITY "Security" diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm index 4af0b5c2cd7..15c8e9b184a 100644 --- a/code/_onclick/hud/new_player.dm +++ b/code/_onclick/hud/new_player.dm @@ -190,18 +190,19 @@ . = ..() if(!.) return + if(!SSticker?.IsRoundInProgress()) to_chat(hud.mymob, span_boldwarning("The round is either not ready, or has already finished...")) return //Determines Relevent Population Cap var/relevant_cap - var/hpc = CONFIG_GET(number/hard_popcap) - var/epc = CONFIG_GET(number/extreme_popcap) - if(hpc && epc) - relevant_cap = min(hpc, epc) + 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(hpc, epc) + relevant_cap = max(hard_popcap, extreme_popcap) var/mob/dead/new_player/new_player = hud.mymob @@ -224,7 +225,11 @@ return // SKYRAT EDIT END - new_player.LateChoices() + if(!LAZYACCESS(params2list(params), CTRL_CLICK)) + GLOB.latejoin_menu.ui_interact(new_player) + else + to_chat(new_player, span_warning("Opening emergency fallback late join menu! If THIS doesn't show, ahelp immediately!")) + GLOB.latejoin_menu.fallback_ui(new_player) /atom/movable/screen/lobby/button/join/proc/show_join_button() SIGNAL_HANDLER diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 8f8ade1ac31..e08018b02f0 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -499,13 +499,13 @@ SUBSYSTEM_DEF(ticker) /datum/controller/subsystem/ticker/proc/check_queue() if(!queued_players.len) return - var/hpc = CONFIG_GET(number/hard_popcap) - if(!hpc) + var/hard_popcap = CONFIG_GET(number/hard_popcap) + if(!hard_popcap) list_clear_nulls(queued_players) - for (var/mob/dead/new_player/NP in queued_players) - to_chat(NP, span_userdanger("The alive players limit has been released!
[html_encode(">>Join Game<<")]")) - 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!
[html_encode(">>Join Game<<")]")) + SEND_SOUND(new_player, sound('sound/misc/notice1.ogg')) + GLOB.latejoin_menu.ui_interact(new_player) queued_players.len = 0 queue_delay = 0 return @@ -516,11 +516,11 @@ SUBSYSTEM_DEF(ticker) switch(queue_delay) if(5) //every 5 ticks check if there is a slot available list_clear_nulls(queued_players) - if(living_player_count() < hpc) + if(living_player_count() < hard_popcap) if(next_in_line?.client) to_chat(next_in_line, span_userdanger("A slot has opened! You have approximately 20 seconds to join. \>\>Join Game\<\<")) 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 diff --git a/code/game/machinery/newscaster/newscaster_machine.dm b/code/game/machinery/newscaster/newscaster_machine.dm index 6f0c6f1d468..e88774f2e0e 100644 --- a/code/game/machinery/newscaster/newscaster_machine.dm +++ b/code/game/machinery/newscaster/newscaster_machine.dm @@ -140,7 +140,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30) data["user"]["department"] = card.registered_account.account_job.paycheck_department else data["user"]["job"] = "No Job" - data["user"]["department"] = "No Department" + data["user"]["department"] = DEPARTMENT_UNASSIGNED else if(issilicon(user)) var/mob/living/silicon/silicon_user = user data["user"] = list() diff --git a/code/modules/antagonists/separatist/separatist.dm b/code/modules/antagonists/separatist/separatist.dm index 81e4106bd19..45ce9bf0ad5 100644 --- a/code/modules/antagonists/separatist/separatist.dm +++ b/code/modules/antagonists/separatist/separatist.dm @@ -105,7 +105,7 @@ objectives -= nation.objectives /datum/antagonist/separatist/proc/setup_ui_color() - var/list/hsl = rgb2num(nation.department.latejoin_color, COLORSPACE_HSL) + var/list/hsl = rgb2num(nation.department.ui_color, COLORSPACE_HSL) hsl[3] = 25 //setting lightness very low ui_color = rgb(hsl[1], hsl[2], hsl[3], space = COLORSPACE_HSL) diff --git a/code/modules/jobs/departments/departments.dm b/code/modules/jobs/departments/departments.dm index 27a7174da73..db45899b5c4 100644 --- a/code/modules/jobs/departments/departments.dm +++ b/code/modules/jobs/departments/departments.dm @@ -1,7 +1,7 @@ /// Singleton representing a category of jobs forming a department. /datum/job_department /// Department as displayed on different menus. - var/department_name = "No department assigned" + var/department_name = DEPARTMENT_UNASSIGNED /// Bitflags associated to the specific department. var/department_bitflags = NONE /// Typepath of the job datum leading this department. @@ -12,8 +12,8 @@ var/display_order = 0 /// The header color to be displayed in the ban panel, classes defined in banpanel.css var/label_class = "undefineddepartment" - /// The color used in the latejoin menu. - var/latejoin_color = "#6681a5" + /// The color used in TGUI or similar menus. + var/ui_color = "#9689db" /// Job singleton datums associated to this department. Populated on job initialization. var/list/department_jobs = list() /// For separatists, what independent name prefix does their nation get named? @@ -54,7 +54,7 @@ department_experience_type = EXP_TYPE_COMMAND display_order = 1 label_class = "command" - latejoin_color = "#ccccff" + ui_color = "#6681a5" /datum/job_department/security @@ -64,7 +64,7 @@ department_experience_type = EXP_TYPE_SECURITY display_order = 2 label_class = "security" - latejoin_color = "#ffdddd" + ui_color = "#d46a78" nation_prefixes = list("Securi", "Beepski", "Shitcuri", "Red", "Stunba", "Flashbango", "Flasha", "Stanfordi") /datum/job_department/engineering @@ -74,7 +74,7 @@ department_experience_type = EXP_TYPE_ENGINEERING display_order = 3 label_class = "engineering" - latejoin_color = "#ffeeaa" + ui_color = "#dfb567" nation_prefixes = list("Atomo", "Engino", "Power", "Teleco") @@ -85,7 +85,7 @@ department_experience_type = EXP_TYPE_MEDICAL display_order = 4 label_class = "medical" - latejoin_color = "#ffddf0" + ui_color = "#65b2bd" nation_prefixes = list("Mede", "Healtha", "Recova", "Chemi", "Viro", "Psych") @@ -96,7 +96,7 @@ department_experience_type = EXP_TYPE_SCIENCE display_order = 5 label_class = "science" - latejoin_color = "#ffddff" + ui_color = "#c973c9" nation_prefixes = list("Sci", "Griffa", "Geneti", "Explosi", "Mecha", "Xeno", "Nani", "Cyto") @@ -107,7 +107,7 @@ department_experience_type = EXP_TYPE_SUPPLY display_order = 6 label_class = "supply" - latejoin_color = "#ddddff" + ui_color = "#cf9c6c" nation_prefixes = list("Cargo", "Guna", "Suppli", "Mule", "Crate", "Ore", "Mini", "Shaf") @@ -118,7 +118,7 @@ department_experience_type = EXP_TYPE_SERVICE display_order = 7 label_class = "service" - latejoin_color = "#bbe291" + ui_color = "#7cc46a" nation_prefixes = list("Honka", "Boozo", "Fatu", "Danka", "Mimi", "Libra", "Jani", "Religi") @@ -129,7 +129,7 @@ department_experience_type = EXP_TYPE_SILICON display_order = 8 label_class = "silicon" - latejoin_color = "#ccffcc" + ui_color = "#5dbda0" /datum/job_department/silicon/generate_nation_name() return "United Nations" //For nations ruleset specifically, because all other sources of nation creation cannot choose silicons diff --git a/code/modules/mob/dead/new_player/latejoin_menu.dm b/code/modules/mob/dead/new_player/latejoin_menu.dm new file mode 100644 index 00000000000..2a9f980a630 --- /dev/null +++ b/code/modules/mob/dead/new_player/latejoin_menu.dm @@ -0,0 +1,221 @@ +#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.joinable_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 + 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(istype(player) && !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" = SSlag_switch.measures[DISABLE_NON_OBSJOBS], + "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/datum/job_department/department as anything in SSjob.joinable_departments) + var/list/department_jobs = list() + var/list/department_data = list( + "jobs" = department_jobs, + "open_slots" = 0, + ) + departments[department.department_name] = department_data + + for(var/datum/job/job_datum as anything in department.department_jobs) + 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"] != "∞") + 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/datum/job_department/department as anything in SSjob.joinable_departments) + var/list/department_jobs = list() + var/list/department_data = list( + "jobs" = department_jobs, + "color" = department.ui_color, + ) + departments[department.department_name] = department_data + + for(var/datum/job/job_datum as anything in department.department_jobs) + var/datum/outfit/outfit = job_datum.outfit + var/datum/id_trim/trim = initial(outfit.id_trim) + + var/list/job_data = list( + "command" = !!(job_datum.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND), + "description" = job_datum.description, + "icon" = initial(trim.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 || ui.user.client.interviewee || !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) + 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(SSlag_switch.measures[DISABLE_NON_OBSJOBS]) + tgui_alert(owner, "There is an administrative lock on entering the game for non-observers!", "Oh No!") + return TRUE + + // SKYRAT EDIT ADDITION START - Flavourtext requirement + if(length_char(owner.client.prefs.read_preference(/datum/preference/text/flavor_text)) < FLAVOR_TEXT_CHAR_REQUIREMENT) + to_chat(owner, span_notice("You need at least [FLAVOR_TEXT_CHAR_REQUIREMENT] characters of flavor text to join the round. You have [length_char(owner.client.prefs.read_preference(/datum/preference/text/flavor_text))] characters.")) + return + // SKYRAT EDIT END + + //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 && !(ckey(owner.key) in GLOB.admin_datums)) + 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 + + if("viewpoll") + var/datum/poll_question/poll = locate(params["viewpoll"]) in GLOB.polls + if(!poll) + return TRUE + + owner.poll_player(poll) + return TRUE + + if("votepollref") + var/datum/poll_question/poll = locate(params["votepollref"]) in GLOB.polls + if(!poll) + return TRUE + + owner.vote_on_poll_handler(poll, params) + + 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/list/dept_data = list() + + for(var/datum/job_department/department as anything in SSjob.joinable_departments) + for(var/datum/job/job_datum as anything in department.department_jobs) + if(owner.IsJobUnavailable(job_datum.title, latejoin = TRUE) != JOB_AVAILABLE) + continue + dept_data += job_datum.title + + if(dept_data.len <= 0) //Congratufuckinglations + tgui_alert(owner, "There are literally no random jobs available for you on this server, ahelp for assistance.", "Oh No!") + return + + var/random_job + + while(random_job != JOB_CHOICE_YES) + if(dept_data.len <= 0) + tgui_alert(owner, "It seems that there are no more random jobs available for you!", "Oh No!") + return + + var/random = pick_n_take(dept_data) + var/list/random_job_options = list(JOB_CHOICE_YES, JOB_CHOICE_REROLL, JOB_CHOICE_CANCEL) + + random_job = tgui_alert(owner, "[random]?", "Random Job", random_job_options) + + if(random_job == JOB_CHOICE_CANCEL) + return + if(random_job == JOB_CHOICE_YES) + return random + +#undef JOB_CHOICE_YES +#undef JOB_CHOICE_REROLL +#undef JOB_CHOICE_CANCEL diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 7d6bdf173e6..56886a7351a 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -13,7 +13,8 @@ var/mob/living/new_character ///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(mapload) @@ -38,83 +39,6 @@ /mob/dead/new_player/prepare_huds() return -/* SKYRAT EDIT REMOVAL - MOVED TO MODULAR -/mob/dead/new_player/Topic(href, href_list[]) - if(src != usr) - return - - if(!client) - return - - if(client.interviewee) - return FALSE - - if(href_list["late_join"]) //This still exists for queue messages in chat - if(!SSticker?.IsRoundInProgress()) - to_chat(usr, span_boldwarning("The round is either not ready, or has already finished...")) - return - LateChoices() - return - - if(href_list["cancrand"]) - src << browse(null, "window=randjob") //closes the random job window - LateChoices() - return - - if(href_list["SelectedJob"]) - if(href_list["SelectedJob"] == "Random") - var/list/dept_data = list() - for(var/datum/job_department/department as anything in SSjob.joinable_departments) - for(var/datum/job/job_datum as anything in department.department_jobs) - if(IsJobUnavailable(job_datum.title, TRUE) != JOB_AVAILABLE) - continue - dept_data += job_datum.title - if(dept_data.len <= 0) //Congratufuckinglations - tgui_alert(src, "There are literally no random jobs available for you on this server, ahelp for assistance.") - return - var/random = pick(dept_data) - var/randomjob = "

[random]
Reroll
Cancel

" - var/datum/browser/popup = new(src, "randjob", "
Random Job
", 200, 150) - popup.set_window_options("can_close=0") - popup.set_content(randomjob) - popup.open(FALSE) - return - if(!SSticker?.IsRoundInProgress()) - to_chat(usr, span_danger("The round is either not ready, or has already finished...")) - return - - if(SSlag_switch.measures[DISABLE_NON_OBSJOBS]) - to_chat(usr, span_notice("There is an administrative lock on entering the game!")) - return - - //Determines Relevent Population Cap - var/relevant_cap - var/hpc = CONFIG_GET(number/hard_popcap) - var/epc = CONFIG_GET(number/extreme_popcap) - if(hpc && epc) - relevant_cap = min(hpc, epc) - else - relevant_cap = max(hpc, epc) - - - - if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums)) - if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1])) - to_chat(usr, span_warning("Server is full.")) - return - - AttemptLateSpawn(href_list["SelectedJob"]) - return - - if(href_list["viewpoll"]) - var/datum/poll_question/poll = locate(href_list["viewpoll"]) in GLOB.polls - poll_player(poll) - - if(href_list["votepollref"]) - var/datum/poll_question/poll = locate(href_list["votepollref"]) in GLOB.polls - vote_on_poll_handler(poll, href_list) -*/ - //When you cop out of the round (NB: this HAS A SLEEP FOR PLAYER INPUT IN IT) /mob/dead/new_player/proc/make_me_an_observer() if(QDELETED(src) || !src.client) @@ -131,11 +55,11 @@ show_title_screen() // SKYRAT EDIT ADDITION return FALSE + hide_title_screen() // SKYRAT EDIT ADDITION - Skyrat Titlescreen var/mob/dead/observer/observer = new() spawning = TRUE observer.started_as_observer = TRUE - close_spawn_windows() var/obj/effect/landmark/observer_start/O = locate(/obj/effect/landmark/observer_start) in GLOB.landmarks_list to_chat(src, span_notice("Now teleporting.")) if (O) @@ -185,7 +109,8 @@ //SKYRAT EDIT END if(JOB_UNAVAILABLE_ANTAG_INCOMPAT) return "[jobtitle] is not compatible with some antagonist role assigned to you." - 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) @@ -227,7 +152,6 @@ return FALSE if(SSshuttle.arrivals) - close_spawn_windows() //In case we get held up if(SSshuttle.arrivals.damaged && CONFIG_GET(flag/arrivals_shuttle_require_safe_latejoin)) tgui_alert(usr,"The arrivals shuttle is currently malfunctioning! You cannot join.") return FALSE @@ -245,6 +169,7 @@ tgui_alert(usr, "There was an unexpected error putting you into your requested job. If you cannot join with any job, you should contact an admin.") return FALSE + hide_title_screen()// SKYRAT EDIT ADDITION mind.late_joiner = TRUE var/atom/destination = mind.assigned_role.get_latejoin_spawn_point() if(!destination) @@ -315,13 +240,13 @@ SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CREWMEMBER_JOINED, character, rank) - //SKYRAT EDIT ADDITION + // SKYRAT EDIT ADDITION START if(humanc) for(var/datum/loadout_item/item as anything in loadout_list_to_datums(humanc?.client?.prefs?.loadout_list)) if (item.restricted_roles && length(item.restricted_roles) && !(job.title in item.restricted_roles)) continue item.post_equip_item(humanc.client?.prefs, humanc) - //SKYRAT EDIT END + // SKYRAT EDIT END /mob/dead/new_player/proc/AddEmploymentContract(mob/living/carbon/human/employee) //TODO: figure out a way to exclude wizards/nukeops/demons from this. @@ -330,65 +255,9 @@ if(!employmentCabinet.virgin) employmentCabinet.addFile(employee) - -/mob/dead/new_player/proc/LateChoices() - var/list/dat = list() - if(SSlag_switch.measures[DISABLE_NON_OBSJOBS]) - dat += "
Only Observers may join at this time.

" - dat += "
Round Duration: [DisplayTimeText(world.time - SSticker.round_start_time)]
" - dat += "
Alert Level: [capitalize(SSsecurity_level.get_current_level_as_text())]
" // SKYRAT EDIT ADDITION - if(SSshuttle.emergency) - switch(SSshuttle.emergency.mode) - if(SHUTTLE_ESCAPE) - dat += "
The station has been evacuated.

" - if(SHUTTLE_CALL) - if(!SSshuttle.canRecall()) - dat += "
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 - dat += "
" - var/column_counter = 0 - - for(var/datum/job_department/department as anything in SSjob.joinable_departments) - var/department_color = department.latejoin_color - dat += "
" - dat += "[department.department_name]" - var/list/dept_data = list() - for(var/datum/job/job_datum as anything in department.department_jobs) - if(IsJobUnavailable(job_datum.title, TRUE) != JOB_AVAILABLE) - continue - - var/command_bold = "" - if(job_datum.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) - command_bold = " command" - if(job_datum in SSjob.prioritized_jobs) - dept_data += "[job_datum.title] ([job_datum.current_positions])" - else - dept_data += "[job_datum.title] ([job_datum.current_positions])" - - - - if(!length(dept_data)) - dept_data += "No positions open." - dat += dept_data.Join() - dat += "

" - column_counter++ - if(column_counter > 0 && (column_counter % 3 == 0)) - dat += "
" - dat += "
" - dat += "" - dat += "" - 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 - - /// Creates, assigns and returns the new_character to spawn as. Assumes a valid mind.assigned_role exists. /mob/dead/new_player/proc/create_character(atom/destination) spawning = TRUE - close_spawn_windows() mind.active = FALSE //we wish to transfer the key manually var/mob/living/spawning_mob = mind.assigned_role.get_spawn_mob(client, destination) @@ -435,12 +304,6 @@ /mob/dead/new_player/Move() return 0 - -/mob/dead/new_player/proc/close_spawn_windows() - hide_title_screen() // SKYRAT EDIT ADDITION - src << browse(null, "window=latechoices") //closes late choices window (Hey numbnuts go make this tgui) - src << browse(null, "window=randjob") //closes the random job window - // 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 // Prevents "antag rolling" by setting antag prefs on, all jobs to never, and "return to lobby if preferences not available" diff --git a/code/modules/modular_computers/file_system/programs/bounty_board.dm b/code/modules/modular_computers/file_system/programs/bounty_board.dm index 97be2c2f0d4..271ac321306 100644 --- a/code/modules/modular_computers/file_system/programs/bounty_board.dm +++ b/code/modules/modular_computers/file_system/programs/bounty_board.dm @@ -30,7 +30,7 @@ data["user"]["department"] = current_user.account_job.paycheck_department else data["user"]["job"] = "No Job" - data["user"]["department"] = "No Department" + data["user"]["department"] = DEPARTMENT_UNASSIGNED else data["user"] = list() data["user"]["name"] = user.name diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index 1db21509119..2f3156bb2ed 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -946,7 +946,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/product_record in product_records + coin_records + hidden_records) diff --git a/modular_skyrat/master_files/code/modules/jobs/departments/departments.dm b/modular_skyrat/master_files/code/modules/jobs/departments/departments.dm index 10ed5ce01da..1b88a250ca4 100644 --- a/modular_skyrat/master_files/code/modules/jobs/departments/departments.dm +++ b/modular_skyrat/master_files/code/modules/jobs/departments/departments.dm @@ -5,4 +5,7 @@ department_experience_type = EXP_TYPE_CENTRAL_COMMAND display_order = 1 label_class = "command" - latejoin_color = "#86ff82" + ui_color = "#86ff82" + +/datum/job_department/security + ui_color = "#528ccf" diff --git a/modular_skyrat/master_files/code/modules/mob/dead/new_player/latejoin_menu.dm b/modular_skyrat/master_files/code/modules/mob/dead/new_player/latejoin_menu.dm new file mode 100644 index 00000000000..60546f1eae8 --- /dev/null +++ b/modular_skyrat/master_files/code/modules/mob/dead/new_player/latejoin_menu.dm @@ -0,0 +1,8 @@ +/datum/latejoin_menu/ui_data(mob/user) + . = ..() + var/color = SSsecurity_level.get_current_level_as_text() + switch(color) + if("delta", "gamma") + color = "red" + + .["alert_level"] = list("name" = capitalize(SSsecurity_level.get_current_level_as_text()), "color" = color) diff --git a/modular_skyrat/modules/title_screen/code/new_player.dm b/modular_skyrat/modules/title_screen/code/new_player.dm index d8c39acbfc7..701932ccf33 100644 --- a/modular_skyrat/modules/title_screen/code/new_player.dm +++ b/modular_skyrat/modules/title_screen/code/new_player.dm @@ -62,58 +62,7 @@ if(href_list["late_join"]) play_lobby_button_sound() - if(!SSticker?.IsRoundInProgress()) - to_chat(src, span_boldwarning("The round is either not ready, or has already finished...")) - return - - //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 || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(key) in GLOB.admin_datums))) - to_chat(src, span_danger("[CONFIG_GET(string/hard_popcap_message)]")) - - var/queue_position = SSticker.queued_players.Find(src) - if(queue_position == 1) - to_chat(src, span_notice("You are next in line to join the game. You will be notified when a slot opens up.")) - else if(queue_position) - to_chat(src, span_notice("There are [queue_position-1] players in front of you in the queue to join the game.")) - else - SSticker.queued_players += src - to_chat(src, span_notice("You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len].")) - return - - if(length_char(src.client.prefs.read_preference(/datum/preference/text/flavor_text)) < FLAVOR_TEXT_CHAR_REQUIREMENT) - to_chat(src, span_notice("You need at least [FLAVOR_TEXT_CHAR_REQUIREMENT] characters of flavor text to join the round. You have [length_char(src.client.prefs.read_preference(/datum/preference/text/flavor_text))] characters.")) - return - - LateChoices() - return - - if(href_list["cancrand"]) - src << browse(null, "window=randjob") //closes the random job window - LateChoices() - return - - if(href_list["SelectedJob"]) - select_job(href_list["SelectedJob"]) - return - - if(href_list["viewpoll"]) - play_lobby_button_sound() - var/datum/poll_question/poll = locate(href_list["viewpoll"]) in GLOB.polls - poll_player(poll) - return - - if(href_list["votepollref"]) - var/datum/poll_question/poll = locate(href_list["votepollref"]) in GLOB.polls - vote_on_poll_handler(poll, href_list) - return + GLOB.latejoin_menu.ui_interact(usr) if(href_list["title_is_ready"]) title_screen_is_ready = TRUE @@ -164,52 +113,6 @@ /mob/dead/new_player/proc/play_lobby_button_sound() SEND_SOUND(src, sound('modular_skyrat/master_files/sound/effects/save.ogg')) -/** - * Selects a new job or gives random if unset. - */ -/mob/dead/new_player/proc/select_job(job) - if(job == "Random") - var/list/dept_data = list() - for(var/datum/job_department/department as anything in SSjob.joinable_departments) - for(var/datum/job/job_datum as anything in department.department_jobs) - if(IsJobUnavailable(job_datum.title, TRUE) != JOB_AVAILABLE) - continue - dept_data += job_datum.title - if(dept_data.len <= 0) //Congratufuckinglations - tgui_alert(src, "There are literally no random jobs available for you on this server, ahelp for assistance.") - return - var/random = pick(dept_data) - var/randomjob = "

[random]
Reroll
Cancel

" - var/datum/browser/popup = new(src, "randjob", "
Random Job
", 200, 150) - popup.set_window_options("can_close=0") - popup.set_content(randomjob) - popup.open(FALSE) - return - - if(!SSticker?.IsRoundInProgress()) - to_chat(usr, span_danger("The round is either not ready, or has already finished...")) - return - - if(SSlag_switch.measures[DISABLE_NON_OBSJOBS]) - to_chat(usr, span_notice("There is an administrative lock on entering the game!")) - return - - //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(LAZYLEN(SSticker.queued_players) && !(ckey(key) in GLOB.admin_datums)) - if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1])) - to_chat(usr, span_warning("Server is full.")) - return - - AttemptLateSpawn(job) - /** * Allows the player to select a server to join from any loaded servers. */ diff --git a/tgstation.dme b/tgstation.dme index fd9284a6098..13f4aab26d8 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3633,6 +3633,7 @@ #include "code\modules\mob\camera\camera.dm" #include "code\modules\mob\dead\crew_manifest.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" @@ -5110,6 +5111,7 @@ #include "modular_skyrat\master_files\code\modules\jobs\job_types\cyborg.dm" #include "modular_skyrat\master_files\code\modules\language\language.dm" #include "modular_skyrat\master_files\code\modules\language\language_holders.dm" +#include "modular_skyrat\master_files\code\modules\mob\dead\new_player\latejoin_menu.dm" #include "modular_skyrat\master_files\code\modules\mob\dead\new_player\preferences_setup.dm" #include "modular_skyrat\master_files\code\modules\mob\living\blood.dm" #include "modular_skyrat\master_files\code\modules\mob\living\emote_popup.dm" diff --git a/tgui/packages/common/collections.ts b/tgui/packages/common/collections.ts index 49f500ebd29..3902feec5cd 100644 --- a/tgui/packages/common/collections.ts +++ b/tgui/packages/common/collections.ts @@ -300,3 +300,24 @@ export const binaryInsertWith = copy.splice(binarySearch(getKey, collection, value), 0, value); return copy; }; + +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; +}; diff --git a/tgui/packages/common/color.js b/tgui/packages/common/color.js index 672fce529b6..b59d82247aa 100644 --- a/tgui/packages/common/color.js +++ b/tgui/packages/common/color.js @@ -22,6 +22,23 @@ export class Color { } return `rgba(${this.r | 0}, ${this.g | 0}, ${this.b | 0}, ${alpha})`; } + + // 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); + } } /** diff --git a/tgui/packages/tgui/components/StyleableSection.tsx b/tgui/packages/tgui/components/StyleableSection.tsx new file mode 100644 index 00000000000..ad38984fee4 --- /dev/null +++ b/tgui/packages/tgui/components/StyleableSection.tsx @@ -0,0 +1,26 @@ +import { SFC } from 'inferno'; +import { Box } from './Box'; + +// The cost of flexibility and prettiness. +export const StyleableSection: SFC<{ + style?; + titleStyle?; + textStyle?; + title?; + titleSubtext?; +}> = (props) => { + return ( + + {/* Yes, this box (line above) is missing the "Section" class. This is very intentional, as the layout looks *ugly* with it.*/} + + + {props.title} + +
{props.titleSubtext}
+
+ + {props.children} + +
+ ); +}; diff --git a/tgui/packages/tgui/components/index.js b/tgui/packages/tgui/components/index.js index 121d2bfa4e2..32af982767a 100644 --- a/tgui/packages/tgui/components/index.js +++ b/tgui/packages/tgui/components/index.js @@ -37,6 +37,7 @@ 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 { Table } from './Table'; export { Tabs } from './Tabs'; diff --git a/tgui/packages/tgui/interfaces/JobSelection.tsx b/tgui/packages/tgui/interfaces/JobSelection.tsx new file mode 100644 index 00000000000..1c691a020fe --- /dev/null +++ b/tgui/packages/tgui/interfaces/JobSelection.tsx @@ -0,0 +1,211 @@ +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; + open_slots: number; +}; + +type Data = { + departments_static: Record; + departments: Record; + alert_state: string; + shuttle_status: string; + disable_jobs_for_non_observers: BooleanLike; + priority: BooleanLike; + round_duration: string; + alert_level: { name: string; color: string }; // SKYRAT EDIT ADDITION - Alert level on jobs menu +}; + +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 ( + + ); +}; + +export const JobSelection = (props, context) => { + const { act, data } = useBackend(context); + if (!data?.departments_static) { + return null; // Stop TGUI whitescreens with TGUI-dev! + } + const departments: Record = deepMerge( + data.departments, + data.departments_static + ); + + return ( + { + // Send a heartbeat back to DM to let it know the window is alive and well + act('ui_mounted_with_no_bluescreen'); + }}> + + + {data.shuttle_status && ( + {data.shuttle_status} + )} + { + // SKYRAT EDIT ADDITION - Alert level on jobs menu + + The current alert level is: {data.alert_level.name} + + } + + It is currently {data.round_duration} into the shift. + +