diff --git a/code/__DEFINES/dcs/signals/signals_mod.dm b/code/__DEFINES/dcs/signals/signals_mod.dm
index bc76dd3e87a..21442ee73eb 100644
--- a/code/__DEFINES/dcs/signals/signals_mod.dm
+++ b/code/__DEFINES/dcs/signals/signals_mod.dm
@@ -54,3 +54,9 @@
#define COMSIG_MOD_GET_VISOR_OVERLAY "mod_get_visor_overlay"
/// Called when the MOD control unit updates its parts speed
#define COMSIG_MOD_UPDATE_SPEED "mod_update_speed"
+/// Called on the module when the user attempts to install it into a MOD control unit (obj/item/mod/control/suit, mob/user)
+#define COMSIG_MODULE_TRY_INSTALL "mod_module_try_install"
+/// Called on the MOD control unit when the user attempts to install a module (obj/item/mod/module/module, mob/user)
+#define COMSIG_MOD_TRY_INSTALL_MODULE "mod_try_install_module"
+ /// Cancel module installation, with no message. Include feedback on your cancel.
+ #define MOD_ABORT_INSTALL (1<<0)
diff --git a/code/datums/components/anomalock_module.dm b/code/datums/components/anomalock_module.dm
new file mode 100644
index 00000000000..5dada406af9
--- /dev/null
+++ b/code/datums/components/anomalock_module.dm
@@ -0,0 +1,130 @@
+/datum/component/anomaly_locked_module
+ var/obj/item/assembly/signaler/anomaly/core
+ /// Accepted types of anomaly cores.
+ var/list/accepted_anomalies
+ /// If the core is removable once socketed.
+ var/core_removable
+ /// A proc to call before the core is inserted. Returns an ITEM_INTERACT define, which the component will itself return.
+ var/pre_insert_callback
+ /// A proc to call when the core is inserted.
+ var/core_insert_callback
+ /// A proc to call when the core is removed.
+ var/core_remove_callback
+
+/datum/component/anomaly_locked_module/Initialize(list/anomaly_types, prebuilt = FALSE, removable = TRUE, pre_insert_callback, insert_callback, remove_callback)
+ . = ..()
+ if(!istype(parent, /obj/item/mod/module))
+ return COMPONENT_INCOMPATIBLE
+ accepted_anomalies = typecacheof(anomaly_types)
+ core_removable = removable
+ src.pre_insert_callback = pre_insert_callback
+ core_insert_callback = insert_callback
+ core_remove_callback = remove_callback
+ if(!(prebuilt && length(anomaly_types)))
+ return
+ var/obj/item/assembly/signaler/anomaly/core_type = pick(anomaly_types)
+ core = new core_type(parent)
+
+/datum/component/anomaly_locked_module/Destroy(force)
+ QDEL_NULL(core)
+ return ..()
+
+/datum/component/anomaly_locked_module/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_MODULE_TRIGGERED, PROC_REF(on_module_triggered))
+ RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_interact))
+ RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_SCREWDRIVER), PROC_REF(on_screwdriver_act))
+ RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
+ RegisterSignal(parent, COMSIG_ATOM_UPDATE_ICON_STATE, PROC_REF(on_update_icon_state))
+
+/datum/component/anomaly_locked_module/UnregisterFromParent()
+ UnregisterSignal(parent, list(
+ COMSIG_MODULE_TRIGGERED,
+ COMSIG_ATOM_ITEM_INTERACTION,
+ COMSIG_ATOM_TOOL_ACT(TOOL_SCREWDRIVER),
+ COMSIG_ATOM_EXAMINE,
+ COMSIG_ATOM_UPDATE_ICON_STATE,
+ ))
+
+/datum/component/anomaly_locked_module/proc/on_module_triggered(obj/item/mod/module/source, mob/living/wearer)
+ SIGNAL_HANDLER
+ if(!core)
+ source.balloon_alert(wearer, "no core!")
+ return MOD_ABORT_USE
+
+/datum/component/anomaly_locked_module/proc/on_item_interact(obj/item/mod/module/source, mob/living/user, obj/item/tool, list/modifiers)
+ SIGNAL_HANDLER
+ if(!is_type_in_typecache(tool, accepted_anomalies))
+ return 0
+ if(core)
+ source.balloon_alert(user, "already has core!")
+ return ITEM_INTERACT_FAILURE
+ if(pre_insert_callback)
+ var/callback_return
+ if(istype(pre_insert_callback, /datum/callback))
+ var/datum/callback/pre_insert_callback_datum = pre_insert_callback
+ callback_return = pre_insert_callback_datum.Invoke(user, tool, modifiers)
+ else
+ callback_return = call(source, pre_insert_callback)(user, tool, modifiers)
+ if(callback_return)
+ return callback_return
+ return insert_core(source, user, tool, modifiers)
+
+/datum/component/anomaly_locked_module/proc/insert_core(obj/item/mod/module/source, mob/living/user, obj/item/tool, list/modifiers)
+ if(!user.transferItemToLoc(tool, source))
+ return ITEM_INTERACT_FAILURE
+ core = tool
+ source.balloon_alert(user, "core inserted")
+ playsound(source, 'sound/machines/click.ogg', 30, TRUE)
+ source.update_appearance(UPDATE_ICON_STATE)
+ if(core_insert_callback)
+ if(istype(core_insert_callback, /datum/callback))
+ var/datum/callback/core_insert_callback_datum = core_insert_callback
+ core_insert_callback_datum.Invoke(core, user, modifiers)
+ else
+ call(source, core_insert_callback)(core, user, modifiers)
+ return ITEM_INTERACT_SUCCESS
+
+/datum/component/anomaly_locked_module/proc/on_screwdriver_act(obj/item/mod/module/source, mob/living/user, obj/item/tool)
+ SIGNAL_HANDLER
+ if(!core)
+ source.balloon_alert(user, "no core!")
+ return ITEM_INTERACT_FAILURE
+ if(!core_removable)
+ source.balloon_alert(user, "cannot remove core!")
+ INVOKE_ASYNC(src, PROC_REF(try_remove_core), source, user, tool)
+ return ITEM_INTERACT_SUCCESS
+
+/datum/component/anomaly_locked_module/proc/try_remove_core(obj/item/mod/module/source, mob/living/user, obj/item/tool)
+ if(!do_after(user, 3 SECONDS, source))
+ source.balloon_alert(user, "interrupted!")
+ return
+ source.balloon_alert(user, "core removed")
+ core.forceMove(source.drop_location())
+ if(source.Adjacent(user) && !issilicon(user))
+ user.put_in_hands(core)
+ core = null
+ source.update_appearance(UPDATE_ICON_STATE)
+ if(core_remove_callback)
+ if(istype(core_remove_callback, /datum/callback))
+ var/datum/callback/core_remove_callback_datum = core_remove_callback
+ core_remove_callback_datum.Invoke(core, user)
+ else
+ call(source, core_remove_callback)(core, user)
+
+/datum/component/anomaly_locked_module/proc/on_examine(obj/item/mod/module/source, mob/viewer, list/examine_list)
+ SIGNAL_HANDLER
+ if(!length(accepted_anomalies))
+ return
+ if(core)
+ examine_list += span_notice("There is a [core.name] installed in it. [core_removable ? "You could remove it with a screwdriver..." : "Unfortunately, due to a design quirk, it's unremovable."]")
+ return
+ var/list/core_list = list()
+ for(var/atom/core_path as anything in accepted_anomalies)
+ core_list += initial(core_path.name)
+ examine_list += span_notice("You need to insert \a [english_list(core_list, and_text = " or ")] for this module to function.")
+ if(!core_removable)
+ examine_list += span_notice("Due to some design quirk, once a core is inserted, it won't be removable.")
+
+/datum/component/anomaly_locked_module/proc/on_update_icon_state(obj/item/mod/module/source)
+ SIGNAL_HANDLER
+ source.icon_state = source::icon_state + (core ? "-core" : "")
diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm
index d8fdb8f846b..9983761fa91 100644
--- a/code/datums/helper_datums/teleport.dm
+++ b/code/datums/helper_datums/teleport.dm
@@ -31,8 +31,13 @@
if(istype(teleatom, /obj/item/storage/backpack/holding))
precision = rand(1,100)
- var/static/list/bag_cache = typecacheof(/obj/item/storage/backpack/holding)
+ var/static/list/bag_cache = typecacheof(/obj/item/storage/backpack/holding, /obj/item/mod/control, /obj/item/mod/module/storage)
var/list/bagholding = typecache_filter_list(teleatom.get_all_contents(), bag_cache)
+ for(var/obj/item/mod/modsuit_or_module in bagholding)
+ var/datum/storage/storage = modsuit_or_module.atom_storage
+ if(istype(storage, /datum/storage/bag_of_holding) && storage.real_location == storage.parent)
+ continue
+ bagholding -= modsuit_or_module
if(bagholding.len)
precision = max(rand(1,100)*bagholding.len,100)
if(isliving(teleatom))
diff --git a/code/datums/storage/subtypes/mod_storage.dm b/code/datums/storage/subtypes/mod_storage.dm
new file mode 100644
index 00000000000..5f6fdc791df
--- /dev/null
+++ b/code/datums/storage/subtypes/mod_storage.dm
@@ -0,0 +1,22 @@
+/datum/storage/mod_storage
+ max_total_storage = 15
+ max_slots = 7
+
+/datum/storage/mod_storage/expanded
+ max_total_storage = 21
+ max_slots = 14
+
+/datum/storage/mod_storage/syndicate
+ max_total_storage = 30
+ max_slots = 21
+
+/datum/storage/mod_storage/belt
+ max_total_storage = 21
+ max_slots = 7
+ max_specific_storage = WEIGHT_CLASS_SMALL
+
+/datum/storage/mod_storage/bluespace
+ max_total_storage = 60
+ max_slots = 21
+ max_specific_storage = WEIGHT_CLASS_GIGANTIC
+ allow_big_nesting = TRUE
diff --git a/code/datums/storage/subtypes/others/bag_of_holding.dm b/code/datums/storage/subtypes/others/bag_of_holding.dm
index 4028d4f789c..8a4ae79c293 100644
--- a/code/datums/storage/subtypes/others/bag_of_holding.dm
+++ b/code/datums/storage/subtypes/others/bag_of_holding.dm
@@ -5,30 +5,38 @@
allow_big_nesting = TRUE
/datum/storage/bag_of_holding/attempt_insert(obj/item/to_insert, mob/user, override, force, messages)
- var/list/obj/item/storage/backpack/holding/matching = typecache_filter_list(to_insert.get_all_contents(), typecacheof(/obj/item/storage/backpack/holding))
+ var/list/obj/item/storage/backpack/holding/matching = typecache_filter_list(to_insert.get_all_contents(), typecacheof(/obj/item/storage/backpack/holding, /obj/item/mod/control, /obj/item/mod/module/storage))
matching -= parent
matching -= real_location
+ for(var/obj/item/mod/modsuit_or_module in matching)
+ var/datum/storage/storage = modsuit_or_module.atom_storage
+ if(istype(storage, /datum/storage/bag_of_holding) && storage.real_location == storage.parent)
+ continue
+ matching -= modsuit_or_module
- if((istype(to_insert, /obj/item/storage/backpack/holding) || length(matching)) && can_insert(to_insert, user))
+ if((istype(to_insert.atom_storage, /datum/storage/bag_of_holding) || length(matching)) && can_insert(to_insert, user))
INVOKE_ASYNC(src, PROC_REF(recursive_insertion), to_insert, user)
return FALSE
return ..()
/datum/storage/bag_of_holding/proc/recursive_insertion(obj/item/to_insert, mob/living/user)
+ if(confirm_recursive_insertion(to_insert, user))
+ create_rift(to_insert, user)
+
+/datum/storage/bag_of_holding/proc/confirm_recursive_insertion(obj/item/to_insert, mob/living/user)
var/area/bag_area = get_area(user)
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(parent) \
- || QDELETED(real_location) \
- || QDELETED(user) \
- || !user.can_perform_action(parent, NEED_DEXTERITY) \
- || !can_insert(to_insert, user) \
- || (bag_area.area_flags & NO_BOH) \
- )
- return
+ return safety == "Proceed" \
+ && !QDELETED(to_insert) \
+ && !QDELETED(parent) \
+ && !QDELETED(real_location) \
+ && !QDELETED(user) \
+ && user.can_perform_action(parent, NEED_DEXTERITY) \
+ && can_insert(to_insert, user) \
+ && !(bag_area.area_flags & NO_BOH)
+/datum/storage/bag_of_holding/proc/create_rift(obj/item/inserted, mob/living/user)
var/turf/rift_loc = get_turf(parent)
user.visible_message(
span_userdanger("The Bluespace interfaces of the two devices catastrophically malfunction!"),
@@ -42,5 +50,5 @@
user.gib()
var/obj/reality_tear/tear = new(rift_loc)
tear.start_disaster()
- qdel(to_insert)
+ qdel(inserted)
qdel(parent)
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
index fcc27c3e2e6..2e7eb25baf8 100644
--- a/code/modules/mod/mod_control.dm
+++ b/code/modules/mod/mod_control.dm
@@ -572,6 +572,13 @@
balloon_alert(user, "can't install!")
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
+ if(SEND_SIGNAL(src, COMSIG_MOD_TRY_INSTALL_MODULE, new_module, user) & MOD_ABORT_INSTALL)
+ return
+ if(SEND_SIGNAL(new_module, COMSIG_MODULE_TRY_INSTALL, src, user) & MOD_ABORT_INSTALL)
+ return
+ finish_install(new_module, user)
+
+/obj/item/mod/control/proc/finish_install(obj/item/mod/module/new_module, mob/user)
new_module.forceMove(src)
modules += new_module
complexity += new_module.complexity
diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm
index c2a43b6dd95..432a2676653 100644
--- a/code/modules/mod/modules/_module.dm
+++ b/code/modules/mod/modules/_module.dm
@@ -35,6 +35,10 @@
var/overlay_state_use
/// Icon file for the overlay.
var/overlay_icon_file = 'icons/mob/clothing/modsuit/mod_modules.dmi'
+ /// Overlay given to the item icon of the control unit
+ var/overlay_state_item
+ /// Icon file for the item overlay
+ var/item_overlay_icon_file = 'icons/obj/clothing/modsuit/mod_modules.dmi'
/// Does the overlay use the control unit's colors?
var/use_mod_colors = FALSE
/// What modules are we incompatible with?
@@ -459,13 +463,10 @@
deactivate()
return COMSIG_KB_ACTIVATED
-///Anomaly Locked - Causes the module to not function without an anomaly.
+///Anomaly Locked - Mostly just a wrapper for modules that don't need to descend from any other module but need the anomaly_locked_module component
/obj/item/mod/module/anomaly_locked
name = "MOD anomaly locked module"
desc = "A form of a module, locked behind an anomalous core to function."
- incompatible_modules = list()
- /// The core item the module runs off.
- var/obj/item/assembly/signaler/anomaly/core
/// Accepted types of anomaly cores.
var/list/accepted_anomalies = list(/obj/item/assembly/signaler/anomaly)
/// If this one starts with a core in.
@@ -475,80 +476,4 @@
/obj/item/mod/module/anomaly_locked/Initialize(mapload)
. = ..()
- if(!prebuilt || !length(accepted_anomalies))
- return
- var/core_path = pick(accepted_anomalies)
- core = new core_path(src)
- update_icon_state()
-
-/obj/item/mod/module/anomaly_locked/Destroy()
- QDEL_NULL(core)
- return ..()
-
-/obj/item/mod/module/anomaly_locked/examine(mob/user)
- . = ..()
- if(!length(accepted_anomalies))
- return
- if(core)
- . += span_notice("There is a [core.name] installed in it. [core_removable ? "You could remove it with a screwdriver..." : "Unfortunately, due to a design quirk, it's unremovable."]")
- else
- var/list/core_list = list()
- for(var/path in accepted_anomalies)
- var/atom/core_path = path
- core_list += initial(core_path.name)
- . += span_notice("You need to insert \a [english_list(core_list, and_text = " or ")] for this module to function.")
- if(!core_removable)
- . += span_notice("Due to some design quirk, once a core is inserted, it won't be removable.")
-
-/obj/item/mod/module/anomaly_locked/on_select()
- if(!core)
- balloon_alert(mod.wearer, "no core!")
- return
- return ..()
-
-/obj/item/mod/module/anomaly_locked/on_process(seconds_per_tick)
- . = ..()
- if(!core)
- return FALSE
-
-/obj/item/mod/module/anomaly_locked/on_active_process(seconds_per_tick)
- if(!core)
- return FALSE
- return TRUE
-
-/obj/item/mod/module/anomaly_locked/attackby(obj/item/item, mob/living/user, list/modifiers, list/attack_modifiers)
- if(item.type in accepted_anomalies)
- if(core)
- balloon_alert(user, "core already in!")
- return
- if(!user.transferItemToLoc(item, src))
- return
- core = item
- balloon_alert(user, "core installed")
- playsound(src, 'sound/machines/click.ogg', 30, TRUE)
- update_icon_state()
- else
- return ..()
-
-/obj/item/mod/module/anomaly_locked/screwdriver_act(mob/living/user, obj/item/tool)
- . = ..()
- if(!core)
- balloon_alert(user, "no core!")
- return
- if(!core_removable)
- balloon_alert(user, "already has core!")
- return
- balloon_alert(user, "removing core...")
- if(!do_after(user, 3 SECONDS, target = src))
- balloon_alert(user, "interrupted!")
- return
- balloon_alert(user, "core removed")
- core.forceMove(drop_location())
- if(Adjacent(user) && !issilicon(user))
- user.put_in_hands(core)
- core = null
- update_icon_state()
-
-/obj/item/mod/module/anomaly_locked/update_icon_state()
- icon_state = initial(icon_state) + (core ? "-core" : "")
- return ..()
+ AddComponent(/datum/component/anomaly_locked_module, accepted_anomalies, prebuilt, core_removable)
diff --git a/code/modules/mod/modules/module_holding.dm b/code/modules/mod/modules/module_holding.dm
new file mode 100644
index 00000000000..fc235663f1f
--- /dev/null
+++ b/code/modules/mod/modules/module_holding.dm
@@ -0,0 +1,100 @@
+#define HOLDING_MODULE_PREVENT_DUPLICATE_CHECK 1
+#define HOLDING_MODULE_CHECK_CONFIRMED 2
+
+/obj/item/mod/module/storage/holding
+ name = "MOD storage module of holding"
+ desc = "A prototype storage module utilizing the power of anomalous bluespace phenomena \
+ to store copious amounts of matter. Unfortunately, it suffers from the same drawbacks as its standalone counterpart, \
+ including tearing catastrophic rifts in reality when nested inside bluespace pockets produced through similar means."
+ icon_state = "storage_holding"
+ complexity = 4
+ storage_type = null // core-less modules should be safe to insert into bags of holding
+ var/prebuilt = FALSE
+ var/core_removable = TRUE
+ var/datum/component/anomaly_locked_module/anomalock
+ var/list/possible_bag_bombs = list()
+
+/obj/item/mod/module/storage/holding/Initialize(mapload)
+ . = ..()
+ anomalock = AddComponent(/datum/component/anomaly_locked_module,\
+ list(/obj/item/assembly/signaler/anomaly/bluespace),\
+ prebuilt,\
+ core_removable,\
+ PROC_REF(pre_core_inserted),\
+ PROC_REF(on_core_inserted),\
+ PROC_REF(on_core_removed),\
+ )
+ RegisterSignal(src, COMSIG_MODULE_TRY_INSTALL, PROC_REF(try_install))
+
+/obj/item/mod/module/storage/holding/Destroy()
+ possible_bag_bombs.Cut()
+ return ..()
+
+/obj/item/mod/module/storage/holding/proc/pre_core_inserted(mob/user, obj/item/core, list/modifiers)
+ if(possible_bag_bombs[core] == HOLDING_MODULE_PREVENT_DUPLICATE_CHECK)
+ return ITEM_INTERACT_FAILURE
+ var/datum/storage/bag_of_holding/other_bag
+ for(var/atom/nested_loc in get_nested_locs(src))
+ if(istype(nested_loc.atom_storage, /datum/storage/bag_of_holding))
+ other_bag = nested_loc.atom_storage
+ break
+ if(other_bag && !possible_bag_bombs[core])
+ INVOKE_ASYNC(src, PROC_REF(recursive_core_insertion), other_bag, user, core, modifiers)
+ return ITEM_INTERACT_BLOCKING
+
+/obj/item/mod/module/storage/holding/proc/recursive_core_insertion(datum/storage/bag_of_holding/bag_storage, mob/user, obj/item/core, list/modifiers)
+ possible_bag_bombs[core] = HOLDING_MODULE_PREVENT_DUPLICATE_CHECK
+ if(bag_storage.confirm_recursive_insertion(core, user) && !QDELETED(core) && user.is_holding(core) && IsReachableBy(user))
+ anomalock.insert_core(src, user, core, modifiers)
+ possible_bag_bombs -= core
+
+/obj/item/mod/module/storage/holding/proc/on_core_inserted(obj/item/core, mob/user)
+ for(var/atom/nested_loc in get_nested_locs(src))
+ var/datum/storage/bag_of_holding/boh = nested_loc.atom_storage
+ if(istype(boh))
+ boh.create_rift(core, user)
+ return
+ core.moveToNullspace() // Otherwise, the core would become part of the suit's inventory.
+ storage_type = /datum/storage/bag_of_holding
+ create_storage(storage_type = /datum/storage/bag_of_holding)
+ atom_storage.set_locked(STORAGE_FULLY_LOCKED)
+
+/obj/item/mod/module/storage/holding/proc/on_core_removed()
+ QDEL_NULL(atom_storage)
+ storage_type = null
+
+/obj/item/mod/module/storage/holding/proc/try_install(_source, obj/item/mod/control/suit, mob/user)
+ SIGNAL_HANDLER
+ if(possible_bag_bombs[suit] == HOLDING_MODULE_PREVENT_DUPLICATE_CHECK)
+ return MOD_ABORT_INSTALL
+ if(!anomalock.core)
+ balloon_alert(user, "no core!")
+ playsound(suit, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return MOD_ABORT_INSTALL
+ var/datum/storage/bag_of_holding/other_bag
+ for(var/atom/nested_loc in get_nested_locs(suit))
+ if(istype(nested_loc.atom_storage, /datum/storage/bag_of_holding))
+ other_bag = nested_loc.atom_storage
+ break
+ if(other_bag)
+ if(possible_bag_bombs[suit] == HOLDING_MODULE_CHECK_CONFIRMED)
+ other_bag.create_rift(src, user)
+ else
+ INVOKE_ASYNC(src, PROC_REF(recursive_installation), other_bag, suit, user)
+ return MOD_ABORT_INSTALL
+
+/obj/item/mod/module/storage/holding/proc/recursive_installation(datum/storage/bag_of_holding/bag_storage, obj/item/mod/control/suit, mob/user)
+ possible_bag_bombs[suit] = HOLDING_MODULE_PREVENT_DUPLICATE_CHECK
+ if(bag_storage.confirm_recursive_insertion(src, user) && !QDELETED(suit) && user.is_holding(src) && suit.IsReachableBy(user))
+ possible_bag_bombs[suit] = HOLDING_MODULE_CHECK_CONFIRMED
+ suit.install(src, user)
+ possible_bag_bombs -= suit
+
+/obj/item/mod/module/storage/holding/prebuilt
+ prebuilt = TRUE
+
+/obj/item/mod/module/storage/holding/prebuilt/locked
+ core_removable = FALSE
+
+#undef HOLDING_MODULE_PREVENT_DUPLICATE_CHECK
+#undef HOLDING_MODULE_CHECK_CONFIRMED
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
index 86e7120237e..e45e59c5f2a 100644
--- a/code/modules/mod/modules/modules_general.dm
+++ b/code/modules/mod/modules/modules_general.dm
@@ -9,26 +9,19 @@
complexity = 1
incompatible_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/plate_compression)
required_slots = list(ITEM_SLOT_BACK)
- /// Max weight class of items in the storage.
- var/max_w_class = WEIGHT_CLASS_NORMAL
- /// Max combined weight of all items in the storage.
- var/max_combined_w_class = 15
- /// Max amount of items in the storage.
- var/max_items = 7
- /// Is nesting same-size storage items allowed?
- var/big_nesting = FALSE
+ /// The storage type to create for the module
+ var/datum/storage/storage_type = /datum/storage/mod_storage
/obj/item/mod/module/storage/Initialize(mapload)
. = ..()
- 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.set_locked(STORAGE_FULLY_LOCKED)
+ if(storage_type)
+ create_storage(storage_type = storage_type)
+ atom_storage.set_locked(STORAGE_FULLY_LOCKED)
/obj/item/mod/module/storage/on_install()
. = ..()
- var/datum/storage/modstorage = mod.create_storage(max_specific_storage = max_w_class, max_total_storage = max_combined_w_class, max_slots = max_items)
+ var/datum/storage/modstorage = mod.create_storage(storage_type = storage_type)
modstorage.set_real_location(src)
- modstorage.allow_big_nesting = big_nesting
atom_storage.set_locked(STORAGE_NOT_LOCKED)
var/obj/item/clothing/suit = mod.get_part_from_slot(ITEM_SLOT_OCLOTHING)
if(istype(suit))
@@ -61,18 +54,16 @@
whether smuggling, or simply hauling."
complexity = 3
icon_state = "storage_large"
- max_combined_w_class = 21
- max_items = 14
+ storage_type = /datum/storage/mod_storage/expanded
/obj/item/mod/module/storage/syndicate
name = "MOD syndicate storage module"
desc = "A storage system using nanotechnology developed by Cybersun Industries, these compartments use \
esoteric technology to compress the physical matter of items put inside of them, \
essentially shrinking items for much easier and more portable storage."
- icon_state = "storage_syndi"
complexity = 3
- max_combined_w_class = 30
- max_items = 21
+ icon_state = "storage_syndi"
+ storage_type = /datum/storage/mod_storage/syndicate
/obj/item/mod/module/storage/belt
name = "MOD case storage module"
@@ -81,10 +72,8 @@
If you find this equipped to a standard modular suit, then someone has almost certainly shortchanged you on a proper storage module."
icon_state = "storage_case"
complexity = 0
- max_w_class = WEIGHT_CLASS_SMALL
- max_combined_w_class = 21
- max_items = 7
required_slots = list(ITEM_SLOT_BELT)
+ storage_type = /datum/storage/mod_storage/belt
/obj/item/mod/module/storage/bluespace
name = "MOD bluespace storage module"
@@ -92,10 +81,7 @@
miniaturized bluespace pockets for the ultimate in storage technology; regardless of the weight of objects put inside."
complexity = 3
icon_state = "storage_large"
- max_w_class = WEIGHT_CLASS_GIGANTIC
- max_combined_w_class = 60
- max_items = 21
- big_nesting = TRUE
+ storage_type = /datum/storage/mod_storage/bluespace
///Ion Jetpack - Lets the user fly freely through space using battery charge.
/obj/item/mod/module/jetpack
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index 3d3270622b2..44aaec1a21b 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -2114,6 +2114,17 @@
)
build_path = /obj/item/mod/module/storage/large_capacity
+/datum/design/module/mod_storage_holding
+ name = "Storage Module of Holding"
+ id = "mod_storage_holding"
+ materials = list(
+ /datum/material/gold =SHEET_MATERIAL_AMOUNT * 1.5,
+ /datum/material/diamond =HALF_SHEET_MATERIAL_AMOUNT * 1.5,
+ /datum/material/uranium = SMALL_MATERIAL_AMOUNT*2.5,
+ /datum/material/bluespace =SHEET_MATERIAL_AMOUNT
+ )
+ build_path = /obj/item/mod/module/storage/holding
+
/datum/design/module/mod_visor_medhud
name = "Medical Visor Module"
id = "mod_visor_medhud"
diff --git a/code/modules/research/techweb/nodes/research_nodes.dm b/code/modules/research/techweb/nodes/research_nodes.dm
index bf22457c7ba..41c2a48ecdd 100644
--- a/code/modules/research/techweb/nodes/research_nodes.dm
+++ b/code/modules/research/techweb/nodes/research_nodes.dm
@@ -93,6 +93,7 @@
design_ids = list(
"bag_holding",
"cybernetic_heart_anomalock",
+ "mod_storage_holding",
"wormholeprojector",
"gravitygun",
"polymorph_belt",
diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi
index 0cd210d85da..7ff5dcd95a9 100644
Binary files a/icons/obj/clothing/modsuit/mod_modules.dmi and b/icons/obj/clothing/modsuit/mod_modules.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 75d442d6ed2..b7cd4d5d5bb 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1089,6 +1089,7 @@
#include "code\datums\components\ai_retaliate_advanced.dm"
#include "code\datums\components\alternative_sharpness.dm"
#include "code\datums\components\amputating_limbs.dm"
+#include "code\datums\components\anomalock_module.dm"
#include "code\datums\components\anti_magic.dm"
#include "code\datums\components\appearance_on_aggro.dm"
#include "code\datums\components\aquarium.dm"
@@ -2067,6 +2068,7 @@
#include "code\datums\storage\subtypes\holsters.dm"
#include "code\datums\storage\subtypes\lockboxes.dm"
#include "code\datums\storage\subtypes\medkits.dm"
+#include "code\datums\storage\subtypes\mod_storage.dm"
#include "code\datums\storage\subtypes\pockets.dm"
#include "code\datums\storage\subtypes\toolboxes.dm"
#include "code\datums\storage\subtypes\others\bag_of_holding.dm"
@@ -5632,6 +5634,7 @@
#include "code\modules\mod\mod_types.dm"
#include "code\modules\mod\mod_ui.dm"
#include "code\modules\mod\modules\_module.dm"
+#include "code\modules\mod\modules\module_holding.dm"
#include "code\modules\mod\modules\module_kinesis.dm"
#include "code\modules\mod\modules\module_pathfinder.dm"
#include "code\modules\mod\modules\modules_antag.dm"