From 9e46e800a02d41626b875383119e86c1643ee95b Mon Sep 17 00:00:00 2001 From: Lucy Date: Fri, 15 Sep 2023 12:55:18 -0400 Subject: [PATCH 01/12] TGUI Crew Manifest: Shion Edition --- beestation.dme | 2 + code/__DEFINES/jobs.dm | 16 -- code/__HELPERS/_lists.dm | 7 + code/__HELPERS/jobs.dm | 161 +++++++++--------- code/_globalvars/lists/jobs.dm | 30 ++++ code/datums/datacore.dm | 41 ++--- code/modules/client/client_defines.dm | 2 +- code/modules/mob/dead/crew_manifest.dm | 50 ++++++ .../modules/mob/dead/new_player/new_player.dm | 13 +- code/modules/mob/dead/observer/observer.dm | 13 +- code/modules/mob/living/silicon/silicon.dm | 11 +- .../packages/tgui/interfaces/CrewManifest.tsx | 121 +++++++++++++ .../tgui/styles/interfaces/CrewManifest.scss | 57 +++++++ tgui/packages/tgui/styles/main.scss | 1 + 14 files changed, 376 insertions(+), 149 deletions(-) create mode 100644 code/_globalvars/lists/jobs.dm create mode 100644 code/modules/mob/dead/crew_manifest.dm create mode 100644 tgui/packages/tgui/interfaces/CrewManifest.tsx create mode 100644 tgui/packages/tgui/styles/interfaces/CrewManifest.scss diff --git a/beestation.dme b/beestation.dme index 7413cf204f3fb..e3316a9c09b93 100644 --- a/beestation.dme +++ b/beestation.dme @@ -302,6 +302,7 @@ #include "code\_globalvars\lists\ambience.dm" #include "code\_globalvars\lists\client.dm" #include "code\_globalvars\lists\flavor_misc.dm" +#include "code\_globalvars\lists\jobs.dm" #include "code\_globalvars\lists\maintenance_loot.dm" #include "code\_globalvars\lists\mapping.dm" #include "code\_globalvars\lists\mobs.dm" @@ -2810,6 +2811,7 @@ #include "code\modules\mob\transform_procs.dm" #include "code\modules\mob\update_icons.dm" #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\login.dm" #include "code\modules\mob\dead\new_player\logout.dm" diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index 281ad8ab25e3a..e0383f51ddb5d 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -114,22 +114,6 @@ #define DEPT_BITFLAG_CAPTAIN (1<<10) #define DEPT_BITFLAG_ASSISTANT (1<<11) -/// For use in the preferences menu. -GLOBAL_LIST_INIT(dept_bitflag_to_name, list( - "[DEPT_BITFLAG_COM]" = "Command", - "[DEPT_BITFLAG_CIV]" = "Civilian", - "[DEPT_BITFLAG_SRV]" = "Service", - "[DEPT_BITFLAG_CAR]" = "Cargo", - "[DEPT_BITFLAG_SCI]" = "Science", - "[DEPT_BITFLAG_ENG]" = "Engineering", - "[DEPT_BITFLAG_MED]" = "Medical", - "[DEPT_BITFLAG_SEC]" = "Security", - "[DEPT_BITFLAG_VIP]" = "Very Important People", - "[DEPT_BITFLAG_SILICON]" = "Silicon", - "[DEPT_BITFLAG_CAPTAIN]" = "Captain", - "[DEPT_BITFLAG_ASSISTANT]" = "Assistant" -)) - // should check the ones in `\_DEFINES\economy.dm` // It's true that bitflags shouldn't be separated in two DEFINES if these are same, but just in case the system can be devided, it's remained separated. diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index ca24fa7f0f91b..247fe03d4b25b 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -768,3 +768,10 @@ stack_trace("[name] is not sorted. value at [index] ([value]) is in the wrong place compared to the previous value of [last_value] (when compared to by [cmp])") last_value = value + +/proc/list_to_assoc_index(list/input) + . = list() + for(var/i = 1 to length(input)) + var/key = "[input[i]]" + if(isnull(.[key])) + .[key] = i diff --git a/code/__HELPERS/jobs.dm b/code/__HELPERS/jobs.dm index b50576932ede8..5891fc3cf4085 100644 --- a/code/__HELPERS/jobs.dm +++ b/code/__HELPERS/jobs.dm @@ -74,88 +74,88 @@ ) return id_style[jobname] || "noname" // default: a card with no shape +GLOBAL_LIST_INIT(id_to_hud, list( + // Command + "Command (Custom)" = JOB_HUD_RAWCOMMAND, + JOB_NAME_CAPTAIN = JOB_HUD_CAPTAIN, + "Acting Captain" = JOB_HUD_ACTINGCAPTAIN , + + // Service + "Service (Custom)" = JOB_HUD_RAWSERVICE, + JOB_NAME_HEADOFPERSONNEL = JOB_HUD_HEADOFPERSONNEL, + JOB_NAME_ASSISTANT = JOB_HUD_ASSISTANT, + JOB_NAME_BARTENDER = JOB_HUD_BARTENDER, + JOB_NAME_COOK = JOB_HUD_COOK, + JOB_NAME_BOTANIST = JOB_HUD_BOTANIST, + JOB_NAME_CURATOR = JOB_HUD_CURATOR, + JOB_NAME_CHAPLAIN = JOB_HUD_CHAPLAIN, + JOB_NAME_JANITOR = JOB_HUD_JANITOR, + JOB_NAME_LAWYER = JOB_HUD_LAWYER, + JOB_NAME_MIME = JOB_HUD_MIME, + JOB_NAME_CLOWN = JOB_HUD_CLOWN, + JOB_NAME_STAGEMAGICIAN = JOB_HUD_STAGEMAGICIAN, + JOB_NAME_BARBER = JOB_HUD_BARBER, + + // Cargo + "Cargo (Custom)" = JOB_HUD_RAWCARGO, + JOB_NAME_QUARTERMASTER = JOB_HUD_QUARTERMASTER, + JOB_NAME_CARGOTECHNICIAN = JOB_HUD_CARGOTECHNICIAN, + JOB_NAME_SHAFTMINER = JOB_HUD_SHAFTMINER, + + // R&D + "Science (Custom)" = JOB_HUD_RAWSCIENCE, + JOB_NAME_RESEARCHDIRECTOR = JOB_HUD_RESEARCHDIRECTOR, + JOB_NAME_SCIENTIST = JOB_HUD_SCIENTIST, + JOB_NAME_ROBOTICIST = JOB_HUD_ROBOTICIST, + JOB_NAME_EXPLORATIONCREW = JOB_HUD_EXPLORATIONCREW, + + // Engineering + "Engineering (Custom)" = JOB_HUD_RAWENGINEERING, + JOB_NAME_CHIEFENGINEER = JOB_HUD_CHIEFENGINEER, + JOB_NAME_STATIONENGINEER = JOB_HUD_STATIONENGINEER, + JOB_NAME_ATMOSPHERICTECHNICIAN = JOB_HUD_ATMOSPHERICTECHNICIAN, + + // Medical + "Medical (Custom)" = JOB_HUD_RAWMEDICAL, + JOB_NAME_CHIEFMEDICALOFFICER = JOB_HUD_CHEIFMEDICALOFFICIER, + JOB_NAME_MEDICALDOCTOR = JOB_HUD_MEDICALDOCTOR, + JOB_NAME_PARAMEDIC = JOB_HUD_PARAMEDIC, + JOB_NAME_VIROLOGIST = JOB_HUD_VIROLOGIST, + JOB_NAME_CHEMIST = JOB_HUD_CHEMIST, + JOB_NAME_GENETICIST = JOB_HUD_GENETICIST, + JOB_NAME_PSYCHIATRIST = JOB_HUD_PSYCHIATRIST, + + // Security + "Security (Custom)" = JOB_HUD_RAWSECURITY, + JOB_NAME_HEADOFSECURITY = JOB_HUD_HEADOFSECURITY, + JOB_NAME_SECURITYOFFICER = JOB_HUD_SECURITYOFFICER, + JOB_NAME_WARDEN = JOB_HUD_WARDEN, + JOB_NAME_DETECTIVE = JOB_HUD_DETECTIVE, + JOB_NAME_BRIGPHYSICIAN = JOB_HUD_BRIGPHYSICIAN, + JOB_NAME_DEPUTY = JOB_HUD_DEPUTY, + + // CentCom + "CentCom (Custom)" = JOB_HUD_RAWCENTCOM, + "CentCom" = JOB_HUD_CENTCOM, + "ERT" = JOB_HUD_CENTCOM, + + // ETC + JOB_NAME_VIP = JOB_HUD_VIP, + JOB_NAME_KING = JOB_HUD_KING, + "Syndicate Agent" = JOB_HUD_SYNDICATE, + "Clown Operative" = JOB_HUD_SYNDICATE, + "Unassigned" = JOB_HUD_UNKNOWN, + JOB_NAME_PRISONER = JOB_HUD_PRISONER +)) + // This returns a hud icon (from `hud.dmi`) by given job name. // Some custom title is from `PDApainter.dm`. You neec to check it if you're going to remove custom job. /proc/get_hud_by_jobname(jobname, returns_unknown=TRUE) if(!jobname) CRASH("The proc has taken a null value") - - var/static/id_to_hud = list( - // Command - "Command (Custom)" = JOB_HUD_RAWCOMMAND, - JOB_NAME_CAPTAIN = JOB_HUD_CAPTAIN, - "Acting Captain" = JOB_HUD_ACTINGCAPTAIN , - - // Service - "Service (Custom)" = JOB_HUD_RAWSERVICE, - JOB_NAME_HEADOFPERSONNEL = JOB_HUD_HEADOFPERSONNEL, - JOB_NAME_ASSISTANT = JOB_HUD_ASSISTANT, - JOB_NAME_BARTENDER = JOB_HUD_BARTENDER, - JOB_NAME_COOK = JOB_HUD_COOK, - JOB_NAME_BOTANIST = JOB_HUD_BOTANIST, - JOB_NAME_CURATOR = JOB_HUD_CURATOR, - JOB_NAME_CHAPLAIN = JOB_HUD_CHAPLAIN, - JOB_NAME_JANITOR = JOB_HUD_JANITOR, - JOB_NAME_LAWYER = JOB_HUD_LAWYER, - JOB_NAME_MIME = JOB_HUD_MIME, - JOB_NAME_CLOWN = JOB_HUD_CLOWN, - JOB_NAME_STAGEMAGICIAN = JOB_HUD_STAGEMAGICIAN, - JOB_NAME_BARBER = JOB_HUD_BARBER, - - // Cargo - "Cargo (Custom)" = JOB_HUD_RAWCARGO, - JOB_NAME_QUARTERMASTER = JOB_HUD_QUARTERMASTER, - JOB_NAME_CARGOTECHNICIAN = JOB_HUD_CARGOTECHNICIAN, - JOB_NAME_SHAFTMINER = JOB_HUD_SHAFTMINER, - - // R&D - "Science (Custom)" = JOB_HUD_RAWSCIENCE, - JOB_NAME_RESEARCHDIRECTOR = JOB_HUD_RESEARCHDIRECTOR, - JOB_NAME_SCIENTIST = JOB_HUD_SCIENTIST, - JOB_NAME_ROBOTICIST = JOB_HUD_ROBOTICIST, - JOB_NAME_EXPLORATIONCREW = JOB_HUD_EXPLORATIONCREW, - - // Engineering - "Engineering (Custom)" = JOB_HUD_RAWENGINEERING, - JOB_NAME_CHIEFENGINEER = JOB_HUD_CHIEFENGINEER, - JOB_NAME_STATIONENGINEER = JOB_HUD_STATIONENGINEER, - JOB_NAME_ATMOSPHERICTECHNICIAN = JOB_HUD_ATMOSPHERICTECHNICIAN, - - // Medical - "Medical (Custom)" = JOB_HUD_RAWMEDICAL, - JOB_NAME_CHIEFMEDICALOFFICER = JOB_HUD_CHEIFMEDICALOFFICIER, - JOB_NAME_MEDICALDOCTOR = JOB_HUD_MEDICALDOCTOR, - JOB_NAME_PARAMEDIC = JOB_HUD_PARAMEDIC, - JOB_NAME_VIROLOGIST = JOB_HUD_VIROLOGIST, - JOB_NAME_CHEMIST = JOB_HUD_CHEMIST, - JOB_NAME_GENETICIST = JOB_HUD_GENETICIST, - JOB_NAME_PSYCHIATRIST = JOB_HUD_PSYCHIATRIST, - - // Security - "Security (Custom)" = JOB_HUD_RAWSECURITY, - JOB_NAME_HEADOFSECURITY = JOB_HUD_HEADOFSECURITY, - JOB_NAME_SECURITYOFFICER = JOB_HUD_SECURITYOFFICER, - JOB_NAME_WARDEN = JOB_HUD_WARDEN, - JOB_NAME_DETECTIVE = JOB_HUD_DETECTIVE, - JOB_NAME_BRIGPHYSICIAN = JOB_HUD_BRIGPHYSICIAN, - JOB_NAME_DEPUTY = JOB_HUD_DEPUTY, - - // CentCom - "CentCom (Custom)" = JOB_HUD_RAWCENTCOM, - "CentCom" = JOB_HUD_CENTCOM, - "ERT" = JOB_HUD_CENTCOM, - - // ETC - JOB_NAME_VIP = JOB_HUD_VIP, - JOB_NAME_KING = JOB_HUD_KING, - "Syndicate Agent" = JOB_HUD_SYNDICATE, - "Clown Operative" = JOB_HUD_SYNDICATE, - "Unassigned" = JOB_HUD_UNKNOWN, - JOB_NAME_PRISONER = JOB_HUD_PRISONER - ) if(returns_unknown) - return id_to_hud[jobname] || JOB_HUD_UNKNOWN // default: a grey unknown hud - return id_to_hud[jobname] // this will return null + return GLOB.id_to_hud[jobname] || JOB_HUD_UNKNOWN // default: a grey unknown hud + return GLOB.id_to_hud[jobname] // this will return null // used to determine chat color by HUD in `chatmessage.dm` // Note: custom colors are what I really didn't put much attention into. feel free to change its color when you feel off. @@ -237,3 +237,12 @@ ) return hud_to_chatcolor[jobname] || JOB_CHATCOLOR_UNKNOWN +/proc/get_job_departments(field) + . = list() + for(var/flag in GLOB.bitflags) + var/key = "[flag]" + var/department = GLOB.dept_bitflag_to_name[key] + if(!department || !GLOB.departments[department]) + continue + if(CHECK_BITFIELD(field, flag)) + . += department diff --git a/code/_globalvars/lists/jobs.dm b/code/_globalvars/lists/jobs.dm new file mode 100644 index 0000000000000..1b9642e40217e --- /dev/null +++ b/code/_globalvars/lists/jobs.dm @@ -0,0 +1,30 @@ +/// A list of each bitflag and the name of its associated department. For use in the preferences menu. +GLOBAL_LIST_INIT(dept_bitflag_to_name, list( + "[DEPT_BITFLAG_COM]" = "Command", + "[DEPT_BITFLAG_CIV]" = "Civilian", + "[DEPT_BITFLAG_SRV]" = "Service", + "[DEPT_BITFLAG_CAR]" = "Cargo", + "[DEPT_BITFLAG_SCI]" = "Science", + "[DEPT_BITFLAG_ENG]" = "Engineering", + "[DEPT_BITFLAG_MED]" = "Medical", + "[DEPT_BITFLAG_SEC]" = "Security", + "[DEPT_BITFLAG_VIP]" = "Very Important People", + "[DEPT_BITFLAG_SILICON]" = "Silicon", + "[DEPT_BITFLAG_CAPTAIN]" = "Captain", + "[DEPT_BITFLAG_ASSISTANT]" = "Assistant" +)) + +/// A list of each department and its associated bitflag. +GLOBAL_LIST_INIT(departments, list( + "Command" = DEPT_BITFLAG_COM, + "Very Important People" = DEPT_BITFLAG_VIP, + "Security" = DEPT_BITFLAG_SEC, + "Engineering" = DEPT_BITFLAG_ENG, + "Medical" = DEPT_BITFLAG_MED, + "Science" = DEPT_BITFLAG_SCI, + "Supply" = DEPT_BITFLAG_CAR, + "Cargo" = DEPT_BITFLAG_CAR, // code seems to switch between calling it Supply and Cargo. not going to fix that today, let's just split the difference. + "Service" = DEPT_BITFLAG_SRV, + "Civilian" = DEPT_BITFLAG_CIV, + "Silicon" = DEPT_BITFLAG_SILICON +)) diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index e281ff55d85c1..fac24c94f9935 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -186,42 +186,27 @@ /datum/datacore/proc/get_manifest() var/list/manifest_out = list() - var/list/dept_list = list( - "Command" = DEPT_BITFLAG_COM, - "Very Important People" = DEPT_BITFLAG_VIP, - "Security" = DEPT_BITFLAG_SEC, - "Engineering" = DEPT_BITFLAG_ENG, - "Medical" = DEPT_BITFLAG_MED, - "Science" = DEPT_BITFLAG_SCI, - "Supply" = DEPT_BITFLAG_CAR, - "Service" = DEPT_BITFLAG_SRV, - "Civilian" = DEPT_BITFLAG_CIV, - "Silicon" = DEPT_BITFLAG_SILICON - ) + var/static/list/heads = make_associative(GLOB.command_positions + list(JOB_NAME_QUARTERMASTER)) + for(var/datum/data/record/t in GLOB.data_core.general) var/name = t.fields["name"] var/rank = t.fields["rank"] var/dept_bitflags = t.fields["active_dept"] var/has_department = FALSE - for(var/department in dept_list) - if(dept_bitflags & dept_list[department]) - if(!manifest_out[department]) - manifest_out[department] = list() - manifest_out[department] += list(list( - "name" = name, - "rank" = rank - )) - has_department = TRUE + var/entry = list("name" = name, "rank" = rank) + for(var/department in get_job_departments(dept_bitflags)) + var/list/department_manifest = manifest_out[department] + if(!department_manifest) + manifest_out[department] = department_manifest = list() + // Append to beginning of list if captain or department head + var/put_at_top = rank == JOB_NAME_CAPTAIN || (department != DEPT_COMMAND && heads[rank]) + department_manifest.Insert(put_at_top, list(entry)) + has_department = TRUE if(!has_department) - if(!manifest_out["Misc"]) - manifest_out["Misc"] = list() - manifest_out["Misc"] += list(list( - "name" = name, - "rank" = rank - )) + LAZYADDASSOCLIST(manifest_out, "Misc", entry) //Sort the list by 'departments' primarily so command is on top. var/list/sorted_out = list() - for(var/department in (dept_list += "Misc")) + for(var/department in (assoc_to_keys(GLOB.departments) + "Misc")) if(!isnull(manifest_out[department])) sorted_out[department] = manifest_out[department] return sorted_out diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index a71d849dc64e5..1656c0ddc7fa4 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -115,7 +115,7 @@ var/last_completed_asset_job = 0 /// rate limiting for the crew manifest - var/crew_manifest_delay + COOLDOWN_DECLARE(crew_manifest_delay) //Tick when ghost roles are useable again var/next_ghost_role_tick = 0 diff --git a/code/modules/mob/dead/crew_manifest.dm b/code/modules/mob/dead/crew_manifest.dm new file mode 100644 index 0000000000000..13ed4a55255d9 --- /dev/null +++ b/code/modules/mob/dead/crew_manifest.dm @@ -0,0 +1,50 @@ +GLOBAL_DATUM_INIT(crew_manifest_tgui, /datum/crew_manifest, new) + +/datum/crew_manifest + +/datum/crew_manifest/ui_state(mob/user) + return GLOB.always_state + +/datum/crew_manifest/ui_status(mob/user, datum/ui_state/state) + var/static/list/allowed_mobs_typecache = typecacheof(list(/mob/dead, /mob/living/silicon)) + return (is_type_in_typecache(user, allowed_mobs_typecache) || user.client?.holder) ? UI_INTERACTIVE : UI_CLOSE + +/datum/crew_manifest/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "CrewManifest") + ui.set_autoupdate(TRUE) + ui.open() + +/datum/crew_manifest/ui_static_data(mob/user) + var/static/list/job_ordering = list_to_assoc_index(GLOB.command_positions + GLOB.engineering_positions + GLOB.supply_positions + (GLOB.nonhuman_positions - "pAI") + GLOB.civilian_positions + GLOB.gimmick_positions + GLOB.medical_positions + GLOB.science_positions + GLOB.security_positions) + return list( + "command" = list( + "jobs" = GLOB.command_positions, + "order" = SSjob.chain_of_command + ), + "icons" = GLOB.id_to_hud, + "order" = job_ordering + ) + +/datum/crew_manifest/ui_data(mob/user) + var/list/positions = list() + for(var/datum/job/job in SSjob.occupations) + // Check if there are additional open positions or if there is no limit + for(var/department in get_job_departments(job.departments)) + var/list/department_info = positions[department] + if(!department_info) + positions[department] = department_info = list("exceptions" = list(), "open" = 0) + if(job.total_positions == -1) + // Add job to list of exceptions, meaning it does not have a position limit + department_info["exceptions"] |= job.title + else + // Add open positions to current department + department_info["open"] += max(0, job.total_positions - job.current_positions) + return list( + "manifest" = GLOB.data_core.get_manifest(), + "positions" = positions + ) + +/datum/crew_manifest/ui_assets(mob/user) + return list(get_asset_datum(/datum/asset/spritesheet/job_icons)) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 25ae9136a0091..82c101da2f0f4 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -477,17 +477,10 @@ qdel(src) /mob/dead/new_player/proc/ViewManifest() - if(!client) - return - if(world.time < client.crew_manifest_delay) + if(!client || !COOLDOWN_FINISHED(client, crew_manifest_delay)) return - client.crew_manifest_delay = world.time + (1 SECONDS) - - var/dat = "" - dat += "

