diff --git a/code/_globalvars/tgui.dm b/code/_globalvars/tgui.dm
new file mode 100644
index 00000000000..6903af6d913
--- /dev/null
+++ b/code/_globalvars/tgui.dm
@@ -0,0 +1 @@
+GLOBAL_DATUM(crew_manifest_tgui, /datum/crew_manifest)
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index a6ed82c4176..8ff5fe52ca7 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -169,12 +169,18 @@
if(rank in jobs)
if(!manifest_out[department])
manifest_out[department] = list()
- manifest_out[department] += list(list(
- "name" = name,
- "rank" = rank
- ))
+ // Append to beginning of list if captain or department head
+ if (rank == "Captain" || (department != "Command" && (rank in GLOB.command_positions)))
+ manifest_out[department] = list(list(
+ "name" = name,
+ "rank" = rank
+ )) + manifest_out[department]
+ else
+ manifest_out[department] += list(list(
+ "name" = name,
+ "rank" = rank
+ ))
has_department = TRUE
- break
if(!has_department)
if(!manifest_out["Misc"])
manifest_out["Misc"] = list()
diff --git a/code/modules/mob/dead/crew_manifest.dm b/code/modules/mob/dead/crew_manifest.dm
new file mode 100644
index 00000000000..fafed6bfee3
--- /dev/null
+++ b/code/modules/mob/dead/crew_manifest.dm
@@ -0,0 +1,52 @@
+/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)
+ return (isnewplayer(user) || isobserver(user) || isAI(user) || ispAI(user)) ? 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.open()
+
+/datum/crew_manifest/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ if (..())
+ return
+
+/datum/crew_manifest/ui_data(mob/user)
+ var/list/positions = list(
+ "Command" = 0,
+ "Security" = 0,
+ "Engineering" = 0,
+ "Medical" = 0,
+ "Science" = 0,
+ "Supply" = 0,
+ "Service" = 0,
+ "Silicon" = 0
+ )
+ var/list/departments = list(
+ list("flag" = DEPARTMENT_COMMAND, "name" = "Command"),
+ list("flag" = DEPARTMENT_SECURITY, "name" = "Security"),
+ list("flag" = DEPARTMENT_ENGINEERING, "name" = "Engineering"),
+ list("flag" = DEPARTMENT_MEDICAL, "name" = "Medical"),
+ list("flag" = DEPARTMENT_SCIENCE, "name" = "Science"),
+ list("flag" = DEPARTMENT_CARGO, "name" = "Supply"),
+ list("flag" = DEPARTMENT_SERVICE, "name" = "Service"),
+ list("flag" = DEPARTMENT_SILICON, "name" = "Silicon")
+ )
+
+ for(var/job in SSjob.occupations)
+ for(var/department in departments)
+ // Check if the job is part of a department using its flag
+ // Will return true for Research Director if the department is Science or Command, for example
+ if(job["departments"] & department["flag"])
+ // Add open positions to current department
+ positions[department["name"]] += (job["total_positions"] - job["current_positions"])
+
+ return list(
+ "manifest" = GLOB.data_core.get_manifest(),
+ "positions" = positions
+ )
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 53832c03b60..d7f8be8de67 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -36,6 +36,7 @@
/mob/dead/new_player/Destroy()
GLOB.new_player_list -= src
+
return ..()
/mob/dead/new_player/prepare_huds()
@@ -489,11 +490,10 @@
return
client.crew_manifest_delay = world.time + (1 SECONDS)
- var/dat = "
"
- dat += "Crew Manifest
"
- dat += GLOB.data_core.get_manifest_html()
+ if(!GLOB.crew_manifest_tgui)
+ GLOB.crew_manifest_tgui = new /datum/crew_manifest(src)
- src << browse(dat, "window=manifest;size=387x420;can_close=1")
+ 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 61b5871729b..c0b12183965 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -681,11 +681,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
return
client.crew_manifest_delay = world.time + (1 SECONDS)
- var/dat
- dat += "Crew Manifest
"
- dat += GLOB.data_core.get_manifest_html()
+ if(!GLOB.crew_manifest_tgui)
+ GLOB.crew_manifest_tgui = new /datum/crew_manifest(src)
- src << browse(dat, "window=manifest;size=387x420;can_close=1")
+ 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 f3e8e5759a6..837381c47ea 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -83,7 +83,7 @@
/mob/living/silicon/proc/freeCamera()
return
-
+
/mob/living/silicon/proc/triggerAlarm()
return
@@ -355,9 +355,10 @@
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()
+ if(!GLOB.crew_manifest_tgui)
+ GLOB.crew_manifest_tgui = new /datum/crew_manifest(src)
+
+ 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/tgstation.dme b/tgstation.dme
index 24655671e59..0d28f663264 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -241,6 +241,7 @@
#include "code\_globalvars\misc.dm"
#include "code\_globalvars\regexes.dm"
#include "code\_globalvars\religion.dm"
+#include "code\_globalvars\tgui.dm"
#include "code\_globalvars\traits.dm"
#include "code\_globalvars\lists\achievements.dm"
#include "code\_globalvars\lists\admin.dm"
@@ -2452,6 +2453,7 @@
#include "code\modules\mob\status_procs.dm"
#include "code\modules\mob\transform_procs.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/tgui/packages/tgui/interfaces/CrewManifest.js b/tgui/packages/tgui/interfaces/CrewManifest.js
new file mode 100644
index 00000000000..fa109d90065
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/CrewManifest.js
@@ -0,0 +1,64 @@
+import { useBackend } from "../backend";
+import { Icon, Section, Table } from "../components";
+import { Window } from "../layouts";
+
+const commandJobs = [
+ "Captain",
+ "Head of Personnel",
+ "Head of Security",
+ "Chief Engineer",
+ "Research Director",
+ "Chief Medical Officer",
+];
+
+export const CrewManifest = (props, context) => {
+ const { data: { manifest, positions } } = useBackend(context);
+
+ return (
+
+
+ {Object.entries(manifest).map(([department, crew]) => (
+
+
+ {Object.entries(crew).map(([crewIndex, crewMember]) => (
+
+
+ {crewMember.name}
+
+
+ {commandJobs.includes(crewMember.rank) && (
+
+ )}
+
+
+ {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 00000000000..8291cbbd50f
--- /dev/null
+++ b/tgui/packages/tgui/styles/interfaces/CrewManifest.scss
@@ -0,0 +1,41 @@
+@use '../colors.scss';
+
+$department_map: (
+ 'Command': colors.$yellow,
+ 'Security': colors.$red,
+ 'Engineering': colors.$orange,
+ 'Medical': colors.$teal,
+ '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;
+
+ &--Captain {
+ color: colors.$yellow;
+ padding: 3px 8px;
+ }
+
+ &--Command {
+ color: colors.$yellow;
+ padding: 3px 9px;
+ }
+ }
+}
diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss
index 6124d863dee..a98b3e60f77 100644
--- a/tgui/packages/tgui/styles/main.scss
+++ b/tgui/packages/tgui/styles/main.scss
@@ -45,6 +45,7 @@
@include meta.load-css('./interfaces/ListInput.scss');
@include meta.load-css('./interfaces/AlertModal.scss');
@include meta.load-css('./interfaces/CameraConsole.scss');
+@include meta.load-css('./interfaces/CrewManifest.scss');
@include meta.load-css('./interfaces/ExperimentConfigure.scss');
@include meta.load-css('./interfaces/NuclearBomb.scss');
@include meta.load-css('./interfaces/Paper.scss');