diff --git a/code/__DEFINES/storage.dm b/code/__DEFINES/storage.dm
index 01d77e0df2f2..ac23c09538e6 100644
--- a/code/__DEFINES/storage.dm
+++ b/code/__DEFINES/storage.dm
@@ -76,16 +76,16 @@ GLOBAL_LIST_INIT(default_weight_class_to_volume, list(
//
#define MAX_WEIGHT_CLASS_S_CONTAINER WEIGHT_CLASS_SMALL
#define MAX_WEIGHT_CLASS_M_CONTAINER WEIGHT_CLASS_NORMAL
-#define MAX_WEIGHT_CLASS_BACKPACK WEIGHT_CLASS_NORMAL
+#define MAX_WEIGHT_CLASS_BACKPACK WEIGHT_CLASS_BULKY
#define MAX_WEIGHT_CLASS_DUFFEL WEIGHT_CLASS_BULKY
// max_volume for storages
#define STORAGE_VOLUME_CONTAINER_S DEFAULT_VOLUME_NORMAL //4 small items
#define STORAGE_VOLUME_CONTAINER_M (DEFAULT_VOLUME_NORMAL * 2) //8 small items
#define STORAGE_VOLUME_SATCHEL (DEFAULT_VOLUME_NORMAL * 4) //4 normal items
-#define STORAGE_VOLUME_BACKPACK (DEFAULT_VOLUME_NORMAL * 6) //1.5x satchel, 3 bulky items
-#define STORAGE_VOLUME_DUFFLEBAG (DEFAULT_VOLUME_NORMAL * 8) // 2 huge items, or 4 bulky items
-#define STORAGE_VOLUME_BAG_OF_HOLDING (DEFAULT_VOLUME_NORMAL * 9) //1.5X backpack
+#define STORAGE_VOLUME_BACKPACK (DEFAULT_VOLUME_NORMAL * 6) //3 bulky items
+#define STORAGE_VOLUME_DUFFLEBAG (DEFAULT_VOLUME_NORMAL * 10) //~1.4X backpack, 5 bulky items
+#define STORAGE_VOLUME_BAG_OF_HOLDING (DEFAULT_VOLUME_NORMAL * 10)
//Whitelist for the suit storage slot on medical suits
#define MEDICAL_SUIT_ALLOWED_ITEMS list( \
diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm
index fab8ffdf3a2c..58002de8ce36 100644
--- a/code/datums/components/storage/storage.dm
+++ b/code/datums/components/storage/storage.dm
@@ -25,7 +25,12 @@
var/list/mob/is_using //lazy list of mobs looking at the contents of this storage.
var/locked = FALSE //when locked nothing can see inside or use it.
- var/locked_flavor = "locked" //prevents tochat messages related to locked from sending
+ var/locked_flavor = "seems to be locked!" //prevents tochat messages related to locked from sending
+
+ /// If the storage object can be accessed while equipped to slot by mob(e.g. backpack in back slot)
+ var/worn_access = TRUE
+ /// If the storage object can be accessed while being held anywhere on a mob
+ var/carry_access = TRUE
/// Storage flags, including what kinds of limiters we use for how many items we can hold
var/storage_flags = STORAGE_FLAGS_LEGACY_DEFAULT
@@ -110,6 +115,7 @@
RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK, PROC_REF(preattack_intercept))
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(attack_self))
RegisterSignal(parent, COMSIG_ITEM_PICKUP, PROC_REF(signal_on_pickup))
+ RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(signal_on_equip))
RegisterSignal(parent, COMSIG_MOVABLE_POST_THROW, PROC_REF(close_all))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
@@ -222,7 +228,7 @@
SIGNAL_HANDLER
if(locked)
- to_chat(M, "[parent] seems to be [locked_flavor]!")
+ to_chat(M, "[parent] [locked_flavor]")
return FALSE
if((M.get_active_held_item() == parent) && allow_quick_empty)
INVOKE_ASYNC(src, PROC_REF(quick_empty), M)
@@ -233,8 +239,10 @@
if(!isitem(O) || !click_gather || SEND_SIGNAL(O, COMSIG_CONTAINS_STORAGE))
return FALSE
. = COMPONENT_NO_ATTACK
+ if(!access_check())
+ return FALSE
if(locked)
- to_chat(M, "[parent] seems to be [locked_flavor]!")
+ to_chat(M, "[parent] [locked_flavor]")
return FALSE
var/obj/item/I = O
if(collection_mode == COLLECT_ONE)
@@ -309,6 +317,8 @@
var/atom/A = parent
if(!M.canUseStorage() || !A.Adjacent(M) || M.incapacitated())
return
+ if(!access_check())
+ return FALSE
if(locked)
to_chat(M, "[parent] seems to be [locked_flavor]!")
return FALSE
@@ -426,6 +436,8 @@
var/atom/A = parent
var/atom/dump_destination = dest_object.get_dumping_location()
if(M.CanReach(A) && dump_destination && M.CanReach(dump_destination))
+ if(!access_check())
+ return FALSE
if(locked)
to_chat(M, "[parent] seems to be [locked_flavor]!")
return FALSE
@@ -530,6 +542,8 @@
if(locked && !force)
to_chat(M, "[parent] seems to be [locked_flavor]!")
return FALSE
+ if(!access_check())
+ return FALSE
if(force || M.CanReach(parent, view_only = TRUE))
if(use_sound && !silent)
playsound(A, use_sound, 50, TRUE, -5)
@@ -548,7 +562,7 @@
//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)
+/datum/component/storage/proc/can_be_inserted(obj/item/I, stop_messages = FALSE, mob/M, bypass_access = FALSE)
if(!istype(I) || (I.item_flags & ABSTRACT))
return FALSE //Not an item
if(I == parent)
@@ -557,6 +571,9 @@
var/atom/host = parent
if(real_location == I.loc)
return FALSE //Means the item is already in the storage item
+ if(!bypass_access)//For stuff like setting up outfits, setting up roundstart backpacks, etc.
+ if(!access_check())
+ return FALSE
if(locked)
if(M && !stop_messages)
host.add_fingerprint(M)
@@ -660,17 +677,17 @@
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)
+/datum/component/storage/proc/signal_insertion_attempt(datum/source, obj/item/I, mob/M, silent = FALSE, force = FALSE, bypass_access = FALSE)
SIGNAL_HANDLER
- if((!force && !can_be_inserted(I, TRUE, M)) || (I == parent))
+ if((!force && !can_be_inserted(I, TRUE, M, bypass_access)) || (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)
+/datum/component/storage/proc/signal_can_insert(datum/source, obj/item/I, mob/M, silent = FALSE, bypass_access = FALSE)
SIGNAL_HANDLER
- return can_be_inserted(I, silent, M)
+ return can_be_inserted(I, silent, M, bypass_access)
/datum/component/storage/proc/show_to_ghost(datum/source, mob/dead/observer/M)
SIGNAL_HANDLER
@@ -755,6 +772,8 @@
if(A.loc == user)
. = COMPONENT_NO_ATTACK_HAND
+ if(!access_check())
+ return FALSE
if(locked)
to_chat(user, "[parent] seems to be [locked_flavor]!")
else
@@ -769,6 +788,12 @@
for(var/mob/M in can_see_contents() - user)
close(M)
+/datum/component/storage/proc/signal_on_equip(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ if(!worn_access)
+ close(user)
+
/datum/component/storage/proc/signal_take_obj(datum/source, atom/movable/AM, new_loc, force = FALSE)
SIGNAL_HANDLER
@@ -794,6 +819,8 @@
/datum/component/storage/proc/on_alt_click_async(datum/source, mob/user)
if(!isliving(user) || !user.CanReach(parent) || user.incapacitated())
return
+ if(!access_check())
+ return FALSE
if(locked)
to_chat(user, "[parent] seems to be [locked_flavor]!")
return
@@ -833,3 +860,28 @@
//Gets our max volume
/datum/component/storage/proc/get_max_volume()
return max_volume || AUTO_SCALE_STORAGE_VOLUME(max_w_class, max_combined_w_class)
+
+//checks for mob-related storage access conditions
+/datum/component/storage/proc/access_check(message = TRUE)
+ var/atom/ourparent = parent
+ var/datum/component/storage/otherstorage
+
+ //if we are inside another storage object, let's move up and check access there instead
+ if(istype(ourparent.loc, /obj/item/storage))
+ ourparent = ourparent.loc
+ //get our parent's storage component so we can check their access vars
+ otherstorage = ourparent.GetComponent(/datum/component/storage)
+
+ if(ismob(ourparent.loc))
+ var/mob/holder = ourparent.loc
+
+ if(otherstorage? !otherstorage.carry_access : !carry_access)
+ if(message)
+ to_chat(holder, span_warning( "[ourparent] is too cumbersome to open inhand, you're going to have to set it down!"))
+ return FALSE
+
+ if((otherstorage? !otherstorage.worn_access : !worn_access) && !holder.held_items.Find(ourparent))
+ if(message)
+ to_chat(holder, span_warning( "Your arms aren't long enough to reach [ourparent] while it's on your back!"))
+ return FALSE
+ return TRUE
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 58cbaabc5e86..4bbd88745752 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -413,6 +413,14 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(anchored)
return
+ //check if the item is inside another item's storage
+ if(istype(loc, /obj/item/storage))
+ //if so, can we actually access it?
+ var/datum/component/storage/ourstorage = loc.GetComponent(/datum/component/storage)
+ if(!ourstorage.access_check())
+ SEND_SIGNAL(loc, COMSIG_TRY_STORAGE_HIDE_FROM, user)//you're not supposed to be in here right now, punk!
+ return
+
if(resistance_flags & ON_FIRE)
var/mob/living/carbon/C = user
var/can_handle_hot = FALSE
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index 34eba9a25191..dc3fa7e790f3 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -39,6 +39,15 @@
STR.max_volume = STORAGE_VOLUME_BACKPACK
STR.max_w_class = MAX_WEIGHT_CLASS_BACKPACK
STR.use_sound = 'sound/items/storage/unzip.ogg'
+ STR.worn_access = FALSE
+
+/obj/machinery/examine(mob/user)
+ . = ..()
+ var/datum/component/storage/bpack = GetComponent(/datum/component/storage)
+ if(bpack.worn_access == FALSE)
+ . += span_notice("You won't be able to open this once it's on your back.")
+ if(bpack.carry_access == FALSE)
+ . += span_notice("You'll have to set this down on the floor if you want to open it.")
/*
* Backpack Types
@@ -184,8 +193,9 @@
/obj/item/storage/backpack/satchel/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_volume = STORAGE_VOLUME_BACKPACK
+ STR.max_volume = STORAGE_VOLUME_SATCHEL
STR.max_w_class = MAX_WEIGHT_CLASS_M_CONTAINER
+ STR.worn_access = TRUE
/obj/item/storage/backpack/satchel/leather
name = "leather satchel"
@@ -316,6 +326,13 @@
greyscale_icon_state = "satchel"
greyscale_colors = list(list(15, 16), list(19, 13), list(13, 18))
+/obj/item/storage/backpack/messenger/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_volume = STORAGE_VOLUME_SATCHEL
+ STR.max_w_class = MAX_WEIGHT_CLASS_M_CONTAINER
+ STR.worn_access = TRUE
+
/obj/item/storage/backpack/messenger/chem
name = "chemistry messenger bag"
desc = "A sterile backpack worn over one shoulder. This one is in Chemistry colors."
@@ -396,6 +413,7 @@
STR.max_w_class = MAX_WEIGHT_CLASS_DUFFEL
LAZYINITLIST(STR.exception_hold) // This code allows you to fit one mob holder into a duffel bag
STR.exception_hold += typecacheof(/obj/item/clothing/head/mob_holder)
+ STR.carry_access = FALSE
/obj/item/storage/backpack/duffelbag/captain
name = "captain's duffel bag"
diff --git a/code/game/objects/items/storage/ration.dm b/code/game/objects/items/storage/ration.dm
index 9a9e4a5a2617..9254e3014f7b 100644
--- a/code/game/objects/items/storage/ration.dm
+++ b/code/game/objects/items/storage/ration.dm
@@ -32,7 +32,7 @@
/obj/item/reagent_containers/food,
/obj/item/ration_heater))
STR.locked = TRUE
- STR.locked_flavor = "sealed closed"
+ STR.locked_flavor = "seems to be sealed closed!"
/obj/item/storage/ration/proc/open_ration(mob/user)
to_chat(user, "You tear open \the [src].")
diff --git a/code/modules/clothing/outfits/ert/roumain_ert.dm b/code/modules/clothing/outfits/ert/roumain_ert.dm
index efe7828173be..74802bdbba0b 100644
--- a/code/modules/clothing/outfits/ert/roumain_ert.dm
+++ b/code/modules/clothing/outfits/ert/roumain_ert.dm
@@ -32,7 +32,7 @@
l_pocket = /obj/item/ammo_box/a44roum_speedloader
- backpack_contents = list(/obj/item/ammo_box/magazine/c45_firestorm_mag/pan = 3, /obj/item/ammo_box/a44roum_speedloader = 2, /obj/item/storage/box/ammo/a44roum)
+ backpack_contents = list(/obj/item/ammo_box/magazine/c45_firestorm_mag/pan = 2, /obj/item/ammo_box/a44roum_speedloader = 2, /obj/item/storage/box/ammo/a44roum)
/datum/outfit/job/roumain/ert/vickland
name = "ERT - Saint-Roumain Hunter (Vickland)" // vickland and candor
diff --git a/code/modules/clothing/outfits/factions/minutemen.dm b/code/modules/clothing/outfits/factions/minutemen.dm
index 3f99cc85087f..040acb06d7c4 100644
--- a/code/modules/clothing/outfits/factions/minutemen.dm
+++ b/code/modules/clothing/outfits/factions/minutemen.dm
@@ -224,8 +224,7 @@
l_hand = /obj/item/storage/briefcase
- backpack = /obj/item/storage/backpack/satchel/leather
- satchel = /obj/item/storage/backpack/satchel/leather
+ backpack = /obj/item/storage/backpack/industrial
l_pocket = /obj/item/toy/crayon/white
r_pocket = /obj/item/radio
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index cbacb2099a0d..5067b4b5bf38 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1273,7 +1273,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
return TRUE
if(ITEM_SLOT_BACKPACK)
if(H.back)
- if(SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_CAN_INSERT, I, H, TRUE))
+ if(SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_CAN_INSERT, I, H, TRUE, TRUE))
return TRUE
return FALSE
return FALSE //Unsupported slot
diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm
index 9525ebd6ec9b..eddb352a47ee 100644
--- a/code/modules/mob/living/carbon/inventory.dm
+++ b/code/modules/mob/living/carbon/inventory.dm
@@ -106,7 +106,7 @@
put_in_hands(I)
update_inv_hands()
if(ITEM_SLOT_BACKPACK)
- if(!back || !SEND_SIGNAL(back, COMSIG_TRY_STORAGE_INSERT, I, src, TRUE))
+ if(!back || !SEND_SIGNAL(back, COMSIG_TRY_STORAGE_INSERT, I, src, TRUE, FALSE, TRUE))
not_handled = TRUE
if(ITEM_SLOT_ID)
if(!wear_id || !SEND_SIGNAL(wear_id, COMSIG_TRY_STORAGE_INSERT, I, src, TRUE))