Crew Manifest

" - dat += GLOB.data_core.get_manifest_html() - - src << browse(dat, "window=manifest;size=387x420;can_close=1") + COOLDOWN_START(client, crew_manifest_delay, 1 SECONDS) + GLOB.crew_manifest_tgui.ui_interact(src) /mob/dead/new_player/Move() return 0 diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 55b5c0d317317..c59a2060159d8 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -671,17 +671,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set name = "View Crew Manifest" set category = "Ghost" - if(!client) - return - if(world.time < client.crew_manifest_delay) + if(!client || !COOLDOWN_FINISHED(client, crew_manifest_delay)) return - client.crew_manifest_delay = world.time + (1 SECONDS) - - var/dat - dat += "

Crew Manifest

" - dat += GLOB.data_core.get_manifest_html() - - src << browse(dat, "window=manifest;size=387x420;can_close=1") + COOLDOWN_START(client, crew_manifest_delay, 1 SECONDS) + GLOB.crew_manifest_tgui.ui_interact(src) //this is called when a ghost is drag clicked to something. /mob/dead/observer/MouseDrop(atom/over) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 4df434aabd866..4ed9a04c30f0d 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -374,15 +374,10 @@ usr << browse(list, "window=laws") /mob/living/silicon/proc/ai_roster() - if(!client) + if(!client || !COOLDOWN_FINISHED(client, crew_manifest_delay)) return - if(world.time < client.crew_manifest_delay) - return - client.crew_manifest_delay = world.time + (1 SECONDS) - - var/datum/browser/popup = new(src, "airoster", "Crew Manifest", 387, 420) - popup.set_content(GLOB.data_core.get_manifest_html()) - popup.open() + COOLDOWN_START(client, crew_manifest_delay, 1 SECONDS) + GLOB.crew_manifest_tgui.ui_interact(src) /mob/living/silicon/proc/set_autosay() //For allowing the AI and borgs to set the radio behavior of auto announcements (state laws, arrivals). if(!radio) diff --git a/tgui/packages/tgui/interfaces/CrewManifest.tsx b/tgui/packages/tgui/interfaces/CrewManifest.tsx new file mode 100644 index 0000000000000..41126db60f09b --- /dev/null +++ b/tgui/packages/tgui/interfaces/CrewManifest.tsx @@ -0,0 +1,121 @@ +import { sortBy } from '../../common/collections'; +import { classes } from 'common/react'; +import { useBackend } from '../backend'; +import { Box, Icon, Section, Table, Tooltip, Flex } from '../components'; +import { Window } from '../layouts'; + +type DepartmentPositions = { [department: string]: DepartmentInfo }; +type DepartmentCrew = { [department: string]: ManifestEntry[] }; +type JobIcons = { [job: string]: string }; +type JobOrdering = { [job: string]: number }; + +const sortSpecific = (entries: ManifestEntry[], chain: JobOrdering) => + sortBy((entry) => chain[entry.rank] ?? Object.keys(chain).length + 1)(entries); + +type DepartmentInfo = { + /** A list of jobs that have no position lab. */ + exceptions: string[]; + /** How many open positions this department has. */ + open: number; +}; + +type ManifestEntry = { + /** The name of this crew member. */ + name: string; + /** The rank of this crew member. */ + rank: string; +}; + +type CommandInfo = { + /** A (static) list of jobs considered to be command roles. */ + jobs: string[]; + /** The ordering of which heads of staff should be listed in the command section, according to chain of command. */ + order: JobOrdering; +}; + +type CrewManifestData = { + /** Information pertaining to the command department */ + command: CommandInfo; + /** A (static) list of which jobs use what icons. */ + icons: JobIcons; + /** The crew staffing each department. */ + manifest: DepartmentCrew; + /** How many positions each department has open. */ + positions: DepartmentPositions; + /** The ordering of which jobs should be listed. */ + order: JobOrdering; +}; + +export const CrewManifest = (_props, context) => { + const { + data: { command, order, icons, manifest, positions }, + } = useBackend(context); + + return ( + + + {Object.entries(manifest).map(([dept, crew]) => { + const department_positions = positions[dept] || []; + const sorted_jobs = dept === 'Command' ? sortSpecific(crew, command.order) : sortSpecific(crew, order); + return ( +
+ {dept} + {dept !== 'Misc' && ({department_positions.open} positions open)} + + }> + + {Object.entries(sorted_jobs).map(([crewIndex, crewMember]) => { + const icon = icons[crewMember.rank] || 'unknown'; + const exceptions = department_positions.exceptions || []; + const is_command = command.jobs.includes(crewMember.rank); + return ( + + + {crewMember.name} + + + {exceptions.includes(crewMember.rank) && ( + + + + )} + {is_command && ( + + + + )} + + + + {crewMember.rank} + + + ); + })} +
+
+ ); + })} +
+
+ ); +}; diff --git a/tgui/packages/tgui/styles/interfaces/CrewManifest.scss b/tgui/packages/tgui/styles/interfaces/CrewManifest.scss new file mode 100644 index 0000000000000..6a52447f1c152 --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/CrewManifest.scss @@ -0,0 +1,57 @@ +@use '../colors.scss'; + +$department_map: ( + 'Command': colors.$yellow, + 'Security': colors.$red, + 'Engineering': colors.$orange, + 'Medical': colors.$teal, + 'Misc': colors.$white, + 'Science': colors.$purple, + 'Supply': colors.$brown, + 'Service': colors.$green, + 'Silicon': colors.$pink, +); + +.CrewManifest { + @each $department-name, $color-value in $department_map { + &--#{$department-name} { + .Section { + &__title { + border-color: $color-value; + } + &__titleText { + color: $color-value; + } + } + } + } + + &__Cell { + padding: 3px 0; + + &--Rank { + color: colors.$label; + } + } + + &__Icons { + text-align: right; + } + + &__Icon { + color: colors.$label; + position: relative; + + &:not(:last-child) { + margin-right: 7px; + } + + &--Chevron { + padding-right: 2px; + } + + &--Command { + color: colors.$yellow; + } + } +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 9806b94565c50..892f72a63a69a 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -49,6 +49,7 @@ @include meta.load-css('./interfaces/ColorPicker.scss'); @include meta.load-css('./interfaces/AntagInfo.scss'); @include meta.load-css('./interfaces/CameraConsole.scss'); +@include meta.load-css('./interfaces/CrewManifest.scss'); @include meta.load-css('./interfaces/ClockworkSlab.scss'); @include meta.load-css('./interfaces/NuclearBomb.scss'); @include meta.load-css('./interfaces/ModularFabricator.scss'); From 07d34e5e620e8454a20773274ba553ade788588c Mon Sep 17 00:00:00 2001 From: Lucy Date: Fri, 15 Sep 2023 22:38:51 -0400 Subject: [PATCH 02/12] Merge department color SCSS --- tgui/packages/tgui/styles/colors.scss | 15 +++++++++++++++ .../tgui/styles/interfaces/CrewManifest.scss | 14 +------------- .../tgui/styles/interfaces/PreferencesMenu.scss | 16 +--------------- 3 files changed, 17 insertions(+), 28 deletions(-) diff --git a/tgui/packages/tgui/styles/colors.scss b/tgui/packages/tgui/styles/colors.scss index d1a9fc4fd9de9..24c6a6acd1239 100644 --- a/tgui/packages/tgui/styles/colors.scss +++ b/tgui/packages/tgui/styles/colors.scss @@ -90,3 +90,18 @@ $bg-map: (); ) ); } + +$departments: ( + 'Assistant': $grey, + 'Captain': fg($blue), + 'Cargo': $brown, + 'Supply': $brown, + 'Civilian': $grey, + 'Command': $yellow, + 'Security': $red, + 'Engineering': #f1a839, + 'Medical': $teal, + 'Science': fg($purple), + 'Service': $green, + 'Silicon': $pink, +); diff --git a/tgui/packages/tgui/styles/interfaces/CrewManifest.scss b/tgui/packages/tgui/styles/interfaces/CrewManifest.scss index 6a52447f1c152..5adb4a0806e74 100644 --- a/tgui/packages/tgui/styles/interfaces/CrewManifest.scss +++ b/tgui/packages/tgui/styles/interfaces/CrewManifest.scss @@ -1,19 +1,7 @@ @use '../colors.scss'; -$department_map: ( - 'Command': colors.$yellow, - 'Security': colors.$red, - 'Engineering': colors.$orange, - 'Medical': colors.$teal, - 'Misc': colors.$white, - 'Science': colors.$purple, - 'Supply': colors.$brown, - 'Service': colors.$green, - 'Silicon': colors.$pink, -); - .CrewManifest { - @each $department-name, $color-value in $department_map { + @each $department-name, $color-value in colors.$department_map { &--#{$department-name} { .Section { &__title { diff --git a/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss index d19ce6445b48a..91acb4464e6d4 100644 --- a/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss +++ b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss @@ -3,20 +3,6 @@ @use '../components/Button.scss'; @use '../colors.scss'; -$department_map: ( - 'Assistant': colors.$grey, - 'Captain': colors.fg(colors.$blue), - 'Cargo': colors.$brown, - 'Civilian': colors.$grey, - 'Command': colors.$yellow, - 'Security': colors.$red, - 'Engineering': #f1a839, - 'Medical': colors.$teal, - 'Science': colors.fg(colors.$purple), - 'Service': colors.$green, - 'Silicon': colors.$pink, -); - .PreferencesMenu { &__Main { .Preferences__standard-palette { @@ -224,7 +210,7 @@ $department_map: ( } &__departments { - @each $department-name, $color-value in $department_map { + @each $department-name, $color-value in colors.$departments { &--#{$department-name} { &.head { background: $color-value; From 0b18540967c6de63ccd976a0aafdbf09b2ae703e Mon Sep 17 00:00:00 2001 From: Lucy Date: Fri, 15 Sep 2023 23:10:50 -0400 Subject: [PATCH 03/12] fixy --- tgui/packages/tgui/styles/interfaces/CrewManifest.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tgui/packages/tgui/styles/interfaces/CrewManifest.scss b/tgui/packages/tgui/styles/interfaces/CrewManifest.scss index 5adb4a0806e74..463eb46c56eca 100644 --- a/tgui/packages/tgui/styles/interfaces/CrewManifest.scss +++ b/tgui/packages/tgui/styles/interfaces/CrewManifest.scss @@ -1,7 +1,7 @@ @use '../colors.scss'; .CrewManifest { - @each $department-name, $color-value in colors.$department_map { + @each $department-name, $color-value in colors.$departments { &--#{$department-name} { .Section { &__title { From 81ac0d44a8922ed5934ccd59aef703b3808444c7 Mon Sep 17 00:00:00 2001 From: Lucy Date: Sat, 16 Sep 2023 22:02:08 -0400 Subject: [PATCH 04/12] Some improvements. --- code/__HELPERS/jobs.dm | 2 ++ code/datums/datacore.dm | 5 +++-- tgui/packages/tgui/interfaces/CrewManifest.tsx | 14 +++++--------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/code/__HELPERS/jobs.dm b/code/__HELPERS/jobs.dm index 5891fc3cf4085..0d33b803260bb 100644 --- a/code/__HELPERS/jobs.dm +++ b/code/__HELPERS/jobs.dm @@ -74,6 +74,8 @@ ) return id_style[jobname] || "noname" // default: a card with no shape +// You really shouldn't use this directly. +// Use get_hud_by_jobname unless you NEED direct access to this, i.e for the crew manifest tgui data GLOBAL_LIST_INIT(id_to_hud, list( // Command "Command (Custom)" = JOB_HUD_RAWCOMMAND, diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index fac24c94f9935..998e0c422c49f 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -186,14 +186,15 @@ /datum/datacore/proc/get_manifest() var/list/manifest_out = list() - var/static/list/heads = make_associative(GLOB.command_positions + list(JOB_NAME_QUARTERMASTER)) + var/static/list/heads = make_associative(GLOB.command_positions) for(var/datum/data/record/t in GLOB.data_core.general) var/name = t.fields["name"] var/rank = t.fields["rank"] var/dept_bitflags = t.fields["active_dept"] + var/hud = t.fields["hud"] var/has_department = FALSE - var/entry = list("name" = name, "rank" = rank) + var/entry = list("name" = name, "rank" = rank, "hud" = hud) for(var/department in get_job_departments(dept_bitflags)) var/list/department_manifest = manifest_out[department] if(!department_manifest) diff --git a/tgui/packages/tgui/interfaces/CrewManifest.tsx b/tgui/packages/tgui/interfaces/CrewManifest.tsx index 41126db60f09b..15f4e626a2d00 100644 --- a/tgui/packages/tgui/interfaces/CrewManifest.tsx +++ b/tgui/packages/tgui/interfaces/CrewManifest.tsx @@ -6,7 +6,6 @@ import { Window } from '../layouts'; type DepartmentPositions = { [department: string]: DepartmentInfo }; type DepartmentCrew = { [department: string]: ManifestEntry[] }; -type JobIcons = { [job: string]: string }; type JobOrdering = { [job: string]: number }; const sortSpecific = (entries: ManifestEntry[], chain: JobOrdering) => @@ -24,6 +23,8 @@ type ManifestEntry = { name: string; /** The rank of this crew member. */ rank: string; + /** The HUD icon of this crew member. */ + hud: string; }; type CommandInfo = { @@ -36,8 +37,6 @@ type CommandInfo = { type CrewManifestData = { /** Information pertaining to the command department */ command: CommandInfo; - /** A (static) list of which jobs use what icons. */ - icons: JobIcons; /** The crew staffing each department. */ manifest: DepartmentCrew; /** How many positions each department has open. */ @@ -48,7 +47,7 @@ type CrewManifestData = { export const CrewManifest = (_props, context) => { const { - data: { command, order, icons, manifest, positions }, + data: { command, order, manifest, positions }, } = useBackend(context); return ( @@ -69,14 +68,11 @@ export const CrewManifest = (_props, context) => { }> {Object.entries(sorted_jobs).map(([crewIndex, crewMember]) => { - const icon = icons[crewMember.rank] || 'unknown'; const exceptions = department_positions.exceptions || []; const is_command = command.jobs.includes(crewMember.rank); return ( - + {crewMember.name} @@ -102,7 +98,7 @@ export const CrewManifest = (_props, context) => { mr={0.5} ml={-0.5} style={{ 'transform': 'translateY(18.75%)' }} - className={`job-icon16x16 job-icon-hud${icon}`} + className={`job-icon16x16 job-icon-hud${crewMember.hud}`} /> From cddbd16874514168e7de17c40425dd1ba27a3dd7 Mon Sep 17 00:00:00 2001 From: Lucy Date: Sat, 16 Sep 2023 22:48:59 -0400 Subject: [PATCH 05/12] More tweaks to satisfy reviews --- code/__HELPERS/jobs.dm | 27 ++++++++++---- code/modules/mob/dead/crew_manifest.dm | 26 +++---------- .../packages/tgui/interfaces/CrewManifest.tsx | 37 ++++++------------- 3 files changed, 36 insertions(+), 54 deletions(-) diff --git a/code/__HELPERS/jobs.dm b/code/__HELPERS/jobs.dm index 0d33b803260bb..c51f774726a49 100644 --- a/code/__HELPERS/jobs.dm +++ b/code/__HELPERS/jobs.dm @@ -78,12 +78,11 @@ // Use get_hud_by_jobname unless you NEED direct access to this, i.e for the crew manifest tgui data GLOBAL_LIST_INIT(id_to_hud, list( // Command - "Command (Custom)" = JOB_HUD_RAWCOMMAND, JOB_NAME_CAPTAIN = JOB_HUD_CAPTAIN, "Acting Captain" = JOB_HUD_ACTINGCAPTAIN , + "Command (Custom)" = JOB_HUD_RAWCOMMAND, // Service - "Service (Custom)" = JOB_HUD_RAWSERVICE, JOB_NAME_HEADOFPERSONNEL = JOB_HUD_HEADOFPERSONNEL, JOB_NAME_ASSISTANT = JOB_HUD_ASSISTANT, JOB_NAME_BARTENDER = JOB_HUD_BARTENDER, @@ -97,28 +96,28 @@ GLOBAL_LIST_INIT(id_to_hud, list( JOB_NAME_CLOWN = JOB_HUD_CLOWN, JOB_NAME_STAGEMAGICIAN = JOB_HUD_STAGEMAGICIAN, JOB_NAME_BARBER = JOB_HUD_BARBER, + "Service (Custom)" = JOB_HUD_RAWSERVICE, // Cargo - "Cargo (Custom)" = JOB_HUD_RAWCARGO, JOB_NAME_QUARTERMASTER = JOB_HUD_QUARTERMASTER, JOB_NAME_CARGOTECHNICIAN = JOB_HUD_CARGOTECHNICIAN, JOB_NAME_SHAFTMINER = JOB_HUD_SHAFTMINER, + "Cargo (Custom)" = JOB_HUD_RAWCARGO, // R&D - "Science (Custom)" = JOB_HUD_RAWSCIENCE, JOB_NAME_RESEARCHDIRECTOR = JOB_HUD_RESEARCHDIRECTOR, JOB_NAME_SCIENTIST = JOB_HUD_SCIENTIST, JOB_NAME_ROBOTICIST = JOB_HUD_ROBOTICIST, JOB_NAME_EXPLORATIONCREW = JOB_HUD_EXPLORATIONCREW, + "Science (Custom)" = JOB_HUD_RAWSCIENCE, // Engineering - "Engineering (Custom)" = JOB_HUD_RAWENGINEERING, JOB_NAME_CHIEFENGINEER = JOB_HUD_CHIEFENGINEER, JOB_NAME_STATIONENGINEER = JOB_HUD_STATIONENGINEER, JOB_NAME_ATMOSPHERICTECHNICIAN = JOB_HUD_ATMOSPHERICTECHNICIAN, + "Engineering (Custom)" = JOB_HUD_RAWENGINEERING, // Medical - "Medical (Custom)" = JOB_HUD_RAWMEDICAL, JOB_NAME_CHIEFMEDICALOFFICER = JOB_HUD_CHEIFMEDICALOFFICIER, JOB_NAME_MEDICALDOCTOR = JOB_HUD_MEDICALDOCTOR, JOB_NAME_PARAMEDIC = JOB_HUD_PARAMEDIC, @@ -126,20 +125,21 @@ GLOBAL_LIST_INIT(id_to_hud, list( JOB_NAME_CHEMIST = JOB_HUD_CHEMIST, JOB_NAME_GENETICIST = JOB_HUD_GENETICIST, JOB_NAME_PSYCHIATRIST = JOB_HUD_PSYCHIATRIST, + "Medical (Custom)" = JOB_HUD_RAWMEDICAL, // Security - "Security (Custom)" = JOB_HUD_RAWSECURITY, JOB_NAME_HEADOFSECURITY = JOB_HUD_HEADOFSECURITY, JOB_NAME_SECURITYOFFICER = JOB_HUD_SECURITYOFFICER, JOB_NAME_WARDEN = JOB_HUD_WARDEN, JOB_NAME_DETECTIVE = JOB_HUD_DETECTIVE, JOB_NAME_BRIGPHYSICIAN = JOB_HUD_BRIGPHYSICIAN, JOB_NAME_DEPUTY = JOB_HUD_DEPUTY, + "Security (Custom)" = JOB_HUD_RAWSECURITY, // CentCom - "CentCom (Custom)" = JOB_HUD_RAWCENTCOM, "CentCom" = JOB_HUD_CENTCOM, "ERT" = JOB_HUD_CENTCOM, + "CentCom (Custom)" = JOB_HUD_RAWCENTCOM, // ETC JOB_NAME_VIP = JOB_HUD_VIP, @@ -150,6 +150,17 @@ GLOBAL_LIST_INIT(id_to_hud, list( JOB_NAME_PRISONER = JOB_HUD_PRISONER )) +GLOBAL_LIST_INIT(command_huds, list( + JOB_HUD_CAPTAIN, + JOB_HUD_ACTINGCAPTAIN, + JOB_HUD_RAWCOMMAND, + JOB_HUD_HEADOFPERSONNEL, + JOB_HUD_RESEARCHDIRECTOR, + JOB_HUD_CHIEFENGINEER, + JOB_HUD_CHEIFMEDICALOFFICIER, + JOB_HUD_HEADOFSECURITY +)) + // This returns a hud icon (from `hud.dmi`) by given job name. // Some custom title is from `PDApainter.dm`. You neec to check it if you're going to remove custom job. /proc/get_hud_by_jobname(jobname, returns_unknown=TRUE) diff --git a/code/modules/mob/dead/crew_manifest.dm b/code/modules/mob/dead/crew_manifest.dm index 13ed4a55255d9..3d9e923dc3744 100644 --- a/code/modules/mob/dead/crew_manifest.dm +++ b/code/modules/mob/dead/crew_manifest.dm @@ -7,7 +7,7 @@ GLOBAL_DATUM_INIT(crew_manifest_tgui, /datum/crew_manifest, new) /datum/crew_manifest/ui_status(mob/user, datum/ui_state/state) var/static/list/allowed_mobs_typecache = typecacheof(list(/mob/dead, /mob/living/silicon)) - return (is_type_in_typecache(user, allowed_mobs_typecache) || user.client?.holder) ? UI_INTERACTIVE : UI_CLOSE + return is_type_in_typecache(user, allowed_mobs_typecache) ? UI_INTERACTIVE : UI_CLOSE /datum/crew_manifest/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) @@ -17,34 +17,18 @@ GLOBAL_DATUM_INIT(crew_manifest_tgui, /datum/crew_manifest, new) ui.open() /datum/crew_manifest/ui_static_data(mob/user) - var/static/list/job_ordering = list_to_assoc_index(GLOB.command_positions + GLOB.engineering_positions + GLOB.supply_positions + (GLOB.nonhuman_positions - "pAI") + GLOB.civilian_positions + GLOB.gimmick_positions + GLOB.medical_positions + GLOB.science_positions + GLOB.security_positions) return list( "command" = list( + "huds" = GLOB.command_huds, "jobs" = GLOB.command_positions, "order" = SSjob.chain_of_command ), - "icons" = GLOB.id_to_hud, - "order" = job_ordering + "order" = list_to_assoc_index(flatten_list(GLOB.id_to_hud)), + "generic" = isdead(user) ) /datum/crew_manifest/ui_data(mob/user) - var/list/positions = list() - for(var/datum/job/job in SSjob.occupations) - // Check if there are additional open positions or if there is no limit - for(var/department in get_job_departments(job.departments)) - var/list/department_info = positions[department] - if(!department_info) - positions[department] = department_info = list("exceptions" = list(), "open" = 0) - if(job.total_positions == -1) - // Add job to list of exceptions, meaning it does not have a position limit - department_info["exceptions"] |= job.title - else - // Add open positions to current department - department_info["open"] += max(0, job.total_positions - job.current_positions) - return list( - "manifest" = GLOB.data_core.get_manifest(), - "positions" = positions - ) + return list("manifest" = GLOB.data_core.get_manifest()) /datum/crew_manifest/ui_assets(mob/user) return list(get_asset_datum(/datum/asset/spritesheet/job_icons)) diff --git a/tgui/packages/tgui/interfaces/CrewManifest.tsx b/tgui/packages/tgui/interfaces/CrewManifest.tsx index 15f4e626a2d00..0dcb1e797780f 100644 --- a/tgui/packages/tgui/interfaces/CrewManifest.tsx +++ b/tgui/packages/tgui/interfaces/CrewManifest.tsx @@ -1,5 +1,5 @@ import { sortBy } from '../../common/collections'; -import { classes } from 'common/react'; +import { BooleanLike, classes } from 'common/react'; import { useBackend } from '../backend'; import { Box, Icon, Section, Table, Tooltip, Flex } from '../components'; import { Window } from '../layouts'; @@ -9,7 +9,7 @@ type DepartmentCrew = { [department: string]: ManifestEntry[] }; type JobOrdering = { [job: string]: number }; const sortSpecific = (entries: ManifestEntry[], chain: JobOrdering) => - sortBy((entry) => chain[entry.rank] ?? Object.keys(chain).length + 1)(entries); + sortBy((entry) => chain[entry.hud] ?? Object.keys(chain).length + 1)(entries); type DepartmentInfo = { /** A list of jobs that have no position lab. */ @@ -28,7 +28,9 @@ type ManifestEntry = { }; type CommandInfo = { - /** A (static) list of jobs considered to be command roles. */ + /** A (static) list of HUD icons used by command roles. */ + huds: string[]; + /** A (static) list of job titles considered to be command roles. */ jobs: string[]; /** The ordering of which heads of staff should be listed in the command section, according to chain of command. */ order: JobOrdering; @@ -39,48 +41,33 @@ type CrewManifestData = { command: CommandInfo; /** The crew staffing each department. */ manifest: DepartmentCrew; - /** How many positions each department has open. */ - positions: DepartmentPositions; - /** The ordering of which jobs should be listed. */ + /** The ordering of which jobs should be listed, based on HUD icon. */ order: JobOrdering; + /** Use the generic TGUI theme. */ + generic: BooleanLike; }; export const CrewManifest = (_props, context) => { const { - data: { command, order, manifest, positions }, + data: { command, order, manifest, generic }, } = useBackend(context); return ( - + {Object.entries(manifest).map(([dept, crew]) => { - const department_positions = positions[dept] || []; const sorted_jobs = dept === 'Command' ? sortSpecific(crew, command.order) : sortSpecific(crew, order); return ( -
- {dept} - {dept !== 'Misc' && ({department_positions.open} positions open)} - - }> +
{Object.entries(sorted_jobs).map(([crewIndex, crewMember]) => { - const exceptions = department_positions.exceptions || []; - const is_command = command.jobs.includes(crewMember.rank); + const is_command = command.huds.includes(crewMember.hud) || command.jobs.includes(crewMember.rank); return ( {crewMember.name} - {exceptions.includes(crewMember.rank) && ( - - - - )} {is_command && ( Date: Sat, 16 Sep 2023 23:00:47 -0400 Subject: [PATCH 06/12] Remove unused typedefs --- tgui/packages/tgui/interfaces/CrewManifest.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tgui/packages/tgui/interfaces/CrewManifest.tsx b/tgui/packages/tgui/interfaces/CrewManifest.tsx index 0dcb1e797780f..3ba88126ac3ef 100644 --- a/tgui/packages/tgui/interfaces/CrewManifest.tsx +++ b/tgui/packages/tgui/interfaces/CrewManifest.tsx @@ -4,20 +4,12 @@ import { useBackend } from '../backend'; import { Box, Icon, Section, Table, Tooltip, Flex } from '../components'; import { Window } from '../layouts'; -type DepartmentPositions = { [department: string]: DepartmentInfo }; type DepartmentCrew = { [department: string]: ManifestEntry[] }; type JobOrdering = { [job: string]: number }; const sortSpecific = (entries: ManifestEntry[], chain: JobOrdering) => sortBy((entry) => chain[entry.hud] ?? Object.keys(chain).length + 1)(entries); -type DepartmentInfo = { - /** A list of jobs that have no position lab. */ - exceptions: string[]; - /** How many open positions this department has. */ - open: number; -}; - type ManifestEntry = { /** The name of this crew member. */ name: string; From 6df2eae4a083f46368b4346375dd6b72b29cfc01 Mon Sep 17 00:00:00 2001 From: Lucy Date: Sat, 16 Sep 2023 23:00:57 -0400 Subject: [PATCH 07/12] And an unused import --- tgui/packages/tgui/interfaces/CrewManifest.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tgui/packages/tgui/interfaces/CrewManifest.tsx b/tgui/packages/tgui/interfaces/CrewManifest.tsx index 3ba88126ac3ef..ec5551beee943 100644 --- a/tgui/packages/tgui/interfaces/CrewManifest.tsx +++ b/tgui/packages/tgui/interfaces/CrewManifest.tsx @@ -1,7 +1,7 @@ import { sortBy } from '../../common/collections'; import { BooleanLike, classes } from 'common/react'; import { useBackend } from '../backend'; -import { Box, Icon, Section, Table, Tooltip, Flex } from '../components'; +import { Box, Icon, Section, Table, Tooltip } from '../components'; import { Window } from '../layouts'; type DepartmentCrew = { [department: string]: ManifestEntry[] }; From e27d2ecab9f57780397c40ebff022c90f2ae414b Mon Sep 17 00:00:00 2001 From: Lucy Date: Sun, 17 Sep 2023 10:28:06 -0400 Subject: [PATCH 08/12] Address reviews --- code/modules/mob/dead/crew_manifest.dm | 6 +++--- tgui/packages/tgui/interfaces/CrewManifest.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/dead/crew_manifest.dm b/code/modules/mob/dead/crew_manifest.dm index 3d9e923dc3744..ab40c53ffd9cf 100644 --- a/code/modules/mob/dead/crew_manifest.dm +++ b/code/modules/mob/dead/crew_manifest.dm @@ -17,18 +17,18 @@ GLOBAL_DATUM_INIT(crew_manifest_tgui, /datum/crew_manifest, new) ui.open() /datum/crew_manifest/ui_static_data(mob/user) + var/static/list/ordering = list_to_assoc_index(flatten_list(GLOB.id_to_hud)) return list( "command" = list( "huds" = GLOB.command_huds, "jobs" = GLOB.command_positions, "order" = SSjob.chain_of_command ), - "order" = list_to_assoc_index(flatten_list(GLOB.id_to_hud)), - "generic" = isdead(user) + "order" = ordering, ) /datum/crew_manifest/ui_data(mob/user) - return list("manifest" = GLOB.data_core.get_manifest()) + return list("manifest" = GLOB.data_core.get_manifest(), "generic" = isdead(user)) /datum/crew_manifest/ui_assets(mob/user) return list(get_asset_datum(/datum/asset/spritesheet/job_icons)) diff --git a/tgui/packages/tgui/interfaces/CrewManifest.tsx b/tgui/packages/tgui/interfaces/CrewManifest.tsx index ec5551beee943..82bcb68f467c9 100644 --- a/tgui/packages/tgui/interfaces/CrewManifest.tsx +++ b/tgui/packages/tgui/interfaces/CrewManifest.tsx @@ -50,7 +50,7 @@ export const CrewManifest = (_props, context) => { {Object.entries(manifest).map(([dept, crew]) => { const sorted_jobs = dept === 'Command' ? sortSpecific(crew, command.order) : sortSpecific(crew, order); return ( -
+
{Object.entries(sorted_jobs).map(([crewIndex, crewMember]) => { const is_command = command.huds.includes(crewMember.hud) || command.jobs.includes(crewMember.rank); From 53a1ac62b6b365bcecf93b0f6ef97e88557ea605 Mon Sep 17 00:00:00 2001 From: Lucy Date: Sun, 17 Sep 2023 10:30:07 -0400 Subject: [PATCH 09/12] That looks less ugly... --- code/datums/datacore.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 998e0c422c49f..ead89498a69b3 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -191,8 +191,8 @@ for(var/datum/data/record/t in GLOB.data_core.general) var/name = t.fields["name"] var/rank = t.fields["rank"] - var/dept_bitflags = t.fields["active_dept"] var/hud = t.fields["hud"] + var/dept_bitflags = t.fields["active_dept"] var/has_department = FALSE var/entry = list("name" = name, "rank" = rank, "hud" = hud) for(var/department in get_job_departments(dept_bitflags)) From f966dddd1177c0cab33fafeffde78e009429d961 Mon Sep 17 00:00:00 2001 From: Lucy Date: Mon, 18 Sep 2023 16:51:57 -0400 Subject: [PATCH 10/12] Candystripe --- tgui/packages/tgui/interfaces/CrewManifest.tsx | 8 ++++---- tgui/packages/tgui/styles/interfaces/CrewManifest.scss | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tgui/packages/tgui/interfaces/CrewManifest.tsx b/tgui/packages/tgui/interfaces/CrewManifest.tsx index 82bcb68f467c9..d93d14850773c 100644 --- a/tgui/packages/tgui/interfaces/CrewManifest.tsx +++ b/tgui/packages/tgui/interfaces/CrewManifest.tsx @@ -55,8 +55,8 @@ export const CrewManifest = (_props, context) => { {Object.entries(sorted_jobs).map(([crewIndex, crewMember]) => { const is_command = command.huds.includes(crewMember.hud) || command.jobs.includes(crewMember.rank); return ( - - + + {crewMember.name} @@ -76,11 +76,11 @@ export const CrewManifest = (_props, context) => { inline mr={0.5} ml={-0.5} - style={{ 'transform': 'translateY(18.75%)' }} + style={{ 'vertical-align': 'middle' }} className={`job-icon16x16 job-icon-hud${crewMember.hud}`} /> - + {crewMember.rank} diff --git a/tgui/packages/tgui/styles/interfaces/CrewManifest.scss b/tgui/packages/tgui/styles/interfaces/CrewManifest.scss index 463eb46c56eca..af1b8fb0a8f29 100644 --- a/tgui/packages/tgui/styles/interfaces/CrewManifest.scss +++ b/tgui/packages/tgui/styles/interfaces/CrewManifest.scss @@ -16,6 +16,7 @@ &__Cell { padding: 3px 0; + vertical-align: middle; &--Rank { color: colors.$label; From 5ff5ac42f097a43a79d1db2ff5459f6d09adf18a Mon Sep 17 00:00:00 2001 From: Lucy Date: Wed, 20 Sep 2023 16:22:41 -0400 Subject: [PATCH 11/12] No more autoupdate --- code/__DEFINES/dcs/signals/signals_global.dm | 2 ++ code/datums/datacore.dm | 8 ++++++-- code/modules/mob/dead/crew_manifest.dm | 9 +++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm index d5d5f1b418693..9ea10b18ad803 100644 --- a/code/__DEFINES/dcs/signals/signals_global.dm +++ b/code/__DEFINES/dcs/signals/signals_global.dm @@ -35,3 +35,5 @@ #define COMSIG_GLOB_POST_START "!post_start" /// Called when the parallax background changes colour. (new_colour, transition_time) #define COMSIG_GLOB_STARLIGHT_COLOUR_CHANGE "!starlight_colour_change" +/// Called whenever the crew manifest is updated +#define COMSIG_GLOB_CREW_MANIFEST_UPDATE "!crew_manifest_update" diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index ead89498a69b3..0d2ae3808f460 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -175,14 +175,16 @@ if(N.new_character) log_manifest(N.ckey,N.new_character.mind,N.new_character) if(ishuman(N.new_character)) - manifest_inject(N.new_character) + manifest_inject(N.new_character, send_signal = FALSE) CHECK_TICK + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CREW_MANIFEST_UPDATE) /datum/datacore/proc/manifest_modify(name, assignment, hudstate) var/datum/data/record/foundrecord = find_record("name", name, GLOB.data_core.general) if(foundrecord) foundrecord.fields["rank"] = assignment foundrecord.fields["hud"] = hudstate + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CREW_MANIFEST_UPDATE) /datum/datacore/proc/get_manifest() var/list/manifest_out = list() @@ -241,7 +243,7 @@ return dat -/datum/datacore/proc/manifest_inject(mob/living/carbon/human/H) +/datum/datacore/proc/manifest_inject(mob/living/carbon/human/H, send_signal = TRUE) set waitfor = FALSE var/static/list/show_directions = list(SOUTH, WEST) if(H.mind && (H.mind.assigned_role != H.mind.special_role)) @@ -326,6 +328,8 @@ L.fields["character_appearance"] = character_appearance L.fields["mindref"] = H.mind locked += L + if(send_signal) + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CREW_MANIFEST_UPDATE) return /** diff --git a/code/modules/mob/dead/crew_manifest.dm b/code/modules/mob/dead/crew_manifest.dm index ab40c53ffd9cf..5632b66b395c2 100644 --- a/code/modules/mob/dead/crew_manifest.dm +++ b/code/modules/mob/dead/crew_manifest.dm @@ -1,6 +1,12 @@ GLOBAL_DATUM_INIT(crew_manifest_tgui, /datum/crew_manifest, new) -/datum/crew_manifest +/datum/crew_manifest/New() + . = ..() + RegisterSignal(SSdcs, COMSIG_GLOB_CREW_MANIFEST_UPDATE, PROC_REF(on_manifest_update)) + +/datum/crew_manifest/proc/on_manifest_update() + SIGNAL_HANDLER + ui_update() /datum/crew_manifest/ui_state(mob/user) return GLOB.always_state @@ -13,7 +19,6 @@ GLOBAL_DATUM_INIT(crew_manifest_tgui, /datum/crew_manifest, new) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "CrewManifest") - ui.set_autoupdate(TRUE) ui.open() /datum/crew_manifest/ui_static_data(mob/user) From 06250a2f038fcf8ac148850dc08f5366fc368662 Mon Sep 17 00:00:00 2001 From: Lucy Date: Fri, 22 Sep 2023 09:37:50 -0400 Subject: [PATCH 12/12] Address reviews --- code/__HELPERS/_lists.dm | 5 +++++ code/datums/datacore.dm | 2 +- code/modules/mob/dead/crew_manifest.dm | 6 +++++- tgui/packages/tgui/interfaces/CrewManifest.tsx | 12 +++++++----- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 81dad2bd0f55d..caa0951c8a138 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -769,6 +769,11 @@ last_value = value +/** + * Converts a normal array list to an associated list, with the keys being the original values, and the value being the index of the value in the original list. + * All keys are converted to strings. + * Example: list("a", "b", 1, 2, 3) -> list("a" = 1, "b" = 2, "1" = 3, "2" = 4, "3" = 5) +*/ /proc/list_to_assoc_index(list/input) . = list() for(var/i = 1 to length(input)) diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 0d2ae3808f460..4b4dee4d96047 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -202,7 +202,7 @@ if(!department_manifest) manifest_out[department] = department_manifest = list() // Append to beginning of list if captain or department head - var/put_at_top = rank == JOB_NAME_CAPTAIN || (department != DEPT_COMMAND && heads[rank]) + var/put_at_top = hud == JOB_HUD_CAPTAIN || hud == JOB_HUD_ACTINGCAPTAIN || (department != DEPT_COMMAND && heads[rank]) department_manifest.Insert(put_at_top, list(entry)) has_department = TRUE if(!has_department) diff --git a/code/modules/mob/dead/crew_manifest.dm b/code/modules/mob/dead/crew_manifest.dm index 5632b66b395c2..3f2662910802f 100644 --- a/code/modules/mob/dead/crew_manifest.dm +++ b/code/modules/mob/dead/crew_manifest.dm @@ -25,6 +25,7 @@ GLOBAL_DATUM_INIT(crew_manifest_tgui, /datum/crew_manifest, new) var/static/list/ordering = list_to_assoc_index(flatten_list(GLOB.id_to_hud)) return list( "command" = list( + "name" = "Command", "huds" = GLOB.command_huds, "jobs" = GLOB.command_positions, "order" = SSjob.chain_of_command @@ -33,7 +34,10 @@ GLOBAL_DATUM_INIT(crew_manifest_tgui, /datum/crew_manifest, new) ) /datum/crew_manifest/ui_data(mob/user) - return list("manifest" = GLOB.data_core.get_manifest(), "generic" = isdead(user)) + var/user_theme = null + if(isdead(user)) + user_theme = "generic" + return list("manifest" = GLOB.data_core.get_manifest(), "user_theme" = user_theme) /datum/crew_manifest/ui_assets(mob/user) return list(get_asset_datum(/datum/asset/spritesheet/job_icons)) diff --git a/tgui/packages/tgui/interfaces/CrewManifest.tsx b/tgui/packages/tgui/interfaces/CrewManifest.tsx index d93d14850773c..cba75cc027584 100644 --- a/tgui/packages/tgui/interfaces/CrewManifest.tsx +++ b/tgui/packages/tgui/interfaces/CrewManifest.tsx @@ -20,6 +20,8 @@ type ManifestEntry = { }; type CommandInfo = { + /** The name of the 'superior' department. Honestly, this is always going to be "Command", but well, apparently this shouldn't be hardcoded, so yolo! */ + dept: string; /** A (static) list of HUD icons used by command roles. */ huds: string[]; /** A (static) list of job titles considered to be command roles. */ @@ -35,20 +37,20 @@ type CrewManifestData = { manifest: DepartmentCrew; /** The ordering of which jobs should be listed, based on HUD icon. */ order: JobOrdering; - /** Use the generic TGUI theme. */ - generic: BooleanLike; + /** The TGUI theme to use. */ + user_theme?: string; }; export const CrewManifest = (_props, context) => { const { - data: { command, order, manifest, generic }, + data: { command, order, manifest, user_theme }, } = useBackend(context); return ( - + {Object.entries(manifest).map(([dept, crew]) => { - const sorted_jobs = dept === 'Command' ? sortSpecific(crew, command.order) : sortSpecific(crew, order); + const sorted_jobs = dept === command.dept ? sortSpecific(crew, command.order) : sortSpecific(crew, order); return (