diff --git a/code/modules/food_and_drinks/kitchen_machinery/cooker.dm b/code/modules/food_and_drinks/kitchen_machinery/cooker.dm
index 5df528da14b..db54faf3631 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/cooker.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/cooker.dm
@@ -20,6 +20,10 @@
///Set to TRUE if the machine supports upgrades / deconstruction, or else it will ignore stuff like screwdrivers and parts exchangers
var/upgradeable = FALSE
var/datum/looping_sound/kitchen/deep_fryer/soundloop
+ /// Time between special attacks
+ var/special_attack_cooldown_time = 5 SECONDS
+ /// Whether or not a special attack can be performed right now
+ var/special_attack_on_cooldown = FALSE
/obj/machinery/cooker/Initialize(mapload)
. = ..()
@@ -45,13 +49,69 @@
/obj/machinery/cooker/proc/setRegents(obj/item/reagent_containers/OldReg, obj/item/reagent_containers/NewReg)
OldReg.reagents.trans_to(NewReg, OldReg.reagents.total_volume)
+/**
+ * Perform the special grab interaction.
+ * Return TRUE to drop the grab or FALSE to keep the grab afterwards.
+ */
+/obj/machinery/cooker/proc/special_attack(mob/user, mob/living/carbon/target, obj/item/grab/G)
+ to_chat(user, "This is ridiculous. You can not fit [target] in this [src].")
+ return FALSE
+
+/obj/machinery/cooker/shove_impact(mob/living/target, mob/living/attacker)
+ if(special_attack_on_cooldown)
+ return FALSE
+
+ if(!on)
+ // only do a special interaction if it's actually cooking something
+ return FALSE
+
+ . = special_attack_shove(target, attacker)
+ addtimer(VARSET_CALLBACK(src, special_attack_on_cooldown, FALSE), special_attack_cooldown_time)
+
+/**
+ * Perform a special shove attack.
+ * The return value of this proc gets passed up to shove_impact, so returning TRUE will prevent any further shove handling (like knockdown).
+ */
+/obj/machinery/cooker/proc/special_attack_shove(mob/living/target, mob/living/attacker)
+ return FALSE
+
+/**
+ * Verify if we would be able to perform our grab attack.
+ */
+/obj/machinery/cooker/proc/can_grab_attack(obj/item/grab/G, mob/user, verbose = FALSE)
+ if(special_attack_on_cooldown)
+ return FALSE
+ if(!istype(G))
+ return FALSE
+ if(!iscarbon(G.affecting))
+ if(verbose)
+ to_chat(user, "You can't shove that in there!")
+ return FALSE
+ if(G.state < GRAB_AGGRESSIVE)
+ if(verbose)
+ to_chat(user, "You need a better grip to do that!")
+ return FALSE
+ return TRUE
+
+/obj/machinery/cooker/proc/special_attack_grab(obj/item/grab/G, mob/user)
+ if(!can_grab_attack(G, user, FALSE)) // do it silently, but still make sure this isn't called without sanity checking first
+ return FALSE
+ var/result = special_attack(user, G.affecting, G)
+ user.changeNext_move(CLICK_CD_MELEE)
+ special_attack_on_cooldown = TRUE
+ addtimer(VARSET_CALLBACK(src, special_attack_on_cooldown, FALSE), special_attack_cooldown_time)
+ if(result && !isnull(G) && !QDELETED(G))
+ qdel(G)
+
+ return TRUE // end the attack chain
+
// check if you can put it in the machine
/obj/machinery/cooker/proc/checkValid(obj/item/check, mob/user)
if(on)
to_chat(user, "[src] is still active!")
return FALSE
if(istype(check, /obj/item/grab))
- return special_attack(check, user)
+ return can_grab_attack(check, user, TRUE) // tell the user here
if(has_specials && checkSpecials(check))
return TRUE
if(istype(check, /obj/item/reagent_containers/food/snacks) || emagged)
@@ -147,6 +207,8 @@
else
L.death()
break
+ if(istype(I, /obj/item/grab))
+ return special_attack_grab(I, user)
addtimer(CALLBACK(src, PROC_REF(finish_cook), I, user), cooktime)
/obj/machinery/cooker/proc/finish_cook(obj/item/I, mob/user, params)
@@ -190,11 +252,6 @@
if(default_deconstruction_screwdriver(user, openicon, officon, I))
return TRUE
-
-
-/obj/machinery/cooker/proc/special_attack(obj/item/grab/G, mob/user)
- return 0
-
// MAKE SURE TO OVERRIDE THESE ON THE MACHINE IF IT HAS SPECIAL FOOD INTERACTIONS!
// FAILURE TO OVERRIDE WILL RESULT IN FAILURE TO PROPERLY HANDLE SPECIAL INTERACTIONS! --FalseIncarnate
/obj/machinery/cooker/proc/checkSpecials(obj/item/I)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm
index 361051f3e8d..fff98c5a86e 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm
@@ -84,26 +84,33 @@
emagged = TRUE
return
-/obj/machinery/cooker/deepfryer/special_attack(obj/item/grab/G, mob/user)
- if(ishuman(G.affecting))
- if(G.state < GRAB_AGGRESSIVE)
- to_chat(user, "You need a better grip to do that!")
- return 0
- var/mob/living/carbon/human/C = G.affecting
- var/obj/item/organ/external/head/head = C.get_organ("head")
- if(!istype(head))
- to_chat(user, "This person doesn't have a head!")
- return 0
- C.visible_message("[user] dunks [C]'s face into [src]!", \
- "[user] dunks your face into [src]!")
- C.emote("scream")
- user.changeNext_move(CLICK_CD_MELEE)
- C.apply_damage(25, BURN, "head") //25 fire damage and disfigurement because your face was just deep fried!
- head.disfigure()
- add_attack_logs(user, G.affecting, "Deep-fried with [src]")
- qdel(G) //Removes the grip so the person MIGHT have a small chance to run the fuck away and to prevent rapid dunks.
- return 0
- return 0
+/obj/machinery/cooker/deepfryer/special_attack_shove(mob/living/target, mob/living/attacker)
+ target.visible_message(
+ "[attacker] shoves [target] against [src], and [target] reaches into the hot oil trying to catch [target.p_their()] fall!",
+ "[attacker] shoves you into [src], your hands landing in hot oil!",
+ "You hear a splash and a loud sizzle."
+ )
+ target.apply_damage(10, BURN, BODY_ZONE_PRECISE_L_HAND)
+ target.apply_damage(10, BURN, BODY_ZONE_PRECISE_R_HAND)
+ playsound(src, 'sound/goonstation/misc/drinkfizz.ogg', 25)
+ return FALSE
+
+/obj/machinery/cooker/deepfryer/special_attack(mob/user, mob/living/carbon/target)
+ var/obj/item/organ/external/head/head = target.get_organ("head")
+ if(!istype(head))
+ to_chat(user, "This person doesn't have a head!")
+ return FALSE // you'll probably get smacked against it
+ target.visible_message(
+ "[user] dunks [target]'s face into [src]!",
+ "[user] dunks your face into [src]!",
+ "You hear a splash and a loud sizzle."
+ )
+ playsound(src, "sound/machines/kitchen/deep_fryer_emerge.ogg", 100)
+ target.emote("scream")
+ target.apply_damage(45, BURN, "head") // 45 fire damage and disfigurement because your face was just deep fried!
+ head.disfigure()
+ add_attack_logs(user, target, "Deep-fried with [src]")
+ return TRUE
/// Make foam consisting of burning oil.
/obj/machinery/cooker/deepfryer/proc/make_foam(ice_amount)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/grill_new.dm b/code/modules/food_and_drinks/kitchen_machinery/grill_new.dm
index 9234a1c535a..d44d9e3caad 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/grill_new.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/grill_new.dm
@@ -44,18 +44,24 @@
E += M.rating
efficiency = round((E/2), 1) // There's 2 lasers, so halve the effect on the efficiency to keep it balanced
-/obj/machinery/kitchen_machine/grill/special_attack(obj/item/grab/G, mob/user)
- if(ishuman(G.affecting))
- if(G.state < GRAB_AGGRESSIVE)
- to_chat(user, "You need a better grip to do that!")
- return 0
- var/mob/living/carbon/human/C = G.affecting
- C.visible_message("[user] forces [C] onto [src], searing [C]'s body!", \
- "[user] forces you onto [src]! It burns!")
- C.emote("scream")
- user.changeNext_move(CLICK_CD_MELEE)
- C.adjustFireLoss(30)
- add_attack_logs(user, G.affecting, "Burned with [src]")
- qdel(G) //Removes the grip to prevent rapid sears and give you a chance to run
- return 0
- return 0
+/obj/machinery/kitchen_machine/grill/special_attack_shove(mob/user, mob/living/carbon/target)
+ target.visible_message(
+ "[user] shoves [target] onto [src], the active grill surface searing [user.p_them()]!",
+ "[user] shoves you onto [src], and the hot surface sears you!",
+ )
+ target.adjustFireLoss(15)
+
+/obj/machinery/kitchen_machine/grill/special_attack(mob/user, mob/living/carbon/target, from_grab)
+ target.visible_message(
+ "[user] forces [target] onto [src]'s hot cooking surface, searing [target]'s body!",
+ "[user] forces you onto [src]! HOT HOT HOT!",
+ "You hear some meat being put on to cook."
+ )
+
+ target.emote("scream")
+ playsound(src, "sound/machines/kitchen/grill_mid.ogg", 100)
+ target.adjustFireLoss(30)
+ target.forceMove(get_turf(src))
+ target.Weaken(2 SECONDS)
+ add_attack_logs(user, target, "Burned with [src]")
+ return TRUE
diff --git a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm
index 1c8eb739f9a..28298addbb6 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm
@@ -25,6 +25,10 @@
///Sound used when starting and ending cooking
var/datum/looping_sound/kitchen/soundloop
var/soundloop_type
+ /// Time between special attacks
+ var/special_attack_cooldown_time = 7 SECONDS
+ /// Whether or not a special attack can be performed right now
+ var/special_attack_on_cooldown = FALSE
/*******************
* Initialising
@@ -111,8 +115,9 @@
if(!(R.id in GLOB.cooking_reagents[recipe_type]))
to_chat(user, "Your [O] contains components unsuitable for cookery.")
return TRUE
- else if(istype(O,/obj/item/grab))
- return special_attack(O, user)
+ else if(istype(O, /obj/item/grab))
+ var/obj/item/grab/G = O
+ return special_attack_grab(G, user)
else
to_chat(user, "You have no idea what you can cook with [O].")
return TRUE
@@ -141,8 +146,49 @@
/obj/machinery/kitchen_machine/attack_ai(mob/user)
return FALSE
-/obj/machinery/kitchen_machine/proc/special_attack(obj/item/grab/G, mob/user)
- to_chat(user, "This is ridiculous. You can not fit [G.affecting] in this [src].")
+/obj/machinery/kitchen_machine/proc/special_attack_grab(obj/item/grab/G, mob/user)
+ if(special_attack_on_cooldown)
+ return FALSE
+ if(!istype(G))
+ return FALSE
+ if(!iscarbon(G.affecting))
+ to_chat(user, "You can't shove that in there!")
+ return FALSE
+ if(G.state < GRAB_AGGRESSIVE)
+ to_chat(user, "You need a better grip to do that!")
+ return FALSE
+ var/result = special_attack(user, G.affecting, TRUE)
+ user.changeNext_move(CLICK_CD_MELEE)
+ special_attack_on_cooldown = TRUE
+ addtimer(VARSET_CALLBACK(src, special_attack_on_cooldown, FALSE), special_attack_cooldown_time)
+ if(result && !isnull(G) && !QDELETED(G))
+ qdel(G)
+
+ return TRUE
+
+/**
+ * Perform the special grab interaction.
+ * Return TRUE to drop the grab or FALSE to keep the grab afterwards.
+ */
+/obj/machinery/kitchen_machine/proc/special_attack(mob/user, mob/living/carbon/target, obj/item/grab/G)
+ to_chat(user, "This is ridiculous. You can not fit [target] in this [src].")
+ return FALSE
+
+/obj/machinery/kitchen_machine/shove_impact(mob/living/target, mob/living/attacker)
+ if(special_attack_on_cooldown)
+ return FALSE
+
+ if(!operating)
+ // only do a special interaction if it's actually cooking something
+ return FALSE
+
+ return special_attack_shove(target, attacker)
+
+/**
+ * Perform a special shove attack.
+ * The return value of this proc gets passed up to shove_impact, so returning TRUE will prevent any further shove handling (like knockdown).
+ */
+/obj/machinery/kitchen_machine/proc/special_attack_shove(mob/living/target, mob/living/attacker)
return FALSE
/********************
diff --git a/code/modules/food_and_drinks/kitchen_machinery/oven_new.dm b/code/modules/food_and_drinks/kitchen_machinery/oven_new.dm
index ecf8850c546..6abaa5d8d0d 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/oven_new.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/oven_new.dm
@@ -44,24 +44,17 @@
E += M.rating
efficiency = round((E/2), 1) // There's 2 lasers, so halve the effect on the efficiency to keep it balanced
-/obj/machinery/kitchen_machine/oven/special_attack(obj/item/grab/G, mob/user)
- if(ishuman(G.affecting))
- if(G.state < GRAB_AGGRESSIVE)
- to_chat(user, "You need a better grip to do that!")
- return 0
- var/mob/living/carbon/human/C = G.affecting
- var/obj/item/organ/external/head/head = C.get_organ("head")
- if(!istype(head))
- to_chat(user, "This person doesn't have a head!")
- return 0
- C.visible_message("[user] bashes [C]'s head in [src]'s door!", \
- "[user] bashes your head in [src]'s door! It feels rather hot in the oven!")
- C.emote("scream")
- user.changeNext_move(CLICK_CD_MELEE)
- C.apply_damage(5, BURN, "head") //5 fire damage, 15 brute damage, and weakening because your head was just in a hot oven with the door bashing into your neck!
- C.apply_damage(15, BRUTE, "head")
- C.Weaken(4 SECONDS)
- add_attack_logs(user, G.affecting, "Smashed with [src]")
- qdel(G) //Removes the grip to prevent rapid bashes. With the weaken, you PROBABLY can't run unless they are slow to grab you again...
- return 0
- return 0
+/obj/machinery/kitchen_machine/oven/special_attack(mob/user, mob/living/target)
+ var/obj/item/organ/external/head/head = target.get_organ("head")
+ if(!istype(head))
+ to_chat(user, "This person doesn't have a head!")
+ return FALSE
+ target.visible_message("[user] bashes [target]'s head in [src]'s door!", \
+ "[user] bashes your head in [src]'s door! It feels rather hot in the oven!")
+ target.apply_damage(25, BURN, "head") //5 fire damage, 15 brute damage, and weakening because your head was just in a hot oven with the door bashing into your neck!
+ target.apply_damage(25, BRUTE, "head")
+ target.Weaken(4 SECONDS) // With the weaken, you PROBABLY can't run unless they are slow to grab you again...
+ target.emote("scream")
+ playsound(src, "sound/machines/kitchen/oven_loop_end.ogg", 100)
+ add_attack_logs(user, target, "Smashed with [src]")
+ return TRUE