diff --git a/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm b/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm
index d01e4f8da1..37a89ba2ff 100644
--- a/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm
+++ b/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm
@@ -18,9 +18,7 @@
/obj/item/mining_scanner,
/obj/item/flashlight/lantern,
/obj/item/card/id/mining,
-/obj/item/gps/mining{
- tracking = 0
- },
+/obj/item/gps/mining,
/turf/open/floor/plating,
/area/ruin/powered/golem_ship)
"d" = (
@@ -34,9 +32,7 @@
/obj/item/mining_scanner,
/obj/item/flashlight/lantern,
/obj/item/card/id/mining,
-/obj/item/gps/mining{
- tracking = 0
- },
+/obj/item/gps/mining,
/turf/open/floor/plating,
/area/ruin/powered/golem_ship)
"e" = (
@@ -156,9 +152,7 @@
"x" = (
/obj/structure/table/wood,
/obj/machinery/reagentgrinder,
-/obj/item/gps/mining{
- tracking = 0
- },
+/obj/item/gps/mining,
/turf/open/floor/mineral/titanium/purple,
/area/ruin/powered/golem_ship)
"z" = (
diff --git a/code/__DEFINES/exosuit_fabs.dm b/code/__DEFINES/exosuit_fabs.dm
new file mode 100644
index 0000000000..d951a7bc28
--- /dev/null
+++ b/code/__DEFINES/exosuit_fabs.dm
@@ -0,0 +1,35 @@
+/// Module is compatible with Security Cyborg models
+#define BORG_MODULE_SECURITY (1<<0)
+/// Module is compatible with Miner Cyborg models
+#define BORG_MODULE_MINER (1<<1)
+/// Module is compatible with Janitor Cyborg models
+#define BORG_MODULE_JANITOR (1<<2)
+/// Module is compatible with Medical Cyborg models
+#define BORG_MODULE_MEDICAL (1<<3)
+/// Module is compatible with Engineering Cyborg models
+#define BORG_MODULE_ENGINEERING (1<<4)
+
+/// Module is compatible with Ripley Exosuit models
+#define EXOSUIT_MODULE_RIPLEY (1<<0)
+/// Module is compatible with Odyseeus Exosuit models
+#define EXOSUIT_MODULE_ODYSSEUS (1<<1)
+/// Module is compatible with Clarke Exosuit models. Rebranded to firefighter because tg nerfed it to this.
+#define EXOSUIT_MODULE_FIREFIGHTER (1<<2)
+// #define EXOSUIT_MODULE_CLARKE (1<<2)
+/// Module is compatible with Gygax Exosuit models
+#define EXOSUIT_MODULE_GYGAX (1<<3)
+/// Module is compatible with Durand Exosuit models
+#define EXOSUIT_MODULE_DURAND (1<<4)
+/// Module is compatible with H.O.N.K Exosuit models
+#define EXOSUIT_MODULE_HONK (1<<5)
+/// Module is compatible with Phazon Exosuit models
+#define EXOSUIT_MODULE_PHAZON (1<<6)
+/// Module is compatable with N models
+#define EXOSUIT_MODULE_GYGAX_MED (1<<7)
+
+/// Module is compatible with "Working" Exosuit models - Ripley and Clarke
+#define EXOSUIT_MODULE_WORKING EXOSUIT_MODULE_RIPLEY | EXOSUIT_MODULE_FIREFIGHTER // | EXOSUIT_MODULE_CLARKE
+/// Module is compatible with "Combat" Exosuit models - Gygax, H.O.N.K, Durand and Phazon
+#define EXOSUIT_MODULE_COMBAT EXOSUIT_MODULE_GYGAX | EXOSUIT_MODULE_HONK | EXOSUIT_MODULE_DURAND | EXOSUIT_MODULE_PHAZON
+/// Module is compatible with "Medical" Exosuit modelsm - Odysseus
+#define EXOSUIT_MODULE_MEDICAL EXOSUIT_MODULE_ODYSSEUS | EXOSUIT_MODULE_GYGAX_MED
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index d294c69972..927b661898 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -341,10 +341,11 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
#define COLOUR_PRIORITY_AMOUNT 4 //how many priority levels there are.
//Endgame Results
-#define NUKE_MISS_STATION 1
-#define NUKE_SYNDICATE_BASE 2
-#define STATION_DESTROYED_NUKE 3
-#define STATION_EVACUATED 4
+#define NUKE_NEAR_MISS 1
+#define NUKE_MISS_STATION 2
+#define NUKE_SYNDICATE_BASE 3
+#define STATION_DESTROYED_NUKE 4
+#define STATION_EVACUATED 5
#define BLOB_WIN 8
#define BLOB_NUKE 9
#define BLOB_DESTROYED 10
diff --git a/code/__DEFINES/tgui.dm b/code/__DEFINES/tgui.dm
index 5ba0096d1b..f5adeadade 100644
--- a/code/__DEFINES/tgui.dm
+++ b/code/__DEFINES/tgui.dm
@@ -1,4 +1,28 @@
-#define UI_INTERACTIVE 2 // Green/Interactive
-#define UI_UPDATE 1 // Orange/Updates Only
-#define UI_DISABLED 0 // Red/Disabled
-#define UI_CLOSE -1 // Closed
\ No newline at end of file
+/// Green eye; fully interactive
+#define UI_INTERACTIVE 2
+/// Orange eye; updates but is not interactive
+#define UI_UPDATE 1
+/// Red eye; disabled, does not update
+#define UI_DISABLED 0
+/// UI Should close
+#define UI_CLOSE -1
+
+/// Maximum number of windows that can be suspended/reused
+#define TGUI_WINDOW_SOFT_LIMIT 5
+/// Maximum number of open windows
+#define TGUI_WINDOW_HARD_LIMIT 9
+
+/// Maximum ping timeout allowed to detect zombie windows
+#define TGUI_PING_TIMEOUT 4 SECONDS
+
+/// Window does not exist
+#define TGUI_WINDOW_CLOSED 0
+/// Window was just opened, but is still not ready to be sent data
+#define TGUI_WINDOW_LOADING 1
+/// Window is free and ready to receive data
+#define TGUI_WINDOW_READY 2
+
+/// Get a window id based on the provided pool index
+#define TGUI_WINDOW_ID(index) "tgui-window-[index]"
+/// Get a pool index of the provided window id
+#define TGUI_WINDOW_INDEX(window_id) text2num(copytext(window_id, 13))
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index 733cbcfd8d..62e6c4daf9 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -201,9 +201,18 @@
WRITE_LOG(log, "Starting up round ID [GLOB.round_id].\n-------------------------")
/* ui logging */
-
-/proc/log_tgui(text)
- WRITE_LOG(GLOB.tgui_log, text)
+/proc/log_tgui(user_or_client, text)
+ var/entry = ""
+ if(!user_or_client)
+ entry += "no user"
+ else if(istype(user_or_client, /mob))
+ var/mob/user = user_or_client
+ entry += "[user.ckey] (as [user])"
+ else if(istype(user_or_client, /client))
+ var/client/client = user_or_client
+ entry += "[client.ckey]"
+ entry += ":\n[text]"
+ WRITE_LOG(GLOB.tgui_log, entry)
/* Close open log handles. This should be called as late as possible, and no logging should hapen after. */
/proc/shutdown_logging()
diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index 0825e51ae4..a688d20451 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -85,9 +85,6 @@ GLOBAL_VAR_INIT(cmp_field, "name")
/proc/cmp_advdisease_resistance_asc(datum/disease/advance/A, datum/disease/advance/B)
return A.totalResistance() - B.totalResistance()
-/proc/cmp_job_display_asc(datum/job/A, datum/job/B)
- return A.display_order - B.display_order
-
/proc/cmp_uplink_items_dsc(datum/uplink_item/A, datum/uplink_item/B)
return sorttext(initial(B.name), initial(A.name))
@@ -97,9 +94,6 @@ GLOBAL_VAR_INIT(cmp_field, "name")
/proc/cmp_numbered_displays_name_dsc(datum/numbered_display/A, datum/numbered_display/B)
return sorttext(B.sample_object.name, A.sample_object.name)
-/proc/cmp_reagents_asc(datum/reagent/a, datum/reagent/b)
- return sorttext(initial(b.name),initial(a.name))
-
/proc/cmp_quirk_asc(datum/quirk/A, datum/quirk/B)
var/a_sign = num2sign(initial(A.value) * -1)
var/b_sign = num2sign(initial(B.value) * -1)
@@ -133,3 +127,12 @@ GLOBAL_VAR_INIT(cmp_field, "name")
return A.required_temp - B.required_temp //return coldest
else
return B.required_temp - A.required_temp //return hottest
+
+/proc/cmp_job_display_asc(datum/job/A, datum/job/B)
+ return A.display_order - B.display_order
+
+/proc/cmp_reagents_asc(datum/reagent/a, datum/reagent/b)
+ return sorttext(initial(b.name),initial(a.name))
+
+/proc/cmp_typepaths_asc(A, B)
+ return sorttext("[B]","[A]")
\ No newline at end of file
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index 9bda1cd233..c802448536 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -775,12 +775,14 @@ SUBSYSTEM_DEF(shuttle)
preview_shuttle = null
-/datum/controller/subsystem/shuttle/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "ShuttleManipulator", name, 800, 600, master_ui, state)
- ui.open()
+/datum/controller/subsystem/shuttle/ui_state(mob/user)
+ return GLOB.admin_state
+/datum/controller/subsystem/shuttle/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ShuttleManipulator")
+ ui.open()
/datum/controller/subsystem/shuttle/ui_data(mob/user)
var/list/data = list()
diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm
index 58bc28fa2f..a5526d2c03 100644
--- a/code/controllers/subsystem/tgui.dm
+++ b/code/controllers/subsystem/tgui.dm
@@ -1,3 +1,12 @@
+/**
+ * tgui subsystem
+ *
+ * Contains all tgui state and subsystem code.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
SUBSYSTEM_DEF(tgui)
name = "tgui"
wait = 9
@@ -5,33 +14,338 @@ SUBSYSTEM_DEF(tgui)
priority = FIRE_PRIORITY_TGUI
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
- var/list/currentrun = list()
- var/list/open_uis = list() // A list of open UIs, grouped by src_object and ui_key.
- var/list/processing_uis = list() // A list of processing UIs, ungrouped.
- var/basehtml // The HTML base used for all UIs.
+ /// A list of UIs scheduled to process
+ var/list/current_run = list()
+ /// A list of open UIs
+ var/list/open_uis = list()
+ /// A list of open UIs, grouped by src_object.
+ var/list/open_uis_by_src = list()
+ /// The HTML base used for all UIs.
+ var/basehtml
/datum/controller/subsystem/tgui/PreInit()
- basehtml = file2text('tgui/packages/tgui/public/tgui.html')
+ basehtml = file2text('tgui/packages/tgui/public/tgui.html')
/datum/controller/subsystem/tgui/Shutdown()
close_all_uis()
/datum/controller/subsystem/tgui/stat_entry()
- ..("P:[processing_uis.len]")
+ ..("P:[open_uis.len]")
/datum/controller/subsystem/tgui/fire(resumed = 0)
- if (!resumed)
- src.currentrun = processing_uis.Copy()
- //cache for sanic speed (lists are references anyways)
- var/list/currentrun = src.currentrun
-
- while(currentrun.len)
- var/datum/tgui/ui = currentrun[currentrun.len]
- currentrun.len--
+ if(!resumed)
+ src.current_run = open_uis.Copy()
+ // Cache for sanic speed (lists are references anyways)
+ var/list/current_run = src.current_run
+ while(current_run.len)
+ var/datum/tgui/ui = current_run[current_run.len]
+ current_run.len--
+ // TODO: Move user/src_object check to process()
if(ui && ui.user && ui.src_object)
ui.process()
else
- processing_uis.Remove(ui)
- if (MC_TICK_CHECK)
+ open_uis.Remove(ui)
+ if(MC_TICK_CHECK)
return
+/**
+ * public
+ *
+ * Requests a usable tgui window from the pool.
+ * Returns null if pool was exhausted.
+ *
+ * required user mob
+ * return datum/tgui
+ */
+/datum/controller/subsystem/tgui/proc/request_pooled_window(mob/user)
+ if(!user.client)
+ return null
+ var/list/windows = user.client.tgui_windows
+ var/window_id
+ var/datum/tgui_window/window
+ var/window_found = FALSE
+ // Find a usable window
+ for(var/i in 1 to TGUI_WINDOW_HARD_LIMIT)
+ window_id = TGUI_WINDOW_ID(i)
+ window = windows[window_id]
+ // As we are looping, create missing window datums
+ if(!window)
+ window = new(user.client, window_id, pooled = TRUE)
+ // Skip windows with acquired locks
+ if(window.locked)
+ continue
+ if(window.status == TGUI_WINDOW_READY)
+ return window
+ if(window.status == TGUI_WINDOW_CLOSED)
+ window.status = TGUI_WINDOW_LOADING
+ window_found = TRUE
+ break
+ if(!window_found)
+ log_tgui(user, "Error: Pool exhausted")
+ return null
+ return window
+
+/**
+ * public
+ *
+ * Force closes all tgui windows.
+ *
+ * required user mob
+ */
+/datum/controller/subsystem/tgui/proc/force_close_all_windows(mob/user)
+ log_tgui(user, "force_close_all_windows")
+ if(user.client)
+ user.client.tgui_windows = list()
+ for(var/i in 1 to TGUI_WINDOW_HARD_LIMIT)
+ var/window_id = TGUI_WINDOW_ID(i)
+ user << browse(null, "window=[window_id]")
+
+/**
+ * public
+ *
+ * Force closes the tgui window by window_id.
+ *
+ * required user mob
+ * required window_id string
+ */
+/datum/controller/subsystem/tgui/proc/force_close_window(mob/user, window_id)
+ log_tgui(user, "force_close_window")
+ // Close all tgui datums based on window_id.
+ for(var/datum/tgui/ui in user.tgui_open_uis)
+ if(ui.window && ui.window.id == window_id)
+ ui.close(can_be_suspended = FALSE)
+ // Unset machine just to be sure.
+ user.unset_machine()
+ // Close window directly just to be sure.
+ user << browse(null, "window=[window_id]")
+
+/**
+ * public
+ *
+ * Try to find an instance of a UI, and push an update to it.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required src_object datum The object/datum which owns the UI.
+ * optional ui datum/tgui The UI to be updated, if it exists.
+ * optional force_open bool If the UI should be re-opened instead of updated.
+ *
+ * return datum/tgui The found UI.
+ */
+/datum/controller/subsystem/tgui/proc/try_update_ui(
+ mob/user,
+ datum/src_object,
+ datum/tgui/ui)
+ // Look up a UI if it wasn't passed
+ if(isnull(ui))
+ ui = get_open_ui(user, src_object)
+ // Couldn't find a UI.
+ if(isnull(ui))
+ return null
+ ui.process_status()
+ // UI ended up with the closed status
+ // or is actively trying to close itself.
+ // FIXME: Doesn't actually fix the paper bug.
+ if(ui.status <= UI_CLOSE)
+ ui.close()
+ return null
+ ui.send_update()
+ return ui
+
+/**
+ * public
+ *
+ * Get a open UI given a user and src_object.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required src_object datum The object/datum which owns the UI.
+ *
+ * return datum/tgui The found UI.
+ */
+/datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object)
+ var/key = "[REF(src_object)]"
+ // No UIs opened for this src_object
+ if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list))
+ return null
+ for(var/datum/tgui/ui in open_uis_by_src[key])
+ // Make sure we have the right user
+ if(ui.user == user)
+ return ui
+ return null
+
+/**
+ * public
+ *
+ * Update all UIs attached to src_object.
+ *
+ * required src_object datum The object/datum which owns the UIs.
+ *
+ * return int The number of UIs updated.
+ */
+/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object)
+ var/count = 0
+ var/key = "[REF(src_object)]"
+ // No UIs opened for this src_object
+ if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list))
+ return count
+ for(var/datum/tgui/ui in open_uis_by_src[key])
+ // Check if UI is valid.
+ if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
+ ui.process(force = 1)
+ count++
+ return count
+
+/**
+ * public
+ *
+ * Close all UIs attached to src_object.
+ *
+ * required src_object datum The object/datum which owns the UIs.
+ *
+ * return int The number of UIs closed.
+ */
+/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object)
+ var/count = 0
+ var/key = "[REF(src_object)]"
+ // No UIs opened for this src_object
+ if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list))
+ return count
+ for(var/datum/tgui/ui in open_uis_by_src[key])
+ // Check if UI is valid.
+ if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
+ ui.close()
+ count++
+ return count
+
+/**
+ * public
+ *
+ * Close all UIs regardless of their attachment to src_object.
+ *
+ * return int The number of UIs closed.
+ */
+/datum/controller/subsystem/tgui/proc/close_all_uis()
+ var/count = 0
+ for(var/key in open_uis_by_src)
+ for(var/datum/tgui/ui in open_uis_by_src[key])
+ // Check if UI is valid.
+ if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
+ ui.close()
+ count++
+ return count
+
+/**
+ * public
+ *
+ * Update all UIs belonging to a user.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * optional src_object datum If provided, only update UIs belonging this src_object.
+ *
+ * return int The number of UIs updated.
+ */
+/datum/controller/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object)
+ var/count = 0
+ if(length(user?.tgui_open_uis) == 0)
+ return count
+ for(var/datum/tgui/ui in user.tgui_open_uis)
+ if(isnull(src_object) || ui.src_object == src_object)
+ ui.process(force = 1)
+ count++
+ return count
+
+/**
+ * public
+ *
+ * Close all UIs belonging to a user.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * optional src_object datum If provided, only close UIs belonging this src_object.
+ *
+ * return int The number of UIs closed.
+ */
+/datum/controller/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object)
+ var/count = 0
+ if(length(user?.tgui_open_uis) == 0)
+ return count
+ for(var/datum/tgui/ui in user.tgui_open_uis)
+ if(isnull(src_object) || ui.src_object == src_object)
+ ui.close()
+ count++
+ return count
+
+/**
+ * private
+ *
+ * Add a UI to the list of open UIs.
+ *
+ * required ui datum/tgui The UI to be added.
+ */
+/datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui)
+ var/key = "[REF(ui.src_object)]"
+ if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list))
+ open_uis_by_src[key] = list()
+ ui.user.tgui_open_uis |= ui
+ var/list/uis = open_uis_by_src[key]
+ uis |= ui
+ open_uis |= ui
+
+/**
+ * private
+ *
+ * Remove a UI from the list of open UIs.
+ *
+ * required ui datum/tgui The UI to be removed.
+ *
+ * return bool If the UI was removed or not.
+ */
+/datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui)
+ var/key = "[REF(ui.src_object)]"
+ if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list))
+ return FALSE
+ // Remove it from the list of processing UIs.
+ open_uis.Remove(ui)
+ // If the user exists, remove it from them too.
+ if(ui.user)
+ ui.user.tgui_open_uis.Remove(ui)
+ var/list/uis = open_uis_by_src[key]
+ uis.Remove(ui)
+ if(length(uis) == 0)
+ open_uis_by_src.Remove(key)
+ return TRUE
+
+/**
+ * private
+ *
+ * Handle client logout, by closing all their UIs.
+ *
+ * required user mob The mob which logged out.
+ *
+ * return int The number of UIs closed.
+ */
+/datum/controller/subsystem/tgui/proc/on_logout(mob/user)
+ close_user_uis(user)
+
+/**
+ * private
+ *
+ * Handle clients switching mobs, by transferring their UIs.
+ *
+ * required user source The client's original mob.
+ * required user target The client's new mob.
+ *
+ * return bool If the UIs were transferred.
+ */
+/datum/controller/subsystem/tgui/proc/on_transfer(mob/source, mob/target)
+ // The old mob had no open UIs.
+ if(length(source?.tgui_open_uis) == 0)
+ return FALSE
+ if(isnull(target.tgui_open_uis) || !istype(target.tgui_open_uis, /list))
+ target.tgui_open_uis = list()
+ // Transfer all the UIs.
+ for(var/datum/tgui/ui in source.tgui_open_uis)
+ // Inform the UIs of their new owner.
+ ui.user = target
+ target.tgui_open_uis.Add(ui)
+ // Clear the old list.
+ source.tgui_open_uis.Cut()
+ return TRUE
diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm
index 9c6bb2ebfd..15115b62c0 100644
--- a/code/datums/ai_laws.dm
+++ b/code/datums/ai_laws.dm
@@ -431,25 +431,31 @@
if(!owner)
owner = M
-/datum/ai_laws/proc/get_law_list(include_zeroth = 0, show_numbers = 1)
+/**
+ * Generates a list of all laws on this datum, including rendered HTML tags if required
+ *
+ * Arguments:
+ * * include_zeroth - Operator that controls if law 0 or law 666 is returned in the set
+ * * show_numbers - Operator that controls if law numbers are prepended to the returned laws
+ * * render_html - Operator controlling if HTML tags are rendered on the returned laws
+ */
+/datum/ai_laws/proc/get_law_list(include_zeroth = FALSE, show_numbers = TRUE, render_html = TRUE)
var/list/data = list()
- if (include_zeroth && devillaws && devillaws.len)
- for(var/i in devillaws)
- data += "[show_numbers ? "666:" : ""] [i] "
+ if (include_zeroth && devillaws)
+ for(var/law in devillaws)
+ data += "[show_numbers ? "666:" : ""] [render_html ? "[law] " : law]"
if (include_zeroth && zeroth)
- data += "[show_numbers ? "0:" : ""] [zeroth] "
+ data += "[show_numbers ? "0:" : ""] [render_html ? "[zeroth] " : zeroth]"
for(var/law in hacked)
if (length(law) > 0)
- var/num = ionnum()
- data += "[show_numbers ? "[num]:" : ""] [law] "
+ data += "[show_numbers ? "[ionnum()]:" : ""] [render_html ? "[law] " : law]"
for(var/law in ion)
if (length(law) > 0)
- var/num = ionnum()
- data += "[show_numbers ? "[num]:" : ""] [law] "
+ data += "[show_numbers ? "[ionnum()]:" : ""] [render_html ? "[law] " : law]"
var/number = 1
for(var/law in inherent)
@@ -459,6 +465,7 @@
for(var/law in supplied)
if (length(law) > 0)
- data += "[show_numbers ? "[number]:" : ""] [law] "
+ data += "[show_numbers ? "[number]:" : ""] [render_html ? "[law] " : law]"
number++
return data
+
diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm
index b9a1b5ec3b..1a9f4c0512 100644
--- a/code/datums/components/crafting/crafting.dm
+++ b/code/datums/components/crafting/crafting.dm
@@ -203,9 +203,9 @@
Set var amt to the value current cycle req is pointing to, its amount of type we need to delete
Get var/surroundings list of things accessable to crafting by get_environment()
Check the type of the current cycle req
- If its reagent then do a while loop, inside it try to locate() reagent containers, inside such containers try to locate needed reagent, if there isnt remove thing from surroundings
+ If its reagent then do a while loop, inside it try to locate() reagent containers, inside such containers try to locate needed reagent, if there isn't remove thing from surroundings
If there is enough reagent in the search result then delete the needed amount, create the same type of reagent with the same data var and put it into deletion list
- If there isnt enough take all of that reagent from the container, put into deletion list, substract the amt var by the volume of reagent, remove the container from surroundings list and keep searching
+ If there isn't enough take all of that reagent from the container, put into deletion list, substract the amt var by the volume of reagent, remove the container from surroundings list and keep searching
While doing above stuff check deletion list if it already has such reagnet, if yes merge instead of adding second one
If its stack check if it has enough amount
If yes create new stack with the needed amount and put in into deletion list, substract taken amount from the stack
@@ -216,7 +216,7 @@
Then do a loop over parts var of the recipe
Do similar stuff to what we have done above, but now in deletion list, until the parts conditions are satisfied keep taking from the deletion list and putting it into parts list for return
- After its done loop over deletion list and delete all the shit that wasnt taken by parts loop
+ After its done loop over deletion list and delete all the shit that wasn't taken by parts loop
del_reqs return the list of parts resulting object will receive as argument of CheckParts proc, on the atom level it will add them all to the contents, on all other levels it calls ..() and does whatever is needed afterwards but from contents list already
*/
@@ -323,9 +323,12 @@
if(user == parent)
ui_interact(user)
+/datum/component/personal_crafting/ui_state(mob/user)
+ return GLOB.not_incapacitated_turf_state
+
//For the UI related things we're going to assume the user is a mob rather than typesetting it to an atom as the UI isn't generated if the parent is an atom
-/datum/component/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_turf_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/component/personal_crafting/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
cur_category = categories[1]
if(islist(categories[cur_category]))
@@ -333,7 +336,7 @@
cur_subcategory = subcats[1]
else
cur_subcategory = CAT_NONE
- ui = new(user, src, ui_key, "PersonalCrafting", "Crafting Menu", 700, 800, master_ui, state)
+ ui = new(user, src, "PersonalCrafting")
ui.open()
/datum/component/personal_crafting/ui_data(mob/user)
@@ -413,13 +416,8 @@
display_compact = !display_compact
. = TRUE
if("set_category")
- if(!isnull(params["category"]))
- cur_category = params["category"]
- if(!isnull(params["subcategory"]))
- if(params["subcategory"] == "0")
- cur_subcategory = ""
- else
- cur_subcategory = params["subcategory"]
+ cur_category = params["category"]
+ cur_subcategory = params["subcategory"] || ""
. = TRUE
/datum/component/personal_crafting/proc/build_recipe_data(datum/crafting_recipe/R)
diff --git a/code/datums/components/gps.dm b/code/datums/components/gps.dm
index 200ec2b956..c2b3ad1f30 100644
--- a/code/datums/components/gps.dm
+++ b/code/datums/components/gps.dm
@@ -80,19 +80,15 @@ GLOBAL_LIST_EMPTY(GPS_list)
to_chat(user, "[parent] is now tracking, and visible to other GPS devices. ")
tracking = TRUE
-/datum/component/gps/item/ui_interact(mob/user, ui_key = "gps", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
+/datum/component/gps/item/ui_interact(mob/user, datum/tgui/ui)
if(emped)
to_chat(user, "[parent] fizzles weakly. ")
return
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- // Variable window height, depending on how many GPS units there are
- // to show, clamped to relatively safe range.
- var/gps_window_height = clamp(325 + GLOB.GPS_list.len * 14, 325, 700)
- ui = new(user, src, ui_key, "Gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height
+ ui = new(user, src, "Gps")
ui.open()
-
- ui.set_autoupdate(state = updating)
+ ui.set_autoupdate(updating)
/datum/component/gps/item/ui_data(mob/user)
var/list/data = list()
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index c7bbe01b2f..460a73bf5a 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -143,23 +143,19 @@ GLOBAL_LIST_EMPTY(uplinks)
// an unlocked uplink blocks also opening the PDA or headset menu
return COMPONENT_NO_INTERACT
-/datum/component/uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
+/datum/component/uplink/ui_state(mob/user)
+ return GLOB.inventory_state
+
+/datum/component/uplink/ui_interact(mob/user, datum/tgui/ui)
active = TRUE
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Uplink", name, 620, 580, master_ui, state)
+ ui = new(user, src, "Uplink", name)
// This UI is only ever opened by one person,
// and never is updated outside of user input.
ui.set_autoupdate(FALSE)
ui.open()
-/datum/component/uplink/ui_host(mob/user)
- if(istype(parent, /obj/item/implant)) //implants are like organs, not really located inside mobs codewise.
- var/obj/item/implant/I = parent
- return I.imp_in
- return ..()
-
/datum/component/uplink/ui_data(mob/user)
if(!user.mind)
return
@@ -187,8 +183,18 @@ GLOBAL_LIST_EMPTY(uplinks)
is_inaccessible = FALSE
if(is_inaccessible)
continue
+ /*
+ if(I.restricted_species) //catpeople specfic gloves.
+ if(ishuman(user))
+ var/is_inaccessible = TRUE
+ var/mob/living/carbon/human/H = user
+ for(var/F in I.restricted_species)
+ if(F == H.dna.species.id || debug)
+ is_inaccessible = FALSE
+ break
if(is_inaccessible)
continue
+ */
cat["items"] += list(list(
"name" = I.name,
"cost" = I.cost,
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index cf942f077f..5aeb7626f6 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -1,13 +1,13 @@
-
+//TODO: someone please get rid of this shit
/datum/datacore
- var/medical[] = list()
+ var/list/medical = list()
var/medicalPrintCount = 0
- var/general[] = list()
- var/security[] = list()
+ var/list/general = list()
+ var/list/security = list()
var/securityPrintCount = 0
var/securityCrimeCounter = 0
- //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character().
- var/locked[] = list()
+ ///This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character().
+ var/list/locked = list()
/datum/data
var/name = "data"
@@ -91,6 +91,42 @@
if(foundrecord)
foundrecord.fields["rank"] = assignment
+/datum/datacore/proc/get_manifest_tg() //copypasted from tg, renamed to avoid namespace conflicts
+ var/list/manifest_out = list()
+ var/list/departments = list(
+ "Command" = GLOB.command_positions,
+ "Security" = GLOB.security_positions,
+ "Engineering" = GLOB.engineering_positions,
+ "Medical" = GLOB.medical_positions,
+ "Science" = GLOB.science_positions,
+ "Supply" = GLOB.supply_positions,
+ "Service" = GLOB.civilian_positions,
+ "Silicon" = GLOB.nonhuman_positions
+ )
+ for(var/datum/data/record/t in GLOB.data_core.general)
+ var/name = t.fields["name"]
+ var/rank = t.fields["rank"]
+ var/has_department = FALSE
+ for(var/department in departments)
+ var/list/jobs = departments[department]
+ if(rank in jobs)
+ if(!manifest_out[department])
+ manifest_out[department] = list()
+ manifest_out[department] += list(list(
+ "name" = name,
+ "rank" = rank
+ ))
+ has_department = TRUE
+ break
+ if(!has_department)
+ if(!manifest_out["Misc"])
+ manifest_out["Misc"] = list()
+ manifest_out["Misc"] += list(list(
+ "name" = name,
+ "rank" = rank
+ ))
+ return manifest_out
+
/datum/datacore/proc/get_manifest(monochrome, OOC)
var/list/heads = list()
var/list/sec = list()
diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm
index 0f4f02ae2a..4bf02e8587 100644
--- a/code/datums/holocall.dm
+++ b/code/datums/holocall.dm
@@ -32,20 +32,29 @@
var/datum/action/innate/end_holocall/hangup //hangup action
var/call_start_time
+ var/head_call = FALSE //calls from a head of staff autoconnect, if the receiving pad is not secure.
//creates a holocall made by `caller` from `calling_pad` to `callees`
-/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees)
+/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees, elevated_access = FALSE)
call_start_time = world.time
user = caller
calling_pad.outgoing_call = src
calling_holopad = calling_pad
+ head_call = elevated_access
dialed_holopads = list()
for(var/I in callees)
var/obj/machinery/holopad/H = I
if(!QDELETED(H) && H.is_operational())
dialed_holopads += H
- H.say("Incoming call.")
+ if(head_call)
+ if(H.secure)
+ calling_pad.say("Auto-connection refused, falling back to call mode.")
+ H.say("Incoming call.")
+ else
+ H.say("Incoming connection.")
+ else
+ H.say("Incoming call.")
LAZYADD(H.holo_calls, src)
if(!dialed_holopads.len)
@@ -79,6 +88,7 @@
dialed_holopads.Cut()
if(calling_holopad)
+ calling_holopad.calling = FALSE
calling_holopad.outgoing_call = null
calling_holopad.SetLightsAndPower()
calling_holopad = null
@@ -145,6 +155,7 @@
if(!Check())
return
+ calling_holopad.calling = FALSE
hologram = H.activate_holo(user)
hologram.HC = src
@@ -160,6 +171,8 @@
hangup = new(eye, src)
hangup.Grant(user)
+ playsound(H, 'sound/machines/ping.ogg', 100)
+ H.say("Connection established.")
//Checks the validity of a holocall and qdels itself if it's not. Returns TRUE if valid, FALSE otherwise
/datum/holocall/proc/Check()
@@ -178,7 +191,6 @@
. = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME)
if(!.)
calling_holopad.say("No answer received.")
- calling_holopad.temp = ""
if(!.)
testing("Holocall Check fail")
@@ -241,10 +253,10 @@
record.caller_image = holodiskOriginal.record.caller_image
record.entries = holodiskOriginal.record.entries.Copy()
record.language = holodiskOriginal.record.language
- to_chat(user, "You copy the record from [holodiskOriginal] to [src] by connecting the ports!")
+ to_chat(user, "You copy the record from [holodiskOriginal] to [src] by connecting the ports! ")
name = holodiskOriginal.name
else
- to_chat(user, "[holodiskOriginal] has no record on it!")
+ to_chat(user, "[holodiskOriginal] has no record on it! ")
..()
/obj/item/disk/holodisk/proc/build_record()
@@ -331,6 +343,21 @@
DELAY 20"}
/datum/preset_holoimage/engineer
+ outfit_type = /datum/outfit/job/engineer
+
+/datum/preset_holoimage/engineer/rig
+ outfit_type = /datum/outfit/job/engineer/gloved/rig
+
+/datum/preset_holoimage/engineer/ce
+ outfit_type = /datum/outfit/job/ce
+
+/datum/preset_holoimage/engineer/ce/rig
+ outfit_type = /datum/outfit/job/engineer/gloved/rig
+
+/datum/preset_holoimage/engineer/atmos
+ outfit_type = /datum/outfit/job/atmos
+
+/datum/preset_holoimage/engineer/atmos/rig
outfit_type = /datum/outfit/job/engineer/gloved/rig
/datum/preset_holoimage/researcher
@@ -350,3 +377,90 @@
/datum/preset_holoimage/clown
outfit_type = /datum/outfit/job/clown
+
+/obj/item/disk/holodisk/donutstation/whiteship
+ name = "Blackbox Print-out #DS024"
+ desc = "A holodisk containing the last viable recording of DS024's blackbox."
+ preset_image_type = /datum/preset_holoimage/engineer/ce
+ preset_record_text = {"
+ NAME Geysr Shorthalt
+ SAY Engine renovations complete and the ships been loaded. We all ready?
+ DELAY 25
+ PRESET /datum/preset_holoimage/engineer
+ NAME Jacob Ullman
+ SAY Lets blow this popsicle stand of a station.
+ DELAY 20
+ PRESET /datum/preset_holoimage/engineer/atmos
+ NAME Lindsey Cuffler
+ SAY Uh, sir? Shouldn't we call for a secondary shuttle? The bluespace drive on this thing made an awfully weird noise when we jumped here..
+ DELAY 30
+ PRESET /datum/preset_holoimage/engineer/ce
+ NAME Geysr Shorthalt
+ SAY Pah! Ship techie at the dock said to give it a good few kicks if it started acting up, let me just..
+ DELAY 25
+ SOUND punch
+ SOUND sparks
+ DELAY 10
+ SOUND punch
+ SOUND sparks
+ DELAY 10
+ SOUND punch
+ SOUND sparks
+ SOUND warpspeed
+ DELAY 15
+ PRESET /datum/preset_holoimage/engineer/atmos
+ NAME Lindsey Cuffler
+ SAY Uhh.. is it supposed to be doing that??
+ DELAY 15
+ PRESET /datum/preset_holoimage/engineer/ce
+ NAME Geysr Shorthalt
+ SAY See? Working as intended. Now, are we all ready?
+ DELAY 10
+ PRESET /datum/preset_holoimage/engineer
+ NAME Jacob Ullman
+ SAY Is it supposed to be glowing like that?
+ DELAY 20
+ SOUND explosion
+
+ "}
+
+/obj/item/disk/holodisk/ruin/snowengieruin
+ name = "Blackbox Print-out #EB412"
+ desc = "A holodisk containing the last moments of EB412. There's a bloody fingerprint on it."
+ preset_image_type = /datum/preset_holoimage/engineer
+ preset_record_text = {"
+ NAME Dave Tundrale
+ SAY Maria, how's Build?
+ DELAY 10
+ NAME Maria Dell
+ PRESET /datum/preset_holoimage/engineer/atmos
+ SAY It's fine, don't worry. I've got Plastic on it. And frankly, i'm kinda busy with, the, uhhm, incinerator.
+ DELAY 30
+ NAME Dave Tundrale
+ PRESET /datum/preset_holoimage/engineer
+ SAY Aight, wonderful. The science mans been kinda shit though. No RCDs-
+ DELAY 20
+ NAME Maria Dell
+ PRESET /datum/preset_holoimage/engineer/atmos
+ SAY Enough about your RCDs. They're not even that important, just bui-
+ DELAY 15
+ SOUND explosion
+ DELAY 10
+ SAY Oh, shit!
+ DELAY 10
+ PRESET /datum/preset_holoimage/engineer/atmos/rig
+ LANGUAGE /datum/language/narsie
+ NAME Unknown
+ SAY RISE, MY LORD!!
+ DELAY 10
+ LANGUAGE /datum/language/common
+ NAME Plastic
+ PRESET /datum/preset_holoimage/engineer/rig
+ SAY Fuck, fuck, fuck!
+ DELAY 20
+ SAY It's loose! CALL THE FUCKING SHUTT-
+ DELAY 10
+ PRESET /datum/preset_holoimage/corgi
+ NAME Blackbox Automated Message
+ SAY Connection lost. Dumping audio logs to disk.
+ DELAY 50"}
diff --git a/code/datums/skills/_check_skills.dm b/code/datums/skills/_check_skills.dm
index 41939dbee3..91b36a123f 100644
--- a/code/datums/skills/_check_skills.dm
+++ b/code/datums/skills/_check_skills.dm
@@ -14,11 +14,14 @@
mind.skill_holder.ui_interact(src)
-/datum/skill_holder/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/skill_holder/ui_state(mob/user)
+ return GLOB.always_state
+
+/datum/skill_holder/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "SkillPanel", "[owner.name]'s Skills", 620, 580, master_ui, state)
- ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
+ ui = new(user, src, "SkillPanel", "[owner.name]'s Skills")
+ ui.set_autoupdate(FALSE)
ui.open()
else if(need_static_data_update)
update_static_data(user)
diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm
index d66445b8a1..95a7d8e633 100644
--- a/code/datums/spawners_menu.dm
+++ b/code/datums/spawners_menu.dm
@@ -6,10 +6,13 @@
qdel(src)
owner = new_owner
-/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/spawners_menu/ui_state(mob/user)
+ return GLOB.observer_state
+
+/datum/spawners_menu/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "SpawnersMenu", "Spawners Menu", 700, 600, master_ui, state)
+ ui = new(user, src, "SpawnersMenu")
ui.open()
/datum/spawners_menu/ui_data(mob/user)
@@ -42,11 +45,15 @@
if(..())
return
- var/spawner_ref = pick(GLOB.mob_spawners[params["name"]])
- var/obj/effect/mob_spawn/MS = locate(spawner_ref) in GLOB.poi_list
- if(!MS)
+ var/group_name = params["name"]
+ if(!group_name || !(group_name in GLOB.mob_spawners))
+ return
+ var/list/spawnerlist = GLOB.mob_spawners[group_name]
+ if(!spawnerlist.len)
+ return
+ var/obj/effect/mob_spawn/MS = pick(spawnerlist)
+ if(!istype(MS) || !(MS in GLOB.poi_list))
return
-
switch(action)
if("jump")
if(MS)
@@ -55,4 +62,4 @@
if("spawn")
if(MS)
MS.attack_ghost(owner)
- . = TRUE
\ No newline at end of file
+ . = TRUE
diff --git a/code/datums/wires/_wires.dm b/code/datums/wires/_wires.dm
index 75bd4f5c17..04fbc4a590 100644
--- a/code/datums/wires/_wires.dm
+++ b/code/datums/wires/_wires.dm
@@ -97,6 +97,12 @@
/datum/wires/proc/get_wire(color)
return colors[color]
+/datum/wires/proc/get_color_of_wire(wire_type)
+ for(var/color in colors)
+ var/other_type = colors[color]
+ if(wire_type == other_type)
+ return color
+
/datum/wires/proc/get_attached(color)
if(assemblies[color])
return assemblies[color]
@@ -117,7 +123,7 @@
return TRUE
/datum/wires/proc/is_dud(wire)
- return findtext(wire, WIRE_DUD_PREFIX)
+ return findtext(wire, WIRE_DUD_PREFIX, 1, length(WIRE_DUD_PREFIX) + 1)
/datum/wires/proc/is_dud_color(color)
return is_dud(get_wire(color))
@@ -197,6 +203,7 @@
S.forceMove(holder.drop_location())
return S
+/// Called from [/atom/proc/emp_act]
/datum/wires/proc/emp_pulse()
var/list/possible_wires = shuffle(wires)
var/remaining_pulses = MAXIMUM_EMP_WIRES
@@ -239,11 +246,13 @@
return ..()
return UI_CLOSE
-/datum/wires/ui_interact(mob/user, ui_key = "wires", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/wires/ui_state(mob/user)
+ return GLOB.physical_state
+
+/datum/wires/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
- ui = new(user, src, ui_key, "Wires", "[holder.name] Wires", 350, 150 + wires.len * 30, master_ui, state)
+ ui = new(user, src, "Wires", "[holder.name] Wires")
ui.open()
/datum/wires/ui_data(mob/user)
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index 2b13344cc6..6cef7ac50f 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -53,11 +53,10 @@
/datum/wires/airlock/interactable(mob/user)
var/obj/machinery/door/airlock/A = holder
- if(!A.panel_open)
- return FALSE
if(!A.hasSiliconAccessInArea(user) && A.isElectrified() && A.shock(user, 100))
return FALSE
- return TRUE
+ if(A.panel_open)
+ return TRUE
/datum/wires/airlock/get_status()
var/obj/machinery/door/airlock/A = holder
@@ -115,10 +114,7 @@
A.aiControlDisabled = -1
if(WIRE_SHOCK) // Pulse to shock the door for 10 ticks.
if(!A.secondsElectrified)
- A.set_electrified(30)
- if(usr)
- LAZYADD(A.shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(usr)]"))
- log_combat(usr, A, "electrified")
+ A.set_electrified(30, usr)
if(WIRE_SAFETY)
A.safe = !A.safe
if(!A.density)
@@ -135,21 +131,17 @@
if(WIRE_POWER1, WIRE_POWER2) // Cut to loose power, repair all to gain power.
if(mend && !is_cut(WIRE_POWER1) && !is_cut(WIRE_POWER2))
A.regainMainPower()
- if(usr)
- A.shock(usr, 50)
else
A.loseMainPower()
- if(usr)
- A.shock(usr, 50)
+ if(isliving(usr))
+ A.shock(usr, 50)
if(WIRE_BACKUP1, WIRE_BACKUP2) // Cut to loose backup power, repair all to gain backup power.
if(mend && !is_cut(WIRE_BACKUP1) && !is_cut(WIRE_BACKUP2))
A.regainBackupPower()
- if(usr)
- A.shock(usr, 50)
else
A.loseBackupPower()
- if(usr)
- A.shock(usr, 50)
+ if(isliving(usr))
+ A.shock(usr, 50)
if(WIRE_BOLTS) // Cut to drop bolts, mend does nothing.
if(!mend)
A.bolt()
@@ -170,10 +162,7 @@
A.set_electrified(0)
else
if(A.secondsElectrified != -1)
- A.set_electrified(-1)
- if(usr)
- LAZYADD(A.shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(usr)]"))
- log_combat(usr, A, "electrified")
+ A.set_electrified(-1, usr)
if(WIRE_SAFETY) // Cut to disable safeties, mend to re-enable.
A.safe = mend
if(WIRE_TIMING) // Cut to disable auto-close, mend to re-enable.
@@ -184,5 +173,5 @@
A.lights = mend
A.update_icon()
if(WIRE_ZAP1, WIRE_ZAP2) // Ouch.
- if(usr)
- A.shock(usr, 50)
+ if(isliving(usr))
+ A.shock(usr, 50)
\ No newline at end of file
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index de9df3a23e..dfa9776489 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -962,15 +962,16 @@
if(source != target)
target.log_talk(message, message_type, tag="[tag] from [key_name(source)]", log_globally=FALSE)
-/*
-Proc for attack log creation, because really why not
-1 argument is the actor performing the action
-2 argument is the target of the action
-3 is a verb describing the action (e.g. punched, throwed, kicked, etc.)
-4 is a tool with which the action was made (usually an item)
-5 is any additional text, which will be appended to the rest of the log line
-*/
-
+/**
+ * Log a combat message in the attack log
+ *
+ * Arguments:
+ * * atom/user - argument is the actor performing the action
+ * * atom/target - argument is the target of the action
+ * * what_done - is a verb describing the action (e.g. punched, throwed, kicked, etc.)
+ * * atom/object - is a tool with which the action was made (usually an item)
+ * * addition - is any additional text, which will be appended to the rest of the log line
+ */
/proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null)
var/ssource = key_name(user)
var/starget = key_name(target)
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 55976d69c2..2415770e66 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -201,15 +201,15 @@
. = ..()
. += "Alt-click [src] to [state_open ? "close" : "open"] it. "
-/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
+/obj/machinery/sleeper/ui_state(mob/user)
+ if(controls_inside)
+ return GLOB.notcontained_state
+ return GLOB.default_state
- if(controls_inside && state == GLOB.notcontained_state)
- state = GLOB.default_state // If it has a set of controls on the inside, make it actually controllable by the mob in it.
-
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/sleeper/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Sleeper", name, 550, 700, master_ui, state)
+ ui = new(user, src, "Sleeper", name)
ui.open()
/obj/machinery/sleeper/process()
diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm
index 8db5ca1d17..b004746051 100644
--- a/code/game/machinery/announcement_system.dm
+++ b/code/game/machinery/announcement_system.dm
@@ -93,13 +93,10 @@ GLOBAL_LIST_EMPTY(announcement_systems)
for(var/channel in channels)
radio.talk_into(src, message, channel)
-//config stuff
-
-/obj/machinery/announcement_system/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- . = ..()
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/announcement_system/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AutomatedAnnouncement", "Automated Announcement System", 500, 225, master_ui, state)
+ ui = new(user, src, "AutomatedAnnouncement")
ui.open()
/obj/machinery/announcement_system/ui_data()
diff --git a/code/game/machinery/bank_machine.dm b/code/game/machinery/bank_machine.dm
index 8033f538ba..882d0b989c 100644
--- a/code/game/machinery/bank_machine.dm
+++ b/code/game/machinery/bank_machine.dm
@@ -3,6 +3,7 @@
desc = "A machine used to deposit and withdraw station funds."
icon = 'goon/icons/obj/goon_terminals.dmi'
idle_power_usage = 100
+
var/siphoning = FALSE
var/next_warning = 0
var/obj/item/radio/radio
@@ -38,7 +39,6 @@
return
return ..()
-
/obj/machinery/computer/bank_machine/process()
..()
if(siphoning)
@@ -51,7 +51,7 @@
end_syphon()
return
- playsound(src.loc, 'sound/items/poster_being_created.ogg', 100, 1)
+ playsound(src, 'sound/items/poster_being_created.ogg', 100, TRUE)
syphoning_credits += 200
D.adjust_money(-200)
if(next_warning < world.time && prob(15))
@@ -60,17 +60,20 @@
radio.talk_into(src, message, radio_channel)
next_warning = world.time + minimum_time_between_warnings
-/obj/machinery/computer/bank_machine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/bank_machine/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "BankMachine", name, 320, 165, master_ui, state)
+ ui = new(user, src, "BankMachine", name)
ui.open()
/obj/machinery/computer/bank_machine/ui_data(mob/user)
var/list/data = list()
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
- data["current_balance"] = D.account_balance
+
+ if(D)
+ data["current_balance"] = D.account_balance
+ else
+ data["current_balance"] = 0
data["siphoning"] = siphoning
data["station_name"] = station_name()
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index c8efe319db..a2fdc2336f 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -43,10 +43,13 @@
table.computer = src
break
-/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/operating/ui_state(mob/user)
+ return GLOB.not_incapacitated_state
+
+/obj/machinery/computer/operating/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "OperatingComputer", name, 350, 470, master_ui, state)
+ ui = new(user, src, "OperatingComputer", name)
ui.open()
/obj/machinery/computer/operating/ui_data(mob/user)
@@ -153,6 +156,8 @@
))
return data
+
+
/obj/machinery/computer/operating/ui_act(action, params)
if(..())
return
diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm
index d8553854b2..8ebb64097d 100644
--- a/code/game/machinery/computer/aifixer.dm
+++ b/code/game/machinery/computer/aifixer.dm
@@ -2,14 +2,13 @@
name = "\improper AI system integrity restorer"
desc = "Used with intelliCards containing nonfunctional AIs to restore them to working order."
req_access = list(ACCESS_CAPTAIN, ACCESS_ROBOTICS, ACCESS_HEADS)
- var/mob/living/silicon/ai/occupier = null
- var/active = 0
- circuit = /obj/item/circuitboard/computer/aifixer
icon_keyboard = "tech_key"
icon_screen = "ai-fixer"
light_color = LIGHT_COLOR_PINK
- ui_x = 370
- ui_y = 360
+ circuit = /obj/item/circuitboard/computer/aifixer
+
+ var/mob/living/silicon/ai/occupier = null
+ var/active = FALSE
/obj/machinery/computer/aifixer/attackby(obj/I, mob/user, params)
if(occupier && istype(I, /obj/item/screwdriver))
@@ -20,64 +19,45 @@
else
return ..()
-/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+
+/obj/machinery/computer/aifixer/ui_interact(mob/user, datum/tgui/ui) //artur didn't port this correctly
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AiRestorer", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "AiRestorer", name)
ui.open()
- var/dat = ""
+/obj/machinery/computer/aifixer/ui_data(mob/user)
+ var/list/data = list()
- if (src.occupier)
- var/laws
- dat += "
Stored AI: [src.occupier.name] "
- dat += "System integrity: [(src.occupier.health+100)/2]% "
+ data["ejectable"] = FALSE
+ data["AI_present"] = FALSE
+ data["error"] = null
+ if(!occupier)
+ data["error"] = "Please transfer an AI unit."
+ else
+ data["AI_present"] = TRUE
+ data["name"] = occupier.name
+ data["restoring"] = active
+ data["health"] = (occupier.health + 100) / 2
+ data["isDead"] = occupier.stat == DEAD
+ data["laws"] = occupier.laws.get_law_list(include_zeroth = TRUE, render_html = FALSE)
- if (src.occupier.laws.zeroth)
- laws += "0: [src.occupier.laws.zeroth] "
+ return data
- for (var/index = 1, index <= src.occupier.laws.hacked.len, index++)
- var/law = src.occupier.laws.hacked[index]
- if (length(law) > 0)
- var/num = ionnum()
- laws += "[num]: [law] "
+/obj/machinery/computer/aifixer/ui_act(action, params)
+ if(..())
+ return
+ if(!occupier)
+ active = FALSE
- for (var/index = 1, index <= src.occupier.laws.ion.len, index++)
- var/law = src.occupier.laws.ion[index]
- if (length(law) > 0)
- var/num = ionnum()
- laws += "[num]: [law] "
-
- var/number = 1
- for (var/index = 1, index <= src.occupier.laws.inherent.len, index++)
- var/law = src.occupier.laws.inherent[index]
- if (length(law) > 0)
- laws += "[number]: [law] "
- number++
-
- for (var/index = 1, index <= src.occupier.laws.supplied.len, index++)
- var/law = src.occupier.laws.supplied[index]
- if (length(law) > 0)
- laws += "[number]: [law] "
- number++
-
- dat += "Laws: [laws] "
-
- if (src.occupier.stat == DEAD)
- dat += "AI non-functional "
- else
- dat += "AI functional "
- if (!src.active)
- dat += {"Begin Reconstruction "}
- else
- dat += " Reconstruction in process, please wait. "
- dat += {"Close "}
- var/datum/browser/popup = new(user, "computer", "AI System Integrity Restorer", 400, 500)
- popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
- popup.open()
- return
+ switch(action)
+ if("PRG_beginReconstruction")
+ if(occupier?.health < 100)
+ to_chat(usr, "Reconstruction in progress. This will take several minutes. ")
+ playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE)
+ active = TRUE
+ occupier.notify_ghost_cloning("Your core files are being restored!", source = src)
+ . = TRUE
/obj/machinery/computer/aifixer/proc/Fix()
use_power(1000)
@@ -97,17 +77,6 @@
active = Fix()
if(oldstat != occupier.stat)
update_icon()
- updateDialog()
-
-/obj/machinery/computer/aifixer/Topic(href, href_list)
- if(..())
- return
- if(href_list["fix"])
- to_chat(usr, "Reconstruction in progress. This will take several minutes. ")
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 25, 0)
- active = TRUE
- add_fingerprint(usr)
- updateUsrDialog()
/obj/machinery/computer/aifixer/update_overlays()
. = ..()
diff --git a/code/game/machinery/computer/apc_control.dm b/code/game/machinery/computer/apc_control.dm
index eae2d5fa5b..c14837b8e3 100644
--- a/code/game/machinery/computer/apc_control.dm
+++ b/code/game/machinery/computer/apc_control.dm
@@ -3,19 +3,19 @@
desc = "Used to remotely control the flow of power to different parts of the station."
icon_screen = "solar"
icon_keyboard = "power_key"
- req_access = list(ACCESS_ENGINE)
+ req_access = list(ACCESS_CE)
circuit = /obj/item/circuitboard/computer/apc_control
light_color = LIGHT_COLOR_YELLOW
var/mob/living/operator //Who's operating the computer right now
var/obj/machinery/power/apc/active_apc //The APC we're using right now
- var/list/result_filters //For sorting the results
- var/checking_logs = 0
+ var/should_log = TRUE
+ var/restoring = FALSE
var/list/logs
- var/auth_id = "\[NULL\]"
+ var/auth_id = "\[NULL\]:"
-/obj/machinery/computer/apc_control/Initialize()
+/obj/machinery/computer/apc_control/Initialize(mapload, obj/item/circuitboard/C)
. = ..()
- result_filters = list("Name" = null, "Charge Above" = null, "Charge Below" = null, "Responsive" = null)
+ logs = list()
/obj/machinery/computer/apc_control/process()
if(operator && (!operator.Adjacent(src) || stat))
@@ -23,8 +23,8 @@
if(active_apc)
if(!active_apc.locked)
active_apc.say("Remote access canceled. Interface locked.")
- playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, 0)
- playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, 0)
+ playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, FALSE)
+ playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, FALSE)
active_apc.locked = TRUE
active_apc.update_icon()
active_apc.remote_control = null
@@ -34,171 +34,163 @@
if(!IsAdminGhost(user))
to_chat(user,"[src] does not support AI control. ") //You already have APC access, cheater!
return
- ..(user)
+ ..()
/obj/machinery/computer/apc_control/proc/check_apc(obj/machinery/power/apc/APC)
return APC.z == z && !APC.malfhack && !APC.aidisabled && !(APC.obj_flags & EMAGGED) && !APC.stat && !istype(APC.area, /area/ai_monitored) && !APC.area.outdoors
-/obj/machinery/computer/apc_control/ui_interact(mob/living/user)
- . = ..()
- var/dat
- if(authenticated)
- if(!checking_logs)
- dat += "Logged in as [auth_id]. "
- dat += "Filters "
- dat += "Name: [result_filters["Name"] ? result_filters["Name"] : "None set"] "
- dat += "Charge: \>[result_filters["Charge Above"] ? result_filters["Charge Above"] : "NaN"]% and \<[result_filters["Charge Below"] ? result_filters["Charge Below"] : "NaN"]% "
- dat += "Accessible: [result_filters["Responsive"] ? "Non-Responsive Only" : "All"] "
- for(var/A in GLOB.apcs_list)
- if(check_apc(A))
- var/obj/machinery/power/apc/APC = A
- if(result_filters["Name"] && !findtext(APC.name, result_filters["Name"]) && !findtext(APC.area.name, result_filters["Name"]))
- continue
- if(result_filters["Charge Above"] && (!APC.cell || (APC.cell && (APC.cell.charge / APC.cell.maxcharge) < result_filters["Charge Above"] / 100)))
- continue
- if(result_filters["Charge Below"] && APC.cell && (APC.cell.charge / APC.cell.maxcharge) > result_filters["Charge Below"] / 100)
- continue
- if(result_filters["Responsive"] && !APC.aidisabled)
- continue
- dat += "[A] \
- Charge: [APC.cell ? "[DisplayEnergy(APC.cell.charge)] / [DisplayEnergy(APC.cell.maxcharge)] ([round((APC.cell.charge / APC.cell.maxcharge) * 100)]%)" : "No Powercell Installed"] \
- Area: [APC.area] \
- [APC.aidisabled || APC.panel_open ? "APC does not respond to interface query. " : "APC responds to interface query. "] "
- dat += "Check Logs "
- dat += "Log Out "
- if(obj_flags & EMAGGED)
- dat += "WARNING: Logging functionality partially disabled from outside source. "
- dat += "Restore logging functionality? "
- else
- if(logs.len)
- for(var/entry in logs)
- dat += "[entry] "
- else
- dat += "No activity has been recorded at this time. "
- if(obj_flags & EMAGGED)
- dat += "@#%! CLEAR LOGS "
- dat += "Return "
- operator = user
- else
- dat = "Please swipe a valid ID to log in... "
- var/datum/browser/popup = new(user, "apc_control", name, 600, 400)
- popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
- popup.open()
+/obj/machinery/computer/apc_control/ui_interact(mob/user, datum/tgui/ui)
+ operator = user
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ApcControl")
+ ui.open()
-/obj/machinery/computer/apc_control/Topic(href, href_list)
+/obj/machinery/computer/apc_control/ui_data(mob/user)
+ var/list/data = list()
+ data["auth_id"] = auth_id
+ data["authenticated"] = authenticated
+ data["emagged"] = obj_flags & EMAGGED
+ data["logging"] = should_log
+ data["restoring"] = restoring
+ data["logs"] = list()
+ data["apcs"] = list()
+
+ for(var/entry in logs)
+ data["logs"] += list(list("entry" = entry))
+
+ for(var/apc in GLOB.apcs_list)
+ if(check_apc(apc))
+ var/obj/machinery/power/apc/A = apc
+ var/has_cell = (A.cell) ? TRUE : FALSE
+ data["apcs"] += list(list(
+ "name" = A.area.name,
+ "operating" = A.operating,
+ "charge" = (has_cell) ? A.cell.percent() : "NOCELL",
+ "load" = DisplayPower(A.lastused_total),
+ "charging" = A.charging,
+ "chargeMode" = A.chargemode,
+ "eqp" = A.equipment,
+ "lgt" = A.lighting,
+ "env" = A.environ,
+ "responds" = A.aidisabled || A.panel_open,
+ "ref" = REF(A)
+ )
+ )
+ return data
+
+/obj/machinery/computer/apc_control/ui_act(action, params)
if(..())
return
- if(!usr || !usr.canUseTopic(src) || stat || QDELETED(src))
- return
- if(href_list["authenticate"])
- var/obj/item/card/id/ID = usr.get_idcard(TRUE)
- if(ID && istype(ID))
- if(check_access(ID))
+ switch(action)
+ if("log-in")
+ if(obj_flags & EMAGGED)
authenticated = TRUE
- auth_id = "[ID.registered_name] ([ID.assignment])"
- log_activity("logged in")
- if(href_list["log_out"])
- log_activity("logged out")
- authenticated = FALSE
- auth_id = "\[NULL\]"
- if(href_list["restore_logging"])
- to_chat(usr, "[icon2html(src, usr)] Logging functionality restored from backup data. ")
- obj_flags &= ~EMAGGED
- LAZYADD(logs, "-=- Logging restored to full functionality at this point -=- ")
- if(href_list["access_apc"])
- playsound(src, "terminal_type", 50, 0)
- var/obj/machinery/power/apc/APC = locate(href_list["access_apc"]) in GLOB.apcs_list
- if(!APC || APC.aidisabled || APC.panel_open || QDELETED(APC))
- to_chat(usr, "[icon2html(src, usr)] APC does not return interface request. Remote access may be disabled. ")
- return
- if(active_apc)
- to_chat(usr, "[icon2html(src, usr)] Disconnected from [active_apc]. ")
- active_apc.say("Remote access canceled. Interface locked.")
- playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, 0)
- playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, 0)
- active_apc.locked = TRUE
- active_apc.update_icon()
- active_apc.remote_control = null
- active_apc = null
- to_chat(usr, "[icon2html(src, usr)] Connected to APC in [get_area_name(APC.area, TRUE)]. Interface request sent. ")
- log_activity("remotely accessed APC in [get_area_name(APC.area, TRUE)]")
- APC.remote_control = src
- APC.ui_interact(usr)
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
- message_admins("[ADMIN_LOOKUPFLW(usr)] remotely accessed [APC] from [src] at [AREACOORD(src)].")
- log_game("[key_name(usr)] remotely accessed [APC] from [src] at [AREACOORD(src)].")
- if(APC.locked)
- APC.say("Remote access detected. Interface unlocked.")
- playsound(APC, 'sound/machines/boltsup.ogg', 25, 0)
- playsound(APC, 'sound/machines/terminal_alert.ogg', 50, 0)
- APC.locked = FALSE
- APC.update_icon()
- active_apc = APC
- if(href_list["name_filter"])
- playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
- var/new_filter = stripped_input(usr, "What name are you looking for?", name)
- if(!src || !usr || !usr.canUseTopic(src) || stat || QDELETED(src))
- return
- log_activity("changed name filter to \"[new_filter]\"")
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
- result_filters["Name"] = new_filter
- if(href_list["above_filter"])
- playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
- var/new_filter = input(usr, "Enter a percentage from 1-100 to sort by (greater than).", name) as null|num
- if(!src || !usr || !usr.canUseTopic(src) || stat || QDELETED(src))
- return
- log_activity("changed greater than charge filter to \"[new_filter]\"")
- if(new_filter)
- new_filter = clamp(new_filter, 0, 100)
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
- result_filters["Charge Above"] = new_filter
- if(href_list["below_filter"])
- playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
- var/new_filter = input(usr, "Enter a percentage from 1-100 to sort by (lesser than).", name) as null|num
- if(!src || !usr || !usr.canUseTopic(src) || stat || QDELETED(src))
- return
- log_activity("changed lesser than charge filter to \"[new_filter]\"")
- if(new_filter)
- new_filter = clamp(new_filter, 0, 100)
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
- result_filters["Charge Below"] = new_filter
- if(href_list["access_filter"])
- if(isnull(result_filters["Responsive"]))
- result_filters["Responsive"] = 1
- log_activity("sorted by non-responsive APCs only")
- else
- result_filters["Responsive"] = !result_filters["Responsive"]
- log_activity("sorted by all APCs")
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
- if(href_list["check_logs"])
- checking_logs = TRUE
- log_activity("checked logs")
- if(href_list["check_apcs"])
- checking_logs = FALSE
- log_activity("checked APCs")
- if(href_list["clear_logs"])
- logs = list()
- ui_interact(usr) //Refresh the UI after a filter changes
+ auth_id = "Unknown (Unknown):"
+ log_activity("[auth_id] logged in to the terminal")
+ return
+ var/obj/item/card/id/ID = operator.get_idcard(TRUE)
+ if(ID && istype(ID))
+ if(check_access(ID))
+ authenticated = TRUE
+ auth_id = "[ID.registered_name] ([ID.assignment]):"
+ log_activity("[auth_id] logged in to the terminal")
+ playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
+ else
+ auth_id = "[ID.registered_name] ([ID.assignment]):"
+ log_activity("[auth_id] attempted to log into the terminal")
+ return
+ auth_id = "Unknown (Unknown):"
+ log_activity("[auth_id] attempted to log into the terminal")
+ if("log-out")
+ log_activity("[auth_id] logged out of the terminal")
+ playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
+ authenticated = FALSE
+ auth_id = "\[NULL\]"
+ if("toggle-logs")
+ should_log = !should_log
+ log_game("[key_name(operator)] set the logs of [src] in [AREACOORD(src)] [should_log ? "On" : "Off"]")
+ if("restore-console")
+ restoring = TRUE
+ addtimer(CALLBACK(src, .proc/restore_comp), rand(3,5) * 9)
+ if("access-apc")
+ var/ref = params["ref"]
+ playsound(src, "terminal_type", 50, FALSE)
+ var/obj/machinery/power/apc/APC = locate(ref) in GLOB.apcs_list
+ if(!APC)
+ return
+ if(active_apc)
+ to_chat(operator, "[icon2html(src, auth_id)] Disconnected from [active_apc]. ")
+ active_apc.say("Remote access canceled. Interface locked.")
+ playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, FALSE)
+ playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, FALSE)
+ active_apc.locked = TRUE
+ active_apc.update_icon()
+ active_apc.remote_control = null
+ active_apc = null
+ APC.remote_control = src
+ APC.ui_interact(operator)
+ playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ log_game("[key_name(operator)] remotely accessed [APC] from [src] at [AREACOORD(src)].")
+ log_activity("[auth_id] remotely accessed APC in [get_area_name(APC.area, TRUE)]")
+ if(APC.locked)
+ APC.say("Remote access detected. Interface unlocked.")
+ playsound(APC, 'sound/machines/boltsup.ogg', 25, FALSE)
+ playsound(APC, 'sound/machines/terminal_alert.ogg', 50, FALSE)
+ APC.locked = FALSE
+ APC.update_icon()
+ active_apc = APC
+ if("check-logs")
+ log_activity("Checked Logs")
+ if("check-apcs")
+ log_activity("Checked APCs")
+ if("toggle-minor")
+ var/ref = params["ref"]
+ var/type = params["type"]
+ var/value = params["value"]
+ var/obj/machinery/power/apc/target = locate(ref) in GLOB.apcs_list
+ if(!target)
+ return
+ target.vars[type] = target.setsubsystem(text2num(value))
+ target.update_icon()
+ target.update()
+ var/setTo = ""
+ switch(target.vars[type])
+ if(0)
+ setTo = "Off"
+ if(1)
+ setTo = "Auto Off"
+ if(2)
+ setTo = "On"
+ if(3)
+ setTo = "Auto On"
+ log_activity("Set APC [target.area.name] [type] to [setTo]")
+ log_game("[key_name(operator)] Set APC [target.area.name] [type] to [setTo]]")
+ if("breaker")
+ var/ref = params["ref"]
+ var/obj/machinery/power/apc/target = locate(ref) in GLOB.apcs_list
+ target.toggle_breaker()
+ var/setTo = target.operating ? "On" : "Off"
+ log_activity("Turned APC [target.area.name]'s breaker [setTo]")
/obj/machinery/computer/apc_control/emag_act(mob/user)
- . = ..()
- if(!authenticated)
- to_chat(user, "You bypass [src]'s access requirements using your emag. ")
- authenticated = TRUE
- log_activity("logged in")
- else
- if(obj_flags & EMAGGED)
- return
- user.visible_message("You emag [src], disabling precise logging and allowing you to clear logs. ")
- log_game("[key_name(user)] emagged [src] at [AREACOORD(src)], disabling operator tracking.")
- obj_flags |= EMAGGED
- playsound(src, "sparks", 50, 1)
- return TRUE
+ if(obj_flags & EMAGGED)
+ return
+ obj_flags |= EMAGGED
+ log_game("[key_name(user)] emagged [src] at [AREACOORD(src)]")
+ playsound(src, "sparks", 50, TRUE)
/obj/machinery/computer/apc_control/proc/log_activity(log_text)
- var/op_string = operator && !(obj_flags & EMAGGED) ? operator : "\[NULL OPERATOR\]"
- LAZYADD(logs, "([STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]) [op_string] [log_text]")
+ if(!should_log)
+ return
+ LAZYADD(logs, "([STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]): [auth_id] [log_text]")
+
+/obj/machinery/computer/apc_control/proc/restore_comp()
+ obj_flags &= ~EMAGGED
+ should_log = TRUE
+ log_game("[key_name(operator)] restored the logs of [src] in [AREACOORD(src)]")
+ log_activity("-=- Logging restored to full functionality at this point -=-")
+ restoring = FALSE
/mob/proc/using_power_flow_console()
for(var/obj/machinery/computer/apc_control/A in range(1, src))
diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm
index fbedf62734..50a6d10302 100644
--- a/code/game/machinery/computer/atmos_alert.dm
+++ b/code/game/machinery/computer/atmos_alert.dm
@@ -19,11 +19,10 @@
SSradio.remove_object(src, receive_frequency)
return ..()
-/obj/machinery/computer/atmos_alert/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/atmos_alert/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AtmosAlertConsole", name, 350, 300, master_ui, state)
+ ui = new(user, src, "AtmosAlertConsole", name)
ui.open()
/obj/machinery/computer/atmos_alert/ui_data(mob/user)
@@ -45,11 +44,11 @@
if("clear")
var/zone = params["zone"]
if(zone in priority_alarms)
- to_chat(usr, "Priority alarm for [zone] cleared.")
+ to_chat(usr, "Priority alarm for [zone] cleared. ")
priority_alarms -= zone
. = TRUE
if(zone in minor_alarms)
- to_chat(usr, "Minor alarm for [zone] cleared.")
+ to_chat(usr, "Minor alarm for [zone] cleared. ")
minor_alarms -= zone
. = TRUE
update_icon()
diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm
index 6ee975b338..4ba8d9f3d3 100644
--- a/code/game/machinery/computer/atmos_control.dm
+++ b/code/game/machinery/computer/atmos_control.dm
@@ -91,8 +91,6 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
icon_screen = "tank"
icon_keyboard = "atmos_key"
circuit = /obj/item/circuitboard/computer/atmos_control
- ui_x = 400
- ui_y = 925
var/frequency = FREQ_ATMOS_STORAGE
var/list/sensors = list(
@@ -123,11 +121,10 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
SSradio.remove_object(src, frequency)
return ..()
-/obj/machinery/computer/atmos_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/atmos_control/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AtmosControlConsole", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "AtmosControlConsole", name)
ui.open()
/obj/machinery/computer/atmos_control/ui_data(mob/user)
@@ -265,13 +262,6 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in devices)
U.broadcast_status()
-/obj/machinery/computer/atmos_control/tank/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "AtmosControlConsole", name, ui_x, ui_y, master_ui, state)
- ui.open()
-
/obj/machinery/computer/atmos_control/tank/ui_data(mob/user)
var/list/data = ..()
data["tank"] = TRUE
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index 2b3416e1c8..d42291cd3c 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -5,8 +5,6 @@
icon_keyboard = "security_key"
circuit = /obj/item/circuitboard/computer/security
light_color = LIGHT_COLOR_RED
- ui_x = 870
- ui_y = 708
var/list/network = list("ss13")
var/obj/machinery/camera/active_camera
@@ -55,11 +53,9 @@
network -= i
network += "[idnum][i]"
-/obj/machinery/computer/security/ui_interact(\
- mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+/obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui)
// Update UI
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SStgui.try_update_ui(user, src, ui)
// Show static if can't use the camera
if(!active_camera?.can_use())
show_camera_static()
@@ -76,10 +72,11 @@
use_power(active_power_usage)
// Register map objects
user.client.register_map_obj(cam_screen)
- user.client.register_map_obj(cam_plane_master)
+ for(var/plane in cam_plane_master)
+ user.client.register_map_obj(plane)
user.client.register_map_obj(cam_background)
// Open UI
- ui = new(user, src, ui_key, "CameraConsole", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "CameraConsole", name)
ui.open()
/obj/machinery/computer/security/ui_data()
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index cd8b5c1c59..19da4f75d8 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -76,11 +76,10 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
/datum/crewmonitor/Destroy()
return ..()
-/datum/crewmonitor/ui_interact(mob/user, ui_key = "crew", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/crewmonitor/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
- ui = new(user, src, ui_key, "CrewConsole", "crew monitor", 800, 600 , master_ui, state)
+ ui = new(user, src, "CrewConsole")
ui.open()
/datum/crewmonitor/proc/show(mob/M, source)
diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm
index 2dce50aa88..286c106788 100644
--- a/code/game/machinery/computer/dna_console.dm
+++ b/code/game/machinery/computer/dna_console.dm
@@ -220,9 +220,7 @@
/obj/machinery/computer/scan_consolenew/examine(mob/user)
. = ..()
-/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- . = ..()
-
+/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, datum/tgui/ui)
// Most of ui_interact is spent setting variables for passing to the tgui
// interface.
// We can also do some general state processing here too as it's a good
@@ -264,10 +262,9 @@
time_to_pulse = round((rad_pulse_timer - world.time)/10)
// Attempt to update tgui ui, open and update if needed.
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
-
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "DnaConsole", name, 539, 710, master_ui, state)
+ ui = new(user, src, "DnaConsole")
ui.open()
/obj/machinery/computer/scan_consolenew/ui_data(mob/user)
diff --git a/code/game/machinery/computer/launchpad_control.dm b/code/game/machinery/computer/launchpad_control.dm
index fdbe2f1060..b2f7ae73a1 100644
--- a/code/game/machinery/computer/launchpad_control.dm
+++ b/code/game/machinery/computer/launchpad_control.dm
@@ -53,10 +53,10 @@
var/obj/machinery/launchpad/pad = launchpads[number]
return pad
-/obj/machinery/computer/launchpad/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/launchpad/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "LaunchpadConsole", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "LaunchpadConsole", name)
ui.open()
/obj/machinery/computer/launchpad/ui_data(mob/user)
@@ -128,4 +128,4 @@
if("pull")
teleport(usr, current_pad, FALSE)
. = TRUE
- . = TRUE
\ No newline at end of file
+ . = TRUE
diff --git a/code/game/machinery/computer/prisoner/gulag_teleporter.dm b/code/game/machinery/computer/prisoner/gulag_teleporter.dm
index dd925fbe3b..13727b585b 100644
--- a/code/game/machinery/computer/prisoner/gulag_teleporter.dm
+++ b/code/game/machinery/computer/prisoner/gulag_teleporter.dm
@@ -6,8 +6,6 @@
icon_keyboard = "security_key"
req_access = list(ACCESS_ARMORY)
circuit = /obj/item/circuitboard/computer/gulag_teleporter_console
- ui_x = 350
- ui_y = 295
var/default_goal = 200
var/obj/machinery/gulag_teleporter/teleporter = null
@@ -21,11 +19,10 @@
. = ..()
scan_machinery()
-/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "GulagTeleporterConsole", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "GulagTeleporterConsole", name)
ui.open()
/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_data(mob/user)
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index 4fd9665b1d..401d05da12 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -23,11 +23,10 @@
return
return TRUE
-/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/robotics/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "RoboticsControlConsole", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "RoboticsControlConsole", name)
ui.open()
/obj/machinery/computer/robotics/ui_data(mob/user)
@@ -123,5 +122,5 @@
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(3, TRUE, D)
s.start()
- D.visible_message("\the [D] self destructs! ")
+ D.visible_message("\the [D] self-destructs! ")
D.gib()
diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm
index b4340b9350..7b2e6ab455 100644
--- a/code/game/machinery/computer/station_alert.dm
+++ b/code/game/machinery/computer/station_alert.dm
@@ -16,11 +16,10 @@
GLOB.alert_consoles -= src
return ..()
-/obj/machinery/computer/station_alert/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/station_alert/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "StationAlertConsole", name, 325, 500, master_ui, state)
+ ui = new(user, src, "StationAlertConsole", name)
ui.open()
/obj/machinery/computer/station_alert/ui_data(mob/user)
@@ -31,6 +30,7 @@
data["alarms"][class] = list()
for(var/area in alarms[class])
data["alarms"][class] += area
+
return data
/obj/machinery/computer/station_alert/proc/triggerAlarm(class, area/A, O, obj/source)
diff --git a/code/game/machinery/computer/teleporter.dm b/code/game/machinery/computer/teleporter.dm
index e7957a5b75..3a8ff7bf19 100644
--- a/code/game/machinery/computer/teleporter.dm
+++ b/code/game/machinery/computer/teleporter.dm
@@ -34,11 +34,10 @@
break
return power_station
-obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/teleporter/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Teleporter", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "Teleporter", name)
ui.open()
/obj/machinery/computer/teleporter/ui_data(mob/user)
@@ -56,7 +55,6 @@ obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", datum/t
return data
-
/obj/machinery/computer/teleporter/ui_act(action, params)
if(..())
return
@@ -92,16 +90,18 @@ obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", datum/t
say("Processing hub calibration to target...")
calibrating = TRUE
power_station.update_icon()
- spawn(50 * (3 - power_station.teleporter_hub.accuracy)) //Better parts mean faster calibration
- calibrating = FALSE
- if(check_hub_connection())
- power_station.teleporter_hub.calibrated = TRUE
- say("Calibration complete.")
- else
- say("Error: Unable to detect hub.")
- power_station.update_icon()
+ addtimer(CALLBACK(src, .proc/finish_calibration), 50 * (3 - power_station.teleporter_hub.accuracy)) //Better parts mean faster calibration
. = TRUE
+/obj/machinery/computer/teleporter/proc/finish_calibration()
+ calibrating = FALSE
+ if(check_hub_connection())
+ power_station.teleporter_hub.calibrated = TRUE
+ say("Calibration complete.")
+ else
+ say("Error: Unable to detect hub.")
+ power_station.update_icon()
+
/obj/machinery/computer/teleporter/proc/check_hub_connection()
if(!power_station)
return FALSE
diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm
index 9ecc19e40f..756f924cfa 100644
--- a/code/game/machinery/dance_machine.dm
+++ b/code/game/machinery/dance_machine.dm
@@ -9,6 +9,7 @@
var/active = FALSE
var/list/rangers = list()
var/stop = 0
+ var/volume = 70
var/datum/track/selection = null
/obj/machinery/jukebox/disco
@@ -51,74 +52,91 @@
else
icon_state = "[initial(icon_state)]"
-/obj/machinery/jukebox/ui_interact(mob/user)
- . = ..()
- if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
- return
- if (!anchored)
+/obj/machinery/jukebox/ui_status(mob/user)
+ if(!anchored)
to_chat(user,"This device must be anchored by a wrench! ")
- return
- if(!allowed(user))
+ return UI_CLOSE
+ if(!allowed(user) && !isobserver(user))
to_chat(user,"Error: Access Denied. ")
- user.playsound_local(src,'sound/misc/compiler-failure.ogg', 25, 1)
- return
- if(!SSjukeboxes.songs.len)
+ user.playsound_local(src, 'sound/misc/compiler-failure.ogg', 25, TRUE)
+ return UI_CLOSE
+ if(!SSjukeboxes.songs.len && !isobserver(user))
to_chat(user,"Error: No music tracks have been authorized for your station. Petition Central Command to resolve this issue. ")
- playsound(src,'sound/misc/compiler-failure.ogg', 25, 1)
- return
- var/list/dat = list()
- dat +=" "
- dat += " Select Track "
- if(istype(selection))
- dat += "Track Selected: [selection.song_name] "
- dat += "Track Length: [DisplayTimeText(selection.song_length)] "
- else
- dat += "Track Selected: None! "
- var/datum/browser/popup = new(user, "vending", "[name]", 400, 350)
- popup.set_content(dat.Join())
- popup.open()
+ playsound(src, 'sound/misc/compiler-failure.ogg', 25, TRUE)
+ return UI_CLOSE
+ return ..()
+/obj/machinery/jukebox/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Jukebox", name)
+ ui.open()
-/obj/machinery/jukebox/Topic(href, href_list)
- if(..())
+/obj/machinery/jukebox/ui_data(mob/user)
+ var/list/data = list()
+ data["active"] = active
+ data["songs"] = list()
+ for(var/datum/track/S in SSjukeboxes.songs)
+ var/list/track_data = list(
+ name = S.song_name
+ )
+ data["songs"] += list(track_data)
+ data["track_selected"] = null
+ data["track_length"] = null
+ data["track_beat"] = null
+ if(selection)
+ data["track_selected"] = selection.song_name
+ data["track_length"] = DisplayTimeText(selection.song_length)
+ data["track_beat"] = selection.song_beat
+ data["volume"] = volume
+ return data
+
+/obj/machinery/jukebox/ui_act(action, list/params)
+ . = ..()
+ if(.)
return
- add_fingerprint(usr)
- switch(href_list["action"])
+
+ switch(action)
if("toggle")
- if (QDELETED(src))
+ if(QDELETED(src))
return
if(!active)
if(stop > world.time)
to_chat(usr, "Error: The device is still resetting from the last activation, it will be ready again in [DisplayTimeText(stop-world.time)]. ")
- playsound(src, 'sound/misc/compiler-failure.ogg', 50, 1)
+ playsound(src, 'sound/misc/compiler-failure.ogg', 50, TRUE)
return
- if(!istype(selection))
- to_chat(usr, "Error: Severe user incompetence detected. ")
- playsound(src, 'sound/misc/compiler-failure.ogg', 50, 1)
- return
- if(!activate_music())
- to_chat(usr, "Error: Generic hardware failure. ")
- playsound(src, 'sound/misc/compiler-failure.ogg', 50, 1)
- return
- updateUsrDialog()
- else if(active)
+ activate_music()
+ START_PROCESSING(SSobj, src)
+ return TRUE
+ else
stop = 0
- updateUsrDialog()
- if("select")
+ return TRUE
+ if("select_track")
if(active)
to_chat(usr, "Error: You cannot change the song until the current one is over. ")
return
-
var/list/available = list()
for(var/datum/track/S in SSjukeboxes.songs)
available[S.song_name] = S
- var/selected = input(usr, "Choose your song", "Track:") as null|anything in available
+ var/selected = params["track"]
if(QDELETED(src) || !selected || !istype(available[selected], /datum/track))
return
selection = available[selected]
- updateUsrDialog()
+ return TRUE
+ if("set_volume")
+ var/new_volume = params["volume"]
+ if(new_volume == "reset")
+ volume = initial(volume)
+ return TRUE
+ else if(new_volume == "min")
+ volume = 0
+ return TRUE
+ else if(new_volume == "max")
+ volume = 100
+ return TRUE
+ else if(text2num(new_volume) != null)
+ volume = text2num(new_volume)
+ return TRUE
/obj/machinery/jukebox/proc/activate_music()
var/jukeboxslottotake = SSjukeboxes.addjukebox(src, selection, 2)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 4771e2eca7..515a4672a5 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -50,7 +50,7 @@
integrity_failure = 0.25
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
autoclose = TRUE
- secondsElectrified = 0 //How many seconds remain until the door is no longer electrified. -1 if it is permanently electrified until someone fixes it.
+ secondsElectrified = NOT_ELECTRIFIED //How many seconds remain until the door is no longer electrified. -1 if it is permanently electrified until someone fixes it.
assemblytype = /obj/structure/door_assembly
normalspeed = 1
explosion_block = 1
@@ -298,10 +298,10 @@
/obj/machinery/door/airlock/Destroy()
QDEL_NULL(wires)
+ QDEL_NULL(electronics)
if(charge)
qdel(charge)
charge = null
- QDEL_NULL(electronics)
if (cyclelinkedairlock)
if (cyclelinkedairlock.cyclelinkedairlock == src)
cyclelinkedairlock.cyclelinkedairlock = null
@@ -309,7 +309,7 @@
if(id_tag)
for(var/obj/machinery/doorButtons/D in GLOB.machines)
D.removeMe(src)
- qdel(note)
+ QDEL_NULL(note)
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.remove_from_hud(src)
return ..()
@@ -765,8 +765,8 @@
/obj/machinery/door/airlock/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!(issilicon(user) || IsAdminGhost(user)))
- if(src.isElectrified())
- if(src.shock(user, 100))
+ if(isElectrified())
+ if(shock(user, 100))
return
if(ishuman(user) && prob(40) && src.density)
@@ -790,15 +790,15 @@
return ..()
/obj/machinery/door/airlock/proc/electrified_loop()
- while (secondsElectrified > 0)
+ while (secondsElectrified > NOT_ELECTRIFIED)
sleep(10)
if(QDELETED(src))
return
- secondsElectrified -= 1
+ secondsElectrified--
updateDialog()
// This is to protect against changing to permanent, mid loop.
- if(secondsElectrified==0)
+ if(secondsElectrified == NOT_ELECTRIFIED)
set_electrified(NOT_ELECTRIFIED)
else
set_electrified(ELECTRIFIED_PERMANENT)
@@ -825,8 +825,8 @@
/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params)
if(!issilicon(user) && !IsAdminGhost(user))
- if(src.isElectrified())
- if(src.shock(user, 75))
+ if(isElectrified())
+ if(shock(user, 75))
return
add_fingerprint(user)
@@ -839,7 +839,7 @@
to_chat(user, "You need at least 2 metal sheets to reinforce [src]. ")
return
to_chat(user, "You start reinforcing [src]. ")
- if(do_after(user, 20, 1, target = src))
+ if(do_after(user, 20, TRUE, target = src))
if(!panel_open || !S.use(2))
return
user.visible_message("[user] reinforces \the [src] with metal. ",
@@ -1070,7 +1070,7 @@
INVOKE_ASYNC(src, (density ? .proc/open : .proc/close), 2)
if(istype(I, /obj/item/crowbar/power))
- if(isElectrified())
+ if(hasPower() && isElectrified())
shock(user,100)//it's like sticking a forck in a power socket
return
@@ -1090,12 +1090,11 @@
time_to_open = 50
playsound(src, 'sound/machines/airlock_alien_prying.ogg',100,1) //is it aliens or just the CE being a dick?
prying_so_hard = TRUE
- var/result = do_after(user, time_to_open,target = src)
- prying_so_hard = FALSE
- if(result)
+ if(do_after(user, time_to_open,target = src))
open(2)
if(density && !open(2))
to_chat(user, "Despite your attempts, [src] refuses to open. ")
+ prying_so_hard = FALSE
/obj/machinery/door/airlock/open(forced=0)
if( operating || welded || locked )
@@ -1359,12 +1358,25 @@
wires.cut_all()
update_icon()
-/obj/machinery/door/airlock/proc/set_electrified(seconds)
+/obj/machinery/door/airlock/proc/set_electrified(seconds, mob/user)
secondsElectrified = seconds
diag_hud_set_electrified()
- if(secondsElectrified > 0)
+ if(secondsElectrified > NOT_ELECTRIFIED)
INVOKE_ASYNC(src, .proc/electrified_loop)
+ if(user)
+ var/message
+ switch(secondsElectrified)
+ if(ELECTRIFIED_PERMANENT)
+ message = "permanently shocked"
+ if(NOT_ELECTRIFIED)
+ message = "unshocked"
+ else
+ message = "temp shocked for [secondsElectrified] seconds"
+ LAZYADD(shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(user)] - ([uppertext(message)])"))
+ log_combat(user, src, message)
+ //add_hiddenprint(user)
+
/obj/machinery/door/airlock/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
if(obj_integrity < (0.75 * max_integrity))
@@ -1438,11 +1450,10 @@
else if(istype(note, /obj/item/photo))
return "photo"
-/obj/machinery/door/airlock/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/door/airlock/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AiAirlock", name, 500, 390, master_ui, state)
+ ui = new(user, src, "AiAirlock", name)
ui.open()
return TRUE
@@ -1450,13 +1461,13 @@
var/list/data = list()
var/list/power = list()
- power["main"] = src.secondsMainPowerLost ? 0 : 2 // boolean
- power["main_timeleft"] = src.secondsMainPowerLost
- power["backup"] = src.secondsBackupPowerLost ? 0 : 2 // boolean
- power["backup_timeleft"] = src.secondsBackupPowerLost
+ power["main"] = secondsMainPowerLost ? 0 : 2 // boolean
+ power["main_timeleft"] = secondsMainPowerLost
+ power["backup"] = secondsBackupPowerLost ? 0 : 2 // boolean
+ power["backup_timeleft"] = secondsBackupPowerLost
data["power"] = power
- data["shock"] = secondsElectrified == 0 ? 2 : 0
+ data["shock"] = secondsElectrified == NOT_ELECTRIFIED ? 2 : 0
data["shock_timeleft"] = secondsElectrified
data["id_scanner"] = !aiDisabledIdScanner
data["emergency"] = emergency // access
@@ -1493,14 +1504,14 @@
loseMainPower()
update_icon()
else
- to_chat(usr, "Main power is already offline.")
+ to_chat(usr, "Main power is already offline. ")
. = TRUE
if("disrupt-backup")
if(!secondsBackupPowerLost)
loseBackupPower()
update_icon()
else
- to_chat(usr, "Backup power is already offline.")
+ to_chat(usr, "Backup power is already offline. ")
. = TRUE
if("shock-restore")
shock_restore(usr)
@@ -1529,7 +1540,6 @@
. = TRUE
if("speed-toggle")
normalspeed = !normalspeed
-
. = TRUE
if("open-close")
user_toggle_open(usr)
diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm
index a0ce3954c3..ef2be8d744 100644
--- a/code/game/machinery/doors/airlock_electronics.dm
+++ b/code/game/machinery/doors/airlock_electronics.dm
@@ -15,11 +15,13 @@
. = ..()
. += "Has a neat selection menu for modifying airlock access levels. "
-/obj/item/electronics/airlock/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/electronics/airlock/ui_state(mob/user)
+ return GLOB.hands_state
+
+/obj/item/electronics/airlock/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AirlockElectronics", name, 420, 485, master_ui, state)
+ ui = new(user, src, "AirlockElectronics", name)
ui.open()
/obj/item/electronics/airlock/ui_static_data(mob/user)
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index 5a1ed7dd19..b2e0050819 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -140,11 +140,10 @@
. = new_time == timer_duration //return 1 on no change
timer_duration = new_time
-/obj/machinery/door_timer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/door_timer/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "BrigTimer", name, 300, 138, master_ui, state)
+ ui = new(user, src, "BrigTimer", name)
ui.open()
//icon update function
@@ -235,7 +234,7 @@
preset_time = PRESET_LONG
. = set_timer(preset_time)
if(timing)
- activation_time = REALTIMEOFDAY
+ activation_time = world.time
else
. = FALSE
diff --git a/code/game/machinery/gulag_item_reclaimer.dm b/code/game/machinery/gulag_item_reclaimer.dm
index 45484caa77..93c6e66867 100644
--- a/code/game/machinery/gulag_item_reclaimer.dm
+++ b/code/game/machinery/gulag_item_reclaimer.dm
@@ -20,18 +20,15 @@
return ..()
/obj/machinery/gulag_item_reclaimer/emag_act(mob/user)
- . = ..()
if(obj_flags & EMAGGED) // emagging lets anyone reclaim all the items
return
req_access = list()
obj_flags |= EMAGGED
- return TRUE
-/obj/machinery/gulag_item_reclaimer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/gulag_item_reclaimer/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "GulagItemReclaimer", name, 300, 400, master_ui, state)
+ ui = new(user, src, "GulagItemReclaimer", name)
ui.open()
/obj/machinery/gulag_item_reclaimer/ui_data(mob/user)
@@ -60,20 +57,22 @@
mobs += list(mob_info)
data["mobs"] = mobs
-
-
data["can_reclaim"] = can_reclaim
return data
-/obj/machinery/gulag_item_reclaimer/ui_act(action, list/params)
+/obj/machinery/gulag_item_reclaimer/ui_act(action, params)
+ if(..())
+ return
+
switch(action)
if("release_items")
- var/mob/M = locate(params["mobref"])
- if(M == usr || allowed(usr))
- drop_items(M)
- else
- to_chat(usr, "Access denied.")
+ var/mob/living/carbon/human/H = locate(params["mobref"]) in stored_items
+ if(H != usr && !allowed(usr))
+ to_chat(usr, "Access denied. ")
+ return
+ drop_items(H)
+ . = TRUE
/obj/machinery/gulag_item_reclaimer/proc/drop_items(mob/user)
if(!stored_items[user])
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 25f4248d6d..4a576c5a6e 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -25,7 +25,6 @@ Possible to do for anyone motivated enough:
*/
GLOBAL_LIST_EMPTY(network_holopads)
-
#define HOLOPAD_PASSIVE_POWER_USAGE 1
#define HOLOGRAM_POWER_USAGE 2
@@ -34,34 +33,64 @@ GLOBAL_LIST_EMPTY(network_holopads)
desc = "It's a floor-mounted device for projecting holographic images."
icon_state = "holopad0"
layer = LOW_OBJ_LAYER
- plane = ABOVE_WALL_PLANE
+ plane = FLOOR_PLANE
flags_1 = HEAR_1
+ req_access = list(ACCESS_KEYCARD_AUTH) //Used to allow for forced connecting to other (not secure) holopads. Anyone can make a call, though.
use_power = IDLE_POWER_USE
idle_power_usage = 5
active_power_usage = 100
max_integrity = 300
armor = list("melee" = 50, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0)
circuit = /obj/item/circuitboard/machine/holopad
- var/list/masters //List of living mobs that use the holopad
- var/list/holorays //Holoray-mob link.
- var/last_request = 0 //to prevent request spam. ~Carn
- var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating.
- var/temp = ""
- var/list/holo_calls //array of /datum/holocalls
- var/datum/holocall/outgoing_call //do not modify the datums only check and call the public procs
- var/obj/item/disk/holodisk/disk //Record disk
- var/replay_mode = FALSE //currently replaying a recording
- var/loop_mode = FALSE //currently looping a recording
- var/record_mode = FALSE //currently recording
- var/record_start = 0 //recording start time
- var/record_user //user that inititiated the recording
- var/obj/effect/overlay/holo_pad_hologram/replay_holo //replay hologram
- var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging
+ /// List of living mobs that use the holopad
+ var/list/masters
+ /// Holoray-mob link
+ var/list/holorays
+ /// To prevent request spam. ~Carn
+ var/last_request = 0
+ /// Change to change how far the AI can move away from the holopad before deactivating
+ var/holo_range = 5
+ /// Array of /datum/holocalls
+ var/list/holo_calls
+ /// Currently outgoing holocall, do not modify the datums only check and call the public procs
+ var/datum/holocall/outgoing_call
+ /// Record disk
+ var/obj/item/disk/holodisk/disk
+ /// Currently replaying a recording
+ var/replay_mode = FALSE
+ /// Currently looping a recording
+ var/loop_mode = FALSE
+ /// Currently recording
+ var/record_mode = FALSE
+ /// Recording start time
+ var/record_start = 0
+ /// User that inititiated the recording
+ var/record_user
+ /// Replay hologram
+ var/obj/effect/overlay/holo_pad_hologram/replay_holo
+ /// Calls will be automatically answered after a couple rings, here for debugging
+ var/static/force_answer_call = FALSE
+ // var/static/list/holopads = list()
var/obj/effect/overlay/holoray/ray
var/ringing = FALSE
var/offset = FALSE
var/on_network = TRUE
+ /// For pads in secure areas; do not allow forced connecting
+ var/secure = FALSE
+ /// If we are currently calling another holopad
+ var/calling = FALSE
+/*
+/obj/machinery/holopad/secure
+ name = "secure holopad"
+ desc = "It's a floor-mounted device for projecting holographic images. This one will refuse to auto-connect incoming calls."
+ secure = TRUE
+/obj/machinery/holopad/secure/Initialize()
+ . = ..()
+ var/obj/item/circuitboard/machine/holopad/board = circuit
+ board.secure = TRUE
+ board.build_path = /obj/machinery/holopad/secure
+*/
/obj/machinery/holopad/tutorial
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
flags_1 = NODECONSTRUCT_1
@@ -121,10 +150,8 @@ GLOBAL_LIST_EMPTY(network_holopads)
return ..()
/obj/machinery/holopad/power_change()
- if (powered())
- stat &= ~NOPOWER
- else
- stat |= NOPOWER
+ . = ..()
+ if (!powered())
if(replay_mode)
replay_stop()
if(record_mode)
@@ -163,171 +190,150 @@ GLOBAL_LIST_EMPTY(network_holopads)
if(istype(P,/obj/item/disk/holodisk))
if(disk)
- to_chat(user,"There's already a disk inside [src] ")
+ to_chat(user,"There's already a disk inside [src]! ")
return
if (!user.transferItemToLoc(P,src))
return
- to_chat(user,"You insert [P] into [src] ")
+ to_chat(user,"You insert [P] into [src]. ")
disk = P
- updateDialog()
return
return ..()
+/obj/machinery/holopad/ui_status(mob/user)
+ if(!is_operational())
+ return UI_CLOSE
+ if(outgoing_call && !calling)
+ return UI_CLOSE
+ return ..()
-/obj/machinery/holopad/ui_interact(mob/living/carbon/human/user) //Carn: Hologram requests.
+/obj/machinery/holopad/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Holopad", name)
+ ui.open()
+
+/obj/machinery/holopad/ui_data(mob/user)
+ var/list/data = list()
+ data["calling"] = calling
+ data["on_network"] = on_network
+ data["on_cooldown"] = last_request + 200 < world.time ? FALSE : TRUE
+ data["allowed"] = allowed(user)
+ data["disk"] = disk ? TRUE : FALSE
+ data["disk_record"] = disk?.record ? TRUE : FALSE
+ data["replay_mode"] = replay_mode
+ data["loop_mode"] = loop_mode
+ data["record_mode"] = record_mode
+ data["holo_calls"] = list()
+ for(var/I in holo_calls)
+ var/datum/holocall/HC = I
+ var/list/call_data = list(
+ caller = HC.user,
+ connected = HC.connected_holopad == src ? TRUE : FALSE,
+ ref = REF(HC)
+ )
+ data["holo_calls"] += list(call_data)
+ return data
+
+/obj/machinery/holopad/ui_act(action, list/params)
. = ..()
- if(!istype(user))
+ if(.)
return
- if(outgoing_call || user.incapacitated() || !is_operational())
- return
-
- user.set_machine(src)
- var/dat
- if(temp)
- dat = temp
- else
- if(on_network)
- dat += "Request an AI's presence "
- dat += "Call another holopad "
- if(disk)
- if(disk.record)
- //Replay
- dat += "Replay disk recording "
- dat += "Loop disk recording "
- //Clear
- dat += "Clear disk recording "
+ switch(action)
+ if("AIrequest")
+ if(last_request + 200 < world.time)
+ last_request = world.time
+ to_chat(usr, "You requested an AI's presence. ")
+ var/area/area = get_area(src)
+ for(var/mob/living/silicon/ai/AI in GLOB.silicon_mobs)
+ if(!AI.client)
+ continue
+ to_chat(AI, "Your presence is requested at \the [area] . ")
+ return TRUE
else
- //Record
- dat += "Start new recording "
- //Eject
- dat += "Eject disk "
+ to_chat(usr, "A request for AI presence was already sent recently. ")
+ return
+ if("holocall")
+ if(outgoing_call)
+ return
+ if(usr.loc == loc)
+ var/list/callnames = list()
+ for(var/I in GLOB.network_holopads)
+ var/area/A = get_area(I)
+ if(A)
+ LAZYADD(callnames[A], I)
+ callnames -= get_area(src)
+ var/result = input(usr, "Choose an area to call", "Holocall") as null|anything in sortNames(callnames)
+ if(QDELETED(usr) || !result || outgoing_call)
+ return
+ if(usr.loc == loc)
+ var/input = text2num(params["headcall"])
+ var/headcall = input == 1 ? TRUE : FALSE
+ new /datum/holocall(usr, src, callnames[result], headcall)
+ calling = TRUE
+ return TRUE
+ else
+ to_chat(usr, "You must stand on the holopad to make a call! ")
+ if("connectcall")
+ var/datum/holocall/call_to_connect = locate(params["holopad"]) in holo_calls
+ if(!QDELETED(call_to_connect))
+ call_to_connect.Answer(src)
+ return TRUE
+ if("disconnectcall")
+ var/datum/holocall/call_to_disconnect = locate(params["holopad"]) in holo_calls
+ if(!QDELETED(call_to_disconnect))
+ call_to_disconnect.Disconnect(src)
+ return TRUE
+ if("disk_eject")
+ if(disk && !replay_mode)
+ disk.forceMove(drop_location())
+ disk = null
+ return TRUE
+ if("replay_mode")
+ if(replay_mode)
+ replay_stop()
+ return TRUE
+ else
+ replay_start()
+ return TRUE
+ if("loop_mode")
+ loop_mode = !loop_mode
+ return TRUE
+ if("record_mode")
+ if(record_mode)
+ record_stop()
+ return TRUE
+ else
+ record_start(usr)
+ return TRUE
+ if("record_clear")
+ record_clear()
+ return TRUE
+ if("offset")
+ offset++
+ if(offset > 4)
+ offset = FALSE
+ var/turf/new_turf
+ if(!offset)
+ new_turf = get_turf(src)
+ else
+ new_turf = get_step(src, GLOB.cardinals[offset])
+ replay_holo.forceMove(new_turf)
+ return TRUE
+ if("hang_up")
+ if(outgoing_call)
+ outgoing_call.Disconnect(src)
+ return TRUE
- if(LAZYLEN(holo_calls))
- dat += "===================================================== "
-
- if(on_network)
- var/one_answered_call = FALSE
- var/one_unanswered_call = FALSE
- for(var/I in holo_calls)
- var/datum/holocall/HC = I
- if(HC.connected_holopad != src)
- dat += "Answer call from [get_area(HC.calling_holopad)] "
- one_unanswered_call = TRUE
- else
- one_answered_call = TRUE
-
- if(one_answered_call && one_unanswered_call)
- dat += "===================================================== "
- //we loop twice for formatting
- for(var/I in holo_calls)
- var/datum/holocall/HC = I
- if(HC.connected_holopad == src)
- dat += "Disconnect call from [HC.user] "
-
-
- var/datum/browser/popup = new(user, "holopad", name, 300, 175)
- popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
- popup.open()
-
-//Stop ringing the AI!!
+/**
+ * hangup_all_calls: Disconnects all current holocalls from the holopad
+ */
/obj/machinery/holopad/proc/hangup_all_calls()
for(var/I in holo_calls)
var/datum/holocall/HC = I
HC.Disconnect(src)
-/obj/machinery/holopad/Topic(href, href_list)
- if(..() || isAI(usr))
- return
- add_fingerprint(usr)
- if(!is_operational())
- return
- if (href_list["AIrequest"])
- if(last_request + 200 < world.time)
- last_request = world.time
- temp = "You requested an AI's presence. "
- temp += "Main Menu "
- var/area/area = get_area(src)
- for(var/mob/living/silicon/ai/AI in GLOB.silicon_mobs)
- if(!AI.client)
- continue
- to_chat(AI, "Your presence is requested at \the [area] . ")
- else
- temp = "A request for AI presence was already sent recently. "
- temp += "Main Menu "
-
- else if(href_list["Holocall"])
- if(outgoing_call)
- return
-
- temp = "You must stand on the holopad to make a call! "
- temp += "Main Menu "
- if(usr.loc == loc)
- var/list/callnames = list()
- for(var/I in GLOB.network_holopads)
- var/area/A = get_area(I)
- if(A)
- LAZYADD(callnames[A], I)
- callnames -= get_area(src)
-
- var/result = input(usr, "Choose an area to call", "Holocall") as null|anything in callnames
- if(QDELETED(usr) || !result || outgoing_call)
- return
-
- if(usr.loc == loc)
- temp = "Dialing... "
- temp += "Main Menu "
- new /datum/holocall(usr, src, callnames[result])
-
- else if(href_list["connectcall"])
- var/datum/holocall/call_to_connect = locate(href_list["connectcall"])
- if(!QDELETED(call_to_connect))
- call_to_connect.Answer(src)
- temp = ""
-
- else if(href_list["disconnectcall"])
- var/datum/holocall/call_to_disconnect = locate(href_list["disconnectcall"])
- if(!QDELETED(call_to_disconnect))
- call_to_disconnect.Disconnect(src)
- temp = ""
-
- else if(href_list["mainmenu"])
- temp = ""
- if(outgoing_call)
- outgoing_call.Disconnect()
-
- else if(href_list["disk_eject"])
- if(disk && !replay_mode)
- disk.forceMove(drop_location())
- disk = null
-
- else if(href_list["replay_stop"])
- replay_stop()
- else if(href_list["replay_start"])
- replay_start()
- else if(href_list["loop_start"])
- loop_mode = TRUE
- replay_start()
- else if(href_list["record_start"])
- record_start(usr)
- else if(href_list["record_stop"])
- record_stop()
- else if(href_list["record_clear"])
- record_clear()
- else if(href_list["offset"])
- offset++
- if (offset > 4)
- offset = FALSE
- var/turf/new_turf
- if (!offset)
- new_turf = get_turf(src)
- else
- new_turf = get_step(src, GLOB.cardinals[offset])
- replay_holo.forceMove(new_turf)
- updateDialog()
-
//do not allow AIs to answer calls or people will use it to meta the AI sattelite
/obj/machinery/holopad/attack_ai(mob/living/silicon/ai/user)
if (!istype(user))
@@ -366,6 +372,9 @@ GLOBAL_LIST_EMPTY(network_holopads)
if(force_answer_call && world.time > (HC.call_start_time + (HOLOPAD_MAX_DIAL_TIME / 2)))
HC.Answer(src)
break
+ if(!secure) //HC.head_call &&
+ HC.Answer(src)
+ break
if(outgoing_call)
HC.Disconnect(src)//can't answer calls while calling
else
@@ -412,17 +421,17 @@ GLOBAL_LIST_EMPTY(network_holopads)
/*This is the proc for special two-way communication between AI and holopad/people talking near holopad.
For the other part of the code, check silicon say.dm. Particularly robot talk.*/
-/obj/machinery/holopad/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
+/obj/machinery/holopad/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list())
. = ..()
if(speaker && LAZYLEN(masters) && !radio_freq)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios.
for(var/mob/living/silicon/ai/master in masters)
if(masters[master] && speaker != master)
- master.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
+ master.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)
for(var/I in holo_calls)
var/datum/holocall/HC = I
if(HC.connected_holopad == src && speaker != HC.hologram)
- HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mode, source)
+ HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)
if(outgoing_call && speaker == outgoing_call.user)
outgoing_call.hologram.say(raw_message)
@@ -449,7 +458,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
else
icon_state = "holopad0"
-/obj/machinery/holopad/proc/set_holo(mob/living/user, var/obj/effect/overlay/holo_pad_hologram/h)
+/obj/machinery/holopad/proc/set_holo(mob/living/user, obj/effect/overlay/holo_pad_hologram/h)
LAZYSET(masters, user, h)
LAZYSET(holorays, user, new /obj/effect/overlay/holoray(loc))
var/mob/living/silicon/ai/AI = user
@@ -504,7 +513,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
else
return FALSE
-/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf, direction)
+/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf)
if(LAZYLEN(masters) && masters[user])
var/obj/effect/overlay/holo_pad_hologram/holo = masters[user]
var/transfered = FALSE
@@ -516,8 +525,6 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
transfered = TRUE
//All is good.
holo.forceMove(new_turf)
- if(direction)
- holo.setDir(direction)
if(!transfered)
update_holoray(user,new_turf)
return TRUE
@@ -568,22 +575,15 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
if(!replay_mode)
replay_mode = TRUE
replay_holo = setup_replay_holo(disk.record)
- temp = "Replaying... "
- temp += "Change offset "
- temp += "End replay "
SetLightsAndPower()
replay_entry(1)
- return
/obj/machinery/holopad/proc/replay_stop()
if(replay_mode)
replay_mode = FALSE
- loop_mode = FALSE
offset = FALSE
- temp = null
QDEL_NULL(replay_holo)
SetLightsAndPower()
- updateDialog()
/obj/machinery/holopad/proc/record_start(mob/living/user)
if(!user || !disk || disk.record)
@@ -593,8 +593,6 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
record_start = world.time
record_user = user
disk.record.set_caller_image(user)
- temp = "Recording... "
- temp += "End recording. "
/obj/machinery/holopad/proc/record_message(mob/living/speaker,message,language)
if(!record_mode)
@@ -641,7 +639,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
if(replay_holo)
replay_holo.say(message)
if(HOLORECORD_SOUND)
- playsound(src,entry[2],50,1)
+ playsound(src,entry[2],50,TRUE)
if(HOLORECORD_DELAY)
addtimer(CALLBACK(src,.proc/replay_entry,entry_number+1),entry[2])
return
@@ -660,14 +658,11 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
/obj/machinery/holopad/proc/record_stop()
if(record_mode)
record_mode = FALSE
- temp = null
record_user = null
- updateDialog()
/obj/machinery/holopad/proc/record_clear()
if(disk && disk.record)
QDEL_NULL(disk.record)
- updateDialog()
/obj/effect/overlay/holo_pad_hologram
initial_language_holder = /datum/language_holder/universal
diff --git a/code/game/machinery/hypnochair.dm b/code/game/machinery/hypnochair.dm
index 16b464ced6..41f420204e 100644
--- a/code/game/machinery/hypnochair.dm
+++ b/code/game/machinery/hypnochair.dm
@@ -6,14 +6,12 @@
circuit = /obj/item/circuitboard/machine/hypnochair
density = TRUE
opacity = 0
- ui_x = 375
- ui_y = 480
+
var/mob/living/carbon/victim = null ///Keeps track of the victim to apply effects if it teleports away
var/interrogating = FALSE ///Is the device currently interrogating someone?
var/start_time = 0 ///Time when the interrogation was started, to calculate effect in case of interruption
var/trigger_phrase = "" ///Trigger phrase to implant
var/timerid = 0 ///Timer ID for interrogations
-
var/message_cooldown = 0 ///Cooldown for breakout message
/obj/machinery/hypnochair/Initialize()
@@ -25,24 +23,24 @@
if(!occupant && default_deconstruction_screwdriver(user, icon_state, icon_state, I))
update_icon()
return
-
if(default_pry_open(I))
return
-
if(default_deconstruction_crowbar(I))
return
-
return ..()
-/obj/machinery/hypnochair/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/hypnochair/ui_state(mob/user)
+ return GLOB.notcontained_state
+
+/obj/machinery/hypnochair/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "HypnoChair", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "HypnoChair", name)
ui.open()
/obj/machinery/hypnochair/ui_data()
var/list/data = list()
- data["occupied"] = occupant ? TRUE : FALSE
+ data["occupied"] = occupant ? 1 : 0
data["open"] = state_open
data["interrogating"] = interrogating
@@ -201,3 +199,4 @@
if(!(L.mobility_flags & MOBILITY_STAND))
return
close_machine(target)
+
diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm
index 63ef40e045..e3df79e291 100644
--- a/code/game/machinery/launch_pad.dm
+++ b/code/game/machinery/launch_pad.dm
@@ -282,12 +282,14 @@
ui_interact(user)
to_chat(user, "[src] projects a display onto your retina. ")
-/obj/item/launchpad_remote/ui_interact(mob/user, ui_key = "launchpad_remote", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "LaunchpadRemote", "Briefcase Launchpad Remote", 300, 240, master_ui, state) //width, height
- ui.open()
+/obj/item/launchpad_remote/ui_state(mob/user)
+ return GLOB.inventory_state
+/obj/item/launchpad_remote/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "LaunchpadRemote")
+ ui.open()
ui.set_autoupdate(TRUE)
/obj/item/launchpad_remote/ui_data(mob/user)
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index f69a722473..54d320e93b 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -4,6 +4,15 @@
#define POPUP_ANIM_TIME 5
#define POPDOWN_ANIM_TIME 5 //Be sure to change the icon animation at the same time or it'll look bad
+#define TURRET_FLAG_SHOOT_ALL_REACT (1<<0) // The turret gets pissed off and shoots at people nearby (unless they have sec access!)
+#define TURRET_FLAG_AUTH_WEAPONS (1<<1) // Checks if it can shoot people that have a weapon they aren't authorized to have
+#define TURRET_FLAG_SHOOT_CRIMINALS (1<<2) // Checks if it can shoot people that are wanted
+#define TURRET_FLAG_SHOOT_ALL (1<<3) // The turret gets pissed off and shoots at people nearby (unless they have sec access!)
+#define TURRET_FLAG_SHOOT_ANOMALOUS (1<<4) // Checks if it can shoot at unidentified lifeforms (ie xenos)
+#define TURRET_FLAG_SHOOT_UNSHIELDED (1<<5) // Checks if it can shoot people that aren't mindshielded and who arent heads
+#define TURRET_FLAG_SHOOT_BORGS (1<<6) // checks if it can shoot cyborgs
+#define TURRET_FLAG_SHOOT_HEADS (1<<7) // checks if it can shoot at heads of staff
+
/obj/machinery/porta_turret
name = "turret"
icon = 'icons/obj/turrets.dmi'
@@ -15,70 +24,79 @@
use_power = IDLE_POWER_USE //this turret uses and requires power
idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power
active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power
- req_access = list(ACCESS_SEC_DOORS)
+ req_access = list(ACCESS_SECURITY) /// Only people with Security access
power_channel = EQUIP //drains power from the EQUIPMENT channel
- speed_process = TRUE
-
- var/base_icon_state = "standard"
- var/scan_range = 7
- var/atom/base = null //for turrets inside other objects
-
- var/raised = 0 //if the turret cover is "open" and the turret is raised
- var/raising= 0 //if the turret is currently opening or closing its cover
-
max_integrity = 160 //the turret's health
integrity_failure = 0.5
armor = list("melee" = 50, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90)
-
- var/locked = TRUE //if the turret's behaviour control access is locked
- var/controllock = FALSE //if the turret responds to control panels
-
- var/installation = /obj/item/gun/energy/e_gun/turret //the type of weapon installed by default
+ /// Base turret icon state
+ var/base_icon_state = "standard"
+ /// Scan range of the turret for locating targets
+ var/scan_range = 7
+ /// For turrets inside other objects
+ var/atom/base = null
+ /// If the turret cover is "open" and the turret is raised
+ var/raised = FALSE
+ /// If the turret is currently opening or closing its cover
+ var/raising = FALSE
+ /// If the turret's behaviour control access is locked
+ var/locked = TRUE
+ /// If the turret responds to control panels
+ var/controllock = FALSE
+ /// The type of weapon installed by default
+ var/installation = /obj/item/gun/energy/e_gun/turret
+ /// What stored gun is in the turret
var/obj/item/gun/stored_gun = null
- var/gun_charge = 0 //the charge of the gun when retrieved from wreckage
-
+ /// The charge of the gun when retrieved from wreckage
+ var/gun_charge = 0
+ /// In which mode is turret in, stun or lethal
var/mode = TURRET_STUN
-
- var/stun_projectile = null //stun mode projectile type
+ /// Stun mode projectile type
+ var/stun_projectile = null
+ /// Sound of stun projectile
var/stun_projectile_sound
- var/nonlethal_projectile //projectile to use in stun mode when the target is resting, if any
+ /// Projectile to use in stun mode when the target is resting, if any
+ var/nonlethal_projectile
+ /// Sound of stun projectile wen the target is resting, optional
var/nonlethal_projectile_sound
- var/lethal_projectile = null //lethal mode projectile type
+ /// Lethal mode projectile type
+ var/lethal_projectile = null
+ /// Sound of lethal projectile
var/lethal_projectile_sound
-
- var/reqpower = 500 //power needed per shot
- var/always_up = 0 //Will stay active
- var/has_cover = 1 //Hides the cover
-
- var/obj/machinery/porta_turret_cover/cover = null //the cover that is covering this turret
-
- var/last_fired = 0 //world.time the turret last fired
- var/shot_delay = 15 //ticks until next shot (1.5 ?)
- var/shot_stagger = 0 // sleep() shots to stagger attacks
-
- var/check_records = 1 //checks if it can use the security records
- var/criminals = 1 //checks if it can shoot people on arrest
- var/auth_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have
- var/stun_all = 0 //if this is active, the turret shoots everything that isn't security or head of staff
- var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos)
- var/shoot_unloyal = 0 //checks if it can shoot people that aren't loyalty implantd
-
- var/attacked = 0 //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!)
-
- var/on = TRUE //determines if the turret is on
-
- var/list/faction = list("turret") // Same faction mobs will never be shot at, no matter the other settings
-
- var/datum/effect_system/spark_spread/spark_system //the spark system, used for generating... sparks?
-
+ /// Power needed per shot
+ var/reqpower = 500
+ /// Will stay active
+ var/always_up = FALSE
+ /// Hides the cover
+ var/has_cover = TRUE
+ /// The cover that is covering this turret
+ var/obj/machinery/porta_turret_cover/cover = null
+ /// World.time the turret last fired
+ var/last_fired = 0
+ /// Ticks until next shot (1.5 ?)
+ var/shot_delay = 15
+ /// Turret flags about who is turret allowed to shoot
+ var/turret_flags = TURRET_FLAG_SHOOT_CRIMINALS | TURRET_FLAG_SHOOT_ANOMALOUS
+ /// Determines if the turret is on
+ var/on = TRUE
+ /// Same faction mobs will never be shot at, no matter the other settings
+ var/list/faction = list("turret")
+ /// The spark system, used for generating... sparks?
+ var/datum/effect_system/spark_spread/spark_system
+ /// Linked turret control panel of the turret
var/obj/machinery/turretid/cp = null
-
- var/wall_turret_direction //The turret will try to shoot from a turf in that direction when in a wall
-
- var/manual_control = FALSE //
+ /// The turret will try to shoot from a turf in that direction when in a wall
+ var/wall_turret_direction
+ /// If the turret is manually controlled
+ var/manual_control = FALSE
+ /// Action button holder for quitting manual control
var/datum/action/turret_quit/quit_action
+ /// Action button holder for switching between turret modes when manually controlling
var/datum/action/turret_toggle/toggle_action
+ /// Mob that is remotely controlling the turret
var/mob/remote_controller
+ /// MISSING:
+ var/shot_stagger = 0
/obj/machinery/porta_turret/Initialize()
. = ..()
@@ -100,6 +118,27 @@
if(!has_cover)
INVOKE_ASYNC(src, .proc/popUp)
+/obj/machinery/porta_turret/proc/toggle_on(var/set_to)
+ var/current = on
+ if (!isnull(set_to))
+ on = set_to
+ else
+ on = !on
+ if (current != on)
+ check_should_process()
+ if (!on)
+ popDown()
+
+/obj/machinery/porta_turret/proc/check_should_process()
+ if (datum_flags & DF_ISPROCESSING)
+ if (!on || !anchored || (stat & BROKEN) || !powered())
+ //end_processing()
+ STOP_PROCESSING(SSmachines, src)
+ else
+ if (on && anchored && !(stat & BROKEN) && powered())
+ START_PROCESSING(SSmachines, src)
+ //begin_processing()
+
/obj/machinery/porta_turret/update_icon_state()
if(!anchored)
icon_state = "turretCover"
@@ -119,7 +158,6 @@
else
icon_state = "[base_icon_state]_unpowered"
-
/obj/machinery/porta_turret/proc/setup(obj/item/gun/turret_gun)
if(stored_gun)
qdel(stored_gun)
@@ -160,83 +198,88 @@
remove_control()
return ..()
-/obj/machinery/porta_turret/ui_interact(mob/user)
- . = ..()
- var/dat
- dat += "Status: [on ? "On" : "Off"] "
- dat += "Behaviour controls are [locked ? "locked" : "unlocked"] "
+/obj/machinery/porta_turret/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "PortableTurret", name)
+ ui.open()
- if(!locked)
- dat += "Check for Weapon Authorization: [auth_weapons ? "Yes" : "No"] "
- dat += "Check Security Records: [check_records ? "Yes" : "No"] "
- dat += "Neutralize Identified Criminals: [criminals ? "Yes" : "No"] "
- dat += "Neutralize All Non-Security and Non-Command Personnel: [stun_all ? "Yes" : "No"] "
- dat += "Neutralize All Unidentified Life Signs: [check_anomalies ? "Yes" : "No"] "
- dat += "Neutralize All Non-Loyalty Implanted Personnel: [shoot_unloyal ? "Yes" : "No"] "
+/obj/machinery/porta_turret/ui_data(mob/user)
+ var/list/data = list(
+ "locked" = locked,
+ "on" = on,
+ "check_weapons" = turret_flags & TURRET_FLAG_AUTH_WEAPONS,
+ "neutralize_criminals" = turret_flags & TURRET_FLAG_SHOOT_CRIMINALS,
+ "neutralize_all" = turret_flags & TURRET_FLAG_SHOOT_ALL,
+ "neutralize_unidentified" = turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS,
+ "neutralize_nonmindshielded" = turret_flags & TURRET_FLAG_SHOOT_UNSHIELDED,
+ "neutralize_cyborgs" = turret_flags & TURRET_FLAG_SHOOT_BORGS,
+ "ignore_heads" = turret_flags & TURRET_FLAG_SHOOT_HEADS,
+ "manual_control" = manual_control,
+ "silicon_user" = FALSE,
+ "allow_manual_control" = FALSE,
+ "lasertag_turret" = istype(src, /obj/machinery/porta_turret/lasertag),
+ )
if(issilicon(user))
+ data["silicon_user"] = TRUE
if(!manual_control)
var/mob/living/silicon/S = user
if(S.hack_software)
- dat += "Assume direct control : Manual Control "
- else
- dat += "Warning! Remote control protocol enabled. "
+ data["allow_manual_control"] = TRUE
+ return data
-
- var/datum/browser/popup = new(user, "autosec", "Automatic Portable Turret Installation", 300, 300)
- popup.set_content(dat)
- popup.open()
-
-/obj/machinery/porta_turret/Topic(href, href_list)
- if(..())
- return
- usr.set_machine(src)
- add_fingerprint(usr)
-
- if(href_list["power"] && !locked)
- if(anchored) //you can't turn a turret on/off if it's not anchored/secured
- on = !on //toggle on/off
- else
- to_chat(usr, "It has to be secured first! ")
- interact(usr)
+/obj/machinery/porta_turret/ui_act(action, list/params)
+ . = ..()
+ if(.)
return
- if(href_list["operation"])
- switch(href_list["operation"]) //toggles customizable behavioural protocols
- if("authweapon")
- auth_weapons = !auth_weapons
- if("checkrecords")
- check_records = !check_records
- if("shootcrooks")
- criminals = !criminals
- if("shootall")
- stun_all = !stun_all
- if("checkxenos")
- check_anomalies = !check_anomalies
- if("checkloyal")
- shoot_unloyal = !shoot_unloyal
- if("manual")
- if(issilicon(usr) && !manual_control)
- give_control(usr)
- interact(usr)
+ switch(action)
+ if("power")
+ if(anchored)
+ toggle_on()
+ return TRUE
+ else
+ to_chat(usr, "It has to be secured first! ")
+ if("authweapon")
+ turret_flags ^= TURRET_FLAG_AUTH_WEAPONS
+ return TRUE
+ if("shootcriminals")
+ turret_flags ^= TURRET_FLAG_SHOOT_CRIMINALS
+ return TRUE
+ if("shootall")
+ turret_flags ^= TURRET_FLAG_SHOOT_ALL
+ return TRUE
+ if("checkxenos")
+ turret_flags ^= TURRET_FLAG_SHOOT_ANOMALOUS
+ return TRUE
+ if("checkloyal")
+ turret_flags ^= TURRET_FLAG_SHOOT_UNSHIELDED
+ return TRUE
+ if("shootborgs")
+ turret_flags ^= TURRET_FLAG_SHOOT_BORGS
+ return TRUE
+ if("shootheads")
+ turret_flags ^= TURRET_FLAG_SHOOT_HEADS
+ return TRUE
+ if("manual")
+ if(!issilicon(usr))
+ return
+ give_control(usr)
+ return TRUE
+
+/obj/machinery/porta_turret/ui_host(mob/user)
+ if(has_cover && cover)
+ return cover
+ if(base)
+ return base
+ return src
/obj/machinery/porta_turret/power_change()
- if(!anchored)
+ . = ..()
+ if(!anchored || (stat & BROKEN) || !powered())
update_icon()
remove_control()
- return
- if(stat & BROKEN)
- update_icon()
- remove_control()
- else
- if( powered() )
- stat &= ~NOPOWER
- update_icon()
- else
- spawn(rand(0, 15))
- stat |= NOPOWER
- remove_control()
- update_icon()
-
+ check_should_process()
/obj/machinery/porta_turret/attackby(obj/item/I, mob/user, params)
if(stat & BROKEN)
@@ -284,8 +327,10 @@
locked = !locked
to_chat(user, "Controls are now [locked ? "locked" : "unlocked"]. ")
else
- to_chat(user, "Access denied. ")
+ to_chat(user, "Access denied. ")
else if(istype(I, /obj/item/multitool) && !locked)
+ if(!multitool_check_buffer(user, I))
+ return
var/obj/item/multitool/M = I
M.buffer = src
to_chat(user, "You add [src] to multitool buffer. ")
@@ -293,19 +338,17 @@
return ..()
/obj/machinery/porta_turret/emag_act(mob/user)
- . = ..()
if(obj_flags & EMAGGED)
return
to_chat(user, "You short out [src]'s threat assessment circuits. ")
- visible_message("[src] hums oddly...")
+ audible_message("[src] hums oddly... ")
obj_flags |= EMAGGED
controllock = TRUE
- on = FALSE //turns off the turret temporarily
+ toggle_on(FALSE) //turns off the turret temporarily
update_icon()
- sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit
- on = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here
- return TRUE
-
+ //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit
+ addtimer(CALLBACK(src, .proc/toggle_on, TRUE), 6 SECONDS)
+ //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here
/obj/machinery/porta_turret/emp_act(severity)
. = ..()
@@ -314,63 +357,41 @@
if(on)
//if the turret is on, the EMP no matter how severe disables the turret for a while
//and scrambles its settings, with a slight chance of having an emag effect
- check_records = pick(0, 1)
- criminals = pick(0, 1)
- auth_weapons = pick(0, 1)
- stun_all = pick(0, 0, 0, 0, 1) //stun_all is a pretty big deal, so it's least likely to get turned on
+ if(prob(50))
+ turret_flags |= TURRET_FLAG_SHOOT_CRIMINALS
+ if(prob(50))
+ turret_flags |= TURRET_FLAG_AUTH_WEAPONS
+ if(prob(20))
+ turret_flags |= TURRET_FLAG_SHOOT_ALL // Shooting everyone is a pretty big deal, so it's least likely to get turned on
- on = FALSE
+ toggle_on(FALSE)
remove_control()
- spawn(rand(60,600))
- if(!on)
- on = TRUE
+ addtimer(CALLBACK(src, .proc/toggle_on, TRUE), rand(60,600))
/obj/machinery/porta_turret/take_damage(damage, damage_type = BRUTE, damage_flag = 0, sound_effect = 1)
. = ..()
- if(.) //damage received
+ if(. && obj_integrity > 0) //damage received
if(prob(30))
spark_system.start()
- if(on && !attacked && !(obj_flags & EMAGGED))
- attacked = TRUE
+ if(on && !(turret_flags & TURRET_FLAG_SHOOT_ALL_REACT) && !(obj_flags & EMAGGED))
+ turret_flags |= TURRET_FLAG_SHOOT_ALL_REACT
addtimer(CALLBACK(src, .proc/reset_attacked), 60)
/obj/machinery/porta_turret/proc/reset_attacked()
- attacked = FALSE
+ turret_flags &= ~TURRET_FLAG_SHOOT_ALL_REACT
/obj/machinery/porta_turret/deconstruct(disassembled = TRUE)
qdel(src)
/obj/machinery/porta_turret/obj_break(damage_flag)
- if(!(flags_1 & NODECONSTRUCT_1) && !(stat & BROKEN))
- stat |= BROKEN //enables the BROKEN bit
+ . = ..()
+ if(.)
power_change()
invisibility = 0
spark_system.start() //creates some sparks because they look cool
qdel(cover) //deletes the cover - no need on keeping it there!
-//turret healing
-/obj/machinery/porta_turret/examine(mob/user)
- . = ..()
- if(obj_integrity < max_integrity)
- . += "Use a welder to fix it. "
-
-/obj/machinery/porta_turret/welder_act(mob/living/user, obj/item/I)
- . = TRUE
- if(obj_integrity < max_integrity)
- if(!I.tool_start_check(user, amount=0))
- return
- user.visible_message("[user] is welding the turret.", \
- "You begin repairing the turret... ", \
- "You hear welding. ")
- if(I.use_tool(src, user, 40, volume=50))
- obj_integrity = max_integrity
- user.visible_message("[user.name] has repaired [src].", \
- "You finish repairing the turret. ")
- else
- to_chat(user, "The turret doesn't need repairing. ")
-
-
/obj/machinery/porta_turret/process()
//the main machinery process
if(cover == null && anchored) //if it has no cover and is anchored
@@ -382,35 +403,43 @@
cover.parent_turret = src //assign the cover its parent_turret, which would be this (src)
if(!on || (stat & (NOPOWER|BROKEN)) || manual_control)
- return
+ return PROCESS_KILL
var/list/targets = list()
for(var/mob/A in view(scan_range, base))
if(A.invisibility > SEE_INVISIBLE_LIVING)
continue
- if(check_anomalies)//if it's set to check for simple animals
+ if(turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS)//if it's set to check for simple animals
if(isanimal(A))
var/mob/living/simple_animal/SA = A
if(SA.stat || in_faction(SA)) //don't target if dead or in faction
continue
targets += SA
- if(issilicon(A))
- var/mob/living/silicon/sillycone = A
- if(sillycone.stat || in_faction(sillycone))
+ continue
+
+ if(issilicon(A))
+ var/mob/living/silicon/sillycone = A
+
+ if(ispAI(A))
+ continue
+
+ if((turret_flags & TURRET_FLAG_SHOOT_BORGS) && sillycone.stat != DEAD && iscyborg(sillycone))
+ targets += sillycone
+ continue
+
+ if(sillycone.stat || in_faction(sillycone))
+ continue
+
+ if(iscyborg(sillycone))
+ var/mob/living/silicon/robot/sillyconerobot = A
+ if(LAZYLEN(faction) && (ROLE_SYNDICATE in faction) && sillyconerobot.emagged == TRUE)
continue
- if(iscyborg(sillycone))
- var/mob/living/silicon/robot/sillyconerobot = A
- if(LAZYLEN(faction) && (ROLE_SYNDICATE in faction) && sillyconerobot.emagged == TRUE)
- continue
-
- targets += sillycone
-
- if(iscarbon(A))
+ else if(iscarbon(A))
var/mob/living/carbon/C = A
- //If not emagged, only target non downed carbons
- if(mode != TURRET_LETHAL && (C.stat || C.handcuffed || (C.combat_flags & COMBAT_FLAG_HARD_STAMCRIT)))//CIT CHANGE - replaces check for lying with check for recoveringstam
+ //If not emagged, only target carbons that can use items
+ if(mode != TURRET_LETHAL && (C.stat || C.handcuffed || !(C.mobility_flags & MOBILITY_USE)))
continue
//If emagged, target all but dead carbons
@@ -419,12 +448,13 @@
//if the target is a human and not in our faction, analyze threat level
if(ishuman(C) && !in_faction(C))
+
if(assess_perp(C) >= 4)
targets += C
-
- else if(check_anomalies) //non humans who are not simple animals (xenos etc)
+ else if(turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS) //non humans who are not simple animals (xenos etc)
if(!in_faction(C))
targets += C
+
for(var/A in GLOB.mechas_list)
if((get_dist(A, base) < scan_range) && can_see(base, A, scan_range))
var/obj/mecha/Mech = A
@@ -432,6 +462,10 @@
if(assess_perp(Mech.occupant) >= 4)
targets += Mech
+ if((turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS) && GLOB.blobs.len && (mode == TURRET_LETHAL))
+ for(var/obj/structure/blob/B in view(scan_range, base))
+ targets += B
+
if(targets.len)
tryToShootAt(targets)
else if(!always_up)
@@ -488,36 +522,37 @@
if(obj_flags & EMAGGED)
return 10 //if emagged, always return 10.
- if((stun_all || attacked) && !allowed(perp))
+ if((turret_flags & (TURRET_FLAG_SHOOT_ALL | TURRET_FLAG_SHOOT_ALL_REACT)) && !allowed(perp))
//if the turret has been attacked or is angry, target all non-sec people
if(!allowed(perp))
return 10
- if(auth_weapons) //check for weapon authorization
+ if(turret_flags & TURRET_FLAG_AUTH_WEAPONS) //check for weapon authorization
if(isnull(perp.wear_id) || istype(perp.wear_id.GetID(), /obj/item/card/id/syndicate))
if(allowed(perp)) //if the perp has security access, return 0
return 0
-
if(perp.is_holding_item_of_type(/obj/item/gun) || perp.is_holding_item_of_type(/obj/item/melee/baton))
threatcount += 4
if(istype(perp.belt, /obj/item/gun) || istype(perp.belt, /obj/item/melee/baton))
threatcount += 2
- if(check_records) //if the turret can check the records, check if they are set to *Arrest* on records
+ if(turret_flags & TURRET_FLAG_SHOOT_CRIMINALS) //if the turret can check the records, check if they are set to *Arrest* on records
var/perpname = perp.get_face_name(perp.get_id_name())
var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security)
if(!R || (R.fields["criminal"] == "*Arrest*"))
threatcount += 4
- if(shoot_unloyal)
- if (!HAS_TRAIT(perp, TRAIT_MINDSHIELD))
- threatcount += 4
+ if((turret_flags & TURRET_FLAG_SHOOT_UNSHIELDED) && (!HAS_TRAIT(perp, TRAIT_MINDSHIELD)))
+ threatcount += 4
+
+ // If we aren't shooting heads then return a threatcount of 0
+ if (!(turret_flags & TURRET_FLAG_SHOOT_HEADS) && (perp.get_assignment() in GLOB.command_positions))
+ return 0
return threatcount
-
/obj/machinery/porta_turret/proc/in_faction(mob/target)
for(var/faction1 in faction)
if(faction1 in target.faction)
@@ -563,15 +598,14 @@
T = closer
break
- var/mob/living/carbon/C
- if(iscarbon(target))
- C = target
-
update_icon()
var/obj/item/projectile/A
//any emagged turrets drains 2x power and uses a different projectile?
if(mode == TURRET_STUN)
- if(nonlethal_projectile && C && C.resting)
+ var/mob/living/carbon/C = null
+ if(iscarbon(target))
+ C = target
+ if(nonlethal_projectile && C?.resting)
use_power(reqpower*0.5)
A = new nonlethal_projectile(T)
playsound(loc, nonlethal_projectile_sound, 75, 1)
@@ -582,7 +616,7 @@
else
use_power(reqpower * 2)
A = new lethal_projectile(T)
- playsound(loc, lethal_projectile_sound, 75, 1)
+ playsound(loc, lethal_projectile_sound, 75, TRUE)
//Shooting Code:
@@ -592,16 +626,15 @@
A.fire()
return A
-/obj/machinery/porta_turret/proc/setState(on, mode)
+/obj/machinery/porta_turret/proc/setState(on, mode, shoot_cyborgs)
if(controllock)
return
- src.on = on
- if(!on)
- popDown()
+
+ shoot_cyborgs ? (turret_flags |= TURRET_FLAG_SHOOT_BORGS) : (turret_flags &= ~TURRET_FLAG_SHOOT_BORGS)
+ toggle_on(on)
src.mode = mode
power_change()
-
/datum/action/turret_toggle
name = "Toggle Mode"
icon_icon = 'icons/mob/actions/actions_mecha.dmi'
@@ -685,7 +718,15 @@
/obj/machinery/porta_turret/syndicate/ComponentInitialize()
. = ..()
- AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
+ // AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
+ AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES) //this one or ^ one?
+
+
+/obj/machinery/porta_turret/syndicate/setup()
+ return
+
+/obj/machinery/porta_turret/syndicate/assess_perp(mob/living/carbon/human/perp)
+ return 10 //Syndicate turrets shoot everything not in their faction
/obj/machinery/porta_turret/syndicate/energy
icon_state = "standard_stun"
@@ -698,7 +739,6 @@
lethal_projectile_sound = 'sound/weapons/laser.ogg'
desc = "An energy blaster auto-turret."
-
/obj/machinery/porta_turret/syndicate/energy/heavy
icon_state = "standard_stun"
base_icon_state = "standard"
@@ -715,14 +755,13 @@
integrity_failure = 0.08
armor = list("melee" = 50, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90)
-
-/obj/machinery/porta_turret/syndicate/setup()
- return
-
-/obj/machinery/porta_turret/syndicate/assess_perp(mob/living/carbon/human/perp)
- return 10 //Syndicate turrets shoot everything not in their faction
+/obj/machinery/porta_turret/syndicate/energy/raven
+ stun_projectile = /obj/item/projectile/beam/laser
+ stun_projectile_sound = 'sound/weapons/laser.ogg'
+ faction = list("neutral","silicon","turret")
/obj/machinery/porta_turret/syndicate/pod
+ integrity_failure = 0.5
max_integrity = 40
stun_projectile = /obj/item/projectile/bullet/syndicate_turret
lethal_projectile = /obj/item/projectile/bullet/syndicate_turret
@@ -749,6 +788,7 @@
faction = list("silicon")
nonlethal_projectile = /obj/item/projectile/beam/disabler
nonlethal_projectile_sound = 'sound/weapons/taser2.ogg'
+ turret_flags = TURRET_FLAG_SHOOT_CRIMINALS | TURRET_FLAG_SHOOT_ANOMALOUS | TURRET_FLAG_SHOOT_HEADS
/obj/machinery/porta_turret/ai/assess_perp(mob/living/carbon/human/perp)
return 10 //AI turrets shoot at everything not in their faction
@@ -804,6 +844,7 @@
/obj/machinery/porta_turret/centcom_shuttle/weak
max_integrity = 120
+ integrity_failure = 0.5
name = "Old Laser Turret"
desc = "A turret built with substandard parts and run down further with age. Still capable of delivering lethal lasers to the odd space carp, but not much else."
stun_projectile = /obj/item/projectile/beam/weak/penetrator
@@ -817,7 +858,6 @@
stun_projectile_sound = 'sound/weapons/gunshot.ogg'
desc = "A ballistic machine gun auto-turret."
-
////////////////////////
//Turret Control Panel//
////////////////////////
@@ -828,14 +868,22 @@
icon = 'icons/obj/machines/turret_control.dmi'
icon_state = "control_standby"
density = FALSE
- var/enabled = 1
- var/lethal = 0
- var/locked = TRUE
- var/control_area = null //can be area name, path or nothing.
- var/ailock = 0 // AI cannot use this
req_access = list(ACCESS_AI_UPLOAD)
- var/list/obj/machinery/porta_turret/turrets = list()
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ /// Variable dictating if linked turrets are active and will shoot targets
+ var/enabled = TRUE
+ /// Variable dictating if linked turrets will shoot lethal projectiles
+ var/lethal = FALSE
+ /// Variable dictating if the panel is locked, preventing changes to turret settings
+ var/locked = TRUE
+ /// An area in which linked turrets are located, it can be an area name, path or nothing
+ var/control_area = null
+ /// AI is unable to use this machine if set to TRUE
+ var/ailock = FALSE
+ /// Variable dictating if linked turrets will shoot cyborgs
+ var/shoot_cyborgs = FALSE
+ /// List of all linked turrets
+ var/list/turrets = list()
/obj/machinery/turretid/Initialize(mapload, ndir = 0, built = 0)
. = ..()
@@ -868,112 +916,111 @@
T.cp = src
/obj/machinery/turretid/examine(mob/user)
- . = ..()
- if(hasSiliconAccessInArea(user) && (!stat & BROKEN))
- . += "Ctrl-click [src] to [ enabled ? "disable" : "enable"] turrets. "
- . += "Alt-click [src] to set turrets to [ lethal ? "stun" : "kill"]. "
+ . += ..()
+ if(issilicon(user) && !(stat & BROKEN))
+ . += {"Ctrl-click [src] to [ enabled ? "disable" : "enable"] turrets.
+ Alt-click [src] to set turrets to [ lethal ? "stun" : "kill"]. "}
/obj/machinery/turretid/attackby(obj/item/I, mob/user, params)
if(stat & BROKEN)
return
if (istype(I, /obj/item/multitool))
+ if(!multitool_check_buffer(user, I))
+ return
var/obj/item/multitool/M = I
if(M.buffer && istype(M.buffer, /obj/machinery/porta_turret))
turrets |= M.buffer
- to_chat(user, "You link \the [M.buffer] with \the [src]")
+ to_chat(user, "You link \the [M.buffer] with \the [src]. ")
return
- if (hasSiliconAccessInArea(user))
+ if (issilicon(user))
return attack_hand(user)
if ( get_dist(src, user) == 0 ) // trying to unlock the interface
if (allowed(usr))
if(obj_flags & EMAGGED)
- to_chat(user, "The turret control is unresponsive. ")
+ to_chat(user, "The turret control is unresponsive! ")
return
locked = !locked
to_chat(user, "You [ locked ? "lock" : "unlock"] the panel. ")
- if (locked)
- if (user.machine==src)
- user.unset_machine()
- user << browse(null, "window=turretid")
- else
- if (user.machine==src)
- attack_hand(user)
else
- to_chat(user, "Access denied. ")
+ to_chat(user, "Access denied. ")
/obj/machinery/turretid/emag_act(mob/user)
- . = ..()
if(obj_flags & EMAGGED)
return
- to_chat(user, "You short out the turret controls' access analysis module. ")
+ to_chat(user, "You short out the turret controls' access analysis module. ")
obj_flags |= EMAGGED
locked = FALSE
- if(user && user.machine == src)
- attack_hand(user)
- return TRUE
/obj/machinery/turretid/attack_ai(mob/user)
if(!ailock || IsAdminGhost(user))
return attack_hand(user)
else
- to_chat(user, "There seems to be a firewall preventing you from accessing this device. ")
+ to_chat(user, "There seems to be a firewall preventing you from accessing this device! ")
-/obj/machinery/turretid/ui_interact(mob/user)
+/obj/machinery/turretid/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "TurretControl", name)
+ ui.open()
+
+/obj/machinery/turretid/ui_data(mob/user)
+ var/list/data = list()
+ data["locked"] = locked
+ data["siliconUser"] = hasSiliconAccessInArea(user) || IsAdminGhost(user)
+ data["enabled"] = enabled
+ data["lethal"] = lethal
+ data["shootCyborgs"] = shoot_cyborgs
+ return data
+
+/obj/machinery/turretid/ui_act(action, list/params)
. = ..()
- if ( get_dist(src, user) > 0 )
- if ( !(hasSiliconAccessInArea(user) || IsAdminGhost(user)) )
- to_chat(user, "You are too far away. ")
- user.unset_machine()
- user << browse(null, "window=turretid")
- return
-
- var/t = ""
-
- if(locked && !(hasSiliconAccessInArea(user) || IsAdminGhost(user)))
- t += "Swipe ID card to unlock interface
"
- else
- if(!hasSiliconAccessInArea(user) && !IsAdminGhost(user))
- t += "Swipe ID card to lock interface
"
- t += "Turrets [enabled?"activated":"deactivated"] - [enabled?"Disable":"Enable"]? "
- t += "Currently set for [lethal?"lethal":"stun repeatedly"] - Change to [lethal?"Stun repeatedly":"Lethal"]? "
-
- var/datum/browser/popup = new(user, "turretid", "Turret Control Panel ([get_area_name(src, TRUE)])")
- popup.set_content(t)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
- popup.open()
-
-/obj/machinery/turretid/Topic(href, href_list)
- if(..())
+ if(.)
return
- if (locked)
- if(!(hasSiliconAccessInArea(usr) || IsAdminGhost(usr)))
- to_chat(usr, "Control panel is locked!")
- return
- if (href_list["toggleOn"])
- toggle_on()
- else if (href_list["toggleLethal"])
- toggle_lethal()
- attack_hand(usr)
-/obj/machinery/turretid/proc/toggle_lethal()
+ switch(action)
+ if("lock")
+ if(!hasSiliconAccessInArea(usr) || IsAdminGhost(usr))
+ return
+ if((obj_flags & EMAGGED) || (stat & BROKEN))
+ to_chat(usr, "The turret control is unresponsive! ")
+ return
+ locked = !locked
+ return TRUE
+ if("power")
+ toggle_on(usr)
+ return TRUE
+ if("mode")
+ toggle_lethal(usr)
+ return TRUE
+ if("shoot_silicons")
+ shoot_silicons(usr)
+ return TRUE
+
+/obj/machinery/turretid/proc/toggle_lethal(mob/user)
lethal = !lethal
+ add_hiddenprint(user)
+ log_combat(user, src, "[lethal ? "enabled" : "disabled"] lethals on")
updateTurrets()
-/obj/machinery/turretid/proc/toggle_on()
+/obj/machinery/turretid/proc/toggle_on(mob/user)
enabled = !enabled
+ add_hiddenprint(user)
+ log_combat(user, src, "[enabled ? "enabled" : "disabled"]")
+ updateTurrets()
+
+/obj/machinery/turretid/proc/shoot_silicons(mob/user)
+ shoot_cyborgs = !shoot_cyborgs
+ add_hiddenprint(user)
+ log_combat(user, src, "[shoot_cyborgs ? "Shooting Borgs" : "Not Shooting Borgs"]")
updateTurrets()
/obj/machinery/turretid/proc/updateTurrets()
for (var/obj/machinery/porta_turret/aTurret in turrets)
- aTurret.setState(enabled, lethal)
- update_icon()
-
-/obj/machinery/turretid/power_change()
- ..()
+ aTurret.setState(enabled, lethal, shoot_cyborgs)
update_icon()
/obj/machinery/turretid/update_icon_state()
@@ -1051,11 +1098,7 @@
/obj/machinery/porta_turret/lasertag
req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE)
- check_records = 0
- criminals = 0
- auth_weapons = 1
- stun_all = 0
- check_anomalies = 0
+ turret_flags = TURRET_FLAG_AUTH_WEAPONS
var/team_color
/obj/machinery/porta_turret/lasertag/assess_perp(mob/living/carbon/human/perp)
@@ -1083,20 +1126,14 @@
if(properties["team_color"])
team_color = properties["team_color"]
-/obj/machinery/porta_turret/lasertag/ui_interact(mob/user)
- . = ..()
+/obj/machinery/porta_turret/lasertag/ui_status(mob/user)
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(team_color == "blue" && istype(H.wear_suit, /obj/item/clothing/suit/redtag))
- return
+ return UI_CLOSE
if(team_color == "red" && istype(H.wear_suit, /obj/item/clothing/suit/bluetag))
- return
-
- var/dat = "Status: [on ? "On" : "Off"] "
-
- var/datum/browser/popup = new(user, "autosec", "Automatic Portable Turret Installation", 300, 300)
- popup.set_content(dat)
- popup.open()
+ return UI_CLOSE
+ return ..()
//lasertag presets
/obj/machinery/porta_turret/lasertag/red
@@ -1112,11 +1149,9 @@
if(on)
if(team_color == "blue")
if(istype(P, /obj/item/projectile/beam/lasertag/redtag))
- on = FALSE
- spawn(100)
- on = TRUE
+ toggle_on(FALSE)
+ addtimer(CALLBACK(src, .proc/toggle_on, TRUE), 10 SECONDS)
else if(team_color == "red")
if(istype(P, /obj/item/projectile/beam/lasertag/bluetag))
- on = FALSE
- spawn(100)
- on = TRUE
+ toggle_on(FALSE)
+ addtimer(CALLBACK(src, .proc/toggle_on, TRUE), 10 SECONDS)
diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm
index e6137c08c2..c6484b051c 100644
--- a/code/game/machinery/spaceheater.dm
+++ b/code/game/machinery/spaceheater.dm
@@ -170,12 +170,10 @@
..()
default_unfasten_wrench(user, I, 5)
return TRUE
-
-/obj/machinery/space_heater/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/space_heater/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "SpaceHeater", name, 400, 305, master_ui, state)
+ ui = new(user, src, "SpaceHeater", name)
ui.open()
/obj/machinery/space_heater/ui_data()
@@ -210,7 +208,7 @@
if("power")
on = !on
mode = HEATER_MODE_STANDBY
- usr.visible_message("[usr] switches [on ? "on" : "off"] \the [src].", "You switch [on ? "on" : "off"] \the [src]. ")
+ usr.visible_message("[usr] switches [on ? "on" : "off"] \the [src]. ", "You switch [on ? "on" : "off"] \the [src]. ")
update_icon()
if (on)
START_PROCESSING(SSmachines, src)
@@ -222,16 +220,7 @@
if(!panel_open)
return
var/target = params["target"]
- var/adjust = text2num(params["adjust"])
- if(target == "input")
- target = input("New target temperature:", name, round(targetTemperature - T0C, 1)) as num|null
- if(!isnull(target) && !..())
- target += T0C
- . = TRUE
- else if(adjust)
- target = targetTemperature + adjust
- . = TRUE
- else if(text2num(target) != null)
+ if(text2num(target) != null)
target= text2num(target) + T0C
. = TRUE
if(.)
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index 75772786fc..cecf351ea9 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -377,11 +377,13 @@
return ..()
-/obj/machinery/suit_storage_unit/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/suit_storage_unit/ui_state(mob/user)
+ return GLOB.notcontained_state
+
+/obj/machinery/suit_storage_unit/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "SuitStorageUnit", name, 400, 305, master_ui, state)
+ ui = new(user, src, "SuitStorageUnit", name)
ui.open()
/obj/machinery/suit_storage_unit/ui_data()
diff --git a/code/game/machinery/telecomms/computers/logbrowser.dm b/code/game/machinery/telecomms/computers/logbrowser.dm
index 9867a1c133..ca04009e3b 100644
--- a/code/game/machinery/telecomms/computers/logbrowser.dm
+++ b/code/game/machinery/telecomms/computers/logbrowser.dm
@@ -17,11 +17,10 @@
var/notice = ""
var/universal_translate = FALSE // set to TRUE(1) if it can translate nonhuman speech
-/obj/machinery/computer/telecomms/server/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/telecomms/server/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "TelecommsLogBrowser", "Telecomms Server Monitor", 575, 400, master_ui, state)
+ ui = new(user, src, "TelecommsLogBrowser", "Telecomms Server Monitor")
ui.open()
/obj/machinery/computer/telecomms/server/ui_data(mob/user)
diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm
index 11d50a2858..539025e4d5 100644
--- a/code/game/machinery/telecomms/computers/message.dm
+++ b/code/game/machinery/telecomms/computers/message.dm
@@ -36,12 +36,11 @@
light_color = LIGHT_COLOR_GREEN
-/obj/machinery/computer/message_monitor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE,\
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/message_monitor/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "TelecommsPDALog", name, 727, 510, master_ui, state)
+ ui = new(user, src, "TelecommsPDALog", name)
ui.open()
/obj/machinery/computer/message_monitor/ui_static_data(mob/user)
diff --git a/code/game/machinery/telecomms/computers/telemonitor.dm b/code/game/machinery/telecomms/computers/telemonitor.dm
index 11312fcfaf..b711b9fa40 100644
--- a/code/game/machinery/telecomms/computers/telemonitor.dm
+++ b/code/game/machinery/telecomms/computers/telemonitor.dm
@@ -17,12 +17,11 @@
circuit = /obj/item/circuitboard/computer/comm_monitor
-/obj/machinery/computer/telecomms/monitor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE,\
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/telecomms/monitor/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "TelecommsMonitor", name, 575, 400, master_ui, state)
+ ui = new(user, src, "TelecommsMonitor", name)
ui.open()
/obj/machinery/computer/telecomms/monitor/ui_data(mob/user)
diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm
index 86aa7905d5..cc8784c5b8 100644
--- a/code/game/machinery/telecomms/machine_interactions.dm
+++ b/code/game/machinery/telecomms/machine_interactions.dm
@@ -27,16 +27,15 @@
else
return ..()
-/obj/machinery/telecomms/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE,\
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+/obj/machinery/telecomms/ui_interact(mob/user, datum/tgui/ui)
if(!canInteract(user))
if(ui)
ui.close() //haha no.
return
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "TelecommsInteraction", "[name] Access", 520, 500, master_ui, state)
+ ui = new(user, src, "TelecommsInteraction", "[name] Access")
ui.open()
/obj/machinery/telecomms/ui_data(mob/user)
diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm
index 86cdd87505..ef1565502e 100644
--- a/code/game/mecha/equipment/mecha_equipment.dm
+++ b/code/game/mecha/equipment/mecha_equipment.dm
@@ -11,10 +11,15 @@
var/equip_ready = 1 //whether the equipment is ready for use. (or deactivated/activated for static stuff)
var/energy_drain = 0
var/obj/mecha/chassis = null
- var/range = MELEE //bitFflags
+ /// Bitflag. Determines the range of the equipment.
+ var/range = MELEE
+ /// Bitflag. Used by exosuit fabricator to assign sub-categories based on which exosuits can equip this.
+ var/mech_flags = NONE
var/salvageable = 1
+ //var/detachable = TRUE // Set to FALSE for built-in equipment that cannot be removed
var/selectable = 1 // Set to 0 for passive equipment such as mining scanner or armor plates
var/harmful = FALSE //Controls if equipment can be used to attack by a pacifist.
+ //var/destroy_sound = 'sound/mecha/critdestr.ogg'
/obj/item/mecha_parts/mecha_equipment/proc/update_chassis_page()
if(chassis)
@@ -35,9 +40,14 @@
if(chassis.selected == src)
chassis.selected = null
src.update_chassis_page()
- chassis.occupant_message("[src] is destroyed! ")
+ //log_message("[src] is destroyed.", LOG_MECHA)
chassis.log_append_to_last("[src] is destroyed.",1)
- SEND_SOUND(chassis.occupant, sound(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon) ? 'sound/mecha/weapdestr.ogg' : 'sound/mecha/critdestr.ogg', volume=50))
+ if(chassis.occupant)
+ chassis.occupant_message("[src] is destroyed! ")
+ SEND_SOUND(chassis.occupant, sound(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon) ? 'sound/mecha/weapdestr.ogg' : 'sound/mecha/critdestr.ogg', volume=50))
+ //chassis.occupant.playsound_local(chassis, destroy_sound, 50)
+ //if(!detachable) //If we're a built-in nondetachable equipment, let's lock up the slot that we were in.
+ // chassis.max_equip--
chassis = null
return ..()
@@ -59,7 +69,7 @@
return txt
/obj/item/mecha_parts/mecha_equipment/proc/is_ranged()//add a distance restricted equipment. Why not?
- return range&RANGED
+ return range&RANGED //rename to MECHA_RANGE and MECHA_MELEE
/obj/item/mecha_parts/mecha_equipment/proc/is_melee()
return range&MELEE
@@ -72,10 +82,10 @@
return 0
if(!equip_ready)
return 0
- if(crit_fail)
- return 0
if(energy_drain && !chassis.has_charge(energy_drain))
return 0
+ if(crit_fail)
+ return 0
if(chassis.equipment_disabled)
to_chat(chassis.occupant, "Error -- Equipment control unit is unresponsive. ")
return 0
@@ -117,8 +127,6 @@
chassis = M
forceMove(M)
M.mecha_log_message("[src] initialized.")
- if(!M.selected && selectable)
- M.selected = src
src.update_chassis_page()
return
@@ -150,7 +158,7 @@
chassis.occupant_message("[icon2html(src, chassis.occupant)] [message]")
return
-/obj/item/mecha_parts/mecha_equipment/proc/mecha_log_message(message, color)
+/obj/item/mecha_parts/mecha_equipment/proc/mecha_log_message(message, color) //on tg this just overrides log_message
log_message(message, LOG_GAME, color) //pass to default admin logging too
if(chassis)
chassis.mecha_log_message(message, color) //and pass to our chassis
diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm
index 4a4d58ea73..1435f78718 100644
--- a/code/game/mecha/equipment/tools/medical_tools.dm
+++ b/code/game/mecha/equipment/tools/medical_tools.dm
@@ -1,6 +1,7 @@
// Sleeper, Medical Beam, and Syringe gun
/obj/item/mecha_parts/mecha_equipment/medical
+ mech_flags = EXOSUIT_MODULE_MEDICAL
/obj/item/mecha_parts/mecha_equipment/medical/Initialize()
. = ..()
diff --git a/code/game/mecha/equipment/tools/mining_tools.dm b/code/game/mecha/equipment/tools/mining_tools.dm
index d4a29f639d..7c7c6be2bc 100644
--- a/code/game/mecha/equipment/tools/mining_tools.dm
+++ b/code/game/mecha/equipment/tools/mining_tools.dm
@@ -17,6 +17,7 @@
toolspeed = 0.9
var/drill_delay = 7
var/drill_level = DRILL_BASIC
+ mech_flags = EXOSUIT_MODULE_WORKING | EXOSUIT_MODULE_COMBAT
/obj/item/mecha_parts/mecha_equipment/drill/Initialize()
. = ..()
@@ -153,6 +154,7 @@
selectable = 0
equip_cooldown = 15
var/scanning_time = 0
+ mech_flags = EXOSUIT_MODULE_WORKING
/obj/item/mecha_parts/mecha_equipment/mining_scanner/Initialize()
. = ..()
diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm
index 759fcd996d..1c4586205a 100644
--- a/code/game/mecha/equipment/tools/work_tools.dm
+++ b/code/game/mecha/equipment/tools/work_tools.dm
@@ -8,11 +8,12 @@
icon_state = "mecha_clamp"
equip_cooldown = 15
energy_drain = 10
+ tool_behaviour = TOOL_RETRACTOR
+ toolspeed = 0.8
var/dam_force = 20
var/obj/mecha/working/ripley/cargo_holder
harmful = TRUE
- tool_behaviour = TOOL_RETRACTOR
- toolspeed = 0.8
+ mech_flags = EXOSUIT_MODULE_RIPLEY
/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/can_attach(obj/mecha/working/ripley/M as obj)
if(..())
@@ -180,6 +181,7 @@
equip_cooldown = 5
energy_drain = 0
range = MELEE|RANGED
+ mech_flags = EXOSUIT_MODULE_WORKING
/obj/item/mecha_parts/mecha_equipment/extinguisher/Initialize()
. = ..()
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 65aa001fe1..f2bced4aa8 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -9,6 +9,7 @@
var/projectile_delay = 0
var/firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect //the visual effect appearing when the weapon is fired.
var/kickback = TRUE //Will using this weapon in no grav push mecha back.
+ mech_flags = EXOSUIT_MODULE_COMBAT
/obj/item/mecha_parts/mecha_equipment/weapon/can_attach(obj/mecha/combat/M)
if(..())
diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm
index dcd8a12b38..e9f682e929 100644
--- a/code/game/mecha/mech_bay.dm
+++ b/code/game/mecha/mech_bay.dm
@@ -81,10 +81,10 @@
var/obj/machinery/mech_bay_recharge_port/recharge_port
light_color = LIGHT_COLOR_PINK
-/obj/machinery/computer/mech_bay_power_console/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/mech_bay_power_console/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "MechBayPowerConsole", "Mech Bay Power Control Console", 400, 200, master_ui, state)
+ ui = new(user, src, "MechBayPowerConsole", name)
ui.open()
/obj/machinery/computer/mech_bay_power_console/ui_act(action, params)
@@ -104,7 +104,6 @@
data["recharge_port"]["mech"] = list("health" = recharge_port.recharging_mech.obj_integrity, "maxhealth" = recharge_port.recharging_mech.max_integrity, "cell" = null, "name" = recharge_port.recharging_mech.name,)
if(recharge_port.recharging_mech.cell && !QDELETED(recharge_port.recharging_mech.cell))
data["recharge_port"]["mech"]["cell"] = list(
- "critfail" = recharge_port.recharging_mech.cell.crit_fail,
"charge" = recharge_port.recharging_mech.cell.charge,
"maxcharge" = recharge_port.recharging_mech.cell.maxcharge
)
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index ca481f0d80..002753c5d4 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -9,17 +9,41 @@
active_power_usage = 5000
req_access = list(ACCESS_ROBOTICS)
circuit = /obj/item/circuitboard/machine/mechfab
- var/time_coeff = 1
- var/component_coeff = 1
- var/datum/techweb/specialized/autounlocking/exofab/stored_research
- var/sync = 0
- var/part_set
- var/datum/design/being_built
+ // processing_flags = START_PROCESSING_MANUALLY
+
+ // subsystem_type = /datum/controller/subsystem/processing/fastprocess
+
+ /// Current items in the build queue.
var/list/queue = list()
- var/processing_queue = 0
- var/screen = "main"
- var/temp
- var/offstation_security_levels = TRUE
+ /// Whether or not the machine is building the entire queue automagically.
+ var/process_queue = FALSE
+
+ /// The current design datum that the machine is building.
+ var/datum/design/being_built
+ /// World time when the build will finish.
+ var/build_finish = 0
+ /// World time when the build started.
+ var/build_start = 0
+ /// Reference to all materials used in the creation of the item being_built.
+ var/list/build_materials
+ /// Part currently stored in the Exofab.
+ var/obj/item/stored_part
+
+ /// Coefficient for the speed of item building. Based on the installed parts.
+ var/time_coeff = 1
+ /// Coefficient for the efficiency of material usage in item building. Based on the installed parts.
+ var/component_coeff = 1
+
+ /// Copy of the currently synced techweb.
+ var/datum/techweb/specialized/autounlocking/exofab/stored_research
+
+ /// Whether the Exofab links to the ore silo on init. Special derelict or maintanance variants should set this to FALSE.
+ var/link_on_init = TRUE
+
+ /// Reference to a remote material inventory, such as an ore silo.
+ var/datum/component/remote_materials/rmat
+
+ /// A list of categories that valid MECHFAB design datums will broadly categorise themselves under.
var/list/part_sets = list(
"Cyborg",
"Ripley",
@@ -33,25 +57,17 @@
"Exosuit Equipment",
"Exosuit Ammunition",
"Cyborg Upgrade Modules",
+ "Cybernetics",
+ "Implants",
+ "Control Interfaces",
"Misc"
)
-/obj/machinery/mecha_part_fabricator/Initialize()
- var/static/list/allowed_types = list(
- /datum/material/iron,
- /datum/material/glass,
- /datum/material/silver,
- /datum/material/gold,
- /datum/material/diamond,
- /datum/material/plasma,
- /datum/material/uranium,
- /datum/material/bananium,
- /datum/material/titanium,
- /datum/material/bluespace
- )
- var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, allowed_types, 0, TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
- materials.precise_insertion = TRUE
+/obj/machinery/mecha_part_fabricator/Initialize(mapload)
stored_research = new
+ rmat = AddComponent(/datum/component/remote_materials, "mechfab", mapload && link_on_init)
+
+ RefreshParts() //Recalculating local material sizes if the fab isn't linked
return ..()
/obj/machinery/mecha_part_fabricator/RefreshParts()
@@ -60,8 +76,7 @@
//maximum stocking amount (default 300000, 600000 at T4)
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
T += M.rating
- var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
- materials.max_amount = (200000 + (T*50000))
+ rmat.set_local_size((200000 + (T*50000)))
//resources adjustment coefficient (1 -> 0.85 -> 0.7 -> 0.55)
T = 1.15
@@ -74,89 +89,157 @@
for(var/obj/item/stock_parts/manipulator/Ml in component_parts)
T += Ml.rating
time_coeff = round(initial(time_coeff) - (initial(time_coeff)*(T))/5,0.01)
- var/obj/item/circuitboard/machine/mechfab/C = circuit
- offstation_security_levels = C.offstation_security_levels
+
+ // Adjust the build time of any item currently being built.
+ if(being_built)
+ var/last_const_time = build_finish - build_start
+ var/new_const_time = get_construction_time_w_coeff(initial(being_built.construction_time))
+ var/const_time_left = build_finish - world.time
+ var/new_build_time = (new_const_time / last_const_time) * const_time_left
+ build_finish = world.time + new_build_time
+
+ update_static_data(usr)
/obj/machinery/mecha_part_fabricator/examine(mob/user)
. = ..()
- var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
if(in_range(user, src) || isobserver(user))
- . += "The status display reads: Storing up to [materials.max_amount] material units. Material consumption at [component_coeff*100]% . Build time reduced by [100-time_coeff*100]% . "
+ . += "The status display reads: Storing up to [rmat.local_size] material units. Material consumption at [component_coeff*100]% . Build time reduced by [100-time_coeff*100]% . "
-/obj/machinery/mecha_part_fabricator/emag_act()
- . = ..()
- if(obj_flags & EMAGGED)
- return
- obj_flags |= EMAGGED
- req_access = list()
- INVOKE_ASYNC(src, .proc/error_action_sucessful)
- return TRUE
-
-/obj/machinery/mecha_part_fabricator/proc/error_action_sucessful()
- say("DB error \[Code 0x00F1\]")
- sleep(10)
- say("Attempting auto-repair...")
- sleep(15)
- say("User DB corrupted \[Code 0x00FA\]. Truncating data structure...")
- sleep(30)
- say("User DB truncated. Please contact your Nanotrasen system operator for future assistance.")
-
-
-/obj/machinery/mecha_part_fabricator/proc/output_parts_list(set_name)
- var/output = ""
- for(var/v in stored_research.researched_designs)
- var/datum/design/D = SSresearch.techweb_design_by_id(v)
- if(D.build_type & MECHFAB)
- if(!(set_name in D.category))
- continue
- output += "[output_part_info(D)]
\["
- if(check_clearance(D) && check_resources(D))
- output += "
Build | "
- output += "
Add to queue \]\[
? \]
"
- return output
-
-/obj/machinery/mecha_part_fabricator/proc/check_clearance(datum/design/D)
- if(!(obj_flags & EMAGGED) && (offstation_security_levels || is_station_level(z)) && !ISINRANGE(GLOB.security_level, D.min_security_level, D.max_security_level))
- return FALSE
- return TRUE
-
-/obj/machinery/mecha_part_fabricator/proc/output_part_info(datum/design/D)
- var/clearance = !(obj_flags & EMAGGED) && (offstation_security_levels || is_station_level(z))
- var/sec_text = ""
- if(clearance && (D.min_security_level > SEC_LEVEL_GREEN || D.max_security_level < SEC_LEVEL_DELTA))
- sec_text = " (Allowed security levels: "
- for(var/n in D.min_security_level to D.max_security_level)
- sec_text += NUM2SECLEVEL(n)
- if(n + 1 <= D.max_security_level)
- sec_text += ", "
- sec_text += ") "
- var/output = "[initial(D.name)] (Cost: [output_part_cost(D)]) [sec_text][get_construction_time_w_coeff(D)/10]sec"
- return output
-
-/obj/machinery/mecha_part_fabricator/proc/output_part_cost(datum/design/D)
- var/i = 0
- var/output
+/**
+ * Generates an info list for a given part.
+ *
+ * Returns a list of part information.
+ * * D - Design datum to get information on.
+ * * categories - Boolean, whether or not to parse snowflake categories into the part information list.
+ */
+/obj/machinery/mecha_part_fabricator/proc/output_part_info(datum/design/D, categories = FALSE)
+ var/cost = list()
for(var/c in D.materials)
var/datum/material/M = c
- output += "[i?" | ":null][get_resource_cost_w_coeff(D, M)] [M.name]"
- i++
- return output
+ cost[M.name] = get_resource_cost_w_coeff(D, M)
+ var/obj/built_item = D.build_path
+
+ var/list/category_override = null
+ var/list/sub_category = null
+
+ if(categories)
+ // Handle some special cases to build up sub-categories for the fab interface.
+ // Start with checking if this design builds a cyborg module.
+ if(built_item in typesof(/obj/item/borg/upgrade))
+ var/obj/item/borg/upgrade/U = built_item
+ var/module_types = initial(U.module_flags)
+ sub_category = list()
+ if(module_types)
+ if(module_types & BORG_MODULE_SECURITY)
+ sub_category += "Security"
+ if(module_types & BORG_MODULE_MINER)
+ sub_category += "Mining"
+ if(module_types & BORG_MODULE_JANITOR)
+ sub_category += "Janitor"
+ if(module_types & BORG_MODULE_MEDICAL)
+ sub_category += "Medical"
+ if(module_types & BORG_MODULE_ENGINEERING)
+ sub_category += "Engineering"
+ else
+ sub_category += "All Cyborgs"
+ // Else check if this design builds a piece of exosuit equipment.
+ else if(built_item in typesof(/obj/item/mecha_parts/mecha_equipment))
+ var/obj/item/mecha_parts/mecha_equipment/E = built_item
+ var/mech_types = initial(E.mech_flags)
+ sub_category = "Equipment"
+ if(mech_types)
+ category_override = list()
+ if(mech_types & EXOSUIT_MODULE_RIPLEY)
+ category_override += "Ripley"
+ if(mech_types & EXOSUIT_MODULE_FIREFIGHTER)
+ category_override += "Firefighter"
+ if(mech_types & EXOSUIT_MODULE_ODYSSEUS)
+ category_override += "Odysseus"
+ // if(mech_types & EXOSUIT_MODULE_CLARKE)
+ // category_override += "Clarke"
+ if(mech_types & EXOSUIT_MODULE_GYGAX_MED)
+ category_override += "Medical-Spec Gygax"
+ if(mech_types & EXOSUIT_MODULE_GYGAX)
+ category_override += "Gygax"
+ if(mech_types & EXOSUIT_MODULE_DURAND)
+ category_override += "Durand"
+ if(mech_types & EXOSUIT_MODULE_HONK)
+ category_override += "H.O.N.K"
+ if(mech_types & EXOSUIT_MODULE_PHAZON)
+ category_override += "Phazon"
+
+
+ var/list/part = list(
+ "name" = D.name,
+ "desc" = initial(built_item.desc),
+ "printTime" = get_construction_time_w_coeff(initial(D.construction_time))/10,
+ "cost" = cost,
+ "id" = D.id,
+ "subCategory" = sub_category,
+ "categoryOverride" = category_override,
+ "searchMeta" = "UNKNOWN"//D.search_metadata
+ )
+
+ return part
+
+/**
+ * Generates a list of resources / materials available to this Exosuit Fab
+ *
+ * Returns null if there is no material container available.
+ * List format is list(material_name = list(amount = ..., ref = ..., etc.))
+ */
/obj/machinery/mecha_part_fabricator/proc/output_available_resources()
- var/output
- var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
- for(var/mat_id in materials.materials)
- var/datum/material/M = mat_id
- var/amount = materials.materials[mat_id]
- output += "[M.name]: [amount] cm³"
- if(amount >= MINERAL_MATERIAL_AMOUNT)
- output += "- Remove \[1 \]"
- if(amount >= (MINERAL_MATERIAL_AMOUNT * 10))
- output += " | \[10 \]"
- output += " | \[All \] "
- output += " "
- return output
+ var/datum/component/material_container/materials = rmat.mat_container
+ var/list/material_data = list()
+
+ if(materials)
+ for(var/mat_id in materials.materials)
+ var/datum/material/M = mat_id
+ var/list/material_info = list()
+ var/amount = materials.materials[mat_id]
+
+ material_info = list(
+ "name" = M.name,
+ "ref" = REF(M),
+ "amount" = amount,
+ "sheets" = round(amount / MINERAL_MATERIAL_AMOUNT),
+ "removable" = amount >= MINERAL_MATERIAL_AMOUNT
+ )
+
+ material_data += list(material_info)
+
+ return material_data
+
+ return null
+
+/**
+ * Intended to be called when an item starts printing.
+ *
+ * Adds the overlay to show the fab working and sets active power usage settings.
+ */
+/obj/machinery/mecha_part_fabricator/proc/on_start_printing()
+ add_overlay("fab-active")
+ use_power = ACTIVE_POWER_USE
+
+/**
+ * Intended to be called when the exofab has stopped working and is no longer printing items.
+ *
+ * Removes the overlay to show the fab working and sets idle power usage settings. Additionally resets the description and turns off queue processing.
+ */
+/obj/machinery/mecha_part_fabricator/proc/on_finish_printing()
+ cut_overlay("fab-active")
+ use_power = IDLE_POWER_USE
+ desc = initial(desc)
+ process_queue = FALSE
+
+/**
+ * Calculates resource/material costs for printing an item based on the machine's resource coefficient.
+ *
+ * Returns a list of k,v resources with their amounts.
+ * * D - Design datum to calculate the modified resource cost of.
+ */
/obj/machinery/mecha_part_fabricator/proc/get_resources_w_coeff(datum/design/D)
var/list/resources = list()
for(var/R in D.materials)
@@ -164,294 +247,419 @@
resources[M] = get_resource_cost_w_coeff(D, M)
return resources
+/**
+ * Checks if the Exofab has enough resources to print a given item.
+ *
+ * Returns FALSE if the design has no reagents used in its construction (?) or if there are insufficient resources.
+ * Returns TRUE if there are sufficient resources to print the item.
+ * * D - Design datum to calculate the modified resource cost of.
+ */
/obj/machinery/mecha_part_fabricator/proc/check_resources(datum/design/D)
- if(D.reagents_list.len) // No reagents storage - no reagent designs.
+ if(length(D.reagents_list)) // No reagents storage - no reagent designs.
return FALSE
- var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
+ var/datum/component/material_container/materials = rmat.mat_container
if(materials.has_materials(get_resources_w_coeff(D)))
return TRUE
return FALSE
-/obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D)
+/**
+ * Attempts to build the next item in the build queue.
+ *
+ * Returns FALSE if either there are no more parts to build or the next part is not buildable.
+ * Returns TRUE if the next part has started building.
+ * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build.
+ */
+/obj/machinery/mecha_part_fabricator/proc/build_next_in_queue(verbose = TRUE)
+ if(!length(queue))
+ return FALSE
+
+ var/datum/design/D = queue[1]
+ if(build_part(D, verbose))
+ remove_from_queue(1)
+ return TRUE
+
+ return FALSE
+
+/**
+ * Starts the build process for a given design datum.
+ *
+ * Returns FALSE if the procedure fails. Returns TRUE when being_built is set.
+ * Uses materials.
+ * * D - Design datum to attempt to print.
+ * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build.
+ */
+/obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D, verbose = TRUE)
+ if(!D)
+ return FALSE
+
+ var/datum/component/material_container/materials = rmat.mat_container
+ if (!materials)
+ if(verbose)
+ say("No access to material storage, please contact the quartermaster.")
+ return FALSE
+ if (rmat.on_hold())
+ if(verbose)
+ say("Mineral access is on hold, please contact the quartermaster.")
+ return FALSE
+ if(!check_resources(D))
+ if(verbose)
+ say("Not enough resources. Processing stopped.")
+ return FALSE
+
+ build_materials = get_resources_w_coeff(D)
+
+ materials.use_materials(build_materials)
being_built = D
- desc = "It's building \a [initial(D.name)]."
- var/list/res_coef = get_resources_w_coeff(D)
+ build_finish = world.time + get_construction_time_w_coeff(initial(D.construction_time))
+ build_start = world.time
+ desc = "It's building \a [D.name]."
- var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
- materials.use_materials(res_coef)
- add_overlay("fab-active")
- use_power = ACTIVE_POWER_USE
- updateUsrDialog()
- sleep(get_construction_time_w_coeff(D))
- use_power = IDLE_POWER_USE
- cut_overlay("fab-active")
- desc = initial(desc)
+ rmat.silo_log(src, "built", -1, "[D.name]", build_materials)
- var/location = get_step(src,(dir))
- var/obj/item/I = new D.build_path(location)
- I.set_custom_materials(res_coef)
- say("\The [I] is complete.")
- being_built = null
-
- updateUsrDialog()
return TRUE
-/obj/machinery/mecha_part_fabricator/proc/update_queue_on_page()
- send_byjax(usr,"mecha_fabricator.browser","queue",list_queue())
- return
+/obj/machinery/mecha_part_fabricator/process()
+ // If there's a stored part to dispense due to an obstruction, try to dispense it.
+ if(stored_part)
+ var/turf/exit = get_step(src,(dir))
+ if(exit.density)
+ return TRUE
-/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(set_name)
- if(set_name in part_sets)
- for(var/v in stored_research.researched_designs)
- var/datum/design/D = SSresearch.techweb_design_by_id(v)
- if(D.build_type & MECHFAB)
- if(set_name in D.category)
- add_to_queue(D)
+ say("Obstruction cleared. \The [stored_part] is complete.")
+ stored_part.forceMove(exit)
+ stored_part = null
-/obj/machinery/mecha_part_fabricator/proc/add_to_queue(D)
+ // If there's nothing being built, try to build something
+ if(!being_built)
+ // If we're not processing the queue anymore or there's nothing to build, end processing.
+ if(!process_queue || !build_next_in_queue())
+ on_finish_printing()
+ STOP_PROCESSING(SSfastprocess, src)
+ //end_processing()
+ return TRUE
+ on_start_printing()
+
+ // If there's an item being built, check if it is complete.
+ if(being_built && (build_finish < world.time))
+ // Then attempt to dispense it and if appropriate build the next item.
+ dispense_built_part(being_built)
+ if(process_queue)
+ build_next_in_queue(FALSE)
+ return TRUE
+
+/**
+ * Dispenses a part to the tile infront of the Exosuit Fab.
+ *
+ * Returns FALSE is the machine cannot dispense the part on the appropriate turf.
+ * Return TRUE if the part was successfully dispensed.
+ * * D - Design datum to attempt to dispense.
+ */
+/obj/machinery/mecha_part_fabricator/proc/dispense_built_part(datum/design/D)
+ var/obj/item/I = new D.build_path(src)
+ // I.material_flags |= MATERIAL_NO_EFFECTS //Find a better way to do this.
+ I.set_custom_materials(build_materials)
+
+ being_built = null
+
+ var/turf/exit = get_step(src,(dir))
+ if(exit.density)
+ say("Error! Part outlet is obstructed.")
+ desc = "It's trying to dispense \a [D.name], but the part outlet is obstructed."
+ stored_part = I
+ return FALSE
+
+ say("\The [I] is complete.")
+ I.forceMove(exit)
+ return TRUE
+
+/**
+ * Adds a list of datum designs to the build queue.
+ *
+ * Will only add designs that are in this machine's stored techweb.
+ * Does final checks for datum IDs and makes sure this machine can build the designs.
+ * * part_list - List of datum design ids for designs to add to the queue.
+ */
+/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(list/part_list)
+ for(var/v in stored_research.researched_designs)
+ var/datum/design/D = SSresearch.techweb_design_by_id(v)
+ if((D.build_type & MECHFAB) && (D.id in part_list))
+ add_to_queue(D)
+
+/**
+ * Adds a datum design to the build queue.
+ *
+ * Returns TRUE if successful and FALSE if the design was not added to the queue.
+ * * D - Datum design to add to the queue.
+ */
+/obj/machinery/mecha_part_fabricator/proc/add_to_queue(datum/design/D)
if(!istype(queue))
queue = list()
if(D)
queue[++queue.len] = D
- return queue.len
+ return TRUE
+ return FALSE
+/**
+ * Removes datum design from the build queue based on index.
+ *
+ * Returns TRUE if successful and FALSE if a design was not removed from the queue.
+ * * index - Index in the build queue of the element to remove.
+ */
/obj/machinery/mecha_part_fabricator/proc/remove_from_queue(index)
- if(!isnum(index) || !ISINTEGER(index) || !istype(queue) || (index<1 || index>queue.len))
+ if(!isnum(index) || !ISINTEGER(index) || !istype(queue) || (index<1 || index>length(queue)))
return FALSE
queue.Cut(index,++index)
return TRUE
-/obj/machinery/mecha_part_fabricator/proc/process_queue()
- var/datum/design/D = queue[1]
- if(!D)
- remove_from_queue(1)
- if(queue.len)
- return process_queue()
- else
- return
- temp = null
- while(D)
- if(stat&(NOPOWER|BROKEN))
- return FALSE
- if(!check_clearance(D))
- say("Security level not met. Queue processing stopped.")
- temp = {"Security level not met to build next part.
- Try again | Return "}
- return FALSE
- if(!check_resources(D))
- say("Not enough resources. Queue processing stopped.")
- temp = {"Not enough resources to build next part.
- Try again | Return "}
- return FALSE
- remove_from_queue(1)
- build_part(D)
- D = listgetindex(queue, 1)
- say("Queue processing finished successfully.")
-
+/**
+ * Generates a list of parts formatted for tgui based on the current build queue.
+ *
+ * Returns a formatted list of lists containing formatted part information for every part in the build queue.
+ */
/obj/machinery/mecha_part_fabricator/proc/list_queue()
- var/output = "Queue contains: "
- if(!istype(queue) || !queue.len)
- output += " Nothing"
- else
- output += ""
- var/i = 0
- for(var/datum/design/D in queue)
- i++
- var/obj/part = D.build_path
- output += ""
- output += initial(part.name) + " - "
- output += "[i>1?"↑ ":null] "
- output += "[i↓ ":null] "
- output += "Remove "
+ if(!istype(queue) || !length(queue))
+ return null
- output += ""
- output += "\[Process queue | Clear queue \]"
- return output
+ var/list/queued_parts = list()
+ for(var/datum/design/D in queue)
+ var/list/part = output_part_info(D)
+ queued_parts += list(part)
+ return queued_parts
+/**
+ * Syncs machine with R&D servers.
+ *
+ * Requires an R&D Console visible within 7 tiles. Copies techweb research. Updates tgui's state data.
+ */
/obj/machinery/mecha_part_fabricator/proc/sync()
- temp = "Updating local R&D database..."
- updateUsrDialog()
- sleep(30) //only sleep if called by user
-
- for(var/obj/machinery/computer/rdconsole/RDC in oview(7,src))
+ for(var/obj/machinery/computer/rdconsole/RDC in orange(7,src))
RDC.stored_research.copy_research_to(stored_research)
- temp = "Processed equipment designs. "
- //check if the tech coefficients have changed
- temp += "Return "
-
- updateUsrDialog()
+ update_static_data(usr)
say("Successfully synchronized with R&D server.")
return
- temp = "Unable to connect to local R&D Database. Please check your connections and try again.Return "
- updateUsrDialog()
+ say("Unable to connect to local R&D server.")
return
+/**
+ * Calculates the coefficient-modified resource cost of a single material component of a design's recipe.
+ *
+ * Returns coefficient-modified resource cost for the given material component.
+ * * D - Design datum to pull the resource cost from.
+ * * resource - Material datum reference to the resource to calculate the cost of.
+ * * roundto - Rounding value for round() proc
+ */
/obj/machinery/mecha_part_fabricator/proc/get_resource_cost_w_coeff(datum/design/D, var/datum/material/resource, roundto = 1)
return round(D.materials[resource]*component_coeff, roundto)
-/obj/machinery/mecha_part_fabricator/proc/get_construction_time_w_coeff(datum/design/D, roundto = 1) //aran
- return round(initial(D.construction_time)*time_coeff, roundto)
+/**
+ * Calculates the coefficient-modified build time of a design.
+ *
+ * Returns coefficient-modified build time of a given design.
+ * * D - Design datum to calculate the modified build time of.
+ * * roundto - Rounding value for round() proc
+ */
+/obj/machinery/mecha_part_fabricator/proc/get_construction_time_w_coeff(construction_time, roundto = 1) //aran
+ return round(construction_time*time_coeff, roundto)
-/obj/machinery/mecha_part_fabricator/ui_interact(mob/user as mob)
- . = ..()
- var/dat, left_part
- user.set_machine(src)
- var/turf/exit = get_step(src,(dir))
- if(exit.density)
- say("Error! Part outlet is obstructed.")
- return
- if(temp)
- left_part = temp
- else if(being_built)
- var/obj/I = being_built.build_path
- left_part = {"Building [initial(I.name)].
- Please wait until completion... "}
+/obj/machinery/mecha_part_fabricator/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/sheetmaterials)
+ )
+
+/obj/machinery/mecha_part_fabricator/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ExosuitFabricator")
+ ui.open()
+
+/obj/machinery/mecha_part_fabricator/ui_static_data(mob/user)
+ var/list/data = list()
+
+ var/list/final_sets = list()
+ var/list/buildable_parts = list()
+
+ for(var/part_set in part_sets)
+ final_sets += part_set
+
+ for(var/v in stored_research.researched_designs)
+ var/datum/design/D = SSresearch.techweb_design_by_id(v)
+ if(D.build_type & MECHFAB)
+ // This is for us.
+ var/list/part = output_part_info(D, TRUE)
+
+ if(part["category_override"])
+ for(var/cat in part["category_override"])
+ buildable_parts[cat] += list(part)
+ if(!(cat in part_sets))
+ final_sets += cat
+ continue
+
+ for(var/cat in part_sets)
+ // Find all matching categories.
+ if(!(cat in D.category))
+ continue
+
+ buildable_parts[cat] += list(part)
+
+ data["partSets"] = final_sets
+ data["buildableParts"] = buildable_parts
+
+ return data
+
+/obj/machinery/mecha_part_fabricator/ui_data(mob/user)
+ var/list/data = list()
+
+ data["materials"] = output_available_resources()
+
+ if(being_built)
+ var/list/part = list(
+ "name" = being_built.name,
+ "duration" = build_finish - world.time,
+ "printTime" = get_construction_time_w_coeff(initial(being_built.construction_time))
+ )
+ data["buildingPart"] = part
else
- switch(screen)
- if("main")
- left_part = output_available_resources()+" "
- left_part += "Sync with R&D servers "
- for(var/part_set in part_sets)
- left_part += "[part_set] - \[Add all parts to queue \] "
- if("parts")
- left_part += output_parts_list(part_set)
- left_part += "Return "
- dat = {"
-
-
- [name] data
-
-
-
-
-
-
-
- [left_part]
-
-
- [list_queue()]
-
-
-
-
- "}
- user << browse(dat, "window=mecha_fabricator;size=1000x430")
- onclose(user, "mecha_fabricator")
- return
+ data["buildingPart"] = null
-/obj/machinery/mecha_part_fabricator/Topic(href, href_list)
+ data["queue"] = list_queue()
+
+ if(stored_part)
+ data["storedPart"] = stored_part.name
+ else
+ data["storedPart"] = null
+
+ data["isProcessingQueue"] = process_queue
+
+ return data
+
+/obj/machinery/mecha_part_fabricator/ui_act(action, var/list/params)
if(..())
- return
- if(href_list["part_set"])
- var/tpart_set = href_list["part_set"]
- if(tpart_set)
- if(tpart_set=="clear")
- part_set = null
- else
- part_set = tpart_set
- screen = "parts"
- if(href_list["part"])
- var/T = href_list["part"]
- for(var/v in stored_research.researched_designs)
- var/datum/design/D = SSresearch.techweb_design_by_id(v)
- if(D.build_type & MECHFAB)
- if(D.id == T)
- if(!processing_queue)
- build_part(D)
- else
- add_to_queue(D)
- break
- if(href_list["add_to_queue"])
- var/T = href_list["add_to_queue"]
- for(var/v in stored_research.researched_designs)
- var/datum/design/D = SSresearch.techweb_design_by_id(v)
- if(D.build_type & MECHFAB)
- if(D.id == T)
+ return TRUE
+
+ . = TRUE
+
+ add_fingerprint(usr)
+ usr.set_machine(src)
+
+ switch(action)
+ if("sync_rnd")
+ // Sync with R&D Servers
+ sync()
+ return
+ if("add_queue_set")
+ // Add all parts of a set to queue
+ var/part_list = params["part_list"]
+ add_part_set_to_queue(part_list)
+ return
+ if("add_queue_part")
+ // Add a specific part to queue
+ var/T = params["id"]
+ for(var/v in stored_research.researched_designs)
+ var/datum/design/D = SSresearch.techweb_design_by_id(v)
+ if((D.build_type & MECHFAB) && (D.id == T))
add_to_queue(D)
break
- return update_queue_on_page()
- if(href_list["remove_from_queue"])
- remove_from_queue(text2num(href_list["remove_from_queue"]))
- return update_queue_on_page()
- if(href_list["partset_to_queue"])
- add_part_set_to_queue(href_list["partset_to_queue"])
- return update_queue_on_page()
- if(href_list["process_queue"])
- spawn(0)
- if(processing_queue || being_built)
- return FALSE
- processing_queue = 1
- process_queue()
- processing_queue = 0
- if(href_list["clear_temp"])
- temp = null
- if(href_list["screen"])
- screen = href_list["screen"]
- if(href_list["queue_move"] && href_list["index"])
- var/index = text2num(href_list["index"])
- var/new_index = index + text2num(href_list["queue_move"])
- if(isnum(index) && isnum(new_index) && ISINTEGER(index) && ISINTEGER(new_index))
- if(ISINRANGE(new_index,1,queue.len))
- queue.Swap(index,new_index)
- return update_queue_on_page()
- if(href_list["clear_queue"])
- queue = list()
- return update_queue_on_page()
- if(href_list["sync"])
- sync()
- if(href_list["part_desc"])
- var/T = href_list["part_desc"]
- for(var/v in stored_research.researched_designs)
- var/datum/design/D = SSresearch.techweb_design_by_id(v)
- if(D.build_type & MECHFAB)
- if(D.id == T)
- var/obj/part = D.build_path
- temp = {"[initial(part.name)] description:
- [initial(part.desc)]
- Return
- "}
- break
+ return
+ if("del_queue_part")
+ // Delete a specific from from the queue
+ var/index = text2num(params["index"])
+ remove_from_queue(index)
+ return
+ if("clear_queue")
+ // Delete everything from queue
+ queue.Cut()
+ return
+ if("build_queue")
+ // Build everything in queue
+ if(process_queue)
+ return
+ process_queue = TRUE
- if(href_list["remove_mat"] && href_list["material"])
- var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
- var/datum/material/Mat = locate(href_list["material"])
- materials.retrieve_sheets(text2num(href_list["remove_mat"]), Mat)
+ if(!being_built)
+ START_PROCESSING(SSfastprocess, src)
+ //begin_processing()
+ return
+ if("stop_queue")
+ // Pause queue building. Also known as stop.
+ process_queue = FALSE
+ return
+ if("build_part")
+ // Build a single part
+ if(being_built || process_queue)
+ return
- updateUsrDialog()
- return
+ var/id = params["id"]
+ var/datum/design/D = SSresearch.techweb_design_by_id(id)
-/obj/machinery/mecha_part_fabricator/on_deconstruction()
- var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
- materials.retrieve_all()
- ..()
+ if(!(D.build_type & MECHFAB) || !(D.id == id))
+ return
+
+ if(build_part(D))
+ on_start_printing()
+ START_PROCESSING(SSfastprocess, src)
+ //begin_processing() teege has this as a helper proc. please port it!
+
+ return
+ if("move_queue_part")
+ // Moves a part up or down in the queue.
+ var/index = text2num(params["index"])
+ var/new_index = index + text2num(params["newindex"])
+ if(isnum(index) && isnum(new_index) && ISINTEGER(index) && ISINTEGER(new_index))
+ if(ISINRANGE(new_index,1,length(queue)))
+ queue.Swap(index,new_index)
+ return
+ if("remove_mat")
+ // Remove a material from the fab
+ var/mat_ref = params["ref"]
+ var/amount = text2num(params["amount"])
+ var/datum/material/mat = locate(mat_ref)
+ eject_sheets(mat, amount)
+ return
+
+ return FALSE
+
+/**
+ * Eject material sheets.
+ *
+ * Returns the number of sheets successfully ejected.
+ * eject_sheet - Byond REF of the material to eject.
+ * eject_amt - Number of sheets to attempt to eject.
+ */
+/obj/machinery/mecha_part_fabricator/proc/eject_sheets(eject_sheet, eject_amt)
+ var/datum/component/material_container/mat_container = rmat.mat_container
+ if (!mat_container)
+ say("No access to material storage, please contact the quartermaster.")
+ return 0
+ if (rmat.on_hold())
+ say("Mineral access is on hold, please contact the quartermaster.")
+ return 0
+ var/count = mat_container.retrieve_sheets(text2num(eject_amt), eject_sheet, drop_location())
+ var/list/matlist = list()
+ matlist[eject_sheet] = text2num(eject_amt)
+ rmat.silo_log(src, "ejected", -count, "sheets", matlist)
+ return count
/obj/machinery/mecha_part_fabricator/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted)
var/datum/material/M = id_inserted
add_overlay("fab-load-[M.name]")
addtimer(CALLBACK(src, /atom/proc/cut_overlay, "fab-load-[M.name]"), 10)
- updateUsrDialog()
-/obj/machinery/mecha_part_fabricator/attackby(obj/item/W, mob/user, params)
- if(default_deconstruction_screwdriver(user, "fab-o", "fab-idle", W))
+/obj/machinery/mecha_part_fabricator/screwdriver_act(mob/living/user, obj/item/I)
+ if(..())
return TRUE
+ if(being_built)
+ to_chat(user, "\The [src] is currently processing! Please wait until completion. ")
+ return FALSE
+ return default_deconstruction_screwdriver(user, "fab-o", "fab-idle", I)
- if(default_deconstruction_crowbar(W))
+/obj/machinery/mecha_part_fabricator/crowbar_act(mob/living/user, obj/item/I)
+ if(..())
return TRUE
-
- return ..()
+ if(being_built)
+ to_chat(user, "\The [src] is currently processing! Please wait until completion. ")
+ return FALSE
+ return default_deconstruction_crowbar(I)
/obj/machinery/mecha_part_fabricator/proc/is_insertion_ready(mob/user)
if(panel_open)
@@ -463,6 +671,9 @@
return TRUE
+/obj/machinery/mecha_part_fabricator/maint
+ link_on_init = FALSE
+
/obj/machinery/mecha_part_fabricator/offstation
- offstation_security_levels = FALSE
+ link_on_init = FALSE
circuit = /obj/item/circuitboard/machine/mechfab/offstation
diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm
index c7db051331..50d18c6fda 100644
--- a/code/game/mecha/mecha_control_console.dm
+++ b/code/game/mecha/mecha_control_console.dm
@@ -5,14 +5,11 @@
icon_keyboard = "tech_key"
req_access = list(ACCESS_ROBOTICS)
circuit = /obj/item/circuitboard/computer/mecha_control
- ui_x = 500
- ui_y = 500
-/obj/machinery/computer/mecha/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/mecha/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "ExosuitControlConsole", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "ExosuitControlConsole", name)
ui.open()
/obj/machinery/computer/mecha/ui_data(mob/user)
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index e75a18dca8..41270bb09e 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -8,7 +8,7 @@
anchored = TRUE
light_range = 3
var/movechance = 70
- var/obj/item/assembly/signaler/anomaly/aSignal
+ var/obj/item/assembly/signaler/anomaly/aSignal = /obj/item/assembly/signaler/anomaly
var/area/impact_area
var/lifespan = 990
@@ -23,8 +23,10 @@
START_PROCESSING(SSobj, src)
impact_area = get_area(src)
- aSignal = new(src)
- aSignal.name = "[name] core"
+ if (!impact_area)
+ return INITIALIZE_HINT_QDEL
+
+ aSignal = new aSignal(src)
aSignal.code = rand(1,100)
aSignal.anomaly_type = type
@@ -75,7 +77,7 @@
/obj/effect/anomaly/attackby(obj/item/I, mob/user, params)
- if(istype(I, /obj/item/analyzer))
+ if(I.tool_behaviour == TOOL_ANALYZER) //revert if runtimed
to_chat(user, "Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(aSignal.frequency)], code [aSignal.code]. ")
///////////////////////
@@ -85,6 +87,7 @@
icon_state = "shield2"
density = FALSE
var/boing = 0
+ aSignal = /obj/item/assembly/signaler/anomaly/grav
/obj/effect/anomaly/grav/anomalyEffect()
..()
@@ -95,17 +98,23 @@
for(var/mob/living/M in range(0, src))
gravShock(M)
for(var/mob/living/M in orange(4, src))
- step_towards(M,src)
+ if(!M.mob_negates_gravity()) //delete when runtimed
+ step_towards(M,src)
for(var/obj/O in range(0,src))
if(!O.anchored)
+ //if(isturf(O.loc))
+ // var/turf/T = O.loc
+ // if(T.intact && HAS_TRAIT(O, TRAIT_T_RAY_VISIBLE))
+ // continue
var/mob/living/target = locate() in view(4,src)
if(target && !target.stat)
O.throw_at(target, 5, 10)
-/obj/effect/anomaly/grav/Crossed(mob/A)
- gravShock(A)
+/obj/effect/anomaly/grav/Crossed(atom/movable/AM)
+ . = ..()
+ gravShock(AM)
-/obj/effect/anomaly/grav/Bump(mob/A)
+/obj/effect/anomaly/grav/Bump(atom/A)
gravShock(A)
/obj/effect/anomaly/grav/Bumped(atom/movable/AM)
@@ -138,6 +147,7 @@
name = "flux wave anomaly"
icon_state = "electricity2"
density = TRUE
+ aSignal = /obj/item/assembly/signaler/anomaly/flux
var/canshock = FALSE
var/shockdamage = 20
var/explosive = TRUE
@@ -148,11 +158,12 @@
for(var/mob/living/M in range(0, src))
mobShock(M)
-/obj/effect/anomaly/flux/Crossed(mob/living/M)
- mobShock(M)
+/obj/effect/anomaly/flux/Crossed(atom/movable/AM)
+ . = ..()
+ mobShock(AM)
-/obj/effect/anomaly/flux/Bump(mob/living/M)
- mobShock(M)
+/obj/effect/anomaly/flux/Bump(atom/A)
+ mobShock(A)
/obj/effect/anomaly/flux/Bumped(atom/movable/AM)
mobShock(AM)
@@ -160,7 +171,7 @@
/obj/effect/anomaly/flux/proc/mobShock(mob/living/M)
if(canshock && istype(M))
canshock = FALSE //Just so you don't instakill yourself if you slam into the anomaly five times in a second.
- M.electrocute_act(shockdamage, "[name]", flags = SHOCK_NOGLOVES)
+ M.electrocute_act(shockdamage, name, flags = SHOCK_NOGLOVES)
/obj/effect/anomaly/flux/detonate()
if(explosive)
@@ -176,6 +187,7 @@
icon = 'icons/obj/projectiles.dmi'
icon_state = "bluespace"
density = TRUE
+ aSignal = /obj/item/assembly/signaler/anomaly/bluespace
/obj/effect/anomaly/bluespace/anomalyEffect()
..()
@@ -187,7 +199,7 @@
do_teleport(AM, locate(AM.x, AM.y, AM.z), 8, channel = TELEPORT_CHANNEL_BLUESPACE)
/obj/effect/anomaly/bluespace/detonate()
- var/turf/T = safepick(get_area_turfs(impact_area))
+ var/turf/T = pick(get_area_turfs(impact_area))
if(T)
// Calculate new position (searches through beacons in world)
var/obj/item/beacon/chosen
@@ -224,21 +236,23 @@
if(!A.Move(newloc) && newloc) // if the atom, for some reason, can't move, FORCE them to move! :) We try Move() first to invoke any movement-related checks the atom needs to perform after moving
A.forceMove(newloc)
- spawn()
- if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect
- var/mob/M = A
- if(M.client)
- var/obj/blueeffect = new /obj(src)
- blueeffect.screen_loc = "WEST,SOUTH to EAST,NORTH"
- blueeffect.icon = 'icons/effects/effects.dmi'
- blueeffect.icon_state = "shieldsparkles"
- blueeffect.layer = FLASH_LAYER
- blueeffect.plane = FULLSCREEN_PLANE
- blueeffect.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- M.client.screen += blueeffect
- sleep(20)
- M.client.screen -= blueeffect
- qdel(blueeffect)
+ if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect
+ var/mob/M = A
+ if(M.client)
+ INVOKE_ASYNC(src, .proc/blue_effect, M)
+
+/obj/effect/anomaly/bluespace/proc/blue_effect(mob/M)
+ var/obj/blueeffect = new /obj(src)
+ blueeffect.screen_loc = "WEST,SOUTH to EAST,NORTH"
+ blueeffect.icon = 'icons/effects/effects.dmi'
+ blueeffect.icon_state = "shieldsparkles"
+ blueeffect.layer = FLASH_LAYER
+ blueeffect.plane = FULLSCREEN_PLANE
+ blueeffect.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ M.client.screen += blueeffect
+ sleep(20)
+ M.client.screen -= blueeffect
+ qdel(blueeffect)
/////////////////////
@@ -246,6 +260,7 @@
name = "pyroclastic anomaly"
icon_state = "mustard"
var/ticks = 0
+ aSignal = /obj/item/assembly/signaler/anomaly/pyro
/obj/effect/anomaly/pyro/anomalyEffect()
..()
@@ -278,6 +293,7 @@
name = "vortex anomaly"
icon_state = "bhole3"
desc = "That's a nice station you have there. It'd be a shame if something happened to it."
+ aSignal = /obj/item/assembly/signaler/anomaly/vortex
/obj/effect/anomaly/bhole/anomalyEffect()
..()
diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index 0b227be247..a7b5091522 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -1,10 +1,10 @@
-/proc/create_portal_pair(turf/source, turf/destination, _creator = null, _lifespan = 300, accuracy = 0, newtype = /obj/effect/portal, atmos_link_override)
+/proc/create_portal_pair(turf/source, turf/destination, _lifespan = 300, accuracy = 0, newtype = /obj/effect/portal, atmos_link_override)
if(!istype(source) || !istype(destination))
return
var/turf/actual_destination = get_teleport_turf(destination, accuracy)
- var/obj/effect/portal/P1 = new newtype(source, _creator, _lifespan, null, FALSE, null, atmos_link_override)
- var/obj/effect/portal/P2 = new newtype(actual_destination, _creator, _lifespan, P1, TRUE, null, atmos_link_override)
+ var/obj/effect/portal/P1 = new newtype(source, _lifespan, null, FALSE, null, atmos_link_override)
+ var/obj/effect/portal/P2 = new newtype(actual_destination, _lifespan, P1, TRUE, null, atmos_link_override)
if(!istype(P1)||!istype(P2))
return
P1.link_portal(P2)
@@ -21,7 +21,6 @@
var/obj/effect/portal/linked
var/hardlinked = TRUE //Requires a linked portal at all times. Destroy if there's no linked portal, if there is destroy it when this one is deleted.
var/teleport_channel = TELEPORT_CHANNEL_BLUESPACE
- var/creator
var/turf/hard_target //For when a portal needs a hard target and isn't to be linked.
var/atmos_link = FALSE //Link source/destination atmos.
var/turf/open/atmos_source //Atmos link source
@@ -35,6 +34,7 @@
name = "wormhole"
icon = 'icons/obj/objects.dmi'
icon_state = "anom"
+ layer = RIPPLE_LAYER
mech_sized = TRUE
teleport_channel = TELEPORT_CHANNEL_WORMHOLE
@@ -49,7 +49,9 @@
user.forceMove(get_turf(src))
return TRUE
-/obj/effect/portal/Crossed(atom/movable/AM, oldloc)
+/obj/effect/portal/Crossed(atom/movable/AM, oldloc, force_stop = 0)
+ if(force_stop)
+ return ..()
if(isobserver(AM))
return ..()
if(linked && (get_turf(oldloc) == get_turf(linked)))
@@ -66,7 +68,7 @@
if(Adjacent(user))
user.forceMove(get_turf(src))
-/obj/effect/portal/Initialize(mapload, _creator, _lifespan = 0, obj/effect/portal/_linked, automatic_link = FALSE, turf/hard_target_override, atmos_link_override)
+/obj/effect/portal/Initialize(mapload, _lifespan = 0, obj/effect/portal/_linked, automatic_link = FALSE, turf/hard_target_override, atmos_link_override)
. = ..()
GLOB.portals += src
if(!istype(_linked) && automatic_link)
@@ -78,7 +80,6 @@
atmos_link = atmos_link_override
link_portal(_linked)
hardlinked = automatic_link
- creator = _creator
if(isturf(hard_target_override))
hard_target = hard_target_override
@@ -129,10 +130,7 @@
LAZYREMOVE(atmos_destination.atmos_adjacent_turfs, atmos_source)
atmos_destination = null
-/obj/effect/portal/Destroy() //Calls on_portal_destroy(destroyed portal, location of destroyed portal) on creator if creator has such call.
- if(creator && hascall(creator, "on_portal_destroy"))
- call(creator, "on_portal_destroy")(src, src.loc)
- creator = null
+/obj/effect/portal/Destroy()
GLOB.portals -= src
unlink_atmos()
if(hardlinked && !QDELETED(linked))
@@ -153,8 +151,6 @@
return FALSE
if(!force && (!ismecha(M) && !istype(M, /obj/item/projectile) && M.anchored && !allow_anchored))
return
- if(ismegafauna(M))
- message_admins("[M] has used a portal at [ADMIN_VERBOSEJMP(src)] made by [usr].")
var/no_effect = FALSE
if(last_effect == world.time)
no_effect = TRUE
diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm
index 93d73b2b5f..b3d5ed431a 100644
--- a/code/game/objects/items/RPD.dm
+++ b/code/game/objects/items/RPD.dm
@@ -238,24 +238,22 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
playsound(get_turf(user), 'sound/items/deconstruct.ogg', 50, 1)
return(BRUTELOSS)
-/obj/item/pipe_dispenser/ui_base_html(html)
- var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/pipes)
- . = replacetext(html, "", assets.css_tag())
+/obj/item/pipe_dispenser/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/pipes),
+ )
-/obj/item/pipe_dispenser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/pipe_dispenser/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/pipes)
- assets.send(user)
-
- ui = new(user, src, ui_key, "RapidPipeDispenser", name, 425, 515, master_ui, state)
+ ui = new(user, src, "RapidPipeDispenser", name)
ui.open()
/obj/item/pipe_dispenser/ui_data(mob/user)
var/list/data = list(
"category" = category,
"piping_layer" = piping_layer,
+ // "ducting_layer" = ducting_layer, //uhh is this for chem thing?
"preview_rows" = recipe.get_preview(p_dir),
"categories" = list(),
"selected_color" = paint_color,
@@ -284,7 +282,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
/obj/item/pipe_dispenser/ui_act(action, params)
if(..())
return
- if(!usr.canUseTopic(src))
+ if(!usr.canUseTopic(src, BE_CLOSE))
return
var/playeffect = TRUE
switch(action)
@@ -304,6 +302,9 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
if("piping_layer")
piping_layer = text2num(params["piping_layer"])
playeffect = FALSE
+ // if("ducting_layer")
+ // ducting_layer = text2num(params["ducting_layer"])
+ // playeffect = FALSE
if("pipe_type")
var/static/list/recipes
if(!recipes)
@@ -320,11 +321,9 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
mode &= ~n
else
mode |= n
-
- if(playeffect && world.time >= effectcooldown)
+ if(playeffect)
spark_system.start()
- effectcooldown = world.time + 100
- playsound(get_turf(src), 'sound/effects/pop.ogg', 50, 0)
+ playsound(get_turf(src), 'sound/effects/pop.ogg', 50, FALSE)
return TRUE
/obj/item/pipe_dispenser/pre_attack(atom/A, mob/user)
diff --git a/code/game/objects/items/airlock_painter.dm b/code/game/objects/items/airlock_painter.dm
index 4f3761f899..b28de437ed 100644
--- a/code/game/objects/items/airlock_painter.dm
+++ b/code/game/objects/items/airlock_painter.dm
@@ -180,10 +180,10 @@
stored_decal_total = "[stored_decal][yellow_fix][stored_color]"
return
-/obj/item/airlock_painter/decal/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/airlock_painter/decal/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "DecalPainter", name, 500, 400, master_ui, state)
+ ui = new(user, src, "DecalPainter", name)
ui.open()
/obj/item/airlock_painter/decal/ui_data(mob/user)
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index a366a2ef88..7f8b720509 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -147,23 +147,23 @@
to_chat(user, "There is not enough of [src] left! ")
. = TRUE
-/obj/item/toy/crayon/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- // tgui is a plague upon this codebase
+/obj/item/toy/crayon/ui_state(mob/user)
+ return GLOB.hands_state
- SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/toy/crayon/ui_interact(mob/user, datum/tgui/ui)
+ // tgui is a plague upon this codebase
+ // no u
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Crayon", name, 600, 600,
- master_ui, state)
+ ui = new(user, src, "Crayon", name)
ui.open()
/obj/item/toy/crayon/spraycan/AltClick(mob/user)
- . = ..()
if(user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
if(has_cap)
is_capped = !is_capped
to_chat(user, "The cap on [src] is now [is_capped ? "on" : "off"]. ")
update_icon()
- return TRUE
/obj/item/toy/crayon/CtrlClick(mob/user)
if(can_change_colour && !isturf(loc) && user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm
index bfb9348d97..503e2a9473 100644
--- a/code/game/objects/items/devices/aicard.dm
+++ b/code/game/objects/items/devices/aicard.dm
@@ -54,18 +54,20 @@
name = initial(name)
icon_state = initial(icon_state)
-/obj/item/aicard/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/aicard/ui_state(mob/user)
+ return GLOB.hands_state
+
+/obj/item/aicard/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Intellicard", name, 500, 500, master_ui, state)
+ ui = new(user, src, "Intellicard", name)
ui.open()
/obj/item/aicard/ui_data()
var/list/data = list()
if(AI)
data["name"] = AI.name
- data["laws"] = AI.laws.get_law_list(include_zeroth = 1)
+ data["laws"] = AI.laws.get_law_list(include_zeroth = TRUE, render_html = FALSE)
data["health"] = (AI.health + 100) / 2
data["wireless"] = !AI.control_disabled //todo disabled->enabled
data["radio"] = AI.radio_enabled
@@ -86,19 +88,19 @@
if(confirm == "Yes" && !..())
flush = TRUE
if(AI && AI.loc == src)
- to_chat(AI, "Your core files are being wiped!")
+ to_chat(AI, "Your core files are being wiped! ")
while(AI.stat != DEAD && flush)
- AI.adjustOxyLoss(1)
+ AI.adjustOxyLoss(5)
AI.updatehealth()
sleep(5)
flush = FALSE
. = TRUE
if("wireless")
AI.control_disabled = !AI.control_disabled
- to_chat(AI, "[src]'s wireless port has been [AI.control_disabled ? "disabled" : "enabled"]!")
+ to_chat(AI, "[src]'s wireless port has been [AI.control_disabled ? "disabled" : "enabled"]! ")
. = TRUE
if("radio")
AI.radio_enabled = !AI.radio_enabled
- to_chat(AI, "Your Subspace Transceiver has been [AI.radio_enabled ? "enabled" : "disabled"]!")
+ to_chat(AI, "Your Subspace Transceiver has been [AI.radio_enabled ? "enabled" : "disabled"]! ")
. = TRUE
update_icon()
diff --git a/code/game/objects/items/devices/dogborg_sleeper.dm b/code/game/objects/items/devices/dogborg_sleeper.dm
index d8abccb336..1f91196b24 100644
--- a/code/game/objects/items/devices/dogborg_sleeper.dm
+++ b/code/game/objects/items/devices/dogborg_sleeper.dm
@@ -163,12 +163,13 @@
return
ui_interact(user)
-/obj/item/dogborg/sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
+/obj/item/dogborg/sleeper/ui_state(mob/user)
+ return GLOB.notcontained_state
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/dogborg/sleeper/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "dogborg_sleeper", name, 375, 550, master_ui, state)
+ ui = new(user, src, "dogborg_sleeper", name, 375, 550) //UI DOES NOT EXIST
ui.open()
/obj/item/dogborg/sleeper/ui_data()
diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm
index ee26d81a0d..bb46577702 100644
--- a/code/game/objects/items/devices/gps.dm
+++ b/code/game/objects/items/devices/gps.dm
@@ -1,4 +1,4 @@
-GLOBAL_LIST_EMPTY(GPS_list)
+
/obj/item/gps
name = "global positioning system"
desc = "Helping lost spacemen find their way through the planets since 2016."
@@ -8,138 +8,10 @@ GLOBAL_LIST_EMPTY(GPS_list)
slot_flags = ITEM_SLOT_BELT
obj_flags = UNIQUE_RENAME
var/gpstag = "COM0"
- var/emped = FALSE
- var/tracking = TRUE
- var/updating = TRUE //Automatic updating of GPS list. Can be set to manual by user.
- var/global_mode = TRUE //If disabled, only GPS signals of the same Z level are shown
-
-/obj/item/gps/examine(mob/user)
- . = ..()
- var/turf/curr = get_turf(src)
- . += "The screen says: [get_area_name(curr, TRUE)] ([curr.x], [curr.y], [curr.z])"
- . += "Alt-click to switch it [tracking ? "off":"on"]. "
/obj/item/gps/Initialize()
. = ..()
- GLOB.GPS_list += src
- name = "global positioning system ([gpstag])"
- add_overlay("working")
-
-/obj/item/gps/Destroy()
- GLOB.GPS_list -= src
- return ..()
-
-/obj/item/gps/emp_act(severity)
- . = ..()
- if (. & EMP_PROTECT_SELF)
- return
- emped = TRUE
- cut_overlay("working")
- add_overlay("emp")
- addtimer(CALLBACK(src, .proc/reboot), 300, TIMER_UNIQUE|TIMER_OVERRIDE) //if a new EMP happens, remove the old timer so it doesn't reactivate early
- SStgui.close_uis(src) //Close the UI control if it is open.
-
-/obj/item/gps/proc/reboot()
- emped = FALSE
- cut_overlay("emp")
- add_overlay("working")
-
-/obj/item/gps/AltClick(mob/user)
- . = ..()
- if(!user.canUseTopic(src, BE_CLOSE))
- return
- toggletracking(user)
- return TRUE
-
-/obj/item/gps/proc/toggletracking(mob/user)
- if(!user.canUseTopic(src, BE_CLOSE))
- return //user not valid to use gps
- if(emped)
- to_chat(user, "It's busted!")
- return
- if(tracking)
- cut_overlay("working")
- to_chat(user, "[src] is no longer tracking, or visible to other GPS devices.")
- tracking = FALSE
- else
- add_overlay("working")
- to_chat(user, "[src] is now tracking, and visible to other GPS devices.")
- tracking = TRUE
-
-
-/obj/item/gps/ui_interact(mob/user, ui_key = "gps", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
- if(emped)
- to_chat(user, "[src] fizzles weakly.")
- return
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- // Variable window height, depending on how many GPS units there are
- // to show, clamped to relatively safe range.
- var/gps_window_height = clamp(325 + GLOB.GPS_list.len * 14, 325, 700)
- ui = new(user, src, ui_key, "Gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height
- ui.open()
-
- ui.set_autoupdate(state = updating)
-
-
-/obj/item/gps/ui_data(mob/user)
- var/list/data = list()
- data["power"] = tracking
- data["tag"] = gpstag
- data["updating"] = updating
- data["globalmode"] = global_mode
- if(!tracking || emped) //Do not bother scanning if the GPS is off or EMPed
- return data
-
- var/turf/curr = get_turf(src)
- data["current"] = "[get_area_name(curr, TRUE)] ([curr.x], [curr.y], [curr.z])"
- data["currentArea"] = "[get_area_name(curr, TRUE)]"
- data["currentCoords"] = "[curr.x], [curr.y], [curr.z]"
-
- var/list/signals = list()
- data["signals"] = list()
-
- for(var/gps in GLOB.GPS_list)
- var/obj/item/gps/G = gps
- if(G.emped || !G.tracking || G == src)
- continue
- var/turf/pos = get_turf(G)
- if(!global_mode && pos.z != curr.z)
- continue
- var/list/signal = list()
- signal["entrytag"] = G.gpstag //Name or 'tag' of the GPS
- signal["coords"] = "[pos.x], [pos.y], [pos.z]"
- if(pos.z == curr.z) //Distance/Direction calculations for same z-level only
- signal["dist"] = max(get_dist(curr, pos), 0) //Distance between the src and remote GPS turfs
- signal["degrees"] = round(Get_Angle(curr, pos)) //0-360 degree directional bearing, for more precision.
-
- signals += list(signal) //Add this signal to the list of signals
- data["signals"] = signals
- return data
-
-
-
-/obj/item/gps/ui_act(action, params)
- if(..())
- return
- switch(action)
- if("rename")
- var/a = input("Please enter desired tag.", name, gpstag) as text
- a = copytext(sanitize(a), 1, 20)
- gpstag = a
- . = TRUE
- name = "global positioning system ([gpstag])"
-
- if("power")
- toggletracking(usr)
- . = TRUE
- if("updating")
- updating = !updating
- . = TRUE
- if("globalmode")
- global_mode = !global_mode
- . = TRUE
-
+ AddComponent(/datum/component/gps/item, gpstag)
/obj/item/gps/science
icon_state = "gps-s"
diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm
index 0cf7ceb55b..e65325587c 100644
--- a/code/game/objects/items/devices/radio/electropack.dm
+++ b/code/game/objects/items/devices/radio/electropack.dm
@@ -29,7 +29,7 @@
/obj/item/electropack/Destroy()
SSradio.remove_object(src, frequency)
- . = ..()
+ return ..()
/obj/item/electropack/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(iscarbon(user))
@@ -39,7 +39,7 @@
return
return ..()
-/obj/item/electropack/attackby(obj/item/W, mob/living/user, params)
+/obj/item/electropack/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/clothing/head/helmet))
var/obj/item/assembly/shock_kit/A = new /obj/item/assembly/shock_kit(user)
A.icon = 'icons/obj/assemblies.dmi'
@@ -59,56 +59,12 @@
else
return ..()
-/obj/item/electropack/Topic(href, href_list)
- var/mob/living/carbon/C = usr
- if(usr.stat || usr.restrained() || C.back == src)
- return
-
- if(!usr.canUseTopic(src, BE_CLOSE))
- usr << browse(null, "window=radio")
- onclose(usr, "radio")
- return
-
- if(href_list["set"])
- if(href_list["set"] == "freq")
- var/new_freq = input(usr, "Input a new receiving frequency", "Electropack Frequency", format_frequency(frequency)) as num|null
- if(!usr.canUseTopic(src, BE_CLOSE))
- return
- new_freq = unformat_frequency(new_freq)
- new_freq = sanitize_frequency(new_freq, TRUE)
- set_frequency(new_freq)
-
- if(href_list["set"] == "code")
- var/new_code = input(usr, "Input a new receiving code", "Electropack Code", code) as num|null
- if(!usr.canUseTopic(src, BE_CLOSE))
- return
- new_code = round(new_code)
- new_code = clamp(new_code, 1, 100)
- code = new_code
-
- if(href_list["set"] == "power")
- if(!usr.canUseTopic(src, BE_CLOSE))
- return
- on = !(on)
- icon_state = "electropack[on]"
-
- if(usr)
- attack_self(usr)
-
- return
-
-/obj/item/electropack/proc/set_frequency(new_frequency)
- SSradio.remove_object(src, frequency)
- frequency = new_frequency
- SSradio.add_object(src, frequency, RADIO_SIGNALER)
- return
-
/obj/item/electropack/receive_signal(datum/signal/signal)
if(!signal || signal.data["code"] != code)
return
if(isliving(loc) && on)
- if(shock_cooldown == TRUE)
+ if(shock_cooldown)
return
shock_cooldown = TRUE
addtimer(VARSET_CALLBACK(src, shock_cooldown, FALSE), 100)
@@ -124,18 +80,59 @@
if(master)
master.receive_signal()
- return
-/obj/item/electropack/ui_interact(mob/user)
- if(!ishuman(user))
+/obj/item/electropack/proc/set_frequency(new_frequency)
+ SSradio.remove_object(src, frequency)
+ frequency = new_frequency
+ SSradio.add_object(src, frequency, RADIO_SIGNALER)
+
+/obj/item/electropack/ui_state(mob/user)
+ return GLOB.hands_state
+
+/obj/item/electropack/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Electropack", name)
+ ui.open()
+
+/obj/item/electropack/ui_data(mob/user)
+ var/list/data = list()
+ data["power"] = on
+ data["frequency"] = frequency
+ data["code"] = code
+ data["minFrequency"] = MIN_FREE_FREQ
+ data["maxFrequency"] = MAX_FREE_FREQ
+ return data
+
+/obj/item/electropack/ui_act(action, params)
+ if(..())
return
-/obj/item/electropack/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "Electropack", name, ui_x, ui_y, master_ui, state)
- ui.open()
+ switch(action)
+ if("power")
+ on = !on
+ icon_state = "electropack[on]"
+ . = TRUE
+ if("freq")
+ var/value = unformat_frequency(params["freq"])
+ if(value)
+ frequency = sanitize_frequency(value, TRUE)
+ set_frequency(frequency)
+ . = TRUE
+ if("code")
+ var/value = text2num(params["code"])
+ if(value)
+ value = round(value)
+ code = clamp(value, 1, 100)
+ . = TRUE
+ if("reset")
+ if(params["reset"] == "freq")
+ frequency = initial(frequency)
+ . = TRUE
+ else if(params["reset"] == "code")
+ code = initial(code)
+ . = TRUE
+
/obj/item/electropack/shockcollar
name = "shock collar"
@@ -145,7 +142,7 @@
icon_state = "shockcollar"
item_state = "shockcollar"
body_parts_covered = NECK
- slot_flags = ITEM_SLOT_NECK | ITEM_SLOT_DENYPOCKET //no more pocket shockers
+ slot_flags = ITEM_SLOT_NECK //no more pocket shockers. Now done without lazyness
w_class = WEIGHT_CLASS_SMALL
strip_delay = 60
equip_delay_other = 60
@@ -167,16 +164,18 @@
return
return ..()
-/obj/item/electropack/shockcollar/receive_signal(datum/signal/signal)
+/obj/item/electropack/shockcollar/receive_signal(datum/signal/signal) //we have to override this because of text
if(!signal || signal.data["code"] != code)
return
- if(isliving(loc) && on)
+ if(isliving(loc) && on) //the "on" arg is currently useless
+ var/mob/living/L = loc
+ if(!L.get_item_by_slot(SLOT_NECK)) //**properly** stops pocket shockers
+ return
if(shock_cooldown == TRUE)
return
shock_cooldown = TRUE
addtimer(VARSET_CALLBACK(src, shock_cooldown, FALSE), 100)
- var/mob/living/L = loc
step(L, pick(GLOB.cardinals))
to_chat(L, "You feel a sharp shock from the collar! ")
@@ -196,10 +195,13 @@
if(t)
tagname = t
name = "[initial(name)] - [t]"
+ return
+ if(istype(W, /obj/item/clothing/head/helmet)) //lazy method of denying this
+ return
else
return ..()
-/obj/item/electropack/shockcollar/ui_interact(mob/user) //on_click calls this
+/obj/item/electropack/shockcollar/ui_interact(mob/user) //note to src: use tgooey
var/dat = {"
Frequency/Code for shock collar:
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index e26b29289a..4623a405de 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -107,19 +107,15 @@
else
..()
-/obj/item/radio/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
- . = ..()
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/radio/ui_state(mob/user)
+ return GLOB.inventory_state
+
+/obj/item/radio/ui_interact(mob/user, datum/tgui/ui, datum/ui_state/state)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- var/ui_width = 360
- var/ui_height = 106
- if(subspace_transmission)
- if(channels.len > 0)
- ui_height += 6 + channels.len * 21
- else
- ui_height += 24
- ui = new(user, src, ui_key, "Radio", name, ui_width, ui_height, master_ui, state)
+ ui = new(user, src, "Radio", name)
+ if(state)
+ ui.set_state(state)
ui.open()
/obj/item/radio/ui_data(mob/user)
diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index 4d255fd339..eb444cdb12 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -86,98 +86,102 @@ effective or pretty fucking useless.
return
if(!used)
log_combat(user, M, "irradiated", src)
- var/cooldown = GetCooldown()
- used = 1
+ var/cooldown = get_cooldown()
+ used = TRUE
icon_state = "health1"
- handle_cooldown(cooldown) // splits off to handle the cooldown while handling wavelength
+ addtimer(VARSET_CALLBACK(src, used, FALSE), cooldown)
+ addtimer(VARSET_CALLBACK(src, icon_state, "health"), cooldown)
to_chat(user, "Successfully irradiated [M]. ")
- spawn((wavelength+(intensity*4))*5)
- if(M)
- if(intensity >= 5)
- M.apply_effect(round(intensity/0.075), EFFECT_UNCONSCIOUS)
- M.rad_act(intensity*10)
+ addtimer(CALLBACK(src, .proc/radiation_aftereffect, M), (wavelength+(intensity*4))*5)
else
to_chat(user, "The radioactive microlaser is still recharging. ")
-/obj/item/healthanalyzer/rad_laser/proc/handle_cooldown(cooldown)
- spawn(cooldown)
- used = 0
- icon_state = "health"
+/obj/item/healthanalyzer/rad_laser/proc/radiation_aftereffect(mob/living/M)
+ if(QDELETED(M))
+ return
+ if(intensity >= 5)
+ M.apply_effect(round(intensity/0.075), EFFECT_UNCONSCIOUS)
+ M.rad_act(intensity*10)
+
+/obj/item/healthanalyzer/rad_laser/proc/get_cooldown()
+ return round(max(10, (stealth*30 + intensity*5 - wavelength/4)))
/obj/item/healthanalyzer/rad_laser/attack_self(mob/user)
interact(user)
-/obj/item/healthanalyzer/rad_laser/proc/GetCooldown()
- return round(max(10, (stealth*30 + intensity*5 - wavelength/4)))
-
/obj/item/healthanalyzer/rad_laser/interact(mob/user)
ui_interact(user)
-/obj/item/healthanalyzer/rad_laser/ui_interact(mob/user)
-/obj/item/healthanalyzer/rad_laser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/healthanalyzer/rad_laser/ui_state(mob/user)
+ return GLOB.hands_state
+
+/obj/item/healthanalyzer/rad_laser/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "RadioactiveMicrolaser", "Radioactive Microlaser", ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "RadioactiveMicrolaser")
ui.open()
- var/dat = "Irradiation: [irradiate ? "On" : "Off"] "
- dat += "Stealth Mode (NOTE: Deactivates automatically while Irradiation is off): [stealth ? "On" : "Off"] "
- dat += "Scan Mode: "
- if(!scanmode)
- dat += "Scan Health"
- else if(scanmode == 1)
- dat += "Scan Reagents"
- else
- dat += "Disabled"
- dat += " "
+/obj/item/healthanalyzer/rad_laser/ui_data(mob/user)
+ var/list/data = list()
+ data["irradiate"] = irradiate
+ data["stealth"] = stealth
+ data["scanmode"] = scanmode
+ data["intensity"] = intensity
+ data["wavelength"] = wavelength
+ data["on_cooldown"] = used
+ data["cooldown"] = DisplayTimeText(get_cooldown())
+ return data
- dat += {"
- Radiation Intensity:
- - -
- [intensity]
- + +
+/obj/item/healthanalyzer/rad_laser/ui_act(action, params)
+ if(..())
+ return
- Radiation Wavelength:
- - -
- [(wavelength+(intensity*4))]
- + +
- Laser Cooldown: [DisplayTimeText(GetCooldown())]
- "}
-
- var/datum/browser/popup = new(user, "radlaser", "Radioactive Microlaser Interface", 400, 240)
- popup.set_content(dat)
- popup.open()
-
-/obj/item/healthanalyzer/rad_laser/Topic(href, href_list)
- if(!usr.canUseTopic(src))
- return 1
-
- usr.set_machine(src)
- if(href_list["rad"])
- irradiate = !irradiate
-
- else if(href_list["stealthy"])
- stealth = !stealth
-
- else if(href_list["mode"])
- scanmode += 1
- if(scanmode > 2)
- scanmode = 0
-
- else if(href_list["radint"])
- var/amount = text2num(href_list["radint"])
- amount += intensity
- intensity = max(1,(min(20,amount)))
-
- else if(href_list["radwav"])
- var/amount = text2num(href_list["radwav"])
- amount += wavelength
- wavelength = max(0,(min(120,amount)))
-
- attack_self(usr)
- add_fingerprint(usr)
- return
+ switch(action)
+ if("irradiate")
+ irradiate = !irradiate
+ . = TRUE
+ if("stealth")
+ stealth = !stealth
+ . = TRUE
+ if("scanmode")
+ scanmode = !scanmode
+ . = TRUE
+ if("radintensity")
+ var/target = params["target"]
+ var/adjust = text2num(params["adjust"])
+ if(target == "min")
+ target = 1
+ . = TRUE
+ else if(target == "max")
+ target = 20
+ . = TRUE
+ else if(adjust)
+ target = intensity + adjust
+ . = TRUE
+ else if(text2num(target) != null)
+ target = text2num(target)
+ . = TRUE
+ if(.)
+ target = round(target)
+ intensity = clamp(target, 1, 20)
+ if("radwavelength")
+ var/target = params["target"]
+ var/adjust = text2num(params["adjust"])
+ if(target == "min")
+ target = 0
+ . = TRUE
+ else if(target == "max")
+ target = 120
+ . = TRUE
+ else if(adjust)
+ target = wavelength + adjust
+ . = TRUE
+ else if(text2num(target) != null)
+ target = text2num(target)
+ . = TRUE
+ if(.)
+ target = round(target)
+ wavelength = clamp(target, 0, 120)
/obj/item/shadowcloak
name = "cloaker belt"
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index c791b7e323..32d9c02a27 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -188,11 +188,10 @@
tank_one.air_contents.merge(temp)
tank_two.air_contents.set_volume(tank_two.air_contents.return_volume() - tank_one.air_contents.return_volume())
- /*
+/*
Exadv1: I know this isn't how it's going to work, but this was just to check
it explodes properly when it gets a signal (and it does).
- */
-
+*/
/obj/item/transfer_valve/proc/toggle_valve()
if(!valve_open && tank_one && tank_two)
valve_open = TRUE
@@ -233,16 +232,20 @@
valve_open = FALSE
update_icon()
-// this doesn't do anything but the timer etc. expects it to be here
-// eventually maybe have it update icon to show state (timer, prox etc.) like old bombs
+/*
+ This doesn't do anything but the timer etc. expects it to be here
+ eventually maybe have it update icon to show state (timer, prox etc.) like old bombs
+*/
/obj/item/transfer_valve/proc/c_state()
return
-/obj/item/transfer_valve/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/transfer_valve/ui_state(mob/user)
+ return GLOB.hands_state
+
+/obj/item/transfer_valve/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "TransferValve", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "TransferValve", name)
ui.open()
/obj/item/transfer_valve/ui_data(mob/user)
diff --git a/code/game/objects/items/eightball.dm b/code/game/objects/items/eightball.dm
index dbc4c297ca..0d5c9a22aa 100644
--- a/code/game/objects/items/eightball.dm
+++ b/code/game/objects/items/eightball.dm
@@ -192,11 +192,13 @@
return top_vote
-/obj/item/toy/eightball/haunted/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state = GLOB.always_state)
+/obj/item/toy/eightball/haunted/ui_state(mob/user)
+ return GLOB.observer_state
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/toy/eightball/haunted/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "EightBallVote", name, 400, 600, master_ui, state)
+ ui = new(user, src, "EightBallVote", name)
ui.open()
/obj/item/toy/eightball/haunted/ui_data(mob/user)
diff --git a/code/game/objects/items/implants/implant_radio.dm b/code/game/objects/items/implants/implant_radio.dm
index 5d3d579a4e..6f42547004 100644
--- a/code/game/objects/items/implants/implant_radio.dm
+++ b/code/game/objects/items/implants/implant_radio.dm
@@ -10,7 +10,7 @@
/obj/item/implant/radio/activate()
. = ..()
// needs to be GLOB.deep_inventory_state otherwise it won't open
- radio.ui_interact(usr, "main", null, FALSE, null, GLOB.deep_inventory_state)
+ radio.ui_interact(usr, state = GLOB.deep_inventory_state)
/obj/item/implant/radio/implant(mob/living/target, mob/user, silent = FALSE)
. = ..()
diff --git a/code/game/objects/items/implants/implantchair.dm b/code/game/objects/items/implants/implantchair.dm
index b3c1f79e94..7b05cf302e 100644
--- a/code/game/objects/items/implants/implantchair.dm
+++ b/code/game/objects/items/implants/implantchair.dm
@@ -26,14 +26,15 @@
open_machine()
update_icon()
+/obj/machinery/implantchair/ui_state(mob/user)
+ return GLOB.notcontained_state
-/obj/machinery/implantchair/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/implantchair/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "implantchair", name, 375, 280, master_ui, state)
+ ui = new(user, src, "ImplantChair", name)
ui.open()
-
/obj/machinery/implantchair/ui_data()
var/list/data = list()
data["occupied"] = occupant ? 1 : 0
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index c50dc6e316..80fd177d84 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -6,21 +6,24 @@
desc = "Protected by FRM."
icon = 'icons/obj/module.dmi'
icon_state = "cyborg_upgrade"
+ w_class = WEIGHT_CLASS_SMALL
var/locked = FALSE
var/installed = 0
var/require_module = 0
var/list/module_type
+ /// Bitflags listing module compatibility. Used in the exosuit fabricator for creating sub-categories.
+ var/module_flags = NONE
// if true, is not stored in the robot to be ejected
// if module is reset
var/one_use = FALSE
/obj/item/borg/upgrade/proc/action(mob/living/silicon/robot/R, user = usr)
if(R.stat == DEAD)
- to_chat(user, "[src] will not function on a deceased cyborg. ")
+ to_chat(user, "[src] will not function on a deceased cyborg. ")
return FALSE
if(module_type && !is_type_in_list(R.module, module_type))
- to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!")
- to_chat(user, "There's no mounting point for the module!")
+ to_chat(R, "Upgrade mounting error! No suitable hardpoint detected. ")
+ to_chat(user, "There's no mounting point for the module! ")
return FALSE
return TRUE
@@ -37,7 +40,7 @@
one_use = TRUE
/obj/item/borg/upgrade/rename/attack_self(mob/user)
- heldname = stripped_input(user, "Enter new robot name", "Cyborg Reclassification", heldname, MAX_NAME_LEN)
+ heldname = sanitize_name(stripped_input(user, "Enter new robot name", "Cyborg Reclassification", heldname, MAX_NAME_LEN))
/obj/item/borg/upgrade/rename/action(mob/living/silicon/robot/R)
. = ..()
@@ -95,6 +98,7 @@
desc = "Used to cool a mounted energy-based firearm, increasing the potential current in it and thus its recharge rate."
icon_state = "cyborg_upgrade3"
require_module = 1
+ module_flags = BORG_MODULE_SECURITY
/obj/item/borg/upgrade/disablercooler/action(mob/living/silicon/robot/R, user = usr)
. = ..()
@@ -147,6 +151,7 @@
icon_state = "cyborg_upgrade3"
require_module = 1
module_type = list(/obj/item/robot_module/miner)
+ module_flags = BORG_MODULE_MINER
/obj/item/borg/upgrade/ddrill/action(mob/living/silicon/robot/R, user = usr)
. = ..()
@@ -207,6 +212,7 @@
icon_state = "cyborg_upgrade3"
require_module = 1
module_type = list(/obj/item/robot_module/butler)
+ module_flags = BORG_MODULE_JANITOR
/obj/item/borg/upgrade/tboh/action(mob/living/silicon/robot/R)
. = ..()
@@ -234,6 +240,7 @@
icon_state = "cyborg_upgrade3"
require_module = 1
module_type = list(/obj/item/robot_module/butler)
+ module_flags = BORG_MODULE_JANITOR
/obj/item/borg/upgrade/amop/action(mob/living/silicon/robot/R)
. = ..()
@@ -283,6 +290,7 @@
resistance_flags = LAVA_PROOF | FIRE_PROOF
require_module = 1
module_type = list(/obj/item/robot_module/miner)
+ module_flags = BORG_MODULE_MINER
/obj/item/borg/upgrade/lavaproof/action(mob/living/silicon/robot/R, user = usr)
. = ..()
@@ -402,6 +410,7 @@
module_type = list(/obj/item/robot_module/medical,
/obj/item/robot_module/syndicate_medical)
var/list/additional_reagents = list()
+ module_flags = BORG_MODULE_MEDICAL
/obj/item/borg/upgrade/hypospray/action(mob/living/silicon/robot/R, user = usr)
. = ..()
@@ -471,6 +480,7 @@
require_module = 1
module_type = list(/obj/item/robot_module/medical,
/obj/item/robot_module/syndicate_medical)
+ module_flags = BORG_MODULE_MEDICAL
/obj/item/borg/upgrade/processor/action(mob/living/silicon/robot/R, user = usr)
. = ..()
@@ -578,6 +588,7 @@
icon_state = "borg_BS_RPED"
require_module = TRUE
module_type = list(/obj/item/robot_module/engineering, /obj/item/robot_module/saboteur)
+ module_flags = BORG_MODULE_ENGINEERING
/obj/item/borg/upgrade/rped/action(mob/living/silicon/robot/R, user = usr)
. = ..()
@@ -612,8 +623,8 @@
icon = 'icons/obj/device.dmi'
icon_state = "pinpointer_crew"
require_module = TRUE
- module_type = list(/obj/item/robot_module/medical,
- /obj/item/robot_module/syndicate_medical)
+ module_type = list(/obj/item/robot_module/medical, /obj/item/robot_module/syndicate_medical)
+ module_flags = BORG_MODULE_MEDICAL
/obj/item/borg/upgrade/pinpointer/action(mob/living/silicon/robot/R, user = usr)
. = ..()
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index 2de5860794..08b91332ef 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -164,11 +164,13 @@
else
. = ..()
-/obj/item/tank/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/tank/ui_state(mob/user)
+ return GLOB.hands_state
+
+/obj/item/tank/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Tank", name, 400, 120, master_ui, state)
+ ui = new(user, src, "Tank", name)
ui.open()
/obj/item/tank/ui_data(mob/user)
@@ -205,10 +207,6 @@
else if(pressure == "max")
pressure = TANK_MAX_RELEASE_PRESSURE
. = TRUE
- else if(pressure == "input")
- pressure = input("New release pressure ([TANK_MIN_RELEASE_PRESSURE]-[TANK_MAX_RELEASE_PRESSURE] kPa):", name, distribute_pressure) as num|null
- if(!isnull(pressure) && !..())
- . = TRUE
else if(text2num(pressure) != null)
pressure = text2num(pressure)
. = TRUE
diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm
index 881e4d5f1a..939b9e4905 100644
--- a/code/game/objects/items/teleportation.dm
+++ b/code/game/objects/items/teleportation.dm
@@ -1,4 +1,3 @@
-
#define SOURCE_PORTAL 1
#define DESTINATION_PORTAL 2
@@ -25,90 +24,72 @@
throw_speed = 3
throw_range = 7
custom_materials = list(/datum/material/iron=400)
+ var/tracking_range = 20
-/obj/item/locator/attack_self(mob/user)
- user.set_machine(src)
- var/dat
- if (temp)
- dat = "[temp]Clear "
- else
- dat = {"
-Persistent Signal Locator
-Refresh "}
- user << browse(dat, "window=radio")
- onclose(user, "radio")
- return
+/obj/item/locator/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "BluespaceLocator", name)
+ ui.open()
-/obj/item/locator/Topic(href, href_list)
- ..()
- if (usr.stat || usr.restrained())
- return
- var/turf/current_location = get_turf(usr)//What turf is the user on?
- if(!current_location || is_centcom_level(current_location.z))//If turf was not found or they're on CentCom
- to_chat(usr, "[src] is malfunctioning.")
- return
- if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)))
- usr.set_machine(src)
- if (href_list["refresh"])
- temp = "Persistent Signal Locator "
- var/turf/sr = get_turf(src)
+/obj/item/locator/ui_data(mob/user)
+ var/list/data = list()
- if (sr)
- temp += "Beacon Signals: "
- for(var/obj/item/beacon/W in GLOB.teleportbeacons)
- if (!W.renamed)
- continue
- var/turf/tr = get_turf(W)
- if (tr.z == sr.z && tr)
- var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
- if (direct < 5)
- direct = "very strong"
- else
- if (direct < 10)
- direct = "strong"
- else
- if (direct < 20)
- direct = "weak"
- else
- direct = "very weak"
- temp += "[W.name]-[dir2text(get_dir(sr, tr))]-[direct] "
+ data["trackingrange"] = tracking_range
- temp += "Implant Signals: "
- for (var/obj/item/implant/tracking/W in GLOB.tracked_implants)
- if (!isliving(W.imp_in))
- continue
- var/mob/living/M = W.imp_in
- if (M.stat == DEAD)
- if (M.timeofdeath + W.lifespan_postmortem < world.time)
- continue
+ // Get our current turf location.
+ var/turf/sr = get_turf(src)
- var/turf/tr = get_turf(M)
- if (tr.z == sr.z && tr)
- var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
- if (direct < 20)
- if (direct < 5)
- direct = "very strong"
- else
- if (direct < 10)
- direct = "strong"
- else
- direct = "weak"
- temp += "[W.imp_in.name]-[dir2text(get_dir(sr, tr))]-[direct] "
+ if (sr)
+ // Check every teleport beacon.
+ var/list/tele_beacons = list()
+ for(var/obj/item/beacon/W in GLOB.teleportbeacons)
- temp += "You are at \[[sr.x],[sr.y],[sr.z]\] in orbital coordinates.Refresh "
+ // Get the tracking beacon's turf location.
+ var/turf/tr = get_turf(W)
+
+ // Make sure it's on a turf and that its Z-level matches the tracker's Z-level
+ if (tr && tr.z == sr.z)
+ // Get the distance between the beacon's turf and our turf
+ var/distance = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
+
+ // If the target is too far away, skip over this beacon.
+ if(distance > tracking_range)
+ continue
+
+ var/beacon_name
+
+ if(W.renamed)
+ beacon_name = W.name
+ else
+ var/area/A = get_area(W)
+ beacon_name = A.name
+
+ var/D = dir2text(get_dir(sr, tr))
+ tele_beacons += list(list(name = beacon_name, direction = D, distance = distance))
+
+ data["telebeacons"] = tele_beacons
+
+ var/list/track_implants = list()
+
+ for (var/obj/item/implant/tracking/W in GLOB.tracked_implants)
+ if (!W.imp_in || !isliving(W.loc))
+ continue
else
- temp += "Processing Error: Unable to locate orbital position. "
- else
- if (href_list["temp"])
- temp = null
- if (ismob(src.loc))
- attack_self(src.loc)
- else
- for(var/mob/M in fov_viewers(1, src))
- if (M.client)
- src.attack_self(M)
- return
+ var/mob/living/M = W.loc
+ if (M.stat == DEAD)
+ if (M.timeofdeath + W.lifespan_postmortem < world.time)
+ continue
+ var/turf/tr = get_turf(W)
+ var/distance = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
+ if(distance > tracking_range)
+ continue
+
+ var/D = dir2text(get_dir(sr, tr))
+ track_implants += list(list(name = W.imp_in.name, direction = D, distance = distance))
+ data["trackimplants"] = track_implants
+ return data
/*
* Hand-tele
@@ -141,14 +122,11 @@
return TRUE
return ..()
-/obj/item/hand_tele/proc/try_dispel_portal(atom/target, mob/user, delay = 30)
- var/datum/beam/B = user.Beam(target, icon_state = "rped_upgrade", maxdistance = 50)
- if(is_parent_of_portal(target) && (!delay || do_after(user, delay, target = target)))
+/obj/item/hand_tele/proc/try_dispel_portal(atom/target, mob/user)
+ if(is_parent_of_portal(target))
qdel(target)
to_chat(user, "You dispel [target] with \the [src]! ")
- qdel(B)
return TRUE
- qdel(B)
return FALSE
/obj/item/hand_tele/afterattack(atom/target, mob/user)
@@ -190,13 +168,8 @@
user.show_message("\The [src] is recharging! ")
return
var/atom/T = L[t1]
- var/implantcheckmate = FALSE
- if(isliving(T))
- var/mob/living/M = T
- if(!locate(/obj/item/implant/tracking) in M.implants) //The user was too slow and let the target mob's tracking implant expire or get removed.
- implantcheckmate = TRUE
var/area/A = get_area(T)
- if(A.noteleport || implantcheckmate)
+ if(A.noteleport)
to_chat(user, "\The [src] is malfunctioning. ")
return
current_location = get_turf(user) //Recheck.
@@ -205,10 +178,12 @@
to_chat(user, "\The [src] is malfunctioning. ")
return
user.show_message("Locked In. ", MSG_AUDIBLE)
- var/list/obj/effect/portal/created = create_portal_pair(current_location, get_teleport_turf(get_turf(T)), src, 300, 1, null, atmos_link_override)
+ var/list/obj/effect/portal/created = create_portal_pair(current_location, get_teleport_turf(get_turf(T)), 300, 1, null, atmos_link_override)
if(!(LAZYLEN(created) == 2))
return
- try_move_adjacent(created[1])
+ RegisterSignal(created[1], COMSIG_PARENT_QDELETING, .proc/on_portal_destroy) //Gosh darn it kevinz.
+ RegisterSignal(created[2], COMSIG_PARENT_QDELETING, .proc/on_portal_destroy)
+ try_move_adjacent(created[1], user.dir)
active_portal_pairs[created[1]] = created[2]
var/obj/effect/portal/c1 = created[1]
var/obj/effect/portal/c2 = created[2]
diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm
index 827e9a7b0a..47584a1775 100644
--- a/code/game/objects/structures/artstuff.dm
+++ b/code/game/objects/structures/artstuff.dm
@@ -45,8 +45,6 @@
var/height = 11
var/list/grid
var/canvas_color = "#ffffff" //empty canvas color
- var/ui_x = 400
- var/ui_y = 400
var/used = FALSE
var/painting_name //Painting name, this is set after framing.
var/finalized = FALSE //Blocks edits
@@ -75,12 +73,16 @@
. = ..()
ui_interact(user)
-/obj/item/canvas/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+/obj/item/canvas/ui_state(mob/user)
+ if(finalized)
+ return GLOB.physical_obscured_state
+ else
+ return GLOB.default_state
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/canvas/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Canvas", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "Canvas", name)
ui.set_autoupdate(FALSE)
ui.open()
@@ -190,8 +192,6 @@
icon_state = "19x19"
width = 19
height = 19
- ui_x = 600
- ui_y = 600
pixel_x = 6
pixel_y = 9
framed_offset_x = 8
@@ -201,8 +201,6 @@
icon_state = "23x19"
width = 23
height = 19
- ui_x = 800
- ui_y = 600
pixel_x = 4
pixel_y = 10
framed_offset_x = 6
@@ -212,8 +210,6 @@
icon_state = "23x23"
width = 23
height = 23
- ui_x = 800
- ui_y = 800
pixel_x = 5
pixel_y = 9
framed_offset_x = 5
@@ -262,7 +258,7 @@
/obj/structure/sign/painting/examine(mob/user)
. = ..()
if(C)
- C.ui_interact(user,state = GLOB.physical_obscured_state)
+ C.ui_interact(user)
/obj/structure/sign/painting/wirecutter_act(mob/living/user, obj/item/I)
. = ..()
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index f0384ca96a..19c0f0aed1 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -345,3 +345,212 @@
name = initial(I.name)
icon = initial(I.icon)
icon_state = initial(I.icon_state)
+/* Selling people in jars is currently disabled.
+/obj/structure/displaycase/forsale
+ name = "vend-a-tray"
+ icon = 'icons/obj/stationobjs.dmi'
+ icon_state = "laserbox0"
+ desc = "A display case with an ID-card swiper. Use your ID to purchase the contents."
+ density = FALSE
+ max_integrity = 100
+ req_access = null
+ showpiece_type = /obj/item/reagent_containers/food
+ alert = FALSE //No, we're not calling the fire department because someone stole your cookie.
+ glass_fix = FALSE //Fixable with tools instead.
+ ///The price of the item being sold. Altered by grab intent ID use.
+ var/sale_price = 20
+ ///The Account which will receive payment for purchases. Set by the first ID to swipe the tray.
+ var/datum/bank_account/payments_acc = null
+ ///We're using the same trick as paper does in order to cache the image, and only load the UI when messed with.
+ var/list/viewing_ui = list()
+
+/obj/structure/displaycase/forsale/update_icon() //remind me to fix my shitcode later
+ var/icon/I
+ if(open)
+ I = icon('icons/obj/stationobjs.dmi',"laserboxb0")
+ else
+ I = icon('icons/obj/stationobjs.dmi',"laserbox0")
+ if(!showpiece && !open)
+ I = icon('icons/obj/stationobjs.dmi',"laserbox_open")
+ if(broken)
+ I = icon('icons/obj/stationobjs.dmi',"laserbox_broken")
+ if(showpiece)
+ var/icon/S = getFlatIcon(showpiece)
+ S.Scale(17,17)
+ I.Blend(S,ICON_UNDERLAY,8,12)
+ src.icon = I
+ return
+
+/obj/structure/displaycase/forsale/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Vendatray", name)
+ ui.set_autoupdate(FALSE)
+ viewing_ui[user] = ui
+ ui.open()
+
+/obj/structure/displaycase/forsale/ui_data(mob/user)
+ var/list/data = list()
+ var/register = FALSE
+ if(payments_acc)
+ register = TRUE
+ data["owner_name"] = payments_acc.account_holder
+ if(showpiece)
+ data["product_name"] = capitalize(showpiece.name)
+ var/base64 = icon2base64(icon(showpiece.icon, showpiece.icon_state))
+ data["product_icon"] = base64
+ data["registered"] = register
+ data["product_cost"] = sale_price
+ data["tray_open"] = open
+ return data
+
+/obj/structure/displaycase/forsale/ui_act(action, params)
+ if(..())
+ return
+ var/obj/item/card/id/potential_acc = usr.get_idcard(hand_first = TRUE)
+ switch(action)
+ if("Buy")
+ if(!showpiece)
+ to_chat(usr, "There's nothing for sale. ")
+ return TRUE
+ if(broken)
+ to_chat(usr, "[src] appears to be broken. ")
+ return TRUE
+ if(!payments_acc)
+ to_chat(usr, "[src] hasn't been registered yet. ")
+ return TRUE
+ if(!usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ return TRUE
+ if(!potential_acc)
+ to_chat(usr, "No ID card detected. ")
+ return
+ var/datum/bank_account/account = potential_acc.registered_account
+ if(!account)
+ to_chat(usr, "[potential_acc] has no account registered! ")
+ return
+ if(!account.has_money(sale_price))
+ to_chat(usr, "You do not possess the funds to purchase this. ")
+ return TRUE
+ else
+ account.adjust_money(-sale_price)
+ if(payments_acc)
+ payments_acc.adjust_money(sale_price)
+ usr.put_in_hands(showpiece)
+ to_chat(usr, "You purchase [showpiece] for [sale_price] credits. ")
+ playsound(src, 'sound/effects/cashregister.ogg', 40, TRUE)
+ icon = 'icons/obj/stationobjs.dmi'
+ flick("laserbox_vend", src)
+ showpiece = null
+ update_icon()
+ SStgui.update_uis(src)
+ return TRUE
+ if("Open")
+ if(!payments_acc)
+ to_chat(usr, "[src] hasn't been registered yet. ")
+ return TRUE
+ if(!potential_acc || !potential_acc.registered_account)
+ return
+ if(!check_access(potential_acc))
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
+ return
+ toggle_lock()
+ SStgui.update_uis(src)
+ if("Register")
+ if(payments_acc)
+ return
+ if(!potential_acc || !potential_acc.registered_account)
+ return
+ if(!check_access(potential_acc))
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
+ return
+ payments_acc = potential_acc.registered_account
+ playsound(src, 'sound/machines/click.ogg', 20, TRUE)
+ if("Adjust")
+ if(!check_access(potential_acc) || potential_acc.registered_account != payments_acc)
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
+ return
+
+ var/new_price_input = input(usr,"Set the sale price for this vend-a-tray.","new price",0) as num|null
+ if(isnull(new_price_input) || (payments_acc != potential_acc.registered_account))
+ to_chat(usr, "[src] rejects your new price. ")
+ return
+ if(!usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) )
+ to_chat(usr, "You need to get closer! ")
+ return
+ new_price_input = clamp(round(new_price_input, 1), 10, 1000)
+ sale_price = new_price_input
+ to_chat(usr, "The cost is now set to [sale_price]. ")
+ SStgui.update_uis(src)
+ return TRUE
+ . = TRUE
+/obj/structure/displaycase/forsale/attackby(obj/item/I, mob/living/user, params)
+ if(isidcard(I))
+ //Card Registration
+ var/obj/item/card/id/potential_acc = I
+ if(!potential_acc.registered_account)
+ to_chat(user, "This ID card has no account registered! ")
+ return
+ if(payments_acc == potential_acc.registered_account)
+ playsound(src, 'sound/machines/click.ogg', 20, TRUE)
+ toggle_lock()
+ return
+ if(istype(I, /obj/item/pda))
+ return TRUE
+ SStgui.update_uis(src)
+ . = ..()
+
+
+/obj/structure/displaycase/forsale/multitool_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(obj_integrity <= (integrity_failure * max_integrity))
+ to_chat(user, "You start recalibrating [src]'s hover field... ")
+ if(do_after(user, 20, target = src))
+ broken = 0
+ obj_integrity = max_integrity
+ update_icon()
+ return TRUE
+
+/obj/structure/displaycase/forsale/wrench_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(open && user.a_intent == INTENT_HELP )
+ if(anchored)
+ to_chat(user, "You start unsecuring [src]... ")
+ else
+ to_chat(user, "You start securing [src]... ")
+ if(I.use_tool(src, user, 16, volume=50))
+ if(QDELETED(I))
+ return
+ if(anchored)
+ to_chat(user, "You unsecure [src]. ")
+ else
+ to_chat(user, "You secure [src]. ")
+ anchored = !anchored
+ return
+ else if(!open && user.a_intent == INTENT_HELP)
+ to_chat(user, "[src] must be open to move it. ")
+ return
+
+/obj/structure/displaycase/forsale/emag_act(mob/user)
+ . = ..()
+ payments_acc = null
+ req_access = list()
+ to_chat(user, "[src]'s card reader fizzles and smokes, and the account owner is reset. ")
+
+/obj/structure/displaycase/forsale/examine(mob/user)
+ . = ..()
+ if(showpiece && !open)
+ . += "[showpiece] is for sale for [sale_price] credits. "
+ if(broken)
+ . += "[src] is sparking and the hover field generator seems to be overloaded. Use a multitool to fix it. "
+
+/obj/structure/displaycase/forsale/obj_break(damage_flag)
+ if(!broken && !(flags_1 & NODECONSTRUCT_1))
+ broken = TRUE
+ playsound(src, "shatter", 70, TRUE)
+ update_icon()
+ trigger_alarm() //In case it's given an alarm anyway.
+
+/obj/structure/displaycase/forsale/kitchen
+ desc = "A display case with an ID-card swiper. Use your ID to purchase the contents. Meant for the bartender and chef."
+ req_one_access = list(ACCESS_KITCHEN, ACCESS_BAR)
+*/
\ No newline at end of file
diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm
index e2298ac6c5..2a441ff0f2 100644
--- a/code/game/objects/structures/tank_dispenser.dm
+++ b/code/game/objects/structures/tank_dispenser.dm
@@ -67,15 +67,14 @@
to_chat(user, "You put [I] in [src]. ")
update_icon()
-/obj/structure/tank_dispenser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "TankDispenser", name, 275, 103, master_ui, state)
- ui.open()
+/obj/structure/tank_dispenser/ui_state(mob/user)
+ return GLOB.physical_state
-/obj/structure/tank_dispenser/attack_robot(mob/user)
- ui_interact(user)
+/obj/structure/tank_dispenser/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "TankDispenser", name)
+ ui.open()
/obj/structure/tank_dispenser/ui_data(mob/user)
var/list/data = list()
diff --git a/code/modules/NTNet/relays.dm b/code/modules/NTNet/relays.dm
index c64e2332cc..8c33dd1eba 100644
--- a/code/modules/NTNet/relays.dm
+++ b/code/modules/NTNet/relays.dm
@@ -9,8 +9,6 @@
icon_state = "bus"
density = TRUE
circuit = /obj/item/circuitboard/machine/ntnet_relay
- ui_x = 400
- ui_y = 300
var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame.
var/enabled = 1 // Set to 0 if the relay was turned off
@@ -64,15 +62,12 @@
SSnetworks.station_network.add_log("Quantum relay switched from overload recovery mode to normal operation mode.")
..()
-/obj/machinery/ntnet_relay/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
-
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
-
+/obj/machinery/ntnet_relay/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "NtnetRelay", "NTNet Quantum Relay", ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "NtnetRelay")
ui.open()
-
/obj/machinery/ntnet_relay/ui_data(mob/user)
var/list/data = list()
data["enabled"] = enabled
@@ -81,7 +76,6 @@
data["dos_crashed"] = dos_failure
return data
-
/obj/machinery/ntnet_relay/ui_act(action, params)
if(..())
return
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index 9953fe4bc5..cc59fe10ab 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -78,10 +78,15 @@
close_machine(target)
ui_interact(user)
-/obj/machinery/vr_sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_contained_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/vr_sleeper/ui_state(mob/user)
+ if(user == occupant)
+ return GLOB.contained_state
+ return GLOB.default_state
+
+/obj/machinery/vr_sleeper/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "VrSleeper", "VR Sleeper", 475, 340, master_ui, state)
+ ui = new(user, src, "VrSleeper", "VR Sleeper")
ui.open()
/obj/machinery/vr_sleeper/ui_act(action, params)
diff --git a/code/modules/antagonists/abductor/equipment/orderable_gear.dm b/code/modules/antagonists/abductor/equipment/orderable_gear.dm
new file mode 100644
index 0000000000..d15e450c99
--- /dev/null
+++ b/code/modules/antagonists/abductor/equipment/orderable_gear.dm
@@ -0,0 +1,79 @@
+GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear))
+
+/datum/abductor_gear
+ /// Name of the gear
+ var/name = "Generic Abductor Gear"
+ /// Description of the gear
+ var/description = "Generic description."
+ /// Unique ID of the gear
+ var/id = "abductor_generic"
+ /// Credit cost of the gear
+ var/cost = 1
+ /// Build path of the gear itself
+ var/build_path = null
+ /// Category of the gear
+ var/category = "Basic Gear"
+
+/datum/abductor_gear/agent_helmet
+ name = "Agent Helmet"
+ description = "Abduct with style - spiky style. Prevents digital tracking."
+ id = "agent_helmet"
+ build_path = /obj/item/clothing/head/helmet/abductor
+
+/datum/abductor_gear/agent_vest
+ name = "Agent Vest"
+ description = "A vest outfitted with advanced stealth technology. It has two modes - combat and stealth."
+ id = "agent_vest"
+ build_path = /obj/item/clothing/suit/armor/abductor/vest
+
+/datum/abductor_gear/radio_silencer
+ name = "Radio Silencer"
+ description = "A compact device used to shut down communications equipment."
+ id = "radio_silencer"
+ build_path = /obj/item/abductor/silencer
+
+/datum/abductor_gear/science_tool
+ name = "Science Tool"
+ description = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras."
+ id = "science_tool"
+ build_path = /obj/item/abductor/gizmo
+/*
+/datum/abductor_gear/advanced_baton
+ name = "Advanced Baton"
+ description = "A quad-mode baton used for incapacitation and restraining of specimens."
+ id = "advanced_baton"
+ cost = 2
+ build_path = /obj/item/melee/baton/abductor //does not exist?
+*/
+/datum/abductor_gear/superlingual_matrix
+ name = "Superlingual Matrix"
+ description = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something."
+ id = "superlingual_matrix"
+ build_path = /obj/item/organ/tongue/abductor
+ category = "Advanced Gear"
+
+/datum/abductor_gear/mental_interface
+ name = "Mental Interface Device"
+ description = "A dual-mode tool for directly communicating with sentient brains. It can be used to send a direct message to a target, \
+ or to send a command to a test subject with a charged gland."
+ id = "mental_interface"
+ cost = 2
+ build_path = /obj/item/abductor/mind_device
+ category = "Advanced Gear"
+
+/datum/abductor_gear/reagent_synthesizer
+ name = "Reagent Synthesizer"
+ description = "Synthesizes a variety of reagents using proto-matter."
+ id = "reagent_synthesizer"
+ cost = 2
+ build_path = /obj/item/abductor_machine_beacon/chem_dispenser
+ category = "Advanced Gear"
+
+/datum/abductor_gear/shrink_ray
+ name = "Shrink Ray Blaster"
+ description = "This is a piece of frightening alien tech that enhances the magnetic pull of atoms in a localized space to temporarily make an object shrink. \
+ That or it's just space magic. Either way, it shrinks stuff."
+ id = "shrink_ray"
+ cost = 2
+ build_path = /obj/item/gun/energy/shrink_ray
+ category = "Advanced Gear"
diff --git a/code/modules/antagonists/abductor/machinery/console.dm b/code/modules/antagonists/abductor/machinery/console.dm
index 6a981b36dc..f06f4fe68a 100644
--- a/code/modules/antagonists/abductor/machinery/console.dm
+++ b/code/modules/antagonists/abductor/machinery/console.dm
@@ -23,99 +23,105 @@
var/obj/machinery/abductor/pad/pad
var/obj/machinery/computer/camera_advanced/abductor/camera
var/list/datum/icon_snapshot/disguises = list()
+ /// Currently selected gear category
+ var/selected_cat
+ /// Dictates if the compact mode of the interface is on or off
+ var/compact_mode = FALSE
+ /// Possible gear to be dispensed
+ var/list/possible_gear
/obj/machinery/abductor/console/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!HAS_TRAIT(user, TRAIT_ABDUCTOR_TRAINING) && !HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_TRAINING))
to_chat(user, "You start mashing alien buttons at random! ")
if(do_after(user,100, target = src))
TeleporterSend()
- return
- user.set_machine(src)
- var/dat = ""
- dat += " Abductsoft 3000 "
+/obj/machinery/abductor/console/ui_status(mob/user)
+ if(!isabductor(user) && !isobserver(user))
+ return UI_CLOSE
+ return ..()
+
+/obj/machinery/abductor/console/ui_state(mob/user)
+ return GLOB.physical_state
+
+/obj/machinery/abductor/console/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "AbductorConsole", name)
+ ui.open()
+
+/obj/machinery/abductor/console/ui_static_data(mob/user)
+ var/list/data = list()
+ data["categories"] = list()
+ for(var/category in possible_gear)
+ var/list/cat = list(
+ "name" = category,
+ "items" = (category == selected_cat ? list() : null))
+ for(var/gear in possible_gear[category])
+ var/datum/abductor_gear/AG = possible_gear[category][gear]
+ cat["items"] += list(list(
+ "name" = AG.name,
+ "cost" = AG.cost,
+ "desc" = AG.description,
+ ))
+ data["categories"] += list(cat)
+ return data
+
+/obj/machinery/abductor/console/ui_data(mob/user)
+ var/list/data = list()
+ data["compactMode"] = compact_mode
+ data["experiment"] = experiment ? TRUE : FALSE
if(experiment)
- var/points = experiment.points
- var/credits = experiment.credits
- dat += "Collected Samples : [points] "
- dat += "Gear Credits: [credits] "
- dat += "Transfer data in exchange for supplies: "
- dat += "Advanced Baton (2 Credits) "
- dat += "Mental Interface Device (2 Credits) "
- dat += "Reagent Synthesizer (2 Credits) "
- dat += "Agent Helmet (1 Credit) "
- dat += "Agent Vest (1 Credit) "
- dat += "Radio Silencer (1 Credit) "
- dat += "Science Tool (1 Credit) "
- dat += "Superlingual Matrix (1 Credit) "
- else
- dat += "NO EXPERIMENT MACHINE DETECTED "
-
+ data["points"] = experiment.points
+ data["credits"] = experiment.credits
+ data["pad"] = pad ? TRUE : FALSE
if(pad)
- dat += "Emergency Teleporter System. "
- dat += "Consider using primary observation console first. "
- dat += "Activate Teleporter "
- if(gizmo && gizmo.marked)
- dat += "Retrieve Mark "
- else
- dat += "Retrieve Mark "
- else
- dat += "NO TELEPAD DETECTED "
-
+ data["gizmo"] = gizmo && gizmo.marked ? TRUE : FALSE
+ data["vest"] = vest ? TRUE : FALSE
if(vest)
- dat += " Agent Vest Mode "
- var/mode = vest.mode
- if(mode == VEST_STEALTH)
- dat += "Combat "
- dat += "Stealth "
- else
- dat += "Combat "
- dat += "Stealth "
+ data["vest_mode"] = vest.mode
+ data["vest_lock"] = HAS_TRAIT_FROM(vest, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT)
+ return data
- dat+=" "
- dat += "Select Agent Vest Disguise "
- dat += "[HAS_TRAIT_FROM(vest, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) ? "Unlock" : "Lock"] Vest "
- else
- dat += "NO AGENT VEST DETECTED "
- var/datum/browser/popup = new(user, "computer", "Abductor Console", 400, 500)
- popup.set_content(dat)
- popup.open()
-
-/obj/machinery/abductor/console/Topic(href, href_list)
- if(..())
+/obj/machinery/abductor/console/ui_act(action, list/params)
+ . = ..()
+ if(.)
return
- usr.set_machine(src)
- if(href_list["teleporter_send"])
- TeleporterSend()
- else if(href_list["teleporter_retrieve"])
- TeleporterRetrieve()
- else if(href_list["flip_vest"])
- FlipVest()
- else if(href_list["toggle_vest"])
- if(vest)
+ switch(action)
+ if("buy")
+ var/item_name = params["name"]
+ var/list/buyable_items = list()
+ for(var/category in possible_gear)
+ buyable_items += possible_gear[category]
+ for(var/key in buyable_items)
+ var/datum/abductor_gear/AG = buyable_items[key]
+ if(AG.name == item_name)
+ Dispense(AG.build_path, AG.cost)
+ return TRUE
+ if("teleporter_send")
+ TeleporterSend()
+ return TRUE
+ if("teleporter_retrieve")
+ TeleporterRetrieve()
+ return TRUE
+ if("flip_vest")
+ FlipVest()
+ return TRUE
+ if("toggle_vest")
+ if(!vest)
+ return
vest.toggle_nodrop()
- else if(href_list["select_disguise"])
- SelectDisguise()
- else if(href_list["dispense"])
- switch(href_list["dispense"])
- if("baton")
- Dispense(/obj/item/abductor/baton,cost=2)
- if("helmet")
- Dispense(/obj/item/clothing/head/helmet/abductor)
- if("silencer")
- Dispense(/obj/item/abductor/silencer)
- if("tool")
- Dispense(/obj/item/abductor/gizmo)
- if("vest")
- Dispense(/obj/item/clothing/suit/armor/abductor/vest)
- if("mind_device")
- Dispense(/obj/item/abductor/mind_device,cost=2)
- if("chem_dispenser")
- Dispense(/obj/item/abductor_machine_beacon/chem_dispenser,cost=2)
- if("tongue")
- Dispense(/obj/item/organ/tongue/abductor)
- updateUsrDialog()
+ return TRUE
+ if("select_disguise")
+ SelectDisguise()
+ return TRUE
+ if("select")
+ selected_cat = params["category"]
+ return TRUE
+ if("compact_toggle")
+ compact_mode = !compact_mode
+ return TRUE
/obj/machinery/abductor/console/proc/TeleporterRetrieve()
if(pad && gizmo && gizmo.marked)
@@ -156,7 +162,6 @@
pad.teleport_target = location
to_chat(user, "Location marked as test subject release point. ")
-
/obj/machinery/abductor/console/Initialize(mapload)
..()
return INITIALIZE_HINT_LATELOAD
diff --git a/code/modules/antagonists/abductor/machinery/dispenser.dm b/code/modules/antagonists/abductor/machinery/dispenser.dm
index 90dcf6626f..f5bc0946e6 100644
--- a/code/modules/antagonists/abductor/machinery/dispenser.dm
+++ b/code/modules/antagonists/abductor/machinery/dispenser.dm
@@ -22,38 +22,44 @@
gland_colors[i] = random_color()
amounts[i] = rand(1,5)
-/obj/machinery/abductor/gland_dispenser/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
- if(!isabductor(user))
+/obj/machinery/abductor/gland_dispenser/ui_status(mob/user)
+ if(!isabductor(user) && !isobserver(user))
+ return UI_CLOSE
+ return ..()
+
+/obj/machinery/abductor/gland_dispenser/ui_state(mob/user)
+ return GLOB.physical_state
+
+/obj/machinery/abductor/gland_dispenser/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "GlandDispenser", name)
+ ui.open()
+
+/obj/machinery/abductor/gland_dispenser/ui_data(mob/user)
+ var/list/data = list()
+ data["glands"] = list()
+ for(var/gland_number=1,gland_number<=gland_colors.len,gland_number++)
+ var/list/gland_information = list(
+ "color" = gland_colors[gland_number],
+ "amount" = amounts[gland_number],
+ "id" = gland_number,
+ )
+ data["glands"] += list(gland_information)
+ return data
+
+/obj/machinery/abductor/gland_dispenser/ui_act(action, list/params)
+ . = ..()
+ if(.)
return
- user.set_machine(src)
- var/box_css = {"
- "}
- var/dat = ""
- var/item_count = 0
- for(var/i=1,i<=gland_colors.len,i++)
- item_count++
- var/g_color = gland_colors[i]
- var/amount = amounts[i]
- dat += "[amount] "
- if(item_count == 4) // Four boxes per line
- dat +=""
- item_count = 0
- var/datum/browser/popup = new(user, "glands", "Gland Dispenser", 200, 200)
- popup.add_head_content(box_css)
- popup.set_content(dat)
- popup.open()
- return
+
+ switch(action)
+ if("dispense")
+ var/gland_id = text2num(params["gland_id"])
+ if(!gland_id)
+ return
+ Dispense(gland_id)
+ return TRUE
/obj/machinery/abductor/gland_dispenser/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/organ/heart/gland))
@@ -65,15 +71,6 @@
else
return ..()
-/obj/machinery/abductor/gland_dispenser/Topic(href, href_list)
- if(..())
- return
- usr.set_machine(src)
-
- if(href_list["dispense"])
- Dispense(text2num(href_list["dispense"]))
- updateUsrDialog()
-
/obj/machinery/abductor/gland_dispenser/proc/Dispense(count)
if(amounts[count]>0)
amounts[count]--
diff --git a/code/modules/antagonists/abductor/machinery/experiment.dm b/code/modules/antagonists/abductor/machinery/experiment.dm
index bc58cf45f0..604730cff2 100644
--- a/code/modules/antagonists/abductor/machinery/experiment.dm
+++ b/code/modules/antagonists/abductor/machinery/experiment.dm
@@ -15,16 +15,13 @@
var/breakout_time = 450
/obj/machinery/abductor/experiment/MouseDrop_T(mob/target, mob/user)
- if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target))
+ var/mob/living/L = user
+ if(user.stat || (isliving(user) && (!(L.mobility_flags & MOBILITY_STAND) || !(L.mobility_flags & MOBILITY_UI))) || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target))
return
if(isabductor(target))
return
close_machine(target)
-/obj/machinery/abductor/experiment/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
-
- experimentUI(user)
-
/obj/machinery/abductor/experiment/open_machine()
if(!state_open && !panel_open)
..()
@@ -46,7 +43,7 @@
/obj/machinery/abductor/experiment/container_resist(mob/living/user)
user.visible_message("You see [user] kicking against the door of [src]! ", \
"You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].) ", \
- "You hear a metallic creaking from [src]. ")
+ "You hear a metallic creaking from [src]. ")
if(do_after(user,(breakout_time), target = src))
if(!user || user.stat != CONSCIOUS || user.loc != src || state_open)
return
@@ -54,122 +51,84 @@
"You successfully break out of [src]! ")
open_machine()
-/obj/machinery/abductor/experiment/proc/dissection_icon(mob/living/carbon/human/H)
- var/icon/photo = null
- var/g = (H.dna.features["body_model"] == FEMALE) ? "f" : "m"
- if(H.dna.species.use_skintones)
- photo = icon("icon" = 'icons/mob/human.dmi', "icon_state" = "[H.skin_tone]_[g]")
- else
- photo = icon("icon" = 'icons/mob/human.dmi', "icon_state" = "[H.dna.species.id]_[g]")
- photo.Blend("#[H.dna.features["mcolor"]]", ICON_MULTIPLY)
+/obj/machinery/abductor/experiment/ui_status(mob/user)
+ if(user == occupant)
+ return UI_CLOSE
+ return ..()
- var/icon/eyes
- if(EYECOLOR in H.dna.species.species_traits)
- eyes = icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "eyes")
- eyes.Blend("#[H.eye_color]", ICON_MULTIPLY)
+/obj/machinery/abductor/experiment/ui_state(mob/user)
+ return GLOB.physical_state
- var/datum/sprite_accessory/S
- S = GLOB.hair_styles_list[H.hair_style]
- if(S && (HAIR in H.dna.species.species_traits))
- var/icon/hair = icon("icon" = S.icon, "icon_state" = "[S.icon_state]")
- hair.Blend("#[H.hair_color]", ICON_MULTIPLY)
- eyes.Blend(hair, ICON_OVERLAY)
+/obj/machinery/abductor/experiment/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ProbingConsole", name)
+ ui.open()
- S = GLOB.facial_hair_styles_list[H.facial_hair_style]
- if(S && (FACEHAIR in H.dna.species.species_traits))
- var/icon/facial = icon("icon" = S.icon, "icon_state" = "[S.icon_state]")
- facial.Blend("#[H.facial_hair_color]", ICON_MULTIPLY)
- eyes.Blend(facial, ICON_OVERLAY)
-
- if(eyes)
- photo.Blend(eyes, ICON_OVERLAY)
-
- var/icon/splat = icon("icon" = 'icons/mob/dam_mob.dmi',"icon_state" = "chest30")
- photo.Blend(splat,ICON_OVERLAY)
-
- return photo
-
-/obj/machinery/abductor/experiment/proc/experimentUI(mob/user)
- var/dat
- dat += " Experiment "
- if(occupant)
- var/obj/item/photo/P = new
- P.picture = new
- P.picture.picture_image = icon(dissection_icon(occupant), dir = SOUTH)
- user << browse_rsc(P.picture.picture_image, "dissection_img")
- dat += ""
- dat += " " //Avert your eyes
- dat += " "
- dat += "Probe "
- dat += "Dissect "
- dat += "Analyze "
- dat += "
"
- else
- dat += "Experiment "
-
- if(!occupant)
- dat += "Machine Unoccupied "
- else
- dat += "Subject Status : "
- dat += "[occupant.name] => "
- var/mob/living/mob_occupant = occupant
- switch(mob_occupant.stat)
- if(CONSCIOUS)
- dat += "Conscious "
- if(UNCONSCIOUS)
- dat += "Unconscious "
- else // DEAD
- dat += "Deceased "
- dat += " "
- dat += "[flash]"
- dat += " "
- dat += "Scan "
- dat += "Close " : "open=1'>Open"]"
- var/datum/browser/popup = new(user, "experiment", "Probing Console", 300, 300)
- popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
- popup.set_content(dat)
- popup.open()
-
-/obj/machinery/abductor/experiment/Topic(href, href_list)
- if(..() || usr == occupant)
- return
- usr.set_machine(src)
- if(href_list["refresh"])
- updateUsrDialog()
- return
- if(href_list["open"])
- open_machine()
- return
- if(href_list["close"])
- close_machine()
- return
+/obj/machinery/abductor/experiment/ui_data(mob/user)
+ var/list/data = list()
+ data["open"] = state_open
+ data["feedback"] = flash
+ data["occupant"] = occupant ? TRUE : FALSE
+ data["occupant_name"] = null
+ data["occupant_status"] = null
if(occupant)
var/mob/living/mob_occupant = occupant
- if(mob_occupant.stat != DEAD)
- if(href_list["experiment"])
- flash = Experiment(occupant,href_list["experiment"],usr)
- updateUsrDialog()
- add_fingerprint(usr)
+ data["occupant_name"] = mob_occupant.name
+ data["occupant_status"] = mob_occupant.stat
+ return data
-/obj/machinery/abductor/experiment/proc/Experiment(mob/occupant,type,mob/user)
+/obj/machinery/abductor/experiment/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("door")
+ if(state_open)
+ close_machine()
+ return TRUE
+ else
+ open_machine()
+ return TRUE
+ if("experiment")
+ if(!occupant)
+ return
+ var/mob/living/mob_occupant = occupant
+ if(mob_occupant.stat == DEAD)
+ return
+ flash = experiment(occupant, params["experiment_type"], usr)
+ return TRUE
+
+/**
+ * experiment: Performs selected experiment on occupant mob, resulting in a point reward on success
+ *
+ * Arguments:
+ * * occupant The mob inside the machine
+ * * type The type of experiment to be performed
+ * * user The mob starting the experiment
+ */
+/obj/machinery/abductor/experiment/proc/experiment(mob/occupant, type, mob/user)
LAZYINITLIST(history)
var/mob/living/carbon/human/H = occupant
var/datum/antagonist/abductor/user_abductor = user.mind.has_antag_datum(/datum/antagonist/abductor)
if(!user_abductor)
- return "Authorization failure. Contact mothership immidiately. "
+ return "Authorization failure. Contact mothership immediately."
var/point_reward = 0
+ if(!H)
+ return "Invalid or missing specimen."
if(H in history)
- return "Specimen already in database. "
+ return "Specimen already in database."
if(H.stat == DEAD)
say("Specimen deceased - please provide fresh sample.")
- return "Specimen deceased. "
+ return "Specimen deceased."
var/obj/item/organ/heart/gland/GlandTest = locate() in H.internal_organs
if(!GlandTest)
say("Experimental dissection not detected!")
- return "No glands detected! "
- if(H.mind != null && (H.voluntary_ghosted || (H.ckey != null)))
+ return "No glands detected!"
+ if(H.mind != null && H.ckey != null)
LAZYINITLIST(abductee_minds)
LAZYADD(history, H)
LAZYADD(abductee_minds, H.mind)
@@ -191,22 +150,27 @@
point_reward++
if(point_reward > 0)
open_machine()
- SendBack(H)
- playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
+ send_back(H)
+ playsound(src.loc, 'sound/machines/ding.ogg', 50, TRUE)
points += point_reward
credits += point_reward
- return "Experiment successful! [point_reward] new data-points collected. "
+ return "Experiment successful! [point_reward] new data-points collected."
else
- playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 1)
- return "Experiment failed! No replacement organ detected. "
+ playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
+ return "Experiment failed! No replacement organ detected."
else
say("Brain activity nonexistent - disposing sample...")
open_machine()
- SendBack(H)
- return "Specimen braindead - disposed. "
+ send_back(H)
+ return "Specimen braindead - disposed."
-
-/obj/machinery/abductor/experiment/proc/SendBack(mob/living/carbon/human/H)
+/**
+ * send_back: Sends a mob back to a selected teleport location if safe
+ *
+ * Arguments:
+ * * H The human mob to be sent back
+ */
+/obj/machinery/abductor/experiment/proc/send_back(mob/living/carbon/human/H)
H.Sleeping(160)
H.uncuff()
if(console && console.pad && console.pad.teleport_target)
@@ -216,7 +180,6 @@
SSjob.SendToLateJoin(H, FALSE)
return
-
/obj/machinery/abductor/experiment/update_icon_state()
if(state_open)
icon_state = "experiment-open"
diff --git a/code/modules/antagonists/changeling/cellular_emporium.dm b/code/modules/antagonists/changeling/cellular_emporium.dm
index 6abefeefe7..2538394db7 100644
--- a/code/modules/antagonists/changeling/cellular_emporium.dm
+++ b/code/modules/antagonists/changeling/cellular_emporium.dm
@@ -13,10 +13,13 @@
changeling = null
. = ..()
-/datum/cellular_emporium/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/cellular_emporium/ui_state(mob/user)
+ return GLOB.always_state
+
+/datum/cellular_emporium/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "CellularEmporium", name, 900, 480, master_ui, state)
+ ui = new(user, src, "CellularEmporium", name)
ui.open()
/datum/cellular_emporium/ui_data(mob/user)
diff --git a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
index 04a8e92bb7..0bae7d3539 100644
--- a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
+++ b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
@@ -25,9 +25,6 @@
/datum/clockwork_scripture/ranged_ability/kindle, /datum/clockwork_scripture/ranged_ability/hateful_manacles) //quickbound scripture, accessed by index
var/maximum_quickbound = 5 //how many quickbound scriptures we can have
- var/ui_x = 800
- var/ui_z = 420
-
var/obj/structure/destructible/clockwork/trap/linking //If we're linking traps together, which ones we're doing
/obj/item/clockwork/slab/internal //an internal motor for mobs running scripture
@@ -39,6 +36,11 @@
speed_multiplier = 0
no_cost = TRUE
+/obj/item/clockwork/slab/debug/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
+ if(!is_servant_of_ratvar(user))
+ add_servant_of_ratvar(user)
+ return ..()
+
/obj/item/clockwork/slab/traitor
var/spent = FALSE
@@ -57,11 +59,6 @@
to_chat(user, "[src] falls dark. It appears you weren't worthy. ")
return ..()
-/obj/item/clockwork/slab/debug/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
- if(!is_servant_of_ratvar(user))
- add_servant_of_ratvar(user)
- return ..()
-
/obj/item/clockwork/slab/cyborg //three scriptures, plus a spear and fabricator
clockwork_desc = "A divine link to the Celestial Derelict, allowing for limited recital of scripture."
quickbound = list(/datum/clockwork_scripture/ranged_ability/judicial_marker, /datum/clockwork_scripture/ranged_ability/linked_vanguard, \
@@ -145,14 +142,15 @@
/obj/item/clockwork/slab/examine(mob/user)
. = ..()
- if(is_servant_of_ratvar(user) || isobserver(user))
- if(LAZYLEN(quickbound))
- for(var/i in 1 to quickbound.len)
- if(!quickbound[i])
- continue
- var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
- . += "Quickbind button: [initial(quickbind_slot.name)] ."
- . += "Available power: [DisplayPower(get_clockwork_power())]. "
+ if(!is_servant_of_ratvar(user) || !isobserver(user))
+ return
+ if(LAZYLEN(quickbound))
+ for(var/i in 1 to quickbound.len)
+ if(!quickbound[i])
+ continue
+ var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
+ . += "Quickbind button: [initial(quickbind_slot.name)] ."
+ . += "Available power: [DisplayPower(get_clockwork_power())]. "
//Slab actions; Hierophant, Quickbind
/obj/item/clockwork/slab/ui_action_click(mob/user, action)
@@ -198,12 +196,6 @@
ui_interact(user)
return TRUE
-/obj/item/clockwork/slab/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "ClockworkSlab", name, ui_x, ui_z, master_ui, state)
- ui.open()
-
/obj/item/clockwork/slab/proc/recite_scripture(datum/clockwork_scripture/scripture, mob/living/user)
if(!scripture || !user || !user.canUseTopic(src) || (!no_cost && !can_recite_scripture(user)))
return FALSE
@@ -223,27 +215,62 @@
scripture_to_recite.run_scripture()
return TRUE
+/*
+ * Gets text for a certain section. "Default" is used for when you first open Recollection.
+ * Current sections (make sure to update this if you add one:
+ * Basics
+ * Terminology
+ * Components
+ * Scripture
+ * Power
+ * Conversion
+ * * what - What section?
+ */
+/obj/item/clockwork/slab/proc/get_recollection(what) //Now DMDOC compliant!*
+ . = list()
+ switch(what) //need someone to rewrite info for this.
+ if("Default")
+ .["title"] = "Default"
+ .["info"] = "Hello servant! Currently these categories dosen't work!"
+ /*
+ if("Basics")
+ .["title"] = "Basics"
+ .["info"] = "# MARKDOWN WITH HTML?"
+ if("Terminology")
+ .["title"] = "Terminology"
+ .["info"] = "# MARKDOWN WITH HTML?"
+ if("Components")
+ .["title"] = "Default"
+ .["info"] = "# MARKDOWN WITH HTML?"
+ if("Scripture")
+ .["title"] = "Default"
+ .["info"] = "# MARKDOWN WITH HTML?"
+ if("Power")
+ .["title"] = "Power"
+ .["info"] = "# MARKDOWN WITH HTML?"
+ if("Conversion")
+ .["title"] = "Conversion"
+ .["info"] = "# MARKDOWN WITH HTML?"
+ */
+ else
+ return null //error text handled tgui side. should not cause BSOD
-//Gets text for a certain section. "Default" is used for when you first open Recollection.
-//Current sections (make sure to update this if you add one:
-//- Basics
-//- Terminology
-//- Components
-//- Scripture
-//- Power
-//- Conversion
+/obj/item/clockwork/slab/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ClockworkSlab", name)
+ ui.open()
/obj/item/clockwork/slab/ui_data(mob/user) //we display a lot of data via TGUI
. = list()
.["recollection"] = recollecting
.["power"] = DisplayPower(get_clockwork_power())
.["power_unformatted"] = get_clockwork_power()
- // .["rec_text"] = recollection() handled TGUI side
.["HONOR_RATVAR"] = GLOB.ratvar_awakens
.["scripture"] = list()
- for(var/s in GLOB.all_scripture)
+ for(var/s in GLOB.all_scripture) //don't block this, even when ratvar spawns for roundend griff.
var/datum/clockwork_scripture/S = GLOB.all_scripture[s]
- if(S.tier == SCRIPTURE_PERIPHERAL) //yes, tiers are the tabs.
+ if(S.tier == SCRIPTURE_PERIPHERAL) // This tier is skiped because this contains basetype stuff
continue
var/list/data = list()
@@ -253,7 +280,7 @@
data["required"] = "([DisplayPower(S.power_cost)][S.special_power_text ? "+ [replacetext(S.special_power_text, "POWERCOST", "[DisplayPower(S.special_power_cost)]")]" : ""])"
data["required_unformatted"] = S.power_cost
data["type"] = "[S.type]"
- data["quickbind"] = S.quickbind //this is if it cant quickbind
+ data["quickbind"] = S.quickbind //this is if it cant quickbind (bool)
data["fontcolor"] = get_component_color_bright(S.primary_component)
data["important"] = S.important //italic!
@@ -265,10 +292,8 @@
.["rec_binds"] = list()
for(var/i in 1 to maximum_quickbound)
- if(GLOB.ratvar_awakens)
- return
if(LAZYLEN(quickbound) < i || !quickbound[i])
- .["rec_binds"] += list(list())
+ .["rec_binds"] += list(list()) //a blank json.
else
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
.["rec_binds"] += list(list(
@@ -280,7 +305,11 @@
/obj/item/clockwork/slab/ui_static_data(mob/user)
. = list()
- .["tier_infos"] = list()
+ .["tier_infos"] = list() //HEY!! WHEN ADDING NEW TIER, ADD IT HERE
+ .["tier_infos"][SCRIPTURE_PERIPHERAL] = list(
+ "requirement" = "Breaking the code DM side. Report to coggerbus if this appears!!",
+ "ready" = FALSE //just in case. Should NOT exist at all
+ )
.["tier_infos"][SCRIPTURE_DRIVER] = list(
"requirement" = "None, this is already unlocked",
"ready" = TRUE //to bold it on JS side, and to say "These scriptures are permanently unlocked."
@@ -297,10 +326,10 @@
"requirement" = "Unlock powerful equipment and structures by converting five servants or if [DisplayPower(JUDGEMENT_UNLOCK_THRESHOLD)] of power is reached..",
"ready" = SSticker.scripture_states[SCRIPTURE_JUDGEMENT]
)
-
- // .["selected"] = selected_scripture
- generate_all_scripture()
- .["recollection_categories"] = GLOB.ratvar_awakens ? list() : list(
+ .["recollection_categories"] = list()
+ if(GLOB.ratvar_awakens)
+ return
+ .["recollection_categories"] = list(
list("name" = "Getting Started", "desc" = "First-time servant? Read this first."),
list("name" = "Basics", "desc" = "A primer on how to play as a servant."),
list("name" = "Terminology", "desc" = "Common acronyms, words, and terms."),
@@ -309,8 +338,9 @@
list("name" = "Power", "desc" = "The power system that certain objects use to function."),
list("name" = "Conversion", "desc" = "Converting the crew, cyborgs, and very walls to your cause.")
)
- // .["rec_section"]["title"] //this is here if ever we decided to return these back.
- // .["rec_section"]["info"]// wall of info for the thing
+ .["rec_section"] = get_recollection(recollection_category)
+ generate_all_scripture()
+ //needs a new place to live, preferably when clockcult unlocks/downgrades a tier. Smart enough to earlyreturn.
/obj/item/clockwork/slab/ui_act(action, params)
switch(action)
@@ -369,4 +399,4 @@
Q.button_icon_state = quickbind_slot.name
Q.UpdateButtonIcon()
if(isliving(loc))
- Q.Grant(loc)
\ No newline at end of file
+ Q.Grant(loc)
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index 5c011b6318..a18906b70b 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -7,17 +7,13 @@
density = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- ui_x = 350
- ui_y = 442
-
var/timer_set = 90
- var/default_timer_set = 90
var/minimum_timer_set = 90
var/maximum_timer_set = 3600
- ui_style = "nanotrasen"
var/numeric_input = ""
var/ui_mode = NUKEUI_AWAIT_DISK
+
var/timing = FALSE
var/exploding = FALSE
var/exploded = FALSE
@@ -34,7 +30,6 @@
var/interior = ""
var/proper_bomb = TRUE //Please
var/obj/effect/countdown/nuclearbomb/countdown
- var/nuclear_cooldown //used to stop global spam.
/obj/machinery/nuclearbomb/Initialize()
. = ..()
@@ -77,15 +72,16 @@
/obj/machinery/nuclearbomb/syndicate/get_cinematic_type(off_station)
var/datum/game_mode/nuclear/NM = SSticker.mode
switch(off_station)
- if(FALSE)
+ if(0)
if(istype(NM) && !NM.nuke_team.syndies_escaped())
return CINEMATIC_ANNIHILATION
else
return CINEMATIC_NUKE_WIN
- if(NUKE_MISS_STATION)
+ if(1)
return CINEMATIC_NUKE_MISS
- else
+ if(2)
return CINEMATIC_NUKE_FAR
+ return CINEMATIC_NUKE_FAR
/obj/machinery/nuclearbomb/proc/disk_check(obj/item/disk/nuclear/D)
if(D.fake)
@@ -194,7 +190,7 @@
icon_state = "nuclearbomb_exploding"
/obj/machinery/nuclearbomb/update_overlays()
- . = ..()
+ . += ..()
update_icon_interior()
update_icon_lights()
@@ -236,7 +232,7 @@
explode()
else
var/volume = (get_time_left() <= 20 ? 30 : 5)
- playsound(loc, 'sound/items/timer.ogg', volume, 0)
+ playsound(loc, 'sound/items/timer.ogg', volume, FALSE)
/obj/machinery/nuclearbomb/proc/update_ui_mode()
if(exploded)
@@ -261,17 +257,18 @@
ui_mode = NUKEUI_AWAIT_TIMER
-
-/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state=GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/nuclearbomb/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "NuclearBomb", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "NuclearBomb", name)
ui.open()
/obj/machinery/nuclearbomb/ui_data(mob/user)
var/list/data = list()
data["disk_present"] = auth
+
var/hidden_code = (ui_mode == NUKEUI_AWAIT_CODE && numeric_input != "ERROR")
+
var/current_code = ""
if(hidden_code)
while(length(current_code) < length(numeric_input))
@@ -388,14 +385,13 @@
if("anchor")
if(auth && yes_code)
playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
- set_anchor(usr)
+ set_anchor()
else
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
-
-/obj/machinery/nuclearbomb/proc/set_anchor(mob/user)
- if((istype(get_area(src), /area/space) || isinspace()) && !anchored)
- to_chat(user, "This is not a suitable platform for anchoring [src]! ")
+/obj/machinery/nuclearbomb/proc/set_anchor()
+ if(isinspace() && !anchored)
+ to_chat(usr, "There is nothing to anchor to! ")
else
anchored = !anchored
@@ -416,9 +412,6 @@
if(safety)
to_chat(usr, "The safety is still on. ")
return
- if(!timing && nuclear_cooldown > world.time)
- to_chat(usr, "[src]'s timer protocols are currently on cooldown, please stand by. ")
- return
timing = !timing
if(timing)
previous_level = NUM2SECLEVEL(GLOB.security_level)
@@ -427,12 +420,6 @@
S.switch_mode_to(TRACK_INFILTRATOR)
countdown.start()
set_security_level("delta")
- nuclear_cooldown = world.time + 15 SECONDS
-
- if(GLOB.war_declared)
- var/area/A = get_area(src)
- priority_announce("Alert: Unexpected increase in radiation levels near [A.name] ([src.x],[src.y],[src.z]). Please send an authorized radiation specialist to investigate.", "Sensory Nuclear Indexer Telemetry Calculation Helper")
-
else
detonation_timer = null
set_security_level(previous_level)
@@ -483,12 +470,19 @@
var/off_station = FALSE
var/turf/bomb_location = get_turf(src)
- if(!bomb_location || !is_station_level(bomb_location.z))
- off_station = NUKE_MISS_STATION
+ var/area/A = get_area(bomb_location)
+
+ if(bomb_location && is_station_level(bomb_location.z))
+ if(istype(A, /area/space))
+ off_station = NUKE_NEAR_MISS
+ if((bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)))
+ off_station = NUKE_NEAR_MISS
else if(bomb_location.onSyndieBase())
off_station = NUKE_SYNDICATE_BASE
+ else
+ off_station = NUKE_MISS_STATION
- if(!off_station)
+ if(off_station < 2) //can only launch when nuke is on syndie base or space
SSshuttle.registerHostileEnvironment(src)
SSshuttle.lockdown = TRUE
@@ -502,13 +496,13 @@
INVOKE_ASYNC(GLOBAL_PROC,.proc/KillEveryoneOnZLevel, z)
/obj/machinery/nuclearbomb/proc/get_cinematic_type(off_station)
- if(!off_station)
+ if(off_station < 2)
return CINEMATIC_SELFDESTRUCT
else
return CINEMATIC_SELFDESTRUCT_MISS
/obj/machinery/nuclearbomb/beer
- name = "Nanotrasen-brand nuclear fission explosive"
+ name = "\improper Nanotrasen-brand nuclear fission explosive"
desc = "One of the more successful achievements of the Nanotrasen Corporate Warfare Division, their nuclear fission explosives are renowned for being cheap to produce and devastatingly effective. Signs explain that though this particular device has been decommissioned, every Nanotrasen station is equipped with an equivalent one, just in case. All Captains carefully guard the disk needed to detonate them - at least, the sign says they do. There seems to be a tap on the back."
proper_bomb = FALSE
var/obj/structure/reagent_dispensers/beerkeg/keg
@@ -521,9 +515,9 @@
/obj/machinery/nuclearbomb/beer/examine(mob/user)
. = ..()
if(keg.reagents.total_volume)
- . += "It has [keg.reagents.total_volume] unit\s left. "
+ to_chat(user, "It has [keg.reagents.total_volume] unit\s left. ")
else
- . += "It's empty. "
+ to_chat(user, "It's empty. ")
/obj/machinery/nuclearbomb/beer/attackby(obj/item/W, mob/user, params)
if(W.is_refillable())
@@ -535,6 +529,8 @@
return ..()
/obj/machinery/nuclearbomb/beer/actually_explode()
+ //Unblock roundend, we're not actually exploding.
+ SSticker.roundend_check_paused = FALSE
var/turf/bomb_location = get_turf(src)
if(!bomb_location)
disarm()
@@ -583,7 +579,7 @@
This is here to make the tiles around the station mininuke change when it's armed.
*/
-/obj/machinery/nuclearbomb/selfdestruct/set_anchor(mob/user)
+/obj/machinery/nuclearbomb/selfdestruct/set_anchor()
return
/obj/machinery/nuclearbomb/selfdestruct/set_active()
@@ -641,18 +637,19 @@ This is here to make the tiles around the station mininuke change when it's arme
if(newturf && lastlocation == newturf)
if(last_disk_move < world.time - 5000 && prob((world.time - 5000 - last_disk_move)*0.0001))
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
- if(istype(loneop))
+ if(istype(loneop) && loneop.occurrences < loneop.max_occurrences)
loneop.weight += 1
- if(loneop.weight % 5 == 0)
+ if(loneop.weight % 5 == 0 && SSticker.totalPlayers > 1) //players count now
message_admins("[src] is stationary in [ADMIN_VERBOSEJMP(newturf)]. The weight of Lone Operative is now [loneop.weight].")
log_game("[src] is stationary for too long in [loc_name(newturf)], and has increased the weight of the Lone Operative event to [loneop.weight].")
+
else
lastlocation = newturf
last_disk_move = world.time
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
- if(istype(loneop) && prob(loneop.weight))
+ if(istype(loneop) && loneop.occurrences < loneop.max_occurrences && prob(loneop.weight))
loneop.weight = max(loneop.weight - 1, 0)
- if(loneop.weight % 5 == 0)
+ if(loneop.weight % 5 == 0 && SSticker.totalPlayers > 1)
message_admins("[src] is on the move (currently in [ADMIN_VERBOSEJMP(newturf)]). The weight of Lone Operative is now [loneop.weight].")
log_game("[src] being on the move has reduced the weight of the Lone Operative event to [loneop.weight].")
@@ -661,9 +658,19 @@ This is here to make the tiles around the station mininuke change when it's arme
if(!fake)
return
- if(isobserver(user) || HAS_TRAIT(user, TRAIT_DISK_VERIFIER) || (user.mind && HAS_TRAIT(user.mind, TRAIT_DISK_VERIFIER)))
+ if(isobserver(user) || HAS_TRAIT(user.mind, TRAIT_DISK_VERIFIER))
. += "The serial numbers on [src] are incorrect. "
+/*
+ * You can't accidentally eat the nuke disk, bro
+ */
+ /*
+/obj/item/disk/nuclear/on_accidental_consumption(mob/living/carbon/M, mob/living/carbon/user, obj/item/source_item, discover_after = TRUE)
+ M.visible_message("[M] looks like [M.p_theyve()] just bitten into something important. ", \
+ "Wait, is this the nuke disk? ")
+
+ return discover_after
+*/
/obj/item/disk/nuclear/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/claymore/highlander) && !fake)
var/obj/item/claymore/highlander/H = I
@@ -686,7 +693,7 @@ This is here to make the tiles around the station mininuke change when it's arme
/obj/item/disk/nuclear/suicide_act(mob/user)
user.visible_message("[user] is going delta! It looks like [user.p_theyre()] trying to commit suicide! ")
- playsound(src, 'sound/machines/alarm.ogg', 50, -1, 1)
+ playsound(src, 'sound/machines/alarm.ogg', 50, -1, TRUE)
for(var/i in 1 to 100)
addtimer(CALLBACK(user, /atom/proc/add_atom_colour, (i % 2)? "#00FF00" : "#FF0000", ADMIN_COLOUR_PRIORITY), i)
addtimer(CALLBACK(src, .proc/manual_suicide, user), 101)
@@ -694,7 +701,7 @@ This is here to make the tiles around the station mininuke change when it's arme
/obj/item/disk/nuclear/proc/manual_suicide(mob/living/user)
user.remove_atom_colour(ADMIN_COLOUR_PRIORITY)
- user.visible_message("[user] was destroyed by the nuclear blast! ")
+ user.visible_message("[user] is destroyed by the nuclear blast! ")
user.adjustOxyLoss(200)
user.death(0)
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index 6e8e2846a2..899eb12511 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -4,8 +4,6 @@
icon_state = "infrared"
custom_materials = list(/datum/material/iron=1000, /datum/material/glass=500)
is_position_sensitive = TRUE
- var/ui_x = 225
- var/ui_y = 110
var/on = FALSE
var/visible = FALSE
var/maxlength = 8
@@ -134,7 +132,7 @@
. = ..()
setDir(t)
-/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE)
+/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
. = ..()
olddir = dir
@@ -186,11 +184,10 @@
return ..()
return UI_CLOSE
-/obj/item/assembly/infra/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/assembly/infra/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "InfraredEmitter", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "InfraredEmitter", name)
ui.open()
/obj/item/assembly/infra/ui_data(mob/user)
diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm
index f0d8f28688..60de1b3e87 100644
--- a/code/modules/assembly/proximity.dm
+++ b/code/modules/assembly/proximity.dm
@@ -4,8 +4,6 @@
icon_state = "prox"
custom_materials = list(/datum/material/iron=800, /datum/material/glass=200)
attachable = TRUE
- var/ui_x = 250
- var/ui_y = 185
var/scanning = FALSE
var/timing = FALSE
var/time = 10
@@ -113,11 +111,10 @@
return ..()
return UI_CLOSE
-/obj/item/assembly/prox_sensor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/assembly/prox_sensor/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "ProximitySensor", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "ProximitySensor", name)
ui.open()
/obj/item/assembly/prox_sensor/ui_data(mob/user)
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index fe548d662d..e586f97a28 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -8,33 +8,46 @@
custom_materials = list(/datum/material/iron=400, /datum/material/glass=120)
wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE
attachable = TRUE
- var/ui_x = 280
- var/ui_y = 132
+
var/code = DEFAULT_SIGNALER_CODE
var/frequency = FREQ_SIGNALER
var/datum/radio_frequency/radio_connection
- var/suicider = null
+ ///Holds the mind that commited suicide.
+ var/datum/mind/suicider
+ ///Holds a reference string to the mob, decides how much of a gamer you are.
+ var/suicide_mob
var/hearing_range = 1
/obj/item/assembly/signaler/suicide_act(mob/living/carbon/user)
user.visible_message("[user] eats \the [src]! If it is signaled, [user.p_they()] will die! ")
playsound(src, 'sound/items/eatfood.ogg', 50, TRUE)
- user.transferItemToLoc(src, user, TRUE)
- suicider = user
+ moveToNullspace()
+ suicider = user.mind
+ suicide_mob = REF(user)
return MANUAL_SUICIDE
-/obj/item/assembly/signaler/proc/manual_suicide(mob/living/carbon/user)
- user.visible_message("[user]'s \the [src] receives a signal, killing [user.p_them()] instantly! ")
+/obj/item/assembly/signaler/proc/manual_suicide(datum/mind/suicidee)
+ var/mob/living/user = suicidee.current
+ if(!istype(user))
+ return
+ if(suicide_mob == REF(user))
+ user.visible_message("[user]'s [src] receives a signal, killing [user.p_them()] instantly! ")
+ else
+ user.visible_message("[user]'s [src] receives a signal and [user.p_they()] die[user.p_s()] like a gamer! ")
user.adjustOxyLoss(200)//it sends an electrical pulse to their heart, killing them. or something.
user.death(0)
+ //user.set_suicide(TRUE)
+ user.suicide_log()
+ playsound(user, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
+ qdel(src)
/obj/item/assembly/signaler/Initialize()
. = ..()
set_frequency(frequency)
-
/obj/item/assembly/signaler/Destroy()
SSradio.remove_object(src,frequency)
+ suicider = null
. = ..()
/obj/item/assembly/signaler/activate()
@@ -53,11 +66,10 @@
return ..()
return UI_CLOSE
-/obj/item/assembly/signaler/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/assembly/signaler/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Signaler", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "Signaler", name)
ui.open()
/obj/item/assembly/signaler/ui_data(mob/user)
@@ -66,12 +78,12 @@
data["code"] = code
data["minFrequency"] = MIN_FREE_FREQ
data["maxFrequency"] = MAX_FREE_FREQ
-
return data
/obj/item/assembly/signaler/ui_act(action, params)
if(..())
return
+
switch(action)
if("signal")
INVOKE_ASYNC(src, .proc/signal)
@@ -115,9 +127,6 @@
if(usr)
GLOB.lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]")
-
- return
-
/obj/item/assembly/signaler/receive_signal(datum/signal/signal)
. = FALSE
if(!signal)
@@ -128,6 +137,7 @@
return
if(suicider)
manual_suicide(suicider)
+ return
pulse(TRUE)
audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
for(var/CHM in get_hearers_in_view(hearing_range, src))
@@ -136,7 +146,6 @@
LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
return TRUE
-
/obj/item/assembly/signaler/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
@@ -165,69 +174,8 @@
return
return ..(signal)
-// Embedded signaller used in anomalies.
-/obj/item/assembly/signaler/anomaly
- name = "anomaly core"
- desc = "The neutralized core of an anomaly. It'd probably be valuable for research."
- icon_state = "anomaly core"
- item_state = "electronic"
- lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
- resistance_flags = FIRE_PROOF
- var/anomaly_type = /obj/effect/anomaly
-
-/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal)
- if(!signal)
- return FALSE
- if(signal.data["code"] != code)
- return FALSE
- if(suicider)
- manual_suicide(suicider)
- for(var/obj/effect/anomaly/A in get_turf(src))
- A.anomalyNeutralize()
- return TRUE
-
-/obj/item/assembly/signaler/anomaly/manual_suicide(mob/living/carbon/user)
- user.visible_message("[user]'s [src] is reacting to the radio signal, warping [user.p_their()] body! ")
- user.suiciding = TRUE
- user.suicide_log()
- user.gib()
-
-/obj/item/assembly/signaler/anomaly/attackby(obj/item/I, mob/user, params)
- if(I.tool_behaviour == TOOL_ANALYZER)
- to_chat(user, "Analyzing... [src]'s stabilized field is fluctuating along frequency [format_frequency(frequency)], code [code]. ")
- ..()
-
-//Anomaly cores
-/obj/item/assembly/signaler/anomaly/pyro
- name = "\improper pyroclastic anomaly core"
- desc = "The neutralized core of a pyroclastic anomaly. It feels warm to the touch. It'd probably be valuable for research."
- icon_state = "pyro core"
- anomaly_type = /obj/effect/anomaly/pyro
-
-/obj/item/assembly/signaler/anomaly/grav
- name = "\improper gravitational anomaly core"
- desc = "The neutralized core of a gravitational anomaly. It feels much heavier than it looks. It'd probably be valuable for research."
- icon_state = "grav core"
- anomaly_type = /obj/effect/anomaly/grav
-
-/obj/item/assembly/signaler/anomaly/flux
- name = "\improper flux anomaly core"
- desc = "The neutralized core of a flux anomaly. Touching it makes your skin tingle. It'd probably be valuable for research."
- icon_state = "flux core"
- anomaly_type = /obj/effect/anomaly/flux
-
-/obj/item/assembly/signaler/anomaly/bluespace
- name = "\improper bluespace anomaly core"
- desc = "The neutralized core of a bluespace anomaly. It keeps phasing in and out of view. It'd probably be valuable for research."
- icon_state = "anomaly core"
- anomaly_type = /obj/effect/anomaly/bluespace
-
-/obj/item/assembly/signaler/anomaly/vortex
- name = "\improper vortex anomaly core"
- desc = "The neutralized core of a vortex anomaly. It won't sit still, as if some invisible force is acting on it. It'd probably be valuable for research."
- icon_state = "vortex core"
- anomaly_type = /obj/effect/anomaly/bhole
+/obj/item/assembly/signaler/anomaly/attack_self()
+ return
/obj/item/assembly/signaler/cyborg
diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm
index d0f0e1e593..e06bb22b0b 100644
--- a/code/modules/assembly/timer.dm
+++ b/code/modules/assembly/timer.dm
@@ -4,8 +4,7 @@
icon_state = "timer"
custom_materials = list(/datum/material/iron=500, /datum/material/glass=50)
attachable = TRUE
- var/ui_x = 275
- var/ui_y = 115
+
var/timing = FALSE
var/time = 5
var/saved_time = 5
@@ -88,11 +87,10 @@
return ..()
return UI_CLOSE
-/obj/item/assembly/timer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/assembly/timer/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Timer", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "Timer", name)
ui.open()
/obj/item/assembly/timer/ui_data(mob/user)
diff --git a/code/modules/asset_cache/asset_cache.dm b/code/modules/asset_cache/asset_cache.dm
index f702bf714e..53a30d4299 100644
--- a/code/modules/asset_cache/asset_cache.dm
+++ b/code/modules/asset_cache/asset_cache.dm
@@ -94,6 +94,13 @@ Note: If your code uses output() with assets you will need to call asset_flush o
var/list/stacktrace = gib_stack_trace()
log_asset("WARNING: dupe asset added to the asset cache: [asset_name] existing asset md5: [OACI.md5] new asset md5:[ACI.md5]\n[stacktrace.Join("\n")]")
SSassets.cache[asset_name] = ACI
+ return ACI
+
+/// Returns the url of the asset, currently this is just its name, here to allow further work cdn'ing assets.
+/// Can be given an asset as well, this is just a work around for buggy edge cases where two assets may have the same name, doesn't matter now, but it will when the cdn comes.
+/proc/get_asset_url(asset_name, asset = null)
+ var/datum/asset_cache_item/ACI = SSassets.cache[asset_name]
+ return ACI?.url
//Generated names do not include file extention.
//Used mainly for code that deals with assets in a generic way
diff --git a/code/modules/asset_cache/asset_cache_item.dm b/code/modules/asset_cache/asset_cache_item.dm
index 0e7d44a7ac..e74293c65e 100644
--- a/code/modules/asset_cache/asset_cache_item.dm
+++ b/code/modules/asset_cache/asset_cache_item.dm
@@ -5,6 +5,7 @@
**/
/datum/asset_cache_item
var/name
+ var/url
var/md5
var/resource
@@ -18,4 +19,5 @@
CRASH("invalid asset sent to asset cache")
debug_world_log("asset cache unexpected success of second fcopy_rsc")
src.name = name
+ url = name
resource = file
diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm
index 2e5881c67f..4ce9dcf6fc 100644
--- a/code/modules/asset_cache/asset_list.dm
+++ b/code/modules/asset_cache/asset_list.dm
@@ -16,6 +16,9 @@ GLOBAL_LIST_EMPTY(asset_datums)
GLOB.asset_datums[type] = src
register()
+/datum/asset/proc/get_url_mappings()
+ return list()
+
/datum/asset/proc/register()
return
@@ -30,11 +33,19 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/simple/register()
for(var/asset_name in assets)
- register_asset(asset_name, assets[asset_name])
+ assets[asset_name] = register_asset(asset_name, assets[asset_name])
/datum/asset/simple/send(client)
. = send_asset_list(client, assets)
+/datum/asset/simple/get_url_mappings()
+ . = list()
+ for (var/asset_name in assets)
+ var/datum/asset_cache_item/ACI = assets[asset_name]
+ if (!ACI)
+ continue
+ .[asset_name] = ACI.url
+
// For registering or sending multiple others at once
/datum/asset/group
@@ -50,6 +61,11 @@ GLOBAL_LIST_EMPTY(asset_datums)
var/datum/asset/A = get_asset_datum(type)
. = A.send(C) || .
+/datum/asset/group/get_url_mappings()
+ . = list()
+ for(var/type in children)
+ var/datum/asset/A = get_asset_datum(type)
+ . += A.get_url_mappings()
// spritesheet implementation - coalesces various icons into a single .png file
// and uses CSS to select icons out of that file - saves on transferring some
@@ -70,7 +86,9 @@ GLOBAL_LIST_EMPTY(asset_datums)
if (!name)
CRASH("spritesheet [type] cannot register without a name")
ensure_stripped()
-
+ for(var/size_id in sizes)
+ var/size = sizes[size_id]
+ register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
var/res_name = "spritesheet_[name].css"
var/fname = "data/spritesheets/[res_name]"
fdel(fname)
@@ -78,10 +96,6 @@ GLOBAL_LIST_EMPTY(asset_datums)
register_asset(res_name, fcopy_rsc(fname))
fdel(fname)
- for(var/size_id in sizes)
- var/size = sizes[size_id]
- register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
-
/datum/asset/spritesheet/send(client/C)
if (!name)
return
@@ -90,6 +104,15 @@ GLOBAL_LIST_EMPTY(asset_datums)
all += "[name]_[size_id].png"
. = send_asset_list(C, all)
+/datum/asset/spritesheet/get_url_mappings()
+ if (!name)
+ return
+ . = list("spritesheet_[name].css" = get_asset_url("spritesheet_[name].css"))
+ for(var/size_id in sizes)
+ .["[name]_[size_id].png"] = get_asset_url("[name]_[size_id].png")
+
+
+
/datum/asset/spritesheet/proc/ensure_stripped(sizes_to_strip = sizes)
for(var/size_id in sizes_to_strip)
var/size = sizes[size_id]
@@ -111,7 +134,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
for (var/size_id in sizes)
var/size = sizes[size_id]
var/icon/tiny = size[SPRSZ_ICON]
- out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[name]_[size_id].png') no-repeat;}"
+ out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[get_asset_url("[name]_[size_id].png")]') no-repeat;}"
for (var/sprite_id in sprites)
var/sprite = sprites[sprite_id]
@@ -162,7 +185,10 @@ GLOBAL_LIST_EMPTY(asset_datums)
Insert("[prefix][prefix2][icon_state_name]", I, icon_state=icon_state_name, dir=direction)
/datum/asset/spritesheet/proc/css_tag()
- return {" "}
+ return {" "}
+
+/datum/asset/spritesheet/proc/css_filename()
+ return get_asset_url("spritesheet_[name].css")
/datum/asset/spritesheet/proc/icon_tag(sprite_name)
var/sprite = sprites[sprite_name]
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index 57d8e55e07..d4ab7b5648 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -6,12 +6,6 @@
"tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css',
)
-/datum/asset/group/tgui
- children = list(
- /datum/asset/simple/tgui,
- /datum/asset/simple/fontawesome
- )
-
/datum/asset/simple/headers
assets = list(
"alarm_green.gif" = 'icons/program_icons/alarm_green.gif',
@@ -78,6 +72,7 @@
"refresh" = 'icons/pda_icons/pda_refresh.png',
"scanner" = 'icons/pda_icons/pda_scanner.png',
"signaler" = 'icons/pda_icons/pda_signaler.png',
+ // "skills" = 'icons/pda_icons/pda_skills.png',
"status" = 'icons/pda_icons/pda_status.png',
"dronephone" = 'icons/pda_icons/pda_dronephone.png',
"emoji" = 'icons/pda_icons/pda_emoji.png'
@@ -97,6 +92,10 @@
"stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png',
"stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png',
"stamp-law" = 'icons/stamp_icons/large_stamp-law.png'
+ // "stamp-chap" = 'icons/stamp_icons/large_stamp-chap.png'
+ // "stamp-mime" = 'icons/stamp_icons/large_stamp-mime.png',
+ // "stamp-centcom" = 'icons/stamp_icons/large_stamp-centcom.png',
+ // "stamp-syndicate" = 'icons/stamp_icons/large_stamp-syndicate.png'
)
@@ -147,7 +146,6 @@
"jquery.min.js" = 'code/modules/goonchat/browserassets/js/jquery.min.js',
)
-
/datum/asset/simple/goonchat
assets = list(
"json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js',
@@ -375,7 +373,18 @@
"ghost.png" = 'html/ghost.png'
)
+/datum/asset/simple/vv
assets = list(
- "ghost.png" = 'html/ghost.png'
+ "view_variables.css" = 'html/admin/view_variables.css'
)
+/datum/asset/spritesheet/sheetmaterials
+ name = "sheetmaterials"
+
+/datum/asset/spritesheet/sheetmaterials/register()
+ InsertAll("", 'icons/obj/stack_objects.dmi')
+
+ // Special case to handle Bluespace Crystals
+ Insert("polycrystal", 'icons/obj/telescience.dmi', "polycrystal")
+ ..()
+
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index 6b89deeb80..8316b29a24 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -237,11 +237,10 @@
return ..()
return UI_CLOSE
-/obj/machinery/airalarm/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/airalarm/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AirAlarm", name, 440, 650, master_ui, state)
+ ui = new(user, src, "AirAlarm", name)
ui.open()
/obj/machinery/airalarm/ui_data(mob/user)
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
index 9e1908a500..00a085c31b 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
@@ -26,9 +26,6 @@ Passive gate is similar to the regular pump except:
construction_type = /obj/item/pipe/directional
pipe_state = "passivegate"
- ui_x = 335
- ui_y = 115
-
/obj/machinery/atmospherics/components/binary/passive_gate/CtrlClick(mob/user)
if(can_interact(user))
on = !on
@@ -102,11 +99,10 @@ Passive gate is similar to the regular pump except:
))
radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
-/obj/machinery/atmospherics/components/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/components/binary/passive_gate/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "AtmosPump", name)
ui.open()
/obj/machinery/atmospherics/components/binary/passive_gate/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
index d41df83e12..eb00e432b7 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
@@ -107,11 +107,10 @@
))
radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
-/obj/machinery/atmospherics/components/binary/pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/components/binary/pump/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AtmosPump", name, 335, 115, master_ui, state)
+ ui = new(user, src, "AtmosPump", name)
ui.open()
/obj/machinery/atmospherics/components/binary/pump/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm
index 7bdd22cbd1..d0b663e4ad 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm
@@ -60,15 +60,14 @@
else if(!opened && our_pressure >= open_pressure)
open()
-/obj/machinery/atmospherics/components/binary/relief_valve/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/components/binary/relief_valve/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "atmos_relief", name, 335, 115, master_ui, state)
+ ui = new(user, src, "AtmosRelief", name)
ui.open()
/obj/machinery/atmospherics/components/binary/relief_valve/ui_data()
- var/data = list()
+ var/list/data = list()
data["open_pressure"] = round(open_pressure)
data["close_pressure"] = round(close_pressure)
data["max_pressure"] = round(50*ONE_ATMOSPHERE)
@@ -79,11 +78,11 @@
return
switch(action)
if("open_pressure")
- var/pressure = params["open_pressure"]
+ var/pressure = params["pressure"]
if(pressure == "max")
pressure = 50*ONE_ATMOSPHERE
. = TRUE
- else if(pressure == "input")
+ else if(pressure == "input") // The manual expirience.
pressure = input("New output pressure ([close_pressure]-[50*ONE_ATMOSPHERE] kPa):", name, open_pressure) as num|null
if(!isnull(pressure) && !..())
. = TRUE
@@ -94,7 +93,7 @@
open_pressure = clamp(pressure, close_pressure, 50*ONE_ATMOSPHERE)
investigate_log("open pressure was set to [open_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS)
if("close_pressure")
- var/pressure = params["close_pressure"]
+ var/pressure = params["pressure"]
if(pressure == "max")
pressure = open_pressure
. = TRUE
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
index d95e4ec123..1b049322a1 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
@@ -92,11 +92,10 @@
))
radio_connection.post_signal(src, signal)
-/obj/machinery/atmospherics/components/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/components/binary/volume_pump/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AtmosPump", name, 310, 115, master_ui, state)
+ ui = new(user, src, "AtmosPump", name)
ui.open()
/obj/machinery/atmospherics/components/binary/volume_pump/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
index dd6952dfc6..ffab6a885c 100644
--- a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
+++ b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
@@ -133,11 +133,10 @@
set_frequency(frequency)
return ..()
-/obj/machinery/atmospherics/components/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/components/trinary/filter/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AtmosFilter", name, 475, 185, master_ui, state)
+ ui = new(user, src, "AtmosFilter", name)
ui.open()
/obj/machinery/atmospherics/components/trinary/filter/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
index 758d27ab61..7dac6d540e 100644
--- a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
+++ b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
@@ -14,9 +14,6 @@
construction_type = /obj/item/pipe/trinary/flippable
pipe_state = "mixer"
- ui_x = 370
- ui_y = 165
-
//node 3 is the outlet, nodes 1 & 2 are intakes
/obj/machinery/atmospherics/components/trinary/mixer/CtrlClick(mob/user)
@@ -127,11 +124,10 @@
var/datum/pipeline/parent3 = parents[3]
parent3.update = TRUE
-/obj/machinery/atmospherics/components/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/components/trinary/mixer/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AtmosMixer", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "AtmosMixer", name)
ui.open()
/obj/machinery/atmospherics/components/trinary/mixer/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index 0f849af0e3..c08eaf2e8a 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -332,11 +332,13 @@
return
return ..()
-/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/components/unary/cryo_cell/ui_state(mob/user)
+ return GLOB.notcontained_state
+
+/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Cryo", name, 400, 550, master_ui, state)
+ ui = new(user, src, "Cryo", name)
ui.open()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
index 70672418a1..a07f131d62 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
@@ -20,9 +20,6 @@
pipe_state = "injector"
- ui_x = 310
- ui_y = 115
-
/obj/machinery/atmospherics/components/unary/outlet_injector/CtrlClick(mob/user)
if(can_interact(user))
on = !on
@@ -132,7 +129,7 @@
on = !on
if("inject" in signal.data)
- spawn inject()
+ INVOKE_ASYNC(src, .proc/inject)
return
if("set_volume_rate" in signal.data)
@@ -140,22 +137,16 @@
var/datum/gas_mixture/air_contents = airs[1]
volume_rate = clamp(number, 0, air_contents.return_volume())
- if("status" in signal.data)
- spawn(2)
- broadcast_status()
- return //do not update_icon
+ addtimer(CALLBACK(src, .proc/broadcast_status), 2)
- spawn(2)
- broadcast_status()
-
- update_icon()
+ if(!("status" in signal.data)) //do not update_icon
+ update_icon()
-/obj/machinery/atmospherics/components/unary/outlet_injector/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/components/unary/outlet_injector/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "AtmosPump", name)
ui.open()
/obj/machinery/atmospherics/components/unary/outlet_injector/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm b/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm
index 0893d2b9e6..f0d0d1d856 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm
@@ -61,15 +61,14 @@
update_parents()
-/obj/machinery/atmospherics/components/unary/relief_valve/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/components/unary/relief_valve/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "atmos_relief", name, 335, 115, master_ui, state)
+ ui = new(user, src, "AtmosRelief", name)
ui.open()
/obj/machinery/atmospherics/components/unary/relief_valve/ui_data()
- var/data = list()
+ var/list/data = list()
data["open_pressure"] = round(open_pressure)
data["close_pressure"] = round(close_pressure)
data["max_pressure"] = round(50*ONE_ATMOSPHERE)
@@ -80,7 +79,7 @@
return
switch(action)
if("open_pressure")
- var/pressure = params["open_pressure"]
+ var/pressure = params["pressure"]
if(pressure == "max")
pressure = 50*ONE_ATMOSPHERE
. = TRUE
@@ -95,7 +94,7 @@
open_pressure = clamp(pressure, close_pressure, 50*ONE_ATMOSPHERE)
investigate_log("open pressure was set to [open_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS)
if("close_pressure")
- var/pressure = params["close_pressure"]
+ var/pressure = params["pressure"]
if(pressure == "max")
pressure = open_pressure
. = TRUE
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
index c6168a6c9c..8456c0b346 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
@@ -11,8 +11,6 @@
layer = OBJ_LAYER
plane = GAME_PLANE
circuit = /obj/item/circuitboard/machine/thermomachine
- ui_x = 300
- ui_y = 230
pipe_flags = PIPING_ONE_PER_TURF
@@ -125,11 +123,10 @@
return ..()
return UI_CLOSE
-/obj/machinery/atmospherics/components/unary/thermomachine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/components/unary/thermomachine/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "ThermoMachine", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "ThermoMachine", name)
ui.open()
/obj/machinery/atmospherics/components/unary/thermomachine/ui_data(mob/user)
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index 12ba7e79b9..c66aabd57f 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -5,8 +5,6 @@
desc = "A canister for the storage of gas."
icon_state = "yellow"
density = TRUE
- ui_x = 300
- ui_y = 232
var/valve_open = FALSE
var/obj/machinery/atmospherics/components/binary/passive_gate/pump
@@ -319,11 +317,13 @@ obj/machinery/portable_atmospherics/canister/welder_act(mob/living/user, obj/ite
air_update_turf() // Update the environment if needed.
update_icon()
-/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/portable_atmospherics/canister/ui_state(mob/user)
+ return GLOB.physical_state
+
+/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Canister", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "Canister", name)
ui.open()
/obj/machinery/portable_atmospherics/canister/ui_data()
diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm
index 7f1dc4d20f..bdde8f0f22 100644
--- a/code/modules/atmospherics/machinery/portable/pump.dm
+++ b/code/modules/atmospherics/machinery/portable/pump.dm
@@ -8,8 +8,6 @@
name = "portable air pump"
icon_state = "psiphon:0"
density = TRUE
- ui_x = 300
- ui_y = 315
var/on = FALSE
var/direction = PUMP_OUT
@@ -83,11 +81,10 @@
investigate_log("[key_name(user)] started a transfer into [holding].", INVESTIGATE_ATMOS)
-/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "PortablePump", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "PortablePump", name)
ui.open()
/obj/machinery/portable_atmospherics/pump/ui_data()
diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm
index bc703b8bbd..7976ba641a 100644
--- a/code/modules/atmospherics/machinery/portable/scrubber.dm
+++ b/code/modules/atmospherics/machinery/portable/scrubber.dm
@@ -62,11 +62,10 @@
on = !on
update_icon()
-/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "PortableScrubber", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "PortableScrubber", name)
ui.open()
/obj/machinery/portable_atmospherics/scrubber/ui_data()
diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm
index 157fdb7bc7..8476090f75 100644
--- a/code/modules/awaymissions/gateway.dm
+++ b/code/modules/awaymissions/gateway.dm
@@ -277,11 +277,10 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
. = ..()
try_to_linkup()
-/obj/machinery/computer/gateway_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui, force_open, datum/tgui/master_ui, datum/ui_state/state = GLOB.default_state)
- . = ..()
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/gateway_control/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Gateway", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "Gateway", name)
ui.open()
/obj/machinery/computer/gateway_control/ui_data(mob/user)
diff --git a/code/modules/cargo/bounty_console.dm b/code/modules/cargo/bounty_console.dm
index f499c38090..8a29715016 100644
--- a/code/modules/cargo/bounty_console.dm
+++ b/code/modules/cargo/bounty_console.dm
@@ -1,18 +1,18 @@
#define PRINTER_TIMEOUT 10
-
-
/obj/machinery/computer/bounty
- name = "Nanotrasen bounty console"
+ name = "\improper Nanotrasen bounty console"
desc = "Used to check and claim bounties offered by Nanotrasen"
icon_screen = "bounty"
circuit = /obj/item/circuitboard/computer/bounty
light_color = "#E2853D"//orange
var/printer_ready = 0 //cooldown var
+ var/static/datum/bank_account/cargocash
/obj/machinery/computer/bounty/Initialize()
. = ..()
printer_ready = world.time + PRINTER_TIMEOUT
+ cargocash = SSeconomy.get_dep_account(ACCOUNT_CAR)
/obj/machinery/computer/bounty/proc/print_paper()
new /obj/item/paper/bounty_printout(loc)
@@ -23,70 +23,43 @@
/obj/item/paper/bounty_printout/Initialize()
. = ..()
info = "Nanotrasen Cargo Bounties "
+ update_icon()
+
for(var/datum/bounty/B in GLOB.bounties_list)
if(B.claimed)
continue
info += {"[B.name]
- Reward: [B.reward_string()]
- Completed: [B.completion_string()] "}
-
-/obj/machinery/computer/bounty/ui_interact(mob/user)
- . = ..()
+ Reward: [B.reward_string()]
+ Completed: [B.completion_string()] "}
+/obj/machinery/computer/bounty/ui_interact(mob/user, datum/tgui/ui)
if(!GLOB.bounties_list.len)
setup_bounties()
- var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
- var/list/dat = list({"Refresh
- Print Paper
- Credits: [D.account_balance]
-
- Name Description Reward Completion Status "})
- for(var/datum/bounty/B in GLOB.bounties_list)
- if(B.claimed)
- dat += ""
- else if(B.can_claim())
- dat += " "
- else
- dat += " "
- if(B.high_priority)
- dat += {"[B.name]
- High Priority: [B.description]
- [B.reward_string()] "}
- else
- dat += {"[B.name]
- [B.description]
- [B.reward_string()] "}
- dat += "[B.completion_string()] "
- if(B.claimed)
- dat += "Claimed "
- else if(B.can_claim())
- dat += "Claim "
- else
- dat += "Unclaimed "
- dat += " "
- dat += "
"
- dat = dat.Join()
- var/datum/browser/popup = new(user, "bounties", "Nanotrasen Bounties", 700, 600)
- popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
- popup.open()
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "CargoBountyConsole", name)
+ ui.open()
-/obj/machinery/computer/bounty/Topic(href, href_list)
+/obj/machinery/computer/bounty/ui_data(mob/user)
+ var/list/data = list()
+ var/list/bountyinfo = list()
+ for(var/datum/bounty/B in GLOB.bounties_list)
+ bountyinfo += list(list("name" = B.name, "description" = B.description, "reward_string" = B.reward_string(), "completion_string" = B.completion_string() , "claimed" = B.claimed, "can_claim" = B.can_claim(), "priority" = B.high_priority, "bounty_ref" = REF(B)))
+ data["stored_cash"] = cargocash.account_balance
+ data["bountydata"] = bountyinfo
+ return data
+
+/obj/machinery/computer/bounty/ui_act(action,params)
if(..())
return
-
- switch(href_list["choice"])
+ switch(action)
+ if("ClaimBounty")
+ var/datum/bounty/cashmoney = locate(params["bounty"]) in GLOB.bounties_list
+ if(cashmoney)
+ cashmoney.claim()
+ return TRUE
if("Print")
if(printer_ready < world.time)
printer_ready = world.time + PRINTER_TIMEOUT
print_paper()
-
- if("Claim")
- var/datum/bounty/B = locate(href_list["d_rec"])
- if(B in GLOB.bounties_list)
- B.claim()
-
- if(href_list["refresh"])
- playsound(src, "terminal_type", 25, 0)
-
- updateUsrDialog()
+ return
diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm
index f33ea7059b..b7eac1e591 100644
--- a/code/modules/cargo/centcom_podlauncher.dm
+++ b/code/modules/cargo/centcom_podlauncher.dm
@@ -11,7 +11,7 @@
/client/proc/centcom_podlauncher() //Creates a verb for admins to open up the ui
set name = "Config/Launch Supplypod"
- set desc = "Configure and launch a Centcom supplypod full of whatever your heart desires!"
+ set desc = "Configure and launch a CentCom supplypod full of whatever your heart desires!"
set category = "Admin"
var/datum/centcom_podlauncher/plaunch = new(usr)//create the datum
plaunch.ui_interact(usr)//datum has a tgui component, here we open the window
@@ -23,7 +23,10 @@
var/turf/oldTurf //Keeps track of where the user was at if they use the "teleport to centcom" button, so they can go back
var/client/holder //client of whoever is using this datum
var/area/bay //What bay we're using to launch shit from.
+ var/turf/dropoff_turf //If we're reversing, where the reverse pods go
+ var/picking_dropoff_turf
var/launchClone = FALSE //If true, then we don't actually launch the thing in the bay. Instead we call duplicateObject() and send the result
+ var/launchRandomItem = FALSE //If true, lauches a single random item instead of everything on a turf.
var/launchChoice = 1 //Determines if we launch all at once (0) , in order (1), or at random(2)
var/explosionChoice = 0 //Determines if there is no explosion (0), custom explosion (1), or just do a maxcap (2)
var/damageChoice = 0 //Determines if we do no damage (0), custom amnt of damage (1), or gib + 5000dmg (2)
@@ -50,20 +53,25 @@
temp_pod = new(locate(/area/centcom/supplypod/podStorage) in GLOB.sortedAreas) //Create a new temp_pod in the podStorage area on centcom (so users are free to look at it and change other variables if needed)
orderedArea = createOrderedArea(bay) //Order all the turfs in the selected bay (top left to bottom right) to a single list. Used for the "ordered" mode (launchChoice = 1)
-/datum/centcom_podlauncher/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, \
-force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)//ui_interact is called when the client verb is called.
+/datum/centcom_podlauncher/ui_state(mob/user)
+ return GLOB.admin_state
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/centcom_podlauncher/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "CentcomPodLauncher", "Config/Launch Supplypod", 700, 700, master_ui, state)
+ ui = new(user, src, "CentcomPodLauncher")
ui.open()
/datum/centcom_podlauncher/ui_data(mob/user) //Sends info about the pod to the UI.
var/list/data = list() //*****NOTE*****: Many of these comments are similarly described in supplypod.dm. If you change them here, please consider doing so in the supplypod code as well!
- var/B = (istype(bay, /area/centcom/supplypod/loading/one)) ? 1 : (istype(bay, /area/centcom/supplypod/loading/two)) ? 2 : (istype(bay, /area/centcom/supplypod/loading/three)) ? 3 : (istype(bay, /area/centcom/supplypod/loading/four)) ? 4 : 0 //top ten THICCEST FUCKING TERNARY CONDITIONALS OF 2036
- data["bay"] = B //Holds the current bay the user is launching objects from. Bays are specific rooms on the centcom map.
+ var/B = (istype(bay, /area/centcom/supplypod/loading/one)) ? 1 : (istype(bay, /area/centcom/supplypod/loading/two)) ? 2 : (istype(bay, /area/centcom/supplypod/loading/three)) ? 3 : (istype(bay, /area/centcom/supplypod/loading/four)) ? 4 : 0 //(istype(bay, /area/centcom/supplypod/loading/ert)) ? 5 : 0 //top ten THICCEST FUCKING TERNARY CONDITIONALS OF 2036
+ data["bay"] = bay //Holds the current bay the user is launching objects from. Bays are specific rooms on the centcom map.
+ data["bayNumber"] = B //Holds the bay as a number. Useful for comparisons in centcom_podlauncher.ract
data["oldArea"] = (oldTurf ? get_area(oldTurf) : null) //Holds the name of the area that the user was in before using the teleportCentcom action
+ data["picking_dropoff_turf"] = picking_dropoff_turf //If we're picking or have picked a dropoff turf. Only works when pod is in reverse mode
+ data["dropoff_turf"] = dropoff_turf //The turf that reverse pods will drop their newly acquired cargo off at
data["launchClone"] = launchClone //Do we launch the actual items in the bay or just launch clones of them?
+ data["launchRandomItem"] = launchRandomItem //Do we launch a single random item instead of everything on the turf?
data["launchChoice"] = launchChoice //Launch turfs all at once (0), ordered (1), or randomly(1)
data["explosionChoice"] = explosionChoice //An explosion that occurs when landing. Can be no explosion (0), custom explosion (1), or maxcap (2)
data["damageChoice"] = damageChoice //Damage that occurs to any mob under the pod when it lands. Can be no damage (0), custom damage (1), or gib+5000dmg (2)
@@ -72,11 +80,12 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
data["openingDelay"] = temp_pod.openingDelay //How long the pod takes to open after landing
data["departureDelay"] = temp_pod.departureDelay //How long the pod takes to leave after opening (if bluespace=true, it deletes. if reversing=true, it flies back to centcom)
data["styleChoice"] = temp_pod.style //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the POD_STYLES list in cargo.dm defines to get the proper icon/name/desc for the pod.
+ data["effectShrapnel"] = FALSE //temp_pod.effectShrapnel //If true, creates a cloud of shrapnel of a decided type and magnitude on landing
data["effectStun"] = temp_pod.effectStun //If true, stuns anyone under the pod when it launches until it lands, forcing them to get hit by the pod. Devilish!
data["effectLimb"] = temp_pod.effectLimb //If true, pops off a limb (if applicable) from anyone caught under the pod when it lands
data["effectOrgans"] = temp_pod.effectOrgans //If true, yeets the organs out of any bodies caught under the pod when it lands
data["effectBluespace"] = temp_pod.bluespace //If true, the pod deletes (in a shower of sparks) after landing
- data["effectStealth"] = temp_pod.effectStealth //If true, a target icon isnt displayed on the turf where the pod will land
+ data["effectStealth"] = temp_pod.effectStealth //If true, a target icon isn't displayed on the turf where the pod will land
data["effectQuiet"] = temp_pod.effectQuiet //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc)
data["effectMissile"] = temp_pod.effectMissile //If true, the pod deletes the second it lands. If you give it an explosion, it will act like a missile exploding as it hits the ground
data["effectCircle"] = temp_pod.effectCircle //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here
@@ -115,20 +124,41 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
bay = locate(/area/centcom/supplypod/loading/four) in GLOB.sortedAreas
refreshBay()
. = TRUE
+ if("bay5")
+ to_chat(usr, "LetterN is lazy and didin't bother porting this new cc area!")
+ return
+ // bay = locate(/area/centcom/supplypod/loading/ert) in GLOB.sortedAreas
+ // refreshBay()
+ // . = TRUE
+ if("pickDropoffTurf") //Enters a mode that lets you pick the dropoff location for reverse pods
+ if (picking_dropoff_turf)
+ picking_dropoff_turf = FALSE
+ updateCursor(FALSE, FALSE) //Update the cursor of the user to a cool looking target icon
+ return
+ if (launcherActivated)
+ launcherActivated = FALSE //We don't want to have launch mode enabled while we're picking a turf
+ picking_dropoff_turf = TRUE
+ updateCursor(FALSE, TRUE) //Update the cursor of the user to a cool looking target icon
+ . = TRUE
+ if("clearDropoffTurf")
+ picking_dropoff_turf = FALSE
+ dropoff_turf = null
+ updateCursor(FALSE, FALSE)
+ . = TRUE
if("teleportCentcom") //Teleports the user to the centcom supply loading facility.
var/mob/M = holder.mob //We teleport whatever mob the client is attached to at the point of clicking
oldTurf = get_turf(M) //Used for the "teleportBack" action
- var/area/A = locate(/area/centcom/supplypod/loading) in GLOB.sortedAreas
+ var/area/A = locate(bay) in GLOB.sortedAreas
var/list/turfs = list()
for(var/turf/T in A)
turfs.Add(T) //Fill a list with turfs in the area
- var/turf/T = safepick(turfs) //Only teleport if the list isn't empty
- if(!T) //If the list is empty, error and cancel
+ if (!length(turfs)) //If the list is empty, error and cancel
to_chat(M, "Nowhere to jump to!")
- return
+ return //Only teleport if the list isn't empty
+ var/turf/T = pick(turfs)
M.forceMove(T) //Perform the actual teleport
- log_admin("[key_name(usr)] jumped to [AREACOORD(A)]")
- message_admins("[key_name_admin(usr)] jumped to [AREACOORD(A)]")
+ log_admin("[key_name(usr)] jumped to [AREACOORD(T)]")
+ message_admins("[key_name_admin(usr)] jumped to [AREACOORD(T)]")
. = TRUE
if("teleportBack") //After teleporting to centcom, this button allows the user to teleport to the last spot they were at.
var/mob/M = holder.mob
@@ -144,6 +174,9 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if("launchClone") //Toggles the launchClone var. See variable declarations above for what this specifically means
launchClone = !launchClone
. = TRUE
+ if("launchRandomItem") //Pick random turfs from the supplypod bay at centcom to launch
+ launchRandomItem = !launchRandomItem
+ . = TRUE
if("launchOrdered") //Launch turfs (from the orderedArea list) one at a time in order, from the supplypod bay at centcom
if (launchChoice == 1) //launchChoice 1 represents ordered. If we push "ordered" and it already is, then we go to default value
launchChoice = 0
@@ -152,7 +185,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
launchChoice = 1
updateSelector()
. = TRUE
- if("launchRandom") //Pick random turfs from the supplypod bay at centcom to launch
+ if("launchRandomTurf") //Pick random turfs from the supplypod bay at centcom to launch
if (launchChoice == 2)
launchChoice = 0
updateSelector()
@@ -170,11 +203,11 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
var/list/expNames = list("Devastation", "Heavy Damage", "Light Damage", "Flame") //Explosions have a range of different types of damage
var/list/boomInput = list()
for (var/i=1 to expNames.len) //Gather input from the user for the value of each type of damage
- boomInput.Add(input("[expNames[i]] Range", "Enter the [expNames[i]] range of the explosion. WARNING: This ignores the bomb cap!", 0) as null|num)
+ boomInput.Add(input("Enter the [expNames[i]] range of the explosion. WARNING: This ignores the bomb cap!", "[expNames[i]] Range", 0) as null|num)
if (isnull(boomInput[i]))
return
if (!isnum(boomInput[i])) //If the user doesn't input a number, set that specific explosion value to zero
- alert(usr, "That wasnt a number! Value set to default (zero) instead.")
+ alert(usr, "That wasn't a number! Value set to default (zero) instead.")
boomInput = 0
explosionChoice = 1
temp_pod.explosionSize = boomInput
@@ -192,11 +225,11 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
damageChoice = 0
temp_pod.damage = 0
return
- var/damageInput = input("How much damage to deal", "Enter the amount of brute damage dealt by getting hit", 0) as null|num
+ var/damageInput = input("Enter the amount of brute damage dealt by getting hit","How much damage to deal", 0) as null|num
if (isnull(damageInput))
return
if (!isnum(damageInput)) //Sanitize the input for damage to deal.s
- alert(usr, "That wasnt a number! Value set to default (zero) instead.")
+ alert(usr, "That wasn't a number! Value set to default (zero) instead.")
damageInput = 0
damageChoice = 1
temp_pod.damage = damageInput
@@ -226,13 +259,32 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
temp_pod.desc = descInput
temp_pod.adminNamed = TRUE //This variable is checked in the supplypod/setStyle() proc
. = TRUE
+ /*
+ if("effectShrapnel") //Creates a cloud of shrapnel on landing
+ if (temp_pod.effectShrapnel == TRUE) //If already doing custom damage, set back to default (no shrapnel)
+ temp_pod.effectShrapnel = FALSE
+ return
+ var/shrapnelInput = input("Please enter the type of pellet cloud you'd like to create on landing (Can be any projectile!)", "Projectile Typepath", 0) in sortList(subtypesof(/obj/item/projectile), /proc/cmp_typepaths_asc)
+ if (isnull(shrapnelInput))
+ return
+ var/shrapnelMagnitude = input("Enter the magnitude of the pellet cloud. This is usually a value around 1-5. Please note that Ryll-Ryll has asked me to tell you that if you go too crazy with the projectiles you might crash the server. So uh, be gentle!", "Shrapnel Magnitude", 0) as null|num
+ if (isnull(shrapnelMagnitude))
+ return
+ if (!isnum(shrapnelMagnitude))
+ alert(usr, "That wasn't a number! Value set to 3 instead.")
+ shrapnelMagnitude = 3
+ temp_pod.shrapnel_type = shrapnelInput
+ temp_pod.shrapnel_magnitude = shrapnelMagnitude
+ temp_pod.effectShrapnel = TRUE
+ . = TRUE
+ */
if("effectStun") //Toggle: Any mob under the pod is stunned (cant move) until the pod lands, hitting them!
temp_pod.effectStun = !temp_pod.effectStun
. = TRUE
if("effectLimb") //Toggle: Anyone carbon mob under the pod loses a limb when it lands
temp_pod.effectLimb = !temp_pod.effectLimb
. = TRUE
- if("effectOrgans") //Toggle: Any carbon mob under the pod loses every limb and organ
+ if("effectOrgans") //Toggle: Anyone carbon mob under the pod loses a limb when it lands
temp_pod.effectOrgans = !temp_pod.effectOrgans
. = TRUE
if("effectBluespace") //Toggle: Deletes the pod after landing
@@ -253,7 +305,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if("effectBurst") //Toggle: Launch 5 pods (with a very slight delay between) in a 3x3 area centered around the target
effectBurst = !effectBurst
. = TRUE
- if("effectAnnounce") //Toggle: Sends a ghost announcement.
+ if("effectAnnounce") //Toggle: Launch 5 pods (with a very slight delay between) in a 3x3 area centered around the target
effectAnnounce = !effectAnnounce
. = TRUE
if("effectReverse") //Toggle: Don't send any items. Instead, after landing, close (taking any objects inside) and go back to the centcom bay it came from
@@ -272,15 +324,15 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
. = TRUE
////////////////////////////TIMER DELAYS//////////////////
- if("fallDuration") //Change the falling animation duration
- if (temp_pod.fallDuration != initial(temp_pod.fallDuration)) //If the fall duration has already been changed when we push the "change value" button, then set it to default
+ if("fallDuration") //Change the time it takes the pod to land, after firing
+ if (temp_pod.fallDuration != initial(temp_pod.fallDuration)) //If the landing delay has already been changed when we push the "change value" button, then set it to default
temp_pod.fallDuration = initial(temp_pod.fallDuration)
return
- var/timeInput = input("Enter the duration of the pod's falling animation, in seconds", "Delay Time", initial(temp_pod.fallDuration) * 0.1) as null|num
+ var/timeInput = input("Enter the duration of the pod's falling animation, in seconds", "Delay Time", initial(temp_pod.fallDuration) * 0.1) as null|num
if (isnull(timeInput))
return
if (!isnum(timeInput)) //Sanitize input, if it doesnt check out, error and set to default
- alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.fallDuration)*0.1]) instead.")
+ alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.fallDuration)*0.1]) instead.")
timeInput = initial(temp_pod.fallDuration)
temp_pod.fallDuration = 10 * timeInput
. = TRUE
@@ -292,7 +344,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (isnull(timeInput))
return
if (!isnum(timeInput)) //Sanitize input, if it doesnt check out, error and set to default
- alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.landingDelay)*0.1]) instead.")
+ alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.landingDelay)*0.1]) instead.")
timeInput = initial(temp_pod.landingDelay)
temp_pod.landingDelay = 10 * timeInput
. = TRUE
@@ -304,7 +356,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (isnull(timeInput))
return
if (!isnum(timeInput)) //Sanitize input
- alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.openingDelay)*0.1]) instead.")
+ alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.openingDelay)*0.1]) instead.")
timeInput = initial(temp_pod.openingDelay)
temp_pod.openingDelay = 10 * timeInput
. = TRUE
@@ -316,13 +368,13 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (isnull(timeInput))
return
if (!isnum(timeInput))
- alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.departureDelay)*0.1]) instead.")
+ alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.departureDelay)*0.1]) instead.")
timeInput = initial(temp_pod.departureDelay)
temp_pod.departureDelay = 10 * timeInput
. = TRUE
////////////////////////////ADMIN SOUNDS//////////////////
- if("fallingSound") //Admin sound from a local file that plays when the pod falls
+ if("fallSound") //Admin sound from a local file that plays when the pod lands
if ((temp_pod.fallingSound) != initial(temp_pod.fallingSound))
temp_pod.fallingSound = initial(temp_pod.fallingSound)
temp_pod.fallingSoundLength = initial(temp_pod.fallingSoundLength)
@@ -334,7 +386,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (isnull(timeInput))
return
if (!isnum(timeInput))
- alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.fallingSoundLength)*0.1]) instead.")
+ alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.fallingSoundLength)*0.1]) instead.")
temp_pod.fallingSound = soundInput
temp_pod.fallingSoundLength = 10 * timeInput
. = TRUE
@@ -369,7 +421,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (temp_pod.soundVolume != initial(temp_pod.soundVolume))
temp_pod.soundVolume = initial(temp_pod.soundVolume)
return
- var/soundInput = input(holder, "Please pick a volume. Default is between 1 and 100 with 80 being average, but pick whatever. I'm a notification, not a cop. If you still cant hear your sound, consider turning on the Quiet effect. It will silence all pod sounds except for the custom admin ones set by the previous three buttons.", "Pick Admin Sound Volume") as null|num
+ var/soundInput = input(holder, "Please pick a volume. Default is between 1 and 100 with 50 being average, but pick whatever. I'm a notification, not a cop. If you still cant hear your sound, consider turning on the Quiet effect. It will silence all pod sounds except for the custom admin ones set by the previous three buttons.", "Pick Admin Sound Volume") as null|num
if (isnull(soundInput))
return
temp_pod.soundVolume = soundInput
@@ -421,26 +473,36 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
. = TRUE
if("giveLauncher") //Enters the "Launch Mode". When the launcher is activated, temp_pod is cloned, and the result it filled and launched anywhere the user clicks (unless specificTarget is true)
launcherActivated = !launcherActivated
- updateCursor(launcherActivated) //Update the cursor of the user to a cool looking target icon
+ updateCursor(launcherActivated, FALSE) //Update the cursor of the user to a cool looking target icon
+ . = TRUE
+ if("clearBay") //Delete all mobs and objs in the selected bay
+ if(alert(usr, "This will delete all objs and mobs in [bay]. Are you sure?", "Confirmation", "Delete that shit", "No") == "Delete that shit")
+ clearBay()
+ refreshBay()
. = TRUE
/datum/centcom_podlauncher/ui_close() //Uses the destroy() proc. When the user closes the UI, we clean up the temp_pod and supplypod_selector variables.
qdel(src)
-/datum/centcom_podlauncher/proc/updateCursor(var/launching) //Update the moues of the user
- if (holder) //Check to see if we have a client
- if (launching) //If the launching param is true, we give the user new mouse icons.
- holder.mouse_up_icon = 'icons/effects/supplypod_target.dmi' //Icon for when mouse is released
- holder.mouse_down_icon = 'icons/effects/supplypod_down_target.dmi' //Icon for when mouse is pressed
- holder.mouse_pointer_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released)
- holder.click_intercept = src //Create a click_intercept so we know where the user is clicking
- else
- var/mob/M = holder.mob
- holder.mouse_up_icon = null
- holder.mouse_down_icon = null
- holder.click_intercept = null
- if (M)
- M.update_mouse_pointer() //set the moues icons to null, then call update_moues_pointer() which resets them to the correct values based on what the mob is doing (in a mech, holding a spell, etc)()
+/datum/centcom_podlauncher/proc/updateCursor(var/launching, var/turf_picking) //Update the mouse of the user
+ if (!holder) //Can't update the mouse icon if the client doesnt exist!
+ return
+ if (launching || turf_picking) //If the launching param is true, we give the user new mouse icons.
+ if(launching)
+ holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_target.dmi' //Icon for when mouse is released
+ holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_down_target.dmi' //Icon for when mouse is pressed
+ if(turf_picking)
+ holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_pickturf.dmi' //Icon for when mouse is released
+ holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_pickturf_down.dmi' //Icon for when mouse is pressed
+ holder.mouse_pointer_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released)
+ holder.click_intercept = src //Create a click_intercept so we know where the user is clicking
+ else
+ var/mob/M = holder.mob
+ holder.mouse_up_icon = null
+ holder.mouse_down_icon = null
+ holder.click_intercept = null
+ if (M)
+ M.update_mouse_pointer() //set the moues icons to null, then call update_moues_pointer() which resets them to the correct values based on what the mob is doing (in a mech, holding a spell, etc)()
/datum/centcom_podlauncher/proc/InterceptClickOn(user,params,atom/target) //Click Intercept so we know where to send pods where the user clicks
var/list/pa = params2list(params)
@@ -461,7 +523,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
else
return //if target is null and we don't have a specific target, cancel
if (effectAnnounce)
- deadchat_broadcast("A special package is being launched at the station! ", turf_target = target)
+ deadchat_broadcast("A special package is being launched at the station!", turf_target = target) //, message_type=DEADCHAT_ANNOUNCEMENT)
var/list/bouttaDie = list()
for (var/mob/living/M in target)
bouttaDie.Add(M)
@@ -479,6 +541,15 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
else
launch(target) //If we couldn't locate an adjacent turf, just launch at the normal target
sleep(rand()*2) //looks cooler than them all appearing at once. Gives the impression of burst fire.
+ else if (picking_dropoff_turf)
+ //Clicking on UI elements shouldn't pick a dropoff turf
+ if(istype(target,/obj/screen))
+ return FALSE
+
+ . = TRUE
+ if(left_click) //When we left click:
+ dropoff_turf = get_turf(target)
+ to_chat(user, " You've selected [dropoff_turf] at [COORD(dropoff_turf)] as your dropoff location. ")
/datum/centcom_podlauncher/proc/refreshBay() //Called whenever the bay is switched, as well as wheneber a pod is launched
orderedArea = createOrderedArea(bay) //Create an ordered list full of turfs form the bay
@@ -489,7 +560,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
to_chat(holder.mob, "No /area/centcom/supplypod/loading/one (or /two or /three or /four) in the world! You can make one yourself (then refresh) for now, but yell at a mapper to fix this, today!")
CRASH("No /area/centcom/supplypod/loading/one (or /two or /three or /four) has been mapped into the centcom z-level!")
orderedArea = list()
- if (!isemptylist(A.contents)) //Go through the area passed into the proc, and figure out the top left and bottom right corners by calculating max and min values
+ if (length(A.contents)) //Go through the area passed into the proc, and figure out the top left and bottom right corners by calculating max and min values
var/startX = A.contents[1].x //Create the four values (we do it off a.contents[1] so they have some sort of arbitrary initial value. They should be overwritten in a few moments)
var/endX = A.contents[1].x
var/startY = A.contents[1].y
@@ -512,12 +583,12 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
numTurfs = 0 //Counts the number of turfs that can be launched (remember, supplypods either launch all at once or one turf-worth of items at a time)
acceptableTurfs = list()
for (var/turf/T in orderedArea) //Go through the orderedArea list
- if (typecache_filter_list_reverse(T.contents, ignored_atoms).len != 0) //if there is something in this turf that isnt in the blacklist, we consider this turf "acceptable" and add it to the acceptableTurfs list
+ if (typecache_filter_list_reverse(T.contents, ignored_atoms).len != 0) //if there is something in this turf that isn't in the blacklist, we consider this turf "acceptable" and add it to the acceptableTurfs list
acceptableTurfs.Add(T) //Because orderedArea was an ordered linear list, acceptableTurfs will be as well.
numTurfs ++
launchList = list() //Anything in launchList will go into the supplypod when it is launched
- if (!isemptylist(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We dont fill the supplypod if acceptableTurfs is empty, if the pod is going in reverse (effectReverse=true), or if the pod is acitng like a missile (effectMissile=true)
+ if (length(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We dont fill the supplypod if acceptableTurfs is empty, if the pod is going in reverse (effectReverse=true), or if the pod is acitng like a missile (effectMissile=true)
switch(launchChoice)
if(0) //If we are launching all the turfs at once
for (var/turf/T in acceptableTurfs)
@@ -536,22 +607,36 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (isnull(A))
return
var/obj/structure/closet/supplypod/centcompod/toLaunch = DuplicateObject(temp_pod) //Duplicate the temp_pod (which we have been varediting or configuring with the UI) and store the result
- toLaunch.bay = bay //Bay is currently a nonstatic expression, so it cant go into toLaunch using DuplicateObject
- toLaunch.update_icon()//we update_icon() here so that the door doesnt "flicker on" right after it lands
- if (launchClone) //We arent launching the actual items from the bay, rather we are creating clones and launching those
- for (var/atom/movable/O in launchList)
- DuplicateObject(O).forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod
- new /obj/effect/abstract/DPtarget(A, toLaunch) //Create the DPTarget, which will eventually forceMove the temp_pod to it's location
+ /*
+ if(dropoff_turf)
+ toLaunch.reverse_dropoff_turf = dropoff_turf
else
- for (var/atom/movable/O in launchList) //If we aren't cloning the objects, just go through the launchList
+ toLaunch.reverse_dropoff_turf = bay //Bay is currently a nonstatic expression, so it cant go into toLaunch using DuplicateObject
+ */
+ toLaunch.update_icon()//we update_icon() here so that the door doesnt "flicker on" right after it lands
+ // var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/fly_me_to_the_moon]
+ // toLaunch.forceMove(shippingLane) The shipping lane is temporarily closed due to ratvarian blockades
+ if (launchClone) //We arent launching the actual items from the bay, rather we are creating clones and launching those
+ if(launchRandomItem)
+ var/atom/movable/O = pick_n_take(launchList)
+ DuplicateObject(O).forceMove(toLaunch) //Duplicate a single atom/movable from launchList and forceMove it into the supplypod
+ else
+ for (var/atom/movable/O in launchList)
+ DuplicateObject(O).forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod
+ else
+ if(launchRandomItem)
+ var/atom/movable/O = pick_n_take(launchList)
O.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod
- new /obj/effect/abstract/DPtarget(A, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to it's location
+ else
+ for (var/atom/movable/O in launchList) //If we aren't cloning the objects, just go through the launchList
+ O.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod
+ new /obj/effect/abstract/DPtarget(A, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to it's location
if (launchClone)
launchCounter++ //We only need to increment launchCounter if we are cloning objects.
//If we aren't cloning objects, taking and removing the first item each time from the acceptableTurfs list will inherently iterate through the list in order
/datum/centcom_podlauncher/proc/updateSelector() //Ensures that the selector effect will showcase the next item if needed
- if (launchChoice == 1 && !isemptylist(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We only show the selector if we are taking items from the bay
+ if (launchChoice == 1 && length(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We only show the selector if we are taking items from the bay
var/index = launchCounter + 1 //launchCounter acts as an index to the ordered acceptableTurfs list, so adding one will show the next item in the list
if (index > acceptableTurfs.len) //out of bounds check
index = 1
@@ -559,8 +644,14 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
else
selector.moveToNullspace() //Otherwise, we move the selector to nullspace until it is needed again
+/datum/centcom_podlauncher/proc/clearBay() //Clear all objs and mobs from the selected bay
+ for (var/obj/O in bay.GetAllContents())
+ qdel(O)
+ for (var/mob/M in bay.GetAllContents())
+ qdel(M)
+
/datum/centcom_podlauncher/Destroy() //The Destroy() proc. This is called by ui_close proc, or whenever the user leaves the game
- updateCursor(FALSE) //Make sure our moues cursor resets to default. False means we are not in launch mode
+ updateCursor(FALSE, FALSE) //Make sure our moues cursor resets to default. False means we are not in launch mode
qdel(temp_pod) //Delete the temp_pod
qdel(selector) //Delete the selector effect
. = ..()
@@ -581,8 +672,8 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
for (var/X in temp_pod.explosionSize)
explosionString += "[X]|"
- var/msg = "launched [podString][whomString].[delayString][damageString][explosionString]]"
- message_admins("[key_name_admin(usr)] [msg] in [AREACOORD(specificTarget)].")
- if (!isemptylist(whoDyin))
+ var/msg = "launched [podString] towards [whomString] [delayString][damageString][explosionString]"
+ message_admins("[key_name_admin(usr)] [msg] in [ADMIN_VERBOSEJMP(specificTarget)].")
+ if (length(whoDyin))
for (var/mob/living/M in whoDyin)
admin_ticket_log(M, "[key_name_admin(usr)] [msg]")
diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm
index 8a438a1342..f5a8d21278 100644
--- a/code/modules/cargo/console.dm
+++ b/code/modules/cargo/console.dm
@@ -3,9 +3,6 @@
desc = "Used to order supplies, approve requests, and control the shuttle."
icon_screen = "supply"
circuit = /obj/item/circuitboard/computer/cargo
- req_access = list(ACCESS_CARGO)
- ui_x = 780
- ui_y = 750
var/requestonly = FALSE
var/contraband = FALSE
@@ -27,7 +24,6 @@
desc = "Used to request supplies from cargo."
icon_screen = "request"
circuit = /obj/item/circuitboard/computer/cargo/request
- req_access = list()
requestonly = TRUE
/obj/machinery/computer/cargo/Initialize()
@@ -66,15 +62,12 @@
var/obj/item/circuitboard/computer/cargo/board = circuit
board.contraband = TRUE
board.obj_flags |= EMAGGED
- req_access = list()
update_static_data(user)
- return ..()
-/obj/machinery/computer/cargo/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/cargo/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Cargo", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "Cargo", name)
ui.open()
/obj/machinery/computer/cargo/ui_data()
@@ -120,7 +113,6 @@
var/list/data = list()
data["requestonly"] = requestonly
data["supplies"] = list()
- data["emagged"] = obj_flags & EMAGGED
for(var/pack in SSshuttle.supply_packs)
var/datum/supply_pack/P = SSshuttle.supply_packs[pack]
if(!data["supplies"][P.group])
@@ -135,8 +127,8 @@
"cost" = P.cost,
"id" = pack,
"desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name.
+ "goody" = P.goody,
"private_goody" = P.goody == PACK_GOODY_PRIVATE,
- "goody" = P.goody == PACK_GOODY_PUBLIC,
"access" = P.access,
"can_private_buy" = P.can_private_buy
))
@@ -145,9 +137,6 @@
/obj/machinery/computer/cargo/ui_act(action, params, datum/tgui/ui)
if(..())
return
- if(!allowed(usr))
- to_chat(usr, "Access denied. ")
- return
switch(action)
if("send")
if(!SSshuttle.supply.canMove())
@@ -179,6 +168,8 @@
else
SSshuttle.shuttle_loan.loan_shuttle()
say("The supply shuttle has been loaned to CentCom.")
+ investigate_log("[key_name(usr)] accepted a shuttle loan event.", INVESTIGATE_CARGO)
+ log_game("[key_name(usr)] accepted a shuttle loan event.")
. = TRUE
if("add")
var/id = text2path(params["id"])
@@ -200,13 +191,15 @@
rank = "Silicon"
var/datum/bank_account/account
- if(self_paid)
- if(!pack.can_private_buy && !(obj_flags & EMAGGED))
- return
- var/obj/item/card/id/id_card = usr.get_idcard(TRUE)
+ if(self_paid && ishuman(usr))
+ var/mob/living/carbon/human/H = usr
+ var/obj/item/card/id/id_card = H.get_idcard(TRUE)
if(!istype(id_card))
say("No ID card detected.")
return
+ if(istype(id_card, /obj/item/card/id/departmental_budget))
+ say("The [src] rejects [id_card].")
+ return
account = id_card.registered_account
if(!istype(account))
say("Invalid bank account.")
@@ -241,6 +234,9 @@
SSshuttle.shoppinglist += SO
if(self_paid)
say("Order processed. The price will be charged to [account.account_holder]'s bank account on delivery.")
+ if(requestonly && message_cooldown < world.time)
+ radio.talk_into(src, "A new order has been requested.", RADIO_CHANNEL_SUPPLY)
+ message_cooldown = world.time + 30 SECONDS
. = TRUE
if("remove")
var/id = text2num(params["id"])
diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm
index dc7e4b5a06..4ca97a13a5 100644
--- a/code/modules/cargo/expressconsole.dm
+++ b/code/modules/cargo/expressconsole.dm
@@ -1,5 +1,5 @@
#define MAX_EMAG_ROCKETS 8
-#define BEACON_COST 5000
+#define BEACON_COST 500
#define SP_LINKED 1
#define SP_READY 2
#define SP_LAUNCH 3
@@ -13,10 +13,9 @@
All sales are near instantaneous - please choose carefully"
icon_screen = "supply_express"
circuit = /obj/item/circuitboard/computer/cargo/express
- ui_x = 600
- ui_y = 700
blockade_warning = "Bluespace instability detected. Delivery impossible."
req_access = list(ACCESS_QM)
+
var/message
var/printed_beacons = 0 //number of beacons printed. Used to determine beacon names.
var/list/meme_pack_data
@@ -42,7 +41,7 @@
to_chat(user, "You [locked ? "lock" : "unlock"] the interface. ")
return
else if(istype(W, /obj/item/disk/cargo/bluespace_pod))
- podType = /obj/structure/closet/supplypod/bluespacepod
+ podType = /obj/structure/closet/supplypod/bluespacepod //doesnt effect circuit board, making reversal possible
to_chat(user, "You insert the disk into [src], allowing for advanced supply delivery vehicles. ")
qdel(W)
return TRUE
@@ -52,22 +51,20 @@
sb.link_console(src, user)
return TRUE
else
- to_chat(user, "[src] is already linked to [sb]. ")
+ to_chat(user, "[src] is already linked to [sb]. ")
..()
/obj/machinery/computer/cargo/express/emag_act(mob/living/user)
- . = SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT)
if(obj_flags & EMAGGED)
return
- user.visible_message("[user] swipes a suspicious card through [src]! ",
- "You change the routing protocols, allowing the Supply Pod to land anywhere on the station. ")
+ if(user)
+ user.visible_message("[user] swipes a suspicious card through [src]! ",
+ "You change the routing protocols, allowing the Supply Pod to land anywhere on the station. ")
obj_flags |= EMAGGED
// This also sets this on the circuit board
var/obj/item/circuitboard/computer/cargo/board = circuit
board.obj_flags |= EMAGGED
packin_up()
- req_access = list()
- return TRUE
/obj/machinery/computer/cargo/express/proc/packin_up() // oh shit, I'm sorry
meme_pack_data = list() // sorry for what?
@@ -89,10 +86,10 @@
"desc" = P.desc || P.name // If there is a description, use it. Otherwise use the pack's name.
))
-/obj/machinery/computer/cargo/express/ui_interact(mob/living/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/cargo/express/ui_interact(mob/living/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "CargoExpress", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "CargoExpress", name)
ui.open()
/obj/machinery/computer/cargo/express/ui_data(mob/user)
@@ -131,9 +128,6 @@
return data
/obj/machinery/computer/cargo/express/ui_act(action, params, datum/tgui/ui)
- if(!allowed(usr))
- to_chat(usr, "Access denied. ")
- return
switch(action)
if("LZCargo")
usingBeacon = FALSE
@@ -153,6 +147,7 @@
printed_beacons++//printed_beacons starts at 0, so the first one out will be called beacon # 1
beacon.name = "Supply Pod Beacon #[printed_beacons]"
+
if("add")//Generate Supply Order first
var/id = text2path(params["id"])
var/datum/supply_pack/pack = SSshuttle.supply_packs[id]
@@ -195,7 +190,6 @@
LZ = pick(empty_turfs)
if (SO.pack.cost <= points_to_check && LZ)//we need to call the cost check again because of the CHECK_TICK call
D.adjust_money(-SO.pack.cost)
- SSblackbox.record_feedback("nested tally", "cargo_imports", 1, list("[SO.pack.cost]", "[SO.pack.name]"))
new /obj/effect/abstract/DPtarget(LZ, podType, SO)
. = TRUE
update_icon()
@@ -209,7 +203,7 @@
CHECK_TICK
if(empty_turfs && empty_turfs.len)
D.adjust_money(-(SO.pack.cost * (0.72*MAX_EMAG_ROCKETS)))
- SSblackbox.record_feedback("nested tally", "cargo_imports", MAX_EMAG_ROCKETS, list("[SO.pack.cost * 0.72]", "[SO.pack.name]"))
+
SO.generateRequisition(get_turf(src))
for(var/i in 1 to MAX_EMAG_ROCKETS)
var/LZ = pick(empty_turfs)
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 5c9b1eec2e..9b7e928852 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -41,6 +41,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(!asset_cache_job)
return
+ // Rate limiting
var/mtl = CONFIG_GET(number/minute_topic_limit)
if (!holder && mtl)
var/minute = round(world.time, 600)
@@ -98,6 +99,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
keyUp(keycode)
return
+ // Tgui Topic middleware
+ if(!tgui_Topic(href_list))
+ return
+
// Admin PM
if(href_list["priv_msg"])
cmd_admin_pm(href_list["priv_msg"],null)
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index f1803b03ee..af44b54ed0 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -95,14 +95,11 @@
icon_state = "dominator"
density = TRUE
var/active = FALSE
- var/obj/item/gps/gps
var/credits_stored = 0
var/siphon_per_tick = 5
/obj/machinery/shuttle_scrambler/Initialize(mapload)
. = ..()
- gps = new/obj/item/gps/internal/pirate(src)
- gps.tracking = FALSE
update_icon()
/obj/machinery/shuttle_scrambler/process()
@@ -112,6 +109,7 @@
if(D)
var/siphoned = min(D.account_balance,siphon_per_tick)
D.adjust_money(-siphoned)
+ credits_stored += siphoned
interrupt_research()
else
return
@@ -120,7 +118,7 @@
/obj/machinery/shuttle_scrambler/proc/toggle_on(mob/user)
SSshuttle.registerTradeBlockade(src)
- gps.tracking = TRUE
+ AddComponent(/datum/component/gps, "Nautical Signal")
active = TRUE
to_chat(user,"You toggle [src] [active ? "on":"off"]. ")
to_chat(user,"The scrambling signal can be now tracked by GPS. ")
@@ -130,7 +128,7 @@
if(!active)
if(alert(user, "Turning the scrambler on will make the shuttle trackable by GPS. Are you sure you want to do it?", "Scrambler", "Yes", "Cancel") == "Cancel")
return
- if(active || !user.canUseTopic(src))
+ if(active || !user.canUseTopic(src, BE_CLOSE))
return
toggle_on(user)
update_icon()
@@ -147,35 +145,31 @@
new /obj/effect/temp_visual/emp(get_turf(S))
/obj/machinery/shuttle_scrambler/proc/dump_loot(mob/user)
- new /obj/item/holochip(drop_location(), credits_stored)
- to_chat(user,"You retrieve the siphoned credits! ")
- credits_stored = 0
+ if(credits_stored) // Prevents spamming empty holochips
+ new /obj/item/holochip(drop_location(), credits_stored)
+ to_chat(user,"You retrieve the siphoned credits! ")
+ credits_stored = 0
+ else
+ to_chat(user,"There's nothing to withdraw. ")
/obj/machinery/shuttle_scrambler/proc/send_notification()
priority_announce("Data theft signal detected, source registered on local gps units.")
/obj/machinery/shuttle_scrambler/proc/toggle_off(mob/user)
SSshuttle.clearTradeBlockade(src)
- gps.tracking = FALSE
active = FALSE
STOP_PROCESSING(SSobj,src)
-/obj/machinery/shuttle_scrambler/update_overlays()
- . = ..()
+/obj/machinery/shuttle_scrambler/update_icon_state()
if(active)
- var/mutable_appearance/M = mutable_appearance(icon, "dominator-overlay")
- M.color = "#00FFFF"
- . += M
+ icon_state = "dominator-blue"
+ else
+ icon_state = "dominator"
/obj/machinery/shuttle_scrambler/Destroy()
toggle_off()
- QDEL_NULL(gps)
return ..()
-/obj/item/gps/internal/pirate
- gpstag = "Nautical Signal"
- desc = "You can hear shanties over the static."
-
/obj/machinery/computer/shuttle/pirate
name = "pirate shuttle console"
shuttleId = "pirateship"
@@ -312,11 +306,10 @@
else
pad = locate() in range(4,src)
-/obj/machinery/computer/piratepad_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/piratepad_control/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "CargoHoldTerminal", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "CargoHoldTerminal", name)
ui.open()
/obj/machinery/computer/piratepad_control/ui_data(mob/user)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
index 36e3b64487..35fa40e15d 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
@@ -12,6 +12,7 @@
idle_power_usage = 5
active_power_usage = 100
circuit = /obj/item/circuitboard/machine/smartfridge
+
var/max_n_of_items = 1500
var/allow_ai_retrieve = FALSE
var/list/initial_contents
@@ -38,12 +39,10 @@
if(in_range(user, src) || isobserver(user))
. += "The status display reads: This unit can hold a maximum of [max_n_of_items] items. "
-/obj/machinery/smartfridge/power_change()
- ..()
- update_icon()
-
/obj/machinery/smartfridge/update_icon_state()
+ SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
if(!stat)
+ SSvis_overlays.add_vis_overlay(src, icon, "smartfridge-light-mask", EMISSIVE_LAYER, EMISSIVE_PLANE, dir, alpha)
if(visible_contents)
switch(contents.len)
if(0)
@@ -66,9 +65,6 @@
********************/
/obj/machinery/smartfridge/attackby(obj/item/O, mob/user, params)
- if(user.a_intent == INTENT_HARM)
- return ..()
-
if(default_deconstruction_screwdriver(user, icon_state, icon_state, O))
cut_overlays()
if(panel_open)
@@ -87,46 +83,53 @@
updateUsrDialog()
return
- if(stat)
- updateUsrDialog()
- return FALSE
+ if(!stat)
- if(contents.len >= max_n_of_items)
- to_chat(user, "\The [src] is full! ")
- return FALSE
-
- if(accept_check(O))
- load(O)
- user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src]. ")
- updateUsrDialog()
- if (visible_contents)
- update_icon()
- return TRUE
-
- if(istype(O, /obj/item/storage/bag))
- var/obj/item/storage/P = O
- var/loaded = 0
- for(var/obj/G in P.contents)
- if(contents.len >= max_n_of_items)
- break
- if(accept_check(G))
- load(G)
- loaded++
- updateUsrDialog()
-
- if(loaded)
- user.visible_message("[user] loads \the [src] with \the [O].", \
- "You [contents.len >= max_n_of_items ? "fill" : "load"] \the [src] with \the [O]. ")
- if(O.contents.len > 0)
- to_chat(user, "Some items are refused. ")
- return TRUE
- else
- to_chat(user, "There is nothing in [O] to put in [src]! ")
+ if(contents.len >= max_n_of_items)
+ to_chat(user, "\The [src] is full! ")
return FALSE
- to_chat(user, "\The [src] smartly refuses [O]. ")
- updateUsrDialog()
- return FALSE
+ if(accept_check(O))
+ load(O)
+ user.visible_message("[user] adds \the [O] to \the [src]. ", "You add \the [O] to \the [src]. ")
+ updateUsrDialog()
+ if (visible_contents)
+ update_icon()
+ return TRUE
+
+ if(istype(O, /obj/item/storage/bag))
+ var/obj/item/storage/P = O
+ var/loaded = 0
+ for(var/obj/G in P.contents)
+ if(contents.len >= max_n_of_items)
+ break
+ if(accept_check(G))
+ load(G)
+ loaded++
+ updateUsrDialog()
+
+ if(loaded)
+ if(contents.len >= max_n_of_items)
+ user.visible_message("[user] loads \the [src] with \the [O]. ", \
+ "You fill \the [src] with \the [O]. ")
+ else
+ user.visible_message("[user] loads \the [src] with \the [O]. ", \
+ "You load \the [src] with \the [O]. ")
+ if(O.contents.len > 0)
+ to_chat(user, "Some items are refused. ")
+ if (visible_contents)
+ update_icon()
+ return TRUE
+ else
+ to_chat(user, "There is nothing in [O] to put in [src]! ")
+ return FALSE
+
+ if(user.a_intent != INTENT_HARM)
+ to_chat(user, "\The [src] smartly refuses [O]. ")
+ updateUsrDialog()
+ return FALSE
+ else
+ return ..()
@@ -151,16 +154,16 @@
return TRUE
///Really simple proc, just moves the object "O" into the hands of mob "M" if able, done so I could modify the proc a little for the organ fridge
-/obj/machinery/smartfridge/proc/dispense(obj/item/O, var/mob/M)
+/obj/machinery/smartfridge/proc/dispense(obj/item/O, mob/M)
if(!M.put_in_hands(O))
O.forceMove(drop_location())
adjust_item_drop_location(O)
-/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/smartfridge/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "SmartVend", name, 440, 550, master_ui, state)
+ ui = new(user, src, "SmartVend", name)
ui.set_autoupdate(FALSE)
ui.open()
@@ -232,7 +235,7 @@
// ----------------------------
/obj/machinery/smartfridge/drying_rack
name = "drying rack"
- desc = "A wooden contraption, used to dry plant products, food and leather."
+ desc = "A wooden contraption, used to dry plant products, food and hide."
icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "drying_rack"
use_power = IDLE_POWER_USE
@@ -277,6 +280,11 @@
return TRUE
return FALSE
+// /obj/machinery/smartfridge/drying_rack/powered() do we have this? no.
+// if(!anchored)
+// return FALSE
+// return ..()
+
/obj/machinery/smartfridge/drying_rack/power_change()
if(powered() && anchored)
stat &= ~NOPOWER
@@ -285,6 +293,10 @@
toggle_drying(TRUE)
update_icon()
+ // . = ..()
+ // if(!powered())
+ // toggle_drying(TRUE)
+
/obj/machinery/smartfridge/drying_rack/load() //For updating the filled overlay
..()
update_icon()
@@ -308,7 +320,7 @@
var/obj/item/reagent_containers/food/snacks/S = O
if(S.dried_type)
return TRUE
- if(istype(O, /obj/item/stack/sheet/wetleather/))
+ if(istype(O, /obj/item/stack/sheet/wetleather/)) //no wethide
return TRUE
return FALSE
@@ -386,19 +398,19 @@
/obj/machinery/smartfridge/extract/preloaded
initial_contents = list(/obj/item/slime_scanner = 2)
-// ------------------------- You think you're better than Chem, huh?
+// -------------------------
// Organ Surgery Smartfridge
-// ------------------------- Just wait till Tamiorgans
+// -------------------------
/obj/machinery/smartfridge/organ
name = "smart organ storage"
desc = "A refrigerated storage unit for organ storage."
- max_n_of_items = 25 //vastly lower to prevent processing too long
+ max_n_of_items = 20 //vastly lower to prevent processing too long
var/repair_rate = 0
/obj/machinery/smartfridge/organ/accept_check(obj/item/O)
- if(istype(O, /obj/item/organ))
+ if(isorgan(O) || isbodypart(O))
return TRUE
- if(istype(O, /obj/item/reagent_containers/syringe))
+ if(istype(O, /obj/item/reagent_containers/syringe)) //other medical things.
return TRUE
if(istype(O, /obj/item/reagent_containers/glass/bottle))
return TRUE
@@ -410,7 +422,7 @@
. = ..()
if(!.) //if the item loads, clear can_decompose
return
- if(istype(O, /obj/item/organ))
+ if(isorgan(O))
var/obj/item/organ/organ = O
organ.organ_flags |= ORGAN_FROZEN
@@ -426,12 +438,13 @@
return
O.applyOrganDamage(-repair_rate)
-/obj/machinery/smartfridge/organ/Exited(obj/item/organ/AM, atom/newLoc)
+/obj/machinery/smartfridge/organ/Exited(atom/movable/AM, atom/newLoc)
. = ..()
- if(istype(AM))
- AM.organ_flags &= ~ORGAN_FROZEN
+ if(isorgan(AM))
+ var/obj/item/organ/O = AM
+ O.organ_flags &= ~ORGAN_FROZEN
-/obj/machinery/smartfridge/organ/preloaded
+/obj/machinery/smartfridge/organ/preloaded //cit specific??????
initial_contents = list(
/obj/item/reagent_containers/medspray/synthtissue = 1,
/obj/item/reagent_containers/medspray/sterilizine = 1)
@@ -450,6 +463,15 @@
desc = "A refrigerated storage unit for medicine storage."
/obj/machinery/smartfridge/chemistry/accept_check(obj/item/O)
+ var/static/list/chemfridge_typecache = typecacheof(list(
+ /obj/item/reagent_containers/syringe,
+ /obj/item/reagent_containers/glass/bottle,
+ /obj/item/reagent_containers/glass/beaker,
+ /obj/item/reagent_containers/spray,
+ // /obj/item/reagent_containers/medigel,
+ /obj/item/reagent_containers/chem_pack
+ ))
+
if(istype(O, /obj/item/storage/pill_bottle))
if(O.contents.len)
for(var/obj/item/I in O)
@@ -463,7 +485,7 @@
return TRUE
if(!O.reagents || !O.reagents.reagent_list.len) // other empty containers not accepted
return FALSE
- if(istype(O, /obj/item/reagent_containers/syringe) || istype(O, /obj/item/reagent_containers/glass/bottle) || istype(O, /obj/item/reagent_containers/glass/beaker) || istype(O, /obj/item/reagent_containers/spray) || istype(O, /obj/item/reagent_containers/medspray) || istype(O, /obj/item/reagent_containers/chem_pack))
+ if(is_type_in_typecache(O, chemfridge_typecache))
return TRUE
return FALSE
@@ -487,6 +509,7 @@
/obj/item/reagent_containers/glass/bottle/cold = 1,
/obj/item/reagent_containers/glass/bottle/flu_virion = 1,
/obj/item/reagent_containers/glass/bottle/mutagen = 1,
+ /obj/item/reagent_containers/glass/bottle/sugar = 1,
/obj/item/reagent_containers/glass/bottle/plasma = 1,
/obj/item/reagent_containers/glass/bottle/synaptizine = 1,
/obj/item/reagent_containers/glass/bottle/formaldehyde = 1)
@@ -498,8 +521,8 @@
name = "disk compartmentalizer"
desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)."
icon_state = "disktoaster"
- visible_contents = FALSE
pass_flags = PASSTABLE
+ visible_contents = FALSE
/obj/machinery/smartfridge/disks/accept_check(obj/item/O)
if(istype(O, /obj/item/disk/))
diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm
index d72e1b1fc8..e5cd36cad6 100644
--- a/code/modules/holodeck/computer.dm
+++ b/code/modules/holodeck/computer.dm
@@ -2,10 +2,7 @@
Holodeck Update
The on-station holodeck area is of type [holodeck_type].
- All types found in GLOB.holodeck_areas_per_comp_type[src.type], generated on make_datum_references_lists(),
- are loaded into the program cache or emag programs list.
- Paths with their abstract_type variable equal to themselves will be skipped.
-
+ All subtypes of [program_type] are loaded into the program cache or emag programs list.
If init_program is null, a random program will be loaded on startup.
If you don't wish this, set it to the offline program or another of your choosing.
@@ -15,6 +12,7 @@
3) Create a new control console that uses those areas
Non-mapped areas should be skipped but you should probably comment them out anyway.
+ The base of program_type will always be ignored; only subtypes will be loaded.
*/
#define HOLODECK_CD 25
@@ -26,18 +24,20 @@
icon_screen = "holocontrol"
idle_power_usage = 10
active_power_usage = 50
+
var/area/holodeck/linked
var/area/holodeck/program
var/area/holodeck/last_program
var/area/offline_program = /area/holodeck/rec_center/offline
- // Splitting this up allows two holodecks of the same size
- // to use the same source patterns. Y'know, if you want to.
- var/holodeck_type = /area/holodeck/rec_center
-
var/list/program_cache
var/list/emag_programs
+ // Splitting this up allows two holodecks of the same size
+ // to use the same source patterns. Y'know, if you want to.
+ var/holodeck_type = /area/holodeck/rec_center // locate(this) to get the target holodeck
+ var/program_type = /area/holodeck/rec_center // subtypes of this (but not this itself) are loadable programs
+
var/active = FALSE
var/damaged = FALSE
var/list/spawned = list()
@@ -49,41 +49,47 @@
return INITIALIZE_HINT_LATELOAD
/obj/machinery/computer/holodeck/LateInitialize()
- linked = SSholodeck.target_holodeck_area[type]
- offline_program = SSholodeck.offline_programs[type]
+ if(ispath(holodeck_type, /area))
+ linked = pop(get_areas(holodeck_type, FALSE))
+ if(ispath(offline_program, /area))
+ offline_program = pop(get_areas(offline_program), FALSE)
+ // the following is necessary for power reasons
if(!linked || !offline_program)
log_world("No matching holodeck area found")
qdel(src)
return
-
- program_cache = SSholodeck.program_cache[type]
- emag_programs = SSholodeck.emag_program_cache[type]
-
- // the following is necessary for power reasons
- var/area/AS = get_base_area(src)
+ var/area/AS = get_area(src)
if(istype(AS, /area/holodeck))
log_mapping("Holodeck computer cannot be in a holodeck, This would cause circular power dependency.")
qdel(src)
return
else
linked.linked = src
-
+ /*
+ var/area/my_area = get_area(src)
+ if(my_area)
+ linked.power_usage = my_area.power_usage
+ else
+ linked.power_usage = new /list(AREA_USAGE_LEN)
+ */
+ generate_program_list()
load_program(offline_program, FALSE, FALSE)
/obj/machinery/computer/holodeck/Destroy()
emergency_shutdown()
if(linked)
linked.linked = null
+ //linked.power_usage = new /list(AREA_USAGE_LEN)
return ..()
/obj/machinery/computer/holodeck/power_change()
. = ..()
toggle_power(!stat)
-/obj/machinery/computer/holodeck/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/holodeck/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Holodeck", name, 400, 500, master_ui, state)
+ ui = new(user, src, "Holodeck", name)
ui.open()
/obj/machinery/computer/holodeck/ui_data(mob/user)
@@ -107,19 +113,27 @@
var/program_to_load = text2path(params["type"])
if(!ispath(program_to_load))
return FALSE
+ var/valid = FALSE
+ var/list/checked = program_cache.Copy()
+ if(obj_flags & EMAGGED)
+ checked |= emag_programs
+ for(var/prog in checked)
+ var/list/P = prog
+ if(P["type"] == program_to_load)
+ valid = TRUE
+ break
+ if(!valid)
+ return FALSE
+
var/area/A = locate(program_to_load) in GLOB.sortedAreas
if(A)
load_program(A)
if("safety")
- if(!hasSiliconAccessInArea(usr) && !IsAdminGhost(usr))
- var/msg = "[key_name(usr)] attempted to emag the holodeck using a href they shouldn't have!"
- message_admins(msg)
- log_admin(msg)
- return
- obj_flags ^= EMAGGED
- if((obj_flags & EMAGGED) && program && emag_programs[program.name])
+ if((obj_flags & EMAGGED) && program)
emergency_shutdown()
nerf(obj_flags & EMAGGED)
+ obj_flags ^= EMAGGED
+ say("Safeties restored. Restarting...")
/obj/machinery/computer/holodeck/process()
if(damaged && prob(10))
@@ -160,13 +174,12 @@
if(!LAZYLEN(emag_programs))
to_chat(user, "[src] does not seem to have a card swipe port. It must be an inferior model.")
return
- playsound(src, "sparks", 75, 1)
+ playsound(src, "sparks", 75, TRUE)
obj_flags |= EMAGGED
to_chat(user, "You vastly increase projector power and override the safety and security protocols. ")
- to_chat(user, "Warning. Automatic shutoff and derezing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator.")
+ say("Warning. Automatic shutoff and derezzing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator.")
log_game("[key_name(user)] emagged the Holodeck Control Console")
nerf(!(obj_flags & EMAGGED))
- return TRUE
/obj/machinery/computer/holodeck/emp_act(severity)
. = ..()
@@ -182,6 +195,19 @@
emergency_shutdown()
return ..()
+/obj/machinery/computer/holodeck/proc/generate_program_list()
+ for(var/typekey in subtypesof(program_type))
+ var/area/holodeck/A = GLOB.areas_by_type[typekey]
+ if(!A || !A.contents.len)
+ continue
+ var/list/info_this = list()
+ info_this["name"] = A.name
+ info_this["type"] = A.type
+ if(A.restricted)
+ LAZYADD(emag_programs, list(info_this))
+ else
+ LAZYADD(program_cache, list(info_this))
+
/obj/machinery/computer/holodeck/proc/toggle_power(toggleOn = FALSE)
if(active == toggleOn)
return
@@ -281,7 +307,7 @@
silent = FALSE // otherwise make sure they are dropped
if(!silent)
- visible_message("[O] fades away!")
+ visible_message("[O] fades away! ")
qdel(O)
#undef HOLODECK_CD
diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm
index 06d2b3efda..b61d9451ef 100644
--- a/code/modules/hydroponics/biogenerator.dm
+++ b/code/modules/hydroponics/biogenerator.dm
@@ -10,13 +10,13 @@
var/processing = FALSE
var/obj/item/reagent_containers/glass/beaker = null
var/points = 0
- var/menustat = "menu"
var/efficiency = 0
var/productivity = 0
var/max_items = 40
var/datum/techweb/stored_research
var/list/show_categories = list("Food", "Botany Chemicals", "Organic Materials")
- var/list/timesFiveCategories = list("Food", "Botany Chemicals")
+ /// Currently selected category in the UI
+ var/selected_cat
/obj/machinery/biogenerator/Initialize()
. = ..()
@@ -37,22 +37,20 @@
if(A == beaker)
beaker = null
update_icon()
- updateUsrDialog()
/obj/machinery/biogenerator/RefreshParts()
- var/E = 0.5
- var/P = 0.5
- var/max_storage = 20
+ var/E = 0
+ var/P = 0
+ var/max_storage = 40
for(var/obj/item/stock_parts/matter_bin/B in component_parts)
- P += B.rating * 0.5
- max_storage = max(20 * B.rating, max_storage)
+ P += B.rating
+ max_storage = 40 * B.rating
for(var/obj/item/stock_parts/manipulator/M in component_parts)
- E += M.rating * 0.5
+ E += M.rating
efficiency = E
productivity = P
max_items = max_storage
-
/obj/machinery/biogenerator/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
@@ -70,7 +68,6 @@
icon_state = "biogen-stand"
else
icon_state = "biogen-work"
- return
/obj/machinery/biogenerator/attackby(obj/item/O, mob/user, params)
if(user.a_intent == INTENT_HARM)
@@ -102,7 +99,6 @@
beaker = O
to_chat(user, "You add the container to the machine. ")
update_icon()
- updateUsrDialog()
else
to_chat(user, "Close the maintenance panel first. ")
return
@@ -139,9 +135,9 @@
to_chat(user, "You put [O.name] in [src.name] ")
return TRUE //no afterattack
else if (istype(O, /obj/item/disk/design_disk))
- user.visible_message("[user] begins to load \the [O] in \the [src]...",
- "You begin to load a design from \the [O]...",
- "You hear the chatter of a floppy drive.")
+ user.visible_message("[user] begins to load \the [O] in \the [src]... ",
+ "You begin to load a design from \the [O]... ",
+ "You hear the chatter of a floppy drive. ")
processing = TRUE
var/obj/item/disk/design_disk/D = O
if(do_after(user, 10, target = src))
@@ -153,106 +149,53 @@
else
to_chat(user, "You cannot put this in [src.name]! ")
-/obj/machinery/biogenerator/ui_interact(mob/user)
- if(stat & BROKEN || panel_open)
- return
- . = ..()
- var/dat
- if(processing)
- dat += "Biogenerator is processing! Please wait...
"
- else
- switch(menustat)
- if("nopoints")
- dat += "You do not have enough biomass to create products. Please, put growns into reactor and activate it.
"
- menustat = "menu"
- if("complete")
- dat += "Operation complete.
"
- menustat = "menu"
- if("void")
- dat += "Error: No growns inside. Please, put growns into reactor.
"
- menustat = "menu"
- if("nobeakerspace")
- dat += "Not enough space left in container. Unable to create product.
"
- menustat = "menu"
- if(beaker)
- var/categories = show_categories.Copy()
- for(var/V in categories)
- categories[V] = list()
- for(var/V in stored_research.researched_designs)
- var/datum/design/D = SSresearch.techweb_design_by_id(V)
- for(var/C in categories)
- if(C in D.category)
- categories[C] += D
-
- dat += "Biomass: [points] units.
"
- dat += "Activate Detach Container "
- for(var/cat in categories)
- dat += "[cat]: "
- dat += ""
- for(var/V in categories[cat])
- var/datum/design/D = V
- dat += "[D.name]:
Make "
- if(cat in timesFiveCategories)
- dat += "
x5 "
- if(ispath(D.build_path, /obj/item/stack))
- dat += "
x10 "
- dat += "([CEILING(D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency, 1)])
"
- dat += "
"
- else
- dat += "No container inside, please insert container.
"
-
- var/datum/browser/popup = new(user, "biogen", name, 350, 520)
- popup.set_content(dat)
- popup.open()
-
/obj/machinery/biogenerator/AltClick(mob/living/user)
. = ..()
- if(istype(user) && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && can_interact(user))
detach(user)
-/obj/machinery/biogenerator/proc/activate()
- if (usr.stat != CONSCIOUS)
+/**
+ * activate: Activates biomass processing and converts all inserted grown products into biomass
+ *
+ * Arguments:
+ * * user The mob starting the biomass processing
+ */
+/obj/machinery/biogenerator/proc/activate(mob/user)
+ if(user.stat != CONSCIOUS)
return
- if (src.stat != NONE) //NOPOWER etc
+ if(stat != NONE)
return
if(processing)
- to_chat(usr, "The biogenerator is in the process of working. ")
+ to_chat(user, "The biogenerator is in the process of working. ")
return
var/S = 0
- var/total = 0
for(var/obj/item/reagent_containers/food/snacks/grown/I in contents)
S += 5
- var/nutri_amount = I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment)
- if(nutri_amount < 0.1)
- total += 1*productivity
+ if(I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) < 0.1)
+ points += 1 * productivity
else
- total += nutri_amount*10*productivity
+ points += I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) * 10 * productivity
qdel(I)
- points += round(total)
if(S)
processing = TRUE
update_icon()
- updateUsrDialog()
- playsound(src.loc, 'sound/machines/blender.ogg', 50, 1)
- use_power(S*30)
- sleep(S+15/productivity)
+ playsound(loc, 'sound/machines/blender.ogg', 50, TRUE)
+ use_power(S * 30)
+ sleep(S + 15 / productivity)
+ if(QDELETED(src)) //let's not.
+ return
processing = FALSE
update_icon()
- else
- menustat = "void"
/obj/machinery/biogenerator/proc/check_cost(list/materials, multiplier = 1, remove_points = TRUE)
if(materials.len != 1 || materials[1] != SSmaterials.GetMaterialRef(/datum/material/biomass))
return FALSE
- var/cost = CEILING(materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency, 1)
- if (cost > points)
- menustat = "nopoints"
+ if (materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency > points)
return FALSE
else
if(remove_points)
- points -= cost
+ points -= materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency
update_icon()
- updateUsrDialog()
return TRUE
/obj/machinery/biogenerator/proc/check_container_volume(list/reagents, multiplier = 1)
@@ -262,7 +205,6 @@
sum_reagents *= multiplier
if(beaker.reagents.total_volume + sum_reagents > beaker.reagents.maximum_volume)
- menustat = "nobeakerspace"
return FALSE
return TRUE
@@ -284,6 +226,7 @@
var/i = amount
while(i > 0)
if(!check_container_volume(D.make_reagents))
+ say("Warning: Attached container does not have enough free capacity!")
return .
if(!check_cost(D.materials))
return .
@@ -293,51 +236,100 @@
beaker.reagents.add_reagent(R, D.make_reagents[R])
. = 1
--i
-
- menustat = "complete"
update_icon()
return .
/obj/machinery/biogenerator/proc/detach(mob/living/user)
if(beaker)
- user.put_in_hands(beaker)
+ if(can_interact(user))
+ user.put_in_hands(beaker)
+ else
+ beaker.drop_location(get_turf(src))
beaker = null
update_icon()
-/obj/machinery/biogenerator/Topic(href, href_list)
- if(..() || panel_open)
+/obj/machinery/biogenerator/ui_status(mob/user)
+ if(stat & BROKEN || panel_open)
+ return UI_CLOSE
+ return ..()
+
+/obj/machinery/biogenerator/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/research_designs),
+ )
+
+/obj/machinery/biogenerator/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Biogenerator", name)
+ ui.open()
+
+/obj/machinery/biogenerator/ui_data(mob/user)
+ var/list/data = list()
+ data["beaker"] = beaker ? TRUE : FALSE
+ data["biomass"] = points
+ data["processing"] = processing
+ if(locate(/obj/item/reagent_containers/food/snacks/grown) in contents)
+ data["can_process"] = TRUE
+ else
+ data["can_process"] = FALSE
+ return data
+
+/obj/machinery/biogenerator/ui_static_data(mob/user)
+ var/list/data = list()
+ data["categories"] = list()
+
+ var/categories = show_categories.Copy()
+ for(var/V in categories)
+ categories[V] = list()
+ for(var/V in stored_research.researched_designs)
+ var/datum/design/D = SSresearch.techweb_design_by_id(V)
+ for(var/C in categories)
+ if(C in D.category)
+ categories[C] += D
+
+ for(var/category in categories)
+ var/list/cat = list(
+ "name" = category,
+ "items" = (category == selected_cat ? list() : null))
+ for(var/item in categories[category])
+ var/datum/design/D = item
+ cat["items"] += list(list(
+ "id" = D.id,
+ "name" = D.name,
+ "cost" = D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency,
+ ))
+ data["categories"] += list(cat)
+
+ return data
+
+/obj/machinery/biogenerator/ui_act(action, list/params)
+ if(..())
return
- usr.set_machine(src)
-
- if(href_list["activate"])
- activate()
- updateUsrDialog()
-
- else if(href_list["detach"])
- detach(usr)
- updateUsrDialog()
-
- else if(href_list["create"])
- var/amount = (text2num(href_list["amount"]))
- //Can't be outside these (if you change this keep a sane limit)
- amount = clamp(amount, 1, 50)
- var/id = href_list["create"]
- if(!stored_research.researched_designs.Find(id))
- //naughty naughty
- stack_trace("ID did not map to a researched datum [id]")
- return
-
- //Get design by id (or may return error design)
- var/datum/design/D = SSresearch.techweb_design_by_id(id)
- //Valid design datum, amount and the datum is not the error design, lets proceed
- if(D && amount && !istype(D, /datum/design/error_design))
- create_product(D, amount)
- //This shouldnt happen normally but href forgery is real
- else
- stack_trace("ID could not be turned into a valid techweb design datum [id]")
- updateUsrDialog()
-
- else if(href_list["menu"])
- menustat = "menu"
- updateUsrDialog()
+ switch(action)
+ if("activate")
+ activate(usr)
+ return TRUE
+ if("detach")
+ detach(usr)
+ return TRUE
+ if("create")
+ var/amount = text2num(params["amount"])
+ amount = clamp(amount, 1, 10)
+ if(!amount)
+ return
+ var/id = params["id"]
+ if(!stored_research.researched_designs.Find(id))
+ stack_trace("ID did not map to a researched datum [id]")
+ return
+ var/datum/design/D = SSresearch.techweb_design_by_id(id)
+ if(D && !istype(D, /datum/design/error_design))
+ create_product(D, amount)
+ else
+ stack_trace("ID could not be turned into a valid techweb design datum [id]")
+ return
+ return TRUE
+ if("select")
+ selected_cat = params["category"]
+ return TRUE
diff --git a/code/modules/hydroponics/seed_extractor.dm b/code/modules/hydroponics/seed_extractor.dm
index 63b96632e6..71701d9637 100644
--- a/code/modules/hydroponics/seed_extractor.dm
+++ b/code/modules/hydroponics/seed_extractor.dm
@@ -1,3 +1,18 @@
+/**
+ * Finds and extracts seeds from an object
+ *
+ * Checks if the object is such that creates a seed when extracted. Used by seed
+ * extractors or posably anything that would create seeds in some way. The seeds
+ * are dropped either at the extractor, if it exists, or where the original object
+ * was and it qdel's the object
+ *
+ * Arguments:
+ * * O - Object containing the seed, can be the loc of the dumping of seeds
+ * * t_max - Amount of seed copies to dump, -1 is ranomized
+ * * extractor - Seed Extractor, used as the dumping loc for the seeds and seed multiplier
+ * * user - checks if we can remove the object from the inventory
+ * *
+ */
/proc/seedify(obj/item/O, t_max, obj/machinery/seed_extractor/extractor, mob/living/user)
var/t_amount = 0
var/list/seeds = list()
@@ -46,20 +61,22 @@
icon_state = "sextractor"
density = TRUE
circuit = /obj/item/circuitboard/machine/seed_extractor
- var/piles = list()
+ /// Associated list of seeds, they are all weak refs. We check the len to see how many refs we have for each
+ // seed
+ var/list/piles = list()
var/max_seeds = 1000
var/seed_multiplier = 1
/obj/machinery/seed_extractor/RefreshParts()
for(var/obj/item/stock_parts/matter_bin/B in component_parts)
- max_seeds = 1000 * B.rating
+ max_seeds = initial(max_seeds) * B.rating
for(var/obj/item/stock_parts/manipulator/M in component_parts)
- seed_multiplier = M.rating
+ seed_multiplier = initial(seed_multiplier) * M.rating
/obj/machinery/seed_extractor/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
- . += "The status display reads: Extracting [seed_multiplier] seed(s) per piece of produce. Machine can store up to [max_seeds] seeds. "
+ . += "The status display reads: Extracting [seed_multiplier] seed(s) per piece of produce. Machine can store up to [max_seeds]% seeds. "
/obj/machinery/seed_extractor/attackby(obj/item/O, mob/user, params)
@@ -102,78 +119,26 @@
else
return ..()
-/datum/seed_pile
- var/name = ""
- var/lifespan = 0 //Saved stats
- var/endurance = 0
- var/maturation = 0
- var/production = 0
- var/yield = 0
- var/potency = 0
- var/amount = 0
+/**
+ * Generate seed string
+ *
+ * Creates a string based of the traits of a seed. We use this string as a bucket for all
+ * seeds that match as well as the key the ui uses to get the seed. We also use the key
+ * for the data shown in the ui. Javascript parses this string to display
+ *
+ * Arguments:
+ * * O - seed to generate the string from
+ */
+/obj/machinery/seed_extractor/proc/generate_seed_string(obj/item/seeds/O)
+ return "name=[O.name];lifespan=[O.lifespan];endurance=[O.endurance];maturation=[O.maturation];production=[O.production];yield=[O.yield];potency=[O.potency];instability=0"
-/datum/seed_pile/New(var/name, var/life, var/endur, var/matur, var/prod, var/yie, var/poten, var/am = 1)
- src.name = name
- src.lifespan = life
- src.endurance = endur
- src.maturation = matur
- src.production = prod
- src.yield = yie
- src.potency = poten
- src.amount = am
-
-/obj/machinery/seed_extractor/ui_interact(mob/user)
- . = ..()
- if (stat)
- return FALSE
-
- var/dat = "Stored seeds: "
-
- if (contents.len == 0)
- dat += "No seeds "
- else
- dat += "Name Lifespan Endurance Maturation Production Yield Potency Stock "
- for (var/datum/seed_pile/O in piles)
- dat += "[O.name] [O.lifespan] [O.endurance] [O.maturation] "
- dat += "[O.production] [O.yield] [O.potency] "
- dat += "Vend ([O.amount] left) "
- dat += "
"
- var/datum/browser/popup = new(user, "seed_ext", name, 700, 400)
- popup.set_content(dat)
- popup.open()
- return
-
-/obj/machinery/seed_extractor/Topic(var/href, var/list/href_list)
- if(..())
- return
- usr.set_machine(src)
-
- href_list["li"] = text2num(href_list["li"])
- href_list["en"] = text2num(href_list["en"])
- href_list["ma"] = text2num(href_list["ma"])
- href_list["pr"] = text2num(href_list["pr"])
- href_list["yi"] = text2num(href_list["yi"])
- href_list["pot"] = text2num(href_list["pot"])
-
- for (var/datum/seed_pile/N in piles)//Find the pile we need to reduce...
- if (href_list["name"] == N.name && href_list["li"] == N.lifespan && href_list["en"] == N.endurance && href_list["ma"] == N.maturation && href_list["pr"] == N.production && href_list["yi"] == N.yield && href_list["pot"] == N.potency)
- if(N.amount <= 0)
- return
- N.amount = max(N.amount - 1, 0)
- if (N.amount <= 0)
- piles -= N
- qdel(N)
- break
-
- for (var/obj/T in contents)//Now we find the seed we need to vend
- var/obj/item/seeds/O = T
- if (O.plantname == href_list["name"] && O.lifespan == href_list["li"] && O.endurance == href_list["en"] && O.maturation == href_list["ma"] && O.production == href_list["pr"] && O.yield == href_list["yi"] && O.potency == href_list["pot"])
- O.forceMove(drop_location())
- break
-
- src.updateUsrDialog()
- return
+/** Add Seeds Proc.
+ *
+ * Adds the seeds to the contents and to an associated list that pregenerates the data
+ * needed to go to the ui handler
+ *
+ **/
/obj/machinery/seed_extractor/proc/add_seed(obj/item/seeds/O)
if(contents.len >= 999)
to_chat(usr, "\The [src] is full. ")
@@ -188,10 +153,47 @@
if(!M.transferItemToLoc(O, src))
return FALSE
- . = TRUE
- for (var/datum/seed_pile/N in piles)
- if (O.plantname == N.name && O.lifespan == N.lifespan && O.endurance == N.endurance && O.maturation == N.maturation && O.production == N.production && O.yield == N.yield && O.potency == N.potency)
- ++N.amount
- return
+ var/seed_string = generate_seed_string(O)
+ if(piles[seed_string])
+ piles[seed_string] += WEAKREF(O)
+ else
+ piles[seed_string] = list(WEAKREF(O))
+
+ . = TRUE
+
+/obj/machinery/seed_extractor/ui_state(mob/user)
+ return GLOB.notcontained_state
+
+/obj/machinery/seed_extractor/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "SeedExtractor", name)
+ ui.open()
+
+/obj/machinery/seed_extractor/ui_data()
+ var/list/V = list()
+ for(var/key in piles)
+ if(piles[key])
+ var/len = length(piles[key])
+ if(len)
+ V[key] = len
+
+ . = list()
+ .["seeds"] = V
+
+/obj/machinery/seed_extractor/ui_act(action, params)
+ if(..())
+ return
+
+ switch(action)
+ if("select")
+ var/item = params["item"]
+ if(piles[item] && length(piles[item]) > 0)
+ var/datum/weakref/WO = piles[item][1]
+ var/obj/item/seeds/O = WO.resolve()
+ if(O)
+ piles[item] -= WO
+ O.forceMove(drop_location())
+ . = TRUE
+ //to_chat(usr, "[src] clanks to life briefly before vending [prize.equipment_name]! ")
- piles += new /datum/seed_pile(O.plantname, O.lifespan, O.endurance, O.maturation, O.production, O.yield, O.potency)
diff --git a/code/modules/language/language_menu.dm b/code/modules/language/language_menu.dm
index 0df7c01fca..bffd3d59af 100644
--- a/code/modules/language/language_menu.dm
+++ b/code/modules/language/language_menu.dm
@@ -8,10 +8,13 @@
language_holder = null
. = ..()
-/datum/language_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.language_menu_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/language_menu/ui_state(mob/user)
+ return GLOB.language_menu_state
+
+/datum/language_menu/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "LanguageMenu", "Language Menu", 700, 600, master_ui, state)
+ ui = new(user, src, "LanguageMenu")
ui.open()
/datum/language_menu/ui_data(mob/user)
diff --git a/code/modules/library/lib_codex_gigas.dm b/code/modules/library/lib_codex_gigas.dm
index 146c4221f8..26fa5b6f3d 100644
--- a/code/modules/library/lib_codex_gigas.dm
+++ b/code/modules/library/lib_codex_gigas.dm
@@ -34,13 +34,13 @@
if(U.check_acedia())
to_chat(user, "None of this matters, why are you reading this? You put [title] down. ")
return
- user.visible_message("[user] opens [title] and begins reading intently.")
+ user.visible_message("[user] opens [title] and begins reading intently. ")
ask_name(user)
/obj/item/book/codex_gigas/proc/perform_research(mob/user, devilName)
if(!devilName)
- user.visible_message("[user] closes [title] without looking anything up.")
+ user.visible_message("[user] closes [title] without looking anything up. ")
return
inUse = TRUE
var/speed = 300
@@ -50,7 +50,7 @@
if(U.job in list("Curator")) // the curator is both faster, and more accurate than normal crew members at research
speed = 100
correctness = 100
- correctness -= U.getOrganLoss(ORGAN_SLOT_BRAIN) *0.5 //Brain damage makes researching hard.
+ correctness -= U.getOrganLoss(ORGAN_SLOT_BRAIN) * 0.5 //Brain damage makes researching hard.
speed += U.getOrganLoss(ORGAN_SLOT_BRAIN) * 3
if(do_after(user, speed, 0, user))
var/usedName = devilName
@@ -95,11 +95,10 @@
currentSection = SUFFIX
return currentSection != oldSection
-/obj/item/book/codex_gigas/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/book/codex_gigas/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "CodexGigas", name, 450, 450, master_ui, state)
+ ui = new(user, src, "CodexGigas", name)
ui.open()
/obj/item/book/codex_gigas/ui_data(mob/user)
diff --git a/code/modules/library/soapstone.dm b/code/modules/library/soapstone.dm
index 50f984c44d..f17040a938 100644
--- a/code/modules/library/soapstone.dm
+++ b/code/modules/library/soapstone.dm
@@ -35,13 +35,13 @@
return
if(existing_message)
- user.visible_message("[user] starts erasing [existing_message]. ", "You start erasing [existing_message]. ", "You hear a chipping sound. ")
- playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1)
+ user.visible_message("[user] starts erasing [existing_message]. ", "You start erasing [existing_message]. ", "You hear a chipping sound. ")
+ playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1)
if(do_after(user, tool_speed, target = existing_message))
user.visible_message("[user] erases [existing_message]. ", "You erase [existing_message][existing_message.creator_key == user.ckey ? ", refunding a use" : ""]. ")
existing_message.persists = FALSE
qdel(existing_message)
- playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1)
+ playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1)
if(existing_message.creator_key == user.ckey)
refund_use()
return
@@ -54,12 +54,12 @@
if(!target.Adjacent(user) && locate(/obj/structure/chisel_message) in T)
to_chat(user, "Someone wrote here before you chose! Find another spot. ")
return
- playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1)
- user.visible_message("[user] starts engraving a message into [T]... ", "You start engraving a message into [T]... ", "You hear a chipping sound. ")
+ playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1)
+ user.visible_message("[user] starts engraving a message into [T]... ", "You start engraving a message into [T]... ", "You hear a chipping sound. ")
if(can_use() && do_after(user, tool_speed, target = T) && can_use()) //This looks messy but it's actually really clever!
if(!locate(/obj/structure/chisel_message) in T)
- user.visible_message("[user] leaves a message for future spacemen! ", "You engrave a message into [T]! ", "You hear a chipping sound. ")
- playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1)
+ user.visible_message("[user] leaves a message for future spacemen! ", "You engrave a message into [T]! ", "You hear a chipping sound. ")
+ playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1)
var/obj/structure/chisel_message/M = new(T)
M.register(user, message)
remove_use()
@@ -112,12 +112,10 @@
desc = "A message from a past traveler."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "soapstone_message"
- layer = HIGH_OBJ_LAYER
+ layer = LATTICE_LAYER
density = FALSE
anchored = TRUE
max_integrity = 30
- layer = LATTICE_LAYER
- light_power = 0.3
var/hidden_message
var/creator_key
@@ -206,10 +204,13 @@
/obj/structure/chisel_message/interact()
return
-/obj/structure/chisel_message/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/structure/chisel_message/ui_state(mob/user)
+ return GLOB.always_state
+
+/obj/structure/chisel_message/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "EngravedMessage", name, 600, 300, master_ui, state)
+ ui = new(user, src, "EngravedMessage", name)
ui.open()
/obj/structure/chisel_message/ui_data(mob/user)
diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm
index 4d0b7dff21..429dc98e8a 100644
--- a/code/modules/mining/laborcamp/laborstacker.dm
+++ b/code/modules/mining/laborcamp/laborstacker.dm
@@ -8,8 +8,6 @@ GLOBAL_LIST(labor_sheet_values)
icon = 'icons/obj/machines/mining_machines.dmi'
icon_state = "console"
density = FALSE
- ui_x = 315
- ui_y = 430
var/obj/machinery/mineral/stacking_machine/laborstacker/stacking_machine = null
var/machinedir = SOUTH
@@ -35,11 +33,10 @@ GLOBAL_LIST(labor_sheet_values)
/proc/cmp_sheet_list(list/a, list/b)
return a["value"] - b["value"]
-/obj/machinery/mineral/labor_claim_console/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/mineral/labor_claim_console/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "LaborClaimConsole", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "LaborClaimConsole", name)
ui.open()
/obj/machinery/mineral/labor_claim_console/ui_data(mob/user)
@@ -121,7 +118,7 @@ GLOBAL_LIST(labor_sheet_values)
/obj/machinery/mineral/stacking_machine/laborstacker
force_connect = TRUE
var/points = 0 //The unclaimed value of ore stacked.
-
+ //damage_deflection = 21
/obj/machinery/mineral/stacking_machine/laborstacker/process_sheet(obj/item/stack/sheet/inp)
points += inp.point_value * inp.amount
..()
diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm
index bae0e94032..0268d32a6b 100644
--- a/code/modules/mining/machine_redemption.dm
+++ b/code/modules/mining/machine_redemption.dm
@@ -195,10 +195,10 @@
to_chat(user, "You change [src]'s I/O settings, setting the input to [dir2text(input_dir)] and the output to [dir2text(output_dir)]. ")
return TRUE
-/obj/machinery/mineral/ore_redemption/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/mineral/ore_redemption/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "OreRedemptionMachine", "Ore Redemption Machine", 440, 550, master_ui, state)
+ ui = new(user, src, "OreRedemptionMachine")
ui.open()
/obj/machinery/mineral/ore_redemption/ui_data(mob/user)
diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm
index f6c9384baf..2cbb965ef5 100644
--- a/code/modules/mining/machine_vending.dm
+++ b/code/modules/mining/machine_vending.dm
@@ -7,8 +7,6 @@
icon_state = "mining"
density = TRUE
circuit = /obj/item/circuitboard/machine/mining_equipment_vendor
- ui_x = 425
- ui_y = 600
var/icon_deny = "mining-deny"
var/obj/item/card/id/inserted_id
var/list/prize_list = list( //if you add something to this, please, for the love of god, sort it by price/type. use tabs and not spaces.
@@ -102,17 +100,15 @@
else
icon_state = "[initial(icon_state)]-off"
-/obj/machinery/mineral/equipment_vendor/ui_base_html(html)
- var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/vending)
- . = replacetext(html, "", assets.css_tag())
+/obj/machinery/mineral/equipment_vendor/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/vending),
+ )
-/obj/machinery/mineral/equipment_vendor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/mineral/equipment_vendor/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/vending)
- assets.send(user)
- ui = new(user, src, ui_key, "MiningVendor", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "MiningVendor", name)
ui.open()
/obj/machinery/mineral/equipment_vendor/ui_static_data(mob/user)
@@ -137,10 +133,12 @@
if(C)
.["user"] = list()
.["user"]["points"] = C.mining_points
- if(C.assignment)
- .["user"]["job"] = C.assignment
- else
- .["user"]["job"] = "No Job"
+ if(C.registered_account)
+ .["user"]["name"] = C.registered_account.account_holder
+ if(C.registered_account.account_job)
+ .["user"]["job"] = C.registered_account.account_job.title
+ else
+ .["user"]["job"] = "No Job"
/obj/machinery/mineral/equipment_vendor/ui_act(action, params)
if(..())
diff --git a/code/modules/mining/satchel_ore_boxdm.dm b/code/modules/mining/satchel_ore_boxdm.dm
index ae42ca4745..36da9d5db9 100644
--- a/code/modules/mining/satchel_ore_boxdm.dm
+++ b/code/modules/mining/satchel_ore_boxdm.dm
@@ -9,9 +9,6 @@
density = TRUE
pressure_resistance = 5*ONE_ATMOSPHERE
- var/ui_x = 335
- var/ui_y = 415
-
/obj/structure/ore_box/attackby(obj/item/W, mob/user, params)
if (istype(W, /obj/item/stack/ore))
user.transferItemToLoc(W, src)
@@ -58,11 +55,10 @@
stoplag()
drop = drop_location()
-/obj/structure/ore_box/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/structure/ore_box/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "OreBox", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "OreBox", name)
ui.open()
/obj/structure/ore_box/ui_data()
diff --git a/code/modules/mob/dead/observer/notificationprefs.dm b/code/modules/mob/dead/observer/notificationprefs.dm
index 160abd57e1..524ff80d5d 100644
--- a/code/modules/mob/dead/observer/notificationprefs.dm
+++ b/code/modules/mob/dead/observer/notificationprefs.dm
@@ -3,12 +3,10 @@
set name = "Notification preferences"
set desc = "Notification preferences"
- var/datum/notificationpanel/panel = new(usr)
+ var/datum/notificationpanel/panel = new(usr)
panel.ui_interact(usr)
-
-
/datum/notificationpanel
var/client/user
@@ -21,10 +19,13 @@
else
src.user = user
-/datum/notificationpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/notificationpanel/ui_state(mob/user)
+ return GLOB.observer_state
+
+/datum/notificationpanel/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "NotificationPreferences", "Notification Preferences", 270, 360, master_ui, state)
+ ui = new(user, src, "NotificationPreferences")
ui.open()
/datum/notificationpanel/ui_data(mob/user)
@@ -35,8 +36,7 @@
"key" = key,
"enabled" = (user.ckey in GLOB.poll_ignore[key]),
"desc" = GLOB.poll_ignore_desc[key]
- ))
-
+ ))
/datum/notificationpanel/ui_act(action, params)
if(..())
diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm
index eb23a5fb67..b81172afad 100644
--- a/code/modules/mob/dead/observer/orbit.dm
+++ b/code/modules/mob/dead/observer/orbit.dm
@@ -6,9 +6,12 @@
qdel(src)
owner = new_owner
-/datum/orbit_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
+/datum/orbit_menu/ui_state(mob/user)
+ return GLOB.observer_state
+
+/datum/orbit_menu/ui_interact(mob/user, datum/tgui/ui)
if (!ui)
- ui = new(user, src, ui_key, "Orbit", "Orbit", 350, 700, master_ui, state)
+ ui = new(user, src, "Orbit")
ui.open()
/datum/orbit_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
@@ -16,11 +19,10 @@
return
if (action == "orbit")
- var/list/pois = getpois(skip_mindless = 1)
- var/atom/movable/poi = pois[params["name"]]
+ var/ref = params["ref"]
+ var/atom/movable/poi = (locate(ref) in GLOB.mob_list) || (locate(ref) in GLOB.poi_list)
if (poi != null)
owner.ManualFollow(poi)
- ui.close()
/datum/orbit_menu/ui_data(mob/user)
var/list/data = list()
@@ -39,6 +41,8 @@
var/poi = pois[name]
+ serialized["ref"] = REF(poi)
+
var/mob/M = poi
if (istype(M))
if (isobserver(M))
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index 9d7f99c14e..c762a52ffc 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -287,11 +287,16 @@
else
ui_interact(owner)
-/datum/action/innate/swap_body/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
+/datum/action/innate/swap_body/ui_host(mob/user)
+ return owner
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/action/innate/swap_body/ui_state(mob/user)
+ return GLOB.not_incapacitated_state
+
+/datum/action/innate/swap_body/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "SlimeBodySwapper", name, 400, 400, master_ui, state)
+ ui = new(user, src, "SlimeBodySwapper", name)
ui.open()
/datum/action/innate/swap_body/ui_data(mob/user)
@@ -361,7 +366,8 @@
return
switch(action)
if("swap")
- var/mob/living/carbon/human/selected = locate(params["ref"])
+ var/datum/species/jelly/slime/SS = H.dna.species
+ var/mob/living/carbon/human/selected = locate(params["ref"]) in SS.bodies
if(!can_swap(selected))
return
SStgui.close_uis(src)
diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm
index 4f2332e99d..2cbd1f35ca 100644
--- a/code/modules/mob/living/silicon/ai/life.dm
+++ b/code/modules/mob/living/silicon/ai/life.dm
@@ -151,7 +151,7 @@
to_chat(src, "Receiving control information from APC.")
sleep(2)
apc_override = 1
- theAPC.ui_interact(src, state = GLOB.conscious_state)
+ theAPC.ui_interact(src)
apc_override = 0
aiRestorePowerRoutine = POWER_RESTORATION_APC_FOUND
sleep(50)
diff --git a/code/modules/mob/living/silicon/ai/robot_control.dm b/code/modules/mob/living/silicon/ai/robot_control.dm
index 0eaea103f2..bbfb7604ba 100644
--- a/code/modules/mob/living/silicon/ai/robot_control.dm
+++ b/code/modules/mob/living/silicon/ai/robot_control.dm
@@ -19,11 +19,13 @@
return ..()
return UI_CLOSE
-/datum/robot_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/robot_control/ui_state(mob/user)
+ return GLOB.always_state
+
+/datum/robot_control/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "RemoteRobotControl", "Remote Robot Control", 500, 500, master_ui, state)
+ ui = new(user, src, "RemoteRobotControl")
ui.open()
/datum/robot_control/ui_data(mob/user)
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 5e96818766..d9bf8cc553 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -29,9 +29,6 @@
model = "MULE"
bot_core_type = /obj/machinery/bot_core/mulebot
- var/ui_x = 350
- var/ui_y = 425
-
var/id
path_image_color = "#7F5200"
@@ -170,11 +167,10 @@
return
ui_interact(user)
-/mob/living/simple_animal/bot/mulebot/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/mob/living/simple_animal/bot/mulebot/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Mule", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "Mule", name)
ui.open()
/mob/living/simple_animal/bot/mulebot/ui_data(mob/user)
@@ -191,8 +187,7 @@
data["modeStatus"] = "average"
if(BOT_NO_ROUTE)
data["modeStatus"] = "bad"
- else
- data["load"] = load ? load.name : null
+ data["load"] = load ? load.name : null //IF YOU CHANGE THE NAME OF THIS, UPDATE MULEBOT/PARANORMAL/UI_DATA.
data["destination"] = destination ? destination : null
data["home"] = home_destination
data["destinations"] = GLOB.deliverybeacontags
@@ -206,18 +201,20 @@
return data
/mob/living/simple_animal/bot/mulebot/ui_act(action, params)
- var/silicon_access = hasSiliconAccessInArea(usr)
- if(..() || (locked && silicon_access))
+ if(..() || (locked && hasSiliconAccessInArea(usr)))
return
switch(action)
if("lock")
- if(silicon_access)
+ if(hasSiliconAccessInArea(usr))
locked = !locked
. = TRUE
if("power")
if(on)
turn_off()
- else if(cell && !open)
+ else if(open)
+ to_chat(usr, "[name]'s maintenance panel is open! ")
+ return
+ else if(cell)
if(!turn_on())
to_chat(usr, "You can't switch on [src]! ")
return
diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
index eeb5212aeb..b5f3bae53d 100644
--- a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
+++ b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
@@ -73,4 +73,4 @@
add_status_message("[client.username] has changed channel title from [title] to [newtitle]")
title = newtitle
-#undef MAX_CHANNELS
\ No newline at end of file
+#undef MAX_CHANNELS
diff --git a/code/modules/modular_computers/computers/_modular_computer_shared.dm b/code/modules/modular_computers/computers/_modular_computer_shared.dm
new file mode 100644
index 0000000000..8ca93e8347
--- /dev/null
+++ b/code/modules/modular_computers/computers/_modular_computer_shared.dm
@@ -0,0 +1,64 @@
+
+/obj/proc/is_modular_computer()
+ return
+
+/obj/proc/get_modular_computer_part(part_type)
+ return null
+
+/obj/item/modular_computer/is_modular_computer()
+ return TRUE
+
+/obj/item/modular_computer/get_modular_computer_part(part_type)
+ if(!part_type)
+ stack_trace("get_modular_computer_part() called without a valid part_type")
+ return null
+ return all_components[part_type]
+
+
+/obj/machinery/modular_computer/is_modular_computer()
+ return TRUE
+
+/obj/machinery/modular_computer/get_modular_computer_part(part_type)
+ if(!part_type)
+ stack_trace("get_modular_computer_part() called without a valid part_type")
+ return null
+ return cpu?.all_components[part_type]
+
+
+/obj/proc/get_modular_computer_parts_examine(mob/user)
+ . = list()
+ if(!is_modular_computer())
+ return
+
+ var/user_is_adjacent = Adjacent(user) //don't reveal full details unless they're close enough to see it on the screen anyway.
+
+ var/obj/item/computer_hardware/ai_slot/ai_slot = get_modular_computer_part(MC_AI)
+ if(ai_slot)
+ if(ai_slot.stored_card)
+ if(user_is_adjacent)
+ . += "It has a slot installed for an intelliCard which contains: [ai_slot.stored_card.name]"
+ else
+ . += "It has a slot installed for an intelliCard, which appears to be occupied."
+ . += "Alt-click to eject the intelliCard. "
+ else
+ . += "It has a slot installed for an intelliCard."
+
+ var/obj/item/computer_hardware/card_slot/card_slot = get_modular_computer_part(MC_CARD)
+ if(card_slot)
+ if(card_slot.stored_card || card_slot.stored_card2)
+ var/obj/item/card/id/first_ID = card_slot.stored_card
+ var/obj/item/card/id/second_ID = card_slot.stored_card2
+ var/multiple_cards = istype(first_ID) && istype(second_ID)
+ if(user_is_adjacent)
+ . += "It has two slots for identification cards installed[multiple_cards ? " which contain [first_ID] and [second_ID]" : ", one of which contains [first_ID ? first_ID : second_ID]"]."
+ else
+ . += "It has two slots for identification cards installed, [multiple_cards ? "both of which appear" : "and one of them appears"] to be occupied."
+ . += "Alt-click [src] to eject the identification card[multiple_cards ? "s":""]. "
+ else
+ . += "It has two slots installed for identification cards."
+
+ var/obj/item/computer_hardware/printer/printer_slot = get_modular_computer_part(MC_PRINT)
+ if(printer_slot)
+ . += "It has a printer installed."
+ if(user_is_adjacent)
+ . += "The printer's paper levels are at: [printer_slot.stored_paper]/[printer_slot.max_paper].]"
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index d722197bd9..67c04de13f 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -7,6 +7,7 @@
var/enabled = 0 // Whether the computer is turned on.
var/screen_on = 1 // Whether the computer is active/opened/it's screen is on.
+ var/device_theme = "ntos" // Sets the theme for the main menu, hardware config, and file browser apps. Overridden by certain non-NT devices.
var/datum/computer_file/program/active_program = null // A currently active program running on the computer.
var/hardware_flag = 0 // A flag that describes this device type
var/last_power_usage = 0
@@ -98,7 +99,7 @@
if(issilicon(usr))
return
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
- if(usr.canUseTopic(src))
+ if(usr.canUseTopic(src, BE_CLOSE))
card_slot.try_eject(null, usr)
// Eject ID card from computer, if it has ID slot with card inside.
@@ -109,7 +110,7 @@
if(issilicon(usr))
return
var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI]
- if(usr.canUseTopic(src))
+ if(usr.canUseTopic(src, BE_CLOSE))
ai_slot.try_eject(null, usr,1)
@@ -121,17 +122,17 @@
if(issilicon(usr))
return
- if(usr.canUseTopic(src))
+ if(usr.canUseTopic(src, BE_CLOSE))
var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD]
if(uninstall_component(portable_drive, usr))
portable_drive.verb_pickup()
/obj/item/modular_computer/AltClick(mob/user)
- . = ..()
+ ..()
if(issilicon(user))
return
- if(user.canUseTopic(src))
+ if(user.canUseTopic(src, BE_CLOSE))
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI]
var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD]
@@ -143,7 +144,7 @@
return
if(ai_slot)
ai_slot.try_eject(null, user)
- return TRUE
+
// Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs.
/obj/item/modular_computer/GetAccess()
@@ -175,7 +176,7 @@
/obj/item/modular_computer/MouseDrop(obj/over_object, src_location, over_location)
var/mob/M = usr
- if((!istype(over_object, /obj/screen)) && usr.canUseTopic(src))
+ if((!istype(over_object, /obj/screen)) && usr.canUseTopic(src, BE_CLOSE))
return attack_self(M)
return ..()
@@ -195,12 +196,22 @@
/obj/item/modular_computer/emag_act(mob/user)
. = ..()
- if(obj_flags & EMAGGED)
- to_chat(user, "\The [src] was already emagged. ")
- return
- obj_flags |= EMAGGED
- to_chat(user, "You emag \the [src]. It's screen briefly shows a \"OVERRIDE ACCEPTED: New software downloads available.\" message. ")
- return TRUE
+ if(!enabled)
+ to_chat(user, "You'd need to turn the [src] on first. ")
+ return FALSE
+ obj_flags |= EMAGGED //Mostly for consistancy purposes; the programs will do their own emag handling
+ var/newemag = FALSE
+ var/obj/item/computer_hardware/hard_drive/drive = all_components[MC_HDD]
+ for(var/datum/computer_file/program/app in drive.stored_files)
+ if(!istype(app))
+ continue
+ if(app.run_emag())
+ newemag = TRUE
+ if(newemag)
+ to_chat(user, "You swipe \the [src]. A console window momentarily fills the screen, with white text rapidly scrolling past. ")
+ return TRUE
+ to_chat(user, "You swipe \the [src]. A console window fills the screen, but it quickly closes itself after only a few lines are written to it. ")
+ return FALSE
/obj/item/modular_computer/examine(mob/user)
. = ..()
@@ -209,13 +220,14 @@
else if(obj_integrity < max_integrity)
. += "It is damaged. "
+ . += get_modular_computer_parts_examine(user)
+
/obj/item/modular_computer/update_icon_state()
if(!enabled)
icon_state = icon_state_unpowered
else
icon_state = icon_state_powered
-
/obj/item/modular_computer/update_overlays()
. = ..()
if(!display_overlays)
@@ -306,6 +318,8 @@
/obj/item/modular_computer/proc/get_header_data()
var/list/data = list()
+ data["PC_device_theme"] = device_theme
+
var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL]
var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE]
@@ -407,17 +421,17 @@
if(install_component(W, user))
return
- if(istype(W, /obj/item/wrench))
+ if(W.tool_behaviour == TOOL_WRENCH)
if(all_components.len)
to_chat(user, "Remove all components from \the [src] before disassembling it. ")
return
new /obj/item/stack/sheet/metal( get_turf(src.loc), steel_sheet_cost )
- physical.visible_message("\The [src] has been disassembled by [user].")
+ physical.visible_message("\The [src] is disassembled by [user]. ")
relay_qdel()
qdel(src)
return
- if(istype(W, /obj/item/weldingtool))
+ if(W.tool_behaviour == TOOL_WELDER)
if(obj_integrity == max_integrity)
to_chat(user, "\The [src] does not require repairs. ")
return
@@ -431,7 +445,7 @@
to_chat(user, "You repair \the [src]. ")
return
- if(istype(W, /obj/item/screwdriver))
+ if(W.tool_behaviour == TOOL_SCREWDRIVER)
if(!all_components.len)
to_chat(user, "This device doesn't have any components installed. ")
return
@@ -440,7 +454,7 @@
var/obj/item/computer_hardware/H = all_components[h]
component_names.Add(H.name)
- var/choice = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in component_names
+ var/choice = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in sortList(component_names)
if(!choice)
return
diff --git a/code/modules/modular_computers/computers/item/computer_damage.dm b/code/modules/modular_computers/computers/item/computer_damage.dm
index 6664b449bd..b510f8aded 100644
--- a/code/modules/modular_computers/computers/item/computer_damage.dm
+++ b/code/modules/modular_computers/computers/item/computer_damage.dm
@@ -18,13 +18,13 @@
/obj/item/modular_computer/proc/break_apart()
if(!(flags_1 & NODECONSTRUCT_1))
- physical.visible_message("\The [src] breaks apart!")
+ physical.visible_message("\The [src] breaks apart! ")
var/turf/newloc = get_turf(src)
new /obj/item/stack/sheet/metal(newloc, round(steel_sheet_cost/2))
for(var/C in all_components)
var/obj/item/computer_hardware/H = all_components[C]
if(QDELETED(H))
- return
+ continue
uninstall_component(H)
H.forceMove(newloc)
if(prob(25))
diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm
index d3c65f86ec..b5188f43d9 100644
--- a/code/modules/modular_computers/computers/item/computer_power.dm
+++ b/code/modules/modular_computers/computers/item/computer_power.dm
@@ -28,8 +28,7 @@
/obj/item/modular_computer/get_cell()
var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL]
- if(battery_module && battery_module.battery)
- return battery_module.battery
+ return battery_module?.get_cell()
// Used in following function to reduce copypaste
/obj/item/modular_computer/proc/power_failure()
diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm
index 6266daad2b..fd017e2b0f 100644
--- a/code/modules/modular_computers/computers/item/computer_ui.dm
+++ b/code/modules/modular_computers/computers/item/computer_ui.dm
@@ -3,7 +3,7 @@
ui_interact(user)
// Operates TGUI
-/obj/item/modular_computer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+/obj/item/modular_computer/ui_interact(mob/user, datum/tgui/ui)
if(!enabled)
if(ui)
ui.close()
@@ -14,7 +14,7 @@
return 0
// Robots don't really need to see the screen, their wireless connection works as long as computer is on.
- if(!screen_on && !hasSiliconAccessInArea(user))
+ if(!screen_on && !issilicon(user))
if(ui)
ui.close()
return 0
@@ -33,19 +33,17 @@
to_chat(user, "\The [src] beeps three times, it's screen displaying a \"DISK ERROR\" warning. ")
return // No HDD, No HDD files list or no stored files. Something is very broken.
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
- var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
- assets.send(user)
- assets = get_asset_datum(/datum/asset/simple/arcade)
- assets.send(user)
- ui = new(user, src, ui_key, "NtosMain", "NtOS Main menu", 400, 500, master_ui, state)
+ ui = new(user, src, "NtosMain")
+ ui.set_autoupdate(TRUE)
ui.open()
- ui.set_autoupdate(state = 1)
+ ui.send_asset(get_asset_datum(/datum/asset/simple/headers))
/obj/item/modular_computer/ui_data(mob/user)
var/list/data = get_header_data()
+ data["device_theme"] = device_theme
data["programs"] = list()
var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD]
for(var/datum/computer_file/program/P in hard_drive.stored_files)
@@ -143,6 +141,7 @@
set_light(comp_light_luminosity, 1, comp_light_color)
else
set_light(0)
+ return TRUE
if("PC_light_color")
var/mob/user = usr
diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm
index da4bd3a463..5927d57a0b 100644
--- a/code/modules/modular_computers/computers/item/laptop.dm
+++ b/code/modules/modular_computers/computers/item/laptop.dm
@@ -7,6 +7,7 @@
icon_state_powered = "laptop"
icon_state_unpowered = "laptop-off"
icon_state_menu = "menu"
+ display_overlays = FALSE
hardware_flag = PROGRAM_LAPTOP
max_hardware_size = 2
@@ -18,8 +19,8 @@
screen_on = 0 // Starts closed
var/start_open = TRUE // unless this var is set to 1
var/icon_state_closed = "laptop-closed"
- display_overlays = FALSE
var/w_class_open = WEIGHT_CLASS_BULKY
+ var/slowdown_open = TRUE
/obj/item/modular_computer/laptop/examine(mob/user)
. = ..()
@@ -38,6 +39,13 @@
else
. = ..()
+/obj/item/modular_computer/laptop/update_overlays()
+ if(screen_on)
+ return ..()
+ else
+ cut_overlays()
+ icon_state = icon_state_closed
+
/obj/item/modular_computer/laptop/attack_self(mob/user)
if(!screen_on)
try_toggle_open(user)
@@ -74,7 +82,7 @@
return
if(!isturf(loc) && !ismob(loc)) // No opening it in backpack.
return
- if(!user.canUseTopic(src))
+ if(!user.canUseTopic(src, BE_CLOSE))
return
toggle_open(user)
@@ -83,15 +91,17 @@
/obj/item/modular_computer/laptop/AltClick(mob/user)
if(screen_on) // Close it.
try_toggle_open(user)
- return TRUE
- return ..()
+ else
+ return ..()
/obj/item/modular_computer/laptop/proc/toggle_open(mob/living/user=null)
if(screen_on)
to_chat(user, "You close \the [src]. ")
+ slowdown = initial(slowdown)
w_class = initial(w_class)
else
to_chat(user, "You open \the [src]. ")
+ slowdown = slowdown_open
w_class = w_class_open
screen_on = !screen_on
diff --git a/code/modules/modular_computers/computers/item/laptop_presets.dm b/code/modules/modular_computers/computers/item/laptop_presets.dm
index e50392e3b3..6bc2919bea 100644
--- a/code/modules/modular_computers/computers/item/laptop_presets.dm
+++ b/code/modules/modular_computers/computers/item/laptop_presets.dm
@@ -20,4 +20,3 @@
/obj/item/modular_computer/laptop/preset/civilian/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD]
hard_drive.store_file(new/datum/computer_file/program/chatclient())
- hard_drive.store_file(new/datum/computer_file/program/nttransfer())
diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm
index 81e2cb95e6..c79d7a9361 100644
--- a/code/modules/modular_computers/computers/item/processor.dm
+++ b/code/modules/modular_computers/computers/item/processor.dm
@@ -1,4 +1,5 @@
// Held by /obj/machinery/modular_computer to reduce amount of copy-pasted code.
+//TODO: REFACTOR THIS SPAGHETTI CODE, MAKE IT A COMPUTER_HARDWARE COMPONENT OR REMOVE IT
/obj/item/modular_computer/processor
name = "processing unit"
desc = "You shouldn't see this. If you do, report it."
@@ -11,19 +12,22 @@
var/obj/machinery/modular_computer/machinery_computer = null
/obj/item/modular_computer/processor/Destroy()
- . = ..()
if(machinery_computer && (machinery_computer.cpu == src))
machinery_computer.cpu = null
+ machinery_computer.UnregisterSignal(src, COMSIG_ATOM_UPDATED_ICON)
machinery_computer = null
-
-/obj/item/modular_computer/processor/Initialize(mapload)
. = ..()
- if(!loc || !istype(loc, /obj/machinery/modular_computer))
+
+/obj/item/modular_computer/processor/New(comp) //intentional new probably
+ ..()
+ STOP_PROCESSING(SSobj, src) // Processed by its machine
+
+ if(!comp || !istype(comp, /obj/machinery/modular_computer))
CRASH("Inapropriate type passed to obj/item/modular_computer/processor/New()! Aborting.")
// Obtain reference to machinery computer
all_components = list()
idle_threads = list()
- machinery_computer = loc
+ machinery_computer = comp
machinery_computer.cpu = src
hardware_flag = machinery_computer.hardware_flag
max_hardware_size = machinery_computer.max_hardware_size
@@ -39,7 +43,7 @@
qdel(machinery_computer)
// This thing is not meant to be used on it's own, get topic data from our machinery owner.
-//obj/item/modular_computer/processor/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)
+//obj/item/modular_computer/processor/canUseTopic(atom/movable/M, be_close=FALSE, no_dexterity=FALSE, no_tk=FALSE)
// if(!machinery_computer)
// return 0
@@ -69,3 +73,6 @@
machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_disk
if(MC_AI)
machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_card
+
+/obj/item/modular_computer/processor/attack_ghost(mob/user)
+ ui_interact(user)
diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm
index a371e97ec6..41a256467f 100644
--- a/code/modules/modular_computers/computers/item/tablet.dm
+++ b/code/modules/modular_computers/computers/item/tablet.dm
@@ -5,6 +5,7 @@
icon_state_unpowered = "tablet"
icon_state_powered = "tablet"
icon_state_menu = "menu"
+ //worn_icon_state = "tablet"
hardware_flag = PROGRAM_TABLET
max_hardware_size = 1
w_class = WEIGHT_CLASS_SMALL
@@ -32,3 +33,17 @@
slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT
comp_light_luminosity = 6.3
has_variants = FALSE
+
+/// Given to Nuke Ops members.
+/obj/item/modular_computer/tablet/nukeops
+ icon_state = "tablet-syndicate"
+ comp_light_luminosity = 6.3
+ has_variants = FALSE
+ device_theme = "syndicate"
+
+/obj/item/modular_computer/tablet/nukeops/emag_act(mob/user)
+ if(!enabled)
+ to_chat(user, "You'd need to turn the [src] on first. ")
+ return FALSE
+ to_chat(user, "You swipe \the [src]. It's screen briefly shows a message reading \"MEMORY CODE INJECTION DETECTED AND SUCCESSFULLY QUARANTINED\". ")
+ return FALSE
diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm
index f516d3802f..7cca8ea5b4 100644
--- a/code/modules/modular_computers/computers/item/tablet_presets.dm
+++ b/code/modules/modular_computers/computers/item/tablet_presets.dm
@@ -22,23 +22,38 @@
/obj/item/modular_computer/tablet/preset/cargo/Initialize()
. = ..()
+ var/obj/item/computer_hardware/hard_drive/small/hard_drive = new
install_component(new /obj/item/computer_hardware/processor_unit/small)
install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer))
- install_component(new /obj/item/computer_hardware/hard_drive/small)
+ install_component(hard_drive)
install_component(new /obj/item/computer_hardware/network_card)
install_component(new /obj/item/computer_hardware/printer/mini)
+ hard_drive.store_file(new /datum/computer_file/program/bounty)
+ //hard_drive.store_file(new /datum/computer_file/program/shipping)
-/obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink/Initialize() // Given by the syndicate as part of the contract uplink bundle - loads in the Contractor Uplink.
+/// Given by the syndicate as part of the contract uplink bundle - loads in the Contractor Uplink.
+/obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink/Initialize()
. = ..()
var/obj/item/computer_hardware/hard_drive/small/syndicate/hard_drive = new
var/datum/computer_file/program/contract_uplink/uplink = new
+
active_program = uplink
uplink.program_state = PROGRAM_STATE_ACTIVE
uplink.computer = src
+
hard_drive.store_file(uplink)
+
install_component(new /obj/item/computer_hardware/processor_unit/small)
install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer))
install_component(hard_drive)
install_component(new /obj/item/computer_hardware/network_card)
install_component(new /obj/item/computer_hardware/card_slot)
- install_component(new /obj/item/computer_hardware/printer/mini)
\ No newline at end of file
+ install_component(new /obj/item/computer_hardware/printer/mini)
+
+/// Given to Nuke Ops members.
+/obj/item/modular_computer/tablet/nukeops/Initialize()
+ . = ..()
+ install_component(new /obj/item/computer_hardware/processor_unit/small)
+ install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer))
+ install_component(new /obj/item/computer_hardware/hard_drive/small/nukeops)
+ install_component(new /obj/item/computer_hardware/network_card)
diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm
index 066f1fb98b..9d29b23e76 100644
--- a/code/modules/modular_computers/computers/machinery/console_presets.dm
+++ b/code/modules/modular_computers/computers/machinery/console_presets.dm
@@ -46,16 +46,12 @@
desc = "A stationary computer. This one comes preloaded with research programs."
_has_ai = TRUE
-/obj/machinery/modular_computer/console/preset/research/examine(mob/user)
- . = ..()
- . += "Alt-click to eject the intelliCard. "
-
/obj/machinery/modular_computer/console/preset/research/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
hard_drive.store_file(new/datum/computer_file/program/ntnetmonitor())
- hard_drive.store_file(new/datum/computer_file/program/nttransfer())
hard_drive.store_file(new/datum/computer_file/program/chatclient())
hard_drive.store_file(new/datum/computer_file/program/aidiag())
+ hard_drive.store_file(new/datum/computer_file/program/robocontrol())
// ===== COMMAND CONSOLE =====
@@ -66,15 +62,27 @@
_has_id_slot = TRUE
_has_printer = TRUE
-/obj/machinery/modular_computer/console/preset/command/examine(mob/user)
- . = ..()
- . += "Alt-click [src] to eject the identification card. "
-
/obj/machinery/modular_computer/console/preset/command/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
hard_drive.store_file(new/datum/computer_file/program/chatclient())
hard_drive.store_file(new/datum/computer_file/program/card_mod())
+
+// ===== IDENTIFICATION CONSOLE =====
+/obj/machinery/modular_computer/console/preset/id
+ console_department = "Identification"
+ name = "identification console"
+ desc = "A stationary computer. This one comes preloaded with identification modification programs."
+ _has_id_slot = TRUE
+ _has_printer = TRUE
+
+/obj/machinery/modular_computer/console/preset/id/install_programs()
+ var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
+ hard_drive.store_file(new/datum/computer_file/program/chatclient())
+ hard_drive.store_file(new/datum/computer_file/program/card_mod())
+ hard_drive.store_file(new/datum/computer_file/program/job_management())
+ hard_drive.store_file(new/datum/computer_file/program/crew_manifest())
+
// ===== CIVILIAN CONSOLE =====
/obj/machinery/modular_computer/console/preset/civilian
console_department = "Civilian"
@@ -84,5 +92,4 @@
/obj/machinery/modular_computer/console/preset/civilian/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
hard_drive.store_file(new/datum/computer_file/program/chatclient())
- hard_drive.store_file(new/datum/computer_file/program/nttransfer())
hard_drive.store_file(new/datum/computer_file/program/arcade())
diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm
index 7e384c3ddd..6f016ad147 100644
--- a/code/modules/modular_computers/computers/machinery/modular_computer.dm
+++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm
@@ -36,6 +36,10 @@
QDEL_NULL(cpu)
return ..()
+/obj/machinery/modular_computer/examine(mob/user)
+ . = ..()
+ . += get_modular_computer_parts_examine(user)
+
/obj/machinery/modular_computer/attack_ghost(mob/dead/observer/user)
. = ..()
if(.)
@@ -45,31 +49,31 @@
/obj/machinery/modular_computer/emag_act(mob/user)
. = ..()
- if(cpu)
- . |= cpu.emag_act(user)
+ if(!cpu)
+ to_chat(user, "You'd need to turn the [src] on first. ")
+ return FALSE
+ return (cpu.emag_act(user))
-/obj/machinery/modular_computer/update_icon_state()
- if(cpu?.enabled)
- icon_state = icon_state_powered
- else if(stat & NOPOWER || !(cpu?.use_power()))
- icon_state = icon_state_unpowered
+/obj/machinery/modular_computer/update_icon()
+ cut_overlays()
+ icon_state = icon_state_powered
-/obj/machinery/modular_computer/update_overlays()
- . = ..()
if(!cpu || !cpu.enabled)
if (!(stat & NOPOWER) && (cpu && cpu.use_power()))
- . += screen_icon_screensaver
+ add_overlay(screen_icon_screensaver)
+ else
+ icon_state = icon_state_unpowered
set_light(0)
else
set_light(light_strength)
if(cpu.active_program)
- . += cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu
+ add_overlay(cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu)
else
- . += screen_icon_state_menu
+ add_overlay(screen_icon_state_menu)
if(cpu && cpu.obj_integrity <= cpu.integrity_failure * cpu.max_integrity)
- . += "bsod"
- . += "broken"
+ add_overlay("bsod")
+ add_overlay("broken")
// Eject ID card from computer, if it has ID slot with card inside.
/obj/machinery/modular_computer/proc/eject_id()
@@ -96,10 +100,10 @@
cpu.eject_card()
/obj/machinery/modular_computer/AltClick(mob/user)
- . = ..()
if(cpu)
- return cpu.AltClick(user)
+ cpu.AltClick(user)
+//ATTACK HAND IGNORING PARENT RETURN VALUE
// On-click handling. Turns on the computer if it's off and opens the GUI.
/obj/machinery/modular_computer/interact(mob/user)
if(cpu)
@@ -130,8 +134,7 @@
stat &= ~NOPOWER
update_icon()
return
- ..()
- update_icon()
+ . = ..()
/obj/machinery/modular_computer/attackby(var/obj/item/W as obj, mob/user)
if(cpu && !(flags_1 & NODECONSTRUCT_1))
@@ -144,6 +147,13 @@
/obj/machinery/modular_computer/ex_act(severity)
if(cpu)
cpu.ex_act(severity)
+ // switch(severity)
+ // if(EXPLODE_DEVASTATE)
+ // SSexplosions.highobj += cpu
+ // if(EXPLODE_HEAVY)
+ // SSexplosions.medobj += cpu
+ // if(EXPLODE_LIGHT)
+ // SSexplosions.lowobj += cpu
..()
// EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components
diff --git a/code/modules/modular_computers/computers/machinery/modular_console.dm b/code/modules/modular_computers/computers/machinery/modular_console.dm
index 3d4ec22e89..5d596f98e4 100644
--- a/code/modules/modular_computers/computers/machinery/modular_console.dm
+++ b/code/modules/modular_computers/computers/machinery/modular_console.dm
@@ -52,4 +52,4 @@
network_card.identification_string = "Unknown Console"
if(cpu)
cpu.screen_on = 1
- update_icon()
\ No newline at end of file
+ update_icon()
diff --git a/code/modules/modular_computers/documentation.md b/code/modules/modular_computers/documentation.md
index 246da7c3d9..88d059da7a 100644
--- a/code/modules/modular_computers/documentation.md
+++ b/code/modules/modular_computers/documentation.md
@@ -1,5 +1,7 @@
# Modular computer programs
+How module computer programs work
+
Ok. so a quick rundown on how to make a program. This is kind of a shitty documentation, but oh well I was asked to.
## Base setup
diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm
index 7776fc04d0..4e862c4ae3 100644
--- a/code/modules/modular_computers/file_system/computer_file.dm
+++ b/code/modules/modular_computers/file_system/computer_file.dm
@@ -3,8 +3,8 @@
var/filetype = "XXX" // File full names are [filename].[filetype] so like NewFile.XXX in this case
var/size = 1 // File size in GQ. Integers only!
var/obj/item/computer_hardware/hard_drive/holder // Holder that contains this file.
- var/unsendable = 0 // Whether the file may be sent to someone via NTNet transfer or other means.
- var/undeletable = 0 // Whether the file may be deleted. Setting to 1 prevents deletion/renaming/etc.
+ var/unsendable = FALSE // Whether the file may be sent to someone via NTNet transfer or other means.
+ var/undeletable = FALSE // Whether the file may be deleted. Setting to TRUE prevents deletion/renaming/etc.
var/uid // UID of this file
var/static/file_uid = 0
@@ -24,7 +24,7 @@
return ..()
// Returns independent copy of this file.
-/datum/computer_file/proc/clone(rename = 0)
+/datum/computer_file/proc/clone(rename = FALSE)
var/datum/computer_file/temp = new type
temp.unsendable = unsendable
temp.undeletable = undeletable
@@ -34,4 +34,4 @@
else
temp.filename = filename
temp.filetype = filetype
- return temp
\ No newline at end of file
+ return temp
diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm
index 6b5390fc60..12e5ef6e95 100644
--- a/code/modules/modular_computers/file_system/program.dm
+++ b/code/modules/modular_computers/file_system/program.dm
@@ -31,15 +31,10 @@
var/available_on_ntnet = 1
/// Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable.
var/available_on_syndinet = 0
- /// ID of TGUI interface
+ /// Name of the tgui interface
var/tgui_id
- /// Default size of TGUI window, in pixels
- var/ui_x = 575
- var/ui_y = 700
/// Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /icons/program_icons. Be careful not to use too large images!
var/ui_header = null
- ///Assets specific to programs
- var/list/special_assets = list()
/datum/computer_file/program/New(obj/item/modular_computer/comp = null)
..()
@@ -69,23 +64,23 @@
/datum/computer_file/program/proc/generate_network_log(text)
if(computer)
return computer.add_log(text)
- return FALSE
+ return 0
/datum/computer_file/program/proc/is_supported_by_hardware(hardware_flag = 0, loud = 0, mob/user = null)
if(!(hardware_flag & usage_flags))
if(loud && computer && user)
- to_chat(user, "\The [computer] flashes an \"Hardware Error - Incompatible software\" warning. ")
- return FALSE
- return TRUE
+ to_chat(user, "\The [computer] flashes a \"Hardware Error - Incompatible software\" warning. ")
+ return 0
+ return 1
/datum/computer_file/program/proc/get_signal(specific_action = 0)
if(computer)
return computer.get_ntnet_status(specific_action)
- return FALSE
+ return 0
// Called by Process() on device that runs us, once every tick.
/datum/computer_file/program/proc/process_tick()
- return TRUE
+ return 1
// Check if the user can run program. Only humans can operate computer. Automatically called in run_program()
// User has to wear their ID for ID Scan to work.
@@ -131,7 +126,7 @@
return TRUE
if(loud)
to_chat(user, "\The [computer] flashes an \"Access Denied\" warning. ")
- return FALSE
+ return 0
// This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones
// always include the device here in this proc. This proc basically relays the request to whatever is running the program.
@@ -143,11 +138,25 @@
// This is performed on program startup. May be overridden to add extra logic. Remember to include ..() call. Return 1 on success, 0 on failure.
// When implementing new program based device, use this to run the program.
/datum/computer_file/program/proc/run_program(mob/living/user)
- if(can_run(user, TRUE))
+ if(can_run(user, 1))
if(requires_ntnet && network_destination)
generate_network_log("Connection opened to [network_destination].")
program_state = PROGRAM_STATE_ACTIVE
- return TRUE
+ return 1
+ return 0
+
+/**
+ *
+ *Called by the device when it is emagged.
+ *
+ *Emagging the device allows certain programs to unlock new functions. However, the program will
+ *need to be downloaded first, and then handle the unlock on their own in their run_emag() proc.
+ *The device will allow an emag to be run multiple times, so the user can re-emag to run the
+ *override again, should they download something new. The run_emag() proc should return TRUE if
+ *the emagging affected anything, and FALSE if no change was made (already emagged, or has no
+ *emag functions).
+**/
+/datum/computer_file/program/proc/run_emag()
return FALSE
// Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client.
@@ -155,20 +164,14 @@
program_state = PROGRAM_STATE_KILLED
if(network_destination)
generate_network_log("Connection to [network_destination] closed.")
- return TRUE
+ return 1
-
-/datum/computer_file/program/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/computer_file/program/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui && tgui_id)
- var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
- assets.send(user)
- for(var/i in special_assets)
- assets = get_asset_datum(i)
- assets.send(user)
-
- ui = new(user, src, ui_key, tgui_id, filedesc, ui_x, ui_y, state = state)
+ ui = new(user, src, tgui_id, filedesc)
ui.open()
+ ui.send_asset(get_asset_datum(/datum/asset/simple/headers))
// CONVENTIONS, READ THIS WHEN CREATING NEW PROGRAM AND OVERRIDING THIS PROC:
// Topic calls are automagically forwarded from NanoModule this program contains.
@@ -177,17 +180,17 @@
// ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE.
/datum/computer_file/program/ui_act(action,list/params,datum/tgui/ui)
if(..())
- return TRUE
+ return 1
if(computer)
switch(action)
if("PC_exit")
computer.kill_program()
ui.close()
- return TRUE
+ return 1
if("PC_shutdown")
computer.shutdown_computer()
ui.close()
- return TRUE
+ return 1
if("PC_minimize")
var/mob/user = usr
if(!computer.active_program || !computer.all_components[MC_CPU])
diff --git a/code/modules/modular_computers/file_system/program_events.dm b/code/modules/modular_computers/file_system/program_events.dm
index 279d646cfd..3c1daa5af3 100644
--- a/code/modules/modular_computers/file_system/program_events.dm
+++ b/code/modules/modular_computers/file_system/program_events.dm
@@ -13,6 +13,6 @@
/datum/computer_file/program/proc/event_networkfailure(background)
kill_program(forced = TRUE)
if(background)
- computer.visible_message("\The [computer]'s screen displays an \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error ")
+ computer.visible_message("\The [computer]'s screen displays a \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error ")
else
computer.visible_message("\The [computer]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error. ")
diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm
index 4e9cd85577..364ad79737 100644
--- a/code/modules/modular_computers/file_system/programs/airestorer.dm
+++ b/code/modules/modular_computers/file_system/programs/airestorer.dm
@@ -9,8 +9,6 @@
transfer_access = ACCESS_HEADS
available_on_ntnet = TRUE
tgui_id = "NtosAiRestorer"
- ui_x = 370
- ui_y = 400
/// Variable dictating if we are in the process of restoring the AI in the inserted intellicard
var/restoring = FALSE
@@ -74,8 +72,8 @@
restoring = FALSE
return
ai_slot.locked =TRUE
- A.adjustOxyLoss(-5, 0)
- A.adjustFireLoss(-5, 0)
+ A.adjustOxyLoss(-5, 0)//, FALSE)
+ A.adjustFireLoss(-5, 0)//, FALSE)
A.adjustToxLoss(-5, 0)
A.adjustBruteLoss(-5, 0)
A.updatehealth()
@@ -113,7 +111,7 @@
data["restoring"] = restoring
data["health"] = (AI.health + 100) / 2
data["isDead"] = AI.stat == DEAD
- data["laws"] = AI.laws.get_law_list(include_zeroth = 1)
+ data["laws"] = AI.laws.get_law_list(include_zeroth = TRUE, render_html = FALSE)
return data
diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm
index 34daeff6ca..577fad83d0 100644
--- a/code/modules/modular_computers/file_system/programs/alarm.dm
+++ b/code/modules/modular_computers/file_system/programs/alarm.dm
@@ -8,9 +8,6 @@
network_destination = "alarm monitoring network"
size = 5
tgui_id = "NtosStationAlertConsole"
- ui_x = 315
- ui_y = 500
-
var/has_alert = 0
var/alarms = list("Fire" = list(), "Atmosphere" = list(), "Power" = list())
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
index cf842f086f..3accb8e02d 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
@@ -1,6 +1,6 @@
/datum/computer_file/program/contract_uplink
filename = "contractor uplink"
- filedesc = "Syndicate Contract Uplink"
+ filedesc = "Syndicate Contractor Uplink"
program_icon_state = "assign"
extended_desc = "A standard, Syndicate issued system for handling important contracts while on the field."
size = 10
@@ -9,8 +9,6 @@
unsendable = 1
undeletable = 1
tgui_id = "SyndContractor"
- ui_x = 500
- ui_y = 600
var/error = ""
var/info_screen = TRUE
var/assigned = FALSE
@@ -22,14 +20,18 @@
/datum/computer_file/program/contract_uplink/ui_act(action, params)
if(..())
return TRUE
+
var/mob/living/user = usr
var/obj/item/computer_hardware/hard_drive/small/syndicate/hard_drive = computer.all_components[MC_HDD]
+
switch(action)
if("PRG_contract-accept")
var/contract_id = text2num(params["contract_id"])
+
// Set as the active contract
hard_drive.traitor_data.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ACTIVE
hard_drive.traitor_data.contractor_hub.current_contract = hard_drive.traitor_data.contractor_hub.assigned_contracts[contract_id]
+
program_icon_state = "single_contract"
return TRUE
if("PRG_login")
@@ -38,26 +40,30 @@
// Bake their data right into the hard drive, or we don't allow non-antags gaining access to an unused
// contract system.
// We also create their contracts at this point.
- if(traitor_data)
+ if (traitor_data)
// Only play greet sound, and handle contractor hub when assigning for the first time.
- if(!traitor_data.contractor_hub)
+ if (!traitor_data.contractor_hub)
user.playsound_local(user, 'sound/effects/contractstartup.ogg', 100, FALSE)
traitor_data.contractor_hub = new
traitor_data.contractor_hub.create_hub_items()
+
// Stops any topic exploits such as logging in multiple times on a single system.
- if(!assigned)
+ if (!assigned)
traitor_data.contractor_hub.create_contracts(traitor_data.owner)
+
hard_drive.traitor_data = traitor_data
+
program_icon_state = "contracts"
assigned = TRUE
else
error = "UNAUTHORIZED USER"
return TRUE
if("PRG_call_extraction")
- if(hard_drive.traitor_data.contractor_hub.current_contract.status != CONTRACT_STATUS_EXTRACTING)
- if(hard_drive.traitor_data.contractor_hub.current_contract.handle_extraction(user))
+ if (hard_drive.traitor_data.contractor_hub.current_contract.status != CONTRACT_STATUS_EXTRACTING)
+ if (hard_drive.traitor_data.contractor_hub.current_contract.handle_extraction(user))
user.playsound_local(user, 'sound/effects/confirmdropoff.ogg', 100, TRUE)
hard_drive.traitor_data.contractor_hub.current_contract.status = CONTRACT_STATUS_EXTRACTING
+
program_icon_state = "extracted"
else
user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
@@ -65,15 +71,19 @@
else
user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
error = "Already extracting... Place the target into the pod. If the pod was destroyed, this contract is no longer possible."
+
return TRUE
if("PRG_contract_abort")
var/contract_id = hard_drive.traitor_data.contractor_hub.current_contract.id
+
hard_drive.traitor_data.contractor_hub.current_contract = null
hard_drive.traitor_data.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ABORTED
+
program_icon_state = "contracts"
+
return TRUE
if("PRG_redeem_TC")
- if(hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem)
+ if (hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem)
var/obj/item/stack/telecrystal/crystals = new /obj/item/stack/telecrystal(get_turf(user),
hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem)
if(ishuman(user))
@@ -82,13 +92,14 @@
to_chat(H, "Your payment materializes into your hands! ")
else
to_chat(user, "Your payment materializes onto the floor. ")
+
hard_drive.traitor_data.contractor_hub.contract_TC_payed_out += hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem
hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem = 0
return TRUE
else
user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
return TRUE
- if("PRG_clear_error")
+ if ("PRG_clear_error")
error = ""
return TRUE
if("PRG_set_first_load_finished")
@@ -97,10 +108,11 @@
if("PRG_toggle_info")
info_screen = !info_screen
return TRUE
- if("buy_hub")
- if(hard_drive.traitor_data.owner.current == user)
+ if ("buy_hub")
+ if (hard_drive.traitor_data.owner.current == user)
var/item = params["item"]
- for(var/datum/contractor_item/hub_item in hard_drive.traitor_data.contractor_hub.hub_items)
+
+ for (var/datum/contractor_item/hub_item in hard_drive.traitor_data.contractor_hub.hub_items)
if (hub_item.name == item)
hub_item.handle_purchase(hard_drive.traitor_data.contractor_hub, user)
else
@@ -112,13 +124,15 @@
var/screen_to_be = null
data["first_load"] = first_load
- if(hard_drive && hard_drive.traitor_data != null)
+
+ if (hard_drive && hard_drive.traitor_data != null)
var/datum/antagonist/traitor/traitor_data = hard_drive.traitor_data
data += get_header_data()
- if(traitor_data.contractor_hub.current_contract)
+
+ if (traitor_data.contractor_hub.current_contract)
data["ongoing_contract"] = TRUE
screen_to_be = "single_contract"
- if(traitor_data.contractor_hub.current_contract.status == CONTRACT_STATUS_EXTRACTING)
+ if (traitor_data.contractor_hub.current_contract.status == CONTRACT_STATUS_EXTRACTING)
data["extraction_enroute"] = TRUE
screen_to_be = "extracted"
else
@@ -126,15 +140,19 @@
else
data["ongoing_contract"] = FALSE
data["extraction_enroute"] = FALSE
+
data["logged_in"] = TRUE
data["station_name"] = GLOB.station_name
data["redeemable_tc"] = traitor_data.contractor_hub.contract_TC_to_redeem
data["earned_tc"] = traitor_data.contractor_hub.contract_TC_payed_out
data["contracts_completed"] = traitor_data.contractor_hub.contracts_completed
data["contract_rep"] = traitor_data.contractor_hub.contract_rep
+
data["info_screen"] = info_screen
+
data["error"] = error
- for(var/datum/contractor_item/hub_item in traitor_data.contractor_hub.hub_items)
+
+ for (var/datum/contractor_item/hub_item in traitor_data.contractor_hub.hub_items)
data["contractor_hub_items"] += list(list(
"name" = hub_item.name,
"desc" = hub_item.desc,
@@ -142,7 +160,8 @@
"limited" = hub_item.limited,
"item_icon" = hub_item.item_icon
))
- for(var/datum/syndicate_contract/contract in traitor_data.contractor_hub.assigned_contracts)
+
+ for (var/datum/syndicate_contract/contract in traitor_data.contractor_hub.assigned_contracts)
data["contracts"] += list(list(
"target" = contract.contract.target,
"target_rank" = contract.target_rank,
@@ -155,23 +174,28 @@
))
var/direction
- if(traitor_data.contractor_hub.current_contract)
+ if (traitor_data.contractor_hub.current_contract)
var/turf/curr = get_turf(user)
var/turf/dropoff_turf
data["current_location"] = "[get_area_name(curr, TRUE)]"
- for(var/turf/content in traitor_data.contractor_hub.current_contract.contract.dropoff.contents)
- if(isturf(content))
+
+ for (var/turf/content in traitor_data.contractor_hub.current_contract.contract.dropoff.contents)
+ if (isturf(content))
dropoff_turf = content
break
+
if(curr.z == dropoff_turf.z) //Direction calculations for same z-level only
direction = uppertext(dir2text(get_dir(curr, dropoff_turf))) //Direction text (East, etc). Not as precise, but still helpful.
if(get_area(user) == traitor_data.contractor_hub.current_contract.contract.dropoff)
direction = "LOCATION CONFIRMED"
else
direction = "???"
+
data["dropoff_direction"] = direction
+
else
data["logged_in"] = FALSE
+
program_icon_state = screen_to_be
update_computer_icon()
return data
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
index 9dedc3810f..803dadc0a0 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
@@ -8,8 +8,6 @@
available_on_ntnet = FALSE
available_on_syndinet = TRUE
tgui_id = "NtosNetDos"
- ui_x = 400
- ui_y = 250
var/obj/machinery/ntnet_relay/target = null
var/dos_speed = 0
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
index a312815008..2ba3d69fe6 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
@@ -8,9 +8,6 @@
available_on_ntnet = FALSE
available_on_syndinet = TRUE
tgui_id = "NtosRevelation"
- ui_x = 400
- ui_y = 250
-
var/armed = 0
/datum/computer_file/program/revelation/run_program(var/mob/living/user)
diff --git a/code/modules/modular_computers/file_system/programs/arcade.dm b/code/modules/modular_computers/file_system/programs/arcade.dm
index 87debafd6b..2503073f9a 100644
--- a/code/modules/modular_computers/file_system/programs/arcade.dm
+++ b/code/modules/modular_computers/file_system/programs/arcade.dm
@@ -7,8 +7,6 @@
network_destination = "arcade network"
size = 6
tgui_id = "NtosArcade"
- ui_x = 450
- ui_y = 350
///Returns TRUE if the game is being played.
var/game_active = TRUE
@@ -27,6 +25,7 @@
/datum/computer_file/program/arcade/proc/game_check(mob/user)
sleep(5)
+ //user?.mind?.adjust_experience(/datum/skill/gaming, 1) No gaming(TM) Yet
if(boss_hp <= 0)
heads_up = "You have crushed [boss_name]! Rejoice!"
playsound(computer.loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3, falloff = 10)
@@ -35,6 +34,7 @@
if(istype(computer))
computer.update_icon()
ticket_count += 1
+ //user?.mind?.adjust_experience(/datum/skill/gaming, 50)
sleep(10)
else if(player_hp <= 0 || player_mp <= 0)
heads_up = "You have been defeated... how will the station survive?"
@@ -43,6 +43,7 @@
program_icon_state = "arcade_off"
if(istype(computer))
computer.update_icon()
+ //user?.mind?.adjust_experience(/datum/skill/gaming, 10)
sleep(10)
/datum/computer_file/program/arcade/proc/enemy_check(mob/user)
@@ -73,14 +74,13 @@
pause_state = FALSE
game_check()
-/datum/computer_file/program/arcade/ui_interact(mob/user, ui_key, datum/tgui/ui, force_open, datum/tgui/master_ui, datum/ui_state/state)
- . = ..()
- var/datum/asset/assets = get_asset_datum(/datum/asset/simple/arcade)
- assets.send(user)
+/datum/computer_file/program/arcade/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/simple/arcade),
+ )
/datum/computer_file/program/arcade/ui_data(mob/user)
var/list/data = get_header_data()
-
data["Hitpoints"] = boss_hp
data["PlayerHitpoints"] = player_hp
data["PlayerMP"] = player_mp
@@ -98,11 +98,13 @@
if(computer)
printer = computer.all_components[MC_PRINT]
+ //var/gamerSkillLevel = usr.mind?.get_skill_level(/datum/skill/gaming)
+ //var/gamerSkill = usr.mind?.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER)
switch(action)
if("Attack")
var/attackamt = 0 //Spam prevention.
if(pause_state == FALSE)
- attackamt = rand(2,6)
+ attackamt = rand(2,6)// + rand(0, gamerSkill)
pause_state = TRUE
heads_up = "You attack for [attackamt] damage."
playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3, falloff = 10)
@@ -115,8 +117,10 @@
var/healamt = 0 //More Spam Prevention.
var/healcost = 0
if(pause_state == FALSE)
- healamt = rand(6,8)
+ healamt = rand(6,8)// + rand(0, gamerSkill)
var/maxPointCost = 3
+ //if(gamerSkillLevel >= SKILL_LEVEL_JOURNEYMAN)
+ // maxPointCost = 2
healcost = rand(1, maxPointCost)
pause_state = TRUE
heads_up = "You heal for [healamt] damage."
@@ -130,7 +134,7 @@
if("Recharge_Power")
var/rechargeamt = 0 //As above.
if(pause_state == FALSE)
- rechargeamt = rand(4, 7)
+ rechargeamt = rand(4,7)// + rand(0, gamerSkill)
pause_state = TRUE
heads_up = "You regain [rechargeamt] magic power."
playsound(computer.loc, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3, falloff = 10)
diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm
index fe3833facd..2df751bebd 100644
--- a/code/modules/modular_computers/file_system/programs/atmosscan.dm
+++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm
@@ -6,8 +6,6 @@
network_destination = "atmos scan"
size = 4
tgui_id = "NtosAtmos"
- ui_x = 300
- ui_y = 350
/datum/computer_file/program/atmosscan/ui_data(mob/user)
var/list/data = get_header_data()
@@ -15,16 +13,16 @@
var/turf/T = get_turf(ui_host())
if(T)
var/datum/gas_mixture/environment = T.return_air()
- var/list/env_gases = environment.gases
+ var/list/env_gases = environment.get_gases()
var/pressure = environment.return_pressure()
var/total_moles = environment.total_moles()
data["AirPressure"] = round(pressure,0.1)
- data["AirTemp"] = round(environment.temperature-T0C)
+ data["AirTemp"] = round(environment.return_temperature()-T0C)
if (total_moles)
for(var/id in env_gases)
- var/gas_level = env_gases[id][MOLES]/total_moles
+ var/gas_level = environment.get_moles(id)/total_moles
if(gas_level > 0)
- airlist += list(list("name" = "[env_gases[id][GAS_META][META_GAS_NAME]]", "percentage" = round(gas_level*100, 0.01)))
+ airlist += list(list("name" = "[GLOB.meta_gas_names[id]]", "percentage" = round(gas_level*100, 0.01)))
data["AirData"] = airlist
return data
diff --git a/code/modules/modular_computers/file_system/programs/borg_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_monitor.dm
index c493926c65..c2160a0e92 100644
--- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm
@@ -9,21 +9,17 @@
network_destination = "cyborg remote monitoring"
size = 5
tgui_id = "NtosCyborgRemoteMonitor"
- ui_x = 600
- ui_y = 800
/datum/computer_file/program/borg_monitor/ui_data(mob/user)
var/list/data = get_header_data()
data["card"] = FALSE
- if(computer.GetID())
+ if(checkID())
data["card"] = TRUE
data["cyborgs"] = list()
for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs)
- if((get_turf(computer)).z != (get_turf(R)).z)
- continue
- if(R.scrambledcodes)
+ if(!evaluate_borg(R))
continue
var/list/upgrade
@@ -36,7 +32,7 @@
var/list/cyborg_data = list(
name = R.name,
- locked_down = R.locked_down,
+ locked_down = R.lockcharge,
status = R.stat,
shell_discon = shell,
charge = R.cell ? round(R.cell.percent()) : null,
@@ -56,14 +52,53 @@
var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
if(!istype(R))
return
- var/obj/item/card/id/ID = computer.GetID()
+ var/ID = checkID()
if(!ID)
return
var/message = stripped_input(usr, message = "Enter message to be sent to remote cyborg.", title = "Send Message")
if(!message)
return
- to_chat(R, "Message from [ID.registered_name] -- \"[message]\" ")
+ to_chat(R, "Message from [ID] -- \"[message]\" ")
SEND_SOUND(R, 'sound/machines/twobeep_high.ogg')
if(R.connected_ai)
- to_chat(R.connected_ai, "Message from [ID.registered_name] to [R] -- \"[message]\" ")
+ to_chat(R.connected_ai, "Message from [ID] to [R] -- \"[message]\" ")
SEND_SOUND(R.connected_ai, 'sound/machines/twobeep_high.ogg')
+ usr.log_talk(message, LOG_PDA, tag="Cyborg Monitor Program: ID name \"[ID]\" to [R]")
+
+///This proc is used to determin if a borg should be shown in the list (based on the borg's scrambledcodes var). Syndicate version overrides this to show only syndicate borgs.
+/datum/computer_file/program/borg_monitor/proc/evaluate_borg(mob/living/silicon/robot/R)
+ if((get_turf(computer)).z != (get_turf(R)).z)
+ return FALSE
+ if(R.scrambledcodes)
+ return FALSE
+ return TRUE
+
+///Gets the ID's name, if one is inserted into the device. This is a seperate proc solely to be overridden by the syndicate version of the app.
+/datum/computer_file/program/borg_monitor/proc/checkID()
+ var/obj/item/card/id/ID = computer.GetID()
+ if(!ID)
+ return FALSE
+ return ID.registered_name
+
+/datum/computer_file/program/borg_monitor/syndicate
+ filename = "scyborgmonitor"
+ filedesc = "Mission-Specific Cyborg Remote Monitoring"
+ ui_header = "borg_mon.gif"
+ program_icon_state = "generic"
+ extended_desc = "This program allows for remote monitoring of mission-assigned cyborgs."
+ requires_ntnet = FALSE
+ available_on_ntnet = FALSE
+ available_on_syndinet = TRUE
+ transfer_access = null
+ network_destination = "cyborg remote monitoring"
+ tgui_id = "NtosCyborgRemoteMonitorSyndicate"
+
+/datum/computer_file/program/borg_monitor/syndicate/evaluate_borg(mob/living/silicon/robot/R)
+ if((get_turf(computer)).z != (get_turf(R)).z)
+ return FALSE
+ if(!R.scrambledcodes)
+ return FALSE
+ return TRUE
+
+/datum/computer_file/program/borg_monitor/syndicate/checkID()
+ return "\[CLASSIFIED\]" //no ID is needed for the syndicate version's message function, and the borg will see "[CLASSIFIED]" as the message sender.
diff --git a/code/modules/modular_computers/file_system/programs/bounty_board.dm b/code/modules/modular_computers/file_system/programs/bounty_board.dm
new file mode 100644
index 0000000000..46fde84f65
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/bounty_board.dm
@@ -0,0 +1,120 @@
+/datum/computer_file/program/bounty_board
+ filename = "bountyboard"
+ filedesc = "Bounty Board Request Network"
+ program_icon_state = "bountyboard"
+ extended_desc = "A multi-platform network for placing requests across the station, with payment across the network being possible.."
+ requires_ntnet = TRUE
+ network_destination = "bounty board interface"
+ size = 10
+ tgui_id = "NtosRequestKiosk"
+ ///Reference to the currently logged in user.
+ var/datum/bank_account/current_user
+ ///The station request datum being affected by UI actions.
+ var/datum/station_request/active_request
+ ///Value of the currently bounty input
+ var/bounty_value = 1
+ ///Text of the currently written bounty
+ var/bounty_text = ""
+ ///Has the app been added to the network yet?
+ var/networked = FALSE
+
+/datum/computer_file/program/bounty_board/ui_data(mob/user)
+ var/list/data = get_header_data()
+ var/list/formatted_requests = list()
+ var/list/formatted_applicants = list()
+ var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD]
+ if(!networked)
+ GLOB.allbountyboards += computer
+ networked = TRUE
+ if(card_slot && card_slot.stored_card && card_slot.stored_card.registered_account)
+ current_user = card_slot.stored_card.registered_account
+ for(var/i in GLOB.request_list)
+ if(!i)
+ continue
+ var/datum/station_request/request = i
+ formatted_requests += list(list("owner" = request.owner, "value" = request.value, "description" = request.description, "acc_number" = request.req_number))
+ if(request.applicants)
+ for(var/datum/bank_account/j in request.applicants)
+ formatted_applicants += list(list("name" = j.account_holder, "request_id" = request.owner_account.account_id, "requestee_id" = j.account_id))
+ if(current_user)
+ data["accountName"] = current_user.account_holder
+ data["requests"] = formatted_requests
+ data["applicants"] = formatted_applicants
+ data["bountyValue"] = bounty_value
+ data["bountyText"] = bounty_text
+ return data
+
+/datum/computer_file/program/bounty_board/ui_act(action, list/params)
+ if(..())
+ return
+ var/current_ref_num = params["request"]
+ var/current_app_num = params["applicant"]
+ var/datum/bank_account/request_target
+ if(current_ref_num)
+ for(var/datum/station_request/i in GLOB.request_list)
+ if("[i.req_number]" == "[current_ref_num]")
+ active_request = i
+ break
+ if(active_request)
+ for(var/datum/bank_account/j in active_request.applicants)
+ if("[j.account_id]" == "[current_app_num]")
+ request_target = j
+ break
+ switch(action)
+ if("createBounty")
+ if(!current_user || !bounty_text)
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 20, TRUE)
+ return TRUE
+ for(var/datum/station_request/i in GLOB.request_list)
+ if("[i.req_number]" == "[current_user.account_id]")
+ computer.say("Account already has active bounty.")
+ return
+ var/datum/station_request/curr_request = new /datum/station_request(current_user.account_holder, bounty_value,bounty_text,current_user.account_id, current_user)
+ GLOB.request_list += list(curr_request)
+ for(var/obj/i in GLOB.allbountyboards)
+ i.say("New bounty has been added!")
+ playsound(i.loc, 'sound/effects/cashregister.ogg', 30, TRUE)
+ return TRUE
+ if("apply")
+ if(!current_user)
+ computer.say("Please swipe a valid ID first.")
+ return TRUE
+ if(current_user.account_holder == active_request.owner)
+ playsound(computer, 'sound/machines/buzz-sigh.ogg', 20, TRUE)
+ return TRUE
+ active_request.applicants += list(current_user)
+ if("payApplicant")
+ if(!current_user)
+ return
+ if(!current_user.has_money(active_request.value) || (current_user.account_holder != active_request.owner))
+ playsound(computer, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ return
+ request_target.transfer_money(current_user, active_request.value)
+ computer.say("Paid out [active_request.value] credits.")
+ return TRUE
+ if("clear")
+ if(current_user)
+ current_user = null
+ computer.say("Account Reset.")
+ return TRUE
+ if("deleteRequest")
+ if(!current_user)
+ playsound(computer, 'sound/machines/buzz-sigh.ogg', 20, TRUE)
+ return TRUE
+ if(active_request.owner != current_user.account_holder)
+ playsound(computer, 'sound/machines/buzz-sigh.ogg', 20, TRUE)
+ return TRUE
+ computer.say("Deleted current request.")
+ GLOB.request_list.Remove(active_request)
+ return TRUE
+ if("bountyVal")
+ bounty_value = text2num(params["bountyval"])
+ if(!bounty_value)
+ bounty_value = 1
+ if("bountyText")
+ bounty_text = (params["bountytext"])
+ . = TRUE
+
+/datum/computer_file/program/bounty_board/Destroy()
+ GLOB.allbountyboards -= computer
+ . = ..()
diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm
index 07d39438d8..842d6e2588 100644
--- a/code/modules/modular_computers/file_system/programs/card.dm
+++ b/code/modules/modular_computers/file_system/programs/card.dm
@@ -15,8 +15,6 @@
requires_ntnet = 0
size = 8
tgui_id = "NtosCard"
- ui_x = 450
- ui_y = 520
var/is_centcom = FALSE
var/minor = FALSE
@@ -275,7 +273,7 @@
departments = list("CentCom" = get_all_centcom_jobs())
else if(isnull(departments))
departments = list(
- CARDCON_DEPARTMENT_COMMAND = list("Captain"),
+ CARDCON_DEPARTMENT_COMMAND = list("Captain"),//lol
CARDCON_DEPARTMENT_ENGINEERING = GLOB.engineering_positions,
CARDCON_DEPARTMENT_MEDICAL = GLOB.medical_positions,
CARDCON_DEPARTMENT_SCIENCE = GLOB.science_positions,
diff --git a/code/modules/modular_computers/file_system/programs/cargobounty.dm b/code/modules/modular_computers/file_system/programs/cargobounty.dm
new file mode 100644
index 0000000000..d9bc65c98d
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/cargobounty.dm
@@ -0,0 +1,48 @@
+/datum/computer_file/program/bounty
+ filename = "bounty"
+ filedesc = "Nanotrasen Bounty Hunter"
+ program_icon_state = "bounty"
+ extended_desc = "A basic interface for supply personnel to check and claim bounties."
+ requires_ntnet = TRUE
+ transfer_access = ACCESS_CARGO
+ network_destination = "cargo claims interface"
+ size = 10
+ tgui_id = "NtosBountyConsole"
+ ///cooldown var for printing paper sheets.
+ var/printer_ready = 0
+ ///The cargo account for grabbing the cargo account's credits.
+ var/static/datum/bank_account/cargocash
+
+/datum/computer_file/program/bounty/proc/print_paper()
+ new /obj/item/paper/bounty_printout(get_turf(computer))
+
+/datum/computer_file/program/bounty/ui_interact(mob/user, datum/tgui/ui)
+ if(!GLOB.bounties_list.len)
+ setup_bounties()
+ printer_ready = world.time + PRINTER_TIMEOUT
+ cargocash = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ . = ..()
+
+/datum/computer_file/program/bounty/ui_data(mob/user)
+ var/list/data = get_header_data()
+ var/list/bountyinfo = list()
+ for(var/datum/bounty/B in GLOB.bounties_list)
+ bountyinfo += list(list("name" = B.name, "description" = B.description, "reward_string" = B.reward_string(), "completion_string" = B.completion_string() , "claimed" = B.claimed, "can_claim" = B.can_claim(), "priority" = B.high_priority, "bounty_ref" = REF(B)))
+ data["stored_cash"] = cargocash.account_balance
+ data["bountydata"] = bountyinfo
+ return data
+
+/datum/computer_file/program/bounty/ui_act(action,params)
+ if(..())
+ return
+ switch(action)
+ if("ClaimBounty")
+ var/datum/bounty/cashmoney = locate(params["bounty"]) in GLOB.bounties_list
+ if(cashmoney)
+ cashmoney.claim()
+ return TRUE
+ if("Print")
+ if(printer_ready < world.time)
+ printer_ready = world.time + PRINTER_TIMEOUT
+ print_paper()
+ return
diff --git a/code/modules/modular_computers/file_system/programs/cargoship.dm b/code/modules/modular_computers/file_system/programs/cargoship.dm
index 39543adfa5..3ba08a3719 100644
--- a/code/modules/modular_computers/file_system/programs/cargoship.dm
+++ b/code/modules/modular_computers/file_system/programs/cargoship.dm
@@ -6,11 +6,9 @@
network_destination = "ship scanner"
size = 6
tgui_id = "NtosShipping"
- ui_x = 450
- ui_y = 350
///Account used for creating barcodes.
var/datum/bank_account/payments_acc
- ///The amount which the tagger will recieve for the sale.
+ ///The amount which the tagger will receive for the sale.
var/percent_cut = 20
/datum/computer_file/program/shipping/ui_data(mob/user)
diff --git a/code/modules/modular_computers/file_system/programs/configurator.dm b/code/modules/modular_computers/file_system/programs/configurator.dm
index 76da58ea11..fae06544d5 100644
--- a/code/modules/modular_computers/file_system/programs/configurator.dm
+++ b/code/modules/modular_computers/file_system/programs/configurator.dm
@@ -10,8 +10,6 @@
unsendable = 1
undeletable = 1
size = 4
- ui_x = 420
- ui_y = 630
available_on_ntnet = 0
requires_ntnet = 0
tgui_id = "NtosConfiguration"
diff --git a/code/modules/modular_computers/file_system/programs/crewmanifest.dm b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
index 662c867a39..a1503ce3a8 100644
--- a/code/modules/modular_computers/file_system/programs/crewmanifest.dm
+++ b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
@@ -7,12 +7,10 @@
requires_ntnet = FALSE
size = 4
tgui_id = "NtosCrewManifest"
- ui_x = 400
- ui_y = 480
/datum/computer_file/program/crew_manifest/ui_static_data(mob/user)
var/list/data = list()
- data["manifest"] = GLOB.data_core.get_manifest()
+ data["manifest"] = GLOB.data_core.get_manifest_tg()
return data
/datum/computer_file/program/crew_manifest/ui_data(mob/user)
@@ -41,9 +39,9 @@
if(computer && printer) //This option should never be called if there is no printer
var/contents = {"Crew Manifest
- [GLOB.data_core ? GLOB.data_core.get_manifest_html(0) : ""]
+ [GLOB.data_core ? GLOB.data_core.get_manifest() : ""]
"}
- if(!printer.print_text(contents,text("crew manifest ([])", station_time_timestamp())))
+ if(!printer.print_text(contents,text("crew manifest ([])", STATION_TIME_TIMESTAMP("hh:mm:ss", world.time))))
to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper. ")
return
else
diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
index 7b847c123e..bccc6e4dbe 100644
--- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm
+++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
@@ -7,8 +7,6 @@
requires_ntnet = 0
size = 4
tgui_id = "NtosJobManager"
- ui_x = 400
- ui_y = 620
var/change_position_cooldown = 30
//Jobs you cannot open new positions for
diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
index 352f13f305..6401d6207f 100644
--- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm
+++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
@@ -11,8 +11,6 @@
available_on_ntnet = 0
ui_header = "downloader_finished.gif"
tgui_id = "NtosNetDownloader"
- ui_x = 480
- ui_y = 735
var/datum/computer_file/program/downloaded_file = null
var/hacked_download = 0
@@ -20,6 +18,21 @@
var/download_netspeed = 0
var/downloaderror = ""
var/obj/item/modular_computer/my_computer = null
+ var/emagged = FALSE
+ var/list/main_repo
+ var/list/antag_repo
+
+/datum/computer_file/program/ntnetdownload/run_program()
+ . = ..()
+ main_repo = SSnetworks.station_network.available_station_software
+ antag_repo = SSnetworks.station_network.available_antag_software
+
+/datum/computer_file/program/ntnetdownload/run_emag()
+ if(emagged)
+ return FALSE
+ emagged = TRUE
+ return TRUE
+
/datum/computer_file/program/ntnetdownload/proc/begin_file_download(filename)
if(downloaded_file)
@@ -30,8 +43,8 @@
if(!PRG || !istype(PRG))
return 0
- // Attempting to download antag only program, but without having emagged computer. No.
- if(PRG.available_on_syndinet && !(computer.obj_flags & EMAGGED))
+ // Attempting to download antag only program, but without having emagged/syndicate computer. No.
+ if(PRG.available_on_syndinet && !emagged)
return 0
var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD]
@@ -41,10 +54,10 @@
ui_header = "downloader_running.gif"
- if(PRG in SSnetworks.station_network.available_station_software)
+ if(PRG in main_repo)
generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from NTNet Software Repository.")
hacked_download = 0
- else if(PRG in SSnetworks.station_network.available_antag_software)
+ else if(PRG in antag_repo)
generate_network_log("Began downloading file **ENCRYPTED**.[PRG.filetype] from unspecified server.")
hacked_download = 1
else
@@ -130,7 +143,7 @@
data["disk_size"] = hard_drive.max_capacity
data["disk_used"] = hard_drive.used_capacity
var/list/all_entries[0]
- for(var/A in SSnetworks.station_network.available_station_software)
+ for(var/A in main_repo)
var/datum/computer_file/program/P = A
// Only those programs our user can run will show in the list
if(!P.can_run(user,transfer = 1) || hard_drive.find_file_by_name(P.filename))
@@ -143,9 +156,9 @@
"size" = P.size,
)))
data["hackedavailable"] = FALSE
- if(computer.obj_flags & EMAGGED) // If we are running on emagged computer we have access to some "bonus" software
+ if(emagged) // If we are running on emagged computer we have access to some "bonus" software
var/list/hacked_programs[0]
- for(var/S in SSnetworks.station_network.available_antag_software)
+ for(var/S in antag_repo)
var/datum/computer_file/program/P = S
if(hard_drive.find_file_by_name(P.filename))
continue
@@ -172,3 +185,24 @@
/datum/computer_file/program/ntnetdownload/kill_program(forced)
abort_file_download()
return ..(forced)
+
+////////////////////////
+//Syndicate Downloader//
+////////////////////////
+
+/// This app only lists programs normally found in the emagged section of the normal downloader app
+
+/datum/computer_file/program/ntnetdownload/syndicate
+ filename = "syndownloader"
+ filedesc = "Software Download Tool"
+ program_icon_state = "generic"
+ extended_desc = "This program allows downloads of software from shared Syndicate repositories"
+ requires_ntnet = 0
+ ui_header = "downloader_finished.gif"
+ tgui_id = "NtosNetDownloader"
+ emagged = TRUE
+
+/datum/computer_file/program/ntnetdownload/syndicate/run_program()
+ . = ..()
+ main_repo = SSnetworks.station_network.available_antag_software
+ antag_repo = null
diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
index 4ae5bd326b..df9b02d8ec 100644
--- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
+++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
@@ -10,9 +10,6 @@
ui_header = "ntnrc_idle.gif"
available_on_ntnet = 1
tgui_id = "NtosNetChat"
- ui_x = 900
- ui_y = 675
-
var/last_message // Used to generate the toolbar icon
var/username
var/active_channel
diff --git a/code/modules/modular_computers/file_system/programs/nttransfer.dm b/code/modules/modular_computers/file_system/programs/nttransfer.dm
deleted file mode 100644
index 698e557941..0000000000
--- a/code/modules/modular_computers/file_system/programs/nttransfer.dm
+++ /dev/null
@@ -1,183 +0,0 @@
-/datum/computer_file/program/nttransfer
- filename = "nttransfer"
- filedesc = "P2P Transfer Client"
- extended_desc = "This program allows for simple file transfer via direct peer to peer connection."
- program_icon_state = "comm_logs"
- size = 7
- requires_ntnet = 1
- requires_ntnet_feature = NTNET_PEERTOPEER
- network_destination = "other device via P2P tunnel"
- available_on_ntnet = 1
- tgui_id = "ntos_net_transfer"
-
- var/error = "" // Error screen
- var/server_password = "" // Optional password to download the file.
- var/datum/computer_file/provided_file = null // File which is provided to clients.
- var/datum/computer_file/downloaded_file = null // File which is being downloaded
- var/list/connected_clients = list() // List of connected clients.
- var/datum/computer_file/program/nttransfer/remote // Client var, specifies who are we downloading from.
- var/download_completion = 0 // Download progress in GQ
- var/download_netspeed = 0 // Our connectivity speed in GQ/s
- var/actual_netspeed = 0 // Displayed in the UI, this is the actual transfer speed.
- var/unique_token // UID of this program
- var/upload_menu = 0 // Whether we show the program list and upload menu
- var/static/nttransfer_uid = 0
-
-/datum/computer_file/program/nttransfer/New()
- unique_token = nttransfer_uid++
- ..()
-
-/datum/computer_file/program/nttransfer/process_tick()
- // Server mode
- update_netspeed()
- if(provided_file)
- for(var/datum/computer_file/program/nttransfer/C in connected_clients)
- // Transfer speed is limited by device which uses slower connectivity.
- // We can have multiple clients downloading at same time, but let's assume we use some sort of multicast transfer
- // so they can all run on same speed.
- C.actual_netspeed = min(C.download_netspeed, download_netspeed)
- C.download_completion += C.actual_netspeed
- if(C.download_completion >= provided_file.size)
- C.finish_download()
- else if(downloaded_file) // Client mode
- if(!remote)
- crash_download("Connection to remote server lost")
-
-/datum/computer_file/program/nttransfer/kill_program(forced = FALSE)
- if(downloaded_file) // Client mode, clean up variables for next use
- finalize_download()
-
- if(provided_file) // Server mode, disconnect all clients
- for(var/datum/computer_file/program/nttransfer/P in connected_clients)
- P.crash_download("Connection terminated by remote server")
- downloaded_file = null
- ..(forced)
-
-/datum/computer_file/program/nttransfer/proc/update_netspeed()
- download_netspeed = 0
- switch(ntnet_status)
- if(1)
- download_netspeed = NTNETSPEED_LOWSIGNAL
- if(2)
- download_netspeed = NTNETSPEED_HIGHSIGNAL
- if(3)
- download_netspeed = NTNETSPEED_ETHERNET
-
-// Finishes download and attempts to store the file on HDD
-/datum/computer_file/program/nttransfer/proc/finish_download()
- var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD]
- if(!computer || !hard_drive || !hard_drive.store_file(downloaded_file))
- error = "I/O Error: Unable to save file. Check your hard drive and try again."
- finalize_download()
-
-// Crashes the download and displays specific error message
-/datum/computer_file/program/nttransfer/proc/crash_download(var/message)
- error = message ? message : "An unknown error has occurred during download"
- finalize_download()
-
-// Cleans up variables for next use
-/datum/computer_file/program/nttransfer/proc/finalize_download()
- if(remote)
- remote.connected_clients.Remove(src)
- downloaded_file = null
- remote = null
- download_completion = 0
-
-/datum/computer_file/program/nttransfer/ui_act(action, params)
- if(..())
- return 1
- switch(action)
- if("PRG_downloadfile")
- for(var/datum/computer_file/program/nttransfer/P in SSnetworks.station_network.fileservers)
- if("[P.unique_token]" == params["id"])
- remote = P
- break
- if(!remote || !remote.provided_file)
- return
- if(remote.server_password)
- var/pass = reject_bad_text(input(usr, "Code 401 Unauthorized. Please enter password:", "Password required"))
- if(pass != remote.server_password)
- error = "Incorrect Password"
- return
- downloaded_file = remote.provided_file.clone()
- remote.connected_clients.Add(src)
- return 1
- if("PRG_reset")
- error = ""
- upload_menu = 0
- finalize_download()
- if(src in SSnetworks.station_network.fileservers)
- SSnetworks.station_network.fileservers.Remove(src)
- for(var/datum/computer_file/program/nttransfer/T in connected_clients)
- T.crash_download("Remote server has forcibly closed the connection")
- provided_file = null
- return 1
- if("PRG_setpassword")
- var/pass = reject_bad_text(input(usr, "Enter new server password. Leave blank to cancel, input 'none' to disable password.", "Server security", "none"))
- if(!pass)
- return
- if(pass == "none")
- server_password = ""
- return
- server_password = pass
- return 1
- if("PRG_uploadfile")
- var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD]
- for(var/datum/computer_file/F in hard_drive.stored_files)
- if("[F.uid]" == params["id"])
- if(F.unsendable)
- error = "I/O Error: File locked."
- return
- if(istype(F, /datum/computer_file/program))
- var/datum/computer_file/program/P = F
- if(!P.can_run(usr,transfer = 1))
- error = "Access Error: Insufficient rights to upload file."
- provided_file = F
- SSnetworks.station_network.fileservers.Add(src)
- return
- error = "I/O Error: Unable to locate file on hard drive."
- return 1
- if("PRG_uploadmenu")
- upload_menu = 1
-
-
-/datum/computer_file/program/nttransfer/ui_data(mob/user)
-
- var/list/data = get_header_data()
-
- if(error)
- data["error"] = error
- else if(downloaded_file)
- data["downloading"] = 1
- data["download_size"] = downloaded_file.size
- data["download_progress"] = download_completion
- data["download_netspeed"] = actual_netspeed
- data["download_name"] = "[downloaded_file.filename].[downloaded_file.filetype]"
- else if (provided_file)
- data["uploading"] = 1
- data["upload_uid"] = unique_token
- data["upload_clients"] = connected_clients.len
- data["upload_haspassword"] = server_password ? 1 : 0
- data["upload_filename"] = "[provided_file.filename].[provided_file.filetype]"
- else if (upload_menu)
- var/list/all_files[0]
- var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD]
- for(var/datum/computer_file/F in hard_drive.stored_files)
- all_files.Add(list(list(
- "uid" = F.uid,
- "filename" = "[F.filename].[F.filetype]",
- "size" = F.size
- )))
- data["upload_filelist"] = all_files
- else
- var/list/all_servers[0]
- for(var/datum/computer_file/program/nttransfer/P in SSnetworks.station_network.fileservers)
- all_servers.Add(list(list(
- "uid" = P.unique_token,
- "filename" = "[P.provided_file.filename].[P.provided_file.filetype]",
- "size" = P.provided_file.size,
- "haspassword" = P.server_password ? 1 : 0
- )))
- data["servers"] = all_servers
-
- return data
\ No newline at end of file
diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm
index d2d57b1447..bd11474858 100644
--- a/code/modules/modular_computers/file_system/programs/powermonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm
@@ -12,8 +12,6 @@
network_destination = "power monitoring system"
size = 9
tgui_id = "NtosPowerMonitor"
- ui_x = 550
- ui_y = 700
var/has_alert = 0
var/obj/structure/cable/attached_wire
diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm
index 7a6e80267c..9b0e09ef99 100644
--- a/code/modules/modular_computers/file_system/programs/radar.dm
+++ b/code/modules/modular_computers/file_system/programs/radar.dm
@@ -2,31 +2,49 @@
filename = "genericfinder"
filedesc = "debug_finder"
ui_header = "borg_mon.gif" //DEBUG -- new icon before PR
- program_icon_state = "generic"
- extended_desc = "generic"
+ program_icon_state = "radarntos"
requires_ntnet = TRUE
transfer_access = null
available_on_ntnet = FALSE
+ usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET
network_destination = "tracking program"
size = 5
tgui_id = "NtosRadar"
- ui_x = 800
- ui_y = 600
- special_assets = list(
- /datum/asset/simple/radar_assets,
- )
///List of trackable entities. Updated by the scan() proc.
var/list/objects
///Ref of the last trackable object selected by the user in the tgui window. Updated in the ui_act() proc.
var/atom/selected
///Used to store when the next scan is available. Updated by the scan() proc.
var/next_scan = 0
+ ///Used to keep track of the last value program_icon_state was set to, to prevent constant unnecessary update_icon() calls
+ var/last_icon_state = ""
+ ///Used by the tgui interface, themed NT or Syndicate.
+ var/arrowstyle = "ntosradarpointer.png"
+ ///Used by the tgui interface, themed for NT or Syndicate colors.
+ var/pointercolor = "green"
+
+/datum/computer_file/program/radar/run_program(mob/living/user)
+ . = ..()
+ if(.)
+ START_PROCESSING(SSfastprocess, src)
+ return
+ return FALSE
/datum/computer_file/program/radar/kill_program(forced = FALSE)
objects = list()
selected = null
+ STOP_PROCESSING(SSfastprocess, src)
return ..()
+/datum/computer_file/program/radar/Destroy()
+ STOP_PROCESSING(SSfastprocess, src)
+ return ..()
+
+/datum/computer_file/program/radar/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/simple/radar_assets),
+ )
+
/datum/computer_file/program/radar/ui_data(mob/user)
var/list/data = get_header_data()
data["selected"] = selected
@@ -65,7 +83,37 @@
*
*/
/datum/computer_file/program/radar/proc/track()
- return
+ var/atom/movable/signal = find_atom()
+ if(!trackable(signal))
+ return
+
+ var/turf/here_turf = (get_turf(computer))
+ var/turf/target_turf = (get_turf(signal))
+ var/userot = FALSE
+ var/rot = 0
+ var/pointer="crosshairs"
+ var/locx = (target_turf.x - here_turf.x) + 24
+ var/locy = (here_turf.y - target_turf.y) + 24
+
+ if(get_dist_euclidian(here_turf, target_turf) > 24)
+ userot = TRUE
+ rot = round(Get_Angle(here_turf, target_turf))
+ else
+ if(target_turf.z > here_turf.z)
+ pointer="caret-up"
+ else if(target_turf.z < here_turf.z)
+ pointer="caret-down"
+
+ var/list/trackinfo = list(
+ "locx" = locx,
+ "locy" = locy,
+ "userot" = userot,
+ "rot" = rot,
+ "arrowstyle" = arrowstyle,
+ "color" = pointercolor,
+ "pointer" = pointer,
+ )
+ return trackinfo
/**
*
@@ -77,10 +125,12 @@
**arg1 is the atom being evaluated.
*/
/datum/computer_file/program/radar/proc/trackable(atom/movable/signal)
- if(!signal)
+ if(!signal || !computer)
return FALSE
var/turf/here = get_turf(computer)
var/turf/there = get_turf(signal)
+ if(!here || !there)
+ return FALSE //I was still getting a runtime even after the above check while scanning, so fuck it
return (there.z == here.z) || (is_station_level(here.z) && is_station_level(there.z))
/**
@@ -98,6 +148,59 @@
/datum/computer_file/program/radar/proc/scan()
return
+/**
+ *
+ *Finds the atom in the appropriate list that the `selected` var indicates
+ *
+ *The `selected` var holds a REF, which is a string. A mob REF may be
+ *something like "mob_209". In order to find the actual atom, we need
+ *to search the appropriate list for the REF string. This is dependant
+ *on the program (Lifeline uses GLOB.human_list, while Fission360 uses
+ *GLOB.poi_list), but the result will be the same; evaluate the string and
+ *return an atom reference.
+*/
+/datum/computer_file/program/radar/proc/find_atom()
+ return
+
+//We use SSfastprocess for the program icon state because it runs faster than process_tick() does.
+/datum/computer_file/program/radar/process()
+ if(computer.active_program != src)
+ STOP_PROCESSING(SSfastprocess, src) //We're not the active program, it's time to stop.
+ return
+ if(!selected)
+ return
+
+ var/atom/movable/signal = find_atom()
+ if(!trackable(signal))
+ program_icon_state = "[initial(program_icon_state)]lost"
+ if(last_icon_state != program_icon_state)
+ computer.update_icon()
+ last_icon_state = program_icon_state
+ return
+
+ var/here_turf = get_turf(computer)
+ var/target_turf = get_turf(signal)
+ var/trackdistance = get_dist_euclidian(here_turf, target_turf)
+ switch(trackdistance)
+ if(0)
+ program_icon_state = "[initial(program_icon_state)]direct"
+ if(1 to 12)
+ program_icon_state = "[initial(program_icon_state)]close"
+ if(13 to 24)
+ program_icon_state = "[initial(program_icon_state)]medium"
+ if(25 to INFINITY)
+ program_icon_state = "[initial(program_icon_state)]far"
+
+ if(last_icon_state != program_icon_state)
+ computer.update_icon()
+ last_icon_state = program_icon_state
+ computer.setDir(get_dir(here_turf, target_turf))
+
+//We can use process_tick to restart fast processing, since the computer will be running this constantly either way.
+/datum/computer_file/program/radar/process_tick()
+ if(computer.active_program == src)
+ START_PROCESSING(SSfastprocess, src)
+
///////////////////
//Suit Sensor App//
///////////////////
@@ -106,44 +209,13 @@
/datum/computer_file/program/radar/lifeline
filename = "Lifeline"
filedesc = "Lifeline"
- program_icon_state = "generic"
extended_desc = "This program allows for tracking of crew members via their suit sensors."
requires_ntnet = TRUE
transfer_access = ACCESS_MEDICAL
available_on_ntnet = TRUE
-/datum/computer_file/program/radar/lifeline/track()
- var/mob/living/carbon/human/humanoid = locate(selected) in GLOB.human_list
- if(!istype(humanoid) || !trackable(humanoid))
- return
-
- var/turf/here_turf = (get_turf(computer))
- var/turf/target_turf = (get_turf(humanoid))
- var/userot = FALSE
- var/rot = 0
- var/pointer="crosshairs"
- var/locx = (target_turf.x - here_turf.x)
- var/locy = (here_turf.y - target_turf.y)
- if(get_dist_euclidian(here_turf, target_turf) > 24) //If they're too far away, we need the angle for the arrow along the edge of the radar display
- userot = TRUE
- rot = round(Get_Angle(here_turf, target_turf))
- else
- locx = locx + 24
- locy = locy + 24
- if(target_turf.z > here_turf.z)
- pointer="caret-up"
- else if(target_turf.z < here_turf.z)
- pointer="caret-down"
- var/list/trackinfo = list(
- locx = locx,
- locy = locy,
- userot = userot,
- rot = rot,
- arrowstyle = "ntosradarpointer.png", //For the rotation arrow, it's stupid I know
- color = "green",
- pointer = pointer,
- )
- return trackinfo
+/datum/computer_file/program/radar/lifeline/find_atom()
+ return locate(selected) in GLOB.human_list
/datum/computer_file/program/radar/lifeline/scan()
if(world.time < next_scan)
@@ -184,46 +256,18 @@
/datum/computer_file/program/radar/fission360
filename = "Fission360"
filedesc = "Fission360"
- program_icon_state = "generic"
+ program_icon_state = "radarsyndicate"
extended_desc = "This program allows for tracking of nuclear authorization disks and warheads."
requires_ntnet = FALSE
transfer_access = null
available_on_ntnet = FALSE
available_on_syndinet = TRUE
tgui_id = "NtosRadarSyndicate"
+ arrowstyle = "ntosradarpointerS.png"
+ pointercolor = "red"
-/datum/computer_file/program/radar/fission360/track()
- var/obj/nuke = locate(selected) in GLOB.poi_list
- if(!trackable(nuke))
- return
-
- var/turf/here_turf = (get_turf(computer))
- var/turf/target_turf = (get_turf(nuke))
- var/userot = FALSE
- var/rot = 0
- var/pointer="crosshairs"
- var/locx = (target_turf.x - here_turf.x)
- var/locy = (here_turf.y - target_turf.y)
- if(get_dist_euclidian(here_turf, target_turf) > 24) //If they're too far away, we need the angle for the arrow along the edge of the radar display
- userot = TRUE
- rot = round(Get_Angle(here_turf, target_turf))
- else
- locx = locx + 24
- locy = locy + 24
- if(target_turf.z > here_turf.z)
- pointer="caret-up"
- else if(target_turf.z < here_turf.z)
- pointer="caret-down"
- var/list/trackinfo = list(
- locx = locx,
- locy = locy,
- userot = userot,
- rot = rot,
- arrowstyle = "ntosradarpointerS.png",
- color = "red",
- pointer = pointer,
- )
- return trackinfo
+/datum/computer_file/program/radar/fission360/find_atom()
+ return locate(selected) in GLOB.poi_list
/datum/computer_file/program/radar/fission360/scan()
if(world.time < next_scan)
diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm
index 910f923327..8644ce09b4 100644
--- a/code/modules/modular_computers/file_system/programs/robocontrol.dm
+++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm
@@ -9,8 +9,6 @@
network_destination = "robotics control network"
size = 12
tgui_id = "NtosRoboControl"
- ui_x = 550
- ui_y = 550
///Number of simple robots on-station.
var/botcount = 0
///Used to find the location of the user for the purposes of summoning robots.
diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
index 0a675a7abc..32ad102871 100644
--- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
@@ -9,8 +9,6 @@
network_destination = "supermatter monitoring system"
size = 5
tgui_id = "NtosSupermatterMonitor"
- ui_x = 600
- ui_y = 350
var/last_status = SUPERMATTER_INACTIVE
var/list/supermatters
var/obj/machinery/power/supermatter_crystal/active // Currently selected supermatter crystal.
diff --git a/code/modules/modular_computers/hardware/CPU.dm b/code/modules/modular_computers/hardware/CPU.dm
index d08d65ff8b..f13081e1f3 100644
--- a/code/modules/modular_computers/hardware/CPU.dm
+++ b/code/modules/modular_computers/hardware/CPU.dm
@@ -37,4 +37,4 @@
icon_state = "cpu_super"
w_class = WEIGHT_CLASS_TINY
power_usage = 75
- max_idle_programs = 2
\ No newline at end of file
+ max_idle_programs = 2
diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm
index 37f3fc434e..b33442f99b 100644
--- a/code/modules/modular_computers/hardware/_hardware.dm
+++ b/code/modules/modular_computers/hardware/_hardware.dm
@@ -32,28 +32,29 @@
/obj/item/computer_hardware/attackby(obj/item/I, mob/living/user)
- // Multitool. Runs diagnostics
- if(istype(I, /obj/item/multitool))
- to_chat(user, "***** DIAGNOSTICS REPORT *****")
- diagnostics(user)
- to_chat(user, "******************************")
- return 1
-
// Cable coil. Works as repair method, but will probably require multiple applications and more cable.
if(istype(I, /obj/item/stack/cable_coil))
+ var/obj/item/stack/S = I
if(obj_integrity == max_integrity)
to_chat(user, "\The [src] doesn't seem to require repairs. ")
return 1
- if(I.use_tool(src, user, 0, 1))
+ if(S.use(1))
to_chat(user, "You patch up \the [src] with a bit of \the [I]. ")
obj_integrity = min(obj_integrity + 10, max_integrity)
return 1
if(try_insert(I, user))
- return 1
+ return TRUE
return ..()
+/obj/item/computer_hardware/multitool_act(mob/living/user, obj/item/I)
+ ..()
+ to_chat(user, "***** DIAGNOSTICS REPORT *****")
+ diagnostics(user)
+ to_chat(user, "******************************")
+ return TRUE
+
// Called on multitool click, prints diagnostic information to the user.
/obj/item/computer_hardware/proc/diagnostics(var/mob/user)
to_chat(user, "Hardware Integrity Test... (Corruption: [damage]/[max_damage]) [damage > damage_failure ? "FAIL" : damage > damage_malfunction ? "WARN" : "PASS"]")
diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm
index 8428467a87..0ad157afcb 100644
--- a/code/modules/modular_computers/hardware/ai_slot.dm
+++ b/code/modules/modular_computers/hardware/ai_slot.dm
@@ -9,6 +9,10 @@
var/obj/item/aicard/stored_card = null
var/locked = FALSE
+/obj/item/computer_hardware/ai_slot/handle_atom_del(atom/A)
+ if(A == stored_card)
+ try_eject(0, null, TRUE)
+ . = ..()
/obj/item/computer_hardware/ai_slot/examine(mob/user)
. = ..()
@@ -41,13 +45,6 @@
/obj/item/computer_hardware/ai_slot/try_eject(slot=0,mob/living/user = null,forced = 0)
- if (get_dist(src,user) > 1)
- if (iscarbon(user))
- var/mob/living/carbon/H = user
- if (!(H.dna && H.dna.check_mutation(TK) && tkMaxRangeCheck(src,H)))
- return FALSE
- else
- return FALSE
if(!stored_card)
to_chat(user, "There is no card in \the [src]. ")
return FALSE
@@ -57,19 +54,21 @@
return FALSE
if(stored_card)
- stored_card.forceMove(get_turf(src))
+ to_chat(user, "You remove [stored_card] from [src]. ")
locked = FALSE
- stored_card.verb_pickup()
+ if(user)
+ user.put_in_hands(stored_card)
+ else
+ stored_card.forceMove(drop_location())
stored_card = null
- to_chat(user, "You remove the card from \the [src]. ")
return TRUE
return FALSE
/obj/item/computer_hardware/ai_slot/attackby(obj/item/I, mob/living/user)
if(..())
return
- if(istype(I, /obj/item/screwdriver))
+ if(I.tool_behaviour == TOOL_SCREWDRIVER)
to_chat(user, "You press down on the manual eject button with \the [I]. ")
try_eject(,user,1)
- return
\ No newline at end of file
+ return
diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm
index e03427cc9c..6e3193abfd 100644
--- a/code/modules/modular_computers/hardware/battery_module.dm
+++ b/code/modules/modular_computers/hardware/battery_module.dm
@@ -7,6 +7,9 @@
var/obj/item/stock_parts/cell/battery = null
device_type = MC_CELL
+/obj/item/computer_hardware/battery/get_cell()
+ return battery
+
/obj/item/computer_hardware/battery/New(loc, battery_type = null)
if(battery_type)
battery = new battery_type(src)
@@ -16,6 +19,11 @@
. = ..()
QDEL_NULL(battery)
+/obj/item/computer_hardware/battery/handle_atom_del(atom/A)
+ if(A == battery)
+ try_eject(0, null, TRUE)
+ . = ..()
+
/obj/item/computer_hardware/battery/try_insert(obj/item/I, mob/living/user = null)
if(!holder)
return FALSE
@@ -45,7 +53,10 @@
to_chat(user, "There is no power cell connected to \the [src]. ")
return FALSE
else
- battery.forceMove(get_turf(src))
+ if(user)
+ user.put_in_hands(battery)
+ else
+ battery.forceMove(drop_location())
to_chat(user, "You detach \the [battery] from \the [src]. ")
battery = null
diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm
index e4bc45dbc5..18b423a42e 100644
--- a/code/modules/modular_computers/hardware/card_slot.dm
+++ b/code/modules/modular_computers/hardware/card_slot.dm
@@ -9,6 +9,13 @@
var/obj/item/card/id/stored_card = null
var/obj/item/card/id/stored_card2 = null
+/obj/item/computer_hardware/card_slot/handle_atom_del(atom/A)
+ if(A == stored_card)
+ try_eject(1, null, TRUE)
+ if(A == stored_card2)
+ try_eject(2, null, TRUE)
+ . = ..()
+
/obj/item/computer_hardware/card_slot/Destroy()
try_eject()
return ..()
@@ -67,19 +74,15 @@
else
stored_card2 = I
to_chat(user, "You insert \the [I] into \the [src]. ")
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.sec_hud_set_ID()
return TRUE
/obj/item/computer_hardware/card_slot/try_eject(slot=0, mob/living/user = null, forced = 0)
- if (get_dist(src,user) > 1)
- if (iscarbon(user))
- var/mob/living/carbon/H = user
- if (!(H.dna && H.dna.check_mutation(TK) && tkMaxRangeCheck(src,H)))
- return FALSE
- else
- return FALSE
if(!stored_card && !stored_card2)
to_chat(user, "There are no cards in \the [src]. ")
return FALSE
@@ -89,7 +92,7 @@
if(user)
user.put_in_hands(stored_card)
else
- stored_card.forceMove(get_turf(src))
+ stored_card.forceMove(drop_location())
stored_card = null
ejected++
@@ -97,7 +100,7 @@
if(user)
user.put_in_hands(stored_card2)
else
- stored_card2.forceMove(get_turf(src))
+ stored_card2.forceMove(drop_location())
stored_card2 = null
ejected++
@@ -109,16 +112,18 @@
for(var/I in holder.idle_threads)
var/datum/computer_file/program/P = I
P.event_idremoved(1, slot)
-
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.sec_hud_set_ID()
to_chat(user, "You remove the card[ejected>1 ? "s" : ""] from \the [src]. ")
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
return TRUE
return FALSE
/obj/item/computer_hardware/card_slot/attackby(obj/item/I, mob/living/user)
if(..())
return
- if(istype(I, /obj/item/screwdriver))
+ if(I.tool_behaviour == TOOL_SCREWDRIVER)
to_chat(user, "You press down on the manual eject button with \the [I]. ")
try_eject(0,user)
return
diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm
index e27eaa53ae..b8b9624388 100644
--- a/code/modules/modular_computers/hardware/hard_drive.dm
+++ b/code/modules/modular_computers/hardware/hard_drive.dm
@@ -157,18 +157,30 @@
max_capacity = 64
icon_state = "ssd_mini"
w_class = WEIGHT_CLASS_TINY
- custom_price = PRICE_ABOVE_NORMAL
+ custom_price = 150
-/obj/item/computer_hardware/hard_drive/small/syndicate // Syndicate variant - very slight better
+// Syndicate variant - very slight better
+/obj/item/computer_hardware/hard_drive/small/syndicate
desc = "An efficient SSD for portable devices developed by a rival organisation."
power_usage = 8
max_capacity = 70
var/datum/antagonist/traitor/traitor_data // Syndicate hard drive has the user's data baked directly into it on creation
+/// For tablets given to nuke ops
+/obj/item/computer_hardware/hard_drive/small/nukeops
+ power_usage = 8
+ max_capacity = 70
+
+/obj/item/computer_hardware/hard_drive/small/nukeops/install_default_programs()
+ store_file(new/datum/computer_file/program/computerconfig(src))
+ store_file(new/datum/computer_file/program/ntnetdownload/syndicate(src)) // Syndicate version; automatic access to syndicate apps and no NT apps
+ store_file(new/datum/computer_file/program/filemanager(src))
+ store_file(new/datum/computer_file/program/radar/fission360(src)) //I am legitimately afraid if I don't do this, Ops players will think they just don't get a pinpointer anymore.
+
/obj/item/computer_hardware/hard_drive/micro
name = "micro solid state drive"
desc = "A highly efficient SSD chip for portable devices."
power_usage = 2
max_capacity = 32
icon_state = "ssd_micro"
- w_class = WEIGHT_CLASS_TINY
\ No newline at end of file
+ w_class = WEIGHT_CLASS_TINY
diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm
index 36c666e5d6..ebe40c1922 100644
--- a/code/modules/modular_computers/hardware/printer.dm
+++ b/code/modules/modular_computers/hardware/printer.dm
@@ -10,14 +10,14 @@
/obj/item/computer_hardware/printer/diagnostics(mob/living/user)
..()
- to_chat(user, "Paper level: [stored_paper]/[max_paper].")
+ to_chat(user, "Paper level: [stored_paper]/[max_paper]. ")
/obj/item/computer_hardware/printer/examine(mob/user)
. = ..()
. += "Paper level: [stored_paper]/[max_paper]. "
-/obj/item/computer_hardware/printer/proc/print_text(var/text_to_print, var/paper_title = "")
+/obj/item/computer_hardware/printer/proc/print_text(text_to_print, paper_title = "")
if(!stored_paper)
return FALSE
if(!check_functionality())
@@ -27,11 +27,12 @@
// Damaged printer causes the resulting paper to be somewhat harder to read.
if(damage > damage_malfunction)
- P.setText(stars(text_to_print, 100-malfunction_probability))
+ P.info = stars(text_to_print, 100-malfunction_probability)
else
- P.setText(text_to_print)
+ P.info = text_to_print
if(paper_title)
P.name = paper_title
+ P.update_icon()
stored_paper--
P = null
return TRUE
diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm
index ee9def4191..a8d30bad21 100644
--- a/code/modules/modular_computers/laptop_vendor.dm
+++ b/code/modules/modular_computers/laptop_vendor.dm
@@ -27,9 +27,6 @@
var/dev_printer = 0 // 0: None, 1: Standard
var/dev_card = 0 // 0: None, 1: Standard
- ui_x = 500
- ui_y = 400
-
// Removes all traces of old order and allows you to begin configuration from scratch.
/obj/machinery/lapvend/proc/reset_order()
state = 0
@@ -224,15 +221,15 @@
return TRUE
return FALSE
-/obj/machinery/lapvend/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+/obj/machinery/lapvend/ui_interact(mob/user, datum/tgui/ui)
if(stat & (BROKEN | NOPOWER | MAINT))
if(ui)
ui.close()
return FALSE
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
- ui = new(user, src, ui_key, "ComputerFabricator", "Personal Computer Vendor", ui_x, ui_y, state = state)
+ ui = new(user, src, "ComputerFabricator")
ui.open()
/obj/machinery/lapvend/attackby(obj/item/I, mob/user)
@@ -241,7 +238,7 @@
if(!user.temporarilyRemoveItemFromInventory(c))
return
credits += c.value
- visible_message("[user] inserts [c.value] credits into [src]. ")
+ visible_message("[user] inserts [c.value] cr into [src]. ")
qdel(c)
return
else if(istype(I, /obj/item/holochip))
@@ -257,10 +254,10 @@
var/datum/bank_account/account = ID.registered_account
var/target_credits = total_price - credits
if(!account.adjust_money(-target_credits))
- say("Insufficient money on card to purchase!")
+ say("Insufficient credits on card to purchase!")
return
credits += target_credits
- say("[target_credits] cr has been desposited from your account.")
+ say("[target_credits] cr has been deposited from your account.")
return
return ..()
@@ -308,4 +305,4 @@
state = 3
addtimer(CALLBACK(src, .proc/reset_order), 100)
return TRUE
- return FALSE
\ No newline at end of file
+ return FALSE
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 01c25b57f1..5d842ef11a 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -1,4 +1,4 @@
-/*
+/**
* Paper
* also scraps of paper
*
@@ -11,12 +11,11 @@
#define MODE_WRITING 1
#define MODE_STAMPING 2
-
/**
- ** This is a custom ui state. All it really does is keep track of pen
- ** being used and if they are editing it or not. This way we can keep
- ** the data with the ui rather than on the paper
- **/
+ * This is a custom ui state. All it really does is keep track of pen
+ * being used and if they are editing it or not. This way we can keep
+ * the data with the ui rather than on the paper
+ */
/datum/ui_state/default/paper_state
/// What edit mode we are in and who is
/// writing on it right now
@@ -33,7 +32,6 @@
var/stamp_name = ""
var/stamp_class = ""
-
/datum/ui_state/default/paper_state/proc/copy_from(datum/ui_state/default/paper_state/from)
switch(from.edit_mode)
if(MODE_READING)
@@ -49,12 +47,11 @@
stamp_class = from.stamp_class
stamp_name = from.stamp_name
-
/**
- ** Paper is now using markdown (like in github pull notes) for ALL rendering
- ** so we do loose a bit of functionality but we gain in easy of use of
- ** paper and getting rid of that crashing bug
- **/
+ * Paper is now using markdown (like in github pull notes) for ALL rendering
+ * so we do loose a bit of functionality but we gain in easy of use of
+ * paper and getting rid of that crashing bug
+ */
/obj/item/paper
name = "paper"
gender = NEUTER
@@ -71,6 +68,8 @@
resistance_flags = FLAMMABLE
max_integrity = 50
dog_fashion = /datum/dog_fashion/head
+ // drop_sound = 'sound/items/handling/paper_drop.ogg'
+ // pickup_sound = 'sound/items/handling/paper_pickup.ogg'
grind_results = list(/datum/reagent/cellulose = 3)
color = "white"
/// What's actually written on the paper.
@@ -89,9 +88,6 @@
var/contact_poison // Reagent ID to transfer on contact
var/contact_poison_volume = 0
- // ui stuff
- var/ui_x = 600
- var/ui_y = 800
// Ok, so WHY are we caching the ui's?
// Since we are not using autoupdate we
// need some way to update the ui's of
@@ -103,7 +99,6 @@
// people look at it
var/list/viewing_ui = list()
-
/// When the sheet can be "filled out"
/// This is an associated list
var/list/form_fields = list()
@@ -116,10 +111,10 @@
. = ..()
/**
- ** This proc copies this sheet of paper to a new
- ** sheet, Makes it nice and easy for carbon and
- ** the copyer machine
- **/
+ * This proc copies this sheet of paper to a new
+ * sheet, Makes it nice and easy for carbon and
+ * the copyer machine
+ */
/obj/item/paper/proc/copy()
var/obj/item/paper/N = new(arglist(args))
N.info = info
@@ -133,10 +128,10 @@
return N
/**
- ** This proc sets the text of the paper and updates the
- ** icons. You can modify the pen_color after if need
- ** be.
- **/
+ * This proc sets the text of the paper and updates the
+ * icons. You can modify the pen_color after if need
+ * be.
+ */
/obj/item/paper/proc/setText(text)
info = text
form_fields = null
@@ -152,30 +147,16 @@
contact_poison = null
. = ..()
-
/obj/item/paper/Initialize()
. = ..()
pixel_y = rand(-8, 8)
pixel_x = rand(-9, 9)
update_icon()
-
/obj/item/paper/update_icon_state()
if(info && show_written_words)
icon_state = "[initial(icon_state)]_words"
-/obj/item/paper/ui_base_html(html)
- /// This might change in a future PR
- var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/paper)
- . = replacetext(html, "", assets.css_tag())
-
-
-/obj/item/paper/examine_more(mob/user)
- ui_interact(user)
-
-/obj/item/paper/proc/show_content(mob/user)
- user.examinate(src)
-
/obj/item/paper/verb/rename()
set name = "Rename paper"
set category = "Object"
@@ -195,17 +176,14 @@
name = "paper[(n_name ? text("- '[n_name]'") : null)]"
add_fingerprint(usr)
-
/obj/item/paper/suicide_act(mob/user)
user.visible_message("[user] scratches a grid on [user.p_their()] wrist with the paper! It looks like [user.p_theyre()] trying to commit sudoku... ")
return (BRUTELOSS)
-
/// ONLY USED FOR APRIL FOOLS
/obj/item/paper/proc/reset_spamflag()
spam_flag = FALSE
-
/obj/item/paper/attack_self(mob/user)
if(rigged && (SSevents.holidays && SSevents.holidays[APRIL_FOOLS]))
if(!spam_flag)
@@ -214,7 +192,6 @@
addtimer(CALLBACK(src, .proc/reset_spamflag), 20)
. = ..()
-
/obj/item/paper/proc/clearpaper()
info = ""
stamps = null
@@ -222,29 +199,28 @@
cut_overlays()
update_icon_state()
-
-/obj/item/paper/examine(mob/user)
+/obj/item/paper/examine_more(mob/user)
ui_interact(user)
-
+ return list("You try to read [src]... ")
/obj/item/paper/can_interact(mob/user)
if(!..())
return FALSE
- if(resistance_flags & ON_FIRE) // Are we on fire? Hard ot read if so
+ // Are we on fire? Hard ot read if so
+ if(resistance_flags & ON_FIRE)
return FALSE
- if(user.is_blind()) // Even harder to read if your blind...braile? humm
+ // Even harder to read if your blind...braile? humm
+ if(user.is_blind())
return FALSE
- return user.can_read(src) // checks if the user can read.
-
+ // checks if the user can read.
+ return user.can_read(src)
/**
- ** This creates the ui, since we are using a custom state but not much else
- ** just makes it easyer to make it. Also we make a custom ui_key as I am
- ** not sure how tgui handles many producers?
-**/
+ * This creates the ui, since we are using a custom state but not much else
+ * just makes it easyer to make it.
+ */
/obj/item/paper/proc/create_ui(mob/user, datum/ui_state/default/paper_state/state)
- ui_interact(user, "main", null, FALSE, null, state)
-
+ ui_interact(user, state = state)
/obj/item/proc/burn_paper_product_attackby_check(obj/item/I, mob/living/user, bypass_clumsy)
var/ignition_message = I.ignition_effect(src, user)
@@ -320,24 +296,27 @@
if(.)
info = "[stars(info)]"
-/obj/item/paper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/default/paper_state/state = new)
- ui_key = "main-[REF(user)]"
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/paper/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/simple/paper),
+ )
+
+/obj/item/paper/ui_interact(mob/user, datum/tgui/ui,
+ datum/ui_state/default/paper_state/state)
+ // Update the state
+ ui = ui || SStgui.get_open_ui(user, src)
+ if(ui && state)
+ var/datum/ui_state/default/paper_state/current_state = ui.state
+ current_state.copy_from(state)
+ // Update the UI
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/simple/paper)
- assets.send(user)
- // The x size is because we double the width for the editor
- ui = new(user, src, ui_key, "PaperSheet", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "PaperSheet", name)
+ state = new
+ ui.set_state(state)
ui.set_autoupdate(FALSE)
viewing_ui[user] = ui
ui.open()
- else
- var/datum/ui_state/default/paper_state/last_state = ui.state
- if(last_state)
- last_state.copy_from(state)
- else
- ui.state = state
-
/obj/item/paper/ui_close(mob/user)
/// close the editing window and change the mode
@@ -347,7 +326,7 @@
// Again, we have to do this as autoupdate is off
/obj/item/paper/proc/update_all_ui()
for(var/datum/tgui/ui in viewing_ui)
- ui.update()
+ ui.process(force = TRUE)
// Again, we have to do this as autoupdate is off
/obj/item/paper/proc/close_all_ui()
@@ -383,7 +362,6 @@
return data
-
/obj/item/paper/ui_act(action, params, datum/tgui/ui, datum/ui_state/default/paper_state/state)
if(..())
return
@@ -445,21 +423,18 @@
. = TRUE
-
-/*
+/**
* Construction paper
*/
-
/obj/item/paper/construction
/obj/item/paper/construction/Initialize()
. = ..()
color = pick("FF0000", "#33cc33", "#ffb366", "#551A8B", "#ff80d5", "#4d94ff")
-/*
+/**
* Natural paper
*/
-
/obj/item/paper/natural/Initialize()
. = ..()
color = "#FFF5ED"
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index 3f3c9b3f94..65b3c0a2e4 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -890,26 +890,19 @@
if((stat & MAINT) && !opened) //no board; no interface
return
-/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
-
+/obj/machinery/power/apc/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Apc", name, 480, 460, master_ui, state)
+ ui = new(user, src, "Apc", name)
ui.open()
/obj/machinery/power/apc/ui_data(mob/user)
- var/obj/item/implant/hijack/H = user.getImplant(/obj/item/implant/hijack)
- var/abilitiesavail = FALSE
- if (H && !H.stealthmode && H.toggled)
- abilitiesavail = TRUE
var/list/data = list(
"locked" = locked,
- "lock_nightshift" = nightshift_requires_auth,
"failTime" = failure_timer,
"isOperating" = operating,
"externalPower" = main_status,
- "powerCellStatus" = (cell?.percent() || null),
+ "powerCellStatus" = cell ? cell.percent() : null,
"chargeMode" = chargemode,
"chargingStatus" = charging,
"totalLoad" = DisplayPower(lastused_total),
@@ -918,10 +911,7 @@
"malfStatus" = get_malf_status(user),
"emergencyLights" = !emergency_lights,
"nightshiftLights" = nightshift_lights,
- "hijackable" = HAS_TRAIT(user,TRAIT_HIJACKER),
- "hijacker" = hijacker == user ? TRUE : FALSE,
- "drainavail" = cell && cell.percent() >= 85 && abilitiesavail,
- "lockdownavail" = cell && cell.percent() >= 35 && abilitiesavail,
+
"powerChannels" = list(
list(
"title" = "Equipment",
@@ -1019,43 +1009,32 @@
. = UI_INTERACTIVE
/obj/machinery/power/apc/ui_act(action, params)
- if(..() || !can_use(usr, 1))
- return
- if(failure_timer)
- if(action == "reboot")
- failure_timer = 0
- update_icon()
- update()
- if(action == "hijack" && can_use(usr, 1)) //don't need auth for hijack button
- hijack(usr)
- return
- var/authorized = (!locked || area.hasSiliconAccessInArea(usr, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE) || (integration_cog && (is_servant_of_ratvar(usr))))
- if((action == "toggle_nightshift") && (!nightshift_requires_auth || authorized))
- toggle_nightshift_lights()
- return TRUE
- if(!authorized)
+ if(..() || !can_use(usr, 1) || (locked && area.hasSiliconAccessInArea(usr, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE) && !failure_timer && action != "toggle_nightshift") || (integration_cog && (is_servant_of_ratvar(usr))))
return
switch(action)
if("lock")
if(area.hasSiliconAccessInArea(usr))
if((obj_flags & EMAGGED) || (stat & (BROKEN|MAINT)))
- to_chat(usr, "The APC does not respond to the command.")
+ to_chat(usr, "The APC does not respond to the command! ")
else
locked = !locked
update_icon()
- return TRUE
+ . = TRUE
if("cover")
coverlocked = !coverlocked
- return TRUE
+ . = TRUE
if("breaker")
- toggle_breaker()
- return TRUE
+ toggle_breaker(usr)
+ . = TRUE
+ if("toggle_nightshift")
+ toggle_nightshift_lights()
+ . = TRUE
if("charge")
chargemode = !chargemode
if(!chargemode)
charging = APC_NOT_CHARGING
update_icon()
- return TRUE
+ . = TRUE
if("channel")
if(params["eqp"])
equipment = setsubsystem(text2num(params["eqp"]))
@@ -1069,23 +1048,24 @@
environ = setsubsystem(text2num(params["env"]))
update_icon()
update()
- return TRUE
+ . = TRUE
if("overload")
- if(area.hasSiliconAccessInArea(usr))
+ if(area.hasSiliconAccessInArea(usr, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE)) //usr.has_unlimited_silicon_privilege)
overload_lighting()
- return TRUE
+ . = TRUE
if("hack")
if(get_malf_status(usr))
malfhack(usr)
- return TRUE
if("occupy")
if(get_malf_status(usr))
malfoccupy(usr)
- return TRUE
if("deoccupy")
if(get_malf_status(usr))
malfvacate()
- return TRUE
+ if("reboot")
+ failure_timer = 0
+ update_icon()
+ update()
if("emergency_lighting")
emergency_lights = !emergency_lights
for(var/obj/machinery/light/L in area)
@@ -1093,31 +1073,14 @@
L.no_emergency = emergency_lights
INVOKE_ASYNC(L, /obj/machinery/light/.proc/update, FALSE)
CHECK_TICK
- if("drain")
- cell.use(cell.charge)
- hijacker.toggleSiliconAccessArea(area)
- hijacker = null
- set_hijacked_lighting()
- update_icon()
- var/obj/item/implant/hijack/H = usr.getImplant(/obj/item/implant/hijack)
- H.stealthcooldown = world.time + 2 MINUTES
- energy_fail(30 SECONDS * (cell.charge / cell.maxcharge))
- if("lockdown")
- var/celluse = rand(20,35)
- celluse = celluse /100
- for (var/obj/machinery/door/D in GLOB.airlocks)
- if (get_area(D) == area)
- INVOKE_ASYNC(D,/obj/machinery/door.proc/hostile_lockdown,usr, FALSE)
- addtimer(CALLBACK(D,/obj/machinery/door.proc/disable_lockdown, FALSE), 30 SECONDS)
- cell.charge -= cell.maxcharge*celluse
- var/obj/item/implant/hijack/H = usr.getImplant(/obj/item/implant/hijack)
- H.stealthcooldown = world.time + 3 MINUTES
return TRUE
-/obj/machinery/power/apc/proc/toggle_breaker()
+/obj/machinery/power/apc/proc/toggle_breaker(mob/user)
if(!is_operational() || failure_timer)
return
operating = !operating
+ add_hiddenprint(user) //delete when runtime
+ log_game("[key_name(user)] turned [operating ? "on" : "off"] the [src] in [AREACOORD(src)]")
update()
update_icon()
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index 30a7aa152b..e37ae56e71 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -116,8 +116,6 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
sprite_number = 8
use_power = IDLE_POWER_USE
interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OFFLINE
- ui_x = 400
- ui_y = 165
var/on = TRUE
var/breaker = TRUE
var/list/parts = list()
@@ -222,11 +220,10 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
return
return ..()
-/obj/machinery/gravity_generator/main/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/gravity_generator/main/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "GravityGenerator", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "GravityGenerator", name)
ui.open()
/obj/machinery/gravity_generator/main/ui_data(mob/user)
diff --git a/code/modules/power/monitor.dm b/code/modules/power/monitor.dm
index 974fb1b9e2..393d403c4d 100644
--- a/code/modules/power/monitor.dm
+++ b/code/modules/power/monitor.dm
@@ -11,8 +11,6 @@
active_power_usage = 100
circuit = /obj/item/circuitboard/computer/powermonitor
tgui_id = "PowerMonitor"
- ui_x = 550
- ui_y = 700
var/obj/structure/cable/attached_wire
var/obj/machinery/power/apc/local_apc
@@ -84,11 +82,10 @@
if(demand.len > record_size)
demand.Cut(1, 2)
-/obj/machinery/computer/monitor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/monitor/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "PowerMonitor", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "PowerMonitor", name)
ui.open()
/obj/machinery/computer/monitor/ui_data()
diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm
index d4e0df8359..e2f8c4e58a 100644
--- a/code/modules/power/port_gen.dm
+++ b/code/modules/power/port_gen.dm
@@ -7,8 +7,6 @@
density = TRUE
anchored = FALSE
use_power = NO_POWER_USE
- ui_x = 450
- ui_y = 340
var/active = FALSE
var/power_gen = 5000
@@ -219,11 +217,10 @@
/obj/machinery/power/port_gen/pacman/attack_paw(mob/user)
interact(user)
-/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "PortableGenerator", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "PortableGenerator", name)
ui.open()
/obj/machinery/power/port_gen/pacman/ui_data()
diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm
index 912fc0a72b..96c8d9a263 100644
--- a/code/modules/power/singularity/particle_accelerator/particle_control.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm
@@ -10,8 +10,6 @@
active_power_usage = 10000
dir = NORTH
mouse_opacity = MOUSE_OPACITY_OPAQUE
- ui_x = 350
- ui_y = 185
var/strength_upper_limit = 2
var/interface_control = TRUE
var/list/obj/structure/particle_accelerator/connected_parts
@@ -275,11 +273,10 @@
return ..()
return UI_CLOSE
-/obj/machinery/particle_accelerator/control_box/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/particle_accelerator/control_box/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "ParticleAccelerator", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "ParticleAccelerator", name)
ui.open()
/obj/machinery/particle_accelerator/control_box/ui_data(mob/user)
diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm
index fcc7faa29e..a4fc7d0641 100644
--- a/code/modules/power/smes.dm
+++ b/code/modules/power/smes.dm
@@ -21,8 +21,6 @@
density = TRUE
use_power = NO_POWER_USE
circuit = /obj/item/circuitboard/machine/smes
- ui_x = 340
- ui_y = 350
var/capacity = 5e6 // maximum charge
var/charge = 0 // actual charge
@@ -202,7 +200,7 @@
/obj/machinery/power/smes/update_overlays()
. = ..()
- if((stat & BROKEN) || panel_open)
+ if(stat & BROKEN)
return
if(panel_open)
@@ -318,11 +316,10 @@
return
-/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/power/smes/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Smes", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "Smes", name)
ui.open()
/obj/machinery/power/smes/ui_data()
diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm
index 2c258c1f49..cf526f083d 100644
--- a/code/modules/power/solar.dm
+++ b/code/modules/power/solar.dm
@@ -115,9 +115,6 @@
panel.icon_state = "solar_panel-b"
else
panel.icon_state = "solar_panel"
-#if DM_VERSION <= 512
- . += new /mutable_appearance(panel)
-#endif
/obj/machinery/power/solar/proc/queue_turn(azimuth)
needs_to_turn = TRUE
@@ -346,11 +343,10 @@
else
. += mutable_appearance(icon, icon_screen)
-/obj/machinery/power/solar_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/power/solar_control/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "SolarControl", name, 380, 230, master_ui, state)
+ ui = new(user, src, "SolarControl", name)
ui.open()
/obj/machinery/power/solar_control/ui_data()
diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm
index e7a97c554f..a9acea719c 100644
--- a/code/modules/power/turbine.dm
+++ b/code/modules/power/turbine.dm
@@ -56,8 +56,6 @@
resistance_flags = FIRE_PROOF
CanAtmosPass = ATMOS_PASS_DENSITY
circuit = /obj/item/circuitboard/machine/power_turbine
- ui_x = 310
- ui_y = 150
var/opened = 0
var/obj/machinery/power/compressor/compressor
var/turf/outturf
@@ -249,11 +247,10 @@
default_deconstruction_crowbar(I)
-/obj/machinery/power/turbine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/power/turbine/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "TurbineComputer", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "TurbineComputer", name)
ui.open()
/obj/machinery/power/turbine/ui_data(mob/user)
@@ -292,8 +289,6 @@
icon_screen = "turbinecomp"
icon_keyboard = "tech_key"
circuit = /obj/item/circuitboard/computer/turbine_computer
- ui_x = 310
- ui_y = 150
var/obj/machinery/power/compressor/compressor
var/id = 0
@@ -313,11 +308,10 @@
else
compressor = locate(/obj/machinery/power/compressor) in range(7, src)
-/obj/machinery/computer/turbine_computer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/turbine_computer/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "TurbineComputer", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "TurbineComputer", name)
ui.open()
/obj/machinery/computer/turbine_computer/ui_data(mob/user)
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index da33f935da..db16a10d1d 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -177,11 +177,10 @@
beaker = null
update_icon()
-/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/chem_dispenser/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "ChemDispenser", name, 565, 550, master_ui, state)
+ ui = new(user, src, "ChemDispenser", name)
if(user.hallucinating())
ui.set_autoupdate(FALSE) //to not ruin the immersion by constantly changing the fake chemicals
ui.open()
@@ -216,7 +215,7 @@
data["beakerTransferAmounts"] = null
data["beakerCurrentpH"] = null
- var/list/chemicals = list()
+ var/chemicals[0]
var/is_hallucinating = FALSE
if(user.hallucinating())
is_hallucinating = TRUE
@@ -275,7 +274,7 @@
. = TRUE
if("eject")
replace_beaker(usr)
- . = TRUE //no afterattack
+ . = TRUE
if("dispense_recipe")
if(!is_operational() || QDELETED(cell))
return
@@ -326,9 +325,9 @@
for(var/reagent in recording_recipe)
var/reagent_id = GLOB.name2reagent[translate_legacy_chem_id(reagent)]
if(!dispensable_reagents.Find(reagent_id))
- visible_message("[src] buzzes. ", "You hear a faint buzz. ")
+ visible_message("[src] buzzes. ", "You hear a faint buzz. ")
to_chat(usr, "[src] cannot find [reagent] ! ")
- playsound(src, 'sound/machines/buzz-two.ogg', 50, 1)
+ playsound(src, 'sound/machines/buzz-two.ogg', 50, TRUE)
return
saved_recipes[name] = recording_recipe
recording_recipe = null
diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm
index 5697a2385c..28f0b2366a 100644
--- a/code/modules/reagents/chemistry/machinery/chem_heater.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm
@@ -7,6 +7,7 @@
idle_power_usage = 40
resistance_flags = FIRE_PROOF | ACID_PROOF
circuit = /obj/item/circuitboard/machine/chem_heater
+
var/obj/item/reagent_containers/beaker = null
var/target_temperature = 300
var/heater_coefficient = 0.1
@@ -30,22 +31,20 @@
/obj/machinery/chem_heater/AltClick(mob/living/user)
. = ..()
- if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ if(!can_interact(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
replace_beaker(user)
- return TRUE
/obj/machinery/chem_heater/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
+ if(!user)
+ return FALSE
if(beaker)
- beaker.forceMove(drop_location())
- if(user && Adjacent(user) && user.can_hold_items())
- user.put_in_hands(beaker)
+ user.put_in_hands(beaker)
+ beaker = null
if(new_beaker)
beaker = new_beaker
- else
- beaker = null
- update_icon()
- return TRUE
+ update_icon()
+ return TRUE
/obj/machinery/chem_heater/RefreshParts()
heater_coefficient = 0.1
@@ -63,6 +62,7 @@
return
if(on)
if(beaker && beaker.reagents.total_volume)
+ //keep constant with the chemical acclimator please
beaker.reagents.adjust_thermal_energy((target_temperature - beaker.reagents.chem_temp) * heater_coefficient * SPECIFIC_HEAT_DEFAULT * beaker.reagents.total_volume)
beaker.reagents.handle_reactions()
@@ -83,27 +83,16 @@
updateUsrDialog()
update_icon()
return
-
- if(beaker)
- if(istype(I, /obj/item/reagent_containers/dropper))
- var/obj/item/reagent_containers/dropper/D = I
- D.afterattack(beaker, user, 1)
-
- if(istype(I, /obj/item/reagent_containers/syringe))
- var/obj/item/reagent_containers/syringe/S = I
- S.afterattack(beaker, user, 1)
-
return ..()
/obj/machinery/chem_heater/on_deconstruction()
replace_beaker()
return ..()
-/obj/machinery/chem_heater/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/chem_heater/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "ChemHeater", name, 300, 400, master_ui, state)
+ ui = new(user, src, "ChemHeater", name)
ui.open()
/obj/machinery/chem_heater/ui_data()
@@ -140,14 +129,7 @@
. = TRUE
if("temperature")
var/target = params["target"]
- var/adjust = text2num(params["adjust"])
- if(target == "input")
- target = input("New target temperature:", name, target_temperature) as num|null
- if(!isnull(target) && !..())
- . = TRUE
- else if(adjust)
- target = target_temperature + adjust
- else if(text2num(target) != null)
+ if(text2num(target) != null)
target = text2num(target)
. = TRUE
if(.)
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index dd9dd7c0bf..207325e1b3 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -12,6 +12,7 @@
idle_power_usage = 20
resistance_flags = FIRE_PROOF | ACID_PROOF
circuit = /obj/item/circuitboard/machine/chem_master
+
var/obj/item/reagent_containers/beaker = null
var/obj/item/storage/pill_bottle/bottle = null
var/mode = 1
@@ -154,19 +155,15 @@
bottle?.forceMove(A)
return ..()
-//Insert our custom spritesheet css link into the html
-/obj/machinery/chem_master/ui_base_html(html)
- var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/pills)
- . = replacetext(html, "", assets.css_tag())
+/obj/machinery/chem_master/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/simple/pills),
+ )
-/obj/machinery/chem_master/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/chem_master/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/simple/pills)
- assets.send(user)
-
- ui = new(user, src, ui_key, "ChemMaster", name, 520, 550, master_ui, state)
+ ui = new(user, src, "ChemMaster", name)
ui.open()
/obj/machinery/chem_master/ui_data(mob/user)
@@ -183,8 +180,8 @@
data["isPillBottleLoaded"] = bottle ? 1 : 0
if(bottle)
var/datum/component/storage/STRB = bottle.GetComponent(/datum/component/storage)
- data["pillBotContent"] = bottle.contents.len
- data["pillBotMaxContent"] = STRB.max_items
+ data["pillBottleCurrentAmount"] = bottle.contents.len
+ data["pillBottleMaxAmount"] = STRB.max_items
var/beakerContents[0]
if(beaker)
@@ -206,213 +203,219 @@
if(..())
return
- switch(action)
- if("eject")
- replace_beaker(usr)
- . = TRUE
+ if(action == "eject")
+ replace_beaker(usr)
+ return TRUE
- if("ejectPillBottle")
- replace_pillbottle(usr)
- . = TRUE
-
- if("transfer")
- if(!beaker)
- return FALSE
- var/reagent = GLOB.name2reagent[params["id"]]
- var/amount = text2num(params["amount"])
- var/to_container = params["to"]
- // Custom amount
- if (amount == -1)
- amount = text2num(input(
- "Enter the amount you want to transfer:",
- name, ""))
- if (amount == null || amount <= 0)
- return FALSE
- if (to_container == "buffer")
- end_fermi_reaction()
- beaker.reagents.trans_id_to(src, reagent, amount)
- return TRUE
- if (to_container == "beaker" && mode)
- end_fermi_reaction()
- reagents.trans_id_to(beaker, reagent, amount)
- return TRUE
- if (to_container == "beaker" && !mode)
- end_fermi_reaction()
- reagents.remove_reagent(reagent, amount)
- return TRUE
+ if(action == "ejectPillBottle")
+ if(!bottle)
return FALSE
+ bottle.forceMove(drop_location())
+ adjust_item_drop_location(bottle)
+ bottle = null
+ return TRUE
- if("toggleMode")
- mode = !mode
- . = TRUE
+ if(action == "transfer")
+ if(!beaker)
+ return FALSE
+ var/reagent = GLOB.name2reagent[params["id"]]
+ var/amount = text2num(params["amount"])
+ var/to_container = params["to"]
+ // Custom amount
+ if (amount == -1)
+ amount = text2num(input(
+ "Enter the amount you want to transfer:",
+ name, ""))
+ if (amount == null || amount <= 0)
+ return FALSE
+ if (to_container == "buffer")
+ end_fermi_reaction()
+ beaker.reagents.trans_id_to(src, reagent, amount)
+ return TRUE
+ if (to_container == "beaker" && mode)
+ end_fermi_reaction()
+ reagents.trans_id_to(beaker, reagent, amount)
+ return TRUE
+ if (to_container == "beaker" && !mode)
+ end_fermi_reaction()
+ reagents.remove_reagent(reagent, amount)
+ return TRUE
+ return FALSE
- if("pillStyle")
- var/id = text2num(params["id"])
- chosenPillStyle = id
+ if(action == "toggleMode")
+ mode = !mode
+ return TRUE
+
+ if(action == "pillStyle")
+ var/id = text2num(params["id"])
+ chosenPillStyle = id
+ return TRUE
+
+ if(action == "create")
+ if(reagents.total_volume == 0)
+ return FALSE
+ var/item_type = params["type"]
+ // Get amount of items
+ var/amount = text2num(params["amount"])
+ if(amount == null)
+ amount = text2num(input(usr,
+ "Max 10. Buffer content will be split evenly.",
+ "How many to make?", 1))
+ amount = clamp(round(amount), 0, 10)
+ if (amount <= 0)
+ return FALSE
+ // Get units per item
+ var/vol_each = text2num(params["volume"])
+ var/vol_each_text = params["volume"]
+ var/vol_each_max = reagents.total_volume / amount
+ if (item_type == "pill")
+ vol_each_max = min(50, vol_each_max)
+ else if (item_type == "patch")
+ vol_each_max = min(40, vol_each_max)
+ else if (item_type == "bottle")
+ vol_each_max = min(30, vol_each_max)
+ else if (item_type == "condimentPack")
+ vol_each_max = min(10, vol_each_max)
+ else if (item_type == "condimentBottle")
+ vol_each_max = min(50, vol_each_max)
+ else if (item_type == "hypoVial")
+ vol_each_max = min(60, vol_each_max)
+ else if (item_type == "smartDart")
+ vol_each_max = min(20, vol_each_max)
+ else
+ return FALSE
+ if(vol_each_text == "auto")
+ vol_each = vol_each_max
+ if(vol_each == null)
+ vol_each = text2num(input(usr,
+ "Maximum [vol_each_max] units per item.",
+ "How many units to fill?",
+ vol_each_max))
+ vol_each = clamp(vol_each, 0, vol_each_max)
+ if(vol_each <= 0)
+ return FALSE
+ // Get item name
+ var/name = params["name"]
+ var/name_has_units = item_type == "pill" || item_type == "patch"
+ if(!name)
+ var/name_default = reagents.get_master_reagent_name()
+ if (name_has_units)
+ name_default += " ([vol_each]u)"
+ name = stripped_input(usr,
+ "Name:",
+ "Give it a name!",
+ name_default,
+ MAX_NAME_LEN)
+ if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
+ return FALSE
+ // Start filling
+ if(item_type == "pill")
+ var/obj/item/reagent_containers/pill/P
+ var/target_loc = drop_location()
+ var/drop_threshold = INFINITY
+ if(bottle)
+ var/datum/component/storage/STRB = bottle.GetComponent(
+ /datum/component/storage)
+ if(STRB)
+ drop_threshold = STRB.max_items - bottle.contents.len
+ for(var/i = 0; i < amount; i++)
+ if(i < drop_threshold)
+ P = new/obj/item/reagent_containers/pill(target_loc)
+ else
+ P = new/obj/item/reagent_containers/pill(drop_location())
+ P.name = trim("[name] pill")
+ if(chosenPillStyle == RANDOM_PILL_STYLE)
+ P.icon_state ="pill[rand(1,21)]"
+ else
+ P.icon_state = "pill[chosenPillStyle]"
+ if(P.icon_state == "pill4")
+ P.desc = "A tablet or capsule, but not just any, a red one, one taken by the ones not scared of knowledge, freedom, uncertainty and the brutal truths of reality."
+ adjust_item_drop_location(P)
+ reagents.trans_to(P, vol_each)//, transfered_by = usr)
+ return TRUE
+ if(item_type == "patch")
+ var/obj/item/reagent_containers/pill/patch/P
+ for(var/i = 0; i < amount; i++)
+ P = new/obj/item/reagent_containers/pill/patch(drop_location())
+ P.name = trim("[name] patch")
+ adjust_item_drop_location(P)
+ reagents.trans_to(P, vol_each)//, transfered_by = usr)
+ return TRUE
+ if(item_type == "bottle")
+ var/obj/item/reagent_containers/glass/bottle/P
+ for(var/i = 0; i < amount; i++)
+ P = new/obj/item/reagent_containers/glass/bottle(drop_location())
+ P.name = trim("[name] bottle")
+ adjust_item_drop_location(P)
+ reagents.trans_to(P, vol_each)//, transfered_by = usr)
+ return TRUE
+ if(item_type == "condimentPack")
+ var/obj/item/reagent_containers/food/condiment/pack/P
+ for(var/i = 0; i < amount; i++)
+ P = new/obj/item/reagent_containers/food/condiment/pack(drop_location())
+ P.originalname = name
+ P.name = trim("[name] pack")
+ P.desc = "A small condiment pack. The label says it contains [name]."
+ reagents.trans_to(P, vol_each)//, transfered_by = usr)
+ return TRUE
+ if(item_type == "condimentBottle")
+ var/obj/item/reagent_containers/food/condiment/P
+ for(var/i = 0; i < amount; i++)
+ P = new/obj/item/reagent_containers/food/condiment(drop_location())
+ P.originalname = name
+ P.name = trim("[name] bottle")
+ reagents.trans_to(P, vol_each)//, transfered_by = usr)
+ return TRUE
+ if(item_type == "hypoVial")
+ var/obj/item/reagent_containers/glass/bottle/vial/small/P
+ for(var/i = 0; i < amount; i++)
+ P = new/obj/item/reagent_containers/glass/bottle/vial/small(drop_location())
+ P.name = trim("[name] hypovial")
+ adjust_item_drop_location(P)
+ reagents.trans_to(P, vol_each)//, transfered_by = usr)
+ return TRUE
+ if(item_type == "smartDart")
+ var/obj/item/reagent_containers/syringe/dart/P
+ for(var/i = 0; i < amount; i++)
+ P = new /obj/item/reagent_containers/syringe/dart(drop_location())
+ P.name = trim("[name] SmartDart")
+ adjust_item_drop_location(P)
+ reagents.trans_to(P, vol_each)//, transfered_by = usr)
+ P.mode=!mode
+ P.update_icon()
+ return TRUE
+ return FALSE
+
+ if(action == "analyze")
+ // var/datum/reagent/R = GLOB.name2reagent[params["id"]]
+ var/reagent = GLOB.name2reagent[params["id"]]
+ var/datum/reagent/R = GLOB.chemical_reagents_list[reagent]
+ if(R)
+ var/state = "Unknown"
+ if(initial(R.reagent_state) == 1)
+ state = "Solid"
+ else if(initial(R.reagent_state) == 2)
+ state = "Liquid"
+ else if(initial(R.reagent_state) == 3)
+ state = "Gas"
+ var/const/P = 3 //The number of seconds between life ticks
+ var/T = initial(R.metabolization_rate) * (60 / P)
+ if(istype(R, /datum/reagent/fermi))
+ fermianalyze = TRUE
+ var/datum/chemical_reaction/Rcr = get_chemical_reaction(reagent)
+ var/pHpeakCache = (Rcr.OptimalpHMin + Rcr.OptimalpHMax)/2
+ analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold), "purityF" = R.purity, "inverseRatioF" = initial(R.inverse_chem_val), "purityE" = initial(Rcr.PurityMin), "minTemp" = initial(Rcr.OptimalTempMin), "maxTemp" = initial(Rcr.OptimalTempMax), "eTemp" = initial(Rcr.ExplodeTemp), "pHpeak" = pHpeakCache)
+ else
+ fermianalyze = FALSE
+ analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold))
+ screen = "analyze"
return TRUE
- if("create")
- if(reagents.total_volume == 0)
- return FALSE
- var/item_type = params["type"]
- // Get amount of items
- var/amount = text2num(params["amount"])
- if(amount == null)
- amount = text2num(input(usr,
- "Max 20. Buffer content will be split evenly.",
- "How many to make?", 1))
- amount = clamp(round(amount), 0, 20)
- if (amount <= 0)
- return FALSE
- // Get units per item
- var/vol_each = text2num(params["volume"])
- var/vol_each_text = params["volume"]
- var/vol_each_max = reagents.total_volume / amount
- if (item_type == "pill")
- vol_each_max = min(50, vol_each_max)
- else if (item_type == "patch")
- vol_each_max = min(40, vol_each_max)
- else if (item_type == "bottle")
- vol_each_max = min(30, vol_each_max)
- else if (item_type == "condimentPack")
- vol_each_max = min(10, vol_each_max)
- else if (item_type == "condimentBottle")
- vol_each_max = min(50, vol_each_max)
- else if (item_type == "hypoVial")
- vol_each_max = min(60, vol_each_max)
- else if (item_type == "smartDart")
- vol_each_max = min(20, vol_each_max)
- else
- return FALSE
- if(vol_each_text == "auto")
- vol_each = vol_each_max
- if(vol_each == null)
- vol_each = text2num(input(usr,
- "Maximum [vol_each_max] units per item.",
- "How many units to fill?",
- vol_each_max))
- vol_each = clamp(vol_each, 0, vol_each_max)
- if(vol_each <= 0)
- return FALSE
- // Get item name
- var/name = params["name"]
- var/name_has_units = item_type == "pill" || item_type == "patch"
- if(!name)
- var/name_default = reagents.get_master_reagent_name()
- if (name_has_units)
- name_default += " ([vol_each]u)"
- name = stripped_input(usr,
- "Name:",
- "Give it a name!",
- name_default,
- MAX_NAME_LEN)
- if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
- return FALSE
- // Start filling
- if(item_type == "pill")
- var/obj/item/reagent_containers/pill/P
- var/target_loc = drop_location()
- var/drop_threshold = INFINITY
- if(bottle)
- var/datum/component/storage/STRB = bottle.GetComponent(
- /datum/component/storage)
- if(STRB)
- drop_threshold = STRB.max_items - bottle.contents.len
- for(var/i = 0; i < amount; i++)
- if(i < drop_threshold)
- P = new/obj/item/reagent_containers/pill(target_loc)
- else
- P = new/obj/item/reagent_containers/pill(drop_location())
- P.name = trim("[name] pill")
- if(chosenPillStyle == RANDOM_PILL_STYLE)
- P.icon_state ="pill[rand(1,21)]"
- else
- P.icon_state = "pill[chosenPillStyle]"
- if(P.icon_state == "pill4")
- P.desc = "A tablet or capsule, but not just any, a red one, one taken by the ones not scared of knowledge, freedom, uncertainty and the brutal truths of reality."
- adjust_item_drop_location(P)
- reagents.trans_to(P, vol_each)
- return TRUE
- if(item_type == "patch")
- var/obj/item/reagent_containers/pill/patch/P
- for(var/i = 0; i < amount; i++)
- P = new/obj/item/reagent_containers/pill/patch(drop_location())
- P.name = trim("[name] patch")
- adjust_item_drop_location(P)
- reagents.trans_to(P, vol_each)
- return TRUE
- if(item_type == "bottle")
- var/obj/item/reagent_containers/glass/bottle/P
- for(var/i = 0; i < amount; i++)
- P = new/obj/item/reagent_containers/glass/bottle(drop_location())
- P.name = trim("[name] bottle")
- adjust_item_drop_location(P)
- reagents.trans_to(P, vol_each)
- return TRUE
- if(item_type == "condimentPack")
- var/obj/item/reagent_containers/food/condiment/pack/P
- for(var/i = 0; i < amount; i++)
- P = new/obj/item/reagent_containers/food/condiment/pack(drop_location())
- P.originalname = name
- P.name = trim("[name] pack")
- P.desc = "A small condiment pack. The label says it contains [name]."
- reagents.trans_to(P, vol_each)
- return TRUE
- if(item_type == "condimentBottle")
- var/obj/item/reagent_containers/food/condiment/P
- for(var/i = 0; i < amount; i++)
- P = new/obj/item/reagent_containers/food/condiment(drop_location())
- P.originalname = name
- P.name = trim("[name] bottle")
- reagents.trans_to(P, vol_each)
- return TRUE
- if(item_type == "hypoVial")
- var/obj/item/reagent_containers/glass/bottle/vial/small/P
- for(var/i = 0; i < amount; i++)
- P = new/obj/item/reagent_containers/glass/bottle/vial/small(drop_location())
- P.name = trim("[name] hypovial")
- adjust_item_drop_location(P)
- reagents.trans_to(P, vol_each)
- return TRUE
- if(item_type == "smartDart")
- var/obj/item/reagent_containers/syringe/dart/P
- for(var/i = 0; i < amount; i++)
- P = new /obj/item/reagent_containers/syringe/dart(drop_location())
- P.name = trim("[name] SmartDart")
- adjust_item_drop_location(P)
- reagents.trans_to(P, vol_each)
- P.mode=!mode
- P.update_icon()
- return TRUE
- return FALSE
+ if(action == "goScreen")
+ screen = params["screen"]
+ return TRUE
- if("analyze")
- var/reagent = GLOB.name2reagent[params["id"]]
- var/datum/reagent/R = GLOB.chemical_reagents_list[reagent]
- if(R)
- var/state = "Unknown"
- if(initial(R.reagent_state) == 1)
- state = "Solid"
- else if(initial(R.reagent_state) == 2)
- state = "Liquid"
- else if(initial(R.reagent_state) == 3)
- state = "Gas"
- var/const/P = 3 //The number of seconds between life ticks
- var/T = initial(R.metabolization_rate) * (60 / P)
- if(istype(R, /datum/reagent/fermi))
- fermianalyze = TRUE
- var/datum/chemical_reaction/Rcr = get_chemical_reaction(reagent)
- var/pHpeakCache = (Rcr.OptimalpHMin + Rcr.OptimalpHMax)/2
- analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold), "purityF" = R.purity, "inverseRatioF" = initial(R.inverse_chem_val), "purityE" = initial(Rcr.PurityMin), "minTemp" = initial(Rcr.OptimalTempMin), "maxTemp" = initial(Rcr.OptimalTempMax), "eTemp" = initial(Rcr.ExplodeTemp), "pHpeak" = pHpeakCache)
- else
- fermianalyze = FALSE
- analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold))
- screen = "analyze"
- return TRUE
-
- if("goScreen")
- screen = params["screen"]
- . = TRUE
+ return FALSE
diff --git a/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm b/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm
index 66c2616972..489f9dd179 100644
--- a/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm
@@ -12,11 +12,10 @@
"tricord" = /datum/reagent/medicine/tricordrazine
)
-/obj/machinery/chem_dispenser/chem_synthesizer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/chem_dispenser/chem_synthesizer/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "ChemDebugSynthesizer", name, 390, 330, master_ui, state)
+ ui = new(user, src, "ChemDebugSynthesizer", name)
ui.open()
/obj/machinery/chem_dispenser/chem_synthesizer/ui_act(action, params)
@@ -31,7 +30,11 @@
beaker = null
. = TRUE
if("input")
- var/input_reagent = replacetext(lowertext(input("Enter the name of any reagent", "Input") as text), " ", "") //95% of the time, the reagent types is a lowercase, no spaces / underscored version of the name
+ var/input_reagent = replacetext(lowertext(input("Enter the name of any reagent", "Input") as text|null), " ", "") //95% of the time, the reagent id is a lowercase/no spaces version of the name
+
+ if (isnull(input_reagent))
+ return
+
if(shortcuts[input_reagent])
input_reagent = shortcuts[input_reagent]
else
@@ -51,7 +54,7 @@
beaker = new /obj/item/reagent_containers/glass/beaker/bluespace(src)
visible_message("[src] dispenses a bluespace beaker. ")
if("amount")
- var/input = input("Units to dispense", "Units") as num|null
+ var/input = text2num(params["amount"])
if(input)
amount = input
update_icon()
diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm
index 4a4bbdb546..36e102be72 100644
--- a/code/modules/reagents/chemistry/machinery/pandemic.dm
+++ b/code/modules/reagents/chemistry/machinery/pandemic.dm
@@ -7,10 +7,11 @@
density = TRUE
icon = 'icons/obj/chemical.dmi'
icon_state = "mixer0"
- circuit = /obj/item/circuitboard/computer/pandemic
use_power = TRUE
idle_power_usage = 20
resistance_flags = ACID_PROOF
+ circuit = /obj/item/circuitboard/computer/pandemic
+
var/wait
var/datum/symptom/selected_symptom
var/obj/item/reagent_containers/beaker
@@ -23,11 +24,27 @@
QDEL_NULL(beaker)
return ..()
-/obj/machinery/computer/pandemic/handle_atom_del(atom/A)
+/obj/machinery/computer/pandemic/examine(mob/user)
. = ..()
+ if(beaker)
+ var/is_close
+ if(Adjacent(user)) //don't reveal exactly what's inside unless they're close enough to see the UI anyway.
+ . += "It contains \a [beaker]."
+ is_close = TRUE
+ else
+ . += "It has a beaker inside it."
+ . += "Alt-click to eject [is_close ? beaker : "the beaker"]. "
+
+/obj/machinery/computer/pandemic/AltClick(mob/user)
+ . = ..()
+ if(user.canUseTopic(src, BE_CLOSE))
+ eject_beaker()
+
+/obj/machinery/computer/pandemic/handle_atom_del(atom/A)
if(A == beaker)
beaker = null
update_icon()
+ return ..()
/obj/machinery/computer/pandemic/proc/get_by_index(thing, index)
if(!beaker || !beaker.reagents)
@@ -107,7 +124,7 @@
/obj/machinery/computer/pandemic/proc/reset_replicator_cooldown()
wait = FALSE
update_icon()
- playsound(loc, 'sound/machines/ping.ogg', 30, 1)
+ playsound(src, 'sound/machines/ping.ogg', 30, TRUE)
/obj/machinery/computer/pandemic/update_icon_state()
if(stat & BROKEN)
@@ -117,13 +134,19 @@
/obj/machinery/computer/pandemic/update_overlays()
. = ..()
- if(!(stat & BROKEN) && wait)
+ if(wait)
. += "waitlight"
-/obj/machinery/computer/pandemic/ui_interact(mob/user, ui_key = "main", datum/tgui/ui, force_open = FALSE, datum/tgui/master_ui, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/pandemic/proc/eject_beaker()
+ if(beaker)
+ beaker.forceMove(drop_location())
+ beaker = null
+ update_icon()
+
+/obj/machinery/computer/pandemic/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Pandemic", name, 520, 550, master_ui, state)
+ ui = new(user, src, "Pandemic", name)
ui.open()
/obj/machinery/computer/pandemic/ui_data(mob/user)
@@ -135,9 +158,9 @@
var/datum/reagent/blood/B = locate() in beaker.reagents.reagent_list
if(B)
data["has_blood"] = TRUE
- data[/datum/reagent/blood] = list()
- data[/datum/reagent/blood]["dna"] = B.data["blood_DNA"] || "none"
- data[/datum/reagent/blood]["type"] = B.data["blood_type"] || "none"
+ data["blood"] = list() //wha why the fuck are we sending pathtypes to tgui frontend?
+ data["blood"]["dna"] = B.data["blood_DNA"] || "none"
+ data["blood"]["type"] = B.data["blood_type"] || "none"
data["viruses"] = get_viruses_data(B)
data["resistances"] = get_resistance_data(B)
else
@@ -153,7 +176,7 @@
return
switch(action)
if("eject_beaker")
- replace_beaker(usr)
+ eject_beaker()
. = TRUE
if("empty_beaker")
if(beaker)
@@ -162,7 +185,7 @@
if("empty_eject_beaker")
if(beaker)
beaker.reagents.clear_reagents()
- replace_beaker(usr)
+ eject_beaker()
. = TRUE
if("rename_disease")
var/id = get_virus_id_by_index(text2num(params["index"]))
@@ -170,75 +193,62 @@
if(!A.mutable)
return
if(A)
- var/new_name = sanitize_name(html_encode(trim(params["name"], 50)))
+ var/new_name = sanitize_name(html_encode(trim(params["name"], 50)))//, allow_numbers = TRUE)
if(!new_name || ..())
return
A.AssignName(new_name)
. = TRUE
if("create_culture_bottle")
+ if (wait)
+ return
var/id = get_virus_id_by_index(text2num(params["index"]))
var/datum/disease/advance/A = SSdisease.archive_diseases[id]
if(!istype(A) || !A.mutable)
to_chat(usr, "ERROR: Cannot replicate virus strain. ")
return
- wait = TRUE
- addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 50)
A = A.Copy()
- var/list/data = list("blood_DNA" = "UNKNOWN DNA", "blood_type" = "SY", "viruses" = list(A))
+ var/list/data = list("viruses" = list(A))
var/obj/item/reagent_containers/glass/bottle/B = new(drop_location())
B.name = "[A.name] culture bottle"
B.desc = "A small bottle. Contains [A.agent] culture in synthblood medium."
B.reagents.add_reagent(/datum/reagent/blood/synthetics, 10, data)
+ wait = TRUE
update_icon()
var/turf/source_turf = get_turf(src)
log_virus("A culture bottle was printed for the virus [A.admin_details()] at [loc_name(source_turf)] by [key_name(usr)]")
-
+ addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 50)
. = TRUE
if("create_vaccine_bottle")
- wait = TRUE
- addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 400)
+ if (wait)
+ return
var/id = params["index"]
var/datum/disease/D = SSdisease.archive_diseases[id]
var/obj/item/reagent_containers/glass/bottle/B = new(drop_location())
B.name = "[D.name] vaccine bottle"
B.reagents.add_reagent(/datum/reagent/vaccine, 15, list(id))
-
+ wait = TRUE
update_icon()
-
+ addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 200)
. = TRUE
+
/obj/machinery/computer/pandemic/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/reagent_containers) && !(I.item_flags & ABSTRACT) && I.is_open_container())
. = TRUE //no afterattack
if(stat & (NOPOWER|BROKEN))
return
- var/obj/item/reagent_containers/B = I
- if(!user.transferItemToLoc(B, src))
+ if(beaker)
+ to_chat(user, "A container is already loaded into [src]! ")
return
- replace_beaker(user, B)
+ if(!user.transferItemToLoc(I, src))
+ return
+
+ beaker = I
to_chat(user, "You insert [I] into [src]. ")
+ update_icon()
else
return ..()
-/obj/machinery/computer/pandemic/AltClick(mob/living/user)
- . = ..()
- if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
- return
- replace_beaker(user)
- return TRUE
-
-/obj/machinery/computer/pandemic/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
- if(beaker)
- if(user && Adjacent(user) && user.can_hold_items())
- if(!user.put_in_hands(beaker))
- beaker.forceMove(drop_location())
- if(new_beaker)
- beaker = new_beaker
- else
- beaker = null
- update_icon()
- return TRUE
-
/obj/machinery/computer/pandemic/on_deconstruction()
- replace_beaker(usr)
+ eject_beaker()
. = ..()
diff --git a/code/modules/reagents/chemistry/machinery/smoke_machine.dm b/code/modules/reagents/chemistry/machinery/smoke_machine.dm
index cac90d1c14..a539897c9d 100644
--- a/code/modules/reagents/chemistry/machinery/smoke_machine.dm
+++ b/code/modules/reagents/chemistry/machinery/smoke_machine.dm
@@ -7,6 +7,7 @@
icon_state = "smoke0"
density = TRUE
circuit = /obj/item/circuitboard/machine/smoke_machine
+
var/efficiency = 10
var/on = FALSE
var/cooldown = 0
@@ -31,6 +32,7 @@
/obj/machinery/smoke_machine/Initialize()
. = ..()
create_reagents(REAGENTS_BASE_VOLUME)
+ // AddComponent(/datum/component/plumbing/simple_demand)
for(var/obj/item/stock_parts/matter_bin/B in component_parts)
reagents.maximum_volume += REAGENTS_BASE_VOLUME * B.rating
@@ -81,10 +83,9 @@
add_fingerprint(user)
if(istype(I, /obj/item/reagent_containers) && I.is_open_container())
var/obj/item/reagent_containers/RC = I
- var/units = RC.reagents.trans_to(src, RC.amount_per_transfer_from_this)
+ var/units = RC.reagents.trans_to(src, RC.amount_per_transfer_from_this) //, transfered_by = user)
if(units)
to_chat(user, "You transfer [units] units of the solution to [src]. ")
- log_combat(usr, src, "has added [english_list(RC.reagents.reagent_list)] to [src]")
return
if(default_unfasten_wrench(user, I, 40))
on = FALSE
@@ -100,11 +101,10 @@
reagents.clear_reagents()
return ..()
-/obj/machinery/smoke_machine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/smoke_machine/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "SmokeMachine", name, 350, 350, master_ui, state)
+ ui = new(user, src, "SmokeMachine", name)
ui.open()
/obj/machinery/smoke_machine/ui_data(mob/user)
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index ba4870903e..c8da9ab5e3 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -299,13 +299,15 @@
// handle machine interaction
-/obj/machinery/disposal/bin/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
+/obj/machinery/disposal/bin/ui_state(mob/user)
+ return GLOB.notcontained_state
+
+/obj/machinery/disposal/bin/ui_interact(mob/user, datum/tgui/ui)
if(stat & BROKEN)
return
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "DisposalUnit", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "DisposalUnit", name)
ui.open()
/obj/machinery/disposal/bin/ui_data(mob/user)
diff --git a/code/modules/research/anomaly/anomaly_core.dm b/code/modules/research/anomaly/anomaly_core.dm
new file mode 100644
index 0000000000..7aeb7b3a9b
--- /dev/null
+++ b/code/modules/research/anomaly/anomaly_core.dm
@@ -0,0 +1,63 @@
+// Embedded signaller used in anomalies.
+/obj/item/assembly/signaler/anomaly
+ name = "anomaly core"
+ desc = "The neutralized core of an anomaly. It'd probably be valuable for research."
+ icon_state = "anomaly_core"
+ //inhand_icon_state = "electronic"
+ lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
+ resistance_flags = FIRE_PROOF
+ var/anomaly_type = /obj/effect/anomaly
+
+/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal)
+ if(!signal)
+ return FALSE
+ if(signal.data["code"] != code)
+ return FALSE
+ if(suicider)
+ manual_suicide(suicider)
+ for(var/obj/effect/anomaly/A in get_turf(src))
+ A.anomalyNeutralize()
+ return TRUE
+
+/obj/item/assembly/signaler/anomaly/manual_suicide(mob/living/carbon/user)
+ user.visible_message("[user]'s [src] is reacting to the radio signal, warping [user.p_their()] body! ")
+ //user.set_suicide(TRUE)
+ user.suicide_log()
+ user.gib()
+
+/obj/item/assembly/signaler/anomaly/attackby(obj/item/I, mob/user, params)
+ if(I.tool_behaviour == TOOL_ANALYZER)
+ to_chat(user, "Analyzing... [src]'s stabilized field is fluctuating along frequency [format_frequency(frequency)], code [code]. ")
+ return ..()
+
+//Anomaly cores
+/obj/item/assembly/signaler/anomaly/pyro
+ name = "\improper pyroclastic anomaly core"
+ desc = "The neutralized core of a pyroclastic anomaly. It feels warm to the touch. It'd probably be valuable for research."
+ icon_state = "pyro_core"
+ anomaly_type = /obj/effect/anomaly/pyro
+
+/obj/item/assembly/signaler/anomaly/grav
+ name = "\improper gravitational anomaly core"
+ desc = "The neutralized core of a gravitational anomaly. It feels much heavier than it looks. It'd probably be valuable for research."
+ icon_state = "grav_core"
+ anomaly_type = /obj/effect/anomaly/grav
+
+/obj/item/assembly/signaler/anomaly/flux
+ name = "\improper flux anomaly core"
+ desc = "The neutralized core of a flux anomaly. Touching it makes your skin tingle. It'd probably be valuable for research."
+ icon_state = "flux_core"
+ anomaly_type = /obj/effect/anomaly/flux
+
+/obj/item/assembly/signaler/anomaly/bluespace
+ name = "\improper bluespace anomaly core"
+ desc = "The neutralized core of a bluespace anomaly. It keeps phasing in and out of view. It'd probably be valuable for research."
+ icon_state = "anomaly_core"
+ anomaly_type = /obj/effect/anomaly/bluespace
+
+/obj/item/assembly/signaler/anomaly/vortex
+ name = "\improper vortex anomaly core"
+ desc = "The neutralized core of a vortex anomaly. It won't sit still, as if some invisible force is acting on it. It'd probably be valuable for research."
+ icon_state = "vortex_core"
+ anomaly_type = /obj/effect/anomaly/bhole
diff --git a/code/modules/research/bepis.dm b/code/modules/research/bepis.dm
index 87aec72646..7b36a614a7 100644
--- a/code/modules/research/bepis.dm
+++ b/code/modules/research/bepis.dm
@@ -33,11 +33,13 @@
var/inaccuracy_percentage = 1.5
var/positive_cash_offset = 0
var/negative_cash_offset = 0
- var/minor_rewards = list(/obj/item/stack/circuit_stack/full, //To add a new minor reward, add it here.
- /obj/item/flashlight/flashdark,
- /obj/item/pen/survival,
- /obj/item/circuitboard/machine/sleeper/party,
- /obj/item/toy/sprayoncan)
+ var/list/minor_rewards = list(
+ //To add a new minor reward, add it here.
+ /obj/item/stack/circuit_stack/full,
+ /obj/item/pen/survival,
+ /obj/item/circuitboard/machine/sleeper/party,
+ /obj/item/toy/sprayoncan,
+ )
var/static/list/item_list = list()
/obj/machinery/rnd/bepis/attackby(obj/item/O, mob/user, params)
@@ -101,6 +103,7 @@
return
account.adjust_money(-deposit_value) //The money vanishes, not paid to any accounts.
SSblackbox.record_feedback("amount", "BEPIS_credits_spent", deposit_value)
+ //log_econ("[deposit_value] credits were inserted into [src] by [account.account_holder]")
banked_cash += deposit_value
use_power(1000 * power_saver)
say("Cash deposit successful. There is [banked_cash] in the chamber.")
@@ -179,10 +182,10 @@
icon_state = "chamber"
return
-/obj/machinery/rnd/bepis/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/rnd/bepis/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "Bepis", name, 500, 480, master_ui, state)
+ ui = new(user, src, "Bepis", name)
ui.open()
RefreshParts()
diff --git a/code/modules/research/nanites/nanite_chamber_computer.dm b/code/modules/research/nanites/nanite_chamber_computer.dm
index b9623b751d..70e4d05590 100644
--- a/code/modules/research/nanites/nanite_chamber_computer.dm
+++ b/code/modules/research/nanites/nanite_chamber_computer.dm
@@ -5,8 +5,6 @@
var/obj/item/disk/nanite_program/disk
icon_screen = "nanite_chamber_control"
circuit = /obj/item/circuitboard/computer/nanite_chamber_control
- ui_x = 380
- ui_y = 570
/obj/machinery/computer/nanite_chamber_control/Initialize()
. = ..()
@@ -25,10 +23,10 @@
find_chamber()
..()
-/obj/machinery/computer/nanite_chamber_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/nanite_chamber_control/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "NaniteChamberControl", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "NaniteChamberControl", name)
ui.open()
/obj/machinery/computer/nanite_chamber_control/ui_data()
diff --git a/code/modules/research/nanites/nanite_cloud_controller.dm b/code/modules/research/nanites/nanite_cloud_controller.dm
index ef0fc33bd7..1a0a5dbd4d 100644
--- a/code/modules/research/nanites/nanite_cloud_controller.dm
+++ b/code/modules/research/nanites/nanite_cloud_controller.dm
@@ -1,11 +1,9 @@
/obj/machinery/computer/nanite_cloud_controller
name = "nanite cloud controller"
desc = "Stores and controls nanite cloud backups."
- circuit = /obj/item/circuitboard/computer/nanite_cloud_controller
icon = 'icons/obj/machines/research.dmi'
icon_state = "nanite_cloud_controller"
- ui_x = 375
- ui_y = 700
+ circuit = /obj/item/circuitboard/computer/nanite_cloud_controller
var/obj/item/disk/nanite_program/disk
var/list/datum/nanite_cloud_backup/cloud_backups = list()
@@ -20,19 +18,25 @@
/obj/machinery/computer/nanite_cloud_controller/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/disk/nanite_program))
var/obj/item/disk/nanite_program/N = I
- if(disk)
- eject(user)
- if(user.transferItemToLoc(N, src))
+ if (user.transferItemToLoc(N, src))
to_chat(user, "You insert [N] into [src]. ")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ if(disk)
+ eject(user)
disk = N
else
..()
+/obj/machinery/computer/nanite_cloud_controller/AltClick(mob/user)
+ if(disk && user.canUseTopic(src, !issilicon(user)))
+ to_chat(user, "You take out [disk] from [src]. ")
+ eject(user)
+ return
+
/obj/machinery/computer/nanite_cloud_controller/proc/eject(mob/living/user)
if(!disk)
return
- if(!istype(user) || !Adjacent(user) ||!user.put_in_active_hand(disk))
+ if(!istype(user) || !Adjacent(user))// ||!user.put_in_active_hand(disk))
disk.forceMove(drop_location())
disk = null
@@ -53,10 +57,10 @@
backup.nanites = cloud_copy
investigate_log("[key_name(user)] created a new nanite cloud backup with id #[cloud_id]", INVESTIGATE_NANITES)
-/obj/machinery/computer/nanite_cloud_controller/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/nanite_cloud_controller/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "NaniteCloudControl", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "NaniteCloudControl", name)
ui.open()
/obj/machinery/computer/nanite_cloud_controller/ui_data()
diff --git a/code/modules/research/nanites/nanite_program_hub.dm b/code/modules/research/nanites/nanite_program_hub.dm
index 3f62a11f89..5a330f7a4f 100644
--- a/code/modules/research/nanites/nanite_program_hub.dm
+++ b/code/modules/research/nanites/nanite_program_hub.dm
@@ -3,26 +3,24 @@
desc = "Compiles nanite programs from the techweb servers and downloads them into disks."
icon = 'icons/obj/machines/research.dmi'
icon_state = "nanite_program_hub"
- circuit = /obj/item/circuitboard/machine/nanite_program_hub
use_power = IDLE_POWER_USE
anchored = TRUE
density = TRUE
- ui_x = 500
- ui_y = 700
+ circuit = /obj/item/circuitboard/machine/nanite_program_hub
var/obj/item/disk/nanite_program/disk
var/datum/techweb/linked_techweb
var/current_category = "Main"
var/detail_view = TRUE
var/categories = list(
- list(name = "Utility Nanites"),
- list(name = "Medical Nanites"),
- list(name = "Sensor Nanites"),
- list(name = "Augmentation Nanites"),
- list(name = "Suppression Nanites"),
- list(name = "Weaponized Nanites"),
- list(name = "Protocols") //Moved to default techweb from B.E.P.I.S. research, for now
- )
+ list(name = "Utility Nanites"),
+ list(name = "Medical Nanites"),
+ list(name = "Sensor Nanites"),
+ list(name = "Augmentation Nanites"),
+ list(name = "Suppression Nanites"),
+ list(name = "Weaponized Nanites"),
+ list(name = "Protocols"),
+ )
/obj/machinery/nanite_program_hub/Initialize()
. = ..()
@@ -31,26 +29,44 @@
/obj/machinery/nanite_program_hub/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/disk/nanite_program))
var/obj/item/disk/nanite_program/N = I
- if(disk)
- eject(user)
if(user.transferItemToLoc(N, src))
to_chat(user, "You insert [N] into [src]. ")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ if(disk)
+ eject(user)
disk = N
else
..()
+/obj/machinery/nanite_program_hub/screwdriver_act(mob/living/user, obj/item/I) //remove when runtimed
+ if(..())
+ return TRUE
+
+ return default_deconstruction_screwdriver(user, "nanite_program_hub_t", "nanite_program_hub", I)
+
+/obj/machinery/nanite_program_hub/crowbar_act(mob/living/user, obj/item/I)
+ if(..())
+ return TRUE
+
+ return default_deconstruction_crowbar(I)
+
/obj/machinery/nanite_program_hub/proc/eject(mob/living/user)
if(!disk)
return
- if(!istype(user) || !Adjacent(user) || !user.put_in_active_hand(disk))
+ if(!istype(user) || !Adjacent(user))// || !user.put_in_active_hand(disk))
disk.forceMove(drop_location())
disk = null
-/obj/machinery/nanite_program_hub/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/nanite_program_hub/AltClick(mob/user)
+ if(disk && user.canUseTopic(src, !issilicon(user)))
+ to_chat(user, "You take out [disk] from [src]. ")
+ eject(user)
+ return
+
+/obj/machinery/nanite_program_hub/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "NaniteProgramHub", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "NaniteProgramHub", name)
ui.open()
/obj/machinery/nanite_program_hub/ui_data()
@@ -123,9 +139,3 @@
disk.program = null
disk.name = initial(disk.name)
. = TRUE
-
-
-/obj/machinery/nanite_program_hub/admin/Initialize()
- . = ..()
- linked_techweb = SSresearch.admin_tech
-
diff --git a/code/modules/research/nanites/nanite_programmer.dm b/code/modules/research/nanites/nanite_programmer.dm
index 3de2a974e2..804f256cf6 100644
--- a/code/modules/research/nanites/nanite_programmer.dm
+++ b/code/modules/research/nanites/nanite_programmer.dm
@@ -3,41 +3,57 @@
desc = "A device that can edit nanite program disks to adjust their functionality."
var/obj/item/disk/nanite_program/disk
var/datum/nanite_program/program
- circuit = /obj/item/circuitboard/machine/nanite_programmer
icon = 'icons/obj/machines/research.dmi'
icon_state = "nanite_programmer"
use_power = IDLE_POWER_USE
anchored = TRUE
density = TRUE
flags_1 = HEAR_1
- ui_x = 420
- ui_y = 550
+ circuit = /obj/item/circuitboard/machine/nanite_programmer
/obj/machinery/nanite_programmer/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/disk/nanite_program))
var/obj/item/disk/nanite_program/N = I
- if(disk)
- eject(user)
if(user.transferItemToLoc(N, src))
to_chat(user, "You insert [N] into [src] ")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ if(disk)
+ eject(user)
disk = N
program = N.program
else
..()
+/obj/machinery/nanite_programmer/screwdriver_act(mob/living/user, obj/item/I)
+ if(..())
+ return TRUE
+
+ return default_deconstruction_screwdriver(user, "nanite_programmer_t", "nanite_programmer", I)
+
+/obj/machinery/nanite_programmer/crowbar_act(mob/living/user, obj/item/I)
+ if(..())
+ return TRUE
+
+ return default_deconstruction_crowbar(I)
+
/obj/machinery/nanite_programmer/proc/eject(mob/living/user)
if(!disk)
return
- if(!istype(user) || !Adjacent(user) || !user.put_in_active_hand(disk))
+ if(!istype(user) || !Adjacent(user))// || !user.put_in_active_hand(disk))
disk.forceMove(drop_location())
disk = null
program = null
-/obj/machinery/nanite_programmer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/nanite_programmer/AltClick(mob/user)
+ if(disk && user.canUseTopic(src, !issilicon(user)))
+ to_chat(user, "You take out [disk] from [src]. ")
+ eject(user)
+ return
+
+/obj/machinery/nanite_programmer/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "NaniteProgrammer", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "NaniteProgrammer", name)
ui.open()
/obj/machinery/nanite_programmer/ui_data()
@@ -131,7 +147,7 @@
program.timer_trigger_delay = timer
. = TRUE
-/obj/machinery/nanite_programmer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
+/obj/machinery/nanite_programmer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list())
. = ..()
var/static/regex/when = regex("(?:^\\W*when|when\\W*$)", "i") //starts or ends with when
if(findtext(raw_message, when) && !istype(speaker, /obj/machinery/nanite_programmer))
diff --git a/code/modules/research/nanites/nanite_remote.dm b/code/modules/research/nanites/nanite_remote.dm
index b222b2ad35..e3f5a0f286 100644
--- a/code/modules/research/nanites/nanite_remote.dm
+++ b/code/modules/research/nanites/nanite_remote.dm
@@ -80,10 +80,13 @@
var/datum/nanite_program/relay/N = X
N.relay_signal(code, relay_code, source)
-/obj/item/nanite_remote/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/nanite_remote/ui_state(mob/user)
+ return GLOB.hands_state
+
+/obj/item/nanite_remote/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "NaniteRemote", name, 420, 500, master_ui, state)
+ ui = new(user, src, "NaniteRemote", name)
ui.open()
/obj/item/nanite_remote/ui_data()
@@ -94,7 +97,6 @@
data["locked"] = locked
data["saved_settings"] = saved_settings
data["program_name"] = current_program_name
-
return data
/obj/item/nanite_remote/ui_act(action, params)
diff --git a/code/modules/ruins/spaceruin_code/TheDerelict.dm b/code/modules/ruins/spaceruin_code/TheDerelict.dm
index 513d16a0cb..d26e023df0 100644
--- a/code/modules/ruins/spaceruin_code/TheDerelict.dm
+++ b/code/modules/ruins/spaceruin_code/TheDerelict.dm
@@ -17,7 +17,6 @@
desc = "Looks like someone started shakily writing a will in space common, but were interrupted by something bloody..."
info = "__Objectives #1__: Find out what is hidden in Kosmicheskaya Stantsiya 13s Vault"
-
/// Vault controller for use on the derelict/KS13.
/obj/machinery/computer/vaultcontroller
name = "vault controller"
@@ -35,15 +34,10 @@
var/siphoned_power = 0
var/siphon_max = 1e7
- ui_x = 300
- ui_y = 120
-
-
/obj/machinery/computer/monitor/examine(mob/user)
. = ..()
. += "It appears to be powered via a cable connector. "
-
//Checks for cable connection, charges if possible.
/obj/machinery/computer/vaultcontroller/process()
if(siphoned_power >= siphon_max)
@@ -52,13 +46,11 @@
if(attached_cable)
attempt_siphon()
-
///Looks for a cable connection beneath the machine.
/obj/machinery/computer/vaultcontroller/proc/update_cable()
var/turf/T = get_turf(src)
attached_cable = locate(/obj/structure/cable) in T
-
///Initializes airlock links.
/obj/machinery/computer/vaultcontroller/proc/find_airlocks()
for(var/obj/machinery/door/airlock/A in GLOB.airlocks)
@@ -70,7 +62,6 @@
door2 = A
break
-
///Tries to charge from powernet excess, no upper limit except max charge.
/obj/machinery/computer/vaultcontroller/proc/attempt_siphon()
var/surpluspower = clamp(attached_cable.surplus(), 0, (siphon_max - siphoned_power))
@@ -78,7 +69,6 @@
attached_cable.add_load(surpluspower)
siphoned_power += surpluspower
-
///Handles the doors closing
/obj/machinery/computer/vaultcontroller/proc/cycle_close(obj/machinery/door/airlock/A)
A.safe = FALSE //Make sure its forced closed, always
@@ -86,14 +76,12 @@
A.close()
A.bolt()
-
///Handles the doors opening
/obj/machinery/computer/vaultcontroller/proc/cycle_open(obj/machinery/door/airlock/A)
A.unbolt()
A.open()
A.bolt()
-
///Attempts to lock the vault doors
/obj/machinery/computer/vaultcontroller/proc/lock_vault()
if(door1 && !door1.density)
@@ -103,7 +91,6 @@
if(door1.density && door1.locked && door2.density && door2.locked)
locked = TRUE
-
///Attempts to unlock the vault doors
/obj/machinery/computer/vaultcontroller/proc/unlock_vault()
if(door1 && door1.density)
@@ -113,7 +100,6 @@
if(!door1.density && door1.locked && !door2.density && door2.locked)
locked = FALSE
-
///Attempts to lock/unlock vault doors, if machine is charged.
/obj/machinery/computer/vaultcontroller/proc/activate_lock()
if(siphoned_power < siphon_max)
@@ -125,15 +111,12 @@
else
lock_vault()
-
-/obj/machinery/computer/vaultcontroller/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/vaultcontroller/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "VaultController", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "VaultController", name)
ui.open()
-
/obj/machinery/computer/vaultcontroller/ui_act(action, params)
if(..())
return
@@ -141,7 +124,6 @@
if("togglelock")
activate_lock()
-
/obj/machinery/computer/vaultcontroller/ui_data()
var/list/data = list()
data["stored"] = siphoned_power
@@ -149,7 +131,6 @@
data["doorstatus"] = locked
return data
-
///Airlock that can't be deconstructed, broken or hacked.
/obj/machinery/door/airlock/vault/derelict
locked = TRUE
@@ -158,16 +139,13 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
id_tag = "derelictvault"
-
///Overrides screwdriver attack to prevent all deconstruction and hacking.
/obj/machinery/door/airlock/vault/derelict/attackby(obj/item/C, mob/user, params)
if(C.tool_behaviour == TOOL_SCREWDRIVER)
return
..()
-
// So drones can teach borgs and AI dronespeak. For best effect, combine with mother drone lawset.
-
/obj/item/dronespeak_manual
name = "dronespeak manual"
desc = "The book's cover reads: \"Understanding Dronespeak - An exercise in futility.\""
@@ -181,7 +159,7 @@
to_chat(user, "You start skimming through [src], but you already know dronespeak. ")
else
to_chat(user, "You start skimming through [src], and suddenly the drone chittering makes sense. ")
- user.grant_language(/datum/language/drone, TRUE, TRUE)
+ user.grant_language(/datum/language/drone, TRUE, TRUE)//, LANGUAGE_MIND)
return
if(user.has_language(/datum/language/drone))
@@ -202,7 +180,7 @@
M.visible_message("[user] beats [M] over the head with [src]! ", "[user] beats you over the head with [src]! ", "You hear smacking. ")
else
M.visible_message("[user] teaches [M] by beating [M.p_them()] over the head with [src]! ", "As [user] hits you with [src], chitters resonate in your mind. ", "You hear smacking. ")
- M.grant_language(/datum/language/drone, TRUE, TRUE)
+ M.grant_language(/datum/language/drone, TRUE, TRUE) //, LANGUAGE_MIND)
return
/obj/structure/fluff/oldturret
diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm
index f6418b9236..7326cad816 100644
--- a/code/modules/security_levels/keycard_authentication.dm
+++ b/code/modules/security_levels/keycard_authentication.dm
@@ -35,11 +35,13 @@ GLOBAL_DATUM_INIT(keycard_events, /datum/events, new)
QDEL_NULL(ev)
return ..()
-/obj/machinery/keycard_auth/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/keycard_auth/ui_state(mob/user)
+ return GLOB.physical_state
+
+/obj/machinery/keycard_auth/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "KeycardAuth", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "KeycardAuth", name)
ui.open()
/obj/machinery/keycard_auth/ui_data()
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index 637d9ae334..810cadcd2c 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -45,11 +45,14 @@
say("Please equip your ID card into your ID slot to authenticate.")
. = ..()
-/obj/machinery/computer/emergency_shuttle/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.human_adjacent_state)
+/obj/machinery/computer/emergency_shuttle/ui_state(mob/user)
+ return GLOB.human_adjacent_state
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/emergency_shuttle/ui_interact(mob/user, datum/tgui/ui)
+
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "EmergencyShuttleConsole", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "EmergencyShuttleConsole", name)
ui.open()
/obj/machinery/computer/emergency_shuttle/ui_data()
@@ -65,8 +68,8 @@
var/job = ID.assignment
if(obj_flags & EMAGGED)
- name = Gibberish(name, 0)
- job = Gibberish(job, 0)
+ name = Gibberish(name)
+ job = Gibberish(job)
A += list(list("name" = name, "job" = job))
data["authorizations"] = A
diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm
index 2ca0e65477..4c3f30a786 100644
--- a/code/modules/station_goals/bsa.dm
+++ b/code/modules/station_goals/bsa.dm
@@ -210,20 +210,23 @@
/obj/machinery/computer/bsa_control
name = "bluespace artillery control"
- var/obj/machinery/bsa/full/cannon
- var/notice
- var/target
use_power = NO_POWER_USE
circuit = /obj/item/circuitboard/computer/bsa_control
icon = 'icons/obj/machines/particle_accelerator.dmi'
icon_state = "control_boxp"
+
+ var/obj/machinery/bsa/full/cannon
+ var/notice
+ var/target
var/area_aim = FALSE //should also show areas for targeting
-/obj/machinery/computer/bsa_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/bsa_control/ui_state(mob/user)
+ return GLOB.physical_state
+
+/obj/machinery/computer/bsa_control/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "BluespaceArtillery", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "BluespaceArtillery", name)
ui.open()
/obj/machinery/computer/bsa_control/ui_data()
@@ -255,7 +258,7 @@
if(!GLOB.bsa_unlock)
return
var/list/gps_locators = list()
- for(var/obj/item/gps/G in GLOB.GPS_list) //nulls on the list somehow
+ for(var/datum/component/gps/G in GLOB.GPS_list) //nulls on the list somehow
if(G.tracking)
gps_locators[G.gpstag] = G
diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm
index 6d8ab9cc7f..4ac3777a41 100644
--- a/code/modules/station_goals/dna_vault.dm
+++ b/code/modules/station_goals/dna_vault.dm
@@ -174,14 +174,13 @@
. = ..()
-/obj/machinery/dna_vault/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/dna_vault/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
roll_powers(user)
- ui = new(user, src, ui_key, "DnaVault", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "DnaVault", name)
ui.open()
-
/obj/machinery/dna_vault/proc/roll_powers(mob/user)
if(user in power_lottery)
return
diff --git a/code/modules/station_goals/shield.dm b/code/modules/station_goals/shield.dm
index 299fda4a26..c8fbda8988 100644
--- a/code/modules/station_goals/shield.dm
+++ b/code/modules/station_goals/shield.dm
@@ -42,10 +42,10 @@
circuit = /obj/item/circuitboard/computer/sat_control
var/notice
-/obj/machinery/computer/sat_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/sat_control/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "SatelliteControl", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, "SatelliteControl", name)
ui.open()
/obj/machinery/computer/sat_control/ui_act(action, params)
diff --git a/code/modules/tgui/external.dm b/code/modules/tgui/external.dm
index 5de54c439c..46b324e151 100644
--- a/code/modules/tgui/external.dm
+++ b/code/modules/tgui/external.dm
@@ -1,7 +1,8 @@
/**
- * tgui external
+ * External tgui definitions, such as src_object APIs.
*
- * Contains all external tgui declarations.
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
/**
@@ -11,13 +12,9 @@
* If this proc is not implemented properly, the UI will not update correctly.
*
* required user mob The mob who opened/is using the UI.
- * optional ui_key string The ui_key of the UI.
* optional ui datum/tgui The UI to be updated, if it exists.
- * optional force_open bool If the UI should be re-opened instead of updated.
- * optional master_ui datum/tgui The parent UI.
- * optional state datum/ui_state The state used to determine status.
*/
-/datum/proc/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+/datum/proc/ui_interact(mob/user, datum/tgui/ui)
return FALSE // Not implemented.
/**
@@ -37,10 +34,11 @@
* public
*
* Static Data to be sent to the UI.
- * Static data differs from normal data in that it's large data that should be sent infrequently
- * This is implemented optionally for heavy uis that would be sending a lot of redundant data
- * frequently.
- * Gets squished into one object on the frontend side, but the static part is cached.
+ *
+ * Static data differs from normal data in that it's large data that should be
+ * sent infrequently. This is implemented optionally for heavy uis that would
+ * be sending a lot of redundant data frequently. Gets squished into one
+ * object on the frontend side, but the static part is cached.
*
* required user mob The mob interacting with the UI.
*
@@ -52,18 +50,17 @@
/**
* public
*
- * Forces an update on static data. Should be done manually whenever something happens to change static data.
+ * Forces an update on static data. Should be done manually whenever something
+ * happens to change static data.
*
* required user the mob currently interacting with the ui
* optional ui ui to be updated
- * optional ui_key ui key of ui to be updated
*/
-/datum/proc/update_static_data(mob/user, datum/tgui/ui, ui_key = "main")
- ui = SStgui.try_update_ui(user, src, ui_key, ui)
- // If there was no ui to update, there's no static data to update either.
+/datum/proc/update_static_data(mob/user, datum/tgui/ui)
if(!ui)
- return
- ui.push_data(null, ui_static_data(), TRUE)
+ ui = SStgui.get_open_ui(user, src)
+ if(ui)
+ ui.send_full_update()
/**
* public
@@ -85,17 +82,12 @@
* public
*
* Called on an object when a tgui object is being created, allowing you to
- * customise the html
- * For example: inserting a custom stylesheet that you need in the head
+ * push various assets to tgui, for examples spritesheets.
*
- * For this purpose, some tags are available in the html, to be parsed out
- ^ with replacetext
- * (customheadhtml) - Additions to the head tag
- *
- * required html the html base text
+ * return list List of asset datums or file paths.
*/
-/datum/proc/ui_base_html(html)
- return html
+/datum/proc/ui_assets(mob/user)
+ return list()
/**
* private
@@ -107,6 +99,15 @@
/datum/proc/ui_host(mob/user)
return src // Default src.
+/**
+ * private
+ *
+ * The UI's state controller to be used for created uis
+ * This is a proc over a var for memory reasons
+ */
+/datum/proc/ui_state(mob/user)
+ return GLOB.default_state
+
/**
* global
*
@@ -118,9 +119,17 @@
/**
* global
*
- * Used to track UIs for a mob.
+ * Tracks open UIs for a user.
*/
-/mob/var/list/open_uis = list()
+/mob/var/list/tgui_open_uis = list()
+
+/**
+ * global
+ *
+ * Tracks open windows for a user.
+ */
+/client/var/list/tgui_windows = list()
+
/**
* public
*
@@ -137,17 +146,43 @@
*
* required uiref ref The UI that was closed.
*/
-/client/verb/uiclose(ref as text)
+/client/verb/uiclose(window_id as text)
// Name the verb, and hide it from the user panel.
set name = "uiclose"
- set hidden = 1
+ set hidden = TRUE
+ var/mob/user = src && src.mob
+ if(!user)
+ return
+ // Close all tgui datums based on window_id.
+ SStgui.force_close_window(user, window_id)
- // Get the UI based on the ref.
- var/datum/tgui/ui = locate(ref)
-
- // If we found the UI, close it.
- if(istype(ui))
- ui.close()
- // Unset machine just to be sure.
- if(src && src.mob)
- src.mob.unset_machine()
+/**
+ * Middleware for /client/Topic.
+ *
+ * return bool Whether the topic is passed (TRUE), or cancelled (FALSE).
+ */
+/proc/tgui_Topic(href_list)
+ // Skip non-tgui topics
+ if(!href_list["tgui"])
+ return TRUE
+ var/type = href_list["type"]
+ // Unconditionally collect tgui logs
+ if(type == "log")
+ log_tgui(usr, href_list["message"])
+ // Locate window
+ var/window_id = href_list["window_id"]
+ var/datum/tgui_window/window
+ if(window_id)
+ window = usr.client.tgui_windows[window_id]
+ if(!window)
+ log_tgui(usr, "Error: Couldn't find the window datum, force closing.")
+ SStgui.force_close_window(usr, window_id)
+ return FALSE
+ // Decode payload
+ var/payload
+ if(href_list["payload"])
+ payload = json_decode(href_list["payload"])
+ // Pass message to window
+ if(window)
+ window.on_message(type, payload, href_list)
+ return FALSE
diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm
index e626b98815..fa88cc1338 100644
--- a/code/modules/tgui/states.dm
+++ b/code/modules/tgui/states.dm
@@ -1,7 +1,9 @@
/**
- * tgui states
+ * Base state and helpers for states. Just does some sanity checks,
+ * implement a proper state for in-depth checks.
*
- * Base state and helpers for states. Just does some sanity checks, implement a state for in-depth checks.
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
/**
@@ -26,9 +28,10 @@
. = max(., UI_INTERACTIVE)
// Regular ghosts can always at least view if in range.
- var/clientviewlist = getviewsize(user.client.view)
- if(get_dist(src_object, user) < max(clientviewlist[1],clientviewlist[2]))
- . = max(., UI_UPDATE)
+ if(user.client)
+ var/clientviewlist = getviewsize(user.client.view)
+ if(get_dist(src_object, user) < max(clientviewlist[1], clientviewlist[2]))
+ . = max(., UI_UPDATE)
// Check if the state allows interaction
var/result = state.can_use_topic(src_object, user)
@@ -46,7 +49,8 @@
* return UI_state The state of the UI.
*/
/datum/ui_state/proc/can_use_topic(src_object, mob/user)
- return UI_CLOSE // Don't allow interaction by default.
+ // Don't allow interaction by default.
+ return UI_CLOSE
/**
* public
@@ -56,21 +60,31 @@
* return UI_state The state of the UI.
*/
/mob/proc/shared_ui_interaction(src_object)
- if(!client) // Close UIs if mindless.
+ // Close UIs if mindless.
+ if(!client)
return UI_CLOSE
- else if(stat) // Disable UIs if unconcious.
+ // Disable UIs if unconcious.
+ else if(stat)
return UI_DISABLED
- else if(incapacitated() || lying) // Update UIs if incapicitated but concious.
+ // Update UIs if incapicitated but concious.
+ else if(incapacitated())
return UI_UPDATE
return UI_INTERACTIVE
+/mob/living/shared_ui_interaction(src_object)
+ . = ..()
+ if(!(mobility_flags & MOBILITY_UI) && . == UI_INTERACTIVE)
+ return UI_UPDATE
+
/mob/living/silicon/ai/shared_ui_interaction(src_object)
- if(lacks_power()) // Disable UIs if the AI is unpowered.
+ // Disable UIs if the AI is unpowered.
+ if(lacks_power())
return UI_DISABLED
return ..()
/mob/living/silicon/robot/shared_ui_interaction(src_object)
- if(!cell || cell.charge <= 0 || locked_down) // Disable UIs if the Borg is unpowered or locked.
+ // Disable UIs if the Borg is unpowered or locked.
+ if(!cell || cell.charge <= 0 || locked_down)
return UI_DISABLED
return ..()
@@ -87,7 +101,8 @@
* return UI_state The state of the UI.
*/
/atom/proc/contents_ui_distance(src_object, mob/living/user)
- return user.shared_living_ui_distance(src_object) // Just call this mob's check.
+ // Just call this mob's check.
+ return user.shared_living_ui_distance(src_object)
/**
* public
@@ -99,17 +114,21 @@
* return UI_state The state of the UI.
*/
/mob/living/proc/shared_living_ui_distance(atom/movable/src_object, viewcheck = TRUE)
- if(viewcheck && !(src_object in view(src))) // If the object is obscured, close it.
+ // If the object is obscured, close it.
+ if(viewcheck && !(src_object in view(src)))
return UI_CLOSE
-
var/dist = get_dist(src_object, src)
- if(dist <= 1 || src_object.hasSiliconAccessInArea(src)) // Open and interact if 1-0 tiles away.
+ // Open and interact if 1-0 tiles away.
+ if(dist <= 1)
return UI_INTERACTIVE
- else if(dist <= 2) // View only if 2-3 tiles away.
+ // View only if 2-3 tiles away.
+ else if(dist <= 2)
return UI_UPDATE
- else if(dist <= 5) // Disable if 5 tiles away.
+ // Disable if 5 tiles away.
+ else if(dist <= 5)
return UI_DISABLED
- return UI_CLOSE // Otherwise, we got nothing.
+ // Otherwise, we got nothing.
+ return UI_CLOSE
/mob/living/carbon/human/shared_living_ui_distance(atom/movable/src_object, viewcheck = TRUE)
if(dna.check_mutation(TK) && tkMaxRangeCheck(src, src_object))
diff --git a/code/modules/tgui/states/admin.dm b/code/modules/tgui/states/admin.dm
index 61fc373118..227a294078 100644
--- a/code/modules/tgui/states/admin.dm
+++ b/code/modules/tgui/states/admin.dm
@@ -2,6 +2,9 @@
* tgui state: admin_state
*
* Checks that the user is an admin, end-of-story.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(admin_state, /datum/ui_state/admin_state, new)
diff --git a/code/modules/tgui/states/always.dm b/code/modules/tgui/states/always.dm
index a741e2e3d4..210f0896a2 100644
--- a/code/modules/tgui/states/always.dm
+++ b/code/modules/tgui/states/always.dm
@@ -2,6 +2,9 @@
* tgui state: always_state
*
* Always grants the user UI_INTERACTIVE. Period.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(always_state, /datum/ui_state/always_state, new)
diff --git a/code/modules/tgui/states/conscious.dm b/code/modules/tgui/states/conscious.dm
index 4e2793d130..670ca7c07e 100644
--- a/code/modules/tgui/states/conscious.dm
+++ b/code/modules/tgui/states/conscious.dm
@@ -2,6 +2,9 @@
* tgui state: conscious_state
*
* Only checks if the user is conscious.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(conscious_state, /datum/ui_state/conscious_state, new)
diff --git a/code/modules/tgui/states/contained.dm b/code/modules/tgui/states/contained.dm
index f02424d01e..1eb8edba25 100644
--- a/code/modules/tgui/states/contained.dm
+++ b/code/modules/tgui/states/contained.dm
@@ -2,6 +2,9 @@
* tgui state: contained_state
*
* Checks that the user is inside the src_object.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(contained_state, /datum/ui_state/contained_state, new)
diff --git a/code/modules/tgui/states/deep_inventory.dm b/code/modules/tgui/states/deep_inventory.dm
index 43758cbab1..a2b9276a59 100644
--- a/code/modules/tgui/states/deep_inventory.dm
+++ b/code/modules/tgui/states/deep_inventory.dm
@@ -1,7 +1,11 @@
/**
* tgui state: deep_inventory_state
*
- * Checks that the src_object is in the user's deep (backpack, box, toolbox, etc) inventory.
+ * Checks that the src_object is in the user's deep
+ * (backpack, box, toolbox, etc) inventory.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(deep_inventory_state, /datum/ui_state/deep_inventory_state, new)
diff --git a/code/modules/tgui/states/default.dm b/code/modules/tgui/states/default.dm
index 6bb159640e..367e57beff 100644
--- a/code/modules/tgui/states/default.dm
+++ b/code/modules/tgui/states/default.dm
@@ -1,7 +1,11 @@
/**
* tgui state: default_state
*
- * Checks a number of things -- mostly physical distance for humans and view for robots.
+ * Checks a number of things -- mostly physical distance for humans
+ * and view for robots.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(default_state, /datum/ui_state/default, new)
diff --git a/code/modules/tgui/states/default_contained.dm b/code/modules/tgui/states/default_contained.dm
deleted file mode 100644
index c532e9f5d1..0000000000
--- a/code/modules/tgui/states/default_contained.dm
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * tgui state: default_contained
- *
- * Basically default and contained combined, allowing for both
- */
-
-GLOBAL_DATUM_INIT(default_contained_state, /datum/ui_state/default/contained, new)
-
-/datum/ui_state/default/contained/can_use_topic(atom/src_object, mob/user)
- if(src_object.contains(user))
- return UI_INTERACTIVE
- return ..()
-
\ No newline at end of file
diff --git a/code/modules/tgui/states/hands.dm b/code/modules/tgui/states/hands.dm
index d73d1058ea..1c885ed414 100644
--- a/code/modules/tgui/states/hands.dm
+++ b/code/modules/tgui/states/hands.dm
@@ -2,6 +2,9 @@
* tgui state: hands_state
*
* Checks that the src_object is in the user's hands.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(hands_state, /datum/ui_state/hands_state, new)
@@ -19,7 +22,7 @@ GLOBAL_DATUM_INIT(hands_state, /datum/ui_state/hands_state, new)
return UI_INTERACTIVE
return UI_CLOSE
-/mob/living/silicon/robot/hands_can_use_topic(obj/src_object)
- if(activated(src_object) || istype(src_object.loc, /obj/item/weapon/gripper))
+/mob/living/silicon/robot/hands_can_use_topic(src_object)
+ if(activated(src_object))
return UI_INTERACTIVE
return UI_CLOSE
diff --git a/code/modules/tgui/states/human_adjacent.dm b/code/modules/tgui/states/human_adjacent.dm
index 7aefe43e44..2ac7c8637b 100644
--- a/code/modules/tgui/states/human_adjacent.dm
+++ b/code/modules/tgui/states/human_adjacent.dm
@@ -3,6 +3,9 @@
*
* In addition to default checks, only allows interaction for a
* human adjacent user.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(human_adjacent_state, /datum/ui_state/human_adjacent_state, new)
diff --git a/code/modules/tgui/states/inventory.dm b/code/modules/tgui/states/inventory.dm
index 43fe2cb451..dc5dd0d57e 100644
--- a/code/modules/tgui/states/inventory.dm
+++ b/code/modules/tgui/states/inventory.dm
@@ -1,7 +1,11 @@
/**
* tgui state: inventory_state
*
- * Checks that the src_object is in the user's top-level (hand, ear, pocket, belt, etc) inventory.
+ * Checks that the src_object is in the user's top-level
+ * (hand, ear, pocket, belt, etc) inventory.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(inventory_state, /datum/ui_state/inventory_state, new)
diff --git a/code/modules/tgui/states/language_menu.dm b/code/modules/tgui/states/language_menu.dm
index 5c816c8922..6389b05cd5 100644
--- a/code/modules/tgui/states/language_menu.dm
+++ b/code/modules/tgui/states/language_menu.dm
@@ -1,5 +1,8 @@
/**
* tgui state: language_menu_state
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(language_menu_state, /datum/ui_state/language_menu, new)
diff --git a/code/modules/tgui/states/not_incapacitated.dm b/code/modules/tgui/states/not_incapacitated.dm
index 364b59424d..16dcb7881e 100644
--- a/code/modules/tgui/states/not_incapacitated.dm
+++ b/code/modules/tgui/states/not_incapacitated.dm
@@ -2,6 +2,9 @@
* tgui state: not_incapacitated_state
*
* Checks that the user isn't incapacitated
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(not_incapacitated_state, /datum/ui_state/not_incapacitated_state, new)
@@ -24,6 +27,10 @@ GLOBAL_DATUM_INIT(not_incapacitated_turf_state, /datum/ui_state/not_incapacitate
/datum/ui_state/not_incapacitated_state/can_use_topic(src_object, mob/user)
if(user.stat)
return UI_CLOSE
- if(user.incapacitated() || user.lying || (turf_check && !isturf(user.loc)))
+ if(user.incapacitated() || (turf_check && !isturf(user.loc)))
return UI_DISABLED
- return UI_INTERACTIVE
\ No newline at end of file
+ if(isliving(user))
+ var/mob/living/L = user
+ if(!(L.mobility_flags & MOBILITY_STAND))
+ return UI_DISABLED
+ return UI_INTERACTIVE
diff --git a/code/modules/tgui/states/notcontained.dm b/code/modules/tgui/states/notcontained.dm
index 642c6ce95f..1d4e6aec19 100644
--- a/code/modules/tgui/states/notcontained.dm
+++ b/code/modules/tgui/states/notcontained.dm
@@ -1,7 +1,11 @@
/**
* tgui state: notcontained_state
*
- * Checks that the user is not inside src_object, and then makes the default checks.
+ * Checks that the user is not inside src_object, and then makes the
+ * default checks.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(notcontained_state, /datum/ui_state/notcontained_state, new)
diff --git a/code/modules/tgui/states/observer.dm b/code/modules/tgui/states/observer.dm
index 86ad776b13..d105de1c0c 100644
--- a/code/modules/tgui/states/observer.dm
+++ b/code/modules/tgui/states/observer.dm
@@ -2,6 +2,9 @@
* tgui state: observer_state
*
* Checks that the user is an observer/ghost.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(observer_state, /datum/ui_state/observer_state, new)
diff --git a/code/modules/tgui/states/physical.dm b/code/modules/tgui/states/physical.dm
index 88c8a291aa..3073039d14 100644
--- a/code/modules/tgui/states/physical.dm
+++ b/code/modules/tgui/states/physical.dm
@@ -2,6 +2,9 @@
* tgui state: physical_state
*
* Short-circuits the default state to only check physical distance.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(physical_state, /datum/ui_state/physical, new)
diff --git a/code/modules/tgui/states/self.dm b/code/modules/tgui/states/self.dm
index b0c9500fbc..4b6e3b9fd9 100644
--- a/code/modules/tgui/states/self.dm
+++ b/code/modules/tgui/states/self.dm
@@ -2,6 +2,9 @@
* tgui state: self_state
*
* Only checks that the user and src_object are the same.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(self_state, /datum/ui_state/self_state, new)
diff --git a/code/modules/tgui/states/zlevel.dm b/code/modules/tgui/states/zlevel.dm
index 5e3ccfb7de..64ea2fa1c0 100644
--- a/code/modules/tgui/states/zlevel.dm
+++ b/code/modules/tgui/states/zlevel.dm
@@ -2,6 +2,9 @@
* tgui state: z_state
*
* Only checks that the Z-level of the user and src_object are the same.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
GLOBAL_DATUM_INIT(z_state, /datum/ui_state/z_state, new)
diff --git a/code/modules/tgui/subsystem.dm b/code/modules/tgui/subsystem.dm
deleted file mode 100644
index cbe94e2d7f..0000000000
--- a/code/modules/tgui/subsystem.dm
+++ /dev/null
@@ -1,247 +0,0 @@
-/**
- * tgui subsystem
- *
- * Contains all tgui state and subsystem code.
- */
-
-/**
- * public
- *
- * Get a open UI given a user, src_object, and ui_key and try to update it with data.
- *
- * required user mob The mob who opened/is using the UI.
- * required src_object datum The object/datum which owns the UI.
- * required ui_key string The ui_key of the UI.
- * optional ui datum/tgui The UI to be updated, if it exists.
- * optional force_open bool If the UI should be re-opened instead of updated.
- *
- * return datum/tgui The found UI.
- */
-/datum/controller/subsystem/tgui/proc/try_update_ui(mob/user, datum/src_object, ui_key, datum/tgui/ui, force_open = FALSE)
- if(isnull(ui)) // No UI was passed, so look for one.
- ui = get_open_ui(user, src_object, ui_key)
-
- if(!isnull(ui))
- var/data = src_object.ui_data(user) // Get data from the src_object.
- if(!force_open) // UI is already open; update it.
- ui.push_data(data)
- else // Re-open it anyways.
- ui.reinitialize(null, data)
- return ui // We found the UI, return it.
- else
- return null // We couldn't find a UI.
-
-/**
- * private
- *
- * Get a open UI given a user, src_object, and ui_key.
- *
- * required user mob The mob who opened/is using the UI.
- * required src_object datum The object/datum which owns the UI.
- * required ui_key string The ui_key of the UI.
- *
- * return datum/tgui The found UI.
- */
-/datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object, ui_key)
- var/src_object_key = "[REF(src_object)]"
- if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
- return null // No UIs open.
- else if(isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list))
- return null // No UIs open for this object.
-
- for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) // Find UIs for this object.
- if(ui.user == user) // Make sure we have the right user
- return ui
-
- return null // Couldn't find a UI!
-
-/**
- * private
- *
- * Update all UIs attached to src_object.
- *
- * required src_object datum The object/datum which owns the UIs.
- *
- * return int The number of UIs updated.
- */
-/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object)
- var/src_object_key = "[REF(src_object)]"
- if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
- return 0 // Couldn't find any UIs for this object.
-
- var/update_count = 0
- for(var/ui_key in open_uis[src_object_key])
- for(var/datum/tgui/ui in open_uis[src_object_key][ui_key])
- if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user)) // Check the UI is valid.
- ui.process(force = 1) // Update the UI.
- update_count++ // Count each UI we update.
- return update_count
-
-/**
- * private
- *
- * Close all UIs attached to src_object.
- *
- * required src_object datum The object/datum which owns the UIs.
- *
- * return int The number of UIs closed.
- */
-/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object)
- var/src_object_key = "[REF(src_object)]"
- if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
- return 0 // Couldn't find any UIs for this object.
-
- var/close_count = 0
- for(var/ui_key in open_uis[src_object_key])
- for(var/datum/tgui/ui in open_uis[src_object_key][ui_key])
- if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user)) // Check the UI is valid.
- ui.close() // Close the UI.
- close_count++ // Count each UI we close.
- return close_count
-
-/**
- * private
- *
- * Close *ALL* UIs
- *
- * return int The number of UIs closed.
- */
-/datum/controller/subsystem/tgui/proc/close_all_uis()
- var/close_count = 0
- for(var/src_object_key in open_uis)
- for(var/ui_key in open_uis[src_object_key])
- for(var/datum/tgui/ui in open_uis[src_object_key][ui_key])
- if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user)) // Check the UI is valid.
- ui.close() // Close the UI.
- close_count++ // Count each UI we close.
- return close_count
-
-/**
- * private
- *
- * Update all UIs belonging to a user.
- *
- * required user mob The mob who opened/is using the UI.
- * optional src_object datum If provided, only update UIs belonging this src_object.
- * optional ui_key string If provided, only update UIs with this UI key.
- *
- * return int The number of UIs updated.
- */
-/datum/controller/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object = null, ui_key = null)
- if(isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0)
- return 0 // Couldn't find any UIs for this user.
-
- var/update_count = 0
- for(var/datum/tgui/ui in user.open_uis)
- if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key))
- ui.process(force = 1) // Update the UI.
- update_count++ // Count each UI we upadte.
- return update_count
-
-/**
- * private
- *
- * Close all UIs belonging to a user.
- *
- * required user mob The mob who opened/is using the UI.
- * optional src_object datum If provided, only close UIs belonging this src_object.
- * optional ui_key string If provided, only close UIs with this UI key.
- *
- * return int The number of UIs closed.
- */
-/datum/controller/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object = null, ui_key = null)
- if(isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0)
- return 0 // Couldn't find any UIs for this user.
-
- var/close_count = 0
- for(var/datum/tgui/ui in user.open_uis)
- if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key))
- ui.close() // Close the UI.
- close_count++ // Count each UI we close.
- return close_count
-
-/**
- * private
- *
- * Add a UI to the list of open UIs.
- *
- * required ui datum/tgui The UI to be added.
- */
-/datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui)
- var/src_object_key = "[REF(ui.src_object)]"
- if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
- open_uis[src_object_key] = list(ui.ui_key = list()) // Make a list for the ui_key and src_object.
- else if(isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list))
- open_uis[src_object_key][ui.ui_key] = list() // Make a list for the ui_key.
-
- // Append the UI to all the lists.
- ui.user.open_uis |= ui
- var/list/uis = open_uis[src_object_key][ui.ui_key]
- uis |= ui
- processing_uis |= ui
-
-/**
- * private
- *
- * Remove a UI from the list of open UIs.
- *
- * required ui datum/tgui The UI to be removed.
- *
- * return bool If the UI was removed or not.
- */
-/datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui)
- var/src_object_key = "[REF(ui.src_object)]"
- if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
- return 0 // It wasn't open.
- else if(isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list))
- return 0 // It wasn't open.
-
- processing_uis.Remove(ui) // Remove it from the list of processing UIs.
- if(ui.user) // If the user exists, remove it from them too.
- ui.user.open_uis.Remove(ui)
- var/Ukey = ui.ui_key
- var/list/uis = open_uis[src_object_key][Ukey] // Remove it from the list of open UIs.
- uis.Remove(ui)
- if(!uis.len)
- var/list/uiobj = open_uis[src_object_key]
- uiobj.Remove(Ukey)
- if(!uiobj.len)
- open_uis.Remove(src_object_key)
-
- return 1 // Let the caller know we did it.
-
-/**
- * private
- *
- * Handle client logout, by closing all their UIs.
- *
- * required user mob The mob which logged out.
- *
- * return int The number of UIs closed.
- */
-/datum/controller/subsystem/tgui/proc/on_logout(mob/user)
- return close_user_uis(user)
-
-/**
- * private
- *
- * Handle clients switching mobs, by transferring their UIs.
- *
- * required user source The client's original mob.
- * required user target The client's new mob.
- *
- * return bool If the UIs were transferred.
- */
-/datum/controller/subsystem/tgui/proc/on_transfer(mob/source, mob/target)
- if(!source || isnull(source.open_uis) || !istype(source.open_uis, /list) || open_uis.len == 0)
- return 0 // The old mob had no open UIs.
-
- if(isnull(target.open_uis) || !istype(target.open_uis, /list))
- target.open_uis = list() // Create a list for the new mob if needed.
-
- for(var/datum/tgui/ui in source.open_uis)
- ui.user = target // Inform the UIs of their new owner.
- target.open_uis.Add(ui) // Transfer all the UIs.
-
- source.open_uis.Cut() // Clear the old list.
- return 1 // Let the caller know we did it.
diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm
index 7971a940d4..d0d5ff8ebb 100644
--- a/code/modules/tgui/tgui.dm
+++ b/code/modules/tgui/tgui.dm
@@ -1,7 +1,6 @@
/**
- * tgui
- *
- * /tg/station user interface library
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
*/
/**
@@ -14,34 +13,26 @@
var/datum/src_object
/// The title of te UI.
var/title
- /// The ui_key of the UI. This allows multiple UIs for one src_object.
- var/ui_key
/// The window_id for browse() and onclose().
- var/window_id
- /// The window width.
- var/width = 0
- /// The window height
- var/height = 0
+ var/datum/tgui_window/window
+ /// Key that is used for remembering the window geometry.
+ var/window_key
+ /// Deprecated: Window size.
+ var/window_size
/// The interface (template) to be used for this UI.
var/interface
/// Update the UI every MC tick.
var/autoupdate = TRUE
/// If the UI has been initialized yet.
var/initialized = FALSE
- /// The data (and datastructure) used to initialize the UI.
- var/list/initial_data
- /// The static data used to initialize the UI.
- var/list/initial_static_data
- /// Holder for the json string, that is sent during the initial update
- var/_initial_update
+ /// Time of opening the window.
+ var/opened_at
+ /// Stops further updates when close() was called.
+ var/closing = FALSE
/// The status/visibility of the UI.
var/status = UI_INTERACTIVE
/// Topic state used to determine status/interactability.
var/datum/ui_state/state = null
- /// The parent UI.
- var/datum/tgui/master_ui
- /// Children of this UI.
- var/list/datum/tgui/children = list()
/**
* public
@@ -50,38 +41,25 @@
*
* required user mob The mob who opened/is using the UI.
* required src_object datum The object or datum which owns the UI.
- * required ui_key string The ui_key of the UI.
* required interface string The interface used to render the UI.
* optional title string The title of the UI.
- * optional width int The window width.
- * optional height int The window height.
- * optional master_ui datum/tgui The parent UI.
- * optional state datum/ui_state The state used to determine status.
+ * optional ui_x int Deprecated: Window width.
+ * optional ui_y int Deprecated: Window height.
*
* return datum/tgui The requested UI.
*/
-/datum/tgui/New(mob/user, datum/src_object, ui_key, interface, title, width = 0, height = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+/datum/tgui/New(mob/user, datum/src_object, interface, title, ui_x, ui_y)
+ log_tgui(user, "new [interface] fancy [user.client.prefs.tgui_fancy]")
src.user = user
src.src_object = src_object
- src.ui_key = ui_key
- // DO NOT replace with \ref here. src_object could potentially be tagged
- src.window_id = "[REF(src_object)]-[ui_key]"
+ src.window_key = "[REF(src_object)]-main"
src.interface = interface
-
if(title)
- src.title = sanitize(title)
- if(width)
- src.width = width
- if(height)
- src.height = height
-
- src.master_ui = master_ui
- if(master_ui)
- master_ui.children += src
- src.state = state
-
- var/datum/asset/assets = get_asset_datum(/datum/asset/group/tgui)
- assets.send(user)
+ src.title = title
+ src.state = src_object.ui_state()
+ // Deprecated
+ if(ui_x && ui_y)
+ src.window_size = list(ui_x, ui_y)
/**
* public
@@ -90,85 +68,53 @@
*/
/datum/tgui/proc/open()
if(!user.client)
- return // Bail if there is no client.
-
- update_status(push = FALSE) // Update the window status.
+ return null
+ if(window)
+ return null
+ process_status()
if(status < UI_UPDATE)
- return // Bail if we're not supposed to open.
-
- // Build window options
- var/window_options = "can_minimize=0;auto_format=0;"
- // If we have a width and height, use them.
- if(width && height)
- window_options += "size=[width]x[height];"
- // Remove titlebar and resize handles for a fancy window
- if(user.client.prefs.tgui_fancy)
- window_options += "titlebar=0;can_resize=0;"
+ return null
+ window = SStgui.request_pooled_window(user)
+ if(!window)
+ return null
+ opened_at = world.time
+ window.acquire_lock(src)
+ if(!window.is_ready())
+ window.initialize(inline_assets = list(
+ get_asset_datum(/datum/asset/simple/tgui),
+ ))
else
- window_options += "titlebar=1;can_resize=1;"
-
- // Generate page html
- var/html
- html = SStgui.basehtml
- // Allow the src object to override the html if needed
- html = src_object.ui_base_html(html)
- // Replace template tokens with important UI data
- // NOTE: Intentional \ref usage; tgui datums can't/shouldn't
- // be tagged, so this is an effective unwrap
- html = replacetextEx(html, "\[tgui:ref]", "\ref[src]")
-
- // Open the window.
- user << browse(html, "window=[window_id];[window_options]")
-
- // Instruct the client to signal UI when the window is closed.
- // NOTE: Intentional \ref usage; tgui datums can't/shouldn't
- // be tagged, so this is an effective unwrap
- winset(user, window_id, "on-close=\"uiclose \ref[src]\"")
-
- // Pre-fetch initial state while browser is still loading in
- // another thread
- if(!initial_data) {
- initial_data = src_object.ui_data(user)
- }
- if(!initial_static_data) {
- initial_static_data = src_object.ui_static_data(user)
- }
- _initial_update = url_encode(get_json(initial_data, initial_static_data))
-
+ window.send_message("ping")
+ window.send_asset(get_asset_datum(/datum/asset/simple/fontawesome))
+ for(var/datum/asset/asset in src_object.ui_assets(user))
+ window.send_asset(asset)
+ window.send_message("update", get_payload(
+ with_data = TRUE,
+ with_static_data = TRUE))
SStgui.on_open(src)
/**
* public
*
- * Reinitialize the UI.
- * (Possibly with a new interface and/or data).
+ * Close the UI.
*
- * optional template string The name of the new interface.
- * optional data list The new initial data.
+ * optional can_be_suspended bool
*/
-/datum/tgui/proc/reinitialize(interface, list/data, list/static_data)
- if(interface)
- src.interface = interface
- if(data)
- initial_data = data
- if(static_data)
- initial_static_data = static_data
- open()
-
-/**
- * public
- *
- * Close the UI, and all its children.
- */
-/datum/tgui/proc/close()
- user << browse(null, "window=[window_id]") // Close the window.
- src_object.ui_close(user)
- SStgui.on_close(src)
- for(var/datum/tgui/child in children) // Loop through and close all children.
- child.close()
- children.Cut()
+/datum/tgui/proc/close(can_be_suspended = TRUE)
+ if(closing)
+ return
+ closing = TRUE
+ // If we don't have window_id, open proc did not have the opportunity
+ // to finish, therefore it's safe to skip this whole block.
+ if(window)
+ // Windows you want to keep are usually blue screens of death
+ // and we want to keep them around, to allow user to read
+ // the error message properly.
+ window.release_lock()
+ window.close(can_be_suspended)
+ src_object.ui_close(user)
+ SStgui.on_close(src)
state = null
- master_ui = null
qdel(src)
/**
@@ -176,187 +122,173 @@
*
* Enable/disable auto-updating of the UI.
*
- * required state bool Enable/disable auto-updating.
+ * required value bool Enable/disable auto-updating.
*/
-/datum/tgui/proc/set_autoupdate(state = TRUE)
- autoupdate = state
+/datum/tgui/proc/set_autoupdate(autoupdate)
+ src.autoupdate = autoupdate
+
+/**
+ * public
+ *
+ * Replace current ui.state with a new one.
+ *
+ * required state datum/ui_state/state Next state
+ */
+/datum/tgui/proc/set_state(datum/ui_state/state)
+ src.state = state
+
+/**
+ * public
+ *
+ * Makes an asset available to use in tgui.
+ *
+ * required asset datum/asset
+ */
+/datum/tgui/proc/send_asset(datum/asset/asset)
+ if(!window)
+ CRASH("send_asset() can only be called after open().")
+ window.send_asset(asset)
+
+/**
+ * public
+ *
+ * Send a full update to the client (includes static data).
+ *
+ * optional custom_data list Custom data to send instead of ui_data.
+ * optional force bool Send an update even if UI is not interactive.
+ */
+/datum/tgui/proc/send_full_update(custom_data, force)
+ if(!user.client || !initialized || closing)
+ return
+ var/should_update_data = force || status >= UI_UPDATE
+ window.send_message("update", get_payload(
+ custom_data,
+ with_data = should_update_data,
+ with_static_data = TRUE))
+
+/**
+ * public
+ *
+ * Send a partial update to the client (excludes static data).
+ *
+ * optional custom_data list Custom data to send instead of ui_data.
+ * optional force bool Send an update even if UI is not interactive.
+ */
+/datum/tgui/proc/send_update(custom_data, force)
+ if(!user.client || !initialized || closing)
+ return
+ var/should_update_data = force || status >= UI_UPDATE
+ window.send_message("update", get_payload(
+ custom_data,
+ with_data = should_update_data))
/**
* private
*
* Package the data to send to the UI, as JSON.
- * This includes the UI data and config_data.
*
- * return string The packaged JSON.
+ * return list
*/
-/datum/tgui/proc/get_json(list/data, list/static_data)
+/datum/tgui/proc/get_payload(custom_data, with_data, with_static_data)
var/list/json_data = list()
-
json_data["config"] = list(
"title" = title,
"status" = status,
"interface" = interface,
- "fancy" = user.client.prefs.tgui_fancy,
- "locked" = user.client.prefs.tgui_lock,
- "observer" = isobserver(user),
- "window" = window_id,
- // NOTE: Intentional \ref usage; tgui datums can't/shouldn't
- // be tagged, so this is an effective unwrap
- "ref" = "\ref[src]"
+ "window" = list(
+ "key" = window_key,
+ "size" = window_size,
+ "fancy" = user.client.prefs.tgui_fancy,
+ "locked" = user.client.prefs.tgui_lock
+ ),
+ "user" = list(
+ "name" = "[user]",
+ "ckey" = "[user.ckey]",
+ "observer" = isobserver(user)
+ )
)
-
- if(!isnull(data))
+ var/data = custom_data || with_data && src_object.ui_data(user)
+ if(data)
json_data["data"] = data
- if(!isnull(static_data))
+ var/static_data = with_static_data && src_object.ui_static_data(user)
+ if(static_data)
json_data["static_data"] = static_data
-
- // Send shared states
if(src_object.tgui_shared_states)
json_data["shared"] = src_object.tgui_shared_states
-
- // Generate the JSON.
- var/json = json_encode(json_data)
- // Strip #255/improper.
- json = replacetext(json, "\proper", "")
- json = replacetext(json, "\improper", "")
- return json
+ return json_data
/**
* private
*
- * Handle clicks from the UI.
- * Call the src_object's ui_act() if status is UI_INTERACTIVE.
- * If the src_object's ui_act() returns 1, update all UIs attacked to it.
- */
-/datum/tgui/Topic(href, href_list)
- if(user != usr)
- return // Something is not right here.
-
- var/action = href_list["action"]
- var/params = href_list; params -= "action"
-
- switch(action)
- if("tgui:initialize")
- user << output(_initial_update, "[window_id].browser:update")
- initialized = TRUE
- if("tgui:setSharedState")
- // Update the window state.
- update_status(push = FALSE)
- // Bail if UI is not interactive or usr calling Topic
- // is not the UI user.
- if(status != UI_INTERACTIVE)
- return
- var/key = params["key"]
- var/value = params["value"]
- if(!src_object.tgui_shared_states)
- src_object.tgui_shared_states = list()
- src_object.tgui_shared_states[key] = value
- SStgui.update_uis(src_object)
- if("tgui:setFancy")
- var/value = text2num(params["value"])
- user.client.prefs.tgui_fancy = value
- if("tgui:log")
- // Force window to show frills on fatal errors
- if(params["fatal"])
- winset(user, window_id, "titlebar=1;can-resize=1;size=600x600")
- log_message(params["log"])
- if("tgui:link")
- user << link(params["url"])
- else
- // Update the window state.
- update_status(push = FALSE)
- // Call ui_act() on the src_object.
- if(src_object.ui_act(action, params, src, state))
- // Update if the object requested it.
- SStgui.update_uis(src_object)
-
-/**
- * private
- *
- * Update the UI.
- * Only updates the data if update is true, otherwise only updates the status.
- *
- * optional force bool If the UI should be forced to update.
+ * Run an update cycle for this UI. Called internally by SStgui
+ * every second or so.
*/
/datum/tgui/process(force = FALSE)
+ if(closing)
+ return
var/datum/host = src_object.ui_host(user)
- if(!src_object || !host || !user) // If the object or user died (or something else), abort.
+ // If the object or user died (or something else), abort.
+ if(!src_object || !host || !user || !window)
+ close(can_be_suspended = FALSE)
+ return
+ // Validate ping
+ if(!initialized && world.time - opened_at > TGUI_PING_TIMEOUT)
+ log_tgui(user, \
+ "Error: Zombie window detected, killing it with fire.\n" \
+ + "window_id: [window.id]\n" \
+ + "opened_at: [opened_at]\n" \
+ + "world.time: [world.time]")
+ close(can_be_suspended = FALSE)
+ return
+ // Update through a normal call to ui_interact
+ if(status != UI_DISABLED && (autoupdate || force))
+ src_object.ui_interact(user, src)
+ return
+ // Update status only
+ var/needs_update = process_status()
+ if(status <= UI_CLOSE)
close()
return
-
- if(status && (force || autoupdate))
- update() // Update the UI if the status and update settings allow it.
- else
- update_status(push = TRUE) // Otherwise only update status.
+ if(needs_update)
+ window.send_message("update", get_payload())
/**
* private
*
- * Push data to an already open UI.
- *
- * required data list The data to send.
- * optional force bool If the update should be sent regardless of state.
+ * Updates the status, and returns TRUE if status has changed.
*/
-/datum/tgui/proc/push_data(data, static_data, force = FALSE)
- // Update the window state.
- update_status(push = FALSE)
- // Cannot update UI if it is not set up yet.
- if(!initialized)
- return
- // Cannot update UI, we have no visibility.
- if(status <= UI_DISABLED && !force)
- return
- // Send the new JSON to the update() Javascript function.
- user << output(
- url_encode(get_json(data, static_data)),
- "[window_id].browser:update")
+/datum/tgui/proc/process_status()
+ var/prev_status = status
+ status = src_object.ui_status(user, state)
+ return prev_status != status
/**
* private
*
- * Updates the UI by interacting with the src_object again, which will hopefully
- * call try_ui_update on it.
- *
- * optional force_open bool If force_open should be passed to ui_interact.
+ * Callback for handling incoming tgui messages.
*/
-/datum/tgui/proc/update(force_open = FALSE)
- src_object.ui_interact(user, ui_key, src, force_open, master_ui, state)
-
-/**
- * private
- *
- * Update the status/visibility of the UI for its user.
- *
- * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED).
- */
-/datum/tgui/proc/update_status(push = FALSE)
- var/status = src_object.ui_status(user, state)
- if(master_ui)
- status = min(status, master_ui.status)
- set_status(status, push)
- if(status == UI_CLOSE)
- close()
-
-/**
- * private
- *
- * Set the status/visibility of the UI.
- *
- * required status int The status to set (UI_CLOSE/UI_DISABLED/UI_UPDATE/UI_INTERACTIVE).
- * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED).
- */
-/datum/tgui/proc/set_status(status, push = FALSE)
- // Only update if status has changed.
- if(src.status != status)
- if(src.status == UI_DISABLED)
- src.status = status
- if(push)
- update()
- else
- src.status = status
- // Update if the UI just because disabled, or a push is requested.
- if(status == UI_DISABLED || push)
- push_data(null, force = TRUE)
-
-/datum/tgui/proc/log_message(message)
- log_tgui("[user] ([user.ckey]) using \"[title]\":\n[message]")
+/datum/tgui/proc/on_message(type, list/payload, list/href_list)
+ // Pass act type messages to ui_act
+ if(type && copytext(type, 1, 5) == "act/")
+ process_status()
+ if(src_object.ui_act(copytext(type, 5), payload, src, state))
+ SStgui.update_uis(src_object)
+ return FALSE
+ switch(type)
+ if("ready")
+ initialized = TRUE
+ if("pingReply")
+ initialized = TRUE
+ if("suspend")
+ close(can_be_suspended = TRUE)
+ if("close")
+ close(can_be_suspended = FALSE)
+ if("log")
+ if(href_list["fatal"])
+ close(can_be_suspended = FALSE)
+ if("setSharedState")
+ if(status != UI_INTERACTIVE)
+ return
+ LAZYINITLIST(src_object.tgui_shared_states)
+ src_object.tgui_shared_states[href_list["key"]] = href_list["value"]
+ SStgui.update_uis(src_object)
diff --git a/code/modules/tgui/tgui_window.dm b/code/modules/tgui/tgui_window.dm
new file mode 100644
index 0000000000..3f271163c9
--- /dev/null
+++ b/code/modules/tgui/tgui_window.dm
@@ -0,0 +1,238 @@
+/**
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
+/datum/tgui_window
+ var/id
+ var/client/client
+ var/pooled
+ var/pool_index
+ var/status = TGUI_WINDOW_CLOSED
+ var/locked = FALSE
+ var/datum/tgui/locked_by
+ var/fatally_errored = FALSE
+ var/message_queue
+ var/sent_assets = list()
+
+/**
+ * public
+ *
+ * Create a new tgui window.
+ *
+ * required client /client
+ * required id string A unique window identifier.
+ */
+/datum/tgui_window/New(client/client, id, pooled = FALSE)
+ src.id = id
+ src.client = client
+ src.pooled = pooled
+ if(pooled)
+ client.tgui_windows[id] = src
+ src.pool_index = TGUI_WINDOW_INDEX(id)
+
+/**
+ * public
+ *
+ * Initializes the window with a fresh page. Puts window into the "loading"
+ * state. You can begin sending messages right after initializing. Messages
+ * will be put into the queue until the window finishes loading.
+ *
+ * optional inline_assets list List of assets to inline into the html.
+ */
+/datum/tgui_window/proc/initialize(inline_assets = list())
+ log_tgui(client, "[id]/initialize")
+ if(!client)
+ return
+ status = TGUI_WINDOW_LOADING
+ fatally_errored = FALSE
+ message_queue = null
+ // Build window options
+ var/options = "file=[id].html;can_minimize=0;auto_format=0;"
+ // Remove titlebar and resize handles for a fancy window
+ if(client.prefs.tgui_fancy)
+ options += "titlebar=0;can_resize=0;"
+ else
+ options += "titlebar=1;can_resize=1;"
+ // Generate page html
+ var/html = SStgui.basehtml
+ html = replacetextEx(html, "\[tgui:windowId]", id)
+ // Process inline assets
+ var/inline_styles = ""
+ var/inline_scripts = ""
+ for(var/datum/asset/asset in inline_assets)
+ var/mappings = asset.get_url_mappings()
+ for(var/name in mappings)
+ var/url = mappings[name]
+ // Not urlencoding since asset strings are considered safe
+ if(copytext(name, -4) == ".css")
+ inline_styles += " \n"
+ else if(copytext(name, -3) == ".js")
+ inline_scripts += "\n"
+ asset.send()
+ html = replacetextEx(html, "\n", inline_styles)
+ html = replacetextEx(html, "\n", inline_scripts)
+ // Open the window
+ client << browse(html, "window=[id];[options]")
+ // Instruct the client to signal UI when the window is closed.
+ winset(client, id, "on-close=\"uiclose [id]\"")
+
+/**
+ * public
+ *
+ * Checks if the window is ready to receive data.
+ *
+ * return bool
+ */
+/datum/tgui_window/proc/is_ready()
+ return status == TGUI_WINDOW_READY
+
+/**
+ * public
+ *
+ * Checks if the window can be sanely suspended.
+ *
+ * return bool
+ */
+/datum/tgui_window/proc/can_be_suspended()
+ return !fatally_errored \
+ && pooled \
+ && pool_index > 0 \
+ && pool_index <= TGUI_WINDOW_SOFT_LIMIT \
+ && status == TGUI_WINDOW_READY
+
+/**
+ * public
+ *
+ * Acquire the window lock. Pool will not be able to provide this window
+ * to other UIs for the duration of the lock.
+ *
+ * Can be given an optional tgui datum, which will hook its on_message
+ * callback into the message stream.
+ *
+ * optional ui /datum/tgui
+ */
+/datum/tgui_window/proc/acquire_lock(datum/tgui/ui)
+ locked = TRUE
+ locked_by = ui
+
+/**
+ * Release the window lock.
+ */
+/datum/tgui_window/proc/release_lock()
+ // Clean up assets sent by tgui datum which requested the lock
+ if(locked)
+ sent_assets = list()
+ locked = FALSE
+ locked_by = null
+
+/**
+ * public
+ *
+ * Close the UI.
+ *
+ * optional can_be_suspended bool
+ */
+/datum/tgui_window/proc/close(can_be_suspended = TRUE)
+ if(!client)
+ return
+ if(can_be_suspended && can_be_suspended())
+ log_tgui(client, "[id]/close: suspending")
+ status = TGUI_WINDOW_READY
+ send_message("suspend")
+ return
+ log_tgui(client, "[id]/close")
+ release_lock()
+ status = TGUI_WINDOW_CLOSED
+ message_queue = null
+ // Do not close the window to give user some time
+ // to read the error message.
+ if(!fatally_errored)
+ client << browse(null, "window=[id]")
+
+/**
+ * public
+ *
+ * Sends a message to tgui window.
+ *
+ * required type string Message type
+ * required payload list Message payload
+ * optional force bool Send regardless of the ready status.
+ */
+/datum/tgui_window/proc/send_message(type, list/payload, force)
+ if(!client)
+ return
+ var/message = json_encode(list(
+ "type" = type,
+ "payload" = payload,
+ ))
+ // Strip #255/improper.
+ message = replacetext(message, "\proper", "")
+ message = replacetext(message, "\improper", "")
+ // Pack for sending via output()
+ message = url_encode(message)
+ // Place into queue if window is still loading
+ if(!force && status != TGUI_WINDOW_READY)
+ if(!message_queue)
+ message_queue = list()
+ message_queue += list(message)
+ return
+ client << output(message, "[id].browser:update")
+
+/**
+ * public
+ *
+ * Makes an asset available to use in tgui.
+ *
+ * required asset datum/asset
+ */
+/datum/tgui_window/proc/send_asset(datum/asset/asset)
+ if(!client || !asset)
+ return
+ if(istype(asset, /datum/asset/spritesheet))
+ var/datum/asset/spritesheet/spritesheet = asset
+ send_message("asset/stylesheet", spritesheet.css_filename())
+ send_message("asset/mappings", asset.get_url_mappings())
+ sent_assets += list(asset)
+ asset.send(client)
+
+/**
+ * private
+ *
+ * Sends queued messages if the queue wasn't empty.
+ */
+/datum/tgui_window/proc/flush_message_queue()
+ if(!client || !message_queue)
+ return
+ for(var/message in message_queue)
+ client << output(message, "[id].browser:update")
+ message_queue = null
+
+/**
+ * private
+ *
+ * Callback for handling incoming tgui messages.
+ */
+/datum/tgui_window/proc/on_message(type, list/payload, list/href_list)
+ switch(type)
+ if("ready")
+ // Status can be READY if user has refreshed the window.
+ if(status == TGUI_WINDOW_READY)
+ // Resend the assets
+ for(var/asset in sent_assets)
+ send_asset(asset)
+ status = TGUI_WINDOW_READY
+ if("log")
+ if(href_list["fatal"])
+ fatally_errored = TRUE
+ // Pass message to UI that requested the lock
+ if(locked && locked_by)
+ locked_by.on_message(type, payload, href_list)
+ flush_message_queue()
+ return
+ // If not locked, handle these message types
+ switch(type)
+ if("suspend")
+ close(can_be_suspended = TRUE)
+ if("close")
+ close(can_be_suspended = FALSE)
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index a42fe97cb4..a115300085 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -501,10 +501,10 @@ GLOBAL_LIST_EMPTY(vending_products)
C.bleed(150)
var/obj/item/bodypart/l_leg/l = C.get_bodypart(BODY_ZONE_L_LEG)
if(l)
- l.receive_damage(brute=200)
+ l.receive_damage(brute=200, updating_health=TRUE)
var/obj/item/bodypart/r_leg/r = C.get_bodypart(BODY_ZONE_R_LEG)
if(r)
- r.receive_damage(brute=200)
+ r.receive_damage(brute=200, updating_health=TRUE)
if(l || r)
C.visible_message("[C]'s legs shatter with a sickening crunch! ", \
"Your legs shatter with a sickening crunch! ")
@@ -672,21 +672,21 @@ GLOBAL_LIST_EMPTY(vending_products)
return
return ..()
-/obj/machinery/vending/ui_base_html(html)
- var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/vending)
- . = replacetext(html, "", assets.css_tag())
+/obj/machinery/vending/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/vending),
+ )
-/obj/machinery/vending/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/vending/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/vending)
- assets.send(user)
- ui = new(user, src, ui_key, "Vending", ui_key, 450, 600, master_ui, state)
+ ui = new(user, src, "Vending")
ui.open()
/obj/machinery/vending/ui_static_data(mob/user)
. = list()
.["onstation"] = onstation
+ .["department"] = payment_department
.["product_records"] = list()
for (var/datum/data/vending_product/R in product_records)
var/list/data = list(
@@ -713,7 +713,7 @@ GLOBAL_LIST_EMPTY(vending_products)
var/list/data = list(
path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"),
name = R.name,
- price = R.custom_price || default_price,
+ price = R.custom_premium_price || extra_price, //may cause breakage. please note
max_amount = R.max_amount,
ref = REF(R),
premium = TRUE
@@ -722,28 +722,24 @@ GLOBAL_LIST_EMPTY(vending_products)
/obj/machinery/vending/ui_data(mob/user)
. = list()
- var/obj/item/card/id/C = user.get_idcard(TRUE)
- .["cost_mult"] = 1
- .["cost_text"] = ""
- if(C && C.registered_account)
- .["user"] = list()
- .["user"]["name"] = C.registered_account.account_holder
- .["user"]["cash"] = C.registered_account.account_balance
- if(C.registered_account.account_job)
- .["user"]["job"] = C.registered_account.account_job.title
- else
- .["user"]["job"] = "No Job"
- var/cost_mult = get_best_discount(C)
- if(cost_mult != 1)
- .["cost_mult"] = cost_mult
- if(cost_mult < 1)
- .["cost_text"] = " ([(1 - cost_mult) * 100]% OFF)"
+ var/mob/living/carbon/human/H
+ var/obj/item/card/id/C
+ if(ishuman(user))
+ H = user
+ C = H.get_idcard(TRUE)
+ if(C?.registered_account)
+ .["user"] = list()
+ .["user"]["name"] = C.registered_account.account_holder
+ .["user"]["cash"] = C.registered_account.account_balance
+ if(C.registered_account.account_job)
+ .["user"]["job"] = C.registered_account.account_job.title
+ .["user"]["department"] = C.registered_account.account_job.paycheck_department
else
- .["cost_text"] = " ([(cost_mult - 1) * 100]% EXTRA)"
+ .["user"]["job"] = "No Job"
+ .["user"]["department"] = "No Department"
.["stock"] = list()
for (var/datum/data/vending_product/R in product_records + coin_records + hidden_records)
.["stock"][R.name] = R.amount
- .
.["extended_inventory"] = extended_inventory
/obj/machinery/vending/ui_act(action, params)
@@ -766,7 +762,9 @@ GLOBAL_LIST_EMPTY(vending_products)
if(!R || !istype(R) || !R.product_path)
vend_ready = TRUE
return
- var/price_to_use = R.custom_price || default_price
+ var/price_to_use = default_price
+ if(R.custom_price)
+ price_to_use = R.custom_price
if(R in hidden_records)
if(!extended_inventory)
vend_ready = TRUE
@@ -780,8 +778,10 @@ GLOBAL_LIST_EMPTY(vending_products)
flick(icon_deny,src)
vend_ready = TRUE
return
- if(onstation && price_to_use >= 0)
- var/obj/item/card/id/C = usr.get_idcard(TRUE)
+ if(onstation && ishuman(usr))
+ var/mob/living/carbon/human/H = usr
+ var/obj/item/card/id/C = H.get_idcard(TRUE)
+
if(!C)
say("No card found.")
flick(icon_deny,src)
@@ -792,11 +792,20 @@ GLOBAL_LIST_EMPTY(vending_products)
flick(icon_deny,src)
vend_ready = TRUE
return
+ // else if(age_restrictions && R.age_restricted && (!C.registered_age || C.registered_age < AGE_MINOR))
+ // say("You are not of legal age to purchase [R.name].")
+ // if(!(usr in GLOB.narcd_underages))
+ // Radio.set_frequency(FREQ_SECURITY)
+ // Radio.talk_into(src, "SECURITY ALERT: Underaged crewmember [H] recorded attempting to purchase [R.name] in [get_area(src)]. Please watch for substance abuse.", FREQ_SECURITY)
+ // GLOB.narcd_underages += H
+ // flick(icon_deny,src)
+ // vend_ready = TRUE
+ // return
var/datum/bank_account/account = C.registered_account
- if(coin_records.Find(R))
- price_to_use = R.custom_premium_price || extra_price
- else if(!hidden_records.Find(R))
- price_to_use = round(price_to_use * get_best_discount(C))
+ if(account.account_job && account.account_job.paycheck_department == payment_department)
+ price_to_use = 0
+ if(coin_records.Find(R) || hidden_records.Find(R))
+ price_to_use = R.custom_premium_price ? R.custom_premium_price : extra_price
if(price_to_use && !account.adjust_money(-price_to_use))
say("You do not possess the funds to purchase [R.name].")
flick(icon_deny,src)
@@ -805,6 +814,8 @@ GLOBAL_LIST_EMPTY(vending_products)
var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
if(D)
D.adjust_money(price_to_use)
+ SSblackbox.record_feedback("amount", "vending_spent", price_to_use)
+ //log_econ("[price_to_use] credits were inserted into [src] by [D.account_holder] to buy [R].")
if(last_shopper != usr || purchase_message_cooldown < world.time)
say("Thank you for shopping with [src]!")
purchase_message_cooldown = world.time + 5 SECONDS
diff --git a/icons/effects/supplypod_down_target.dmi b/icons/effects/mouse_pointers/supplypod_down_target.dmi
similarity index 100%
rename from icons/effects/supplypod_down_target.dmi
rename to icons/effects/mouse_pointers/supplypod_down_target.dmi
diff --git a/icons/effects/mouse_pointers/supplypod_pickturf.dmi b/icons/effects/mouse_pointers/supplypod_pickturf.dmi
new file mode 100644
index 0000000000..3ca1131e1a
Binary files /dev/null and b/icons/effects/mouse_pointers/supplypod_pickturf.dmi differ
diff --git a/icons/effects/mouse_pointers/supplypod_pickturf_down.dmi b/icons/effects/mouse_pointers/supplypod_pickturf_down.dmi
new file mode 100644
index 0000000000..113fe47540
Binary files /dev/null and b/icons/effects/mouse_pointers/supplypod_pickturf_down.dmi differ
diff --git a/icons/effects/supplypod_target.dmi b/icons/effects/mouse_pointers/supplypod_target.dmi
similarity index 100%
rename from icons/effects/supplypod_target.dmi
rename to icons/effects/mouse_pointers/supplypod_target.dmi
diff --git a/icons/obj/modular_console.dmi b/icons/obj/modular_console.dmi
index cdcf6f1bd5..5d3cd0312c 100644
Binary files a/icons/obj/modular_console.dmi and b/icons/obj/modular_console.dmi differ
diff --git a/icons/obj/modular_laptop.dmi b/icons/obj/modular_laptop.dmi
index 7cf5a77621..fd24d27a97 100644
Binary files a/icons/obj/modular_laptop.dmi and b/icons/obj/modular_laptop.dmi differ
diff --git a/icons/obj/modular_tablet.dmi b/icons/obj/modular_tablet.dmi
index 2f9a0559ae..32edb57475 100644
Binary files a/icons/obj/modular_tablet.dmi and b/icons/obj/modular_tablet.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index c34bd2a1d8..9306ae0ef1 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -47,6 +47,7 @@
#include "code\__DEFINES\dynamic.dm"
#include "code\__DEFINES\economy.dm"
#include "code\__DEFINES\events.dm"
+#include "code\__DEFINES\exosuit_fabs.dm"
#include "code\__DEFINES\exports.dm"
#include "code\__DEFINES\fantasy_affixes.dm"
#include "code\__DEFINES\food.dm"
@@ -430,6 +431,7 @@
#include "code\datums\components\field_of_vision.dm"
#include "code\datums\components\footstep.dm"
#include "code\datums\components\fried.dm"
+#include "code\datums\components\gps.dm"
#include "code\datums\components\identification.dm"
#include "code\datums\components\igniter.dm"
#include "code\datums\components\infective.dm"
@@ -1407,6 +1409,7 @@
#include "code\modules\antagonists\abductor\equipment\abduction_outfits.dm"
#include "code\modules\antagonists\abductor\equipment\abduction_surgery.dm"
#include "code\modules\antagonists\abductor\equipment\gland.dm"
+#include "code\modules\antagonists\abductor\equipment\orderable_gear.dm"
#include "code\modules\antagonists\abductor\equipment\glands\access.dm"
#include "code\modules\antagonists\abductor\equipment\glands\blood.dm"
#include "code\modules\antagonists\abductor\equipment\glands\chem.dm"
@@ -2721,6 +2724,7 @@
#include "code\modules\mob\living\simple_animal\slime\slime_mobility.dm"
#include "code\modules\mob\living\simple_animal\slime\subtypes.dm"
#include "code\modules\modular_computers\laptop_vendor.dm"
+#include "code\modules\modular_computers\computers\_modular_computer_shared.dm"
#include "code\modules\modular_computers\computers\item\computer.dm"
#include "code\modules\modular_computers\computers\item\computer_components.dm"
#include "code\modules\modular_computers\computers\item\computer_damage.dm"
@@ -2741,13 +2745,16 @@
#include "code\modules\modular_computers\file_system\programs\airestorer.dm"
#include "code\modules\modular_computers\file_system\programs\alarm.dm"
#include "code\modules\modular_computers\file_system\programs\arcade.dm"
+#include "code\modules\modular_computers\file_system\programs\atmosscan.dm"
#include "code\modules\modular_computers\file_system\programs\card.dm"
+#include "code\modules\modular_computers\file_system\programs\cargobounty.dm"
#include "code\modules\modular_computers\file_system\programs\configurator.dm"
+#include "code\modules\modular_computers\file_system\programs\crewmanifest.dm"
#include "code\modules\modular_computers\file_system\programs\file_browser.dm"
+#include "code\modules\modular_computers\file_system\programs\jobmanagement.dm"
#include "code\modules\modular_computers\file_system\programs\ntdownloader.dm"
#include "code\modules\modular_computers\file_system\programs\ntmonitor.dm"
#include "code\modules\modular_computers\file_system\programs\ntnrc_client.dm"
-#include "code\modules\modular_computers\file_system\programs\nttransfer.dm"
#include "code\modules\modular_computers\file_system\programs\powermonitor.dm"
#include "code\modules\modular_computers\file_system\programs\radar.dm"
#include "code\modules\modular_computers\file_system\programs\robocontrol.dm"
@@ -3080,6 +3087,7 @@
#include "code\modules\research\research_disk.dm"
#include "code\modules\research\server.dm"
#include "code\modules\research\stock_parts.dm"
+#include "code\modules\research\anomaly\anomaly_core.dm"
#include "code\modules\research\designs\AI_module_designs.dm"
#include "code\modules\research\designs\autobotter_designs.dm"
#include "code\modules\research\designs\autoylathe_designs.dm"
@@ -3362,15 +3370,14 @@
#include "code\modules\tgs\includes.dm"
#include "code\modules\tgui\external.dm"
#include "code\modules\tgui\states.dm"
-#include "code\modules\tgui\subsystem.dm"
#include "code\modules\tgui\tgui.dm"
+#include "code\modules\tgui\tgui_window.dm"
#include "code\modules\tgui\states\admin.dm"
#include "code\modules\tgui\states\always.dm"
#include "code\modules\tgui\states\conscious.dm"
#include "code\modules\tgui\states\contained.dm"
#include "code\modules\tgui\states\deep_inventory.dm"
#include "code\modules\tgui\states\default.dm"
-#include "code\modules\tgui\states\default_contained.dm"
#include "code\modules\tgui\states\hands.dm"
#include "code\modules\tgui\states\human_adjacent.dm"
#include "code\modules\tgui\states\inventory.dm"
diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml
index 67e74085c7..9fd4db9fd2 100644
--- a/tgui/.eslintrc.yml
+++ b/tgui/.eslintrc.yml
@@ -8,6 +8,8 @@ env:
es6: true
browser: true
node: true
+globals:
+ Byond: readonly
plugins:
- react
settings:
@@ -388,7 +390,7 @@ rules:
## Enforce a particular style for multiline comments
# multiline-comment-style: error
## Enforce newlines between operands of ternary expressions
- multiline-ternary: [error, always-multiline]
+ # multiline-ternary: [error, always-multiline]
## Require constructor names to begin with a capital letter
# new-cap: error
## Enforce or disallow parentheses when invoking a constructor with no
diff --git a/tgui/.gitattributes b/tgui/.gitattributes
index 0016cc3bf6..9382416e69 100644
--- a/tgui/.gitattributes
+++ b/tgui/.gitattributes
@@ -2,9 +2,18 @@
## Enforce text mode and LF line breaks
*.js text eol=lf
+*.jsx text eol=lf
+*.ts text eol=lf
+*.tsx text eol=lf
*.css text eol=lf
+*.scss text eol=lf
*.html text eol=lf
*.json text eol=lf
+*.yml text eol=lf
+*.md text eol=lf
+*.bat text eol=lf
+yarn.lock text eol=lf
+bin/tgui text eol=lf
## Treat bundles as binary and ignore them during conflicts
*.bundle.* binary merge=tgui-merge-bundle
diff --git a/tgui/README.md b/tgui/README.md
index 5ddeb18fdd..9eab0196de 100644
--- a/tgui/README.md
+++ b/tgui/README.md
@@ -67,8 +67,9 @@ Run one of the following:
game as you code it. Very useful, highly recommended.
- In order to use it, you should start the game server first, connect to it
and wait until the world has been properly loaded and you are no longer
- in the lobby. Start tgui dev server. You'll know that it's hooked correctly
- if data gets dumped to the log when tgui windows are opened.
+ in the lobby. Start tgui dev server, and once it has finished building,
+ press F5 on any tgui window. You'll know that it's hooked correctly if
+ you see a green bug icon in titlebar and data gets dumped to the console.
- `bin/tgui --dev --reload` - reload byond cache once.
- `bin/tgui --dev --debug` - run server with debug logging enabled.
- `bin/tgui --dev --no-hot` - disable hot module replacement (helps when
@@ -134,11 +135,11 @@ logs and time spent on rendering. Use this information to optimize your
code, and try to keep re-renders below 16ms.
**Kitchen Sink.**
-Press `Ctrl+Alt+=` to open the KitchenSink interface. This interface is a
+Press `F12` to open the KitchenSink interface. This interface is a
playground to test various tgui components.
**Layout Debugger.**
-Press `Ctrl+Alt+-` to toggle the *layout debugger*. It will show outlines of
+Press `F11` to toggle the *layout debugger*. It will show outlines of
all tgui elements, which makes it easy to understand how everything comes
together, and can reveal certain layout bugs which are not normally visible.
@@ -180,8 +181,11 @@ See: [Component Reference](docs/component-reference.md).
## License
-All code is licensed with the parent license of *tgstation*, **AGPL-3.0**.
+Source code is covered by /tg/station's parent license - **AGPL-3.0**
+(see the main [README](../README.md)), unless otherwise indicated.
-See the main [README](../README.md) for more details.
+Some files are annotated with a copyright header, which explicitly states
+the copyright holder and license of the file. Most of the core tgui
+source code is available under the **MIT** license.
The Authors retain all copyright to their respective work here submitted.
diff --git a/tgui/bin/tgui b/tgui/bin/tgui
index eb1f200b31..97a86159e6 100755
--- a/tgui/bin/tgui
+++ b/tgui/bin/tgui
@@ -52,6 +52,7 @@ task-dev-server() {
task-eslint() {
cd "${base_dir}"
eslint ./packages "${@}"
+ echo "tgui: eslint check passed"
}
## Mr. Proper
@@ -153,6 +154,13 @@ if [[ ${1} == '--lint-harder' ]]; then
exit 0
fi
+if [[ ${1} == '--fix' ]]; then
+ shift 1
+ task-install
+ task-eslint --fix "${@}"
+ exit 0
+fi
+
## Analyze the bundle
if [[ ${1} == '--analyze' ]]; then
task-install
diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md
index a2a0066a70..ff1b4e7dfd 100644
--- a/tgui/docs/component-reference.md
+++ b/tgui/docs/component-reference.md
@@ -30,6 +30,8 @@ Make sure to add new items to this list if you document new components.
- [`Icon`](#icon)
- [`Input`](#input)
- [`Knob`](#knob)
+ - [`LabeledControls`](#labeledcontrols)
+ - [`LabeledControls.Item`](#labeledcontrolsitem)
- [`LabeledList`](#labeledlist)
- [`LabeledList.Item`](#labeledlistitem)
- [`LabeledList.Divider`](#labeledlistdivider)
@@ -239,7 +241,7 @@ A ghetto checkbox, made entirely using existing Button API.
### `Button.Confirm`
-A button with a an extra confirmation step, using native button component.
+A button with an extra confirmation step, using native button component.
**Props:**
@@ -273,11 +275,11 @@ interface.
Example (button):
-```
+```jsx
@@ -285,11 +287,10 @@ Example (button):
Example (map):
-```
+```jsx
```
@@ -584,6 +585,24 @@ the input, or successfully enter a number.
- `onDrag: (e, value) => void` - An event, which fires about every 500ms
when you drag the input up and down, on release and on manual editing.
+### `LabeledControls`
+
+LabeledControls is a horizontal grid, that is designed to hold various
+controls, like [Knobs](#knob) or small [Buttons](#button). Every item in
+this grid is labeled at the bottom.
+
+**Props:**
+
+- See inherited props: [Box](#box)
+- `children: LabeledControls.Item` - Items to render.
+
+### `LabeledControls.Item`
+
+**Props:**
+
+- See inherited props: [Box](#box)
+- `label: string` - Item label.
+
### `LabeledList`
LabeledList is a continuous, vertical list of text and other content, where
@@ -962,6 +981,7 @@ Example:
- `className: string` - Applies a CSS class to the element.
- `theme: string` - A name of the theme.
- For a list of themes, see `packages/tgui/styles/themes`.
+- `title: string` - Window title.
- `resizable: boolean` - Controls resizability of the window.
- `children: any` - Child elements, which are rendered directly inside the
window. If you use a [Dimmer](#dimmer) or [Modal](#modal) in your UI,
diff --git a/tgui/docs/converting-old-tgui-interfaces.md b/tgui/docs/converting-old-tgui-interfaces.md
index a42724e05c..fe2feebfee 100644
--- a/tgui/docs/converting-old-tgui-interfaces.md
+++ b/tgui/docs/converting-old-tgui-interfaces.md
@@ -73,6 +73,7 @@ This might look a bit intimidating compared to the reactive part but it's not as
You don't really need to know all this to understand how to use it, but I find it helps with understanding when things go wrong.
Ractive conditionals can have an `else` as well
+
```ractive
{{#if data.condition}}
value
@@ -116,7 +117,7 @@ and you can mix string literals, values, and tags as well.
Ractive has loops for iterating over data and inserting something for each
member of an array or object
-```
+```ractive
{{#each data.list_of_foo}}
foo {{number}} is here.
{{/each}}
@@ -135,6 +136,7 @@ Objects are represented by `{}`, arrays by `[]`
`list("bla", "blo")` would become `["bla", "blo"]` and `list("foo" = 1, "bar" = 2)` would become `{"foo": 1, "bar": 2}`
First things first, above the `return` of the function you're making the interface in, you're going to want to add something like this
+
```jsx
const things = data.things || [];
```
@@ -142,6 +144,7 @@ const things = data.things || [];
This ensures that you'll never be reading a null entry by mistake. Substitute `{}` for objects as appropriate.
If it's an array, you'll want to do this in the template
+
```jsx
{things.map(thing => (
@@ -187,7 +190,7 @@ const fooArray = toArray(fooObject);
Also occasionally you'd see an else:
-```
+```ractive
{{#each data.potentially_empty_list}}
Thing "{{name}}" is in this list!
{{else}}
@@ -220,7 +223,7 @@ This will be a reference of tgui components and the tgui-next equivalent.
Equivalent of `` is ``
-```
+```ractive
Contents
@@ -236,7 +239,7 @@ becomes
A feature sometimes used is if `ui-display` has the `button` property, it will contain a `partial` command. This becomes the `buttons` property on `Section`:
-```
+```ractive
{{#partial button}}
// lots more button bullshit here
@@ -263,7 +266,7 @@ Very important to note `ui-section` is NOT the equivalent of `Section`
`` does not have a direct equivalent, but the closest equivalent is ``
-```
+```ractive
No Power
@@ -293,7 +296,7 @@ Also good to know that if you need the contents of a `LabeledList.Item` to be co
`` has a direct equivalent in ``
-```
+```ractive
Notice stuff!
@@ -311,7 +314,7 @@ becomes
The equivalent of `ui-button` is `Button` but it works quite a bit differently.
-```
+```ractive
{
act('test')}>
+ onClick={() => act('test')} />
@@ -294,10 +292,10 @@ here's what you need (note that you'll probably be forced to clean your shit up
upon code review):
```dm
-/obj/copypasta/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state) // Remember to use the appropriate state.
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/copypasta/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "copypasta", name, 300, 300, master_ui, state)
+ ui = new(user, src, "copypasta")
ui.open()
/obj/copypasta/ui_data(mob/user)
@@ -345,7 +343,7 @@ export const SampleInterface = (props, context) => {
act('test')}>
+ onClick={() => act('test')} />
diff --git a/tgui/package.json b/tgui/package.json
index bfceeec49e..8dd15925b2 100644
--- a/tgui/package.json
+++ b/tgui/package.json
@@ -13,7 +13,7 @@
},
"dependencies": {
"babel-eslint": "^10.0.3",
- "eslint": "^6.7.2",
+ "eslint": "^7.4.0",
"eslint-plugin-react": "^7.17.0"
}
}
diff --git a/tgui/packages/common/collections.js b/tgui/packages/common/collections.js
index 1e5c978942..b542967d47 100644
--- a/tgui/packages/common/collections.js
+++ b/tgui/packages/common/collections.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
/**
* Converts a given collection to an array.
*
diff --git a/tgui/packages/common/fp.js b/tgui/packages/common/fp.js
index 4be45877e2..7aa00a00f3 100644
--- a/tgui/packages/common/fp.js
+++ b/tgui/packages/common/fp.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
/**
* Creates a function that returns the result of invoking the given
* functions, where each successive invocation is supplied the return
diff --git a/tgui/packages/common/logging.js b/tgui/packages/common/logging.js
index 4ae1855fda..0dc222ae3d 100644
--- a/tgui/packages/common/logging.js
+++ b/tgui/packages/common/logging.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
const inception = Date.now();
// Runtime detection
diff --git a/tgui/packages/common/math.js b/tgui/packages/common/math.js
index cc7a309563..c3013b5a32 100644
--- a/tgui/packages/common/math.js
+++ b/tgui/packages/common/math.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
/**
* Limits a number to the range between 'min' and 'max'.
*/
diff --git a/tgui/packages/common/perf.js b/tgui/packages/common/perf.js
new file mode 100644
index 0000000000..319b77cea3
--- /dev/null
+++ b/tgui/packages/common/perf.js
@@ -0,0 +1,44 @@
+/**
+ * Ghetto performance measurement tools.
+ *
+ * Uses NODE_ENV to redact itself from production bundles.
+ *
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
+let markersByLabel = {};
+
+/**
+ * Marks a certain spot in the code for later measurements.
+ */
+const mark = (label, timestamp) => {
+ if (process.env.NODE_ENV !== 'production') {
+ markersByLabel[label] = timestamp || Date.now();
+ }
+};
+
+/**
+ * Calculates and returns the difference between two markers as a string.
+ *
+ * Use logger.log() to print the measurement.
+ */
+const measure = (markerA, markerB) => {
+ if (process.env.NODE_ENV !== 'production') {
+ return timeDiff(
+ markersByLabel[markerA],
+ markersByLabel[markerB]);
+ }
+};
+
+const timeDiff = (startedAt, finishedAt) => {
+ const diff = Math.abs(finishedAt - startedAt);
+ const diffFrames = (diff / 16.6667).toFixed(2);
+ return `${diff}ms (${diffFrames} frames)`;
+};
+
+export const perf = {
+ mark,
+ measure,
+};
diff --git a/tgui/packages/common/react.js b/tgui/packages/common/react.js
index 0828bb8864..dba84b7b10 100644
--- a/tgui/packages/common/react.js
+++ b/tgui/packages/common/react.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
/**
* Helper for conditionally adding/removing classes in React
*
diff --git a/tgui/packages/common/redux.js b/tgui/packages/common/redux.js
index 257a9eebf5..dc486ff7b8 100644
--- a/tgui/packages/common/redux.js
+++ b/tgui/packages/common/redux.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
import { compose } from './fp';
/**
@@ -63,3 +69,31 @@ export const applyMiddleware = (...middlewares) => {
};
};
};
+
+/**
+ * Combines reducers by running them in their own object namespaces as
+ * defined in reducersObj paramter.
+ *
+ * Main difference from redux/combineReducers is that it preserves keys
+ * in the state that are not present in the reducers object. This function
+ * is also more flexible than the redux counterpart.
+ */
+export const combineReducers = reducersObj => {
+ const keys = Object.keys(reducersObj);
+ let hasChanged = false;
+ return (prevState, action) => {
+ const nextState = { ...prevState };
+ for (let key of keys) {
+ const reducer = reducersObj[key];
+ const prevDomainState = prevState[key];
+ const nextDomainState = reducer(prevDomainState, action);
+ if (prevDomainState !== nextDomainState) {
+ hasChanged = true;
+ nextState[key] = nextDomainState;
+ }
+ }
+ return hasChanged
+ ? nextState
+ : prevState;
+ };
+};
diff --git a/tgui/packages/common/storage.js b/tgui/packages/common/storage.js
new file mode 100644
index 0000000000..8e1d2183e4
--- /dev/null
+++ b/tgui/packages/common/storage.js
@@ -0,0 +1,76 @@
+/**
+ * Browser-agnostic abstraction of key-value web storage.
+ *
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
+export const STORAGE_NONE = 0;
+export const STORAGE_LOCAL_STORAGE = 1;
+export const STORAGE_INDEXED_DB = 2;
+
+const createMock = () => {
+ let storage = {};
+ const get = key => storage[key];
+ const set = (key, value) => {
+ storage[key] = value;
+ };
+ const remove = key => {
+ storage[key] = undefined;
+ };
+ const clear = () => {
+ // NOTE: On IE8, this will probably leak memory if used often.
+ storage = {};
+ };
+ return {
+ get,
+ set,
+ remove,
+ clear,
+ engine: STORAGE_NONE,
+ };
+};
+
+const createLocalStorage = () => {
+ const get = key => {
+ const value = localStorage.getItem(key);
+ if (typeof value !== 'string') {
+ return;
+ }
+ return JSON.parse(value);
+ };
+ const set = (key, value) => {
+ localStorage.setItem(key, JSON.stringify(value));
+ };
+ const remove = key => {
+ localStorage.removeItem(key);
+ };
+ const clear = () => {
+ localStorage.clear();
+ };
+ return {
+ get,
+ set,
+ remove,
+ clear,
+ engine: STORAGE_LOCAL_STORAGE,
+ };
+};
+
+const testLocalStorage = () => {
+ // Localstorage can sometimes throw an error, even if DOM storage is not
+ // disabled in IE11 settings.
+ // See: https://superuser.com/questions/1080011
+ try {
+ return Boolean(window.localStorage && window.localStorage.getItem);
+ }
+ catch {
+ return false;
+ }
+};
+
+export const storage = (
+ testLocalStorage() && createLocalStorage()
+ || createMock()
+);
diff --git a/tgui/packages/common/string.babel-plugin.cjs b/tgui/packages/common/string.babel-plugin.cjs
index ba25e3f91d..68295aefcf 100644
--- a/tgui/packages/common/string.babel-plugin.cjs
+++ b/tgui/packages/common/string.babel-plugin.cjs
@@ -1,5 +1,7 @@
/**
- * @file
+ * This plugin saves overall about 10KB on the final bundle size, so it's
+ * sort of worth it.
+ *
* We are using a .cjs extension because:
*
* 1. Webpack CLI only supports CommonJS modules;
@@ -9,8 +11,9 @@
* We need to copy-paste the whole "multiline" function because we can't
* synchronously import an ES module from a CommonJS module.
*
- * This plugin saves overall about 10KB on the final bundle size, so it's
- * sort of worth it.
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
*/
/**
diff --git a/tgui/packages/common/string.js b/tgui/packages/common/string.js
index d05dbfb6fc..16a0921a25 100644
--- a/tgui/packages/common/string.js
+++ b/tgui/packages/common/string.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
/**
* Removes excess whitespace and indentation from the string.
*/
diff --git a/tgui/packages/common/timer.js b/tgui/packages/common/timer.js
index e3feb69ca9..f4e26fa5aa 100644
--- a/tgui/packages/common/timer.js
+++ b/tgui/packages/common/timer.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
/**
* Returns a function, that, as long as it continues to be invoked, will
* not be triggered. The function will be called after it stops being
diff --git a/tgui/packages/common/vector.js b/tgui/packages/common/vector.js
index fa98597896..c3ac350a4e 100644
--- a/tgui/packages/common/vector.js
+++ b/tgui/packages/common/vector.js
@@ -1,14 +1,14 @@
-import { map, reduce, zipWith } from './collections';
-
/**
- * Creates a vector, with as many dimensions are there are arguments.
+ * N-dimensional vector manipulation functions.
+ *
+ * Vectors are plain number arrays, i.e. [x, y, z].
+ *
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
*/
-export const vecCreate = (...components) => {
- if (Array.isArray(components[0])) {
- return [...components[0]];
- }
- return components;
-};
+
+import { map, reduce, zipWith } from './collections';
const ADD = (a, b) => a + b;
const SUB = (a, b) => a - b;
diff --git a/tgui/packages/tgui-dev-server/index.js b/tgui/packages/tgui-dev-server/index.js
index 1e7683080c..f4e8155d29 100644
--- a/tgui/packages/tgui-dev-server/index.js
+++ b/tgui/packages/tgui-dev-server/index.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
import { setupWebpack, getWebpackConfig } from './webpack.js';
import { reloadByondCache } from './reloader.js';
diff --git a/tgui/packages/tgui-dev-server/link/client.js b/tgui/packages/tgui-dev-server/link/client.js
index 774b7ed6db..4671b340c5 100644
--- a/tgui/packages/tgui-dev-server/link/client.js
+++ b/tgui/packages/tgui-dev-server/link/client.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
let socket;
const queue = [];
const subscribers = [];
@@ -92,8 +98,8 @@ const sendRawMessage = msg => {
socket.send(json);
}
else {
- // Keep only 10 latest messages in the queue
- if (queue.length > 10) {
+ // Keep only 100 latest messages in the queue
+ if (queue.length > 100) {
queue.shift();
}
queue.push(json);
diff --git a/tgui/packages/tgui-dev-server/link/retrace.js b/tgui/packages/tgui-dev-server/link/retrace.js
index 74c87e6a55..e0b17a01d6 100644
--- a/tgui/packages/tgui-dev-server/link/retrace.js
+++ b/tgui/packages/tgui-dev-server/link/retrace.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
import { createLogger } from 'common/logging.js';
import fs from 'fs';
import { basename } from 'path';
diff --git a/tgui/packages/tgui-dev-server/link/server.js b/tgui/packages/tgui-dev-server/link/server.js
index 84f3700048..94a79c9ad5 100644
--- a/tgui/packages/tgui-dev-server/link/server.js
+++ b/tgui/packages/tgui-dev-server/link/server.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
import { createLogger, directLog } from 'common/logging.js';
import http from 'http';
import { inspect } from 'util';
diff --git a/tgui/packages/tgui-dev-server/reloader.js b/tgui/packages/tgui-dev-server/reloader.js
index 394e55afdb..e33f7226b9 100644
--- a/tgui/packages/tgui-dev-server/reloader.js
+++ b/tgui/packages/tgui-dev-server/reloader.js
@@ -1,9 +1,16 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
import { createLogger } from 'common/logging.js';
import fs from 'fs';
import os from 'os';
import { basename } from 'path';
import { promisify } from 'util';
import { resolveGlob, resolvePath } from './util.js';
+import { regQuery } from './winreg.js';
const logger = createLogger('reloader');
@@ -40,6 +47,21 @@ export const findCacheRoot = async () => {
return cacheRoot;
}
}
+ // Query the Windows Registry
+ if (process.platform === 'win32') {
+ logger.log('querying windows registry');
+ let userpath = await regQuery(
+ 'HKCU\\Software\\Dantom\\BYOND',
+ 'userpath');
+ if (userpath) {
+ cacheRoot = userpath
+ .replace(/\\$/, '')
+ .replace(/\\/g, '/')
+ + '/cache';
+ logger.log(`found cache at '${cacheRoot}'`);
+ return cacheRoot;
+ }
+ }
logger.log('found no cache directories');
};
diff --git a/tgui/packages/tgui-dev-server/util.js b/tgui/packages/tgui-dev-server/util.js
index db34626721..50c0baad5a 100644
--- a/tgui/packages/tgui-dev-server/util.js
+++ b/tgui/packages/tgui-dev-server/util.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
import glob from 'glob';
import { resolve as resolvePath } from 'path';
import fs from 'fs';
diff --git a/tgui/packages/tgui-dev-server/webpack.js b/tgui/packages/tgui-dev-server/webpack.js
index 778469a15b..c625827409 100644
--- a/tgui/packages/tgui-dev-server/webpack.js
+++ b/tgui/packages/tgui-dev-server/webpack.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
import { createLogger } from 'common/logging.js';
import fs from 'fs';
import { createRequire } from 'module';
diff --git a/tgui/packages/tgui-dev-server/winreg.js b/tgui/packages/tgui-dev-server/winreg.js
new file mode 100644
index 0000000000..974135e76d
--- /dev/null
+++ b/tgui/packages/tgui-dev-server/winreg.js
@@ -0,0 +1,47 @@
+/**
+ * Tools for dealing with Windows Registry bullshit.
+ *
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
+import { exec } from 'child_process';
+import { createLogger } from 'common/logging.js';
+import { promisify } from 'util';
+
+const logger = createLogger('winreg');
+
+export const regQuery = async (path, key) => {
+ if (process.platform !== 'win32') {
+ return null;
+ }
+ try {
+ const command = `reg query "${path}" /v ${key}`;
+ const { stdout } = await promisify(exec)(command);
+ const keyPattern = ` ${key} `;
+ const indexOfKey = stdout.indexOf(keyPattern);
+ if (indexOfKey === -1) {
+ logger.error('could not find the registry key');
+ return null;
+ }
+ const indexOfEol = stdout.indexOf('\r\n', indexOfKey);
+ if (indexOfEol === -1) {
+ logger.error('could not find the end of the line');
+ return null;
+ }
+ const indexOfValue = stdout.indexOf(
+ ' ',
+ indexOfKey + keyPattern.length);
+ if (indexOfValue === -1) {
+ logger.error('could not find the start of the key value');
+ return null;
+ }
+ const value = stdout.substring(indexOfValue + 4, indexOfEol);
+ return value;
+ }
+ catch (err) {
+ logger.error(err);
+ return null;
+ }
+};
diff --git a/tgui/packages/tgui/assets.js b/tgui/packages/tgui/assets.js
new file mode 100644
index 0000000000..b0f71bb9ff
--- /dev/null
+++ b/tgui/packages/tgui/assets.js
@@ -0,0 +1,54 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
+import { loadCSS as fgLoadCSS } from 'fg-loadcss';
+import { createLogger } from './logging';
+
+const logger = createLogger('assets');
+
+const EXCLUDED_PATTERNS = [
+ /v4shim/i,
+];
+
+const loadedStyles = [];
+const loadedMappings = {};
+
+export const loadCSS = url => {
+ if (loadedStyles.includes(url)) {
+ return;
+ }
+ loadedStyles.push(url);
+ logger.log(`loading stylesheet '${url}'`);
+ fgLoadCSS(url);
+};
+
+export const resolveAsset = name => (
+ loadedMappings[name] || name
+);
+
+export const assetMiddleware = store => next => action => {
+ const { type, payload } = action;
+ if (type === 'asset/stylesheet') {
+ loadCSS(payload);
+ return;
+ }
+ if (type === 'asset/mappings') {
+ for (let name of Object.keys(payload)) {
+ // Skip anything that matches excluded patterns
+ if (EXCLUDED_PATTERNS.some(regex => regex.test(name))) {
+ continue;
+ }
+ const url = payload[name];
+ const ext = name.split('.').pop();
+ loadedMappings[name] = url;
+ if (ext === 'css') {
+ loadCSS(url);
+ }
+ }
+ return;
+ }
+ next(action);
+};
diff --git a/tgui/packages/tgui/backend.js b/tgui/packages/tgui/backend.js
index 0c35483487..dab86ee918 100644
--- a/tgui/packages/tgui/backend.js
+++ b/tgui/packages/tgui/backend.js
@@ -5,10 +5,18 @@
* Sometimes backend can response without a "data" field, but our final
* state will still contain previous "data" because we are merging
* the response with already existing state.
+ *
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
*/
+import { perf } from 'common/perf';
import { UI_DISABLED, UI_INTERACTIVE } from './constants';
-import { callByond } from './byond';
+import { releaseHeldKeys } from './hotkeys';
+import { createLogger } from './logging';
+
+const logger = createLogger('backend');
export const backendUpdate = state => ({
type: 'backend/update',
@@ -20,7 +28,23 @@ export const backendSetSharedState = (key, nextState) => ({
payload: { key, nextState },
});
-export const backendReducer = (state, action) => {
+export const backendSuspendStart = () => ({
+ type: 'backend/suspendStart',
+});
+
+export const backendSuspendSuccess = () => ({
+ type: 'backend/suspendSuccess',
+ payload: {
+ timestamp: Date.now(),
+ },
+});
+
+const initialState = {
+ config: {},
+ data: {},
+};
+
+export const backendReducer = (state = initialState, action) => {
const { type, payload } = action;
if (type === 'backend/update') {
@@ -59,6 +83,7 @@ export const backendReducer = (state, action) => {
shared,
visible,
interactive,
+ suspended: false,
};
}
@@ -73,30 +98,175 @@ export const backendReducer = (state, action) => {
};
}
+ if (type === 'backend/suspendStart') {
+ return {
+ ...state,
+ suspending: true,
+ };
+ }
+
+ if (type === 'backend/suspendSuccess') {
+ const { timestamp } = payload;
+ return {
+ ...state,
+ data: {},
+ shared: {},
+ config: {
+ ...state.config,
+ title: '',
+ status: 1,
+ },
+ suspending: false,
+ suspended: timestamp,
+ };
+ }
+
return state;
};
+export const backendMiddleware = store => {
+ let fancyState;
+ let suspendInterval;
+
+ return next => action => {
+ const { config, suspended } = selectBackend(store.getState());
+ const { type, payload } = action;
+
+ if (type === 'backend/suspendStart' && !suspendInterval) {
+ logger.log(`suspending (${window.__windowId__})`);
+ // Keep sending suspend messages until it succeeds.
+ // It may fail multiple times due to topic rate limiting.
+ const suspendFn = () => sendMessage({
+ type: 'suspend',
+ });
+ suspendFn();
+ suspendInterval = setInterval(suspendFn, 2000);
+ }
+
+ if (type === 'backend/suspendSuccess') {
+ clearInterval(suspendInterval);
+ suspendInterval = undefined;
+ releaseHeldKeys();
+ Byond.winset(window.__windowId__, {
+ 'is-visible': false,
+ });
+ }
+
+ if (type === 'backend/update') {
+ const fancy = payload.config?.window?.fancy;
+ // Initialize fancy state
+ if (fancyState === undefined) {
+ fancyState = fancy;
+ }
+ // React to changes in fancy
+ else if (fancyState !== fancy) {
+ logger.log('changing fancy mode to', fancy);
+ fancyState = fancy;
+ Byond.winset(window.__windowId__, {
+ titlebar: !fancy,
+ 'can-resize': !fancy,
+ });
+ }
+ }
+
+ if (type === 'backend/update' && suspended) {
+ // We schedule this for the next tick here because resizing and unhiding
+ // during the same tick will flash with a white background.
+ setImmediate(() => {
+ perf.mark('resume/start');
+ // Doublecheck if we are not re-suspended.
+ const { suspended } = selectBackend(store.getState());
+ if (suspended) {
+ return;
+ }
+ Byond.winset(window.__windowId__, {
+ 'is-visible': true,
+ });
+ perf.mark('resume/finish');
+ if (process.env.NODE_ENV !== 'production') {
+ logger.log('visible in',
+ perf.measure('render/finish', 'resume/finish'));
+ }
+ });
+ }
+
+ return next(action);
+ };
+};
+
+/**
+ * Sends a message to /datum/tgui_window.
+ */
+export const sendMessage = (message = {}) => {
+ const { payload, ...rest } = message;
+ const data = {
+ // Message identifying header
+ tgui: 1,
+ window_id: window.__windowId__,
+ // Message body
+ ...rest,
+ };
+ // JSON-encode the payload
+ if (payload !== null && payload !== undefined) {
+ data.payload = JSON.stringify(payload);
+ }
+ Byond.topic(data);
+};
+
+/**
+ * Sends an action to `ui_act` on `src_object` that this tgui window
+ * is associated with.
+ */
+export const sendAct = (action, payload = {}) => {
+ // Validate that payload is an object
+ const isObject = typeof payload === 'object'
+ && payload !== null
+ && !Array.isArray(payload);
+ if (!isObject) {
+ logger.error(`Payload for act() must be an object, got this:`, payload);
+ return;
+ }
+ sendMessage({
+ type: 'act/' + action,
+ payload,
+ });
+};
+
/**
* @typedef BackendState
* @type {{
* config: {
* title: string,
* status: number,
- * screen: string,
- * style: string,
* interface: string,
- * fancy: number,
- * locked: number,
- * observer: number,
- * window: string,
- * ref: string,
+ * user: {
+ * name: string,
+ * ckey: string,
+ * observer: number,
+ * },
+ * window: {
+ * key: string,
+ * size: [number, number],
+ * fancy: boolean,
+ * locked: boolean,
+ * },
* },
* data: any,
+ * shared: any,
* visible: boolean,
* interactive: boolean,
+ * suspending: boolean,
+ * suspended: boolean,
* }}
*/
+/**
+ * Selects a backend-related slice of Redux state
+ *
+ * @return {BackendState}
+ */
+export const selectBackend = state => state.backend || {};
+
/**
* A React hook (sort of) for getting tgui state and related functions.
*
@@ -104,21 +274,16 @@ export const backendReducer = (state, action) => {
* be used in functional components.
*
* @return {BackendState & {
- * act: (action: string, params?: object) => void,
+ * act: sendAct,
* }}
*/
export const useBackend = context => {
const { store } = context;
- const state = store.getState();
- const ref = state.config.ref;
- const act = (action, params = {}) => {
- callByond('', {
- src: ref,
- action,
- ...params,
- });
+ const state = selectBackend(store.getState());
+ return {
+ ...state,
+ act: sendAct,
};
- return { ...state, act };
};
/**
@@ -136,7 +301,7 @@ export const useBackend = context => {
*/
export const useLocalState = (context, key, initialState) => {
const { store } = context;
- const state = store.getState();
+ const state = selectBackend(store.getState());
const sharedStates = state.shared ?? {};
const sharedState = (key in sharedStates)
? sharedStates[key]
@@ -144,7 +309,11 @@ export const useLocalState = (context, key, initialState) => {
return [
sharedState,
nextState => {
- store.dispatch(backendSetSharedState(key, nextState));
+ store.dispatch(backendSetSharedState(key, (
+ typeof nextState === 'function'
+ ? nextState(sharedState)
+ : nextState
+ )));
},
];
};
@@ -165,8 +334,7 @@ export const useLocalState = (context, key, initialState) => {
*/
export const useSharedState = (context, key, initialState) => {
const { store } = context;
- const state = store.getState();
- const ref = state.config.ref;
+ const state = selectBackend(store.getState());
const sharedStates = state.shared ?? {};
const sharedState = (key in sharedStates)
? sharedStates[key]
@@ -174,11 +342,14 @@ export const useSharedState = (context, key, initialState) => {
return [
sharedState,
nextState => {
- callByond('', {
- src: ref,
- action: 'tgui:setSharedState',
+ sendMessage({
+ type: 'setSharedState',
key,
- value: JSON.stringify(nextState) || '',
+ value: JSON.stringify(
+ typeof nextState === 'function'
+ ? nextState(sharedState)
+ : nextState
+ ) || '',
});
},
];
diff --git a/tgui/packages/tgui/byond.js b/tgui/packages/tgui/byond.js
index bd70056613..fa2b03e715 100644
--- a/tgui/packages/tgui/byond.js
+++ b/tgui/packages/tgui/byond.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
// Reference a global Byond object
const { Byond } = window;
diff --git a/tgui/packages/tgui/components/Box.js b/tgui/packages/tgui/components/Box.js
index cb86890a97..a85c692e9a 100644
--- a/tgui/packages/tgui/components/Box.js
+++ b/tgui/packages/tgui/components/Box.js
@@ -9,17 +9,22 @@ import { createVNode } from 'inferno';
import { ChildFlags, VNodeFlags } from 'inferno-vnode-flags';
import { CSS_COLORS } from '../constants';
-const UNIT_PX = 12;
-
/**
* Coverts our rem-like spacing unit into a CSS unit.
*/
export const unit = value => {
if (typeof value === 'string') {
+ // Transparently convert pixels into rem units
+ if (value.endsWith('px') && !Byond.IS_LTE_IE8) {
+ return parseFloat(value) / 12 + 'rem';
+ }
return value;
}
if (typeof value === 'number') {
- return (value * UNIT_PX) + 'px';
+ if (Byond.IS_LTE_IE8) {
+ return value * 12 + 'px';
+ }
+ return value + 'rem';
}
};
@@ -28,10 +33,10 @@ export const unit = value => {
*/
export const halfUnit = value => {
if (typeof value === 'string') {
- return value;
+ return unit(value);
}
if (typeof value === 'number') {
- return (value * UNIT_PX * 0.5) + 'px';
+ return unit(value * 0.5);
}
};
@@ -90,7 +95,13 @@ const styleMapperByPropName = {
maxHeight: mapUnitPropTo('max-height', unit),
fontSize: mapUnitPropTo('font-size', unit),
fontFamily: mapRawPropTo('font-family'),
- lineHeight: mapRawPropTo('line-height'),
+ lineHeight: (style, value) => {
+ if (!isFalsy(value)) {
+ style['line-height'] = typeof value === 'number'
+ ? value
+ : unit(value);
+ }
+ },
opacity: mapRawPropTo('opacity'),
textAlign: mapRawPropTo('text-align'),
verticalAlign: mapRawPropTo('vertical-align'),
diff --git a/tgui/packages/tgui/components/Button.js b/tgui/packages/tgui/components/Button.js
index 52adf286ad..bb8b4bcbf0 100644
--- a/tgui/packages/tgui/components/Button.js
+++ b/tgui/packages/tgui/components/Button.js
@@ -6,7 +6,6 @@
import { classes, pureComponentHooks } from 'common/react';
import { Component, createRef } from 'inferno';
-import { IS_IE8 } from '../byond';
import { KEY_ENTER, KEY_ESCAPE, KEY_SPACE } from '../hotkeys';
import { refocusLayout } from '../layouts';
import { createLogger } from '../logging';
@@ -61,7 +60,7 @@ export const Button = props => {
className,
])}
tabIndex={!disabled && '0'}
- unselectable={IS_IE8}
+ unselectable={Byond.IS_LTE_IE8}
onclick={e => {
refocusLayout();
if (!disabled && onClick) {
@@ -87,7 +86,10 @@ export const Button = props => {
}}
{...rest}>
{icon && (
-
+
)}
{content}
{children}
diff --git a/tgui/packages/tgui/components/ByondUi.js b/tgui/packages/tgui/components/ByondUi.js
index db9c5822ba..2369cc7993 100644
--- a/tgui/packages/tgui/components/ByondUi.js
+++ b/tgui/packages/tgui/components/ByondUi.js
@@ -7,7 +7,6 @@
import { shallowDiffers } from 'common/react';
import { debounce } from 'common/timer';
import { Component, createRef } from 'inferno';
-import { callByond, IS_IE8 } from '../byond';
import { createLogger } from '../logging';
import { computeBoxProps } from './Box';
@@ -28,16 +27,12 @@ const createByondUiElement = elementId => {
render: params => {
logger.log(`rendering '${id}'`);
byondUiStack[index] = id;
- callByond('winset', {
- ...params,
- id,
- });
+ Byond.winset(id, params);
},
unmount: () => {
logger.log(`unmounting '${id}'`);
byondUiStack[index] = null;
- callByond('winset', {
- id,
+ Byond.winset(id, {
parent: '',
});
},
@@ -51,8 +46,7 @@ window.addEventListener('beforeunload', () => {
if (typeof id === 'string') {
logger.log(`unmounting '${id}' (beforeunload)`);
byondUiStack[index] = null;
- callByond('winset', {
- id,
+ Byond.winset(id, {
parent: '',
});
}
@@ -83,7 +77,7 @@ export class ByondUi extends Component {
this.byondUiElement = createByondUiElement(props.params?.id);
this.handleResize = debounce(() => {
this.forceUpdate();
- }, 500);
+ }, 100);
}
shouldComponentUpdate(nextProps) {
@@ -101,16 +95,17 @@ export class ByondUi extends Component {
componentDidMount() {
// IE8: It probably works, but fuck you anyway.
- if (IS_IE8) {
+ if (Byond.IS_LTE_IE10) {
return;
}
window.addEventListener('resize', this.handleResize);
- return this.componentDidUpdate();
+ this.componentDidUpdate();
+ this.handleResize();
}
componentDidUpdate() {
// IE8: It probably works, but fuck you anyway.
- if (IS_IE8) {
+ if (Byond.IS_LTE_IE10) {
return;
}
const {
@@ -119,6 +114,7 @@ export class ByondUi extends Component {
const box = getBoundingBox(this.containerRef.current);
logger.log('bounding box', box);
this.byondUiElement.render({
+ parent: window.__windowId__,
...params,
pos: box.pos[0] + ',' + box.pos[1],
size: box.size[0] + 'x' + box.size[1],
@@ -127,7 +123,7 @@ export class ByondUi extends Component {
componentWillUnmount() {
// IE8: It probably works, but fuck you anyway.
- if (IS_IE8) {
+ if (Byond.IS_LTE_IE10) {
return;
}
window.removeEventListener('resize', this.handleResize);
@@ -135,26 +131,16 @@ export class ByondUi extends Component {
}
render() {
- const {
- parent,
- params,
- ...rest
- } = this.props;
+ const { params, ...rest } = this.props;
const type = params?.type;
const boxProps = computeBoxProps(rest);
return (
- {type === 'button' &&
}
+ {/* Filler */}
+
);
}
}
-
-const ButtonMock = () => (
-
-);
diff --git a/tgui/packages/tgui/components/Chart.js b/tgui/packages/tgui/components/Chart.js
index 16ed82c355..77913779db 100644
--- a/tgui/packages/tgui/components/Chart.js
+++ b/tgui/packages/tgui/components/Chart.js
@@ -7,7 +7,6 @@
import { map, zipWith } from 'common/collections';
import { pureComponentHooks } from 'common/react';
import { Component, createRef } from 'inferno';
-import { IS_IE8 } from '../byond';
import { Box } from './Box';
const normalizeData = (data, scale, rangeX, rangeY) => {
@@ -123,5 +122,5 @@ const Stub = props => null;
// IE8: No inline svg support
export const Chart = {
- Line: IS_IE8 ? Stub : LineChart,
+ Line: Byond.IS_LTE_IE8 ? Stub : LineChart,
};
diff --git a/tgui/packages/tgui/components/Flex.js b/tgui/packages/tgui/components/Flex.js
index 4ee69a1902..02d2fac314 100644
--- a/tgui/packages/tgui/components/Flex.js
+++ b/tgui/packages/tgui/components/Flex.js
@@ -5,7 +5,6 @@
*/
import { classes, pureComponentHooks } from 'common/react';
-import { IS_IE8 } from '../byond';
import { Box, unit } from './Box';
export const computeFlexProps = props => {
@@ -22,10 +21,10 @@ export const computeFlexProps = props => {
return {
className: classes([
'Flex',
- IS_IE8 && (
+ Byond.IS_LTE_IE10 && (
direction === 'column'
- ? 'Flex--ie8--column'
- : 'Flex--ie8'
+ ? 'Flex--iefix--column'
+ : 'Flex--iefix'
),
inline && 'Flex--inline',
spacing > 0 && 'Flex--spacing--' + spacing,
@@ -63,7 +62,7 @@ export const computeFlexItemProps = props => {
return {
className: classes([
'Flex__item',
- IS_IE8 && 'Flex__item--ie8',
+ Byond.IS_LTE_IE10 && 'Flex__item--iefix',
className,
]),
style: {
diff --git a/tgui/packages/tgui/components/Knob.js b/tgui/packages/tgui/components/Knob.js
index e72b15f195..4861e71bf3 100644
--- a/tgui/packages/tgui/components/Knob.js
+++ b/tgui/packages/tgui/components/Knob.js
@@ -6,7 +6,6 @@
import { keyOfMatchingRange, scale } from 'common/math';
import { classes } from 'common/react';
-import { IS_IE8 } from '../byond';
import { computeBoxClassName, computeBoxProps } from './Box';
import { DraggableControl } from './DraggableControl';
import { NumberInput } from './NumberInput';
@@ -14,7 +13,7 @@ import { NumberInput } from './NumberInput';
export const Knob = props => {
// IE8: I don't want to support a yet another component on IE8.
// IE8: It also can't handle SVG.
- if (IS_IE8) {
+ if (Byond.IS_LTE_IE8) {
return (
);
diff --git a/tgui/packages/tgui/components/NumberInput.js b/tgui/packages/tgui/components/NumberInput.js
index 806c81d542..cba6f5025e 100644
--- a/tgui/packages/tgui/components/NumberInput.js
+++ b/tgui/packages/tgui/components/NumberInput.js
@@ -7,7 +7,6 @@
import { clamp } from 'common/math';
import { classes, pureComponentHooks } from 'common/react';
import { Component, createRef } from 'inferno';
-import { IS_IE8 } from '../byond';
import { AnimatedNumber } from './AnimatedNumber';
import { Box } from './Box';
@@ -168,7 +167,7 @@ export class NumberInput extends Component {
const renderContentElement = value => (
+ unselectable={Byond.IS_LTE_IE8}>
{value + (unit ? ' ' + unit : '')}
);
diff --git a/tgui/packages/tgui/components/Section.js b/tgui/packages/tgui/components/Section.js
index 2f9fa239b1..af59bc4022 100644
--- a/tgui/packages/tgui/components/Section.js
+++ b/tgui/packages/tgui/components/Section.js
@@ -5,7 +5,7 @@
*/
import { classes, isFalsy, pureComponentHooks } from 'common/react';
-import { Box } from './Box';
+import { computeBoxClassName, computeBoxProps } from './Box';
export const Section = props => {
const {
@@ -13,20 +13,22 @@ export const Section = props => {
title,
level = 1,
buttons,
- content,
+ fill,
children,
...rest
} = props;
const hasTitle = !isFalsy(title) || !isFalsy(buttons);
- const hasContent = !isFalsy(content) || !isFalsy(children);
+ const hasContent = !isFalsy(children);
return (
-
+ {...computeBoxProps(rest)}>
{hasTitle && (
@@ -39,11 +41,10 @@ export const Section = props => {
)}
{hasContent && (
- {content}
{children}
)}
-
+
);
};
diff --git a/tgui/packages/tgui/components/Slider.js b/tgui/packages/tgui/components/Slider.js
index 7ae74684fb..005e6c1f8f 100644
--- a/tgui/packages/tgui/components/Slider.js
+++ b/tgui/packages/tgui/components/Slider.js
@@ -6,14 +6,13 @@
import { clamp01, keyOfMatchingRange, scale } from 'common/math';
import { classes } from 'common/react';
-import { IS_IE8 } from '../byond';
import { computeBoxClassName, computeBoxProps } from './Box';
import { DraggableControl } from './DraggableControl';
import { NumberInput } from './NumberInput';
export const Slider = props => {
// IE8: I don't want to support a yet another component on IE8.
- if (IS_IE8) {
+ if (Byond.IS_LTE_IE8) {
return (
);
diff --git a/tgui/packages/tgui/components/TimeDisplay.js b/tgui/packages/tgui/components/TimeDisplay.js
new file mode 100644
index 0000000000..dd8a062b2b
--- /dev/null
+++ b/tgui/packages/tgui/components/TimeDisplay.js
@@ -0,0 +1,69 @@
+import { toFixed } from 'common/math';
+import { Component } from 'inferno';
+
+// AnimatedNumber Copypaste
+const isSafeNumber = value => {
+ return typeof value === 'number'
+ && Number.isFinite(value)
+ && !Number.isNaN(value);
+};
+
+export class TimeDisplay extends Component {
+ constructor(props) {
+ super(props);
+ this.timer = null;
+ this.last_seen_value = undefined;
+ this.state = {
+ value: 0,
+ };
+ // Set initial state with value provided in props
+ if (isSafeNumber(props.value)) {
+ this.state.value = Number(props.value);
+ this.last_seen_value = Number(props.value);
+ }
+ }
+
+ componentDidUpdate() {
+ if (this.props.auto !== undefined) {
+ clearInterval(this.timer);
+ this.timer = setInterval(() => this.tick(), 1000); // every 1 s
+ }
+ }
+
+ tick() {
+ let current = Number(this.state.value);
+ if (this.props.value !== this.last_seen_value) {
+ this.last_seen_value = this.props.value;
+ current = this.props.value;
+ }
+ const mod = this.props.auto === "up" ? 10 : -10; // Time down by default.
+ const value = Math.max(0, current + mod); // one sec tick
+ this.setState({ value });
+ }
+
+ componentDidMount() {
+ if (this.props.auto !== undefined) {
+ this.timer = setInterval(() => this.tick(), 1000); // every 1 s
+ }
+ }
+
+ componentWillUnmount() {
+ clearInterval(this.timer);
+ }
+
+ render() {
+ const val = this.state.value;
+ // Directly display weird stuff
+ if (!isSafeNumber(val)) {
+ return this.state.value || null;
+ }
+ // THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER
+ // HH:MM:SS
+ // 00:02:13
+ const seconds = toFixed(Math.floor((val/10) % 60)).padStart(2, "0");
+ const minutes = toFixed(Math.floor((val/(10*60)) % 60)).padStart(2, "0");
+ const hours = toFixed(Math.floor((val/(10*60*60)) % 24)).padStart(2, "0");
+ const formattedValue = `${hours}:${minutes}:${seconds}`;
+ return formattedValue;
+ }
+}
diff --git a/tgui/packages/tgui/components/index.js b/tgui/packages/tgui/components/index.js
index 9102c6f79c..5580cc38fb 100644
--- a/tgui/packages/tgui/components/index.js
+++ b/tgui/packages/tgui/components/index.js
@@ -14,6 +14,7 @@ export { Collapsible } from './Collapsible';
export { ColorBox } from './ColorBox';
export { Dimmer } from './Dimmer';
export { Divider } from './Divider';
+export { DraggableControl } from './DraggableControl';
export { Dropdown } from './Dropdown';
export { Flex } from './Flex';
export { Grid } from './Grid';
@@ -32,3 +33,4 @@ export { Table } from './Table';
export { TextArea } from './TextArea';
export { Tabs } from './Tabs';
export { Tooltip } from './Tooltip';
+export { TimeDisplay } from './TimeDisplay';
diff --git a/tgui/packages/tgui/constants.js b/tgui/packages/tgui/constants.js
index 20ba00cb75..92c1f80e6c 100644
--- a/tgui/packages/tgui/constants.js
+++ b/tgui/packages/tgui/constants.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
// UI states, which are mirrored from the BYOND code.
export const UI_INTERACTIVE = 2;
export const UI_UPDATE = 1;
diff --git a/tgui/packages/tgui/debug/KitchenSink.js b/tgui/packages/tgui/debug/KitchenSink.js
new file mode 100644
index 0000000000..050adcf193
--- /dev/null
+++ b/tgui/packages/tgui/debug/KitchenSink.js
@@ -0,0 +1,608 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
+import { Fragment } from 'inferno';
+import { useBackend, useLocalState } from '../backend';
+import { BlockQuote, Box, Button, ByondUi, Collapsible, DraggableControl, Flex, Icon, Input, Knob, LabeledList, NoticeBox, NumberInput, ProgressBar, Section, Slider, Tabs, Tooltip } from '../components';
+import { formatSiUnit } from '../format';
+import { Window } from '../layouts';
+import { createLogger } from '../logging';
+
+const logger = createLogger('KitchenSink');
+
+const COLORS_SPECTRUM = [
+ 'red',
+ 'orange',
+ 'yellow',
+ 'olive',
+ 'green',
+ 'teal',
+ 'blue',
+ 'violet',
+ 'purple',
+ 'pink',
+ 'brown',
+ 'grey',
+];
+
+const COLORS_STATES = [
+ 'good',
+ 'average',
+ 'bad',
+ 'black',
+ 'white',
+];
+
+const PAGES = [
+ {
+ title: 'Button',
+ component: () => KitchenSinkButton,
+ },
+ {
+ title: 'Box',
+ component: () => KitchenSinkBox,
+ },
+ {
+ title: 'Flex & Sections',
+ component: () => KitchenSinkFlexAndSections,
+ },
+ {
+ title: 'ProgressBar',
+ component: () => KitchenSinkProgressBar,
+ },
+ {
+ title: 'Tabs',
+ component: () => KitchenSinkTabs,
+ },
+ {
+ title: 'Tooltip',
+ component: () => KitchenSinkTooltip,
+ },
+ {
+ title: 'Input / Control',
+ component: () => KitchenSinkInput,
+ },
+ {
+ title: 'Collapsible',
+ component: () => KitchenSinkCollapsible,
+ },
+ {
+ title: 'BlockQuote',
+ component: () => KitchenSinkBlockQuote,
+ },
+ {
+ title: 'ByondUi',
+ component: () => KitchenSinkByondUi,
+ },
+ {
+ title: 'Themes',
+ component: () => KitchenSinkThemes,
+ },
+ {
+ title: 'Storage',
+ component: () => KitchenSinkStorage,
+ },
+];
+
+export const KitchenSink = (props, context) => {
+ const [theme] = useLocalState(context, 'kitchenSinkTheme');
+ const [pageIndex, setPageIndex] = useLocalState(context, 'pageIndex', 0);
+ const PageComponent = PAGES[pageIndex].component();
+ return (
+
+
+
+
+ {PAGES.map((page, i) => (
+ setPageIndex(i)}>
+ {page.title}
+
+ ))}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const KitchenSinkButton = props => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {COLORS_STATES.map(color => (
+
+ ))}
+
+ {COLORS_SPECTRUM.map(color => (
+
+ ))}
+
+ {COLORS_SPECTRUM.map(color => (
+
+ {color}
+
+ ))}
+
+
+ );
+};
+
+const KitchenSinkBox = props => {
+ return (
+
+
+ bold
+
+
+ italic
+
+
+ opacity 0.5
+
+
+ opacity 0.25
+
+
+ m: 2
+
+
+ left
+
+
+ center
+
+
+ right
+
+
+ );
+};
+
+const KitchenSinkFlexAndSections = (props, context) => {
+ const [grow, setGrow] = useLocalState(
+ context, 'fs_grow', 1);
+ const [direction, setDirection] = useLocalState(
+ context, 'fs_direction', 'column');
+ const [fill, setFill] = useLocalState(
+ context, 'fs_fill', true);
+ const [hasTitle, setHasTitle] = useLocalState(
+ context, 'fs_title', true);
+ return (
+
+
+
+ setDirection(
+ direction === 'column' ? 'row' : 'column'
+ )}>
+ {`Flex direction="${direction}"`}
+
+ setGrow(Number(!grow))}>
+ {`Flex.Item grow={${grow}}`}
+
+ setFill(!fill)}>
+ {`Section fill={${String(fill)}}`}
+
+ setHasTitle(!hasTitle)}>
+ {`Section title`}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const KitchenSinkProgressBar = (props, context) => {
+ const [
+ progress,
+ setProgress,
+ ] = useLocalState(context, 'progress', 0.5);
+ return (
+
+
+ Value: {Number(progress).toFixed(1)}
+
+
+ setProgress(progress - 0.1)} />
+ setProgress(progress + 0.1)} />
+
+
+ );
+};
+
+const KitchenSinkTabs = (props, context) => {
+ const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0);
+ const [vertical, setVertical] = useLocalState(context, 'tabVert');
+ const [altSelection, setAltSelection] = useLocalState(context, 'tabAlt');
+ const TAB_RANGE = [1, 2, 3, 4, 5];
+ return (
+
+
+ setVertical(!vertical)} />
+ setAltSelection(!altSelection)} />
+
+
+ {TAB_RANGE.map((number, i) => (
+ setTabIndex(i)}>
+ Tab #{number}
+
+ ))}
+
+
+ );
+};
+
+const KitchenSinkTooltip = props => {
+ const positions = [
+ 'top',
+ 'left',
+ 'right',
+ 'bottom',
+ 'bottom-left',
+ 'bottom-right',
+ ];
+ return (
+
+
+
+ Box (hover me).
+
+
+
+
+
+ {positions.map(position => (
+
+ ))}
+
+
+ );
+};
+
+const KitchenSinkInput = (props, context) => {
+ const [
+ number,
+ setNumber,
+ ] = useLocalState(context, 'number', 0);
+ const [
+ text,
+ setText,
+ ] = useLocalState(context, 'text', "Sample text");
+ return (
+
+ );
+};
+
+const KitchenSinkCollapsible = props => {
+ return (
+
+ );
+};
+
+const BoxWithSampleText = props => {
+ return (
+
+
+ Jackdaws love my big sphinx of quartz.
+
+
+ The wide electrification of the southern
+ provinces will give a powerful impetus to the
+ growth of agriculture.
+
+
+ );
+};
+
+const KitchenSinkBlockQuote = props => {
+ return (
+
+ );
+};
+
+const KitchenSinkByondUi = (props, context) => {
+ const { config } = useBackend(context);
+ const [code, setCode] = useLocalState(context,
+ 'byondUiEvalCode',
+ `Byond.winset('${window.__windowId__}', {\n 'is-visible': true,\n})`);
+ return (
+
+
+ setImmediate(() => {
+ try {
+ const result = new Function('return (' + code + ')')();
+ if (result && result.then) {
+ logger.log('Promise');
+ result.then(logger.log);
+ }
+ else {
+ logger.log(result);
+ }
+ }
+ catch (err) {
+ logger.log(err);
+ }
+ })}>
+ Evaluate
+
+ )}>
+ setCode(e.target.value)}>
+ {code}
+
+
+
+ );
+};
+
+const KitchenSinkThemes = (props, context) => {
+ const [theme, setTheme] = useLocalState(context, 'kitchenSinkTheme');
+ return (
+
+ );
+};
+
+const KitchenSinkStorage = (props, context) => {
+ if (!window.localStorage) {
+ return (
+
+ Local storage is not available.
+
+ );
+ }
+ return (
+ {
+ localStorage.clear();
+ }}>
+ Clear
+
+ )}>
+
+
+ {localStorage.length}
+
+
+ {formatSiUnit(localStorage.remainingSpace, 0, 'B')}
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/debug/index.js b/tgui/packages/tgui/debug/index.js
new file mode 100644
index 0000000000..83fc136548
--- /dev/null
+++ b/tgui/packages/tgui/debug/index.js
@@ -0,0 +1,49 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
+import { subscribeToHotKey } from '../hotkeys';
+
+export const toggleKitchenSink = () => ({
+ type: 'debug/toggleKitchenSink',
+});
+
+export const toggleDebugLayout = () => ({
+ type: 'debug/toggleDebugLayout',
+});
+
+subscribeToHotKey('F11', () => toggleDebugLayout());
+subscribeToHotKey('F12', () => toggleKitchenSink());
+subscribeToHotKey('Ctrl+Alt+[8]', () => {
+ // NOTE: We need to call this in a timeout, because we need a clean
+ // stack in order for this to be a fatal error.
+ setTimeout(() => {
+ throw new Error(
+ 'OOPSIE WOOPSIE!! UwU We made a fucky wucky!! A wittle'
+ + ' fucko boingo! The code monkeys at our headquarters are'
+ + ' working VEWY HAWD to fix this!');
+ });
+});
+
+export const selectDebug = state => state.debug;
+
+export const useDebug = context => selectDebug(context.store.getState());
+
+export const debugReducer = (state = {}, action) => {
+ const { type, payload } = action;
+ if (type === 'debug/toggleKitchenSink') {
+ return {
+ ...state,
+ kitchenSink: !state.kitchenSink,
+ };
+ }
+ if (type === 'debug/toggleDebugLayout') {
+ return {
+ ...state,
+ debugLayout: !state.debugLayout,
+ };
+ }
+ return state;
+};
diff --git a/tgui/packages/tgui/drag.js b/tgui/packages/tgui/drag.js
index a29146b796..99db716410 100644
--- a/tgui/packages/tgui/drag.js
+++ b/tgui/packages/tgui/drag.js
@@ -1,76 +1,174 @@
-import { vecAdd, vecInverse, vecMultiply } from 'common/vector';
-import { winget, winset } from './byond';
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
+import { storage } from 'common/storage';
+import { vecAdd, vecInverse, vecMultiply, vecScale } from 'common/vector';
import { createLogger } from './logging';
const logger = createLogger('drag');
-let ref;
+let windowKey = window.__windowId__;
let dragging = false;
let resizing = false;
let screenOffset = [0, 0];
+let screenOffsetPromise;
let dragPointOffset;
let resizeMatrix;
let initialSize;
let size;
-const getWindowPosition = ref => {
- return winget(ref, 'pos').then(pos => [pos.x, pos.y]);
+export const setWindowKey = key => {
+ windowKey = key;
};
-const setWindowPosition = (ref, vec) => {
- return winset(ref, 'pos', vec[0] + ',' + vec[1]);
+export const getWindowPosition = () => [
+ window.screenLeft,
+ window.screenTop,
+];
+
+export const getWindowSize = () => [
+ window.innerWidth,
+ window.innerHeight,
+];
+
+export const setWindowPosition = vec => {
+ const byondPos = vecAdd(vec, screenOffset);
+ return Byond.winset(window.__windowId__, {
+ pos: byondPos[0] + ',' + byondPos[1],
+ });
};
-const setWindowSize = (ref, vec) => {
- return winset(ref, 'size', vec[0] + ',' + vec[1]);
+export const setWindowSize = vec => {
+ return Byond.winset(window.__windowId__, {
+ size: vec[0] + 'x' + vec[1],
+ });
};
-export const setupDrag = async state => {
- logger.log('setting up');
- ref = state.config.window;
- // Calculate offset caused by windows taskbar
- const realPosition = await getWindowPosition(ref);
- screenOffset = [
- realPosition[0] - window.screenLeft,
- realPosition[1] - window.screenTop,
- ];
- // Constraint window position
- const [relocated, safePosition] = constraintPosition(realPosition);
- if (relocated) {
- setWindowPosition(ref, safePosition);
+export const getScreenPosition = () => [
+ 0 - screenOffset[0],
+ 0 - screenOffset[1],
+];
+
+export const getScreenSize = () => [
+ window.screen.availWidth,
+ window.screen.availHeight,
+];
+
+/**
+ * Moves an item to the top of the recents array, and keeps its length
+ * limited to the number in `limit` argument.
+ *
+ * Uses a strict equality check for comparisons.
+ *
+ * Returns new recents and an item which was trimmed.
+ */
+const touchRecents = (recents, touchedItem, limit = 50) => {
+ const nextRecents = [touchedItem];
+ let trimmedItem;
+ for (let i = 0; i < recents.length; i++) {
+ const item = recents[i];
+ if (item === touchedItem) {
+ continue;
+ }
+ if (nextRecents.length < limit) {
+ nextRecents.push(item);
+ }
+ else {
+ trimmedItem = item;
+ }
}
- logger.debug('current state', { ref, screenOffset });
+ return [nextRecents, trimmedItem];
+};
+
+export const storeWindowGeometry = windowKey => {
+ logger.log('storing geometry');
+ const geometry = {
+ pos: getWindowPosition(),
+ size: getWindowSize(),
+ };
+ storage.set(windowKey, geometry);
+ // Update the list of stored geometries
+ const [geometries, trimmedKey] = touchRecents(
+ storage.get('geometries') || [],
+ windowKey);
+ if (trimmedKey) {
+ storage.remove(trimmedKey);
+ }
+ storage.set('geometries', geometries);
+};
+
+export const recallWindowGeometry = async (windowKey, options = {}) => {
+ // Only recall geometry in fancy mode
+ const geometry = options.fancy && storage.get(windowKey);
+ if (geometry) {
+ logger.log('recalled geometry:', geometry);
+ }
+ let pos = geometry?.pos || options.pos;
+ const size = options.size;
+ // Set window size
+ if (size) {
+ setWindowSize(size);
+ }
+ // Set window position
+ if (pos) {
+ await screenOffsetPromise;
+ // Constraint window position if monitor lock was set in preferences.
+ if (size && options.locked) {
+ pos = constraintPosition(pos, size)[1];
+ }
+ setWindowPosition(pos);
+ }
+ // Set window position at the center of the screen.
+ else if (size) {
+ await screenOffsetPromise;
+ const areaAvailable = [
+ window.screen.availWidth - Math.abs(screenOffset[0]),
+ window.screen.availHeight - Math.abs(screenOffset[1]),
+ ];
+ const pos = vecAdd(
+ vecScale(areaAvailable, 0.5),
+ vecScale(size, -0.5),
+ vecScale(screenOffset, -1.0));
+ setWindowPosition(pos);
+ }
+};
+
+export const setupDrag = async () => {
+ // Calculate screen offset caused by the windows taskbar
+ screenOffsetPromise = Byond.winget(window.__windowId__, 'pos')
+ .then(pos => [
+ pos.x - window.screenLeft,
+ pos.y - window.screenTop,
+ ]);
+ screenOffset = await screenOffsetPromise;
+ logger.debug('screen offset', screenOffset);
};
/**
* Constraints window position to safe screen area, accounting for safe
* margins which could be a system taskbar.
*/
-const constraintPosition = position => {
- let x = position[0];
- let y = position[1];
+const constraintPosition = (pos, size) => {
+ const screenPos = getScreenPosition();
+ const screenSize = getScreenSize();
+ const nextPos = [pos[0], pos[1]];
let relocated = false;
- // Left
- if (x < 0) {
- x = 0;
- relocated = true;
+ for (let i = 0; i < 2; i++) {
+ const leftBoundary = screenPos[i];
+ const rightBoundary = screenPos[i] + screenSize[i];
+ if (pos[i] < leftBoundary) {
+ nextPos[i] = leftBoundary;
+ relocated = true;
+ }
+ else if (pos[i] + size[i] > rightBoundary) {
+ nextPos[i] = rightBoundary - size[i];
+ relocated = true;
+ }
}
- // Right
- else if (x + window.innerWidth > window.screen.availWidth) {
- x = window.screen.availWidth - window.innerWidth;
- relocated = true;
- }
- // Top
- if (y < 0) {
- y = 0;
- relocated = true;
- }
- // Bottom
- else if (y + window.innerHeight > window.screen.availHeight) {
- y = window.screen.availHeight - window.innerHeight;
- relocated = true;
- }
- return [relocated, [x, y]];
+ return [relocated, nextPos];
};
export const dragStartHandler = event => {
@@ -91,6 +189,7 @@ const dragEndHandler = event => {
document.removeEventListener('mousemove', dragMoveHandler);
document.removeEventListener('mouseup', dragEndHandler);
dragging = false;
+ storeWindowGeometry(windowKey);
};
const dragMoveHandler = event => {
@@ -98,9 +197,8 @@ const dragMoveHandler = event => {
return;
}
event.preventDefault();
- setWindowPosition(ref, vecAdd(
+ setWindowPosition(vecAdd(
[event.screenX, event.screenY],
- screenOffset,
dragPointOffset));
};
@@ -127,6 +225,7 @@ const resizeEndHandler = event => {
document.removeEventListener('mousemove', resizeMoveHandler);
document.removeEventListener('mouseup', resizeEndHandler);
resizing = false;
+ storeWindowGeometry(windowKey);
};
const resizeMoveHandler = event => {
@@ -140,7 +239,7 @@ const resizeMoveHandler = event => {
dragPointOffset,
[1, 1])));
// Sane window size values
- size[0] = Math.max(size[0], 250);
- size[1] = Math.max(size[1], 120);
- setWindowSize(ref, size);
+ size[0] = Math.max(size[0], 150);
+ size[1] = Math.max(size[1], 50);
+ setWindowSize(size);
};
diff --git a/tgui/packages/tgui/format.js b/tgui/packages/tgui/format.js
index 8ce1915f51..1f8420860e 100644
--- a/tgui/packages/tgui/format.js
+++ b/tgui/packages/tgui/format.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
import { clamp, round, toFixed } from 'common/math';
const SI_SYMBOLS = [
@@ -28,7 +34,14 @@ const SI_BASE_INDEX = SI_SYMBOLS.indexOf(' ');
* Formats a number to a human readable form, by reducing it to SI units.
* TODO: This is quite a shit code and shit math, needs optimization.
*/
-const formatSiUnit = (value, minBase1000 = -SI_BASE_INDEX, unit = '') => {
+export const formatSiUnit = (
+ value,
+ minBase1000 = -SI_BASE_INDEX,
+ unit = ''
+) => {
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
+ return value;
+ }
const realBase10 = Math.floor(Math.log10(value));
const base10 = Math.floor(Math.max(minBase1000 * 3, realBase10));
const realBase1000 = Math.floor(realBase10 / 3);
diff --git a/tgui/packages/tgui/global.d.ts b/tgui/packages/tgui/global.d.ts
new file mode 100644
index 0000000000..ab8870f15c
--- /dev/null
+++ b/tgui/packages/tgui/global.d.ts
@@ -0,0 +1,95 @@
+interface ByondType {
+ /**
+ * True if javascript is running in BYOND.
+ */
+ IS_BYOND: boolean;
+
+ /**
+ * True if browser is IE8 or lower.
+ */
+ IS_LTE_IE8: boolean;
+
+ /**
+ * True if browser is IE9 or lower.
+ */
+ IS_LTE_IE9: boolean;
+
+ /**
+ * True if browser is IE10 or lower.
+ */
+ IS_LTE_IE10: boolean;
+
+ /**
+ * True if browser is IE11 or lower.
+ */
+ IS_LTE_IE11: boolean;
+
+ /**
+ * Makes a BYOND call.
+ *
+ * If path is empty, this will trigger a Topic call.
+ * You can reference a specific object by setting the "src" parameter.
+ *
+ * See: https://secure.byond.com/docs/ref/skinparams.html
+ */
+ call(path: string, params: object): void;
+
+ /**
+ * Makes an asynchronous BYOND call. Returns a promise.
+ */
+ callAsync(path: string, params: object): Promise;
+
+ /**
+ * Makes a Topic call.
+ *
+ * You can reference a specific object by setting the "src" parameter.
+ */
+ topic(params: object): void;
+
+ /**
+ * Runs a command or a verb.
+ */
+ command(command: string): void;
+
+ /**
+ * Retrieves all properties of the BYOND skin element.
+ *
+ * Returns a promise with a key-value object containing all properties.
+ */
+ winget(id: string): Promise;
+
+ /**
+ * Retrieves all properties of the BYOND skin element.
+ *
+ * Returns a promise with a key-value object containing all properties.
+ */
+ winget(id: string, propName: '*'): Promise;
+
+ /**
+ * Retrieves an exactly one property of the BYOND skin element,
+ * as defined in `propName`.
+ *
+ * Returns a promise with the value of that property.
+ */
+ winget(id: string, propName: string): Promise;
+
+ /**
+ * Retrieves multiple properties of the BYOND skin element,
+ * as listen in the `propNames` array.
+ *
+ * Returns a promise with a key-value object containing listed properties.
+ */
+ winget(id: string, propNames: string[]): Promise;
+
+ /**
+ * Assigns properties to the BYOND skin element.
+ */
+ winset(id: string, props: object): void;
+
+ /**
+ * Sets a property on the BYOND skin element to a certain value.
+ */
+ winset(id: string, propName: string, propValue: any): void;
+};
+
+declare const Byond: ByondType;
diff --git a/tgui/packages/tgui/hotkeys.js b/tgui/packages/tgui/hotkeys.js
index 30c1b910bf..0beff6b028 100644
--- a/tgui/packages/tgui/hotkeys.js
+++ b/tgui/packages/tgui/hotkeys.js
@@ -1,4 +1,9 @@
-import { callByond, IS_IE8 } from './byond';
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
import { createLogger } from './logging';
const logger = createLogger('hotkeys');
@@ -48,6 +53,18 @@ export const KEY_W = 87;
export const KEY_X = 88;
export const KEY_Y = 89;
export const KEY_Z = 90;
+export const KEY_F1 = 112;
+export const KEY_F2 = 113;
+export const KEY_F3 = 114;
+export const KEY_F4 = 115;
+export const KEY_F5 = 116;
+export const KEY_F6 = 117;
+export const KEY_F7 = 118;
+export const KEY_F8 = 119;
+export const KEY_F9 = 120;
+export const KEY_F10 = 121;
+export const KEY_F11 = 122;
+export const KEY_F12 = 123;
export const KEY_EQUAL = 187;
export const KEY_MINUS = 189;
@@ -64,6 +81,18 @@ const NO_PASSTHROUGH_KEYS = [
KEY_TAB,
KEY_CTRL,
KEY_SHIFT,
+ KEY_F1,
+ KEY_F2,
+ KEY_F3,
+ KEY_F4,
+ KEY_F5,
+ KEY_F6,
+ KEY_F7,
+ KEY_F8,
+ KEY_F9,
+ KEY_F10,
+ KEY_F11,
+ KEY_F12,
];
// Tracks the "pressed" state of keys
@@ -83,6 +112,9 @@ const createHotkeyString = (ctrlKey, altKey, shiftKey, keyCode) => {
if (keyCode >= 48 && keyCode <= 90) {
str += String.fromCharCode(keyCode);
}
+ else if (keyCode >= KEY_F1 && keyCode <= KEY_F12) {
+ str += 'F' + (keyCode - 111);
+ }
else {
str += '[' + keyCode + ']';
}
@@ -129,11 +161,11 @@ const handlePassthrough = (e, eventType) => {
// Send this keypress to BYOND
if (eventType === 'keydown' && !keyState[keyCode]) {
logger.debug('passthrough', eventType, keyData);
- return callByond('', { __keydown: keyCode });
+ return Byond.topic({ __keydown: keyCode });
}
if (eventType === 'keyup' && keyState[keyCode]) {
logger.debug('passthrough', eventType, keyData);
- return callByond('', { __keyup: keyCode });
+ return Byond.topic({ __keyup: keyCode });
}
};
@@ -146,41 +178,45 @@ export const releaseHeldKeys = () => {
if (keyState[keyCode]) {
logger.log(`releasing [${keyCode}] key`);
keyState[keyCode] = false;
- callByond('', { __keyup: keyCode });
+ Byond.topic({ __keyup: keyCode });
}
}
};
-const handleHotKey = (e, eventType, dispatch) => {
+const hotKeySubscribers = [];
+
+/**
+ * Subscribes to a certain hotkey, and dispatches a redux action returned
+ * by the callback function.
+ */
+export const subscribeToHotKey = (keyString, fn) => {
+ hotKeySubscribers.push((store, keyData) => {
+ if (keyData.keyString === keyString) {
+ const action = fn(store);
+ if (action) {
+ store.dispatch(action);
+ }
+ }
+ });
+};
+
+const handleHotKey = (e, eventType, store) => {
if (eventType !== 'keyup') {
return;
}
const keyData = getKeyData(e);
const {
- ctrlKey,
- altKey,
keyCode,
hasModifierKeys,
keyString,
} = keyData;
// Dispatch a detected hotkey as a store action
- if (hasModifierKeys && !MODIFIER_KEYS.includes(keyCode)) {
+ if (hasModifierKeys && !MODIFIER_KEYS.includes(keyCode)
+ || keyCode >= KEY_F1 && keyCode <= KEY_F12) {
logger.log(keyString);
- // Fun stuff
- if (ctrlKey && altKey && keyCode === KEY_BACKSPACE) {
- // NOTE: We need to call this in a timeout, because we need a clean
- // stack in order for this to be a fatal error.
- setTimeout(() => {
- throw new Error(
- 'OOPSIE WOOPSIE!! UwU We made a fucky wucky!! A wittle'
- + ' fucko boingo! The code monkeys at our headquarters are'
- + ' working VEWY HAWD to fix this!');
- });
+ for (let subscriberFn of hotKeySubscribers) {
+ subscriberFn(store, keyData);
}
- dispatch({
- type: 'hotKey',
- payload: keyData,
- });
}
};
@@ -218,18 +254,17 @@ const subscribeToKeyPresses = listenerFn => {
// Middleware
export const hotKeyMiddleware = store => {
- const { dispatch } = store;
// Subscribe to key events
subscribeToKeyPresses((e, eventType) => {
// IE8: Can't determine the focused element, so by extension it passes
// keypresses when inputs are focused.
- if (!IS_IE8) {
+ if (!Byond.IS_LTE_IE8) {
handlePassthrough(e, eventType);
}
- handleHotKey(e, eventType, dispatch);
+ handleHotKey(e, eventType, store);
});
// IE8: focusin/focusout only available on IE9+
- if (!IS_IE8) {
+ if (!Byond.IS_LTE_IE8) {
// Clean up when browser window completely loses focus
subscribeToLossOfFocus(() => {
releaseHeldKeys();
@@ -238,27 +273,3 @@ export const hotKeyMiddleware = store => {
// Pass through store actions (do nothing)
return next => action => next(action);
};
-
-// Reducer
-export const hotKeyReducer = (state, action) => {
- const { type, payload } = action;
- if (type === 'hotKey') {
- const { ctrlKey, altKey, keyCode } = payload;
- // Toggle kitchen sink mode
- if (ctrlKey && altKey && keyCode === KEY_EQUAL) {
- return {
- ...state,
- showKitchenSink: !state.showKitchenSink,
- };
- }
- // Toggle layout debugger
- if (ctrlKey && altKey && keyCode === KEY_MINUS) {
- return {
- ...state,
- debugLayout: !state.debugLayout,
- };
- }
- return state;
- }
- return state;
-};
diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js
index fb778ac58c..e4ec6d285d 100644
--- a/tgui/packages/tgui/index.js
+++ b/tgui/packages/tgui/index.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
// Polyfills
import 'core-js/es';
import 'core-js/web/immediate';
@@ -12,75 +18,79 @@ import './polyfills/inferno';
// Themes
import './styles/main.scss';
+import './styles/themes/abductor.scss';
+import './styles/themes/cardtable.scss';
+import './styles/themes/hackerman.scss';
import './styles/themes/malfunction.scss';
import './styles/themes/ntos.scss';
-import './styles/themes/hackerman.scss';
+import './styles/themes/paper.scss';
import './styles/themes/retro.scss';
import './styles/themes/syndicate.scss';
import './styles/themes/clockcult.scss';
-import './styles/themes/paper.scss';
-import { loadCSS } from 'fg-loadcss';
+import { perf } from 'common/perf';
import { render } from 'inferno';
import { setupHotReloading } from 'tgui-dev-server/link/client';
-import { backendUpdate } from './backend';
-import { IS_IE8 } from './byond';
+import { backendUpdate, backendSuspendSuccess, selectBackend, sendMessage } from './backend';
import { setupDrag } from './drag';
import { logger } from './logging';
import { createStore, StoreProvider } from './store';
-const enteredBundleAt = Date.now();
+perf.mark('inception', window.__inception__);
+perf.mark('init');
+
const store = createStore();
let reactRoot;
let initialRender = true;
const renderLayout = () => {
- // Mark the beginning of the render
- let startedAt;
- if (process.env.NODE_ENV !== 'production') {
- startedAt = Date.now();
- }
- try {
- const state = store.getState();
- // Initial render setup
- if (initialRender) {
- logger.log('initial render', state);
- // Setup dragging
- setupDrag(state);
+ perf.mark('render/start');
+ const state = store.getState();
+ const { suspended, assets } = selectBackend(state);
+ // Initial render setup
+ if (initialRender) {
+ logger.log('initial render', state);
+ // Setup dragging
+ if (initialRender !== 'recycled') {
+ setupDrag();
}
- // Start rendering
- const { getRoutedComponent } = require('./routes');
- const Component = getRoutedComponent(state);
- const element = (
-
-
-
- );
- if (!reactRoot) {
- reactRoot = document.getElementById('react-root');
- }
- render(element, reactRoot);
}
- catch (err) {
- logger.error('rendering error', err);
- throw err;
+ // Start rendering
+ const { getRoutedComponent } = require('./routes');
+ const Component = getRoutedComponent(state);
+ const element = (
+
+
+
+ );
+ if (!reactRoot) {
+ reactRoot = document.getElementById('react-root');
}
+ render(element, reactRoot);
+ if (suspended) {
+ return;
+ }
+ perf.mark('render/finish');
// Report rendering time
if (process.env.NODE_ENV !== 'production') {
- const finishedAt = Date.now();
- if (initialRender) {
+ if (initialRender === 'recycled') {
+ logger.log('rendered in',
+ perf.measure('render/start', 'render/finish'));
+ }
+ else if (initialRender) {
logger.debug('serving from:', location.href);
- logger.debug('bundle entered in', timeDiff(
- window.__inception__, enteredBundleAt));
- logger.debug('initialized in', timeDiff(
- enteredBundleAt, startedAt));
- logger.log('rendered in', timeDiff(
- startedAt, finishedAt));
- logger.log('fully loaded in', timeDiff(
- window.__inception__, finishedAt));
+ logger.debug('bundle entered in',
+ perf.measure('inception', 'init'));
+ logger.debug('initialized in',
+ perf.measure('init', 'render/start'));
+ logger.log('rendered in',
+ perf.measure('render/start', 'render/finish'));
+ logger.log('fully loaded in',
+ perf.measure('inception', 'render/finish'));
}
else {
- logger.debug('rendered in', timeDiff(startedAt, finishedAt));
+ logger.debug('rendered in',
+ perf.measure('render/start', 'render/finish'));
}
}
if (initialRender) {
@@ -88,12 +98,6 @@ const renderLayout = () => {
}
};
-const timeDiff = (startedAt, finishedAt) => {
- const diff = finishedAt - startedAt;
- const diffFrames = (diff / 16.6667).toFixed(2);
- return `${diff}ms (${diffFrames} frames)`;
-};
-
// Parse JSON and report all abnormal JSON strings coming from BYOND
const parseStateJson = json => {
let reviver = (key, value) => {
@@ -106,7 +110,7 @@ const parseStateJson = json => {
};
// IE8: No reviver for you!
// See: https://stackoverflow.com/questions/1288962
- if (IS_IE8) {
+ if (Byond.IS_LTE_IE8) {
reviver = undefined;
}
try {
@@ -127,14 +131,36 @@ const setupApp = () => {
});
// Subscribe for bankend updates
- window.update = stateJson => {
- // NOTE: stateJson can be an object only if called manually from console.
+ window.update = messageJson => {
+ const { suspended } = selectBackend(store.getState());
+ // NOTE: messageJson can be an object only if called manually from console.
// This is useful for debugging tgui in external browsers, like Chrome.
- const state = typeof stateJson === 'string'
- ? parseStateJson(stateJson)
- : stateJson;
- // Backend update dispatches a store action
- store.dispatch(backendUpdate(state));
+ const message = typeof messageJson === 'string'
+ ? parseStateJson(messageJson)
+ : messageJson;
+ logger.debug(`received message '${message?.type}'`);
+ const { type, payload } = message;
+ if (type === 'update') {
+ if (suspended) {
+ logger.log('resuming');
+ initialRender = 'recycled';
+ }
+ // Backend update dispatches a store action
+ store.dispatch(backendUpdate(payload));
+ return;
+ }
+ if (type === 'suspend') {
+ store.dispatch(backendSuspendSuccess());
+ return;
+ }
+ if (type === 'ping') {
+ sendMessage({
+ type: 'pingReply',
+ });
+ return;
+ }
+ // Pass the message directly to the store
+ store.dispatch(message);
};
// Enable hot module reloading
@@ -157,9 +183,26 @@ const setupApp = () => {
}
window.update(stateJson);
}
+};
- // Dynamically load font-awesome from browser's cache
- loadCSS('font-awesome.css');
+// Setup a fatal error reporter
+window.__logger__ = {
+ fatal: (error, stack) => {
+ // Get last state for debugging purposes
+ const backendState = selectBackend(store.getState());
+ const reportedState = {
+ config: backendState.config,
+ suspended: backendState.suspended,
+ suspending: backendState.suspending,
+ };
+ // Send to development server
+ logger.log('FatalError:', error || stack);
+ logger.log('State:', reportedState);
+ // Append this data to the stack
+ stack += '\nState: ' + JSON.stringify(reportedState);
+ // Return an updated stack
+ return stack;
+ },
};
if (document.readyState === 'loading') {
diff --git a/tgui/packages/tgui/interfaces/AbductorConsole.js b/tgui/packages/tgui/interfaces/AbductorConsole.js
new file mode 100644
index 0000000000..de750bc9ca
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AbductorConsole.js
@@ -0,0 +1,157 @@
+import { GenericUplink } from './Uplink';
+import { useBackend, useSharedState } from '../backend';
+import { Button, LabeledList, NoticeBox, Section, Tabs } from '../components';
+import { Fragment } from 'inferno';
+import { Window } from '../layouts';
+
+export const AbductorConsole = (props, context) => {
+ const [tab, setTab] = useSharedState(context, 'tab', 1);
+ return (
+
+
+
+ setTab(1)}>
+ Abductsoft 3000
+
+ setTab(2)}>
+ Mission Settings
+
+
+ {tab === 1 && (
+
+ )}
+ {tab === 2 && (
+
+
+
+
+ )}
+
+
+ );
+};
+
+const Abductsoft = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ experiment,
+ points,
+ credits,
+ } = data;
+
+ if (!experiment) {
+ return (
+
+ No Experiment Machine Detected
+
+ );
+ }
+
+ return (
+
+
+
+
+ );
+};
+
+const EmergencyTeleporter = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ pad,
+ gizmo,
+ } = data;
+
+ if (!pad) {
+ return (
+
+ No Telepad Detected
+
+ );
+ }
+
+ return (
+ act('teleporter_send')} />
+ )}>
+
+
+ act('teleporter_retrieve')} />
+
+
+
+ );
+};
+
+const VestSettings = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ vest,
+ vest_mode,
+ vest_lock,
+ } = data;
+
+ if (!vest) {
+ return (
+
+ No Agent Vest Detected
+
+ );
+ }
+
+ return (
+ act('toggle_vest')} />
+ )}>
+
+
+ act('flip_vest')} />
+
+
+ act('select_disguise')} />
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/Achievements.js b/tgui/packages/tgui/interfaces/Achievements.js
index 464708d248..512c39fb59 100644
--- a/tgui/packages/tgui/interfaces/Achievements.js
+++ b/tgui/packages/tgui/interfaces/Achievements.js
@@ -12,7 +12,11 @@ export const Achievements = (props, context) => {
const achievements = data.achievements
.filter(x => x.category === selectedCategory);
return (
-
+
{categories.map(category => (
diff --git a/tgui/packages/tgui/interfaces/AiAirlock.js b/tgui/packages/tgui/interfaces/AiAirlock.js
index 4886e71034..a48ce24402 100644
--- a/tgui/packages/tgui/interfaces/AiAirlock.js
+++ b/tgui/packages/tgui/interfaces/AiAirlock.js
@@ -24,7 +24,9 @@ export const AiAirlock = (props, context) => {
const statusBackup = dangerMap[data.power.backup] || dangerMap[0];
const statusElectrify = dangerMap[data.shock] || dangerMap[0];
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/AiRestorer.js b/tgui/packages/tgui/interfaces/AiRestorer.js
index e91d392d10..9dfacda0c8 100644
--- a/tgui/packages/tgui/interfaces/AiRestorer.js
+++ b/tgui/packages/tgui/interfaces/AiRestorer.js
@@ -5,7 +5,10 @@ import { Window } from '../layouts';
export const AiRestorer = () => {
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/AirAlarm.js b/tgui/packages/tgui/interfaces/AirAlarm.js
index c6f440985a..e669d8d948 100644
--- a/tgui/packages/tgui/interfaces/AirAlarm.js
+++ b/tgui/packages/tgui/interfaces/AirAlarm.js
@@ -6,12 +6,16 @@ import { Box, Button, LabeledList, NumberInput, Section } from '../components';
import { getGasLabel } from '../constants';
import { Window } from '../layouts';
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
+import { Vent, Scrubber } from './common/AtmosControls';
export const AirAlarm = (props, context) => {
const { act, data } = useBackend(context);
const locked = data.locked && !data.siliconUser;
return (
-
+
@@ -202,108 +206,6 @@ const AirAlarmControlVents = (props, context) => {
));
};
-const Vent = (props, context) => {
- const { vent } = props;
- const { act } = useBackend(context);
- const {
- id_tag,
- long_name,
- power,
- checks,
- excheck,
- incheck,
- direction,
- external,
- internal,
- extdefault,
- intdefault,
- } = vent;
- return (
- act('power', {
- id_tag,
- val: Number(!power),
- })} />
- )}>
-
-
- {direction === 'release' ? 'Pressurizing' : 'Releasing'}
-
-
- act('incheck', {
- id_tag,
- val: checks,
- })} />
- act('excheck', {
- id_tag,
- val: checks,
- })} />
-
- {!!incheck && (
-
- act('set_internal_pressure', {
- id_tag,
- value,
- })} />
- act('reset_internal_pressure', {
- id_tag,
- })} />
-
- )}
- {!!excheck && (
-
- act('set_external_pressure', {
- id_tag,
- value,
- })} />
- act('reset_external_pressure', {
- id_tag,
- })} />
-
- )}
-
-
- );
-};
-
-
// Scrubbers
// --------------------------------------------------------
@@ -320,71 +222,6 @@ const AirAlarmControlScrubbers = (props, context) => {
));
};
-const Scrubber = (props, context) => {
- const { scrubber } = props;
- const { act } = useBackend(context);
- const {
- long_name,
- power,
- scrubbing,
- id_tag,
- widenet,
- filter_types,
- } = scrubber;
- return (
- act('power', {
- id_tag,
- val: Number(!power),
- })} />
- )}>
-
-
- act('scrubbing', {
- id_tag,
- val: Number(!scrubbing),
- })} />
- act('widenet', {
- id_tag,
- val: Number(!widenet),
- })} />
-
-
- {scrubbing
- && filter_types.map(filter => (
- act('toggle_filter', {
- id_tag,
- val: filter.gas_id,
- })} />
- ))
- || 'N/A'}
-
-
-
- );
-};
-
-
// Modes
// --------------------------------------------------------
diff --git a/tgui/packages/tgui/interfaces/AirlockElectronics.js b/tgui/packages/tgui/interfaces/AirlockElectronics.js
index e291052e25..a448f55e9b 100644
--- a/tgui/packages/tgui/interfaces/AirlockElectronics.js
+++ b/tgui/packages/tgui/interfaces/AirlockElectronics.js
@@ -12,7 +12,9 @@ export const AirlockElectronics = (props, context) => {
const regions = data.regions || [];
const accesses = data.accesses || [];
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/Apc.js b/tgui/packages/tgui/interfaces/Apc.js
index 0f44e3509d..60a11789bc 100644
--- a/tgui/packages/tgui/interfaces/Apc.js
+++ b/tgui/packages/tgui/interfaces/Apc.js
@@ -6,7 +6,10 @@ import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
export const Apc = (props, context) => {
return (
-
+
@@ -82,7 +85,6 @@ const ApcContent = (props, context) => {
);
}
-
return (
@@ -182,57 +184,35 @@ const ApcContent = (props, context) => {
onClick={() => act('overload')} />
)}>
- act('cover')} />
- )} />
- act('emergency_lighting')} />
- )} />
- act('toggle_nightshift')} />
- )} />
-
- {data.hijackable && (
-
+
+ act('hijack')} />
- act('lockdown')} />
+ icon={data.coverLocked ? 'lock' : 'unlock'}
+ content={data.coverLocked ? 'Engaged' : 'Disengaged'}
+ disabled={locked}
+ onClick={() => act('cover')} />
+ )} />
+ act('drain')} />
-
- )} />
- )}
+ content={data.emergencyLights ? 'Enabled' : 'Disabled'}
+ disabled={locked}
+ onClick={() => act('emergency_lighting')} />
+ )} />
+ act('toggle_nightshift')} />
+ )} />
+
+
);
};
diff --git a/tgui/packages/tgui/interfaces/ApcControl.js b/tgui/packages/tgui/interfaces/ApcControl.js
new file mode 100644
index 0000000000..449101d529
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/ApcControl.js
@@ -0,0 +1,306 @@
+import { map, sortBy } from 'common/collections';
+import { flow } from 'common/fp';
+import { pureComponentHooks } from 'common/react';
+import { useBackend, useLocalState } from '../backend';
+import { Box, Button, Dimmer, Flex, Icon, Table, Tabs } from '../components';
+import { Fragment, Window } from '../layouts';
+import { AreaCharge, powerRank } from './PowerMonitor';
+
+export const ApcControl = (props, context) => {
+ const { data } = useBackend(context);
+ return (
+
+ {data.authenticated === 1 && (
+
+ )}
+ {data.authenticated === 0 && (
+
+ )}
+
+ );
+};
+
+const ApcLoggedOut = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { emagged } = data;
+ const text = emagged === 1 ? 'Open' : 'Log In';
+ return (
+
+ act('log-in')} />
+
+ );
+};
+
+const ApcLoggedIn = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { restoring } = data;
+ const [
+ tabIndex,
+ setTabIndex,
+ ] = useLocalState(context, 'tab-index', 1);
+ return (
+
+
+ {
+ setTabIndex(1);
+ act('check-apcs');
+ }}>
+ APC Control Panel
+
+ {
+ setTabIndex(2);
+ act('check-logs');
+ }}>
+ Log View Panel
+
+
+ {restoring === 1 && (
+
+
+ {' Resetting...'}
+
+ )}
+ {tabIndex === 1 && (
+
+
+
+
+
+
+
+
+ )}
+ {tabIndex === 2 && (
+
+
+
+
+
+ )}
+
+ );
+};
+
+const ControlPanel = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ emagged,
+ logging,
+ } = data;
+ const [
+ sortByField,
+ setSortByField,
+ ] = useLocalState(context, 'sortByField', null);
+ return (
+
+
+
+ Sort by:
+
+ setSortByField(sortByField !== 'name' && 'name')} />
+ setSortByField(
+ sortByField !== 'charge' && 'charge'
+ )} />
+ setSortByField(sortByField !== 'draw' && 'draw')} />
+
+
+
+ {emagged === 1 && (
+
+ act('toggle-logs')}
+ />
+ act('restore-console')}
+ />
+
+ )}
+ act('log-out')}
+ />
+
+
+ );
+};
+
+const ApcControlScene = (props, context) => {
+ const { data, act } = useBackend(context);
+
+ const [
+ sortByField,
+ ] = useLocalState(context, 'sortByField', null);
+
+ const apcs = flow([
+ map((apc, i) => ({
+ ...apc,
+ // Generate a unique id
+ id: apc.name + i,
+ })),
+ sortByField === 'name' && sortBy(apc => apc.name),
+ sortByField === 'charge' && sortBy(apc => -apc.charge),
+ sortByField === 'draw' && sortBy(
+ apc => -powerRank(apc.load),
+ apc => -parseFloat(apc.load)),
+ ])(data.apcs);
+ return (
+
+
+
+ On/Off
+
+
+ Area
+
+
+ Charge
+
+
+ Draw
+
+
+ Eqp
+
+
+ Lgt
+
+
+ Env
+
+
+ {apcs.map((apc, i) => (
+
+
+ act('breaker', {
+ ref: apc.ref,
+ })}
+ />
+
+
+ act('access-apc', {
+ ref: apc.ref,
+ })}>
+ {apc.name}
+
+
+
+
+
+
+ {apc.load}
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ );
+};
+
+const LogPanel = (props, context) => {
+ const { data } = useBackend(context);
+
+ const logs = flow([
+ map((line, i) => ({
+ ...line,
+ // Generate a unique id
+ id: line.entry + i,
+ })),
+ logs => logs.reverse(),
+ ])(data.logs);
+ return (
+
+ {logs.map(line => (
+
+ {line.entry}
+
+ ))}
+
+ );
+};
+
+const AreaStatusColorButton = props => {
+ const { target, status, apc, act } = props;
+ const power = Boolean(status & 2);
+ const mode = Boolean(status & 1);
+ return (
+ act('toggle-minor', {
+ type: target,
+ value: statusChange(status),
+ ref: apc.ref,
+ })}
+ />
+ );
+};
+
+const statusChange = status => {
+ // mode flip power flip both flip
+ // 0, 2, 3
+ return status === 0 ? 2 : status === 2 ? 3 : 0;
+};
+
+AreaStatusColorButton.defaultHooks = pureComponentHooks;
+
diff --git a/tgui/packages/tgui/interfaces/AtmosAlertConsole.js b/tgui/packages/tgui/interfaces/AtmosAlertConsole.js
index 58d4cc7456..1d55ccf595 100644
--- a/tgui/packages/tgui/interfaces/AtmosAlertConsole.js
+++ b/tgui/packages/tgui/interfaces/AtmosAlertConsole.js
@@ -7,7 +7,10 @@ export const AtmosAlertConsole = (props, context) => {
const priorityAlerts = data.priority || [];
const minorAlerts = data.minor || [];
return (
-
+
@@ -25,7 +28,7 @@ export const AtmosAlertConsole = (props, context) => {
onClick={() => act('clear', { zone: alert })} />
))}
- {minorAlerts.length > 0 && (
+ {minorAlerts.length === 0 && (
No Minor Alerts
diff --git a/tgui/packages/tgui/interfaces/AtmosControlConsole.js b/tgui/packages/tgui/interfaces/AtmosControlConsole.js
index 7a35db6870..454866aa2f 100644
--- a/tgui/packages/tgui/interfaces/AtmosControlConsole.js
+++ b/tgui/packages/tgui/interfaces/AtmosControlConsole.js
@@ -8,7 +8,10 @@ export const AtmosControlConsole = (props, context) => {
const { act, data } = useBackend(context);
const sensors = data.sensors || [];
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/AtmosFilter.js b/tgui/packages/tgui/interfaces/AtmosFilter.js
index 1bc7e8f63b..8b382cbad9 100644
--- a/tgui/packages/tgui/interfaces/AtmosFilter.js
+++ b/tgui/packages/tgui/interfaces/AtmosFilter.js
@@ -7,7 +7,9 @@ export const AtmosFilter = (props, context) => {
const { act, data } = useBackend(context);
const filterTypes = data.filter_types || [];
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/AtmosMixer.js b/tgui/packages/tgui/interfaces/AtmosMixer.js
index 067118dea1..9ac9dbb878 100644
--- a/tgui/packages/tgui/interfaces/AtmosMixer.js
+++ b/tgui/packages/tgui/interfaces/AtmosMixer.js
@@ -5,7 +5,9 @@ import { Window } from '../layouts';
export const AtmosMixer = (props, context) => {
const { act, data } = useBackend(context);
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/AtmosPump.js b/tgui/packages/tgui/interfaces/AtmosPump.js
index ec3285cd44..3d3a7de10c 100644
--- a/tgui/packages/tgui/interfaces/AtmosPump.js
+++ b/tgui/packages/tgui/interfaces/AtmosPump.js
@@ -5,7 +5,9 @@ import { Window } from '../layouts';
export const AtmosPump = (props, context) => {
const { act, data } = useBackend(context);
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/AtmosRelief.js b/tgui/packages/tgui/interfaces/AtmosRelief.js
new file mode 100644
index 0000000000..fd74c5bbd7
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AtmosRelief.js
@@ -0,0 +1,61 @@
+import { useBackend } from '../backend';
+import { Button, LabeledList, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
+
+export const AtmosRelief = (props, context) => { // rebranded AtmoPump
+ const { act, data } = useBackend(context);
+ return (
+
+
+
+
+
+ act('open_pressure', {
+ pressure: value,
+ })} />
+ act('open_pressure', {
+ pressure: 'max',
+ })} />
+
+
+ act('close_pressure', {
+ pressure: value,
+ })} />
+ act('close_pressure', {
+ pressure: 'max',
+ })} />
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/AutomatedAnnouncement.js b/tgui/packages/tgui/interfaces/AutomatedAnnouncement.js
index d7b517d961..0baea4de97 100644
--- a/tgui/packages/tgui/interfaces/AutomatedAnnouncement.js
+++ b/tgui/packages/tgui/interfaces/AutomatedAnnouncement.js
@@ -17,7 +17,10 @@ export const AutomatedAnnouncement = (props, context) => {
newhead,
} = data;
return (
-
+
{
station_name,
} = data;
return (
-
+
- {
amount,
} = data;
return (
-
+
{
+ const { data } = useBackend(context);
+ const {
+ beaker,
+ processing,
+ } = data;
+ return (
+
+ {!!processing && (
+
+
+ {' Processing...'}
+
+ )}
+
+ {!beaker && (
+ No Container
+ )}
+ {!!beaker && (
+
+ )}
+
+
+ );
+};
+
+export const BiogeneratorContent = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ biomass,
+ can_process,
+ categories = [],
+ } = data;
+ const [
+ searchText,
+ setSearchText,
+ ] = useLocalState(context, 'searchText', '');
+ const [
+ selectedCategory,
+ setSelectedCategory,
+ ] = useLocalState(context, 'category', categories[0]?.name);
+ const testSearch = createSearch(searchText, item => {
+ return item.name;
+ });
+ const items = searchText.length > 0
+ // Flatten all categories and apply search to it
+ && categories
+ .flatMap(category => category.items || [])
+ .filter(testSearch)
+ .filter((item, i) => i < MAX_SEARCH_RESULTS)
+ // Select a category and show all items in it
+ || categories
+ .find(category => category.name === selectedCategory)
+ ?.items
+ // If none of that results in a list, return an empty list
+ || [];
+ return (
+
+ );
+};
+
+const ItemList = (props, context) => {
+ const { act } = useBackend(context);
+ const [
+ hoveredItem,
+ setHoveredItem,
+ ] = useLocalState(context, 'hoveredItem', {});
+ const hoveredCost = hoveredItem && hoveredItem.cost || 0;
+ // Append extra hover data to items
+ const items = props.items.map(item => {
+ const [
+ amount,
+ setAmount,
+ ] = useLocalState(context, "amount" + item.name, 1);
+ const notSameItem = hoveredItem && hoveredItem.name !== item.name;
+ const notEnoughHovered = props.biomass - hoveredCost
+ * hoveredItem.amount < item.cost * amount;
+ const disabledDueToHovered = notSameItem && notEnoughHovered;
+ const disabled = props.biomass < item.cost * amount || disabledDueToHovered;
+ return {
+ ...item,
+ disabled,
+ amount,
+ setAmount,
+ };
+ });
+ return items.map(item => (
+
+
+
+ {' '}{item.name}
+
+
+ item.setAmount(value)} />
+
+
+ setHoveredItem(item)}
+ onmouseout={() => setHoveredItem({})}
+ onClick={() => act('create', {
+ id: item.id,
+ amount: item.amount,
+ })} />
+
+
+ ));
+};
diff --git a/tgui/packages/tgui/interfaces/BlackmarketUplink.js b/tgui/packages/tgui/interfaces/BlackmarketUplink.js
index 0d51cc4c36..8cfcff5756 100644
--- a/tgui/packages/tgui/interfaces/BlackmarketUplink.js
+++ b/tgui/packages/tgui/interfaces/BlackmarketUplink.js
@@ -1,5 +1,5 @@
import { useBackend } from '../backend';
-import { AnimatedNumber, Box, Button, Flex, Modal, Section, Table, Tabs } from '../components';
+import { AnimatedNumber, Box, Button, Flex, Modal, Section, Tabs } from '../components';
import { formatMoney } from '../format';
import { Window } from '../layouts';
@@ -15,6 +15,8 @@ export const BlackMarketUplink = (props, context) => {
} = data;
return (
diff --git a/tgui/packages/tgui/interfaces/BluespaceArtillery.js b/tgui/packages/tgui/interfaces/BluespaceArtillery.js
index 6d2d94f7bf..91f7c50761 100644
--- a/tgui/packages/tgui/interfaces/BluespaceArtillery.js
+++ b/tgui/packages/tgui/interfaces/BluespaceArtillery.js
@@ -12,7 +12,9 @@ export const BluespaceArtillery = (props, context) => {
target,
} = data;
return (
-
+
{!!notice && (
diff --git a/tgui/packages/tgui/interfaces/BluespaceLocator.js b/tgui/packages/tgui/interfaces/BluespaceLocator.js
new file mode 100644
index 0000000000..ef063a4942
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/BluespaceLocator.js
@@ -0,0 +1,101 @@
+import { useBackend, useSharedState } from '../backend';
+import { Icon, ProgressBar, Tabs } from '../components';
+import { Window } from '../layouts';
+
+const directionToIcon = {
+ north: 0,
+ northeast: 45,
+ east: 90,
+ southeast: 135,
+ south: 180,
+ southwest: 225,
+ west: 270,
+ northwest: 315,
+};
+
+export const BluespaceLocator = (props, context) => {
+ const [tab, setTab] = useSharedState(context, "tab", "implant");
+ return (
+
+
+
+ setTab("implant")}>
+ Implants
+
+ setTab("beacon")}>
+ Teleporter Beacons
+
+
+ {tab === "beacon" && (
+
+ )
+ || tab === "implant" && (
+
+ )}
+
+
+ );
+};
+
+const TeleporterBeacons = (props, context) => {
+ const { data } = useBackend(context);
+ const { telebeacons } = data;
+ return (
+ telebeacons.map(beacon => (
+
+ ))
+ );
+};
+
+const TrackingImplants = (props, context) => {
+ const { data } = useBackend(context);
+ const { trackimplants } = data;
+ return (
+ trackimplants.map(implant => (
+
+ ))
+ );
+};
+
+const SignalLocator = (props, context) => {
+ const { data } = useBackend(context);
+ const { trackingrange } = data;
+ const {
+ name,
+ direction,
+ distance,
+ } = props;
+ return (
+
+ {name}
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/BorgPanel.js b/tgui/packages/tgui/interfaces/BorgPanel.js
index 1661940196..7e7aa60d83 100644
--- a/tgui/packages/tgui/interfaces/BorgPanel.js
+++ b/tgui/packages/tgui/interfaces/BorgPanel.js
@@ -13,7 +13,11 @@ export const BorgPanel = (props, context) => {
const ais = data.ais || [];
const laws = data.laws || [];
return (
-
+
{
const { act, data } = useBackend(context);
return (
-
+
{
nextCameraName,
] = prevNextCamera(cameras, activeCamera);
return (
-
+
@@ -81,7 +84,6 @@ export const CameraConsole = (props, context) => {
className="CameraConsole__map"
params={{
id: mapRef,
- parent: config.window,
type: 'map',
}} />
diff --git a/tgui/packages/tgui/interfaces/Canister.js b/tgui/packages/tgui/interfaces/Canister.js
index e369f798cd..b94dc8ef8f 100644
--- a/tgui/packages/tgui/interfaces/Canister.js
+++ b/tgui/packages/tgui/interfaces/Canister.js
@@ -21,7 +21,9 @@ export const Canister = (props, context) => {
restricted,
} = data;
return (
-
+
this.clickwrapper(e)}>
Canvas failed to render.
@@ -76,10 +76,20 @@ class PaintCanvas extends Component {
}
}
+const getImageSize = value => {
+ const width = value.length;
+ const height = width !== 0 ? value[0].length : 0;
+ return [width, height];
+};
+
export const Canvas = (props, context) => {
const { act, data } = useBackend(context);
+ const [width, height] = getImageSize(data.grid);
return (
-
+
{
const cart = data.cart || [];
const requests = data.requests || [];
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/CargoBountyConsole.js b/tgui/packages/tgui/interfaces/CargoBountyConsole.js
new file mode 100644
index 0000000000..ad485231eb
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/CargoBountyConsole.js
@@ -0,0 +1,119 @@
+import { useBackend } from '../backend';
+import { AnimatedNumber, Box, Button, Section, Table } from '../components';
+import { formatMoney } from '../format';
+import { Window } from '../layouts';
+
+export const CargoBountyConsole = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ bountydata = [],
+ } = data;
+ return (
+
+
+ }
+ buttons={(
+ act('Print')} />
+ )}>
+
+
+
+ Bounty Object
+
+
+ Description
+
+
+ Progress
+
+
+ Value
+
+
+ Claim
+
+
+ {bountydata.map(bounty => (
+
+
+ {bounty.name}
+
+
+ {bounty.description}
+
+
+ {bounty.priority === 1
+ ? High Priority
+ : ""}
+ {bounty.completion_string}
+
+
+ {bounty.reward_string}
+
+
+ act('ClaimBounty', {
+ bounty: bounty.bounty_ref,
+ })} />
+
+
+ ))}
+
+
+
+
+ );
+};
+
+const BountyHeader = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ stored_cash,
+ } = data;
+ return (
+
+ formatMoney(value)} />
+ {' credits'}
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/CargoExpress.js b/tgui/packages/tgui/interfaces/CargoExpress.js
index 4e4ccf6a88..685569f6d5 100644
--- a/tgui/packages/tgui/interfaces/CargoExpress.js
+++ b/tgui/packages/tgui/interfaces/CargoExpress.js
@@ -8,7 +8,10 @@ import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
export const CargoExpress = (props, context) => {
const { act, data } = useBackend(context);
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/CargoHoldTerminal.js b/tgui/packages/tgui/interfaces/CargoHoldTerminal.js
index aaf66e701c..b20c0c9f9b 100644
--- a/tgui/packages/tgui/interfaces/CargoHoldTerminal.js
+++ b/tgui/packages/tgui/interfaces/CargoHoldTerminal.js
@@ -12,7 +12,10 @@ export const CargoHoldTerminal = (props, context) => {
status_report,
} = data;
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/CellularEmporium.js b/tgui/packages/tgui/interfaces/CellularEmporium.js
index 8e8d58cb94..07989d8bad 100644
--- a/tgui/packages/tgui/interfaces/CellularEmporium.js
+++ b/tgui/packages/tgui/interfaces/CellularEmporium.js
@@ -7,7 +7,10 @@ export const CellularEmporium = (props, context) => {
const { act, data } = useBackend(context);
const { abilities } = data;
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher.js b/tgui/packages/tgui/interfaces/CentcomPodLauncher.js
index 7b21239144..05982b2fbf 100644
--- a/tgui/packages/tgui/interfaces/CentcomPodLauncher.js
+++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher.js
@@ -6,7 +6,11 @@ import { Window } from '../layouts';
export const CentcomPodLauncher = () => {
return (
-
+
@@ -57,6 +61,42 @@ export const CentcomPodLauncherContent = (props, context) => {
`}
onClick={() => act('bay5')} />
+ {!!data.effectReverse && (
+
+ act('pickDropoffTurf')} />
+ act('clearDropoffTurf')} />
+
+ Reverse Drop-off Location:
+ {data.dropoff_turf ? data.dropoff_turf : 'None'}
+
+
+ )}
+ {!data.effectReverse && (
+
+
+ [Enable Reverse Mode for this feature]
+
+
+ )}
{
disabled={!data.oldArea}
onClick={() => act('teleportBack')} />
-
+
{
onClick={() => act('damageGib')} />
+ act('effectShrapnel')} />
{
This gondola can control when he wants to deliver his supplies
if he has a smart enough mind, so offer up his body to ghosts
for maximum enjoyment. (Make sure to turn off bluespace and
- set a arbitrarily high open-time if you do!
+ set an arbitrarily high open-time if you do!
`}
onClick={() => act('styleGondola')} />
{
const { act, data } = useBackend(context);
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.js b/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.js
index 94b939eeae..0e073ba885 100644
--- a/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.js
+++ b/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.js
@@ -13,7 +13,10 @@ export const ChemDebugSynthesizer = (props, context) => {
beakerContents = [],
} = data;
return (
-
+
{
|| data.beakerContents
|| [];
return (
-
+
{
key={recipe.name}
icon="tint"
width="129.5px"
- lineHeight="21px"
+ lineHeight={1.75}
content={recipe.name}
onClick={() => act('dispense_recipe', {
recipe: recipe.name,
@@ -119,7 +122,7 @@ export const ChemDispenser = (props, context) => {
key={chemical.id}
icon="tint"
width="129.5px"
- lineHeight="21px"
+ lineHeight={1.75}
content={chemical.title}
onClick={() => act('dispense', {
reagent: chemical.id,
diff --git a/tgui/packages/tgui/interfaces/ChemFilter.js b/tgui/packages/tgui/interfaces/ChemFilter.js
index 18c99adef4..ed72dbd68c 100644
--- a/tgui/packages/tgui/interfaces/ChemFilter.js
+++ b/tgui/packages/tgui/interfaces/ChemFilter.js
@@ -51,7 +51,10 @@ export const ChemFilter = (props, context) => {
const [leftName, setLeftName] = useLocalState(context, 'leftName', '');
const [rightName, setRightName] = useLocalState(context, 'rightName', '');
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/ChemHeater.js b/tgui/packages/tgui/interfaces/ChemHeater.js
index b49349d4d7..998ac3e708 100644
--- a/tgui/packages/tgui/interfaces/ChemHeater.js
+++ b/tgui/packages/tgui/interfaces/ChemHeater.js
@@ -18,7 +18,10 @@ export const ChemHeater = (props, context) => {
currentpH,
} = data;
return (
-
+
{
const { data } = useBackend(context);
const { screen } = data;
return (
-
+
{screen === 'analyze' && (
@@ -220,7 +223,6 @@ const PackagingControls = (props, context) => {
const [
pillAmount,
setPillAmount,
-
] = useSharedState(context, 'pillAmount', 1);
const [
patchAmount,
@@ -374,79 +376,56 @@ const AnalysisResults = (props, context) => {
screen: 'home',
})} />
)}>
- {!fermianalyze && (
-
-
- {analyzeVars.name}
-
-
- {analyzeVars.state}
-
-
-
- {analyzeVars.color}
-
-
- {analyzeVars.description}
-
-
- {analyzeVars.metaRate} u/minute
-
-
- {analyzeVars.overD}
-
-
- {analyzeVars.addicD}
-
-
- )}
- {!!fermianalyze && (
-
-
- {analyzeVars.name}
-
-
- {analyzeVars.state}
-
-
-
- {analyzeVars.color}
-
-
- {analyzeVars.description}
-
-
- {analyzeVars.metaRate} u/minute
-
-
- {analyzeVars.overD}
-
-
- {analyzeVars.addicD}
-
-
- {analyzeVars.purityF}
-
-
- {analyzeVars.inverseRatioF}
-
-
- {analyzeVars.purityE}
-
-
- {analyzeVars.minTemp}
-
-
- {analyzeVars.maxTemp}
-
-
- {analyzeVars.eTemp}
-
-
- {analyzeVars.pHpeak}
-
-
- )}
+
+
+
+ {analyzeVars.name}
+
+
+ {analyzeVars.state}
+
+
+
+ {analyzeVars.color}
+
+
+ {analyzeVars.description}
+
+
+ {analyzeVars.metaRate} u/minute
+
+
+ {analyzeVars.overD}
+
+
+ {analyzeVars.addicD}
+
+ {!!fermianalyze && ( // why did you do that before? it's fucking bad.
+
+
+ {analyzeVars.purityF}
+
+
+ {analyzeVars.inverseRatioF}
+
+
+ {analyzeVars.purityE}
+
+
+ {analyzeVars.minTemp}
+
+
+ {analyzeVars.maxTemp}
+
+
+ {analyzeVars.eTemp}
+
+
+ {analyzeVars.pHpeak}
+
+
+ )}
+
);
};
diff --git a/tgui/packages/tgui/interfaces/ChemPress.js b/tgui/packages/tgui/interfaces/ChemPress.js
index e2b8818136..d978e74137 100644
--- a/tgui/packages/tgui/interfaces/ChemPress.js
+++ b/tgui/packages/tgui/interfaces/ChemPress.js
@@ -5,51 +5,85 @@ import { Window } from '../layouts';
export const ChemPress = (props, context) => {
const { act, data } = useBackend(context);
const {
- pill_size,
- pill_name,
+ current_volume,
+ product_name,
pill_style,
pill_styles = [],
+ product,
+ min_volume,
+ max_volume,
} = data;
return (
-
+
-
+
+ act('change_product', {
+ product: "pill",
+ })}
+ />
+ act('change_product', {
+ product: "patch",
+ })}
+ />
+ act('change_product', {
+ product: "bottle",
+ })}
+ />
+
+
act('change_pill_size', {
+ onChange={(e, value) => act('change_current_volume', {
volume: value,
})} />
-
+
act('change_pill_name', {
+ value={product_name}
+ placeholder={product_name}
+ onChange={(e, value) => act('change_product_name', {
name: value,
})} />
+
+ {product}
+
-
- {pill_styles.map(pill => (
- act('change_pill_style', {
- id: pill.id,
- })}>
-
-
- ))}
-
+ {product === "pill" && (
+
+ {pill_styles.map(pill => (
+ act('change_pill_style', {
+ id: pill.id,
+ })}>
+
+
+ ))}
+
+ )}
diff --git a/tgui/packages/tgui/interfaces/ChemReactionChamber.js b/tgui/packages/tgui/interfaces/ChemReactionChamber.js
index 9af7ef7ddb..cbcf5574d8 100644
--- a/tgui/packages/tgui/interfaces/ChemReactionChamber.js
+++ b/tgui/packages/tgui/interfaces/ChemReactionChamber.js
@@ -17,7 +17,10 @@ export const ChemReactionChamber = (props, context) => {
const emptying = data.emptying;
const reagents = data.reagents || [];
return (
-
+
{
max_transfer,
} = data;
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/ChemSynthesizer.js b/tgui/packages/tgui/interfaces/ChemSynthesizer.js
index 26db58cb3a..536173f039 100644
--- a/tgui/packages/tgui/interfaces/ChemSynthesizer.js
+++ b/tgui/packages/tgui/interfaces/ChemSynthesizer.js
@@ -12,7 +12,9 @@ export const ChemSynthesizer = (props, context) => {
possible_amounts = [],
} = data;
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.js b/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.js
new file mode 100644
index 0000000000..5fe3874422
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.js
@@ -0,0 +1,103 @@
+import { Fragment } from 'inferno';
+import { useBackend } from '../backend';
+import { AnimatedNumber, Box, Button, Flex, LabeledList, NoticeBox, Section } from '../components';
+import { Window } from '../layouts';
+
+export const CivCargoHoldTerminal = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ pad,
+ sending,
+ status_report,
+ id_inserted,
+ id_bounty_info,
+ id_bounty_value,
+ id_bounty_num,
+ } = data;
+ const in_text = "Welcome valued employee.";
+ const out_text = "To begin, insert your ID into the console.";
+ return (
+
+
+
+
+
+ {id_inserted ? in_text : out_text}
+
+
+
+
+ {pad ? "Online" : "Not Found"}
+
+
+ {status_report}
+
+
+
+
+
+
+
+ act('recalc')} />
+ act(sending ? 'stop' : 'send')} />
+ act('bounty')} />
+ act('eject')} />
+
+
+
+
+
+ );
+};
+
+const BountyTextBox = (props, context) => {
+ const { data } = useBackend(context);
+ const {
+ id_bounty_info,
+ id_bounty_value,
+ id_bounty_num,
+ } = data;
+ const na_text = "N/A, please add a new bounty.";
+ return (
+
+
+
+ {id_bounty_info ? id_bounty_info : na_text}
+
+
+ {id_bounty_info ? id_bounty_num : "N/A"}
+
+
+ {id_bounty_info ? id_bounty_value : "N/A"}
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/ClockworkSlab.js b/tgui/packages/tgui/interfaces/ClockworkSlab.js
index f9e7cd6e6e..0c662b98d3 100644
--- a/tgui/packages/tgui/interfaces/ClockworkSlab.js
+++ b/tgui/packages/tgui/interfaces/ClockworkSlab.js
@@ -5,9 +5,10 @@ import { Fragment } from 'inferno';
import { Window } from '../layouts';
let REC_RATVAR = "";
+// You may ask "why is this not inside ClockworkSlab"
+// It's because cslab gets called every time. Lag is bad.
for (let index = 0; index < Math.min(Math.random()*100); index++) {
- // HEY! is it faster to do it serverside or client side?
- REC_RATVAR.concat("HONOR RATVAR ");
+ REC_RATVAR += "HONOR RATVAR ";
}
export const ClockworkSlab = (props, context) => {
@@ -17,7 +18,7 @@ export const ClockworkSlab = (props, context) => {
recollection_categories = [],
rec_section = null,
rec_binds = [],
- scripture = {}, // this is a {}, not a []
+ scripture = {},
tier_infos = {},
power = "0 W",
power_unformatted = 0,
@@ -35,7 +36,10 @@ export const ClockworkSlab = (props, context) => {
|| {};
return (
-
+
{recollection ? ( // tutorial
- {recollection_categories?.map(cat => {
- return (
-
-
- act('rec_category', {
- "category": cat.name,
- })} />
-
- );
- })}
+ {recollection_categories?.map(cat => (
+
+
+ act('rec_category', {
+ "category": cat.name,
+ })} />
+
+ ))}
- {data.rec_section}
{
{rec_section?.title ? (
rec_section.title
) : (
- '500 Server Internal archives not found.'
+ '500 Slab Internal archives not found.'
)}
@@ -165,16 +166,14 @@ export const ClockworkSlab = (props, context) => {
and other consumers.
- {map((scriptures, name) => {
- return (
- setTab(name)}>
- {name}
-
- );
- })(scripture)}
+ {map((scriptures, name) => (
+ setTab(name)}>
+ {name}
+
+ ))(scripture)}
{
- {!!scriptInTab && scriptInTab.map(script => (
+ {scriptInTab?.map(script => (
diff --git a/tgui/packages/tgui/interfaces/CodexGigas.js b/tgui/packages/tgui/interfaces/CodexGigas.js
index b55911750b..3bdc75d22c 100644
--- a/tgui/packages/tgui/interfaces/CodexGigas.js
+++ b/tgui/packages/tgui/interfaces/CodexGigas.js
@@ -52,7 +52,9 @@ const SUFFIXES = [
export const CodexGigas = (props, context) => {
const { act, data } = useBackend(context);
return (
-
+
{data.name}
diff --git a/tgui/packages/tgui/interfaces/ComputerFabricator.js b/tgui/packages/tgui/interfaces/ComputerFabricator.js
index ec4cffcb7b..f88e08ffb7 100644
--- a/tgui/packages/tgui/interfaces/ComputerFabricator.js
+++ b/tgui/packages/tgui/interfaces/ComputerFabricator.js
@@ -7,7 +7,11 @@ import { Window } from '../layouts';
export const ComputerFabricator = (props, context) => {
const { act, data } = useBackend(context);
return (
-
+
Your perfect device, only three steps away...
@@ -61,7 +65,7 @@ const CfStep1 = (props, context) => {
content="Laptop"
textAlign="center"
fontSize="30px"
- lineHeight="50px"
+ lineHeight={2}
onClick={() => act('pick_device', {
pick: '1',
})} />
@@ -73,7 +77,7 @@ const CfStep1 = (props, context) => {
content="Tablet"
textAlign="center"
fontSize="30px"
- lineHeight="50px"
+ lineHeight={2}
onClick={() => act('pick_device', {
pick: '2',
})} />
@@ -108,7 +112,7 @@ const CfStep2 = (props, context) => {
`}
position="right" />
-
+
{
color="good"
textAlign="center"
fontSize="18px"
- lineHeight="26px"
+ lineHeight={2}
onClick={() => act('confirm_order')} />
);
@@ -389,7 +393,7 @@ const CfStep3 = (props, context) => {
color="good"
textAlign="center"
fontSize="20px"
- lineHeight="28px"
+ lineHeight={2}
onClick={() => act('purchase')} />
);
diff --git a/tgui/packages/tgui/interfaces/Crayon.js b/tgui/packages/tgui/interfaces/Crayon.js
index 68d3bb46fc..05191f8c66 100644
--- a/tgui/packages/tgui/interfaces/Crayon.js
+++ b/tgui/packages/tgui/interfaces/Crayon.js
@@ -7,7 +7,10 @@ export const Crayon = (props, context) => {
const capOrChanges = data.has_cap || data.can_change_colour;
const drawables = data.drawables || [];
return (
-
+
{!!capOrChanges && (
diff --git a/tgui/packages/tgui/interfaces/CrewConsole.js b/tgui/packages/tgui/interfaces/CrewConsole.js
index 754574a641..2bdeb43ce6 100644
--- a/tgui/packages/tgui/interfaces/CrewConsole.js
+++ b/tgui/packages/tgui/interfaces/CrewConsole.js
@@ -60,7 +60,11 @@ const HealthStat = props => {
export const CrewConsole = () => {
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/Cryo.js b/tgui/packages/tgui/interfaces/Cryo.js
index 4ec29490c1..ddcf0d88a7 100644
--- a/tgui/packages/tgui/interfaces/Cryo.js
+++ b/tgui/packages/tgui/interfaces/Cryo.js
@@ -25,7 +25,10 @@ const damageTypes = [
export const Cryo = () => {
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/DecalPainter.js b/tgui/packages/tgui/interfaces/DecalPainter.js
index 36e764bc66..90ab589208 100644
--- a/tgui/packages/tgui/interfaces/DecalPainter.js
+++ b/tgui/packages/tgui/interfaces/DecalPainter.js
@@ -8,7 +8,9 @@ export const DecalPainter = (props, context) => {
const color_list = data.color_list || [];
const dir_list = data.dir_list || [];
return (
-
+
{decal_list.map(decal => (
diff --git a/tgui/packages/tgui/interfaces/DisposalUnit.js b/tgui/packages/tgui/interfaces/DisposalUnit.js
index e734e3b30e..46ef63a993 100644
--- a/tgui/packages/tgui/interfaces/DisposalUnit.js
+++ b/tgui/packages/tgui/interfaces/DisposalUnit.js
@@ -23,7 +23,9 @@ export const DisposalUnit = (props, context) => {
stateText = 'Off';
}
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/DnaConsole.js b/tgui/packages/tgui/interfaces/DnaConsole.js
index f4fd255120..194b9a4a6b 100644
--- a/tgui/packages/tgui/interfaces/DnaConsole.js
+++ b/tgui/packages/tgui/interfaces/DnaConsole.js
@@ -3,6 +3,7 @@ import { flow } from 'common/fp';
import { classes } from 'common/react';
import { capitalize } from 'common/string';
import { Fragment } from 'inferno';
+import { resolveAsset } from '../assets';
import { useBackend } from '../backend';
import { Box, Button, Collapsible, Dimmer, Divider, Dropdown, Flex, Icon, LabeledList, NumberInput, ProgressBar, Section } from '../components';
import { Window } from '../layouts';
@@ -75,7 +76,11 @@ export const DnaConsole = (props, context) => {
} = data;
const { consoleMode } = data.view;
return (
-
+
{!!isPulsingRads && (
{
{mutations.map(mutation => (
{
act('set_view', {
diff --git a/tgui/packages/tgui/interfaces/DnaVault.js b/tgui/packages/tgui/interfaces/DnaVault.js
index 6ce2093086..37d6d22737 100644
--- a/tgui/packages/tgui/interfaces/DnaVault.js
+++ b/tgui/packages/tgui/interfaces/DnaVault.js
@@ -17,7 +17,9 @@ export const DnaVault = (props, context) => {
animals_max,
} = data;
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/EightBallVote.js b/tgui/packages/tgui/interfaces/EightBallVote.js
index 340751e1fa..f8ce25f6f8 100644
--- a/tgui/packages/tgui/interfaces/EightBallVote.js
+++ b/tgui/packages/tgui/interfaces/EightBallVote.js
@@ -9,7 +9,9 @@ export const EightBallVote = (props, context) => {
shaking,
} = data;
return (
-
+
{!shaking && (
diff --git a/tgui/packages/tgui/interfaces/Electrolyzer.js b/tgui/packages/tgui/interfaces/Electrolyzer.js
index f60319269c..4a8641e83b 100644
--- a/tgui/packages/tgui/interfaces/Electrolyzer.js
+++ b/tgui/packages/tgui/interfaces/Electrolyzer.js
@@ -1,12 +1,14 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
-import { Box, Button, LabeledList, NumberInput, ProgressBar, Section } from '../components';
+import { Button, LabeledList, ProgressBar, Section } from '../components';
import { Window } from '../layouts';
export const Electrolyzer = (props, context) => {
const { act, data } = useBackend(context);
return (
-
+
{
maxFrequency,
} = data;
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.js b/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.js
index be64709a19..a476907058 100644
--- a/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.js
+++ b/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.js
@@ -13,7 +13,9 @@ export const EmergencyShuttleConsole = (props, context) => {
authorizations = [],
} = data;
return (
-
+
{
No Active Authorizations
)}
- {authorizations.map(authorization => (
-
- {authorization.name} ({authorization.job})
-
- ))}
diff --git a/tgui/packages/tgui/interfaces/EngravedMessage.js b/tgui/packages/tgui/interfaces/EngravedMessage.js
index 40d34d0427..e62fbf0ff7 100644
--- a/tgui/packages/tgui/interfaces/EngravedMessage.js
+++ b/tgui/packages/tgui/interfaces/EngravedMessage.js
@@ -18,7 +18,10 @@ export const EngravedMessage = (props, context) => {
realdate,
} = data;
return (
-
+
{
mechs = [],
} = data;
return (
-
+
{mechs.length === 0 && (
diff --git a/tgui/packages/tgui/interfaces/ExosuitFabricator.js b/tgui/packages/tgui/interfaces/ExosuitFabricator.js
new file mode 100644
index 0000000000..a9c42d91a8
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/ExosuitFabricator.js
@@ -0,0 +1,796 @@
+import { classes } from 'common/react';
+import { uniqBy } from 'common/collections';
+import { useBackend, useSharedState } from '../backend';
+import { formatSiUnit, formatMoney } from '../format';
+import { Flex, Section, Tabs, Box, Button, Fragment, ProgressBar, NumberInput, Icon, Input } from '../components';
+import { Window } from '../layouts';
+import { createSearch } from 'common/string';
+
+const MATERIAL_KEYS = {
+ "iron": "sheet-metal_3",
+ "glass": "sheet-glass_3",
+ "silver": "sheet-silver_3",
+ "gold": "sheet-gold_3",
+ "diamond": "sheet-diamond",
+ "plasma": "sheet-plasma_3",
+ "uranium": "sheet-uranium",
+ "bananium": "sheet-bananium",
+ "titanium": "sheet-titanium_3",
+ "bluespace crystal": "polycrystal",
+ "plastic": "sheet-plastic_3",
+};
+
+const COLOR_NONE = 0;
+const COLOR_AVERAGE = 1;
+const COLOR_BAD = 2;
+
+const COLOR_KEYS = {
+ [COLOR_NONE]: false,
+ [COLOR_AVERAGE]: "average",
+ [COLOR_BAD]: "bad",
+};
+
+const materialArrayToObj = materials => {
+ let materialObj = {};
+
+ materials.forEach(m => {
+ materialObj[m.name] = m.amount; });
+
+ return materialObj;
+};
+
+const partBuildColor = (cost, tally, material) => {
+ if (cost > material) {
+ return { color: COLOR_BAD, deficit: (cost - material) };
+ }
+
+ if (tally > material) {
+ return { color: COLOR_AVERAGE, deficit: cost };
+ }
+
+ if (cost + tally > material) {
+ return { color: COLOR_AVERAGE, deficit: ((cost + tally) - material) };
+ }
+
+ return { color: COLOR_NONE, deficit: 0 };
+};
+
+const partCondFormat = (materials, tally, part) => {
+ let format = { "textColor": COLOR_NONE };
+
+ Object.keys(part.cost).forEach(mat => {
+ format[mat] = partBuildColor(part.cost[mat], tally[mat], materials[mat]);
+
+ if (format[mat].color > format["textColor"]) {
+ format["textColor"] = format[mat].color;
+ }
+ });
+
+ return format;
+};
+
+const queueCondFormat = (materials, queue) => {
+ let materialTally = {};
+ let matFormat = {};
+ let missingMatTally = {};
+ let textColors = {};
+
+ queue.forEach((part, i) => {
+ textColors[i] = COLOR_NONE;
+ Object.keys(part.cost).forEach(mat => {
+ materialTally[mat] = materialTally[mat] || 0;
+ missingMatTally[mat] = missingMatTally[mat] || 0;
+
+ matFormat[mat] = partBuildColor(
+ part.cost[mat], materialTally[mat], materials[mat]
+ );
+
+ if (matFormat[mat].color !== COLOR_NONE) {
+ if (textColors[i] < matFormat[mat].color) {
+ textColors[i] = matFormat[mat].color;
+ }
+ }
+ else {
+ materialTally[mat] += part.cost[mat];
+ }
+
+ missingMatTally[mat] += matFormat[mat].deficit;
+ });
+ });
+ return { materialTally, missingMatTally, textColors, matFormat };
+};
+
+const searchFilter = (search, allparts) => {
+ let searchResults = [];
+
+ if (!search.length) {
+ return;
+ }
+
+ const resultFilter = createSearch(search, part => (
+ (part.name || "")
+ + (part.desc || "")
+ + (part.searchMeta || "")
+ ));
+
+ Object.keys(allparts).forEach(category => {
+ allparts[category]
+ .filter(resultFilter)
+ .forEach(e => { searchResults.push(e); });
+ });
+
+ searchResults = uniqBy(part => part.name)(searchResults);
+
+ return searchResults;
+};
+
+export const ExosuitFabricator = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const queue = data.queue || [];
+ const materialAsObj = materialArrayToObj(data.materials || []);
+
+ const {
+ materialTally,
+ missingMatTally,
+ textColors,
+ } = queueCondFormat(materialAsObj, queue);
+
+ const [
+ displayMatCost,
+ setDisplayMatCost,
+ ] = useSharedState(context, "display_mats", false);
+
+ return (
+
+
+
+
+
+
+
+
+
+ setDisplayMatCost(!displayMatCost)}
+ checked={displayMatCost}>
+ Display Material Costs
+
+
+
+
+
+
+
+ act("sync_rnd")} />
+ )}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const EjectMaterial = (props, context) => {
+ const { act } = useBackend(context);
+
+ const { material } = props;
+
+ const {
+ name,
+ removable,
+ sheets,
+ ref,
+ } = material;
+
+ const [
+ removeMaterials,
+ setRemoveMaterials,
+ ] = useSharedState(context, "remove_mats_" + name, 1);
+
+ if ((removeMaterials > 1) && (sheets < removeMaterials)) {
+ setRemoveMaterials(sheets || 1);
+ }
+
+ return (
+
+ {
+ const newVal = parseInt(val, 10);
+ if (Number.isInteger(newVal)) {
+ setRemoveMaterials(newVal);
+ }
+ }} />
+ act("remove_mat", {
+ ref: ref,
+ amount: removeMaterials,
+ })} />
+
+ );
+};
+
+const Materials = (props, context) => {
+ const { data } = useBackend(context);
+
+ const materials = data.materials || [];
+
+ return (
+
+ {materials.map(material => (
+
+
+
+
+
+
+ ))}
+
+ );
+};
+
+const MaterialAmount = (props, context) => {
+ const {
+ name,
+ amount,
+ formatsi,
+ formatmoney,
+ color,
+ style,
+ } = props;
+
+ return (
+
+
+
+
+
+
+ {(formatsi && formatSiUnit(amount, 0))
+ || (formatmoney && formatMoney(amount))
+ || (amount)}
+
+
+
+ );
+};
+
+const PartSets = (props, context) => {
+ const { data } = useBackend(context);
+
+ const partSets = data.partSets || [];
+ const buildableParts = data.buildableParts || {};
+
+ const [
+ selectedPartTab,
+ setSelectedPartTab,
+ ] = useSharedState(
+ context,
+ "part_tab",
+ partSets.length ? buildableParts[0] : ""
+ );
+
+ return (
+
+ {partSets.map(set => (
+ !!(buildableParts[set]) && (
+ setSelectedPartTab(set)}>
+ {set}
+
+ )
+ ))}
+
+ );
+};
+
+const PartLists = (props, context) => {
+ const { data } = useBackend(context);
+
+ const getFirstValidPartSet = (sets => {
+ for (let set of sets) {
+ if (buildableParts[set]) {
+ return set;
+ }
+ }
+ return null;
+ });
+
+ const partSets = data.partSets || [];
+ const buildableParts = data.buildableParts || [];
+
+ const {
+ queueMaterials,
+ materials,
+ } = props;
+
+ const [
+ selectedPartTab,
+ setSelectedPartTab,
+ ] = useSharedState(
+ context,
+ "part_tab",
+ getFirstValidPartSet(partSets)
+ );
+
+ const [
+ searchText,
+ setSearchText,
+ ] = useSharedState(context, "search_text", "");
+
+ if (!selectedPartTab || !buildableParts[selectedPartTab]) {
+ const validSet = getFirstValidPartSet(partSets);
+ if (validSet) {
+ setSelectedPartTab(validSet);
+ }
+ else {
+ return;
+ }
+ }
+
+ let partsList;
+ // Build list of sub-categories if not using a search filter.
+ if (!searchText) {
+ partsList = { "Parts": [] };
+ buildableParts[selectedPartTab].forEach(part => {
+ part["format"] = partCondFormat(materials, queueMaterials, part);
+ if (!part.subCategory) {
+ partsList["Parts"].push(part);
+ return;
+ }
+ if (!(part.subCategory in partsList)) {
+ partsList[part.subCategory] = [];
+ }
+ partsList[part.subCategory].push(part);
+ });
+ }
+ else {
+ partsList = [];
+ searchFilter(searchText, buildableParts).forEach(part => {
+ part["format"] = partCondFormat(materials, queueMaterials, part);
+ partsList.push(part);
+ });
+ }
+
+
+ return (
+
+
+ {(!!searchText && (
+
+ )) || (
+ Object.keys(partsList).map(category => (
+
+ ))
+ )}
+
+ );
+};
+
+const PartCategory = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ buildingPart,
+ } = data;
+
+ const {
+ parts,
+ name,
+ forceShow,
+ placeholder,
+ } = props;
+
+ const [
+ displayMatCost,
+ ] = useSharedState(context, "display_mats", false);
+
+ return (
+ ((!!parts.length || forceShow) && (
+ act("add_queue_set", {
+ part_list: parts.map(part => part.id),
+ })} />
+ }>
+ {(!parts.length) && (placeholder)}
+ {parts.map(part => (
+
+
+
+ act("build_part", { id: part.id })} />
+
+
+ act("add_queue_part", { id: part.id })} />
+
+
+
+ {part.name}
+
+
+
+
+
+
+
+ {(displayMatCost && (
+
+ {Object.keys(part.cost).map(material => (
+
+
+
+ ))}
+
+ ))}
+
+
+ ))}
+
+ ))
+ );
+};
+
+const Queue = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const { isProcessingQueue } = data;
+
+ const queue = data.queue || [];
+
+ const {
+ queueMaterials,
+ missingMaterials,
+ textColors,
+ } = props;
+
+ return (
+
+
+
+ act("clear_queue")} />
+ {(!!isProcessingQueue && (
+ act("stop_queue")} />
+ )) || (
+ act("build_queue")} />
+ )}
+
+ }>
+
+
+
+
+
+
+
+
+
+
+ {!!queue.length && (
+
+
+
+ )}
+
+ );
+};
+
+const QueueMaterials = (props, context) => {
+ const {
+ queueMaterials,
+ missingMaterials,
+ } = props;
+
+ return (
+
+ {Object.keys(queueMaterials).map(material => (
+
+
+ {(!!missingMaterials[material] && (
+
+ {formatMoney(missingMaterials[material])}
+
+ ))}
+
+ ))}
+
+ );
+};
+
+const QueueList = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ textColors,
+ } = props;
+
+ const queue = data.queue || [];
+
+ if (!queue.length) {
+ return (
+
+ No parts in queue.
+
+ );
+ }
+
+ return (
+ queue.map((part, index) => (
+
+
+
+ act("del_queue_part", { index: index+1 })} />
+
+
+
+ {part.name}
+
+
+
+
+ ))
+ );
+};
+
+const BeingBuilt = (props, context) => {
+ const { data } = useBackend(context);
+
+ const {
+ buildingPart,
+ storedPart,
+ } = data;
+
+ if (storedPart) {
+ const {
+ name,
+ } = storedPart;
+
+ return (
+
+
+
+
+ {name}
+
+
+
+ {"Fabricator outlet obstructed..."}
+
+
+
+
+ );
+ }
+
+ if (buildingPart) {
+ const {
+ name,
+ duration,
+ printTime,
+ } = buildingPart;
+
+ const timeLeft = Math.ceil(duration/10);
+
+ return (
+
+
+
+
+ {name}
+
+
+
+ {((timeLeft >= 0) && (timeLeft + "s")) || ("Dispensing...")}
+
+
+
+
+ );
+ }
+};
diff --git a/tgui/packages/tgui/interfaces/ForbiddenLore.js b/tgui/packages/tgui/interfaces/ForbiddenLore.js
new file mode 100644
index 0000000000..1225132810
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/ForbiddenLore.js
@@ -0,0 +1,61 @@
+import { sortBy } from 'common/collections';
+import { flow } from 'common/fp';
+import { useBackend } from '../backend';
+import { Box, Button, Section } from '../components';
+import { Window } from '../layouts';
+
+export const ForbiddenLore = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ charges,
+ } = data;
+ const to_know = flow([
+ sortBy(to_know => to_know.state !== "Research",
+ to_know => to_know.path === "Side"),
+ ])(data.to_know || []);
+ return (
+
+
+
+ Charges left : {charges}
+ {to_know!== null ? (
+ to_know.map(knowledge => (
+
+
+ {knowledge.path} path
+
+
+ act('research', {
+ name: knowledge.name,
+ cost: knowledge.cost,
+ })} />
+ {' '}
+ Cost : {knowledge.cost}
+
+
+ {knowledge.flavour}
+
+
+ {knowledge.desc}
+
+
+ ))
+ ) : (
+
+ No more knowledge can be found
+
+ )}
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/GlandDispenser.js b/tgui/packages/tgui/interfaces/GlandDispenser.js
new file mode 100644
index 0000000000..f572a92f06
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/GlandDispenser.js
@@ -0,0 +1,37 @@
+import { useBackend } from '../backend';
+import { Button, Section } from '../components';
+import { Window } from '../layouts';
+
+export const GlandDispenser = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ glands = [],
+ } = data;
+ return (
+
+
+
+ {glands.map(gland => (
+ act('dispense', {
+ gland_id: gland.id,
+ })} />
+ ))}
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/Gps.js b/tgui/packages/tgui/interfaces/Gps.js
index a5fcb8d194..beffdad9c4 100644
--- a/tgui/packages/tgui/interfaces/Gps.js
+++ b/tgui/packages/tgui/interfaces/Gps.js
@@ -37,7 +37,11 @@ export const Gps = (props, context) => {
signal => signal.entrytag),
])(data.signals || []);
return (
-
+
{
operational,
} = data;
return (
-
+
{!operational && (
diff --git a/tgui/packages/tgui/interfaces/GulagItemReclaimer.js b/tgui/packages/tgui/interfaces/GulagItemReclaimer.js
index 846b465998..6f47eed342 100644
--- a/tgui/packages/tgui/interfaces/GulagItemReclaimer.js
+++ b/tgui/packages/tgui/interfaces/GulagItemReclaimer.js
@@ -8,7 +8,10 @@ export const GulagItemReclaimer = (props, context) => {
mobs = [],
} = data;
return (
-
+
{mobs.length === 0 && (
diff --git a/tgui/packages/tgui/interfaces/GulagTeleporterConsole.js b/tgui/packages/tgui/interfaces/GulagTeleporterConsole.js
index 3b709cbcb0..abb8da8b64 100644
--- a/tgui/packages/tgui/interfaces/GulagTeleporterConsole.js
+++ b/tgui/packages/tgui/interfaces/GulagTeleporterConsole.js
@@ -19,7 +19,9 @@ export const GulagTeleporterConsole = (props, context) => {
prisoner = {},
} = data;
return (
-
+
{
program,
} = data;
return (
-
+
{
+ const { act, data } = useBackend(context);
+ const {
+ calling,
+ } = data;
+ return (
+
+ {!!calling && (
+
+
+
+
+
+
+ {'Dialing...'}
+
+
+
+ act('hang_up')} />
+
+
+ )}
+
+
+
+
+ );
+};
+
+const HolopadContent = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ on_network,
+ on_cooldown,
+ allowed,
+ disk,
+ disk_record,
+ replay_mode,
+ loop_mode,
+ record_mode,
+ holo_calls = [],
+ } = data;
+ return (
+
+ act('AIrequest')} />
+ )} >
+
+
+ act('holocall', { headcall: allowed })} />
+
+ {holo_calls.map((call => {
+ return (
+
+ act(call.connected
+ ? 'disconnectcall'
+ : 'connectcall', { holopad: call.ref })} />
+
+ );
+ }))}
+
+
+ act('disk_eject')} />
+ }>
+ {!disk && (
+
+ No holodisk
+
+ ) || (
+
+
+ act('replay_mode')} />
+ act('loop_mode')} />
+ act('offset')} />
+
+
+ act('record_mode')} />
+ act('record_clear')} />
+
+
+ )}
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/HypnoChair.js b/tgui/packages/tgui/interfaces/HypnoChair.js
index 58ac1b0a80..1d590a112d 100644
--- a/tgui/packages/tgui/interfaces/HypnoChair.js
+++ b/tgui/packages/tgui/interfaces/HypnoChair.js
@@ -5,7 +5,9 @@ import { Window } from '../layouts';
export const HypnoChair = (props, context) => {
const { act, data } = useBackend(context);
return (
-
+
{
const { act, data } = useBackend(context);
return (
-
+
{
visible,
} = data;
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/Intellicard.js b/tgui/packages/tgui/interfaces/Intellicard.js
index 99e653ed31..1a6e3885d9 100644
--- a/tgui/packages/tgui/interfaces/Intellicard.js
+++ b/tgui/packages/tgui/interfaces/Intellicard.js
@@ -16,7 +16,10 @@ export const Intellicard = (props, context) => {
} = data;
const offline = isDead || isBraindead;
return (
-
+
{
+ const { act, data } = useBackend(context);
+ const {
+ active,
+ track_selected,
+ track_length,
+ track_beat,
+ volume,
+ } = data;
+ const songs = flow([
+ sortBy(
+ song => song.name),
+ ])(data.songs || []);
+ return (
+
+
+ act('toggle')} />
+ )}>
+
+
+ song.name)}
+ disabled={active}
+ selected={track_selected || "Select a Track"}
+ onSelected={value => act('select_track', {
+ track: value,
+ })} />
+
+
+ {track_selected ? track_length : "No Track Selected"}
+
+
+ {track_selected ? track_beat : "No Track Selected"}
+ {track_beat === 1 ? " beat" : " beats"}
+
+
+
+
+
+
+
+ = 50 ? 'red' : 'green'}
+ value={volume}
+ unit="%"
+ minValue={0}
+ maxValue={100}
+ step={1}
+ stepPixelSize={1}
+ disabled={active}
+ onDrag={(e, value) => act('set_volume', {
+ volume: value,
+ })} />
+ act('set_volume', {
+ volume: "min",
+ })} />
+ act('set_volume', {
+ volume: "max",
+ })} />
+ act('set_volume', {
+ volume: "reset",
+ })} />
+
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/KeycardAuth.js b/tgui/packages/tgui/interfaces/KeycardAuth.js
index eb4dafeb28..199067b161 100644
--- a/tgui/packages/tgui/interfaces/KeycardAuth.js
+++ b/tgui/packages/tgui/interfaces/KeycardAuth.js
@@ -6,7 +6,9 @@ import { Window } from '../layouts';
export const KeycardAuth = (props, context) => {
const { act, data } = useBackend(context);
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/LaborClaimConsole.js b/tgui/packages/tgui/interfaces/LaborClaimConsole.js
index b3ee427844..5483b1dd70 100644
--- a/tgui/packages/tgui/interfaces/LaborClaimConsole.js
+++ b/tgui/packages/tgui/interfaces/LaborClaimConsole.js
@@ -13,7 +13,9 @@ export const LaborClaimConsole = (props, context) => {
unclaimed_points,
} = data;
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/LanguageMenu.js b/tgui/packages/tgui/interfaces/LanguageMenu.js
index 79249fd1f5..656d4a818e 100644
--- a/tgui/packages/tgui/interfaces/LanguageMenu.js
+++ b/tgui/packages/tgui/interfaces/LanguageMenu.js
@@ -13,7 +13,11 @@ export const LanguageMenu = (props, context) => {
unknown_languages = [],
} = data;
return (
-
+
@@ -91,9 +95,7 @@ export const LanguageMenu = (props, context) => {
{' '}
Key: ,{language.key}
{' '}
- {language.can_understand
- ? 'Can understand.'
- : 'Cannot understand.'}
+ {!!language.shadow && '(gained from mob)'}
{' '}
{language.can_speak
? 'Can speak.'
diff --git a/tgui/packages/tgui/interfaces/LaunchpadConsole.js b/tgui/packages/tgui/interfaces/LaunchpadConsole.js
index 1fd24e981d..83f8d0aa59 100644
--- a/tgui/packages/tgui/interfaces/LaunchpadConsole.js
+++ b/tgui/packages/tgui/interfaces/LaunchpadConsole.js
@@ -197,7 +197,10 @@ export const LaunchpadConsole = (props, context) => {
selected_id,
} = data;
return (
-
+
{launchpads.length === 0 && (
diff --git a/tgui/packages/tgui/interfaces/LaunchpadRemote.js b/tgui/packages/tgui/interfaces/LaunchpadRemote.js
index 2720914611..2173630c08 100644
--- a/tgui/packages/tgui/interfaces/LaunchpadRemote.js
+++ b/tgui/packages/tgui/interfaces/LaunchpadRemote.js
@@ -10,7 +10,11 @@ export const LaunchpadRemote = (props, context) => {
pad_closed,
} = data;
return (
-
+
{!has_pad && (
diff --git a/tgui/packages/tgui/interfaces/MafiaPanel.js b/tgui/packages/tgui/interfaces/MafiaPanel.js
new file mode 100644
index 0000000000..e3c2c893ec
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/MafiaPanel.js
@@ -0,0 +1,164 @@
+import { Fragment } from 'inferno';
+import { useBackend } from '../backend';
+import { Box, Button, Flex, LabeledList, Section, TimeDisplay } from '../components';
+import { Window } from '../layouts';
+
+export const MafiaPanel = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ players,
+ actions,
+ phase,
+ roleinfo,
+ role_theme,
+ admin_controls,
+ judgement_phase,
+ timeleft,
+ all_roles,
+ } = data;
+ return (
+
+
+
+ {!!roleinfo && (
+
+
+
+
+
+ You are a {roleinfo.role}
+
+
+ {roleinfo.desc}
+
+
+ )}
+
+
+ {!!actions && actions.map(action => (
+
+ act("mf_action", { atype: action })}>
+ {action}
+
+
+ ))}
+
+ {!!admin_controls && (
+
+ THESE ARE DEBUG, THEY WILL BREAK THE GAME, DO NOT TOUCH
+ Also because an admin did it: do not gib/delete/etc
+ anyone! It will runtime the game to death!
+ act("next_phase")}>
+ Next Phase
+
+ act("players_home")}>
+ Send All Players Home
+
+ act("new_game")}>
+ New Game
+
+
+ act("nuke")}
+ color="black">
+ Nuke (delete datum + landmarks, hope it fixes everything!)
+
+
+ )}
+
+
+ {!!players && players.map(player => (
+
+ {!player.alive && (DEAD )}
+ {player.votes !== undefined && !!player.alive
+ && (Votes : {player.votes} )}
+ {
+ !!player.actions && player.actions.map(action => {
+ return (
+ act('mf_targ_action', {
+ atype: action,
+ target: player.ref,
+ })}>
+ {action}
+ ); })
+ }
+ )
+ )}
+
+
+ {!!judgement_phase && (
+
+
+ act("vote_innocent")}>
+ INNOCENT!
+
+ Use these buttons to vote the accused innocent or guilty!
+ act("vote_guilty")}>
+ GUILTY!
+
+
+
+ )}
+
+
+
+ {!!all_roles && all_roles.map(r => (
+
+
+ {r}
+ act("mf_lookup", {
+ atype: r.slice(0, -3),
+ })}
+ />
+
+
+ ))}
+
+
+
+
+ {roleinfo !== undefined && !!roleinfo.action_log
+ && roleinfo.action_log.map(log_line => (
+
+ {log_line}
+
+ ))}
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/MalfunctionModulePicker.js b/tgui/packages/tgui/interfaces/MalfunctionModulePicker.js
index 298e56655d..70a1716ed6 100644
--- a/tgui/packages/tgui/interfaces/MalfunctionModulePicker.js
+++ b/tgui/packages/tgui/interfaces/MalfunctionModulePicker.js
@@ -9,6 +9,8 @@ export const MalfunctionModulePicker = (props, context) => {
} = data;
return (
diff --git a/tgui/packages/tgui/interfaces/MechBayPowerConsole.js b/tgui/packages/tgui/interfaces/MechBayPowerConsole.js
index bed15d261e..22a440791c 100644
--- a/tgui/packages/tgui/interfaces/MechBayPowerConsole.js
+++ b/tgui/packages/tgui/interfaces/MechBayPowerConsole.js
@@ -8,7 +8,9 @@ export const MechBayPowerConsole = (props, context) => {
const mech = recharge_port && recharge_port.mech;
const cell = mech && mech.cell;
return (
-
+
{
active_status_4,
} = data;
return (
-
+
@@ -97,7 +100,9 @@ const MedicalKioskScanButton = (props, context) => {
tooltipPosition="right"
content={name}
onClick={() => {
- act(`beginScan_${index}`);
+ if (!paid) {
+ act(`beginScan_${index}`);
+ }
setScanIndex(index);
}} />
@@ -247,8 +252,10 @@ const MedicalKioskScanResults3 = (props, context) => {
clone_health,
brain_damage,
brain_health,
- rad_status,
- rad_value,
+ rad_contamination_status,
+ rad_contamination_value,
+ rad_sickness_status,
+ rad_sickness_value,
trauma_status,
} = data;
return (
@@ -282,12 +289,20 @@ const MedicalKioskScanResults3 = (props, context) => {
- {rad_status}
+ label="Radiation Sickness Status">
+ {rad_sickness_status}
- {rad_value}%
+ label="Radiation Sickness Percentage">
+ {rad_sickness_value}%
+
+
+ {rad_contamination_status}
+
+
+ {rad_contamination_value}%
diff --git a/tgui/packages/tgui/interfaces/MiningVendor.js b/tgui/packages/tgui/interfaces/MiningVendor.js
index 950deb196b..39ff2969b1 100644
--- a/tgui/packages/tgui/interfaces/MiningVendor.js
+++ b/tgui/packages/tgui/interfaces/MiningVendor.js
@@ -9,7 +9,10 @@ export const MiningVendor = (props, context) => {
...data.product_records,
];
return (
-
+
{data.user && (
@@ -27,7 +30,7 @@ export const MiningVendor = (props, context) => {
)}
-
+
{inventory.map((product => {
return (
diff --git a/tgui/packages/tgui/interfaces/Mule.js b/tgui/packages/tgui/interfaces/Mule.js
index b6ae6c1f24..71c14ed3b1 100644
--- a/tgui/packages/tgui/interfaces/Mule.js
+++ b/tgui/packages/tgui/interfaces/Mule.js
@@ -1,6 +1,6 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
-import { Button, Dropdown, Grid, Input, LabeledList, ProgressBar, Section, Flex } from '../components';
+import { Button, Dropdown, Flex, Input, LabeledList, ProgressBar, Section } from '../components';
import { Window } from '../layouts';
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
@@ -24,7 +24,9 @@ export const Mule = (props, context) => {
} = data;
const locked = data.locked && !data.siliconUser;
return (
-
+
{
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/NaniteCloudControl.js b/tgui/packages/tgui/interfaces/NaniteCloudControl.js
index 40f3dd3b21..e9eb566464 100644
--- a/tgui/packages/tgui/interfaces/NaniteCloudControl.js
+++ b/tgui/packages/tgui/interfaces/NaniteCloudControl.js
@@ -10,7 +10,6 @@ export const NaniteDiskBox = (props, context) => {
has_program,
disk,
} = data;
-
if (!has_disk) {
return (
@@ -18,7 +17,6 @@ export const NaniteDiskBox = (props, context) => {
);
}
-
if (!has_program) {
return (
@@ -26,7 +24,6 @@ export const NaniteDiskBox = (props, context) => {
);
}
-
return (
);
@@ -34,7 +31,6 @@ export const NaniteDiskBox = (props, context) => {
export const NaniteInfoBox = (props, context) => {
const { program } = props;
-
const {
name,
desc,
@@ -52,9 +48,7 @@ export const NaniteInfoBox = (props, context) => {
timer_trigger,
timer_trigger_delay,
} = program;
-
const extra_settings = program.extra_settings || [];
-
return (
{
export const NaniteCloudBackupList = (props, context) => {
const { act, data } = useBackend(context);
const cloud_backups = data.cloud_backups || [];
- return (
- cloud_backups.map(backup => (
- act('set_view', {
- view: backup.cloud_id,
- })} />
- ))
- );
+ return cloud_backups.map(backup => (
+ act('set_view', {
+ view: backup.cloud_id,
+ })} />
+ ));
};
export const NaniteCloudBackupDetails = (props, context) => {
@@ -187,9 +179,7 @@ export const NaniteCloudBackupDetails = (props, context) => {
has_program,
cloud_backup,
} = data;
-
const can_rule = (disk && disk.can_rule) || false;
-
if (!cloud_backup) {
return (
@@ -197,9 +187,7 @@ export const NaniteCloudBackupDetails = (props, context) => {
);
}
-
const cloud_programs = data.cloud_programs || [];
-
return (
{
)}>
- {!!can_rule && (
+ {(!!can_rule || !!program.has_rules) && (
{
)}>
{program.has_rules ? (
rules.map(rule => (
-
+
{
program_id: program.id,
rule_id: rule.id,
})} />
- {rule.display}
-
+ {` ${rule.display}`}
+
))
) : (
@@ -278,9 +266,11 @@ export const NaniteCloudControl = (props, context) => {
current_view,
new_backup_id,
} = data;
-
return (
-
+
{
&& programs[selectedCategory]
|| [];
return (
-
+
{
const { act, data } = useBackend(context);
-
return (
{
export const NaniteProgrammer = (props, context) => {
return (
-
+
@@ -251,7 +253,6 @@ export const NaniteProgrammerContent = (props, context) => {
has_extra_settings,
extra_settings = {},
} = data;
-
if (!has_disk) {
return (
@@ -259,7 +260,6 @@ export const NaniteProgrammerContent = (props, context) => {
);
}
-
if (!has_program) {
return (
{
)} />
);
}
-
return (
{
return (
-
+
@@ -51,7 +54,7 @@ export const NaniteRemoteContent = (props, context) => {
icon="lock"
content="Lock Interface"
onClick={() => act('lock')} />
- )} >
+ )}>
{
content="Save"
onClick={() => act('save')} />
-
+
{
{saved_settings.map(setting => (
+ className="candystripe">
{setting.name}:
diff --git a/tgui/packages/tgui/interfaces/NotificationPreferences.js b/tgui/packages/tgui/interfaces/NotificationPreferences.js
index b46e12504c..3d8e4d15da 100644
--- a/tgui/packages/tgui/interfaces/NotificationPreferences.js
+++ b/tgui/packages/tgui/interfaces/NotificationPreferences.js
@@ -4,7 +4,6 @@ import { Window } from '../layouts';
export const NotificationPreferences = (props, context) => {
const { act, data } = useBackend(context);
-
const ignoresPreSort = data.ignore || [];
const ignores = ignoresPreSort.sort((a, b) => {
const descA = a.desc.toLowerCase();
@@ -17,9 +16,12 @@ export const NotificationPreferences = (props, context) => {
}
return 0;
});
-
return (
-
+
{ignores.map(ignore => (
diff --git a/tgui/packages/tgui/interfaces/NtnetRelay.js b/tgui/packages/tgui/interfaces/NtnetRelay.js
index 69d6b4dd8d..b41723edad 100644
--- a/tgui/packages/tgui/interfaces/NtnetRelay.js
+++ b/tgui/packages/tgui/interfaces/NtnetRelay.js
@@ -4,16 +4,17 @@ import { Window } from '../layouts';
export const NtnetRelay = (props, context) => {
const { act, data } = useBackend(context);
-
const {
enabled,
dos_capacity,
dos_overload,
dos_crashed,
} = data;
-
return (
-
+
{
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/NtosArcade.js b/tgui/packages/tgui/interfaces/NtosArcade.js
index d6048e2e1e..f0f7241ab8 100644
--- a/tgui/packages/tgui/interfaces/NtosArcade.js
+++ b/tgui/packages/tgui/interfaces/NtosArcade.js
@@ -1,3 +1,4 @@
+import { resolveAsset } from '../assets';
import { useBackend } from '../backend';
import { AnimatedNumber, Box, Button, Grid, LabeledList, ProgressBar, Section } from '../components';
import { NtosWindow } from '../layouts';
@@ -5,7 +6,9 @@ import { NtosWindow } from '../layouts';
export const NtosArcade = (props, context) => {
const { act, data } = useBackend(context);
return (
-
+
{
inline
width="156px"
textAlign="center">
-
+
diff --git a/tgui/packages/tgui/interfaces/NtosAtmos.js b/tgui/packages/tgui/interfaces/NtosAtmos.js
index 0356cc2994..0e8141b9ea 100644
--- a/tgui/packages/tgui/interfaces/NtosAtmos.js
+++ b/tgui/packages/tgui/interfaces/NtosAtmos.js
@@ -18,7 +18,10 @@ export const NtosAtmos = (props, context) => {
])(data.AirData || []);
const gasMaxPercentage = Math.max(1, ...gases.map(gas => gas.percentage));
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/NtosBountyConsole.js b/tgui/packages/tgui/interfaces/NtosBountyConsole.js
new file mode 100644
index 0000000000..bfd2b0e4c9
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NtosBountyConsole.js
@@ -0,0 +1,119 @@
+import { useBackend } from '../backend';
+import { AnimatedNumber, Box, Button, Section, Table } from '../components';
+import { formatMoney } from '../format';
+import { NtosWindow } from '../layouts';
+
+export const NtosBountyConsole = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ bountydata = [],
+ } = data;
+ return (
+
+
+ }
+ buttons={(
+ act('Print')} />
+ )}>
+
+
+
+ Bounty Object
+
+
+ Description
+
+
+ Progress
+
+
+ Value
+
+
+ Claim
+
+
+ {bountydata.map(bounty => (
+
+
+ {bounty.name}
+
+
+ {bounty.description}
+
+
+ {bounty.priority === 1
+ ? High Priority
+ : ""}
+ {bounty.completion_string}
+
+
+ {bounty.reward_string}
+
+
+ act('ClaimBounty', {
+ bounty: bounty.bounty_ref,
+ })} />
+
+
+ ))}
+
+
+
+
+ );
+};
+
+const BountyHeader = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ stored_cash,
+ } = data;
+ return (
+
+ formatMoney(value)} />
+ {' credits'}
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/NtosCard.js b/tgui/packages/tgui/interfaces/NtosCard.js
index 95d7754b83..3b114589a9 100644
--- a/tgui/packages/tgui/interfaces/NtosCard.js
+++ b/tgui/packages/tgui/interfaces/NtosCard.js
@@ -6,7 +6,10 @@ import { AccessList } from './common/AccessList';
export const NtosCard = (props, context) => {
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/NtosConfiguration.js b/tgui/packages/tgui/interfaces/NtosConfiguration.js
index 0a86ccb728..2d60a6dd76 100644
--- a/tgui/packages/tgui/interfaces/NtosConfiguration.js
+++ b/tgui/packages/tgui/interfaces/NtosConfiguration.js
@@ -5,8 +5,8 @@ import { NtosWindow } from '../layouts';
export const NtosConfiguration = (props, context) => {
const { act, data } = useBackend(context);
-
const {
+ PC_device_theme,
power_usage,
battery_exists,
battery = {},
@@ -14,9 +14,12 @@ export const NtosConfiguration = (props, context) => {
disk_used,
hardware = [],
} = data;
-
return (
-
+
{
mr={1}
onClick={() => act('PC_toggle_component', {
name: component.name,
- })}
- />
+ })} />
)}
{
const { act, data } = useBackend(context);
-
const {
have_printer,
manifest = {},
} = data;
-
return (
-
+
{
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitorSyndicate.js b/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitorSyndicate.js
new file mode 100644
index 0000000000..efa93513e7
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitorSyndicate.js
@@ -0,0 +1,15 @@
+import { NtosWindow } from '../layouts';
+import { NtosCyborgRemoteMonitorContent } from './NtosCyborgRemoteMonitor';
+
+export const NtosCyborgRemoteMonitorSyndicate = (props, context) => {
+ return (
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/NtosFileManager.js b/tgui/packages/tgui/interfaces/NtosFileManager.js
index 9e5da68298..da762e459f 100644
--- a/tgui/packages/tgui/interfaces/NtosFileManager.js
+++ b/tgui/packages/tgui/interfaces/NtosFileManager.js
@@ -6,12 +6,13 @@ import { NtosWindow } from '../layouts';
export const NtosFileManager = (props, context) => {
const { act, data } = useBackend(context);
const {
+ PC_device_theme,
usbconnected,
files = [],
usbfiles = [],
} = data;
return (
-
+
{
return (
-
+
@@ -14,14 +17,12 @@ export const NtosJobManager = (props, context) => {
export const NtosJobManagerContent = (props, context) => {
const { act, data } = useBackend(context);
-
const {
authed,
cooldown,
slots = [],
prioritized = [],
} = data;
-
if (!authed) {
return (
@@ -29,7 +30,6 @@ export const NtosJobManagerContent = (props, context) => {
);
}
-
return (
{cooldown > 0 && (
@@ -64,8 +64,7 @@ export const NtosJobManagerContent = (props, context) => {
checked={slot.total > 0 && prioritized.includes(slot.title)}
onClick={() => act('PRG_priority', {
target: slot.title,
- })}
- />
+ })} />
{slot.current} / {slot.total}
@@ -76,15 +75,13 @@ export const NtosJobManagerContent = (props, context) => {
disabled={!slot.status_open}
onClick={() => act('PRG_open_job', {
target: slot.title,
- })}
- />
+ })} />
act('PRG_close_job', {
target: slot.title,
- })}
- />
+ })} />
))}
diff --git a/tgui/packages/tgui/interfaces/NtosMain.js b/tgui/packages/tgui/interfaces/NtosMain.js
index d03e9ba4b3..c61767bd7b 100644
--- a/tgui/packages/tgui/interfaces/NtosMain.js
+++ b/tgui/packages/tgui/interfaces/NtosMain.js
@@ -23,13 +23,21 @@ const PROGRAM_ICONS = {
export const NtosMain = (props, context) => {
const { act, data } = useBackend(context);
const {
+ device_theme,
programs = [],
has_light,
light_on,
comp_light_color,
} = data;
return (
-
+
{!!has_light && (
@@ -55,7 +63,6 @@ export const NtosMain = (props, context) => {
{
{!!program.running && (
{
const { act, data } = useBackend(context);
-
const {
can_admin,
adminmode,
@@ -17,17 +16,15 @@ export const NtosNetChat = (props, context) => {
clients = [],
messages = [],
} = data;
-
const in_channel = (active_channel !== null);
const authorized = (authed || adminmode);
-
return (
-
+
-
-
+
+
{
)
)}
-
{
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/NtosNetDownloader.js b/tgui/packages/tgui/interfaces/NtosNetDownloader.js
index e1f8871167..24d127455a 100644
--- a/tgui/packages/tgui/interfaces/NtosNetDownloader.js
+++ b/tgui/packages/tgui/interfaces/NtosNetDownloader.js
@@ -5,6 +5,7 @@ import { NtosWindow } from '../layouts';
export const NtosNetDownloader = (props, context) => {
const { act, data } = useBackend(context);
const {
+ PC_device_theme,
disk_size,
disk_used,
downloadable_programs = [],
@@ -13,7 +14,11 @@ export const NtosNetDownloader = (props, context) => {
hackedavailable,
} = data;
return (
-
+
{!!error && (
diff --git a/tgui/packages/tgui/interfaces/NtosPowerMonitor.js b/tgui/packages/tgui/interfaces/NtosPowerMonitor.js
index a9f6d2e8fc..c178fa7663 100644
--- a/tgui/packages/tgui/interfaces/NtosPowerMonitor.js
+++ b/tgui/packages/tgui/interfaces/NtosPowerMonitor.js
@@ -3,7 +3,10 @@ import { PowerMonitorContent } from './PowerMonitor';
export const NtosPowerMonitor = () => {
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/NtosRadar.js b/tgui/packages/tgui/interfaces/NtosRadar.js
index ca38935056..2d2fe6a452 100644
--- a/tgui/packages/tgui/interfaces/NtosRadar.js
+++ b/tgui/packages/tgui/interfaces/NtosRadar.js
@@ -1,12 +1,15 @@
-import { Fragment } from 'inferno';
-import { useBackend } from '../backend';
import { classes } from 'common/react';
-import { Box, Button, LabeledList, NoticeBox, Section, Table, Flex, Icon } from '../components';
+import { resolveAsset } from '../assets';
+import { useBackend } from '../backend';
+import { Box, Button, Flex, Icon, NoticeBox, Section } from '../components';
import { NtosWindow } from '../layouts';
export const NtosRadar = (props, context) => {
return (
-
+
);
@@ -20,9 +23,8 @@ export const NtosRadarContent = (props, context) => {
target = [],
scanning,
} = data;
-
return (
-
{
{
)
: !!target.userot && (
+ }} />
) || (
+ top={((target.locy * 10) + 19) + 'px'}
+ left={((target.locx * 10) + 16) + 'px'} />
)}
diff --git a/tgui/packages/tgui/interfaces/NtosRadarSyndicate.js b/tgui/packages/tgui/interfaces/NtosRadarSyndicate.js
index 2079ce6bcf..3cb4626bb9 100644
--- a/tgui/packages/tgui/interfaces/NtosRadarSyndicate.js
+++ b/tgui/packages/tgui/interfaces/NtosRadarSyndicate.js
@@ -1,14 +1,13 @@
-import { Fragment } from 'inferno';
-import { useBackend } from '../backend';
-import { classes } from 'common/react';
-import { Box, Button, LabeledList, NoticeBox, Section, Table, Flex, Icon } from '../components';
import { NtosWindow } from '../layouts';
import { NtosRadarContent } from './NtosRadar';
export const NtosRadarSyndicate = (props, context) => {
return (
-
+
);
-};
\ No newline at end of file
+};
diff --git a/tgui/packages/tgui/interfaces/NtosRequestKiosk.js b/tgui/packages/tgui/interfaces/NtosRequestKiosk.js
new file mode 100644
index 0000000000..63a8dc63f6
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NtosRequestKiosk.js
@@ -0,0 +1,15 @@
+import { RequestKioskContent } from './RequestKiosk';
+import { NtosWindow } from '../layouts';
+
+export const NtosRequestKiosk = (props, context) => {
+ return (
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/NtosRevelation.js b/tgui/packages/tgui/interfaces/NtosRevelation.js
index c4df0fa5b2..6431375011 100644
--- a/tgui/packages/tgui/interfaces/NtosRevelation.js
+++ b/tgui/packages/tgui/interfaces/NtosRevelation.js
@@ -1,20 +1,23 @@
-import { Section, Button, LabeledList } from "../components";
-import { useBackend } from "../backend";
-import { NtosWindow } from "../layouts";
+import { Section, Button, LabeledList } from '../components';
+import { useBackend } from '../backend';
+import { NtosWindow } from '../layouts';
export const NtosRevelation = (props, context) => {
const { act, data } = useBackend(context);
-
return (
-
+
act('PRG_obfuscate', { new_name: value })}
- mb={1}
- />
+ onCommit={(e, value) => act('PRG_obfuscate', {
+ new_name: value,
+ })}
+ mb={1} />
{
act('PRG_arm')}
- />
- )}
- />
+ onClick={() => act('PRG_arm')} />
+ )} />
{
content="ACTIVATE"
textAlign="center"
color="bad"
- disabled={!data.armed}
- />
+ disabled={!data.armed} />
diff --git a/tgui/packages/tgui/interfaces/NtosRoboControl.js b/tgui/packages/tgui/interfaces/NtosRoboControl.js
index 6bab7b9948..7343fadc23 100644
--- a/tgui/packages/tgui/interfaces/NtosRoboControl.js
+++ b/tgui/packages/tgui/interfaces/NtosRoboControl.js
@@ -15,7 +15,10 @@ export const NtosRoboControl = (props, context) => {
has_id,
} = data;
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/NtosShipping.js b/tgui/packages/tgui/interfaces/NtosShipping.js
index ac387b2ef9..0ff0704cd1 100644
--- a/tgui/packages/tgui/interfaces/NtosShipping.js
+++ b/tgui/packages/tgui/interfaces/NtosShipping.js
@@ -5,7 +5,10 @@ import { NtosWindow } from '../layouts';
export const NtosShipping = (props, context) => {
const { act, data } = useBackend(context);
return (
-
+
{
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/NtosSupermatterMonitor.js b/tgui/packages/tgui/interfaces/NtosSupermatterMonitor.js
index c62eb5686f..9975113334 100644
--- a/tgui/packages/tgui/interfaces/NtosSupermatterMonitor.js
+++ b/tgui/packages/tgui/interfaces/NtosSupermatterMonitor.js
@@ -10,7 +10,10 @@ const logScale = value => Math.log2(16 + Math.max(0, value)) - 4;
export const NtosSupermatterMonitor = (props, context) => {
return (
-
+
diff --git a/tgui/packages/tgui/interfaces/NuclearBomb.js b/tgui/packages/tgui/interfaces/NuclearBomb.js
index a708602469..0a4a3324b1 100644
--- a/tgui/packages/tgui/interfaces/NuclearBomb.js
+++ b/tgui/packages/tgui/interfaces/NuclearBomb.js
@@ -24,11 +24,11 @@ const NukeKeypad = (props, context) => {
fluid
bold
key={key}
- mb={1}
+ mb="6px"
content={key}
textAlign="center"
fontSize="40px"
- lineHeight="50px"
+ lineHeight={1.25}
width="55px"
className={classes([
'NuclearBomb__Button',
@@ -53,11 +53,14 @@ export const NuclearBomb = (props, context) => {
status2,
} = data;
return (
-
+
-
+
{status1}
@@ -71,10 +74,10 @@ export const NuclearBomb = (props, context) => {
{
-
+
{
content="ARM"
textAlign="center"
fontSize="28px"
- lineHeight="32px"
- mb={1}
+ lineHeight={1.1}
+ mb="6px"
className="NuclearBomb__Button NuclearBomb__Button--C"
onClick={() => act('arm')} />