Cyborgs now use storage datum (#90927)

This moves Cyborgs onto using storage datums, removing the remenants of
the shitcode that was Cyborg inventory. It's now done mostly by
equipping/unequipping/storage items, much like how other mobs do.
This allows borgs to take advantage of more hand support stuff and
things like ``dropped()``, so borgs no longer have to copy paste drop
code to ``cyborg_unequip``

It also:
- Removes ``CYBORG_ITEM_TRAIT``
- Removes all borg items being ``NODROP``

https://github.com/user-attachments/assets/11442a10-3443-41f2-8c72-b38fb0126cdb

Currently borgs are able to have their entire inventory open and a bag
below it, which I thought was a little weird. I always assumed they WERE
storage items, so I guess I'm doing it myself.
Cyborgs using storage code makes it easier for contributors to actually
do stuff with, without risking breaking everything. It also hopefully
will make borg items more resilient against breaking in the future, now
that we're not relying on nodrop.
Also just brings them more in line with other mobs, all of which make
use of storages.

🆑
refactor: Cyborg's modules now use storage (so opening a bag will close
modules instead of overlap one over the other).
qol: Observers can now see Cyborg's inventories (like they can for
humans).
/🆑

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
John Willard
2025-06-05 19:49:49 -04:00
committed by Roxy
co-authored by Ghom
parent 4ec2eefc19
commit 3c1505ac06
43 changed files with 390 additions and 427 deletions
+3 -4
View File
@@ -23,6 +23,8 @@
/// List of all the mobs currently viewing the contents of this storage.
VAR_PRIVATE/list/mob/is_using = list()
///The type of storage interface this datum uses.
var/datum/storage_interface/storage_type = /datum/storage_interface
/// Associated list that keeps track of all storage UI datums per person.
VAR_PRIVATE/list/datum/storage_interface/storage_interfaces = null
@@ -860,9 +862,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
/// Called directly from the attack chain if [insert_on_attack] is TRUE.
/// Handles inserting an item into the storage when clicked.
/datum/storage/proc/item_interact_insert(mob/living/user, obj/item/thing)
if(iscyborg(user))
return ITEM_INTERACT_BLOCKING
attempt_insert(thing, user)
return ITEM_INTERACT_SUCCESS
@@ -1041,7 +1040,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
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)
storage_interfaces[to_show] = new storage_type(ui_style, src, to_show)
orient_storage()
+91 -11
View File
@@ -13,25 +13,27 @@
/// Storage that owns us
var/datum/storage/parent_storage
/datum/storage_interface/New(ui_style, parent_storage)
/datum/storage_interface/New(ui_style, datum/storage/parent_storage, mob/user)
..()
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())
var/datum/hud/owner_hud = user.hud_used
closer = new(null, owner_hud, parent_storage)
cells = new(null, owner_hud, parent_storage)
corner_top_left = new(null, owner_hud, parent_storage)
corner_top_right = new(null, owner_hud, parent_storage)
corner_bottom_left = new(null, owner_hud, parent_storage)
corner_bottom_right = new(null, owner_hud, parent_storage)
rowjoin_left = new(null, owner_hud, parent_storage)
rowjoin_right = new(null, owner_hud, parent_storage)
for (var/atom/movable/screen/ui_elem as anything in list_ui_elements(initializing = TRUE))
ui_elem.icon = ui_style
/// Returns all UI elements under this theme
/datum/storage_interface/proc/list_ui_elements()
/datum/storage_interface/proc/list_ui_elements(initializing = FALSE)
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(closer)
QDEL_NULL(cells)
QDEL_NULL(corner_top_left)
QDEL_NULL(corner_top_right)
@@ -81,6 +83,20 @@
closer.screen_loc = "[screen_start_x + columns]:[screen_pixel_x - 5],[screen_start_y]:[screen_pixel_y]"
add_items(arglist(args))
/datum/storage_interface/proc/add_items(
screen_start_x,
screen_pixel_x,
screen_start_y,
screen_pixel_y,
columns,
rows,
mob/user_looking,
atom/real_location,
list/datum/numbered_display/numbered_contents,
)
var/current_x = screen_start_x
var/current_y = screen_start_y
var/turf/our_turf = get_turf(real_location)
@@ -107,3 +123,67 @@
current_y++
if(current_y - screen_start_y >= rows)
break
///Silicon subtype of storage interface used by their model storage.
/datum/storage_interface/silicon
var/atom/movable/screen/robot/store/store
var/obj/item/robot_model/robot_model
/datum/storage_interface/silicon/New(ui_style, datum/storage/parent_storage, mob/user)
. = ..()
robot_model = parent_storage.real_location
if(iscyborg(user))
store = new(null, user.hud_used)
/datum/storage_interface/silicon/Destroy(force)
QDEL_NULL(store)
return ..()
/datum/storage_interface/silicon/list_ui_elements(initializing = FALSE)
if(initializing || isnull(store))
return ..()
//we're purposely excluding 'store' from having its icon changed from initialization.
return ..() + store
/datum/storage_interface/silicon/add_items(
screen_start_x,
screen_pixel_x,
screen_start_y,
screen_pixel_y,
columns,
rows,
mob/user_looking,
atom/real_location,
list/datum/numbered_display/numbered_contents,
)
var/list/usable_modules = robot_model.get_usable_modules()
var/current_x = screen_start_x
var/current_y = screen_start_y
var/turf/our_turf = get_turf(real_location)
for(var/i in 1 to length(usable_modules))
var/atom/movable/item = usable_modules[i]
if(item in robot_model.robot.held_items)
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
//Module is currently active
continue
item.mouse_opacity = MOUSE_OPACITY_OPAQUE
SET_PLANE(item, ABOVE_HUD_PLANE, our_turf)
item.screen_loc = "[current_x]:[screen_pixel_x],[current_y]:[screen_pixel_y]"
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
+50
View File
@@ -0,0 +1,50 @@
/datum/storage/cyborg_internal_storage
allow_big_nesting = TRUE
max_slots = 99
max_specific_storage = WEIGHT_CLASS_GIGANTIC
max_total_storage = 99
do_rustle = FALSE
silent = TRUE
screen_max_columns = 8
storage_type = /datum/storage_interface/silicon
/datum/storage/cyborg_internal_storage/can_insert(obj/item/to_insert, mob/living/silicon/robot/user, messages = TRUE, force = STORAGE_NOT_LOCKED)
return (to_insert in user.model.modules)
/datum/storage/cyborg_internal_storage/attempt_insert(obj/item/to_insert, mob/living/silicon/robot/user, override = FALSE, force = STORAGE_NOT_LOCKED, messages = TRUE)
user.deactivate_module(to_insert)
/**
* Cyborg internal storage orienting
* We're using the model's total amount of modules as reference for how many slots we fill,
* otherwise having enough items in hand and only 1 item in a row would mean, as we're a static inventory,
* the rows won't fill for all items in the UI.
* We also don't give an additional row if all slots are filled because as a static inventory borgs don't need extra space
* to put items in, you can click on the slot you took it out from, or use the dedicated "store" button.
*/
/datum/storage/cyborg_internal_storage/orient_storage()
var/obj/item/robot_model/model = real_location
var/adjusted_contents = length(model.modules)
var/list/datum/numbered_display/numbered_contents
if(numerical_stacking)
numbered_contents = process_numerical_display()
adjusted_contents = length(numbered_contents)
var/columns = clamp(max_slots, 1, screen_max_columns)
var/rows = clamp(CEILING(adjusted_contents / columns, 1), 1, screen_max_rows)
for (var/mob/ui_user as anything in storage_interfaces)
if (isnull(storage_interfaces[ui_user]))
continue
storage_interfaces[ui_user].update_position(
screen_start_x,
screen_pixel_x,
screen_start_y,
screen_pixel_y,
columns,
rows,
ui_user,
real_location,
numbered_contents,
)