diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index a6d1ee163c2..1000c8e7be3 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -99,7 +99,7 @@
#define ANNOUNCER_ALIENS "announcer_aliens"
#define ANNOUNCER_ANIMES "announcer_animes"
#define ANNOUNCER_GRANOMALIES "announcer_granomalies"
-#define ANNOUNCER_INTERCEPT "announcer_animes"
+#define ANNOUNCER_INTERCEPT "announcer_intercept"
#define ANNOUNCER_IONSTORM "announcer_ionstorm"
#define ANNOUNCER_METEORS "announcer_meteors"
#define ANNOUNCER_OUTBREAK5 "announcer_outbreak5"
@@ -111,3 +111,23 @@
#define ANNOUNCER_SHUTTLEDOCK "announcer_shuttledock"
#define ANNOUNCER_SHUTTLERECALLED "announcer_shuttlerecalled"
#define ANNOUNCER_SPANOMALIES "announcer_spanomalies"
+
+/// Global list of all of our announcer keys.
+GLOBAL_LIST_INIT(announcer_keys, list(
+ ANNOUNCER_AIMALF,
+ ANNOUNCER_ALIENS,
+ ANNOUNCER_ANIMES,
+ ANNOUNCER_GRANOMALIES,
+ ANNOUNCER_INTERCEPT,
+ ANNOUNCER_IONSTORM,
+ ANNOUNCER_METEORS,
+ ANNOUNCER_OUTBREAK5,
+ ANNOUNCER_OUTBREAK7,
+ ANNOUNCER_POWEROFF,
+ ANNOUNCER_POWERON,
+ ANNOUNCER_RADIATION,
+ ANNOUNCER_SHUTTLECALLED,
+ ANNOUNCER_SHUTTLEDOCK,
+ ANNOUNCER_SHUTTLERECALLED,
+ ANNOUNCER_SPANOMALIES,
+))
diff --git a/code/modules/admin/verbs/commandreport.dm b/code/modules/admin/verbs/commandreport.dm
new file mode 100644
index 00000000000..3a164f2b75d
--- /dev/null
+++ b/code/modules/admin/verbs/commandreport.dm
@@ -0,0 +1,146 @@
+/// The default command report announcement sound.
+#define DEFAULT_ANNOUNCEMENT_SOUND "default_announcement"
+
+/// Preset central command names to chose from for centcom reports.
+#define CENTCOM_PRESET "Central Command"
+#define SYNDICATE_PRESET "The Syndicate"
+#define WIZARD_PRESET "The Wizard Federation"
+#define CUSTOM_PRESET "Custom Command Name"
+
+/// Verb to change the global command name.
+/client/proc/cmd_change_command_name()
+ set category = "Admin.Events"
+ set name = "Change Command Name"
+
+ if(!check_rights(R_ADMIN))
+ return
+
+ var/input = input(usr, "Please input a new name for Central Command.", "What?", "") as text|null
+ if(!input)
+ return
+ change_command_name(input)
+ message_admins("[key_name_admin(src)] has changed Central Command's name to [input]")
+ log_admin("[key_name(src)] has changed the Central Command name to: [input]")
+
+/// Verb to open the create command report window and send command reports.
+/client/proc/cmd_admin_create_centcom_report()
+ set category = "Admin.Events"
+ set name = "Create Command Report"
+
+ if(!check_rights(R_ADMIN))
+ return
+
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Create Command Report") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ var/datum/command_report_menu/tgui = new(usr)
+ tgui.ui_interact(usr)
+
+/// Datum for holding the TGUI window for command reports.
+/datum/command_report_menu
+ /// The mob using the UI.
+ var/mob/ui_user
+ /// The name of central command that will accompany our report
+ var/command_name = CENTCOM_PRESET
+ /// Whether we are using a custom name instead of a preset.
+ var/custom_name
+ /// The actual contents of the report we're going to send.
+ var/command_report_content
+ /// Whether the report's contents are announced.
+ var/announce_contents = TRUE
+ /// The sound that's going to accompany our message.
+ var/played_sound = DEFAULT_ANNOUNCEMENT_SOUND
+ /// A static list of preset names that can be chosen.
+ var/static/list/preset_names = list(CENTCOM_PRESET, SYNDICATE_PRESET, WIZARD_PRESET, CUSTOM_PRESET)
+
+/datum/command_report_menu/New(mob/user)
+ ui_user = user
+
+/datum/command_report_menu/ui_state(mob/user)
+ return GLOB.admin_state
+
+/datum/command_report_menu/ui_close()
+ qdel(src)
+
+/datum/command_report_menu/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "CommandReport")
+ ui.open()
+
+/datum/command_report_menu/ui_data(mob/user)
+ var/list/data = list()
+ data["command_name"] = command_name
+ data["custom_name"] = custom_name
+ data["command_report_content"] = command_report_content
+ data["announce_contents"] = announce_contents
+ data["played_sound"] = played_sound
+
+ return data
+
+/datum/command_report_menu/ui_static_data(mob/user)
+ var/list/data = list()
+ data["command_name_presets"] = preset_names
+ data["announcer_sounds"] = list(DEFAULT_ANNOUNCEMENT_SOUND) + GLOB.announcer_keys
+
+ return data
+
+/datum/command_report_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("update_command_name")
+ if(params["updated_name"] == CUSTOM_PRESET)
+ custom_name = TRUE
+ else if (params["updated_name"] in preset_names)
+ custom_name = FALSE
+
+ command_name = params["updated_name"]
+ if("update_report_contents")
+ command_report_content = params["updated_contents"]
+ if("set_report_sound")
+ played_sound = params["picked_sound"]
+ if("toggle_announce")
+ announce_contents = !announce_contents
+ if("submit_report")
+ if(!command_name)
+ to_chat(ui_user, "You can't send a report with no command name.")
+ return
+ if(!command_report_content)
+ to_chat(ui_user, "You can't send a report with no contents.")
+ return
+ send_announcement()
+
+ return TRUE
+
+/*
+ * The actual proc that sends the priority announcement and reports
+ *
+ * Uses the variables set by the user on our datum as the arguments for the report.
+ */
+/datum/command_report_menu/proc/send_announcement()
+ /// Our current command name to swap back to after sending the report.
+ var/original_command_name = command_name()
+ change_command_name(command_name)
+
+ /// The sound we're going to play on report.
+ var/report_sound = played_sound
+ if(played_sound == DEFAULT_ANNOUNCEMENT_SOUND)
+ report_sound = SSstation.announcer.get_rand_report_sound()
+
+ if(announce_contents)
+ priority_announce(command_report_content, null, report_sound, has_important_message = TRUE)
+ print_command_report(command_report_content, "[announce_contents ? "" : "Classified "][command_name] Update", !announce_contents)
+
+ change_command_name(original_command_name)
+
+ log_admin("[key_name(ui_user)] has created a command report: \"[command_report_content]\", sent from \"[command_name]\" with the sound \"[played_sound]\".")
+ message_admins("[key_name_admin(ui_user)] has created a command report, sent from \"[command_name]\" with the sound \"[played_sound]\"")
+
+
+#undef DEFAULT_ANNOUNCEMENT_SOUND
+
+#undef CENTCOM_PRESET
+#undef SYNDICATE_PRESET
+#undef WIZARD_PRESET
+#undef CUSTOM_PRESET
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 3fb30fcf892..1a13a1dfd76 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -545,46 +545,6 @@ Traitors and the like can also be revived with the previous role mostly intact.
admin_ticket_log(M, msg)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Rejuvenate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/client/proc/cmd_admin_create_centcom_report()
- set category = "Admin.Events"
- set name = "Create Command Report"
-
- if(!check_rights(R_ADMIN))
- return
-
- var/input = input(usr, "Enter a Command Report. Ensure it makes sense IC. Command's name is currently set to [command_name()].", "What?", "") as message|null
- if(!input)
- return
-
- var/confirm = alert(src, "Do you want to announce the contents of the report to the crew?", "Announce", "Yes", "No", "Cancel")
- var/announce_command_report = TRUE
- switch(confirm)
- if("Yes")
- priority_announce(input, null, SSstation.announcer.get_rand_report_sound(), has_important_message = TRUE)
- announce_command_report = FALSE
- if("Cancel")
- return
-
- print_command_report(input, "[announce_command_report ? "Classified " : ""][command_name()] Update", announce_command_report)
-
- log_admin("[key_name(src)] has created a command report: [input]")
- message_admins("[key_name_admin(src)] has created a command report")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Create Command Report") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/proc/cmd_change_command_name()
- set category = "Admin.Events"
- set name = "Change Command Name"
-
- if(!check_rights(R_ADMIN))
- return
-
- var/input = input(usr, "Please input a new name for Central Command.", "What?", "") as text|null
- if(!input)
- return
- change_command_name(input)
- message_admins("[key_name_admin(src)] has changed Central Command's name to [input]")
- log_admin("[key_name(src)] has changed the Central Command name to: [input]")
-
/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in world)
set category = "Debug"
set name = "Delete"
diff --git a/tgstation.dme b/tgstation.dme
index d7609078eb0..ad7fda7c778 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1543,6 +1543,7 @@
#include "code\modules\admin\verbs\borgpanel.dm"
#include "code\modules\admin\verbs\BrokenInhands.dm"
#include "code\modules\admin\verbs\cinematic.dm"
+#include "code\modules\admin\verbs\commandreport.dm"
#include "code\modules\admin\verbs\deadsay.dm"
#include "code\modules\admin\verbs\debug.dm"
#include "code\modules\admin\verbs\diagnostics.dm"
diff --git a/tgui/packages/tgui/interfaces/CommandReport.js b/tgui/packages/tgui/interfaces/CommandReport.js
new file mode 100644
index 00000000000..f86c808b00d
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/CommandReport.js
@@ -0,0 +1,89 @@
+import { useBackend } from '../backend';
+import { Button, Dropdown, Input, Section, Stack, TextArea } from '../components';
+import { Window } from '../layouts';
+
+export const CommandReport = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ command_name,
+ custom_name,
+ command_name_presets = [],
+ command_report_content,
+ played_sound,
+ announcer_sounds = [],
+ announce_contents,
+ } = data;
+ return (
+
+
+
+
+
+
+
+
+ act('set_report_sound', {
+ picked_sound: value,
+ })} />
+
+
+
+
+
+
+
+
+ );
+};