diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 42201e992e5..f76e216af78 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -821,3 +821,7 @@
#define COMSIG_XENO_TURF_CLICK_CTRL "xeno_turf_click_alt"
///from monkey CtrlClickOn(): (/mob)
#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl"
+
+// /datum/component/container_item
+/// (atom/container, mob/user) - returns bool
+#define COMSIG_CONTAINER_TRY_ATTACH "container_try_attach"
diff --git a/code/datums/components/container_item/container_item.dm b/code/datums/components/container_item/container_item.dm
new file mode 100644
index 00000000000..fda012cb0ea
--- /dev/null
+++ b/code/datums/components/container_item/container_item.dm
@@ -0,0 +1,10 @@
+/// Container item, an item which can be stored by specialized containers.
+/datum/component/container_item
+
+/datum/component/container_item/Initialize()
+ . = ..()
+ RegisterSignal(parent, COMSIG_CONTAINER_TRY_ATTACH, .proc/try_attach)
+
+/// Called when parent is added to the container.
+/datum/component/container_item/proc/try_attach(datum/source, atom/container, mob/user)
+ return FALSE
diff --git a/code/datums/components/container_item/tank_holder.dm b/code/datums/components/container_item/tank_holder.dm
new file mode 100644
index 00000000000..b68c413519c
--- /dev/null
+++ b/code/datums/components/container_item/tank_holder.dm
@@ -0,0 +1,31 @@
+/// Tank holder item - Added to an object which can be added to a tank holder.
+/datum/component/container_item/tank_holder
+ var/tank_holder_icon_state
+ var/make_density
+
+/datum/component/container_item/tank_holder/Initialize(state, density = TRUE)
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ tank_holder_icon_state = state
+ make_density = density
+ return ..()
+
+/datum/component/container_item/tank_holder/try_attach(datum/source, obj/structure/tank_holder/container, mob/user)
+ if(!container || !istype(container))
+ return FALSE
+ var/obj/item/I = parent
+ if(container.contents.len)
+ if(user)
+ to_chat(user, "There's already something in [container].")
+ return TRUE
+ if(user)
+ if(!user.transferItemToLoc(I, container))
+ return TRUE
+ to_chat(user, "You put [I] into [container].")
+ else
+ I.forceMove(container)
+ container.tank = I
+ container.density = make_density
+ container.icon_state = tank_holder_icon_state
+ return TRUE
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index bd4b3334867..c1c5599a2bc 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -187,9 +187,6 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
var/canMouseDown = FALSE
- /// Item state of tank holder when inside tank holder. null makes it unable to enter a tank holder.
- var/tank_holder_icon_state = null
-
/obj/item/Initialize()
if(attack_verb_continuous)
diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm
index 47aedf15ca7..f4902e9cd90 100644
--- a/code/game/objects/items/extinguisher.dm
+++ b/code/game/objects/items/extinguisher.dm
@@ -5,7 +5,6 @@
icon_state = "fire_extinguisher0"
inhand_icon_state = "fire_extinguisher"
hitsound = 'sound/weapons/smash.ogg'
- tank_holder_icon_state = "holder_extinguisher"
flags_1 = CONDUCT_1
throwforce = 10
w_class = WEIGHT_CLASS_NORMAL
@@ -27,12 +26,13 @@
var/power = 5 //Maximum distance launched water will travel
var/precision = FALSE //By default, turfs picked from a spray are random, set to 1 to make it always have at least one water effect per row
var/cooling_power = 2 //Sets the cooling_temperature of the water reagent datum inside of the extinguisher when it is refilled
+ /// Icon state when inside a tank holder
+ var/tank_holder_icon_state = "holder_extinguisher"
/obj/item/extinguisher/mini
name = "pocket fire extinguisher"
desc = "A light and compact fibreglass-framed model fire extinguisher."
icon_state = "miniFE0"
- tank_holder_icon_state = "holder_extinguisher"
inhand_icon_state = "miniFE"
hitsound = null //it is much lighter, after all.
flags_1 = null //doesn't CONDUCT_1
@@ -54,6 +54,11 @@
. = ..()
refill()
+/obj/item/extinguisher/ComponentInitialize()
+ . = ..()
+ if(tank_holder_icon_state)
+ AddComponent(/datum/component/container_item/tank_holder, tank_holder_icon_state)
+
/obj/item/extinguisher/advanced
name = "advanced fire extinguisher"
desc = "Used to stop thermonuclear fires from spreading inside your engine."
diff --git a/code/game/objects/items/miscellaneous.dm b/code/game/objects/items/miscellaneous.dm
index aed7b07413b..78f18e0a3d7 100644
--- a/code/game/objects/items/miscellaneous.dm
+++ b/code/game/objects/items/miscellaneous.dm
@@ -319,11 +319,14 @@
name = "skub"
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "skub"
- tank_holder_icon_state = "holder_skub"
w_class = WEIGHT_CLASS_BULKY
attack_verb_continuous = list("skubs")
attack_verb_simple = list("skub")
+/obj/item/skub/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/container_item/tank_holder, "holder_skub", FALSE)
+
/obj/item/skub/suicide_act(mob/living/user)
user.visible_message("[user] has declared themself as anti-skub! The skub tears them apart!")
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index b29fbae6489..3aba2a129bf 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -4,7 +4,6 @@
icon_state = "generic"
lefthand_file = 'icons/mob/inhands/equipment/tanks_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tanks_righthand.dmi'
- tank_holder_icon_state = "holder_generic"
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BACK
worn_icon = 'icons/mob/clothing/back.dmi' //since these can also get thrown into suit storage slots. if something goes on the belt, set this to null.
@@ -21,6 +20,8 @@
var/distribute_pressure = ONE_ATMOSPHERE
var/integrity = 3
var/volume = 70
+ /// Icon state when in a tank holder. Null makes it incompatible with tank holder.
+ var/tank_holder_icon_state = "holder_generic"
/obj/item/tank/ui_action_click(mob/user)
toggle_internals(user)
@@ -75,6 +76,11 @@
STOP_PROCESSING(SSobj, src)
. = ..()
+/obj/item/tank/ComponentInitialize()
+ . = ..()
+ if(tank_holder_icon_state)
+ AddComponent(/datum/component/container_item/tank_holder, tank_holder_icon_state)
+
/obj/item/tank/examine(mob/user)
var/obj/icon = src
. = ..()
diff --git a/code/game/objects/structures/tank_holder.dm b/code/game/objects/structures/tank_holder.dm
index d631bd45ec8..b03c17b2f12 100644
--- a/code/game/objects/structures/tank_holder.dm
+++ b/code/game/objects/structures/tank_holder.dm
@@ -1,3 +1,4 @@
+///?
/obj/structure/tank_holder
name = "tank holder"
desc = "A metallic frame that can hold tanks and extinguishers."
@@ -19,10 +20,13 @@
/obj/structure/tank_holder/Initialize()
. = ..()
if(tank)
- after_put_tank(new tank)
+ var/obj/item/tank_ = new tank(null)
+ tank = null
+ SEND_SIGNAL(tank_, COMSIG_CONTAINER_TRY_ATTACH, src, null)
/obj/structure/tank_holder/Destroy()
if(tank)
+ contents.Cut()
QDEL_NULL(tank)
return ..()
@@ -38,18 +42,8 @@
/obj/structure/tank_holder/attackby(obj/item/W, mob/user, params)
if(user.a_intent == INTENT_HARM)
return ..()
- if(contents.len)
- to_chat(user, "There's already something in [src].")
- return
-
- if(!W.tank_holder_icon_state)
+ if(!SEND_SIGNAL(W, COMSIG_CONTAINER_TRY_ATTACH, src, user))
to_chat(user, "[W] does not fit in [src].")
- return
-
- if(!user.transferItemToLoc(W, src))
- return
- to_chat(user, "You put [W] into [src].")
- after_put_tank(W)
/obj/structure/tank_holder/screwdriver_act(mob/living/user, obj/item/I)
if(..())
@@ -64,7 +58,7 @@
new /obj/item/stack/rods(get_turf(src), 2)
if(tank)
tank.forceMove(get_turf(src))
- tank = null
+ after_detach_tank()
qdel(src)
/obj/structure/tank_holder/attack_paw(mob/user)
@@ -77,31 +71,25 @@
return ..()
to_chat(user, "You take [tank] from [src].")
add_fingerprint(user)
+ tank.add_fingerprint(user)
user.put_in_hands(tank)
- after_remove_tank()
+ after_detach_tank()
/obj/structure/tank_holder/handle_atom_del(atom/A)
if(A == tank)
- after_remove_tank()
+ after_detach_tank()
return ..()
/obj/structure/tank_holder/contents_explosion(severity, target)
if(tank)
tank.ex_act(severity, target)
-/// Call this after inserting the tank into contents in order to update references, icon
-/// and density.
-/obj/structure/tank_holder/proc/after_put_tank(obj/item/tank_)
- tank = tank_
- icon_state = tank.tank_holder_icon_state
- density = TRUE
-
/// Call this after taking the tank from contents in order to update references, icon
/// and density.
-/obj/structure/tank_holder/proc/after_remove_tank()
+/obj/structure/tank_holder/proc/after_detach_tank()
tank = null
- icon_state = "holder"
density = FALSE
+ icon_state = "holder"
/obj/structure/tank_holder/oxygen
icon_state = "holder_oxygen"
diff --git a/tgstation.dme b/tgstation.dme
index 218214fbb31..84c1758f9af 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -483,6 +483,8 @@
#include "code\datums\components\uplink.dm"
#include "code\datums\components\wearertargeting.dm"
#include "code\datums\components\wet_floor.dm"
+#include "code\datums\components\container_item\container_item.dm"
+#include "code\datums\components\container_item\tank_holder.dm"
#include "code\datums\components\crafting\crafting.dm"
#include "code\datums\components\crafting\guncrafting.dm"
#include "code\datums\components\crafting\recipes.dm"