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.

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.

- 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 = "
"
- 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 = "