diff --git a/.travis.yml b/.travis.yml
index cea5f854c91..09cefa27b32 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,13 +4,10 @@ language: c
env:
global:
- BASENAME="vorestation" # $BASENAME.dmb, $BASENAME.dme, etc.
- - BYOND_MAJOR="513"
- - BYOND_MINOR="1520"
- - MACRO_COUNT=4
cache:
directories:
- - $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}
+ - $HOME/BYOND
addons:
apt:
@@ -37,13 +34,17 @@ jobs:
include:
- stage: "File Tests" #This is the odd man out, with specific installs and stuff.
name: "Validate Files"
- install: #Need python for some of the tag matching stuff
- - pip install --user PyYaml -q
- - pip install --user beautifulsoup4 -q
- script: ./tools/travis/validate_files.sh
addons:
apt:
- packages: ~ # Don't need any packages for this
+ packages:
+ - python3
+ - python3-pip
+ - python3-setuptools
+ install: #Need python for some of the tag matching stuff
+ - tools/travis/install_build_deps.sh
+ script:
+ - tools/travis/validate_files.sh
+ - tools/travis/build_tgui.sh
- stage: "Unit Tests"
env: TEST_DEFINE="UNIT_TEST" TEST_FILE="code/_unit_tests.dm" RUN="1"
name: "Compile normally (unit tests)"
diff --git a/_build_dependencies.sh b/_build_dependencies.sh
new file mode 100644
index 00000000000..b33548a58a0
--- /dev/null
+++ b/_build_dependencies.sh
@@ -0,0 +1,13 @@
+# This file has all the information on what versions of libraries are thrown into the code
+# For dreamchecker
+export SPACEMANDMM_TAG=suite-1.4
+# For NanoUI + TGUI
+export NODE_VERSION=12
+# For the scripts in tools
+export PHP_VERSION=5.6
+# Byond Major
+export BYOND_MAJOR=513
+# Byond Minor
+export BYOND_MINOR=1526
+# Macro Count
+export MACRO_COUNT=4
\ No newline at end of file
diff --git a/code/__defines/species_languages_vr.dm b/code/__defines/species_languages_vr.dm
index 831df5253dd..54362278d4d 100644
--- a/code/__defines/species_languages_vr.dm
+++ b/code/__defines/species_languages_vr.dm
@@ -1,5 +1,6 @@
#define SPECIES_WHITELIST_SELECTABLE 0x20 // Can select and customize, but not join as
+#define LANGUAGE_DRUDAKAR "D'Rudak'Ar"
#define LANGUAGE_SLAVIC "Pan-Slavic"
#define LANGUAGE_BIRDSONG "Birdsong"
#define LANGUAGE_SAGARU "Sagaru"
diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm
index 0fa2532bf9b..94228f34399 100644
--- a/code/__defines/subsystems.dm
+++ b/code/__defines/subsystems.dm
@@ -100,6 +100,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
#define FIRE_PRIORITY_TICKER 60
#define FIRE_PRIORITY_PLANETS 75
#define FIRE_PRIORITY_MACHINES 100
+#define FIRE_PRIORITY_TGUI 110
#define FIRE_PRIORITY_PROJECTILES 150
#define FIRE_PRIORITY_CHAT 400
#define FIRE_PRIORITY_OVERLAYS 500
diff --git a/code/__defines/tgui.dm b/code/__defines/tgui.dm
new file mode 100644
index 00000000000..a6708c8bb7a
--- /dev/null
+++ b/code/__defines/tgui.dm
@@ -0,0 +1,19 @@
+/// 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))
\ No newline at end of file
diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index 6ec3ccb9662..194ec86d3d9 100644
--- a/code/_helpers/game.dm
+++ b/code/_helpers/game.dm
@@ -615,4 +615,25 @@ datum/projectile_data
/proc/window_flash(var/client_or_usr)
if (!client_or_usr)
return
- winset(client_or_usr, "mainwindow", "flash=5")
\ No newline at end of file
+ winset(client_or_usr, "mainwindow", "flash=5")
+
+/**
+ * Get a bounding box of a list of atoms.
+ *
+ * Arguments:
+ * - atoms - List of atoms. Can accept output of view() and range() procs.
+ *
+ * Returns: list(x1, y1, x2, y2)
+ */
+/proc/get_bbox_of_atoms(list/atoms)
+ var/list/list_x = list()
+ var/list/list_y = list()
+ for(var/_a in atoms)
+ var/atom/a = _a
+ list_x += a.x
+ list_y += a.y
+ return list(
+ min(list_x),
+ min(list_y),
+ max(list_x),
+ max(list_y))
\ No newline at end of file
diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm
index d27abcbf7e2..757555f0375 100644
--- a/code/_helpers/logging.dm
+++ b/code/_helpers/logging.dm
@@ -176,6 +176,22 @@
/proc/log_unit_test(text)
to_world_log("## UNIT_TEST: [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(diary, entry)
+
+/proc/log_asset(text)
+ WRITE_LOG(diary, "ASSET: [text]")
+
/proc/report_progress(var/progress_message)
admin_notice("[progress_message]", R_DEBUG)
to_world_log(progress_message)
diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm
index 67844cf4a2d..1b028e117b7 100644
--- a/code/_helpers/text.dm
+++ b/code/_helpers/text.dm
@@ -138,6 +138,19 @@
/proc/sanitize_old(var/t,var/list/repl_chars = list("\n"="#","\t"="#"))
return html_encode(replace_characters(t,repl_chars))
+
+//Removes a few problematic characters
+/proc/sanitize_simple(t,list/repl_chars = list("\n"="#","\t"="#"))
+ for(var/char in repl_chars)
+ var/index = findtext(t, char)
+ while(index)
+ t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index + length(char))
+ index = findtext(t, char, index + length(char))
+ return t
+
+/proc/sanitize_filename(t)
+ return sanitize_simple(t, list("\n"="", "\t"="", "/"="", "\\"="", "?"="", "%"="", "*"="", ":"="", "|"="", "\""="", "<"="", ">"=""))
+
/*
* Text searches
*/
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index e218727049b..99a313e4544 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -1571,6 +1571,14 @@ var/mob/dview/dview_mob = new
/datum/proc/stack_trace(msg)
CRASH(msg)
+GLOBAL_REAL_VAR(list/stack_trace_storage)
+/proc/gib_stack_trace()
+ stack_trace_storage = list()
+ stack_trace()
+ stack_trace_storage.Cut(1, min(3,stack_trace_storage.len))
+ . = stack_trace_storage
+ stack_trace_storage = null
+
// \ref behaviour got changed in 512 so this is necesary to replicate old behaviour.
// If it ever becomes necesary to get a more performant REF(), this lies here in wait
// #define REF(thing) (thing && istype(thing, /datum) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : "\ref[thing]")
diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm
index 7060451b275..f0a959c6a7b 100644
--- a/code/_onclick/hud/fullscreen.dm
+++ b/code/_onclick/hud/fullscreen.dm
@@ -123,8 +123,3 @@
/obj/screen/fullscreen/fishbed
icon_state = "fishbed"
-
-#undef FULLSCREEN_LAYER
-#undef BLIND_LAYER
-#undef DAMAGE_LAYER
-#undef CRIT_LAYER
\ No newline at end of file
diff --git a/code/_onclick/hud/map_popups.dm b/code/_onclick/hud/map_popups.dm
new file mode 100644
index 00000000000..aae5c808c97
--- /dev/null
+++ b/code/_onclick/hud/map_popups.dm
@@ -0,0 +1,171 @@
+/client
+ /**
+ * Assoc list with all the active maps - when a screen obj is added to
+ * a map, it's put in here as well.
+ *
+ * Format: list( = list(/obj/screen))
+ */
+ var/list/screen_maps = list()
+
+/obj/screen
+ /**
+ * Map name assigned to this object.
+ * Automatically set by /client/proc/register_map_obj.
+ */
+ var/assigned_map
+ /**
+ * Mark this object as garbage-collectible after you clean the map
+ * it was registered on.
+ *
+ * This could probably be changed to be a proc, for conditional removal.
+ * But for now, this works.
+ */
+ var/del_on_map_removal = TRUE
+
+/**
+ * A screen object, which acts as a container for turfs and other things
+ * you want to show on the map, which you usually attach to "vis_contents".
+ */
+/obj/screen/map_view
+ icon_state = "blank"
+ // Map view has to be on the lowest plane to enable proper lighting
+ layer = SPACE_PLANE
+ plane = SPACE_PLANE
+
+/**
+ * A generic background object.
+ * It is also implicitly used to allocate a rectangle on the map, which will
+ * be used for auto-scaling the map.
+ */
+/obj/screen/background
+ name = "background"
+ icon = 'icons/mob/map_backgrounds.dmi'
+ icon_state = "clear"
+ layer = SPACE_PLANE
+ plane = SPACE_PLANE
+
+/**
+ * Sets screen_loc of this screen object, in form of point coordinates,
+ * with optional pixel offset (px, py).
+ *
+ * If applicable, "assigned_map" has to be assigned before this proc call.
+ */
+/obj/screen/proc/set_position(x, y, px = 0, py = 0)
+ if(assigned_map)
+ screen_loc = "[assigned_map]:[x]:[px],[y]:[py]"
+ else
+ screen_loc = "[x]:[px],[y]:[py]"
+
+/**
+ * Sets screen_loc to fill a rectangular area of the map.
+ *
+ * If applicable, "assigned_map" has to be assigned before this proc call.
+ */
+/obj/screen/proc/fill_rect(x1, y1, x2, y2)
+ if(assigned_map)
+ screen_loc = "[assigned_map]:[x1],[y1] to [x2],[y2]"
+ else
+ screen_loc = "[x1],[y1] to [x2],[y2]"
+
+/**
+ * Registers screen obj with the client, which makes it visible on the
+ * assigned map, and becomes a part of the assigned map's lifecycle.
+ */
+/client/proc/register_map_obj(obj/screen/screen_obj)
+ if(!screen_obj.assigned_map)
+ CRASH("Can't register [screen_obj] without 'assigned_map' property.")
+ if(!screen_maps[screen_obj.assigned_map])
+ screen_maps[screen_obj.assigned_map] = list()
+ // NOTE: Possibly an expensive operation
+ var/list/screen_map = screen_maps[screen_obj.assigned_map]
+ if(!screen_map.Find(screen_obj))
+ screen_map += screen_obj
+ if(!screen.Find(screen_obj))
+ screen += screen_obj
+
+/**
+ * Clears the map of registered screen objects.
+ *
+ * Not really needed most of the time, as the client's screen list gets reset
+ * on relog. any of the buttons are going to get caught by garbage collection
+ * anyway. they're effectively qdel'd.
+ */
+/client/proc/clear_map(map_name)
+ if(!map_name || !(map_name in screen_maps))
+ return FALSE
+ for(var/obj/screen/screen_obj in screen_maps[map_name])
+ screen_maps[map_name] -= screen_obj
+ if(screen_obj.del_on_map_removal)
+ qdel(screen_obj)
+ screen_maps -= map_name
+
+/**
+ * Clears all the maps of registered screen objects.
+ */
+/client/proc/clear_all_maps()
+ for(var/map_name in screen_maps)
+ clear_map(map_name)
+
+/**
+ * Creates a popup window with a basic map element in it, without any
+ * further initialization.
+ *
+ * Ratio is how many pixels by how many pixels (keep it simple).
+ *
+ * Returns a map name.
+ */
+/client/proc/create_popup(name, ratiox = 100, ratioy = 100)
+ winclone(src, "popupwindow", name)
+ var/list/winparams = list()
+ winparams["size"] = "[ratiox]x[ratioy]"
+ winparams["on-close"] = "handle-popup-close [name]"
+ winset(src, "[name]", list2params(winparams))
+ winshow(src, "[name]", 1)
+
+ var/list/params = list()
+ params["parent"] = "[name]"
+ params["type"] = "map"
+ params["size"] = "[ratiox]x[ratioy]"
+ params["anchor1"] = "0,0"
+ params["anchor2"] = "[ratiox],[ratioy]"
+ winset(src, "[name]_map", list2params(params))
+
+ return "[name]_map"
+
+/**
+ * Create the popup, and get it ready for generic use by giving
+ * it a background.
+ *
+ * Width and height are multiplied by 64 by default.
+ */
+/client/proc/setup_popup(popup_name, width = 9, height = 9, \
+ tilesize = 2, bg_icon)
+ if(!popup_name)
+ return
+ clear_map("[popup_name]_map")
+ var/x_value = world.icon_size * tilesize * width
+ var/y_value = world.icon_size * tilesize * height
+ var/map_name = create_popup(popup_name, x_value, y_value)
+
+ var/obj/screen/background/background = new
+ background.assigned_map = map_name
+ background.fill_rect(1, 1, width, height)
+ if(bg_icon)
+ background.icon_state = bg_icon
+ register_map_obj(background)
+
+ return map_name
+
+/**
+ * Closes a popup.
+ */
+/client/proc/close_popup(popup)
+ winshow(src, popup, 0)
+ handle_popup_close(popup)
+
+/**
+ * When the popup closes in any way (player or proc call) it calls this.
+ */
+/client/verb/handle_popup_close(window_id as text)
+ set hidden = TRUE
+ clear_map("[window_id]_map")
diff --git a/code/_onclick/hud/skybox.dm b/code/_onclick/hud/skybox.dm
index f055ea86936..efd11798915 100644
--- a/code/_onclick/hud/skybox.dm
+++ b/code/_onclick/hud/skybox.dm
@@ -3,17 +3,20 @@
#define SKYBOX_TURFS (SKYBOX_PIXELS/WORLD_ICON_SIZE)
// Skybox screen object.
-/obj/skybox
+/obj/screen/skybox
name = "skybox"
+ icon = null
+ appearance_flags = TILE_BOUND|PIXEL_SCALE
mouse_opacity = 0
anchored = TRUE
simulated = FALSE
screen_loc = "CENTER,CENTER"
+ layer = OBJ_LAYER
plane = SKYBOX_PLANE
blend_mode = BLEND_MULTIPLY // You actually need to do it this way or you see it in occlusion.
// Adjust transform property to scale for client's view var. We assume the skybox is 736x736 px
-/obj/skybox/proc/scale_to_view(var/view)
+/obj/screen/skybox/proc/scale_to_view(var/view)
var/matrix/M = matrix()
// Translate to center the icon over us!
M.Translate(-(SKYBOX_PIXELS - WORLD_ICON_SIZE) / 2)
@@ -23,7 +26,7 @@
src.transform = M
/client
- var/obj/skybox/skybox
+ var/obj/screen/skybox/skybox
/client/proc/update_skybox(rebuild)
if(!skybox)
diff --git a/code/controllers/subsystems/ai.dm b/code/controllers/subsystems/ai.dm
index 0641453f2ba..4ed43a34811 100644
--- a/code/controllers/subsystems/ai.dm
+++ b/code/controllers/subsystems/ai.dm
@@ -32,7 +32,7 @@ SUBSYSTEM_DEF(ai)
while(currentrun.len)
var/datum/ai_holder/A = currentrun[currentrun.len]
--currentrun.len
- if(!A || QDELETED(A) || A.busy) // Doesn't exist or won't exist soon or not doing it this tick
+ if(!A || QDELETED(A) || !A.holder?.loc || A.busy) // Doesn't exist or won't exist soon or not doing it this tick
continue
if(process_z[get_z(A.holder)])
diff --git a/code/controllers/subsystems/mobs.dm b/code/controllers/subsystems/mobs.dm
index 8fb2bf3f891..1ae1d647579 100644
--- a/code/controllers/subsystems/mobs.dm
+++ b/code/controllers/subsystems/mobs.dm
@@ -43,7 +43,7 @@ SUBSYSTEM_DEF(mobs)
if(!M || QDELETED(M))
mob_list -= M
continue
- else if(M.low_priority && !(process_z[get_z(M)]))
+ else if(M.low_priority && !(M.loc && process_z[get_z(M)]))
slept_mobs++
continue
diff --git a/code/controllers/subsystems/skybox.dm b/code/controllers/subsystems/skybox.dm
index 03f41f0f33d..6befac0b605 100644
--- a/code/controllers/subsystems/skybox.dm
+++ b/code/controllers/subsystems/skybox.dm
@@ -132,7 +132,7 @@ SUBSYSTEM_DEF(skybox)
for(var/z in zlevels)
skybox_cache["[z]"] = generate_skybox(z)
- for(var/client/C)
+ for(var/client/C in GLOB.clients)
var/their_z = get_z(C.mob)
if(!their_z) //Nullspace
continue
diff --git a/code/controllers/subsystems/tgui.dm b/code/controllers/subsystems/tgui.dm
new file mode 100644
index 00000000000..91533a77833
--- /dev/null
+++ b/code/controllers/subsystems/tgui.dm
@@ -0,0 +1,343 @@
+ /**
+ * tgui subsystem
+ *
+ * Contains all tgui state and subsystem code.
+ **/
+
+
+SUBSYSTEM_DEF(tgui)
+ name = "TGUI"
+ wait = 9
+ flags = SS_NO_INIT
+ priority = FIRE_PRIORITY_TGUI
+ runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
+
+ /// 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 and ui_key.
+ 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')
+
+/datum/controller/subsystem/tgui/Shutdown()
+ close_all_uis()
+
+/datum/controller/subsystem/tgui/stat_entry()
+ ..("P:[open_uis.len]")
+
+/datum/controller/subsystem/tgui/fire(resumed = 0)
+ 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
+ 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)
+ return null
+ return window
+
+/**
+ * public
+ *
+ * Force closes all tgui windows.
+ *
+ * required user mob
+ */
+/datum/controller/subsystem/tgui/proc/force_close_all_windows(mob/user)
+ 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)
+ // 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
+ *
+ * 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.
+ *
+ * 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 <= STATUS_CLOSE)
+ ui.close()
+ return null
+ ui.send_update()
+ return 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)
+ 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 // No UIs open.
+ for(var/datum/tgui/ui in open_uis_by_src[key]) // Find UIs for this object.
+ // Make sure we have the right user
+ if(ui.user == 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/count = 0
+ var/key = "[REF(src_object)]"
+ if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list))
+ return count // Couldn't find any UIs for this object.
+ for(var/datum/tgui/ui in open_uis_by_src[key])
+ // Check the UI is valid.
+ if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user))
+ ui.process(force = 1) // Update the UI.
+ count++ // Count each UI we update.
+ return 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/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])
+ if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid.
+ ui.close() // Close the UI.
+ count++ // Count each UI we close.
+ return count
+
+ /**
+ * private
+ *
+ * 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])
+ if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid.
+ ui.close() // Close the UI.
+ count++ // Count each UI we close.
+ return 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.
+ *
+ * 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
+
+ /**
+ * 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.
+ *
+ * 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)
+ 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)
+ // 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
\ No newline at end of file
diff --git a/code/datums/repositories/cameras.dm b/code/datums/repositories/cameras.dm
index d5161133948..e69de29bb2d 100644
--- a/code/datums/repositories/cameras.dm
+++ b/code/datums/repositories/cameras.dm
@@ -1,49 +0,0 @@
-var/global/datum/repository/cameras/camera_repository = new()
-
-/proc/invalidateCameraCache()
- camera_repository.networks.Cut()
- camera_repository.invalidated = 1
- camera_repository.camera_cache_id = (++camera_repository.camera_cache_id % 999999)
-
-/datum/repository/cameras
- var/list/networks
- var/invalidated = 1
- var/camera_cache_id = 1
-
-/datum/repository/cameras/New()
- networks = list()
- ..()
-
-/datum/repository/cameras/proc/cameras_in_network(var/network, var/list/zlevels)
- setup_cache()
- var/list/network_list = networks[network]
- if(LAZYLEN(zlevels))
- var/list/filtered_cameras = list()
- for(var/list/C in network_list)
- //Camera is marked as always-visible
- if(C["omni"])
- filtered_cameras[++filtered_cameras.len] = C
- continue
- //Camera might be in an adjacent zlevel
- var/camz = C["z"]
- if(!camz) //It's inside something (helmet, communicator, etc) or nullspace or who knows
- camz = get_z(locate(C["camera"]) in cameranet.cameras)
- if(camz in zlevels)
- filtered_cameras[++filtered_cameras.len] = C //Can't add lists to lists with +=
- return filtered_cameras
- else
- return network_list
-
-/datum/repository/cameras/proc/setup_cache()
- if(!invalidated)
- return
- invalidated = 0
-
- cameranet.process_sort()
- for(var/obj/machinery/camera/C in cameranet.cameras)
- var/cam = C.nano_structure()
- for(var/network in C.network)
- if(!networks[network])
- networks[network] = list()
- var/list/netlist = networks[network]
- netlist[++netlist.len] = cam
diff --git a/code/datums/repositories/crew.dm b/code/datums/repositories/crew.dm
index f67a17ca75b..7a748049bc2 100644
--- a/code/datums/repositories/crew.dm
+++ b/code/datums/repositories/crew.dm
@@ -51,6 +51,7 @@ var/global/datum/repository/crew/crew_repository = new()
crewmemberData["area"] = sanitize(A.get_name())
crewmemberData["x"] = pos.x
crewmemberData["y"] = pos.y
+ crewmemberData["realZ"] = pos.z
crewmemberData["z"] = using_map.get_zlevel_name(pos.z)
crewmembers[++crewmembers.len] = crewmemberData
diff --git a/code/datums/supplypacks/atmospherics.dm b/code/datums/supplypacks/atmospherics.dm
index cf102f129db..fb5d4a0e597 100644
--- a/code/datums/supplypacks/atmospherics.dm
+++ b/code/datums/supplypacks/atmospherics.dm
@@ -11,42 +11,42 @@
name = "Inflatable barriers"
contains = list(/obj/item/weapon/storage/briefcase/inflatable = 3)
cost = 20
- containertype = /obj/structure/closet/crate/engineering
+ containertype = /obj/structure/closet/crate/aether
containername = "Inflatable Barrier Crate"
/datum/supply_pack/atmos/canister_empty
name = "Empty gas canister"
cost = 7
containername = "Empty gas canister crate"
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/aether
contains = list(/obj/machinery/portable_atmospherics/canister)
/datum/supply_pack/atmos/canister_air
name = "Air canister"
cost = 10
containername = "Air canister crate"
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/aether
contains = list(/obj/machinery/portable_atmospherics/canister/air)
/datum/supply_pack/atmos/canister_oxygen
name = "Oxygen canister"
cost = 15
containername = "Oxygen canister crate"
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/aether
contains = list(/obj/machinery/portable_atmospherics/canister/oxygen)
/datum/supply_pack/atmos/canister_nitrogen
name = "Nitrogen canister"
cost = 10
containername = "Nitrogen canister crate"
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/aether
contains = list(/obj/machinery/portable_atmospherics/canister/nitrogen)
/datum/supply_pack/atmos/canister_phoron
name = "Phoron gas canister"
cost = 60
containername = "Phoron gas canister crate"
- containertype = /obj/structure/closet/crate/secure/large
+ containertype = /obj/structure/closet/crate/secure/large/aether
access = access_atmospherics
contains = list(/obj/machinery/portable_atmospherics/canister/phoron)
@@ -54,7 +54,7 @@
name = "N2O gas canister"
cost = 15
containername = "N2O gas canister crate"
- containertype = /obj/structure/closet/crate/secure/large
+ containertype = /obj/structure/closet/crate/secure/large/aether
access = access_atmospherics
contains = list(/obj/machinery/portable_atmospherics/canister/sleeping_agent)
@@ -62,7 +62,7 @@
name = "Carbon dioxide gas canister"
cost = 15
containername = "CO2 canister crate"
- containertype = /obj/structure/closet/crate/secure/large
+ containertype = /obj/structure/closet/crate/secure/large/aether
access = access_atmospherics
contains = list(/obj/machinery/portable_atmospherics/canister/carbon_dioxide)
@@ -70,7 +70,7 @@
contains = list(/obj/machinery/pipedispenser/orderable)
name = "Pipe Dispenser"
cost = 25
- containertype = /obj/structure/closet/crate/secure/large
+ containertype = /obj/structure/closet/crate/secure/large/aether
containername = "Pipe Dispenser Crate"
access = access_atmospherics
@@ -78,7 +78,7 @@
contains = list(/obj/machinery/pipedispenser/disposal/orderable)
name = "Disposals Pipe Dispenser"
cost = 25
- containertype = /obj/structure/closet/crate/secure/large
+ containertype = /obj/structure/closet/crate/secure/large/aether
containername = "Disposal Dispenser Crate"
access = access_atmospherics
@@ -89,7 +89,7 @@
/obj/item/weapon/tank/air = 3
)
cost = 10
- containertype = /obj/structure/closet/crate/internals
+ containertype = /obj/structure/closet/crate/aether
containername = "Internals crate"
/datum/supply_pack/atmos/evacuation
@@ -104,5 +104,5 @@
/obj/item/clothing/mask/gas = 4
)
cost = 35
- containertype = /obj/structure/closet/crate/internals
+ containertype = /obj/structure/closet/crate/aether
containername = "Emergency crate"
diff --git a/code/datums/supplypacks/contraband.dm b/code/datums/supplypacks/contraband.dm
index 7b266b2a76f..7d8616a8ad0 100644
--- a/code/datums/supplypacks/contraband.dm
+++ b/code/datums/supplypacks/contraband.dm
@@ -28,7 +28,7 @@
/obj/item/weapon/grenade/chem_grenade/incendiary
)
cost = 25
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/weapon
containername = "Special Ops crate"
contraband = 1
@@ -39,7 +39,7 @@
/obj/item/weapon/reagent_containers/food/snacks/unajerky = 4
)
cost = 25
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/unathi
containername = "Moghes imports crate"
contraband = 1
@@ -51,7 +51,7 @@
)
cost = 50
contraband = 1
- containertype = /obj/structure/closet/crate/secure/weapon
+ containertype = /obj/structure/closet/crate/hedberg
containername = "Ballistic weapons crate"
/datum/supply_pack/randomised/misc/telecrate //you get something awesome, a couple of decent things, and a few weak/filler things
@@ -103,5 +103,5 @@
)
cost = 250 //more than a hat crate!,
contraband = 1
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large
containername = "Suspicious crate"
diff --git a/code/datums/supplypacks/costumes.dm b/code/datums/supplypacks/costumes.dm
index 879d5accd0c..f9b61d95723 100644
--- a/code/datums/supplypacks/costumes.dm
+++ b/code/datums/supplypacks/costumes.dm
@@ -1,6 +1,6 @@
/*
* Here is where any supply packs
-* related to weapons live.
+* related to costumes live.
*/
@@ -19,7 +19,7 @@
/obj/item/clothing/head/wizard/fake
)
cost = 20
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/nanothreads
containername = "Wizard costume crate"
/datum/supply_pack/randomised/costumes/hats
@@ -48,8 +48,8 @@
)
name = "Collectable hat crate!"
cost = 200
- containertype = /obj/structure/closet/crate
- containername = "Collectable hats crate! Brought to you by Bass.inc!"
+ containertype = /obj/structure/closet/crate/nanothreads
+ containername = "Collectable hats crate"
/datum/supply_pack/randomised/costumes/costume
num_contained = 3
@@ -84,7 +84,7 @@
)
name = "Costumes crate"
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/nanothreads
containername = "Actor Costumes"
/datum/supply_pack/costumes/formal_wear
@@ -104,15 +104,15 @@
/obj/item/clothing/shoes/leather,
/obj/item/clothing/accessory/wcoat
)
- name = "Formalwear closet"
+ name = "Formalwear (Suits)"
cost = 30
- containertype = /obj/structure/closet
- containername = "Formalwear for the best occasions."
+ containertype = /obj/structure/closet/crate/gilthari
+ containername = "Formal suit crate"
datum/supply_pack/costumes/witch
name = "Witch costume"
containername = "Witch costume"
- containertype = /obj/structure/closet
+ containertype = /obj/structure/closet/crate/nanothreads
cost = 20
contains = list(
/obj/item/clothing/suit/wizrobe/marisa/fake,
@@ -124,7 +124,7 @@ datum/supply_pack/costumes/witch
/datum/supply_pack/randomised/costumes/costume_hats
name = "Costume hats"
containername = "Actor hats crate"
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/nanothreads
cost = 10
num_contained = 3
contains = list(
@@ -147,9 +147,9 @@ datum/supply_pack/costumes/witch
)
/datum/supply_pack/randomised/costumes/dresses
- name = "Womens formal dress locker"
- containername = "Pretty dress locker"
- containertype = /obj/structure/closet
+ name = "Formalwear (Dresses)"
+ containername = "Formal dress crate"
+ containertype = /obj/structure/closet/crate/gilthari
cost = 15
num_contained = 3
contains = list(
diff --git a/code/datums/supplypacks/engineering.dm b/code/datums/supplypacks/engineering.dm
index 5ffa26d7117..5d2bc99089a 100644
--- a/code/datums/supplypacks/engineering.dm
+++ b/code/datums/supplypacks/engineering.dm
@@ -11,30 +11,72 @@
name = "Replacement lights"
contains = list(/obj/item/weapon/storage/box/lights/mixed = 3)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/galaksi
containername = "Replacement lights"
/datum/supply_pack/eng/smescoil
name = "Superconducting Magnetic Coil"
contains = list(/obj/item/weapon/smes_coil)
cost = 75
- containertype = /obj/structure/closet/crate/engineering
+ containertype = /obj/structure/closet/crate/focalpoint
containername = "Superconducting Magnetic Coil crate"
/datum/supply_pack/eng/smescoil/super_capacity
name = "Superconducting Capacitance Coil"
contains = list(/obj/item/weapon/smes_coil/super_capacity)
cost = 90
- containertype = /obj/structure/closet/crate/engineering
+ containertype = /obj/structure/closet/crate/focalpoint
containername = "Superconducting Capacitance Coil crate"
/datum/supply_pack/eng/smescoil/super_io
name = "Superconducting Transmission Coil"
contains = list(/obj/item/weapon/smes_coil/super_io)
cost = 90
- containertype = /obj/structure/closet/crate/engineering
+ containertype = /obj/structure/closet/crate/focalpoint
containername = "Superconducting Transmission Coil crate"
+/datum/supply_pack/eng/shield_capacitor
+ name = "Shield Capacitor"
+ contains = list(/obj/machinery/shield_capacitor)
+ cost = 20
+ containertype = /obj/structure/closet/crate/focalpoint
+ containername = "shield capacitor crate"
+
+/datum/supply_pack/eng/shield_capacitor/advanced
+ name = "Advanced Shield Capacitor"
+ contains = list(/obj/machinery/shield_capacitor/advanced)
+ cost = 30
+ containertype = /obj/structure/closet/crate/focalpoint
+ containername = "advanced shield capacitor crate"
+
+/datum/supply_pack/eng/bubble_shield
+ name = "Bubble Shield Generator"
+ contains = list(/obj/machinery/shield_gen)
+ cost = 40
+ containertype =/obj/structure/closet/crate/focalpoint
+ containername = "shield bubble generator crate"
+
+/datum/supply_pack/eng/bubble_shield/advanced
+ name = "Advanced Bubble Shield Generator"
+ contains = list(/obj/machinery/shield_gen/advanced)
+ cost = 60
+ containertype = /obj/structure/closet/crate/focalpoint
+ containername = "advanced bubble shield generator crate"
+
+/datum/supply_pack/eng/hull_shield
+ name = "Hull Shield Generator"
+ contains = list(/obj/machinery/shield_gen/external)
+ cost = 80
+ containertype = /obj/structure/closet/crate/focalpoint
+ containername = "shield hull generator crate"
+
+/datum/supply_pack/eng/hull_shield/advanced
+ name = "Advanced Hull Shield Generator"
+ contains = list(/obj/machinery/shield_gen/external/advanced)
+ cost = 120
+ containertype = /obj/structure/closet/crate/focalpoint
+ containername = "advanced hull shield generator crate"
+
/datum/supply_pack/eng/electrical
name = "Electrical maintenance crate"
contains = list(
@@ -44,7 +86,7 @@
/obj/item/weapon/cell/high = 2
)
cost = 10
- containertype = /obj/structure/closet/crate/engineering/electrical
+ containertype = /obj/structure/closet/crate/ward
containername = "Electrical maintenance crate"
/datum/supply_pack/eng/e_welders
@@ -53,7 +95,7 @@
/obj/item/weapon/weldingtool/electric = 3
)
cost = 15
- containertype = /obj/structure/closet/crate/engineering/electrical
+ containertype = /obj/structure/closet/crate/ward
containername = "Electric welder crate"
/datum/supply_pack/eng/mechanical
@@ -65,14 +107,14 @@
/obj/item/clothing/head/hardhat
)
cost = 10
- containertype = /obj/structure/closet/crate/engineering
+ containertype = /obj/structure/closet/crate/xion
containername = "Mechanical maintenance crate"
/datum/supply_pack/eng/fueltank
name = "Fuel tank crate"
contains = list(/obj/structure/reagent_dispensers/fueltank)
cost = 10
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/nanotrasen
containername = "fuel tank crate"
/datum/supply_pack/eng/solar
@@ -84,35 +126,35 @@
/obj/item/weapon/paper/solar
)
cost = 20
- containertype = /obj/structure/closet/crate/engineering
+ containertype = /obj/structure/closet/crate/einstein
containername = "Solar pack crate"
/datum/supply_pack/eng/engine
name = "Emitter crate"
contains = list(/obj/machinery/power/emitter = 2)
cost = 10
- containertype = /obj/structure/closet/crate/secure/engineering
+ containertype = /obj/structure/closet/crate/secure/einstein
containername = "Emitter crate"
access = access_ce
/datum/supply_pack/eng/engine/field_gen
name = "Field Generator crate"
contains = list(/obj/machinery/field_generator = 2)
- containertype = /obj/structure/closet/crate/secure/engineering
+ containertype = /obj/structure/closet/crate/secure/xion
containername = "Field Generator crate"
access = access_ce
/datum/supply_pack/eng/engine/sing_gen
name = "Singularity Generator crate"
contains = list(/obj/machinery/the_singularitygen)
- containertype = /obj/structure/closet/crate/secure/engineering
+ containertype = /obj/structure/closet/crate/secure/einstein
containername = "Singularity Generator crate"
access = access_ce
/datum/supply_pack/eng/engine/collector
name = "Collector crate"
contains = list(/obj/machinery/power/rad_collector = 3)
- containertype = /obj/structure/closet/crate/secure/engineering
+ containertype = /obj/structure/closet/crate/secure/einstein
containername = "Collector crate"
/datum/supply_pack/eng/engine/PA
@@ -127,23 +169,33 @@
/obj/structure/particle_accelerator/power_box,
/obj/structure/particle_accelerator/end_cap
)
- containertype = /obj/structure/closet/crate/secure/engineering
+ containertype = /obj/structure/closet/crate/secure/einstein
containername = "Particle Accelerator crate"
access = access_ce
-/datum/supply_pack/eng/shield_generator
- name = "Shield Generator Construction Kit"
- contains = list(
- /obj/item/weapon/circuitboard/shield_generator,
- /obj/item/weapon/stock_parts/capacitor,
- /obj/item/weapon/stock_parts/micro_laser,
- /obj/item/weapon/smes_coil,
- /obj/item/weapon/stock_parts/console_screen,
- /obj/item/weapon/stock_parts/subspace/amplifier
- )
- cost = 80
- containertype = /obj/structure/closet/crate/engineering
- containername = "shield generator construction kit crate"
+/datum/supply_pack/eng/shield_gen
+ contains = list(/obj/item/weapon/circuitboard/shield_gen)
+ name = "Bubble shield generator circuitry"
+ cost = 30
+ containertype = /obj/structure/closet/crate/secure/focalpoint
+ containername = "bubble shield generator circuitry crate"
+ access = access_ce
+
+/datum/supply_pack/eng/shield_gen_ex
+ contains = list(/obj/item/weapon/circuitboard/shield_gen_ex)
+ name = "Hull shield generator circuitry"
+ cost = 30
+ containertype = /obj/structure/closet/crate/secure/focalpoint
+ containername = "hull shield generator circuitry crate"
+ access = access_ce
+
+/datum/supply_pack/eng/shield_cap
+ contains = list(/obj/item/weapon/circuitboard/shield_cap)
+ name = "Bubble shield capacitor circuitry"
+ cost = 30
+ containertype = /obj/structure/closet/crate/secure/focalpoint
+ containername = "shield capacitor circuitry crate"
+ access = access_ce
/datum/supply_pack/eng/smbig
name = "Supermatter Core"
@@ -157,7 +209,7 @@
contains = list(/obj/machinery/power/generator)
name = "Mark I Thermoelectric Generator"
cost = 40
- containertype = /obj/structure/closet/crate/secure/large
+ containertype = /obj/structure/closet/crate/secure/large/einstein
containername = "Mk1 TEG crate"
access = access_engine
@@ -165,7 +217,7 @@
contains = list(/obj/machinery/atmospherics/binary/circulator)
name = "Binary atmospheric circulator"
cost = 20
- containertype = /obj/structure/closet/crate/secure/large
+ containertype = /obj/structure/closet/crate/secure/large/einstein
containername = "Atmospheric circulator crate"
access = access_engine
@@ -183,7 +235,7 @@
name = "P.A.C.M.A.N. portable generator parts"
cost = 25
containername = "P.A.C.M.A.N. Portable Generator Construction Kit"
- containertype = /obj/structure/closet/crate/secure/engineering
+ containertype = /obj/structure/closet/crate/secure/focalpoint
access = access_tech_storage
contains = list(
/obj/item/weapon/stock_parts/micro_laser,
@@ -196,7 +248,7 @@
name = "Super P.A.C.M.A.N. portable generator parts"
cost = 35
containername = "Super P.A.C.M.A.N. portable generator construction kit"
- containertype = /obj/structure/closet/crate/secure/engineering
+ containertype = /obj/structure/closet/crate/secure/focalpoint
access = access_tech_storage
contains = list(
/obj/item/weapon/stock_parts/micro_laser,
@@ -209,7 +261,7 @@
name = "R-UST Mk. 8 Tokamak fusion core crate"
cost = 50
containername = "R-UST Mk. 8 Tokamak Fusion Core crate"
- containertype = /obj/structure/closet/crate/secure/engineering
+ containertype = /obj/structure/closet/crate/secure/einstein
access = access_engine
contains = list(
/obj/item/weapon/book/manual/rust_engine,
@@ -221,7 +273,7 @@
name = "R-UST Mk. 8 fuel injector crate"
cost = 30
containername = "R-UST Mk. 8 fuel injector crate"
- containertype = /obj/structure/closet/crate/secure/engineering
+ containertype = /obj/structure/closet/crate/secure/einstein
access = access_engine
contains = list(
/obj/machinery/fusion_fuel_injector,
@@ -233,7 +285,7 @@
name = "Gyrotron crate"
cost = 15
containername = "Gyrotron Crate"
- containertype = /obj/structure/closet/crate/secure/engineering
+ containertype = /obj/structure/closet/crate/secure/einstein
access = access_engine
contains = list(
/obj/machinery/power/emitter/gyrotron,
@@ -244,12 +296,12 @@
name = "Fusion Fuel Compressor circuitry crate"
cost = 10
containername = "Fusion Fuel Compressor circuitry crate"
- containertype = /obj/structure/closet/crate/engineering
+ containertype = /obj/structure/closet/crate/einstein
contains = list(/obj/item/weapon/circuitboard/fusion_fuel_compressor)
/datum/supply_pack/eng/tritium
name = "Tritium crate"
cost = 75
containername = "Tritium crate"
- containertype = /obj/structure/closet/crate/engineering
+ containertype = /obj/structure/closet/crate/einstein
contains = list(/obj/fiftyspawner/tritium)
diff --git a/code/datums/supplypacks/engineering_vr.dm b/code/datums/supplypacks/engineering_vr.dm
index 273d3f65585..61c0f0e1803 100644
--- a/code/datums/supplypacks/engineering_vr.dm
+++ b/code/datums/supplypacks/engineering_vr.dm
@@ -1,3 +1,17 @@
+/datum/supply_pack/eng/modern_shield
+ name = "Modern Shield Construction Kit"
+ contains = list(
+ /obj/item/weapon/circuitboard/shield_generator,
+ /obj/item/weapon/stock_parts/capacitor,
+ /obj/item/weapon/stock_parts/micro_laser,
+ /obj/item/weapon/smes_coil,
+ /obj/item/weapon/stock_parts/console_screen,
+ /obj/item/weapon/stock_parts/subspace/amplifier
+ )
+ cost = 80
+ containertype = /obj/structure/closet/crate/focalpoint
+ containername = "shield generator construction kit crate"
+
/datum/supply_pack/eng/thermoregulator
contains = list(/obj/machinery/power/thermoregulator)
name = "Thermal Regulator"
diff --git a/code/datums/supplypacks/hospitality.dm b/code/datums/supplypacks/hospitality.dm
index fa7f09ebd9f..2c9bbbce518 100644
--- a/code/datums/supplypacks/hospitality.dm
+++ b/code/datums/supplypacks/hospitality.dm
@@ -23,7 +23,7 @@
/obj/item/weapon/reagent_containers/food/drinks/bottle/small/beer = 4,
)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/gilthari
containername = "Party equipment"
/datum/supply_pack/hospitality/barsupplies
@@ -43,7 +43,7 @@
/obj/item/weapon/storage/box/glass_extras/sticks
)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/gilthari
containername = "crate of bar supplies"
/datum/supply_pack/hospitality/cookingoil
@@ -67,7 +67,7 @@
)
name = "Surprise pack of five pizzas"
cost = 15
- containertype = /obj/structure/closet/crate/freezer
+ containertype = /obj/structure/closet/crate/freezer/centauri
containername = "Pizza crate"
/datum/supply_pack/hospitality/gifts
@@ -81,5 +81,5 @@
/obj/item/weapon/paper/card/flower
)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/allico
containername = "crate of gifts"
\ No newline at end of file
diff --git a/code/datums/supplypacks/hydroponics.dm b/code/datums/supplypacks/hydroponics.dm
index 4341a696ad9..069911606e5 100644
--- a/code/datums/supplypacks/hydroponics.dm
+++ b/code/datums/supplypacks/hydroponics.dm
@@ -11,7 +11,7 @@
name = "Monkey crate"
contains = list (/obj/item/weapon/storage/box/monkeycubes)
cost = 20
- containertype = /obj/structure/closet/crate/freezer
+ containertype = /obj/structure/closet/crate/freezer/nanotrasen
containername = "Monkey crate"
/datum/supply_pack/hydro/farwa
@@ -110,7 +110,7 @@
/obj/item/seeds/sugarcaneseed
)
cost = 10
- containertype = /obj/structure/closet/crate/hydroponics
+ containertype = /obj/structure/closet/crate/carp
containername = "Seeds crate"
access = access_hydroponics
@@ -124,7 +124,7 @@
/obj/item/weapon/material/twohanded/fireaxe/scythe
)
cost = 45
- containertype = /obj/structure/closet/crate/hydroponics
+ containertype = /obj/structure/closet/crate/grayson
containername = "Weed control crate"
access = access_hydroponics
@@ -132,7 +132,7 @@
name = "Water tank crate"
contains = list(/obj/structure/reagent_dispensers/watertank)
cost = 10
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/aether
containername = "water tank crate"
/datum/supply_pack/hydro/bee_keeper
@@ -144,14 +144,14 @@
/obj/item/bee_pack
)
cost = 40
- containertype = /obj/structure/closet/crate/hydroponics
+ containertype = /obj/structure/closet/crate/carp
containername = "Beekeeping crate"
access = access_hydroponics
/datum/supply_pack/hydro/tray
name = "Empty hydroponics trays"
cost = 50
- containertype = /obj/structure/closet/crate/hydroponics
+ containertype = /obj/structure/closet/crate/aether
containername = "Hydroponics tray crate"
contains = list(/obj/machinery/portable_atmospherics/hydroponics{anchored = 0} = 3)
access = access_hydroponics
diff --git a/code/datums/supplypacks/materials.dm b/code/datums/supplypacks/materials.dm
index cd799a235b1..95f15caa6d1 100644
--- a/code/datums/supplypacks/materials.dm
+++ b/code/datums/supplypacks/materials.dm
@@ -11,40 +11,40 @@
name = "50 metal sheets"
contains = list(/obj/fiftyspawner/steel)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/grayson
containername = "Metal sheets crate"
/datum/supply_pack/materials/glass50
name = "50 glass sheets"
contains = list(/obj/fiftyspawner/glass)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/grayson
containername = "Glass sheets crate"
/datum/supply_pack/materials/wood50
name = "50 wooden planks"
contains = list(/obj/fiftyspawner/wood)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/grayson
containername = "Wooden planks crate"
/datum/supply_pack/materials/plastic50
name = "50 plastic sheets"
contains = list(/obj/fiftyspawner/plastic)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/grayson
containername = "Plastic sheets crate"
/datum/supply_pack/materials/cardboard_sheets
contains = list(/obj/fiftyspawner/cardboard)
name = "50 cardboard sheets"
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/grayson
containername = "Cardboard sheets crate"
/datum/supply_pack/materials/carpet
name = "Imported carpet"
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/grayson
containername = "Imported carpet crate"
cost = 15
contains = list(
@@ -55,7 +55,7 @@
/datum/supply_pack/misc/linoleum
name = "Linoleum"
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/grayson
containername = "Linoleum crate"
cost = 15
contains = list(/obj/fiftyspawner/linoleum)
\ No newline at end of file
diff --git a/code/datums/supplypacks/medical.dm b/code/datums/supplypacks/medical.dm
index 1b164a2cdf3..3edcbed2008 100644
--- a/code/datums/supplypacks/medical.dm
+++ b/code/datums/supplypacks/medical.dm
@@ -22,28 +22,28 @@
/obj/item/weapon/storage/box/autoinjectors
)
cost = 10
- containertype = /obj/structure/closet/crate/medical
+ containertype = /obj/structure/closet/crate/zenghu
containername = "Medical crate"
/datum/supply_pack/med/bloodpack
name = "BloodPack crate"
contains = list(/obj/item/weapon/storage/box/bloodpacks = 3)
cost = 10
- containertype = /obj/structure/closet/crate/medical
+ containertype = /obj/structure/closet/crate/nanocare
containername = "BloodPack crate"
/datum/supply_pack/med/bodybag
name = "Body bag crate"
contains = list(/obj/item/weapon/storage/box/bodybags = 3)
cost = 10
- containertype = /obj/structure/closet/crate/medical
+ containertype = /obj/structure/closet/crate/nanocare
containername = "Body bag crate"
/datum/supply_pack/med/cryobag
name = "Stasis bag crate"
contains = list(/obj/item/bodybag/cryobag = 3)
cost = 40
- containertype = /obj/structure/closet/crate/medical
+ containertype = /obj/structure/closet/crate/nanocare
containername = "Stasis bag crate"
/datum/supply_pack/med/surgery
@@ -62,7 +62,7 @@
/obj/item/weapon/surgical/circular_saw
)
cost = 25
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/veymed
containername = "Surgery crate"
access = access_medical
@@ -73,7 +73,7 @@
/obj/item/weapon/storage/box/cdeathalarm_kit
)
cost = 40
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/ward
containername = "Death Alarm crate"
access = access_medical
@@ -83,7 +83,7 @@
/obj/item/weapon/storage/firstaid/clotting
)
cost = 100
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/zenghu
containername = "Clotting Medicine crate"
access = access_medical
@@ -97,7 +97,7 @@
/obj/item/weapon/storage/belt/medical = 3
)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/veymed
containername = "Sterile equipment crate"
/datum/supply_pack/med/extragear
@@ -109,7 +109,7 @@
/obj/item/clothing/suit/storage/hooded/wintercoat/medical = 3
)
cost = 10
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanocare
containername = "Medical surplus equipment"
access = access_medical
@@ -133,7 +133,7 @@
/obj/item/weapon/reagent_containers/syringe
)
cost = 50
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanocare
containername = "Chief medical officer equipment"
access = access_cmo
@@ -156,7 +156,7 @@
/obj/item/weapon/reagent_containers/syringe
)
cost = 20
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanocare
containername = "Medical Doctor equipment"
access = access_medical_equip
@@ -179,7 +179,7 @@
/obj/item/weapon/reagent_containers/syringe
)
cost = 20
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanocare
containername = "Chemist equipment"
access = access_chemistry
@@ -207,7 +207,7 @@
/obj/item/clothing/accessory/storage/white_vest
)
cost = 20
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanocare
containername = "Paramedic equipment"
access = access_medical_equip
@@ -226,7 +226,7 @@
/obj/item/weapon/cartridge/medical
)
cost = 20
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanocare
containername = "Psychiatrist equipment"
access = access_psychiatrist
@@ -247,7 +247,7 @@
/obj/item/weapon/storage/box/gloves
)
cost = 10
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanocare
containername = "Medical scrubs crate"
access = access_medical_equip
@@ -264,7 +264,7 @@
/obj/item/weapon/pen
)
cost = 20
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/veymed
containername = "Autopsy equipment crate"
access = access_morgue
@@ -291,7 +291,7 @@
/obj/item/weapon/storage/box/gloves
)
cost = 10
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanocare
containername = "Medical uniform crate"
access = access_medical_equip
@@ -309,7 +309,7 @@
/obj/item/weapon/storage/box/gloves
)
cost = 50
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanocare
containername = "Medical biohazard equipment"
access = access_medical_equip
@@ -317,7 +317,7 @@
name = "Portable freezers crate"
contains = list(/obj/item/weapon/storage/box/freezer = 7)
cost = 25
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/veymed
containername = "Portable freezers"
access = access_medical_equip
@@ -325,7 +325,7 @@
name = "Virus sample crate"
contains = list(/obj/item/weapon/virusdish/random = 4)
cost = 25
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/zenghu
containername = "Virus sample crate"
access = access_cmo
@@ -333,40 +333,40 @@
name = "Defibrillator crate"
contains = list(/obj/item/device/defib_kit = 2)
cost = 30
- containertype = /obj/structure/closet/crate/medical
+ containertype = /obj/structure/closet/crate/veymed
containername = "Defibrillator crate"
/datum/supply_pack/med/distillery
name = "Chemical distiller crate"
contains = list(/obj/machinery/portable_atmospherics/powered/reagent_distillery = 1)
cost = 50
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/nanotrasen
containername = "Chemical distiller crate"
/datum/supply_pack/med/advdistillery
name = "Industrial Chemical distiller crate"
contains = list(/obj/machinery/portable_atmospherics/powered/reagent_distillery/industrial = 1)
cost = 150
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/xion
containername = "Industrial Chemical distiller crate"
/datum/supply_pack/med/oxypump
name = "Oxygen pump crate"
contains = list(/obj/machinery/oxygen_pump/mobile = 1)
cost = 125
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/xion
containername = "Oxygen pump crate"
/datum/supply_pack/med/anestheticpump
name = "Anesthetic pump crate"
contains = list(/obj/machinery/oxygen_pump/mobile/anesthetic = 1)
cost = 130
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/nanotrasen
containername = "Anesthetic pump crate"
/datum/supply_pack/med/stablepump
name = "Portable stabilizer crate"
contains = list(/obj/machinery/oxygen_pump/mobile/stabilizer = 1)
cost = 175
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/nanotrasen
containername = "Portable stabilizer crate"
diff --git a/code/datums/supplypacks/misc.dm b/code/datums/supplypacks/misc.dm
index 1e5890f0c8b..db46de1e047 100644
--- a/code/datums/supplypacks/misc.dm
+++ b/code/datums/supplypacks/misc.dm
@@ -20,7 +20,7 @@
)
name = "Trading Card Crate"
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/oculum
containername = "cards crate"
/datum/supply_pack/randomised/misc/dnd
@@ -36,7 +36,7 @@
)
name = "Miniatures Crate"
cost = 200
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/oculum
containername = "Miniature Crate"
/datum/supply_pack/randomised/misc/plushies
@@ -88,14 +88,14 @@
//VOREStation Add End
name = "Plushies Crate"
cost = 15
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/allico
containername = "Plushies Crate"
/datum/supply_pack/misc/eftpos
contains = list(/obj/item/device/eftpos)
name = "EFTPOS scanner"
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/nanotrasen
containername = "EFTPOS crate"
/datum/supply_pack/misc/chaplaingear
@@ -113,7 +113,7 @@
/obj/item/weapon/storage/fancy/candle_box = 3
)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/gilthari
containername = "Chaplain equipment crate"
/datum/supply_pack/misc/hoverpod
@@ -136,14 +136,14 @@
/obj/item/clothing/accessory/storage/webbing
)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/nanothreads
containername = "Webbing crate"
/datum/supply_pack/misc/holoplant
name = "Holoplant Pot"
contains = list(/obj/machinery/holoplant/shipped)
cost = 15
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/thinktronic
containername = "Holoplant crate"
/datum/supply_pack/misc/glucose_hypos
@@ -152,7 +152,7 @@
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 5
)
cost = 25
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/zenghu
containername = "Glucose Hypo Crate"
/datum/supply_pack/misc/mre_rations
@@ -169,7 +169,7 @@
/obj/item/weapon/storage/mre/menu9,
/obj/item/weapon/storage/mre/menu10)
cost = 50
- containertype = /obj/structure/closet/crate/freezer
+ containertype = /obj/structure/closet/crate/centauri
containername = "ready to eat rations"
/datum/supply_pack/misc/paste_rations
@@ -178,7 +178,7 @@
/obj/item/weapon/storage/mre/menu11 = 2
)
cost = 25
- containertype = /obj/structure/closet/crate/freezer
+ containertype = /obj/structure/closet/crate/freezer/centauri
containername = "emergency rations"
/datum/supply_pack/misc/medical_rations
@@ -187,5 +187,5 @@
/obj/item/weapon/storage/mre/menu13 = 2
)
cost = 40
- containertype = /obj/structure/closet/crate/freezer
+ containertype = /obj/structure/closet/crate/zenghu
containername = "emergency rations"
diff --git a/code/datums/supplypacks/recreation.dm b/code/datums/supplypacks/recreation.dm
index 134ae76e303..9bb6b1a4429 100644
--- a/code/datums/supplypacks/recreation.dm
+++ b/code/datums/supplypacks/recreation.dm
@@ -20,7 +20,7 @@
/obj/item/weapon/material/twohanded/fireaxe/foam = 2
)
cost = 50
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/allico
containername = "foam weapon crate"
/datum/supply_pack/recreation/lasertag
@@ -31,8 +31,8 @@
/obj/item/weapon/gun/energy/lasertag/blue,
/obj/item/clothing/suit/bluetag
)
- containertype = /obj/structure/closet
- containername = "Lasertag Closet"
+ containertype = /obj/structure/closet/crate/ward
+ containername = "Lasertag Supplies"
cost = 10
/datum/supply_pack/recreation/artscrafts
@@ -55,14 +55,14 @@
/obj/item/weapon/wrapping_paper = 3
)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/allico
containername = "Arts and Crafts crate"
/datum/supply_pack/recreation/painters
name = "Station Painting Supplies"
cost = 10
containername = "station painting supplies crate"
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/grayson
contains = list(
/obj/item/device/pipe_painter = 2,
/obj/item/device/floor_painter = 2,
@@ -82,7 +82,7 @@
name = "Deluxe Fishing Bait"
cost = 40
containername = "deluxe bait crate"
- containertype = /obj/structure/closet/crate/freezer
+ containertype = /obj/structure/closet/crate/carp
num_contained = 8
contains = list(
/obj/item/weapon/storage/box/wormcan,
@@ -93,7 +93,7 @@
name = "Laser Tag Turrets"
cost = 40
containername = "laser tag turret crate"
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/ward
contains = list(
/obj/machinery/porta_turret/lasertag/blue,
/obj/machinery/porta_turret/lasertag/red
diff --git a/code/datums/supplypacks/robotics.dm b/code/datums/supplypacks/robotics.dm
index 757d38b9535..5f8cf6337c8 100644
--- a/code/datums/supplypacks/robotics.dm
+++ b/code/datums/supplypacks/robotics.dm
@@ -20,7 +20,7 @@
/obj/item/weapon/cell/high = 2
)
cost = 10
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/nanotrasen
containername = "Robotics assembly"
access = access_robotics
@@ -56,7 +56,7 @@
name = "Morpheus robolimb blueprints"
contains = list(/obj/item/weapon/disk/limb/morpheus)
cost = 20
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/morpheus
containername = "Robolimb blueprints (Morpheus)"
access = access_robotics
@@ -64,7 +64,7 @@
name = "Cyber Solutions robolimb blueprints"
contains = list(/obj/item/weapon/disk/limb/cybersolutions)
cost = 20
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/cybersolutions
containername = "Robolimb blueprints (Cyber Solutions)"
access = access_robotics
@@ -72,7 +72,7 @@
name = "Xion robolimb blueprints"
contains = list(/obj/item/weapon/disk/limb/xion)
cost = 20
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/xion
containername = "Robolimb blueprints (Xion)"
access = access_robotics
@@ -80,7 +80,7 @@
name = "Grayson robolimb blueprints"
contains = list(/obj/item/weapon/disk/limb/grayson)
cost = 30
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/grayson
containername = "Robolimb blueprints (Grayson)"
access = access_robotics
@@ -88,7 +88,7 @@
name = "Hephaestus robolimb blueprints"
contains = list(/obj/item/weapon/disk/limb/hephaestus)
cost = 35
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Robolimb blueprints (Hephaestus)"
access = access_robotics
@@ -96,7 +96,7 @@
name = "Ward-Takahashi robolimb blueprints"
contains = list(/obj/item/weapon/disk/limb/wardtakahashi)
cost = 35
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/ward
containername = "Robolimb blueprints (Ward-Takahashi)"
access = access_robotics
@@ -104,7 +104,7 @@
name = "Zeng Hu robolimb blueprints"
contains = list(/obj/item/weapon/disk/limb/zenghu)
cost = 35
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/zenghu
containername = "Robolimb blueprints (Zeng Hu)"
access = access_robotics
@@ -112,7 +112,7 @@
name = "Bishop robolimb blueprints"
contains = list(/obj/item/weapon/disk/limb/bishop)
cost = 70
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/bishop
containername = "Robolimb blueprints (Bishop)"
access = access_robotics
@@ -133,7 +133,7 @@
/obj/item/weapon/circuitboard/mecha/ripley/peripherals
)
cost = 25
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/xion
containername = "APLU \"Ripley\" Circuit Crate"
access = access_robotics
@@ -144,7 +144,7 @@
/obj/item/weapon/circuitboard/mecha/odysseus/main
)
cost = 25
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/veymed
containername = "\"Odysseus\" Circuit Crate"
access = access_robotics
@@ -158,7 +158,7 @@
)
name = "Random APLU modkit"
cost = 200
- containertype = /obj/structure/closet/crate/science
+ containertype = /obj/structure/closet/crate/xion
containername = "heavy crate"
/datum/supply_pack/randomised/robotics/exosuit_mod/durand
@@ -168,6 +168,7 @@
/obj/item/device/kit/paint/durand/phazon
)
name = "Random Durand exosuit modkit"
+ containertype = /obj/structure/closet/crate/heph
/datum/supply_pack/randomised/robotics/exosuit_mod/gygax
contains = list(
@@ -176,6 +177,7 @@
/obj/item/device/kit/paint/gygax/recitence
)
name = "Random Gygax exosuit modkit"
+ containertype = /obj/structure/closet/crate/heph
/datum/supply_pack/robotics/jumper_cables
name = "Jumper kit crate"
@@ -183,7 +185,7 @@
/obj/item/device/defib_kit/jumper_kit = 2
)
cost = 30
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/einstein
containername = "Jumper kit crate"
access = access_robotics
diff --git a/code/datums/supplypacks/science.dm b/code/datums/supplypacks/science.dm
index ab55b8571e8..c4c03ffd047 100644
--- a/code/datums/supplypacks/science.dm
+++ b/code/datums/supplypacks/science.dm
@@ -9,7 +9,7 @@
name = "Coolant tank crate"
contains = list(/obj/structure/reagent_dispensers/coolanttank)
cost = 15
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/aether
containername = "coolant tank crate"
/datum/supply_pack/sci/phoron
@@ -39,7 +39,7 @@
/obj/item/seeds/kudzuseed
)
cost = 15
- containertype = /obj/structure/closet/crate/hydroponics
+ containertype = /obj/structure/closet/crate/carp
containername = "Exotic Seeds crate"
access = access_hydroponics
@@ -47,14 +47,14 @@
name = "Integrated circuit printer"
contains = list(/obj/item/device/integrated_circuit_printer = 2)
cost = 15
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/ward
containername = "Integrated circuit crate"
/datum/supply_pack/sci/integrated_circuit_printer_upgrade
name = "Integrated circuit printer upgrade - advanced designs"
contains = list(/obj/item/weapon/disk/integrated_circuit/upgrade/advanced)
cost = 30
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/ward
containername = "Integrated circuit crate"
/datum/supply_pack/sci/xenoarch
@@ -75,6 +75,6 @@
/obj/item/weapon/storage/bag/fossils,
/obj/item/weapon/hand_labeler)
cost = 100
- containertype = /obj/structure/closet/crate/secure/science
+ containertype = /obj/structure/closet/crate/secure/xion
containername = "Xenoarchaeology Tech crate"
access = access_research
\ No newline at end of file
diff --git a/code/datums/supplypacks/security.dm b/code/datums/supplypacks/security.dm
index cd523475834..5fd24a473fe 100644
--- a/code/datums/supplypacks/security.dm
+++ b/code/datums/supplypacks/security.dm
@@ -53,11 +53,11 @@
/obj/item/clothing/accessory/storage/pouches/blue,
)
cost = 30
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Plate Carrier crate"
/datum/supply_pack/security/carriersgreen
- name = "Armor - Blue modular armor"
+ name = "Armor - Green modular armor"
contains = list(
/obj/item/clothing/suit/armor/pcarrier/green,
/obj/item/clothing/accessory/armor/armguards/green,
@@ -65,7 +65,7 @@
/obj/item/clothing/accessory/storage/pouches/green,
)
cost = 30
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Plate Carrier crate"
/datum/supply_pack/security/carriersnavy
@@ -77,7 +77,7 @@
/obj/item/clothing/accessory/storage/pouches/navy,
)
cost = 30
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Plate Carrier crate"
/datum/supply_pack/security/carrierstan
@@ -89,7 +89,7 @@
/obj/item/clothing/accessory/storage/pouches/tan,
)
cost = 30
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Plate Carrier crate"
/datum/supply_pack/security/armorplate
@@ -98,7 +98,7 @@
/obj/item/clothing/accessory/armor/armorplate,
)
cost = 5
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/lawson
containername = "Armor plate crate"
/datum/supply_pack/security/armorplatestab
@@ -107,7 +107,7 @@
/obj/item/clothing/accessory/armor/armorplate/stab,
)
cost = 10
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/lawson
containername = "Armor plate crate"
/datum/supply_pack/security/armorplatemedium
@@ -116,7 +116,7 @@
/obj/item/clothing/accessory/armor/armorplate/medium,
)
cost = 10
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/lawson
containername = "Armor plate crate"
/datum/supply_pack/security/armorplatetac
@@ -125,7 +125,7 @@
/obj/item/clothing/accessory/armor/armorplate/tactical,
)
cost = 15
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Armor plate crate"
/datum/supply_pack/randomised/security/carriers
@@ -140,7 +140,7 @@
/obj/item/clothing/suit/armor/pcarrier/press
)
cost = 10
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/scg
containername = "Plate Carrier crate"
/datum/supply_pack/security/carriertags
@@ -158,7 +158,7 @@
/obj/item/clothing/accessory/armor/tag/abneg
)
cost = 20
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/scg
containername = "Plate Carrier crate"
/datum/supply_pack/security/helmcovers
@@ -174,7 +174,7 @@
/obj/item/clothing/accessory/armor/helmcover/tan
)
cost = 20
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/scg
containername = "Helmet Covers crate"
/datum/supply_pack/randomised/security/armorplates
@@ -193,7 +193,7 @@
/obj/item/clothing/accessory/armor/armorplate/bulletproof
)
cost = 40
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/scg
containername = "Armor plate crate"
/datum/supply_pack/randomised/security/carrierarms
@@ -210,7 +210,7 @@
/obj/item/clothing/accessory/armor/armguards/bulletproof
)
cost = 40
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/scg
containername = "Armor plate crate"
/datum/supply_pack/randomised/security/carrierlegs
@@ -227,7 +227,7 @@
/obj/item/clothing/accessory/armor/legguards/bulletproof
)
cost = 40
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/scg
containername = "Armor plate crate"
/datum/supply_pack/randomised/security/carrierbags
@@ -246,7 +246,7 @@
/obj/item/clothing/accessory/storage/pouches/large/tan
)
cost = 50
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/scg
containername = "Armor plate crate"
/datum/supply_pack/security/riot_gear
@@ -260,7 +260,7 @@
/obj/item/weapon/storage/box/handcuffs
)
cost = 40
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/lawson
containername = "Riot gear crate"
access = access_armory
@@ -273,7 +273,7 @@
/obj/item/clothing/shoes/leg_guard/riot
)
cost = 30
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/lawson
containername = "Riot armor crate"
access = access_armory
@@ -287,7 +287,7 @@
/obj/item/clothing/accessory/armor/legguards/riot
)
cost = 40
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/lawson
containername = "Riot armor crate"
access = access_armory
@@ -300,7 +300,7 @@
/obj/item/clothing/shoes/leg_guard/laserproof
)
cost = 40
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/lawson
containername = "Ablative armor crate"
access = access_armory
@@ -314,7 +314,7 @@
/obj/item/clothing/accessory/armor/legguards/laserproof
)
cost = 50
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/lawson
containername = "Ablative armor crate"
access = access_armory
@@ -327,7 +327,7 @@
/obj/item/clothing/shoes/leg_guard/bulletproof
)
cost = 40
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Ballistic armor crate"
access = access_armory
/* VOREStation Removal - Howabout no ERT armor being orderable?
@@ -342,7 +342,7 @@
/obj/item/clothing/accessory/armor/legguards/bulletproof
)
cost = 50
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Ballistic armor crate"
access = access_armory
@@ -355,13 +355,13 @@
/obj/item/clothing/shoes/leg_guard/combat
)
cost = 40
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/saare
containername = "Combat armor crate"
access = access_armory
/datum/supply_pack/security/tactical
name = "Armor - Tactical"
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/saare
containername = "Tactical armor crate"
cost = 40
access = access_armory
@@ -387,7 +387,7 @@
/datum/supply_pack/security/flexitac
name = "Armor - Tactical Light"
- containertype = /obj/structure/closet/crate/secure/gear
+ containertype = /obj/structure/closet/crate/secure/saare
containername = "Tactical Light armor crate"
cost = 75
access = access_armory
@@ -412,15 +412,14 @@
name = "Misc - Security Barriers"
contains = list(/obj/machinery/deployable/barrier = 4)
cost = 20
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/secure/heph
containername = "Security barrier crate"
- access = null
/datum/supply_pack/security/securityshieldgen
name = "Misc - Wall shield generators"
contains = list(/obj/machinery/shieldwallgen = 4)
cost = 20
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Wall shield generators crate"
access = access_teleporter
@@ -434,7 +433,7 @@
/obj/item/clothing/accessory/holster/hip
)
cost = 15
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/hedberg
containername = "Holster crate"
/datum/supply_pack/security/extragear
@@ -446,7 +445,7 @@
/obj/item/clothing/suit/storage/hooded/wintercoat/security = 3
)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/nanothreads
containername = "Security surplus equipment"
/datum/supply_pack/security/detectivegear
@@ -473,7 +472,7 @@
/obj/item/weapon/storage/bag/detective
)
cost = 20
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanotrasen
containername = "Forensic equipment"
access = access_forensics_lockers
@@ -486,7 +485,7 @@
/obj/item/device/detective_scanner
)
cost = 60
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/ward
containername = "Forensic equipment"
access = access_forensics_lockers
@@ -508,7 +507,7 @@
/obj/item/clothing/gloves/black = 2
)
cost = 10
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanotrasen
containername = "Investigation clothing"
access = access_forensics_lockers
@@ -538,7 +537,7 @@
/obj/item/device/flashlight/maglight
)
cost = 20
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanotrasen
containername = "Officer equipment"
access = access_brig
@@ -567,7 +566,7 @@
/obj/item/device/flashlight/maglight
)
cost = 20
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanotrasen
containername = "Warden equipment"
access = access_armory
@@ -594,7 +593,7 @@
/obj/item/device/flashlight/maglight
)
cost = 50
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanotrasen
containername = "Head of security equipment"
access = access_hos
@@ -613,7 +612,7 @@
/obj/item/weapon/storage/box/holobadge
)
cost = 10
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanotrasen
containername = "Security uniform crate"
/datum/supply_pack/security/navybluesecurityclothing
@@ -634,7 +633,7 @@
/obj/item/weapon/storage/box/holobadge
)
cost = 10
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanotrasen
containername = "Navy blue security uniform crate"
/datum/supply_pack/security/corporatesecurityclothing
@@ -654,7 +653,7 @@
/obj/item/weapon/storage/box/holobadge
)
cost = 10
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanotrasen
containername = "Corporate security uniform crate"
/datum/supply_pack/security/biosuit
@@ -670,7 +669,7 @@
/obj/item/weapon/storage/box/gloves
)
cost = 25
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanotrasen
containername = "Security biohazard gear"
access = access_security
@@ -680,6 +679,6 @@
/obj/item/weapon/contraband/poster/nanotrasen = 6
)
cost = 20
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanotrasen
containername = "Morale Posters"
access = access_maint_tunnels
diff --git a/code/datums/supplypacks/supply.dm b/code/datums/supplypacks/supply.dm
index b58c5960457..0863ec8cbb7 100644
--- a/code/datums/supplypacks/supply.dm
+++ b/code/datums/supplypacks/supply.dm
@@ -18,14 +18,14 @@
/obj/item/weapon/reagent_containers/food/condiment/yeast = 3
)
cost = 10
- containertype = /obj/structure/closet/crate/freezer
+ containertype = /obj/structure/closet/crate/freezer/centauri
containername = "Food crate"
/datum/supply_pack/supply/toner
name = "Toner cartridges"
contains = list(/obj/item/device/toner = 6)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/ummarcar
containername = "Toner cartridges"
/datum/supply_pack/supply/janitor
@@ -48,7 +48,7 @@
/obj/structure/mopbucket
)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/galaksi
containername = "Janitorial supplies"
/datum/supply_pack/supply/shipping
@@ -62,7 +62,7 @@
/obj/item/weapon/tool/wirecutters,
/obj/item/weapon/tape_roll = 2)
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/ummarcar
containername = "Shipping supplies crate"
/datum/supply_pack/supply/bureaucracy
@@ -82,13 +82,13 @@
)
name = "Office supplies"
cost = 15
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/ummarcar
containername = "Office supplies crate"
/datum/supply_pack/supply/spare_pda
name = "Spare PDAs"
cost = 10
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/thinktronic
containername = "Spare PDA crate"
contains = list(/obj/item/device/pda = 3)
@@ -112,7 +112,7 @@
/obj/item/clothing/glasses/meson
)
cost = 10
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/xion
containername = "Shaft miner equipment"
access = access_mining
/* //VOREStation Edit - Pointless on Tether.
@@ -127,12 +127,12 @@
name = "Cargo Train Tug"
contains = list(/obj/vehicle/train/engine)
cost = 35
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/xion
containername = "Cargo Train Tug Crate"
/datum/supply_pack/supply/cargotrailer
name = "Cargo Train Trolley"
contains = list(/obj/vehicle/train/trolley)
cost = 15
- containertype = /obj/structure/largecrate
+ containertype = /obj/structure/closet/crate/large/xion
containername = "Cargo Train Trolley Crate"
diff --git a/code/datums/supplypacks/voidsuits.dm b/code/datums/supplypacks/voidsuits.dm
index 6ab3fa9d858..a867b20255a 100644
--- a/code/datums/supplypacks/voidsuits.dm
+++ b/code/datums/supplypacks/voidsuits.dm
@@ -17,7 +17,7 @@
/obj/item/weapon/tank/oxygen = 2,
)
cost = 40
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/aether
containername = "Atmospheric voidsuit crate"
access = access_atmospherics
@@ -31,7 +31,7 @@
/obj/item/weapon/tank/oxygen = 2,
)
cost = 50
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/aether
containername = "Heavy Duty Atmospheric voidsuit crate"
access = access_atmospherics
@@ -45,7 +45,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 40
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/xion
containername = "Engineering voidsuit crate"
access = access_engine_equip
@@ -59,7 +59,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 40
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/xion
containername = "Engineering Construction voidsuit crate"
access = access_engine_equip
@@ -73,7 +73,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 45
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/xion
containername = "Engineering Hazmat voidsuit crate"
access = access_engine_equip
@@ -87,7 +87,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 50
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/xion
containername = "Reinforced Engineering voidsuit crate"
access = access_engine_equip
@@ -101,7 +101,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 40
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/veymed
containername = "Medical voidsuit crate"
access = access_medical_equip
@@ -115,7 +115,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 40
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/veymed
containername = "Medical EMT voidsuit crate"
access = access_medical_equip
@@ -129,7 +129,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 45
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/nanocare
containername = "Medical Biohazard voidsuit crate"
access = access_medical_equip
@@ -143,7 +143,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 60
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/veymed
containername = "Vey-Med Autoadaptive voidsuit (humanoid) crate"
access = access_medical_equip
@@ -168,7 +168,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 40
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Security voidsuit crate"
/datum/supply_pack/voidsuits/security/crowd
@@ -181,7 +181,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 60
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Security Crowd Control voidsuit crate"
access = access_armory
@@ -195,7 +195,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 60
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/heph
containername = "Security EVA voidsuit crate"
access = access_armory
@@ -208,7 +208,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 40
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/xion
containername = "Mining voidsuit crate"
access = access_mining
@@ -221,7 +221,7 @@
/obj/item/weapon/tank/oxygen = 2
)
cost = 50
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/grayson
containername = "Frontier Mining voidsuit crate"
access = access_mining
@@ -232,6 +232,6 @@
/obj/item/clothing/mask/gas/zaddat = 1
)
cost = 30
- containertype = /obj/structure/closet/crate
+ containertype = /obj/structure/closet/crate/nanotrasen
containername = "Zaddat Shroud crate"
access = null
\ No newline at end of file
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 01f5e687d87..09b226a076b 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -1,6 +1,6 @@
/atom/movable
layer = OBJ_LAYER
- appearance_flags = TILE_BOUND|PIXEL_SCALE
+ appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER
glide_size = 8
var/last_move = null //The direction the atom last moved
var/anchored = 0
@@ -24,7 +24,7 @@
var/datum/riding/riding_datum = null
var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P)
var/movement_type = NONE
-
+
var/cloaked = FALSE //If we're cloaked or not
var/image/cloaked_selfimage //The image we use for our client to let them see where we are
@@ -107,7 +107,7 @@
glide_for(movetime)
loc = newloc
. = TRUE
-
+
// So objects can be informed of z-level changes
if (old_z != dest_z)
onTransitZ(old_z, dest_z)
@@ -133,7 +133,7 @@
var/atom/movable/thing = i
// We don't call parent so we are calling this for byond
thing.Crossed(src)
-
+
// We're a multi-tile object (multiple locs)
else if(. && newloc)
. = doMove(newloc)
@@ -282,7 +282,7 @@
glide_for(movetime)
last_move = isnull(direction) ? 0 : direction
loc = destination
-
+
// Unset this in case it was set in some other proc. We're no longer moving diagonally for sure.
moving_diagonally = 0
@@ -294,27 +294,27 @@
// If it's not the same area, Exited() it
if(old_area && old_area != destarea)
old_area.Exited(src, destination)
-
+
// Uncross everything where we left
for(var/i in oldloc)
var/atom/movable/AM = i
if(AM == src)
continue
AM.Uncrossed(src)
-
+
// Information about turf and z-levels for source and dest collected
var/turf/oldturf = get_turf(oldloc)
var/turf/destturf = get_turf(destination)
var/old_z = (oldturf ? oldturf.z : null)
var/dest_z = (destturf ? destturf.z : null)
-
+
// So objects can be informed of z-level changes
if (old_z != dest_z)
onTransitZ(old_z, dest_z)
-
+
// Destination atom Entered
destination.Entered(src, oldloc)
-
+
// Entered() the new area if it's not the same area
if(destarea && old_area != destarea)
destarea.Entered(src, oldloc)
@@ -366,7 +366,7 @@
glide_size = initial(glide_size)
else
glide_size = initial(glide_size)
-
+
/////////////////////////////////////////////////////////////////
//called when src is thrown into hit_atom
@@ -623,7 +623,7 @@
/atom/movable/proc/cloak_animation(var/length = 1 SECOND)
//Save these
var/initial_alpha = alpha
-
+
//Animate alpha fade
animate(src, alpha = 0, time = length)
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 454025d4259..b57e1f5a0cb 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -232,12 +232,6 @@
else
to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...")
O << browse(text("[][]", itemname, info), text("window=[]", itemname))
- for(var/mob/O in player_list)
- if (istype(O.machine, /obj/machinery/computer/security))
- var/obj/machinery/computer/security/S = O.machine
- if (S.current_camera == src)
- to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...")
- O << browse(text("[][]", itemname, info), text("window=[]", itemname))
else if (istype(W, /obj/item/weapon/camera_bug))
if (!src.can_use())
@@ -494,8 +488,6 @@
else
cameranet.updateVisibility(src, 0)
- invalidateCameraCache()
-
// Resets the camera's wires to fully operational state. Used by one of Malfunction abilities.
/obj/machinery/camera/proc/reset_wires()
if(!wires)
diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm
index ab5d82b3a5f..bb3cdfb4edb 100644
--- a/code/game/machinery/camera/presets.dm
+++ b/code/game/machinery/camera/presets.dm
@@ -173,7 +173,6 @@ var/global/list/engineering_networks = list(
var/number = my_area.len
c_tag = "[A.name] #[number]"
- invalidateCameraCache()
/obj/machinery/camera/autoname/Destroy()
var/area/A = get_area(src)
diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm
index daca64cc42d..c3041344a0f 100644
--- a/code/game/machinery/computer/aifixer.dm
+++ b/code/game/machinery/computer/aifixer.dm
@@ -1,142 +1,132 @@
/obj/machinery/computer/aifixer
name = "\improper AI system integrity restorer"
- desc = "Restores AI units to working condition, assuming you have one inside!"
- icon_keyboard = "rd_key"
- icon_screen = "ai-fixer"
- light_color = "#a97faa"
- circuit = /obj/item/weapon/circuitboard/aifixer
+ desc = "Used with intelliCards containing nonfunctional AIs to restore them to working order."
req_one_access = list(access_robotics, access_heads)
- var/mob/living/silicon/ai/occupant = null
- var/active = 0
+ circuit = /obj/item/weapon/circuitboard/aifixer
+ icon_keyboard = "tech_key"
+ icon_screen = "ai-fixer"
+ light_color = LIGHT_COLOR_PINK
+
+ active_power_usage = 1000
-/obj/machinery/computer/aifixer/New()
- ..()
- update_icon()
-
-/obj/machinery/computer/aifixer/proc/load_ai(var/mob/living/silicon/ai/transfer, var/obj/item/device/aicard/card, var/mob/user)
-
- if(!transfer)
- return
-
- // Transfer over the AI.
- to_chat(transfer, "You have been transferred into a stationary terminal. Sadly, there is no remote access from here.")
- to_chat(user, "Transfer successful: [transfer.name] placed within stationary terminal.")
-
- transfer.loc = src
- transfer.cancel_camera()
- transfer.control_disabled = 1
- occupant = transfer
-
- if(card)
- card.clear()
-
- update_icon()
-
-/obj/machinery/computer/aifixer/attackby(I as obj, user as mob)
+ /// Variable containing transferred AI
+ var/mob/living/silicon/ai/occupier
+ /// Variable dictating if we are in the process of restoring the occupier AI
+ var/restoring = FALSE
+/obj/machinery/computer/aifixer/attackby(obj/item/I, mob/living/user)
+ if(I.is_screwdriver())
+ if(occupier)
+ if(stat & (NOPOWER|BROKEN))
+ to_chat(user, "The screws on [name]'s screen won't budge.")
+ else
+ to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep.")
+ return
if(istype(I, /obj/item/device/aicard))
-
if(stat & (NOPOWER|BROKEN))
- to_chat(user, "This terminal isn't functioning right now.")
+ to_chat(user, "This terminal isn't functioning right now.")
+ return
+ if(restoring)
+ to_chat(user, "Terminal is busy restoring [occupier] right now.")
return
var/obj/item/device/aicard/card = I
- var/mob/living/silicon/ai/comp_ai = locate() in src
- var/mob/living/silicon/ai/card_ai = locate() in card
+ if(occupier)
+ if(card.grab_ai(occupier, user))
+ occupier = null
+ else if(card.carded_ai)
+ var/mob/living/silicon/ai/new_occupant = card.carded_ai
+ to_chat(new_occupant, "You have been transferred into a stationary terminal. Sadly there is no remote access from here.")
+ to_chat(user, "Transfer Successful: [new_occupant] placed within stationary terminal.")
+ new_occupant.forceMove(src)
+ new_occupant.cancel_camera()
+ new_occupant.control_disabled = TRUE
+ occupier = new_occupant
+ card.clear()
+ update_icon()
+ else
+ to_chat(user, "There is no AI loaded onto this computer, and no AI loaded onto [I]. What exactly are you trying to do here?")
+ return ..()
- if(istype(comp_ai))
- if(active)
- to_chat(user, "ERROR: Reconstruction in progress.")
- return
- card.grab_ai(comp_ai, user)
- if(!(locate(/mob/living/silicon/ai) in src)) occupant = null
- else if(istype(card_ai))
- load_ai(card_ai,card,user)
- occupant = locate(/mob/living/silicon/ai) in src
-
- update_icon()
+/obj/machinery/computer/aifixer/attack_hand(mob/user)
+ if(stat & (NOPOWER|BROKEN))
return
- ..()
- return
+ tgui_interact(user)
-/obj/machinery/computer/aifixer/attack_ai(var/mob/user as mob)
- return attack_hand(user)
+/obj/machinery/computer/aifixer/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "AiRestorer", name)
+ ui.open()
-/obj/machinery/computer/aifixer/attack_hand(var/mob/user as mob)
+/obj/machinery/computer/aifixer/tgui_data(mob/user)
+ var/list/data = list()
+
+ 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"] = restoring
+ data["health"] = (occupier.health + 100) / 2
+ data["isDead"] = occupier.stat == DEAD
+ var/list/laws = list()
+ for(var/datum/ai_law/law in occupier.laws.all_laws())
+ laws += "[law.get_index()]: [law.law]"
+ data["laws"] = laws
+
+ return data
+
+/obj/machinery/computer/aifixer/tgui_act(action, params)
if(..())
return
+ if(!occupier)
+ restoring = FALSE
- user.set_machine(src)
- var/dat = "AI System Integrity Restorer
"
+ 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)
+ restoring = TRUE
+ var/mob/observer/dead/ghost = occupier.get_ghost()
+ if(ghost)
+ ghost.notify_revive("Your core files are being restored!", source = src)
+ . = TRUE
- if (src.occupant)
- var/laws
- dat += "Stored AI: [src.occupant.name]
System integrity: [src.occupant.hardware_integrity()]%
Backup Capacitor: [src.occupant.backup_capacitor()]%
"
+/obj/machinery/computer/aifixer/proc/Fix()
+ use_power(active_power_usage)
+ occupier.adjustOxyLoss(-5, 0, FALSE)
+ occupier.adjustFireLoss(-5, 0, FALSE)
+ occupier.adjustBruteLoss(-5, 0)
+ if(occupier.health >= 0 && occupier.stat == DEAD)
+ occupier.revive()
- for (var/datum/ai_law/law in occupant.laws.all_laws())
- laws += "[law.get_index()]: [law.law]
"
-
- dat += "Laws:
[laws]
"
-
- if (src.occupant.stat == 2)
- dat += "AI nonfunctional"
- else
- dat += "AI functional"
- if (!src.active)
- dat += {"
Begin Reconstruction"}
- else
- dat += "
Reconstruction in process, please wait.
"
- dat += {" Close"}
-
- user << browse(dat, "window=computer;size=400x500")
- onclose(user, "computer")
- return
+ return occupier.health < 100
/obj/machinery/computer/aifixer/process()
if(..())
- src.updateDialog()
- return
-
-/obj/machinery/computer/aifixer/Topic(href, href_list)
- if(..())
- return 1
- if (href_list["fix"])
- src.active = 1
- src.overlays += image(icon, "ai-fixer-on")
- while (src.occupant.getOxyLoss() > 0 || src.occupant.getFireLoss() > 0 || src.occupant.getToxLoss() > 0 || src.occupant.getBruteLoss() > 0)
- src.occupant.adjustOxyLoss(-1)
- src.occupant.adjustFireLoss(-1)
- src.occupant.adjustToxLoss(-1)
- src.occupant.adjustBruteLoss(-1)
- src.occupant.updatehealth()
- if (src.occupant.health >= 0 && src.occupant.stat == DEAD)
- src.occupant.set_stat(CONSCIOUS)
- src.occupant.lying = 0
- dead_mob_list -= src.occupant
- living_mob_list += src.occupant
- src.overlays -= image(icon, "ai-fixer-404")
- src.overlays += image(icon, "ai-fixer-full")
- src.occupant.add_ai_verbs()
- src.updateUsrDialog()
- sleep(10)
- src.active = 0
- src.overlays -= image(icon, "ai-fixer-on")
-
-
- src.add_fingerprint(usr)
- src.updateUsrDialog()
- return
-
+ if(restoring)
+ var/oldstat = occupier.stat
+ restoring = Fix()
+ if(oldstat != occupier.stat)
+ update_icon()
/obj/machinery/computer/aifixer/update_icon()
- ..()
- if((stat & BROKEN) || (stat & NOPOWER))
+ . = ..()
+ if(stat & (NOPOWER|BROKEN))
return
- if(occupant)
- if(occupant.stat)
- add_overlay("ai-fixer-404")
- else
- add_overlay("ai-fixer-full")
+ if(restoring)
+ . += "ai-fixer-on"
+ if (occupier)
+ switch (occupier.stat)
+ if (CONSCIOUS)
+ . += "ai-fixer-full"
+ if (UNCONSCIOUS)
+ . += "ai-fixer-404"
else
- add_overlay("ai-fixer-empty")
+ . += "ai-fixer-empty"
\ No newline at end of file
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index e695173efd4..f87d69a9a7b 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -3,201 +3,44 @@
/obj/machinery/computer/security
name = "security camera monitor"
desc = "Used to access the various cameras on the station."
+
icon_keyboard = "security_key"
icon_screen = "cameras"
light_color = "#a91515"
- var/current_network = null
- var/obj/machinery/camera/current_camera = null
- var/last_pic = 1.0
- var/list/network
- var/mapping = 0//For the overview file, interesting bit of code.
- var/cache_id = 0
circuit = /obj/item/weapon/circuitboard/security
-/obj/machinery/computer/security/New()
- if(!network)
+ var/mapping = 0//For the overview file, interesting bit of code.
+ var/list/network = list()
+
+ var/datum/tgui_module/camera/camera
+
+/obj/machinery/computer/security/Initialize()
+ . = ..()
+ if(!LAZYLEN(network))
network = using_map.station_networks.Copy()
- ..()
- if(network.len)
- current_network = network[1]
+ camera = new(src, network)
-/obj/machinery/computer/security/attack_ai(var/mob/user as mob)
- return attack_hand(user)
+/obj/machinery/computer/security/Destroy()
+ QDEL_NULL(camera)
+ return ..()
-/obj/machinery/computer/security/check_eye(var/mob/user as mob)
- if (user.stat || ((get_dist(user, src) > 1 || !( user.canmove ) || user.blinded) && !istype(user, /mob/living/silicon))) //user can't see - not sure why canmove is here.
- return -1
- if(!current_camera)
- return 0
- var/viewflag = current_camera.check_eye(user)
- if ( viewflag < 0 ) //camera doesn't work
- reset_current()
- return viewflag
+/obj/machinery/computer/security/tgui_interact(mob/user, datum/tgui/ui = null)
+ camera.tgui_interact(user, ui)
-/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
- if(stat & (NOPOWER|BROKEN)) return
- if(user.stat) return
-
- var/data[0]
-
- data["current_camera"] = current_camera ? current_camera.nano_structure() : null
- data["current_network"] = current_network
- data["networks"] = network ? network : list()
-
- var/map_levels = using_map.get_map_levels(src.z, TRUE, om_range = DEFAULT_OVERMAP_RANGE)
- data["map_levels"] = map_levels
-
- if(current_network)
- data["cameras"] = camera_repository.cameras_in_network(current_network, map_levels)
- if(current_camera)
- switch_to_camera(user, current_camera)
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800)
-
- // adding a template with the key "mapContent" enables the map ui functionality
- ui.add_template("mapContent", "sec_camera_map_content.tmpl")
- // adding a template with the key "mapHeader" replaces the map header content
- ui.add_template("mapHeader", "sec_camera_map_header.tmpl")
-
- ui.set_initial_data(data)
- ui.open()
-
-/obj/machinery/computer/security/Topic(href, href_list)
- if(..())
- return 1
- if(href_list["switch_camera"])
- if(stat&(NOPOWER|BROKEN)) return //VOREStation Edit - Removed zlevel check
- if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return
- var/obj/machinery/camera/C = locate(href_list["switch_camera"]) in cameranet.cameras
- if(!C)
- return
- if(!(current_network in C.network))
- return
-
- switch_to_camera(usr, C)
- return 1
- else if(href_list["switch_network"])
- if(stat&(NOPOWER|BROKEN)) return //VOREStation Edit - Removed zlevel check
- if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return
- if(href_list["switch_network"] in network)
- current_network = href_list["switch_network"]
- return 1
- else if(href_list["reset"])
- if(stat&(NOPOWER|BROKEN)) return //VOREStation Edit - Removed zlevel check
- if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return
- reset_current()
- usr.reset_view(current_camera)
- return 1
- else
- . = ..()
-
-/obj/machinery/computer/security/attack_hand(var/mob/user as mob)
- if(stat & (NOPOWER|BROKEN)) return
-
- if(!isAI(user))
- user.set_machine(src)
- ui_interact(user)
-
-/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C)
- //don't need to check if the camera works for AI because the AI jumps to the camera location and doesn't actually look through cameras.
- if(isAI(user))
- var/mob/living/silicon/ai/A = user
- // Only allow non-carded AIs to view because the interaction with the eye gets all wonky otherwise.
- if(!A.is_in_chassis())
- return 0
-
- A.eyeobj.setLoc(get_turf(C))
- A.client.eye = A.eyeobj
- return 1
-
- if (!C.can_use() || user.stat || (get_dist(user, src) > 1 || user.machine != src || user.blinded || !( user.canmove ) && !istype(user, /mob/living/silicon)))
- return 0
- set_current(C)
- user.reset_view(current_camera)
- check_eye(user)
- return 1
-
-/obj/machinery/computer/security/relaymove(mob/user,direct)
- var/turf/T = get_turf(current_camera)
- for(var/i; i < 10; i++)
- T = get_step(T, direct)
- jump_on_click(user, T)
-
-//Camera control: moving.
-/obj/machinery/computer/security/proc/jump_on_click(var/mob/user,var/A)
- if(user.machine != src)
+/obj/machinery/computer/security/attack_hand(mob/user)
+ add_fingerprint(user)
+ if(stat & (BROKEN|NOPOWER))
return
- var/obj/machinery/camera/jump_to
- if(istype(A,/obj/machinery/camera))
- jump_to = A
- else if(ismob(A))
- if(ishuman(A))
- jump_to = locate() in A:head
- else if(isrobot(A))
- jump_to = A:camera
- else if(isobj(A))
- jump_to = locate() in A
- else if(isturf(A))
- var/best_dist = INFINITY
- for(var/obj/machinery/camera/camera in get_area(A))
- if(!camera.can_use())
- continue
- if(!can_access_camera(camera))
- continue
- var/dist = get_dist(camera,A)
- if(dist < best_dist)
- best_dist = dist
- jump_to = camera
- if(isnull(jump_to))
- return
- if(can_access_camera(jump_to))
- switch_to_camera(user,jump_to)
+ tgui_interact(user)
-/obj/machinery/computer/security/process()
- if(cache_id != camera_repository.camera_cache_id)
- cache_id = camera_repository.camera_cache_id
- SSnanoui.update_uis(src)
+/obj/machinery/computer/security/attack_ai(mob/user)
+ to_chat(user, "You realise its kind of stupid to access a camera console when you have the entire camera network at your metaphorical fingertips")
+ return
-/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C)
- var/list/shared_networks = src.network & C.network
- if(shared_networks.len)
- return 1
- return 0
-
-/obj/machinery/computer/security/proc/set_current(var/obj/machinery/camera/C)
- if(current_camera == C)
- return
-
- if(current_camera)
- reset_current()
-
- src.current_camera = C
- if(current_camera)
- current_camera.camera_computers_using_this.Add(src)
- update_use_power(USE_POWER_ACTIVE)
- var/mob/living/L = current_camera.loc
- if(istype(L))
- L.tracking_initiated()
-
-/obj/machinery/computer/security/proc/reset_current()
- if(current_camera)
- current_camera.camera_computers_using_this.Remove(src)
- var/mob/living/L = current_camera.loc
- if(istype(L))
- L.tracking_cancelled()
- current_camera = null
- update_use_power(USE_POWER_IDLE)
-
-//Camera control: mouse.
-/* Oh my god
-/atom/DblClick()
- ..()
- if(istype(usr.machine,/obj/machinery/computer/security))
- var/obj/machinery/computer/security/console = usr.machine
- console.jump_on_click(usr,src)
-*/
+/obj/machinery/computer/security/proc/set_network(list/new_network)
+ network = new_network
+ camera.network = network
+ camera.access_based = FALSE
//Camera control: arrow keys.
/obj/machinery/computer/security/telescreen
diff --git a/code/game/machinery/computer/camera_circuit.dm b/code/game/machinery/computer/camera_circuit.dm
deleted file mode 100644
index 2bbe82ee1b8..00000000000
--- a/code/game/machinery/computer/camera_circuit.dm
+++ /dev/null
@@ -1,116 +0,0 @@
-
-//the researchable camera circuit that can connect to any camera network
-
-/obj/item/weapon/circuitboard/camera
- //name = "Circuit board (Camera)"
- var/secured = 1
- var/authorised = 0
- var/possibleNets[0]
- var/network = ""
- build_path = null
-
-//when adding a new camera network, you should only need to update these two procs
- New()
- possibleNets["Engineering"] = access_ce
- possibleNets["SS13"] = access_hos
- possibleNets["Mining"] = access_mining
- possibleNets["Cargo"] = access_qm
- possibleNets["Research"] = access_rd
- possibleNets["Medbay"] = access_cmo
- ..()
-
- proc/updateBuildPath()
- build_path = null
- if(authorised && secured)
- switch(network)
- if("SS13")
- build_path = /obj/machinery/computer/security
- if("Engineering")
- build_path = /obj/machinery/computer/security/engineering
- if("Mining")
- build_path = /obj/machinery/computer/security/mining
- if("Research")
- build_path = /obj/machinery/computer/security/research
- if("Medbay")
- build_path = /obj/machinery/computer/security/medbay
- if("Cargo")
- build_path = /obj/machinery/computer/security/cargo
-
- attackby(var/obj/item/I, var/mob/user)//if(health > 50)
- ..()
- else if(I.is_screwdriver())
- secured = !secured
- user.visible_message("The [src] can [secured ? "no longer" : "now"] be modified.")
- playsound(src, I.usesound, 50, 1)
- updateBuildPath()
- return
-
- attack_self(var/mob/user)
- if(!secured && ishuman(user))
- user.machine = src
- interact(user, 0)
-
- proc/interact(var/mob/user, var/ai=0)
- if(secured)
- return
- if (!ishuman(user))
- return ..(user)
- var/t = "Circuitboard Console - Camera Monitoring Computer
"
- t += "Close
"
- t += "
Please select a camera network:
"
-
- for(var/curNet in possibleNets)
- if(network == curNet)
- t += "- [curNet]
"
- else
- t += "- [curNet]
"
- t += "
"
- if(network)
- if(authorised)
- t += "Authenticated (Clear Auth)
"
- else
- t += "*Authenticate* (Requires an appropriate access ID)
"
- else
- t += "*Authenticate* (Requires an appropriate access ID)
"
- t += "Close
"
- user << browse(t, "window=camcircuit;size=500x400")
- onclose(user, "camcircuit")
-
- Topic(href, href_list)
- ..()
- if( href_list["close"] )
- usr << browse(null, "window=camcircuit")
- usr.machine = null
- return
- else if(href_list["net"])
- network = href_list["net"]
- authorised = 0
- else if( href_list["auth"] )
- var/mob/M = usr
- var/obj/item/weapon/card/id/I = M.equipped()
- if (istype(I, /obj/item/device/pda))
- var/obj/item/device/pda/pda = I
- I = pda.id
- if (I && istype(I))
- if(access_captain in I.access)
- authorised = 1
- else if (possibleNets[network] in I.access)
- authorised = 1
- if(istype(I,/obj/item/weapon/card/emag))
- I.resolve_attackby(src, usr)
- else if( href_list["removeauth"] )
- authorised = 0
- updateDialog()
-
- updateDialog()
- if(istype(src.loc,/mob))
- attack_self(src.loc)
-
-/obj/item/weapon/circuitboard/camera/emag_act(var/remaining_charges, var/mob/user)
- if(network)
- authorised = 1
- to_chat(user, "You authorised the circuit network!")
- updateDialog()
- return 1
- else
- to_chat(user, "You must select a camera network circuit!")
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index 077c12c451f..1af31afe80d 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -8,7 +8,7 @@
idle_power_usage = 250
active_power_usage = 500
circuit = /obj/item/weapon/circuitboard/crew
- var/datum/nano_module/program/crew_monitor/crew_monitor
+ var/datum/tgui_module/crew_monitor/crew_monitor
/obj/machinery/computer/crew/New()
crew_monitor = new(src)
@@ -20,16 +20,16 @@
..()
/obj/machinery/computer/crew/attack_ai(mob/user)
- ui_interact(user)
+ attack_hand(user)
/obj/machinery/computer/crew/attack_hand(mob/user)
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- crew_monitor.ui_interact(user, ui_key, ui, force_open)
+/obj/machinery/computer/crew/tgui_interact(mob/user, datum/tgui/ui = null)
+ crew_monitor.tgui_interact(user, ui)
/obj/machinery/computer/crew/interact(mob/user)
- crew_monitor.ui_interact(user)
+ crew_monitor.tgui_interact(user)
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 631a998a42f..a133aab9b1a 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -522,7 +522,7 @@
announce.autosay("[to_despawn.real_name], [to_despawn.mind.role_alt_title], [on_store_message]", "[on_store_name]", announce_channel, using_map.get_map_levels(z, TRUE, om_range = DEFAULT_OVERMAP_RANGE))
//visible_message("\The [initial(name)] hums and hisses as it moves [to_despawn.real_name] into storage.", 3)
- visible_message("\The [initial(name)] [on_store_visible_message_1] [to_despawn.real_name] [on_store_visible_message_2].", 3)
+ visible_message("\The [initial(name)] [on_store_visible_message_1] [to_despawn.real_name] [on_store_visible_message_2]", 3)
//VOREStation Edit begin: Dont delete mobs-in-mobs
if(to_despawn.client && to_despawn.stat<2)
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 2f96fb75cc7..57a17310984 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -164,6 +164,8 @@
var/datum/action/innate/mecha/mech_switch_damtype/switch_damtype_action = new
var/datum/action/innate/mecha/mech_toggle_phasing/phasing_action = new
+ var/weapons_only_cycle = FALSE //So combat mechs don't switch to their equipment at times.
+
/obj/mecha/drain_power(var/drain_check)
diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm
index 0d2034d790e..33307c77d3c 100644
--- a/code/game/mecha/mecha_actions.dm
+++ b/code/game/mecha/mecha_actions.dm
@@ -185,12 +185,15 @@
var/list/available_equipment = list()
available_equipment = chassis.equipment
+ if(chassis.weapons_only_cycle)
+ available_equipment = chassis.weapon_equipment
+
if(available_equipment.len == 0)
chassis.occupant_message("No equipment available.")
return
if(!chassis.selected)
chassis.selected = available_equipment[1]
- chassis.occupant_message("You select [chassis.selected]")
+ chassis.occupant_message("You select [chassis.selected]")
send_byjax(chassis.occupant,"exosuit.browser","eq_list",chassis.get_equipment_list())
button_icon_state = "mech_cycle_equip_on"
button.UpdateIcon()
@@ -419,3 +422,17 @@
return
+/obj/mecha/verb/toggle_weapons_only_cycle()
+ set category = "Exosuit Interface"
+ set name = "Toggle weapons only cycling"
+ set src = usr.loc
+ set popup_menu = 0
+ set_weapons_only_cycle()
+
+/obj/mecha/proc/set_weapons_only_cycle()
+ if(usr!=src.occupant)
+ return
+ weapons_only_cycle = !weapons_only_cycle
+ src.occupant_message("En":"#f00\">Dis"]abled weapons only cycling.")
+ return
+
diff --git a/code/game/objects/items/robot/robot_upgrades_vr.dm b/code/game/objects/items/robot/robot_upgrades_vr.dm
index 13f09a980d6..2bc402b355e 100644
--- a/code/game/objects/items/robot/robot_upgrades_vr.dm
+++ b/code/game/objects/items/robot/robot_upgrades_vr.dm
@@ -6,7 +6,8 @@
R.add_language(LANGUAGE_ECUREUILIAN, 1)
R.add_language(LANGUAGE_DAEMON, 1)
R.add_language(LANGUAGE_ENOCHIAN, 1)
- R.add_language(LANGUAGE_SLAVIC, 1)
+ R.add_language(LANGUAGE_SLAVIC, 1)
+ R.add_language(LANGUAGE_DRUDAKAR, 1)
return 1
else
return 0
diff --git a/code/game/objects/items/weapons/RPD_vr.dm b/code/game/objects/items/weapons/RPD_vr.dm
index b40f28701fb..9ae806f63c6 100644
--- a/code/game/objects/items/weapons/RPD_vr.dm
+++ b/code/game/objects/items/weapons/RPD_vr.dm
@@ -33,7 +33,7 @@
var/datum/pipe_recipe/recipe // pipe recipie selected for display/construction
var/static/datum/pipe_recipe/first_atmos
var/static/datum/pipe_recipe/first_disposal
- var/static/datum/asset/iconsheet/pipes/icon_assets
+ var/static/datum/asset/spritesheet/pipes/icon_assets
var/static/list/pipe_layers = list(
"Regular" = PIPING_LAYER_REGULAR,
"Supply" = PIPING_LAYER_SUPPLY,
@@ -75,7 +75,7 @@
/obj/item/weapon/pipe_dispenser/interact(mob/user)
SetupPipes()
if(!icon_assets)
- icon_assets = get_asset_datum(/datum/asset/iconsheet/pipes)
+ icon_assets = get_asset_datum(/datum/asset/spritesheet/pipes)
icon_assets.send(user)
var/list/lines = list()
@@ -365,7 +365,7 @@
if(_dir == p_dir && flipped == p_flipped)
attrs += " class=\"linkOn\""
if(icon_state)
- var/img_tag = icon_assets.icon_tag(icon_state, _dir)
+ var/img_tag = icon_assets.icon_tag("[dirtext]-[icon_state]")
return "[img_tag]"
else
return "[noimg]"
diff --git a/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm b/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm
index 097f465a512..0648b586277 100644
--- a/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm
+++ b/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm
@@ -12,7 +12,6 @@
/obj/item/weapon/circuitboard/security/New()
..()
- network = using_map.station_networks
/obj/item/weapon/circuitboard/security/tv
name = T_BOARD("security camera monitor - television")
@@ -45,7 +44,7 @@
/obj/item/weapon/circuitboard/security/construct(var/obj/machinery/computer/security/C)
if (..(C))
- C.network = network.Copy()
+ C.set_network(network.Copy())
/obj/item/weapon/circuitboard/security/deconstruct(var/obj/machinery/computer/security/C)
if (..(C))
diff --git a/code/game/objects/random/mapping.dm b/code/game/objects/random/mapping.dm
index bc6d29a5a5e..230b4ff3d29 100644
--- a/code/game/objects/random/mapping.dm
+++ b/code/game/objects/random/mapping.dm
@@ -34,6 +34,36 @@
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/remains/robot)
+/obj/random/crate //Random 'standard' crates for variety in maintenance spawns.
+ name = "random crate"
+ desc = "This is a random crate"
+ icon = 'icons/obj/closets/bases/crate.dmi'
+ icon_state = "base"
+
+/obj/random/crate/item_to_spawn() //General crates, excludes some more high-grade and medical brands
+ return pick (/obj/structure/closet/crate/plastic,
+ /obj/structure/closet/crate/aether,
+ /obj/structure/closet/crate/centauri,
+ /obj/structure/closet/crate/einstein,
+ /obj/structure/closet/crate/focalpoint,
+ /obj/structure/closet/crate/gilthari,
+ /obj/structure/closet/crate/grayson,
+ /obj/structure/closet/crate/nanotrasen,
+ /obj/structure/closet/crate/nanothreads,
+ /obj/structure/closet/crate/oculum,
+ /obj/structure/closet/crate/ward,
+ /obj/structure/closet/crate/xion,
+ /obj/structure/closet/crate/zenghu,
+ /obj/structure/closet/crate/allico,
+ /obj/structure/closet/crate/carp,
+ /obj/structure/closet/crate/galaksi,
+ /obj/structure/closet/crate/thinktronic,
+ /obj/structure/closet/crate/ummarcar,
+ /obj/structure/closet/crate/unathi,
+ /obj/structure/closet/crate/hydroponics,
+ /obj/structure/closet/crate/engineering,
+ /obj/structure/closet/crate)
+
/obj/random/obstruction //Large objects to block things off in maintenance
name = "random obstruction"
desc = "This is a random obstruction."
diff --git a/code/game/objects/structures/crates_lockers/_closets_appearance_definitions.dm b/code/game/objects/structures/crates_lockers/_closets_appearance_definitions.dm
index c158ea0b832..b90a7961e2a 100644
--- a/code/game/objects/structures/crates_lockers/_closets_appearance_definitions.dm
+++ b/code/game/objects/structures/crates_lockers/_closets_appearance_definitions.dm
@@ -761,8 +761,210 @@
"lid_stripes" = COLOR_NT_RED
)
+// Freezers
+
/decl/closet_appearance/crate/freezer
+ color = COLOR_OFF_WHITE
+
+/decl/closet_appearance/crate/freezer/centauri
color = COLOR_BABY_BLUE
+ extra_decals = list(
+ "centauri" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/freezer/nanotrasen
+ color = COLOR_BABY_BLUE
+ extra_decals = list(
+ "nano" = COLOR_OFF_WHITE
+ )
+
+// Corporate Branding
+
+/decl/closet_appearance/crate/aether
+ color = COLOR_YELLOW_GRAY
+ decals = list(
+ "crate_stripes" = COLOR_BLUE_LIGHT,
+ "aether" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/allico
+ color = COLOR_LIGHT_VIOLET
+ decals = list(
+ "crate_stripe" = COLOR_AMBER
+ )
+
+/decl/closet_appearance/crate/carp
+ color = COLOR_PURPLE
+ decals = list(
+ "toptext" = COLOR_OFF_WHITE,
+ "crate_reticle" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/centauri
+ color = COLOR_BABY_BLUE
+ decals = list(
+ "crate_stripe" = COLOR_LUMINOL
+ )
+ extra_decals = list(
+ "centauri" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/cybersolutions
+ color = COLOR_ALUMINIUM
+ extra_decals = list(
+ "hazard" = COLOR_DARK_GOLD,
+ "toptext" = COLOR_DARK_GOLD
+ )
+
+/decl/closet_appearance/crate/einstein
+ color = COLOR_DARK_BLUE_GRAY
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_BEIGE,
+ "crate_stripe_right" = COLOR_BEIGE,
+ "einstein" = COLOR_OFF_WHITE,
+ "hazard" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/focalpoint
+ color = COLOR_GOLD
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_NAVY_BLUE,
+ "crate_stripe_right" = COLOR_NAVY_BLUE,
+ "focal" = COLOR_OFF_WHITE,
+ "hazard" = COLOR_NAVY_BLUE
+ )
+
+/decl/closet_appearance/crate/galaksi
+ color = COLOR_OFF_WHITE
+ decals = list(
+ "lid_stripes" = COLOR_HULL,
+ "galaksi" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/gilthari
+ color = COLOR_GRAY20
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_GOLD,
+ "crate_stripe_right" = COLOR_GOLD,
+ "gilthari" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/grayson
+ color = COLOR_STEEL
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_MAROON,
+ "crate_stripe_right" = COLOR_MAROON,
+ "grayson" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/heph
+ color = COLOR_GRAY20
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_NT_RED,
+ "crate_stripe_right" = COLOR_NT_RED,
+ "heph" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/morpheus
+ color = COLOR_ALUMINIUM
+ extra_decals = list(
+ "hazard" = COLOR_GUNMETAL,
+ "toptext" = COLOR_GUNMETAL
+ )
+
+/decl/closet_appearance/crate/nanotrasen
+ color = COLOR_NT_RED
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_OFF_WHITE,
+ "crate_stripe_right" = COLOR_OFF_WHITE,
+ "nano" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/nanotrasenclothing
+ color = COLOR_NT_RED
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_SEDONA,
+ "crate_stripe_right" = COLOR_SEDONA,
+ "nano" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/nanotrasenmedical
+ color = COLOR_OFF_WHITE
+ extra_decals = list(
+ "crate_stripe" = COLOR_NT_RED,
+ "nano" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/oculum
+ color = COLOR_SURGERY_BLUE
+ decals = list(
+ "crate_stripe_left" = COLOR_OFF_WHITE,
+ "crate_stripe_right" = COLOR_OFF_WHITE,
+ "oculum" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/secure/saare
+ color = COLOR_ALUMINIUM
+ extra_decals = list(
+ "hazard" = COLOR_RED,
+ "xion" = COLOR_GRAY40
+ )
+
+/decl/closet_appearance/crate/thinktronic
+ color = COLOR_PALE_PURPLE_GRAY
+ decals = list(
+ "toptext" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/ummarcar
+ color = COLOR_BEIGE
+ decals = list(
+ "crate_stripes" = COLOR_OFF_WHITE,
+ "toptext" = COLOR_GRAY20
+ )
+
+/decl/closet_appearance/crate/unathiimport
+ color = COLOR_SILVER
+ decals = list(
+ "crate_stripe" = COLOR_RED,
+ "crate_reticle" = COLOR_RED_GRAY
+ )
+
+/decl/closet_appearance/crate/veymed
+ color = COLOR_OFF_WHITE
+ decals = list(
+ "crate_stripe" = COLOR_PALE_BTL_GREEN
+ )
+ extra_decals = list(
+ "lid_stripes" = COLOR_RED,
+ "crate_cross" = COLOR_GREEN
+ )
+
+/decl/closet_appearance/crate/ward
+ color = COLOR_OFF_WHITE
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_COMMAND_BLUE,
+ "crate_stripe_right" = COLOR_COMMAND_BLUE,
+ "hazard" = COLOR_OFF_WHITE,
+ "wt" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/xion
+ color = COLOR_ORANGE
+ extra_decals = list(
+ "crate_stripes" = COLOR_OFF_WHITE,
+ "xion" = COLOR_OFF_WHITE,
+ "hazard" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/zenghu
+ color = COLOR_OFF_WHITE
+ extra_decals = list(
+ "crate_stripes" = COLOR_RED,
+ "zenghu" = COLOR_OFF_WHITE
+ )
+
+// Secure Crates
/decl/closet_appearance/crate/secure
can_lock = TRUE
@@ -775,7 +977,8 @@
extra_decals = list(
"crate_stripe_left" = COLOR_OFF_WHITE,
"crate_stripe_right" = COLOR_OFF_WHITE,
- "toxin" = COLOR_OFF_WHITE
+ "toxin" = COLOR_OFF_WHITE,
+ "nano" = COLOR_OFF_WHITE
)
/decl/closet_appearance/crate/secure/weapon
@@ -789,16 +992,61 @@
"hazard" = COLOR_OFF_WHITE
)
-/decl/closet_appearance/crate/secure/heph
- color = COLOR_GRAY20
+// Secure corporate branding
+
+/decl/closet_appearance/crate/secure/aether
+ color = COLOR_YELLOW_GRAY
decals = list(
"crate_bracing"
)
extra_decals = list(
- "crate_stripe_left" = COLOR_NT_RED,
- "crate_stripe_right" = COLOR_NT_RED,
- "hazard" = COLOR_OFF_WHITE,
- "heph" = COLOR_OFF_WHITE
+ "crate_stripes" = COLOR_BLUE_LIGHT,
+ "aether" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/secure/bishop
+ color = COLOR_OFF_WHITE
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_SKY_BLUE,
+ "crate_stripe_right" = COLOR_SKY_BLUE,
+ "bishop" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/secure/cybersolutions
+ color = COLOR_ALUMINIUM
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "hazard" = COLOR_DARK_GOLD,
+ "toptext" = COLOR_DARK_GOLD
+ )
+
+/decl/closet_appearance/crate/secure/einstein
+ color = COLOR_DARK_BLUE_GRAY
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_BEIGE,
+ "crate_stripe_right" = COLOR_BEIGE,
+ "einstein" = COLOR_OFF_WHITE,
+ "hazard" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/secure/focalpoint
+ color = COLOR_GOLD
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_NAVY_BLUE,
+ "crate_stripe_right" = COLOR_NAVY_BLUE,
+ "focal" = COLOR_OFF_WHITE,
+ "hazard" = COLOR_NAVY_BLUE
)
/decl/closet_appearance/crate/secure/gilthari
@@ -813,16 +1061,15 @@
"gilthari" = COLOR_OFF_WHITE
)
-/decl/closet_appearance/crate/secure/ward
- color = COLOR_OFF_WHITE
+/decl/closet_appearance/crate/secure/grayson
+ color = COLOR_STEEL
decals = list(
"crate_bracing"
)
extra_decals = list(
- "crate_stripe_left" = COLOR_COMMAND_BLUE,
- "crate_stripe_right" = COLOR_COMMAND_BLUE,
- "hazard" = COLOR_OFF_WHITE,
- "wt" = COLOR_OFF_WHITE
+ "crate_stripe_left" = COLOR_MAROON,
+ "crate_stripe_right" = COLOR_MAROON,
+ "grayson" = COLOR_OFF_WHITE
)
/decl/closet_appearance/crate/secure/hedberg
@@ -837,6 +1084,18 @@
"hedberg" = COLOR_OFF_WHITE
)
+/decl/closet_appearance/crate/secure/heph
+ color = COLOR_GRAY20
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_NT_RED,
+ "crate_stripe_right" = COLOR_NT_RED,
+ "hazard" = COLOR_OFF_WHITE,
+ "heph" = COLOR_OFF_WHITE
+ )
+
/decl/closet_appearance/crate/secure/lawson
color = COLOR_SAN_MARINO_BLUE
decals = list(
@@ -849,6 +1108,103 @@
"lawson" = COLOR_OFF_WHITE
)
+/decl/closet_appearance/crate/secure/morpheus
+ color = COLOR_ALUMINIUM
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "hazard" = COLOR_GUNMETAL,
+ "toptext" = COLOR_GUNMETAL
+ )
+
+/decl/closet_appearance/crate/secure/nanotrasen
+ color = COLOR_NT_RED
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_OFF_WHITE,
+ "crate_stripe_right" = COLOR_OFF_WHITE,
+ "nano" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/secure/nanotrasenmedical
+ color = COLOR_OFF_WHITE
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "crate_stripe" = COLOR_NT_RED,
+ "nano" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/secure/saare
+ color = COLOR_ALUMINIUM
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "hazard" = COLOR_RED,
+ "xion" = COLOR_GRAY40
+ )
+
+/decl/closet_appearance/crate/secure/solgov
+ color = COLOR_SAN_MARINO_BLUE
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_OFF_WHITE,
+ "crate_stripes" = COLOR_OFF_WHITE,
+ "hazard" = COLOR_OFF_WHITE,
+ "scg" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/secure/veymed
+ color = COLOR_OFF_WHITE
+ decals = list(
+ "crate_bracing",
+ "crate_stripe" = COLOR_PALE_BTL_GREEN
+ )
+ extra_decals = list(
+ "lid_stripes" = COLOR_RED,
+ "crate_cross" = COLOR_GREEN
+ )
+
+/decl/closet_appearance/crate/secure/ward
+ color = COLOR_OFF_WHITE
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_COMMAND_BLUE,
+ "crate_stripe_right" = COLOR_COMMAND_BLUE,
+ "hazard" = COLOR_OFF_WHITE,
+ "wt" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/secure/xion
+ color = COLOR_ORANGE
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "crate_stripes" = COLOR_OFF_WHITE,
+ "xion" = COLOR_OFF_WHITE,
+ "hazard" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/crate/secure/zenghu
+ color = COLOR_OFF_WHITE
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "crate_stripes" = COLOR_RED,
+ "zenghu" = COLOR_OFF_WHITE
+ )
+
/decl/closet_appearance/crate/secure/hydroponics
extra_decals = list(
"crate_stripe_left" = COLOR_GREEN_GRAY,
@@ -869,6 +1225,7 @@
extra_decals = null
/decl/closet_appearance/large_crate/critter
+ color = COLOR_BEIGE
decals = list(
"airholes"
)
@@ -882,6 +1239,42 @@
"text" = COLOR_GREEN_GRAY
)
+/decl/closet_appearance/large_crate/aether
+ color = COLOR_YELLOW_GRAY
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "text" = COLOR_BLUE_LIGHT
+ )
+
+/decl/closet_appearance/large_crate/einstein
+ color = COLOR_DARK_BLUE_GRAY
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "text" = COLOR_BEIGE
+ )
+
+/decl/closet_appearance/large_crate/nanotrasen
+ color = COLOR_NT_RED
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "text" = COLOR_OFF_WHITE
+ )
+
+/decl/closet_appearance/large_crate/xion
+ color = COLOR_ORANGE
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "text" = COLOR_OFF_WHITE
+ )
+
/decl/closet_appearance/large_crate/secure
can_lock = TRUE
@@ -895,6 +1288,46 @@
"text_upper" = COLOR_OFF_WHITE
)
+/decl/closet_appearance/large_crate/secure/aether
+ color = COLOR_YELLOW_GRAY
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "marking" = COLOR_OFF_WHITE,
+ "text_upper" = COLOR_BLUE_LIGHT
+ )
+
+/decl/closet_appearance/large_crate/secure/einstein
+ color = COLOR_DARK_BLUE_GRAY
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "marking" = COLOR_OFF_WHITE,
+ "text_upper" = COLOR_BEIGE
+ )
+
+/decl/closet_appearance/large_crate/secure/heph
+ color = COLOR_GRAY20
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "marking" = COLOR_NT_RED,
+ "text_upper" = COLOR_NT_RED
+ )
+
+/decl/closet_appearance/large_crate/secure/xion
+ color = COLOR_ORANGE
+ decals = list(
+ "crate_bracing"
+ )
+ extra_decals = list(
+ "marking" = COLOR_OFF_WHITE,
+ "text_upper" = COLOR_OFF_WHITE
+ )
+
// Cabinets.
/decl/closet_appearance/cabinet
base_icon = 'icons/obj/closets/bases/cabinet.dmi'
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index bd81b8ab761..6223061ed88 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -6,6 +6,7 @@
icon = 'icons/obj/closets/bases/crate.dmi'
closet_appearance = /decl/closet_appearance/crate
climbable = 1
+ dir = 4 //Spawn facing 'forward' by default.
var/points_per_crate = 5
var/rigged = 0
@@ -65,6 +66,26 @@
update_icon()
return 1
+/obj/structure/closet/crate/verb/rotate_clockwise()
+ set name = "Rotate Crate Clockwise"
+ set category = "Object"
+ set src in oview(1)
+
+ if (usr.stat || usr.restrained() || anchored)
+ return
+
+ src.set_dir(turn(src.dir, 270))
+
+/obj/structure/closet/crate/verb/rotate_counterclockwise()
+ set category = "Object"
+ set name = "Rotate Crate Counterclockwise"
+ set src in view(1)
+
+ if (usr.stat || usr.restrained() || anchored)
+ return
+
+ src.set_dir(turn(src.dir, 90))
+
/obj/structure/closet/crate/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(opened)
if(isrobot(user))
@@ -274,6 +295,14 @@
var/target_temp = T0C - 40
var/cooling_power = 40
+/obj/structure/closet/crate/freezer/centauri
+ desc = "A freezer stamped with the logo of Centauri Provisions."
+ closet_appearance = /decl/closet_appearance/crate/freezer/centauri
+
+/obj/structure/closet/crate/freezer/nanotrasen
+ desc = "A freezer stamped with the logo of NanoTrasen."
+ closet_appearance = /decl/closet_appearance/crate/freezer/nanotrasen
+
/obj/structure/closet/crate/freezer/return_air()
var/datum/gas_mixture/gas = (..())
if(!gas) return null
@@ -303,6 +332,11 @@
organ.preserved = 0
..()
+/obj/structure/closet/crate/weapon
+ name = "weapons crate"
+ desc = "A barely secured weapons crate."
+ closet_appearance = /decl/closet_appearance/crate/secure/weapon
+
/obj/structure/closet/crate/freezer/rations //Fpr use in the escape shuttle
name = "emergency rations"
desc = "A crate of emergency rations."
@@ -310,14 +344,12 @@
starts_with = list(
/obj/random/mre = 6)
-
/obj/structure/closet/crate/bin
name = "large bin"
desc = "A large bin."
closet_appearance = null
icon = 'icons/obj/closets/largebin.dmi'
-
/obj/structure/closet/crate/radiation
name = "radioactive gear crate"
desc = "A crate with a radiation sign on it."
@@ -327,27 +359,145 @@
/obj/item/clothing/suit/radiation = 4,
/obj/item/clothing/head/radiation = 4)
+//TSCs
+
+/obj/structure/closet/crate/aether
+ desc = "A crate painted in the colours of Aether Atmospherics and Recycling."
+ closet_appearance = /decl/closet_appearance/crate/aether
+
+/obj/structure/closet/crate/centauri
+ desc = "A crate decorated with the logo of Centauri Provisions."
+ closet_appearance = /decl/closet_appearance/crate/centauri
+
+/obj/structure/closet/crate/einstein
+ desc = "A crate labelled with an Einstein Engines sticker."
+ closet_appearance = /decl/closet_appearance/crate/einstein
+
+/obj/structure/closet/crate/focalpoint
+ desc = "A crate marked with the decal of Focal Point Energistics."
+ closet_appearance = /decl/closet_appearance/crate/focalpoint
+
+/obj/structure/closet/crate/gilthari
+ desc = "A crate embossed with the logo of Gilthari Exports."
+ closet_appearance = /decl/closet_appearance/crate/gilthari
+
+/obj/structure/closet/crate/grayson
+ desc = "A bare metal crate spraypainted with Grayson Manufactories decals."
+ closet_appearance = /decl/closet_appearance/crate/grayson
+
+/obj/structure/closet/crate/heph
+ desc = "A sturdy crate marked with the logo of Hephaestus Industries."
+ closet_appearance = /decl/closet_appearance/crate/heph
+
+/obj/structure/closet/crate/morpheus
+ desc = "A crate crudely imprinted with 'MORPHEUS CYBERKINETICS'."
+ closet_appearance = /decl/closet_appearance/crate/morpheus
+
+/obj/structure/closet/crate/nanotrasen
+ desc = "A crate emblazoned with the standard NanoTrasen livery."
+ closet_appearance = /decl/closet_appearance/crate/nanotrasen
+
+/obj/structure/closet/crate/nanothreads
+ desc = "A crate emblazoned with the NanoThreads Garments livery, a subsidary of the NanoTrasen Corporation."
+ closet_appearance = /decl/closet_appearance/crate/nanotrasenclothing
+
+/obj/structure/closet/crate/nanocare
+ desc = "A crate emblazoned with the NanoCare Medical livery, a subsidary of the NanoTrasen Corporation."
+ closet_appearance = /decl/closet_appearance/crate/nanotrasenmedical
+
+/obj/structure/closet/crate/oculum
+ desc = "A crate minimally decorated with the logo of media giant Oculum Broadcast."
+ closet_appearance = /decl/closet_appearance/crate/oculum
+
+/obj/structure/closet/crate/veymed
+ desc = "A sterile crate extensively detailed in Veymed colours."
+ closet_appearance = /decl/closet_appearance/crate/veymed
+
+/obj/structure/closet/crate/ward
+ desc = "A crate decaled with the logo of Ward-Takahashi."
+ closet_appearance = /decl/closet_appearance/crate/ward
+
+/obj/structure/closet/crate/xion
+ desc = "A crate painted in Xion Manufacturing Group orange."
+ closet_appearance = /decl/closet_appearance/crate/xion
+
+/obj/structure/closet/crate/zenghu
+ desc = "A sterile crate marked with the logo of Zeng-Hu Pharmaceuticals."
+ closet_appearance = /decl/closet_appearance/crate/zenghu
+
+// Brands/subsidiaries
+
+/obj/structure/closet/crate/allico
+ desc = "A crate painted in the distinctive cheerful colours of AlliCo. Ltd."
+ closet_appearance = /decl/closet_appearance/crate/allico
+
+/obj/structure/closet/crate/carp
+ desc = "A crate painted with the garish livery of Consolidated Agricultural Resources Plc."
+ closet_appearance = /decl/closet_appearance/crate/carp
+
+/obj/structure/closet/crate/hedberg
+ name = "weapons crate"
+ desc = "A weapons crate stamped with the logo of Hedberg-Hammarstrom and the lock conspicuously absent."
+ closet_appearance = /decl/closet_appearance/crate/secure/hedberg
+
+/obj/structure/closet/crate/galaksi
+ desc = "A crate printed with the markings of Ward-Takahashi's Galaksi Appliance branding."
+ closet_appearance = /decl/closet_appearance/crate/galaksi
+
+/obj/structure/closet/crate/thinktronic
+ desc = "A crate printed with the markings of Thinktronic Systems."
+ closet_appearance = /decl/closet_appearance/crate/thinktronic
+
+/obj/structure/closet/crate/ummarcar
+ desc = "A flimsy crate marked labelled 'UmMarcar Office Supply'."
+ closet_appearance = /decl/closet_appearance/crate/ummarcar
+
+/obj/structure/closet/crate/unathi
+ name = "import crate"
+ desc = "A crate painted with the markings of Moghes Imported Sissalik Jerky."
+ closet_appearance = /decl/closet_appearance/crate/unathiimport
+
+
+// Secure Crates
/obj/structure/closet/crate/secure/weapon
name = "weapons crate"
desc = "A secure weapons crate."
closet_appearance = /decl/closet_appearance/crate/secure/weapon
+/obj/structure/closet/crate/secure/aether
+ desc = "A secure crate painted in the colours of Aether Atmospherics and Recycling."
+ closet_appearance = /decl/closet_appearance/crate/secure/aether
+
+/obj/structure/closet/crate/secure/bishop
+ desc = "A secure crate finely decorated with the emblem of Bishop Cybernetics."
+ closet_appearance = /decl/closet_appearance/crate/secure/bishop
+
+/obj/structure/closet/crate/secure/cybersolutions
+ desc = "An unadorned secure metal crate labelled 'Cyber Solutions'."
+ closet_appearance = /decl/closet_appearance/crate/secure/cybersolutions
+
+/obj/structure/closet/crate/secure/einstein
+ desc = "A secure crate labelled with an Einstein Engines sticker."
+ closet_appearance = /decl/closet_appearance/crate/secure/einstein
+
+/obj/structure/closet/crate/secure/focalpoint
+ desc = "A secure crate marked with the decal of Focal Point Energistics."
+ closet_appearance = /decl/closet_appearance/crate/secure/focalpoint
+
+/obj/structure/closet/crate/secure/gilthari
+ desc = "A secure crate embossed with the logo of Gilthari Exports."
+ closet_appearance = /decl/closet_appearance/crate/secure/gilthari
+
+/obj/structure/closet/crate/secure/grayson
+ desc = "A secure bare metal crate spraypainted with Grayson Manufactories decals."
+ closet_appearance = /decl/closet_appearance/crate/secure/grayson
+
/obj/structure/closet/crate/secure/hedberg
name = "weapons crate"
desc = "A secure weapons crate stamped with the logo of Hedberg-Hammarstrom."
closet_appearance = /decl/closet_appearance/crate/secure/hedberg
-/obj/structure/closet/crate/secure/gilthari
- name = "weapons crate"
- desc = "A secure weapons crate embossed with the logo of Gilthari Exports."
- closet_appearance = /decl/closet_appearance/crate/secure/gilthari
-
-/obj/structure/closet/crate/secure/ward
- name = "weapons crate"
- desc = "A secure weapons crate decaled with the logo of Ward-Takahashi."
- closet_appearance = /decl/closet_appearance/crate/secure/ward
-
/obj/structure/closet/crate/secure/heph
name = "weapons crate"
desc = "A secure weapons crate marked with the logo of Hephaestus Industries."
@@ -358,9 +508,47 @@
desc = "A secure weapons crate marked with the logo of Lawson Arms."
closet_appearance = /decl/closet_appearance/crate/secure/lawson
+/obj/structure/closet/crate/secure/morpheus
+ desc = "A secure crate crudely imprinted with 'MORPHEUS CYBERKINETICS'."
+ closet_appearance = /decl/closet_appearance/crate/secure/morpheus
+
+/obj/structure/closet/crate/secure/nanotrasen
+ desc = "A secure crate emblazoned with the standard NanoTrasen livery."
+ closet_appearance = /decl/closet_appearance/crate/secure/nanotrasen
+
+/obj/structure/closet/crate/secure/nanocare
+ desc = "A secure crate emblazoned with the NanoCare Medical livery, a subsidary of the NanoTrasen Corporation."
+ closet_appearance = /decl/closet_appearance/crate/secure/nanotrasenmedical
+
+/obj/structure/closet/crate/secure/scg
+ name = "weapons crate"
+ desc = "A secure crate in the official colours of the Solar Confederate Government."
+ closet_appearance = /decl/closet_appearance/crate/secure/solgov
+
+/obj/structure/closet/crate/secure/saare
+ name = "weapons crate"
+ desc = "A secure weapons crate plainly stamped with the logo of Stealth Assault Enterprises."
+ closet_appearance = /decl/closet_appearance/crate/secure/saare
+
+/obj/structure/closet/crate/secure/veymed
+ desc = "A secure sterile crate extensively detailed in Veymed colours."
+ closet_appearance = /decl/closet_appearance/crate/secure/veymed
+
+/obj/structure/closet/crate/secure/ward
+ desc = "A secure crate decaled with the logo of Ward-Takahashi."
+ closet_appearance = /decl/closet_appearance/crate/secure/ward
+
+/obj/structure/closet/crate/secure/xion
+ desc = "A secure crate painted in Xion Manufacturing Group orange."
+ closet_appearance = /decl/closet_appearance/crate/secure/xion
+
+/obj/structure/closet/crate/secure/zenghu
+ desc = "A secure sterile crate marked with the logo of Zeng-Hu Pharmaceuticals."
+ closet_appearance = /decl/closet_appearance/crate/secure/zenghu
+
/obj/structure/closet/crate/secure/phoron
name = "phoron crate"
- desc = "A secure phoron crate."
+ desc = "A secure phoron crate painted in standard NanoTrasen livery."
closet_appearance = /decl/closet_appearance/crate/secure/hazard
/obj/structure/closet/crate/secure/gear
@@ -368,27 +556,24 @@
desc = "A secure gear crate."
closet_appearance = /decl/closet_appearance/crate/secure/weapon
-
/obj/structure/closet/crate/secure/hydrosec
name = "secure hydroponics crate"
desc = "A crate with a lock on it, painted in the scheme of the station's botanists."
closet_appearance = /decl/closet_appearance/crate/secure/hydroponics
-
/obj/structure/closet/crate/secure/engineering
desc = "A crate with a lock on it, painted in the scheme of the station's engineers."
name = "secure engineering crate"
-
/obj/structure/closet/crate/secure/science
name = "secure science crate"
desc = "A crate with a lock on it, painted in the scheme of the station's scientists."
-
/obj/structure/closet/crate/secure/bin
name = "secure bin"
desc = "A secure bin."
+// Large crates
/obj/structure/closet/crate/large
name = "large crate"
@@ -414,6 +599,30 @@
break
return
+/obj/structure/closet/crate/large/critter
+ name = "animal crate"
+ desc = "A hefty crate for hauling animals."
+ closet_appearance = /decl/closet_appearance/large_crate/critter
+
+/obj/structure/closet/crate/large/aether
+ name = "large atmospherics crate"
+ desc = "A hefty metal crate, painted in Aether Atmospherics and Recycling colours."
+ closet_appearance = /decl/closet_appearance/large_crate/aether
+
+/obj/structure/closet/crate/large/einstein
+ name = "large crate"
+ desc = "A hefty metal crate, painted in Einstein Engines colours."
+ closet_appearance = /decl/closet_appearance/large_crate/einstein
+
+/obj/structure/closet/crate/large/nanotrasen
+ name = "large crate"
+ desc = "A hefty metal crate, painted in standard NanoTrasen livery."
+ closet_appearance = /decl/closet_appearance/large_crate/nanotrasen
+
+/obj/structure/closet/crate/large/xion
+ name = "large crate"
+ desc = "A hefty metal crate, painted in Xion Manufacturing Group orange."
+ closet_appearance = /decl/closet_appearance/large_crate/xion
/obj/structure/closet/crate/secure/large
name = "large crate"
@@ -441,10 +650,30 @@
return
-//fluff variant
/obj/structure/closet/crate/secure/large/reinforced
desc = "A hefty, reinforced metal crate with an electronic locking system."
+/obj/structure/closet/crate/secure/large/aether
+ name = "secure atmospherics crate"
+ desc = "A hefty metal crate with an electronic locking system, painted in Aether Atmospherics and Recycling colours."
+ closet_appearance = /decl/closet_appearance/large_crate/secure/aether
+
+/obj/structure/closet/crate/secure/large/einstein
+ desc = "A hefty metal crate with an electronic locking system, painted in Einstein Engines colours."
+ closet_appearance = /decl/closet_appearance/large_crate/secure/einstein
+
+/obj/structure/closet/crate/large/secure/heph
+ desc = "A hefty metal crate with an electronic locking system, marked with Hephaestus Industries colours."
+ closet_appearance = /decl/closet_appearance/large_crate/secure/heph
+
+/obj/structure/closet/crate/secure/large/nanotrasen
+ desc = "A hefty metal crate with an electronic locking system, painted in standard NanoTrasen livery."
+ closet_appearance = /decl/closet_appearance/large_crate/secure/hazard
+
+/obj/structure/closet/crate/large/secure/xion
+ desc = "A hefty metal crate with an electronic locking system, painted in Xion Manufacturing Group orange."
+ closet_appearance = /decl/closet_appearance/large_crate/secure/xion
+
/obj/structure/closet/crate/engineering
name = "engineering crate"
diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm
index 29f1b231aa6..7ed37d06207 100644
--- a/code/game/objects/structures/crates_lockers/largecrate.dm
+++ b/code/game/objects/structures/crates_lockers/largecrate.dm
@@ -44,8 +44,8 @@
/obj/structure/largecrate/hoverpod
name = "\improper Hoverpod assembly crate"
- desc = "It comes in a box for the fabricator's sake. Where does the wood come from? ... And why is it lighter?"
- icon_state = "mulecrate"
+ desc = "You aren't sure how this crate is so light, but the Wulf Aeronautics logo might be a hint."
+ icon_state = "vehiclecrate"
/obj/structure/largecrate/hoverpod/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(W.is_crowbar())
@@ -60,7 +60,7 @@
/obj/structure/largecrate/vehicle
name = "vehicle crate"
- desc = "It comes in a box for the consumer's sake. ..How is this lighter?"
+ desc = "Wulf Aeronautics says it comes in a box for the consumer's sake... How is this so light?"
icon_state = "vehiclecrate"
/obj/structure/largecrate/vehicle/Initialize()
@@ -74,18 +74,22 @@
/obj/structure/largecrate/vehicle/quadbike
name = "\improper ATV crate"
+ desc = "A hefty wooden crate proudly displaying the logo of Ward-Takahashi's automotive division."
starts_with = list(/obj/structure/vehiclecage/quadbike)
/obj/structure/largecrate/vehicle/quadtrailer
name = "\improper ATV trailer crate"
+ desc = "A hefty wooden crate proudly displaying the logo of Ward-Takahashi's automotive division."
starts_with = list(/obj/structure/vehiclecage/quadtrailer)
/obj/structure/largecrate/animal
- icon_state = "lisacrate" //VOREStation Edit
+ icon_state = "crittercrate"
+ desc = "A hefty wooden crate with air holes. It is marked with the logo of NanoTrasen Pastures and the slogan, '90% less cloning defects* than competing brands**, or your money back***!'"
/obj/structure/largecrate/animal/mulebot
name = "Mulebot crate"
- icon_state = "mulecrate" //VOREStation Edit
+ desc = "A hefty wooden crate labelled 'Proud Product of the Xion Manufacturing Group'"
+ icon_state = "mulecrate"
starts_with = list(/mob/living/bot/mulebot)
/obj/structure/largecrate/animal/corgi
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 7b16e0fd801..c5846c39eb6 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -253,6 +253,10 @@
'sound/vore/sunesound/prey/death_07.ogg','sound/vore/sunesound/prey/death_08.ogg','sound/vore/sunesound/prey/death_09.ogg',
'sound/vore/sunesound/prey/death_10.ogg')
//END VORESTATION EDIT
+ if ("terminal_type")
+ soundin = pick('sound/machines/terminal_button01.ogg', 'sound/machines/terminal_button02.ogg', 'sound/machines/terminal_button03.ogg', \
+ 'sound/machines/terminal_button04.ogg', 'sound/machines/terminal_button05.ogg', 'sound/machines/terminal_button06.ogg', \
+ 'sound/machines/terminal_button07.ogg', 'sound/machines/terminal_button08.ogg')
return soundin
//Are these even used?
diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm
index 33ca558e801..90a5c26e543 100644
--- a/code/modules/admin/holder2.dm
+++ b/code/modules/admin/holder2.dm
@@ -119,3 +119,11 @@ NOTE: It checks usr by default. Supply the "user" argument if you wish to check
holder.disassociate()
//qdel(holder)
return 1
+
+//This proc checks whether subject has at least ONE of the rights specified in rights_required.
+/proc/check_rights_for(client/subject, rights_required)
+ if(subject && subject.holder)
+ if(rights_required && !(rights_required & subject.holder.rights))
+ return 0
+ return 1
+ return 0
\ No newline at end of file
diff --git a/code/modules/alarm/alarm.dm b/code/modules/alarm/alarm.dm
index f8d853ca8cd..30de8cc5605 100644
--- a/code/modules/alarm/alarm.dm
+++ b/code/modules/alarm/alarm.dm
@@ -18,7 +18,6 @@
var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared.
var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source.
var/list/cameras //List of cameras that can be switched to, if the player has that capability.
- var/cache_id //ID for camera cache, changed by invalidateCameraCache().
var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera).
var/area/last_name //The last acquired name, used should origin be lost
var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated.
@@ -78,15 +77,10 @@
return last_name
/datum/alarm/proc/cameras()
- // reset camera cache
- if(camera_repository.camera_cache_id != cache_id)
- cameras = null
- cache_id = camera_repository.camera_cache_id
// If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras
- else if(cameras && (last_camera_area != alarm_area()))
+ if(cameras && (last_camera_area != alarm_area()))
cameras = null
- // The list of cameras is also reset by /proc/invalidateCameraCache()
if(!cameras)
cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras()
diff --git a/code/modules/asset_cache/asset_cache.dm b/code/modules/asset_cache/asset_cache.dm
new file mode 100644
index 00000000000..53a30d4299a
--- /dev/null
+++ b/code/modules/asset_cache/asset_cache.dm
@@ -0,0 +1,110 @@
+/*
+Asset cache quick users guide:
+
+Make a datum in asset_list_items.dm with your assets for your thing.
+Checkout asset_list.dm for the helper subclasses
+The simple subclass will most like be of use for most cases.
+Then call get_asset_datum() with the type of the datum you created and store the return
+Then call .send(client) on that stored return value.
+
+Note: If your code uses output() with assets you will need to call asset_flush on the client and wait for it to return before calling output(). You only need do this if .send(client) returned TRUE
+*/
+
+//When sending mutiple assets, how many before we give the client a quaint little sending resources message
+#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
+
+//This proc sends the asset to the client, but only if it needs it.
+//This proc blocks(sleeps) unless verify is set to false
+/proc/send_asset(client/client, asset_name)
+ return send_asset_list(client, list(asset_name))
+
+/// Sends a list of assets to a client
+/// This proc will no longer block, use client.asset_flush() if you to need know when the client has all assets (such as for output()). (This is not required for browse() calls as they use the same message queue as asset sends)
+/// client - a client or mob
+/// asset_list - A list of asset filenames to be sent to the client.
+/// Returns TRUE if any assets were sent.
+/proc/send_asset_list(client/client, list/asset_list)
+ if(!istype(client))
+ if(ismob(client))
+ var/mob/M = client
+ if(M.client)
+ client = M.client
+ else
+ return
+ else
+ return
+
+ var/list/unreceived = list()
+
+ for (var/asset_name in asset_list)
+ var/datum/asset_cache_item/asset = SSassets.cache[asset_name]
+ if (!asset)
+ continue
+ var/asset_file = asset.resource
+ if (!asset_file)
+ continue
+
+ var/asset_md5 = asset.md5
+ if (client.sent_assets[asset_name] == asset_md5)
+ continue
+ unreceived[asset_name] = asset_md5
+
+ if (unreceived.len)
+ if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
+ to_chat(client, "Sending Resources...")
+
+ for(var/asset in unreceived)
+ var/datum/asset_cache_item/ACI
+ if ((ACI = SSassets.cache[asset]))
+ log_asset("Sending asset [asset] to client [client]")
+ client << browse_rsc(ACI.resource, asset)
+
+ client.sent_assets |= unreceived
+ addtimer(CALLBACK(client, /client/proc/asset_cache_update_json), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE)
+ return TRUE
+ return FALSE
+
+//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
+//The proc calls procs that sleep for long times.
+/proc/getFilesSlow(client/client, list/files, register_asset = TRUE, filerate = 3)
+ var/startingfilerate = filerate
+ for(var/file in files)
+ if (!client)
+ break
+ if (register_asset)
+ register_asset(file, files[file])
+
+ if (send_asset(client, file))
+ if (!(--filerate))
+ filerate = startingfilerate
+ client.asset_flush()
+ stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
+
+//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
+//icons and virtual assets get copied to the dyn rsc before use
+/proc/register_asset(asset_name, asset)
+ var/datum/asset_cache_item/ACI = new(asset_name, asset)
+
+ //this is technically never something that was supported and i want metrics on how often it happens if at all.
+ if (SSassets.cache[asset_name])
+ var/datum/asset_cache_item/OACI = SSassets.cache[asset_name]
+ if (OACI.md5 != ACI.md5)
+ stack_trace("ERROR: new asset added to the asset cache with the same name as another asset: [asset_name] existing asset md5: [OACI.md5] new asset md5:[ACI.md5]")
+ else
+ 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
+//The same asset will always lead to the same asset name
+/proc/generate_asset_name(file)
+ return "asset.[md5(fcopy_rsc(file))]"
+
diff --git a/code/modules/asset_cache/asset_cache_client.dm b/code/modules/asset_cache/asset_cache_client.dm
new file mode 100644
index 00000000000..0f51520f13a
--- /dev/null
+++ b/code/modules/asset_cache/asset_cache_client.dm
@@ -0,0 +1,51 @@
+
+/// Process asset cache client topic calls for "asset_cache_confirm_arrival=[INT]"
+/client/proc/asset_cache_confirm_arrival(job_id)
+ var/asset_cache_job = round(text2num(job_id))
+ //because we skip the limiter, we have to make sure this is a valid arrival and not somebody tricking us into letting them append to a list without limit.
+ if (asset_cache_job > 0 && asset_cache_job <= last_asset_job && !(completed_asset_jobs["[asset_cache_job]"]))
+ completed_asset_jobs["[asset_cache_job]"] = TRUE
+ last_completed_asset_job = max(last_completed_asset_job, asset_cache_job)
+ else
+ return asset_cache_job || TRUE
+
+
+/// Process asset cache client topic calls for "asset_cache_preload_data=[HTML+JSON_STRING]
+/client/proc/asset_cache_preload_data(data)
+ /*var/jsonend = findtextEx(data, "{{{ENDJSONDATA}}}")
+ if (!jsonend)
+ CRASH("invalid asset_cache_preload_data, no jsonendmarker")*/
+ //var/json = html_decode(copytext(data, 1, jsonend))
+ var/json = data
+ var/list/preloaded_assets = json_decode(json)
+
+ for (var/preloaded_asset in preloaded_assets)
+ if (copytext(preloaded_asset, findlasttext(preloaded_asset, ".")+1) in list("js", "jsm", "htm", "html"))
+ preloaded_assets -= preloaded_asset
+ continue
+ sent_assets |= preloaded_assets
+
+
+/// Updates the client side stored html/json combo file used to keep track of what assets the client has between restarts/reconnects.
+/client/proc/asset_cache_update_json(verify = FALSE, list/new_assets = list())
+ if (world.time - connection_time < 10 SECONDS) //don't override the existing data file on a new connection
+ return
+ if (!islist(new_assets))
+ new_assets = list("[new_assets]" = md5(SSassets.cache[new_assets]))
+
+ src << browse(json_encode(new_assets|sent_assets), "file=asset_data.json&display=0")
+
+/// Blocks until all currently sending browser assets have been sent.
+/// Due to byond limitations, this proc will sleep for 1 client round trip even if the client has no pending asset sends.
+/// This proc will return an untrue value if it had to return before confirming the send, such as timeout or the client going away.
+/client/proc/asset_flush(timeout = 50)
+ var/job = ++last_asset_job
+ var/t = 0
+ var/timeout_time = timeout
+ src << browse({""}, "window=asset_cache_browser&file=asset_cache_send_verify.htm")
+
+ while(!completed_asset_jobs["[job]"] && t < timeout_time) // Reception is handled in Topic()
+ stoplag(1) // Lock up the caller until this is received.
+ t++
+ if (t < timeout_time)
+ return TRUE
diff --git a/code/modules/asset_cache/asset_cache_item.dm b/code/modules/asset_cache/asset_cache_item.dm
new file mode 100644
index 00000000000..5f02e561c6f
--- /dev/null
+++ b/code/modules/asset_cache/asset_cache_item.dm
@@ -0,0 +1,23 @@
+/**
+ * # asset_cache_item
+ *
+ * An internal datum containing info on items in the asset cache. Mainly used to cache md5 info for speed.
+**/
+/datum/asset_cache_item
+ var/name
+ var/url
+ var/md5
+ var/resource
+
+/datum/asset_cache_item/New(name, file)
+ if (!isfile(file))
+ file = fcopy_rsc(file)
+ md5 = md5(file)
+ if (!md5)
+ md5 = md5(fcopy_rsc(file))
+ if (!md5)
+ CRASH("invalid asset sent to asset cache")
+ log_world("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
new file mode 100644
index 00000000000..5cc16d06bc7
--- /dev/null
+++ b/code/modules/asset_cache/asset_list.dm
@@ -0,0 +1,260 @@
+
+//These datums are used to populate the asset cache, the proc "register()" does this.
+//Place any asset datums you create in asset_list_items.dm
+
+//all of our asset datums, used for referring to these later
+GLOBAL_LIST_EMPTY(asset_datums)
+
+//get an assetdatum or make a new one
+/proc/get_asset_datum(type)
+ return GLOB.asset_datums[type] || new type()
+
+/datum/asset
+ var/_abstract = /datum/asset
+
+/datum/asset/New()
+ GLOB.asset_datums[type] = src
+ register()
+
+/datum/asset/proc/get_url_mappings()
+ return list()
+
+/datum/asset/proc/register()
+ return
+
+/datum/asset/proc/send(client)
+ return
+
+
+//If you don't need anything complicated.
+/datum/asset/simple
+ _abstract = /datum/asset/simple
+ var/assets = list()
+
+/datum/asset/simple/register()
+ for(var/asset_name in assets)
+ 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
+ _abstract = /datum/asset/group
+ var/list/children
+
+/datum/asset/group/register()
+ for(var/type in children)
+ get_asset_datum(type)
+
+/datum/asset/group/send(client/C)
+ for(var/type in children)
+ 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
+// 1400-odd individual PNG files
+#define SPR_SIZE 1
+#define SPR_IDX 2
+#define SPRSZ_COUNT 1
+#define SPRSZ_ICON 2
+#define SPRSZ_STRIPPED 3
+
+/datum/asset/spritesheet
+ _abstract = /datum/asset/spritesheet
+ var/name
+ var/list/sizes = list() // "32x32" -> list(10, icon/normal, icon/stripped)
+ var/list/sprites = list() // "foo_bar" -> list("32x32", 5)
+
+/datum/asset/spritesheet/register()
+ 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)
+ text2file(generate_css(), fname)
+ register_asset(res_name, fcopy_rsc(fname))
+ fdel(fname)
+
+/datum/asset/spritesheet/send(client/C)
+ if (!name)
+ return
+ var/all = list("spritesheet_[name].css")
+ for(var/size_id in sizes)
+ 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]
+ if (size[SPRSZ_STRIPPED])
+ continue
+
+ #ifdef RUST_G
+ // save flattened version
+ var/fname = "data/spritesheets/[name]_[size_id].png"
+ fcopy(size[SPRSZ_ICON], fname)
+ var/error = call(RUST_G, "dmi_strip_metadata")(fname)
+ if(length(error))
+ stack_trace("Failed to strip [name]_[size_id].png: [error]")
+ size[SPRSZ_STRIPPED] = icon(fname)
+ fdel(fname)
+ #else
+ #warn It looks like you don't have RUST_G enabled. Without RUST_G, the RPD icons will not function, so it strongly recommended you reenable it.
+ #endif
+
+/datum/asset/spritesheet/proc/generate_css()
+ var/list/out = list()
+
+ 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('[get_asset_url("[name]_[size_id].png")]') no-repeat;}"
+
+ for (var/sprite_id in sprites)
+ var/sprite = sprites[sprite_id]
+ var/size_id = sprite[SPR_SIZE]
+ var/idx = sprite[SPR_IDX]
+ var/size = sizes[size_id]
+
+ var/icon/tiny = size[SPRSZ_ICON]
+ var/icon/big = size[SPRSZ_STRIPPED]
+ var/per_line = big.Width() / tiny.Width()
+ var/x = (idx % per_line) * tiny.Width()
+ var/y = round(idx / per_line) * tiny.Height()
+
+ out += ".[name][size_id].[sprite_id]{background-position:-[x]px -[y]px;}"
+
+ return out.Join("\n")
+
+/datum/asset/spritesheet/proc/Insert(sprite_name, icon/I, icon_state="", dir=SOUTH, frame=1, moving=FALSE)
+ I = icon(I, icon_state=icon_state, dir=dir, frame=frame, moving=moving)
+ if (!I || !length(icon_states(I))) // that direction or state doesn't exist
+ return
+ var/size_id = "[I.Width()]x[I.Height()]"
+ var/size = sizes[size_id]
+
+ if (sprites[sprite_name])
+ CRASH("duplicate sprite \"[sprite_name]\" in sheet [name] ([type])")
+
+ if (size)
+ var/position = size[SPRSZ_COUNT]++
+ var/icon/sheet = size[SPRSZ_ICON]
+ size[SPRSZ_STRIPPED] = null
+ sheet.Insert(I, icon_state=sprite_name)
+ sprites[sprite_name] = list(size_id, position)
+ else
+ sizes[size_id] = size = list(1, I, null)
+ sprites[sprite_name] = list(size_id, 0)
+
+/datum/asset/spritesheet/proc/InsertAll(prefix, icon/I, list/directions)
+ if (length(prefix))
+ prefix = "[prefix]-"
+
+ if (!directions)
+ directions = list(SOUTH)
+
+ for (var/icon_state_name in icon_states(I))
+ for (var/direction in directions)
+ var/prefix2 = (directions.len > 1) ? "[dir2text(direction)]-" : ""
+ Insert("[prefix][prefix2][icon_state_name]", I, icon_state=icon_state_name, dir=direction)
+
+/datum/asset/spritesheet/proc/css_tag()
+ 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]
+ if (!sprite)
+ return null
+ var/size_id = sprite[SPR_SIZE]
+ return {""}
+
+/datum/asset/spritesheet/proc/icon_class_name(sprite_name)
+ var/sprite = sprites[sprite_name]
+ if (!sprite)
+ return null
+ var/size_id = sprite[SPR_SIZE]
+ return {"[name][size_id] [sprite_name]"}
+
+#undef SPR_SIZE
+#undef SPR_IDX
+#undef SPRSZ_COUNT
+#undef SPRSZ_ICON
+#undef SPRSZ_STRIPPED
+
+
+/datum/asset/spritesheet/simple
+ _abstract = /datum/asset/spritesheet/simple
+ var/list/assets
+
+/datum/asset/spritesheet/simple/register()
+ for (var/key in assets)
+ Insert(key, assets[key])
+ ..()
+
+//Generates assets based on iconstates of a single icon
+/datum/asset/simple/icon_states
+ _abstract = /datum/asset/simple/icon_states
+ var/icon
+ var/list/directions = list(SOUTH)
+ var/frame = 1
+ var/movement_states = FALSE
+
+ var/prefix = "default" //asset_name = "[prefix].[icon_state_name].png"
+ var/generic_icon_names = FALSE //generate icon filenames using generate_asset_name() instead the above format
+
+/datum/asset/simple/icon_states/register(_icon = icon)
+ for(var/icon_state_name in icon_states(_icon))
+ for(var/direction in directions)
+ var/asset = icon(_icon, icon_state_name, direction, frame, movement_states)
+ if (!asset)
+ continue
+ asset = fcopy_rsc(asset) //dedupe
+ var/prefix2 = (directions.len > 1) ? "[dir2text(direction)]." : ""
+ var/asset_name = sanitize_filename("[prefix].[prefix2][icon_state_name].png")
+ if (generic_icon_names)
+ asset_name = "[generate_asset_name(asset)].png"
+
+ register_asset(asset_name, asset)
+
+/datum/asset/simple/icon_states/multiple_icons
+ _abstract = /datum/asset/simple/icon_states/multiple_icons
+ var/list/icons
+
+/datum/asset/simple/icon_states/multiple_icons/register()
+ for(var/i in icons)
+ ..(i)
+
+
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
new file mode 100644
index 00000000000..2b90000ea0c
--- /dev/null
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -0,0 +1,445 @@
+//DEFINITIONS FOR ASSET DATUMS START HERE.
+
+/datum/asset/simple/tgui
+ assets = list(
+ "tgui.bundle.js" = 'tgui/packages/tgui/public/tgui.bundle.js',
+ "tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css',
+ )
+
+// /datum/asset/simple/headers
+// assets = list(
+// "alarm_green.gif" = 'icons/program_icons/alarm_green.gif',
+// "alarm_red.gif" = 'icons/program_icons/alarm_red.gif',
+// "batt_5.gif" = 'icons/program_icons/batt_5.gif',
+// "batt_20.gif" = 'icons/program_icons/batt_20.gif',
+// "batt_40.gif" = 'icons/program_icons/batt_40.gif',
+// "batt_60.gif" = 'icons/program_icons/batt_60.gif',
+// "batt_80.gif" = 'icons/program_icons/batt_80.gif',
+// "batt_100.gif" = 'icons/program_icons/batt_100.gif',
+// "charging.gif" = 'icons/program_icons/charging.gif',
+// "downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif',
+// "downloader_running.gif" = 'icons/program_icons/downloader_running.gif',
+// "ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif',
+// "ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif',
+// "power_norm.gif" = 'icons/program_icons/power_norm.gif',
+// "power_warn.gif" = 'icons/program_icons/power_warn.gif',
+// "sig_high.gif" = 'icons/program_icons/sig_high.gif',
+// "sig_low.gif" = 'icons/program_icons/sig_low.gif',
+// "sig_lan.gif" = 'icons/program_icons/sig_lan.gif',
+// "sig_none.gif" = 'icons/program_icons/sig_none.gif',
+// "smmon_0.gif" = 'icons/program_icons/smmon_0.gif',
+// "smmon_1.gif" = 'icons/program_icons/smmon_1.gif',
+// "smmon_2.gif" = 'icons/program_icons/smmon_2.gif',
+// "smmon_3.gif" = 'icons/program_icons/smmon_3.gif',
+// "smmon_4.gif" = 'icons/program_icons/smmon_4.gif',
+// "smmon_5.gif" = 'icons/program_icons/smmon_5.gif',
+// "smmon_6.gif" = 'icons/program_icons/smmon_6.gif',
+// "borg_mon.gif" = 'icons/program_icons/borg_mon.gif'
+// )
+
+// /datum/asset/simple/radar_assets
+// assets = list(
+// "ntosradarbackground.png" = 'icons/UI_Icons/tgui/ntosradar_background.png',
+// "ntosradarpointer.png" = 'icons/UI_Icons/tgui/ntosradar_pointer.png',
+// "ntosradarpointerS.png" = 'icons/UI_Icons/tgui/ntosradar_pointer_S.png'
+// )
+
+// /datum/asset/spritesheet/simple/pda
+// name = "pda"
+// assets = list(
+// "atmos" = 'icons/pda_icons/pda_atmos.png',
+// "back" = 'icons/pda_icons/pda_back.png',
+// "bell" = 'icons/pda_icons/pda_bell.png',
+// "blank" = 'icons/pda_icons/pda_blank.png',
+// "boom" = 'icons/pda_icons/pda_boom.png',
+// "bucket" = 'icons/pda_icons/pda_bucket.png',
+// "medbot" = 'icons/pda_icons/pda_medbot.png',
+// "floorbot" = 'icons/pda_icons/pda_floorbot.png',
+// "cleanbot" = 'icons/pda_icons/pda_cleanbot.png',
+// "crate" = 'icons/pda_icons/pda_crate.png',
+// "cuffs" = 'icons/pda_icons/pda_cuffs.png',
+// "eject" = 'icons/pda_icons/pda_eject.png',
+// "flashlight" = 'icons/pda_icons/pda_flashlight.png',
+// "honk" = 'icons/pda_icons/pda_honk.png',
+// "mail" = 'icons/pda_icons/pda_mail.png',
+// "medical" = 'icons/pda_icons/pda_medical.png',
+// "menu" = 'icons/pda_icons/pda_menu.png',
+// "mule" = 'icons/pda_icons/pda_mule.png',
+// "notes" = 'icons/pda_icons/pda_notes.png',
+// "power" = 'icons/pda_icons/pda_power.png',
+// "rdoor" = 'icons/pda_icons/pda_rdoor.png',
+// "reagent" = 'icons/pda_icons/pda_reagent.png',
+// "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'
+// )
+
+// /datum/asset/spritesheet/simple/paper
+// name = "paper"
+// assets = list(
+// "stamp-clown" = 'icons/stamp_icons/large_stamp-clown.png',
+// "stamp-deny" = 'icons/stamp_icons/large_stamp-deny.png',
+// "stamp-ok" = 'icons/stamp_icons/large_stamp-ok.png',
+// "stamp-hop" = 'icons/stamp_icons/large_stamp-hop.png',
+// "stamp-cmo" = 'icons/stamp_icons/large_stamp-cmo.png',
+// "stamp-ce" = 'icons/stamp_icons/large_stamp-ce.png',
+// "stamp-hos" = 'icons/stamp_icons/large_stamp-hos.png',
+// "stamp-rd" = 'icons/stamp_icons/large_stamp-rd.png',
+// "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'
+// )
+
+
+// /datum/asset/simple/irv
+// assets = list(
+// "jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js',
+// )
+
+// /datum/asset/group/irv
+// children = list(
+// /datum/asset/simple/jquery,
+// /datum/asset/simple/irv
+// )
+
+/datum/asset/simple/generic
+ assets = list(
+ "search.js" = 'html/search.js',
+ "panels.css" = 'html/panels.css',
+ "loading.gif" = 'html/images/loading.gif',
+ "ntlogo.png" = 'html/images/ntlogo.png',
+ "sglogo.png" = 'html/images/sglogo.png',
+ "talisman.png" = 'html/images/talisman.png',
+ "paper_bg.png" = 'html/images/paper_bg.png',
+ "no_image32.png" = 'html/images/no_image32.png',
+ )
+
+/datum/asset/simple/changelog
+ assets = list(
+ "88x31.png" = 'html/88x31.png',
+ "bug-minus.png" = 'html/bug-minus.png',
+ "cross-circle.png" = 'html/cross-circle.png',
+ "hard-hat-exclamation.png" = 'html/hard-hat-exclamation.png',
+ "image-minus.png" = 'html/image-minus.png',
+ "image-plus.png" = 'html/image-plus.png',
+ "map-pencil.png" = 'html/map-pencil.png',
+ "music-minus.png" = 'html/music-minus.png',
+ "music-plus.png" = 'html/music-plus.png',
+ "tick-circle.png" = 'html/tick-circle.png',
+ "wrench-screwdriver.png" = 'html/wrench-screwdriver.png',
+ "spell-check.png" = 'html/spell-check.png',
+ "burn-exclamation.png" = 'html/burn-exclamation.png',
+ "chevron.png" = 'html/chevron.png',
+ "chevron-expand.png" = 'html/chevron-expand.png',
+ "changelog.css" = 'html/changelog.css',
+ "changelog.js" = 'html/changelog.js',
+ "changelog.html" = 'html/changelog.html'
+ )
+
+// /datum/asset/group/goonchat
+// children = list(
+// /datum/asset/simple/jquery,
+// /datum/asset/simple/goonchat,
+// /datum/asset/spritesheet/goonchat,
+// /datum/asset/simple/fontawesome
+// )
+
+// /datum/asset/simple/jquery
+// assets = list(
+// "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',
+// "browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js',
+// "browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
+// "browserOutput_white.css" = 'code/modules/goonchat/browserassets/css/browserOutput_white.css',
+// )
+
+/datum/asset/simple/fontawesome
+ assets = list(
+ "fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
+ "fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
+ "fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot',
+ "fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff',
+ "font-awesome.css" = 'html/font-awesome/css/all.min.css',
+ "v4shim.css" = 'html/font-awesome/css/v4-shims.min.css'
+ )
+
+// /datum/asset/spritesheet/goonchat
+// name = "chat"
+
+// /datum/asset/spritesheet/goonchat/register()
+// InsertAll("emoji", 'icons/emoji.dmi')
+
+// // pre-loading all lanugage icons also helps to avoid meta
+// InsertAll("language", 'icons/misc/language.dmi')
+// // catch languages which are pulling icons from another file
+// for(var/path in typesof(/datum/language))
+// var/datum/language/L = path
+// var/icon = initial(L.icon)
+// if (icon != 'icons/misc/language.dmi')
+// var/icon_state = initial(L.icon_state)
+// Insert("language-[icon_state]", icon, icon_state=icon_state)
+
+// ..()
+
+// /datum/asset/simple/permissions
+// assets = list(
+// "padlock.png" = 'html/padlock.png'
+// )
+
+// /datum/asset/simple/notes
+// assets = list(
+// "high_button.png" = 'html/high_button.png',
+// "medium_button.png" = 'html/medium_button.png',
+// "minor_button.png" = 'html/minor_button.png',
+// "none_button.png" = 'html/none_button.png',
+// )
+
+// /datum/asset/simple/arcade
+// assets = list(
+// "boss1.gif" = 'icons/UI_Icons/Arcade/boss1.gif',
+// "boss2.gif" = 'icons/UI_Icons/Arcade/boss2.gif',
+// "boss3.gif" = 'icons/UI_Icons/Arcade/boss3.gif',
+// "boss4.gif" = 'icons/UI_Icons/Arcade/boss4.gif',
+// "boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif',
+// "boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif',
+// )
+
+// /datum/asset/spritesheet/simple/achievements
+// name ="achievements"
+// assets = list(
+// "default" = 'icons/UI_Icons/Achievements/default.png',
+// "basemisc" = 'icons/UI_Icons/Achievements/basemisc.png',
+// "baseboss" = 'icons/UI_Icons/Achievements/baseboss.png',
+// "baseskill" = 'icons/UI_Icons/Achievements/baseskill.png',
+// "bbgum" = 'icons/UI_Icons/Achievements/Boss/bbgum.png',
+// "colossus" = 'icons/UI_Icons/Achievements/Boss/colossus.png',
+// "hierophant" = 'icons/UI_Icons/Achievements/Boss/hierophant.png',
+// "legion" = 'icons/UI_Icons/Achievements/Boss/legion.png',
+// "miner" = 'icons/UI_Icons/Achievements/Boss/miner.png',
+// "swarmer" = 'icons/UI_Icons/Achievements/Boss/swarmer.png',
+// "tendril" = 'icons/UI_Icons/Achievements/Boss/tendril.png',
+// "featofstrength" = 'icons/UI_Icons/Achievements/Misc/featofstrength.png',
+// "helbital" = 'icons/UI_Icons/Achievements/Misc/helbital.png',
+// "jackpot" = 'icons/UI_Icons/Achievements/Misc/jackpot.png',
+// "meteors" = 'icons/UI_Icons/Achievements/Misc/meteors.png',
+// "timewaste" = 'icons/UI_Icons/Achievements/Misc/timewaste.png',
+// "upgrade" = 'icons/UI_Icons/Achievements/Misc/upgrade.png',
+// "clownking" = 'icons/UI_Icons/Achievements/Misc/clownking.png',
+// "clownthanks" = 'icons/UI_Icons/Achievements/Misc/clownthanks.png',
+// "rule8" = 'icons/UI_Icons/Achievements/Misc/rule8.png',
+// "snail" = 'icons/UI_Icons/Achievements/Misc/snail.png',
+// "mining" = 'icons/UI_Icons/Achievements/Skills/mining.png',
+// )
+
+// /datum/asset/spritesheet/simple/pills
+// name ="pills"
+// assets = list(
+// "pill1" = 'icons/UI_Icons/Pills/pill1.png',
+// "pill2" = 'icons/UI_Icons/Pills/pill2.png',
+// "pill3" = 'icons/UI_Icons/Pills/pill3.png',
+// "pill4" = 'icons/UI_Icons/Pills/pill4.png',
+// "pill5" = 'icons/UI_Icons/Pills/pill5.png',
+// "pill6" = 'icons/UI_Icons/Pills/pill6.png',
+// "pill7" = 'icons/UI_Icons/Pills/pill7.png',
+// "pill8" = 'icons/UI_Icons/Pills/pill8.png',
+// "pill9" = 'icons/UI_Icons/Pills/pill9.png',
+// "pill10" = 'icons/UI_Icons/Pills/pill10.png',
+// "pill11" = 'icons/UI_Icons/Pills/pill11.png',
+// "pill12" = 'icons/UI_Icons/Pills/pill12.png',
+// "pill13" = 'icons/UI_Icons/Pills/pill13.png',
+// "pill14" = 'icons/UI_Icons/Pills/pill14.png',
+// "pill15" = 'icons/UI_Icons/Pills/pill15.png',
+// "pill16" = 'icons/UI_Icons/Pills/pill16.png',
+// "pill17" = 'icons/UI_Icons/Pills/pill17.png',
+// "pill18" = 'icons/UI_Icons/Pills/pill18.png',
+// "pill19" = 'icons/UI_Icons/Pills/pill19.png',
+// "pill20" = 'icons/UI_Icons/Pills/pill20.png',
+// "pill21" = 'icons/UI_Icons/Pills/pill21.png',
+// "pill22" = 'icons/UI_Icons/Pills/pill22.png',
+// )
+
+// //this exists purely to avoid meta by pre-loading all language icons.
+// /datum/asset/language/register()
+// for(var/path in typesof(/datum/language))
+// set waitfor = FALSE
+// var/datum/language/L = new path ()
+// L.get_icon()
+
+/datum/asset/spritesheet/pipes
+ name = "pipes"
+
+/datum/asset/spritesheet/pipes/register()
+ for(var/each in list('icons/obj/pipe-item.dmi', 'icons/obj/pipes/disposal.dmi'))
+ InsertAll("", each, global.alldirs)
+ ..()
+
+// // Representative icons for each research design
+// /datum/asset/spritesheet/research_designs
+// name = "design"
+
+// /datum/asset/spritesheet/research_designs/register()
+// for (var/path in subtypesof(/datum/design))
+// var/datum/design/D = path
+
+// var/icon_file
+// var/icon_state
+// var/icon/I
+
+// if(initial(D.research_icon) && initial(D.research_icon_state)) //If the design has an icon replacement skip the rest
+// icon_file = initial(D.research_icon)
+// icon_state = initial(D.research_icon_state)
+// if(!(icon_state in icon_states(icon_file)))
+// warning("design [D] with icon '[icon_file]' missing state '[icon_state]'")
+// continue
+// I = icon(icon_file, icon_state, SOUTH)
+
+// else
+// // construct the icon and slap it into the resource cache
+// var/atom/item = initial(D.build_path)
+// if (!ispath(item, /atom))
+// // biogenerator outputs to beakers by default
+// if (initial(D.build_type) & BIOGENERATOR)
+// item = /obj/item/reagent_containers/glass/beaker/large
+// else
+// continue // shouldn't happen, but just in case
+
+// // circuit boards become their resulting machines or computers
+// if (ispath(item, /obj/item/circuitboard))
+// var/obj/item/circuitboard/C = item
+// var/machine = initial(C.build_path)
+// if (machine)
+// item = machine
+
+// icon_file = initial(item.icon)
+// icon_state = initial(item.icon_state)
+
+// if(!(icon_state in icon_states(icon_file)))
+// warning("design [D] with icon '[icon_file]' missing state '[icon_state]'")
+// continue
+// I = icon(icon_file, icon_state, SOUTH)
+
+// // computers (and snowflakes) get their screen and keyboard sprites
+// if (ispath(item, /obj/machinery/computer) || ispath(item, /obj/machinery/power/solar_control))
+// var/obj/machinery/computer/C = item
+// var/screen = initial(C.icon_screen)
+// var/keyboard = initial(C.icon_keyboard)
+// var/all_states = icon_states(icon_file)
+// if (screen && (screen in all_states))
+// I.Blend(icon(icon_file, screen, SOUTH), ICON_OVERLAY)
+// if (keyboard && (keyboard in all_states))
+// I.Blend(icon(icon_file, keyboard, SOUTH), ICON_OVERLAY)
+
+// Insert(initial(D.id), I)
+// return ..()
+
+// /datum/asset/spritesheet/vending
+// name = "vending"
+
+// /datum/asset/spritesheet/vending/register()
+// for (var/k in GLOB.vending_products)
+// var/atom/item = k
+// if (!ispath(item, /atom))
+// continue
+
+// var/icon_file = initial(item.icon)
+// var/icon_state = initial(item.icon_state)
+// var/icon/I
+
+// var/icon_states_list = icon_states(icon_file)
+// if(icon_state in icon_states_list)
+// I = icon(icon_file, icon_state, SOUTH)
+// var/c = initial(item.color)
+// if (!isnull(c) && c != "#FFFFFF")
+// I.Blend(c, ICON_MULTIPLY)
+// else
+// var/icon_states_string
+// for (var/an_icon_state in icon_states_list)
+// if (!icon_states_string)
+// icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])"
+// else
+// icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])"
+// stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]")
+// I = icon('icons/turf/floors.dmi', "", SOUTH)
+
+// var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-")
+
+// Insert(imgid, I)
+// return ..()
+
+// /datum/asset/simple/genetics
+// assets = list(
+// "dna_discovered.gif" = 'html/dna_discovered.gif',
+// "dna_undiscovered.gif" = 'html/dna_undiscovered.gif',
+// "dna_extra.gif" = 'html/dna_extra.gif'
+// )
+
+// /datum/asset/simple/orbit
+// assets = list(
+// "ghost.png" = 'html/ghost.png'
+// )
+
+// /datum/asset/simple/vv
+// assets = list(
+// "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")
+// ..()
+
+/datum/asset/nanoui
+ var/list/common = list()
+
+ var/list/common_dirs = list(
+ "nano/css/",
+ "nano/images/",
+ "nano/images/modular_computers/",
+ "nano/js/"
+ )
+ var/list/template_dirs = list(
+ "nano/templates/"
+ )
+
+/datum/asset/nanoui/register()
+ // Crawl the directories to find files.
+ for(var/path in common_dirs)
+ var/list/filenames = flist(path)
+ for(var/filename in filenames)
+ if(copytext(filename, length(filename)) != "/") // Ignore directories.
+ if(fexists(path + filename))
+ common[filename] = fcopy_rsc(path + filename)
+ register_asset(filename, common[filename])
+ // Combine all templates into a single bundle.
+ var/list/template_data = list()
+ for(var/path in template_dirs)
+ var/list/filenames = flist(path)
+ for(var/filename in filenames)
+ if(copytext(filename, length(filename) - 4) == ".tmpl") // Ignore directories.
+ template_data[filename] = file2text(path + filename)
+ var/template_bundle = "function nanouiTemplateBundle(){return [json_encode(template_data)];}"
+ var/fname = "data/nano_templates_bundle.js"
+ fdel(fname)
+ text2file(template_bundle, fname)
+ register_asset("nano_templates_bundle.js", fcopy_rsc(fname))
+ fdel(fname)
+
+/datum/asset/nanoui/send(client)
+ send_asset_list(client, common)
diff --git a/code/modules/asset_cache/validate_assets.html b/code/modules/asset_cache/validate_assets.html
new file mode 100644
index 00000000000..b27a266c00d
--- /dev/null
+++ b/code/modules/asset_cache/validate_assets.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
deleted file mode 100644
index 2317cab19ee..00000000000
--- a/code/modules/client/asset_cache.dm
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
-Asset cache quick users guide:
-
-Make a datum at the bottom of this file with your assets for your thing.
-The simple subsystem will most like be of use for most cases.
-Then call get_asset_datum() with the type of the datum you created and store the return
-Then call .send(client) on that stored return value.
-
-You can set verify to TRUE if you want send() to sleep until the client has the assets.
-*/
-
-
-// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping.
-// This is doubled for the first asset, then added per asset after
-#define ASSET_CACHE_SEND_TIMEOUT 7
-
-//When sending mutiple assets, how many before we give the client a quaint little sending resources message
-#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
-
-//When passively preloading assets, how many to send at once? Too high creates noticable lag where as too low can flood the client's cache with "verify" files
-#define ASSET_CACHE_PRELOAD_CONCURRENT 3
-
-/client
- var/list/cache = list() // List of all assets sent to this client by the asset cache.
- var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement.
- var/list/sending = list()
- var/last_asset_job = 0 // Last job done.
-
-//This proc sends the asset to the client, but only if it needs it.
-//This proc blocks(sleeps) unless verify is set to false
-/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE)
- client = CLIENT_FROM_VAR(client) // Will get client from a mob, or accept a client, or return null
- if(!istype(client))
- return 0
-
- if(client.cache.Find(asset_name) || client.sending.Find(asset_name))
- return 0
-
- client << browse_rsc(SSassets.cache[asset_name], asset_name)
- if(!verify) // Can't access the asset cache browser, rip.
- client.cache += asset_name
- return 1
-
- client.sending |= asset_name
- var/job = ++client.last_asset_job
-
- client << browse({"
-
- "}, "window=asset_cache_browser")
-
- var/t = 0
- var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT
- while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
- sleep(1) // Lock up the caller until this is received.
- t++
-
- if(client)
- client.sending -= asset_name
- client.cache |= asset_name
- client.completed_asset_jobs -= job
-
- return 1
-
-//This proc blocks(sleeps) unless verify is set to false
-/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE)
- client = CLIENT_FROM_VAR(client) // Will get client from a mob, or accept a client, or return null
- if(!istype(client))
- return 0
-
- var/list/unreceived = asset_list - (client.cache + client.sending)
- if(!unreceived || !unreceived.len)
- return 0
- if(unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
- to_chat(client, "Sending Resources...")
- for(var/asset in unreceived)
- if(asset in SSassets.cache)
- client << browse_rsc(SSassets.cache[asset], asset)
-
- if(!verify) // Can't access the asset cache browser, rip.
- client.cache += unreceived
- return 1
-
- client.sending |= unreceived
- var/job = ++client.last_asset_job
-
- client << browse({"
-
- "}, "window=asset_cache_browser")
-
- var/t = 0
- var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len
- while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
- sleep(1) // Lock up the caller until this is received.
- t++
-
- if(client)
- client.sending -= unreceived
- client.cache |= unreceived
- client.completed_asset_jobs -= job
-
- return 1
-
-//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
-//The proc calls procs that sleep for long times.
-/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
- var/concurrent_tracker = 1
- for(var/file in files)
- if(!client)
- break
- if(register_asset)
- register_asset(file, files[file])
- if(concurrent_tracker >= ASSET_CACHE_PRELOAD_CONCURRENT)
- concurrent_tracker = 1
- send_asset(client, file)
- else
- concurrent_tracker++
- send_asset(client, file, verify = FALSE)
- sleep(0) //queuing calls like this too quickly can cause issues in some client versions
-
-//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
-//if it's an icon or something be careful, you'll have to copy it before further use.
-/proc/register_asset(var/asset_name, var/asset)
- SSassets.cache[asset_name] = asset
-
-//These datums are used to populate the asset cache, the proc "register()" does this.
-
-//all of our asset datums, used for referring to these later
-/var/global/list/asset_datums = list()
-
-//get a assetdatum or make a new one
-/proc/get_asset_datum(var/type)
- if(!(type in asset_datums))
- return new type()
- return asset_datums[type]
-
-/datum/asset
- var/_abstract = /datum/asset // Marker so we don't instanatiate abstract types
-
-/datum/asset/New()
- asset_datums[type] = src
- register()
-
-/datum/asset/proc/register()
- return
-
-/datum/asset/proc/send(client)
- return
-
-//If you don't need anything complicated.
-/datum/asset/simple
- _abstract = /datum/asset/simple
- var/assets = list()
- var/verify = FALSE
-
-/datum/asset/simple/register()
- for(var/asset_name in assets)
- register_asset(asset_name, assets[asset_name])
-/datum/asset/simple/send(client)
- send_asset_list(client,assets,verify)
-
-//
-// iconsheet Assets - For making lots of icon states available at once without sending a thousand tiny files.
-//
-/datum/asset/iconsheet
- _abstract = /datum/asset/iconsheet
- var/name // Name of the iconsheet. Asset will be named after this.
- var/verify = FALSE
-
-/datum/asset/iconsheet/register(var/list/sprites)
- if (!name)
- CRASH("iconsheet [type] cannot register without a name")
- if (!islist(sprites))
- CRASH("iconsheet [type] cannot register without a sprites list")
-
- var/res_name = "iconsheet_[name].css"
- var/fname = "data/iconsheets/[res_name]"
- fdel(fname)
- text2file(generate_css(sprites), fname)
- register_asset(res_name, fcopy_rsc(fname))
- fdel(fname)
-
-/datum/asset/iconsheet/send(client/C)
- if (!name)
- return
- send_asset_list(C, list("iconsheet_[name].css"), verify)
-
-/datum/asset/iconsheet/proc/generate_css(var/list/sprites)
- var/list/out = list(".[name]{display:inline-block;}")
- for(var/sprite_id in sprites)
- var/icon/I = sprites[sprite_id]
- var/data_url = "'data:image/png;base64,[icon2base64(I)]'"
- out += ".[name].[sprite_id]{width:[I.Width()]px;height:[I.Height()]px;background-image:url([data_url]);}"
- return out.Join("\n")
-
-/datum/asset/iconsheet/proc/build_sprite_list(icon/I, list/directions, prefix = null)
- if (length(prefix))
- prefix = "[prefix]-"
-
- if (!directions)
- directions = list(SOUTH)
-
- var/sprites = list()
- for (var/icon_state_name in cached_icon_states(I))
- for (var/direction in directions)
- var/suffix = (directions.len > 1) ? "-[dir2text(direction)]" : ""
- var/sprite_name = "[prefix][icon_state_name][suffix]"
- var/icon/sprite = icon(I, icon_state=icon_state_name, dir=direction, frame=1, moving=FALSE)
- if (!sprite || !length(cached_icon_states(sprite))) // that direction or state doesn't exist
- continue
- sprites[sprite_name] = sprite
- return sprites
-
-// Get HTML link tag for including the iconsheet css file.
-/datum/asset/iconsheet/proc/css_tag()
- return ""
-
-// get HTML tag for showing an icon
-/datum/asset/iconsheet/proc/icon_tag(icon_state, dir = SOUTH)
- return ""
-
-//DEFINITIONS FOR ASSET DATUMS START HERE.
-/datum/asset/simple/generic
- assets = list(
- "search.js" = 'html/search.js',
- "panels.css" = 'html/panels.css',
- "loading.gif" = 'html/images/loading.gif',
- "ntlogo.png" = 'html/images/ntlogo.png',
- "sglogo.png" = 'html/images/sglogo.png',
- "talisman.png" = 'html/images/talisman.png',
- "paper_bg.png" = 'html/images/paper_bg.png',
- "no_image32.png" = 'html/images/no_image32.png',
- )
-
-/datum/asset/simple/changelog
- assets = list(
- "88x31.png" = 'html/88x31.png',
- "bug-minus.png" = 'html/bug-minus.png',
- "cross-circle.png" = 'html/cross-circle.png',
- "hard-hat-exclamation.png" = 'html/hard-hat-exclamation.png',
- "image-minus.png" = 'html/image-minus.png',
- "image-plus.png" = 'html/image-plus.png',
- "map-pencil.png" = 'html/map-pencil.png',
- "music-minus.png" = 'html/music-minus.png',
- "music-plus.png" = 'html/music-plus.png',
- "tick-circle.png" = 'html/tick-circle.png',
- "wrench-screwdriver.png" = 'html/wrench-screwdriver.png',
- "spell-check.png" = 'html/spell-check.png',
- "burn-exclamation.png" = 'html/burn-exclamation.png',
- "chevron.png" = 'html/chevron.png',
- "chevron-expand.png" = 'html/chevron-expand.png',
- "changelog.css" = 'html/changelog.css',
- "changelog.js" = 'html/changelog.js',
- "changelog.html" = 'html/changelog.html'
- )
-
-/datum/asset/nanoui
- var/list/common = list()
-
- var/list/common_dirs = list(
- "nano/css/",
- "nano/images/",
- "nano/images/modular_computers/",
- "nano/js/"
- )
- var/list/template_dirs = list(
- "nano/templates/"
- )
-
-/datum/asset/nanoui/register()
- // Crawl the directories to find files.
- for(var/path in common_dirs)
- var/list/filenames = flist(path)
- for(var/filename in filenames)
- if(copytext(filename, length(filename)) != "/") // Ignore directories.
- if(fexists(path + filename))
- common[filename] = fcopy_rsc(path + filename)
- register_asset(filename, common[filename])
- // Combine all templates into a single bundle.
- var/list/template_data = list()
- for(var/path in template_dirs)
- var/list/filenames = flist(path)
- for(var/filename in filenames)
- if(copytext(filename, length(filename) - 4) == ".tmpl") // Ignore directories.
- template_data[filename] = file2text(path + filename)
- var/template_bundle = "function nanouiTemplateBundle(){return [json_encode(template_data)];}"
- var/fname = "data/nano_templates_bundle.js"
- fdel(fname)
- text2file(template_bundle, fname)
- register_asset("nano_templates_bundle.js", fcopy_rsc(fname))
- fdel(fname)
-
-/datum/asset/nanoui/send(client)
- send_asset_list(client, common)
-
-
-// VOREStation Add Start - pipes iconsheet asset
-/datum/asset/iconsheet/pipes
- name = "pipes"
-
-/datum/asset/iconsheet/pipes/register()
- var/list/sprites = list()
- for (var/each in list('icons/obj/pipe-item.dmi', 'icons/obj/pipes/disposal.dmi'))
- sprites += build_sprite_list(each, global.alldirs)
- ..(sprites)
-// VOREStation Add End
diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm
index b6b8d1b14d3..e11ff9e4f7a 100644
--- a/code/modules/client/client defines.dm
+++ b/code/modules/client/client defines.dm
@@ -59,3 +59,18 @@
preload_rsc = PRELOAD_RSC
var/global/obj/screen/click_catcher/void
+
+ // List of all asset filenames sent to this client by the asset cache, along with their assoicated md5s
+ var/list/sent_assets = list()
+ /// List of all completed blocking send jobs awaiting acknowledgement by send_asset
+ var/list/completed_asset_jobs = list()
+ /// Last asset send job id.
+ var/last_asset_job = 0
+ var/last_completed_asset_job = 0
+
+ ///world.time they connected
+ var/connection_time
+ ///world.realtime they connected
+ var/connection_realtime
+ ///world.timeofday they connected
+ var/connection_timeofday
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index 36f11e736f0..dd84f559820 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -34,10 +34,12 @@
#endif
+ // asset_cache
+ var/asset_cache_job
if(href_list["asset_cache_confirm_arrival"])
- var/job = text2num(href_list["asset_cache_confirm_arrival"])
- completed_asset_jobs += job
- return
+ asset_cache_job = asset_cache_confirm_arrival(href_list["asset_cache_confirm_arrival"])
+ if (!asset_cache_job)
+ return
//search the href for script injection
if( findtext(href,"\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/vchat/css/ss13styles.css b/code/modules/vchat/css/ss13styles.css
index 372c3ecf22a..98dcbc18e82 100644
--- a/code/modules/vchat/css/ss13styles.css
+++ b/code/modules/vchat/css/ss13styles.css
@@ -162,6 +162,7 @@ h1.alert, h2.alert {color: #000000;}
.vulpkanin {color: #B97A57;}
.enochian {color: #848A33; letter-spacing:-1pt; word-spacing:4pt; font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;}
.daemon {color: #5E339E; letter-spacing:-1pt; word-spacing:0pt; font-family: "Courier New", Courier, monospace;}
+.drudakar {color: #bb2463; word-spacing:0pt; font-family: "High Tower Text", monospace;}
.bug {color: #9e9e39;}
.vox {color: #AA00AA;}
.promethean {color: #5A5A5A; font-family:"Comic Sans MS","Comic Sans",cursive;}
diff --git a/code/modules/vchat/vchat_client.dm b/code/modules/vchat/vchat_client.dm
index 25486ac554e..4a2288faac8 100644
--- a/code/modules/vchat/vchat_client.dm
+++ b/code/modules/vchat/vchat_client.dm
@@ -407,6 +407,7 @@ var/to_chat_src
// Write the messages to the log
for(var/list/result in results)
o_file << "[result["message"]]
"
+ CHECK_TICK
o_file << "