diff --git a/code/__DEFINES/crafting.dm b/code/__DEFINES/crafting.dm index 8218d392640..4b783490568 100644 --- a/code/__DEFINES/crafting.dm +++ b/code/__DEFINES/crafting.dm @@ -5,3 +5,10 @@ #define CAT_FOOD "Food" #define CAT_MISC "Misc" #define CAT_PRIMAL "Tribal" + +#define RECIPE_MICROWAVE "Microwave" +#define RECIPE_OVEN "Oven" +#define RECIPE_GRILL "Grill" +#define RECIPE_CANDY "Candy" + +#define RECIPE_FAIL null \ No newline at end of file diff --git a/code/_globalvars/lists/misc.dm b/code/_globalvars/lists/misc.dm index 65bffa7515a..8a06b552db2 100644 --- a/code/_globalvars/lists/misc.dm +++ b/code/_globalvars/lists/misc.dm @@ -31,6 +31,16 @@ var/list/round_end_sounds = list( // Maps available round end sounds to their du 'sound/goonstation/misc/newround2.ogg' = 14.8 SECONDS ) +GLOBAL_LIST_INIT(cooking_recipe_types, list( + RECIPE_MICROWAVE = /datum/recipe/microwave, + RECIPE_OVEN = /datum/recipe/oven, + RECIPE_GRILL = /datum/recipe/grill, + RECIPE_CANDY = /datum/recipe/candy + )) +GLOBAL_LIST_INIT(cooking_recipes, list(RECIPE_MICROWAVE = list(), RECIPE_OVEN = list(), RECIPE_GRILL = list(), RECIPE_CANDY = list())) +GLOBAL_LIST_INIT(cooking_ingredients, list(RECIPE_MICROWAVE = list(), RECIPE_OVEN = list(), RECIPE_GRILL = list(), RECIPE_CANDY = list())) +GLOBAL_LIST_INIT(cooking_reagents, list(RECIPE_MICROWAVE = list(), RECIPE_OVEN = list(), RECIPE_GRILL = list(), RECIPE_CANDY = list())) + GLOBAL_LIST(station_level_space_turfs) -#define EGG_LAYING_MESSAGES list("lays an egg.", "squats down and croons.", "begins making a huge racket.", "begins clucking raucously.") \ No newline at end of file +#define EGG_LAYING_MESSAGES list("lays an egg.", "squats down and croons.", "begins making a huge racket.", "begins clucking raucously.") diff --git a/code/datums/recipe.dm b/code/datums/recipe.dm index 83ca8f7f0ea..99d97e45626 100644 --- a/code/datums/recipe.dm +++ b/code/datums/recipe.dm @@ -53,19 +53,18 @@ return -1 return . -/datum/recipe/proc/check_items(obj/container) //1=precisely, 0=insufficiently, -1=superfluous - if(!items) - if(locate(/obj/) in container) - return -1 - else - return 1 +/datum/recipe/proc/check_items(obj/container, list/ignored_items = null) //1=precisely, 0=insufficiently, -1=superfluous . = 1 - var/list/checklist = items.Copy() + var/list/checklist = items ? items.Copy() : list() 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 + if(!items) + return -1 var/found = 0 for(var/type in checklist) if(istype(O,type)) - checklist-=type + checklist -= type found = 1 break if(!found) @@ -95,16 +94,16 @@ container.reagents.clear_reagents() return result_obj -/proc/select_recipe(list/datum/recipe/avaiable_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 avaiable_recipes) - if(recipe.check_reagents(obj.reagents)==exact && recipe.check_items(obj)==exact) - possible_recipes+=recipe - if(possible_recipes.len==0) + for(var/datum/recipe/recipe in available_recipes) + if(recipe.check_reagents(obj.reagents) == exact && recipe.check_items(obj, ignored_items) == exact) + possible_recipes += recipe + if(possible_recipes.len == 0) return null - else if(possible_recipes.len==1) + else if(possible_recipes.len == 1) return possible_recipes[1] else //okay, let's select the most complicated recipe var/r_count = 0 diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index 628b754a5ae..2db64b30bdb 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -943,6 +943,8 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /obj/item/reagent_containers/food/condiment/peppermill, /obj/item/kitchen/rollingpin, /obj/item/storage/fancy/egg_box, + /obj/item/mixing_bowl, + /obj/item/mixing_bowl, /obj/item/reagent_containers/food/condiment/enzyme, /obj/item/reagent_containers/food/condiment/sugar, /obj/item/reagent_containers/food/snacks/meat/monkey, diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 8b179c7618c..823727d46ef 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -1223,7 +1223,7 @@ /obj/item/reagent_containers/food/condiment/pack/hotsauce = 5, /obj/item/reagent_containers/food/condiment/saltshaker =5, /obj/item/reagent_containers/food/condiment/peppermill =5, - /obj/item/whetstone = 2, + /obj/item/whetstone = 2, /obj/item/mixing_bowl = 10, /obj/item/kitchen/mould/bear = 1, /obj/item/kitchen/mould/worm = 1, /obj/item/kitchen/mould/bean = 1, /obj/item/kitchen/mould/ball = 1, /obj/item/kitchen/mould/cane = 1, /obj/item/kitchen/mould/cash = 1, diff --git a/code/game/objects/items/mixing_bowl.dm b/code/game/objects/items/mixing_bowl.dm new file mode 100644 index 00000000000..1edfdac9561 --- /dev/null +++ b/code/game/objects/items/mixing_bowl.dm @@ -0,0 +1,170 @@ + +/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/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, GLOB.cooking_ingredients[RECIPE_MICROWAVE]) || is_type_in_list(I, GLOB.cooking_ingredients[RECIPE_GRILL]) || is_type_in_list(I, GLOB.cooking_ingredients[RECIPE_OVEN]) || is_type_in_list(I, GLOB.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)) + var/obj/item/stack/S = I + if(S.amount > 1) + var/obj/item/stack/to_add = S.split(user, 1) + to_add.forceMove(src) + user.visible_message("[user] adds one of [S] to [src].", "You add one of [S] to [src].") + else + return add_item(S, user) + else + return add_item(I, user) + else if(is_type_in_list(I, list(/obj/item/reagent_containers/glass, /obj/item/reagent_containers/food/drinks, /obj/item/reagent_containers/food/condiment))) + if(!I.reagents) + return 1 + for(var/datum/reagent/R in I.reagents.reagent_list) + if(!(R.id in GLOB.cooking_reagents[RECIPE_MICROWAVE]) && !(R.id in GLOB.cooking_reagents[RECIPE_GRILL]) && !(R.id in GLOB.cooking_reagents[RECIPE_OVEN]) && !(R.id in GLOB.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/proc/add_item(obj/item/I, mob/user) + if(!user.drop_item()) + to_chat(user, "\The [I] is stuck to your hand, you cannot put it in [src]") + //return 0 + else + I.forceMove(src) + user.visible_message("[user] adds [I] to [src].", "You add [I] to [src].") + +/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/reagent_containers/food/snacks/egg)) + items_measures[display_name] = "egg" + items_measures_p[display_name] = "eggs" + if(istype(O,/obj/item/reagent_containers/food/snacks/tofu)) + items_measures[display_name] = "tofu chunk" + items_measures_p[display_name] = "tofu chunks" + if(istype(O,/obj/item/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/reagent_containers/food/snacks/donkpocket)) + display_name = "Turnovers" + items_measures[display_name] = "turnover" + items_measures_p[display_name] = "turnovers" + if(istype(O,/obj/item/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 += {"

