diff --git a/code/__HELPERS/screen_objs.dm b/code/__HELPERS/screen_objs.dm
index 7debecf816f..cb8520225ab 100644
--- a/code/__HELPERS/screen_objs.dm
+++ b/code/__HELPERS/screen_objs.dm
@@ -101,3 +101,15 @@
/proc/cut_relative_direction(fragment)
var/static/regex/regex = regex(@"([A-Z])\w+", "g")
return regex.Replace(fragment, "")
+
+/// Returns a screen_loc format for a tiling screen objects from start and end positions. Start should be bottom left corner, and end top right corner.
+/proc/spanning_screen_loc(start_px, start_py, end_px, end_py)
+ var/starting_tile_x = round(start_px / 32)
+ start_px -= starting_tile_x * 32
+ var/starting_tile_y = round(start_py/ 32)
+ start_py -= starting_tile_y * 32
+ var/ending_tile_x = round(end_px / 32)
+ end_px -= ending_tile_x * 32
+ var/ending_tile_y = round(end_py / 32)
+ end_py -= ending_tile_y * 32
+ return "[starting_tile_x]:[start_px],[starting_tile_y]:[start_py] to [ending_tile_x]:[end_px],[ending_tile_y]:[end_py]"
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 2fc1fe644ee..8cc29740870 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -294,7 +294,8 @@
/atom/movable/screen/close
name = "close"
plane = ABOVE_HUD_PLANE
- icon_state = "backpack_close"
+ icon = 'icons/hud/screen_midnight.dmi'
+ icon_state = "storage_close"
/atom/movable/screen/close/Initialize(mapload, datum/hud/hud_owner, new_master)
. = ..()
@@ -439,8 +440,8 @@
/atom/movable/screen/storage
name = "storage"
- icon_state = "block"
- screen_loc = "7,7 to 10,8"
+ icon = 'icons/hud/screen_midnight.dmi'
+ icon_state = "storage_cell"
plane = HUD_PLANE
/atom/movable/screen/storage/Initialize(mapload, datum/hud/hud_owner, new_master)
@@ -465,6 +466,27 @@
return TRUE
+/atom/movable/screen/storage/corner
+ name = "storage"
+ icon_state = "storage_corner_topleft"
+
+/atom/movable/screen/storage/corner/top_right
+ icon_state = "storage_corner_topright"
+
+/atom/movable/screen/storage/corner/bottom_left
+ icon_state = "storage_corner_bottomleft"
+
+/atom/movable/screen/storage/corner/bottom_right
+ icon_state = "storage_corner_bottomright"
+
+/atom/movable/screen/storage/rowjoin
+ name = "storage"
+ icon_state = "storage_rowjoin_left"
+ alpha = 0
+
+/atom/movable/screen/storage/rowjoin/right
+ icon_state = "storage_rowjoin_right"
+
/atom/movable/screen/throw_catch
name = "throw/catch"
icon = 'icons/hud/screen_midnight.dmi'
diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm
index ecdb7aeb4d9..e04092481b2 100644
--- a/code/datums/storage/storage.dm
+++ b/code/datums/storage/storage.dm
@@ -4,6 +4,7 @@
* The parent and real_location variables are both weakrefs, so they must be resolved before they can be used.
* If you're looking to create custom storage type behaviors, check ../subtypes
*/
+
/datum/storage
/**
* A reference to the atom linked to this storage object
@@ -22,10 +23,8 @@
/// List of all the mobs currently viewing the contents of this storage.
VAR_PRIVATE/list/mob/is_using = list()
- /// The storage display screen object.
- VAR_PRIVATE/atom/movable/screen/storage/boxes
- /// The 'close button' screen object.
- VAR_PRIVATE/atom/movable/screen/close/closer
+ /// Associated list that keeps track of all storage UI datums per person.
+ VAR_PRIVATE/list/datum/storage_interface/storage_interfaces = null
/// Typecache of items that can be inserted into this storage.
/// By default, all item types can be inserted (assuming other conditions are met).
@@ -131,9 +130,6 @@
qdel(src)
return
- boxes = new(null, null, src)
- closer = new(null, null, src)
-
set_parent(parent)
set_real_location(parent)
@@ -141,22 +137,15 @@
src.max_specific_storage = max_specific_storage
src.max_total_storage = max_total_storage
- orient_to_hud()
-
/datum/storage/Destroy()
parent = null
real_location = null
for(var/mob/person in is_using)
- if(person.active_storage == src)
- person.active_storage = null
- person.client?.screen -= boxes
- person.client?.screen -= closer
-
- QDEL_NULL(boxes)
- QDEL_NULL(closer)
+ hide_contents(person)
is_using.Cut()
+ QDEL_LAZYLIST(storage_interfaces)
return ..()
@@ -863,69 +852,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
return toreturn
-/// Updates the storage UI to fit all objects inside storage.
-/datum/storage/proc/orient_to_hud()
- var/adjusted_contents = real_location.contents.len
-
- //Numbered contents display
- var/list/datum/numbered_display/numbered_contents
- if(numerical_stacking)
- numbered_contents = process_numerical_display()
- adjusted_contents = numbered_contents.len
- //if the ammount of contents reaches some multiplier of the final column (and its not the last slot), let the player view an additional row
- var/additional_row = (!(adjusted_contents % screen_max_columns) && adjusted_contents < max_slots)
-
- var/columns = clamp(max_slots, 1, screen_max_columns)
- var/rows = clamp(CEILING(adjusted_contents / columns, 1) + additional_row, 1, screen_max_rows)
-
- orient_item_boxes(rows, columns, numbered_contents)
-
-/// Generates the actual UI objects, their location, and alignments whenever we open storage up.
-/datum/storage/proc/orient_item_boxes(rows, cols, list/obj/item/numerical_display_contents)
- boxes.screen_loc = "[screen_start_x]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x+cols-1]:[screen_pixel_x],[screen_start_y+rows-1]:[screen_pixel_y]"
- var/current_x = screen_start_x
- var/current_y = screen_start_y
- var/turf/our_turf = get_turf(real_location)
-
- if(islist(numerical_display_contents))
- for(var/type in numerical_display_contents)
- var/datum/numbered_display/numberdisplay = numerical_display_contents[type]
-
- var/obj/item/display_sample = numberdisplay.sample_object
- display_sample.mouse_opacity = MOUSE_OPACITY_OPAQUE
- display_sample.screen_loc = "[current_x]:[screen_pixel_x],[current_y]:[screen_pixel_y]"
- display_sample.maptext = MAPTEXT("[(numberdisplay.number > 1)? "[numberdisplay.number]" : ""]")
- SET_PLANE(display_sample, ABOVE_HUD_PLANE, our_turf)
-
- current_x++
-
- if(current_x - screen_start_x >= cols)
- current_x = screen_start_x
- current_y++
-
- if(current_y - screen_start_y >= rows)
- break
-
- else
- for(var/obj/item in real_location)
- item.mouse_opacity = MOUSE_OPACITY_OPAQUE
- item.screen_loc = "[current_x]:[screen_pixel_x],[current_y]:[screen_pixel_y]"
- item.maptext = ""
- item.plane = ABOVE_HUD_PLANE
- SET_PLANE(item, ABOVE_HUD_PLANE, our_turf)
-
- current_x++
-
- if(current_x - screen_start_x >= cols)
- current_x = screen_start_x
- current_y++
-
- if(current_y - screen_start_y >= rows)
- break
-
- closer.screen_loc = "[screen_start_x + cols]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y]"
-
-
/// Signal handler for when we get attacked with secondary click by an item.
/datum/storage/proc/on_item_interact_secondary(datum/source, mob/user, atom/weapon)
SIGNAL_HANDLER
@@ -999,10 +925,10 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
/// Async version of putting something into a mobs hand.
-/datum/storage/proc/put_in_hands_async(mob/toshow, obj/item/toremove)
- if(!toshow.put_in_hands(toremove))
+/datum/storage/proc/put_in_hands_async(mob/to_show, obj/item/toremove)
+ if(!to_show.put_in_hands(toremove))
if(!silent)
- toremove.balloon_alert(toshow, "fumbled!")
+ toremove.balloon_alert(to_show, "fumbled!")
return TRUE
/// Signal handler for whenever a mob walks away with us, close if they can't reach us.
@@ -1037,48 +963,55 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
* Show our storage to a mob.
*
* Arguments
- * * mob/toshow - the mob to show the storage to
+ * * mob/to_show - the mob to show the storage to
*
* Returns
* * FALSE if the show failed
* * TRUE otherwise
*/
-/datum/storage/proc/show_contents(mob/toshow)
- if(!toshow.client)
+/datum/storage/proc/show_contents(mob/to_show)
+ if(!to_show.client)
return FALSE
// You can only inspect hidden contents if you're an observer
- if(!isobserver(toshow) && !display_contents)
+ if(!isobserver(to_show) && !display_contents)
return FALSE
- if(toshow.active_storage != src && (toshow.stat == CONSCIOUS))
+ if(to_show.active_storage != src && (to_show.stat == CONSCIOUS))
for(var/obj/item/thing in real_location)
- if(thing.on_found(toshow))
- toshow.active_storage.hide_contents(toshow)
+ if(thing.on_found(to_show))
+ to_show.active_storage.hide_contents(to_show)
- if(toshow.active_storage)
- toshow.active_storage.hide_contents(toshow)
+ if(to_show.active_storage)
+ to_show.active_storage.hide_contents(to_show)
- toshow.active_storage = src
+ to_show.active_storage = src
if(ismovable(real_location))
var/atom/movable/movable_loc = real_location
movable_loc.become_active_storage(src)
- orient_to_hud()
+ LAZYINITLIST(storage_interfaces)
- is_using |= toshow
+ var/ui_style = ui_style2icon(to_show.client?.prefs?.read_preference(/datum/preference/choiced/ui_style))
+
+ if (isnull(storage_interfaces[to_show]))
+ storage_interfaces[to_show] = new /datum/storage_interface(ui_style, src)
+
+ orient_storage()
+
+ is_using |= to_show
+
+ to_show.client.screen |= storage_interfaces[to_show].list_ui_elements()
+ to_show.client.screen |= real_location.contents
- toshow.client.screen |= boxes
- toshow.client.screen |= closer
- toshow.client.screen |= real_location.contents
return TRUE
/**
* Hide our storage from a mob.
*
* Arguments
- * * mob/toshow - the mob to hide the storage from
+ * * mob/to_hide - the mob to hide the storage from
*/
/datum/storage/proc/hide_contents(mob/to_hide)
if(!to_hide.client)
@@ -1090,13 +1023,18 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
var/atom/movable/movable_loc = real_location
movable_loc.lose_active_storage(src)
+ if (isnull(storage_interfaces[to_hide]))
+ return TRUE
+
is_using -= to_hide
- to_hide.client.screen -= boxes
- to_hide.client.screen -= closer
+ to_hide.client.screen -= storage_interfaces[to_hide].list_ui_elements()
to_hide.client.screen -= real_location.contents
+ QDEL_NULL(storage_interfaces[to_hide])
+
return TRUE
+
/datum/storage/proc/action_trigger(datum/source, datum/action/triggered)
SIGNAL_HANDLER
@@ -1107,10 +1045,54 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
modeswitch_action = null
+/// Updates views of all objects in storage and stretches UI to appropriate size
+/datum/storage/proc/orient_storage()
+ var/adjusted_contents = length(real_location.contents)
+ var/list/datum/numbered_display/numbered_contents
+ if(numerical_stacking)
+ numbered_contents = process_numerical_display()
+ adjusted_contents = length(numbered_contents)
+
+ //if the ammount of contents reaches some multiplier of the final column (and its not the last slot), let the player view an additional row
+ var/additional_row = (!(adjusted_contents % screen_max_columns) && adjusted_contents < max_slots)
+
+ var/columns = clamp(max_slots, 1, screen_max_columns)
+ var/rows = clamp(CEILING(adjusted_contents / columns, 1) + additional_row, 1, screen_max_rows)
+
+ for (var/ui_user in storage_interfaces)
+ storage_interfaces[ui_user].update_position(screen_start_x, screen_pixel_x, screen_start_y, screen_pixel_y, columns, rows)
+
+ var/current_x = screen_start_x
+ var/current_y = screen_start_y
+ var/turf/our_turf = get_turf(real_location)
+
+ var/list/obj/storage_contents = list()
+ if (islist(numbered_contents))
+ for(var/content_type in numbered_contents)
+ var/datum/numbered_display/numberdisplay = numbered_contents[content_type]
+ storage_contents[numberdisplay.sample_object] = MAPTEXT("[(numberdisplay.number > 1)? "[numberdisplay.number]" : ""]")
+ else
+ for(var/obj/item as anything in real_location)
+ storage_contents[item] = ""
+
+ for(var/obj/item as anything in storage_contents)
+ item.mouse_opacity = MOUSE_OPACITY_OPAQUE
+ item.screen_loc = "[current_x]:[screen_pixel_x],[current_y]:[screen_pixel_y]"
+ item.maptext = storage_contents[item]
+ SET_PLANE(item, ABOVE_HUD_PLANE, our_turf)
+ current_x++
+ if(current_x - screen_start_x < columns)
+ continue
+ current_x = screen_start_x
+
+ current_y++
+ if(current_y - screen_start_y >= rows)
+ break
+
/**
* Toggles the collectmode of our storage.
*
- * @param mob/toshow the mob toggling us
+ * @param mob/to_show the mob toggling us
*/
/datum/storage/proc/toggle_collection_mode(mob/user)
collection_mode = (collection_mode + 1) % 3
diff --git a/code/datums/storage/storage_interface.dm b/code/datums/storage/storage_interface.dm
new file mode 100644
index 00000000000..df6bdc9f2d1
--- /dev/null
+++ b/code/datums/storage/storage_interface.dm
@@ -0,0 +1,67 @@
+/// Datum tracker for storage UI
+/datum/storage_interface
+ /// UI elements for this theme
+ var/atom/movable/screen/close/closer
+ var/atom/movable/screen/storage/cells
+ var/atom/movable/screen/storage/corner/corner_top_left
+ var/atom/movable/screen/storage/corner/top_right/corner_top_right
+ var/atom/movable/screen/storage/corner/bottom_left/corner_bottom_left
+ var/atom/movable/screen/storage/corner/bottom_right/corner_bottom_right
+ var/atom/movable/screen/storage/rowjoin/rowjoin_left
+ var/atom/movable/screen/storage/rowjoin/right/rowjoin_right
+
+ /// Storage that owns us
+ var/datum/storage/parent_storage
+
+/datum/storage_interface/New(ui_style, parent_storage)
+ ..()
+ src.parent_storage = parent_storage
+ closer = new(null, null, parent_storage)
+ cells = new(null, null, parent_storage)
+ corner_top_left = new(null, null, parent_storage)
+ corner_top_right = new(null, null, parent_storage)
+ corner_bottom_left = new(null, null, parent_storage)
+ corner_bottom_right = new(null, null, parent_storage)
+ rowjoin_left = new(null, null, parent_storage)
+ rowjoin_right = new(null, null, parent_storage)
+ for (var/atom/movable/screen/ui_elem as anything in list_ui_elements())
+ ui_elem.icon = ui_style
+
+/// Returns all UI elements under this theme
+/datum/storage_interface/proc/list_ui_elements()
+ return list(cells, corner_top_left, corner_top_right, corner_bottom_left, corner_bottom_right, rowjoin_left, rowjoin_right, closer)
+
+/datum/storage_interface/Destroy(force)
+ QDEL_NULL(cells)
+ QDEL_NULL(corner_top_left)
+ QDEL_NULL(corner_top_right)
+ QDEL_NULL(corner_bottom_left)
+ QDEL_NULL(corner_bottom_right)
+ QDEL_NULL(rowjoin_left)
+ QDEL_NULL(rowjoin_right)
+ parent_storage = null
+ return ..()
+
+/// Updates position of all UI elements
+/datum/storage_interface/proc/update_position(screen_start_x, screen_pixel_x, screen_start_y, screen_pixel_y, columns, rows)
+ var/start_pixel_x = screen_start_x * 32 + screen_pixel_x
+ var/start_pixel_y = screen_start_y * 32 + screen_pixel_y
+ var/end_pixel_x = start_pixel_x + (columns - 1) * 32
+ var/end_pixel_y = start_pixel_y + (rows - 1) * 32
+
+ cells.screen_loc = spanning_screen_loc(start_pixel_x, start_pixel_y, end_pixel_x, end_pixel_y)
+ var/left_edge_loc = spanning_screen_loc(start_pixel_x + 32, start_pixel_y, end_pixel_x, end_pixel_y)
+ var/right_edge_loc = spanning_screen_loc(start_pixel_x, start_pixel_y, end_pixel_x + max(0, (columns - 2)) * 32, end_pixel_y)
+ corner_top_left.screen_loc = left_edge_loc
+ corner_top_right.screen_loc = right_edge_loc
+ corner_bottom_left.screen_loc = left_edge_loc
+ corner_bottom_right.screen_loc = right_edge_loc
+
+ var/row_loc = spanning_screen_loc(start_pixel_x, start_pixel_y + 27, end_pixel_x, end_pixel_y + 27 + max(0, rows - 2) * 32)
+ rowjoin_left.screen_loc = row_loc
+ rowjoin_left.alpha = (rows > 1) * 255
+
+ rowjoin_right.screen_loc = row_loc
+ rowjoin_right.alpha = (rows > 1) * 255
+
+ closer.screen_loc = "[screen_start_x + columns]:[screen_pixel_x - 5],[screen_start_y]:[screen_pixel_y]"
diff --git a/icons/hud/screen_clockwork.dmi b/icons/hud/screen_clockwork.dmi
index 0923e42e7e4..17e0c92972e 100644
Binary files a/icons/hud/screen_clockwork.dmi and b/icons/hud/screen_clockwork.dmi differ
diff --git a/icons/hud/screen_detective.dmi b/icons/hud/screen_detective.dmi
index aed6e0d6572..9704ca96f4c 100644
Binary files a/icons/hud/screen_detective.dmi and b/icons/hud/screen_detective.dmi differ
diff --git a/icons/hud/screen_glass.dmi b/icons/hud/screen_glass.dmi
index 63ad3293921..6b6d9d515c5 100644
Binary files a/icons/hud/screen_glass.dmi and b/icons/hud/screen_glass.dmi differ
diff --git a/icons/hud/screen_midnight.dmi b/icons/hud/screen_midnight.dmi
index 5483ddf4564..8a6f2e1e8e0 100644
Binary files a/icons/hud/screen_midnight.dmi and b/icons/hud/screen_midnight.dmi differ
diff --git a/icons/hud/screen_operative.dmi b/icons/hud/screen_operative.dmi
index f2d60d394ac..73afee5b3ca 100644
Binary files a/icons/hud/screen_operative.dmi and b/icons/hud/screen_operative.dmi differ
diff --git a/icons/hud/screen_plasmafire.dmi b/icons/hud/screen_plasmafire.dmi
index 5423d3855b2..b0b2c7999c9 100644
Binary files a/icons/hud/screen_plasmafire.dmi and b/icons/hud/screen_plasmafire.dmi differ
diff --git a/icons/hud/screen_retro.dmi b/icons/hud/screen_retro.dmi
index b4252109d68..a0d5abf3be5 100644
Binary files a/icons/hud/screen_retro.dmi and b/icons/hud/screen_retro.dmi differ
diff --git a/icons/hud/screen_slimecore.dmi b/icons/hud/screen_slimecore.dmi
index a75fe55c378..84a7abec2be 100644
Binary files a/icons/hud/screen_slimecore.dmi and b/icons/hud/screen_slimecore.dmi differ
diff --git a/icons/hud/screen_trasenknox.dmi b/icons/hud/screen_trasenknox.dmi
index 2569d2a635e..00ad01258a3 100644
Binary files a/icons/hud/screen_trasenknox.dmi and b/icons/hud/screen_trasenknox.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 94849010765..b84349811ad 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1857,6 +1857,7 @@
#include "code\datums\status_effects\debuffs\slime\slime_leech.dm"
#include "code\datums\status_effects\debuffs\slime\slimed.dm"
#include "code\datums\storage\storage.dm"
+#include "code\datums\storage\storage_interface.dm"
#include "code\datums\storage\subtypes\backpack.dm"
#include "code\datums\storage\subtypes\bag_of_holding.dm"
#include "code\datums\storage\subtypes\cards.dm"