diff --git a/code/__DEFINES/dcs/signals/signals_storage.dm b/code/__DEFINES/dcs/signals/signals_storage.dm
deleted file mode 100644
index 72bddfba280..00000000000
--- a/code/__DEFINES/dcs/signals/signals_storage.dm
+++ /dev/null
@@ -1,32 +0,0 @@
-// /datum/component/storage signals
-///() - returns bool.
-#define COMSIG_CONTAINS_STORAGE "is_storage"
-///(obj/item/inserting, mob/user, silent, force) - returns bool
-#define COMSIG_TRY_STORAGE_INSERT "storage_try_insert"
-///(mob/show_to, force) - returns bool.
-#define COMSIG_TRY_STORAGE_SHOW "storage_show_to"
-///(mob/hide_from) - returns bool
-#define COMSIG_TRY_STORAGE_HIDE_FROM "storage_hide_from"
-///returns bool
-#define COMSIG_TRY_STORAGE_HIDE_ALL "storage_hide_all"
-///(newstate)
-#define COMSIG_TRY_STORAGE_SET_LOCKSTATE "storage_lock_set_state"
-///() - returns bool. MUST CHECK IF STORAGE IS THERE FIRST!
-#define COMSIG_IS_STORAGE_LOCKED "storage_get_lockstate"
-///(type, atom/destination, amount = INFINITY, check_adjacent, force, mob/user, list/inserted) - returns bool - type can be a list of types.
-#define COMSIG_TRY_STORAGE_TAKE_TYPE "storage_take_type"
-///(type, amount = INFINITY, force = FALSE). Force will ignore max_items, and amount is normally clamped to max_items.
-#define COMSIG_TRY_STORAGE_FILL_TYPE "storage_fill_type"
-///(obj, new_loc, force = FALSE) - returns bool
-#define COMSIG_TRY_STORAGE_TAKE "storage_take_obj"
-///(loc) - returns bool - if loc is null it will dump at parent location.
-#define COMSIG_TRY_STORAGE_QUICK_EMPTY "storage_quick_empty"
-///(list/list_to_inject_results_into, recursively_search_inside_storages = TRUE)
-#define COMSIG_TRY_STORAGE_RETURN_INVENTORY "storage_return_inventory"
-///(obj/item/insertion_candidate, mob/user, silent) - returns bool
-#define COMSIG_TRY_STORAGE_CAN_INSERT "storage_can_equip"
-
-//from base of atom/movable/on_enter_storage(): (datum/component/storage/concrete/master_storage)
-#define COMSIG_STORAGE_ENTERED "storage_entered"
-//from base of atom/movable/on_exit_storage(): (datum/component/storage/concrete/master_storage)
-#define COMSIG_STORAGE_EXITED "storage_exited"
diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index fa4c59612d3..c596b9bb5c5 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -58,6 +58,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define IS_PLAYER_COLORABLE_1 (1<<21)
/// Whether or not this atom has contextual screentips when hovered OVER
#define HAS_CONTEXTUAL_SCREENTIPS_1 (1<<22)
+// Whether or not this atom is storing contents for a disassociated storage object
+#define HAS_DISASSOCIATED_STORAGE_1 (1<<23)
// Update flags for [/atom/proc/update_appearance]
/// Update the atom's name
diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm
index 7ad02ab4525..a18ac4fb395 100644
--- a/code/__DEFINES/inventory.dm
+++ b/code/__DEFINES/inventory.dm
@@ -2,15 +2,15 @@
//ITEM INVENTORY WEIGHT, FOR w_class
/// Usually items smaller then a human hand, (e.g. playing cards, lighter, scalpel, coins/holochips)
-#define WEIGHT_CLASS_TINY 1
+#define WEIGHT_CLASS_TINY 1
/// Pockets can hold small and tiny items, (e.g. flashlight, multitool, grenades, GPS device)
-#define WEIGHT_CLASS_SMALL 2
+#define WEIGHT_CLASS_SMALL 2
/// Standard backpacks can carry tiny, small & normal items, (e.g. fire extinguisher, stun baton, gas mask, metal sheets)
-#define WEIGHT_CLASS_NORMAL 3
+#define WEIGHT_CLASS_NORMAL 3
/// Items that can be weilded or equipped but not stored in an inventory, (e.g. defibrillator, backpack, space suits)
-#define WEIGHT_CLASS_BULKY 4
+#define WEIGHT_CLASS_BULKY 4
/// Usually represents objects that require two hands to operate, (e.g. shotgun, two-handed melee weapons)
-#define WEIGHT_CLASS_HUGE 5
+#define WEIGHT_CLASS_HUGE 5
/// Essentially means it cannot be picked up or placed in an inventory, (e.g. mech parts, safe)
#define WEIGHT_CLASS_GIGANTIC 6
diff --git a/code/__HELPERS/atoms.dm b/code/__HELPERS/atoms.dm
index ebb704678b8..861f9475208 100644
--- a/code/__HELPERS/atoms.dm
+++ b/code/__HELPERS/atoms.dm
@@ -319,11 +319,7 @@ rough example of the "cone" made by the 3 dirs checked
. = list()
if(!istype(target) || !(target.item_flags & IN_STORAGE))
return
- var/datum/component/storage/concrete/storage_datum = target.loc.GetComponent(/datum/component/storage/concrete)
+ var/datum/storage/storage_datum = target.loc.atom_storage
if(!storage_datum)
return
. += storage_datum.parent
- for(var/datum/component/storage/slave as anything in storage_datum.slaves)
- if(!isatom(slave.parent))
- continue
- . += slave.parent
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 79067e9666d..80d8b5bb631 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -116,6 +116,7 @@ DEFINE_BITFIELD(flags_1, list(
"CAN_BE_DIRTY_1" = CAN_BE_DIRTY_1,
"CONDUCT_1" = CONDUCT_1,
"HAS_CONTEXTUAL_SCREENTIPS_1" = HAS_CONTEXTUAL_SCREENTIPS_1,
+ "HAS_DISASSOCIATED_STORAGE_1" = HAS_DISASSOCIATED_STORAGE_1,
"HOLOGRAM_1" = HOLOGRAM_1,
"INITIALIZED_1" = INITIALIZED_1,
"IS_ONTOP_1" = IS_ONTOP_1,
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 5c8ca3cd6cb..6accd544551 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -153,6 +153,12 @@
if(!loc.AllowClick())
return
+ // In a storage item with a disassociated storage parent
+ var/obj/item/item_atom = A
+ if(istype(item_atom))
+ if((item_atom.item_flags & IN_STORAGE) && (item_atom.loc.flags_1 & HAS_DISASSOCIATED_STORAGE_1))
+ UnarmedAttack(item_atom, TRUE, modifiers)
+
//Standard reach turf to turf or reaching inside storage
if(CanReach(A,W))
if(W)
@@ -206,6 +212,7 @@
var/list/closed = list()
var/list/checking = list(ultimate_target)
+
while (checking.len && depth > 0)
var/list/next = list()
--depth
@@ -214,7 +221,7 @@
if(closed[target] || isarea(target)) // avoid infinity situations
continue
- if(isturf(target) || isturf(target.loc) || (target in direct_access) || (ismovable(target) && target.flags_1 & IS_ONTOP_1)) //Directly accessible atoms
+ if(isturf(target) || isturf(target.loc) || (target in direct_access) || (ismovable(target) && target.flags_1 & IS_ONTOP_1) || target.loc.atom_storage) //Directly accessible atoms
if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks
return TRUE
@@ -223,8 +230,7 @@
if (!target.loc)
continue
- //Storage and things with reachable internal atoms need add to next here. Or return COMPONENT_ALLOW_REACH.
- if(SEND_SIGNAL(target.loc, COMSIG_ATOM_CANREACH, next) & COMPONENT_ALLOW_REACH)
+ if(target.loc.atom_storage)
next += target.loc
checking = next
@@ -285,6 +291,7 @@
* modifiers is a lazy list of click modifiers this attack had,
* used for figuring out different properties of the click, mostly right vs left and such.
*/
+
/mob/proc/UnarmedAttack(atom/A, proximity_flag, list/modifiers)
if(ismob(A))
changeNext_move(CLICK_CD_MELEE)
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index c39f4d28836..85755575b7b 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -247,8 +247,8 @@
master = new_master
/atom/movable/screen/close/Click()
- var/datum/component/storage/S = master
- S.hide_from(usr)
+ var/datum/storage/storage = master
+ storage.hide_contents(usr)
return TRUE
/atom/movable/screen/drop
@@ -453,7 +453,7 @@
return TRUE
if(usr.incapacitated())
return TRUE
- if (ismecha(usr.loc)) // stops inventory actions in a mech
+ if(ismecha(usr.loc)) // stops inventory actions in a mech
return TRUE
if(master)
var/obj/item/I = usr.get_active_held_item()
diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm
index 58b65127f1d..39cb6dd1cdd 100644
--- a/code/datums/components/crafting/crafting.dm
+++ b/code/datums/components/crafting/crafting.dm
@@ -161,7 +161,7 @@
var/list/present_qualities = list()
for(var/obj/item/contained_item in source.contents)
- if(contained_item.GetComponent(/datum/component/storage))
+ if(contained_item.atom_storage)
for(var/obj/item/subcontained_item in contained_item.contents)
available_tools[subcontained_item.type] = TRUE
if(subcontained_item.tool_behaviour)
diff --git a/code/datums/components/explodable.dm b/code/datums/components/explodable.dm
index 5059eb3ebd1..a91f849846c 100644
--- a/code/datums/components/explodable.dm
+++ b/code/datums/components/explodable.dm
@@ -34,8 +34,10 @@
if(isclothing(parent))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop)
- if(SEND_SIGNAL(parent, COMSIG_CONTAINS_STORAGE))
- RegisterSignal(parent, COMSIG_TRY_STORAGE_INSERT, .proc/explodable_insert_item)
+
+ var/atom/atom_parent = parent
+ if(atom_parent.atom_storage)
+ RegisterSignal(parent, COMSIG_ATOM_ENTERED, .proc/explodable_insert_item)
if (devastation_range)
src.devastation_range = devastation_range
@@ -51,9 +53,11 @@
src.delete_after = delete_after
/// Explode if our parent is a storage place and something with high heat is inserted in.
-/datum/component/explodable/proc/explodable_insert_item(datum/source, obj/item/I, mob/M, silent = FALSE, force = FALSE)
+/datum/component/explodable/proc/explodable_insert_item(datum/source, obj/item/I)
SIGNAL_HANDLER
-
+ if(!(I.item_flags & IN_STORAGE))
+ return
+
check_if_detonate(I)
/datum/component/explodable/proc/explodable_impact(datum/source, atom/hit_atom, datum/thrownthing/throwingdatum)
diff --git a/code/datums/components/food/decomposition.dm b/code/datums/components/food/decomposition.dm
index 3ee6d6191fc..8a233cfb86b 100644
--- a/code/datums/components/food/decomposition.dm
+++ b/code/datums/components/food/decomposition.dm
@@ -36,11 +36,11 @@
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/handle_movement)
RegisterSignal(parent, list(
COMSIG_ITEM_PICKUP, //person picks up an item
- COMSIG_STORAGE_ENTERED), //Object enters a storage object (boxes, etc.)
+ COMSIG_ATOM_ENTERED), //Object enters a storage object (boxes, etc.)
.proc/picked_up)
RegisterSignal(parent, list(
COMSIG_ITEM_DROPPED, //Object is dropped anywhere
- COMSIG_STORAGE_EXITED), //Object exits a storage object (boxes, etc)
+ COMSIG_ATOM_EXITED), //Object exits a storage object (boxes, etc)
.proc/dropped)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examine)
@@ -59,10 +59,10 @@
/datum/component/decomposition/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ITEM_PICKUP,
- COMSIG_STORAGE_ENTERED,
+ COMSIG_ATOM_ENTERED,
COMSIG_MOVABLE_MOVED,
COMSIG_ITEM_DROPPED,
- COMSIG_STORAGE_EXITED,
+ COMSIG_ATOM_EXITED,
COMSIG_PARENT_EXAMINE))
/datum/component/decomposition/proc/handle_movement()
diff --git a/code/datums/components/storage/food_storage.dm b/code/datums/components/food_storage.dm
similarity index 100%
rename from code/datums/components/storage/food_storage.dm
rename to code/datums/components/food_storage.dm
diff --git a/code/datums/components/holderloving.dm b/code/datums/components/holderloving.dm
index 03dcb1da2d9..e5e25dbb34f 100644
--- a/code/datums/components/holderloving.dm
+++ b/code/datums/components/holderloving.dm
@@ -35,8 +35,8 @@
RegisterSignal(parent, list(
COMSIG_ITEM_DROPPED,
COMSIG_ITEM_EQUIPPED,
- COMSIG_STORAGE_ENTERED,
- COMSIG_STORAGE_EXITED,
+ COMSIG_ATOM_ENTERED,
+ COMSIG_ATOM_EXITED,
), .proc/check_my_loc)
/datum/component/holderloving/UnregisterFromParent()
@@ -44,8 +44,8 @@
UnregisterSignal(parent, list(
COMSIG_ITEM_DROPPED,
COMSIG_ITEM_EQUIPPED,
- COMSIG_STORAGE_ENTERED,
- COMSIG_STORAGE_EXITED,
+ COMSIG_ATOM_ENTERED,
+ COMSIG_ATOM_EXITED,
))
/datum/component/holderloving/PostTransfer()
diff --git a/code/datums/components/storage/concrete/_concrete.dm b/code/datums/components/storage/concrete/_concrete.dm
deleted file mode 100644
index b78bebba07c..00000000000
--- a/code/datums/components/storage/concrete/_concrete.dm
+++ /dev/null
@@ -1,204 +0,0 @@
-
-// External storage-related logic:
-// /mob/proc/ClickOn() in /_onclick/click.dm - clicking items in storages
-// /mob/living/Move() in /modules/mob/living/living.dm - hiding storage boxes on mob movement
-
-/datum/component/storage/concrete
- can_transfer = TRUE
- var/drop_all_on_deconstruct = TRUE
- var/drop_all_on_destroy = FALSE
- var/transfer_contents_on_component_transfer = FALSE
- var/list/datum/component/storage/slaves = list()
-
- var/list/_contents_limbo // Where objects go to live mid transfer
- var/list/_user_limbo // The last users before the component started moving
-
-/datum/component/storage/concrete/Initialize()
- . = ..()
- RegisterSignal(parent, COMSIG_ATOM_CONTENTS_DEL, .proc/on_contents_del)
- RegisterSignal(parent, COMSIG_OBJ_DECONSTRUCT, .proc/on_deconstruct)
-
-/datum/component/storage/concrete/Destroy()
- var/atom/real_location = real_location()
- for(var/atom/_A in real_location)
- _A.mouse_opacity = initial(_A.mouse_opacity)
- if(drop_all_on_destroy)
- do_quick_empty()
- for(var/i in slaves)
- var/datum/component/storage/slave = i
- slave.change_master(null)
- QDEL_LIST(_contents_limbo)
- _user_limbo = null
- return ..()
-
-/datum/component/storage/concrete/master()
- return src
-
-/datum/component/storage/concrete/real_location()
- return parent
-
-/datum/component/storage/concrete/PreTransfer()
- if(is_using)
- _user_limbo = is_using.Copy()
- close_all()
- if(transfer_contents_on_component_transfer)
- _contents_limbo = list()
- for(var/atom/movable/AM in parent)
- _contents_limbo += AM
- AM.moveToNullspace()
-
-/datum/component/storage/concrete/PostTransfer()
- if(!isatom(parent))
- return COMPONENT_INCOMPATIBLE
- if(transfer_contents_on_component_transfer)
- for(var/i in _contents_limbo)
- var/atom/movable/AM = i
- AM.forceMove(parent)
- _contents_limbo = null
- if(_user_limbo)
- for(var/i in _user_limbo)
- show_to(i)
- _user_limbo = null
-
-/datum/component/storage/concrete/_insert_physical_item(obj/item/I, override = FALSE)
- . = TRUE
- var/atom/real_location = real_location()
- if(I.loc != real_location)
- I.forceMove(real_location)
- refresh_mob_views()
-
-/datum/component/storage/concrete/refresh_mob_views()
- . = ..()
- for(var/i in slaves)
- var/datum/component/storage/slave = i
- slave.refresh_mob_views()
-
-/datum/component/storage/concrete/emp_act(datum/source, severity)
- if(emp_shielded)
- return
- var/atom/real_location = real_location()
- for(var/i in real_location)
- var/atom/A = i
- A.emp_act(severity)
-
-/datum/component/storage/concrete/proc/on_slave_link(datum/component/storage/S)
- if(S == src)
- return FALSE
- slaves += S
- return TRUE
-
-/datum/component/storage/concrete/proc/on_slave_unlink(datum/component/storage/S)
- slaves -= S
- return FALSE
-
-/datum/component/storage/concrete/proc/on_contents_del(datum/source, atom/A)
- SIGNAL_HANDLER
- var/atom/real_location = parent
- if(A in real_location)
- usr = null
- remove_from_storage(A, null)
-
-/datum/component/storage/concrete/proc/on_deconstruct(datum/source, disassembled)
- SIGNAL_HANDLER
- if(drop_all_on_deconstruct)
- do_quick_empty()
-
-/datum/component/storage/concrete/can_see_contents()
- . = ..()
- for(var/i in slaves)
- var/datum/component/storage/slave = i
- . |= slave.can_see_contents()
-
-//Resets screen loc and other vars of something being removed from storage.
-/datum/component/storage/concrete/_removal_reset(atom/movable/thing)
- thing.layer = initial(thing.layer)
- thing.plane = initial(thing.plane)
- thing.mouse_opacity = initial(thing.mouse_opacity)
- if(thing.maptext)
- thing.maptext = ""
-
-/datum/component/storage/concrete/remove_from_storage(atom/movable/AM, atom/new_location)
- //Cache this as it should be reusable down the bottom, will not apply if anyone adds a sleep to dropped
- //or moving objects, things that should never happen
- var/atom/parent = src.parent
- var/list/seeing_mobs = can_see_contents()
- for(var/mob/M in seeing_mobs)
- M.client.screen -= AM
- if(isitem(AM))
- var/obj/item/removed_item = AM
- removed_item.item_flags &= ~IN_STORAGE
- if(ismob(parent.loc))
- var/mob/carrying_mob = parent.loc
- removed_item.dropped(carrying_mob, TRUE)
- if(new_location)
- //Reset the items values
- _removal_reset(AM)
- AM.forceMove(new_location)
- //We don't want to call this if the item is being destroyed
- AM.on_exit_storage(src)
- else
- //Being destroyed, just move to nullspace now (so it's not in contents for the icon update)
- AM.moveToNullspace()
- refresh_mob_views()
- if(isobj(parent))
- var/obj/O = parent
- O.update_appearance()
- return TRUE
-
-/datum/component/storage/concrete/proc/slave_can_insert_object(datum/component/storage/slave, obj/item/I, stop_messages = FALSE, mob/M)
- return TRUE
-
-/datum/component/storage/concrete/proc/handle_item_insertion_from_slave(datum/component/storage/slave, obj/item/I, prevent_warning = FALSE, M)
- . = handle_item_insertion(I, prevent_warning, M, slave)
- if(. && !prevent_warning)
- slave.mob_item_insertion_feedback(usr, M, I)
-
-/datum/component/storage/concrete/handle_item_insertion(obj/item/I, prevent_warning = FALSE, mob/M, datum/component/storage/remote) //Remote is null or the slave datum
- var/datum/component/storage/concrete/master = master()
- var/atom/parent = src.parent
- var/moved = FALSE
- if(!istype(I))
- return FALSE
- if(M)
- if(!M.temporarilyRemoveItemFromInventory(I))
- return FALSE
- else
- moved = TRUE //At this point if the proc fails we need to manually move the object back to the turf/mob/whatever.
- if(I.pulledby)
- I.pulledby.stop_pulling()
- if(silent)
- prevent_warning = TRUE
- if(!_insert_physical_item(I))
- if(moved)
- if(M)
- if(!M.put_in_active_hand(I))
- I.forceMove(parent.drop_location())
- else
- I.forceMove(parent.drop_location())
- return FALSE
- I.on_enter_storage(master)
- I.item_flags |= IN_STORAGE
- refresh_mob_views()
- I.mouse_opacity = MOUSE_OPACITY_OPAQUE //So you can click on the area around the item to equip it, instead of having to pixel hunt
- if(M)
- if(M.client && M.active_storage != src)
- M.client.screen -= I
- if(M.observers && M.observers.len)
- for(var/i in M.observers)
- var/mob/dead/observe = i
- if(observe.client && observe.active_storage != src)
- observe.client.screen -= I
- if(!remote)
- parent.add_fingerprint(M)
- if(!prevent_warning)
- mob_item_insertion_feedback(usr, M, I)
- update_icon()
- return TRUE
-
-/datum/component/storage/concrete/update_icon()
- if(isobj(parent))
- var/obj/O = parent
- O.update_appearance()
- for(var/i in slaves)
- var/datum/component/storage/slave = i
- slave.update_icon()
diff --git a/code/datums/components/storage/concrete/bag_of_holding.dm b/code/datums/components/storage/concrete/bag_of_holding.dm
deleted file mode 100644
index 0b63707d3ad..00000000000
--- a/code/datums/components/storage/concrete/bag_of_holding.dm
+++ /dev/null
@@ -1,27 +0,0 @@
-/datum/component/storage/concrete/bluespace/bag_of_holding/handle_item_insertion(obj/item/W, prevent_warning = FALSE, mob/living/user)
- var/atom/A = parent
- if(A == W) //don't put yourself into yourself.
- return
- var/list/obj/item/storage/backpack/holding/matching = typecache_filter_list(W.get_all_contents(), typecacheof(/obj/item/storage/backpack/holding))
- matching -= A
- if(istype(W, /obj/item/storage/backpack/holding) || matching.len)
- INVOKE_ASYNC(src, .proc/recursive_insertion, W, user)
- return
- . = ..()
-
-/datum/component/storage/concrete/bluespace/bag_of_holding/proc/recursive_insertion(obj/item/W, mob/living/user)
- var/atom/A = parent
- var/safety = tgui_alert(user, "Doing this will have extremely dire consequences for the station and its crew. Be sure you know what you're doing.", "Put in [A.name]?", list("Proceed", "Abort"))
- if(safety != "Proceed" || QDELETED(A) || QDELETED(W) || QDELETED(user) || !user.canUseTopic(A, BE_CLOSE, iscarbon(user)))
- return
- var/turf/loccheck = get_turf(A)
- to_chat(user, span_danger("The Bluespace interfaces of the two devices catastrophically malfunction!"))
- qdel(W)
- playsound(loccheck,'sound/effects/supermatter.ogg', 200, TRUE)
-
- message_admins("[ADMIN_LOOKUPFLW(user)] detonated a bag of holding at [ADMIN_VERBOSEJMP(loccheck)].")
- log_game("[key_name(user)] detonated a bag of holding at [loc_name(loccheck)].")
-
- user.gib(TRUE, TRUE, TRUE)
- new/obj/boh_tear(loccheck)
- qdel(A)
diff --git a/code/datums/components/storage/concrete/bluespace.dm b/code/datums/components/storage/concrete/bluespace.dm
deleted file mode 100644
index 5c5b4f86d0f..00000000000
--- a/code/datums/components/storage/concrete/bluespace.dm
+++ /dev/null
@@ -1,22 +0,0 @@
-/datum/component/storage/concrete/bluespace
- var/dumping_range = 8
- var/dumping_sound = 'sound/items/pshoom.ogg'
- var/alt_sound = 'sound/items/pshoom_2.ogg'
-
-/datum/component/storage/concrete/bluespace/dump_content_at(atom/dest, mob/M)
- var/atom/A = parent
- if(A.Adjacent(M))
- var/atom/dumping_location = dest.get_dumping_location()
- var/turf/bagT = get_turf(parent)
- var/turf/destT = get_turf(dumping_location)
- if(destT && bagT && bagT.z == destT.z && get_dist(M, dumping_location) < dumping_range)
- if(dumping_location.storage_contents_dump_act(src, M))
- if(alt_sound && prob(1))
- playsound(src, alt_sound, 40, TRUE)
- else
- playsound(src, dumping_sound, 40, TRUE)
- M.Beam(dumping_location, icon_state="rped_upgrade", time=5)
- return TRUE
- to_chat(M, span_hear("The [A.name] buzzes."))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
- return FALSE
diff --git a/code/datums/components/storage/concrete/extract_inventory.dm b/code/datums/components/storage/concrete/extract_inventory.dm
deleted file mode 100644
index ece6c638a48..00000000000
--- a/code/datums/components/storage/concrete/extract_inventory.dm
+++ /dev/null
@@ -1,37 +0,0 @@
-/datum/component/storage/concrete/extract_inventory
- max_combined_w_class = WEIGHT_CLASS_TINY * 3
- max_items = 3
- insert_preposition = "in"
-//These need to be false in order for the extract's food to be unextractable
-//from the inventory
- attack_hand_interact = FALSE
- quickdraw = FALSE
- can_transfer = FALSE
- drop_all_on_deconstruct = FALSE
- locked = TRUE //True in order to prevent messing with the inventory in any way other than the specified ways on reproductive.dm
- rustle_sound = FALSE
- silent = TRUE
- var/obj/item/slimecross/reproductive/parentSlimeExtract
-
-
-/datum/component/storage/concrete/extract_inventory/Initialize()
- . = ..()
- set_holdable(/obj/item/food/monkeycube)
- if(!istype(parent, /obj/item/slimecross/reproductive))
- return COMPONENT_INCOMPATIBLE
- parentSlimeExtract = parent
-
-
-/datum/component/storage/concrete/extract_inventory/proc/processCubes(obj/item/slimecross/reproductive/parentSlimeExtract, mob/user)
-
- if(length(parentSlimeExtract.contents) >= max_items)
- QDEL_LIST(parentSlimeExtract.contents)
- createExtracts(parentSlimeExtract,user)
-
-/datum/component/storage/concrete/extract_inventory/proc/createExtracts(obj/item/slimecross/reproductive/parentSlimeExtract, mob/user)
- var/cores = rand(1,4)
- playsound(parentSlimeExtract, 'sound/effects/splat.ogg', 40, TRUE)
- parentSlimeExtract.last_produce = world.time
- to_chat(user, span_notice("[parentSlimeExtract] briefly swells to a massive size, and expels [cores] extract[cores > 1 ? "s":""]!"))
- for(var/i in 1 to cores)
- new parentSlimeExtract.extract_type(parentSlimeExtract.drop_location())
diff --git a/code/datums/components/storage/concrete/fish_case.dm b/code/datums/components/storage/concrete/fish_case.dm
deleted file mode 100644
index 398956c77b4..00000000000
--- a/code/datums/components/storage/concrete/fish_case.dm
+++ /dev/null
@@ -1,4 +0,0 @@
-/datum/component/storage/concrete/fish_case
- max_items = 1
- can_hold_trait = TRAIT_FISH_CASE_COMPATIBILE
- can_hold_description = "fish and aquarium equipment"
diff --git a/code/datums/components/storage/concrete/implant.dm b/code/datums/components/storage/concrete/implant.dm
deleted file mode 100644
index 405b3098fe5..00000000000
--- a/code/datums/components/storage/concrete/implant.dm
+++ /dev/null
@@ -1,18 +0,0 @@
-/datum/component/storage/concrete/implant
- max_w_class = WEIGHT_CLASS_NORMAL
- max_combined_w_class = 6
- max_items = 2
- drop_all_on_destroy = TRUE
- drop_all_on_deconstruct = TRUE
- silent = TRUE
- allow_big_nesting = TRUE
-
-/datum/component/storage/concrete/implant/Initialize()
- . = ..()
- set_holdable(null, list(/obj/item/disk/nuclear))
-
-/datum/component/storage/concrete/implant/InheritComponent(datum/component/storage/concrete/implant/I, original)
- if(!istype(I))
- return ..()
- max_combined_w_class += I.max_combined_w_class
- max_items += I.max_items
diff --git a/code/datums/components/storage/concrete/rped.dm b/code/datums/components/storage/concrete/rped.dm
deleted file mode 100644
index a6ffb79e93a..00000000000
--- a/code/datums/components/storage/concrete/rped.dm
+++ /dev/null
@@ -1,33 +0,0 @@
-/datum/component/storage/concrete/rped
- collection_mode = COLLECT_EVERYTHING
- allow_quick_gather = TRUE
- allow_quick_empty = TRUE
- click_gather = TRUE
- max_w_class = WEIGHT_CLASS_NORMAL
- max_combined_w_class = 100
- max_items = 50
- display_numerical_stacking = TRUE
-
-/datum/component/storage/concrete/rped/can_be_inserted(obj/item/I, stop_messages, mob/M)
- . = ..()
- if(!I.get_part_rating())
- if (!stop_messages)
- to_chat(M, span_warning("[parent] only accepts machine parts!"))
- return FALSE
-
-/datum/component/storage/concrete/bluespace/rped
- collection_mode = COLLECT_EVERYTHING
- allow_quick_gather = TRUE
- allow_quick_empty = TRUE
- click_gather = TRUE
- max_w_class = WEIGHT_CLASS_BULKY // can fit vending refills
- max_combined_w_class = 800
- max_items = 400
- display_numerical_stacking = TRUE
-
-/datum/component/storage/concrete/bluespace/rped/can_be_inserted(obj/item/I, stop_messages, mob/M)
- . = ..()
- if(!I.get_part_rating())
- if (!stop_messages)
- to_chat(M, span_warning("[parent] only accepts machine parts!"))
- return FALSE
diff --git a/code/datums/components/storage/concrete/stack.dm b/code/datums/components/storage/concrete/stack.dm
deleted file mode 100644
index 1b09c7f53c7..00000000000
--- a/code/datums/components/storage/concrete/stack.dm
+++ /dev/null
@@ -1,68 +0,0 @@
-//Stack-only storage.
-/datum/component/storage/concrete/stack
- display_numerical_stacking = TRUE
- var/max_combined_stack_amount = 300
- max_w_class = WEIGHT_CLASS_NORMAL
- max_combined_w_class = WEIGHT_CLASS_NORMAL * 14
-
-/datum/component/storage/concrete/stack/proc/total_stack_amount()
- . = 0
- var/atom/real_location = real_location()
- for(var/i in real_location)
- var/obj/item/stack/S = i
- if(!istype(S))
- continue
- . += S.amount
-
-/datum/component/storage/concrete/stack/proc/remaining_space()
- return max(0, max_combined_stack_amount - total_stack_amount())
-
-//emptying procs do not need modification as stacks automatically merge.
-
-/datum/component/storage/concrete/stack/_insert_physical_item(obj/item/I, override = FALSE)
- if(!istype(I, /obj/item/stack))
- if(override)
- return ..()
- return FALSE
- var/atom/real_location = real_location()
- var/obj/item/stack/S = I
- var/can_insert = min(S.amount, remaining_space())
- if(!can_insert)
- return FALSE
- for(var/i in real_location) //combine.
- if(QDELETED(I))
- return
- var/obj/item/stack/_S = i
- if(!istype(_S))
- continue
- if(_S.merge_type == S.merge_type)
- _S.add(can_insert)
- S.use(can_insert, TRUE)
- return TRUE
- I = S.split_stack(null, can_insert)
- return ..()
-
-/datum/component/storage/concrete/stack/remove_from_storage(obj/item/I, atom/new_location)
- var/atom/real_location = real_location()
- var/obj/item/stack/S = I
- if(!istype(S))
- return ..()
- if(S.amount > S.max_amount)
- var/overrun = S.amount - S.max_amount
- S.amount = S.max_amount
- var/obj/item/stack/temp = new S.type(real_location, overrun)
- handle_item_insertion(temp)
- return ..(S, new_location)
-
-/datum/component/storage/concrete/stack/_process_numerical_display()
- var/atom/real_location = real_location()
- . = list()
- for(var/i in real_location)
- var/obj/item/stack/I = i
- if(!istype(I) || QDELETED(I)) //We're specialized stack storage, just ignore non stacks.
- continue
- if(!.[I.merge_type])
- .[I.merge_type] = new /datum/numbered_display(I, I.amount)
- else
- var/datum/numbered_display/ND = .[I.merge_type]
- ND.number += I.amount
diff --git a/code/datums/components/storage/concrete/trading_cards.dm b/code/datums/components/storage/concrete/trading_cards.dm
deleted file mode 100644
index 4c68685068c..00000000000
--- a/code/datums/components/storage/concrete/trading_cards.dm
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- *A storage component to be used on card piles, for use as hands/decks/discard piles. Don't use on something that's not a card pile!
- */
-/datum/component/storage/concrete/tcg
- display_numerical_stacking = FALSE
- max_w_class = WEIGHT_CLASS_TINY
- max_items = 30
- max_combined_w_class = WEIGHT_CLASS_TINY * 30
- ///The deck that the card pile is using for FAIR PLAY.
-
-/datum/component/storage/concrete/tcg/Initialize()
- . = ..()
- set_holdable(list(/obj/item/tcgcard))
-
-/datum/component/storage/concrete/tcg/PostTransfer()
- . = ..()
- handle_empty_deck()
-
-/datum/component/storage/concrete/tcg/remove_from_storage(atom/movable/AM, atom/new_location)
- . = ..()
- handle_empty_deck()
-
-/datum/component/storage/concrete/tcg/show_to(mob/M)
- . = ..()
- M.visible_message(span_notice("[M] starts to look through the contents of \the [parent]!"), \
- span_notice("You begin looking into the contents of \the [parent]!"))
-
-/datum/component/storage/concrete/tcg/close(mob/M)
- . = ..()
- var/list/card_contents = contents()
- var/obj/temp_parent = parent
- temp_parent.visible_message(span_notice("\the [parent] is shuffled after looking through it."))
- card_contents = shuffle(card_contents)
-
-/datum/component/storage/concrete/tcg/mass_remove_from_storage(atom/target, list/things, datum/progressbar/progress, trigger_on_found)
- . = ..()
- if(!things.len)
- qdel(parent)
-
-/datum/component/storage/concrete/tcg/proc/handle_empty_deck()
- var/list/contents = contents()
- //You can't have a deck of one card!
- if(contents.len == 1)
- var/obj/item/tcgcard_deck/deck = parent
- var/obj/item/tcgcard/card = contents[1]
- remove_from_storage(card, card.drop_location())
- card.flipped = deck.flipped
- card.update_icon_state()
- qdel(parent)
diff --git a/code/datums/components/storage/concrete/wallet.dm b/code/datums/components/storage/concrete/wallet.dm
deleted file mode 100644
index 3da70fb7294..00000000000
--- a/code/datums/components/storage/concrete/wallet.dm
+++ /dev/null
@@ -1,13 +0,0 @@
-/datum/component/storage/concrete/wallet/open_storage(mob/user)
- if(!isliving(user) || !user.CanReach(parent) || user.incapacitated())
- return FALSE
- if(locked)
- to_chat(user, span_warning("[parent] seems to be locked!"))
- return
-
- var/obj/item/storage/wallet/A = parent
- if(istype(A) && A.front_id && !issilicon(user) && !(A.item_flags & IN_STORAGE)) //if it's a wallet in storage seeing the full inventory is more useful
- var/obj/item/I = A.front_id
- INVOKE_ASYNC(src, .proc/attempt_put_in_hands, I, user)
- return TRUE
- return ..()
diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm
deleted file mode 100644
index 37dc4b0de7c..00000000000
--- a/code/datums/components/storage/storage.dm
+++ /dev/null
@@ -1,960 +0,0 @@
-// External storage-related logic:
-// /mob/proc/ClickOn() in /_onclick/click.dm - clicking items in storages
-// /mob/living/Move() in /modules/mob/living/living.dm - hiding storage boxes on mob movement
-
-/datum/component/storage
- dupe_mode = COMPONENT_DUPE_UNIQUE
- ///If not null, all actions act on master and this is just an access point.
- var/datum/component/storage/concrete/master
-
- ///if this is set, only items, and their children, will fit
- var/list/can_hold
- ///if this is set, items, and their children, won't fit
- var/list/cant_hold
- ///if set, these items will be the exception to the max size of object that can fit.
- var/list/exception_hold
- /// If set can only contain stuff with this single trait present.
- var/list/can_hold_trait
-
- var/can_hold_description
-
- ///lazy list of mobs looking at the contents of this storage.
- var/list/mob/is_using
-
- ///when locked nothing can see inside or use it.
- var/locked = FALSE
-
- ///max size of objects that will fit.
- var/max_w_class = WEIGHT_CLASS_SMALL
- ///max combined sizes of objects that will fit.
- var/max_combined_w_class = 14
- ///max number of objects that will fit.
- var/max_items = 7
-
- var/emp_shielded = FALSE
-
- ///whether this makes a message when things are put in.
- var/silent = FALSE
- ///whether this can be clicked on items to pick it up rather than the other way around.
- var/click_gather = FALSE
- ///play rustle sound on interact.
- var/rustle_sound = TRUE
- ///allow empty verb which allows dumping on the floor of everything inside quickly.
- var/allow_quick_empty = FALSE
- ///allow toggle mob verb which toggles collecting all items from a tile.
- var/allow_quick_gather = FALSE
-
- var/collection_mode = COLLECT_EVERYTHING
-
- ///you put things "in" a bag, but "on" a tray.
- var/insert_preposition = "in"
-
- ///stack things of the same type and show as a single object with a number.
- var/display_numerical_stacking = FALSE
-
- ///storage display object
- var/atom/movable/screen/storage/boxes
- ///close button object
- var/atom/movable/screen/close/closer
-
- ///allow storage objects of the same or greater size.
- var/allow_big_nesting = FALSE
-
- ///interact on attack hand.
- var/attack_hand_interact = TRUE
- ///altclick interact
- var/quickdraw = FALSE
-
- var/datum/weakref/modeswitch_action_ref
-
- //Screen variables: Do not mess with these vars unless you know what you're doing. They're not defines so storage that isn't in the same location can be supported in the future.
- var/screen_max_columns = 7 //These two determine maximum screen sizes.
- var/screen_max_rows = INFINITY
- var/screen_pixel_x = 16 //These two are pixel values for screen loc of boxes and closer
- var/screen_pixel_y = 16
- var/screen_start_x = 4 //These two are where the storage starts being rendered, screen_loc wise.
- var/screen_start_y = 2
- //End
-
-/datum/component/storage/Initialize(datum/component/storage/concrete/master)
- if(!isatom(parent))
- return COMPONENT_INCOMPATIBLE
- if(master)
- change_master(master)
- boxes = new(null, src)
- closer = new(null, src)
- orient2hud()
-
- RegisterSignal(parent, COMSIG_CONTAINS_STORAGE, .proc/on_check)
- RegisterSignal(parent, COMSIG_IS_STORAGE_LOCKED, .proc/check_locked)
- RegisterSignal(parent, COMSIG_TRY_STORAGE_SHOW, .proc/signal_show_attempt)
- RegisterSignal(parent, COMSIG_TRY_STORAGE_INSERT, .proc/signal_insertion_attempt)
- RegisterSignal(parent, COMSIG_TRY_STORAGE_CAN_INSERT, .proc/signal_can_insert)
- RegisterSignal(parent, COMSIG_TRY_STORAGE_TAKE_TYPE, .proc/signal_take_type)
- RegisterSignal(parent, COMSIG_TRY_STORAGE_FILL_TYPE, .proc/signal_fill_type)
- RegisterSignal(parent, COMSIG_TRY_STORAGE_SET_LOCKSTATE, .proc/set_locked)
- RegisterSignal(parent, COMSIG_TRY_STORAGE_TAKE, .proc/signal_take_obj)
- RegisterSignal(parent, COMSIG_TRY_STORAGE_QUICK_EMPTY, .proc/signal_quick_empty)
- RegisterSignal(parent, COMSIG_TRY_STORAGE_HIDE_FROM, .proc/signal_hide_attempt)
- RegisterSignal(parent, COMSIG_TRY_STORAGE_HIDE_ALL, .proc/close_all)
- RegisterSignal(parent, COMSIG_TRY_STORAGE_RETURN_INVENTORY, .proc/signal_return_inv)
-
- RegisterSignal(parent, COMSIG_TOPIC, .proc/topic_handle)
-
- RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/attackby)
-
- RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, .proc/on_attack_hand)
- RegisterSignal(parent, COMSIG_ATOM_ATTACK_PAW, .proc/on_attack_hand)
- RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/emp_act)
- RegisterSignal(parent, COMSIG_ATOM_ATTACK_GHOST, .proc/show_to_ghost)
- RegisterSignal(parent, COMSIG_ATOM_ENTERED, .proc/refresh_mob_views)
- RegisterSignal(parent, COMSIG_ATOM_EXITED, .proc/_remove_and_refresh)
- RegisterSignal(parent, COMSIG_ATOM_CANREACH, .proc/canreach_react)
-
- RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK, .proc/preattack_intercept)
- RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/attack_self)
- RegisterSignal(parent, COMSIG_ITEM_PICKUP, .proc/signal_on_pickup)
- RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/update_actions)
-
- RegisterSignal(parent, COMSIG_MOVABLE_POST_THROW, .proc/close_all)
- RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_move)
-
- RegisterSignal(parent, list(COMSIG_CLICK_ALT, COMSIG_ATOM_ATTACK_HAND_SECONDARY), .proc/on_open_storage_click)
- RegisterSignal(parent, COMSIG_PARENT_ATTACKBY_SECONDARY, .proc/on_open_storage_attackby)
- RegisterSignal(parent, COMSIG_MOUSEDROP_ONTO, .proc/mousedrop_onto)
- RegisterSignal(parent, COMSIG_MOUSEDROPPED_ONTO, .proc/mousedrop_receive)
-
- update_actions()
-
-/datum/component/storage/Destroy()
- close_all()
- QDEL_NULL(boxes)
- QDEL_NULL(closer)
- LAZYCLEARLIST(is_using)
- return ..()
-
-/datum/component/storage/PreTransfer()
- update_actions()
-
-/// Almost 100% of the time the lists passed into set_holdable are reused for each instance of the component
-/// Just fucking cache it 4head
-/// Yes I could generalize this, but I don't want anyone else using it. in fact, DO NOT COPY THIS
-/// If you find yourself needing this pattern, you're likely better off using static typecaches
-/// I'm not because I do not trust implementers of the storage component to use them, BUT
-/// IF I FIND YOU USING THIS PATTERN IN YOUR CODE I WILL BREAK YOU ACROSS MY KNEES
-/// ~Lemon
-GLOBAL_LIST_EMPTY(cached_storage_typecaches)
-
-/datum/component/storage/proc/set_holdable(list/can_hold_list, list/cant_hold_list)
- if(!islist(can_hold_list))
- can_hold_list = list(can_hold_list)
- if(!islist(cant_hold_list))
- cant_hold_list = list(cant_hold_list)
-
- can_hold_description = generate_hold_desc(can_hold_list)
- if (can_hold_list)
- var/unique_key = can_hold_list.Join("-")
- if(!GLOB.cached_storage_typecaches[unique_key])
- GLOB.cached_storage_typecaches[unique_key] = typecacheof(can_hold_list)
- can_hold = GLOB.cached_storage_typecaches[unique_key]
-
- if (cant_hold_list != null)
- var/unique_key = cant_hold_list.Join("-")
- if(!GLOB.cached_storage_typecaches[unique_key])
- GLOB.cached_storage_typecaches[unique_key] = typecacheof(cant_hold_list)
- cant_hold = GLOB.cached_storage_typecaches[unique_key]
-
-/datum/component/storage/proc/generate_hold_desc(can_hold_list)
- var/list/desc = list()
-
- for(var/valid_type in can_hold_list)
- var/obj/item/valid_item = valid_type
- desc += "\a [initial(valid_item.name)]"
-
- return "\n\t[span_notice("[desc.Join("\n\t")]")]"
-
-/datum/component/storage/proc/update_actions()
- SIGNAL_HANDLER
-
- if(!isitem(parent) || !allow_quick_gather)
- QDEL_NULL(modeswitch_action_ref)
- return
-
- var/datum/action/existing = modeswitch_action_ref?.resolve()
- if(!QDELETED(existing))
- return
-
- var/obj/item/item_parent = parent
- var/datum/action/modeswitch_action = item_parent.add_item_action(/datum/action/item_action/storage_gather_mode)
- RegisterSignal(modeswitch_action, COMSIG_ACTION_TRIGGER, .proc/action_trigger)
- modeswitch_action_ref = WEAKREF(modeswitch_action)
-
-/datum/component/storage/proc/change_master(datum/component/storage/concrete/new_master)
- if(new_master == src || (!isnull(new_master) && !istype(new_master)))
- return FALSE
- if(master)
- master.on_slave_unlink(src)
- master = new_master
- if(master)
- master.on_slave_link(src)
- return TRUE
-
-/datum/component/storage/proc/master()
- if(master == src)
- return //infinite loops yo.
- return master
-
-/datum/component/storage/proc/real_location()
- var/datum/component/storage/concrete/master = master()
- return master? master.real_location() : null
-
-/datum/component/storage/proc/canreach_react(datum/source, list/next)
- SIGNAL_HANDLER
-
- var/datum/component/storage/concrete/master = master()
- if(!master)
- return
- . = COMPONENT_ALLOW_REACH
- next += master.parent
- for(var/i in master.slaves)
- var/datum/component/storage/slave = i
- next += slave.parent
-
-/datum/component/storage/proc/on_move()
- SIGNAL_HANDLER
-
- var/atom/A = parent
- for(var/mob/living/L in can_see_contents())
- if(!L.CanReach(A))
- hide_from(L)
-
-/datum/component/storage/proc/attack_self(datum/source, mob/M)
- SIGNAL_HANDLER
-
- if(locked)
- to_chat(M, span_warning("[parent] seems to be locked!"))
- return FALSE
- if((M.get_active_held_item() == parent) && allow_quick_empty)
- INVOKE_ASYNC(src, .proc/quick_empty, M)
-
-/datum/component/storage/proc/preattack_intercept(datum/source, obj/O, mob/M, params)
- SIGNAL_HANDLER
-
- if(!isitem(O) || !click_gather || SEND_SIGNAL(O, COMSIG_CONTAINS_STORAGE))
- return FALSE
- . = COMPONENT_CANCEL_ATTACK_CHAIN
- if(locked)
- to_chat(M, span_warning("[parent] seems to be locked!"))
- return FALSE
- var/obj/item/I = O
- if(collection_mode == COLLECT_ONE)
- if(can_be_inserted(I, null, M))
- handle_item_insertion(I, null, M)
- return
- if(!isturf(I.loc))
- return
- INVOKE_ASYNC(src, .proc/async_preattack_intercept, I, M)
-
-///async functionality from preattack_intercept
-/datum/component/storage/proc/async_preattack_intercept(obj/item/attack_item, mob/pre_attack_mob)
- var/list/things = attack_item.loc.contents.Copy()
- if(collection_mode == COLLECT_SAME)
- things = typecache_filter_list(things, typecacheof(attack_item.type))
- var/len = length(things)
- if(!len)
- to_chat(pre_attack_mob, span_warning("You failed to pick up anything with [parent]!"))
- return
- var/datum/progressbar/progress = new(pre_attack_mob, len, attack_item.loc)
- var/list/rejections = list()
- while(do_after(pre_attack_mob, 1 SECONDS, parent, NONE, FALSE, CALLBACK(src, .proc/handle_mass_pickup, things, attack_item.loc, rejections, progress)))
- stoplag(1)
- progress.end_progress()
- to_chat(pre_attack_mob, span_notice("You put everything you could [insert_preposition] [parent]."))
-
-/datum/component/storage/proc/handle_mass_item_insertion(list/things, datum/component/storage/src_object, mob/user, datum/progressbar/progress)
- var/atom/source_real_location = src_object.real_location()
- for(var/obj/item/I in things)
- things -= I
- if(I.loc != source_real_location)
- continue
- if(user.active_storage != src_object)
- if(I.on_found(user))
- break
- if(can_be_inserted(I,FALSE,user))
- handle_item_insertion(I, TRUE, user)
- if (TICK_CHECK)
- progress.update(progress.goal - things.len)
- return TRUE
-
- progress.update(progress.goal - things.len)
- return FALSE
-
-/datum/component/storage/proc/handle_mass_pickup(list/things, atom/thing_loc, list/rejections, datum/progressbar/progress)
- var/atom/real_location = real_location()
- for(var/obj/item/I in things)
- things -= I
- if(I.loc != thing_loc)
- continue
- if(I.type in rejections) // To limit bag spamming: any given type only complains once
- continue
- if(!can_be_inserted(I, stop_messages = TRUE)) // Note can_be_inserted still makes noise when the answer is no
- if(real_location.contents.len >= max_items)
- break
- rejections += I.type // therefore full bags are still a little spammy
- continue
-
- handle_item_insertion(I, TRUE) //The TRUE stops the "You put the [parent] into [S]" insertion message from being displayed.
-
- if (TICK_CHECK)
- progress.update(progress.goal - things.len)
- return TRUE
-
- progress.update(progress.goal - things.len)
- return FALSE
-
-/datum/component/storage/proc/quick_empty(mob/M)
- var/atom/A = parent
- if(!M.canUseStorage() || !A.Adjacent(M) || M.incapacitated())
- return
- if(locked)
- to_chat(M, span_warning("[parent] seems to be locked!"))
- return FALSE
- A.add_fingerprint(M)
- to_chat(M, span_notice("You start dumping out [parent]."))
- var/turf/T = get_turf(A)
- var/list/things = contents()
- var/datum/progressbar/progress = new(M, length(things), T)
- while (do_after(M, 1 SECONDS, T, NONE, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress)))
- stoplag(1)
- progress.end_progress()
-
-/datum/component/storage/proc/mass_remove_from_storage(atom/target, list/things, datum/progressbar/progress, trigger_on_found = TRUE)
- var/atom/real_location = real_location()
- for(var/obj/item/I in things)
- things -= I
- if(I.loc != real_location)
- continue
- remove_from_storage(I, target)
- I.pixel_x = rand(-10,10)
- I.pixel_y = rand(-10,10)
- if(trigger_on_found && I.on_found())
- return FALSE
- if(TICK_CHECK)
- progress.update(progress.goal - length(things))
- return TRUE
- progress.update(progress.goal - length(things))
- return FALSE
-
-/datum/component/storage/proc/do_quick_empty(atom/_target)
- if(!_target)
- _target = get_turf(parent)
- if(usr)
- hide_from(usr)
- var/list/contents = contents()
- var/atom/real_location = real_location()
- for(var/obj/item/I in contents)
- if(I.loc != real_location)
- continue
- remove_from_storage(I, _target)
- return TRUE
-
-/datum/component/storage/proc/set_locked(datum/source, new_state)
- SIGNAL_HANDLER
-
- locked = new_state
- if(locked)
- close_all()
-
-/datum/component/storage/proc/_process_numerical_display()
- . = list()
- var/atom/real_location = real_location()
- for(var/obj/item/I in real_location.contents)
- if(QDELETED(I))
- continue
- if(!.["[I.type]-[I.name]"])
- .["[I.type]-[I.name]"] = new /datum/numbered_display(I, 1)
- else
- var/datum/numbered_display/ND = .["[I.type]-[I.name]"]
- ND.number++
-
-//This proc determines the size of the inventory to be displayed. Please touch it only if you know what you're doing.
-/datum/component/storage/proc/orient2hud()
- var/atom/real_location = real_location()
- var/adjusted_contents = real_location.contents.len
-
- //Numbered contents display
- var/list/datum/numbered_display/numbered_contents
- if(display_numerical_stacking)
- numbered_contents = _process_numerical_display()
- adjusted_contents = numbered_contents.len
-
- var/columns = clamp(max_items, 1, screen_max_columns)
- var/rows = clamp(CEILING(adjusted_contents / columns, 1), 1, screen_max_rows)
- standard_orient_objs(rows, columns, numbered_contents)
-
-//This proc draws out the inventory and places the items on it. It uses the standard position.
-/datum/component/storage/proc/standard_orient_objs(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/cx = screen_start_x
- var/cy = screen_start_y
- if(islist(numerical_display_contents))
- for(var/type in numerical_display_contents)
- var/datum/numbered_display/ND = numerical_display_contents[type]
- ND.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE
- ND.sample_object.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
- ND.sample_object.maptext = MAPTEXT("[(ND.number > 1)? "[ND.number]" : ""]")
- ND.sample_object.plane = ABOVE_HUD_PLANE
- cx++
- if(cx - screen_start_x >= cols)
- cx = screen_start_x
- cy++
- if(cy - screen_start_y >= rows)
- break
- else
- var/atom/real_location = real_location()
- for(var/obj/O in real_location)
- if(QDELETED(O))
- continue
- O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip"
- O.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
- O.maptext = ""
- O.plane = ABOVE_HUD_PLANE
- cx++
- if(cx - screen_start_x >= cols)
- cx = screen_start_x
- cy++
- if(cy - screen_start_y >= rows)
- break
- closer.screen_loc = "[screen_start_x + cols]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y]"
-
-/datum/component/storage/proc/show_to(mob/M)
- if(!M.client)
- return FALSE
- var/atom/real_location = real_location()
- if(M.active_storage != src && (M.stat == CONSCIOUS))
- for(var/obj/item/I in real_location)
- if(I.on_found(M))
- return FALSE
- if(M.active_storage)
- M.active_storage.hide_from(M)
- orient2hud()
- M.client.screen |= boxes
- M.client.screen |= closer
- M.client.screen |= real_location.contents
- M.set_active_storage(src)
- if(ismovable(real_location))
- var/atom/movable/movable_loc = real_location
- movable_loc.become_active_storage(src)
- LAZYOR(is_using, M)
- RegisterSignal(M, COMSIG_PARENT_QDELETING, .proc/mob_deleted)
- return TRUE
-
-/datum/component/storage/proc/mob_deleted(datum/source)
- SIGNAL_HANDLER
- hide_from(source)
-
-/datum/component/storage/proc/hide_from(mob/M)
- if(M.active_storage == src)
- M.set_active_storage(null)
- LAZYREMOVE(is_using, M)
-
- UnregisterSignal(M, COMSIG_PARENT_QDELETING)
- if(!M.client)
- return TRUE
- var/atom/real_location = real_location()
- if(!length(is_using) && ismovable(real_location))
- var/atom/movable/movable_loc = real_location
- movable_loc.lose_active_storage(src)
-
- M.client.screen -= boxes
- M.client.screen -= closer
- M.client.screen -= real_location.contents
- return TRUE
-
-/datum/component/storage/proc/close(mob/M)
- hide_from(M)
-
-/datum/component/storage/proc/close_all()
- SIGNAL_HANDLER
-
- . = FALSE
- for(var/mob/M in can_see_contents())
- close(M)
- . = TRUE //returns TRUE if any mobs actually got a close(M) call
-
-/datum/component/storage/proc/emp_act(datum/source, severity)
- SIGNAL_HANDLER
-
- if(emp_shielded)
- return
- var/datum/component/storage/concrete/master = master()
- master.emp_act(source, severity)
-
-//This proc draws out the inventory and places the items on it. tx and ty are the upper left tile and mx, my are the bottm right.
-//The numbers are calculated from the bottom-left The bottom-left slot being 1,1.
-/datum/component/storage/proc/orient_objs(tx, ty, mx, my)
- var/atom/real_location = real_location()
- var/cx = tx
- var/cy = ty
- boxes.screen_loc = "[tx]:,[ty] to [mx],[my]"
- for(var/obj/O in real_location)
- if(QDELETED(O))
- continue
- O.screen_loc = "[cx],[cy]"
- O.plane = ABOVE_HUD_PLANE
- cx++
- if(cx > mx)
- cx = tx
- cy--
- closer.screen_loc = "[mx+1],[my]"
-
-//Resets something that is being removed from storage.
-/datum/component/storage/proc/_removal_reset(atom/movable/thing)
- if(!istype(thing))
- return FALSE
- var/datum/component/storage/concrete/master = master()
- if(!istype(master))
- return FALSE
- return master._removal_reset(thing)
-
-/datum/component/storage/proc/_remove_and_refresh(datum/source, atom/movable/gone, direction)
- SIGNAL_HANDLER
-
- _removal_reset(gone)
- refresh_mob_views()
-
-//Call this proc to handle the removal of an item from the storage item. The item will be moved to the new_location target, if that is null it's being deleted
-/datum/component/storage/proc/remove_from_storage(atom/movable/AM, atom/new_location)
- if(!istype(AM))
- return FALSE
- var/datum/component/storage/concrete/master = master()
- if(!istype(master))
- return FALSE
- return master.remove_from_storage(AM, new_location)
-
-/datum/component/storage/proc/refresh_mob_views()
- SIGNAL_HANDLER
-
- var/list/seeing = can_see_contents()
- for(var/i in seeing)
- show_to(i)
- return TRUE
-
-/datum/component/storage/proc/can_see_contents()
- var/list/cansee = list()
- for(var/mob/M in is_using)
- if(M.active_storage == src && M.client)
- cansee |= M
- else
- LAZYREMOVE(is_using, M)
- UnregisterSignal(M, COMSIG_PARENT_QDELETING)
- return cansee
-
-//Tries to dump content
-/datum/component/storage/proc/dump_content_at(atom/dest_object, mob/M)
- var/atom/A = parent
- var/atom/dump_destination = get_dumping_location(dest_object)
- if(M.CanReach(A) && dump_destination && M.CanReach(dump_destination))
- if(locked)
- to_chat(M, span_warning("[parent] seems to be locked!"))
- return FALSE
- if(dump_destination.storage_contents_dump_act(src, M))
- playsound(A, SFX_RUSTLE, 50, TRUE, -5)
- return TRUE
- return FALSE
-
-/datum/component/storage/proc/get_dumping_location(atom/dest_object)
- var/datum/component/storage/storage = dest_object.GetComponent(/datum/component/storage)
- if(storage)
- return storage.real_location()
- return dest_object.get_dumping_location()
-
-//This proc is called when you want to place an item into the storage item.
-/datum/component/storage/proc/attackby(datum/source, obj/item/I, mob/M, params)
- SIGNAL_HANDLER
-
- if(!I.attackby_storage_insert(src, parent, M))
- return FALSE
- . = TRUE //no afterattack
- if(iscyborg(M))
- return
- if(!can_be_inserted(I, FALSE, M))
- var/atom/real_location = real_location()
- if(real_location.contents.len >= max_items) //don't use items on the backpack if they don't fit
- return TRUE
- return FALSE
- handle_item_insertion(I, FALSE, M)
-
-/datum/component/storage/proc/return_inv(recursive)
- var/list/ret = list()
- ret |= contents()
- if(recursive)
- for(var/i in ret.Copy())
- var/atom/A = i
- SEND_SIGNAL(A, COMSIG_TRY_STORAGE_RETURN_INVENTORY, ret, TRUE)
- return ret
-
-/datum/component/storage/proc/contents() //ONLY USE IF YOU NEED TO COPY CONTENTS OF REAL LOCATION, COPYING IS NOT AS FAST AS DIRECT ACCESS!
- var/atom/real_location = real_location()
- return real_location.contents.Copy()
-
-//Abuses the fact that lists are just references, or something like that.
-/datum/component/storage/proc/signal_return_inv(datum/source, list/interface, recursive = TRUE)
- SIGNAL_HANDLER
-
- if(!islist(interface))
- return FALSE
- interface |= return_inv(recursive)
- return TRUE
-
-/datum/component/storage/proc/topic_handle(datum/source, user, href_list)
- SIGNAL_HANDLER
-
- if(href_list["show_valid_pocket_items"])
- handle_show_valid_items(source, user)
-
-/datum/component/storage/proc/handle_show_valid_items(datum/source, user)
- to_chat(user, span_notice("[source] can hold: [can_hold_description]"))
-
-/datum/component/storage/proc/mousedrop_onto(datum/source, atom/over_object, mob/M)
- SIGNAL_HANDLER
-
- set waitfor = FALSE
- . = COMPONENT_NO_MOUSEDROP
- if(!ismob(M))
- return
- if(!over_object)
- return
- if(ismecha(M.loc)) // stops inventory actions in a mech
- return
- if(M.incapacitated() || !M.canUseStorage())
- return
- var/atom/A = parent
- A.add_fingerprint(M)
- // this must come before the screen objects only block, dunno why it wasn't before
- if(over_object == M)
- user_show_to_mob(M)
- if(!istype(over_object, /atom/movable/screen))
- INVOKE_ASYNC(src, .proc/dump_content_at, over_object, M)
- return
- if(A.loc != M)
- return
- playsound(A, SFX_RUSTLE, 50, TRUE, -5)
- if(istype(over_object, /atom/movable/screen/inventory/hand))
- var/atom/movable/screen/inventory/hand/H = over_object
- M.putItemFromInventoryInHandIfPossible(A, H.held_index)
- return
- A.add_fingerprint(M)
-
-/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE)
- var/atom/A = parent
- if(!istype(M))
- return FALSE
- A.add_fingerprint(M)
- if(locked && !force)
- to_chat(M, span_warning("[parent] seems to be locked!"))
- return FALSE
- if(force || M.CanReach(parent, view_only = TRUE))
- show_to(M)
-
-/datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M)
- SIGNAL_HANDLER
-
- if(isitem(O))
- var/obj/item/I = O
- if(iscarbon(M) || isdrone(M))
- var/mob/living/L = M
- if(!L.incapacitated() && I == L.get_active_held_item())
- if(!SEND_SIGNAL(I, COMSIG_CONTAINS_STORAGE) && can_be_inserted(I, FALSE)) //If it has storage it should be trying to dump, not insert.
- handle_item_insertion(I, FALSE, L)
-
-//This proc return 1 if the item can be picked up and 0 if it can't.
-//Set the stop_messages to stop it from printing messages
-/datum/component/storage/proc/can_be_inserted(obj/item/I, stop_messages = FALSE, mob/M)
- if(!istype(I) || (I.item_flags & ABSTRACT))
- return FALSE //Not an item
- if(I == parent)
- return FALSE //no paradoxes for you
- var/atom/real_location = real_location()
- var/atom/host = parent
- if(real_location == I.loc)
- return FALSE //Means the item is already in the storage item
- if(locked)
- if(M && !stop_messages)
- host.add_fingerprint(M)
- to_chat(M, span_warning("[host] seems to be locked!"))
- return FALSE
- if(real_location.contents.len >= max_items)
- if(!stop_messages)
- to_chat(M, span_warning("[host] is full, make some space!"))
- return FALSE //Storage item is full
- if(length(can_hold))
- if(!is_type_in_typecache(I, can_hold))
- if(!stop_messages)
- to_chat(M, span_warning("[host] cannot hold [I]!"))
- return FALSE
- if(is_type_in_typecache(I, cant_hold) || HAS_TRAIT(I, TRAIT_NO_STORAGE_INSERT) || (can_hold_trait && !HAS_TRAIT(I, can_hold_trait))) //Items which this container can't hold.
- if(!stop_messages)
- to_chat(M, span_warning("[host] cannot hold [I]!"))
- return FALSE
- if(I.w_class > max_w_class && !is_type_in_typecache(I, exception_hold))
- if(!stop_messages)
- to_chat(M, span_warning("[I] is too big for [host]!"))
- return FALSE
- var/datum/component/storage/biggerfish = real_location.loc.GetComponent(/datum/component/storage)
- if(biggerfish && biggerfish.max_w_class < max_w_class) //return false if we are inside of another container, and that container has a smaller max_w_class than us (like if we're a bag in a box)
- if(!stop_messages)
- to_chat(M, span_warning("[I] can't fit in [host] while [real_location.loc] is in the way!"))
- return FALSE
- var/sum_w_class = I.w_class
- for(var/obj/item/_I in real_location)
- sum_w_class += _I.w_class //Adds up the combined w_classes which will be in the storage item if the item is added to it.
- if(sum_w_class > max_combined_w_class)
- if(!stop_messages)
- to_chat(M, span_warning("[I] won't fit in [host], make some space!"))
- return FALSE
- if(isitem(host))
- var/obj/item/IP = host
- var/datum/component/storage/STR_I = I.GetComponent(/datum/component/storage)
- if((I.w_class >= IP.w_class) && STR_I && !allow_big_nesting)
- if(!stop_messages)
- to_chat(M, span_warning("[IP] cannot hold [I] as it's a storage item of the same size!"))
- return FALSE //To prevent the stacking of same sized storage items.
- if(HAS_TRAIT(I, TRAIT_NODROP)) //SHOULD be handled in unEquip, but better safe than sorry.
- if(!stop_messages)
- to_chat(M, span_warning("\the [I] is stuck to your hand, you can't put it in \the [host]!"))
- return FALSE
- var/datum/component/storage/concrete/master = master()
- if(!istype(master))
- return FALSE
- return master.slave_can_insert_object(src, I, stop_messages, M)
-
-/datum/component/storage/proc/_insert_physical_item(obj/item/I, override = FALSE)
- return FALSE
-
-//This proc handles items being inserted. It does not perform any checks of whether an item can or can't be inserted. That's done by can_be_inserted()
-//The prevent_warning parameter will stop the insertion message from being displayed. It is intended for cases where you are inserting multiple items at once,
-//such as when picking up all the items on a tile with one click.
-/datum/component/storage/proc/handle_item_insertion(obj/item/I, prevent_warning = FALSE, mob/M, datum/component/storage/remote)
- var/atom/parent = src.parent
- var/datum/component/storage/concrete/master = master()
- if(!istype(master))
- return FALSE
- if(silent)
- prevent_warning = TRUE
- if(M)
- parent.add_fingerprint(M)
- . = master.handle_item_insertion_from_slave(src, I, prevent_warning, M)
-
-/datum/component/storage/proc/mob_item_insertion_feedback(mob/user, mob/M, obj/item/I, override = FALSE)
- if(silent && !override)
- return
- if(rustle_sound)
- playsound(parent, SFX_RUSTLE, 50, TRUE, -5)
- for(var/mob/viewing in viewers(user, null))
- if(M == viewing)
- to_chat(usr, span_notice("You put [I] [insert_preposition]to [parent]."))
- else if(in_range(M, viewing)) //If someone is standing close enough, they can tell what it is...
- viewing.show_message(span_notice("[M] puts [I] [insert_preposition]to [parent]."), MSG_VISUAL)
- else if(I && I.w_class >= 3) //Otherwise they can only see large or normal items from a distance...
- viewing.show_message(span_notice("[M] puts [I] [insert_preposition]to [parent]."), MSG_VISUAL)
-
-/datum/component/storage/proc/update_icon()
- if(isobj(parent))
- var/obj/O = parent
- O.update_appearance()
-
-/datum/component/storage/proc/signal_insertion_attempt(datum/source, obj/item/I, mob/M, silent = FALSE, force = FALSE)
- SIGNAL_HANDLER
-
- if((!force && !can_be_inserted(I, TRUE, M)) || (I == parent))
- return FALSE
- return handle_item_insertion(I, silent, M)
-
-/datum/component/storage/proc/signal_can_insert(datum/source, obj/item/I, mob/M, silent = FALSE)
- SIGNAL_HANDLER
-
- return can_be_inserted(I, silent, M)
-
-/datum/component/storage/proc/show_to_ghost(datum/source, mob/dead/observer/M)
- SIGNAL_HANDLER
-
- return user_show_to_mob(M, TRUE)
-
-/datum/component/storage/proc/signal_show_attempt(datum/source, mob/showto, force = FALSE)
- SIGNAL_HANDLER
-
- return user_show_to_mob(showto, force)
-
-/datum/component/storage/proc/on_check()
- SIGNAL_HANDLER
-
- return TRUE
-
-/datum/component/storage/proc/check_locked()
- SIGNAL_HANDLER
-
- return locked
-
-/datum/component/storage/proc/signal_take_type(datum/source, type, atom/destination, amount = INFINITY, check_adjacent = FALSE, force = FALSE, mob/user, list/inserted)
- SIGNAL_HANDLER
-
- if(!force)
- if(check_adjacent)
- if(!user || !user.CanReach(destination) || !user.CanReach(parent))
- return FALSE
- var/list/taking = typecache_filter_list(contents(), typecacheof(type))
- if(taking.len > amount)
- taking.len = amount
- if(inserted) //duplicated code for performance, don't bother checking retval/checking for list every item.
- for(var/i in taking)
- if(remove_from_storage(i, destination))
- inserted |= i
- else
- for(var/i in taking)
- remove_from_storage(i, destination)
- return TRUE
-
-/datum/component/storage/proc/remaining_space_items()
- var/atom/real_location = real_location()
- return max(0, max_items - real_location.contents.len)
-
-/datum/component/storage/proc/signal_fill_type(datum/source, type, amount = 20, force = FALSE)
- SIGNAL_HANDLER
-
- var/atom/real_location = real_location()
- if(!force)
- amount = min(remaining_space_items(), amount)
- for(var/i in 1 to amount)
- if(!handle_item_insertion(new type(real_location), TRUE))
- return i > 1 //return TRUE only if at least one insertion has been successful.
- if(QDELETED(src))
- return TRUE
- return TRUE
-
-
-/datum/component/storage/proc/on_attack_hand(datum/source, mob/user)
- SIGNAL_HANDLER
-
- var/atom/A = parent
- if(!attack_hand_interact)
- return
- if(user.active_storage == src && A.loc == user) //if you're already looking inside the storage item
- user.active_storage.close(user)
- close(user)
- . = COMPONENT_CANCEL_ATTACK_CHAIN
- return
-
- if(rustle_sound)
- playsound(A, SFX_RUSTLE, 50, TRUE, -5)
-
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- if(H.l_store == A && !H.get_active_held_item()) //Prevents opening if it's in a pocket.
- . = COMPONENT_CANCEL_ATTACK_CHAIN
- INVOKE_ASYNC(H, /mob.proc/put_in_hands, A)
- H.l_store = null
- return
- if(H.r_store == A && !H.get_active_held_item())
- . = COMPONENT_CANCEL_ATTACK_CHAIN
- INVOKE_ASYNC(H, /mob.proc/put_in_hands, A)
- H.r_store = null
- return
-
- if(A.loc == user)
- . = COMPONENT_CANCEL_ATTACK_CHAIN
- if(locked)
- to_chat(user, span_warning("[parent] seems to be locked!"))
- else
- show_to(user)
-
-
-/datum/component/storage/proc/signal_on_pickup(datum/source, mob/user)
- SIGNAL_HANDLER
-
- update_actions()
- for(var/mob/M in can_see_contents() - user)
- close(M)
-
-/datum/component/storage/proc/signal_take_obj(datum/source, atom/movable/AM, new_loc, force = FALSE)
- SIGNAL_HANDLER
-
- if(!(AM in real_location()))
- return FALSE
- return remove_from_storage(AM, new_loc)
-
-/datum/component/storage/proc/signal_quick_empty(datum/source, atom/loctarget)
- SIGNAL_HANDLER
-
- return do_quick_empty(loctarget)
-
-/datum/component/storage/proc/signal_hide_attempt(datum/source, mob/target)
- SIGNAL_HANDLER
-
- return hide_from(target)
-
-
-/datum/component/storage/proc/open_storage(mob/user)
- if(!user.CanReach(parent))
- user.balloon_alert(user, "can't reach!")
- return FALSE
- if(!isliving(user) || user.incapacitated())
- return FALSE
- if(locked)
- to_chat(user, span_warning("[parent] seems to be locked!"))
- return FALSE
-
- . = TRUE
- var/atom/A = parent
- if(!quickdraw)
- A.add_fingerprint(user)
- user_show_to_mob(user)
- playsound(A, SFX_RUSTLE, 50, TRUE, -5)
- return
-
- var/obj/item/to_remove = locate() in real_location()
- if(!to_remove)
- return
-
- INVOKE_ASYNC(src, .proc/attempt_put_in_hands, to_remove, user)
-
-/datum/component/storage/proc/on_open_storage_click(datum/source, mob/user, list/modifiers)
- SIGNAL_HANDLER
-
- if(open_storage(user))
- return COMPONENT_CANCEL_ATTACK_CHAIN
- if(LAZYACCESS(modifiers, RIGHT_CLICK))
- return COMPONENT_SECONDARY_CANCEL_ATTACK_CHAIN
-
-/datum/component/storage/proc/on_open_storage_attackby(datum/source, obj/item/weapon, mob/user, params)
- SIGNAL_HANDLER
-
- if(open_storage(user))
- return COMPONENT_SECONDARY_CANCEL_ATTACK_CHAIN
-
-///attempt to put an item from contents into the users hands
-/datum/component/storage/proc/attempt_put_in_hands(obj/item/to_remove, mob/user)
- var/atom/parent_as_atom = parent
-
- parent_as_atom.add_fingerprint(user)
- remove_from_storage(to_remove, get_turf(user))
- if(!user.put_in_hands(to_remove))
- to_chat(user, span_notice("You fumble for [to_remove] and it falls on the floor."))
- return
- user.visible_message(span_warning("[user] draws [to_remove] from [parent]!"), span_notice("You draw [to_remove] from [parent]."))
-
-/datum/component/storage/proc/action_trigger(datum/signal_source, datum/action/source)
- SIGNAL_HANDLER
-
- gather_mode_switch(source.owner)
- return COMPONENT_ACTION_BLOCK_TRIGGER
-
-/datum/component/storage/proc/gather_mode_switch(mob/user)
- collection_mode = (collection_mode+1)%3
- switch(collection_mode)
- if(COLLECT_SAME)
- to_chat(user, span_notice("[parent] now picks up all items of a single type at once."))
- if(COLLECT_EVERYTHING)
- to_chat(user, span_notice("[parent] now picks up all items in a tile at once."))
- if(COLLECT_ONE)
- to_chat(user, span_notice("[parent] now picks up one item at a time."))
diff --git a/code/datums/elements/item_scaling.dm b/code/datums/elements/item_scaling.dm
index 1f804beee16..490a762bbcb 100644
--- a/code/datums/elements/item_scaling.dm
+++ b/code/datums/elements/item_scaling.dm
@@ -42,10 +42,10 @@
// Make sure overlays also inherit the scaling.
ADD_KEEP_TOGETHER(target, ITEM_SCALING_TRAIT)
- // Object scaled when dropped/thrown OR when exiting a storage component.
- RegisterSignal(target, list(COMSIG_ITEM_DROPPED, COMSIG_STORAGE_EXITED), .proc/scale_overworld)
+ // Object scaled when dropped/thrown OR when exiting a storage object.
+ RegisterSignal(target, list(COMSIG_ITEM_DROPPED, COMSIG_ATOM_EXITED), .proc/scale_overworld)
// Object scaled when placed in an inventory slot OR when entering a storage component.
- RegisterSignal(target, list(COMSIG_ITEM_EQUIPPED, COMSIG_STORAGE_ENTERED), .proc/scale_storage)
+ RegisterSignal(target, list(COMSIG_ITEM_EQUIPPED, COMSIG_ATOM_ENTERED), .proc/scale_storage)
/**
* Detach proc for the item_scaling element.
@@ -58,8 +58,8 @@
UnregisterSignal(target, list(
COMSIG_ITEM_PICKUP,
COMSIG_ITEM_DROPPED,
- COMSIG_STORAGE_ENTERED,
- COMSIG_STORAGE_EXITED,
+ COMSIG_ATOM_ENTERED,
+ COMSIG_ATOM_EXITED,
))
REMOVE_KEEP_TOGETHER(target, ITEM_SCALING_TRAIT)
@@ -82,7 +82,7 @@
scalable_object.transform = M.Scale(scaling)
/**
- * Signal handler for COMSIG_ITEM_DROPPED or COMSIG_STORAGE_EXITED
+ * Signal handler for COMSIG_ITEM_DROPPED or COMSIG_ATOM_EXITED
*
* Longer detailed paragraph about the proc
* including any relevant detail
@@ -95,7 +95,7 @@
scale(source, overworld_scaling)
/**
- * Signal handler for COMSIG_ITEM_EQUIPPED or COMSIG_STORAGE_ENTERED.
+ * Signal handler for COMSIG_ITEM_EQUIPPED or COMSIG_ATOM_ENTERED.
*
* Longer detailed paragraph about the proc
* including any relevant detail
diff --git a/code/datums/quirks/_quirk.dm b/code/datums/quirks/_quirk.dm
index 0e5f0816020..2510e8233aa 100644
--- a/code/datums/quirks/_quirk.dm
+++ b/code/datums/quirks/_quirk.dm
@@ -161,7 +161,7 @@
var/mob/living/carbon/human/human_holder = quirk_holder
// post_add() can be called via delayed callback. Check they still have a backpack equipped before trying to open it.
if(human_holder.back)
- SEND_SIGNAL(human_holder.back, COMSIG_TRY_STORAGE_SHOW, human_holder)
+ human_holder.back.atom_storage.show_contents(human_holder)
for(var/chat_string in where_items_spawned)
to_chat(quirk_holder, chat_string)
diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm
new file mode 100644
index 00000000000..3eee1a40609
--- /dev/null
+++ b/code/datums/storage/storage.dm
@@ -0,0 +1,1063 @@
+/**
+ * Datumized Storage
+ * Eliminates the need for custom signals specifically for the storage component, and attaches a storage variable (atom_storage) to every atom.
+ * 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
+ /// the actual item we're attached to
+ var/datum/weakref/parent
+ /// the actual item we're storing in
+ var/datum/weakref/real_location
+
+ /// if this is set, only items, and their children, will fit
+ var/list/can_hold
+ /// if this is set, items, and their children, won't fit
+ var/list/cant_hold
+ /// if set, these items will be the exception to the max size of object that can fit.
+ var/list/exception_hold
+ /// if set can only contain stuff with this single trait present.
+ var/list/can_hold_trait
+
+ /// whether or not we should have those cute little animations
+ var/animated = TRUE
+
+ var/max_slots = 7
+ /// max weight class for a single item being inserted
+ var/max_specific_storage = WEIGHT_CLASS_NORMAL
+ /// max combined weight classes the storage can hold
+ var/max_total_storage = 14
+
+ /// list of all the mobs currently viewing the contents
+ var/list/is_using = list()
+
+ var/locked = FALSE
+ /// whether or not we should open when clicked
+ var/attack_hand_interact = TRUE
+ /// whether or not we allow storage objects of the same size inside
+ var/allow_big_nesting = FALSE
+
+ /// should we be allowed to pickup an object by clicking it
+ var/allow_quick_gather = FALSE
+ /// show we allow emptying all contents by using the storage object in hand
+ var/allow_quick_empty = FALSE
+ /// the mode for collection when allow_quick_gather is enabled
+ var/collection_mode = COLLECT_ONE
+
+ /// shows what we can hold in examine text
+ var/can_hold_description
+
+ /// contents shouldn't be emped
+ var/emp_shielded
+
+ /// you put things *in* a bag, but *on* a plate
+ var/insert_preposition = "in"
+
+ /// don't show any chat messages regarding inserting items
+ var/silent = FALSE
+ /// play a rustling sound when interacting with the bag
+ var/rustle_sound = TRUE
+
+ /// alt click takes an item out instead of opening up storage
+ var/quickdraw = FALSE
+
+ /// instead of displaying multiple items of the same type, display them as numbered contents
+ var/numerical_stacking = FALSE
+
+ /// storage display object
+ var/atom/movable/screen/storage/boxes
+ /// close button object
+ var/atom/movable/screen/close/closer
+
+ /// maximum amount of columns a storage object can have
+ var/screen_max_columns = 7
+ var/screen_max_rows = INFINITY
+ /// pixel location of the boxes and close button
+ var/screen_pixel_x = 16
+ var/screen_pixel_y = 16
+ /// where storage starts being rendered, screen_loc wise
+ var/screen_start_x = 4
+ var/screen_start_y = 2
+
+ var/datum/weakref/modeswitch_action_ref
+
+/datum/storage/New(atom/parent, max_slots, max_specific_storage, max_total_storage, numerical_stacking, allow_quick_gather, allow_quick_empty, collection_mode, attack_hand_interact)
+ boxes = new(null, src)
+ closer = new(null, src)
+
+ src.parent = WEAKREF(parent)
+ src.real_location = src.parent
+ src.max_slots = max_slots || src.max_slots
+ src.max_specific_storage = max_specific_storage || src.max_specific_storage
+ src.max_total_storage = max_total_storage || src.max_total_storage
+ src.numerical_stacking = numerical_stacking || src.numerical_stacking
+ src.allow_quick_gather = allow_quick_gather || src.allow_quick_gather
+ src.allow_quick_empty = allow_quick_empty || src.allow_quick_empty
+ src.collection_mode = collection_mode || src.collection_mode
+ src.attack_hand_interact = attack_hand_interact || src.attack_hand_interact
+
+ var/atom/resolve_parent = src.parent?.resolve()
+ var/atom/resolve_location = src.real_location?.resolve()
+
+ if(!resolve_parent)
+ stack_trace("storage could not resolve parent weakref")
+ qdel(src)
+ return
+
+ if(!resolve_location)
+ stack_trace("storage could not resolve location weakref")
+ qdel(src)
+ return
+
+ RegisterSignal(resolve_parent, list(COMSIG_ATOM_ATTACK_PAW, COMSIG_ATOM_ATTACK_HAND), .proc/handle_attack)
+ RegisterSignal(resolve_parent, COMSIG_MOUSEDROP_ONTO, .proc/mousedrop_onto)
+ RegisterSignal(resolve_parent, COMSIG_MOUSEDROPPED_ONTO, .proc/mousedropped_onto)
+
+ RegisterSignal(resolve_parent, COMSIG_ATOM_EMP_ACT, .proc/emp_act)
+ RegisterSignal(resolve_parent, COMSIG_PARENT_ATTACKBY, .proc/attackby)
+ RegisterSignal(resolve_parent, COMSIG_ITEM_PRE_ATTACK, .proc/intercept_preattack)
+ RegisterSignal(resolve_parent, COMSIG_OBJ_DECONSTRUCT, .proc/deconstruct)
+
+ RegisterSignal(resolve_parent, COMSIG_ITEM_ATTACK_SELF, .proc/mass_empty)
+
+ RegisterSignal(resolve_parent, list(COMSIG_ATOM_ATTACK_HAND_SECONDARY, COMSIG_CLICK_ALT, COMSIG_ATOM_ATTACK_GHOST), .proc/open_storage)
+
+ RegisterSignal(resolve_location, COMSIG_ATOM_ENTERED, .proc/handle_enter)
+ RegisterSignal(resolve_location, COMSIG_ATOM_EXITED, .proc/handle_exit)
+ RegisterSignal(resolve_parent, COMSIG_MOVABLE_MOVED, .proc/close_distance)
+ RegisterSignal(resolve_parent, COMSIG_ITEM_EQUIPPED, .proc/update_actions)
+
+ RegisterSignal(resolve_parent, COMSIG_TOPIC, .proc/topic_handle)
+
+ orient_to_hud()
+
+/datum/storage/Destroy()
+ parent = null
+ real_location = null
+ boxes = null
+ closer = null
+
+ for(var/mob/person in is_using)
+ if(person.active_storage == src)
+ person.active_storage = null
+
+ is_using.Cut()
+
+ return ..()
+
+/datum/storage/proc/deconstruct()
+ SIGNAL_HANDLER
+
+ remove_all()
+
+/// Automatically ran on all object insertions: flag marking and view refreshing.
+/datum/storage/proc/handle_enter(datum/source, obj/item/arrived)
+ SIGNAL_HANDLER
+
+ if(!istype(arrived))
+ return
+
+ arrived.item_flags |= IN_STORAGE
+
+ refresh_views()
+
+ arrived.on_enter_storage(src)
+
+/// Automatically ran on all object removals: flag marking and view refreshing.
+/datum/storage/proc/handle_exit(datum/source, obj/item/gone)
+ SIGNAL_HANDLER
+
+ if(!istype(gone))
+ return
+
+ gone.item_flags &= ~IN_STORAGE
+
+ remove_and_refresh(gone)
+
+ gone.on_exit_storage(src)
+
+/**
+ * Sets where items are physically being stored in the case it shouldn't be on the parent.
+ *
+ * @param atom/real the new real location of the datum
+ * @param should_drop if TRUE, all the items in the old real location will be dropped
+ */
+/datum/storage/proc/set_real_location(atom/real, should_drop = FALSE)
+ if(!real)
+ return
+
+ var/atom/resolve_location = src.real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ var/atom/resolve_parent = src.parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ if(should_drop)
+ remove_all(src, get_turf(resolve_parent))
+
+ resolve_location.flags_1 &= ~HAS_DISASSOCIATED_STORAGE_1
+ real.flags_1 |= HAS_DISASSOCIATED_STORAGE_1
+
+ UnregisterSignal(resolve_location, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_EXITED))
+
+ RegisterSignal(real, COMSIG_ATOM_ENTERED, .proc/handle_enter)
+ RegisterSignal(real, COMSIG_ATOM_EXITED, .proc/handle_exit)
+
+ real_location = WEAKREF(real)
+
+/datum/storage/proc/topic_handle(datum/source, user, href_list)
+ SIGNAL_HANDLER
+
+ if(href_list["show_valid_pocket_items"])
+ handle_show_valid_items(source, user)
+
+/datum/storage/proc/handle_show_valid_items(datum/source, user)
+ to_chat(user, span_notice("[source] can hold: [can_hold_description]"))
+
+/// Almost 100% of the time the lists passed into set_holdable are reused for each instance of the component
+/// Just fucking cache it 4head
+/// Yes I could generalize this, but I don't want anyone else using it. in fact, DO NOT COPY THIS
+/// If you find yourself needing this pattern, you're likely better off using static typecaches
+/// I'm not because I do not trust implementers of the storage component to use them, BUT
+/// IF I FIND YOU USING THIS PATTERN IN YOUR CODE I WILL BREAK YOU ACROSS MY KNEES
+/// ~Lemon
+GLOBAL_LIST_EMPTY(cached_storage_typecaches)
+
+/datum/storage/proc/set_holdable(list/can_hold_list = null, list/cant_hold_list = null)
+ if(!islist(can_hold_list))
+ can_hold_list = list(can_hold_list)
+ if(!islist(cant_hold_list))
+ cant_hold_list = list(cant_hold_list)
+
+ can_hold_description = generate_hold_desc(can_hold_list)
+
+ if (can_hold_list)
+ var/unique_key = can_hold_list.Join("-")
+ if(!GLOB.cached_storage_typecaches[unique_key])
+ GLOB.cached_storage_typecaches[unique_key] = typecacheof(can_hold_list)
+ can_hold = GLOB.cached_storage_typecaches[unique_key]
+
+ if (cant_hold_list != null)
+ var/unique_key = cant_hold_list.Join("-")
+ if(!GLOB.cached_storage_typecaches[unique_key])
+ GLOB.cached_storage_typecaches[unique_key] = typecacheof(cant_hold_list)
+ cant_hold = GLOB.cached_storage_typecaches[unique_key]
+
+/// Generates a description, primarily for clothing storage.
+/datum/storage/proc/generate_hold_desc(can_hold_list)
+ var/list/desc = list()
+
+ for(var/valid_type in can_hold_list)
+ var/obj/item/valid_item = valid_type
+ desc += "\a [initial(valid_item.name)]"
+
+ return "\n\t[span_notice("[desc.Join("\n\t")]")]"
+
+/// Updates the action button for toggling collectmode.
+/datum/storage/proc/update_actions()
+ SIGNAL_HANDLER
+
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ if(!istype(resolve_parent) || !allow_quick_gather)
+ QDEL_NULL(modeswitch_action_ref)
+ return
+
+ var/datum/action/existing = modeswitch_action_ref?.resolve()
+ if(!QDELETED(existing))
+ return
+
+ var/datum/action/modeswitch_action = resolve_parent.add_item_action(/datum/action/item_action/storage_gather_mode)
+ RegisterSignal(modeswitch_action, COMSIG_ACTION_TRIGGER, .proc/action_trigger)
+ modeswitch_action_ref = WEAKREF(modeswitch_action)
+
+/// Refreshes and item to be put back into the real world, out of storage.
+/datum/storage/proc/reset_item(obj/item/thing)
+ thing.layer = initial(thing.layer)
+ thing.plane = initial(thing.plane)
+ thing.mouse_opacity = initial(thing.mouse_opacity)
+ thing.screen_loc = null
+ if(thing.maptext)
+ thing.maptext = ""
+
+/**
+ * Checks if an item is capable of being inserted into the storage
+ *
+ * @param obj/item/to_insert the item we're checking
+ * @param messages if TRUE, will print out a message if the item is not valid
+ * @param force bypass locked storage
+ */
+/datum/storage/proc/can_insert(obj/item/to_insert, mob/user, messages = TRUE, force = FALSE)
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ if(!isitem(to_insert))
+ return FALSE
+
+ if(locked && !force)
+ return FALSE
+
+ if((to_insert == resolve_parent) || (to_insert == real_location))
+ return FALSE
+
+ if(to_insert.w_class > max_specific_storage && !is_type_in_typecache(to_insert, exception_hold))
+ if(messages)
+ to_chat(user, span_warning("\The [to_insert] is too big for \the [resolve_parent]!"))
+ return FALSE
+
+ if(resolve_location.contents.len >= max_slots)
+ if(messages)
+ to_chat(user, span_warning("\The [to_insert] can't fit into \the [resolve_parent]! Make some space!"))
+ return FALSE
+
+ var/total_weight = to_insert.w_class
+
+ for(var/obj/item/thing in resolve_location)
+ total_weight += thing.w_class
+
+ if(total_weight > max_total_storage)
+ if(messages)
+ to_chat(user, span_warning("\The [to_insert] can't fit into \the [resolve_parent]! Make some space!"))
+ return FALSE
+
+ if(length(can_hold))
+ if(!is_type_in_typecache(to_insert, can_hold))
+ if(messages)
+ to_chat(user, span_warning("\The [resolve_parent] cannot hold \the [to_insert]!"))
+ return FALSE
+
+ if(is_type_in_typecache(to_insert, cant_hold) || HAS_TRAIT(to_insert, TRAIT_NO_STORAGE_INSERT) || (can_hold_trait && !HAS_TRAIT(to_insert, can_hold_trait)))
+ if(messages)
+ to_chat(user, span_warning("\The [resolve_parent] cannot hold \the [to_insert]!"))
+ return FALSE
+
+ var/datum/storage/biggerfish = resolve_parent.loc.atom_storage // this is valid if the container our resolve_parent is being held in is a storage item
+
+ if(biggerfish && biggerfish.max_specific_storage < max_specific_storage)
+ if(messages)
+ to_chat(user, span_warning("[to_insert] can't fit in [resolve_parent] while [resolve_parent.loc] is in the way!"))
+ return FALSE
+
+ if(istype(resolve_parent))
+ var/datum/storage/item_storage = to_insert.atom_storage
+ if((to_insert.w_class >= resolve_parent.w_class) && item_storage && !allow_big_nesting)
+ if(messages)
+ to_chat(user, span_warning("[resolve_parent] cannot hold [to_insert] as it's a storage item of the same size!"))
+ return FALSE
+
+ return TRUE
+
+/**
+ * Attempts to insert an item into the storage
+ *
+ * @param datum/source used by the signal handler
+ * @param obj/item/to_insert the item we're inserting
+ * @param mob/user the user who is inserting the item
+ * @param override see item_insertion_feedback()
+ * @param force bypass locked storage
+ */
+/datum/storage/proc/attempt_insert(datum/source, obj/item/to_insert, mob/user, override = FALSE, force = FALSE)
+ SIGNAL_HANDLER
+
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ if(!can_insert(to_insert, user, force = force))
+ return FALSE
+
+ to_insert.item_flags |= IN_STORAGE
+
+ to_insert.forceMove(resolve_location)
+ item_insertion_feedback(user, to_insert, override)
+
+ return TRUE
+
+/**
+ * Inserts every time in a given list, with a progress bar
+ *
+ * @param mob/user the user who is inserting the items
+ * @param list/things the list of items to insert
+ * @param atom/thing_loc the location of the items (used to make sure an item hasn't moved during pickup)
+ * @param list/rejections a list used to make sure we only complain once about an invalid insertion
+ * @param datum/progressbar/progress the progressbar used to show the progress of the insertion
+ */
+/datum/storage/proc/handle_mass_pickup(mob/user, list/things, atom/thing_loc, list/rejections, datum/progressbar/progress)
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ for(var/obj/item/thing in things)
+ things -= thing
+ if(thing.loc != thing_loc)
+ continue
+ if(thing.type in rejections) // To limit bag spamming: any given type only complains once
+ continue
+ if(!attempt_insert(resolve_parent, thing, user, TRUE)) // Note can_be_inserted still makes noise when the answer is no
+ if(resolve_location.contents.len >= max_slots)
+ break
+ rejections += thing.type // therefore full bags are still a little spammy
+ continue
+
+ if (TICK_CHECK)
+ progress.update(progress.goal - things.len)
+ return TRUE
+
+ progress.update(progress.goal - things.len)
+ return FALSE
+
+/**
+ * Used to transfer all the items inside of us to another atom
+ *
+ * @param mob/user the user who is transferring the items
+ * @param atom/going_to the atom we're transferring to
+ */
+/datum/storage/proc/handle_mass_transfer(mob/user, atom/going_to)
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ if(!going_to.atom_storage)
+ return
+
+ for (var/atom/thing in resolve_location.contents)
+ going_to.atom_storage.attempt_insert(src, thing, user)
+
+/**
+ * Provides visual feedback in chat for an item insertion
+ *
+ * @param mob/user the user who is inserting the item
+ * @param obj/item/thing the item we're inserting
+ * @param override skip feedback, only do animation check
+ */
+/datum/storage/proc/item_insertion_feedback(mob/user, obj/item/thing, override = FALSE)
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ if(animated)
+ animate_parent()
+
+ if(override)
+ return
+
+ if(silent)
+ return
+
+ if(rustle_sound)
+ playsound(resolve_parent, SFX_RUSTLE, 50, TRUE, -5)
+
+ to_chat(user, span_notice("You put [thing] [insert_preposition]to [resolve_parent]."))
+
+ for(var/mob/viewing in oviewers(user, null))
+ if(in_range(user, viewing))
+ viewing.show_message(span_notice("[user] puts [thing] [insert_preposition]to [resolve_parent]."), MSG_VISUAL)
+ return
+ if(thing && thing.w_class >= 3)
+ viewing.show_message(span_notice("[user] puts [thing] [insert_preposition]to [resolve_parent]."), MSG_VISUAL)
+ return
+
+/**
+ * Attempts to remove an item from the storage
+ *
+ * @param obj/item/thing the object we're removing
+ * @param atom/newLoc where we're placing the item
+ * @param silent if TRUE, we won't play any exit sounds
+ */
+/datum/storage/proc/attempt_remove(obj/item/thing, atom/newLoc, silent = FALSE)
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ if(istype(thing))
+ if(ismob(resolve_parent.loc))
+ var/mob/mobparent = resolve_parent.loc
+ thing.dropped(mobparent, TRUE)
+
+ if(newLoc)
+ reset_item(thing)
+ thing.forceMove(newLoc)
+
+ if(rustle_sound && !silent)
+ playsound(resolve_parent, SFX_RUSTLE, 50, TRUE, -5)
+ else
+ thing.moveToNullspace()
+
+ thing.item_flags &= ~IN_STORAGE
+
+ if(animated)
+ animate_parent()
+
+ refresh_views()
+
+ if(isobj(resolve_parent))
+ resolve_parent.update_appearance()
+
+ return TRUE
+
+/**
+ * Removes everything inside of our storage
+ *
+ * @param atom/target where we're placing the item
+ */
+/datum/storage/proc/remove_all(atom/target)
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ if(!target)
+ target = get_turf(resolve_parent)
+
+ for(var/obj/item/thing in resolve_location)
+ if(thing.loc != resolve_location)
+ continue
+ attempt_remove(thing, target, silent = TRUE)
+
+/**
+ * Removes only a specific type of item from our storage
+ *
+ * @param type the type of item to remove
+ * @param amount how many we should attempt to pick up at one time
+ * @param check_adjacent if TRUE, we'll check adjacent locations for the item type
+ * @param force if TRUE, we'll bypass the check_adjacent check all together
+ * @param mob/user the user who is removing the items
+ * @param list/inserted a list passed to attempt_remove for ultimate removal
+ */
+/datum/storage/proc/remove_type(type, atom/destination, amount = INFINITY, check_adjacent = FALSE, force = FALSE, mob/user, list/inserted)
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ if(!force)
+ if(check_adjacent)
+ if(!user || !user.CanReach(destination) || !user.CanReach(parent))
+ return FALSE
+ var/list/taking = typecache_filter_list(resolve_location.contents, typecacheof(type))
+ if(taking.len > amount)
+ taking.len = amount
+ if(inserted) //duplicated code for performance, don't bother checking retval/checking for list every item.
+ for(var/i in taking)
+ if(attempt_remove(i, destination))
+ inserted |= i
+ else
+ for(var/i in taking)
+ attempt_remove(i, destination)
+ return TRUE
+
+/// Signal handler for remove_all()
+/datum/storage/proc/mass_empty(datum/source, atom/location, force)
+ SIGNAL_HANDLER
+
+ if(!allow_quick_empty && !force)
+ return
+
+ remove_all(get_turf(location))
+
+/**
+ * Recursive proc to get absolutely EVERYTHING inside a storage item, including the contents of inner items.
+ *
+ * @param list/interface the list we're adding objects to
+ * @param recursive whether or not we're checking inside of inner items
+ */
+/datum/storage/proc/return_inv(list/interface, recursive = TRUE)
+ if(!islist(interface))
+ return FALSE
+
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ var/list/ret = list()
+ ret |= resolve_location.contents
+ if(recursive)
+ for(var/i in ret.Copy())
+ var/atom/atom = i
+ atom.atom_storage?.return_inv(ret, TRUE)
+
+ interface |= ret
+
+ return TRUE
+
+/**
+ * Resets an object, removes it from our screen, and refreshes the view.
+ *
+ * @param atom/movable/gone the object leaving our storage
+ */
+/datum/storage/proc/remove_and_refresh(atom/movable/gone)
+ SIGNAL_HANDLER
+
+ for(var/mob/user in is_using)
+ if(user.client)
+ var/client/cuser = user.client
+ cuser.screen -= gone
+
+ reset_item(gone)
+ refresh_views()
+
+/// Signal handler for the emp_act() of all contents
+/datum/storage/proc/emp_act(datum/source, severity)
+ SIGNAL_HANDLER
+
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ if(emp_shielded)
+ return
+
+ for(var/atom/thing in resolve_location)
+ thing.emp_act(severity)
+
+/// Signal handler for preattack from an object.
+/datum/storage/proc/intercept_preattack(datum/source, obj/item/thing, mob/user, params)
+ SIGNAL_HANDLER
+
+ if(!istype(thing) || !allow_quick_gather || thing.atom_storage)
+ return FALSE
+
+ if(collection_mode == COLLECT_ONE)
+ attempt_insert(source, thing, user)
+ return TRUE
+
+ if(!isturf(thing.loc))
+ return TRUE
+
+ INVOKE_ASYNC(src, .proc/collect_on_turf, thing, user)
+ return TRUE
+
+/**
+ * Collects every item of a type on a turf.
+ *
+ * @param obj/item/thing the initial object to pick up
+ * @param mob/user the user who is picking up the items
+ */
+/datum/storage/proc/collect_on_turf(obj/item/thing, mob/user)
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ var/list/turf_things = thing.loc.contents.Copy()
+
+ if(collection_mode == COLLECT_SAME)
+ turf_things = typecache_filter_list(turf_things, typecacheof(thing.type))
+
+ var/amount = length(turf_things)
+ if(!amount)
+ to_chat(user, span_warning("You failed to pick up anything with [resolve_parent]!"))
+ return
+
+ var/datum/progressbar/progress = new(user, amount, thing.loc)
+ var/list/rejections = list()
+
+ while(do_after(user, 1 SECONDS, resolve_parent, NONE, FALSE, CALLBACK(src, .proc/handle_mass_pickup, user, turf_things, thing.loc, rejections, progress)))
+ stoplag(1)
+
+ progress.end_progress()
+ to_chat(user, span_notice("You put everything you could [insert_preposition]to [resolve_parent]."))
+
+/// Signal handler for whenever we drag the storage somewhere.
+/datum/storage/proc/mousedrop_onto(datum/source, atom/over_object, mob/user)
+ SIGNAL_HANDLER
+
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ if(!istype(user))
+ return
+ if(!over_object)
+ return
+ if(ismecha(user.loc))
+ return
+ if(user.incapacitated() || !user.canUseStorage())
+ return
+
+ resolve_parent.add_fingerprint(user)
+
+ if(over_object == user)
+ open_storage(resolve_parent, user)
+
+ if(!istype(over_object, /atom/movable/screen))
+ INVOKE_ASYNC(src, .proc/dump_content_at, over_object, user)
+ return
+
+ if(resolve_parent.loc != user)
+ return
+
+ if(rustle_sound)
+ playsound(resolve_parent, SFX_RUSTLE, 50, TRUE, -5)
+
+ if(istype(over_object, /atom/movable/screen/inventory/hand))
+ var/atom/movable/screen/inventory/hand/hand = over_object
+ user.putItemFromInventoryInHandIfPossible(resolve_parent, hand.held_index)
+ return
+
+/**
+ * Dumps all of our contents at a specific location.
+ *
+ * @param atom/dest_object where to dump to
+ * @param mob/user the user who is dumping the contents
+ */
+/datum/storage/proc/dump_content_at(atom/dest_object, mob/user)
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ if(user.CanReach(resolve_parent) && dest_object && user.CanReach(dest_object))
+ if(locked)
+ return
+ if(dest_object.storage_contents_dump_act(resolve_parent, user))
+ return
+
+/// Signal handler for whenever something gets mouse-dropped onto us.
+/datum/storage/proc/mousedropped_onto(datum/source, obj/item/dropping, mob/user)
+ SIGNAL_HANDLER
+
+ var/obj/item/resolve_parent = parent?.resolve()
+
+ if(!resolve_parent)
+ return
+ if(!istype(dropping))
+ return
+
+ if(iscarbon(user) || isdrone(user))
+ var/mob/living/user_living = user
+ if(!user_living.incapacitated() && dropping == user_living.get_active_held_item())
+ if(!dropping.atom_storage && can_insert(dropping, user)) //If it has storage it should be trying to dump, not insert.
+ attempt_insert(src, dropping, user)
+
+/// Signal handler for whenever we're attacked by an object.
+/datum/storage/proc/attackby(datum/source, obj/item/thing, mob/user, params)
+ SIGNAL_HANDLER
+
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ if(!thing.attackby_storage_insert(src, resolve_parent, user))
+ return FALSE
+
+ if(iscyborg(user))
+ return TRUE
+
+ attempt_insert(resolve_parent, thing, user)
+ return TRUE
+
+/// Signal handler for whenever we're attacked by a mob.
+/datum/storage/proc/handle_attack(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+ if(!attack_hand_interact)
+ return
+ if(user.active_storage == src && resolve_parent.loc == user)
+ user.active_storage.hide_contents(user)
+ hide_contents(user)
+ return TRUE
+ if(ishuman(user))
+ var/mob/living/carbon/human/hum = user
+ if(hum.l_store == resolve_parent && !hum.get_active_held_item())
+ INVOKE_ASYNC(hum, /mob.proc/put_in_hands, resolve_parent)
+ hum.l_store = null
+ return
+ if(hum.r_store == resolve_parent && !hum.get_active_held_item())
+ INVOKE_ASYNC(hum, /mob.proc/put_in_hands, resolve_parent)
+ hum.r_store = null
+ return
+
+ if(resolve_parent.loc == user)
+ open_storage(resolve_parent, user)
+ return TRUE
+
+/// Generates the numbers on an item in storage to show stacking.
+/datum/storage/proc/process_numerical_display()
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ var/list/toreturn = list()
+
+ for(var/obj/item/thing in resolve_location.contents)
+ var/total_amnt = 1
+
+ if(istype(thing, /obj/item/stack))
+ var/obj/item/stack/things = thing
+ total_amnt = things.amount
+
+ if(!toreturn["[thing.type]-[thing.name]"])
+ toreturn["[thing.type]-[thing.name]"] = new /datum/numbered_display(thing, total_amnt)
+ else
+ var/datum/numbered_display/numberdisplay = toreturn["[thing.type]-[thing.name]"]
+ numberdisplay.number += total_amnt
+
+ return toreturn
+
+/// Updates the storage UI to fit all objects inside storage.
+/datum/storage/proc/orient_to_hud()
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ var/adjusted_contents = resolve_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
+
+ var/columns = clamp(max_slots, 1, screen_max_columns)
+ var/rows = clamp(CEILING(adjusted_contents / columns, 1), 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)
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ 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
+
+ if(islist(numerical_display_contents))
+ for(var/type in numerical_display_contents)
+ var/datum/numbered_display/numberdisplay = numerical_display_contents[type]
+
+ numberdisplay.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE
+ numberdisplay.sample_object.screen_loc = "[current_x]:[screen_pixel_x],[current_y]:[screen_pixel_y]"
+ numberdisplay.sample_object.maptext = MAPTEXT("[(numberdisplay.number > 1)? "[numberdisplay.number]" : ""]")
+ numberdisplay.sample_object.plane = ABOVE_HUD_PLANE
+
+ 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 resolve_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
+
+ 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're showing ourselves to a mob.
+/datum/storage/proc/open_storage(datum/source, mob/toshow)
+ SIGNAL_HANDLER
+
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ if(!toshow.CanReach(resolve_parent))
+ resolve_parent.balloon_alert(toshow, "can't reach!")
+ return FALSE
+
+ if(!isliving(toshow) || toshow.incapacitated())
+ return FALSE
+
+ if(locked)
+ if(!silent)
+ to_chat(toshow, span_warning("[pick("Ka-chunk!", "Ka-chink!", "Plunk!", "Glorf!")] \The [resolve_parent] appears to be locked!"))
+ return FALSE
+
+ if(!quickdraw || toshow.get_active_held_item())
+ show_contents(toshow)
+
+ if(animated)
+ animate_parent()
+
+ if(rustle_sound)
+ playsound(resolve_parent, SFX_RUSTLE, 50, TRUE, -5)
+
+ return TRUE
+
+ var/obj/item/to_remove = locate() in resolve_location
+
+ if(!to_remove)
+ return TRUE
+
+ attempt_remove(to_remove)
+
+ INVOKE_ASYNC(src, .proc/put_in_hands_async, toshow, to_remove)
+
+ if(!silent)
+ toshow.visible_message(span_warning("[toshow] draws [to_remove] from [resolve_parent]!"), span_notice("You draw [to_remove] from [resolve_parent]."))
+
+ return TRUE
+
+/// 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))
+ if(!silent)
+ to_chat(toshow, span_notice("You fumble for [toremove] and it falls on the floor."))
+ return TRUE
+
+/// Signal handler for whenever a mob walks away with us, close if they can't reach us.
+/datum/storage/proc/close_distance(datum/source)
+ SIGNAL_HANDLER
+
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ for(var/mob/user in can_see_contents())
+ if (!user.CanReach(resolve_parent))
+ hide_contents(user)
+
+/// Close the storage UI for everyone viewing us.
+/datum/storage/proc/close_all()
+ for(var/mob/user in is_using)
+ hide_contents(user)
+
+/// Refresh the views of everyone currently viewing the storage.
+/datum/storage/proc/refresh_views()
+ for (var/mob/user in can_see_contents())
+ show_contents(user)
+
+/// Checks who is currently capable of viewing our storage (and is.)
+/datum/storage/proc/can_see_contents()
+ var/list/seeing = list()
+ for (var/mob/user in is_using)
+ if(user.active_storage == src && user.client)
+ seeing += user
+ else
+ is_using -= user
+ return seeing
+
+/**
+ * Show our storage to a mob.
+ *
+ * @param mob/toshow the mob to show the storage to
+ */
+/datum/storage/proc/show_contents(mob/toshow)
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ if(!toshow.client)
+ return
+
+ if(toshow.active_storage != src && (toshow.stat == CONSCIOUS))
+ for(var/obj/item/thing in resolve_location)
+ if(thing.on_found(toshow))
+ toshow.active_storage.hide_contents(toshow)
+
+ if(toshow.active_storage)
+ toshow.active_storage.hide_contents(toshow)
+
+ toshow.active_storage = src
+
+ if(ismovable(resolve_location))
+ var/atom/movable/movable_loc = resolve_location
+ movable_loc.become_active_storage(src)
+
+ orient_to_hud()
+
+ is_using |= toshow
+
+ toshow.client.screen |= boxes
+ toshow.client.screen |= closer
+ toshow.client.screen |= resolve_location.contents
+
+/**
+ * Hide our storage from a mob.
+ *
+ * @param mob/toshow the mob to hide the storage from
+ */
+/datum/storage/proc/hide_contents(mob/toshow)
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ if(!toshow.client)
+ return TRUE
+ if(toshow.active_storage == src)
+ toshow.active_storage = null
+
+ if(!length(is_using) && ismovable(resolve_location))
+ var/atom/movable/movable_loc = resolve_location
+ movable_loc.lose_active_storage(src)
+
+ is_using -= toshow
+
+ toshow.client.screen -= boxes
+ toshow.client.screen -= closer
+ toshow.client.screen -= resolve_location.contents
+
+/datum/storage/proc/action_trigger(datum/signal_source, datum/action/source)
+ SIGNAL_HANDLER
+
+ toggle_collection_mode(source.owner)
+ return TRUE
+
+/**
+ * Toggles the collectmode of our storage.
+ *
+ * @param mob/toshow the mob toggling us
+ */
+/datum/storage/proc/toggle_collection_mode(mob/user)
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ collection_mode = (collection_mode+1)%3
+ switch(collection_mode)
+ if(COLLECT_SAME)
+ to_chat(user, span_notice("[resolve_parent] now picks up all items of a single type at once."))
+ if(COLLECT_EVERYTHING)
+ to_chat(user, span_notice("[resolve_parent] now picks up all items in a tile at once."))
+ if(COLLECT_ONE)
+ to_chat(user, span_notice("[resolve_parent] now picks up one item at a time."))
+
+/// Gives a spiffy animation to our parent to represent opening and closing.
+/datum/storage/proc/animate_parent()
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ animate(resolve_parent, time = 1.5, loop = 0, transform = matrix().Scale(1.07, 0.9))
+ animate(time = 2, transform = null)
diff --git a/code/datums/storage/subtypes/bag_of_holding.dm b/code/datums/storage/subtypes/bag_of_holding.dm
new file mode 100644
index 00000000000..b891cc6e68c
--- /dev/null
+++ b/code/datums/storage/subtypes/bag_of_holding.dm
@@ -0,0 +1,34 @@
+/datum/storage/bag_of_holding/attempt_insert(datum/source, obj/item/to_insert, mob/user, override, force)
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ var/list/obj/item/storage/backpack/holding/matching = typecache_filter_list(to_insert.get_all_contents(), typecacheof(/obj/item/storage/backpack/holding))
+ matching -= resolve_parent
+
+ if(istype(to_insert, /obj/item/storage/backpack/holding) || matching.len)
+ INVOKE_ASYNC(src, .proc/recursive_insertion, to_insert, user)
+ return
+
+ return ..()
+
+/datum/storage/bag_of_holding/proc/recursive_insertion(obj/item/to_insert, mob/living/user)
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ var/safety = tgui_alert(user, "Doing this will have extremely dire consequences for the station and its crew. Be sure you know what you're doing.", "Put in [to_insert.name]?", list("Proceed", "Abort"))
+ if(safety != "Proceed" || QDELETED(to_insert) || QDELETED(resolve_parent) || QDELETED(user) || !user.canUseTopic(resolve_parent, BE_CLOSE, iscarbon(user)))
+ return
+
+ var/turf/loccheck = get_turf(resolve_parent)
+ to_chat(user, span_danger("The Bluespace interfaces of the two devices catastrophically malfunction!"))
+ qdel(to_insert)
+ playsound(loccheck,'sound/effects/supermatter.ogg', 200, TRUE)
+
+ message_admins("[ADMIN_LOOKUPFLW(user)] detonated a bag of holding at [ADMIN_VERBOSEJMP(loccheck)].")
+ log_game("[key_name(user)] detonated a bag of holding at [loc_name(loccheck)].")
+
+ user.gib(TRUE, TRUE, TRUE)
+ new/obj/boh_tear(loccheck)
+ qdel(resolve_parent)
diff --git a/code/datums/storage/subtypes/cards.dm b/code/datums/storage/subtypes/cards.dm
new file mode 100644
index 00000000000..18f0d276bf9
--- /dev/null
+++ b/code/datums/storage/subtypes/cards.dm
@@ -0,0 +1,66 @@
+/**
+ *A storage component to be used on card piles, for use as hands/decks/discard piles. Don't use on something that's not a card pile!
+ */
+/datum/storage/tcg
+ max_specific_storage = WEIGHT_CLASS_TINY
+ max_slots = 30
+ max_total_storage = WEIGHT_CLASS_TINY * 30
+
+/datum/storage/tcg/New()
+ . = ..()
+ set_holdable(list(/obj/item/tcgcard))
+
+/datum/storage/tcg/attempt_remove(silent = FALSE)
+ . = ..()
+ handle_empty_deck()
+
+/datum/storage/tcg/show_contents(mob/to_show)
+ . = ..()
+
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ to_show.visible_message(span_notice("[to_show] starts to look through the contents of \the [resolve_parent]!"), \
+ span_notice("You begin looking into the contents of \the [resolve_parent]!"))
+
+/datum/storage/tcg/hide_contents()
+ . = ..()
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!resolve_location)
+ return
+
+ resolve_location.visible_message(span_notice("\the [resolve_parent] is shuffled after looking through it."))
+ resolve_location.contents = shuffle(resolve_location.contents)
+
+/datum/storage/tcg/remove_all()
+ . = ..()
+
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ if(!resolve_parent.contents.len)
+ qdel(resolve_parent)
+
+/datum/storage/tcg/proc/handle_empty_deck()
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ var/obj/item/resolve_location = real_location?.resolve()
+ if(!real_location)
+ return
+
+ //You can't have a deck of one card!
+ if(resolve_location.contents.len == 1)
+ var/obj/item/tcgcard_deck/deck = resolve_location
+ var/obj/item/tcgcard/card = resolve_location.contents[1]
+ attempt_remove(card, card.drop_location())
+ card.flipped = deck.flipped
+ card.update_icon_state()
+ qdel(resolve_parent)
diff --git a/code/datums/storage/subtypes/extract_inventory.dm b/code/datums/storage/subtypes/extract_inventory.dm
new file mode 100644
index 00000000000..9dfc07bb23c
--- /dev/null
+++ b/code/datums/storage/subtypes/extract_inventory.dm
@@ -0,0 +1,43 @@
+/datum/storage/extract_inventory
+ max_total_storage = WEIGHT_CLASS_TINY * 3
+ max_slots = 3
+ insert_preposition = "in"
+ attack_hand_interact = FALSE
+ quickdraw = FALSE
+ locked = TRUE
+ rustle_sound = FALSE
+ silent = TRUE
+
+/datum/storage/extract_inventory/New()
+ . = ..()
+ set_holdable(/obj/item/food/monkeycube)
+
+ var/obj/item/slimecross/reproductive/parentSlimeExtract = parent?.resolve()
+ if(!parentSlimeExtract)
+ return
+
+ if(!istype(parentSlimeExtract, /obj/item/slimecross/reproductive))
+ stack_trace("storage subtype extract_inventory incompatible with [parentSlimeExtract]")
+ qdel(src)
+
+/datum/storage/extract_inventory/proc/processCubes(mob/user)
+ var/obj/item/slimecross/reproductive/parentSlimeExtract = parent?.resolve()
+ if(!parentSlimeExtract)
+ return
+
+ message_admins(parentSlimeExtract.contents.len)
+ if(parentSlimeExtract.contents.len >= max_slots)
+ QDEL_LIST(parentSlimeExtract.contents)
+ createExtracts(user)
+
+/datum/storage/extract_inventory/proc/createExtracts(mob/user)
+ var/obj/item/slimecross/reproductive/parentSlimeExtract = parent?.resolve()
+ if(!parentSlimeExtract)
+ return
+
+ var/cores = rand(1,4)
+ playsound(parentSlimeExtract, 'sound/effects/splat.ogg', 40, TRUE)
+ parentSlimeExtract.last_produce = world.time
+ to_chat(user, span_notice("[parentSlimeExtract] briefly swells to a massive size, and expels [cores] extract[cores > 1 ? "s":""]!"))
+ for(var/i in 1 to cores)
+ new parentSlimeExtract.extract_type(parentSlimeExtract.drop_location())
diff --git a/code/datums/storage/subtypes/implant.dm b/code/datums/storage/subtypes/implant.dm
new file mode 100644
index 00000000000..998e19a587c
--- /dev/null
+++ b/code/datums/storage/subtypes/implant.dm
@@ -0,0 +1,10 @@
+/datum/storage/implant
+ max_specific_storage = WEIGHT_CLASS_NORMAL
+ max_total_storage = 6
+ max_slots = 2
+ silent = TRUE
+ allow_big_nesting = TRUE
+
+/datum/storage/implant/New()
+ . = ..()
+ set_holdable(cant_hold_list = list(/obj/item/disk/nuclear))
diff --git a/code/datums/components/storage/concrete/pockets.dm b/code/datums/storage/subtypes/pockets.dm
similarity index 63%
rename from code/datums/components/storage/concrete/pockets.dm
rename to code/datums/storage/subtypes/pockets.dm
index e6b6a3170f3..49d05155b2b 100644
--- a/code/datums/components/storage/concrete/pockets.dm
+++ b/code/datums/storage/subtypes/pockets.dm
@@ -1,28 +1,33 @@
-/datum/component/storage/concrete/pockets
- max_items = 2
- max_w_class = WEIGHT_CLASS_SMALL
- max_combined_w_class = 50
+/datum/storage/pockets
+ max_slots = 2
+ max_specific_storage = WEIGHT_CLASS_SMALL
+ max_total_storage = 50
rustle_sound = FALSE
-/datum/component/storage/concrete/pockets/handle_item_insertion(obj/item/I, prevent_warning, mob/user)
+/datum/storage/pockets/attempt_insert(datum/source, obj/item/to_insert, mob/user, override, force)
. = ..()
- if(. && silent && !prevent_warning)
+
+ var/obj/item/resolve_parent = parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ if(. && silent && !override)
if(quickdraw)
- to_chat(user, span_notice("You discreetly slip [I] into [parent]. Right-click [parent] to remove it."))
+ to_chat(user, span_notice("You discreetly slip [to_insert] into [resolve_parent]. Right-click [resolve_parent] to remove it."))
else
- to_chat(user, span_notice("You discreetly slip [I] into [parent]."))
+ to_chat(user, span_notice("You discreetly slip [to_insert] into [resolve_parent]."))
-/datum/component/storage/concrete/pockets/small
- max_items = 1
- max_w_class = WEIGHT_CLASS_SMALL
+/datum/storage/pockets/small
+ max_slots = 1
+ max_specific_storage = WEIGHT_CLASS_SMALL
attack_hand_interact = FALSE
-/datum/component/storage/concrete/pockets/tiny
- max_items = 1
- max_w_class = WEIGHT_CLASS_TINY
+/datum/storage/pockets/tiny
+ max_slots = 1
+ max_specific_storage = WEIGHT_CLASS_TINY
attack_hand_interact = FALSE
-/datum/component/storage/concrete/pockets/small/fedora/Initialize()
+/datum/storage/pockets/small/fedora/New()
. = ..()
var/static/list/exception_cache = typecacheof(list(
/obj/item/katana,
@@ -33,35 +38,36 @@
))
exception_hold = exception_cache
-/datum/component/storage/concrete/pockets/small/fedora/detective
+/datum/storage/pockets/small/fedora/detective
attack_hand_interact = TRUE // so the detectives would discover pockets in their hats
-/datum/component/storage/concrete/pockets/chefhat
+/datum/storage/pockets/chefhat
attack_hand_interact = TRUE
- max_items = 1
- max_w_class = WEIGHT_CLASS_NORMAL
+ max_slots = 1
+ max_specific_storage = WEIGHT_CLASS_NORMAL
-/datum/component/storage/concrete/pockets/chefhat/Initialize()
+/datum/storage/pockets/chefhat/New()
. = ..()
set_holdable(list(
/obj/item/clothing/head/mob_holder,
/obj/item/food/deadmouse
))
-/datum/component/storage/concrete/pockets/chefhat/can_be_inserted(obj/item/I, stop_messages, mob/M)
+/datum/storage/pockets/chefhat/can_insert(obj/item/to_insert, mob/user, messages, force)
. = ..()
- if(istype(I,/obj/item/clothing/head/mob_holder))
- var/obj/item/clothing/head/mob_holder/mausholder = I
+ if(istype(to_insert, /obj/item/clothing/head/mob_holder))
+ var/obj/item/clothing/head/mob_holder/mausholder = to_insert
if(locate(/mob/living/simple_animal/mouse) in mausholder.contents)
return
return FALSE
-/datum/component/storage/concrete/pockets/shoes
+/datum/storage/pockets/shoes
+ max_slots = 2
attack_hand_interact = FALSE
quickdraw = TRUE
silent = TRUE
-/datum/component/storage/concrete/pockets/shoes/Initialize()
+/datum/storage/pockets/shoes/New()
. = ..()
set_holdable(list(
/obj/item/knife,
@@ -92,7 +98,7 @@
/obj/item/toy/crayon/spraycan)
)
-/datum/component/storage/concrete/pockets/shoes/clown/Initialize()
+/datum/storage/pockets/shoes/clown/New()
. = ..()
set_holdable(list(
/obj/item/knife,
@@ -124,13 +130,11 @@
/obj/item/toy/crayon/spraycan)
)
-/datum/component/storage/concrete/pockets/pocketprotector
- max_items = 3
- max_w_class = WEIGHT_CLASS_TINY
- var/atom/original_parent
+/datum/storage/pockets/pocketprotector
+ max_slots = 3
+ max_specific_storage = WEIGHT_CLASS_TINY
-/datum/component/storage/concrete/pockets/pocketprotector/Initialize()
- original_parent = parent
+/datum/storage/pockets/pocketprotector/New()
. = ..()
set_holdable(list( //Same items as a PDA
/obj/item/pen,
@@ -140,15 +144,12 @@
/obj/item/clothing/mask/cigarette)
)
-/datum/component/storage/concrete/pockets/pocketprotector/real_location()
- // if the component is reparented to a jumpsuit, the items still go in the protector
- return original_parent
-
-/datum/component/storage/concrete/pockets/helmet
+/datum/storage/pockets/helmet
+ max_slots = 2
quickdraw = TRUE
- max_combined_w_class = 6
+ max_total_storage = 6
-/datum/component/storage/concrete/pockets/helmet/Initialize()
+/datum/storage/pockets/helmet/New()
. = ..()
set_holdable(list(/obj/item/reagent_containers/food/drinks/bottle/vodka,
/obj/item/reagent_containers/food/drinks/bottle/molotov,
@@ -156,12 +157,12 @@
/obj/item/ammo_box/a762))
-/datum/component/storage/concrete/pockets/void_cloak
+/datum/storage/pockets/void_cloak
quickdraw = TRUE
- max_combined_w_class = 5 // 2 small items + 1 tiny item, or 1 normal item + 1 small item
- max_items = 3
+ max_total_storage = 5 // 2 small items + 1 tiny item, or 1 normal item + 1 small item
+ max_slots = 3
-/datum/component/storage/concrete/pockets/void_cloak/Initialize()
+/datum/storage/pockets/void_cloak/New()
. = ..()
set_holdable(list(
/obj/item/ammo_box/a762/lionhunter,
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 229a18bc281..921228f93de 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -162,6 +162,9 @@
/// forensics datum, contains fingerprints, fibres, blood_dna and hiddenprints on this atom
var/datum/forensics/forensics
+ /// the datum handler for our contents - see create_storage() for creation method
+ var/datum/storage/atom_storage
+
/**
* Called when an atom is created in byond (built in engine proc)
*
@@ -314,6 +317,9 @@
if(forensics)
QDEL_NULL(forensics)
+ if(atom_storage)
+ QDEL_NULL(atom_storage)
+
orbiters = null // The component is attached to us normaly and will be deleted elsewhere
LAZYCLEARLIST(overlays)
@@ -327,6 +333,43 @@
return ..()
+/// A quick and easy way to create a storage datum for an atom
+/atom/proc/create_storage(
+ max_slots,
+ max_specific_storage,
+ max_total_storage,
+ numerical_stacking = FALSE,
+ allow_quick_gather = FALSE,
+ allow_quick_empty = FALSE,
+ collection_mode = COLLECT_ONE,
+ attack_hand_interact = TRUE,
+ list/canhold,
+ list/canthold,
+ type = /datum/storage,
+)
+
+ if(atom_storage)
+ QDEL_NULL(atom_storage)
+
+ atom_storage = new type(src, max_slots, max_specific_storage, max_total_storage, numerical_stacking, allow_quick_gather, collection_mode, attack_hand_interact)
+
+ if(canhold || canthold)
+ atom_storage.set_holdable(canhold, canthold)
+
+ return atom_storage
+
+/// A quick and easy way to /clone/ a storage datum for an atom (does not copy over contents, only the datum details)
+/atom/proc/clone_storage(datum/storage/cloning)
+ if(atom_storage)
+ QDEL_NULL(atom_storage)
+
+ atom_storage = new cloning.type(src, cloning.max_slots, cloning.max_specific_storage, cloning.max_total_storage, cloning.numerical_stacking, cloning.allow_quick_gather, cloning.collection_mode, cloning.attack_hand_interact)
+
+ if(cloning.can_hold || cloning.cant_hold)
+ atom_storage.set_holdable(cloning.can_hold, cloning.cant_hold)
+
+ return atom_storage
+
/atom/proc/handle_ricochet(obj/projectile/ricocheting_projectile)
var/turf/p_turf = get_turf(ricocheting_projectile)
var/face_direction = get_dir(src, p_turf)
@@ -991,8 +1034,8 @@
* TODO these should be purely component items that intercept the atom clicks higher in the
* call chain
*/
-/atom/proc/storage_contents_dump_act(obj/item/storage/src_object, mob/user)
- if(GetComponent(/datum/component/storage))
+/atom/proc/storage_contents_dump_act(obj/item/src_object, mob/user)
+ if(atom_storage)
return component_storage_contents_dump_act(src_object, user)
return FALSE
@@ -1005,19 +1048,17 @@
* TODO these should be purely component items that intercept the atom clicks higher in the
* call chain
*/
-/atom/proc/component_storage_contents_dump_act(datum/component/storage/src_object, mob/user)
- var/list/things = src_object.contents()
+/atom/proc/component_storage_contents_dump_act(obj/item/src_object, mob/user)
+ var/list/things = src_object.contents
var/datum/progressbar/progress = new(user, things.len, src)
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- while (do_after(user, 1 SECONDS, src, NONE, FALSE, CALLBACK(STR, /datum/component/storage.proc/handle_mass_item_insertion, things, src_object, user, progress)))
+ while (do_after(user, 1 SECONDS, src, NONE, FALSE, CALLBACK(src_object.atom_storage, /datum/storage.proc/handle_mass_transfer, user, src)))
stoplag(1)
progress.end_progress()
- to_chat(user, span_notice("You dump as much of [src_object.parent]'s contents [STR.insert_preposition]to [src] as you can."))
- STR.orient2hud(user)
- src_object.orient2hud(user)
+ to_chat(user, span_notice("You dump as much of [src_object]'s contents [atom_storage.insert_preposition]to [src] as you can."))
+ atom_storage.orient_to_hud(user)
+ src_object.atom_storage.orient_to_hud(user)
if(user.active_storage) //refresh the HUD to show the transfered contents
- user.active_storage.close(user)
- user.active_storage.show_to(user)
+ user.active_storage.refresh_views()
return TRUE
///Get the best place to dump the items contained in the source storage item?
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index ffc89ecbe69..f124448629d 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -880,17 +880,17 @@
UNSETEMPTY(movable_loc.important_recursive_contents)
///called when this movable becomes the parent of a storage component that is currently being viewed by a player. uses important_recursive_contents
-/atom/movable/proc/become_active_storage(datum/component/storage/component_source)
+/atom/movable/proc/become_active_storage(datum/storage/source)
if(!HAS_TRAIT(src, TRAIT_ACTIVE_STORAGE))
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
LAZYADDASSOCLIST(location.important_recursive_contents, RECURSIVE_CONTENTS_ACTIVE_STORAGE, src)
- ADD_TRAIT(src, TRAIT_ACTIVE_STORAGE, component_source)
+ ADD_TRAIT(src, TRAIT_ACTIVE_STORAGE, REF(source))
///called when this movable's storage component is no longer viewed by any players, unsets important_recursive_contents
-/atom/movable/proc/lose_active_storage(datum/component/storage/component_source)
+/atom/movable/proc/lose_active_storage(datum/storage/source)
if(!HAS_TRAIT(src, TRAIT_ACTIVE_STORAGE))
return
- REMOVE_TRAIT(src, TRAIT_ACTIVE_STORAGE, component_source)
+ REMOVE_TRAIT(src, TRAIT_ACTIVE_STORAGE, REF(source))
if(HAS_TRAIT(src, TRAIT_ACTIVE_STORAGE))
return
@@ -1198,12 +1198,12 @@
return blocker_opinion
/// called when this atom is removed from a storage item, which is passed on as S. The loc variable is already set to the new destination before this is called.
-/atom/movable/proc/on_exit_storage(datum/component/storage/concrete/master_storage)
- SEND_SIGNAL(src, COMSIG_STORAGE_EXITED, master_storage)
+/atom/movable/proc/on_exit_storage(datum/storage/master_storage)
+ return
/// called when this atom is added into a storage item, which is passed on as S. The loc variable is already set to the storage item.
-/atom/movable/proc/on_enter_storage(datum/component/storage/concrete/master_storage)
- SEND_SIGNAL(src, COMSIG_STORAGE_ENTERED, master_storage)
+/atom/movable/proc/on_enter_storage(datum/storage/master_storage)
+ return
/atom/movable/proc/get_spacemove_backup()
for(var/checked_range in orange(1, get_turf(src)))
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index c57ba341b3b..4add7d36756 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -924,10 +924,10 @@
var/obj/item/stack/secondary_inserted = new secondary_stack.merge_type(null,used_amt)
component_parts += secondary_inserted
else
- if(SEND_SIGNAL(replacer_tool, COMSIG_TRY_STORAGE_TAKE, secondary_part, src))
+ if(replacer_tool.atom_storage.attempt_remove(secondary_part, src))
component_parts += secondary_part
secondary_part.forceMove(src)
- SEND_SIGNAL(replacer_tool, COMSIG_TRY_STORAGE_INSERT, primary_part, null, null, TRUE)
+ replacer_tool.atom_storage.attempt_insert(replacer_tool, primary_part, user, TRUE)
component_parts -= primary_part
to_chat(user, span_notice("[capitalize(primary_part.name)] replaced with [secondary_part.name]."))
shouldplaysound = 1 //Only play the sound when parts are actually replaced!
diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index 2d4abb2dafa..6fa7295d49e 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -255,7 +255,7 @@
req_components[path] -= used_amt
else
added_components[part] = path
- if(SEND_SIGNAL(replacer, COMSIG_TRY_STORAGE_TAKE, part, src))
+ if(replacer.atom_storage.attempt_remove(part, src))
req_components[path]--
for(var/obj/item/part in added_components)
diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm
index 85f412ee8a4..88871ed632f 100644
--- a/code/game/machinery/launch_pad.dm
+++ b/code/game/machinery/launch_pad.dm
@@ -309,7 +309,7 @@
pad.update_indicator()
pad.closed = FALSE
user.transferItemToLoc(src, pad, TRUE)
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_HIDE_ALL)
+ atom_storage.close_all()
/obj/item/storage/briefcase/launchpad/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/launchpad_remote))
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index e5ce21c40b9..4d99dfdd626 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -544,7 +544,7 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
//If the item is in a storage item, take it out
- SEND_SIGNAL(loc, COMSIG_TRY_STORAGE_TAKE, src, user.loc, TRUE)
+ loc.atom_storage?.attempt_remove(src, user.loc, silent = TRUE)
if(QDELETED(src)) //moving it out of the storage to the floor destroyed it.
return
@@ -579,7 +579,7 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
return
//If the item is in a storage item, take it out
- SEND_SIGNAL(loc, COMSIG_TRY_STORAGE_TAKE, src, user.loc, TRUE)
+ loc.atom_storage?.attempt_remove(src, user.loc, silent = TRUE)
if(QDELETED(src)) //moving it out of the storage to the floor destroyed it.
return
@@ -768,9 +768,9 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
else
return
-/obj/item/on_exit_storage(datum/component/storage/concrete/master_storage)
+/obj/item/on_exit_storage(datum/storage/master_storage)
. = ..()
- var/atom/location = master_storage.real_location()
+ var/atom/location = master_storage.real_location?.resolve()
do_drop_animation(location)
/obj/item/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
@@ -819,8 +819,8 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
/obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/storage
if(!newLoc)
return FALSE
- if(SEND_SIGNAL(loc, COMSIG_CONTAINS_STORAGE))
- return SEND_SIGNAL(loc, COMSIG_TRY_STORAGE_TAKE, src, newLoc, TRUE)
+ if(loc.atom_storage)
+ return loc.atom_storage.attempt_remove(src, newLoc, silent = TRUE)
return FALSE
/// Returns the icon used for overlaying the object on a belt
@@ -971,7 +971,7 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
/obj/item/MouseEntered(location, control, params)
. = ..()
- if(get(src, /mob) == usr && !QDELETED(src))
+ if(((get(src, /mob) == usr) || src.loc.atom_storage || (src.item_flags & IN_STORAGE)) && !QDELETED(src))
var/mob/living/L = usr
if(usr.client.prefs.read_preference(/datum/preference/toggle/enable_tooltips))
var/timedelay = usr.client.prefs.read_preference(/datum/preference/numeric/tooltip_delay) / 100
@@ -992,8 +992,8 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
remove_filter("hover_outline")
/obj/item/proc/apply_outline(outline_color = null)
- if(get(src, /mob) != usr || QDELETED(src) || isobserver(usr)) //cancel if the item isn't in an inventory, is being deleted, or if the person hovering is a ghost (so that people spectating you don't randomly make your items glow)
- return
+ if(((get(src, /mob) != usr) && !src.loc.atom_storage && !(src.item_flags & IN_STORAGE)) || QDELETED(src) || isobserver(usr)) //cancel if the item isn't in an inventory, is being deleted, or if the person hovering is a ghost (so that people spectating you don't randomly make your items glow)
+ return FALSE
var/theme = lowertext(usr.client?.prefs?.read_preference(/datum/preference/choiced/ui_style))
if(!outline_color) //if we weren't provided with a color, take the theme's color
switch(theme) //yeah it kinda has to be this way
@@ -1345,7 +1345,7 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
return
/// Whether or not this item can be put into a storage item through attackby
-/obj/item/proc/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user)
+/obj/item/proc/attackby_storage_insert(datum/storage, atom/storage_holder, mob/user)
return TRUE
/obj/item/proc/do_pickup_animation(atom/target)
@@ -1385,6 +1385,9 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
if(!istype(loc, /turf))
return
+ if(!istype(moving_from))
+ return
+
var/turf/current_turf = get_turf(src)
var/direction = get_dir(moving_from, current_turf)
var/from_x = moving_from.base_pixel_x
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index d8ff5c984bc..8a3b81b265b 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -599,9 +599,7 @@
/obj/item/storage/crayons/Initialize(mapload)
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 7
- STR.set_holdable(list(/obj/item/toy/crayon))
+ create_storage(canhold = list(/obj/item/toy/crayon))
/obj/item/storage/crayons/PopulateContents()
new /obj/item/toy/crayon/red(src)
@@ -822,7 +820,7 @@
return SECONDARY_ATTACK_CONTINUE_CHAIN
-/obj/item/toy/crayon/spraycan/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user)
+/obj/item/toy/crayon/spraycan/attackby_storage_insert(datum/storage, atom/storage_holder, mob/user)
return is_capped
/obj/item/toy/crayon/spraycan/update_icon_state()
diff --git a/code/game/objects/items/devices/portable_chem_mixer.dm b/code/game/objects/items/devices/portable_chem_mixer.dm
index ba904a534eb..a5b634fe61a 100644
--- a/code/game/objects/items/devices/portable_chem_mixer.dm
+++ b/code/game/objects/items/devices/portable_chem_mixer.dm
@@ -18,13 +18,11 @@
///If the UI has the pH meter shown
var/show_ph = TRUE
-/obj/item/storage/portable_chem_mixer/ComponentInitialize()
+/obj/item/storage/portable_chem_mixer/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 200
- STR.max_items = 50
- STR.insert_preposition = "in"
- STR.set_holdable(list(
+ atom_storage.max_total_storage = 200
+ atom_storage.max_slots = 50
+ atom_storage.set_holdable(list(
/obj/item/reagent_containers/glass/beaker,
/obj/item/reagent_containers/glass/bottle,
/obj/item/reagent_containers/food/drinks/waterbottle,
@@ -40,8 +38,7 @@
return ..()
/obj/item/storage/portable_chem_mixer/attackby(obj/item/I, mob/user, params)
- var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
- if (istype(I, /obj/item/reagent_containers) && !(I.item_flags & ABSTRACT) && I.is_open_container() && locked)
+ if (istype(I, /obj/item/reagent_containers) && !(I.item_flags & ABSTRACT) && I.is_open_container() && atom_storage.locked)
var/obj/item/reagent_containers/B = I
. = TRUE //no afterattack
if(!user.transferItemToLoc(B, src))
@@ -70,7 +67,7 @@
return
/obj/item/storage/portable_chem_mixer/update_icon_state()
- if(!SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED))
+ if(!atom_storage.locked)
icon_state = "portablechemicalmixer_open"
return ..()
if(beaker)
@@ -81,8 +78,7 @@
/obj/item/storage/portable_chem_mixer/AltClick(mob/living/user)
- var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
- if(!locked)
+ if(!atom_storage.locked)
return ..()
if(!can_interact(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
@@ -90,11 +86,10 @@
update_appearance()
/obj/item/storage/portable_chem_mixer/CtrlClick(mob/living/user)
- var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, !locked)
- if (!locked)
+ atom_storage.locked = !atom_storage.locked
+ if (!atom_storage.locked)
update_contents()
- if (locked)
+ if (atom_storage.locked)
replace_beaker(user)
update_appearance()
playsound(src, 'sound/items/screwdriver2.ogg', 50)
@@ -122,17 +117,15 @@
if (loc != user)
return ..()
else
- var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
- if (!locked)
+ if (!atom_storage.locked)
return ..()
- if(SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED))
+ if(atom_storage?.locked)
ui_interact(user)
return
/obj/item/storage/portable_chem_mixer/attack_self(mob/user)
if(loc == user)
- var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
- if (locked)
+ if (atom_storage.locked)
ui_interact(user)
return
else
diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm
index 22e18207cc2..6e61c32c8ad 100644
--- a/code/game/objects/items/dice.dm
+++ b/code/game/objects/items/dice.dm
@@ -8,10 +8,8 @@
/obj/item/storage/dice/Initialize(mapload)
. = ..()
- var/datum/component/storage/storage = GetComponent(/datum/component/storage)
- storage.allow_quick_gather = TRUE
- storage.click_gather = TRUE
- storage.set_holdable(list(/obj/item/dice))
+ atom_storage.allow_quick_gather = TRUE
+ atom_storage.set_holdable(list(/obj/item/dice))
/obj/item/storage/dice/PopulateContents()
new /obj/item/dice/d4(src)
diff --git a/code/game/objects/items/implants/implant_storage.dm b/code/game/objects/items/implants/implant_storage.dm
index c0f1a7af754..a87451c4627 100644
--- a/code/game/objects/items/implants/implant_storage.dm
+++ b/code/game/objects/items/implants/implant_storage.dm
@@ -7,31 +7,34 @@
/obj/item/implant/storage/activate()
. = ..()
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SHOW, imp_in, TRUE)
+ atom_storage?.open_storage(src, imp_in)
/obj/item/implant/storage/removed(source, silent = FALSE, special = 0)
if(!special)
- var/datum/component/storage/lostimplant = GetComponent(/datum/component/storage/concrete/implant)
var/mob/living/implantee = source
- for (var/obj/item/I in lostimplant.contents())
+
+ var/atom/resolve_parent = atom_storage.parent?.resolve()
+ if(!resolve_parent)
+ return
+
+ for (var/obj/item/I in resolve_parent.contents)
I.add_mob_blood(implantee)
- lostimplant.do_quick_empty()
+ atom_storage.remove_all(implantee)
implantee.visible_message(span_warning("A bluespace pocket opens around [src] as it exits [implantee], spewing out its contents and rupturing the surrounding tissue!"))
implantee.apply_damage(20, BRUTE, BODY_ZONE_CHEST)
- qdel(lostimplant)
+ qdel(atom_storage)
return ..()
/obj/item/implant/storage/implant(mob/living/target, mob/user, silent = FALSE, force = FALSE)
for(var/X in target.implants)
if(istype(X, type))
var/obj/item/implant/storage/imp_e = X
- var/datum/component/storage/STR = imp_e.GetComponent(/datum/component/storage)
- if(!STR || (STR && STR.max_items < max_slot_stacking))
- imp_e.AddComponent(/datum/component/storage/concrete/implant)
+ if(!imp_e.atom_storage || (imp_e.atom_storage && imp_e.atom_storage.max_slots < max_slot_stacking))
+ imp_e.create_storage(type = /datum/storage/implant)
qdel(src)
return TRUE
return FALSE
- AddComponent(/datum/component/storage/concrete/implant)
+ create_storage(type = /datum/storage/implant)
return ..()
diff --git a/code/game/objects/items/mail.dm b/code/game/objects/items/mail.dm
index 54973e2916d..5d4459c77b4 100644
--- a/code/game/objects/items/mail.dm
+++ b/code/game/objects/items/mail.dm
@@ -268,14 +268,13 @@
worn_icon_state = "mailbag"
resistance_flags = FLAMMABLE
-/obj/item/storage/bag/mail/ComponentInitialize()
+/obj/item/storage/bag/mail/Initialize()
. = ..()
- var/datum/component/storage/storage = GetComponent(/datum/component/storage)
- storage.max_w_class = WEIGHT_CLASS_NORMAL
- storage.max_combined_w_class = 42
- storage.max_items = 21
- storage.display_numerical_stacking = FALSE
- storage.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 42
+ atom_storage.max_slots = 21
+ atom_storage.numerical_stacking = FALSE
+ atom_storage.set_holdable(list(
/obj/item/mail,
/obj/item/delivery/small,
/obj/item/paper
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index 3006069931f..fcbff5025f9 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -86,13 +86,13 @@
final_block_chance = 0 //Don't bring a sword to a gunfight
return ..()
-/obj/item/melee/sabre/on_exit_storage(datum/component/storage/concrete/container)
- var/obj/item/storage/belt/sabre/sabre = container.real_location()
+/obj/item/melee/sabre/on_exit_storage(datum/storage/container)
+ var/obj/item/storage/belt/sabre/sabre = container.real_location?.resolve()
if(istype(sabre))
playsound(sabre, 'sound/items/unsheath.ogg', 25, TRUE)
-/obj/item/melee/sabre/on_enter_storage(datum/component/storage/concrete/container)
- var/obj/item/storage/belt/sabre/sabre = container.real_location()
+/obj/item/melee/sabre/on_enter_storage(datum/storage/container)
+ var/obj/item/storage/belt/sabre/sabre = container.real_location?.resolve()
if(istype(sabre))
playsound(sabre, 'sound/items/sheath.ogg', 25, TRUE)
diff --git a/code/game/objects/items/religion.dm b/code/game/objects/items/religion.dm
index 9a8810ff350..2541d737bee 100644
--- a/code/game/objects/items/religion.dm
+++ b/code/game/objects/items/religion.dm
@@ -254,8 +254,7 @@
/obj/item/storage/backpack/bannerpack/Initialize(mapload)
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 27 //6 more then normal, for the tradeoff of declaring yourself an antag at all times.
+ atom_storage.max_total_storage = 27 //6 more then normal, for the tradeoff of declaring yourself an antag at all times.
/obj/item/storage/backpack/bannerpack/red
name = "red banner backpack"
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index ef097aa51b6..b67c15f12cf 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -670,6 +670,7 @@
var/obj/item/stack/F = new type(user? user : drop_location(), amount, FALSE, mats_per_unit)
. = F
F.copy_evidences(src)
+ loc.atom_storage?.refresh_views()
if(user)
if(!user.put_in_hands(F, merge_stacks = FALSE))
F.forceMove(user.drop_location())
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index 8a4bffbbfd4..2fa57439c32 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -21,21 +21,17 @@
resistance_flags = NONE
max_integrity = 300
-/obj/item/storage/backpack/ComponentInitialize()
+/obj/item/storage/backpack/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 21
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_items = 21
+ create_storage(max_slots = 21, max_total_storage = 21)
/*
* Backpack Types
*/
-/obj/item/storage/backpack/old/ComponentInitialize()
+/obj/item/storage/backpack/old/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 12
+ atom_storage.max_total_storage = 12
/obj/item/bag_of_holding_inert
name = "inert bag of holding"
@@ -57,14 +53,12 @@
resistance_flags = FIRE_PROOF
item_flags = NO_MAT_REDEMPTION
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 60, ACID = 50)
- component_type = /datum/component/storage/concrete/bluespace/bag_of_holding
-/obj/item/storage/backpack/holding/ComponentInitialize()
+/obj/item/storage/backpack/holding/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.allow_big_nesting = TRUE
- STR.max_w_class = WEIGHT_CLASS_GIGANTIC
- STR.max_combined_w_class = 35
+
+ create_storage(max_specific_storage = WEIGHT_CLASS_GIGANTIC, max_total_storage = 35, max_slots = 30, type = /datum/storage/bag_of_holding)
+ atom_storage.allow_big_nesting = TRUE
/obj/item/storage/backpack/holding/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is jumping into [src]! It looks like [user.p_theyre()] trying to commit suicide."))
@@ -85,11 +79,10 @@
. = ..()
regenerate_presents()
-/obj/item/storage/backpack/santabag/ComponentInitialize()
+/obj/item/storage/backpack/santabag/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 60
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 60
/obj/item/storage/backpack/santabag/suicide_act(mob/user)
user.visible_message(span_suicide("[user] places [src] over [user.p_their()] head and pulls it tight! It looks like [user.p_they()] [user.p_are()]n't in the Christmas spirit..."))
@@ -98,17 +91,14 @@
/obj/item/storage/backpack/santabag/proc/regenerate_presents()
addtimer(CALLBACK(src, .proc/regenerate_presents), 30 SECONDS)
- var/mob/M = get(loc, /mob)
- if(!istype(M))
+ var/mob/user = get(loc, /mob)
+ if(!istype(user))
return
- if(M.mind && HAS_TRAIT(M.mind, TRAIT_CANNOT_OPEN_PRESENTS))
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ if(user.mind && HAS_TRAIT(user.mind, TRAIT_CANNOT_OPEN_PRESENTS))
var/turf/floor = get_turf(src)
- var/obj/item/I = new /obj/item/a_gift/anything(floor)
- if(STR.can_be_inserted(I, stop_messages=TRUE))
- STR.handle_item_insertion(I, prevent_warning=TRUE)
- else
- qdel(I)
+ var/obj/item/thing = new /obj/item/a_gift/anything(floor)
+ if(!atom_storage.attempt_insert(src, thing, user, override = TRUE))
+ qdel(thing)
/obj/item/storage/backpack/cultpack
@@ -314,12 +304,8 @@
/obj/item/storage/backpack/satchel/flat/Initialize(mapload)
. = ..()
AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE, INVISIBILITY_OBSERVER, use_anchor = TRUE)
-
-/obj/item/storage/backpack/satchel/flat/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 15
- STR.set_holdable(null, list(/obj/item/storage/backpack/satchel/flat)) //muh recursive backpacks)
+ atom_storage.max_total_storage = 15
+ atom_storage.set_holdable(cant_hold_list = list(/obj/item/storage/backpack/satchel/flat)) //muh recursive backpacks)
/obj/item/storage/backpack/satchel/flat/PopulateContents()
var/datum/supply_pack/costumes_toys/randomised/contraband/C = new
@@ -345,10 +331,9 @@
inhand_icon_state = "duffel"
slowdown = 1
-/obj/item/storage/backpack/duffelbag/ComponentInitialize()
+/obj/item/storage/backpack/duffelbag/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 30
+ atom_storage.max_total_storage = 30
/obj/item/storage/backpack/duffelbag/cursed
name = "living duffel bag"
@@ -498,10 +483,9 @@
slowdown = 0
resistance_flags = FIRE_PROOF
-/obj/item/storage/backpack/duffelbag/syndie/ComponentInitialize()
+/obj/item/storage/backpack/duffelbag/syndie/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.silent = TRUE
+ atom_storage.silent = TRUE
/obj/item/storage/backpack/duffelbag/syndie/hitman
desc = "A large duffel bag for holding extra things. There is a Nanotrasen logo on the back."
@@ -655,11 +639,10 @@
new /obj/item/grenade/syndieminibomb(src)
// For ClownOps.
-/obj/item/storage/backpack/duffelbag/clown/syndie/ComponentInitialize()
+/obj/item/storage/backpack/duffelbag/clown/syndie/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
slowdown = 0
- STR.silent = TRUE
+ atom_storage.silent = TRUE
/obj/item/storage/backpack/duffelbag/clown/syndie/PopulateContents()
new /obj/item/modular_computer/tablet/pda/clown(src)
diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm
index af6eb003a55..8ffd47933a8 100644
--- a/code/game/objects/items/storage/bags.dm
+++ b/code/game/objects/items/storage/bags.dm
@@ -20,13 +20,11 @@
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_BULKY
-/obj/item/storage/bag/ComponentInitialize()
+/obj/item/storage/bag/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.allow_quick_gather = TRUE
- STR.allow_quick_empty = TRUE
- STR.display_numerical_stacking = TRUE
- STR.click_gather = TRUE
+ atom_storage.allow_quick_gather = TRUE
+ atom_storage.allow_quick_empty = TRUE
+ atom_storage.numerical_stacking = TRUE
// -----------------------------
// Trash bag
@@ -43,13 +41,12 @@
///If true, can be inserted into the janitor cart
var/insertable = TRUE
-/obj/item/storage/bag/trash/ComponentInitialize()
+/obj/item/storage/bag/trash/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_SMALL
- STR.max_combined_w_class = 30
- STR.max_items = 30
- STR.set_holdable(null, list(/obj/item/disk/nuclear))
+ atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
+ atom_storage.max_total_storage = 30
+ atom_storage.max_slots = 30
+ atom_storage.set_holdable(cant_hold_list = list(/obj/item/disk/nuclear))
/obj/item/storage/bag/trash/suicide_act(mob/user)
user.visible_message(span_suicide("[user] puts [src] over [user.p_their()] head and starts chomping at the insides! Disgusting!"))
@@ -87,11 +84,10 @@
inhand_icon_state = "bluetrashbag"
item_flags = NO_MAT_REDEMPTION
-/obj/item/storage/bag/trash/bluespace/ComponentInitialize()
+/obj/item/storage/bag/trash/bluespace/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 60
- STR.max_items = 60
+ atom_storage.max_total_storage = 60
+ atom_storage.max_slots = 60
/obj/item/storage/bag/trash/bluespace/cyborg
insertable = FALSE
@@ -108,17 +104,17 @@
worn_icon_state = "satchel"
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKETS
w_class = WEIGHT_CLASS_NORMAL
- component_type = /datum/component/storage/concrete/stack
var/spam_protection = FALSE //If this is TRUE, the holder won't receive any messages when they fail to pick up ore through crossing it
var/mob/listeningTo
-/obj/item/storage/bag/ore/ComponentInitialize()
+/obj/item/storage/bag/ore/Initialize(mapload)
. = ..()
- var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
- STR.allow_quick_empty = TRUE
- STR.set_holdable(list(/obj/item/stack/ore))
- STR.max_w_class = WEIGHT_CLASS_HUGE
- STR.max_combined_stack_amount = 50
+ atom_storage.max_specific_storage = WEIGHT_CLASS_HUGE
+ atom_storage.max_total_storage = 50
+ atom_storage.numerical_stacking = TRUE
+ atom_storage.allow_quick_empty = TRUE
+ atom_storage.allow_quick_gather = TRUE
+ atom_storage.set_holdable(list(/obj/item/stack/ore))
/obj/item/storage/bag/ore/equipped(mob/user)
. = ..()
@@ -126,7 +122,7 @@
return
if(listeningTo)
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
- RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/Pickup_ores)
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/pickup_ores)
listeningTo = user
/obj/item/storage/bag/ore/dropped()
@@ -135,24 +131,27 @@
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
listeningTo = null
-/obj/item/storage/bag/ore/proc/Pickup_ores(mob/living/user)
+/obj/item/storage/bag/ore/proc/pickup_ores(mob/living/user)
SIGNAL_HANDLER
+
var/show_message = FALSE
var/obj/structure/ore_box/box
- var/turf/tile = user.loc
- if (!isturf(tile))
+ var/turf/tile = get_turf(user)
+
+ if(!isturf(tile))
return
- if (istype(user.pulling, /obj/structure/ore_box))
+
+ if(istype(user.pulling, /obj/structure/ore_box))
box = user.pulling
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- if(STR)
- for(var/A in tile)
- if (!is_type_in_typecache(A, STR.can_hold))
+
+ if(atom_storage)
+ for(var/thing in tile)
+ if(!is_type_in_typecache(thing, atom_storage.can_hold))
continue
- if (box)
- user.transferItemToLoc(A, box)
+ if(box)
+ user.transferItemToLoc(thing, box)
show_message = TRUE
- else if(SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, A, user, TRUE))
+ else if(atom_storage.attempt_insert(src, thing, user))
show_message = TRUE
else
if(!spam_protection)
@@ -161,12 +160,14 @@
continue
if(show_message)
playsound(user, SFX_RUSTLE, 50, TRUE)
+
if (box)
user.visible_message(span_notice("[user] offloads the ores beneath [user.p_them()] into [box]."), \
- span_notice("You offload the ores beneath you into your [box]."))
+ span_notice("You offload the ores beneath you into your [box]."))
else
user.visible_message(span_notice("[user] scoops up the ores beneath [user.p_them()]."), \
span_notice("You scoop up the ores beneath you with your [name]."))
+
spam_protection = FALSE
/obj/item/storage/bag/ore/cyborg
@@ -177,12 +178,11 @@
desc = "A revolution in convenience, this satchel allows for huge amounts of ore storage. It's been outfitted with anti-malfunction safety measures."
icon_state = "satchel_bspace"
-/obj/item/storage/bag/ore/holding/ComponentInitialize()
+/obj/item/storage/bag/ore/holding/Initialize()
. = ..()
- var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
- STR.max_items = INFINITY
- STR.max_combined_w_class = INFINITY
- STR.max_combined_stack_amount = INFINITY
+ atom_storage.max_slots = INFINITY
+ atom_storage.max_specific_storage = INFINITY
+ atom_storage.max_total_storage = INFINITY
// -----------------------------
// Plant bag
@@ -195,13 +195,12 @@
worn_icon_state = "plantbag"
resistance_flags = FLAMMABLE
-/obj/item/storage/bag/plants/ComponentInitialize()
+/obj/item/storage/bag/plants/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 100
- STR.max_items = 100
- STR.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 100
+ atom_storage.max_slots = 100
+ atom_storage.set_holdable(list(
/obj/item/food/grown,
/obj/item/graft,
/obj/item/grown,
@@ -243,8 +242,7 @@
// -----------------------------
// Sheet Snatcher
// -----------------------------
-// Because it stacks stacks, this doesn't operate normally.
-// However, making it a storage/bag allows us to reuse existing code in some places. -Sayu
+// sorry sayu your sheet snatcher is now OBSOLETE but i'm leaving it because idc
/obj/item/storage/bag/sheetsnatcher
name = "sheet snatcher"
@@ -254,20 +252,20 @@
worn_icon_state = "satchel"
var/capacity = 300; //the number of sheets it can carry.
- component_type = /datum/component/storage/concrete/stack
-/obj/item/storage/bag/sheetsnatcher/ComponentInitialize()
+/obj/item/storage/bag/sheetsnatcher/Initialize()
. = ..()
- var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
- STR.allow_quick_empty = TRUE
- STR.set_holdable(list(
+ atom_storage.allow_quick_empty = TRUE
+ atom_storage.allow_quick_gather = TRUE
+ atom_storage.numerical_stacking = TRUE
+ atom_storage.set_holdable(list(
/obj/item/stack/sheet
),
list(
/obj/item/stack/sheet/mineral/sandstone,
/obj/item/stack/sheet/mineral/wood,
))
- STR.max_combined_stack_amount = 300
+ atom_storage.max_total_storage = capacity / 2
// -----------------------------
// Sheet Snatcher (Cyborg)
@@ -278,11 +276,6 @@
desc = ""
capacity = 500//Borgs get more because >specialization
-/obj/item/storage/bag/sheetsnatcher/borg/ComponentInitialize()
- . = ..()
- var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
- STR.max_combined_stack_amount = 500
-
// -----------------------------
// Book bag
// -----------------------------
@@ -295,14 +288,12 @@
worn_icon_state = "bookbag"
resistance_flags = FLAMMABLE
-/obj/item/storage/bag/books/ComponentInitialize()
+/obj/item/storage/bag/books/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 21
- STR.max_items = 7
- STR.display_numerical_stacking = FALSE
- STR.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 21
+ atom_storage.max_slots = 7
+ atom_storage.set_holdable(list(
/obj/item/book,
/obj/item/spellbook,
/obj/item/storage/book,
@@ -326,11 +317,10 @@
custom_materials = list(/datum/material/iron=3000)
custom_price = PAYCHECK_CREW * 0.6
-/obj/item/storage/bag/tray/ComponentInitialize()
+/obj/item/storage/bag/tray/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_BULKY //Plates are required bulky to keep them out of backpacks
- STR.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_BULKY //Plates are required bulky to keep them out of backpacks
+ atom_storage.set_holdable(list(
/obj/item/clothing/mask/cigarette,
/obj/item/food,
/obj/item/kitchen,
@@ -345,14 +335,14 @@
/obj/item/storage/fancy,
/obj/item/trash,
)) //Should cover: Bottles, Beakers, Bowls, Booze, Glasses, Food, Food Containers, Food Trash, Organs, Tobacco Products, Lighters, and Kitchen Tools.
- STR.insert_preposition = "on"
- STR.max_items = 7
+ atom_storage.insert_preposition = "on"
+ atom_storage.max_slots = 7
/obj/item/storage/bag/tray/attack(mob/living/M, mob/living/user)
. = ..()
// Drop all the things. All of them.
var/list/obj/item/oldContents = contents.Copy()
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_QUICK_EMPTY)
+ atom_storage.remove_all(user)
// Make each item scatter a bit
for(var/obj/item/tray_item in oldContents)
do_scatter(tray_item)
@@ -414,13 +404,11 @@
desc = "A bag for storing pills, patches, and bottles."
resistance_flags = FLAMMABLE
-/obj/item/storage/bag/chemistry/ComponentInitialize()
+/obj/item/storage/bag/chemistry/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 200
- STR.max_items = 50
- STR.insert_preposition = "in"
- STR.set_holdable(list(
+ atom_storage.max_total_storage = 200
+ atom_storage.max_slots = 50
+ atom_storage.set_holdable(list(
/obj/item/reagent_containers/chem_pack,
/obj/item/reagent_containers/dropper,
/obj/item/reagent_containers/food/drinks/waterbottle,
@@ -443,13 +431,11 @@
desc = "A bag for the safe transportation and disposal of biowaste and other virulent materials."
resistance_flags = FLAMMABLE
-/obj/item/storage/bag/bio/ComponentInitialize()
+/obj/item/storage/bag/bio/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 200
- STR.max_items = 25
- STR.insert_preposition = "in"
- STR.set_holdable(list(
+ atom_storage.max_total_storage = 200
+ atom_storage.max_slots = 25
+ atom_storage.set_holdable(list(
/obj/item/bodypart,
/obj/item/food/monkeycube,
/obj/item/healthanalyzer,
@@ -474,13 +460,11 @@
desc = "A bag for the storage and transport of anomalous materials."
resistance_flags = FLAMMABLE
-/obj/item/storage/bag/xeno/ComponentInitialize()
+/obj/item/storage/bag/xeno/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 200
- STR.max_items = 25
- STR.insert_preposition = "in"
- STR.set_holdable(list(
+ atom_storage.max_total_storage = 200
+ atom_storage.max_slots = 25
+ atom_storage.set_holdable(list(
/obj/item/bodypart,
/obj/item/food/deadmouse,
/obj/item/food/monkeycube,
@@ -507,14 +491,12 @@
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKETS
resistance_flags = FLAMMABLE
-/obj/item/storage/bag/construction/ComponentInitialize()
+/obj/item/storage/bag/construction/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 100
- STR.max_items = 50
- STR.max_w_class = WEIGHT_CLASS_SMALL
- STR.insert_preposition = "in"
- STR.set_holdable(list(
+ atom_storage.max_total_storage = 100
+ atom_storage.max_slots = 50
+ atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
+ atom_storage.set_holdable(list(
/obj/item/assembly,
/obj/item/circuitboard,
/obj/item/electronics,
@@ -532,13 +514,12 @@
inhand_icon_state = "quiver"
worn_icon_state = "harpoon_quiver"
-/obj/item/storage/bag/harpoon_quiver/ComponentInitialize()
+/obj/item/storage/bag/harpoon_quiver/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_TINY
- STR.max_items = 40
- STR.max_combined_w_class = 100
- STR.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_TINY
+ atom_storage.max_slots = 40
+ atom_storage.max_total_storage = 100
+ atom_storage.set_holdable(list(
/obj/item/ammo_casing/caseless/harpoon
))
diff --git a/code/game/objects/items/storage/basket.dm b/code/game/objects/items/storage/basket.dm
index faf11e2c828..73904d99ab6 100644
--- a/code/game/objects/items/storage/basket.dm
+++ b/code/game/objects/items/storage/basket.dm
@@ -5,8 +5,7 @@
w_class = WEIGHT_CLASS_BULKY
resistance_flags = FLAMMABLE
-/obj/item/storage/basket/ComponentInitialize()
+/obj/item/storage/basket/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 21
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 21
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 9ecfa0a5822..3fc62d89f34 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -41,12 +41,11 @@
drop_sound = 'sound/items/handling/toolbelt_drop.ogg'
pickup_sound = 'sound/items/handling/toolbelt_pickup.ogg'
-/obj/item/storage/belt/utility/ComponentInitialize()
+/obj/item/storage/belt/utility/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 21
- STR.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 21
+ atom_storage.set_holdable(list(
/obj/item/airlock_painter,
/obj/item/analyzer,
/obj/item/assembly/signaler,
@@ -206,12 +205,11 @@
inhand_icon_state = "medical"
worn_icon_state = "medical"
-/obj/item/storage/belt/medical/ComponentInitialize()
+/obj/item/storage/belt/medical/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 21
- STR.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 21
+ atom_storage.set_holdable(list(
/obj/item/bikehorn/rubberducky,
/obj/item/blood_filter,
/obj/item/bonesetter,
@@ -320,12 +318,11 @@
worn_icon_state = "security"
content_overlays = TRUE
-/obj/item/storage/belt/security/ComponentInitialize()
+/obj/item/storage/belt/security/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 5
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.set_holdable(list(
+ atom_storage.max_slots = 5
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.set_holdable(list(
/obj/item/ammo_box,
/obj/item/ammo_casing/shotgun,
/obj/item/assembly/flash/handheld,
@@ -360,10 +357,9 @@
content_overlays = FALSE
custom_premium_price = PAYCHECK_COMMAND * 3
-/obj/item/storage/belt/security/webbing/ComponentInitialize()
+/obj/item/storage/belt/security/webbing/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
+ atom_storage.max_slots = 6
/obj/item/storage/belt/mining
name = "explorer's webbing"
@@ -373,13 +369,12 @@
worn_icon_state = "explorer1"
w_class = WEIGHT_CLASS_BULKY
-/obj/item/storage/belt/mining/ComponentInitialize()
+/obj/item/storage/belt/mining/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 20
- STR.set_holdable(list(
+ atom_storage.max_slots = 6
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 20
+ atom_storage.set_holdable(list(
/obj/item/analyzer,
/obj/item/clothing/gloves,
/obj/item/crowbar,
@@ -435,10 +430,9 @@
inhand_icon_state = "ebelt"
worn_icon_state = "ebelt"
-/obj/item/storage/belt/mining/primitive/ComponentInitialize()
+/obj/item/storage/belt/mining/primitive/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 5
+ atom_storage.max_slots = 5
/obj/item/storage/belt/soulstone
name = "soul stone belt"
@@ -447,11 +441,10 @@
inhand_icon_state = "soulstonebelt"
worn_icon_state = "soulstonebelt"
-/obj/item/storage/belt/soulstone/ComponentInitialize()
+/obj/item/storage/belt/soulstone/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.set_holdable(list(
+ atom_storage.max_slots = 6
+ atom_storage.set_holdable(list(
/obj/item/soulstone
))
@@ -471,11 +464,10 @@
worn_icon_state = "championbelt"
custom_materials = list(/datum/material/gold=400)
-/obj/item/storage/belt/champion/ComponentInitialize()
+/obj/item/storage/belt/champion/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 1
- STR.set_holdable(list(
+ atom_storage.max_slots = 1
+ atom_storage.set_holdable(list(
/obj/item/clothing/mask/luchador
))
@@ -494,10 +486,9 @@
worn_icon_state = "militarywebbing"
resistance_flags = FIRE_PROOF
-/obj/item/storage/belt/military/ComponentInitialize()
+/obj/item/storage/belt/military/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_SMALL
+ atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
/obj/item/storage/belt/military/snack
name = "tactical snack rig"
@@ -507,12 +498,11 @@
var/sponsor = pick("Donk Co.", "Waffle Co.", "Roffle Co.", "Gorlax Marauders", "Tiger Cooperative")
desc = "A set of snack-tical webbing worn by athletes of the [sponsor] VR sports division."
-/obj/item/storage/belt/military/snack/ComponentInitialize()
+/obj/item/storage/belt/military/snack/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.max_w_class = WEIGHT_CLASS_SMALL
- STR.set_holdable(list(
+ atom_storage.max_slots = 6
+ atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
+ atom_storage.set_holdable(list(
/obj/item/food,
/obj/item/reagent_containers/food/drinks
))
@@ -578,10 +568,9 @@
inhand_icon_state = "security"
worn_icon_state = "assault"
-/obj/item/storage/belt/military/assault/ComponentInitialize()
+/obj/item/storage/belt/military/assault/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
+ atom_storage.max_slots = 6
/obj/item/storage/belt/military/assault/full/PopulateContents()
generate_items_inside(list(
@@ -596,14 +585,13 @@
inhand_icon_state = "security"
worn_icon_state = "grenadebeltnew"
-/obj/item/storage/belt/grenade/ComponentInitialize()
+/obj/item/storage/belt/grenade/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 30
- STR.display_numerical_stacking = TRUE
- STR.max_combined_w_class = 60
- STR.max_w_class = WEIGHT_CLASS_BULKY
- STR.set_holdable(list(
+ atom_storage.max_slots = 30
+ atom_storage.numerical_stacking = TRUE
+ atom_storage.max_total_storage = 60
+ atom_storage.max_specific_storage = WEIGHT_CLASS_BULKY
+ atom_storage.set_holdable(list(
/obj/item/food/grown/cherry_bomb,
/obj/item/food/grown/firelemon,
/obj/item/grenade,
@@ -636,11 +624,10 @@
inhand_icon_state = "soulstonebelt"
worn_icon_state = "soulstonebelt"
-/obj/item/storage/belt/wands/ComponentInitialize()
+/obj/item/storage/belt/wands/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.set_holdable(list(
+ atom_storage.max_slots = 6
+ atom_storage.set_holdable(list(
/obj/item/gun/magic/wand
))
@@ -663,12 +650,11 @@
inhand_icon_state = "janibelt"
worn_icon_state = "janibelt"
-/obj/item/storage/belt/janitor/ComponentInitialize()
+/obj/item/storage/belt/janitor/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.max_w_class = WEIGHT_CLASS_NORMAL // Set to this so the light replacer can fit.
- STR.set_holdable(list(
+ atom_storage.max_slots = 6
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL // Set to this so the light replacer can fit.
+ atom_storage.set_holdable(list(
/obj/item/assembly/mousetrap,
/obj/item/clothing/gloves,
/obj/item/flashlight,
@@ -699,13 +685,12 @@
inhand_icon_state = "bandolier"
worn_icon_state = "bandolier"
-/obj/item/storage/belt/bandolier/ComponentInitialize()
+/obj/item/storage/belt/bandolier/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 18
- STR.max_combined_w_class = 18
- STR.display_numerical_stacking = TRUE
- STR.set_holdable(list(
+ atom_storage.max_slots = 18
+ atom_storage.max_total_storage = 18
+ atom_storage.numerical_stacking = TRUE
+ atom_storage.set_holdable(list(
/obj/item/ammo_casing/a762,
/obj/item/ammo_casing/shotgun,
))
@@ -719,11 +704,10 @@
dying_key = DYE_REGISTRY_FANNYPACK
custom_price = PAYCHECK_CREW * 2
-/obj/item/storage/belt/fannypack/ComponentInitialize()
+/obj/item/storage/belt/fannypack/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 3
- STR.max_w_class = WEIGHT_CLASS_SMALL
+ atom_storage.max_slots = 3
+ atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
/obj/item/storage/belt/fannypack/black
name = "black fannypack"
@@ -793,14 +777,14 @@
worn_icon_state = "sheath"
w_class = WEIGHT_CLASS_BULKY
-/obj/item/storage/belt/sabre/ComponentInitialize()
+/obj/item/storage/belt/sabre/Initialize()
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 1
- STR.rustle_sound = FALSE
- STR.max_w_class = WEIGHT_CLASS_BULKY
- STR.set_holdable(list(
+
+ atom_storage.max_slots = 1
+ atom_storage.rustle_sound = FALSE
+ atom_storage.max_specific_storage = WEIGHT_CLASS_BULKY
+ atom_storage.set_holdable(list(
/obj/item/melee/sabre,
))
@@ -842,12 +826,11 @@
worn_icon_state = "plantbelt"
content_overlays = TRUE
-/obj/item/storage/belt/plant/ComponentInitialize()
+/obj/item/storage/belt/plant/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.set_holdable(list(
+ atom_storage.max_slots = 6
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.set_holdable(list(
/obj/item/cultivator,
/obj/item/geneshears,
/obj/item/graft,
diff --git a/code/game/objects/items/storage/book.dm b/code/game/objects/items/storage/book.dm
index b6e296ccd1a..e2a7f040bc9 100644
--- a/code/game/objects/items/storage/book.dm
+++ b/code/game/objects/items/storage/book.dm
@@ -10,10 +10,9 @@
resistance_flags = FLAMMABLE
var/title = "book"
-/obj/item/storage/book/ComponentInitialize()
+/obj/item/storage/book/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 1
+ atom_storage.max_slots = 1
/obj/item/storage/book/attack_self(mob/user)
to_chat(user, span_notice("The pages of [title] have been cut out!"))
diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm
index 8e15a8ee4ae..83dacd2aace 100644
--- a/code/game/objects/items/storage/boxes.dm
+++ b/code/game/objects/items/storage/boxes.dm
@@ -462,10 +462,9 @@
for(var/i in 1 to 6)
new donktype(src)
-/obj/item/storage/box/donkpockets/ComponentInitialize()
+/obj/item/storage/box/donkpockets/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.set_holdable(list(/obj/item/food/donkpocket))
+ atom_storage.set_holdable(list(/obj/item/food/donkpocket))
/obj/item/storage/box/donkpockets/donkpocketspicy
name = "box of spicy-flavoured donk-pockets"
@@ -504,11 +503,10 @@
illustration = null
var/cube_type = /obj/item/food/monkeycube
-/obj/item/storage/box/monkeycubes/ComponentInitialize()
+/obj/item/storage/box/monkeycubes/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 7
- STR.set_holdable(list(/obj/item/food/monkeycube))
+ atom_storage.max_slots = 7
+ atom_storage.set_holdable(list(/obj/item/food/monkeycube))
/obj/item/storage/box/monkeycubes/PopulateContents()
for(var/i in 1 to 5)
@@ -524,11 +522,10 @@
icon_state = "monkeycubebox"
illustration = null
-/obj/item/storage/box/gorillacubes/ComponentInitialize()
+/obj/item/storage/box/gorillacubes/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 3
- STR.set_holdable(list(/obj/item/food/monkeycube))
+ atom_storage.max_slots = 3
+ atom_storage.set_holdable(list(/obj/item/food/monkeycube))
/obj/item/storage/box/gorillacubes/PopulateContents()
for(var/i in 1 to 3)
@@ -682,15 +679,15 @@
icon_state = "spbox"
illustration = ""
-/obj/item/storage/box/snappops/ComponentInitialize()
+/obj/item/storage/box/snappops/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.set_holdable(list(/obj/item/toy/snappop))
- STR.max_items = 8
+ atom_storage.set_holdable(list(/obj/item/toy/snappop))
+ atom_storage.max_slots = 8
/obj/item/storage/box/snappops/PopulateContents()
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_FILL_TYPE, /obj/item/toy/snappop)
-
+ for(var/i in 1 to 8)
+ new /obj/item/toy/snappop(src)
+
/obj/item/storage/box/matches
name = "matchbox"
desc = "A small box of Almost But Not Quite Plasma Premium Matches."
@@ -706,14 +703,14 @@
base_icon_state = "matchbox"
illustration = null
-/obj/item/storage/box/matches/ComponentInitialize()
+/obj/item/storage/box/matches/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 10
- STR.set_holdable(list(/obj/item/match))
+ atom_storage.max_slots = 10
+ atom_storage.set_holdable(list(/obj/item/match))
/obj/item/storage/box/matches/PopulateContents()
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_FILL_TYPE, /obj/item/match)
+ for(var/i in 1 to 10)
+ new /obj/item/match(src)
/obj/item/storage/box/matches/attackby(obj/item/match/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/match))
@@ -741,13 +738,12 @@
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
foldable = /obj/item/stack/sheet/cardboard //BubbleWrap
-/obj/item/storage/box/lights/ComponentInitialize()
+/obj/item/storage/box/lights/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 21
- STR.set_holdable(list(/obj/item/light/tube, /obj/item/light/bulb))
- STR.max_combined_w_class = 21
- STR.click_gather = FALSE //temp workaround to re-enable filling the light replacer with the box
+ atom_storage.max_slots = 21
+ atom_storage.set_holdable(list(/obj/item/light/tube, /obj/item/light/bulb))
+ atom_storage.max_total_storage = 21
+ atom_storage.allow_quick_gather = FALSE //temp workaround to re-enable filling the light replacer with the box
/obj/item/storage/box/lights/bulbs/PopulateContents()
for(var/i in 1 to 21)
@@ -1239,11 +1235,10 @@
foldable = null
custom_price = PAYCHECK_CREW
-/obj/item/storage/box/gum/ComponentInitialize()
+/obj/item/storage/box/gum/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.set_holdable(list(/obj/item/food/bubblegum))
- STR.max_items = 4
+ atom_storage.set_holdable(list(/obj/item/food/bubblegum))
+ atom_storage.max_slots = 4
/obj/item/storage/box/gum/PopulateContents()
for(var/i in 1 to 4)
diff --git a/code/game/objects/items/storage/briefcase.dm b/code/game/objects/items/storage/briefcase.dm
index 3390fb4fd87..1edca978656 100644
--- a/code/game/objects/items/storage/briefcase.dm
+++ b/code/game/objects/items/storage/briefcase.dm
@@ -16,11 +16,10 @@
max_integrity = 150
var/folder_path = /obj/item/folder //this is the path of the folder that gets spawned in New()
-/obj/item/storage/briefcase/ComponentInitialize()
+/obj/item/storage/briefcase/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 21
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 21
/obj/item/storage/briefcase/PopulateContents()
new /obj/item/pen(src)
diff --git a/code/game/objects/items/storage/fancy.dm b/code/game/objects/items/storage/fancy.dm
index bdc8d1b461c..b8193f0c74c 100644
--- a/code/game/objects/items/storage/fancy.dm
+++ b/code/game/objects/items/storage/fancy.dm
@@ -21,16 +21,22 @@
var/contents_tag = "errors"
/// What type of thing to fill this storage with.
var/spawn_type = null
+ /// How many of the things to fill this storage with.
+ var/spawn_count = 0
/// Whether the container is open or not
var/is_open = FALSE
/// What this container folds up into when it's empty.
var/obj/fold_result = /obj/item/stack/sheet/cardboard
+/obj/item/storage/fancy/Initialize()
+ . = ..()
+
+ atom_storage.max_slots = spawn_count
+
/obj/item/storage/fancy/PopulateContents()
if(!spawn_type)
return
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- for(var/i = 1 to STR.max_items)
+ for(var/i = 1 to spawn_count)
new spawn_type(src)
/obj/item/storage/fancy/update_icon_state()
@@ -80,16 +86,15 @@
icon_state = "donutbox_open" //composite image used for mapping
base_icon_state = "donutbox"
spawn_type = /obj/item/food/donut/plain
+ spawn_count = 6
is_open = TRUE
appearance_flags = KEEP_TOGETHER|LONG_GLIDE
custom_premium_price = PAYCHECK_COMMAND * 1.75
contents_tag = "donut"
-/obj/item/storage/fancy/donut_box/ComponentInitialize()
+/obj/item/storage/fancy/donut_box/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.set_holdable(list(/obj/item/food/donut))
+ atom_storage.set_holdable(list(/obj/item/food/donut))
/obj/item/storage/fancy/donut_box/PopulateContents()
. = ..()
@@ -131,13 +136,12 @@
name = "egg box"
desc = "A carton for containing eggs."
spawn_type = /obj/item/food/egg
+ spawn_count = 12
contents_tag = "egg"
-/obj/item/storage/fancy/egg_box/ComponentInitialize()
+/obj/item/storage/fancy/egg_box/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 12
- STR.set_holdable(list(/obj/item/food/egg))
+ atom_storage.set_holdable(list(/obj/item/food/egg))
/*
* Candle Box
@@ -154,14 +158,10 @@
throwforce = 2
slot_flags = ITEM_SLOT_BELT
spawn_type = /obj/item/candle
+ spawn_count = 5
is_open = TRUE
contents_tag = "candle"
-/obj/item/storage/fancy/candle_box/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 5
-
/obj/item/storage/fancy/candle_box/attack_self(mob/user)
if(!contents.len)
new fold_result(user.drop_location())
@@ -184,6 +184,7 @@
throwforce = 0
slot_flags = ITEM_SLOT_BELT
spawn_type = /obj/item/clothing/mask/cigarette/space_cigarette
+ spawn_count = 6
custom_price = PAYCHECK_CREW
age_restricted = TRUE
contents_tag = "cigarette"
@@ -208,15 +209,13 @@
spawn_coupon = FALSE
name = "discarded cigarette packet"
desc = "An old cigarette packet with the back torn off, worth less than nothing now."
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 0
+ atom_storage.max_slots = 0
return
-/obj/item/storage/fancy/cigarettes/ComponentInitialize()
+/obj/item/storage/fancy/cigarettes/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.set_holdable(list(/obj/item/clothing/mask/cigarette, /obj/item/lighter))
+ atom_storage.quickdraw = TRUE
+ atom_storage.set_holdable(list(/obj/item/clothing/mask/cigarette, /obj/item/lighter))
/obj/item/storage/fancy/cigarettes/examine(mob/user)
. = ..()
@@ -225,18 +224,6 @@
if(spawn_coupon)
. += span_notice("There's a coupon on the back of the pack! You can tear it off once it's empty.")
-/obj/item/storage/fancy/cigarettes/AltClick(mob/user)
- if(!user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, FALSE, TRUE))
- return
- var/obj/item/clothing/mask/cigarette/W = locate(/obj/item/clothing/mask/cigarette) in contents
- if(W && contents.len > 0)
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_TAKE, W, user)
- user.put_in_hands(W)
- contents -= W
- to_chat(user, span_notice("You take \a [W] out of the pack."))
- else
- to_chat(user, span_notice("There are no [contents_tag]s left in the pack."))
-
/obj/item/storage/fancy/cigarettes/update_icon_state()
. = ..()
icon_state = "[base_icon_state][contents.len ? null : "_empty"]"
@@ -267,24 +254,6 @@
. += "[use_icon_state]_[cig_position]"
cig_position++
-/obj/item/storage/fancy/cigarettes/attack(mob/living/carbon/target, mob/living/carbon/user)
- if(!istype(target))
- return
-
- var/obj/item/clothing/mask/cigarette/cig = locate() in contents
- if(!cig)
- to_chat(user, span_notice("There are no [contents_tag]s left in the pack."))
- return
- if(target != user || !contents.len || user.wear_mask)
- return ..()
-
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_TAKE, cig, target)
- target.equip_to_slot_if_possible(cig, ITEM_SLOT_MASK)
- contents -= cig
- to_chat(user, span_notice("You take \a [cig] out of the pack."))
- return
-
-
/obj/item/storage/fancy/cigarettes/dromedaryco
name = "\improper DromedaryCo packet"
desc = "A packet of six imported DromedaryCo cancer sticks. A label on the packaging reads, \"Wouldn't a slow death make a change?\""
@@ -388,11 +357,10 @@
spawn_type = /obj/item/rollingpaper
custom_price = PAYCHECK_LOWER
-/obj/item/storage/fancy/rollingpapers/ComponentInitialize()
+/obj/item/storage/fancy/rollingpapers/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 10
- STR.set_holdable(list(/obj/item/rollingpaper))
+ atom_storage.max_slots = 10
+ atom_storage.set_holdable(list(/obj/item/rollingpaper))
///Overrides to do nothing because fancy boxes are fucking insane.
/obj/item/storage/fancy/rollingpapers/update_icon_state()
@@ -417,14 +385,13 @@
w_class = WEIGHT_CLASS_NORMAL
contents_tag = "premium cigar"
spawn_type = /obj/item/clothing/mask/cigarette/cigar
+ spawn_count = 5
spawn_coupon = FALSE
display_cigs = FALSE
-/obj/item/storage/fancy/cigarettes/cigars/ComponentInitialize()
+/obj/item/storage/fancy/cigarettes/cigars/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 5
- STR.set_holdable(list(/obj/item/clothing/mask/cigarette/cigar))
+ atom_storage.set_holdable(list(/obj/item/clothing/mask/cigarette/cigar))
/obj/item/storage/fancy/cigarettes/cigars/update_icon_state()
. = ..()
@@ -469,12 +436,11 @@
righthand_file = 'icons/mob/inhands/misc/food_righthand.dmi'
contents_tag = "chocolate"
spawn_type = /obj/item/food/tinychocolate
+ spawn_count = 8
-/obj/item/storage/fancy/heart_box/ComponentInitialize()
+/obj/item/storage/fancy/heart_box/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 8
- STR.set_holdable(list(/obj/item/food/tinychocolate))
+ atom_storage.set_holdable(list(/obj/item/food/tinychocolate))
/obj/item/storage/fancy/nugget_box
@@ -485,9 +451,8 @@
base_icon_state = "nuggetbox"
contents_tag = "nugget"
spawn_type = /obj/item/food/nugget
+ spawn_count = 6
-/obj/item/storage/fancy/nugget_box/ComponentInitialize()
+/obj/item/storage/fancy/nugget_box/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.set_holdable(list(/obj/item/food/nugget))
+ atom_storage.set_holdable(list(/obj/item/food/nugget))
diff --git a/code/game/objects/items/storage/garment.dm b/code/game/objects/items/storage/garment.dm
index 53892fd28ce..74cde69244b 100644
--- a/code/game/objects/items/storage/garment.dm
+++ b/code/game/objects/items/storage/garment.dm
@@ -30,15 +30,14 @@
name = "chief engineer's garment bag"
desc = "A bag for storing extra clothes and shoes. This one belongs to the chief engineer."
-/obj/item/storage/bag/garment/ComponentInitialize()
+/obj/item/storage/bag/garment/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.display_numerical_stacking = FALSE
- STR.max_combined_w_class = 200
- STR.max_items = 15
- STR.insert_preposition = "in"
- STR.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.numerical_stacking = FALSE
+ atom_storage.max_total_storage = 200
+ atom_storage.max_slots = 15
+ atom_storage.insert_preposition = "in"
+ atom_storage.set_holdable(list(
/obj/item/clothing,
))
diff --git a/code/game/objects/items/storage/holsters.dm b/code/game/objects/items/storage/holsters.dm
index 8115d891699..650a32801db 100644
--- a/code/game/objects/items/storage/holsters.dm
+++ b/code/game/objects/items/storage/holsters.dm
@@ -17,12 +17,11 @@
. = ..()
REMOVE_TRAIT(user, TRAIT_GUNFLIP, CLOTHING_TRAIT)
-/obj/item/storage/belt/holster/ComponentInitialize()
+/obj/item/storage/belt/holster/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 1
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.set_holdable(list(
+ atom_storage.max_slots = 1
+ atom_storage.max_total_storage = 16
+ atom_storage.set_holdable(list(
/obj/item/gun/ballistic/automatic/pistol,
/obj/item/gun/ballistic/revolver,
/obj/item/gun/energy/e_gun/mini,
@@ -36,12 +35,11 @@
name = "thermal shoulder holsters"
desc = "A rather plain pair of shoulder holsters with a bit of insulated padding inside. Meant to hold a twinned pair of thermal pistols, but can fit several kinds of energy handguns as well."
-/obj/item/storage/belt/holster/thermal/ComponentInitialize()
+/obj/item/storage/belt/holster/thermal/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 2
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.set_holdable(list(
+ atom_storage.max_slots = 2
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.set_holdable(list(
/obj/item/gun/energy/e_gun/mini,
/obj/item/gun/energy/disabler,
/obj/item/gun/energy/dueling,
@@ -60,12 +58,11 @@
desc = "A holster able to carry handguns and some ammo. WARNING: Badasses only."
w_class = WEIGHT_CLASS_BULKY
-/obj/item/storage/belt/holster/detective/ComponentInitialize()
+/obj/item/storage/belt/holster/detective/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 3
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.set_holdable(list(
+ atom_storage.max_slots = 3
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.set_holdable(list(
/obj/item/gun/ballistic/automatic/pistol,
/obj/item/ammo_box/magazine/m9mm, // Pistol magazines.
/obj/item/ammo_box/magazine/m9mm_aps,
@@ -119,10 +116,9 @@
chameleon_action.initialize_disguises()
add_item_action(chameleon_action)
-/obj/item/storage/belt/holster/chameleon/ComponentInitialize()
+/obj/item/storage/belt/holster/chameleon/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.silent = TRUE
+ atom_storage.silent = TRUE
/obj/item/storage/belt/holster/chameleon/emp_act(severity)
. = ..()
@@ -134,12 +130,11 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
-/obj/item/storage/belt/holster/chameleon/ComponentInitialize()
+/obj/item/storage/belt/holster/chameleon/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 2
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.set_holdable(list(
+ atom_storage.max_slots = 2
+ atom_storage.max_total_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.set_holdable(list(
/obj/item/gun/ballistic/automatic/pistol,
/obj/item/ammo_box/magazine/m9mm,
/obj/item/ammo_box/magazine/m9mm_aps,
@@ -164,12 +159,11 @@
worn_icon_state = "syndicate_holster"
w_class = WEIGHT_CLASS_BULKY
-/obj/item/storage/belt/holster/nukie/ComponentInitialize()
+/obj/item/storage/belt/holster/nukie/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 2
- STR.max_w_class = WEIGHT_CLASS_BULKY
- STR.set_holdable(list(
+ atom_storage.max_slots = 2
+ atom_storage.max_specific_storage = WEIGHT_CLASS_BULKY
+ atom_storage.set_holdable(list(
/obj/item/gun, // ALL guns.
/obj/item/ammo_box/magazine, // ALL magazines.
/obj/item/ammo_box/c38, //There isn't a speedloader parent type, so I just put these three here by hand.
diff --git a/code/game/objects/items/storage/lockbox.dm b/code/game/objects/items/storage/lockbox.dm
index 47e72082e23..b005081c052 100644
--- a/code/game/objects/items/storage/lockbox.dm
+++ b/code/game/objects/items/storage/lockbox.dm
@@ -13,27 +13,26 @@
var/icon_closed = "lockbox"
var/icon_broken = "lockbox+b"
-/obj/item/storage/lockbox/ComponentInitialize()
+/obj/item/storage/lockbox/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 14
- STR.max_items = 4
- STR.locked = TRUE
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 14
+ atom_storage.max_slots = 4
+ atom_storage.locked = TRUE
/obj/item/storage/lockbox/attackby(obj/item/W, mob/user, params)
- var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ var/locked = atom_storage.locked
if(W.GetID())
if(broken)
to_chat(user, span_danger("It appears to be broken."))
return
if(allowed(user))
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, !locked)
- locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ atom_storage.locked = !locked
+ locked = atom_storage.locked
if(locked)
icon_state = icon_locked
to_chat(user, span_danger("You lock the [src.name]!"))
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_HIDE_ALL)
+ atom_storage.close_all()
return
else
icon_state = icon_closed
@@ -50,7 +49,7 @@
/obj/item/storage/lockbox/emag_act(mob/user)
if(!broken)
broken = TRUE
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, FALSE)
+ atom_storage.locked = FALSE
desc += "It appears to be broken."
icon_state = src.icon_broken
if(user)
@@ -97,22 +96,21 @@
icon_closed = "medalbox"
icon_broken = "medalbox+b"
-/obj/item/storage/lockbox/medal/ComponentInitialize()
+/obj/item/storage/lockbox/medal/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_SMALL
- STR.max_items = 10
- STR.max_combined_w_class = 20
- STR.set_holdable(list(/obj/item/clothing/accessory/medal))
+ atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
+ atom_storage.max_slots = 10
+ atom_storage.max_total_storage = 20
+ atom_storage.set_holdable(list(/obj/item/clothing/accessory/medal))
/obj/item/storage/lockbox/medal/examine(mob/user)
. = ..()
- if(!SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED))
+ if(!atom_storage.locked)
. += span_notice("Alt-click to [open ? "close":"open"] it.")
/obj/item/storage/lockbox/medal/AltClick(mob/user)
if(user.canUseTopic(src, BE_CLOSE))
- if(!SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED))
+ if(!atom_storage.locked)
open = (open ? FALSE : TRUE)
update_appearance()
..()
@@ -129,8 +127,7 @@
new /obj/item/clothing/accessory/medal/conduct(src)
/obj/item/storage/lockbox/medal/update_icon_state()
- var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
- if(locked)
+ if(atom_storage?.locked)
icon_state = "medalbox+l"
return ..()
@@ -145,8 +142,7 @@
. = ..()
if(!contents || !open)
return
- var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
- if(locked)
+ if(atom_storage?.locked)
return
for(var/i in 1 to contents.len)
var/obj/item/clothing/accessory/medal/M = contents[i]
@@ -240,8 +236,8 @@
to_chat(user, span_warning("Bank account does not match with buyer!"))
return
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, !privacy_lock)
- privacy_lock = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ atom_storage.locked = !privacy_lock
+ privacy_lock = atom_storage.locked
user.visible_message(span_notice("[user] [privacy_lock ? "" : "un"]locks [src]'s privacy lock."),
span_notice("You [privacy_lock ? "" : "un"]lock [src]'s privacy lock."))
diff --git a/code/game/objects/items/storage/medkit.dm b/code/game/objects/items/storage/medkit.dm
index b6ced74e03b..8ed34642744 100644
--- a/code/game/objects/items/storage/medkit.dm
+++ b/code/game/objects/items/storage/medkit.dm
@@ -60,13 +60,12 @@
inhand_icon_state = "medkit"
desc = "A high capacity aid kit for doctors, full of medical supplies and basic surgical equipment"
-/obj/item/storage/medkit/surgery/ComponentInitialize()
+/obj/item/storage/medkit/surgery/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL //holds the same equipment as a medibelt
- STR.max_items = 12
- STR.max_combined_w_class = 24
- STR.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL //holds the same equipment as a medibelt
+ atom_storage.max_slots = 12
+ atom_storage.max_total_storage = 24
+ atom_storage.set_holdable(list(
/obj/item/healthanalyzer,
/obj/item/dnainjector,
/obj/item/reagent_containers/dropper,
@@ -259,10 +258,9 @@
icon_state = "medkit_tactical"
damagetype_healed = "all"
-/obj/item/storage/medkit/tactical/ComponentInitialize()
+/obj/item/storage/medkit/tactical/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
/obj/item/storage/medkit/tactical/PopulateContents()
if(empty)
@@ -318,12 +316,10 @@
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
-/obj/item/storage/pill_bottle/ComponentInitialize()
+/obj/item/storage/pill_bottle/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.allow_quick_gather = TRUE
- STR.click_gather = TRUE
- STR.set_holdable(list(/obj/item/reagent_containers/pill))
+ atom_storage.allow_quick_gather = TRUE
+ atom_storage.set_holdable(list(/obj/item/reagent_containers/pill))
/obj/item/storage/pill_bottle/suicide_act(mob/user)
user.visible_message(span_suicide("[user] is trying to get the cap off [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
@@ -528,12 +524,11 @@
/// var to prevent it freezing the same things over and over
var/cooling = FALSE
-/obj/item/storage/organbox/ComponentInitialize()
+/obj/item/storage/organbox/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_BULKY /// you have to remove it from your bag before opening it but I think that's fine
- STR.max_combined_w_class = 21
- STR.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_BULKY /// you have to remove it from your bag before opening it but I think that's fine
+ atom_storage.max_total_storage = 21
+ atom_storage.set_holdable(list(
/obj/item/organ,
/obj/item/bodypart,
/obj/item/food/icecream
@@ -543,7 +538,7 @@
. = ..()
create_reagents(100, TRANSPARENT)
RegisterSignal(src, COMSIG_ATOM_ENTERED, .proc/freeze)
- RegisterSignal(src, COMSIG_TRY_STORAGE_TAKE, .proc/unfreeze)
+ RegisterSignal(src, COMSIG_ATOM_EXITED, .proc/unfreeze)
START_PROCESSING(SSobj, src)
/obj/item/storage/organbox/process(delta_time)
diff --git a/code/game/objects/items/storage/secure.dm b/code/game/objects/items/storage/secure.dm
index 89cd747da22..9b31d7009da 100644
--- a/code/game/objects/items/storage/secure.dm
+++ b/code/game/objects/items/storage/secure.dm
@@ -33,11 +33,10 @@
var/can_hack_open = TRUE
-/obj/item/storage/secure/ComponentInitialize()
+/obj/item/storage/secure/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_SMALL
- STR.max_combined_w_class = 14
+ atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
+ atom_storage.max_total_storage = 14
/obj/item/storage/secure/examine(mob/user)
. = ..()
@@ -45,7 +44,7 @@
. += "The service panel is currently [panel_open ? "unscrewed" : "screwed shut"]."
/obj/item/storage/secure/tool_act(mob/living/user, obj/item/tool)
- if(can_hack_open && SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED))
+ if(can_hack_open && atom_storage.locked)
return ..()
else
return FALSE
@@ -78,7 +77,7 @@
to_chat(user, span_warning("You must unscrew the service panel before you can pulse the wiring!"))
/obj/item/storage/secure/attack_self(mob/user)
- var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ var/locked = atom_storage.locked
user.set_machine(src)
var/dat = text("[]
\n\nLock Status: []",src, (locked ? "LOCKED" : "UNLOCKED"))
var/message = "Code"
@@ -100,7 +99,7 @@
lock_code = entered_code
lock_set = TRUE
else if ((entered_code == lock_code) && lock_set)
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, FALSE)
+ atom_storage.locked = TRUE
cut_overlays()
add_overlay(icon_opened)
entered_code = null
@@ -108,10 +107,10 @@
entered_code = "ERROR"
else
if (href_list["type"] == "R")
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE)
+ atom_storage.locked = FALSE
cut_overlays()
entered_code = null
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_HIDE_FROM, usr)
+ atom_storage.hide_contents(usr)
else
entered_code += text("[]", sanitize_text(href_list["type"]))
if (length(entered_code) > 5)
@@ -144,11 +143,10 @@
new /obj/item/paper(src)
new /obj/item/pen(src)
-/obj/item/storage/secure/briefcase/ComponentInitialize()
+/obj/item/storage/secure/briefcase/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 21
- STR.max_w_class = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 21
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
///Syndie variant of Secure Briefcase. Contains space cash, slightly more robust.
/obj/item/storage/secure/briefcase/syndie
@@ -174,11 +172,10 @@
MAPPING_DIRECTIONAL_HELPERS(/obj/item/storage/secure/safe, 32)
-/obj/item/storage/secure/safe/ComponentInitialize()
+/obj/item/storage/secure/safe/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.set_holdable(null, list(/obj/item/storage/secure/briefcase))
- STR.max_w_class = 8 //??
+ atom_storage.set_holdable(cant_hold_list = list(/obj/item/storage/secure/briefcase))
+ atom_storage.max_specific_storage = WEIGHT_CLASS_GIGANTIC
/obj/item/storage/secure/safe/PopulateContents()
new /obj/item/paper(src)
@@ -219,7 +216,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/item/storage/secure/safe/caps_spare, 32)
lock_code = SSid_access.spare_id_safe_code
lock_set = TRUE
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE)
+ atom_storage.locked = TRUE
/obj/item/storage/secure/safe/caps_spare/PopulateContents()
new /obj/item/card/id/advanced/gold/captains_spare(src)
diff --git a/code/game/objects/items/storage/sixpack.dm b/code/game/objects/items/storage/sixpack.dm
index 9d626d5bbab..dd67c5d222a 100644
--- a/code/game/objects/items/storage/sixpack.dm
+++ b/code/game/objects/items/storage/sixpack.dm
@@ -21,13 +21,12 @@
. = ..()
update_appearance()
-/obj/item/storage/cans/ComponentInitialize()
+/obj/item/storage/cans/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_SMALL
- STR.max_combined_w_class = 12
- STR.max_items = 6
- STR.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
+ atom_storage.max_total_storage = 12
+ atom_storage.max_slots = 6
+ atom_storage.set_holdable(list(
/obj/item/reagent_containers/food/drinks/soda_cans,
/obj/item/reagent_containers/food/drinks/bottle/beer,
/obj/item/reagent_containers/food/drinks/bottle/ale,
diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/storage.dm
index f4dfa226af9..dafe93f01fa 100644
--- a/code/game/objects/items/storage/storage.dm
+++ b/code/game/objects/items/storage/storage.dm
@@ -3,7 +3,6 @@
icon = 'icons/obj/storage.dmi'
w_class = WEIGHT_CLASS_NORMAL
var/rummage_if_nodrop = TRUE
- var/component_type = /datum/component/storage/concrete
/// Should we preload the contents of this type?
/// BE CAREFUL, THERE'S SOME REALLY NASTY SHIT IN THIS TYPEPATH
/// SANTA IS EVIL
@@ -11,13 +10,14 @@
/obj/item/storage/Initialize(mapload)
. = ..()
+
+ create_storage()
+
PopulateContents()
+
for (var/obj/item/item in src)
item.item_flags |= IN_STORAGE
-/obj/item/storage/ComponentInitialize()
- AddComponent(component_type)
-
/obj/item/storage/AllowDrop()
return FALSE
@@ -37,8 +37,7 @@
/obj/item/storage/doStrip(mob/who)
if(HAS_TRAIT(src, TRAIT_NODROP) && rummage_if_nodrop)
- var/datum/component/storage/CP = GetComponent(/datum/component/storage)
- CP.do_quick_empty()
+ atom_storage.remove_all()
return TRUE
return ..()
@@ -48,8 +47,7 @@
/obj/item/storage/proc/PopulateContents()
/obj/item/storage/proc/emptyStorage()
- var/datum/component/storage/ST = GetComponent(/datum/component/storage)
- ST.do_quick_empty()
+ atom_storage.remove_all()
/obj/item/storage/Destroy()
for(var/obj/important_thing in contents)
diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm
index 2c64585b7e3..b44c548cd0b 100644
--- a/code/game/objects/items/storage/toolbox.dm
+++ b/code/game/objects/items/storage/toolbox.dm
@@ -38,10 +38,9 @@
if(has_latches)
. += latches
-/obj/item/storage/toolbox/ComponentInitialize()
+/obj/item/storage/toolbox/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
/obj/item/storage/toolbox/suicide_act(mob/user)
user.visible_message(span_suicide("[user] robusts [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
@@ -102,10 +101,9 @@
force = 5
w_class = WEIGHT_CLASS_NORMAL
-/obj/item/storage/toolbox/mechanical/old/heirloom/ComponentInitialize()
+/obj/item/storage/toolbox/mechanical/old/heirloom/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_SMALL
+ atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
/obj/item/storage/toolbox/mechanical/old/heirloom/PopulateContents()
return
@@ -170,10 +168,9 @@
throwforce = 18
material_flags = NONE
-/obj/item/storage/toolbox/syndicate/ComponentInitialize()
+/obj/item/storage/toolbox/syndicate/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.silent = TRUE
+ atom_storage.silent = TRUE
/obj/item/storage/toolbox/syndicate/PopulateContents()
new /obj/item/screwdriver/nuke(src)
@@ -208,11 +205,10 @@
w_class = WEIGHT_CLASS_GIGANTIC //Holds more than a regular toolbox!
material_flags = NONE
-/obj/item/storage/toolbox/artistic/ComponentInitialize()
+/obj/item/storage/toolbox/artistic/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 20
- STR.max_items = 10
+ atom_storage.max_total_storage = 20
+ atom_storage.max_slots = 10
/obj/item/storage/toolbox/artistic/PopulateContents()
new /obj/item/storage/crayons(src)
@@ -275,13 +271,12 @@
w_class = WEIGHT_CLASS_NORMAL
has_latches = FALSE
-/obj/item/storage/toolbox/infiltrator/ComponentInitialize()
+/obj/item/storage/toolbox/infiltrator/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 10
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 24
- STR.set_holdable(list(
+ atom_storage.max_slots = 10
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 24
+ atom_storage.set_holdable(list(
/obj/item/clothing/head/helmet/infiltrator,
/obj/item/clothing/suit/armor/vest/infiltrator,
/obj/item/clothing/under/syndicate/bloodred,
diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm
index b02c974e1ed..a9f50856558 100644
--- a/code/game/objects/items/storage/uplink_kits.dm
+++ b/code/game/objects/items/storage/uplink_kits.dm
@@ -385,11 +385,10 @@
/obj/item/storage/box/syndie_kit/space
name = "boxed space suit and helmet"
-/obj/item/storage/box/syndie_kit/space/ComponentInitialize()
+/obj/item/storage/box/syndie_kit/space/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.set_holdable(list(/obj/item/clothing/suit/space/syndicate, /obj/item/clothing/head/helmet/space/syndicate))
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.set_holdable(list(/obj/item/clothing/suit/space/syndicate, /obj/item/clothing/head/helmet/space/syndicate))
/obj/item/storage/box/syndie_kit/space/PopulateContents()
if(prob(50))
@@ -411,10 +410,9 @@
/obj/item/storage/box/syndie_kit/chemical
name = "chemical kit"
-/obj/item/storage/box/syndie_kit/chemical/ComponentInitialize()
+/obj/item/storage/box/syndie_kit/chemical/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 14
+ atom_storage.max_slots = 14
/obj/item/storage/box/syndie_kit/chemical/PopulateContents()
new /obj/item/reagent_containers/glass/bottle/polonium(src)
diff --git a/code/game/objects/items/storage/wallets.dm b/code/game/objects/items/storage/wallets.dm
index 093f664803b..5906b510340 100644
--- a/code/game/objects/items/storage/wallets.dm
+++ b/code/game/objects/items/storage/wallets.dm
@@ -5,17 +5,15 @@
w_class = WEIGHT_CLASS_SMALL
resistance_flags = FLAMMABLE
slot_flags = ITEM_SLOT_ID
- component_type = /datum/component/storage/concrete/wallet
var/obj/item/card/id/front_id = null
var/list/combined_access
var/cached_flat_icon
-/obj/item/storage/wallet/ComponentInitialize()
+/obj/item/storage/wallet/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage/concrete/wallet)
- STR.max_items = 4
- STR.set_holdable(list(
+ atom_storage.max_slots = 4
+ atom_storage.set_holdable(list(
/obj/item/stack/spacecash,
/obj/item/holochip,
/obj/item/card,
diff --git a/code/game/objects/items/tcg/tcg.dm b/code/game/objects/items/tcg/tcg.dm
index b1f89cf21c9..3ff00f4e1b2 100644
--- a/code/game/objects/items/tcg/tcg.dm
+++ b/code/game/objects/items/tcg/tcg.dm
@@ -184,7 +184,7 @@ GLOBAL_LIST_EMPTY(tcgcard_radial_choices)
/obj/item/tcgcard_deck/Initialize(mapload)
. = ..()
- LoadComponent(/datum/component/storage/concrete/tcg)
+ create_storage(type = /datum/storage/tcg)
/obj/item/tcgcard_deck/update_icon_state()
if(!flipped)
@@ -284,7 +284,7 @@ GLOBAL_LIST_EMPTY(tcgcard_radial_choices)
return
contents = shuffle(contents)
if(user.active_storage)
- user.active_storage.close(user)
+ user.active_storage.hide_contents(user)
if(visable)
user.visible_message(span_notice("[user] shuffles \the [src]!"), \
span_notice("You shuffle \the [src]!"))
@@ -396,10 +396,9 @@ GLOBAL_LIST_EMPTY(tcgcard_radial_choices)
/obj/item/storage/card_binder/Initialize(mapload)
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.set_holdable(list(/obj/item/tcgcard))
- STR.max_combined_w_class = 120
- STR.max_items = 60
+ atom_storage.set_holdable(list(/obj/item/tcgcard))
+ atom_storage.max_total_storage = 120
+ atom_storage.max_slots = 60
///Returns a list of cards ids of card_cnt weighted by rarity from the pack's tables that have matching series, with gnt_cnt of the guarenteed table.
/obj/item/cardpack/proc/buildCardListWithRarity(card_cnt, rarity_cnt)
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 79e4295b65b..9a3ccb49928 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -248,7 +248,7 @@
break
for(var/i in reverse_range(location.get_all_contents()))
var/atom/movable/thing = i
- SEND_SIGNAL(thing, COMSIG_TRY_STORAGE_HIDE_ALL)
+ thing.atom_storage?.close_all()
/obj/structure/closet/proc/open(mob/living/user, force = FALSE)
if(!can_open(user, force))
diff --git a/code/game/objects/structures/crates_lockers/crates/bins.dm b/code/game/objects/structures/crates_lockers/crates/bins.dm
index 5f7faf406fb..4b68e9fce4c 100644
--- a/code/game/objects/structures/crates_lockers/crates/bins.dm
+++ b/code/game/objects/structures/crates_lockers/crates/bins.dm
@@ -28,7 +28,7 @@
var/obj/item/storage/bag/trash/T = W
to_chat(user, span_notice("You fill the bag."))
for(var/obj/item/O in src)
- SEND_SIGNAL(T, COMSIG_TRY_STORAGE_INSERT, O, user, TRUE)
+ T.atom_storage?.attempt_insert(T, O, user, TRUE)
T.update_appearance()
do_animate()
return TRUE
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 912a9eea96c..22d536e9681 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -209,7 +209,7 @@
for(var/x in T.contents)
var/obj/item/item = x
AfterPutItemOnTable(item, user)
- SEND_SIGNAL(I, COMSIG_TRY_STORAGE_QUICK_EMPTY, drop_location())
+ I.atom_storage.remove_all(drop_location())
user.visible_message(span_notice("[user] empties [I] on [src]."))
return
// If the tray IS empty, continue on (tray will be placed on the table like other items)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index d014ceea7cb..87b3e0546a5 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -452,20 +452,21 @@ GLOBAL_LIST_EMPTY(station_turfs)
/turf/proc/Bless()
new /obj/effect/blessing(src)
-/turf/storage_contents_dump_act(datum/component/storage/src_object, mob/user)
+/turf/storage_contents_dump_act(atom/src_object, mob/user)
. = ..()
if(.)
return
- if(length(src_object.contents()))
- to_chat(usr, span_notice("You start dumping out the contents..."))
- if(!do_after(usr,20,target=src_object.parent))
+ if(!src_object.atom_storage)
+ return
+ var/atom/resolve_parent = src_object.atom_storage.real_location?.resolve()
+ if(!resolve_parent)
+ return FALSE
+ if(length(resolve_parent.contents))
+ to_chat(user, span_notice("You start dumping out the contents of [src_object]..."))
+ if(!do_after(user, 20, target=resolve_parent))
return FALSE
- var/list/things = src_object.contents()
- var/datum/progressbar/progress = new(user, things.len, src)
- while (do_after(usr, 1 SECONDS, src, NONE, FALSE, CALLBACK(src_object, /datum/component/storage.proc/mass_remove_from_storage, src, things, progress)))
- stoplag(1)
- progress.end_progress()
+ src_object.atom_storage.remove_all(src)
return TRUE
diff --git a/code/modules/antagonists/clown_ops/clown_weapons.dm b/code/modules/antagonists/clown_ops/clown_weapons.dm
index 3c5584c894f..4cb548d9071 100644
--- a/code/modules/antagonists/clown_ops/clown_weapons.dm
+++ b/code/modules/antagonists/clown_ops/clown_weapons.dm
@@ -20,7 +20,11 @@
armor = list(MELEE = 25, BULLET = 25, LASER = 25, ENERGY = 25, BOMB = 50, BIO = 90, FIRE = 70, ACID = 50)
strip_delay = 70
resistance_flags = NONE
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
+
+/obj/item/clothing/shoes/clown_shoes/combat/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
/// Recharging rate in PPS (peels per second)
#define BANANA_SHOES_RECHARGE_RATE 17
@@ -34,11 +38,13 @@
armor = list(MELEE = 25, BULLET = 25, LASER = 25, ENERGY = 25, BOMB = 50, BIO = 50, FIRE = 90, ACID = 50)
strip_delay = 70
resistance_flags = NONE
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
always_noslip = TRUE
/obj/item/clothing/shoes/clown_shoes/banana_shoes/combat/Initialize(mapload)
. = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
+
var/datum/component/material_container/bananium = GetComponent(/datum/component/material_container)
bananium.insert_amount_mat(BANANA_SHOES_MAX_CHARGE, /datum/material/bananium)
START_PROCESSING(SSobj, src)
diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm
index 689a92343a1..56318bf5286 100644
--- a/code/modules/antagonists/cult/cult.dm
+++ b/code/modules/antagonists/cult/cult.dm
@@ -126,7 +126,7 @@
else
to_chat(mob, span_danger("You have a [item_name] in your [where]."))
if(where == "backpack")
- SEND_SIGNAL(mob.back, COMSIG_TRY_STORAGE_SHOW, mob)
+ mob.back.atom_storage?.show_contents(mob)
return TRUE
/datum/antagonist/cult/apply_innate_effects(mob/living/mob_override)
diff --git a/code/modules/antagonists/heretic/items/heretic_armor.dm b/code/modules/antagonists/heretic/items/heretic_armor.dm
index 7092cf120e8..55d42e0b48c 100644
--- a/code/modules/antagonists/heretic/items/heretic_armor.dm
+++ b/code/modules/antagonists/heretic/items/heretic_armor.dm
@@ -60,8 +60,12 @@
body_parts_covered = CHEST|GROIN|ARMS
// slightly worse than normal cult robes
armor = list(MELEE = 30, BULLET = 30, LASER = 30,ENERGY = 30, BOMB = 15, BIO = 0, FIRE = 0, ACID = 0)
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/void_cloak
alternative_mode = TRUE
+
+/obj/item/clothing/suit/hooded/cultrobes/void/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/void_cloak)
/obj/item/clothing/suit/hooded/cultrobes/void/Initialize(mapload)
. = ..()
diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm
index 240b8c68e9c..8aafde978f9 100644
--- a/code/modules/clothing/chameleon.dm
+++ b/code/modules/clothing/chameleon.dm
@@ -602,12 +602,14 @@
desc = "A pair of black shoes."
resistance_flags = NONE
armor = list(MELEE = 10, BULLET = 10, LASER = 10, ENERGY = 0, BOMB = 0, BIO = 90, FIRE = 50, ACID = 50)
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
var/datum/action/item_action/chameleon/change/chameleon_action
/obj/item/clothing/shoes/chameleon/Initialize(mapload)
. = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
+
chameleon_action = new(src)
chameleon_action.chameleon_type = /obj/item/clothing/shoes
chameleon_action.chameleon_name = "Shoes"
@@ -665,10 +667,7 @@
chameleon_action.initialize_disguises()
add_item_action(chameleon_action)
-/obj/item/storage/belt/chameleon/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.silent = TRUE
+ atom_storage.silent = TRUE
/obj/item/storage/belt/chameleon/emp_act(severity)
. = ..()
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 5772f8ba1c8..e7b6c0d75cb 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -287,20 +287,19 @@
if(30 to 59)
. += span_danger("The [zone_name] is partially shredded.")
- var/datum/component/storage/pockets = GetComponent(/datum/component/storage)
- if(pockets)
+ if(atom_storage)
var/list/how_cool_are_your_threads = list("")
- if(pockets.attack_hand_interact)
+ if(atom_storage.attack_hand_interact)
how_cool_are_your_threads += "[src]'s storage opens when clicked.\n"
else
how_cool_are_your_threads += "[src]'s storage opens when dragged to yourself.\n"
- if (pockets.can_hold?.len) // If pocket type can hold anything, vs only specific items
- how_cool_are_your_threads += "[src] can store [pockets.max_items] item\s.\n"
+ if (atom_storage.can_hold?.len) // If pocket type can hold anything, vs only specific items
+ how_cool_are_your_threads += "[src] can store [atom_storage.max_slots] item\s.\n"
else
- how_cool_are_your_threads += "[src] can store [pockets.max_items] item\s that are [weight_class_to_text(pockets.max_w_class)] or smaller.\n"
- if(pockets.quickdraw)
+ how_cool_are_your_threads += "[src] can store [atom_storage.max_slots] item\s that are [weight_class_to_text(atom_storage.max_specific_storage)] or smaller.\n"
+ if(atom_storage.quickdraw)
how_cool_are_your_threads += "You can quickly remove an item from [src] using Right-Click.\n"
- if(pockets.silent)
+ if(atom_storage.silent)
how_cool_are_your_threads += "Adding or removing items from [src] makes no noise.\n"
how_cool_are_your_threads += ""
. += how_cool_are_your_threads.Join()
diff --git a/code/modules/clothing/head/fedora.dm b/code/modules/clothing/head/fedora.dm
index 3207609cc90..c3f7074841c 100644
--- a/code/modules/clothing/head/fedora.dm
+++ b/code/modules/clothing/head/fedora.dm
@@ -3,7 +3,11 @@
icon_state = "fedora"
inhand_icon_state = "fedora"
desc = "A really cool hat if you're a mobster. A really lame hat if you're not."
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/small/fedora
+
+/obj/item/clothing/head/fedora/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/small/fedora)
/obj/item/clothing/head/fedora/white
name = "white fedora"
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index 09c710a3bd6..c9fd22b3f85 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -340,7 +340,11 @@
icon_state = "rus_helmet"
inhand_icon_state = "rus_helmet"
armor = list(MELEE = 25, BULLET = 30, LASER = 0, ENERGY = 10, BOMB = 10, BIO = 0, FIRE = 20, ACID = 50, WOUND = 5)
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/helmet
+
+/obj/item/clothing/head/helmet/rus_helmet/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/helmet)
/obj/item/clothing/head/helmet/rus_ushanka
name = "battle ushanka"
diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm
index f942c3397bb..7ed8a101b8d 100644
--- a/code/modules/clothing/head/jobs.dm
+++ b/code/modules/clothing/head/jobs.dm
@@ -13,11 +13,15 @@
strip_delay = 10
equip_delay_other = 10
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/chefhat
dog_fashion = /datum/dog_fashion/head/chef
///the chance that the movements of a mouse inside of this hat get relayed to the human wearing the hat
var/mouse_control_probability = 20
+/obj/item/clothing/head/chefhat/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/chefhat)
+
/obj/item/clothing/head/chefhat/i_am_assuming_direct_control
desc = "The commander in chef's head wear. Upon closer inspection, there seem to be dozens of tiny levers, buttons, dials, and screens inside of this hat. What the hell...?"
mouse_control_probability = 100
@@ -94,11 +98,13 @@
armor = list(MELEE = 25, BULLET = 5, LASER = 25, ENERGY = 35, BOMB = 0, BIO = 0, FIRE = 30, ACID = 50, WOUND = 5)
icon_state = "detective"
var/candy_cooldown = 0
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/small/fedora/detective
dog_fashion = /datum/dog_fashion/head/detective
/obj/item/clothing/head/fedora/det_hat/Initialize(mapload)
. = ..()
+
+ create_storage(type = /datum/storage/pockets/small/fedora/detective)
+
new /obj/item/reagent_containers/food/drinks/flask/det(src)
/obj/item/clothing/head/fedora/det_hat/examine(mob/user)
diff --git a/code/modules/clothing/head/religious.dm b/code/modules/clothing/head/religious.dm
index cd4e5b4ad1f..29eb4c2aeb2 100644
--- a/code/modules/clothing/head/religious.dm
+++ b/code/modules/clothing/head/religious.dm
@@ -17,10 +17,18 @@
name = "white taqiyah"
desc = "An extra-mustahabb way of showing your devotion to Allah."
icon_state = "taqiyahwhite"
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/small
+
+/obj/item/clothing/head/taqiyahwhite/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/small)
/obj/item/clothing/head/taqiyahred
name = "red taqiyah"
desc = "An extra-mustahabb way of showing your devotion to Allah."
icon_state = "taqiyahred"
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/small
+
+/obj/item/clothing/head/taqiyahred/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/small)
diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm
index 0ed64495fea..bea204111c0 100644
--- a/code/modules/clothing/outfits/standard.dm
+++ b/code/modules/clothing/outfits/standard.dm
@@ -252,11 +252,11 @@
for(var/obj/item/briefcase_item in sec_briefcase)
qdel(briefcase_item)
for(var/i = 3 to 0 step -1)
- SEND_SIGNAL(sec_briefcase, COMSIG_TRY_STORAGE_INSERT, new /obj/item/stack/spacecash/c1000, null, TRUE, TRUE)
- SEND_SIGNAL(sec_briefcase, COMSIG_TRY_STORAGE_INSERT, new /obj/item/gun/energy/recharge/ebow, null, TRUE, TRUE)
- SEND_SIGNAL(sec_briefcase, COMSIG_TRY_STORAGE_INSERT, new /obj/item/gun/ballistic/revolver/mateba, null, TRUE, TRUE)
- SEND_SIGNAL(sec_briefcase, COMSIG_TRY_STORAGE_INSERT, new /obj/item/ammo_box/a357, null, TRUE, TRUE)
- SEND_SIGNAL(sec_briefcase, COMSIG_TRY_STORAGE_INSERT, new /obj/item/grenade/c4/x4, null, TRUE, TRUE)
+ sec_briefcase.contents += new /obj/item/stack/spacecash/c1000
+ sec_briefcase.contents += new /obj/item/gun/energy/recharge/ebow
+ sec_briefcase.contents += new /obj/item/gun/ballistic/revolver/mateba
+ sec_briefcase.contents += new /obj/item/ammo_box/a357
+ sec_briefcase.contents += new /obj/item/grenade/c4/x4
var/obj/item/modular_computer/tablet/pda/heads/pda = H.belt
pda.saved_identification = H.real_name
diff --git a/code/modules/clothing/outfits/vv_outfit.dm b/code/modules/clothing/outfits/vv_outfit.dm
index 3718e6bdef5..bedd65010f9 100644
--- a/code/modules/clothing/outfits/vv_outfit.dm
+++ b/code/modules/clothing/outfits/vv_outfit.dm
@@ -107,10 +107,10 @@
O.vv_values = result
//Copy backpack contents if exist.
var/obj/item/backpack = get_item_by_slot(ITEM_SLOT_BACK)
- if(istype(backpack) && SEND_SIGNAL(backpack, COMSIG_CONTAINS_STORAGE))
+ if(istype(backpack) && backpack.atom_storage)
var/list/bp_stuff = list()
var/list/typecounts = list()
- SEND_SIGNAL(backpack, COMSIG_TRY_STORAGE_RETURN_INVENTORY, bp_stuff, FALSE)
+ backpack.atom_storage.return_inv(bp_stuff, FALSE)
for(var/obj/item/I in bp_stuff)
if(typecounts[I.type])
typecounts[I.type] += 1
diff --git a/code/modules/clothing/shoes/boots.dm b/code/modules/clothing/shoes/boots.dm
index 58b0f9832cd..dbec77cd09a 100644
--- a/code/modules/clothing/shoes/boots.dm
+++ b/code/modules/clothing/shoes/boots.dm
@@ -8,9 +8,13 @@
armor = list(MELEE = 25, BULLET = 25, LASER = 25, ENERGY = 25, BOMB = 50, BIO = 90, FIRE = 70, ACID = 50)
strip_delay = 40
resistance_flags = NONE
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
lace_time = 12 SECONDS
+/obj/item/clothing/shoes/combat/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
+
/obj/item/clothing/shoes/combat/sneakboots
name = "sneakboots"
desc = "These boots have special noise cancelling soles. Perfect for stealth, if it wasn't for the color scheme."
@@ -37,9 +41,13 @@
equip_delay_other = 50
resistance_flags = NONE
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 90, FIRE = 0, ACID = 0)
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
can_be_tied = FALSE
+/obj/item/clothing/shoes/jackboots/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
+
/obj/item/clothing/shoes/jackboots/fast
slowdown = -1
@@ -53,9 +61,13 @@
min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
heat_protection = FEET|LEGS
max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
lace_time = 8 SECONDS
+/obj/item/clothing/shoes/winterboots/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
+
/obj/item/clothing/shoes/winterboots/ice_boots
name = "ice hiking boots"
desc = "A pair of winter boots with special grips on the bottom, designed to prevent slipping on frozen surfaces."
@@ -73,10 +85,14 @@
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 80, FIRE = 0, ACID = 0)
strip_delay = 20
equip_delay_other = 40
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
lace_time = 8 SECONDS
species_exception = list(/datum/species/golem/uranium)
+/obj/item/clothing/shoes/workboots/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
+
/obj/item/clothing/shoes/workboots/mining
name = "mining boots"
desc = "Steel-toed mining boots for mining in hazardous environments. Very good at keeping toes uncrushed."
@@ -88,9 +104,13 @@
desc = "Comfy shoes."
icon_state = "rus_shoes"
inhand_icon_state = "rus_shoes"
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
lace_time = 8 SECONDS
+/obj/item/clothing/shoes/russian/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
+
/obj/item/clothing/shoes/discoshoes
name = "green lizardskin shoes"
desc = "They may have lost some of their lustre over the years, but these green lizardskin shoes fit you perfectly."
diff --git a/code/modules/clothing/shoes/clown.dm b/code/modules/clothing/shoes/clown.dm
index 55a8a8ba6be..83324d934e9 100644
--- a/code/modules/clothing/shoes/clown.dm
+++ b/code/modules/clothing/shoes/clown.dm
@@ -4,13 +4,14 @@
icon_state = "clown"
inhand_icon_state = "clown_shoes"
slowdown = SHOES_SLOWDOWN+1
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes/clown
var/enabled_waddle = TRUE
lace_time = 20 SECONDS // how the hell do these laces even work??
species_exception = list(/datum/species/golem/bananium)
/obj/item/clothing/shoes/clown_shoes/Initialize(mapload)
. = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes/clown)
LoadComponent(/datum/component/squeak, list('sound/effects/clownstep1.ogg'=1,'sound/effects/clownstep2.ogg'=1), 50, falloff_exponent = 20) //die off quick please
AddElement(/datum/element/swabable, CELL_LINE_TABLE_CLOWN, CELL_VIRUS_TABLE_GENERIC, rand(2,3), 0)
diff --git a/code/modules/clothing/shoes/costume.dm b/code/modules/clothing/shoes/costume.dm
index 764dab663c8..353c71ed007 100644
--- a/code/modules/clothing/shoes/costume.dm
+++ b/code/modules/clothing/shoes/costume.dm
@@ -13,9 +13,13 @@
desc = "A pair of costume boots fashioned after bird talons."
icon_state = "griffinboots"
inhand_icon_state = "griffinboots"
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
lace_time = 8 SECONDS
+/obj/item/clothing/shoes/griffin/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
+
/obj/item/clothing/shoes/singery
name = "yellow performer's boots"
desc = "These boots were made for dancing."
diff --git a/code/modules/clothing/shoes/cowboy.dm b/code/modules/clothing/shoes/cowboy.dm
index 80d6e94074f..b6fb0c8dde5 100644
--- a/code/modules/clothing/shoes/cowboy.dm
+++ b/code/modules/clothing/shoes/cowboy.dm
@@ -3,13 +3,15 @@
desc = "A small sticker lets you know they've been inspected for snakes, It is unclear how long ago the inspection took place..."
icon_state = "cowboy_brown"
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 90, FIRE = 0, ACID = 0) //these are quite tall
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
custom_price = PAYCHECK_CREW
var/max_occupants = 4
can_be_tied = FALSE
/obj/item/clothing/shoes/cowboy/Initialize(mapload)
. = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
+
if(prob(2))
//There's a snake in my boot
new /mob/living/simple_animal/hostile/retaliate/snake(src)
diff --git a/code/modules/clothing/shoes/gunboots.dm b/code/modules/clothing/shoes/gunboots.dm
index ff285afe509..85c239c4b75 100644
--- a/code/modules/clothing/shoes/gunboots.dm
+++ b/code/modules/clothing/shoes/gunboots.dm
@@ -5,7 +5,6 @@
inhand_icon_state = "jackboots"
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
/// What projectile do we shoot?
var/projectile_type = /obj/projectile/bullet/c10mm
/// Each step, this is the chance we fire a shot
@@ -13,6 +12,9 @@
/obj/item/clothing/shoes/gunboots/Initialize(mapload)
. = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
+
RegisterSignal(src, COMSIG_SHOES_STEP_ACTION, .proc/check_step)
/obj/item/clothing/shoes/gunboots/equipped(mob/user, slot)
diff --git a/code/modules/clothing/shoes/jumpboots.dm b/code/modules/clothing/shoes/jumpboots.dm
index b91a335e67a..f3a77ac5e7f 100644
--- a/code/modules/clothing/shoes/jumpboots.dm
+++ b/code/modules/clothing/shoes/jumpboots.dm
@@ -4,7 +4,6 @@
icon_state = "jetboots"
inhand_icon_state = "jetboots"
resistance_flags = FIRE_PROOF
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
actions_types = list(/datum/action/item_action/bhop)
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 90, FIRE = 0, ACID = 0)
strip_delay = 30
@@ -13,6 +12,11 @@
var/recharging_rate = 60 //default 6 seconds between each dash
var/recharging_time = 0 //time until next dash
+/obj/item/clothing/shoes/bhop/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets/shoes)
+
/obj/item/clothing/shoes/bhop/ui_action_click(mob/user, action)
if(!isliving(user))
return
diff --git a/code/modules/clothing/suits/moth.dm b/code/modules/clothing/suits/moth.dm
index d0997c09457..89931c4fb83 100644
--- a/code/modules/clothing/suits/moth.dm
+++ b/code/modules/clothing/suits/moth.dm
@@ -13,7 +13,11 @@
/obj/item/clothing/suit/mothcoat/original
desc = "An old-school flightsuit from the moth fleet. A perfect token of mothic survivalistic and adaptable attitude, yet a bitter reminder that with the loss of their home planet and institution of the fleet, their beloved wings remain as a burden to bear, condemned to never fly again."
greyscale_colors = "#dfa409"
- pocket_storage_component_path = /datum/component/storage/concrete/pockets
+
+/obj/item/clothing/suit/mothcoat/original/Initialize(mapload)
+ . = ..()
+
+ create_storage(type = /datum/storage/pockets)
/obj/item/clothing/suit/mothcoat/winter
name = "mothic mantella"
diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm
index 5ee14d66d9a..96ba7ad88d9 100755
--- a/code/modules/clothing/under/accessories.dm
+++ b/code/modules/clothing/under/accessories.dm
@@ -11,15 +11,9 @@
var/above_suit = TRUE
/// TRUE if shown as a small icon in corner, FALSE if overlayed
var/minimize_when_attached = TRUE
- /// Whether the accessory has any storage to apply to the clothing it's attached to.
- var/datum/component/storage/detached_pockets
/// What equipment slot the accessory attaches to.
var/attachment_slot = CHEST
-/obj/item/clothing/accessory/Destroy()
- set_detached_pockets(null)
- return ..()
-
/obj/item/clothing/accessory/proc/can_attach_accessory(obj/item/clothing/U, mob/user)
if(!attachment_slot || (U && U.body_parts_covered & attachment_slot))
return TRUE
@@ -27,12 +21,11 @@
to_chat(user, span_warning("There doesn't seem to be anywhere to put [src]..."))
/obj/item/clothing/accessory/proc/attach(obj/item/clothing/under/U, user)
- var/datum/component/storage/storage = GetComponent(/datum/component/storage)
- if(storage)
- if(SEND_SIGNAL(U, COMSIG_CONTAINS_STORAGE))
+ if(atom_storage)
+ if(U.atom_storage)
return FALSE
- U.TakeComponent(storage)
- set_detached_pockets(storage)
+ U.clone_storage(atom_storage)
+ U.atom_storage.set_real_location(src)
U.attached_accessory = src
forceMove(U)
layer = FLOAT_LAYER
@@ -57,8 +50,8 @@
return TRUE
/obj/item/clothing/accessory/proc/detach(obj/item/clothing/under/U, user)
- if(detached_pockets && detached_pockets.parent == U)
- TakeComponent(detached_pockets)
+ if(U.atom_storage && U.atom_storage.real_location?.resolve() == src)
+ QDEL_NULL(U.atom_storage)
U.armor = U.armor.detachArmor(armor)
@@ -75,16 +68,6 @@
U.attached_accessory = null
U.accessory_overlay = null
-/obj/item/clothing/accessory/proc/set_detached_pockets(new_pocket)
- if(detached_pockets)
- UnregisterSignal(detached_pockets, COMSIG_PARENT_QDELETING)
- detached_pockets = new_pocket
- if(detached_pockets)
- RegisterSignal(detached_pockets, COMSIG_PARENT_QDELETING, .proc/handle_pockets_del)
-
-/obj/item/clothing/accessory/proc/handle_pockets_del(datum/source)
- SIGNAL_HANDLER
- set_detached_pockets(null)
/obj/item/clothing/accessory/proc/on_uniform_equip(obj/item/clothing/under/U, user)
return
@@ -356,10 +339,12 @@
name = "pocket protector"
desc = "Can protect your clothing from ink stains, but you'll look like a nerd if you're using one."
icon_state = "pocketprotector"
- pocket_storage_component_path = /datum/component/storage/concrete/pockets/pocketprotector
/obj/item/clothing/accessory/pocketprotector/full/Initialize(mapload)
. = ..()
+
+ create_storage(type = /datum/storage/pockets/pocketprotector)
+
new /obj/item/pen/red(src)
new /obj/item/pen(src)
new /obj/item/pen/blue(src)
diff --git a/code/modules/detectivework/evidence.dm b/code/modules/detectivework/evidence.dm
index 0e1902a2a85..9ca6c7fac7f 100644
--- a/code/modules/detectivework/evidence.dm
+++ b/code/modules/detectivework/evidence.dm
@@ -28,7 +28,7 @@
if(!istype(I) || I.anchored)
return
- if(SEND_SIGNAL(loc, COMSIG_CONTAINS_STORAGE) && SEND_SIGNAL(I, COMSIG_CONTAINS_STORAGE))
+ if(loc.atom_storage && I.atom_storage)
to_chat(user, span_warning("No matter what way you try, you can't get [I] to fit inside [src]."))
return TRUE //begone infinite storage ghosts, begone from me
@@ -53,8 +53,8 @@
return
if(!isturf(I.loc)) //If it isn't on the floor. Do some checks to see if it's in our hands or a box. Otherwise give up.
- if(SEND_SIGNAL(I.loc, COMSIG_CONTAINS_STORAGE)) //in a container.
- SEND_SIGNAL(I.loc, COMSIG_TRY_STORAGE_TAKE, I, src)
+ if(I.loc.atom_storage) //in a container.
+ I.loc.atom_storage.attempt_remove(I, src)
if(!user.dropItemToGround(I))
return
diff --git a/code/modules/events/wizard/rpgloot.dm b/code/modules/events/wizard/rpgloot.dm
index 6907fa99be7..a26be5c09e5 100644
--- a/code/modules/events/wizard/rpgloot.dm
+++ b/code/modules/events/wizard/rpgloot.dm
@@ -86,10 +86,10 @@ GLOBAL_DATUM(rpgloot_controller, /datum/rpgloot_controller)
if(istype(fantasy_item, /obj/item/storage))
var/obj/item/storage/storage_item = fantasy_item
- var/datum/component/storage/storage_component = storage_item.GetComponent(/datum/component/storage)
- if(prob(upgrade_scroll_chance) && storage_item.contents.len < storage_component.max_items && !storage_item.invisibility)
+ var/datum/storage/storage_component = storage_item.atom_storage
+ if(prob(upgrade_scroll_chance) && storage_item.contents.len < storage_component.max_slots && !storage_item.invisibility)
var/obj/item/upgradescroll/scroll = new(get_turf(storage_item))
- SEND_SIGNAL(storage_item, COMSIG_TRY_STORAGE_INSERT, scroll, null, TRUE, TRUE)
+ storage_item.atom_storage?.attempt_insert(storage_item, scroll, null, TRUE)
upgrade_scroll_chance = max(0,upgrade_scroll_chance-100)
if(isturf(scroll.loc))
qdel(scroll)
diff --git a/code/modules/explorer_drone/exodrone.dm b/code/modules/explorer_drone/exodrone.dm
index 357df208fd5..00628f7dfe7 100644
--- a/code/modules/explorer_drone/exodrone.dm
+++ b/code/modules/explorer_drone/exodrone.dm
@@ -71,10 +71,8 @@ GLOBAL_LIST_EMPTY(exodrone_launchers)
name_counter[name] = 1
GLOB.exodrones += src
/// Cargo storage
- var/datum/component/storage/storage = AddComponent(/datum/component/storage/concrete)
- storage.cant_hold = GLOB.blacklisted_cargo_types
- storage.max_w_class = WEIGHT_CLASS_NORMAL
- storage.max_items = EXODRONE_CARGO_SLOTS
+ create_storage(max_slots = EXODRONE_CARGO_SLOTS)
+ atom_storage.set_holdable(cant_hold_list = GLOB.blacklisted_cargo_types)
/obj/item/exodrone/Destroy()
. = ..()
@@ -148,8 +146,7 @@ GLOBAL_LIST_EMPTY(exodrone_launchers)
/// Resizes storage component depending on slots used by tools.
/obj/item/exodrone/proc/update_storage_size()
- var/datum/component/storage/storage = GetComponent(/datum/component/storage/concrete)
- storage.max_items = EXODRONE_CARGO_SLOTS - length(tools)
+ atom_storage.max_slots = EXODRONE_CARGO_SLOTS - length(tools)
/// Builds ui data for drone storage.
/obj/item/exodrone/proc/get_cargo_data()
diff --git a/code/modules/fishing/aquarium/aquarium_kit.dm b/code/modules/fishing/aquarium/aquarium_kit.dm
index 3221acce755..4e53bc2e46d 100644
--- a/code/modules/fishing/aquarium/aquarium_kit.dm
+++ b/code/modules/fishing/aquarium/aquarium_kit.dm
@@ -21,12 +21,14 @@
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
- component_type = /datum/component/storage/concrete/fish_case
-
/obj/item/storage/fish_case/Initialize(mapload)
ADD_TRAIT(src, TRAIT_FISH_SAFE_STORAGE, TRAIT_GENERIC) // Before populate so fish instatiates in ready container already
. = ..()
+ create_storage(max_slots = 1)
+ atom_storage.can_hold_trait = TRAIT_FISH_CASE_COMPATIBILE
+ atom_storage.can_hold_description = "fish and aquarium equipment"
+
///Fish case with single random fish inside.
/obj/item/storage/fish_case/random/PopulateContents()
. = ..()
diff --git a/code/modules/fishing/fishing_equipment.dm b/code/modules/fishing/fishing_equipment.dm
index 6ad286c5285..35447d79b51 100644
--- a/code/modules/fishing/fishing_equipment.dm
+++ b/code/modules/fishing/fishing_equipment.dm
@@ -70,12 +70,11 @@
inhand_icon_state = "artistic_toolbox"
material_flags = NONE
-/obj/item/storage/toolbox/ComponentInitialize()
+/obj/item/storage/toolbox/Initialize()
. = ..()
// Can hold fishing rod despite the size
var/static/list/exception_cache = typecacheof(/obj/item/fishing_rod)
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.exception_hold = exception_cache
+ atom_storage.exception_hold = exception_cache
/obj/item/storage/toolbox/fishing/PopulateContents()
new /obj/item/bait_can/worm(src)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm
index 515d6d0f7a9..eae6a05d0d9 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm
@@ -86,7 +86,7 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
if(default_deconstruction_screwdriver(user, "fryer_off", "fryer_off", weapon)) //where's the open maint panel icon?!
return
else
- if(is_type_in_typecache(weapon, deepfry_blacklisted_items) || is_type_in_typecache(weapon, GLOB.oilfry_blacklisted_items) || SEND_SIGNAL(weapon, COMSIG_CONTAINS_STORAGE) || HAS_TRAIT(weapon, TRAIT_NODROP) || (weapon.item_flags & (ABSTRACT | DROPDEL)))
+ if(is_type_in_typecache(weapon, deepfry_blacklisted_items) || is_type_in_typecache(weapon, GLOB.oilfry_blacklisted_items) || weapon.atom_storage || HAS_TRAIT(weapon, TRAIT_NODROP) || (weapon.item_flags & (ABSTRACT | DROPDEL)))
return ..()
else if(!frying && user.transferItemToLoc(weapon, src))
fry(weapon, user)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
index c6f8e7ec4e8..9cf843fe2e1 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
@@ -183,7 +183,7 @@
if(ingredients.len >= max_n_of_items)
to_chat(user, span_warning("\The [src] is full, you can't put anything in!"))
return TRUE
- if(SEND_SIGNAL(T, COMSIG_TRY_STORAGE_TAKE, S, src))
+ if(T.atom_storage.attempt_remove(S, src))
loaded++
ingredients += S
if(loaded)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/processor.dm b/code/modules/food_and_drinks/kitchen_machinery/processor.dm
index 9155ef1a3fb..616d4700adf 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/processor.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/processor.dm
@@ -93,7 +93,7 @@
continue
var/datum/food_processor_process/P = PROCESSOR_SELECT_RECIPE(S)
if(P)
- if(SEND_SIGNAL(T, COMSIG_TRY_STORAGE_TAKE, S, src))
+ if(T.atom_storage.attempt_remove(S, src))
LAZYADD(processor_contents, S)
loaded++
diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
index a67c76511d2..7f3036ee8c4 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
@@ -155,8 +155,8 @@
else
return TRUE
else
- if(SEND_SIGNAL(O.loc, COMSIG_CONTAINS_STORAGE))
- return SEND_SIGNAL(O.loc, COMSIG_TRY_STORAGE_TAKE, O, src)
+ if(O.loc.atom_storage)
+ return O.loc.atom_storage.attempt_remove(O, src)
else
O.forceMove(src)
return TRUE
diff --git a/code/modules/holiday/easter.dm b/code/modules/holiday/easter.dm
index b49d491d40f..7c8b9c9cac7 100644
--- a/code/modules/holiday/easter.dm
+++ b/code/modules/holiday/easter.dm
@@ -36,8 +36,7 @@
/obj/item/storage/basket/easter/Initialize(mapload)
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.set_holdable(list(/obj/item/food/egg, /obj/item/food/chocolateegg, /obj/item/food/boiledegg, /obj/item/surprise_egg))
+ atom_storage.set_holdable(list(/obj/item/food/egg, /obj/item/food/chocolateegg, /obj/item/food/boiledegg, /obj/item/surprise_egg))
/obj/item/storage/basket/easter/proc/countEggs()
cut_overlays()
diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm
index 945d0cd453c..22ebffa3869 100644
--- a/code/modules/hydroponics/biogenerator.dm
+++ b/code/modules/hydroponics/biogenerator.dm
@@ -113,7 +113,7 @@
for(var/obj/item/food/grown/G in PB.contents)
if(i >= max_items)
break
- if(SEND_SIGNAL(PB, COMSIG_TRY_STORAGE_TAKE, G, src))
+ if(PB.atom_storage.attempt_remove(G, src))
i++
if(i= mod.w_class)
+ var/datum/storage/holding_storage = mod.loc.atom_storage
+ if(!holding_storage || holding_storage.max_specific_storage >= mod.w_class)
return
mod.forceMove(drop_location())
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
index 2dfeebafd1a..486593ce482 100644
--- a/code/modules/mod/modules/modules_general.dm
+++ b/code/modules/mod/modules/modules_general.dm
@@ -8,8 +8,6 @@
icon_state = "storage"
complexity = 3
incompatible_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/plate_compression)
- /// The storage component of the module.
- var/datum/component/storage/concrete/storage
/// Max weight class of items in the storage.
var/max_w_class = WEIGHT_CLASS_NORMAL
/// Max combined weight of all items in the storage.
@@ -19,35 +17,28 @@
/obj/item/mod/module/storage/Initialize(mapload)
. = ..()
- storage = AddComponent(/datum/component/storage/concrete)
- storage.max_w_class = max_w_class
- storage.max_combined_w_class = max_combined_w_class
- storage.max_items = max_items
- storage.allow_big_nesting = TRUE
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE)
+ create_storage(max_specific_storage = max_w_class, max_total_storage = max_combined_w_class, max_slots = max_items)
+ atom_storage.allow_big_nesting = TRUE
+ atom_storage.locked = TRUE
/obj/item/mod/module/storage/on_install()
- var/datum/component/storage/modstorage = mod.AddComponent(/datum/component/storage, storage)
- modstorage.max_w_class = max_w_class
- modstorage.max_combined_w_class = max_combined_w_class
- modstorage.max_items = max_items
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, FALSE)
- RegisterSignal(mod.chestplate, COMSIG_ITEM_PRE_UNEQUIP, .proc/on_chestplate_unequip)
+ var/datum/storage/modstorage = mod.create_storage(max_specific_storage = max_w_class, max_total_storage = max_combined_w_class, max_slots = max_items)
+ modstorage.set_real_location(src)
+ atom_storage.locked = FALSE
/obj/item/mod/module/storage/on_uninstall(deleting = FALSE)
- var/datum/component/storage/modstorage = mod.GetComponent(/datum/component/storage)
- storage.slaves -= modstorage
+ var/datum/storage/modstorage = mod.atom_storage
+ atom_storage.locked = TRUE
qdel(modstorage)
- UnregisterSignal(mod.chestplate, COMSIG_ITEM_PRE_UNEQUIP)
if(!deleting)
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_QUICK_EMPTY, drop_location())
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE)
+ atom_storage.remove_all(get_turf(src))
+ UnregisterSignal(mod.chestplate, COMSIG_ITEM_PRE_UNEQUIP)
/obj/item/mod/module/storage/proc/on_chestplate_unequip(obj/item/source, force, atom/newloc, no_move, invdrop, silent)
if(QDELETED(source) || !mod.wearer || newloc == mod.wearer || !mod.wearer.s_store)
return
to_chat(mod.wearer, span_notice("[src] tries to store [mod.wearer.s_store] inside itself."))
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, mod.wearer.s_store, mod.wearer, TRUE)
+ atom_storage?.attempt_insert(src, mod.wearer.s_store, mod.wearer, TRUE)
/obj/item/mod/module/storage/large_capacity
name = "MOD expanded storage module"
diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm
index e6532776af7..bf3e11ff398 100644
--- a/code/modules/paperwork/handlabeler.dm
+++ b/code/modules/paperwork/handlabeler.dm
@@ -88,7 +88,7 @@
qdel(I)
labels_left = initial(labels_left) //Yes, it's capped at its initial value
-/obj/item/hand_labeler/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user)
+/obj/item/hand_labeler/attackby_storage_insert(datum/storage, atom/storage_holder, mob/user)
return !mode
/obj/item/hand_labeler/borg
diff --git a/code/modules/photography/photos/album.dm b/code/modules/photography/photos/album.dm
index bbf315f5e8f..3fd36d17a74 100644
--- a/code/modules/photography/photos/album.dm
+++ b/code/modules/photography/photos/album.dm
@@ -16,10 +16,9 @@
/obj/item/storage/photo_album/Initialize(mapload)
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.set_holdable(list(/obj/item/photo))
- STR.max_combined_w_class = 42
- STR.max_items = 21
+ atom_storage.set_holdable(list(/obj/item/photo))
+ atom_storage.max_total_storage = 42
+ atom_storage.max_slots = 21
LAZYADD(SSpersistence.photo_albums, src)
/obj/item/storage/photo_album/Destroy()
@@ -53,7 +52,7 @@
continue
var/obj/item/photo/old/P = load_photo_from_disk(i)
if(istype(P))
- if(!SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, P, null, TRUE, TRUE))
+ if(!atom_storage?.attempt_insert(src, P, null, TRUE))
qdel(P)
/obj/item/storage/photo_album/hos
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index 3841b47bd1d..b2ad3beaeb7 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -255,7 +255,7 @@
/obj/item/stock_parts/cell/get_part_rating()
return maxcharge * 10 + charge
-/obj/item/stock_parts/cell/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user)
+/obj/item/stock_parts/cell/attackby_storage_insert(datum/storage, atom/storage_holder, mob/user)
var/obj/item/mod/control/mod = storage_holder
return !(istype(mod) && mod.open)
diff --git a/code/modules/projectiles/guns/ballistic/bow.dm b/code/modules/projectiles/guns/ballistic/bow.dm
index 224f1dc09f0..a23f0cf3bd6 100644
--- a/code/modules/projectiles/guns/ballistic/bow.dm
+++ b/code/modules/projectiles/guns/ballistic/bow.dm
@@ -103,11 +103,10 @@
/obj/item/storage/bag/quiver/Initialize(mapload)
. = ..()
- var/datum/component/storage/storage = GetComponent(/datum/component/storage)
- storage.max_w_class = WEIGHT_CLASS_TINY
- storage.max_items = 40
- storage.max_combined_w_class = 100
- storage.set_holdable(list(
+ atom_storage.max_specific_storage = WEIGHT_CLASS_TINY
+ atom_storage.max_slots = 40
+ atom_storage.max_total_storage = 100
+ atom_storage.set_holdable(list(
/obj/item/ammo_casing/caseless/arrow
))
diff --git a/code/modules/projectiles/guns/energy/dueling.dm b/code/modules/projectiles/guns/energy/dueling.dm
index 2dcd6a9b252..2db258cb89f 100644
--- a/code/modules/projectiles/guns/energy/dueling.dm
+++ b/code/modules/projectiles/guns/energy/dueling.dm
@@ -317,15 +317,14 @@
icon_broken = "medalbox+b"
base_icon_state = "medalbox"
-/obj/item/storage/lockbox/dueling/ComponentInitialize()
+/obj/item/storage/lockbox/dueling/Initialize()
. = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_SMALL
- STR.max_items = 2
- STR.set_holdable(list(/obj/item/gun/energy/dueling))
+ atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
+ atom_storage.max_slots = 2
+ atom_storage.set_holdable(list(/obj/item/gun/energy/dueling))
/obj/item/storage/lockbox/dueling/update_icon_state()
- if(SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED))
+ if(atom_storage?.locked)
icon_state = icon_locked
return ..()
if(broken)
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index 3c7c895c537..90c066843bf 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -232,9 +232,8 @@
data["autoCondiStyle"] = CONDIMASTER_STYLE_AUTO
data["isPillBottleLoaded"] = bottle ? 1 : 0
if(bottle)
- var/datum/component/storage/STRB = bottle.GetComponent(/datum/component/storage)
data["pillBottleCurrentAmount"] = bottle.contents.len
- data["pillBottleMaxAmount"] = STRB.max_items
+ data["pillBottleMaxAmount"] = bottle.atom_storage.max_slots
var/beaker_contents[0]
if(beaker)
@@ -386,10 +385,8 @@
var/target_loc = drop_location()
var/drop_threshold = INFINITY
if(bottle)
- var/datum/component/storage/STRB = bottle.GetComponent(
- /datum/component/storage)
- if(STRB)
- drop_threshold = STRB.max_items - bottle.contents.len
+ if(bottle.atom_storage)
+ drop_threshold = bottle.atom_storage.max_slots - bottle.contents.len
target_loc = bottle
for(var/i in 1 to amount)
if(i-1 < drop_threshold)
diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
index 22bab9373c4..f334b28da76 100644
--- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
+++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
@@ -174,7 +174,7 @@
//Fill machine with a bag!
if(istype(I, /obj/item/storage/bag))
var/list/inserted = list()
- if(SEND_SIGNAL(I, COMSIG_TRY_STORAGE_TAKE_TYPE, /obj/item/food/grown, src, limit - length(holdingitems), null, null, user, inserted))
+ if(I.atom_storage.remove_type(/obj/item/food/grown, src, limit - length(holdingitems), null, null, user, inserted))
for(var/i in inserted)
holdingitems[i] = TRUE
if(!I.contents.len)
diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm
index 302db2b807f..987405050a2 100644
--- a/code/modules/reagents/chemistry/reagents/food_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm
@@ -177,7 +177,7 @@
if(is_type_in_typecache(exposed_obj, GLOB.oilfry_blacklisted_items) || (exposed_obj.resistance_flags & INDESTRUCTIBLE))
exposed_obj.loc.visible_message(span_notice("The hot oil has no effect on [exposed_obj]!"))
return
- if(SEND_SIGNAL(exposed_obj, COMSIG_CONTAINS_STORAGE))
+ if(exposed_obj.atom_storage)
exposed_obj.loc.visible_message(span_notice("The hot oil splatters about as [exposed_obj] touches it. It seems too full to cook properly!"))
return
exposed_obj.loc.visible_message(span_warning("[exposed_obj] rapidly fries as it's splashed with hot oil! Somehow."))
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index fd04f5c4c05..c61c7229121 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -259,15 +259,18 @@
return src
//How disposal handles getting a storage dump from a storage object
-/obj/machinery/disposal/storage_contents_dump_act(datum/component/storage/src_object, mob/user)
+/obj/machinery/disposal/storage_contents_dump_act(datum/storage/src_object, mob/user)
. = ..()
if(.)
return
- for(var/obj/item/I in src_object.parent)
+ var/atom/resolve_parent = src_object.real_location?.resolve()
+ if(!resolve_parent)
+ return FALSE
+ for(var/obj/item/I in resolve_parent)
if(user.active_storage != src_object)
if(I.on_found(user))
return
- src_object.remove_from_storage(I, src)
+ src_object.attempt_remove(I, src)
return TRUE
// Disposal bin
@@ -286,10 +289,9 @@
/obj/machinery/disposal/bin/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/storage/bag/trash)) //Not doing component overrides because this is a specific type.
var/obj/item/storage/bag/trash/T = I
- var/datum/component/storage/STR = T.GetComponent(/datum/component/storage)
to_chat(user, span_warning("You empty the bag."))
for(var/obj/item/O in T.contents)
- STR.remove_from_storage(O,src)
+ T.atom_storage.attempt_remove(O,src)
T.update_appearance()
update_appearance()
else
diff --git a/code/modules/research/stock_parts.dm b/code/modules/research/stock_parts.dm
index 6c4203371e3..8aab5b28eb9 100644
--- a/code/modules/research/stock_parts.dm
+++ b/code/modules/research/stock_parts.dm
@@ -10,11 +10,21 @@ If you create T5+ please take a pass at mech_fabricator.dm. The parts being good
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
w_class = WEIGHT_CLASS_HUGE
- component_type = /datum/component/storage/concrete/rped
var/works_from_distance = FALSE
var/pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/rped.ogg'
var/alt_sound = null
+/obj/item/storage/part_replacer/Initialize()
+ . = ..()
+
+ atom_storage.allow_quick_empty = TRUE
+ atom_storage.allow_quick_gather = TRUE
+ atom_storage.max_slots = 50
+ atom_storage.max_total_storage = 100
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.numerical_stacking = TRUE
+ atom_storage.set_holdable(list(/obj/item/stock_parts), null)
+
/obj/item/storage/part_replacer/pre_attack(obj/attacked_object, mob/living/user, params)
if(!istype(attacked_object, /obj/machinery) && !istype(attacked_object, /obj/structure/frame/machine))
return ..()
@@ -88,11 +98,14 @@ If you create T5+ please take a pass at mech_fabricator.dm. The parts being good
works_from_distance = TRUE
pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/pshoom.ogg'
alt_sound = 'sound/items/pshoom_2.ogg'
- component_type = /datum/component/storage/concrete/bluespace/rped
/obj/item/storage/part_replacer/bluespace/Initialize(mapload)
. = ..()
+ atom_storage.max_slots = 400
+ atom_storage.max_total_storage = 800
+ atom_storage.max_specific_storage = WEIGHT_CLASS_GIGANTIC
+
RegisterSignal(src, COMSIG_ATOM_ENTERED, .proc/on_part_entered)
RegisterSignal(src, COMSIG_ATOM_EXITED, .proc/on_part_exited)
diff --git a/code/modules/research/xenobiology/crossbreeding/reproductive.dm b/code/modules/research/xenobiology/crossbreeding/reproductive.dm
index 204cf215417..eb717e1449a 100644
--- a/code/modules/research/xenobiology/crossbreeding/reproductive.dm
+++ b/code/modules/research/xenobiology/crossbreeding/reproductive.dm
@@ -15,7 +15,6 @@ Reproductive extracts:
var/cooldown = 3 SECONDS
var/feedAmount = 3
var/last_produce = 0
- var/datum/component/storage/concrete/extract_inventory/slimeStorage
/obj/item/slimecross/reproductive/examine()
. = ..()
@@ -23,45 +22,42 @@ Reproductive extracts:
/obj/item/slimecross/reproductive/Initialize(mapload)
. = ..()
- slimeStorage = AddComponent(/datum/component/storage/concrete/extract_inventory)
+ create_storage(type = /datum/storage/extract_inventory)
/obj/item/slimecross/reproductive/attackby(obj/item/O, mob/user)
+ var/datum/storage/extract_inventory/slime_storage = atom_storage
+ if(!istype(slime_storage))
+ return
+
if((last_produce + cooldown) > world.time)
to_chat(user, span_warning("[src] is still digesting!"))
return
if(length(contents) >= feedAmount) //if for some reason the contents are full, but it didnt digest, attempt to digest again
to_chat(user, span_warning("[src] appears to be full but is not digesting! Maybe poking it stimulated it to digest."))
- slimeStorage.processCubes(src, user)
+ slime_storage?.processCubes(user)
return
if(istype(O, /obj/item/storage/bag/xeno))
var/list/inserted = list()
- SEND_SIGNAL(O, COMSIG_TRY_STORAGE_TAKE_TYPE, /obj/item/food/monkeycube, src, feedAmount - length(contents), TRUE, FALSE, user, inserted)
+ O.atom_storage.remove_type(/obj/item/food/monkeycube, src, feedAmount - length(contents), TRUE, FALSE, user, inserted)
if(inserted.len)
to_chat(user, span_notice("You feed [length(inserted)] Monkey Cube[p_s()] to [src], and it pulses gently."))
playsound(src, 'sound/items/eatfood.ogg', 20, TRUE)
- slimeStorage.processCubes(src, user)
+ slime_storage?.processCubes(user)
else
to_chat(user, span_warning("There are no monkey cubes in the bio bag!"))
return
else if(istype(O, /obj/item/food/monkeycube))
- slimeStorage.locked = FALSE //This weird unlock-then-lock nonsense brought to you courtesy of storage jank
- if(SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, O, user, TRUE))
+ if(atom_storage?.attempt_insert(src, O, user, override = TRUE, force = TRUE))
to_chat(user, span_notice("You feed 1 Monkey Cube to [src], and it pulses gently."))
- slimeStorage.processCubes(src, user)
+ slime_storage?.processCubes(user)
playsound(src, 'sound/items/eatfood.ogg', 20, TRUE)
- slimeStorage.locked = TRUE //relock once its done inserting
return
else
- slimeStorage.locked = TRUE //it couldnt insert for some reason, relock it
to_chat(user, span_notice("The [src] rejects the Monkey Cube!")) //in case it fails to insert for whatever reason you get feedback
-/obj/item/slimecross/reproductive/Destroy()
- slimeStorage = null
- return ..()
-
/obj/item/slimecross/reproductive/grey
extract_type = /obj/item/slime_extract/grey
colour = "grey"
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index 69a51a8d874..6fddd6ac561 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -904,7 +904,7 @@
C.add_atom_colour("#FF0000", FIXED_COLOUR_PRIORITY)
qdel(src)
-/obj/item/slimepotion/speed/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user)
+/obj/item/slimepotion/speed/attackby_storage_insert(datum/storage, atom/storage_holder, mob/user)
if(!isitem(storage_holder))
return TRUE
if(istype(storage_holder, /obj/item/mod/control))
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index 73b613d2970..4c2084fb627 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -712,7 +712,7 @@
/obj/item/storage/pod/attack_hand(mob/user, list/modifiers)
if (can_interact(user))
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SHOW, user)
+ atom_storage?.show_contents(user)
return TRUE
/obj/item/storage/pod/MouseDrop(over_object, src_location, over_location)
diff --git a/code/modules/unit_tests/serving_tray.dm b/code/modules/unit_tests/serving_tray.dm
index 00e911ae509..706a1407edf 100644
--- a/code/modules/unit_tests/serving_tray.dm
+++ b/code/modules/unit_tests/serving_tray.dm
@@ -13,7 +13,7 @@
TEST_ASSERT_EQUAL((the_bread in test_tray.contents), FALSE, "The bread is on the serving tray at test start")
// set the tray to single item mode the dirty way
- var/datum/component/storage/tray_storage = test_tray.GetComponent(/datum/component/storage)
+ var/datum/storage/tray_storage = test_tray.atom_storage
tray_storage.collection_mode = COLLECT_ONE
test_tray.pre_attack(the_bread, human)
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index 0656327c4f9..4a6a1a62397 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -486,7 +486,7 @@ GLOBAL_LIST_EMPTY(vending_products)
to_chat(user, span_warning("[src]'s compartment is full."))
break
if(canLoadItem(the_item) && loadingAttempt(the_item,user))
- SEND_SIGNAL(T, COMSIG_TRY_STORAGE_TAKE, the_item, src, TRUE)
+ T.atom_storage?.attempt_remove(the_item, src)
loaded++
else
denied_items++
diff --git a/config/admins.txt b/config/admins.txt
index 9956a197b9b..a051d77535e 100644
--- a/config/admins.txt
+++ b/config/admins.txt
@@ -137,6 +137,7 @@ shizcalev = Game Master
NicBR = Game Master
LoserWasTaken = Game Master
Fikou = Game Master
+Magatsuchi = Game Master
Skoglol = Game Master
4dplanner = Game Master
Time-Green = Game Master
diff --git a/tgstation.dme b/tgstation.dme
index 83e39dc553d..85000b0f6b6 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -249,7 +249,6 @@
#include "code\__DEFINES\dcs\signals\signals_spatial_grid.dm"
#include "code\__DEFINES\dcs\signals\signals_specie.dm"
#include "code\__DEFINES\dcs\signals\signals_spell.dm"
-#include "code\__DEFINES\dcs\signals\signals_storage.dm"
#include "code\__DEFINES\dcs\signals\signals_subsystem.dm"
#include "code\__DEFINES\dcs\signals\signals_swab.dm"
#include "code\__DEFINES\dcs\signals\signals_techweb.dm"
@@ -774,6 +773,7 @@
#include "code\datums\components\explodable.dm"
#include "code\datums\components\faction_granter.dm"
#include "code\datums\components\fishing_spot.dm"
+#include "code\datums\components\food_storage.dm"
#include "code\datums\components\force_move.dm"
#include "code\datums\components\fov_handler.dm"
#include "code\datums\components\fullauto.dm"
@@ -896,19 +896,6 @@
#include "code\datums\components\riding\riding.dm"
#include "code\datums\components\riding\riding_mob.dm"
#include "code\datums\components\riding\riding_vehicle.dm"
-#include "code\datums\components\storage\food_storage.dm"
-#include "code\datums\components\storage\storage.dm"
-#include "code\datums\components\storage\concrete\_concrete.dm"
-#include "code\datums\components\storage\concrete\bag_of_holding.dm"
-#include "code\datums\components\storage\concrete\bluespace.dm"
-#include "code\datums\components\storage\concrete\extract_inventory.dm"
-#include "code\datums\components\storage\concrete\fish_case.dm"
-#include "code\datums\components\storage\concrete\implant.dm"
-#include "code\datums\components\storage\concrete\pockets.dm"
-#include "code\datums\components\storage\concrete\rped.dm"
-#include "code\datums\components\storage\concrete\stack.dm"
-#include "code\datums\components\storage\concrete\trading_cards.dm"
-#include "code\datums\components\storage\concrete\wallet.dm"
#include "code\datums\diseases\_disease.dm"
#include "code\datums\diseases\_MobProcs.dm"
#include "code\datums\diseases\adrenal_crisis.dm"
@@ -1198,6 +1185,12 @@
#include "code\datums\status_effects\debuffs\jitteriness.dm"
#include "code\datums\status_effects\debuffs\speech_debuffs.dm"
#include "code\datums\status_effects\debuffs\strandling.dm"
+#include "code\datums\storage\storage.dm"
+#include "code\datums\storage\subtypes\bag_of_holding.dm"
+#include "code\datums\storage\subtypes\cards.dm"
+#include "code\datums\storage\subtypes\extract_inventory.dm"
+#include "code\datums\storage\subtypes\implant.dm"
+#include "code\datums\storage\subtypes\pockets.dm"
#include "code\datums\votes\_vote_datum.dm"
#include "code\datums\votes\custom_vote.dm"
#include "code\datums\votes\map_vote.dm"