Eject ingredients!
"} + + var/datum/browser/popup = new(user, name, name, 400, 400) + popup.set_content(dat) + popup.open(0) + onclose(user, "[name]") + return + +/obj/item/mixing_bowl/Topic(href, href_list) + if(..()) + return + if("dispose") + dispose() + return + +/obj/item/mixing_bowl/proc/dispose() + for(var/obj/O in contents) + O.forceMove(loc) + if(reagents.total_volume) + make_dirty(5) + reagents.clear_reagents() + to_chat(usr, "You dispose of [src]'s contents.") + updateUsrDialog() + +/obj/item/mixing_bowl/proc/make_dirty(chance) + if(!chance) + return + if(prob(chance)) + dirty = TRUE + flags = null + icon_state = dirty_icon + +/obj/item/mixing_bowl/proc/clean() + dirty = FALSE + flags = OPENCONTAINER + icon_state = clean_icon + +/obj/item/mixing_bowl/wash(mob/user, atom/source) + if(..()) + clean() + +/obj/item/mixing_bowl/proc/fail(obj/source) + if(!source) + source = src + var/amount = 0 + for(var/obj/O in contents) + amount++ + if(O.reagents) + var/id = O.reagents.get_master_reagent_id() + if(id) + amount+=O.reagents.get_reagent_amount(id) + qdel(O) + if(reagents && reagents.total_volume) + var/id = reagents.get_master_reagent_id() + if(id) + amount += reagents.get_reagent_amount(id) + reagents.clear_reagents() + var/obj/item/reagent_containers/food/snacks/badrecipe/ffuu = new(get_turf(source)) + ffuu.reagents.add_reagent("carbon", amount) + ffuu.reagents.add_reagent("????", amount/10) + make_dirty(75) \ No newline at end of file diff --git a/code/modules/food_and_drinks/kitchen_machinery/candy_maker.dm b/code/modules/food_and_drinks/kitchen_machinery/candy_maker.dm index d83bfaeccb0..be4f18539e2 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/candy_maker.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/candy_maker.dm @@ -5,7 +5,7 @@ icon = 'icons/obj/cooking_machines.dmi' icon_state = "candymaker_off" cook_verbs = list("Wonderizing", "Scrumpdiddlyumptiousification", "Miracle-coating", "Flavorifaction") - recipe_type = /datum/recipe/candy + recipe_type = RECIPE_CANDY off_icon = "candymaker_off" on_icon = "candymaker_on" broken_icon = "candymaker_broke" 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 c1b2612533d..e95b186a9f0 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/grill_new.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/grill_new.dm @@ -5,7 +5,7 @@ icon = 'icons/obj/cooking_machines.dmi' icon_state = "grill_off" cook_verbs = list("Grilling", "Searing", "Frying") - recipe_type = /datum/recipe/grill + recipe_type = RECIPE_GRILL off_icon = "grill_off" on_icon = "grill_on" broken_icon = "grill_broke" 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 83f1126f78f..93b6fdbe8c4 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm @@ -16,10 +16,7 @@ var/list/cook_verbs = list("Cooking") //Recipe & Item vars var/recipe_type //Make sure to set this on the machine definition, or else you're gonna runtime on New() - var/list/datum/recipe/available_recipes // List of the recipes you can use - var/list/acceptable_items // List of the items you can put in - var/list/acceptable_reagents // List of the reagents you can put in - var/max_n_of_items = 0 + var/max_n_of_items = 25 //Icon states var/off_icon var/on_icon @@ -35,23 +32,28 @@ ..() create_reagents(100) reagents.set_reacting(FALSE) - if(!available_recipes) - available_recipes = new - acceptable_items = new - acceptable_reagents = new - for(var/type in subtypesof(recipe_type)) + init_lists() + +/obj/machinery/kitchen_machine/proc/init_lists() + if(!GLOB.cooking_recipes[recipe_type]) + GLOB.cooking_recipes[recipe_type] = list() + GLOB.cooking_ingredients[recipe_type] = list() + GLOB.cooking_reagents[recipe_type] = list() + if(!length(GLOB.cooking_recipes[recipe_type])) + for(var/type in subtypesof(GLOB.cooking_recipe_types[recipe_type])) var/datum/recipe/recipe = new type + if(recipe in GLOB.cooking_recipes[recipe_type]) + qdel(recipe) + continue if(recipe.result) // Ignore recipe subtypes that lack a result - available_recipes += recipe + GLOB.cooking_recipes[recipe_type] += recipe for(var/item in recipe.items) - acceptable_items |= item + GLOB.cooking_ingredients[recipe_type] |= item for(var/reagent in recipe.reagents) - acceptable_reagents |= reagent - if(recipe.items) - max_n_of_items = max(max_n_of_items,recipe.count_n_items()) + GLOB.cooking_reagents[recipe_type] |= reagent else qdel(recipe) - acceptable_items |= /obj/item/reagent_containers/food/snacks/grown + GLOB.cooking_ingredients[recipe_type] |= /obj/item/reagent_containers/food/snacks/grown /******************* * Item Adding @@ -80,26 +82,14 @@ if(broken > 0) if(broken == 2 && istype(O, /obj/item/screwdriver)) // If it's broken and they're using a screwdriver - user.visible_message( \ - "[user] starts to fix part of \the [src].", \ - "You start to fix part of \the [src]." \ - ) + user.visible_message("[user] starts to fix part of [src].", "You start to fix part of [src].") if(do_after(user, 20 * O.toolspeed, target = src)) - user.visible_message( \ - "[user] fixes part of \the [src].", \ - "You have fixed part of \the [src]." \ - ) + user.visible_message("[user] fixes part of [src].", "You have fixed part of \the [src].") broken = 1 // Fix it a bit else if(broken == 1 && istype(O, /obj/item/wrench)) // If it's broken and they're doing the wrench - user.visible_message( \ - "[user] starts to fix part of \the [src].", \ - "You start to fix part of \the [src]." \ - ) + user.visible_message("[user] starts to fix part of [src].", "You start to fix part of [src].") if(do_after(user, 20 * O.toolspeed, target = src)) - user.visible_message( \ - "[user] fixes \the [src].", \ - "You have fixed \the [src]." \ - ) + user.visible_message("[user] fixes [src].", "You have fixed [src].") icon_state = off_icon broken = 0 // Fix it! dirty = 0 // just to be sure @@ -109,15 +99,9 @@ return 1 else if(dirty==100) // The machine is all dirty so can't be used! if(istype(O, /obj/item/reagent_containers/spray/cleaner) || istype(O, /obj/item/soap)) // If they're trying to clean it then let them - user.visible_message( \ - "[user] starts to clean \the [src].", \ - "You start to clean \the [src]." \ - ) + user.visible_message("[user] starts to clean [src].", "You start to clean [src].") if(do_after(user, 20 * O.toolspeed, target = src)) - user.visible_message( \ - "[user] has cleaned \the [src].", \ - "You have cleaned \the [src]." \ - ) + user.visible_message("[user] has cleaned [src].", "You have cleaned [src].") dirty = 0 // It's clean! broken = 0 // just to be sure icon_state = off_icon @@ -125,33 +109,25 @@ else //Otherwise bad luck!! to_chat(user, "It's dirty!") return 1 - else if(is_type_in_list(O,acceptable_items)) + else if(is_type_in_list(O, GLOB.cooking_ingredients[recipe_type]) || istype(O, /obj/item/mixing_bowl)) if(contents.len>=max_n_of_items) to_chat(user, "This [src] is full of ingredients, you cannot put more.") return 1 - if(istype(O,/obj/item/stack) && O:amount>1) - new O.type (src) - O:use(1) - user.visible_message( \ - "[user] has added one of [O] to \the [src].", \ - "You add one of [O] to \the [src].") + if(istype(O,/obj/item/stack)) + var/obj/item/stack/S = O + if(S.amount > 1) + var/obj/item/stack/to_add = S.split(user, 1) + to_add.forceMove(src) + user.visible_message("[user] adds one of [S] to [src].", "You add one of [S] to [src].") + else + add_item(S, user) else - if(!user.drop_item()) - to_chat(user, "\The [O] is stuck to your hand, you cannot put it in \the [src]") - return 0 - - O.forceMove(src) - user.visible_message( \ - "[user] has added \the [O] to \the [src].", \ - "You add \the [O] to \the [src].") - else if(istype(O,/obj/item/reagent_containers/glass) || \ - istype(O,/obj/item/reagent_containers/food/drinks) || \ - istype(O,/obj/item/reagent_containers/food/condiment) \ - ) + add_item(O, user) + else if(is_type_in_list(O, list(/obj/item/reagent_containers/glass, /obj/item/reagent_containers/food/drinks, /obj/item/reagent_containers/food/condiment))) if(!O.reagents) return 1 for(var/datum/reagent/R in O.reagents.reagent_list) - if(!(R.id in acceptable_reagents)) + if(!(R.id in GLOB.cooking_reagents[recipe_type])) to_chat(user, "Your [O] contains components unsuitable for cookery.") return 1 //G.reagents.trans_to(src,G.amount_per_transfer_from_this) @@ -162,6 +138,14 @@ return 1 updateUsrDialog() +/obj/machinery/kitchen_machine/proc/add_item(obj/item/I, mob/user) + if(!user.drop_item()) + to_chat(user, "\The [I] is stuck to your hand, you cannot put it in [src]") + //return 0 + else + I.forceMove(src) + user.visible_message("[user] adds [I] to [src].", "You add [I] to [src].") + /obj/machinery/kitchen_machine/attack_ai(mob/user) return 0 @@ -182,11 +166,11 @@ return var/dat = "" if(broken > 0) - dat = {"Bzzzzttttt"} + dat = {"Bzzzzttttt"} else if(operating) - dat = {"[pick(cook_verbs)] in progress!
Please wait...!
"} + dat = {"[pick(cook_verbs)] in progress!
Please wait...!
"} else if(dirty==100) - dat = {"This [src] is dirty!
Please clean it before use!
"} + dat = {"This [src] is dirty!
Please clean it before use!
"} else var/list/items_counts = new var/list/items_measures = new @@ -260,12 +244,13 @@ stop() return - var/datum/recipe/recipe = select_recipe(available_recipes,src) - var/obj/cooked - var/obj/byproduct - if(!recipe) + var/list/recipes_to_make = choose_recipes() + + if(recipes_to_make.len == 1 && recipes_to_make[1][2] == RECIPE_FAIL) + //This only runs if there is a single recipe source to be made and it is a failure (the machine was loaded with only 1 mixing bowl that results in failure OR was directly loaded with ingredients that results in failure). + //If there are multiple sources, this bit gets skipped. dirty += 1 - if(prob(max(10,dirty*5))) + if(prob(max(10,dirty*5))) //chance to get so dirty we require cleaning before next use if(!wzhzhzh(4)) abort() return @@ -274,14 +259,14 @@ muck_finish() fail() return - else if(has_extra_item()) + else if(has_extra_item()) //if extra items present, break down and require repair before next use if(!wzhzhzh(4)) abort() return broke() fail() return - else + else //otherwise just stop without requiring cleaning/repair if(!wzhzhzh(10)) abort() return @@ -289,24 +274,69 @@ fail() return else - var/halftime = round(recipe.time/10/2) - if(!wzhzhzh(halftime)) + if(!wzhzhzh(5)) abort() return - if(!wzhzhzh(halftime)) + if(!wzhzhzh(5)) abort() fail() return - cooked = recipe.make_food(src) - byproduct = recipe.get_byproduct() - stop() - if(cooked) - cooked.forceMove(loc) - for(var/i=1,i