diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index 74512ccd272..4397361deb2 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -854,7 +854,10 @@ GLOBAL_LIST_EMPTY(multiverse)
desc = "An enchanted mug which can be filled with any of various liquids on command."
icon_state = "evermug"
-/obj/item/reagent_containers/drinks/everfull/attack_self__legacy__attackchain(mob/user)
+/obj/item/reagent_containers/drinks/everfull/activate_self(mob/user)
+ if(..())
+ return
+
var/static/list/options = list("Omnizine" = image(icon = 'icons/obj/storage.dmi', icon_state = "firstaid"),
"Ale" = image(icon = 'icons/obj/drinks.dmi', icon_state = "alebottle"),
"Wine" = image(icon = 'icons/obj/drinks.dmi', icon_state = "wineglass"),
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index 3e1dc638f37..650330cf16a 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -40,7 +40,9 @@
. += filling
/obj/machinery/iv_drip/MouseDrop(mob/living/target)
- var/mob/user = usr
+ drag_drop_onto(target, usr)
+
+/obj/machinery/iv_drip/proc/drag_drop_onto(mob/living/target, mob/user)
if(HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
return
if(!ishuman(user))
@@ -50,7 +52,7 @@
if(!bag)
to_chat(user, "There's no IV bag connected to [src]!")
return
- bag.afterattack__legacy__attackchain(target, usr, TRUE)
+ bag.mob_act(target, user)
START_PROCESSING(SSmachines, src)
/obj/machinery/iv_drip/attack_hand(mob/user)
@@ -75,8 +77,8 @@
START_PROCESSING(SSmachines, src)
return ITEM_INTERACT_COMPLETE
else if(bag && istype(used, /obj/item/reagent_containers))
- bag.attackby__legacy__attackchain(used)
- used.afterattack__legacy__attackchain(bag, usr, TRUE)
+ var/obj/item/reagent_containers/container = used
+ container.normal_act(bag, user)
update_icon(UPDATE_OVERLAYS)
return ITEM_INTERACT_COMPLETE
diff --git a/code/game/objects/items/weapons/alien_specific.dm b/code/game/objects/items/weapons/alien_specific.dm
index a5780bc2f34..870f0f1ece4 100644
--- a/code/game/objects/items/weapons/alien_specific.dm
+++ b/code/game/objects/items/weapons/alien_specific.dm
@@ -31,7 +31,8 @@
icon_state = "borg-spray-smoke"
list_reagents = list("water" = 50)
-/obj/item/reagent_containers/spray/alien/smoke/afterattack__legacy__attackchain(atom/A as mob|obj, mob/user as mob)
+/obj/item/reagent_containers/spray/alient/normal_act(atom/A, mob/living/user)
+ . = TRUE
if(istype(A, /obj/structure/reagent_dispensers) && get_dist(src,A) <= 1)
if(!A.reagents.total_volume && A.reagents)
to_chat(user, "\The [A] is empty.")
diff --git a/code/game/objects/items/weapons/paint_bucket.dm b/code/game/objects/items/weapons/paint_bucket.dm
index 4dc67b6b783..a41a5f472c4 100644
--- a/code/game/objects/items/weapons/paint_bucket.dm
+++ b/code/game/objects/items/weapons/paint_bucket.dm
@@ -16,16 +16,15 @@
container_type = OPENCONTAINER
blocks_emissive = EMISSIVE_BLOCK_GENERIC
-/obj/item/reagent_containers/glass/paint/afterattack__legacy__attackchain(turf/simulated/target, mob/user, proximity)
- if(!proximity)
- return
+/obj/item/reagent_containers/glass/paint/interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(!is_open_container())
- return
+ return ITEM_INTERACT_COMPLETE
if(istype(target) && reagents.total_volume >= 5)
user.visible_message("[target] has been splashed with something by [user]!")
spawn(5)
reagents.reaction(target, REAGENT_TOUCH)
reagents.remove_any(5)
+ return ITEM_INTERACT_COMPLETE
else
return ..()
diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm
index c6d394ba222..2fef108b22d 100644
--- a/code/game/objects/items/weapons/storage/firstaid.dm
+++ b/code/game/objects/items/weapons/storage/firstaid.dm
@@ -308,7 +308,7 @@
return
applying_meds = TRUE
for(var/obj/item/reagent_containers/P in contents)
- if(P.attack__legacy__attackchain(M, user))
+ if(P.mob_act(M, user))
applying_meds = FALSE
else
applying_meds = FALSE
@@ -357,7 +357,7 @@
C.visible_message("[C] [rapid_intake_message]")
if(do_mob(C, C, 100)) // 10 seconds
for(var/obj/item/reagent_containers/pill/P in contents)
- P.attack__legacy__attackchain(C, C)
+ P.interact_with_atom(C, C)
C.visible_message("[C] [rapid_post_instake_message]")
return
diff --git a/code/game/objects/items/weapons/tanks/watertank.dm b/code/game/objects/items/weapons/tanks/watertank.dm
index d8289c434da..27583c49901 100644
--- a/code/game/objects/items/weapons/tanks/watertank.dm
+++ b/code/game/objects/items/weapons/tanks/watertank.dm
@@ -125,6 +125,7 @@
return INITIALIZE_HINT_QDEL
tank = loc
reagents = tank.reagents //This mister is really just a proxy for the tank's reagents
+ RegisterSignal(src, COMSIG_ACTIVATE_SELF, TYPE_PROC_REF(/datum, signal_cancel_activate_self))
return ..()
/obj/item/reagent_containers/spray/mister/Destroy()
@@ -138,9 +139,6 @@
tank.on = FALSE
loc = tank
-/obj/item/reagent_containers/spray/mister/attack_self__legacy__attackchain()
- return
-
/proc/check_tank_exists(parent_tank, mob/living/carbon/human/M, obj/O)
if(!parent_tank || (!istype(parent_tank, /obj/item/watertank) && !istype(parent_tank, /obj/item/mod/module/firefighting_tank))) //To avoid weird issues from admin spawns
return FALSE
@@ -152,10 +150,11 @@
if(loc != tank.loc)
loc = tank.loc
-/obj/item/reagent_containers/spray/mister/afterattack__legacy__attackchain(obj/target, mob/user, proximity)
- if(target.loc == loc || target == tank) //Safety check so you don't fill your mister with mutagen or something and then blast yourself in the face with it putting it away
- return
- ..()
+/obj/item/reagent_containers/spray/mister/normal_act(atom/target, mob/living/user)
+ if(target.loc == loc || target == tank)
+ return FALSE
+
+ return ..()
//Janitor tank
/obj/item/watertank/janitor
@@ -182,7 +181,10 @@
/obj/item/watertank/janitor/make_noz()
return new /obj/item/reagent_containers/spray/mister/janitor(src)
-/obj/item/reagent_containers/spray/mister/janitor/attack_self__legacy__attackchain(mob/user)
+/obj/item/reagent_containers/spray/mister/janitor/activate_self(mob/user)
+ if(..())
+ return FINISH_ATTACK
+
amount_per_transfer_from_this = (amount_per_transfer_from_this == 5 ? 10 : 5)
spray_currentrange = (spray_currentrange == 2 ? spray_maxrange : 2)
to_chat(user, "You [amount_per_transfer_from_this == 5 ? "remove" : "fix"] the nozzle. You'll now use [amount_per_transfer_from_this] units per spray.")
diff --git a/code/modules/detective_work/footprints_and_rag.dm b/code/modules/detective_work/footprints_and_rag.dm
index 66afdc83796..0e3c97665c7 100644
--- a/code/modules/detective_work/footprints_and_rag.dm
+++ b/code/modules/detective_work/footprints_and_rag.dm
@@ -13,19 +13,18 @@
blocks_emissive = EMISSIVE_BLOCK_GENERIC
var/wipespeed = 30
-/obj/item/reagent_containers/glass/rag/attack__legacy__attackchain(atom/target as obj|turf|area, mob/user as mob , flag)
- if(ismob(target) && target.reagents && reagents.total_volume)
+/obj/item/reagent_containers/glass/rag/mob_act(mob/target, mob/living/user)
+ if(target.reagents && reagents.total_volume)
user.visible_message("[user] has smothered [target] with [src]!", "You smother [target] with [src]!", "You hear some struggling and muffled cries of surprise")
src.reagents.reaction(target, REAGENT_TOUCH)
src.reagents.clear_reagents()
- return
+ return TRUE
else
- ..()
+ return ..()
-/obj/item/reagent_containers/glass/rag/afterattack__legacy__attackchain(atom/target, mob/user, proximity)
- if(!proximity || ishuman(target)) //Human check so we don't clean the person we're trying to ether
- return
+/obj/item/reagent_containers/glass/rag/normal_act(atom/target, mob/living/user)
target.cleaning_act(user, src, wipespeed)
+ return TRUE
/obj/item/reagent_containers/glass/rag/can_clean()
return TRUE
diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
index 97e8a9479c2..f46025035ca 100644
--- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
@@ -42,17 +42,26 @@
qdel(src)
-/obj/item/reagent_containers/drinks/bottle/attack__legacy__attackchain(mob/living/target, mob/living/user)
-
- if(!target)
- return
-
- if(user.a_intent != INTENT_HARM || !is_glass)
+/obj/item/reagent_containers/drinks/bottle/interact_with_atom(atom/target, mob/living/user, list/modifiers)
+ if(user.a_intent != INTENT_HARM)
return ..()
- if(HAS_TRAIT(user, TRAIT_PACIFISM))
- to_chat(user, "You don't want to harm [target]!")
- return
+/obj/item/reagent_containers/drinks/bottle/pre_attack(atom/A, mob/living/user, params)
+ if(..())
+ return FINISH_ATTACK
+
+ if(isliving(A))
+ if(!is_glass)
+ mob_act(A, user)
+ return FINISH_ATTACK
+
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to harm [A]!")
+ return FINISH_ATTACK
+
+/obj/item/reagent_containers/drinks/bottle/attack(mob/living/target, mob/living/user, params)
+ if(..())
+ return FINISH_ATTACK
force = 15 //Smashing bottles over someoen's head hurts.
@@ -392,8 +401,8 @@
hotspot.temperature = 1000
hotspot.recolor()
-/obj/item/reagent_containers/drinks/bottle/molotov/attackby__legacy__attackchain(obj/item/I, mob/user, params)
- if(I.get_heat() && !active)
+/obj/item/reagent_containers/drinks/bottle/molotov/item_interaction(mob/living/user, obj/item/used, list/modifiers)
+ if(used.get_heat() && !active)
active = TRUE
var/turf/bombturf = get_turf(src)
var/area/bombarea = get_area(bombturf)
@@ -415,8 +424,12 @@
SplashReagents(A)
A.fire_act()
qdel(src)
+ return ITEM_INTERACT_COMPLETE
+
+/obj/item/reagent_containers/drinks/bottle/molotov/activate_self(mob/user)
+ if(..())
+ return
-/obj/item/reagent_containers/drinks/bottle/molotov/attack_self__legacy__attackchain(mob/user)
if(active)
if(!is_glass)
to_chat(user, "The flame's spread too far on it!")
diff --git a/code/modules/food_and_drinks/drinks/drinks/cans.dm b/code/modules/food_and_drinks/drinks/drinks/cans.dm
index 0970938947c..e84fecd9a31 100644
--- a/code/modules/food_and_drinks/drinks/drinks/cans.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/cans.dm
@@ -15,17 +15,17 @@
else
. += "Ctrl-click to shake it up!"
-/obj/item/reagent_containers/drinks/cans/attack_self__legacy__attackchain(mob/user)
- if(can_opened)
- return ..()
+/obj/item/reagent_containers/drinks/cans/activate_self(mob/user)
+ if(..() || can_opened)
+ return
+
if(times_shaken)
fizzy_open(user)
- return ..()
+ return
playsound(loc, 'sound/effects/canopen.ogg', rand(10, 50), 1)
can_opened = TRUE
container_type |= OPENCONTAINER
to_chat(user, "You open the drink with an audible pop!")
- return ..()
/obj/item/reagent_containers/drinks/cans/proc/crush(mob/user)
var/obj/item/trash/can/crushed_can = new /obj/item/trash/can(user.loc)
@@ -67,35 +67,19 @@
else
return ..()
-/obj/item/reagent_containers/drinks/cans/attack__legacy__attackchain(mob/M, mob/user, proximity)
- if(!can_opened)
- to_chat(user, "You need to open the drink!")
- return
- else if(M == user && !reagents.total_volume && user.a_intent == INTENT_HARM && user.zone_selected == "head")
+/obj/item/reagent_containers/drinks/cans/interact_with_atom(atom/target, mob/living/user, list/modifiers)
+ if(target == user && !reagents.total_volume && user.a_intent == INTENT_HARM && user.zone_selected == "head")
user.visible_message("[user] crushes [src] on [user.p_their()] forehead!", "You crush [src] on your forehead.")
crush(user)
- return
+ return ITEM_INTERACT_COMPLETE
return ..()
-/obj/item/reagent_containers/drinks/cans/attackby__legacy__attackchain(obj/item/I, mob/user, params)
- if(istype(I, /obj/item/storage/bag/trash/cyborg))
+/obj/item/reagent_containers/drinks/cans/item_interaction(mob/living/user, obj/item/used, list/modifiers) // This doesn't belong here.
+ if(istype(used, /obj/item/storage/bag/trash/cyborg))
user.visible_message("[user] crushes [src] in [user.p_their()] trash compactor.", "You crush [src] in your trash compactor.")
- var/obj/can = crush(user)
- can.attackby__legacy__attackchain(I, user, params)
- return TRUE
- ..()
-
-/obj/item/reagent_containers/drinks/cans/afterattack__legacy__attackchain(obj/target, mob/user, proximity)
- if(!proximity)
- return
- if(istype(target, /obj/structure/reagent_dispensers) && !can_opened)
- to_chat(user, "You need to open the drink!")
- return
- else if(target.is_open_container() && !can_opened)
- to_chat(user, "You need to open the drink!")
- return
- else
- return ..(target, user, proximity)
+ // Automatic crushed can pickup seems to be broken until storage is migrated.
+ crush(user)
+ return ITEM_INTERACT_COMPLETE
/obj/item/reagent_containers/drinks/cans/throw_impact(atom/A)
. = ..()
diff --git a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
index c342d71e80b..c882598b9d1 100644
--- a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
@@ -15,19 +15,17 @@
drop_sound = 'sound/items/handling/drinkglass_drop.ogg'
pickup_sound = 'sound/items/handling/drinkglass_pickup.ogg'
-/obj/item/reagent_containers/drinks/drinkingglass/attackby__legacy__attackchain(obj/item/I, mob/user, params)
- if(istype(I, /obj/item/food/egg)) //breaking eggs
- var/obj/item/food/egg/E = I
+/obj/item/reagent_containers/drinks/drinkingglass/item_interaction(mob/living/user, obj/item/used, list/modifiers)
+ if(istype(used, /obj/item/food/egg)) //breaking eggs
+ var/obj/item/food/egg/egg = used
if(reagents)
if(reagents.total_volume >= reagents.maximum_volume)
to_chat(user, "[src] is full.")
else
- to_chat(user, "You break [E] in [src].")
- E.reagents.trans_to(src, E.reagents.total_volume)
- qdel(E)
- return
- else
- ..()
+ to_chat(user, "You break [egg] in [src].")
+ egg.reagents.trans_to(src, egg.reagents.total_volume)
+ qdel(egg)
+ return ITEM_INTERACT_COMPLETE
/obj/item/reagent_containers/drinks/drinkingglass/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE)
if(!reagents.total_volume)
diff --git a/code/modules/food_and_drinks/drinks/drinks/shotglass.dm b/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
index 47e131a6552..2264eac6058 100644
--- a/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
@@ -90,24 +90,25 @@
/obj/item/reagent_containers/drinks/drinkingglass/shotglass/burn() //Let's override fire deleting the reagents inside the shot
return
-/obj/item/reagent_containers/drinks/drinkingglass/shotglass/attack__legacy__attackchain(mob/living/carbon/human/user)
+/obj/item/reagent_containers/drinks/drinkingglass/shotglass/interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50) && (resistance_flags & ON_FIRE))
clumsilyDrink(user)
- else
- ..()
+ return ITEM_INTERACT_COMPLETE
+ return ..()
-/obj/item/reagent_containers/drinks/drinkingglass/shotglass/attackby__legacy__attackchain(obj/item/W)
- ..()
- if(W.get_heat())
+/obj/item/reagent_containers/drinks/drinkingglass/shotglass/item_interaction(mob/living/user, obj/item/used, list/modifiers)
+ if(used.get_heat())
fire_act()
+ return ITEM_INTERACT_COMPLETE
+ return ..()
/obj/item/reagent_containers/drinks/drinkingglass/shotglass/attack_hand(mob/user, pickupfireoverride = TRUE)
..()
-/obj/item/reagent_containers/drinks/drinkingglass/shotglass/attack_self__legacy__attackchain(mob/living/carbon/human/user)
- ..()
- if(!(resistance_flags & ON_FIRE))
+/obj/item/reagent_containers/drinks/drinkingglass/shotglass/activate_self(mob/user)
+ if(..() || !(resistance_flags & ON_FIRE))
return
+
if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
clumsilyDrink(user)
else
diff --git a/code/modules/food_and_drinks/drinks/drinks_base.dm b/code/modules/food_and_drinks/drinks/drinks_base.dm
index 161865a7fcd..40e22f83f97 100644
--- a/code/modules/food_and_drinks/drinks/drinks_base.dm
+++ b/code/modules/food_and_drinks/drinks/drinks_base.dm
@@ -14,23 +14,19 @@
var/consume_sound = 'sound/items/drink.ogg'
var/chugging = FALSE
-/obj/item/reagent_containers/drinks/attack_self__legacy__attackchain(mob/user)
- return
-
-/obj/item/reagent_containers/drinks/attack__legacy__attackchain(mob/M, mob/user, def_zone)
+/obj/item/reagent_containers/drinks/mob_act(mob/target, mob/living/user)
+ . = TRUE
if(!reagents || !reagents.total_volume)
to_chat(user, "None of [src] left, oh no!")
- return FALSE
+ return
if(!is_drainable())
to_chat(user, "You need to open [src] first!")
- return FALSE
+ return
- if(iscarbon(M))
- var/mob/living/carbon/C = M
- if(C.drink(src, user))
- return TRUE
- return FALSE
+ if(iscarbon(target))
+ var/mob/living/carbon/C = target
+ C.drink(src, user)
/obj/item/reagent_containers/drinks/MouseDrop(atom/over_object) //CHUG! CHUG! CHUG!
if(!iscarbon(over_object))
@@ -54,41 +50,39 @@
break
chugging = FALSE
-/obj/item/reagent_containers/drinks/afterattack__legacy__attackchain(obj/target, mob/user, proximity)
- if(!proximity)
- return
+/obj/item/reagent_containers/drinks/normal_act(obj/target, mob/living/user) // The 2 if checks are forced true to preserve tapping behaviour.
if(chugging)
- return
+ return TRUE
if(target.is_refillable() && is_drainable()) //Something like a glass. Player probably wants to transfer TO it.
+ . = TRUE
if(!reagents.total_volume)
to_chat(user, "[src] is empty.")
- return FALSE
+ return
if(target.reagents.holder_full())
to_chat(user, "[target] is full.")
- return FALSE
+ return
var/trans = reagents.trans_to(target, amount_per_transfer_from_this)
to_chat(user, "You transfer [trans] units of the solution to [target].")
else if(target.is_drainable()) //A dispenser. Transfer FROM it TO us.
+ . = TRUE
if(!is_refillable())
to_chat(user, "[src]'s tab isn't open!")
- return FALSE
+ return
if(!target.reagents.total_volume)
to_chat(user, "[target] is empty.")
- return FALSE
+ return
if(reagents.holder_full())
to_chat(user, "[src] is full.")
- return FALSE
+ return
var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this)
to_chat(user, "You fill [src] with [trans] units of the contents of [target].")
- return FALSE
-
/obj/item/reagent_containers/drinks/examine(mob/user)
. = ..()
if(in_range(user, src))
@@ -277,7 +271,10 @@
. = ..()
reagents.set_reacting(FALSE)
-/obj/item/reagent_containers/drinks/shaker/attack_self__legacy__attackchain(mob/user)
+/obj/item/reagent_containers/drinks/shaker/activate_self(mob/user)
+ if(..())
+ return
+
if(!reagents.total_volume)
to_chat(user, "You won't shake an empty shaker now, will you?")
return
diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm
index b0371fbacd5..cf77fdd2735 100644
--- a/code/modules/food_and_drinks/food/condiment.dm
+++ b/code/modules/food_and_drinks/food/condiment.dm
@@ -39,54 +39,44 @@
"rice" = list("rice", "rice sack", "A big bag of rice. Good for cooking!"))
var/originalname = "condiment" //Can't use initial(name) for this. This stores the name set by condimasters.
-/obj/item/reagent_containers/condiment/attack_self__legacy__attackchain(mob/user)
- return
-
-/obj/item/reagent_containers/condiment/attack__legacy__attackchain(mob/M, mob/user, def_zone)
-
+/obj/item/reagent_containers/condiment/mob_act(mob/target, mob/living/user)
if(!reagents || !reagents.total_volume)
to_chat(user, "None of [src] left, oh no!")
- return FALSE
-
- if(!iscarbon(M)) // Non-carbons can't process reagents
- to_chat(user, "You cannot find a way to feed [M].")
+ return
+ if(!iscarbon(target)) // Non-carbons can't process reagents
+ to_chat(user, "You cannot find a way to feed [target].")
return
- if(M == user)
+ if(target == user)
to_chat(user, "You swallow some of the contents of [src].")
else
- user.visible_message("[user] attempts to feed [M] from [src].")
- if(!do_mob(user, M))
+ user.visible_message("[user] attempts to feed [target] from [src].")
+ if(!do_mob(user, target))
return
if(!reagents || !reagents.total_volume)
return // The condiment might be empty after the delay.
- user.visible_message("[user] feeds [M] from [src].")
- add_attack_logs(user, M, "Fed [src] containing [reagents.log_list()]", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null)
-
+ user.visible_message("[user] feeds [target] from [src].")
+ add_attack_logs(user, target, "Fed [src] containing [reagents.log_list()]", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null)
var/fraction = min(10/reagents.total_volume, 1)
- reagents.reaction(M, REAGENT_INGEST, fraction)
- reagents.trans_to(M, 10)
- playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1)
- return 1
+ reagents.reaction(target, REAGENT_INGEST, fraction)
+ reagents.trans_to(target, 10)
+ playsound(target.loc,'sound/items/drink.ogg', rand(10,50), TRUE)
-/obj/item/reagent_containers/condiment/afterattack__legacy__attackchain(obj/target, mob/user , proximity)
- if(!proximity)
- return
+/obj/item/reagent_containers/condiment/normal_act(atom/target, mob/living/user) // Proc is true if any of the if checks go through. Preserves tapping behaviour on certain machinery.
if(istype(target, /obj/structure/reagent_dispensers)) //A dispenser. Transfer FROM it TO us.
-
+ . = TRUE
if(!target.reagents.total_volume)
to_chat(user, "[target] is empty!")
return
-
if(reagents.total_volume >= reagents.maximum_volume)
to_chat(user, "[src] is full!")
return
-
var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this)
to_chat(user, "You fill [src] with [trans] units of the contents of [target].")
//Something like a glass or a food item. Player probably wants to transfer TO it.
else if(target.is_refillable() || istype(target, /obj/item/food))
+ . = TRUE
if(!reagents.total_volume)
to_chat(user, "[src] is empty!")
return
@@ -319,26 +309,25 @@
"sugar" = list("condi_sugar", "Sugar", "Tasty spacey sugar!"),
"vinegar" =list("condi_mixed", "vinegar", "Perfect for chips, if you're feeling Space British."))
-/obj/item/reagent_containers/condiment/pack/attack__legacy__attackchain(mob/M, mob/user, def_zone) //Can't feed these to people directly.
- return
-
-/obj/item/reagent_containers/condiment/pack/afterattack__legacy__attackchain(obj/target, mob/user , proximity)
- if(!proximity) return
-
+/obj/item/reagent_containers/condiment/pack/interact_with_atom(atom/target, mob/living/user, list/modifiers)
//You can tear the bag open above food to put the condiments on it, obviously.
if(istype(target, /obj/item/food))
if(!reagents.total_volume)
to_chat(user, "You tear open [src], but there's nothing in it.")
qdel(src)
- return
+ return ITEM_INTERACT_COMPLETE
if(target.reagents.total_volume >= target.reagents.maximum_volume)
to_chat(user, "You tear open [src], but [target] is stacked so high that it just drips off!") //Not sure if food can ever be full, but better safe than sorry.
qdel(src)
- return
+ return ITEM_INTERACT_COMPLETE
else
to_chat(user, "You tear open [src] above [target] and the condiments drip onto it.")
reagents.trans_to(target, amount_per_transfer_from_this)
qdel(src)
+ return ITEM_INTERACT_COMPLETE
+
+ if(isliving(target)) // Ignore mobs. Don't tap mobs.
+ return ITEM_INTERACT_COMPLETE
/obj/item/reagent_containers/condiment/pack/on_reagent_change()
if(length(reagents.reagent_list) > 0)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index d0246f6d34a..67548d69dcc 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -176,8 +176,10 @@
// blind_message (optional) is what blind people will hear e.g. "You hear something!"
/atom/proc/visible_message(message, blind_message)
for(var/mob/M as anything in get_mobs_in_view(7, src, ai_eyes = AI_EYE_INCLUDE))
+#ifndef GAME_TESTS
if(!M.client)
continue
+#endif
M.show_message(message, EMOTE_VISIBLE, blind_message, EMOTE_AUDIBLE)
// Show a message to all mobs in earshot of this one
diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm
index 51733f92f80..25554d88c93 100644
--- a/code/modules/reagents/reagent_containers.dm
+++ b/code/modules/reagents/reagent_containers.dm
@@ -14,6 +14,21 @@
var/has_lid = FALSE // Used for containers where we want to put lids on and off
var/temperature_min = 0 // To limit the temperature of a reagent container can atain when exposed to heat/cold
var/temperature_max = 10000
+ new_attack_chain = TRUE
+
+/obj/item/reagent_containers/interact_with_atom(atom/target, mob/living/user, list/modifiers)
+ if(isliving(target) && mob_act(target, user))
+ return ITEM_INTERACT_COMPLETE
+ if(normal_act(target, user))
+ return ITEM_INTERACT_COMPLETE
+ return ..()
+
+// Overriden inside its subtypes. Might add the basic container shit here (eg, beaker/bucket behaviour)
+/obj/item/reagent_containers/proc/mob_act(mob/target, mob/living/user)
+ return FALSE
+
+/obj/item/reagent_containers/proc/normal_act(atom/target, mob/living/user)
+ return FALSE
/obj/item/reagent_containers/proc/can_set_transfer_amount(mob/user)
if(!length(possible_transfer_amounts))
@@ -88,18 +103,16 @@
container_type |= REFILLABLE | DRAINABLE
update_icon(UPDATE_OVERLAYS)
-/obj/item/reagent_containers/attack_self__legacy__attackchain(mob/user)
- if(has_lid)
- if(is_open_container())
- to_chat(usr, "You put the lid on [src].")
- add_lid()
- else
- to_chat(usr, "You take the lid off [src].")
- remove_lid()
+/obj/item/reagent_containers/activate_self(mob/user)
+ if(..() || !has_lid)
+ return
-/obj/item/reagent_containers/attack__legacy__attackchain(mob/M, mob/user, def_zone)
- if(user.a_intent == INTENT_HARM)
- return ..()
+ if(is_open_container())
+ to_chat(usr, "You put the lid on [src].")
+ add_lid()
+ else
+ to_chat(usr, "You take the lid off [src].")
+ remove_lid()
/obj/item/reagent_containers/wash(mob/user, atom/source)
if(is_open_container())
diff --git a/code/modules/reagents/reagent_containers/applicator.dm b/code/modules/reagents/reagent_containers/applicator.dm
index ade44dc8a67..3f92498960d 100644
--- a/code/modules/reagents/reagent_containers/applicator.dm
+++ b/code/modules/reagents/reagent_containers/applicator.dm
@@ -11,6 +11,7 @@
container_type = REFILLABLE | AMOUNT_VISIBLE
temperature_min = 270
temperature_max = 350
+ var/delay = 10
var/ignore_flags = FALSE
var/applied_amount = 8 // How much it applies
var/applying = FALSE // So it can't be spammed.
@@ -72,8 +73,6 @@
if(applying)
to_chat(user, "You're already applying [src].")
return
- if(!iscarbon(M))
- return
if(ignore_flags || M.can_inject(user, TRUE))
if(M == user)
@@ -83,7 +82,7 @@
if(M.reagents)
applying = TRUE
update_icon(UPDATE_ICON_STATE)
- while(do_after(user, 10, target = M))
+ while(do_after(user, delay, target = M))
measured_health = M.health
apply_to(M, user, 1, FALSE)
if(measured_health == M.health)
@@ -96,11 +95,16 @@
update_icon()
user.changeNext_move(CLICK_CD_MELEE)
-/obj/item/reagent_containers/applicator/attack__legacy__attackchain(mob/living/M, mob/user)
- return apply(M, user)
+/obj/item/reagent_containers/applicator/interact_with_atom(atom/target, mob/living/user, list/modifiers)
+ if(!iscarbon(target))
+ return ..()
+ apply(target, user)
+ return ITEM_INTERACT_COMPLETE
-/obj/item/reagent_containers/applicator/attack_self__legacy__attackchain(mob/user)
- return apply(user, user)
+/obj/item/reagent_containers/applicator/activate_self(mob/user)
+ if(..() || !iscarbon(user))
+ return
+ apply(user, user)
/obj/item/reagent_containers/applicator/proc/apply_to(mob/living/carbon/M, mob/user, multiplier = 1, show_message = TRUE)
var/total_applied_amount = applied_amount * multiplier
diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm
index 11c8af8fa0c..981fa51b601 100644
--- a/code/modules/reagents/reagent_containers/borghydro.dm
+++ b/code/modules/reagents/reagent_containers/borghydro.dm
@@ -88,28 +88,36 @@
robot.cell.use(charge_cost)
total_reagents = min((total_reagents + BORGHYPO_REFILL_VALUE), maximum_reagents)
-/obj/item/reagent_containers/borghypo/attack__legacy__attackchain(mob/living/carbon/human/M, mob/user)
- if(!total_reagents)
- to_chat(user, "The injector is empty.")
- return
- if(!istype(M))
- return
- if(total_reagents && M.can_inject(user, TRUE, user.zone_selected, penetrate_thick))
- to_chat(user, "You inject [M] with the injector.")
- to_chat(M, "You feel a tiny prick!")
+/obj/item/reagent_containers/borghypo/interact_with_atom(atom/target, mob/living/user, list/modifiers)
+ if(ishuman(target))
+ var/mob/living/carbon/human/mob = target
+ if(!total_reagents)
+ to_chat(user, "The injector is empty.")
+ return ITEM_INTERACT_COMPLETE
+ if(!mob.can_inject(user, TRUE, user.zone_selected, penetrate_thick))
+ return ITEM_INTERACT_COMPLETE
- M.reagents.add_reagent(choosen_reagent, 5)
+ to_chat(user, "You inject [mob] with the injector.")
+ to_chat(mob, "You feel a tiny prick!")
+ mob.reagents.add_reagent(choosen_reagent, 5)
total_reagents = (total_reagents - 5)
- if(M.reagents)
+ if(mob.reagents)
var/datum/reagent/injected = GLOB.chemical_reagents_list[choosen_reagent]
var/contained = injected.name
- add_attack_logs(user, M, "Injected with [name] containing [contained], transfered [5] units", injected.harmless ? ATKLOG_ALMOSTALL : null)
+ add_attack_logs(user, mob, "Injected with [name] containing [contained], transfered [5] units", injected.harmless ? ATKLOG_ALMOSTALL : null)
to_chat(user, "[5] units injected. [total_reagents] units remaining.")
+ return ITEM_INTERACT_COMPLETE
+
+ if(isliving(target)) // ignore non-human mobs
+ return ITEM_INTERACT_COMPLETE
/obj/item/reagent_containers/borghypo/proc/get_radial_contents()
return reagent_icons & reagent_ids
-/obj/item/reagent_containers/borghypo/attack_self__legacy__attackchain(mob/user)
+/obj/item/reagent_containers/borghypo/activate_self(mob/user)
+ if(..())
+ return
+
playsound(loc, 'sound/effects/pop.ogg', 50, 0)
var/selected_reagent = show_radial_menu(user, src, get_radial_contents(), radius = 48)
if(!selected_reagent)
diff --git a/code/modules/reagents/reagent_containers/dropper.dm b/code/modules/reagents/reagent_containers/dropper.dm
index 79809807061..12b0c30cdb8 100644
--- a/code/modules/reagents/reagent_containers/dropper.dm
+++ b/code/modules/reagents/reagent_containers/dropper.dm
@@ -18,12 +18,7 @@
else
icon_state = "[initial(icon_state)]1"
-/obj/item/reagent_containers/dropper/attack__legacy__attackchain(mob/living/M, mob/living/user, def_zone)
- return
-
-/obj/item/reagent_containers/dropper/afterattack__legacy__attackchain(atom/target, mob/user, proximity)
- if(!proximity)
- return
+/obj/item/reagent_containers/dropper/mob_act(mob/target, mob/living/user)
var/to_transfer = 0
if(iscarbon(target))
var/mob/living/carbon/C = target
@@ -67,34 +62,37 @@
to_transfer = reagents.trans_to(C, amount_per_transfer_from_this)
to_chat(user, "You transfer [to_transfer] units of the solution.")
- if(isobj(target))
- if(!target.reagents)
+/obj/item/reagent_containers/dropper/normal_act(atom/target, mob/living/user)
+ . = FALSE
+ var/to_transfer = 0
+ if(!target.reagents)
+ return
+
+ if(reagents.total_volume)
+ if(!target.is_open_container() && !(isfood(target) && !ispill(target)) && !istype(target, /obj/item/clothing/mask/cigarette))
+ to_chat(user, "You cannot directly fill this object.")
return
- if(reagents.total_volume)
- if(!target.is_open_container() && !(isfood(target) && !ispill(target)) && !istype(target, /obj/item/clothing/mask/cigarette))
- to_chat(user, "You cannot directly fill this object.")
- return
+ if(target.reagents.total_volume >= target.reagents.maximum_volume)
+ to_chat(user, "[target] is full.")
+ return
- if(target.reagents.total_volume >= target.reagents.maximum_volume)
- to_chat(user, "[target] is full.")
- return
+ to_transfer = reagents.trans_to(target, amount_per_transfer_from_this)
+ to_chat(user, "You transfer [to_transfer] units of the solution.")
- to_transfer = reagents.trans_to(target, amount_per_transfer_from_this)
- to_chat(user, "You transfer [to_transfer] units of the solution.")
+ else
+ if(!target.is_open_container() && !istype(target, /obj/structure/reagent_dispensers))
+ to_chat(user, "You cannot directly remove reagents from [target].")
+ return
- else
- if(!target.is_open_container() && !istype(target, /obj/structure/reagent_dispensers))
- to_chat(user, "You cannot directly remove reagents from [target].")
- return
+ if(!target.reagents.total_volume)
+ to_chat(user, "[target] is empty.")
+ return
- if(!target.reagents.total_volume)
- to_chat(user, "[target] is empty.")
- return
+ to_transfer = target.reagents.trans_to(src, amount_per_transfer_from_this)
- to_transfer = target.reagents.trans_to(src, amount_per_transfer_from_this)
-
- to_chat(user, "You fill [src] with [to_transfer] units of the solution.")
+ to_chat(user, "You fill [src] with [to_transfer] units of the solution.")
+ return TRUE
/obj/item/reagent_containers/dropper/cyborg
name = "Industrial Dropper"
@@ -113,10 +111,10 @@
//Syndicate item. Virus transmitting mini hypospray
/obj/item/reagent_containers/dropper/precision/viral_injector
-/obj/item/reagent_containers/dropper/precision/viral_injector/attack__legacy__attackchain(mob/living/M, mob/living/user, def_zone)
- if(M.can_inject(user, TRUE))
- to_chat(user, "You stealthily stab [M] with [src].")
- if(reagents.total_volume && M.reagents)
+/obj/item/reagent_containers/dropper/precision/viral_injector/mob_act(mob/living/target, mob/living/user)
+ if(target.can_inject(user, TRUE))
+ to_chat(user, "You stealthily stab [target] with [src].")
+ if(reagents.total_volume && target.reagents)
var/list/injected = list()
for(var/datum/reagent/R in reagents.reagent_list)
injected += R.name
@@ -135,10 +133,10 @@
virusData += " ([english_list(english_symptoms)])"
virList += virusData
var/str = english_list(virList)
- add_attack_logs(user, M, "Infected with [str].")
+ add_attack_logs(user, target, "Infected with [str].")
- reagents.reaction(M, REAGENT_INGEST, reagents.total_volume)
- reagents.trans_to(M, 1)
+ reagents.reaction(target, REAGENT_INGEST, reagents.total_volume)
+ reagents.trans_to(target, 1)
var/contained = english_list(injected)
- add_attack_logs(user, M, "Injected with [src] containing ([contained])")
+ add_attack_logs(user, target, "Injected with [src] containing ([contained])")
diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm
index 300fc2ee411..f8371f4a554 100644
--- a/code/modules/reagents/reagent_containers/glass_containers.dm
+++ b/code/modules/reagents/reagent_containers/glass_containers.dm
@@ -27,58 +27,53 @@
. += "[src] can hold up to [reagents.maximum_volume] units."
-/obj/item/reagent_containers/glass/attack__legacy__attackchain(mob/M, mob/user, def_zone)
+/obj/item/reagent_containers/glass/mob_act(mob/target, mob/living/user)
if(!is_open_container())
- return ..()
+ return
if(!reagents || !reagents.total_volume)
to_chat(user, "[src] is empty!")
return
- if(istype(M))
+ if(istype(target))
var/list/transferred = list()
for(var/datum/reagent/R in reagents.reagent_list)
transferred += R.name
var/contained = english_list(transferred)
if(user.a_intent == INTENT_HARM)
- M.visible_message("[user] splashes the contents of [src] onto [M]!", \
- "[user] splashes the contents of [src] onto [M]!")
- add_attack_logs(user, M, "Splashed with [name] containing [contained]", !!M.ckey ? null : ATKLOG_ALL)
+ target.visible_message("[user] splashes the contents of [src] onto [target]!", \
+ "[user] splashes the contents of [src] onto [target]!")
+ add_attack_logs(user, target, "Splashed with [name] containing [contained]", !!target.ckey ? null : ATKLOG_ALL)
- reagents.reaction(M, REAGENT_TOUCH)
+ reagents.reaction(target, REAGENT_TOUCH)
reagents.clear_reagents()
else
- if(!iscarbon(M)) // Non-carbons can't process reagents
- to_chat(user, "You cannot find a way to feed [M].")
+ if(!iscarbon(target)) // Non-carbons can't process reagents
+ to_chat(user, "You cannot find a way to feed [target].")
return
- if(M != user)
- M.visible_message("[user] attempts to feed something to [M].", \
+ if(target != user)
+ target.visible_message("[user] attempts to feed something to [target].", \
"[user] attempts to feed something to you.")
- if(!do_mob(user, M))
+ if(!do_mob(user, target))
return
if(!reagents || !reagents.total_volume)
return // The drink might be empty after the delay, such as by spam-feeding
- M.visible_message("[user] feeds something to [M].", "[user] feeds something to you.")
- add_attack_logs(user, M, "Fed with [name] containing [contained]", !!M.ckey ? null : ATKLOG_ALL)
+ target.visible_message("[user] feeds something to [target].", "[user] feeds something to you.")
+ add_attack_logs(user, target, "Fed with [name] containing [contained]", !!target.ckey ? null : ATKLOG_ALL)
else
to_chat(user, "You swallow a gulp of [src].")
var/fraction = min(5 / reagents.total_volume, 1)
- reagents.reaction(M, REAGENT_INGEST, fraction)
- addtimer(CALLBACK(reagents, TYPE_PROC_REF(/datum/reagents, trans_to), M, 5), 5)
- playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1)
+ reagents.reaction(target, REAGENT_INGEST, fraction)
+ addtimer(CALLBACK(reagents, TYPE_PROC_REF(/datum/reagents, trans_to), target, 5), 5)
+ playsound(target.loc,'sound/items/drink.ogg', rand(10,50), TRUE)
-/obj/item/reagent_containers/glass/afterattack__legacy__attackchain(obj/target, mob/user, proximity)
- if((!proximity) || !check_allowed_items(target, target_self = TRUE))
- return
-
- if(!is_open_container())
- return
-
- if(!reagents)
+/obj/item/reagent_containers/glass/normal_act(atom/target, mob/living/user)
+ if(!check_allowed_items(target, target_self = TRUE) || !is_open_container() || !reagents)
return
+ . = TRUE
if(target.is_refillable()) //Something like a glass. Player probably wants to transfer TO it.
if(!reagents.total_volume)
to_chat(user, "[src] is empty!")
@@ -110,13 +105,13 @@
reagents.reaction(target, REAGENT_TOUCH)
reagents.clear_reagents()
-/obj/item/reagent_containers/glass/attackby__legacy__attackchain(obj/item/I, mob/user, params)
- if(is_pen(I))
- var/t = rename_interactive(user, I)
+/obj/item/reagent_containers/glass/item_interaction(mob/living/user, obj/item/used, list/modifiers)
+ if(is_pen(used))
+ var/t = rename_interactive(user, used)
if(!isnull(t))
label_text = t
- else
- return ..()
+ return ITEM_INTERACT_COMPLETE
+ return ..()
/obj/item/reagent_containers/glass/beaker
name = "beaker"
@@ -178,17 +173,17 @@
if(reagents)
reagents.temperature_reagents(4000)
-/obj/item/reagent_containers/glass/beaker/attackby__legacy__attackchain(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/assembly_holder) && can_assembly)
+/obj/item/reagent_containers/glass/beaker/item_interaction(mob/living/user, obj/item/used, list/modifiers)
+ if(istype(used, /obj/item/assembly_holder) && can_assembly)
if(assembly)
to_chat(usr, "[src] already has an assembly.")
- return ..()
- assembly = W
- user.drop_item()
- W.forceMove(src)
- update_icon(UPDATE_OVERLAYS)
- else
- ..()
+ else
+ assembly = used
+ user.drop_item()
+ used.forceMove(src)
+ update_icon(UPDATE_OVERLAYS)
+ return ITEM_INTERACT_COMPLETE
+ return ..()
/obj/item/reagent_containers/glass/beaker/HasProximity(atom/movable/AM)
if(assembly)
@@ -332,19 +327,19 @@
reagents.reaction(user, REAGENT_TOUCH)
reagents.clear_reagents()
-/obj/item/reagent_containers/glass/bucket/attackby__legacy__attackchain(obj/D, mob/user, params)
- if(istype(D, /obj/item/mop))
- var/obj/item/mop/m = D
- m.wet_mop(src, user)
- return
- if(isprox(D))
- to_chat(user, "You add [D] to [src].")
- qdel(D)
+/obj/item/reagent_containers/glass/bucket/item_interaction(mob/living/user, obj/item/used, list/modifiers)
+ if(istype(used, /obj/item/mop))
+ var/obj/item/mop/mop = used
+ mop.wet_mop(src, user)
+ return ITEM_INTERACT_COMPLETE
+ if(isprox(used))
+ to_chat(user, "You add [used] to [src].")
+ qdel(used)
user.put_in_hands(new /obj/item/bucket_sensor)
user.unequip(src)
qdel(src)
- else
- ..()
+ return ITEM_INTERACT_COMPLETE
+ return ..()
/obj/item/reagent_containers/glass/beaker/waterbottle
name = "bottle of water"
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 00777a16dd4..122d8016986 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -69,16 +69,20 @@
reagents.reaction(M, REAGENT_INGEST, 0.1)
return TRUE
-/obj/item/reagent_containers/hypospray/attack__legacy__attackchain(mob/living/M, mob/user)
- return apply(M, user)
+/obj/item/reagent_containers/hypospray/mob_act(mob/target, mob/living/user)
+ apply(target, user)
+ return TRUE
-/obj/item/reagent_containers/hypospray/attack_self__legacy__attackchain(mob/user)
- return apply(user, user)
+/obj/item/reagent_containers/hypospray/activate_self(mob/user)
+ if(..())
+ return FINISH_ATTACK
-/obj/item/reagent_containers/hypospray/attackby__legacy__attackchain(obj/item/I, mob/user, params)
+ apply(user, user)
+
+/obj/item/reagent_containers/hypospray/item_interaction(mob/living/user, obj/item/I, list/modifiers)
if(is_pen(I))
rename_interactive(user, I, use_prefix = TRUE, prompt = "Give [src] a title.")
- return TRUE
+ return ITEM_INTERACT_COMPLETE
return ..()
@@ -189,18 +193,16 @@
container_type = DRAWABLE
flags = null
-/obj/item/reagent_containers/hypospray/autoinjector/attack__legacy__attackchain(mob/M, mob/user)
+/obj/item/reagent_containers/hypospray/autoinjector/mob_act(mob/target, mob/living/user)
if(!reagents.total_volume)
to_chat(user, "[src] is empty!")
- return
- ..()
+ return TRUE
+ . = ..()
update_icon(UPDATE_ICON_STATE)
- return TRUE
-/obj/item/reagent_containers/hypospray/autoinjector/attack_self__legacy__attackchain(mob/user)
- ..()
+/obj/item/reagent_containers/hypospray/autoinjector/activate_self(mob/user)
+ . = ..()
update_icon(UPDATE_ICON_STATE)
- return TRUE
/obj/item/reagent_containers/hypospray/autoinjector/update_icon_state()
if(reagents.total_volume > 0)
@@ -268,9 +270,9 @@
volume = 40
list_reagents = list("nanocalcium" = 30, "epinephrine" = 10)
-/obj/item/reagent_containers/hypospray/autoinjector/nanocalcium/attack__legacy__attackchain(mob/living/M, mob/user)
+/obj/item/reagent_containers/hypospray/autoinjector/nanocalcium/apply(mob/living/M, mob/user)
if(..())
- playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 20, 1)
+ playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 20, TRUE)
/obj/item/reagent_containers/hypospray/autoinjector/zombiecure
name = "\improper Anti-Plague Sequence Alpha autoinjector"
@@ -281,7 +283,7 @@
container_type = null //No sucking out the reagent
list_reagents = list("zombiecure1" = 15)
-/obj/item/reagent_containers/hypospray/autoinjector/zombiecure/attack__legacy__attackchain(mob/living/M, mob/user)
+/obj/item/reagent_containers/hypospray/autoinjector/zombiecure/apply(mob/living/M, mob/user)
if(..())
playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 20, TRUE) //Sucker for sounds, also gets zombies attention.
diff --git a/code/modules/reagents/reagent_containers/iv_bag.dm b/code/modules/reagents/reagent_containers/iv_bag.dm
index 30bfa515a70..9c718c0d69c 100644
--- a/code/modules/reagents/reagent_containers/iv_bag.dm
+++ b/code/modules/reagents/reagent_containers/iv_bag.dm
@@ -16,6 +16,7 @@
var/label_text
var/mode = IV_INJECT
var/mob/living/carbon/human/injection_target
+ var/injection_action_delay = 3 SECONDS
/obj/item/reagent_containers/iv_bag/Destroy()
end_processing()
@@ -32,8 +33,10 @@
..()
update_icon(UPDATE_OVERLAYS)
-/obj/item/reagent_containers/iv_bag/attack_self__legacy__attackchain(mob/user)
- ..()
+/obj/item/reagent_containers/iv_bag/activate_self(mob/user)
+ if(..())
+ return
+
mode = !mode
update_icon(UPDATE_OVERLAYS)
@@ -87,12 +90,7 @@
injection_target.reagents.trans_id_to(src, reagent.id, amount_per_transfer_from_this / 10)
update_icon(UPDATE_OVERLAYS)
-/obj/item/reagent_containers/iv_bag/attack__legacy__attackchain(mob/living/M, mob/living/user, def_zone)
- return
-
-/obj/item/reagent_containers/iv_bag/afterattack__legacy__attackchain(atom/target, mob/user, proximity)
- if(!proximity)
- return
+/obj/item/reagent_containers/iv_bag/mob_act(mob/target, mob/living/user)
if(!target.reagents)
return
@@ -105,7 +103,7 @@
if(L != user)
L.visible_message("[user] is trying to remove [src]'s needle from [L]'s arm!", \
"[user] is trying to remove [src]'s needle from [L]'s arm!")
- if(!do_mob(user, L))
+ if(!do_mob(user, L, injection_action_delay))
return
L.visible_message("[user] removes [src]'s needle from [L]'s arm!", \
"[user] removes [src]'s needle from [L]'s arm!")
@@ -119,7 +117,7 @@
if(L != user)
L.visible_message("[user] is trying to insert [src]'s needle into [L]'s arm!", \
"[user] is trying to insert [src]'s needle into [L]'s arm!")
- if(!do_mob(user, L))
+ if(!do_mob(user, L, injection_action_delay))
return
L.visible_message("[user] inserts [src]'s needle into [L]'s arm!", \
"[user] inserts [src]'s needle into [L]'s arm!")
@@ -157,9 +155,10 @@
if(IV_INJECT)
. += "inject"
-/obj/item/reagent_containers/iv_bag/attackby__legacy__attackchain(obj/item/I, mob/user, params)
- if(is_pen(I))
- rename_interactive(user, I)
+/obj/item/reagent_containers/iv_bag/item_interaction(mob/living/user, obj/item/used, list/modifiers)
+ if(is_pen(used))
+ rename_interactive(user, used)
+ return ITEM_INTERACT_COMPLETE
// PRE-FILLED IV BAGS BELOW
diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm
index fb1cbc239ef..022eb458082 100644
--- a/code/modules/reagents/reagent_containers/patch.dm
+++ b/code/modules/reagents/reagent_containers/patch.dm
@@ -12,11 +12,9 @@
var/instant_application = FALSE
var/needs_to_apply_reagents = TRUE
-/obj/item/reagent_containers/patch/attack__legacy__attackchain(mob/living/carbon/C, mob/user)
- return apply(C, user)
-
-/obj/item/reagent_containers/patch/attack_self__legacy__attackchain(mob/user)
- return apply(user, user)
+/obj/item/reagent_containers/patch/mob_act(mob/target, mob/living/user)
+ if(!apply(target, user))
+ return ITEM_INTERACT_COMPLETE
/obj/item/reagent_containers/patch/proc/apply(mob/living/carbon/C, mob/user)
if(!istype(C))
diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm
index dfbb26ab892..4475085c51d 100644
--- a/code/modules/reagents/reagent_containers/pill.dm
+++ b/code/modules/reagents/reagent_containers/pill.dm
@@ -47,14 +47,13 @@
qdel(src)
return TRUE
-/obj/item/reagent_containers/pill/attack__legacy__attackchain(mob/living/carbon/C, mob/user)
- return apply(C, user)
+/obj/item/reagent_containers/pill/mob_act(mob/target, mob/living/user)
+ apply(target, user)
+ return TRUE
-/obj/item/reagent_containers/pill/attack_self__legacy__attackchain(mob/user)
- return apply(user, user)
-
-/obj/item/reagent_containers/pill/afterattack__legacy__attackchain(obj/target, mob/user, proximity)
- if(!proximity || !target.is_refillable())
+/obj/item/reagent_containers/pill/normal_act(atom/target, mob/living/user)
+ . = TRUE
+ if(!target.is_refillable())
return
if(target.reagents.holder_full())
to_chat(user, "[target] is full.")
diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm
index 9ca16326b7f..e3e60e03472 100644
--- a/code/modules/reagents/reagent_containers/spray.dm
+++ b/code/modules/reagents/reagent_containers/spray.dm
@@ -23,7 +23,8 @@
. = ..()
ADD_TRAIT(src, TRAIT_CAN_POINT_WITH, ROUNDSTART_TRAIT)
-/obj/item/reagent_containers/spray/afterattack__legacy__attackchain(atom/A, mob/user)
+/obj/item/reagent_containers/spray/normal_act(atom/A, mob/living/user)
+ . = TRUE
if(isstorage(A) || ismodcontrol(A) || istype(A, /obj/structure/table) || istype(A, /obj/structure/rack) || istype(A, /obj/structure/closet) \
|| istype(A, /obj/item/reagent_containers) || istype(A, /obj/structure/sink) || istype(A, /obj/structure/janitorialcart) || istype(A, /obj/machinery/hydroponics))
return
@@ -86,7 +87,9 @@
chem_puff.RegisterSignal(our_loop, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/obj/effect/decal/chempuff, loop_ended))
chem_puff.RegisterSignal(our_loop, COMSIG_MOVELOOP_POSTPROCESS, TYPE_PROC_REF(/obj/effect/decal/chempuff, check_move))
-/obj/item/reagent_containers/spray/attack_self__legacy__attackchain(mob/user)
+/obj/item/reagent_containers/spray/activate_self(mob/user)
+ if(..())
+ return FINISH_ATTACK
amount_per_transfer_from_this = (amount_per_transfer_from_this == 10 ? 5 : 10)
spray_currentrange = (spray_currentrange == 1 ? spray_maxrange : 1)
@@ -126,7 +129,9 @@
amount_per_transfer_from_this = 10
list_reagents = list("cleaner" = 250)
-/obj/item/reagent_containers/spray/cleaner/attack_self__legacy__attackchain(mob/user)
+/obj/item/reagent_containers/spray/cleaner/activate_self(mob/user)
+ if(..())
+ return
amount_per_transfer_from_this = (amount_per_transfer_from_this == 5 ? 10 : 5)
spray_currentrange = (spray_currentrange == 1 ? spray_maxrange : 1)
to_chat(user, "You [amount_per_transfer_from_this == 5 ? "remove" : "fix"] the nozzle. You'll now use [amount_per_transfer_from_this] units per spray.")
@@ -206,8 +211,10 @@
volume = 10
list_reagents = list("water" = 10)
-/obj/item/reagent_containers/spray/waterflower/attack_self__legacy__attackchain(mob/user) //Don't allow changing how much the flower sprays
- return
+/obj/item/reagent_containers/spray/waterflower/Initialize(mapload)
+ . = ..()
+ // Don't allow changing how much the flower sprays
+ RegisterSignal(src, COMSIG_ACTIVATE_SELF, TYPE_PROC_REF(/datum, signal_cancel_activate_self))
//chemsprayer
/obj/item/reagent_containers/spray/chemsprayer
@@ -262,7 +269,9 @@
-/obj/item/reagent_containers/spray/chemsprayer/attack_self__legacy__attackchain(mob/user)
+/obj/item/reagent_containers/spray/chemsprayer/activate_self(mob/user)
+ if(..())
+ return
amount_per_transfer_from_this = (amount_per_transfer_from_this == 10 ? 5 : 10)
to_chat(user, "You adjust the output switch. You'll now use [amount_per_transfer_from_this] units per spray.")
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index 41e0f9979f7..285cac38a08 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -15,6 +15,7 @@
container_type = TRANSPARENT
///If this variable is true, the syringe will work through hardsuits / modsuits / biosuits.
var/penetrates_thick = FALSE
+ var/syringe_draw_time = 3 SECONDS
/obj/item/reagent_containers/syringe/Initialize(mapload)
. = ..()
@@ -38,7 +39,10 @@
..()
update_icon()
-/obj/item/reagent_containers/syringe/attack_self__legacy__attackchain(mob/user)
+/obj/item/reagent_containers/syringe/activate_self(mob/user)
+ if(..())
+ return
+
mode = !mode
update_icon()
@@ -46,16 +50,8 @@
..()
update_icon()
-/obj/item/reagent_containers/syringe/attack__legacy__attackchain(mob/living/M, mob/living/user, def_zone)
- return
-
-/obj/item/reagent_containers/syringe/attackby__legacy__attackchain(obj/item/I, mob/user, params)
- if(istype(I, /obj/item/storage/bag))
- ..()
-
-/obj/item/reagent_containers/syringe/afterattack__legacy__attackchain(atom/target, mob/user , proximity)
- if(!proximity)
- return
+/obj/item/reagent_containers/syringe/mob_act(mob/target, mob/living/user)
+ . = TRUE
if(!target.reagents)
return
@@ -78,7 +74,7 @@
target.visible_message("[user] is trying to take a blood sample from [target]!", \
"[user] is trying to take a blood sample from [target]!")
busy = TRUE
- if(!do_mob(user, target))
+ if(!do_mob(user, target, syringe_draw_time))
busy = FALSE
return
if(reagents.holder_full())
diff --git a/code/modules/research/circuitprinter.dm b/code/modules/research/circuitprinter.dm
index f7fd4e682cf..e544c86fe1c 100644
--- a/code/modules/research/circuitprinter.dm
+++ b/code/modules/research/circuitprinter.dm
@@ -82,9 +82,6 @@ using metal and glass, it uses glass and reagents (usually sulfuric acis).
to_chat(user, "You can't load [src] while it's opened.")
return ITEM_INTERACT_COMPLETE
- if(used.is_open_container())
- return ITEM_INTERACT_SKIP_TO_AFTER_ATTACK
-
return ..()
/obj/machinery/r_n_d/circuit_imprinter/crowbar_act(mob/living/user, obj/item/I)
diff --git a/code/modules/research/protolathe.dm b/code/modules/research/protolathe.dm
index d0b4feac679..350850b098e 100644
--- a/code/modules/research/protolathe.dm
+++ b/code/modules/research/protolathe.dm
@@ -92,9 +92,6 @@ Note: Must be placed west/left of and R&D console to function.
to_chat(user, "You can't load [src] while it's opened.")
return ITEM_INTERACT_COMPLETE
- if(used.is_open_container())
- return ITEM_INTERACT_SKIP_TO_AFTER_ATTACK
-
return ..()
/obj/machinery/r_n_d/protolathe/crowbar_act(mob/living/user, obj/item/I)
diff --git a/code/tests/_game_test_puppeteer.dm b/code/tests/_game_test_puppeteer.dm
index 8d77ea0057b..e19bfe3042a 100644
--- a/code/tests/_game_test_puppeteer.dm
+++ b/code/tests/_game_test_puppeteer.dm
@@ -51,6 +51,7 @@
/datum/test_puppeteer/proc/use_item_in_hand()
var/obj/item/item = puppet.get_active_hand()
if(!item)
+ origin_test.Fail("could not find obj in [puppet] active hand", __FILE__, __LINE__)
return
if(item.new_attack_chain)
@@ -104,6 +105,9 @@
/datum/test_puppeteer/proc/set_intent(new_intent)
puppet.a_intent_change(new_intent)
+/datum/test_puppeteer/proc/set_zone(new_zone)
+ puppet.zone_selected = new_zone
+
/datum/test_puppeteer/proc/rejuvenate()
puppet.rejuvenate()
@@ -162,3 +166,8 @@
backpack.remove_item_from_storage(object)
puppet.put_in_active_hand(object)
+
+/datum/test_puppeteer/proc/click_on_self()
+ puppet.ClickOn(puppet)
+ puppet.next_click = world.time
+ puppet.next_move = world.time
diff --git a/code/tests/attack_chain/test_attack_chain_reagent_containers.dm b/code/tests/attack_chain/test_attack_chain_reagent_containers.dm
new file mode 100644
index 00000000000..b0980d0248b
--- /dev/null
+++ b/code/tests/attack_chain/test_attack_chain_reagent_containers.dm
@@ -0,0 +1,258 @@
+/datum/game_test/attack_chain_applicator/Run()
+ var/datum/test_puppeteer/player = new(src)
+ var/datum/test_puppeteer/target = player.spawn_puppet_nearby()
+ var/obj/item/reagent_containers/applicator/mender = player.spawn_obj_in_hand(/obj/item/reagent_containers/applicator/burn)
+
+ mender.delay = 0
+ target.puppet.apply_damage(1, BURN)
+ player.click_on(target)
+ TEST_ASSERT(target.check_attack_log("Automends with"), "player failed to use mender on target")
+ TEST_ASSERT_EQUAL(target.puppet.health, target.puppet.getMaxHealth(), "mender failed to mend damage")
+
+ player.puppet.attack_log_old = null
+ player.puppet.apply_damage(1, BURN)
+ player.use_item_in_hand()
+ TEST_ASSERT(player.check_attack_log("Automends with"), "player failed to use mender on target")
+ TEST_ASSERT_EQUAL(player.puppet.health, player.puppet.getMaxHealth(), "mender failed to mend damage")
+
+ mender.reagents.total_volume = 0
+ player.click_on(target)
+ TEST_ASSERT_LAST_CHATLOG(player, "[mender] is empty")
+
+ var/obj/item/storage/backpack/backpack = player.spawn_obj_nearby(/obj/item/storage/backpack)
+ player.click_on(backpack)
+ TEST_ASSERT_LAST_CHATLOG(player, "You put")
+
+ // ToasTODO: Add an AltClick test
+
+/datum/game_test/attack_chain_condiment/Run()
+ var/datum/test_puppeteer/player = new(src)
+ var/obj/item/reagent_containers/saltshaker = player.spawn_obj_in_hand(/obj/item/reagent_containers/condiment/saltshaker)
+
+ var/obj/structure/reagent_dispensers/watertank = player.spawn_obj_nearby(/obj/structure/reagent_dispensers/watertank)
+ player.click_on(watertank)
+ TEST_ASSERT_LAST_CHATLOG(player, "[saltshaker] is full!")
+
+ var/obj/machinery/kitchen_machine/grill = player.spawn_obj_nearby(/obj/machinery/kitchen_machine/grill)
+ player.click_on(grill)
+ TEST_ASSERT_ANY_CHATLOG(player, "You transfer")
+ TEST_ASSERT_NOT_CHATLOG(player, "You hit")
+
+ player.click_on(player)
+ TEST_ASSERT_LAST_CHATLOG(player, "You swallow some")
+
+ saltshaker.reagents.total_volume = 0
+ player.click_on(grill)
+ TEST_ASSERT_LAST_CHATLOG(player, "is empty!")
+ player.click_on(player)
+ TEST_ASSERT_LAST_CHATLOG(player, "None of [saltshaker] left, oh no!")
+
+ player.click_on(watertank)
+ TEST_ASSERT_LAST_CHATLOG(player, "You fill")
+
+ var/obj/item/storage/backpack = player.spawn_obj_nearby(/obj/item/storage/backpack)
+ player.click_on(backpack)
+ TEST_ASSERT_LAST_CHATLOG(player, "You put")
+
+ var/obj/item/food/sliced/margherita_pizza = player.spawn_obj_nearby(/obj/item/food/sliced/margherita_pizza)
+ player.spawn_obj_in_hand(/obj/item/reagent_containers/condiment/pack)
+ player.click_on(margherita_pizza)
+ TEST_ASSERT_LAST_CHATLOG(player, "You tear open")
+
+ player.spawn_obj_in_hand(/obj/item/reagent_containers/condiment/pack)
+ player.click_on(backpack)
+ TEST_ASSERT_LAST_CHATLOG(player, "You put")
+
+/datum/game_test/attack_chain_drinks/Run()
+ var/datum/test_puppeteer/player = new(src)
+ var/obj/structure/reagent_dispensers/watertank/watertank = player.spawn_obj_nearby(/obj/structure/reagent_dispensers/watertank)
+ var/obj/item/reagent_containers/glass/bucket/bucket = player.spawn_obj_nearby(/obj/item/reagent_containers/glass/bucket)
+ var/obj/item/storage/backpack/backpack = player.spawn_obj_nearby(/obj/item/storage/backpack)
+
+ // Drinks
+ var/obj/item/reagent_containers/drinks/coffee/coffee = player.spawn_obj_in_hand(/obj/item/reagent_containers/drinks/coffee)
+ player.click_on(player)
+ TEST_ASSERT_ANY_CHATLOG(player, "You swallow a gulp of [coffee]")
+
+ player.click_on(bucket)
+ TEST_ASSERT_LAST_CHATLOG(player, "You transfer")
+
+ coffee.reagents.total_volume = 0
+ player.click_on(player)
+ TEST_ASSERT_LAST_CHATLOG(player, "None of [coffee] left, oh no!")
+
+ player.click_on(bucket)
+ TEST_ASSERT_LAST_CHATLOG(player, "[coffee] is empty.")
+
+ player.click_on(watertank)
+ TEST_ASSERT_LAST_CHATLOG(player, "You fill [coffee] with")
+
+ player.click_on(backpack)
+ TEST_ASSERT(coffee in backpack.contents, "player failed to put [coffee] in backpack")
+
+ bucket.reagents.total_volume = 0
+ qdel(coffee)
+
+ // Cans
+ var/obj/item/reagent_containers/drinks/cans/can = player.spawn_obj_in_hand(/obj/item/reagent_containers/drinks/cans/cola)
+
+ player.click_on(player)
+ TEST_ASSERT_LAST_CHATLOG(player, "You need to open [can] first!")
+
+ player.use_item_in_hand()
+ TEST_ASSERT_LAST_CHATLOG(player, "You open the drink with an audible pop!")
+
+ player.click_on(watertank)
+ TEST_ASSERT_LAST_CHATLOG(player, "You fill [can] with")
+
+ player.click_on(player)
+ TEST_ASSERT_ANY_CHATLOG(player, "You swallow a gulp of [can]")
+
+ player.click_on(bucket)
+ TEST_ASSERT_LAST_CHATLOG(player, "You transfer")
+
+ player.set_zone("head")
+ player.set_intent("harm")
+ can.reagents.total_volume = 0
+ player.click_on(player)
+ TEST_ASSERT_LAST_CHATLOG(player, "You crush [can] on your forehead.")
+
+ can = player.spawn_obj_in_hand(/obj/item/reagent_containers/drinks/cans/cola)
+ player.set_intent("help")
+ player.click_on(backpack)
+ TEST_ASSERT(can in backpack.contents, "player failed to put can in backpack")
+ qdel(can)
+
+ // Bottles
+ var/obj/item/reagent_containers/drinks/bottle/whiskey/bottle = player.spawn_obj_in_hand(/obj/item/reagent_containers/drinks/bottle/whiskey)
+ player.click_on(bucket)
+ TEST_ASSERT_LAST_CHATLOG(player, "You transfer")
+
+ player.click_on(player)
+ TEST_ASSERT_ANY_CHATLOG(player, "You swallow a gulp of [bottle]")
+
+ player.click_on(watertank)
+ TEST_ASSERT_LAST_CHATLOG(player, "You fill [bottle] with")
+
+ player.set_intent("harm")
+ player.click_on(player)
+ TEST_ASSERT(player.check_attack_log("Hit with [bottle]"), "player failed to smash a bottle on harm intent")
+ TEST_ASSERT_NOTEQUAL(player.puppet.health, player.puppet.getMaxHealth(), "bottle smash didnt deal damage")
+ player.puppet.drop_item()
+ player.set_intent("help")
+
+ bottle = player.spawn_obj_in_hand(/obj/item/reagent_containers/drinks/bottle/whiskey)
+ player.click_on(backpack)
+ TEST_ASSERT(bottle in backpack.contents, "player failed to put bottle in backpack")
+ qdel(bottle)
+
+ // Molotov
+ var/obj/item/reagent_containers/drinks/bottle/molotov/molotov = player.spawn_obj_nearby(/obj/item/reagent_containers/drinks/bottle/molotov)
+ molotov.list_reagents = list("vodka" = 100)
+
+ var/lighter = player.spawn_obj_in_hand(/obj/item/lighter/zippo)
+ player.use_item_in_hand()
+ player.click_on(molotov)
+ TEST_ASSERT(molotov.active, "player failed to light molotov")
+
+ player.puppet.drop_item()
+ player.click_on(molotov)
+ player.use_item_in_hand()
+ TEST_ASSERT(!molotov.active, "player failed to extinguish molotov")
+
+ player.click_on(backpack)
+ TEST_ASSERT(molotov in backpack.contents, "player failed to put molotov in backpack")
+ qdel(molotov)
+
+ // Drinking glass
+ var/obj/item/reagent_containers/drinks/drinkingglass/glass = player.spawn_obj_nearby(/obj/item/reagent_containers/drinks/drinkingglass)
+ var/obj/item/food/egg/egg = player.spawn_obj_in_hand(/obj/item/food/egg)
+ player.click_on(glass)
+ TEST_ASSERT_LAST_CHATLOG(player, "You break [egg] in")
+
+ player.click_on(glass)
+ player.click_on(backpack)
+ TEST_ASSERT(glass in backpack.contents, "player failed to put drinking glass in backpack")
+ qdel(glass)
+
+ // Shot glass - For some reason the shotglass doesn't get clicked. Works ingame
+ var/obj/item/reagent_containers/drinks/drinkingglass/shotglass/shotglass = player.spawn_obj_nearby(/obj/item/reagent_containers/drinks/drinkingglass/shotglass)
+ shotglass.reagents.add_reagent("vodka", 15)
+
+ player.click_on(lighter)
+ player.click_on(shotglass)
+ TEST_ASSERT_LAST_CHATLOG(player, "The shot glass of Vodka begins to burn with a blue hue!")
+
+ player.puppet.drop_item()
+ player.click_on(shotglass)
+ player.use_item_in_hand()
+ TEST_ASSERT_LAST_CHATLOG(player, "The dancing flame on the flaming shot glass of Vodka dies out.")
+
+/datum/game_test/attack_chain_medcontainers/Run()
+ var/datum/test_puppeteer/player = new(src)
+ var/datum/test_puppeteer/target = player.spawn_puppet_nearby()
+
+ // Syringes
+ var/obj/item/reagent_containers/syringe/syringe = player.spawn_obj_in_hand(/obj/item/reagent_containers/syringe)
+ syringe.syringe_draw_time = 0
+ player.click_on(target)
+ TEST_ASSERT_LAST_CHATLOG(player, "[player.puppet] takes a blood sample")
+ player.put_away(syringe)
+
+ // IV Bags
+ var/obj/machinery/iv_drip/iv_drip = player.spawn_obj_nearby(/obj/machinery/iv_drip)
+ var/obj/blood_bag = player.spawn_obj_in_hand(/obj/item/reagent_containers/iv_bag)
+ player.click_on(iv_drip)
+ TEST_ASSERT_LAST_CHATLOG(player, "You attach [blood_bag] to [iv_drip].")
+ var/obj/beaker = player.spawn_obj_in_hand(/obj/item/reagent_containers/glass/beaker/cryoxadone)
+ player.click_on(iv_drip)
+ TEST_ASSERT_LAST_CHATLOG(player, "You transfer 10 units of the solution to [blood_bag].")
+ player.put_away(beaker)
+ target.puppet.forceMove(get_turf(iv_drip))
+ iv_drip.drag_drop_onto(target.puppet, player.puppet)
+ TEST_ASSERT_LAST_CHATLOG(player, "[player.puppet] inserts [blood_bag]'s needle into [target.puppet]'s arm")
+
+ // Pills
+ var/obj/pill = player.spawn_obj_in_hand(/obj/item/reagent_containers/pill/salicylic)
+ player.click_on_self()
+ TEST_ASSERT_LAST_CHATLOG(player, "You swallow [pill].")
+ player.retrieve(beaker)
+ player.puppet.swap_hand()
+ pill = player.spawn_obj_in_hand(/obj/item/reagent_containers/pill/salicylic)
+ player.click_on(beaker)
+ TEST_ASSERT_LAST_CHATLOG(player, "You dissolve [pill] in [beaker].")
+
+ // Patches
+ var/obj/item/reagent_containers/patch/patch = player.spawn_obj_in_hand(/obj/item/reagent_containers/patch/silver_sulf)
+ player.click_on_self()
+ TEST_ASSERT_LAST_CHATLOG(player, "You apply [patch].")
+ patch = player.spawn_obj_in_hand(/obj/item/reagent_containers/patch/silver_sulf)
+ patch.instant_application = TRUE
+ player.click_on(target)
+ TEST_ASSERT_LAST_CHATLOG(player, "[player.puppet] forces [target.puppet] to apply [patch].")
+
+/datum/game_test/attack_chain_rags/Run()
+ var/datum/test_puppeteer/player = new(src)
+ var/turf/simulated/floor/floor = get_turf(player.puppet)
+ new/obj/effect/decal/cleanable/dirt(floor)
+ var/obj/item/reagent_containers/glass/rag/rag = player.spawn_obj_in_hand(/obj/item/reagent_containers/glass/rag)
+ rag.wipespeed = 0
+ player.click_on(floor)
+ TEST_ASSERT_LAST_CHATLOG(player, "You clean the floor with the damp rag.")
+
+ var/datum/test_puppeteer/target = player.spawn_puppet_nearby()
+ rag.reagents.add_reagent("omnizine", 10)
+ player.click_on(target)
+ TEST_ASSERT_LAST_CHATLOG(player, "You smother [target.puppet] with [rag]")
+
+/datum/game_test/attack_chain_hyposprays/Run()
+ var/datum/test_puppeteer/player = new(src)
+ var/obj/autoinjector = player.spawn_obj_in_hand(/obj/item/reagent_containers/hypospray/autoinjector/stimpack)
+ player.click_on_self()
+ TEST_ASSERT_ANY_CHATLOG(player, "You feel a tiny prick")
+ TEST_ASSERT_ANY_CHATLOG(player, "You inject [player.puppet] with [autoinjector]")
+ var/mob/target = player.spawn_mob_nearby(/mob/living/carbon/human)
+ player.put_away(autoinjector)
+ autoinjector = player.spawn_obj_in_hand(/obj/item/reagent_containers/hypospray/autoinjector/stimpack)
+ player.click_on(target)
+ TEST_ASSERT_ANY_CHATLOG(player, "You inject [target] with [autoinjector]")
diff --git a/code/tests/game_tests.dm b/code/tests/game_tests.dm
index 7fa7da132d9..6058b4f2057 100644
--- a/code/tests/game_tests.dm
+++ b/code/tests/game_tests.dm
@@ -13,8 +13,9 @@
#include "attack_chain\test_attack_chain_borgs.dm"
#include "attack_chain\test_attack_chain_cult_dagger.dm"
#include "attack_chain\test_attack_chain_machinery.dm"
-#include "attack_chain\test_attack_chain_stunbaton.dm"
#include "attack_chain\test_attack_chain_mobs.dm"
+#include "attack_chain\test_attack_chain_reagent_containers.dm"
+#include "attack_chain\test_attack_chain_stunbaton.dm"
#include "attack_chain\test_attack_chain_turf.dm"
#include "attack_chain\test_attack_chain_vehicles.dm"
#include "games\test_cards.dm"