diff --git a/code/datums/recipe.dm b/code/datums/recipe.dm
index 668c254fe54..edb9c3fc374 100644
--- a/code/datums/recipe.dm
+++ b/code/datums/recipe.dm
@@ -53,15 +53,28 @@
return -1
return .
-/datum/recipe/proc/check_items(obj/container) //1=precisely, 0=insufficiently, -1=superfluous
+/datum/recipe/proc/check_items(obj/container, list/ignored_items = null) //1=precisely, 0=insufficiently, -1=superfluous
if(!items)
if(locate(/obj/) in container)
- return -1
- else
+ if(ignored_items) //are we supposed to be ignoring anything?
+ var/unignored = FALSE
+ for(var/obj/O in container)
+ if(!is_type_in_list(O, ignored_items))
+ unignored = TRUE //found something we aren't ignoring, quit looking
+ break
+ if(unignored)
+ return -1
+ else //anything we found is something we are ignoring
+ return 1
+ else //not ignoring anything, and we found something
+ return -1
+ else //didn't find anything
return 1
. = 1
var/list/checklist = items.Copy()
for(var/obj/O in container)
+ if(ignored_items && is_type_in_list(O, ignored_items)) //skip if this is something we are ignoring
+ continue
var/found = 0
for(var/type in checklist)
if(istype(O,type))
@@ -95,12 +108,12 @@
container.reagents.clear_reagents()
return result_obj
-/proc/select_recipe(list/datum/recipe/available_recipes, obj/obj, exact = 1 as num)
+/proc/select_recipe(list/datum/recipe/available_recipes, obj/obj, exact = 1 as num, list/ignored_items = null)
if(!exact)
exact = -1
var/list/datum/recipe/possible_recipes = new
for(var/datum/recipe/recipe in available_recipes)
- if(recipe.check_reagents(obj.reagents) == exact && recipe.check_items(obj) == exact)
+ if(recipe.check_reagents(obj.reagents) == exact && recipe.check_items(obj, ignored_items) == exact)
possible_recipes += recipe
if(possible_recipes.len == 0)
return null
diff --git a/code/game/objects/items/mixing_bowl.dm b/code/game/objects/items/mixing_bowl.dm
new file mode 100644
index 00000000000..7f66fb2d37f
--- /dev/null
+++ b/code/game/objects/items/mixing_bowl.dm
@@ -0,0 +1,159 @@
+
+/obj/item/mixing_bowl
+ name = "mixing bowl"
+ desc = "Mixing it up in the kitchen."
+ flags = OPENCONTAINER
+ icon = 'icons/obj/kitchen.dmi'
+ icon_state = "mixing_bowl"
+ var/max_n_of_items = 25
+ var/dirty = FALSE
+ var/clean_icon = "mixing_bowl"
+ var/dirty_icon = "mixing_bowl_dirty"
+
+/obj/item/mixing_bowl/New()
+ ..()
+ create_reagents(100)
+
+/obj/item/mixing_bowl/attackby(obj/item/I, mob/user, params)
+ if(dirty)
+ if(istype(I, /obj/item/weapon/soap))
+ user.visible_message("[user] starts to scrub [src].", "You start to scrub [src].")
+ if(do_after(user, 20 * I.toolspeed, target = src))
+ clean()
+ user.visible_message("[user] has scrubbed [src] clean.", "You have scrubbed [src] clean.")
+ return 1
+ else
+ to_chat(user, "You should clean [src] before you use it for food prep.")
+ return 0
+ if(is_type_in_list(I, cooking_ingredients[RECIPE_MICROWAVE]) || is_type_in_list(I, cooking_ingredients[RECIPE_GRILL]) || is_type_in_list(I, cooking_ingredients[RECIPE_OVEN]) || is_type_in_list(I, cooking_ingredients[RECIPE_CANDY]))
+ if(contents.len>=max_n_of_items)
+ to_chat(user, "This [src] is full of ingredients, you cannot put more.")
+ return 1
+ if(istype(I, /obj/item/stack) && I:amount > 1)
+ new I.type (src)
+ I:use(1)
+ user.visible_message("[user] has added one of [I] to [src].", "You add one of [I] to [src].")
+ else
+ if(!user.drop_item())
+ to_chat(user, "\The [I] is stuck to your hand, you cannot put it in [src]")
+ return 0
+ I.forceMove(src)
+ user.visible_message("[user] has added [I] to [src].", "You add [I] to [src].")
+ else if(is_type_in_list(I, list(/obj/item/weapon/reagent_containers/glass, /obj/item/weapon/reagent_containers/food/drinks, /obj/item/weapon/reagent_containers/food/condiment)))
+ if(!I.reagents)
+ return 1
+ for(var/datum/reagent/R in I.reagents.reagent_list)
+ if(!(R.id in cooking_reagents[RECIPE_MICROWAVE]) && !(R.id in cooking_reagents[RECIPE_GRILL]) && !(R.id in cooking_reagents[RECIPE_OVEN]) && !(R.id in cooking_reagents[RECIPE_CANDY]))
+ to_chat(user, "Your [I] contains components unsuitable for cookery.")
+ return 1
+ else
+ to_chat(user, "You have no idea what you can cook with [I].")
+ return 1
+
+/obj/item/mixing_bowl/attack_self(mob/user)
+ var/dat = ""
+ if(dirty)
+ dat = {"This [src] is dirty!
Please clean it before use!"}
+ else
+ var/list/items_counts = new
+ var/list/items_measures = new
+ var/list/items_measures_p = new
+ for(var/obj/O in contents)
+ var/display_name = O.name
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg))
+ items_measures[display_name] = "egg"
+ items_measures_p[display_name] = "eggs"
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu))
+ items_measures[display_name] = "tofu chunk"
+ items_measures_p[display_name] = "tofu chunks"
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat
+ items_measures[display_name] = "slab of meat"
+ items_measures_p[display_name] = "slabs of meat"
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket))
+ display_name = "Turnovers"
+ items_measures[display_name] = "turnover"
+ items_measures_p[display_name] = "turnovers"
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat))
+ items_measures[display_name] = "fillet of meat"
+ items_measures_p[display_name] = "fillets of meat"
+ items_counts[display_name]++
+ for(var/O in items_counts)
+ var/N = items_counts[O]
+ if(!(O in items_measures))
+ dat += {"[capitalize(O)]: [N] [lowertext(O)]\s
"}
+ else
+ if(N==1)
+ dat += {"[capitalize(O)]: [N] [items_measures[O]]
"}
+ else
+ dat += {"[capitalize(O)]: [N] [items_measures_p[O]]
"}
+
+ for(var/datum/reagent/R in reagents.reagent_list)
+ var/display_name = R.name
+ if(R.id == "capsaicin")
+ display_name = "Hotsauce"
+ if(R.id == "frostoil")
+ display_name = "Coldsauce"
+ dat += {"[display_name]: [R.volume] unit\s
"}
+
+ if(items_counts.len==0 && reagents.reagent_list.len==0)
+ dat = {"The [src] is empty
"}
+ else
+ dat = {"Ingredients:
[dat]"}
+ dat += {"