From 7eb404f562b1c1f9cf8d1567777988735b638e30 Mon Sep 17 00:00:00 2001 From: warriorstar-orion Date: Fri, 21 Mar 2025 16:42:24 -0400 Subject: [PATCH] attack chain migration: reagent containers (#28699) * migrate reagent_containers.dm * applicator migration + game test * borg hypo migration * condiment migration + game test * fixed and expanded applicator game test, helper proc for puppet activate_self * expanded condiment game test, added negative any_chatlog define * drinks_base.dm migration * bottle.dm migrated * molotov migrated * cans migrated * drinking glass migrated * shot glass migration * bottles suck * some fixes * more bottle.dm * bottles finally behave * bottle fixed, shotglass fixed, everfull migrated * bottle tests * cyborg trashbag can crush autopickup is broken * fixed cans insertion into backpacks * added zone targeting to puppet * more tests for drinks * dropper migration * dropper finished, removed a repeated proc * smol tweak * glass containers * glass containers finished * fixed can and bottle tests * molotov test * drinking glass and started shot glass * fixed puppet activate_self + done with drinkingglass * med containers, shuffle tests around a tiny bit * fix autoinjectors * remove return value from spawn block * Apply suggestions from code review Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> Signed-off-by: warriorstar-orion --------- Signed-off-by: warriorstar-orion Co-authored-by: Toastical Co-authored-by: Toastical Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> --- code/game/gamemodes/wizard/artefact.dm | 5 +- code/game/machinery/iv_drip.dm | 10 +- .../objects/items/weapons/alien_specific.dm | 3 +- .../objects/items/weapons/paint_bucket.dm | 7 +- .../objects/items/weapons/storage/firstaid.dm | 4 +- .../objects/items/weapons/tanks/watertank.dm | 18 +- .../detective_work/footprints_and_rag.dm | 13 +- .../food_and_drinks/drinks/drinks/bottle.dm | 37 ++- .../food_and_drinks/drinks/drinks/cans.dm | 42 +-- .../drinks/drinks/drinkingglass.dm | 16 +- .../drinks/drinks/shotglass.dm | 19 +- .../food_and_drinks/drinks/drinks_base.dm | 43 ++- .../modules/food_and_drinks/food/condiment.dm | 55 ++-- code/modules/mob/mob.dm | 2 + code/modules/reagents/reagent_containers.dm | 35 ++- .../reagents/reagent_containers/applicator.dm | 18 +- .../reagents/reagent_containers/borghydro.dm | 34 ++- .../reagents/reagent_containers/dropper.dm | 68 +++-- .../reagent_containers/glass_containers.dm | 93 +++---- .../reagents/reagent_containers/hypospray.dm | 34 +-- .../reagents/reagent_containers/iv_bag.dm | 25 +- .../reagents/reagent_containers/patch.dm | 8 +- .../reagents/reagent_containers/pill.dm | 13 +- .../reagents/reagent_containers/spray.dm | 21 +- .../reagents/reagent_containers/syringes.dm | 20 +- code/modules/research/circuitprinter.dm | 3 - code/modules/research/protolathe.dm | 3 - code/tests/_game_test_puppeteer.dm | 9 + .../test_attack_chain_reagent_containers.dm | 258 ++++++++++++++++++ code/tests/game_tests.dm | 3 +- 30 files changed, 596 insertions(+), 323 deletions(-) create mode 100644 code/tests/attack_chain/test_attack_chain_reagent_containers.dm 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"