diff --git a/code/game/machinery/computer/supply.dm b/code/game/machinery/computer/supply.dm
index a310b33f785..6a6fd607038 100644
--- a/code/game/machinery/computer/supply.dm
+++ b/code/game/machinery/computer/supply.dm
@@ -36,7 +36,7 @@
if(!allowed(user))
return
user.set_machine(src)
- ui_interact(user)
+ tgui_interact(user)
return
/obj/machinery/computer/supplycomp/emag_act(var/remaining_charges, var/mob/user)
@@ -44,24 +44,27 @@
to_chat(user, "Special supplies unlocked.")
authorization |= SUP_CONTRABAND
req_access = list()
+ can_order_contraband = TRUE
return 1
+// TGUI
+/obj/machinery/computer/supplycomp/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "SupplyConsole", name)
+ ui.open()
-
-/obj/machinery/computer/supplycomp/ui_interact(mob/user, ui_key = "supply_records", var/datum/nanoui/ui = null, var/force_open = 1, var/key_state = null)
- var/data[0]
- var/shuttle_status[0] // Supply shuttle status
- var/pack_list[0] // List of supply packs within the active_category
- var/orders[0]
- var/receipts[0]
+/obj/machinery/computer/supplycomp/tgui_data(mob/user)
+ var/list/data = ..()
+ var/list/shuttle_status = list()
var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle
if(shuttle)
if(shuttle.has_arrive_time())
shuttle_status["location"] = "In transit"
shuttle_status["mode"] = SUP_SHUTTLE_TRANSIT
- shuttle_status["time"] = shuttle.eta_minutes()
+ shuttle_status["time"] = shuttle.eta_seconds()
else
shuttle_status["time"] = 0
@@ -107,149 +110,192 @@
shuttle_status["engine"] = "Engaged"
else
- shuttle["mode"] = SUP_SHUTTLE_ERROR
-
- for(var/pack_name in SSsupply.supply_pack)
- var/datum/supply_pack/P = SSsupply.supply_pack[pack_name]
- if(P.group == active_category)
- var/list/pack = list(
- "name" = P.name,
- "cost" = P.cost,
- "contraband" = P.contraband,
- "manifest" = uniquelist(P.manifest),
- "random" = P.num_contained,
- "expand" = 0,
- "ref" = "\ref[P]"
- )
-
- if(P in expanded_packs)
- pack["expand"] = 1
-
- pack_list[++pack_list.len] = pack
+ shuttle_status["mode"] = SUP_SHUTTLE_ERROR
// Compile user-side orders
// Status determines which menus the entry will display in
// Organized in field-entry list for iterative display
// List is nested so both the list of orders, and the list of elements in each order, can be iterated over
+ var/list/orders = list()
for(var/datum/supply_order/S in SSsupply.order_history)
- orders[++orders.len] = list(
- "ref" = "\ref[S]",
- "status" = S.status,
- "entries" = list(
- list("field" = "Supply Pack", "entry" = S.name),
- list("field" = "Cost", "entry" = S.cost),
- list("field" = "Index", "entry" = S.index),
- list("field" = "Reason", "entry" = S.comment),
- list("field" = "Ordered by", "entry" = S.ordered_by),
- list("field" = "Ordered at", "entry" = S.ordered_at),
- list("field" = "Approved by", "entry" = S.approved_by),
- list("field" = "Approved at", "entry" = S.approved_at)
- )
- )
+ orders.Add(list(list(
+ "ref" = "\ref[S]",
+ "status" = S.status,
+ "cost" = S.cost,
+ "entries" = list(
+ list("field" = "Supply Pack", "entry" = S.name),
+ list("field" = "Cost", "entry" = S.cost),
+ list("field" = "Index", "entry" = S.index),
+ list("field" = "Reason", "entry" = S.comment),
+ list("field" = "Ordered by", "entry" = S.ordered_by),
+ list("field" = "Ordered at", "entry" = S.ordered_at),
+ list("field" = "Approved by", "entry" = S.approved_by),
+ list("field" = "Approved at", "entry" = S.approved_at)
+ )
+ )))
// Compile exported crates
+ var/list/receipts = list()
for(var/datum/exported_crate/E in SSsupply.exported_crates)
- receipts[++receipts.len] = list(
- "ref" = "\ref[E]",
- "contents" = E.contents,
- "error" = E.contents["error"],
- "title" = list(
- list("field" = "Name", "entry" = E.name),
- list("field" = "Value", "entry" = E.value)
- )
+ receipts.Add(list(list(
+ "ref" = "\ref[E]",
+ "contents" = E.contents,
+ "error" = E.contents["error"],
+ "title" = list(
+ list("field" = "Name", "entry" = E.name),
+ list("field" = "Value", "entry" = E.value)
)
+ )))
- data["user"] = "\ref[user]"
- data["currentTab"] = menu_tab // Communicator compatibility, controls which menu is in use
data["shuttle_auth"] = (authorization & SUP_SEND_SHUTTLE) // Whether this ui is permitted to control the supply shuttle
data["order_auth"] = (authorization & SUP_ACCEPT_ORDERS) // Whether this ui is permitted to accept/deny requested orders
data["shuttle"] = shuttle_status
data["supply_points"] = SSsupply.points
- data["categories"] = all_supply_groups
- data["active_category"] = active_category
- data["supply_packs"] = pack_list
data["orders"] = orders
data["receipts"] = receipts
data["contraband"] = can_order_contraband || (authorization & SUP_CONTRABAND)
+ data["modal"] = tgui_modal_data(src)
+ return data
- // update the ui if it exists, returns null if no ui is passed/found
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if(!ui)
- // the ui does not exist, so we'll create a new() one
- // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
- ui = new(user, src, ui_key, "supply_records.tmpl", "Supply Console", 475, 700, state = key_state)
- // when the ui is first opened this is the data it will use
- ui.set_initial_data(data)
- // open the new ui window
- ui.open()
- // auto update every 20 Master Controller tick
- ui.set_auto_update(20) // Longer term to reduce the rate of data collection and processing
+/obj/machinery/computer/supplycomp/tgui_static_data(mob/user)
+ var/list/data = ..()
+
+ var/list/pack_list = list()
+ for(var/pack_name in SSsupply.supply_pack)
+ var/datum/supply_pack/P = SSsupply.supply_pack[pack_name]
+ var/list/pack = list(
+ "name" = P.name,
+ "cost" = P.cost,
+ "group" = P.group,
+ "contraband" = P.contraband,
+ "manifest" = uniquelist(P.manifest),
+ "random" = P.num_contained,
+ "ref" = "\ref[P]"
+ )
+ pack_list.Add(list(pack))
+ data["supply_packs"] = pack_list
+ data["categories"] = all_supply_groups
+ return data
-
-
-/obj/machinery/computer/supplycomp/Topic(href, href_list)
- if(!SSsupply)
- to_world_log("## ERROR: The SSsupply datum is missing.")
- return
- var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle
- if (!shuttle)
- to_world_log("## ERROR: The supply shuttle datum is missing.")
- return
+/obj/machinery/computer/supplycomp/tgui_act(action, params)
if(..())
- return 1
+ return TRUE
+ if(!SSsupply)
+ log_runtime(EXCEPTION("## ERROR: The SSsupply datum is missing."))
+ return TRUE
+ var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle
+ if(!shuttle)
+ log_runtime(EXCEPTION("## ERROR: The supply shuttle datum is missing."))
+ return TRUE
- if(isturf(loc) && ( in_range(src, usr) || istype(usr, /mob/living/silicon) ) )
- usr.set_machine(src)
+ if(tgui_modal_act(src, action, params))
+ return TRUE
- // NEW TOPIC
+ switch(action)
+ if("view_crate")
+ var/datum/supply_pack/P = locate(params["crate"])
+ if(!istype(P))
+ return FALSE
+ var/list/payload = list(
+ "name" = P.name,
+ "cost" = P.cost,
+ "manifest" = uniquelist(P.manifest),
+ "ref" = "\ref[P]",
+ "random" = P.num_contained,
+ )
+ tgui_modal_message(src, action, "", null, payload)
+ . = TRUE
+ if("request_crate_multi")
+ var/datum/supply_pack/S = locate(params["ref"])
- // Switch menu
- if(href_list["switch_tab"])
- menu_tab = href_list["switch_tab"]
+ // Invalid ref
+ if(!istype(S))
+ return FALSE
- if(href_list["active_category"])
- active_category = href_list["active_category"]
-
- if(href_list["pack_ref"])
- var/datum/supply_pack/S = locate(href_list["pack_ref"])
-
- // Invalid ref
- if(!istype(S))
- return
-
- // Expand the supply pack's contents
- if(href_list["expand"])
- expanded_packs ^= S
-
- // Make a request for the pack
- if(href_list["request"])
- var/mob/user = locate(href_list["user"])
- if(!istype(user)) // Invalid ref
- return
+ if(S.contraband && !(authorization & SUP_CONTRABAND || can_order_contraband))
+ return FALSE
if(world.time < reqtime)
visible_message("[src]'s monitor flashes, \"[reqtime - world.time] seconds remaining until another requisition form may be printed.\"")
- return
+ return FALSE
+
+ var/amount = clamp(input(usr, "How many crates? (0 to 20)") as num|null, 0, 20)
+ if(!amount)
+ return FALSE
var/timeout = world.time + 600
- var/reason = sanitize(input(user, "Reason:","Why do you require this item?","") as null|text)
+ var/reason = sanitize(input(usr, "Reason:","Why do you require this item?","") as null|text)
if(world.time > timeout)
- to_chat(user, "Error. Request timed out.")
- return
+ to_chat(usr, "Error. Request timed out.")
+ return FALSE
if(!reason)
- return
+ return FALSE
- SSsupply.create_order(S, user, reason)
+ for(var/i in 1 to amount)
+ SSsupply.create_order(S, usr, reason)
var/idname = "*None Provided*"
var/idrank = "*None Provided*"
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
+ if(ishuman(usr))
+ var/mob/living/carbon/human/H = usr
idname = H.get_authentification_name()
idrank = H.get_assignment()
- else if(issilicon(user))
- idname = user.real_name
+ else if(issilicon(usr))
+ idname = usr.real_name
+ idrank = "Stationbound synthetic"
+
+ var/obj/item/weapon/paper/reqform = new /obj/item/weapon/paper(loc)
+ reqform.name = "Requisition Form - [S.name]"
+ reqform.info += "
[station_name()] Supply Requisition Form
"
+ reqform.info += "INDEX: #[SSsupply.ordernum]
"
+ reqform.info += "REQUESTED BY: [idname]
"
+ reqform.info += "RANK: [idrank]
"
+ reqform.info += "REASON: [reason]
"
+ reqform.info += "SUPPLY CRATE TYPE: [S.name]
"
+ reqform.info += "ACCESS RESTRICTION: [get_access_desc(S.access)]
"
+ reqform.info += "AMOUNT: [amount]
"
+ reqform.info += "CONTENTS:
"
+ reqform.info += S.get_html_manifest()
+ reqform.info += "
"
+ reqform.info += "STAMP BELOW TO APPROVE THIS REQUISITION:
"
+
+ reqform.update_icon() //Fix for appearing blank when printed.
+ reqtime = (world.time + 5) % 1e5
+ . = TRUE
+
+ if("request_crate")
+ var/datum/supply_pack/S = locate(params["ref"])
+
+ // Invalid ref
+ if(!istype(S))
+ return FALSE
+
+ if(S.contraband && !(authorization & SUP_CONTRABAND || can_order_contraband))
+ return FALSE
+
+ if(world.time < reqtime)
+ visible_message("[src]'s monitor flashes, \"[reqtime - world.time] seconds remaining until another requisition form may be printed.\"")
+ return FALSE
+
+ var/timeout = world.time + 600
+ var/reason = sanitize(input(usr, "Reason:","Why do you require this item?","") as null|text)
+ if(world.time > timeout)
+ to_chat(usr, "Error. Request timed out.")
+ return FALSE
+ if(!reason)
+ return FALSE
+
+ SSsupply.create_order(S, usr, reason)
+
+ var/idname = "*None Provided*"
+ var/idrank = "*None Provided*"
+ if(ishuman(usr))
+ var/mob/living/carbon/human/H = usr
+ idname = H.get_authentification_name()
+ idrank = H.get_assignment()
+ else if(issilicon(usr))
+ idname = usr.real_name
idrank = "Stationbound synthetic"
var/obj/item/weapon/paper/reqform = new /obj/item/weapon/paper(loc)
@@ -268,24 +314,19 @@
reqform.update_icon() //Fix for appearing blank when printed.
reqtime = (world.time + 5) % 1e5
-
- if(href_list["order_ref"])
- var/datum/supply_order/O = locate(href_list["order_ref"])
-
- // Invalid ref
- if(!istype(O))
- return
-
- var/mob/user = locate(href_list["user"])
- if(!istype(user)) // Invalid ref
- return
-
- if(href_list["edit"])
- var/new_val = sanitize(input(user, href_list["edit"], "Enter the new value for this field:", href_list["default"]) as null|text)
+ . = TRUE
+ // Approving Orders
+ if("edit_order_value")
+ var/datum/supply_order/O = locate(params["ref"])
+ if(!istype(O))
+ return FALSE
+ if(!(authorization & SUP_ACCEPT_ORDERS))
+ return FALSE
+ var/new_val = sanitize(input(usr, params["edit"], "Enter the new value for this field:", params["default"]) as null|text)
if(!new_val)
- return
+ return FALSE
- switch(href_list["edit"])
+ switch(params["edit"])
if("Supply Pack")
O.name = new_val
@@ -313,68 +354,95 @@
if("Approved at")
O.approved_at = new_val
+ . = TRUE
+ if("approve_order")
+ var/datum/supply_order/O = locate(params["ref"])
+ if(!istype(O))
+ return FALSE
+ if(!(authorization & SUP_ACCEPT_ORDERS))
+ return FALSE
+ SSsupply.approve_order(O, usr)
+ . = TRUE
+ if("deny_order")
+ var/datum/supply_order/O = locate(params["ref"])
+ if(!istype(O))
+ return FALSE
+ if(!(authorization & SUP_ACCEPT_ORDERS))
+ return FALSE
+ SSsupply.deny_order(O, usr)
+ . = TRUE
+ if("delete_order")
+ var/datum/supply_order/O = locate(params["ref"])
+ if(!istype(O))
+ return FALSE
+ if(!(authorization & SUP_ACCEPT_ORDERS))
+ return FALSE
+ SSsupply.delete_order(O, usr)
+ . = TRUE
+ if("clear_all_requests")
+ if(!(authorization & SUP_ACCEPT_ORDERS))
+ return FALSE
+ SSsupply.deny_all_pending(usr)
+ . = TRUE
+ // Exports
+ if("export_edit_field")
+ var/datum/exported_crate/E = locate(params["ref"])
+ // Invalid ref
+ if(!istype(E))
+ return FALSE
+ if(!(authorization & SUP_ACCEPT_ORDERS))
+ return FALSE
+ var/list/L = E.contents[params["index"]]
+ var/field = alert(usr, "Select which field to edit", , "Name", "Quantity", "Value")
- if(href_list["approve"])
- SSsupply.approve_order(O, user)
-
- if(href_list["deny"])
- SSsupply.deny_order(O, user)
-
- if(href_list["delete"])
- SSsupply.delete_order(O, user)
-
- if(href_list["clear_all_requests"])
- var/mob/user = locate(href_list["user"])
- if(!istype(user)) // Invalid ref
- return
-
- SSsupply.deny_all_pending(user)
-
- if(href_list["export_ref"])
- var/datum/exported_crate/E = locate(href_list["export_ref"])
-
- // Invalid ref
- if(!istype(E))
- return
-
- var/mob/user = locate(href_list["user"])
- if(!istype(user)) // Invalid ref
- return
-
- if(href_list["index"])
- var/list/L = E.contents[href_list["index"]]
-
- if(href_list["edit"])
- var/field = alert(user, "Select which field to edit", , "Name", "Quantity", "Value")
-
- var/new_val = sanitize(input(user, href_list["edit"], "Enter the new value for this field:", href_list["default"]) as null|text)
- if(!new_val)
- return
-
- switch(field)
- if("Name")
- L["object"] = new_val
-
- if("Quantity")
- var/num = text2num(new_val)
- if(num)
- L["quantity"] = num
-
- if("Value")
- var/num = text2num(new_val)
- if(num)
- L["value"] = num
-
- if(href_list["delete"])
- E.contents.Cut(href_list["index"], href_list["index"] + 1)
-
- // Else clause means they're editing/deleting the whole export report, rather than a specific item in it
- else if(href_list["edit"])
- var/new_val = sanitize(input(user, href_list["edit"], "Enter the new value for this field:", href_list["default"]) as null|text)
+ var/new_val = sanitize(input(usr, field, "Enter the new value for this field:", L[lowertext(field)]) as null|text)
if(!new_val)
return
- switch(href_list["edit"])
+ switch(field)
+ if("Name")
+ L["object"] = new_val
+
+ if("Quantity")
+ var/num = text2num(new_val)
+ if(num)
+ L["quantity"] = num
+
+ if("Value")
+ var/num = text2num(new_val)
+ if(num)
+ L["value"] = num
+ . = TRUE
+ if("export_delete_field")
+ var/datum/exported_crate/E = locate(params["ref"])
+ // Invalid ref
+ if(!istype(E))
+ return FALSE
+ if(!(authorization & SUP_ACCEPT_ORDERS))
+ return FALSE
+ E.contents.Cut(params["index"], params["index"] + 1)
+ . = TRUE
+ if("export_add_field")
+ var/datum/exported_crate/E = locate(params["ref"])
+ // Invalid ref
+ if(!istype(E))
+ return FALSE
+ if(!(authorization & SUP_ACCEPT_ORDERS))
+ return FALSE
+ SSsupply.add_export_item(E, usr)
+ . = TRUE
+ if("export_edit")
+ var/datum/exported_crate/E = locate(params["ref"])
+ // Invalid ref
+ if(!istype(E))
+ return FALSE
+ if(!(authorization & SUP_ACCEPT_ORDERS))
+ return FALSE
+ var/new_val = sanitize(input(usr, params["edit"], "Enter the new value for this field:", params["default"]) as null|text)
+ if(!new_val)
+ return
+
+ switch(params["edit"])
if("Name")
E.name = new_val
@@ -382,39 +450,39 @@
var/num = text2num(new_val)
if(num)
E.value = num
+ . = TRUE
+ if("export_delete")
+ var/datum/exported_crate/E = locate(params["ref"])
+ // Invalid ref
+ if(!istype(E))
+ return FALSE
+ if(!(authorization & SUP_ACCEPT_ORDERS))
+ return FALSE
+ SSsupply.delete_export(E, usr)
+ . = TRUE
+ if("send_shuttle")
+ switch(params["mode"])
+ if("send_away")
+ if (shuttle.forbidden_atoms_check())
+ to_chat(usr, "For safety reasons the automated supply shuttle cannot transport live organisms, classified nuclear weaponry or homing beacons.")
+ else
+ shuttle.launch(src)
+ to_chat(usr, "Initiating launch sequence.")
- else if(href_list["delete"])
- SSsupply.delete_export(E, user)
+ if("send_to_station")
+ shuttle.launch(src)
+ to_chat(usr, "The supply shuttle has been called and will arrive in approximately [round(SSsupply.movetime/600,1)] minutes.")
- else if(href_list["add_item"])
- SSsupply.add_export_item(E, user)
+ if("cancel_shuttle")
+ shuttle.cancel_launch(src)
-
-
- switch(href_list["send_shuttle"])
- if("send_away")
- if (shuttle.forbidden_atoms_check())
- to_chat(usr, "For safety reasons the automated supply shuttle cannot transport live organisms, classified nuclear weaponry or homing beacons.")
- else
- shuttle.launch(src)
- to_chat(usr, "Initiating launch sequence.")
-
- if("send_to_station")
- shuttle.launch(src)
- to_chat(usr, "The supply shuttle has been called and will arrive in approximately [round(SSsupply.movetime/600,1)] minutes.")
-
- if("cancel_shuttle")
- shuttle.cancel_launch(src)
-
- if("force_shuttle")
- shuttle.force_launch(src)
+ if("force_shuttle")
+ shuttle.force_launch(src)
+ . = TRUE
add_fingerprint(usr)
- updateUsrDialog()
- return
/obj/machinery/computer/supplycomp/proc/post_signal(var/command)
-
var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435)
if(!frequency) return
diff --git a/tgui/packages/tgui/format.js b/tgui/packages/tgui/format.js
index db21025bdf4..8bddc19bff7 100644
--- a/tgui/packages/tgui/format.js
+++ b/tgui/packages/tgui/format.js
@@ -103,4 +103,13 @@ export const formatCommaNumber = value => {
let parts = value.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
-};
\ No newline at end of file
+
+// Function from https://stackoverflow.com/a/34841026. CC BY-SA 4.0.
+export const formatTime = seconds => {
+ let hours = Math.floor(seconds / 3600);
+ let minutes = Math.floor(seconds / 60) % 60;
+ return [hours, minutes, seconds % 60]
+ .map(v => v < 10 ? "0" + v : v)
+ .filter((v, i) => v !== "00" || i > 0)
+ .join(":");
+};
diff --git a/tgui/packages/tgui/interfaces/SupplyConsole.js b/tgui/packages/tgui/interfaces/SupplyConsole.js
new file mode 100644
index 00000000000..69ee550849e
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/SupplyConsole.js
@@ -0,0 +1,439 @@
+import { filter, sortBy } from 'common/collections';
+import { round } from "common/math";
+import { Fragment } from "inferno";
+import { formatTime } from "../format";
+import { useBackend, useLocalState } from "../backend";
+import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, Tabs, Collapsible, AnimatedNumber } from "../components";
+import { ComplexModal, modalRegisterBodyOverride } from '../interfaces/common/ComplexModal';
+import { Window } from "../layouts";
+import { flow } from 'common/fp';
+
+const viewCrateContents = (modal, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ supply_points,
+ } = data;
+ const {
+ name,
+ cost,
+ manifest,
+ ref,
+ random,
+ } = modal.args;
+ return (
+ supply_points}
+ onClick={() => act("request_crate", { ref: ref })} />
+ }>
+
+ {manifest.map(m => (
+
+ {m}
+
+ ))}
+
+
+ );
+};
+
+export const SupplyConsole = (props, context) => {
+ const { act, data } = useBackend(context);
+ modalRegisterBodyOverride("view_crate", viewCrateContents);
+ return (
+
+
+
+
+
+
+ );
+};
+
+const SupplyConsoleShuttleStatus = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ supply_points,
+ shuttle,
+ shuttle_auth,
+ } = data;
+
+ let shuttle_buttons = null;
+ let showShuttleForce = false;
+
+ if (shuttle_auth) {
+ if (shuttle.launch === 1 && shuttle.mode === 0) {
+ shuttle_buttons = (
+